From fd7e92418a3b66bca0926e88f2bf66de13c25ca3 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 12:23:40 -0700 Subject: [PATCH 001/926] Create a summary Markdown file for all asm diffs (#54430) Create a per-MCH file summary.md file, then accumulate them all into a single overall summary Markdown file, for use in GitHub comments. Uses the existing `jit-analyze --md` functionality. Also, change asm diffs to not report missing data or asm diffs as a replay failure. Finally, don't overwrite superpmi.log files (or the new diff_summary.md files): create new, unique file names for each run. --- src/coreclr/scripts/superpmi.py | 95 ++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index d4033201ca0..ce4332c3145 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -630,6 +630,39 @@ def create_unique_directory_name(root_directory, base_name): return full_path +def create_unique_file_name(directory, base_name, extension): + """ Create a unique file name in the specified directory by joining `base_name` and `extension`. + If this name already exists, append ".1", ".2", ".3", etc., to the `base_name` + name component until the full file name is not found. + + Args: + directory (str) : directory in which a new file will be created + base_name (str) : the base name of the new filename to be added + extension (str) : the filename extension of the new filename to be added + + Returns: + (str) The full absolute path of the new filename. + """ + + directory = os.path.abspath(directory) + if not os.path.isdir(directory): + try: + os.makedirs(directory) + except Exception as exception: + logging.critical(exception) + raise exception + + full_path = os.path.join(directory, base_name + "." + extension) + + count = 1 + while os.path.isfile(full_path): + new_full_path = os.path.join(directory, base_name + "." + str(count) + "." + extension) + count += 1 + full_path = new_full_path + + return full_path + + def get_files_from_path(path, match_func=lambda path: True): """ Return all files in a directory tree matching a criteria. @@ -1517,14 +1550,6 @@ class SuperPMIReplay: result = True # Assume success - # Possible return codes from SuperPMI - # - # 0 : success - # -1 : general fatal error (e.g., failed to initialize, failed to read files) - # -2 : JIT failed to initialize - # 1 : there were compilation failures - # 2 : there were assembly diffs - with TempDir() as temp_location: logging.debug("") logging.debug("Temp Location: %s", temp_location) @@ -1596,8 +1621,12 @@ class SuperPMIReplay: if return_code == 0: logging.info("Clean SuperPMI replay") else: - files_with_replay_failures.append(mch_file) result = False + # Don't report as replay failure missing data (return code 3). + # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be + # reported as a replay failure. + if return_code != 3: + files_with_replay_failures.append(mch_file) if is_nonzero_length_file(fail_mcl_file): # Unclean replay. Examine the contents of the fail.mcl file to dig into failures. @@ -1669,14 +1698,6 @@ class SuperPMIReplayAsmDiffs: result = True # Assume success - # Possible return codes from SuperPMI - # - # 0 : success - # -1 : general fatal error (e.g., failed to initialize, failed to read files) - # -2 : JIT failed to initialize - # 1 : there were compilation failures - # 2 : there were assembly diffs - # Set up some settings we'll use below. asm_complus_vars = { @@ -1744,6 +1765,9 @@ class SuperPMIReplayAsmDiffs: files_with_asm_diffs = [] files_with_replay_failures = [] + # List of all Markdown summary files + all_md_summary_files = [] + with TempDir(self.coreclr_args.temp_dir, self.coreclr_args.skip_cleanup) as temp_location: logging.debug("") logging.debug("Temp Location: %s", temp_location) @@ -1804,8 +1828,12 @@ class SuperPMIReplayAsmDiffs: if return_code == 0: logging.info("Clean SuperPMI replay") else: - files_with_replay_failures.append(mch_file) result = False + # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3). + # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be + # reported as a replay failure. + if return_code != 2 and return_code != 3: + files_with_replay_failures.append(mch_file) artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) @@ -1938,7 +1966,10 @@ class SuperPMIReplayAsmDiffs: jit_analyze_path = find_file(jit_analyze_file, path_var.split(os.pathsep)) if jit_analyze_path is not None: # It appears we have a built jit-analyze on the path, so try to run it. - command = [ jit_analyze_path, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] + md_summary_file = os.path.join(asm_root_dir, "summary.md") + summary_file_info = ( mch_file, md_summary_file ) + all_md_summary_files.append(summary_file_info) + command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] run_and_log(command, logging.INFO) ran_jit_analyze = True @@ -1971,8 +2002,32 @@ class SuperPMIReplayAsmDiffs: ################################################################################################ end of for mch_file in self.mch_files + # Report the overall results summary of the asmdiffs run + logging.info("Asm diffs summary:") + # Construct an overall Markdown summary file. + + if len(all_md_summary_files) > 0: + overall_md_summary_file = create_unique_file_name(self.coreclr_args.spmi_location, "diff_summary", "md") + if not os.path.isdir(self.coreclr_args.spmi_location): + os.makedirs(self.coreclr_args.spmi_location) + if os.path.isfile(overall_md_summary_file): + os.remove(overall_md_summary_file) + + with open(overall_md_summary_file, "w") as write_fh: + for summary_file_info in all_md_summary_files: + summary_mch = summary_file_info[0] + summary_mch_filename = os.path.basename(summary_mch) # Display just the MCH filename, not the full path + summary_file = summary_file_info[1] + with open(summary_file, "r") as read_fh: + write_fh.write("## " + summary_mch_filename + ":\n\n") + shutil.copyfileobj(read_fh, write_fh) + + logging.info(" Summary Markdown file: %s", overall_md_summary_file) + + # Report the set of MCH files with asm diffs and replay failures. + if len(files_with_replay_failures) != 0: logging.info(" Replay failures in %s MCH files:", len(files_with_replay_failures)) for file in files_with_replay_failures: @@ -3173,7 +3228,7 @@ def setup_args(args): log_file = None if coreclr_args.log_file is None: if hasattr(coreclr_args, "spmi_location"): - log_file = os.path.join(coreclr_args.spmi_location, "superpmi.log") + log_file = create_unique_file_name(coreclr_args.spmi_location, "superpmi", "log") if not os.path.isdir(coreclr_args.spmi_location): os.makedirs(coreclr_args.spmi_location) else: From f722ad0061a628a7b7b3c0772153de71ddfe8196 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 18 Jun 2021 21:40:18 +0200 Subject: [PATCH 002/926] set UsableAfterCanceledReads for QUIC tests (#54416) --- .../QuicStreamConnectedStreamConformanceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index dbf3fae0f65..35b54bc6d70 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -21,6 +21,7 @@ namespace System.Net.Quic.Tests public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTests { protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; + protected override bool UsableAfterCanceledReads => false; // TODO: These are all hanging, likely due to Stream close behavior. [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] @@ -42,8 +43,6 @@ namespace System.Net.Quic.Tests [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads() => base.ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); From 6737dce33d35970fde2ca349cc1492da977b9699 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Fri, 18 Jun 2021 23:17:18 +0300 Subject: [PATCH 003/926] Backport bugfixes, infrastructural changes and perf improvements from the polymorphism feature branch (#54420) * backport opportunistic bugfixes * strengthen debug assertions and add clarifying comments * Rework WriteStack/ReadStack implementations * Use array instead of list for storing frames. Previous frames can now be passed by reference * Ensure that the _count field always reflects the current depth. * Remove StackFrame.Reset() methods which are occassionally a source of dirty frames being reused. * Take advantage of JIT optimizations in the serialization hot path * address PR feedback --- .../Object/ObjectDefaultConverter.cs | 4 +- .../Json/Serialization/JsonConverterOfT.cs | 148 ++++++++++-------- .../JsonSerializer.Write.String.cs | 3 - .../Metadata/JsonPropertyInfoOfT.cs | 12 +- .../Text/Json/Serialization/ReadStack.cs | 133 ++++++++-------- .../Text/Json/Serialization/ReadStackFrame.cs | 15 -- .../Text/Json/Serialization/WriteStack.cs | 137 ++++++++-------- .../Json/Serialization/WriteStackFrame.cs | 15 -- .../ReferenceHandlerTests.IgnoreCycles.cs | 21 ++- 9 files changed, 237 insertions(+), 251 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index 4a2be44c862..89b9cdad1ee 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -312,7 +312,9 @@ namespace System.Text.Json.Serialization.Converters if (!jsonPropertyInfo.GetMemberAndWriteJson(objectValue!, ref state, writer)) { - Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value); + Debug.Assert(jsonPropertyInfo.ConverterBase.ConverterStrategy != ConverterStrategy.Value || + jsonPropertyInfo.ConverterBase.TypeToConvert == JsonTypeInfo.ObjectType); + return false; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 6ea73dd49c9..543fbe2f522 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -22,7 +22,7 @@ namespace System.Text.Json.Serialization // In the future, this will be check for !IsSealed (and excluding value types). CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType; IsValueType = TypeToConvert.IsValueType; - CanBeNull = !IsValueType || TypeToConvert.IsNullableOfT(); + CanBeNull = default(T) is null; IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; if (HandleNull) @@ -220,7 +220,12 @@ namespace System.Text.Json.Serialization // Remember if we were a continuation here since Push() may affect IsContinuation. bool wasContinuation = state.IsContinuation; +#if DEBUG + // DEBUG: ensure push/pop operations preserve stack integrity + JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; +#endif state.Push(); + Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. @@ -257,6 +262,9 @@ namespace System.Text.Json.Serialization value = default; state.Pop(true); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif return true; } @@ -288,6 +296,9 @@ namespace System.Text.Json.Serialization } state.Pop(success); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif return success; } @@ -298,6 +309,9 @@ namespace System.Text.Json.Serialization return success; } + /// + /// Overridden by the nullable converter to prevent boxing of values by the JIT. + /// internal virtual bool IsNull(in T value) => value == null; internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions options, ref WriteStack state) @@ -307,7 +321,7 @@ namespace System.Text.Json.Serialization ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); } - if (CanBeNull && !HandleNullOnWrite && IsNull(value)) + if (default(T) is null && !HandleNullOnWrite && IsNull(value)) { // We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties were // already handled in GetMemberAndWriteJson() so we don't need to check for IgnoreNullValues here. @@ -317,81 +331,76 @@ namespace System.Text.Json.Serialization bool ignoreCyclesPopReference = false; - if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. - ConverterStrategy != ConverterStrategy.Value && - !IsValueType && !IsNull(value)) + if ( +#if NET6_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#else + !IsValueType && +#endif + value is not null) { - // Custom (user) converters shall not track references - // it is responsibility of the user to break cycles in case there's any - // if we compare against Preserve, objects don't get preserved when a custom converter exists - // given that the custom converter executes prior to the preserve logic. - Debug.Assert(IsInternalConverter); - Debug.Assert(value != null); - ReferenceResolver resolver = state.ReferenceResolver; - - // Write null to break reference cycles. - if (resolver.ContainsReferenceForCycleDetection(value)) + if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. + ConverterStrategy != ConverterStrategy.Value) { - writer.WriteNullValue(); - return true; - } + // Custom (user) converters shall not track references + // it is responsibility of the user to break cycles in case there's any + // if we compare against Preserve, objects don't get preserved when a custom converter exists + // given that the custom converter executes prior to the preserve logic. + Debug.Assert(IsInternalConverter); - // For boxed reference types: do not push when boxed in order to avoid false positives - // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value. - Debug.Assert(!CanBePolymorphic); - resolver.PushReferenceForCycleDetection(value); - ignoreCyclesPopReference = true; - } + ReferenceResolver resolver = state.ReferenceResolver; - if (CanBePolymorphic) - { - if (value == null) - { - Debug.Assert(ConverterStrategy == ConverterStrategy.Value); - Debug.Assert(!state.IsContinuation); - Debug.Assert(HandleNullOnWrite); - - int originalPropertyDepth = writer.CurrentDepth; - Write(writer, value, options); - VerifyWrite(originalPropertyDepth, writer); - - return true; - } - - Type type = value.GetType(); - if (type == JsonTypeInfo.ObjectType) - { - writer.WriteStartObject(); - writer.WriteEndObject(); - return true; - } - - if (type != TypeToConvert && IsInternalConverter) - { - // For internal converter only: Handle polymorphic case and get the new converter. - // Custom converter, even though polymorphic converter, get called for reading AND writing. - JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options); - Debug.Assert(jsonConverter != this); - - if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - jsonConverter.IsValueType) + // Write null to break reference cycles. + if (resolver.ContainsReferenceForCycleDetection(value)) { - // For boxed value types: push the value before it gets unboxed on TryWriteAsObject. - state.ReferenceResolver.PushReferenceForCycleDetection(value); - ignoreCyclesPopReference = true; + writer.WriteNullValue(); + return true; } - // We found a different converter; forward to that. - bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state); + // For boxed reference types: do not push when boxed in order to avoid false positives + // when we run the ContainsReferenceForCycleDetection check for the converter of the unboxed value. + Debug.Assert(!CanBePolymorphic); + resolver.PushReferenceForCycleDetection(value); + ignoreCyclesPopReference = true; + } - if (ignoreCyclesPopReference) + if (CanBePolymorphic) + { + Type type = value.GetType(); + if (type == JsonTypeInfo.ObjectType) { - state.ReferenceResolver.PopReferenceForCycleDetection(); + writer.WriteStartObject(); + writer.WriteEndObject(); + return true; } - return success2; + if (type != TypeToConvert && IsInternalConverter) + { + // For internal converter only: Handle polymorphic case and get the new converter. + // Custom converter, even though polymorphic converter, get called for reading AND writing. + JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options); + Debug.Assert(jsonConverter != this); + + if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + jsonConverter.IsValueType) + { + // For boxed value types: push the value before it gets unboxed on TryWriteAsObject. + state.ReferenceResolver.PushReferenceForCycleDetection(value); + ignoreCyclesPopReference = true; + } + + // We found a different converter; forward to that. + bool success2 = jsonConverter.TryWriteAsObject(writer, value, options, ref state); + + if (ignoreCyclesPopReference) + { + state.ReferenceResolver.PopReferenceForCycleDetection(); + } + + return success2; + } } } @@ -416,7 +425,12 @@ namespace System.Text.Json.Serialization bool isContinuation = state.IsContinuation; +#if DEBUG + // DEBUG: ensure push/pop operations preserve stack integrity + JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; +#endif state.Push(); + Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); if (!isContinuation) { @@ -432,6 +446,9 @@ namespace System.Text.Json.Serialization } state.Pop(success); +#if DEBUG + Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); +#endif if (ignoreCyclesPopReference) { @@ -476,6 +493,7 @@ namespace System.Text.Json.Serialization // Ignore the naming policy for extension data. state.Current.IgnoreDictionaryKeyPolicy = true; + state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; success = dictionaryConverter.OnWriteResume(writer, value, options, ref state); if (success) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs index 71c30c51275..db52ffc079f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs @@ -125,9 +125,6 @@ namespace System.Text.Json throw new ArgumentNullException(nameof(jsonTypeInfo)); } - WriteStack state = default; - state.Initialize(jsonTypeInfo, supportContinuation: false); - JsonSerializerOptions options = jsonTypeInfo.Options; using (var output = new PooledByteBufferWriter(options.DefaultBufferSize)) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index 911ee9f3ede..0f2ff8574f0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -234,11 +234,17 @@ namespace System.Text.Json.Serialization.Metadata { T value = Get!(obj); - if (Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && - value != null && + if ( +#if NET6_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#else + !Converter.IsValueType && +#endif + Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && + value is not null && // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection e.g: string. // However JsonConverter that uses ConverterStrategy == Value is an exception. - (Converter.CanBePolymorphic || (!Converter.IsValueType && ConverterStrategy != ConverterStrategy.Value)) && + (Converter.CanBePolymorphic || ConverterStrategy != ConverterStrategy.Value) && state.ReferenceResolver.ContainsReferenceForCycleDetection(value)) { // If a reference cycle is detected, treat value as null. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs index 2f0347f6833..c78646f45f4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs @@ -16,16 +16,24 @@ namespace System.Text.Json internal static readonly char[] SpecialCharacters = { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; /// - /// The number of stack frames when the continuation started. + /// Exposes the stackframe that is currently active. /// - private int _continuationCount; + public ReadStackFrame Current; /// - /// The number of stack frames including Current. _previous will contain _count-1 higher frames. + /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1. + /// + private ReadStackFrame[] _stack; + + /// + /// Tracks the current depth of the stack. /// private int _count; - private List _previous; + /// + /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth. + /// + private int _continuationCount; // State cache when deserializing objects with parameterized constructors. private List? _ctorArgStateCache; @@ -35,11 +43,10 @@ namespace System.Text.Json /// public long BytesConsumed; - // A field is used instead of a property to avoid value semantics. - public ReadStackFrame Current; - + /// + /// Indicates that the state still contains suspended frames waiting re-entry. + /// public bool IsContinuation => _continuationCount != 0; - public bool IsLastContinuation => _continuationCount == _count; /// /// Internal flag to let us know that we need to read ahead in the inner read loop. @@ -59,25 +66,19 @@ namespace System.Text.Json /// public bool UseFastPath; - private void AddCurrent() + /// + /// Ensures that the stack buffer has sufficient capacity to hold an additional frame. + /// + private void EnsurePushCapacity() { - if (_previous == null) + if (_stack is null) { - _previous = new List(); + _stack = new ReadStackFrame[4]; } - - if (_count > _previous.Count) + else if (_count - 1 == _stack.Length) { - // Need to allocate a new array element. - _previous.Add(Current); + Array.Resize(ref _stack, 2 * _stack.Length); } - else - { - // Use a previously allocated slot. - _previous[_count - 1] = Current; - } - - _count++; } public void Initialize(Type type, JsonSerializerOptions options, bool supportContinuation) @@ -143,8 +144,10 @@ namespace System.Text.Json jsonTypeInfo = Current.JsonTypeInfo.ElementTypeInfo!; } - AddCurrent(); - Current.Reset(); + EnsurePushCapacity(); + _stack[_count - 1] = Current; + Current = default; + _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; @@ -152,29 +155,26 @@ namespace System.Text.Json Current.NumberHandling = numberHandling ?? Current.JsonPropertyInfo.NumberHandling; } } - else if (_continuationCount == 1) - { - // No need for a push since there is only one stack frame. - Debug.Assert(_count == 1); - _continuationCount = 0; - } else { - // A continuation; adjust the index. - Current = _previous[_count - 1]; + // We are re-entering a continuation, adjust indices accordingly + if (_count++ > 0) + { + Current = _stack[_count - 1]; + } - // Check if we are done. - if (_count == _continuationCount) + // check if we are done + if (_continuationCount == _count) { _continuationCount = 0; } - else - { - _count++; - } } SetConstructorArgumentState(); +#if DEBUG + // Ensure the method is always exercised in debug builds. + _ = JsonPath(); +#endif } public void Pop(bool success) @@ -188,41 +188,34 @@ namespace System.Text.Json { if (_count == 1) { - // No need for a continuation since there is only one stack frame. + // No need to copy any frames here. _continuationCount = 1; - } - else - { - AddCurrent(); - _count--; - _continuationCount = _count; - _count--; - Current = _previous[_count - 1]; + _count = 0; + return; } - return; + // Need to push the Current frame to the stack, + // ensure that we have sufficient capacity. + EnsurePushCapacity(); + _continuationCount = _count--; } - - if (_continuationCount == 1) + else if (--_count == 0) { - // No need for a pop since there is only one stack frame. - Debug.Assert(_count == 1); + // reached the root, no need to copy frames. return; } - // Update the list entry to the current value. - _previous[_count - 1] = Current; - - Debug.Assert(_count > 0); + _stack[_count] = Current; + Current = _stack[_count - 1]; } else { Debug.Assert(_continuationCount == 0); - } - if (_count > 1) - { - Current = _previous[--_count -1]; + if (--_count > 0) + { + Current = _stack[_count - 1]; + } } SetConstructorArgumentState(); @@ -240,26 +233,25 @@ namespace System.Text.Json for (int i = 0; i < count - 1; i++) { - AppendStackFrame(sb, _previous[i]); + AppendStackFrame(sb, ref _stack[i]); } if (_continuationCount == 0) { - AppendStackFrame(sb, Current); + AppendStackFrame(sb, ref Current); } return sb.ToString(); - static void AppendStackFrame(StringBuilder sb, in ReadStackFrame frame) + static void AppendStackFrame(StringBuilder sb, ref ReadStackFrame frame) { // Append the property name. - string? propertyName = GetPropertyName(frame); + string? propertyName = GetPropertyName(ref frame); AppendPropertyName(sb, propertyName); if (frame.JsonTypeInfo != null && frame.IsProcessingEnumerable()) { - IEnumerable? enumerable = (IEnumerable?)frame.ReturnValue; - if (enumerable == null) + if (frame.ReturnValue is not IEnumerable enumerable) { return; } @@ -276,7 +268,7 @@ namespace System.Text.Json } } - static int GetCount(IEnumerable enumerable) + static int GetCount(IEnumerable enumerable) { if (enumerable is ICollection collection) { @@ -311,7 +303,7 @@ namespace System.Text.Json } } - static string? GetPropertyName(in ReadStackFrame frame) + static string? GetPropertyName(ref ReadStackFrame frame) { string? propertyName = null; @@ -350,10 +342,7 @@ namespace System.Text.Json // A zero index indicates a new stack frame. if (Current.CtorArgumentStateIndex == 0) { - if (_ctorArgStateCache == null) - { - _ctorArgStateCache = new List(); - } + _ctorArgStateCache ??= new List(); var newState = new ArgumentState(); _ctorArgStateCache.Add(newState); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs index f4f64dcbb1a..137305f1d5b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs @@ -89,20 +89,5 @@ namespace System.Text.Json { return (JsonTypeInfo.PropertyInfoForTypeInfo.ConverterStrategy & ConverterStrategy.Enumerable) != 0; } - - public void Reset() - { - CtorArgumentStateIndex = 0; - CtorArgumentState = null; - JsonTypeInfo = null!; - ObjectState = StackFrameObjectState.None; - OriginalDepth = 0; - OriginalTokenType = JsonTokenType.None; - PropertyIndex = 0; - PropertyRefCache = null; - ReturnValue = null; - - EndProperty(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index 22f67983ec3..e9a9f1e9e75 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -16,15 +16,25 @@ namespace System.Text.Json internal struct WriteStack { /// - /// The number of stack frames when the continuation started. + /// Exposes the stackframe that is currently active. /// - private int _continuationCount; + public WriteStackFrame Current; /// - /// The number of stack frames including Current. _previous will contain _count-1 higher frames. + /// Buffer containing all frames in the stack. For performance it is only populated for serialization depths > 1. + /// + private WriteStackFrame[] _stack; + + /// + /// Tracks the current depth of the stack. /// private int _count; + /// + /// If not zero, indicates that the stack is part of a re-entrant continuation of given depth. + /// + private int _continuationCount; + /// /// Cancellation token used by converters performing async serialization (e.g. IAsyncEnumerable) /// @@ -41,17 +51,15 @@ namespace System.Text.Json /// public List? PendingAsyncDisposables; - private List _previous; - - // A field is used instead of a property to avoid value semantics. - public WriteStackFrame Current; - /// /// The amount of bytes to write before the underlying Stream should be flushed and the /// current buffer adjusted to remove the processed bytes. /// public int FlushThreshold; + /// + /// Indicates that the state still contains suspended frames waiting re-entry. + /// public bool IsContinuation => _continuationCount != 0; // The bag of preservable references. @@ -62,25 +70,16 @@ namespace System.Text.Json /// public bool SupportContinuation; - private void AddCurrent() + private void EnsurePushCapacity() { - if (_previous == null) + if (_stack is null) { - _previous = new List(); + _stack = new WriteStackFrame[4]; } - - if (_count > _previous.Count) + else if (_count - 1 == _stack.Length) { - // Need to allocate a new array element. - _previous.Add(Current); + Array.Resize(ref _stack, 2 * _stack.Length); } - else - { - // Use a previously allocated slot. - _previous[_count - 1] = Current; - } - - _count++; } /// @@ -125,8 +124,10 @@ namespace System.Text.Json JsonTypeInfo jsonTypeInfo = Current.GetPolymorphicJsonPropertyInfo().RuntimeTypeInfo; JsonNumberHandling? numberHandling = Current.NumberHandling; - AddCurrent(); - Current.Reset(); + EnsurePushCapacity(); + _stack[_count - 1] = Current; + Current = default; + _count++; Current.JsonTypeInfo = jsonTypeInfo; Current.DeclaredJsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; @@ -134,27 +135,25 @@ namespace System.Text.Json Current.NumberHandling = numberHandling ?? Current.DeclaredJsonPropertyInfo.NumberHandling; } } - else if (_continuationCount == 1) - { - // No need for a push since there is only one stack frame. - Debug.Assert(_count == 1); - _continuationCount = 0; - } else { - // A continuation, adjust the index. - Current = _previous[_count - 1]; + // We are re-entering a continuation, adjust indices accordingly + if (_count++ > 0) + { + Current = _stack[_count - 1]; + } - // Check if we are done. - if (_count == _continuationCount) + // check if we are done + if (_continuationCount == _count) { _continuationCount = 0; } - else - { - _count++; - } } + +#if DEBUG + // Ensure the method is always exercised in debug builds. + _ = PropertyPath(); +#endif } public void Pop(bool success) @@ -168,33 +167,25 @@ namespace System.Text.Json { if (_count == 1) { - // No need for a continuation since there is only one stack frame. + // No need to copy any frames here. _continuationCount = 1; - _count = 1; - } - else - { - AddCurrent(); - _count--; - _continuationCount = _count; - _count--; - Current = _previous[_count - 1]; + _count = 0; + return; } - return; + // Need to push the Current frame to the stack, + // ensure that we have sufficient capacity. + EnsurePushCapacity(); + _continuationCount = _count--; } - - if (_continuationCount == 1) + else if (--_count == 0) { - // No need for a pop since there is only one stack frame. - Debug.Assert(_count == 1); + // reached the root, no need to copy frames. return; } - // Update the list entry to the current value. - _previous[_count - 1] = Current; - - Debug.Assert(_count > 0); + _stack[_count] = Current; + Current = _stack[_count - 1]; } else { @@ -207,11 +198,11 @@ namespace System.Text.Json PendingAsyncDisposables ??= new List(); PendingAsyncDisposables.Add(Current.AsyncEnumerator); } - } - if (_count > 1) - { - Current = _previous[--_count - 1]; + if (--_count > 0) + { + Current = _stack[_count - 1]; + } } } @@ -253,13 +244,10 @@ namespace System.Text.Json DisposeFrame(Current.CollectionEnumerator, ref exception); int stackSize = Math.Max(_count, _continuationCount); - if (stackSize > 1) + for (int i = 0; i < stackSize - 1; i++) { - for (int i = 0; i < stackSize - 1; i++) - { - Debug.Assert(_previous[i].AsyncEnumerator is null); - DisposeFrame(_previous[i].CollectionEnumerator, ref exception); - } + Debug.Assert(_stack[i].AsyncEnumerator is null); + DisposeFrame(_stack[i].CollectionEnumerator, ref exception); } if (exception is not null) @@ -294,12 +282,9 @@ namespace System.Text.Json exception = await DisposeFrame(Current.CollectionEnumerator, Current.AsyncEnumerator, exception).ConfigureAwait(false); int stackSize = Math.Max(_count, _continuationCount); - if (stackSize > 1) + for (int i = 0; i < stackSize - 1; i++) { - for (int i = 0; i < stackSize - 1; i++) - { - exception = await DisposeFrame(_previous[i].CollectionEnumerator, _previous[i].AsyncEnumerator, exception).ConfigureAwait(false); - } + exception = await DisposeFrame(_stack[i].CollectionEnumerator, _stack[i].AsyncEnumerator, exception).ConfigureAwait(false); } if (exception is not null) @@ -343,17 +328,17 @@ namespace System.Text.Json for (int i = 0; i < count - 1; i++) { - AppendStackFrame(sb, _previous[i]); + AppendStackFrame(sb, ref _stack[i]); } if (_continuationCount == 0) { - AppendStackFrame(sb, Current); + AppendStackFrame(sb, ref Current); } return sb.ToString(); - void AppendStackFrame(StringBuilder sb, in WriteStackFrame frame) + static void AppendStackFrame(StringBuilder sb, ref WriteStackFrame frame) { // Append the property name. string? propertyName = frame.DeclaredJsonPropertyInfo?.MemberInfo?.Name; @@ -366,7 +351,7 @@ namespace System.Text.Json AppendPropertyName(sb, propertyName); } - void AppendPropertyName(StringBuilder sb, string? propertyName) + static void AppendPropertyName(StringBuilder sb, string? propertyName) { if (propertyName != null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs index b162b55709c..7d9ed3b6863 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStackFrame.cs @@ -120,20 +120,5 @@ namespace System.Text.Json return PolymorphicJsonPropertyInfo.ConverterBase; } - - public void Reset() - { - CollectionEnumerator = null; - EnumeratorIndex = 0; - AsyncEnumerator = null; - AsyncEnumeratorIsPendingCompletion = false; - IgnoreDictionaryKeyPolicy = false; - JsonTypeInfo = null!; - OriginalDepth = 0; - ProcessedStartToken = false; - ProcessedEndToken = false; - - EndProperty(); - } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs index 8157f9bbdf3..235fa8010de 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs @@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Tests public class ReferenceHandlerTests_IgnoreCycles { private static readonly JsonSerializerOptions s_optionsIgnoreCycles = - new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles }; + new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles, DefaultBufferSize = 1 }; [Fact] public async Task IgnoreCycles_OnObject() @@ -401,6 +401,25 @@ namespace System.Text.Json.Serialization.Tests await Test_Serialize_And_SerializeAsync_Contains(root, expectedSubstring: @"""DayOfBirth"":15", expectedTimes: 2, s_optionsIgnoreCycles); } + [Fact] + public async Task CycleDetectionStatePersistsAcrossContinuations() + { + string expectedValueJson = @"{""LargePropertyName"":""A large-ish string to force continuations"",""Nested"":null}"; + var recVal = new RecursiveValue { LargePropertyName = "A large-ish string to force continuations" }; + recVal.Nested = recVal; + + var value = new List { recVal, recVal }; + string expectedJson = $"[{expectedValueJson},{expectedValueJson}]"; + + await Test_Serialize_And_SerializeAsync(value, expectedJson, s_optionsIgnoreCycles); + } + + public class RecursiveValue + { + public string LargePropertyName { get; set; } + public RecursiveValue? Nested { get; set; } + } + private async Task Test_Serialize_And_SerializeAsync(T obj, string expected, JsonSerializerOptions options) { string json; From 97477e250c1448431d4070fb4b324ebd3d7f1a4c Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 18 Jun 2021 13:48:22 -0700 Subject: [PATCH 004/926] Enable loading composite r2r images from a singlefile bundle (#53739) * Enable loading composite r2r images from a singlefile bundle --- src/coreclr/utilcode/pedecoder.cpp | 21 +++- src/coreclr/vm/nativeimage.cpp | 106 ++++++++++-------- .../BundledAppWithSubDirs.cs | 26 +++++ 3 files changed, 102 insertions(+), 51 deletions(-) diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp index 3b46c56a415..639cb02099b 100644 --- a/src/coreclr/utilcode/pedecoder.cpp +++ b/src/coreclr/utilcode/pedecoder.cpp @@ -1034,14 +1034,29 @@ CHECK PEDecoder::CheckCorHeader() const IMAGE_COR20_HEADER *pCor = GetCorHeader(); + // Currently composite r2r images miss some information, for example the version is 0.0. + // We may want to change that to something more conforming and explicit. + // For now, for compatibility purposes, we will accept that as a valid format. + bool possiblyCompositeR2R = + pCor->MinorRuntimeVersion == 0 && + pCor->MajorRuntimeVersion == 0; + //CHECK(((ULONGLONG)pCor & 0x3)==0); // If the file is COM+ 1.0, which by definition has nothing the runtime can // use, or if the file requires a newer version of this engine than us, // it cannot be run by this engine. - CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR); + if (!possiblyCompositeR2R) + CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR); +#ifdef HOST_WINDOWS CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK)); +#else + CHECK(CheckDirectory( + &pCor->MetaData, + possiblyCompositeR2R ? 0 : IMAGE_SCN_MEM_WRITE, + HasNativeHeader() ? NULL_OK : NULL_NOT_OK)); +#endif CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK)); CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK)); CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK)); @@ -1083,7 +1098,7 @@ CHECK PEDecoder::CheckCorHeader() const // IL library files (really a misnomer - these are native images or ReadyToRun images) // only they can have a native image header - if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0) + if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0 && !possiblyCompositeR2R) { CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0); } @@ -1769,7 +1784,7 @@ void PEDecoder::LayoutILOnly(void *base, bool enableExecution) const PAGE_READONLY, &oldProtection)) ThrowLastError(); - // Finally, apply proper protection to copied sections + // Finally, apply proper protection to copied sections for (section = sectionStart; section < sectionEnd; section++) { // Add appropriate page protection. diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index 5500da1acc8..8338dbc6096 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -143,59 +143,69 @@ NativeImage *NativeImage::Open( NewHolder peLoadedImage; - EX_TRY + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(fullPath, /*pathIsBundleRelative */ true); + if (bundleFileLocation.IsValid()) { - peLoadedImage = PEImageLayout::LoadNative(fullPath); + PEImageHolder pImage = PEImage::OpenImage(fullPath, MDInternalImport_NoCache, bundleFileLocation); + peLoadedImage = pImage->GetLayout(PEImageLayout::LAYOUT_MAPPED, PEImage::LAYOUT_CREATEIFNEEDED); } - EX_CATCH - { - SString searchPaths(searchPathsConfig); - SString::CIterator start = searchPaths.Begin(); - while (start != searchPaths.End()) - { - SString::CIterator end = start; - if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W)) - { - end = searchPaths.End(); - } - fullPath.Set(searchPaths, start, (COUNT_T)(end - start)); - - if (end != searchPaths.End()) - { - // Skip path separator character - ++end; - } - start = end; - - if (fullPath.GetCount() == 0) - { - continue; - } - - fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W); - fullPath += compositeImageFileName; - - EX_TRY - { - peLoadedImage = PEImageLayout::LoadNative(fullPath); - break; - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions) - } - } - EX_END_CATCH(SwallowAllExceptions) if (peLoadedImage.IsNull()) { - // Failed to locate the native composite R2R image - LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n", - nativeImageFileName, - path.GetUnicode(), - searchPathsConfig != nullptr ? searchPathsConfig : W(""))); - RaiseFailFastException(nullptr, nullptr, 0); + EX_TRY + { + peLoadedImage = PEImageLayout::LoadNative(fullPath); + } + EX_CATCH + { + SString searchPaths(searchPathsConfig); + SString::CIterator start = searchPaths.Begin(); + while (start != searchPaths.End()) + { + SString::CIterator end = start; + if (!searchPaths.Find(end, PATH_SEPARATOR_CHAR_W)) + { + end = searchPaths.End(); + } + fullPath.Set(searchPaths, start, (COUNT_T)(end - start)); + + if (end != searchPaths.End()) + { + // Skip path separator character + ++end; + } + start = end; + + if (fullPath.GetCount() == 0) + { + continue; + } + + fullPath.Append(DIRECTORY_SEPARATOR_CHAR_W); + fullPath += compositeImageFileName; + + EX_TRY + { + peLoadedImage = PEImageLayout::LoadNative(fullPath); + break; + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + } + } + EX_END_CATCH(SwallowAllExceptions) + + if (peLoadedImage.IsNull()) + { + // Failed to locate the native composite R2R image + LOG((LF_LOADER, LL_ALWAYS, "LOADER: failed to load native image '%s' for component assembly '%S' using search paths: '%S'\n", + nativeImageFileName, + path.GetUnicode(), + searchPathsConfig != nullptr ? searchPathsConfig : W(""))); + RaiseFailFastException(nullptr, nullptr, 0); + } } READYTORUN_HEADER *pHeader = (READYTORUN_HEADER *)peLoadedImage->GetExport("RTR_HEADER"); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 9ede20c516e..66212592138 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -126,6 +126,18 @@ namespace AppHost.Bundle.Tests Assert.Throws(()=>BundleHelper.BundleApp(fixture, options, new Version(5, 0))); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54234")] + // NOTE: when enabling this test take a look at commented code maked by "ACTIVE ISSUE:" in SharedTestState + public void Bundled_Self_Contained_Composite_App_Run_Succeeds() + { + var fixture = sharedTestState.TestSelfContainedFixtureComposite.Copy(); + var singleFile = BundleSelfContainedApp(fixture, BundleOptions.None, disableCompression: true); + + // Run the app + RunTheApp(singleFile, fixture); + } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] @@ -144,6 +156,7 @@ namespace AppHost.Bundle.Tests public TestProjectFixture TestFrameworkDependentFixture { get; set; } public TestProjectFixture TestSelfContainedFixture { get; set; } public TestProjectFixture TestAppWithEmptyFileFixture { get; set; } + public TestProjectFixture TestSelfContainedFixtureComposite { get; set; } public SharedTestState() { @@ -169,6 +182,18 @@ namespace AppHost.Bundle.Tests .EnsureRestoredForRid(TestAppWithEmptyFileFixture.CurrentRid) .PublishProject(runtime: TestAppWithEmptyFileFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestAppWithEmptyFileFixture)); + + TestSelfContainedFixtureComposite = new TestProjectFixture("AppWithSubDirs", RepoDirectories); + BundleHelper.AddLongNameContentToAppWithSubDirs(TestSelfContainedFixtureComposite); + TestSelfContainedFixtureComposite + .EnsureRestoredForRid(TestSelfContainedFixtureComposite.CurrentRid) + .PublishProject(runtime: TestSelfContainedFixtureComposite.CurrentRid, + // ACTIVE ISSUE: https://github.com/dotnet/runtime/issues/54234 + // uncomment extraArgs when fixed. + outputDirectory: BundleHelper.GetPublishPath(TestSelfContainedFixtureComposite) /*, + extraArgs: new string[] { + "/p:PublishReadyToRun=true", + "/p:PublishReadyToRunComposite=true" } */); } public void Dispose() @@ -176,6 +201,7 @@ namespace AppHost.Bundle.Tests TestFrameworkDependentFixture.Dispose(); TestSelfContainedFixture.Dispose(); TestAppWithEmptyFileFixture.Dispose(); + TestSelfContainedFixtureComposite.Dispose(); } } } From 8761cb9cf2201054409dd8e0bc2e6bfd85ae22bd Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 18 Jun 2021 18:40:07 -0400 Subject: [PATCH 005/926] [wasm][aot] Add support for passing items to the aot/helix test project (#54425) --- eng/testing/tests.wasm.targets | 13 +++- ...ix.proj => ProxyProjectForAOTOnHelix.proj} | 7 +- src/tasks/WasmBuildTasks/GenerateAOTProps.cs | 68 +++++++++++++++++-- 3 files changed, 77 insertions(+), 11 deletions(-) rename src/mono/wasm/data/aot-tests/{AOTTestProjectForHelix.proj => ProxyProjectForAOTOnHelix.proj} (85%) diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index ce10194388d..1e1c8669579 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -35,7 +35,7 @@ - <_AOTBuildCommand>dotnet msbuild publish/AOTTestProjectForHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog + <_AOTBuildCommand>dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:$XHARNESS_OUT/AOTBuild.binlog <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) @@ -96,16 +96,23 @@ <_WasmPropertiesToPass Include="$(%(_WasmPropertyNames.Identity))" Name="%(_WasmPropertyNames.Identity)" - ConditionToUse="%(_WasmPropertyNames.ConditionToUse)" /> + ConditionToUse__="%(_WasmPropertyNames.ConditionToUse__)" /> <_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" /> <_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(TargetPath)' == ''" /> + + + Items="@(_WasmItemsToPass)" + OutputFile="$(BundleDir)publish\ProxyProjectForAOTOnHelix.props" /> diff --git a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj similarity index 85% rename from src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj rename to src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index f7b9d3fcc4f..0a0b915ad43 100644 --- a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -13,13 +13,18 @@ true false PrepareForWasmBuildApp;$(WasmBuildAppDependsOn) + + <_PropsFile>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).props - + + + $(TestRootDir)AppBundle\ $(OriginalPublishDir)WasmTestRunner.dll diff --git a/src/tasks/WasmBuildTasks/GenerateAOTProps.cs b/src/tasks/WasmBuildTasks/GenerateAOTProps.cs index 8e6c9c3142a..83d2901a0f4 100644 --- a/src/tasks/WasmBuildTasks/GenerateAOTProps.cs +++ b/src/tasks/WasmBuildTasks/GenerateAOTProps.cs @@ -17,10 +17,35 @@ namespace Microsoft.WebAssembly.Build.Tasks [Required] public ITaskItem[]? Properties { get; set; } + public ITaskItem[] Items { get; set; } = Array.Empty(); + [NotNull] [Required] public string? OutputFile { get; set; } + private const string s_originalItemNameMetadata = "OriginalItemName__"; + private const string s_conditionToUseMetadata = "ConditionToUse__"; + private static readonly HashSet s_metdataNamesToSkip = new() + { + "FullPath", + "RootDir", + "Filename", + "Extension", + "RelativeDir", + "Directory", + "RecursiveDir", + "Identity", + "ModifiedTime", + "CreatedTime", + "AccessedTime", + "DefiningProjectFullPath", + "DefiningProjectDirectory", + "DefiningProjectName", + "DefiningProjectExtension", + s_originalItemNameMetadata, + s_conditionToUseMetadata + }; + public override bool Execute() { var outDir = Path.GetDirectoryName(OutputFile); @@ -30,21 +55,50 @@ namespace Microsoft.WebAssembly.Build.Tasks StringBuilder sb = new(); sb.AppendLine(""); - sb.AppendLine(" "); + sb.AppendLine("\t"); - foreach (var prop in Properties) + foreach (ITaskItem2 prop in Properties) { - string value = prop.ItemSpec; + string value = prop.EvaluatedIncludeEscaped; string? name = prop.GetMetadata("Name"); - string? condition = prop.GetMetadata("ConditionToUse"); + string? condition = prop.GetMetadataValueEscaped(s_conditionToUseMetadata); if (!string.IsNullOrEmpty(condition)) - sb.AppendLine($" <{name} Condition=\"{condition}\">{value}"); + sb.AppendLine($"\t\t<{name} Condition=\"{condition}\">{value}"); else - sb.AppendLine($" <{name}>{value}"); + sb.AppendLine($"\t\t<{name}>{value}"); } - sb.AppendLine(" "); + sb.AppendLine("\t"); + + sb.AppendLine("\t"); + foreach (ITaskItem2 item in Items) + { + string value = item.EvaluatedIncludeEscaped; + string name = item.GetMetadata(s_originalItemNameMetadata); + + if (string.IsNullOrEmpty(name)) + { + Log.LogError($"Item {value} is missing {s_originalItemNameMetadata} metadata, for the item name"); + return false; + } + + sb.AppendLine($"\t\t<{name} Include=\"{value}\""); + + string? condition = item.GetMetadataValueEscaped(s_conditionToUseMetadata); + if (!string.IsNullOrEmpty(condition)) + sb.AppendLine($"\t\t\tCondition=\"{condition}\""); + + foreach (string mdName in item.MetadataNames) + { + if (!s_metdataNamesToSkip.Contains(mdName)) + sb.AppendLine($"\t\t\t{mdName}=\"{item.GetMetadataValueEscaped(mdName)}\""); + } + + sb.AppendLine($"\t\t/>"); + } + + sb.AppendLine("\t"); sb.AppendLine(""); File.WriteAllText(OutputFile, sb.ToString()); From 27fece253494ac80f9f13843cbaa0e99a5d502ae Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:48:58 -0700 Subject: [PATCH 006/926] Disable two Interop tests failing in outerloop under crossgen2 (#54435) https://github.com/dotnet/runtime/issues/54379 https://github.com/dotnet/runtime/issues/54316 --- src/tests/issues.targets | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 552b8b6400f..255236a9d34 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,6 +123,12 @@ https://github.com/dotnet/runtime/issues/48727 + + https://github.com/dotnet/runtime/issues/54379 + + + https://github.com/dotnet/runtime/issues/54316 + From f5da499194580958cbaca9abdaf209572ec3b748 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:51:50 -0700 Subject: [PATCH 007/926] Fix AMD64 epilog ABI (#54357) * Fix AMD64 epilog ABI The Windows AMD64 epilog ABI states that an `lea rsp,[rbp+constant]` instruction may only be used if a frame pointer has been reported to the OS in the prolog unwind info, otherwise an `add rsp, constant` instruction must be used. There were a number of cases where the JIT used the `lea` form simply because a frame pointer had been established and was available, even though it had not been reported to the OS (and, thus, the frame was effectively an `rsp` frame). Fix this by using the same condition in the epilog for determining which form to use, `lea` or `add`, that was used in the prolog to determine whether or not to report the frame pointer in the unwind info. Fixes #54320 * Formatting * Fix OSR --- src/coreclr/jit/codegencommon.cpp | 37 +++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index f977ddfe6f3..e99f047c061 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -7454,7 +7454,7 @@ void CodeGen::genFnProlog() // Establish the AMD64 frame pointer after the OS-reported prolog. if (doubleAlignOrFramePointerUsed()) { - bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; genEstablishFramePointer(compiler->codeGen->genSPtoFPdelta(), reportUnwindData); } #endif // TARGET_AMD64 @@ -8147,7 +8147,31 @@ void CodeGen::genFnEpilog(BasicBlock* block) /* Compute the size in bytes we've pushed/popped */ - if (!doubleAlignOrFramePointerUsed()) + bool removeEbpFrame = doubleAlignOrFramePointerUsed(); + +#ifdef TARGET_AMD64 + // We only remove the EBP frame using the frame pointer (using `lea rsp, [rbp + const]`) + // if we reported the frame pointer in the prolog. The Windows x64 unwinding ABI specifically + // disallows this `lea` form: + // + // See https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code + // + // "When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the + // stack. It may not use lea RSP,constant[RSP] instead. This restriction exists so the unwind code has fewer + // patterns to recognize when searching for epilogs." + // + // Otherwise, we must use `add RSP, constant`, as stated. So, we need to use the same condition + // as genFnProlog() used in determining whether to report the frame pointer in the unwind data. + // This is a subset of the `doubleAlignOrFramePointerUsed()` cases. + // + if (removeEbpFrame) + { + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + removeEbpFrame = removeEbpFrame && reportUnwindData; + } +#endif // TARGET_AMD64 + + if (!removeEbpFrame) { // We have an ESP frame */ @@ -8177,6 +8201,15 @@ void CodeGen::genFnEpilog(BasicBlock* block) genPopCalleeSavedRegisters(); +#ifdef TARGET_AMD64 + // In the case where we have an RSP frame, and no frame pointer reported in the OS unwind info, + // but we do have a pushed frame pointer and established frame chain, we do need to pop RBP. + if (doubleAlignOrFramePointerUsed()) + { + inst_RV(INS_pop, REG_EBP, TYP_I_IMPL); + } +#endif // TARGET_AMD64 + // Extra OSR adjust to get to where RBP was saved by the original frame, and // restore RBP. // From 3174651b1ed15a2cdd9789ac2282601925428e55 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 18 Jun 2021 16:57:05 -0700 Subject: [PATCH 008/926] Remove crossgen from test build and run scripts (#54348) --- src/tests/build.cmd | 60 +++++++---------------- src/tests/build.sh | 37 ++------------ src/tests/run.cmd | 75 ----------------------------- src/tests/run.py | 115 +------------------------------------------- src/tests/run.sh | 17 ------- 5 files changed, 21 insertions(+), 283 deletions(-) diff --git a/src/tests/build.cmd b/src/tests/build.cmd index 18a85d6ae62..cb8834ad0b0 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -47,9 +47,9 @@ set __SkipTestWrappers= set __BuildTestWrappersOnly= set __SkipNative= set __TargetsWindows=1 -set __DoCrossgen= set __DoCrossgen2= set __CompositeBuildMode= +set __TestBuildMode= set __CreatePdb= set __CopyNativeTestBinaries=0 set __CopyNativeProjectsAfterCombinedTestBuild=true @@ -100,7 +100,6 @@ if /i "%1" == "buildtestwrappersonly" (set __SkipNative=1&set __SkipManaged=1&se if /i "%1" == "-msbuild" (set __Ninja=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "buildagainstpackages" (echo error: Remove /BuildAgainstPackages switch&&exit /b1) -if /i "%1" == "crossgen" (set __DoCrossgen=1&set __TestBuildMode=crossgen&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "crossgen2" (set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "composite" (set __CompositeBuildMode=1&set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "pdb" (set __CreatePdb=1&shift&goto Arg_Loop) @@ -506,23 +505,7 @@ REM ============================================================================ if defined __SkipCrossgenFramework goto SkipCrossgen if defined __BuildTestWrappersOnly goto SkipCrossgen -set __CrossgenArg = "" -if defined __DoCrossgen ( - set __CrossgenArg="/p:Crossgen=true" - if "%__TargetsWindows%" == "1" ( - echo %__MsgPrefix%Running crossgen on framework assemblies in CORE_ROOT: %CORE_ROOT% - call :PrecompileFX - if ERRORLEVEL 1 ( - echo %__ErrMsgPrefix%%__MsgPrefix%Error: crossgen precompilation of framework assemblies failed - exit /b 1 - ) - ) else ( - echo "%__MsgPrefix%Crossgen only supported on Windows, for now" - ) -) - if defined __DoCrossgen2 ( - set __CrossgenArg="/p:Crossgen2=true" echo %__MsgPrefix%Running crossgen2 on framework assemblies in CORE_ROOT: %CORE_ROOT% call :PrecompileFX if ERRORLEVEL 1 ( @@ -555,14 +538,17 @@ echo. echo.-? -h -help --help: view this message. echo Build architecture: one of x64, x86, arm, arm64 ^(default: x64^). echo Build type: one of Debug, Checked, Release ^(default: Debug^). +echo skipgeneratelayout: Do not generate the Core_Root layout echo skipmanaged: skip the managed tests build echo skipnative: skip the native tests build echo skiprestorepackages: skip package restore -echo crossgen: Precompiles the framework managed assemblies +echo skiptestwrappers: skip generating test wrappers +echo buildtestwrappersonly: generate test wrappers without building managed or native test components or generating layouts echo copynativeonly: Only copy the native test binaries to the managed output. Do not build the native or managed tests. -echo skipgeneratelayout: Do not generate the Core_Root layout +echo crossgen2: Precompiles the framework managed assemblies +echo composite: Precompiles the framework managed assemblies in composite build mode +echo pdb: create PDB files when precompiling the framework managed assemblies echo generatelayoutonly: Generate the Core_Root layout without building managed or native test components -echo targetsNonWindows: echo Exclude- Optional parameter - specify location of default exclusion file ^(defaults to tests\issues.targets if not specified^) echo Set to "" to disable default exclusion file. echo -- ... : all arguments following this tag will be passed directly to msbuild. @@ -591,32 +577,20 @@ if defined __CompositeBuildMode ( ) set __CrossgenDir=%__BinDir% -if defined __DoCrossgen ( - if /i "%__BuildArch%" == "arm" ( - set __CrossgenDir=!__CrossgenDir!\x86 - ) - if /i "%__BuildArch%" == "arm64" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - set __CrossgenCmd=%__CrossgenCmd% --crossgen --nocrossgen2 --crossgen-path "!__CrossgenDir!\crossgen.exe" -) else ( - if /i "%__BuildArch%" == "arm" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - if /i "%__BuildArch%" == "arm64" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - if /i "%__BuildArch%" == "x86" ( - set __CrossgenDir=!__CrossgenDir!\x64 - ) - set __CrossgenCmd=%__CrossgenCmd% --verify-type-and-field-layout --crossgen2-path "!__CrossgenDir!\crossgen2\crossgen2.dll" +if /i "%__BuildArch%" == "arm" ( + set __CrossgenDir=!__CrossgenDir!\x64 ) +if /i "%__BuildArch%" == "arm64" ( + set __CrossgenDir=!__CrossgenDir!\x64 +) +if /i "%__BuildArch%" == "x86" ( + set __CrossgenDir=!__CrossgenDir!\x64 +) +set __CrossgenCmd=%__CrossgenCmd% --verify-type-and-field-layout --crossgen2-path "!__CrossgenDir!\crossgen2\crossgen2.dll" echo Running %__CrossgenCmd% call %__CrossgenCmd% -set /a __exitCode = !errorlevel! - -if %__exitCode% neq 0 ( +if %errorlevel% neq 0 ( echo Failed to crossgen the framework exit /b 1 ) diff --git a/src/tests/build.sh b/src/tests/build.sh index 0d15d53b539..da6fa03e1cf 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -133,8 +133,7 @@ generate_layout() build_MSBuild_projects "Tests_Overlay_Managed" "$__RepoRootDir/src/tests/run.proj" "Creating test overlay" "/t:CreateTestOverlay" # Precompile framework assemblies with crossgen if required - if [[ "$__DoCrossgen" != 0 || "$__DoCrossgen2" != 0 ]]; then - chmod +x "$__CrossgenExe" + if [[ "$__DoCrossgen2" != 0 ]]; then if [[ "$__SkipCrossgenFramework" == 0 ]]; then precompile_coreroot_fx fi @@ -171,11 +170,7 @@ precompile_coreroot_fx() crossgenDir="$crossgenDir/$__HostArch" fi - if [[ "$__DoCrossgen" != 0 ]]; then - crossgenCmd="$crossgenCmd --crossgen --nocrossgen2 --crossgen-path \"$crossgenDir/crossgen\"" - else - crossgenCmd="$crossgenCmd --verify-type-and-field-layout --crossgen2-path \"$crossgenDir/crossgen2/crossgen2.dll\"" - fi + crossgenCmd="$crossgenCmd --verify-type-and-field-layout --crossgen2-path \"$crossgenDir/crossgen2/crossgen2.dll\"" echo "Running $crossgenCmd" eval $crossgenCmd @@ -191,17 +186,6 @@ precompile_coreroot_fx() return 0 } -declare -a skipCrossGenFiles - -function is_skip_crossgen_test { - for skip in "${skipCrossGenFiles[@]}"; do - if [[ "$1" == "$skip" ]]; then - return 0 - fi - done - return 1 -} - build_Tests() { echo "${__MsgPrefix}Building Tests..." @@ -447,7 +431,6 @@ usage_list+=("-buildtestwrappersonly: only build the test wrappers.") usage_list+=("-copynativeonly: Only copy the native test binaries to the managed output. Do not build the native or managed tests.") usage_list+=("-generatelayoutonly: only pull down dependencies and build coreroot.") -usage_list+=("-crossgen: Precompiles the framework managed assemblies in coreroot.") usage_list+=("-crossgen2: Precompiles the framework managed assemblies in coreroot using the Crossgen2 compiler.") usage_list+=("-priority1: include priority=1 tests in the build.") usage_list+=("-allTargets: Build managed tests for all target platforms.") @@ -480,11 +463,6 @@ handle_arguments_local() { __SkipCrossgenFramework=1 ;; - crossgen|-crossgen) - __DoCrossgen=1 - __TestBuildMode=crossgen - ;; - crossgen2|-crossgen2) __DoCrossgen2=1 __TestBuildMode=crossgen2 @@ -559,9 +537,9 @@ __CopyNativeProjectsAfterCombinedTestBuild=true __CopyNativeTestBinaries=0 __CrossBuild=0 __DistroRid="" -__DoCrossgen=0 __DoCrossgen2=0 __CompositeBuildMode=0 +__TestBuildMode= __DotNetCli="$__RepoRootDir/dotnet.sh" __GenerateLayoutOnly= __IsMSBuildOnNETCoreSupported=0 @@ -607,18 +585,9 @@ __TestDir="$__RepoRootDir/src/tests" __TestWorkingDir="$__RootBinDir/tests/coreclr/$__OSPlatformConfig" __IntermediatesDir="$__RootBinDir/obj/coreclr/$__OSPlatformConfig" __TestIntermediatesDir="$__RootBinDir/tests/coreclr/obj/$__OSPlatformConfig" -__CrossComponentBinDir="$__BinDir" __CrossCompIntermediatesDir="$__IntermediatesDir/crossgen" __MonoBinDir="$__RootBinDir/bin/mono/$__OSPlatformConfig" -__CrossArch="$__HostArch" -if [[ "$__CrossBuild" == 1 ]]; then - __CrossComponentBinDir="$__CrossComponentBinDir/$__CrossArch" -fi -__CrossgenCoreLibLog="$__LogsDir/CrossgenCoreLib_$__TargetOS.$BuildArch.$__BuildType.log" -__CrossgenExe="$__CrossComponentBinDir/crossgen" -__Crossgen2Dll="$__CrossComponentBinDir/crossgen2/crossgen2.dll" - # CI_SPECIFIC - On CI machines, $HOME may not be set. In such a case, create a subfolder and set the variable to it. # This is needed by CLI to function. if [[ -z "$HOME" ]]; then diff --git a/src/tests/run.cmd b/src/tests/run.cmd index 98f90f3344a..3f1e762df09 100644 --- a/src/tests/run.cmd +++ b/src/tests/run.cmd @@ -26,8 +26,6 @@ set __msbuildExtraArgs= set __LongGCTests= set __GCSimulatorTests= set __IlasmRoundTrip= -set __DoCrossgen= -set __CrossgenAltJit= set __PrintLastResultsOnly= set RunInUnloadableContext= @@ -50,13 +48,8 @@ if /i "%1" == "debug" (set __BuildType=Debug&s if /i "%1" == "release" (set __BuildType=Release&shift&goto Arg_Loop) if /i "%1" == "checked" (set __BuildType=Checked&shift&goto Arg_Loop) -if /i "%1" == "vs2017" (set __VSVersion=%1&shift&goto Arg_Loop) -if /i "%1" == "vs2019" (set __VSVersion=%1&shift&goto Arg_Loop) - if /i "%1" == "TestEnv" (set __TestEnv=%2&shift&shift&goto Arg_Loop) if /i "%1" == "sequential" (set __Sequential=1&shift&goto Arg_Loop) -if /i "%1" == "crossgen" (set __DoCrossgen=1&shift&goto Arg_Loop) -if /i "%1" == "crossgenaltjit" (set __DoCrossgen=1&set __CrossgenAltJit=%2&shift&shift&goto Arg_Loop) if /i "%1" == "longgc" (set __LongGCTests=1&shift&goto Arg_Loop) if /i "%1" == "gcsimulator" (set __GCSimulatorTests=1&shift&goto Arg_Loop) if /i "%1" == "jitstress" (set COMPlus_JitStress=%2&shift&shift&goto Arg_Loop) @@ -66,10 +59,8 @@ if /i "%1" == "jitforcerelocs" (set COMPlus_ForceRelocs if /i "%1" == "ilasmroundtrip" (set __IlasmRoundTrip=1&shift&goto Arg_Loop) if /i "%1" == "printlastresultsonly" (set __PrintLastResultsOnly=1&shift&goto Arg_Loop) -if /i "%1" == "runcrossgentests" (set RunCrossGen=true&shift&goto Arg_Loop) if /i "%1" == "runcrossgen2tests" (set RunCrossGen2=true&shift&goto Arg_Loop) REM This test feature is currently intentionally undocumented -if /i "%1" == "runlargeversionbubblecrossgentests" (set RunCrossGen=true&set CrossgenLargeVersionBubble=true&shift&goto Arg_Loop) if /i "%1" == "runlargeversionbubblecrossgen2tests" (set RunCrossGen2=true&set CrossgenLargeVersionBubble=true&shift&goto Arg_Loop) if /i "%1" == "link" (set DoLink=true&set ILLINK=%2&shift&shift&goto Arg_Loop) if /i "%1" == "gcname" (set COMPlus_GCName=%2&shift&shift&goto Arg_Loop) @@ -138,18 +129,10 @@ if defined __Sequential ( set __RuntestPyArgs=%__RuntestPyArgs% --sequential ) -if defined RunCrossGen ( - set __RuntestPyArgs=%__RuntestPyArgs% --run_crossgen_tests -) - if defined RunCrossGen2 ( set __RuntestPyArgs=%__RuntestPyArgs% --run_crossgen2_tests ) -if defined __DoCrossgen ( - set __RuntestPyArgs=%__RuntestPyArgs% --precompile_core_root -) - if defined CrossgenLargeVersionBubble ( set __RuntestPyArgs=%__RuntestPyArgs% --large_version_bubble ) @@ -242,14 +225,6 @@ call :SetTestEnvironment call :ResolveDependencies if errorlevel 1 exit /b 1 -if defined __DoCrossgen ( - echo %__MsgPrefix%Running crossgen on framework assemblies - call :PrecompileFX -) - -REM Delete the unecessary mscorlib.ni file. -if exist %CORE_ROOT%\mscorlib.ni.dll del %CORE_ROOT%\mscorlib.ni.dll - ::Check if the test Binaries are built if not exist %XunitTestBinBase% ( echo %__MsgPrefix%Error: Ensure the Test Binaries are built and are present at %XunitTestBinBase%. @@ -286,52 +261,6 @@ echo %__TestRunHtmlLog% echo %__TestRunXmlLog% exit /b 0 -REM ========================================================================================= -REM === -REM === Compile the managed assemblies in Core_ROOT before running the tests -REM === -REM ========================================================================================= - -:PrecompileAssembly - -REM Skip mscorlib since it is already precompiled. -if /I "%3" == "mscorlib.dll" exit /b 0 -if /I "%3" == "mscorlib.ni.dll" exit /b 0 - -"%1\crossgen.exe" /nologo /Platform_Assemblies_Paths "%CORE_ROOT%" "%2" >nul 2>nul -set /a __exitCode = %errorlevel% -if "%__exitCode%" == "-2146230517" ( - echo %2 is not a managed assembly. - exit /b 0 -) - -if %__exitCode% neq 0 ( - echo Unable to precompile %2 - exit /b 0 -) - -echo %__MsgPrefix%Successfully precompiled %2 -exit /b 0 - -:PrecompileFX -setlocal - -if defined __CrossgenAltJit ( - REM Set altjit flags for the crossgen run. Note that this entire crossgen section is within a setlocal/endlocal scope, - REM so we don't need to save or unset these afterwards. - echo %__MsgPrefix%Setting altjit environment variables for %__CrossgenAltJit%. - set COMPlus_AltJit=* - set COMPlus_AltJitNgen=* - set COMPlus_AltJitName=%__CrossgenAltJit% - set COMPlus_AltJitAssertOnNYI=1 - set COMPlus_NoGuiOnAssert=1 - set COMPlus_ContinueOnAssert=0 -) - -for %%F in (%CORE_ROOT%\*.dll) do call :PrecompileAssembly "%CORE_ROOT%" "%%F" %%~nF%%~xF -endlocal -exit /b 0 - REM ========================================================================================= REM === REM === Subroutine to invoke msbuild. @@ -447,12 +376,8 @@ echo. echo./? -? /h -h /help -help - View this message. echo ^ - Specifies build architecture: x64, x86, arm, or arm64 ^(default: x64^). echo ^ - Specifies build type: Debug, Release, or Checked ^(default: Debug^). -echo VSVersion ^ - VS2017 or VS2019 ^(default: VS2019^). echo TestEnv ^ - Run a custom script before every test to set custom test environment settings. echo sequential - Run tests sequentially (no parallelism). -echo crossgen - Precompile ^(crossgen^) the managed assemblies in CORE_ROOT before running the tests. -echo crossgenaltjit ^ - Precompile ^(crossgen^) the managed assemblies in CORE_ROOT before running the tests, using the given altjit. -echo RunCrossgenTests - Runs ReadytoRun tests echo RunCrossgen2Tests - Runs ReadytoRun tests compiled with Crossgen2 echo jitstress ^ - Runs the tests with COMPlus_JitStress=n echo jitstressregs ^ - Runs the tests with COMPlus_JitStressRegs=n diff --git a/src/tests/run.py b/src/tests/run.py index 8de44fefeff..c6750a248bd 100755 --- a/src/tests/run.py +++ b/src/tests/run.py @@ -84,7 +84,6 @@ parser.add_argument("-test_location", dest="test_location", nargs="?", default=N parser.add_argument("-core_root", dest="core_root", nargs='?', default=None) parser.add_argument("-runtime_repo_location", dest="runtime_repo_location", default=os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) parser.add_argument("-test_env", dest="test_env", default=None) -parser.add_argument("-crossgen_altjit", dest="crossgen_altjit", default=None) # Optional arguments which change execution. @@ -92,11 +91,9 @@ parser.add_argument("--il_link", dest="il_link", action="store_true", default=Fa parser.add_argument("--long_gc", dest="long_gc", action="store_true", default=False) parser.add_argument("--gcsimulator", dest="gcsimulator", action="store_true", default=False) parser.add_argument("--ilasmroundtrip", dest="ilasmroundtrip", action="store_true", default=False) -parser.add_argument("--run_crossgen_tests", dest="run_crossgen_tests", action="store_true", default=False) parser.add_argument("--run_crossgen2_tests", dest="run_crossgen2_tests", action="store_true", default=False) parser.add_argument("--large_version_bubble", dest="large_version_bubble", action="store_true", default=False) -parser.add_argument("--precompile_core_root", dest="precompile_core_root", action="store_true", default=False) -parser.add_argument("--skip_test_run", dest="skip_test_run", action="store_true", default=False, help="Does not run tests. Useful in conjunction with --precompile_core_root") +parser.add_argument("--skip_test_run", dest="skip_test_run", action="store_true", default=False, help="Does not run tests.") parser.add_argument("--sequential", dest="sequential", action="store_true", default=False) parser.add_argument("--analyze_results_only", dest="analyze_results_only", action="store_true", default=False) @@ -842,9 +839,6 @@ def run_tests(args, test_env_script_path : Path to script to use to set the test environment, if any. """ - if args.precompile_core_root: - precompile_core_root(args) - if args.skip_test_run: return @@ -869,11 +863,6 @@ def run_tests(args, print("Setting RunningIlasmRoundTrip=1") os.environ["RunningIlasmRoundTrip"] = "1" - if args.run_crossgen_tests: - print("Running tests R2R") - print("Setting RunCrossGen=true") - os.environ["RunCrossGen"] = "true" - if args.run_crossgen2_tests: print("Running tests R2R (Crossgen2)") print("Setting RunCrossGen2=true") @@ -962,11 +951,6 @@ def setup_args(args): lambda arg: True, "Error setting analyze_results_only") - coreclr_setup_args.verify(args, - "crossgen_altjit", - lambda arg: True, - "Error setting crossgen_altjit") - coreclr_setup_args.verify(args, "rid", lambda arg: True, @@ -997,21 +981,11 @@ def setup_args(args): lambda arg: True, "Error setting large_version_bubble") - coreclr_setup_args.verify(args, - "run_crossgen_tests", - lambda arg: True, - "Error setting run_crossgen_tests") - coreclr_setup_args.verify(args, "run_crossgen2_tests", lambda unused: True, "Error setting run_crossgen2_tests") - coreclr_setup_args.verify(args, - "precompile_core_root", - lambda arg: True, - "Error setting precompile_core_root") - coreclr_setup_args.verify(args, "skip_test_run", lambda arg: True, @@ -1044,7 +1018,6 @@ def setup_args(args): print("core_root : %s" % coreclr_setup_args.core_root) print("test_location : %s" % coreclr_setup_args.test_location) - coreclr_setup_args.crossgen_path = os.path.join(coreclr_setup_args.core_root, "crossgen%s" % (".exe" if coreclr_setup_args.host_os == "windows" else "")) coreclr_setup_args.corerun_path = os.path.join(coreclr_setup_args.core_root, "corerun%s" % (".exe" if coreclr_setup_args.host_os == "windows" else "")) coreclr_setup_args.dotnetcli_script_path = os.path.join(coreclr_setup_args.runtime_repo_location, "dotnet%s" % (".cmd" if coreclr_setup_args.host_os == "windows" else ".sh")) coreclr_setup_args.coreclr_tests_dir = os.path.join(coreclr_setup_args.coreclr_dir, "tests") @@ -1054,92 +1027,6 @@ def setup_args(args): return coreclr_setup_args -def precompile_core_root(args): - """ Precompile all of the assemblies in the core_root directory - - Args: - args - """ - - skip_list = [ - ".*xunit.*", - ".*api-ms-win-core.*", - ".*api-ms-win.*", - ".*System.Private.CoreLib.*" - ] - - unix_skip_list = [ - ".*mscorlib.*", - ".*System.Runtime.WindowsRuntime.*", - ".*System.Runtime.WindowsRuntime.UI.Xaml.*", - ".*R2RDump.dll.*" - ] - - arm64_unix_skip_list = [ - ".*Microsoft.CodeAnalysis.VisualBasic.*", - ".*System.Net.NameResolution.*", - ".*System.Net.Sockets.*", - ".*System.Net.Primitives.*" - ] - - if args.host_os != "windows": - skip_list += unix_skip_list - - if args.arch == "arm64": - skip_list += arm64_unix_skip_list - - assert os.path.isdir(args.test_location) - assert os.path.isdir(args.core_root) - - def call_crossgen(file, env): - assert os.path.isfile(args.crossgen_path) - command = [args.crossgen_path, "/Platform_Assemblies_Paths", args.core_root, file] - - proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) - proc.communicate() - - return_code = proc.returncode - - if return_code == -2146230517: - print("%s is not a managed assembly." % file) - return False - - if return_code != 0: - print("Unable to precompile %s (%d)" % (file, return_code)) - return False - - print("Successfully precompiled %s" % file) - return True - - print("Precompiling all assemblies in %s" % args.core_root) - print("") - - env = os.environ.copy() - - if not args.crossgen_altjit is None: - env["COMPlus_AltJit"]="*" - env["COMPlus_AltJitNgen"]="*" - env["COMPlus_AltJitName"]=args.crossgen_altjit - env["COMPlus_AltJitAssertOnNYI"]="1" - env["COMPlus_NoGuiOnAssert"]="1" - env["COMPlus_ContinueOnAssert"]="0" - - dlls = [os.path.join(args.core_root, item) for item in os.listdir(args.core_root) if item.endswith("dll") and "mscorlib" not in item] - - def in_skip_list(item): - found = False - for skip_re in skip_list: - if re.match(skip_re, item.lower()) is not None: - found = True - return found - - dlls = [dll for dll in dlls if not in_skip_list(dll)] - - for dll in dlls: - call_crossgen(dll, env) - - print("") - if sys.version_info.major < 3: def to_unicode(s): return unicode(s, "utf-8") diff --git a/src/tests/run.sh b/src/tests/run.sh index bfa564d4cee..362d42027f2 100755 --- a/src/tests/run.sh +++ b/src/tests/run.sh @@ -17,8 +17,6 @@ function print_usage { echo ' --testRootDir= : Root directory of the test build (e.g. runtime/artifacts/tests/windows.x64.Debug).' echo ' --disableEventLogging : Disable the events logged by both VM and Managed Code' echo ' --sequential : Run tests sequentially (default is to run in parallel).' - echo ' --crossgen : Precompiles the framework managed assemblies' - echo ' --runcrossgentests : Runs the ReadyToRun tests' echo ' --runcrossgen2tests : Runs the ReadyToRun tests compiled with Crossgen2' echo ' --jitstress= : Runs the tests with COMPlus_JitStress=n' echo ' --jitstressregs= : Runs the tests with COMPlus_JitStressRegs=n' @@ -94,7 +92,6 @@ limitedCoreDumps= # Handle arguments verbose=0 -doCrossgen=0 ilasmroundtrip= printLastResultsOnly= runSequential=0 @@ -140,9 +137,6 @@ do --printLastResultsOnly) printLastResultsOnly=1 ;; - --crossgen) - doCrossgen=1 - ;; --jitstress=*) export COMPlus_JitStress=${i#*=} ;; @@ -168,9 +162,6 @@ do --disableEventLogging) ((disableEventLogging = 1)) ;; - --runcrossgentests) - export RunCrossGen=1 - ;; --runcrossgen2tests) export RunCrossGen2=1 ;; @@ -281,18 +272,10 @@ if [ ! -z "$printLastResultsOnly" ]; then runtestPyArguments+=("--analyze_results_only") fi -if [ ! -z "$RunCrossGen" ]; then - runtestPyArguments+=("--run_crossgen_tests") -fi - if [ ! -z "$RunCrossGen2" ]; then runtestPyArguments+=("--run_crossgen2_tests") fi -if (($doCrossgen!=0)); then - runtestPyArguments+=("--precompile_core_root") -fi - if [ "$limitedCoreDumps" == "ON" ]; then runtestPyArguments+=("--limited_core_dumps") fi From ca3c11d64516217134430d04354ac770b07c91ef Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sat, 19 Jun 2021 02:37:42 +0200 Subject: [PATCH 009/926] enable ZeroByteRead_BlocksUntilDataAvailableOrNops test for quic (#54431) --- .../QuicStreamConnectedStreamConformanceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 35b54bc6d70..cd83662a1ba 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -22,6 +22,7 @@ namespace System.Net.Quic.Tests { protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; protected override bool UsableAfterCanceledReads => false; + protected override bool BlocksOnZeroByteReads => true; // TODO: These are all hanging, likely due to Stream close behavior. [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] @@ -43,8 +44,6 @@ namespace System.Net.Quic.Tests [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); } From 9c5581c09e2c90450d644c9215493f2131515da1 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 19 Jun 2021 10:07:59 -0700 Subject: [PATCH 010/926] Disable tests for #54007 (#54207) --- src/libraries/System.IO.Hashing/tests/Crc32Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/Crc64Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs | 1 + src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs | 1 + 8 files changed, 8 insertions(+) diff --git a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs index 6d2993f1509..4e5ac8753d3 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs @@ -93,6 +93,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs index d647e4b9587..546501aa4d5 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs @@ -97,6 +97,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs index eed5aec365b..cf2e967d32a 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs @@ -105,6 +105,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs index f9dccad0294..15c671d12be 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs @@ -118,6 +118,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs index 5171e62f8ca..007acd7655f 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs @@ -105,6 +105,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs index 5c2e5753778..4da264fb5ff 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs @@ -115,6 +115,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs index 973d108fc93..39245a975d8 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs @@ -131,6 +131,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs index 23006a57162..79d370e93d1 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs @@ -115,6 +115,7 @@ namespace System.IO.Hashing.Tests InstanceAppendAllocateAndResetDriver(testCase); } + [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/54007", RuntimeConfiguration.Checked)] [Theory] [MemberData(nameof(TestCases))] public void InstanceMultiAppendGetCurrentHash(TestCase testCase) From 24950a310bde0d816060f920090b4e9666c15fed Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Sat, 19 Jun 2021 13:31:12 -0700 Subject: [PATCH 011/926] Remove System.Security.Cryptography.Cng from ASP.NET transport package (#54428) --- .../System.Security.Cryptography.Cng/Directory.Build.props | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props index 7bdd796606b..709a22a7537 100644 --- a/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props +++ b/src/libraries/System.Security.Cryptography.Cng/Directory.Build.props @@ -2,7 +2,6 @@ Microsoft - true windows \ No newline at end of file From 30f4269767c46317cfa5647d46eba4be837c1817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 21 Jun 2021 03:13:44 -0400 Subject: [PATCH 012/926] Allow mono samples to build and run again (#54089) --- Directory.Build.props | 11 +++++++++++ src/libraries/Directory.Build.props | 9 --------- src/mono/sample/Android/AndroidSampleApp.csproj | 1 - src/mono/sample/iOS/Program.csproj | 1 - src/mono/sample/mbr/apple/AppleDelta.csproj | 1 - src/mono/wasm/build/WasmApp.InTree.props | 1 - src/mono/wasm/wasm.proj | 3 --- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7efd3a57291..d1b03d7ef8d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -202,6 +202,17 @@ true + + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data')) + + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)')) + $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) + + true diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index cc5a02e3869..b06fc950644 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -136,15 +136,6 @@ $(ArtifactsBinDir)pkg\aspnetcoreapp\ref $(ArtifactsBinDir)pkg\aspnetcoreapp\lib - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data')) - - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) - $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common')) $([MSBuild]::NormalizeDirectory('$(CommonPathRoot)', 'src')) $([MSBuild]::NormalizeDirectory('$(CommonPathRoot)', 'tests')) diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index 4f5a245e314..8fb25b90b71 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -6,7 +6,6 @@ android-$(TargetArchitecture) true Link - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)\ false diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj index e9f7c503205..04b2bde9b0b 100644 --- a/src/mono/sample/iOS/Program.csproj +++ b/src/mono/sample/iOS/Program.csproj @@ -5,7 +5,6 @@ $(NetCoreAppToolCurrent) iOS iOSSimulator - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\ $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj index 090fe47dc7a..af4ec2b324a 100644 --- a/src/mono/sample/mbr/apple/AppleDelta.csproj +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -3,7 +3,6 @@ Exe bin $(NetCoreAppToolCurrent) - $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\ $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST true diff --git a/src/mono/wasm/build/WasmApp.InTree.props b/src/mono/wasm/build/WasmApp.InTree.props index 42b75742514..e5968f1320a 100644 --- a/src/mono/wasm/build/WasmApp.InTree.props +++ b/src/mono/wasm/build/WasmApp.InTree.props @@ -6,7 +6,6 @@ AnyCPU false $(NetCoreAppToolCurrent) - $([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(Configuration), 'runtimes', 'browser-wasm')) $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk')) false true diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 81ce7272efe..489d88259f5 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -7,9 +7,6 @@ browser-wasm $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture)')) $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'lib')) - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) - $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) false false emcc From f891033db5b8ebf651176a3dcc3bec74a217f85e Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Mon, 21 Jun 2021 10:20:03 +0300 Subject: [PATCH 013/926] Remove redundant P/Invoke-s in S.D.Process on Apple platforms (#54273) * Remove P/Invoke to SystemNative_ConfigureTerminalForChildProcess which doesn't exist on Apple platforms * Address the feedback based on the ifdef approach * Add IsiOSLike property * Use a partial method approach * Address the feedback --- .../src/System.Diagnostics.Process.csproj | 10 ++++-- ...Unix.ConfigureTerminalForChildProcesses.cs | 36 +++++++++++++++++++ .../src/System/Diagnostics/Process.Unix.cs | 24 ++----------- 3 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index f0c7d8eb136..cdd65c22c44 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -10,6 +10,9 @@ SR.Process_PlatformNotSupported + + true + @@ -234,8 +237,6 @@ Link="Common\Interop\Unix\Interop.GetHostName.cs" /> - + + + + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs new file mode 100644 index 00000000000..6f84319b0c0 --- /dev/null +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Diagnostics +{ + public partial class Process + { + private static int s_childrenUsingTerminalCount; + + static partial void ConfigureTerminalForChildProcessesInner(int increment) + { + Debug.Assert(increment != 0); + + int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); + if (increment > 0) + { + Debug.Assert(s_processStartLock.IsReadLockHeld); + + // At least one child is using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); + } + else + { + Debug.Assert(s_processStartLock.IsWriteLockHeld); + + if (childrenUsingTerminalRemaining == 0) + { + // No more children are using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); + } + } + } + } +} diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 0894dd9e6f7..1b52e4d141e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -19,7 +19,6 @@ namespace System.Diagnostics private static volatile bool s_initialized; private static readonly object s_initializedGate = new object(); private static readonly ReaderWriterLockSlim s_processStartLock = new ReaderWriterLockSlim(); - private static int s_childrenUsingTerminalCount; /// /// Puts a Process component in state to interact with operating system processes that run in a @@ -1051,26 +1050,9 @@ namespace System.Diagnostics /// internal static void ConfigureTerminalForChildProcesses(int increment) { - Debug.Assert(increment != 0); - - int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); - if (increment > 0) - { - Debug.Assert(s_processStartLock.IsReadLockHeld); - - // At least one child is using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); - } - else - { - Debug.Assert(s_processStartLock.IsWriteLockHeld); - - if (childrenUsingTerminalRemaining == 0) - { - // No more children are using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); - } - } + ConfigureTerminalForChildProcessesInner(increment); } + + static partial void ConfigureTerminalForChildProcessesInner(int increment); } } From a3f0e2bebe30fd5e82518d86cefc7895127ae474 Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Mon, 21 Jun 2021 10:10:58 -0400 Subject: [PATCH 014/926] [WASM] Converted mono-config.js to mono-config.json (#53606) * converted mono-config.js to mono-config.json * Fixed test * fixed handling of case where Module isn't defined * Fixed remaining failing tests * Replaced alerts with console.log to fix Helix test * replaced all vars with consts * use fetch * clean syntax * prevent timeouts when the mono-config file fails to load * Moved config file loading to preInit * Fixed tests * Adjusted file linking * removed the unnecessary js_support.js * cleaned up function * updated samples * removed lingering pre-js flag * Fixed trimming tests * addressed PR comments * removed useless function --- src/mono/sample/mbr/browser/index.html | 1 - src/mono/sample/mbr/browser/runtime.js | 20 ++++- src/mono/sample/wasm/browser-bench/index.html | 1 - src/mono/sample/wasm/browser-bench/runtime.js | 27 +++++- .../sample/wasm/browser-profile/index.html | 59 ------------- .../sample/wasm/browser-profile/runtime.js | 86 ++++++++++++++++--- src/mono/sample/wasm/browser/index.html | 1 - src/mono/sample/wasm/browser/runtime.js | 19 +++- src/mono/wasm/build/WasmApp.targets | 2 +- .../tests/debugger-test/debugger-driver.html | 1 - .../tests/debugger-test/runtime-debugger.js | 18 +++- src/mono/wasm/runtime-test.js | 17 ++-- src/mono/wasm/runtime/library_mono.js | 27 +++++- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 6 +- .../Wasm.Build.Tests/BuildTestBase.cs | 2 +- .../WebAssembly/Browser/AOT/index.html | 1 - .../WebAssembly/Browser/AOT/runtime.js | 19 +++- .../Browser/NormalInterp/index.html | 1 - .../Browser/NormalInterp/runtime.js | 19 +++- 19 files changed, 218 insertions(+), 109 deletions(-) diff --git a/src/mono/sample/mbr/browser/index.html b/src/mono/sample/mbr/browser/index.html index 472cf5f29ea..eb19b7bb2f5 100644 --- a/src/mono/sample/mbr/browser/index.html +++ b/src/mono/sample/mbr/browser/index.html @@ -29,7 +29,6 @@ }, }; - diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js index 0856b8d0e03..32918aa573a 100644 --- a/src/mono/sample/mbr/browser/runtime.js +++ b/src/mono/sample/mbr/browser/runtime.js @@ -2,17 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { App.init (); }; - config.environment_variables = { + Module.config.environment_variables = { "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); }, }; diff --git a/src/mono/sample/wasm/browser-bench/index.html b/src/mono/sample/wasm/browser-bench/index.html index 8f7748798d4..5c5271954f1 100644 --- a/src/mono/sample/wasm/browser-bench/index.html +++ b/src/mono/sample/wasm/browser-bench/index.html @@ -51,7 +51,6 @@ } }; - diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js index a39b0b97b14..60c383d13cb 100644 --- a/src/mono/sample/wasm/browser-bench/runtime.js +++ b/src/mono/sample/wasm/browser-bench/runtime.js @@ -1,9 +1,20 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - var Module = { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +22,21 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } + if (Module.config.enable_profiler) + { + Module.config.aot_profiler_options = { + write_at:"Sample.Test::StopProfile", + send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData" + } + } + try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/mono/sample/wasm/browser-profile/index.html b/src/mono/sample/wasm/browser-profile/index.html index 50eb0ff28c2..de4a5599e94 100644 --- a/src/mono/sample/wasm/browser-profile/index.html +++ b/src/mono/sample/wasm/browser-profile/index.html @@ -10,67 +10,8 @@ Result from Sample.Test.TestMeaning: - - - diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js index 1ce1c0b7360..2c83ff54ef9 100644 --- a/src/mono/sample/wasm/browser-profile/runtime.js +++ b/src/mono/sample/wasm/browser-profile/runtime.js @@ -1,22 +1,35 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -var Module = { +var Module = { + is_testing: false, + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { try { - App.init (); + Module.init(); } catch (error) { - test_exit(1); + Module.test_exit(1); throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } - if (config.enable_profiler) + if (Module.config.enable_profiler) { - config.aot_profiler_options = { + Module.config.aot_profiler_options = { write_at:"Sample.Test::StopProfile", send_to: "System.Runtime.InteropServices.JavaScript.Runtime::DumpAotProfileData" } @@ -24,10 +37,63 @@ var Module = { try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { - test_exit(1); + Module.test_exit(1); throw(error); } + }, + + init: function () { + console.log("not ready yet") + var ret = BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:TestMeaning", []); + document.getElementById("out").innerHTML = ret; + console.log ("ready"); + + if (Module.is_testing) + { + console.debug(`ret: ${ret}`); + let exit_code = ret == 42 ? 0 : 1; + Module.test_exit(exit_code); + } + + if (Module.config.enable_profiler) { + BINDING.call_static_method("[Wasm.BrowserProfile.Sample] Sample.Test:StopProfile", []); + Module.saveProfile(); + } + }, + + onLoad: function() { + var url = new URL(decodeURI(window.location)); + let args = url.searchParams.getAll('arg'); + Module.is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined); + }, + + test_exit: function(exit_code) { + if (!Module.is_testing) { + console.log(`test_exit: ${exit_code}`); + return; + } + + /* Set result in a tests_done element, to be read by xharness */ + var tests_done_elem = document.createElement("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString(); + document.body.appendChild(tests_done_elem); + + console.log(`WASM EXIT ${exit_code}`); + }, + + saveProfile: function () { + var a = document.createElement('a'); + var blob = new Blob([Module.aot_profile_data]); + a.href = URL.createObjectURL(blob); + a.download = "data.aotprofile"; + // Append anchor to body. + document.body.appendChild(a); + a.click(); + + // Remove anchor from body + document.body.removeChild(a); } -}; \ No newline at end of file +}; diff --git a/src/mono/sample/wasm/browser/index.html b/src/mono/sample/wasm/browser/index.html index bd8e5015a0e..5f170bb38e7 100644 --- a/src/mono/sample/wasm/browser/index.html +++ b/src/mono/sample/wasm/browser/index.html @@ -48,7 +48,6 @@ }, }; - diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js index a39b0b97b14..e97feef745a 100644 --- a/src/mono/sample/wasm/browser/runtime.js +++ b/src/mono/sample/wasm/browser/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + return; + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 2141cce41b6..b886f327d71 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -65,7 +65,7 @@ - @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs - @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle. - - @(WasmExtraConfig) - json elements to add to `mono-config.js` + - @(WasmExtraConfig) - json elements to add to `mono-config.json` Eg. - Value attribute can have a number, bool, quoted string, or json string diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html index 87ba804792e..b1bfcd859d0 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html @@ -83,7 +83,6 @@ return App.int_add (a, b); } - diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 4c641eb2c6a..360aa9b8b30 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -2,8 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { - onRuntimeInitialized: function () { - config.loaded_cb = function () { + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + // Called when the runtime is initialized and wasm is ready + onRuntimeInitialized: function () { + if (!Module.config || Module.config.error) { + console.log("An error occured while loading the config file"); + return; + } + + Module.config.loaded_cb = function () { App.init (); }; // For custom logging patch the functions below @@ -15,6 +27,6 @@ var Module = { MONO.mono_wasm_setenv ("MONO_LOG_LEVEL", "debug"); MONO.mono_wasm_setenv ("MONO_LOG_MASK", "all"); */ - MONO.mono_load_runtime_and_bcl_args (config) + MONO.mono_load_runtime_and_bcl_args (Module.config) }, }; diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 74aa1083c03..8b3395f4ec5 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -204,14 +204,16 @@ function loadScript (url) } } -loadScript ("mono-config.js"); - var Module = { mainScriptUrlOrBlob: "dotnet.js", - + config: null, print, printErr, + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onAbort: function(x) { print ("ABORT: " + x); var err = new Error(); @@ -230,7 +232,7 @@ var Module = { Module.ccall ('mono_wasm_enable_on_demand_gc', 'void', ['number'], [0]); } - config.loaded_cb = function () { + Module.config.loaded_cb = function () { let wds = FS.stat (working_dir); if (wds === undefined || !FS.isDir (wds.mode)) { fail_exec (`Could not find working directory ${working_dir}`); @@ -240,13 +242,13 @@ var Module = { FS.chdir (working_dir); App.init (); }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { // console.log("fetch_file_cb('" + asset + "')"); // for testing purposes add BCL assets to VFS until we special case File.Open // to identify when an assembly from the BCL is being open and resolve it correctly. /* var content = new Uint8Array (read (asset, 'binary')); - var path = asset.substr(config.deploy_prefix.length); + var path = asset.substr(Module.config.deploy_prefix.length); writeContentToFile(content, path); */ @@ -280,10 +282,9 @@ var Module = { } }; - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); }, }; - loadScript ("dotnet.js"); const IGNORE_PARAM_COUNT = -1; diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 11866606f4d..36837f62cf7 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -86,6 +86,7 @@ var MonoSupportLib = { module ["mono_wasm_new_root"] = MONO.mono_wasm_new_root.bind(MONO); module ["mono_wasm_new_roots"] = MONO.mono_wasm_new_roots.bind(MONO); module ["mono_wasm_release_roots"] = MONO.mono_wasm_release_roots.bind(MONO); + module ["mono_wasm_load_config"] = MONO.mono_wasm_load_config.bind(MONO); }, _base64Converter: { @@ -2362,6 +2363,30 @@ var MonoSupportLib = { console.debug('mono_wasm_debug_event_raised:aef14bca-5519-4dfe-b35a-f867abc123ae', JSON.stringify(event), JSON.stringify(args)); }, + + /** + * Loads the mono config file (typically called mono-config.json) + * + * @param {string} configFilePath - relative path to the config file + * @throws Will throw an error if the config file loading fails + */ + mono_wasm_load_config: async function (configFilePath) { + try { + let config = null; + // NOTE: when we add nodejs make sure to include the nodejs fetch package + if (ENVIRONMENT_IS_WEB) { + const configRaw = await fetch(configFilePath); + config = await configRaw.json(); + }else if (ENVIRONMENT_IS_NODE) { + config = require(configFilePath); + } else { // shell or worker + config = JSON.parse(read(configFilePath)); // read is a v8 debugger command + } + return config; + } catch(e) { + return {message: "failed to load config file", error: e}; + } + } }, mono_wasm_add_typed_value: function (type, str_value, value) { @@ -2549,7 +2574,7 @@ var MonoSupportLib = { assembly_b64, pdb_b64 }); - }, + } }; autoAddDeps(MonoSupportLib, '$MONO') diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 9ef7679c292..49d984490a6 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -51,7 +51,7 @@ public class WasmAppBuilder : Task public ITaskItem[]? ExtraFilesToDeploy { get; set; } // - // Extra json elements to add to mono-config.js + // Extra json elements to add to mono-config.json // // Metadata: // - Value: can be a number, bool, quoted string, or json string @@ -246,11 +246,11 @@ public class WasmAppBuilder : Task config.Extra[name] = valueObject; } - string monoConfigPath = Path.Combine(AppDir, "mono-config.js"); + string monoConfigPath = Path.Combine(AppDir, "mono-config.json"); using (var sw = File.CreateText(monoConfigPath)) { var json = JsonSerializer.Serialize (config, new JsonSerializerOptions { WriteIndented = true }); - sw.Write($"config = {json};"); + sw.Write(json); } _fileWrites.Add(monoConfigPath); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b5f1f7e0b95..99e721dfa65 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -347,7 +347,7 @@ namespace Wasm.Build.Tests "runtime.js", "dotnet.timezones.blat", "dotnet.wasm", - "mono-config.js", + "mono-config.json", "dotnet.js", "run-v8.sh" }); diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html index efab9ac4324..642987d23c5 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html @@ -47,7 +47,6 @@ }, }; - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js index a39b0b97b14..65cba13a9b1 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html index 03f68679a5b..9de05f5031b 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html @@ -47,7 +47,6 @@ }, }; - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js index a39b0b97b14..1a8abf503fb 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js @@ -2,8 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + onRuntimeInitialized: function () { - config.loaded_cb = function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { try { App.init (); } catch (error) { @@ -11,13 +24,13 @@ var Module = { throw (error); } }; - config.fetch_file_cb = function (asset) { + Module.config.fetch_file_cb = function (asset) { return fetch (asset, { credentials: 'same-origin' }); } try { - MONO.mono_load_runtime_and_bcl_args (config); + MONO.mono_load_runtime_and_bcl_args (Module.config); } catch (error) { test_exit(1); throw(error); From 1cf9e342e744b96423d59f11c8e203788a5cf52b Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:21:52 +0100 Subject: [PATCH 015/926] Clarify Visual Studio minimum requirement (#52820) --- docs/workflow/requirements/windows-requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/workflow/requirements/windows-requirements.md b/docs/workflow/requirements/windows-requirements.md index 56f662f6f21..2629df320d2 100644 --- a/docs/workflow/requirements/windows-requirements.md +++ b/docs/workflow/requirements/windows-requirements.md @@ -34,7 +34,7 @@ Visual Studio 2019 installation process: A `.vsconfig` file is included in the root of the dotnet/runtime repository that includes all components needed to build the dotnet/runtime repository. You can [import `.vsconfig` in your Visual Studio installer](https://docs.microsoft.com/en-us/visualstudio/install/import-export-installation-configurations?view=vs-2019#import-a-configuration) to install all necessary components. -The dotnet/runtime repository requires at least Visual Studio 2019 16.6. +Visual Studio 2019 16.6 or later is required for building the repository. Visual Studio 2019 16.10 is required to work with the libraries projects inside the Visual Studio IDE. ## CMake From 0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 21 Jun 2021 08:24:16 -0700 Subject: [PATCH 016/926] Fix the slot calculation for multiple services (#54462) --- .../src/ServiceLookup/CallSiteFactory.cs | 13 ++- .../ServiceLookup/CallSiteFactoryTest.cs | 35 ++++++++ .../DI.Tests/ServiceProviderContainerTests.cs | 85 ++++++++++++++++--- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index da791135ff0..a2e02ce4f50 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -77,6 +77,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup } } + // For unit testing + internal int? GetSlot(ServiceDescriptor serviceDescriptor) + { + if (_descriptorLookup.TryGetValue(serviceDescriptor.ServiceType, out ServiceDescriptorCacheItem item)) + { + return item.GetSlot(serviceDescriptor); + } + + return null; + } + internal ServiceCallSite GetCallSite(Type serviceType, CallSiteChain callSiteChain) => _callSiteCache.TryGetValue(new ServiceCacheKey(serviceType, DefaultSlot), out ServiceCallSite site) ? site : CreateCallSite(serviceType, callSiteChain); @@ -537,7 +548,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup int index = _items.IndexOf(descriptor); if (index != -1) { - return Count - index + 1; + return _items.Count - (index + 1); } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs index e027508f784..8d6a78029fe 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs @@ -556,6 +556,41 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup ex.Message); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void GetSlotTests(int numberOfServices) + { + var serviceDescriptors = new[] { + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton() + }; + + var callsiteFactory = new CallSiteFactory(serviceDescriptors.Take(numberOfServices)); + + for (int i = 0; i < numberOfServices; i++) + { + Assert.Equal(numberOfServices - i - 1, callsiteFactory.GetSlot(serviceDescriptors[i])); + } + } + + interface ICustomService + { + + } + + class CustomService1 : ICustomService { } + class CustomService2 : ICustomService { } + class CustomService3 : ICustomService { } + class CustomService4 : ICustomService { } + class CustomService5 : ICustomService { } + [Theory] [InlineData(typeof(TypeWithMultipleParameterizedConstructors))] [InlineData(typeof(TypeWithSupersetConstructors))] diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs index 22fd778450f..6da83cf2d3e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Fakes; using Microsoft.Extensions.DependencyInjection.Specification; using Microsoft.Extensions.DependencyInjection.Specification.Fakes; @@ -80,6 +81,70 @@ namespace Microsoft.Extensions.DependencyInjection.Tests $"'{typeof(DependOnNonexistentService)}'.", ex.Message); } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + [InlineData(6)] + public void MultipleServicesAreOrdered(int numberOfServices) + { + // Arrange + var collection = new ServiceCollection(); + + var serviceDescriptors = new[] { + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton(), + ServiceDescriptor.Singleton() + }; + + var serviceTypes = new[] + { + typeof(CustomService1), + typeof(CustomService2), + typeof(CustomService3), + typeof(CustomService4), + typeof(CustomService5), + typeof(CustomService6), + }; + + foreach (var sd in serviceDescriptors.Take(numberOfServices)) + { + collection.Add(sd); + } + + var provider = collection.BuildServiceProvider(new ServiceProviderOptions + { + ValidateOnBuild = true + }); + + // Act and Assert + var customServices = provider.GetService>().ToArray(); + + Assert.Equal(numberOfServices, customServices.Length); + + for (int i = 0; i < numberOfServices; i++) + { + Assert.IsAssignableFrom(serviceTypes[i], customServices[i]); + } + } + + interface ICustomService + { + + } + + class CustomService1 : ICustomService { } + class CustomService2 : ICustomService { } + class CustomService3 : ICustomService { } + class CustomService4 : ICustomService { } + class CustomService5 : ICustomService { } + class CustomService6 : ICustomService { } + [Theory] // GenericTypeDefintion, Abstract GenericTypeDefintion [InlineData(typeof(IFakeOpenGenericService<>), typeof(AbstractFakeOpenGenericService<>))] @@ -121,11 +186,11 @@ namespace Microsoft.Extensions.DependencyInjection.Tests { get { - Type serviceType = typeof(IFakeOpenGenericService<>); - // Service type is GenericTypeDefintion, implementation type is ConstructedGenericType - yield return new object[] {serviceType, typeof(ClassWithNoConstraints), $"Open generic service type '{serviceType}' requires registering an open generic implementation type."}; - // Service type is GenericTypeDefintion, implementation type has different generic type definition arity - yield return new object[] {serviceType, typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>), $"Arity of open generic service type '{serviceType}' does not equal arity of open generic implementation type '{typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>)}'."}; + Type serviceType = typeof(IFakeOpenGenericService<>); + // Service type is GenericTypeDefintion, implementation type is ConstructedGenericType + yield return new object[] { serviceType, typeof(ClassWithNoConstraints), $"Open generic service type '{serviceType}' requires registering an open generic implementation type." }; + // Service type is GenericTypeDefintion, implementation type has different generic type definition arity + yield return new object[] { serviceType, typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>), $"Arity of open generic service type '{serviceType}' does not equal arity of open generic implementation type '{typeof(FakeOpenGenericServiceWithTwoTypeArguments<,>)}'." }; } } @@ -566,8 +631,8 @@ namespace Microsoft.Extensions.DependencyInjection.Tests sb.Append("4"); } - // Let Thread 1 over take Thread 2 - Thing1 value = lazy.Value; + // Let Thread 1 over take Thread 2 + Thing1 value = lazy.Value; return value; }); services.AddSingleton(); @@ -631,7 +696,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests sb.Append("3"); mreForThread2.Set(); // Now that thread 1 holds lazy lock, allow thread 2 to continue - thing3 = sp.GetRequiredService(); + thing3 = sp.GetRequiredService(); return new Thing4(thing3); }); @@ -1012,7 +1077,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests } } - private class FakeMultipleServiceWithIEnumerableDependency: IFakeMultipleService + private class FakeMultipleServiceWithIEnumerableDependency : IFakeMultipleService { public FakeMultipleServiceWithIEnumerableDependency(IEnumerable fakeServices) { @@ -1125,7 +1190,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests Assert.Same(sp.GetRequiredService>().Value, sp.GetRequiredService()); } - + [Theory] [InlineData(ServiceProviderMode.Default)] [InlineData(ServiceProviderMode.Dynamic)] From dbcb38752d3303705a463ea029c83b016e99816a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Takym=20=28=E3=81=9F=E3=81=8B=E3=82=84=E3=81=BE=29?= <15681312+Takym@users.noreply.github.com> Date: Tue, 22 Jun 2021 02:06:29 +0900 Subject: [PATCH 017/926] Syntax highlighted `type-loader.md` (#54497) * Syntax-highlighted `type-loader.md` * Update type-loader.md --- docs/design/coreclr/botr/type-loader.md | 88 +++++++++++++++---------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/docs/design/coreclr/botr/type-loader.md b/docs/design/coreclr/botr/type-loader.md index b873d8541a0..7c8b3462766 100644 --- a/docs/design/coreclr/botr/type-loader.md +++ b/docs/design/coreclr/botr/type-loader.md @@ -76,21 +76,25 @@ There is a relatively small number of entry-points to the loader. Although the s There are usually many calls to the type loader during JITting. Consider: - object CreateClass() - { - return new MyClass(); - } +```csharp +object CreateClass() +{ + return new MyClass(); +} +``` In the IL, MyClass is referred to using a metadata token. In order to generate a call to the `JIT_New` helper which takes care of the actual instantiation, the JIT will ask the type loader to load the type and return a handle to it. This handle will be then directly embedded in the JITted code as an immediate value. The fact that types and members are usually resolved and loaded at JIT time and not at run-time also explains the sometimes confusing behavior easily hit with code like this: - object CreateClass() - { - try { - return new MyClass(); - } catch (TypeLoadException) { - return null; - } - } +```csharp +object CreateClass() +{ + try { + return new MyClass(); + } catch (TypeLoadException) { + return null; + } +} +``` If `MyClass` fails to load, for example because it's supposed to be defined in another assembly and it was accidentally removed in the newest build, then this code will still throw `TypeLoadException`. The reason that the catch block did not catch it is that it never ran! The exception occurred during JITting and would only be catchable in the method that called `CreateClass` and caused it to be JITted. In addition, it may not be always obvious at which point the JITting is triggered due to inlining, so users should not expect and rely on deterministic behavior. @@ -153,14 +157,16 @@ both the same type. When the type loader is asked to load a specified type, identified for example by a typedef/typeref/typespec **token** and a **Module** , it does not do all the work atomically at once. The loading is done in phases instead. The reason for this is that the type usually depends on other types and requiring it to be fully loaded before it can be referred to by other types would result in infinite recursion and deadlocks. Consider: - class A : C> - { } +```csharp +class A : C> +{ } - class B : C> - { } +class B : C> +{ } - class C - { } +class C +{ } +``` These are valid types and apparently `A` depends on `B` and `B` depends on `A`. @@ -195,10 +201,12 @@ A placeholder to be substituted by another type; the `T` in the declaration of ` A type being substituted for a generic parameter; the `int` in `List`. Note that a generic parameter can also be used as an argument. Consider: - List GetList() - { - return new List(); - } +```csharp +List GetList() +{ + return new List(); +} +``` The method has one generic parameter `T` which is used as a generic argument for the generic list class. @@ -209,28 +217,38 @@ An optional requirement placed by generic parameters on its potential generic ar 1. Special constraints - Reference type constraint - the generic argument must be a reference type (as opposed to a value type). The `class` keyword is used in C# to express this constraint. - public class A where T : class + ```csharp + public class A where T : class + ``` - Value type constraint - the generic argument must be a value type different from `System.Nullable`. C# uses the `struct` keyword. - public class A where T : struct + ```csharp + public class A where T : struct + ``` - Default constructor constraint - the generic argument must have a public parameterless constructor. This is expressed by `new()` in C#. - public class A where T : new() + ```csharp + public class A where T : new() + ``` 2. Base type constraints - the generic argument must be derived from (or directly be of) the given non-interface type. It obviously makes sense to use only zero or one reference type as a base types constraint. - public class A where T : EventArgs + ```csharp + public class A where T : EventArgs + ``` 3. Implemented interface constraints - the generic argument must implement (or directly be of) the given interface type. Zero or more interfaces can be given. - public class A where T : ICloneable, IComparable + ```csharp + public class A where T : ICloneable, IComparable + ``` The above constraints are combined with an implicit AND, i.e. a generic parameter can be constrained to be derived from a given type, @@ -239,11 +257,13 @@ generic parameters of the declaring type can be used to express the constraints, introducing interdependencies among the parameters. For example: - public class A - where S : T - where T : IList { - void f(V v) where V : S {} - } +```csharp +public class A + where S : T + where T : IList { + void f(V v) where V : S {} +} +``` **Instantiation** @@ -259,7 +279,9 @@ declared. There exists exactly one typical instantiation for each generic type and method. Usually when one talks about an open generic type, they have the typical instantiation in mind. Example: - public class A {} +```csharp +public class A {} +``` The C# `typeof(A<,,>)` compiles to ldtoken A\'3 which makes the runtime load ``A`3`` instantiated at `S` , `T` , `U`. From 9199eb6952a4569cf5be57b68732989651ca55e2 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 21 Jun 2021 12:10:47 -0500 Subject: [PATCH 018/926] Remove BitmapSelector.Suffix property (#54364) With https://github.com/dotnet/runtime/issues/22761 and https://github.com/dotnet/corefx/pull/22833, BitmapSelector.Suffix will always be null. This means this feature is dead code, and users are unable to use it. Removing this dead code because: 1. It doesn't do anything. 2. It is causing ILLink warnings, and it is easier to delete than to try to address the warnings. I logged https://github.com/dotnet/runtime/issues/54363 to follow up and either re-implement this feature, or obsolete the attributes that no longer have any effect on the app. --- .../src/ILLink/ILLink.Suppressions.xml | 14 +- .../src/System/Drawing/BitmapSelector.cs | 209 +----------------- .../System/Drawing/ToolboxBitmapAttribute.cs | 19 -- 3 files changed, 7 insertions(+), 235 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index ec106d48670..d5734aab1a2 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,18 +1,6 @@  - - ILLink - IL2026 - member - M:System.Drawing.BitmapSelector.SameAssemblyOptIn(System.Reflection.Assembly) - - - ILLink - IL2026 - member - M:System.Drawing.BitmapSelector.SatelliteAssemblyOptIn(System.Reflection.Assembly) - ILLink IL2050 @@ -80,4 +68,4 @@ M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) - \ No newline at end of file + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs index fcec880055a..b5daa033aa8 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs @@ -1,135 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO; +using System.Reflection; + namespace System.Drawing { - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Reflection; - /// - /// Provides methods to select from multiple bitmaps depending on a "bitmapSuffix" config setting. + /// Provides methods to select bitmaps. /// internal static class BitmapSelector { - /// - /// Gets the bitmap ID suffix defined in the application configuration, or string.Empty if - /// the suffix is not specified. Internal for unit tests - /// - /// - /// For performance, the suffix is cached in a static variable so it only has to be read - /// once per appdomain. - /// - private static string? s_suffix; - internal static string? Suffix - { - get - { - // NOTE: This value is read from the "SystemDrawingSection" of the ConfigurationManager on - // the .NET Framework. To avoid pulling in a direct dependency to that assembly, we are not - // reading the value in this implementation. - return s_suffix; - } - set - { - // So unit tests can clear the cached suffix - s_suffix = value; - } - } - - /// - /// Appends the current suffix to . The suffix is appended - /// before the existing extension (if any). Internal for unit tests. - /// - /// - /// The new path with the suffix included. If there is no suffix defined or there are - /// invalid characters in the original path, the original path is returned. - /// - internal static string AppendSuffix(string filePath) - { - try - { - return Path.ChangeExtension(filePath, Suffix + Path.GetExtension(filePath)); - } - catch (ArgumentException) - { // there are invalid characters in the path - return filePath; - } - } - - /// - /// Returns with the current suffix appended (before the - /// existing extension) if the resulting file path exists; otherwise the original path is - /// returned. - /// - public static string GetFileName(string originalPath) - { - if (string.IsNullOrEmpty(Suffix)) - return originalPath; - - string newPath = AppendSuffix(originalPath); - return File.Exists(newPath) ? newPath : originalPath; - } - - // Calls assembly.GetManifestResourceStream in a try/catch and returns null if not found - private static Stream? GetResourceStreamHelper(Assembly assembly, Type type, string name) - { - Stream? stream = null; - try - { - stream = assembly.GetManifestResourceStream(type, name); - } - catch (FileNotFoundException) - { - } - return stream; - } - - [RequiresUnreferencedCode("Calls Assembly.GetType which may be trimmed")] - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName) - { - return DoesAssemblyHaveCustomAttribute(assembly, assembly.GetType(typeName)); - } - - private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, Type? attrType) - { - if (attrType != null) - { - var attr = assembly.GetCustomAttributes(attrType, false); - if (attr.Length > 0) - { - return true; - } - } - return false; - } - - // internal for unit tests - internal static bool SatelliteAssemblyOptIn(Assembly assembly) - { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSatelliteAssemblyAttribute))) - { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute"); - } - - // internal for unit tests - internal static bool SameAssemblyOptIn(Assembly assembly) - { - // Try 4.5 public attribute type first - if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSameAssemblyAttribute))) - { - return true; - } - - // Also load attribute type by name for dlls compiled against older frameworks - return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute"); - } - /// /// Returns a resource stream loaded from the appropriate location according to the current /// suffix. @@ -138,57 +19,10 @@ namespace System.Drawing /// The type whose namespace is used to scope the manifest resource name /// The name of the manifest resource being requested /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . + /// The manifest resource stream corresponding to . /// public static Stream? GetResourceStream(Assembly assembly, Type type, string originalName) { - if (Suffix != string.Empty) - { - try - { - // Resource with suffix has highest priority - if (SameAssemblyOptIn(assembly)) - { - string newName = AppendSuffix(originalName); - Stream? stream = GetResourceStreamHelper(assembly, type, newName); - if (stream != null) - { - return stream; - } - } - } - catch - { - // Ignore failures and continue to try other options - } - - try - { - // Satellite assembly has second priority, using the original name - if (SatelliteAssemblyOptIn(assembly)) - { - AssemblyName assemblyName = assembly.GetName(); - assemblyName.Name += Suffix; - assemblyName.ProcessorArchitecture = ProcessorArchitecture.None; - Assembly satellite = Assembly.Load(assemblyName); - if (satellite != null) - { - Stream? stream = GetResourceStreamHelper(satellite, type, originalName); - if (stream != null) - { - return stream; - } - } - } - } - catch - { - // Ignore failures and continue to try other options - } - } - - // Otherwise fall back to specified assembly and original name requested return assembly.GetManifestResourceStream(type, originalName); } @@ -199,42 +33,11 @@ namespace System.Drawing /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name /// The name of the manifest resource being requested /// - /// The manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . + /// The manifest resource stream corresponding to . /// public static Stream? GetResourceStream(Type type, string originalName) { return GetResourceStream(type.Module.Assembly, type, originalName); } - - /// - /// Returns an Icon created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The icon created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Icon CreateIcon(Type type, string originalName) - { - return new Icon(GetResourceStream(type, originalName)!); - } - - /// - /// Returns an Bitmap created from a resource stream loaded from the appropriate location according to the current - /// suffix. - /// - /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name - /// The name of the manifest resource being requested - /// - /// The bitmap created from a manifest resource stream corresponding to with the - /// current suffix applied; or if that is not found, the stream corresponding to . - /// - public static Bitmap CreateBitmap(Type type, string originalName) - { - return new Bitmap(GetResourceStream(type, originalName)!); - } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs index 2cc414e3e18..aad75a27f35 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs @@ -28,10 +28,6 @@ namespace System.Drawing private static readonly Size s_largeSize = new Size(32, 32); private static readonly Size s_smallSize = new Size(16, 16); - // Used to help cache the last result of BitmapSelector.GetFileName. - private static string? s_lastOriginalFileName; - private static string? s_lastUpdatedFileName; - public ToolboxBitmapAttribute(string imageFile) : this(GetImageFromFile(imageFile, false), GetImageFromFile(imageFile, true)) { _imageFile = imageFile; @@ -168,19 +164,6 @@ namespace System.Drawing return b; } - // Cache the last result of BitmapSelector.GetFileName because we commonly load images twice - // in succession from the same file and we don't need to compute the name twice. - private static string? GetFileNameFromBitmapSelector(string originalName) - { - if (originalName != s_lastOriginalFileName) - { - s_lastOriginalFileName = originalName; - s_lastUpdatedFileName = BitmapSelector.GetFileName(originalName); - } - - return s_lastUpdatedFileName; - } - // Just forwards to Image.FromFile eating any non-critical exceptions that may result. private static Image? GetImageFromFile(string? imageFile, bool large, bool scaled = true) { @@ -189,8 +172,6 @@ namespace System.Drawing { if (imageFile != null) { - imageFile = GetFileNameFromBitmapSelector(imageFile); - string? ext = Path.GetExtension(imageFile); if (ext != null && string.Equals(ext, ".ico", StringComparison.OrdinalIgnoreCase)) { From 4f3364ebf9237a1454c1bc60a3f742045102bd82 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 21 Jun 2021 12:42:57 -0500 Subject: [PATCH 019/926] Build for any RID if building from source (#54223) --- .../Microsoft.NETCore.App.Crossgen2.sfxproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj index e9cd505eea1..b039715be97 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj @@ -10,7 +10,8 @@ $(SharedFrameworkName)$(PgoSuffix).$(RuntimeIdentifier) dotnet-crossgen2 crossgen2 - linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64;win-arm + + linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64;win-arm false AddRuntimeFilesToPackage; From 1ccd65f4f887460c019b0908f524b898ed02ce55 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 21 Jun 2021 13:59:28 -0400 Subject: [PATCH 020/926] Special-case no attributes in GetCustomAttributeData (#54405) --- .../src/System/Reflection/CustomAttribute.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 1b825f0dc95..cf35de24627 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -237,6 +237,10 @@ namespace System.Reflection private static IList GetCustomAttributes(RuntimeModule module, int tkTarget) { CustomAttributeRecord[] records = GetCustomAttributeRecords(module, tkTarget); + if (records.Length == 0) + { + return Array.Empty(); + } CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length]; for (int i = 0; i < records.Length; i++) From 89b39c50c3778f787bd0f4c29f57a55f73955573 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 21 Jun 2021 13:59:46 -0400 Subject: [PATCH 021/926] Avoid Attribute.InternalGetCustomAttributes overheads when inherit==true is useless (#54402) --- .../src/System/Attribute.CoreCLR.cs | 64 +++++++++++-------- .../System/Reflection/RuntimePropertyInfo.cs | 6 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs index bd108893d6c..0779c2401f0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs @@ -25,24 +25,26 @@ namespace System if (!inherit) return attributes; + // if this is an index we need to get the parameter types to help disambiguate + Type[] indexParamTypes = GetIndexParameterTypes(element); + PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); + if (baseProp == null) + return attributes; + // create the hashtable that keeps track of inherited types Dictionary types = new Dictionary(11); // create an array list to collect all the requested attibutes List attributeList = new List(); - CopyToArrayList(attributeList, attributes, types); - - // if this is an index we need to get the parameter types to help disambiguate - Type[] indexParamTypes = GetIndexParameterTypes(element); - - - PropertyInfo? baseProp = GetParentDefinition(element, indexParamTypes); - while (baseProp != null) + CopyToAttributeList(attributeList, attributes, types); + do { attributes = GetCustomAttributes(baseProp, type, false); AddAttributesToList(attributeList, attributes, types); baseProp = GetParentDefinition(baseProp, indexParamTypes); } + while (baseProp != null); + Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); attributeList.CopyTo(array, 0); return array; @@ -123,27 +125,33 @@ namespace System // walk up the hierarchy chain Attribute[] attributes = (Attribute[])element.GetCustomAttributes(type, inherit); - if (inherit) + if (!inherit) { - // create the hashtable that keeps track of inherited types - Dictionary types = new Dictionary(11); - // create an array list to collect all the requested attibutes - List attributeList = new List(); - CopyToArrayList(attributeList, attributes, types); - - EventInfo? baseEvent = GetParentDefinition(element); - while (baseEvent != null) - { - attributes = GetCustomAttributes(baseEvent, type, false); - AddAttributesToList(attributeList, attributes, types); - baseEvent = GetParentDefinition(baseEvent); - } - Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); - attributeList.CopyTo(array, 0); - return array; - } - else return attributes; + } + + EventInfo? baseEvent = GetParentDefinition(element); + if (baseEvent == null) + { + return attributes; + } + + // create the hashtable that keeps track of inherited types + // create an array list to collect all the requested attibutes + Dictionary types = new Dictionary(11); + List attributeList = new List(); + CopyToAttributeList(attributeList, attributes, types); + do + { + attributes = GetCustomAttributes(baseEvent, type, false); + AddAttributesToList(attributeList, attributes, types); + baseEvent = GetParentDefinition(baseEvent); + } + while (baseEvent != null); + + Attribute[] array = CreateAttributeArrayHelper(type, attributeList.Count); + attributeList.CopyTo(array, 0); + return array; } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", @@ -357,7 +365,7 @@ namespace System #endregion #region Utility - private static void CopyToArrayList(List attributeList, Attribute[] attributes, Dictionary types) + private static void CopyToAttributeList(List attributeList, Attribute[] attributes, Dictionary types) { for (int i = 0; i < attributes.Length; i++) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index b7e6a58f1fe..f01ae3933d0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -304,9 +304,11 @@ namespace System.Reflection // Now copy over the parameter info's and change their // owning member info to the current property info. - ParameterInfo[] propParams = new ParameterInfo[numParams]; + ParameterInfo[] propParams = numParams != 0 ? + new ParameterInfo[numParams] : + Array.Empty(); - for (int i = 0; i < numParams; i++) + for (int i = 0; i < propParams.Length; i++) propParams[i] = new RuntimeParameterInfo((RuntimeParameterInfo)methParams![i], this); m_parameters = propParams; From 19b5bd1b588fe19efe835153fd049921bf612204 Mon Sep 17 00:00:00 2001 From: Steve MacLean Date: Mon, 21 Jun 2021 15:10:16 -0400 Subject: [PATCH 022/926] Add Cross DAC documentation (#54498) * Add Cross DAC documentation --- docs/design/features/cross-dac.md | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/design/features/cross-dac.md diff --git a/docs/design/features/cross-dac.md b/docs/design/features/cross-dac.md new file mode 100644 index 00000000000..30becca5be6 --- /dev/null +++ b/docs/design/features/cross-dac.md @@ -0,0 +1,97 @@ +# Cross DAC Notes + +The `crossdac` is a cross-compiled DAC. It is compiled to execute on one platform, but debug a target of a different architecture. + +Our current crossdacs are all: + +- compiled to run on Windows +- Same bitness. (Target and host have the same number of bits. +- target a *nix variant + +The crossdac allow us to use Windows debugging tools to debug dumps from *nix processes. + +## Design + +### Limitations + +- To avoid solving remoting and synchronization issues, the crossdac will not support live processes. Only dump debugging is supported. +- Similar to the DAC, each cross DAC must match its runtime. The DACs are indexed on a symbol server to allow the debuggers to get these as needed. + +### Conditional Code Selection + +This is a simple cross compilation of the DAC, `C++` code. This mean the `HOST_*` and the `TARGET_*` are configured differently. In this context: + +- `HOST` refers to the architecture of the platform that is running the debugger. +- `TARGET` refers to the platform that generated the code dump. + +In general, most code should be conditioned on `TARGET_*` variables. This is because in general we want the `DAC` to behave identically when cross compiled. + +Code must be conditioned on `HOST` when it refers to host needed services. These have typically been thing like file i/o and memory allocation. + +Initial implementation allowed the compiler to find most of these. The strategy was to assume all code should be conditioned on `TARGET` and let the compiler gripe. + +### Type Layout + +The DAC is essentially a memory parsing tool with supporting functionality. The layout of types in the DAC must match the layout of types in the runtime. + +The `C++` standard is not explicit about all layout rules of data structures. Due to its historical evolution from `C`, most structures are arranged in an intuitive easily understood fashion. Newer and more exotic structures are less consistent. + +Experimentation has shown that layout varies in inheritance cases. The DAC does not support general multiple inheritance, so that simplifies things. It does support multiple inheritance with the empty base classes. + +These cases have proven to be problematic: + +- Classes with empty base classes. (I the only issue is with multiple base classes.) + - By default `gcc` use an empty base class optimization to eliminate the 1 byte of space these empty base classes normally consume (alone). + - By default `Windows` compilers do not do this optimization. This is to preserve backward binary compatibility. + - The Windows compilers allow this optimization to be enabled. Our code uses `EMPTY_BASES_DECL` to enable this optimization. It has to be applied to every structure that has multiple base classes or derives from a such a structure. See `__declspec(empty_bases)`. +- Packing of the first member of the derived class. In the case where the base class ended with padding: + - `gcc` compilers reuse the padding for the first member of the derived class. This effectively removes the padding of the base class in the derived class. + - Windows compilers do not remove this padding. + - Our code uses the `DAC_ALIGNAS(a)` macro before the first element of the derived class to force the `gcc` compiler to align that member and keep the base classes padding. + - The `a` parameter is preferentially the base classes typename. + - However, in some cases the compiler will not allow this due to some circular layout issues it causes. In these cases, `a` can refer to a well known type instead. I prefer `int64_t`, `int32_t`, `size_t` ... + +#### DacCompareNativeTypes Usage + +I wrote and used [DacCompareNativeTypes](https://github.com/dotnet/diagnostics/tree/main/src/tests/DacCompareNativeTypes), to locate and identify type layout issues. + +The tool is a bit crude, but it helped get the job done. + +The `libcoreclr.so` has a lot of symbols. This proved very slow. So to expedite things, I compared the `dac` and later the `dbi` libraries for structure layout. This had the advantage of eliminating irrelevant data structures. + +The compilers generate different debug data and different hidden data structures. The tool tries to overlook these. Be aware that not all differences are real. Some data structures are host only so these are expected to be different. + +I usually ran the tool in a debugger so that I could look at other available meta-data the tool keeps. i.e. source file and line number. + +### Missing/Different types + +There are some cases where types are defined by the Target. These types maybe missing or different on the Host. In these cases we define the cross compilation types in `src/coreclr/inc/crosscomp.h`. + +See `T_CRITICAL_SECTION` for a key example. In this case both host and target supported critical sections, but we needed to correctly map the target data structures. So we needed a type defined which was the TARGET's `CRITICAL_SECTION`. + +So the Target's definition was made available for the cross compile. Additionally the macro was created to make sure references which required the Target's definition could be separated from ones which might need the host's definition. + +There is also some defensive programming to make sure these structures accurate. See `T_CRITICAL_SECTION_VALIDATION_MESSAGE` for one example. + +### Out of Process Unwinding + +To fully support native stack processing, we needed a Target unwinder. For this `libunwind` was also cross-compiled. + +See [CMake cross libunwind](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/CMakeLists.txt#L113) + +### DBI + +I use the term `DAC` in this document to refer to both the `DAC` and the `DBI` debug interface. Both were actually cross compiled. Be aware. + +### Build entry point + +The main build systme change is adding the ability to set the Target OS on a Windows build. + +- See [build-runtime.cmd changes](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/src/coreclr/build-runtime.cmd#L133-L134) +- See [Subsets.props](https://github.com/dotnet/runtime/blob/0049c629381c5a18e4dadd1038c2bd6b3ae6e3e6/eng/Subsets.props#L191-L197) + +There are also changes to the official build to set these flags package the results and upload to the symbol server. + +### Client changes + +Various changes were required in the DAC clients to consume the new crossdac. These are really out of the scope of this document. \ No newline at end of file From d08c08d2630acc5866225636587f78b35e60fcfe Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Mon, 21 Jun 2021 15:30:03 -0400 Subject: [PATCH 023/926] Add UnixFilePermissions.xml for Mono AOT compilers (#54501) * Add UnixFilePermissions.xml for Mono AOT compilers Ref: https://github.com/dotnet/runtime/issues/53545 Ref: https://github.com/dotnet/sdk/issues/16894 Ref: https://github.com/xamarin/xamarin-macios/pull/11869 Ref: https://github.com/xamarin/xamarin-android/pull/6010 * Only set permissions on !windows --- ...icrosoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml | 5 +++++ .../Microsoft.NETCore.App.MonoCrossAOT.sfxproj | 1 + 2 files changed, 6 insertions(+) create mode 100644 src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml new file mode 100644 index 00000000000..9437f7953f3 --- /dev/null +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj index 174cf76bb8c..3c0e7fac2d8 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj @@ -20,6 +20,7 @@ + From 2bc86980ba50713ced7a2ef12187c299ad6a355a Mon Sep 17 00:00:00 2001 From: Thad House Date: Mon, 21 Jun 2021 12:56:27 -0700 Subject: [PATCH 024/926] Switch MsQuicOpen to MsQuicOpenVersion (#54443) * Switch MsQuicOpen to MsQuicOpenVersion MsQuicOpenVersion is designed so we can update the API without breaking old consumers. The version of MsQuic specified in the readme already has this change, so it is safe to completely switch over. Also switches the API to function pointers, as it was easier then updating the delegate too. * Add version contant --- .../Implementations/MsQuic/Internal/MsQuicApi.cs | 12 +++++++----- .../MsQuic/Interop/MsQuicNativeMethods.cs | 4 ---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index 5914cf200c6..4483920196a 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -119,18 +119,19 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal internal static bool IsQuicSupported { get; } + private const int MsQuicVersion = 1; + static MsQuicApi() { - // TODO: Consider updating all of these delegates to instead use function pointers. if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { try { - if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpen", out IntPtr msQuicOpenAddress)) + if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) { - MsQuicOpenDelegate msQuicOpen = - Marshal.GetDelegateForFunctionPointer(msQuicOpenAddress); - uint status = msQuicOpen(out NativeApi* vtable); + delegate* unmanaged[Cdecl] msQuicOpenVersion = + (delegate* unmanaged[Cdecl])msQuicOpenVersionAddress; + uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable); if (MsQuicStatusHelper.SuccessfulStatusCode(status)) { IsQuicSupported = true; @@ -148,6 +149,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal } } + // TODO: Consider updating all of these delegates to instead use function pointers. internal RegistrationOpenDelegate RegistrationOpenDelegate { get; } internal RegistrationCloseDelegate RegistrationCloseDelegate { get; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 3700edbb303..325ee8170ae 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -51,10 +51,6 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal internal IntPtr DatagramSend; } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint MsQuicOpenDelegate( - out NativeApi* registration); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetContextDelegate( SafeHandle handle, From 213600c17635bf25f812a6a7e6ab53d4fa875883 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:04:39 -0400 Subject: [PATCH 025/926] [Mono] Enable runtime tests to run on Android x64 with interpreter (#54084) * Enable Android x64 with interpreter * Make variable RuntimeVariant available for testenvironment * Pass down runtimeVariant * Verify that tests are running with interpreter * Pass MONO_ENV_OPTIONS value to the app * Set ForceInterpreter to true * Change default value for interp to false * dummy commit * dummy commit 2 * dummy commit * Configure interp for Android * Pass RuntimeVariant down as a parameter * Add issue link * Enable Mono with interpreter on desktop * Disable Android x64 with JIT * Revert hacks to enable all lanes * revert unintentional change * Disable Vector128_1_r* --- .../android-runtime-and-send-to-helix.yml | 3 +- eng/pipelines/runtime-staging.yml | 38 ++++ src/mono/sample/Android/Makefile | 2 + src/tests/Common/testenvironment.proj | 4 +- src/tests/issues.targets | 179 ++++++++---------- src/tests/run.proj | 8 +- 6 files changed, 126 insertions(+), 108 deletions(-) diff --git a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml index c45d9f2f5d3..f42787af8eb 100644 --- a/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml +++ b/eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml @@ -26,7 +26,7 @@ parameters: steps: - - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} $(buildConfigUpper) + - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) /p:LibrariesConfiguration=${{ parameters.buildConfig }} -ci -excludemonofailures os ${{ parameters.osGroup }} ${{ parameters.archType }} /p:RuntimeVariant=${{ parameters.runtimeVariant }} $(buildConfigUpper) displayName: Build Tests # Send tests to Helix @@ -40,6 +40,7 @@ steps: coreClrRepoRoot: $(Build.SourcesDirectory)/src/coreclr runtimeFlavorDisplayName: ${{ parameters.runtimeFlavorDisplayName }} shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + runtimeVariant: ${{ parameters.runtimeVariant }} ${{ if eq(variables['System.TeamProject'], 'public') }}: creator: $(Build.DefinitionName) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index eaaa9e08fe2..5def476efef 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -298,6 +298,44 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# +# Build the whole product using Mono for Android and run runtime tests with interpreter +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Android_x64 + variables: + - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}: + - name: _HelixSource + value: pr/dotnet/runtime/$(Build.SourceBranch) + - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}: + - name: _HelixSource + value: ci/dotnet/runtime/$(Build.SourceBranch) + - name: timeoutPerTestInMinutes + value: 60 + - name: timeoutPerTestCollectionInMinutes + value: 180 + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_RuntimeTests + buildArgs: -s mono+libs -c $(_BuildConfig) + timeoutInMinutes: 240 + runtimeVariant: monointerpreter + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) # # Build the whole product using Mono for Android and run runtime tests with Android devices diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 11f1c35df9f..6d59ee860fe 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -3,6 +3,7 @@ MONO_ARCH?=x64 DOTNET := ../../../../dotnet.sh USE_LLVM=true AOT=false +INTERP=false DEPLOY_AND_RUN?=true #If DIAGNOSTIC_PORTS is enabled, RUNTIME_COMPONENTS must also be enabled. @@ -26,6 +27,7 @@ run: /p:Configuration=$(MONO_CONFIG) \ /p:DeployAndRun=$(DEPLOY_AND_RUN) \ /p:ForceAOT=$(AOT) \ + /p:MonoForceInterpreter=$(INTERP) \ /p:UseLLVM=$(USE_LLVM) \ /p:RunActivity=false \ '/p:RuntimeComponents="$(RUNTIME_COMPONENTS)"' \ diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index 4dc72fdf06d..37392b57df4 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -193,7 +193,7 @@ <_TestEnvFileLine Include="@(_COMPlusVariable->'set %(Identity)=%(Value)')" /> - <_TestEnvFileLine Include="set MONO_ENV_OPTIONS=--interpreter" Condition="'$(Scenario)' == 'interpreter'" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="set MONO_ENV_OPTIONS=--interpreter" /> <_TestEnvFileLine Condition="'$(Scenario)' == 'clrinterpreter'" Include="set COMPlus_Interpret=%2A" /> @@ -208,7 +208,7 @@ <_TestEnvFileLine Include="@(_COMPlusVariable->'export %(Identity)=%(Value)')" /> - <_TestEnvFileLine Include="export MONO_ENV_OPTIONS=--interpreter" Condition="'$(Scenario)' == 'interpreter'" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="export MONO_ENV_OPTIONS=--interpreter" /> <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'llvmaot'" Include="export MONO_ENV_OPTIONS=--llvm" /> diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 255236a9d34..46132c96a34 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1694,22 +1694,22 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54395 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54396 - needs triage + https://github.com/dotnet/runtime/issues/54374 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54374 needs triage @@ -1727,7 +1727,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54394 needs triage @@ -1736,16 +1736,16 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54371 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1757,13 +1757,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54372 - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1775,7 +1775,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54372 needs triage @@ -1784,25 +1784,25 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54371 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54371 - needs triage + https://github.com/dotnet/runtime/issues/54372 needs triage @@ -1814,19 +1814,19 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54374 - needs triage + https://github.com/dotnet/runtime/issues/54381 - needs triage + https://github.com/dotnet/runtime/issues/54391 needs triage @@ -1838,10 +1838,10 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54391 - needs triage + https://github.com/dotnet/runtime/issues/54391 needs triage @@ -1868,7 +1868,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54381 needs triage @@ -1916,13 +1916,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54388 - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -1934,7 +1934,7 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -1943,10 +1943,10 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -1961,13 +1961,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54359 needs triage @@ -1982,16 +1982,16 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54392 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54375 - needs triage + https://github.com/dotnet/runtime/issues/54374 needs triage @@ -2026,17 +2026,11 @@ needs triage - - needs triage - needs triage - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -2044,17 +2038,11 @@ needs triage - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54388 - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54388 needs triage @@ -2102,10 +2090,7 @@ needs triage - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54393 needs triage @@ -2113,14 +2098,8 @@ needs triage - - needs triage - - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54359 needs triage @@ -2134,98 +2113,86 @@ needs triage - - needs triage - needs triage - - needs triage - - - needs triage - - needs triage + https://github.com/dotnet/runtime/issues/54399 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54359 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54373 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54373 - needs triage + https://github.com/dotnet/runtime/issues/54393 - needs triage + https://github.com/dotnet/runtime/issues/54401 - needs triage + https://github.com/dotnet/runtime/issues/54401 - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54392 - needs triage + https://github.com/dotnet/runtime/issues/54399 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54392 needs triage - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 - needs triage + https://github.com/dotnet/runtime/issues/54376 needs triage @@ -2234,19 +2201,25 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/54358 needs triage - needs triage + https://github.com/dotnet/runtime/issues/54389 - needs triage + https://github.com/dotnet/runtime/issues/54389 - needs triage + https://github.com/dotnet/runtime/issues/54376 + + + https://github.com/dotnet/runtime/issues/54374e + + + https://github.com/dotnet/runtime/issues/54374 diff --git a/src/tests/run.proj b/src/tests/run.proj index c16a18fe9f3..a64233555d2 100644 --- a/src/tests/run.proj +++ b/src/tests/run.proj @@ -648,6 +648,8 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). armeabi-v7a x86_64 x86 + false + true @@ -683,14 +685,16 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). ProjectName="$(Category)" MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)/native/include/mono-2.0" StripDebugSymbols="$(StripDebugSymbols)" + ForceInterpreter="$(MonoInterp)" AppDir="$(BuildDir)" OutputDir="$(AppDir)"> - - + + + From dcb03e5689141eb6c55a6643e2b845490947cb4e Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 22 Jun 2021 09:45:57 +1200 Subject: [PATCH 026/926] HTTP/3: Fix header field length calculation (#54442) --- .../aspnetcore/Http3/QPack/HeaderField.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs index b4b80c53798..9cf18591863 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/HeaderField.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Text; + namespace System.Net.Http.QPack { internal readonly struct HeaderField @@ -11,6 +14,8 @@ namespace System.Net.Http.QPack public HeaderField(byte[] name, byte[] value) { + Debug.Assert(name.Length > 0); + Name = name; Value = value; } @@ -19,6 +24,20 @@ namespace System.Net.Http.QPack public byte[] Value { get; } - public int Length => Name.Length + Value.Length; + public int Length => GetLength(Name.Length, Value.Length); + + public static int GetLength(int nameLength, int valueLength) => nameLength + valueLength + RfcOverhead; + + public override string ToString() + { + if (Name != null) + { + return Encoding.ASCII.GetString(Name) + ": " + Encoding.ASCII.GetString(Value); + } + else + { + return ""; + } + } } } From 0cdd423fbe396275d8faaca0c2fee8b6b6e23674 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Mon, 21 Jun 2021 15:08:27 -0700 Subject: [PATCH 027/926] Add code to flush JSON writer after root-level fast-path serialization (#54502) --- .../Serialization/JsonSerializer.Write.Helpers.cs | 1 + .../SerializationLogicTests.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs index 60836120d89..cd6cd1b163b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs @@ -43,6 +43,7 @@ namespace System.Text.Json typedInfo.Options._context?.CanUseSerializationLogic == true) { typedInfo.Serialize(writer, value); + writer.Flush(); } else { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs index 53474be5f1e..daa3a4cb71e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.IO; using System.Text.Encodings.Web; using System.Text.Json.Serialization; using Xunit; @@ -111,5 +112,16 @@ namespace System.Text.Json.SourceGeneration.Tests yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { DefaultIgnoreCondition = JsonIgnoreCondition.Never } }; yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { IgnoreReadOnlyFields = true } }; } + + [Fact] + public static void WriterIsFlushedAtRootCall() + { + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms); + + JsonSerializer.Serialize(writer, new HighLowTemps(), SerializationContext.Default.HighLowTemps); + Assert.Equal(18, writer.BytesCommitted); + Assert.Equal(0, writer.BytesPending); + } } } From 44bb2ad691c0e6a744846b853bea1a64dbcbd3b2 Mon Sep 17 00:00:00 2001 From: Oded Hanson Date: Tue, 22 Jun 2021 01:12:18 +0300 Subject: [PATCH 028/926] Dispose transient CFData in Interop.AppleCrypto.X509GetRawData --- .../Interop.X509.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index d50ca014c4e..7094e90dd8f 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -55,18 +55,21 @@ internal static partial class Interop out data, out osStatus); - if (ret == 1) + using (data) { - return CoreFoundation.CFGetData(data); - } + if (ret == 1) + { + return CoreFoundation.CFGetData(data); + } - if (ret == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } + if (ret == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } } internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity) From fb2e61e92572273dbbc356cb1c80e67f82ec143d Mon Sep 17 00:00:00 2001 From: Oded Hanson Date: Tue, 22 Jun 2021 01:14:16 +0300 Subject: [PATCH 029/926] Add name of corrupted certificate to CryptographicException on Mac * Add name of corrupted certificate to CryptographicException on Mac When trying to create a CertificateData out of raw X509 byte array it might throw if the data is corrupted. The existing exception message does not provide any information which might help the user identify the corrupted certificate and fix it. This change, makes a native call to SecCertificateCopySubjectSummary which will provide a human readable summary of the certificate, and will generate an exception message using this string. Co-authored-by: Jeremy Barton --- .../Interop.X509.cs | 30 +++++++++++++++++++ .../entrypoints.c | 1 + .../pal_x509.c | 12 ++++++++ .../pal_x509.h | 10 +++++++ .../Pal.OSX/AppleCertificatePal.cs | 21 ++++++++++++- .../src/Resources/Strings.resx | 3 ++ 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index 7094e90dd8f..b7a8be1ba8a 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -20,6 +20,11 @@ internal static partial class Interop out SafeCFDataHandle cfDataOut, out int pOSStatus); + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509GetSubjectSummary( + SafeSecCertificateHandle cert, + out SafeCFStringHandle cfSubjectSummaryOut); + [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_X509GetPublicKey(SafeSecCertificateHandle cert, out SafeSecKeyRefHandle publicKey, out int pOSStatus); @@ -72,6 +77,31 @@ internal static partial class Interop } } + internal static string? X509GetSubjectSummary(SafeSecCertificateHandle cert) + { + SafeCFStringHandle subjectSummary; + + int ret = AppleCryptoNative_X509GetSubjectSummary( + cert, + out subjectSummary); + + using (subjectSummary) + { + if (ret == 1) + { + return CoreFoundation.CFStringToString(subjectSummary); + } + } + + if (ret == 0) + { + return null; + } + + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity) { SafeSecKeyRefHandle key; diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c index ff4df6f41fc..1833d4a2161 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -106,6 +106,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_GetOSStatusForChainStatus) DllImportEntry(AppleCryptoNative_X509ChainSetTrustAnchorCertificates) DllImportEntry(AppleCryptoNative_Pbkdf2) + DllImportEntry(AppleCryptoNative_X509GetSubjectSummary) }; EXTERN_C const void* CryptoAppleResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c index 7d3e61f4b5d..0b6d1f889bc 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c @@ -230,3 +230,15 @@ int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDa *pOSStatus = *ppDataOut == NULL ? errSecParam : noErr; return (*pOSStatus == noErr); } + +int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut) +{ + if (ppSummaryOut != NULL) + *ppSummaryOut = NULL; + + if (cert == NULL || ppSummaryOut == NULL) + return kErrorBadInput; + + *ppSummaryOut = SecCertificateCopySubjectSummary(cert); + return (*ppSummaryOut != NULL); +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h index 28124de10c9..a0bc58044c3 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h @@ -72,3 +72,13 @@ ppDataOut: Receives a CFDataRef with the exported blob pOSStatus: Receives the result of SecItemExport */ PALEXPORT int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDataOut, int32_t* pOSStatus); + +/* +Extract a string that contains a human-readable summary of the contents of the certificate + +Returns 1 on success, 0 on failure, any other value indicates invalid state. + +Output: +ppSummaryOut: Receives a CFDataRef with the exported blob +*/ +PALEXPORT int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index cb949b8a93f..07959c3e77b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -399,7 +399,26 @@ namespace Internal.Cryptography.Pal return; Debug.Assert(!_certHandle.IsInvalid); - _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle); + + try + { + _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + } + catch (CryptographicException e) + { + if (subjectSummary is null) + { + throw; + } + + string message = SR.Format( + SR.Cryptography_X509_CertificateCorrupted, + subjectSummary); + + throw new CryptographicException(message, e); + } + _readCertData = true; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index a5c33a43b40..322e2bab91d 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -331,6 +331,9 @@ The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate. + + Certificate '{0}' is corrupted. + Enumeration has not started. Call MoveNext. From 066894e0b74fc5ecbff95fe37207caa269d5695d Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 21 Jun 2021 19:36:51 -0400 Subject: [PATCH 030/926] SYSLIB0026: Obsolete mutable X509 certificate APIs --- docs/project/list-of-diagnostics.md | 1 + src/libraries/Common/src/System/Obsoletions.cs | 3 +++ .../UnitTests/SslAuthenticationOptionsTests.cs | 4 ++-- .../tests/Pkcs12/Pkcs12SafeContentsTests.cs | 2 +- ...ystem.Security.Cryptography.X509Certificates.cs | 14 ++++++++++++++ ...m.Security.Cryptography.X509Certificates.csproj | 3 ++- ...m.Security.Cryptography.X509Certificates.csproj | 4 +++- .../X509Certificates/X509Certificate.cs | 12 ++++++++++++ .../X509Certificates/X509Certificate2.cs | 7 +++++++ ...rity.Cryptography.X509Certificates.Tests.csproj | 1 + .../tests/MembershipConditionTests.cs | 2 +- .../tests/PermissionTests.cs | 2 +- 12 files changed, 48 insertions(+), 7 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 46092b55280..fc946ddb03b 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -80,6 +80,7 @@ The PR that reveals the implementation of the ` { return new X509Certificate(); }; + LocalCertificateSelectionCallback callback = (sender, host, localCertificates, remoteCertificate, issuers) => default; _clientOptions.LocalCertificateSelectionCallback = callback; Assert.Equal(callback, _clientOptions.LocalCertificateSelectionCallback); @@ -109,7 +109,7 @@ namespace System.Net.Security.Tests _serverOptions.ServerCertificate = null; Assert.Null(_serverOptions.ServerCertificate); - X509Certificate cert = new X509Certificate(); + X509Certificate cert = new X509Certificate2(stackalloc byte[0]); _serverOptions.ServerCertificate = cert; Assert.Equal(cert, _serverOptions.ServerCertificate); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs index 9acecd8c695..1e1bf33769f 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12SafeContentsTests.cs @@ -74,7 +74,7 @@ namespace System.Security.Cryptography.Pkcs.Tests.Pkcs12 public static void AddCertificateDisallowedInReadOnly() { Pkcs12SafeContents contents = MakeReadonly(new Pkcs12SafeContents()); - X509Certificate2 cert = new X509Certificate2(); + X509Certificate2 cert = new X509Certificate2(stackalloc byte[0]); Assert.Throws(() => contents.AddCertificate(cert)); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index 0bf7a6e4b22..e3ade1a368c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -147,6 +147,7 @@ namespace System.Security.Cryptography.X509Certificates } public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public X509Certificate() { } public X509Certificate(byte[] data) { } [System.CLSCompliantAttribute(false)] @@ -200,13 +201,19 @@ namespace System.Security.Cryptography.X509Certificates public virtual string GetRawCertDataString() { throw null; } public virtual byte[] GetSerialNumber() { throw null; } public virtual string GetSerialNumberString() { throw null; } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public virtual void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public virtual void Reset() { } void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object? sender) { } @@ -217,6 +224,7 @@ namespace System.Security.Cryptography.X509Certificates } public partial class X509Certificate2 : System.Security.Cryptography.X509Certificates.X509Certificate { + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public X509Certificate2() { } public X509Certificate2(byte[] rawData) { } [System.CLSCompliantAttribute(false)] @@ -265,13 +273,19 @@ namespace System.Security.Cryptography.X509Certificates public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPrivateKey() { throw null; } public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPublicKey() { throw null; } public string GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType nameType, bool forIssuer) { throw null; } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId = "SYSLIB0026", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public override void Reset() { } public override string ToString() { throw null; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj index d670fc85a01..66188cc890c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent) enable + $(NoWarn);SYSLIB0026 @@ -15,4 +16,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 458fa927707..2c0921f9fcd 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -6,7 +6,7 @@ enable - $(NoWarn);CS8769 + $(NoWarn);CS8769;SYSLIB0026 SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported @@ -22,6 +22,8 @@ Link="Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs" /> + $(DefineConstants);HAVE_THUMBPRINT_OVERLOADS $(DefineConstants);Unix true + $(NoWarn);SYSLIB0026 $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS diff --git a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs index d2714f5a3b6..62703d868cc 100644 --- a/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/MembershipConditionTests.cs @@ -82,7 +82,7 @@ namespace System.Security.Permissions.Tests [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")] public static void PublisherMembershipConditionCallMethods() { - PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate()); + PublisherMembershipCondition pmc = new PublisherMembershipCondition(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0])); bool check = pmc.Check(new Evidence()); IMembershipCondition obj = pmc.Copy(); check = pmc.Equals(new object()); diff --git a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs index af714344d0d..daa9d8ac55a 100644 --- a/src/libraries/System.Security.Permissions/tests/PermissionTests.cs +++ b/src/libraries/System.Security.Permissions/tests/PermissionTests.cs @@ -213,7 +213,7 @@ namespace System.Security.Permissions.Tests [SkipOnPlatform(TestPlatforms.Browser, "System.Security.Cryptography.X509Certificates is not supported on this platform.")] public static void PublisherIdentityPermissionCallMethods() { - PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate()); + PublisherIdentityPermission pip = new PublisherIdentityPermission(new System.Security.Cryptography.X509Certificates.X509Certificate2(stackalloc byte[0])); PublisherIdentityPermission pip2 = new PublisherIdentityPermission(new Permissions.PermissionState()); IPermission ip = pip.Copy(); IPermission ip2 = pip.Intersect(ip); From 2a43c07bb82113a029090730d8ca001a68b22c05 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 21 Jun 2021 18:10:58 -0700 Subject: [PATCH 031/926] Disable mainv1 and mainv2 for GCStress due to #54203 (#54514) --- src/tests/readytorun/tests/mainv1.csproj | 2 ++ src/tests/readytorun/tests/mainv2.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tests/readytorun/tests/mainv1.csproj b/src/tests/readytorun/tests/mainv1.csproj index e8546a30d5b..032c8995360 100644 --- a/src/tests/readytorun/tests/mainv1.csproj +++ b/src/tests/readytorun/tests/mainv1.csproj @@ -3,6 +3,8 @@ exe BuildAndRun false + + true diff --git a/src/tests/readytorun/tests/mainv2.csproj b/src/tests/readytorun/tests/mainv2.csproj index 4ca1d852b13..a60c79cf464 100644 --- a/src/tests/readytorun/tests/mainv2.csproj +++ b/src/tests/readytorun/tests/mainv2.csproj @@ -3,6 +3,8 @@ exe BuildAndRun false + + true From bea8d9564c4e63e204efa97bcd584fb0099c72b0 Mon Sep 17 00:00:00 2001 From: Prashanth Govindarajan Date: Mon, 21 Jun 2021 18:22:30 -0700 Subject: [PATCH 032/926] More Parse tests for double, single and Half (#50394) * Test ibm-fpgen locally for validation * sq --- THIRD-PARTY-NOTICES.TXT | 2 +- eng/Version.Details.xml | 4 ++ eng/Versions.props | 1 + .../tests/System.Runtime.Tests.csproj | 3 +- .../tests/System/DoubleTests.cs | 65 +++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index a877e8fb7ab..14c806c5ca3 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -680,7 +680,7 @@ worldwide. This software is distributed without any warranty. See . -License for fastmod (https://github.com/lemire/fastmod) +License for fastmod (https://github.com/lemire/fastmod) and ibm-fpgen (https://github.com/nigeltao/parse-number-fxx-test-data) -------------------------------------- Copyright 2018 Daniel Lemire diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2485b69e114..f1ef354b76c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -214,5 +214,9 @@ https://github.com/dotnet/hotreload-utils 25b814e010cd4796cedfbcce72a274c26928f496 + + https://github.com/dotnet/runtime-assets + 8d7b898b96cbdb868cac343e938173105287ed9e + diff --git a/eng/Versions.props b/eng/Versions.props index 493ee882e59..b62b9b4854c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -110,6 +110,7 @@ 4.5.0 6.0.0-preview.6.21314.1 + 6.0.0-beta.21314.1 6.0.0-beta.21307.1 6.0.0-beta.21307.1 6.0.0-beta.21307.1 diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 2a68cc12651..2ca6739e9c6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -1,4 +1,4 @@ - + true $(NoWarn),1718,SYSLIB0013 @@ -282,6 +282,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs index cfe3690e588..a80eb05598c 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.IO; using Xunit; #pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0, NaN from -NaN @@ -346,6 +347,70 @@ namespace System.Tests } } + internal static string SplitPairs(string input) + { + string[] splitPairs = input.Split('-'); + string ret = ""; + foreach (var pair in splitPairs) + { + string reversedPair = Reverse(pair); + ret += reversedPair; + } + + return ret; + } + + internal static string Reverse(string s) + { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public static void ParsePatterns() + { + string path = Directory.GetCurrentDirectory(); + using (FileStream file = new FileStream(Path.Combine(path, "ibm-fpgen.txt"), FileMode.Open)) + { + using (var streamReader = new StreamReader(file)) + { + string line = streamReader.ReadLine(); + while (line != null) + { + string[] data = line.Split(' '); + string inputHalfBytes = data[0]; + string inputFloatBytes = data[1]; + string inputDoubleBytes = data[2]; + string correctValue = data[3]; + + double doubleValue = double.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string doubleBytes = BitConverter.ToString(BitConverter.GetBytes(doubleValue)); + float floatValue = float.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string floatBytes = BitConverter.ToString(BitConverter.GetBytes(floatValue)); + Half halfValue = Half.Parse(correctValue, NumberFormatInfo.InvariantInfo); + string halfBytes = BitConverter.ToString(BitConverter.GetBytes(halfValue)); + + doubleBytes = SplitPairs(doubleBytes); + floatBytes = SplitPairs(floatBytes); + halfBytes = SplitPairs(halfBytes); + + if (BitConverter.IsLittleEndian) + { + doubleBytes = Reverse(doubleBytes); + floatBytes = Reverse(floatBytes); + halfBytes = Reverse(halfBytes); + } + + Assert.Equal(doubleBytes, inputDoubleBytes); + Assert.Equal(floatBytes, inputFloatBytes); + Assert.Equal(halfBytes, inputHalfBytes); + line = streamReader.ReadLine(); + } + } + } + } + public static IEnumerable Parse_Invalid_TestData() { NumberStyles defaultStyle = NumberStyles.Float; From a2f58177b9ca0998ab6adfde98e5ac3fdd946225 Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Mon, 21 Jun 2021 18:52:40 -0700 Subject: [PATCH 033/926] removing more crossgen code from being built. (#54458) Should improve build times further. --- src/coreclr/md/enc/CMakeLists.txt | 4 - src/coreclr/md/runtime/CMakeLists.txt | 4 - src/coreclr/utilcode/CMakeLists.txt | 11 - src/coreclr/vm/crossgen/CMakeLists.txt | 283 ------------------------- 4 files changed, 302 deletions(-) delete mode 100644 src/coreclr/vm/crossgen/CMakeLists.txt diff --git a/src/coreclr/md/enc/CMakeLists.txt b/src/coreclr/md/enc/CMakeLists.txt index 6bd2518d868..d4abb371ffb 100644 --- a/src/coreclr/md/enc/CMakeLists.txt +++ b/src/coreclr/md/enc/CMakeLists.txt @@ -62,10 +62,6 @@ add_library_clr(mdruntimerw-dbi ${MDRUNTIMERW_SOURCES}) set_target_properties(mdruntimerw-dbi PROPERTIES DBI_COMPONENT TRUE) target_precompile_headers(mdruntimerw-dbi PRIVATE stdafx.h) -add_library_clr(mdruntimerw_crossgen ${MDRUNTIMERW_SOURCES}) -set_target_properties(mdruntimerw_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(mdruntimerw_crossgen PRIVATE stdafx.h) - add_library_clr(mdruntimerw_ppdb ${MDRUNTIMERW_SOURCES}) target_compile_definitions(mdruntimerw_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) target_precompile_headers(mdruntimerw_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/md/runtime/CMakeLists.txt b/src/coreclr/md/runtime/CMakeLists.txt index 6ff49d3e803..3e1fc8eda75 100644 --- a/src/coreclr/md/runtime/CMakeLists.txt +++ b/src/coreclr/md/runtime/CMakeLists.txt @@ -59,10 +59,6 @@ add_library_clr(mdruntime-dbi ${MDRUNTIME_SOURCES}) set_target_properties(mdruntime-dbi PROPERTIES DBI_COMPONENT TRUE) target_precompile_headers(mdruntime-dbi PRIVATE stdafx.h) -add_library_clr(mdruntime_crossgen ${MDRUNTIME_SOURCES}) -set_target_properties(mdruntime_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(mdruntime_crossgen PRIVATE stdafx.h) - add_library_clr(mdruntime_ppdb ${MDRUNTIME_SOURCES}) target_compile_definitions(mdruntime_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) target_precompile_headers(mdruntime_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index fec22cf9dce..1ae433adbfd 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -76,10 +76,6 @@ set(UTILCODE_DAC_SOURCES hostimpl.cpp ) -set(UTILCODE_CROSSGEN_SOURCES - ${UTILCODE_COMMON_SOURCES} - hostimpl.cpp -) set(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_COMMON_SOURCES} @@ -90,7 +86,6 @@ set (UTILCODE_DEPENDENCIES eventing_headers) convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES}) convert_to_absolute_path(UTILCODE_DAC_SOURCES ${UTILCODE_DAC_SOURCES}) -convert_to_absolute_path(UTILCODE_CROSSGEN_SOURCES ${UTILCODE_CROSSGEN_SOURCES}) convert_to_absolute_path(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_STATICNOHOST_SOURCES}) add_library_clr(utilcode_dac STATIC ${UTILCODE_DAC_SOURCES}) @@ -98,11 +93,9 @@ add_library_clr(utilcode_obj OBJECT ${UTILCODE_SOURCES}) add_library(utilcode INTERFACE) target_sources(utilcode INTERFACE $) add_library_clr(utilcodestaticnohost STATIC ${UTILCODE_STATICNOHOST_SOURCES}) -add_library_clr(utilcode_crossgen STATIC ${UTILCODE_CROSSGEN_SOURCES}) if(CLR_CMAKE_HOST_UNIX) target_link_libraries(utilcodestaticnohost nativeresourcestring) - target_link_libraries(utilcode_crossgen nativeresourcestring) target_link_libraries(utilcode_dac nativeresourcestring) target_link_libraries(utilcode INTERFACE nativeresourcestring) add_dependencies(utilcode_dac coreclrpal) @@ -114,20 +107,16 @@ if(CLR_CMAKE_HOST_WIN32) target_compile_definitions(utilcodestaticnohost PRIVATE _CRTIMP=) # use static version of crt link_natvis_sources_for_target(utilcodestaticnohost INTERFACE utilcode.natvis) - link_natvis_sources_for_target(utilcode_crossgen INTERFACE utilcode.natvis) link_natvis_sources_for_target(utilcode_dac INTERFACE utilcode.natvis) link_natvis_sources_for_target(utilcode INTERFACE utilcode.natvis) endif(CLR_CMAKE_HOST_WIN32) set_target_properties(utilcode_dac PROPERTIES DAC_COMPONENT TRUE) -set_target_properties(utilcode_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) target_compile_definitions(utilcode_dac PRIVATE SELF_NO_HOST) target_compile_definitions(utilcodestaticnohost PRIVATE SELF_NO_HOST) add_dependencies(utilcode_dac ${UTILCODE_DEPENDENCIES}) add_dependencies(utilcode_obj ${UTILCODE_DEPENDENCIES}) -add_dependencies(utilcode_crossgen ${UTILCODE_DEPENDENCIES}) add_dependencies(utilcodestaticnohost ${UTILCODE_DEPENDENCIES}) target_precompile_headers(utilcode_dac PRIVATE [["stdafx.h"]]) target_precompile_headers(utilcode_obj PRIVATE [["stdafx.h"]]) -target_precompile_headers(utilcode_crossgen PRIVATE [["stdafx.h"]]) target_precompile_headers(utilcodestaticnohost PRIVATE [["stdafx.h"]]) diff --git a/src/coreclr/vm/crossgen/CMakeLists.txt b/src/coreclr/vm/crossgen/CMakeLists.txt deleted file mode 100644 index fa585145325..00000000000 --- a/src/coreclr/vm/crossgen/CMakeLists.txt +++ /dev/null @@ -1,283 +0,0 @@ -set(VM_CROSSGEN_SOURCES - ../appdomain.cpp - ../array.cpp - ../assembly.cpp - ../assemblyloadcontext.cpp - ../assemblyspec.cpp - ../baseassemblyspec.cpp - ../binder.cpp - ../bundle.cpp - ../callconvbuilder.cpp - ../castcache.cpp - ../ceeload.cpp - ../ceemain.cpp - ../class.cpp - ../classhash.cpp - ../classlayoutinfo.cpp - ../clrex.cpp - ../clsload.cpp - ../codeman.cpp - ../codeversion.cpp - ../comdelegate.cpp - ../compile.cpp - ../contractimpl.cpp - ../coreassemblyspec.cpp - ../corebindresult.cpp - ../corelib.cpp - ../crossgencompile.cpp - ../custommarshalerinfo.cpp - ../dataimage.cpp - ../debuginfostore.cpp - ../decodemd.cpp - ../dllimport.cpp - ../dllimportcallback.cpp - ../domainfile.cpp - ../ecall.cpp - ../eeconfig.cpp - ../eehash.cpp - ../eetwain.cpp - ../excep.cpp - ../field.cpp - ../fieldmarshaler.cpp - ../formattype.cpp - ../frames.cpp - ../gcinfodecoder.cpp - ../genericdict.cpp - ../generics.cpp - ../genmeth.cpp - ../hash.cpp - ../ilinstrumentation.cpp - ../ilmarshalers.cpp - ../ilstubcache.cpp - ../ilstubresolver.cpp - ../inlinetracking.cpp - ../instmethhash.cpp - ../interoputil.cpp - ../invokeutil.cpp - ../jithost.cpp - ../jitinterface.cpp - ../loaderallocator.cpp - ../memberload.cpp - ../method.cpp - ../methodimpl.cpp - ../methodtable.cpp - ../methodtablebuilder.cpp - ../mlinfo.cpp - ../nativeimage.cpp - ../olevariant.cpp - ../pefile.cpp - ../peimage.cpp - ../peimagelayout.cpp - ../pendingload.cpp - ../precode.cpp - ../sigformat.cpp - ../siginfo.cpp - ../simplerwlock.cpp - ../spinlock.cpp - ../stackingallocator.cpp - ../stubcache.cpp - ../stubgen.cpp - ../stublink.cpp - ../tailcallhelp.cpp - ../typectxt.cpp - ../typedesc.cpp - ../typeequivalencehash.cpp - ../typehandle.cpp - ../typehash.cpp - ../typeparse.cpp - ../typestring.cpp - ../util.cpp - ../vars.cpp - ../versionresilienthashcode.cpp - ../zapsig.cpp -) - -set(VM_CROSSGEN_HEADERS - ../appdomain.hpp - ../appdomain.inl - ../array.h - ../assembly.hpp - ../assemblyloadcontext.h - ../assemblyspec.hpp - ../assemblyspecbase.h - ../baseassemblyspec.h - ../baseassemblyspec.inl - ../binder.h - ../ceeload.h - ../ceeload.inl - ../ceemain.h - ../class.h - ../class.inl - ../classhash.h - ../clrex.h - ../clsload.hpp - ../clsload.inl - ../codeman.h - ../codeversion.h - ../comdelegate.h - ../compile.h - ../contractimpl.h - ../corelib.h - ../custommarshalerinfo.h - ../dataimage.h - ../debuginfostore.h - ../decodemd.h - ../dllimport.h - ../dllimportcallback.h - ../domainfile.h - ../domainfile.inl - ../ecall.h - ../eeconfig.h - ../eehash.h - ../eehash.inl - ../excep.h - ../field.h - ../fieldmarshaler.h - ../genericdict.h - ../generics.h - ../hash.h - ../ilinstrumentation.h - ../ilmarshalers.h - ../ilstubcache.h - ../ilstubresolver.h - ../inlinetracking.h - ../instmethhash.h - ../interoputil.h - ../invokeutil.h - ../jithost.h - ../jitinterface.h - ../loaderallocator.hpp - ../loaderallocator.inl - ../memberload.h - ../method.hpp - ../method.inl - ../methodimpl.h - ../methodtable.h - ../methodtable.inl - ../methodtablebuilder.h - ../methodtablebuilder.inl - ../mlinfo.h - ../olevariant.h - ../pefile.h - ../pefile.inl - ../peimage.h - ../peimage.inl - ../peimagelayout.h - ../peimagelayout.inl - ../pendingload.h - ../precode.h - ../sigformat.h - ../siginfo.hpp - ../simplerwlock.hpp - ../spinlock.h - ../stackingallocator.h - ../stubcache.h - ../stubgen.h - ../stublink.h - ../stublink.inl - ../tailcallhelp.h - ../typectxt.h - ../typedesc.h - ../typedesc.inl - ../typeequivalencehash.hpp - ../typehandle.h - ../typehandle.inl - ../typehash.h - ../typeparse.h - ../typestring.h - ../util.hpp - ../vars.hpp - ../versionresilienthashcode.h - ../zapsig.h -) - -if(FEATURE_READYTORUN) - list(APPEND VM_CROSSGEN_SOURCES - ../readytoruninfo.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../readytoruninfo.h - ) -endif(FEATURE_READYTORUN) - -include_directories(BEFORE ..) -include_directories(${CLR_DIR}/gc) -include_directories(../${ARCH_SOURCES_DIR}) - -if(CLR_CMAKE_TARGET_ARCH_AMD64) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stublinkeramd64.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../${ARCH_SOURCES_DIR}/stublinkeramd64.h - ) -elseif(CLR_CMAKE_TARGET_ARCH_I386) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stublinkerx86.cpp - ../gcdecode.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../${ARCH_SOURCES_DIR}/stublinkerx86.h - ) -elseif(CLR_CMAKE_TARGET_ARCH_ARM) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stubs.cpp - ) -elseif(CLR_CMAKE_TARGET_ARCH_ARM64) - list(APPEND VM_CROSSGEN_SOURCES - ../${ARCH_SOURCES_DIR}/stubs.cpp - ) -elseif(CLR_CMAKE_TARGET_ARCH_S390X) - list(APPEND VM_CROSSGEN_SOURCES - # Not supported as VM target - ) -else() - clr_unknown_arch() -endif() - -if (CLR_CMAKE_TARGET_WIN32) - - # COM interop scenarios - list(APPEND VM_CROSSGEN_SOURCES - ../classcompat.cpp - ../comtoclrcall.cpp - ../clrtocomcall.cpp - ../runtimecallablewrapper.cpp - ) - list(APPEND VM_CROSSGEN_HEADERS - ../classcompat.h - ../clrtocomcall.h - ../comtoclrcall.h - ../runtimecallablewrapper.h - ) - - list(APPEND VM_CROSSGEN_SOURCES ${VM_CROSSGEN_HEADERS}) -endif (CLR_CMAKE_TARGET_WIN32) - -if (CLR_CMAKE_HOST_LINUX) - list(APPEND VM_CROSSGEN_SOURCES - ../perfmap.cpp - ../perfinfo.cpp - ) -endif (CLR_CMAKE_HOST_LINUX) - -add_library_clr(cee_crossgen ${VM_CROSSGEN_SOURCES}) -add_dependencies(cee_crossgen eventing_headers) -set_target_properties(cee_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -target_precompile_headers(cee_crossgen PRIVATE [["common.h"]]) -if (MSVC) - # corelib.cpp does not compile with precompiled header file - set_source_files_properties(../corelib.cpp PROPERTIES COMPILE_FLAGS "/Y-") -endif() - -add_library_clr(corelib_crossgen ../corelib.cpp) -add_dependencies(corelib_crossgen eventing_headers) -target_compile_definitions(corelib_crossgen - PRIVATE - EnC_SUPPORTED - FEATURE_EVENT_TRACE - FEATURE_MULTICOREJIT - CROSSGEN_CORELIB) - -set_target_properties(corelib_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) - From eb57372f7eb239c301ddbff0b1314e5a8f7d66ae Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 21 Jun 2021 21:58:30 -0400 Subject: [PATCH 034/926] [Test] Move leakwheel to Pri1 (#54522) * Move to Pri1 * Remove leakwheel from issues.targets file for mono --- src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj | 1 + src/tests/issues.targets | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj b/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj index d0dca8486ab..ceda84368bf 100644 --- a/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj +++ b/src/tests/GC/Scenarios/LeakWheel/leakwheel.csproj @@ -2,6 +2,7 @@ Exe true + 1 diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 46132c96a34..25b94c7c75c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1696,9 +1696,6 @@ https://github.com/dotnet/runtime/issues/54395 - - needs triage - https://github.com/dotnet/runtime/issues/54396 @@ -3149,9 +3146,6 @@ https://github.com/dotnet/runtime/issues/53353 - - long running test https://github.com/dotnet/runtime/issues/53386 - From 8a20ae03566e3aabb0c95d2bb206a9ee780db4fd Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 21 Jun 2021 23:44:16 -0700 Subject: [PATCH 035/926] Convert some COM object checking functions to managed code (#54471) * Convert COM object checking to managed code * Convert IsComWrapperClass to a managed "can cast to" implementation. * Add testing for updates. --- .../InteropServices/Marshal.CoreCLR.cs | 11 ++++++-- .../src/System/RuntimeHandles.cs | 13 +++++++-- src/coreclr/vm/ecalllist.h | 2 -- src/coreclr/vm/interoputil.cpp | 19 ------------- src/coreclr/vm/interoputil.h | 4 --- src/coreclr/vm/marshalnative.cpp | 24 ---------------- src/coreclr/vm/marshalnative.h | 5 ---- src/coreclr/vm/runtimehandles.cpp | 28 ------------------- src/coreclr/vm/runtimehandles.h | 1 - .../COM/NETClients/Aggregation/Program.cs | 6 ++++ .../NETClients/ConsumeNETServer/Program.cs | 3 ++ 11 files changed, 29 insertions(+), 87 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index 688e4f83908..e064c36746d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -464,8 +464,15 @@ namespace System.Runtime.InteropServices /// /// Checks if the object is classic COM component. /// - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern bool IsComObject(object o); + public static bool IsComObject(object o) + { + if (o is null) + { + throw new ArgumentNullException(nameof(o)); + } + + return o is __ComObject; + } /// /// Release the COM component and if the reference hits 0 zombie this object. diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 75aff556de7..dd17a2fa744 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -460,8 +460,17 @@ namespace System return GetInterfaceMethodImplementation(new QCallTypeHandle(ref nativeHandle), new QCallTypeHandle(ref nativeInterfaceHandle), interfaceMethodHandle); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool IsComObject(RuntimeType type, bool isGenericCOM); + internal static bool IsComObject(RuntimeType type, bool isGenericCOM) + { +#if FEATURE_COMINTEROP + if (isGenericCOM) + return type == typeof(__ComObject); + + return RuntimeTypeHandle.CanCastTo(type, (RuntimeType)typeof(__ComObject)); +#else + return false; +#endif + } [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsInterface(RuntimeType type); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 48a2d7d322e..1822422f596 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -209,7 +209,6 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("GetNumVirtualsAndStaticVirtuals", RuntimeTypeHandle::GetNumVirtualsAndStaticVirtuals) QCFuncElement("VerifyInterfaceIsImplemented", RuntimeTypeHandle::VerifyInterfaceIsImplemented) QCFuncElement("GetInterfaceMethodImplementation", RuntimeTypeHandle::GetInterfaceMethodImplementation) - FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject) FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType) FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface) FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike) @@ -768,7 +767,6 @@ FCFuncStart(gInteropMarshalFuncs) #ifdef FEATURE_COMINTEROP FCFuncElement("GetHRForException", MarshalNative::GetHRForException) - FCFuncElement("IsComObject", MarshalNative::IsComObject) FCFuncElement("GetObjectForIUnknownNative", MarshalNative::GetObjectForIUnknownNative) FCFuncElement("GetUniqueObjectForIUnknownNative", MarshalNative::GetUniqueObjectForIUnknownNative) FCFuncElement("GetNativeVariantForObjectNative", MarshalNative::GetNativeVariantForObjectNative) diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index e3797b13d50..2b6697887aa 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -820,25 +820,6 @@ BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT) } } -// Returns TRUE iff the argument represents the "__ComObject" type or -// any type derived from it (i.e. typelib-imported RCWs). -BOOL IsComWrapperClass(TypeHandle type) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - MethodTable* pMT = type.GetMethodTable(); - if (pMT == NULL) - return FALSE; - - return pMT->IsComObjectType(); -} - // Returns TRUE iff the argument represents the "__ComObject" type. BOOL IsComObjectClass(TypeHandle type) { diff --git a/src/coreclr/vm/interoputil.h b/src/coreclr/vm/interoputil.h index c7209467b4a..b99764688dd 100644 --- a/src/coreclr/vm/interoputil.h +++ b/src/coreclr/vm/interoputil.h @@ -83,10 +83,6 @@ ULONG SafeReleasePreemp(IUnknown* pUnk, RCW* pRCW = NULL); // Determines if a COM object can be cast to the specified type. BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT); -// includes Types which hold a "ComObject" class -// and types which are imported through typelib -BOOL IsComWrapperClass(TypeHandle type); - // includes Type which hold a "__ComObject" class BOOL IsComObjectClass(TypeHandle type); diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index 6d1c38b9d33..b28f34aa266 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -946,30 +946,6 @@ FCIMPL0(FC_BOOL_RET, MarshalNative::AreComObjectsAvailableForCleanup) } FCIMPLEND -//==================================================================== -// check if the object is classic COM component -//==================================================================== -FCIMPL1(FC_BOOL_RET, MarshalNative::IsComObject, Object* objUNSAFE) -{ - FCALL_CONTRACT; - - BOOL retVal = FALSE; - OBJECTREF obj = (OBJECTREF) objUNSAFE; - HELPER_METHOD_FRAME_BEGIN_RET_1(obj); - - if(!obj) - COMPlusThrowArgumentNull(W("o")); - - MethodTable* pMT = obj->GetMethodTable(); - PREFIX_ASSUME(pMT != NULL); - retVal = pMT->IsComObjectType(); - - HELPER_METHOD_FRAME_END(); - FC_RETURN_BOOL(retVal); -} -FCIMPLEND - - //==================================================================== // free the COM component and zombie this object if the ref count hits 0 // further usage of this Object might throw an exception, diff --git a/src/coreclr/vm/marshalnative.h b/src/coreclr/vm/marshalnative.h index 790c7316d32..8a3615294ff 100644 --- a/src/coreclr/vm/marshalnative.h +++ b/src/coreclr/vm/marshalnative.h @@ -103,11 +103,6 @@ public: //==================================================================== static FCDECL2(IUnknown*, CreateAggregatedObjectNative, IUnknown* pOuter, Object* refObjUNSAFE); - //==================================================================== - // check if the object is classic COM component - //==================================================================== - static FCDECL1(FC_BOOL_RET, IsComObject, Object* objUNSAFE); - //==================================================================== // free the COM component and zombie this object // further usage of this Object might throw an exception, diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index a8851f307ed..d3c21535cc4 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1033,34 +1033,6 @@ RuntimeTypeHandle::IsVisible( return fIsExternallyVisible; } // RuntimeTypeHandle::IsVisible -FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL isGenericCOM) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - BOOL ret = FALSE; - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - TypeHandle typeHandle = refType->GetType(); - - HELPER_METHOD_FRAME_BEGIN_RET_1(refType); - { - if (isGenericCOM) - ret = IsComObjectClass(typeHandle); - else - ret = IsComWrapperClass(typeHandle); - } - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - FCIMPL1(LPCUTF8, RuntimeTypeHandle::GetUtf8Name, ReflectClassBaseObject* pTypeUNSAFE) { CONTRACTL { FCALL_CHECK; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index d40b45458af..33645adc3ec 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -191,7 +191,6 @@ public: static BOOL QCALLTYPE IsVisible(QCall::TypeHandle pTypeHandle); - static FCDECL2(FC_BOOL_RET, IsComObject, ReflectClassBaseObject *pType, CLR_BOOL isGenericCOM); static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget); static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object); diff --git a/src/tests/Interop/COM/NETClients/Aggregation/Program.cs b/src/tests/Interop/COM/NETClients/Aggregation/Program.cs index ee1984f5842..2072b41a011 100644 --- a/src/tests/Interop/COM/NETClients/Aggregation/Program.cs +++ b/src/tests/Interop/COM/NETClients/Aggregation/Program.cs @@ -21,6 +21,12 @@ namespace NetClient var managedInner = new ManagedInner(); var nativeOuter = (AggregationTesting)managedInner; + Assert.IsTrue(typeof(ManagedInner).IsCOMObject); + Assert.IsTrue(typeof(AggregationTestingClass).IsCOMObject); + Assert.IsFalse(typeof(AggregationTesting).IsCOMObject); + Assert.IsTrue(Marshal.IsComObject(managedInner)); + Assert.IsTrue(Marshal.IsComObject(nativeOuter)); + Assert.IsTrue(nativeOuter.IsAggregated()); Assert.IsTrue(nativeOuter.AreAggregated(managedInner, nativeOuter)); Assert.IsFalse(nativeOuter.AreAggregated(nativeOuter, new object())); diff --git a/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs b/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs index a78274eb9c8..1bade41b255 100644 --- a/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs +++ b/src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs @@ -24,6 +24,9 @@ namespace NetClient // The CoClass should be the activated type, _not_ the activation interface. Assert.AreEqual(test.GetType(), typeof(CoClass.ConsumeNETServerTestingClass)); + Assert.IsTrue(typeof(CoClass.ConsumeNETServerTestingClass).IsCOMObject); + Assert.IsFalse(typeof(CoClass.ConsumeNETServerTesting).IsCOMObject); + Assert.IsTrue(Marshal.IsComObject(test)); } static void Validate_CCW_Wasnt_Unwrapped() From 849033aea0ac5746e9030961c3eadd51149bcf90 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 22 Jun 2021 03:14:07 -0500 Subject: [PATCH 036/926] ArPow stage 1: local source-build infrastructure (#53294) * Initial arcade-powered source-build infra * Add patches, fixup from 5.0 * Rename patches to match 6.0-p1 work * Add source-build specific build script * Incorporate build-source-build.sh, into eng/ * Run inner build script, through coreclr * Initial source-build args based on live build (not props file) * Cleanup: add RID comments, rm empty/absolute args * Fix subsets * Disable sourcelink in env, not args * Fix CI NZEC * Revert "Use same code to DetectCiphersuiteConfiguration for portable and non-portable builds" This reverts commit 464010d9d0241bbdcbfbda25b32e78991ddf6093. * Fix portability * Fix AllJits build * Fix missing crossgen2 for non-portable RID * Create supplemental intermediate nupkgs * Tweak category names * Use centralized supplemental nupkg infra * Add additional 6.0 patches * Patch updates after merging in main * SourceBuild.props cleanup * Fix issue with incorrect patch merge * Patch updates * Edit clr.tools patch * patch updates * Revert patch integration * Patch updates * Edits per code review feedback * ILAsm workaround * patch updates * Move logic to set ILAsmToolPath for source-build * Update eng/SourceBuild.props Co-authored-by: Viktor Hofer * Remove libraries specific patches * Patch updates necessary with latest main merge * Add back libraries-packages.proj harvesting patch * Refactor intermediate package split to be chunkier * Integrate patch 0017 * Subsets update per code review * Remove obsolete patch * Removed patches that were integrated into main * Remove two additional patches * Remove remaining patches Co-authored-by: Davis Goodin Co-authored-by: dseefeld Co-authored-by: Viktor Hofer --- Directory.Build.props | 1 + eng/SourceBuild.props | 73 +++++++++++++++++++++++++++ eng/SourceBuildPrebuiltBaseline.xml | 5 ++ eng/Subsets.props | 21 ++++---- eng/Version.Details.xml | 1 + eng/packaging.props | 4 -- eng/restore/harvestPackages.targets | 10 ++++ src/libraries/Directory.Build.props | 1 - src/libraries/libraries-packages.proj | 16 ++---- 9 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 eng/SourceBuild.props create mode 100644 eng/SourceBuildPrebuiltBaseline.xml diff --git a/Directory.Build.props b/Directory.Build.props index d1b03d7ef8d..e2c0de947f3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -83,6 +83,7 @@ $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)')) diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props new file mode 100644 index 00000000000..7dfc80d41fd --- /dev/null +++ b/eng/SourceBuild.props @@ -0,0 +1,73 @@ + + + + runtime + + + + + ./build.sh + + true + false + + + $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) + $(__DistroRid) + + + <_targetRidPlatformIndex>$(TargetRid.LastIndexOfAny("-")) + $(TargetRid.Substring(0, $(_targetRidPlatformIndex))) + $(TargetRid.Substring($(_targetRidPlatformIndex)).TrimStart('-')) + + minimal + + + + + + + + + + + + + $(InnerBuildArgs) --arch $(TargetRidPlatform) + $(InnerBuildArgs) --configuration $(Configuration) + $(InnerBuildArgs) --ci + $(InnerBuildArgs) --allconfigurations + $(InnerBuildArgs) --verbosity $(LogVerbosity) + $(InnerBuildArgs) --nodereuse false + $(InnerBuildArgs) --warnAsError false + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_LIBUNWIND=TRUE + $(InnerBuildArgs) /p:MicrosoftNetFrameworkReferenceAssembliesVersion=1.0.0 + $(InnerBuildArgs) /p:ContinuousIntegrationBuild=true + $(InnerBuildArgs) /p:PackageRid=$(TargetRid) + $(InnerBuildArgs) /p:NoPgoOptimize=true + $(InnerBuildArgs) /p:KeepNativeSymbols=true + $(InnerBuildArgs) /p:RuntimeOS=$(TargetRidWithoutPlatform) + $(InnerBuildArgs) /p:PortableBuild=$(SourceBuildPortable) + $(InnerBuildArgs) /p:BuildDebPackage=false + + + + + + + + + + + + + diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml new file mode 100644 index 00000000000..c1b6dfbf053 --- /dev/null +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/eng/Subsets.props b/eng/Subsets.props index bf6b25bffc7..105b34254c6 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -25,8 +25,8 @@ clr+mono+libs+host+packs mono+libs+packs - - clr+libs+host+packs + + clr+libs+host+packs @@ -49,18 +49,21 @@ $(DefaultMonoSubsets)mono.wasmruntime+ $(DefaultMonoSubsets)mono.aotcross+ $(DefaultMonoSubsets)mono.runtime+mono.corelib+mono.packages - + libs.native+ - $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.pretest+libs.packages + $(DefaultLibrariesSubsets)libs.ref+libs.src+libs.packages + $(DefaultLibrariesSubsets)+libs.pretest - host.native+host.pkg+host.tools+host.tests + host.native+host.tools + $(DefaultHostSubsets)+host.pkg+host.tests host.native packs.product $(DefaultPacksSubsets)+packs.tests + $(DefaultPacksSubsets)+packs.installers @@ -204,12 +207,12 @@ + $(CoreClrProjectRoot)tools\r2rtest\R2RTest.csproj" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/> + + Test="true" Category="clr" Condition="'$(__DistroRid)' != 'linux-musl-x64' and '$(DotNetBuildFromSource)' != 'true'"/> @@ -221,7 +224,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f1ef354b76c..4177e280edf 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,6 +9,7 @@ https://github.com/dotnet/arcade 4a2b475948d498b89fedef7cf890883f49bc1ea3 + https://github.com/dotnet/arcade diff --git a/eng/packaging.props b/eng/packaging.props index 87cecef871a..534d3ddcf32 100644 --- a/eng/packaging.props +++ b/eng/packaging.props @@ -16,10 +16,6 @@ - - false - - true false diff --git a/eng/restore/harvestPackages.targets b/eng/restore/harvestPackages.targets index a7fc8aa3465..07eb5a91a7a 100644 --- a/eng/restore/harvestPackages.targets +++ b/eng/restore/harvestPackages.targets @@ -1,4 +1,14 @@  + + + $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\ + $(PackagingTaskAssembly)netcoreapp3.1\ + $(PackagingTaskAssembly)net472\ + + $(PackagingTaskAssembly)net5.0\ + $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll + + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index b06fc950644..0078c1331d3 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -28,7 +28,6 @@ net461;net462;net47;net471;net472 - $(AdditionalBuildTargetFrameworks);netstandard2.0 diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj index 2f4d0949db3..383dd088e12 100644 --- a/src/libraries/libraries-packages.proj +++ b/src/libraries/libraries-packages.proj @@ -1,20 +1,11 @@ - - $(AdditionalBuildTargetFrameworks);package-$(Configuration) - - - - $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\ - $(PackagingTaskAssembly)netcoreapp3.1\ - $(PackagingTaskAssembly)net472\ - $(PackagingTaskAssembly)Microsoft.DotNet.Build.Tasks.Packaging.dll - - + + @@ -31,7 +22,6 @@ ones that might do this. After we ship a stable set of packages this target should be ran and the changes to the package index should be commited to the repo. --> - From 04ad80aff37e2ef5eac0cc6d2a895ccf0f9d65a3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 09:53:13 +0000 Subject: [PATCH 037/926] [main] Update dependencies from 9 repositories (#54218) [main] Update dependencies from 9 repositories - Merge branch 'main' into darc-main-b0a81754-f267-416d-a8e2-cf56f8c1ee3e - PInvoke warnings fixes for OOB assemblies - Update testPackages.proj - Update testPackages.proj - Update dependencies from https://github.com/dotnet/arcade build 20210621.1 - Merge branch 'main' into darc-main-b0a81754-f267-416d-a8e2-cf56f8c1ee3e Conflicts: eng/Version.Details.xml eng/Versions.props - Fix xml - Update dependencies from https://github.com/dotnet/xharness build 20210622.2 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 203 +++++++++--------- eng/Versions.props | 88 ++++---- eng/common/generate-locproject.ps1 | 13 +- .../templates/job/source-index-stage1.yml | 4 + global.json | 10 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 20 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 26 +-- src/libraries/pkg/test/testPackages.proj | 2 +- 12 files changed, 198 insertions(+), 176 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 02e0543e98b..6428121a0fe 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21314.1", + "version": "1.0.0-prerelease.21322.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4177e280edf..e8995dca95d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,222 +1,221 @@ - + https://github.com/dotnet/icu - d7db669b70f4dd67ec001c192f9809c218cab88b + 59588c1257a842089d0b7df3bad1cdd69ac720e1 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/mono/linker - caeaf2a3fb3f636805fdd4881df4f9a539fff8f6 + c739a81ba553b00df1cb2f5b9974deae996b757a - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e - + https://github.com/dotnet/arcade - 85a65ea1fca1d0867f699fed44d191358270bf6a + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c https://github.com/dotnet/emsdk defa37b05c734e025292c5747664e970cd2ac444 - + https://github.com/dotnet/hotreload-utils - 25b814e010cd4796cedfbcce72a274c26928f496 + 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e diff --git a/eng/Versions.props b/eng/Versions.props index b62b9b4854c..3bc9edaa1b1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,28 +49,28 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 2.5.1-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 2.5.1-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 3.1.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 5.0.0 4.3.0 @@ -104,27 +104,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 4.5.4 4.5.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 6.0.0-beta.21314.1 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -148,9 +148,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21314.1 - 1.0.0-prerelease.21314.1 - 1.0.1-alpha.0.21311.1 + 1.0.0-prerelease.21322.2 + 1.0.0-prerelease.21322.2 + 1.0.1-alpha.0.21314.1 2.4.1 2.4.2 1.3.0 @@ -161,19 +161,19 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21310.3 + 6.0.100-preview.6.21317.4 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.6.21307.1 + 6.0.0-preview.7.21315.3 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 6.0.0-preview.6.21275.1 $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index de348a2e225..25e97ac0077 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -25,8 +25,15 @@ Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to # Template files $jsonFiles = @() -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern +$jsonTemplateFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" + $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern $xlfFiles = @() @@ -44,7 +51,7 @@ $langXlfFiles | ForEach-Object { $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru } -$locFiles = $jsonFiles + $xlfFiles +$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles $locJson = @{ Projects = @( diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index 6e8aa9f7f21..b58d42364b9 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -7,9 +7,13 @@ parameters: binlogPath: artifacts/log/Debug/Build.binlog pool: vmImage: vs2017-win2016 + condition: '' + dependsOn: '' jobs: - job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} variables: - name: SourceIndexPackageVersion value: ${{ parameters.sourceIndexPackageVersion }} diff --git a/global.json b/global.json index df8f7ead637..433797e8116 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.6.21314.1" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" } } diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 533fc1cd0ed..0504df00e76 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:Interop.Odbc.SQLSetConnectAttrW(System.Data.Odbc.OdbcConnectionHandle,System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) + M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) ILLink diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 2e52a33c8a9..5386e4bd294 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,25 @@ ILLink IL2050 member - M:System.Data.Common.UnsafeNativeMethods.GetErrorInfo(System.Int32,System.Data.Common.UnsafeNativeMethods.IErrorInfo@) + M:System.Data.OleDb.DBPropSet.SetLastErrorInfo(System.Data.OleDb.OleDbHResult) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbConnection.ProcessResults(System.Data.OleDb.OleDbHResult,System.Data.OleDb.OleDbConnection,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillClose(System.Boolean,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillFromADODB(System.Object,System.Object,System.String,System.Boolean) ILLink diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml index ae9db1185dd..7572cc7074a 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml index 0e87e736269..ca9681a8e8f 100644 --- a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.Interop.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index d5734aab1a2..3f367cccd24 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -5,67 +5,61 @@ ILLink IL2050 member - M:System.Drawing.Icon.OleCreatePictureIndirect(System.Drawing.Icon.PICTDESC,System.Guid@,System.Boolean) + M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Icon.Save(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateMetafileFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream,System.IntPtr) + M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.RectangleF@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.IntPtr,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStreamI(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.Rectangle@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) - - - ILLink - IL2050 - member - M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) + M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 03492c30ee0..83ad6233b64 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -75,7 +75,7 @@ - + From 56778f0b3bc724fe4dae87e626ebc1403db99bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emek=20Vysok=C3=BD?= Date: Tue, 22 Jun 2021 13:20:02 +0200 Subject: [PATCH 038/926] Revert "[main] Update dependencies from 9 repositories (#54218)" (#54541) This reverts commit 04ad80aff37e2ef5eac0cc6d2a895ccf0f9d65a3. --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 203 +++++++++--------- eng/Versions.props | 88 ++++---- eng/common/generate-locproject.ps1 | 13 +- .../templates/job/source-index-stage1.yml | 4 - global.json | 10 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 20 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 26 ++- src/libraries/pkg/test/testPackages.proj | 2 +- 12 files changed, 176 insertions(+), 198 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 6428121a0fe..02e0543e98b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21322.2", + "version": "1.0.0-prerelease.21314.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e8995dca95d..4177e280edf 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,221 +1,222 @@ - + https://github.com/dotnet/icu - 59588c1257a842089d0b7df3bad1cdd69ac720e1 + d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 + - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 4a2b475948d498b89fedef7cf890883f49bc1ea3 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 0612b036e67746930105231b605c4df9ac6ed47e - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + af5c238556e204583b129cc8f5c7338f84dc2c40 - + https://github.com/mono/linker - c739a81ba553b00df1cb2f5b9974deae996b757a + caeaf2a3fb3f636805fdd4881df4f9a539fff8f6 - + https://github.com/dotnet/xharness - 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e + d6f8a4ad30908fb210390380eae97264e4fbe8ce - + https://github.com/dotnet/xharness - 0bb4a23b1b686e8fefde9d4c860b26a8fafc303e + d6f8a4ad30908fb210390380eae97264e4fbe8ce - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 85a65ea1fca1d0867f699fed44d191358270bf6a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f https://github.com/dotnet/emsdk defa37b05c734e025292c5747664e970cd2ac444 - + https://github.com/dotnet/hotreload-utils - 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d + 25b814e010cd4796cedfbcce72a274c26928f496 - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e diff --git a/eng/Versions.props b/eng/Versions.props index 3bc9edaa1b1..b62b9b4854c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,28 +49,28 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 2.5.1-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 2.5.1-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 + 6.0.0-beta.21311.3 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 + 6.0.0-preview.6.21314.1 3.1.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 5.0.0 4.3.0 @@ -104,27 +104,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 + 6.0.0-preview.6.21314.1 4.5.4 4.5.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.6.21314.1 6.0.0-beta.21314.1 6.0.0-beta.21307.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21307.1 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21313.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -148,9 +148,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21322.2 - 1.0.0-prerelease.21322.2 - 1.0.1-alpha.0.21314.1 + 1.0.0-prerelease.21314.1 + 1.0.0-prerelease.21314.1 + 1.0.1-alpha.0.21311.1 2.4.1 2.4.2 1.3.0 @@ -161,19 +161,19 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21317.4 + 6.0.100-preview.6.21310.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21315.3 + 6.0.0-preview.6.21307.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21308.1 6.0.0-preview.6.21275.1 $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index 25e97ac0077..de348a2e225 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -25,15 +25,8 @@ Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to # Template files $jsonFiles = @() -$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern -$jsonTemplateFiles | ForEach-Object { - $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json - - $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" - $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru -} - -$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern $xlfFiles = @() @@ -51,7 +44,7 @@ $langXlfFiles | ForEach-Object { $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru } -$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles +$locFiles = $jsonFiles + $xlfFiles $locJson = @{ Projects = @( diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b58d42364b9..6e8aa9f7f21 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -7,13 +7,9 @@ parameters: binlogPath: artifacts/log/Debug/Build.binlog pool: vmImage: vs2017-win2016 - condition: '' - dependsOn: '' jobs: - job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} variables: - name: SourceIndexPackageVersion value: ${{ parameters.sourceIndexPackageVersion }} diff --git a/global.json b/global.json index 433797e8116..df8f7ead637 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21311.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21311.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.6.21314.1" } } diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 0504df00e76..533fc1cd0ed 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) + M:Interop.Odbc.SQLSetConnectAttrW(System.Data.Odbc.OdbcConnectionHandle,System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) ILLink diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 5386e4bd294..2e52a33c8a9 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -5,25 +5,7 @@ ILLink IL2050 member - M:System.Data.OleDb.DBPropSet.SetLastErrorInfo(System.Data.OleDb.OleDbHResult) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbConnection.ProcessResults(System.Data.OleDb.OleDbHResult,System.Data.OleDb.OleDbConnection,System.Object) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbDataAdapter.FillClose(System.Boolean,System.Object) - - - ILLink - IL2050 - member - M:System.Data.OleDb.OleDbDataAdapter.FillFromADODB(System.Object,System.Object,System.String,System.Boolean) + M:System.Data.Common.UnsafeNativeMethods.GetErrorInfo(System.Int32,System.Data.Common.UnsafeNativeMethods.IErrorInfo@) ILLink diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml index 7572cc7074a..ae9db1185dd 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml index ca9681a8e8f..0e87e736269 100644 --- a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.Interop.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index 3f367cccd24..d5734aab1a2 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -5,61 +5,67 @@ ILLink IL2050 member - M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) + M:System.Drawing.Icon.OleCreatePictureIndirect(System.Drawing.Icon.PICTDESC,System.Guid@,System.Boolean) ILLink IL2050 member - M:System.Drawing.Icon.Save(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateMetafileFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) + M:System.Drawing.SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream,System.IntPtr) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.RectangleF@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.IntPtr,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) ILLink IL2050 member - M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) + M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStreamI(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.Rectangle@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + + + ILLink + IL2050 + member + M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 83ad6233b64..03492c30ee0 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -75,7 +75,7 @@ - + From fe89236891914593e6181102c80f5c36561e1753 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Tue, 22 Jun 2021 15:02:45 +0200 Subject: [PATCH 039/926] [WIP][wasm][testing] remote loop network tests via xharness and websocket (#54289) Makes it possible to run LoopbackServer tests with WASM - conditional compilation of LoopbackServer via #if TARGET_BROWSER - minimal implementation of WebSocketStream for the unit tests - simple SocketWrapper class to abstract Socket and WebSocket close/dispose/shutdown - added handling of CORS headers and pre-flight requests as necessary - new xharness web server middleware - adding it to helix payload --- .../tests/System/Net/Configuration.Http.cs | 4 + .../System/Net/Http/GenericLoopbackServer.cs | 52 +++- .../Net/Http/Http2LoopbackConnection.cs | 12 +- .../System/Net/Http/Http2LoopbackServer.cs | 7 +- .../System/Net/Http/Http3LoopbackServer.cs | 2 +- .../Net/Http/HttpAgnosticLoopbackServer.cs | 10 +- .../HttpClientHandlerTest.Authentication.cs | 10 - .../HttpClientHandlerTest.Cancellation.cs | 1 + .../Net/Http/HttpClientHandlerTest.Cookies.cs | 16 + .../HttpClientHandlerTest.Decompression.cs | 8 +- .../System/Net/Http/HttpClientHandlerTest.cs | 151 +++++++--- .../tests/System/Net/Http/LoopbackServer.cs | 152 ++++++++-- .../System/Net/Http/ResponseStreamTest.cs | 5 +- .../Net/Prerequisites/LocalEchoServer.props | 7 +- .../Net/Prerequisites/NetCoreServer.sln | 25 -- .../Net/Prerequisites/NetTestServers.sln | 30 ++ .../RemoteLoopServer/GenericHandler.cs | 47 +++ .../Handlers/RemoteLoopHandler.cs | 161 ++++++++++ .../Prerequisites/RemoteLoopServer/Program.cs | 23 ++ .../RemoteLoopServer/RemoteLoopServer.csproj | 15 + .../Prerequisites/RemoteLoopServer/Startup.cs | 23 ++ .../appsettings.Development.json | 9 + .../RemoteLoopServer/appsettings.json | 8 + .../System/Net/WebSockets/WebSocketStream.cs | 285 ++++++++++++++++++ .../HttpClientHandlerTest.AltSvc.cs | 2 +- .../HttpClientHandlerTest.Headers.cs | 33 +- .../HttpClientHandlerTest.Http1.cs | 1 - .../HttpClientHandlerTest.RequestRetry.cs | 1 - .../HttpClientMiniStressTest.cs | 1 + .../tests/FunctionalTests/HttpClientTest.cs | 42 +-- .../tests/FunctionalTests/HttpContentTest.cs | 21 +- .../FunctionalTests/HttpRequestMessageTest.cs | 10 +- .../FunctionalTests/SocketsHttpHandlerTest.cs | 49 ++- .../tests/FunctionalTests/SocksProxyTest.cs | 6 +- .../System.Net.Http.Functional.Tests.csproj | 5 + .../tests/CloseTest.cs | 2 +- .../tests/ConnectTest.cs | 10 +- .../tests/DeflateTests.cs | 1 - .../tests/SendReceiveTest.cs | 2 +- .../System.Net.WebSockets.Client.Tests.csproj | 5 + src/libraries/sendtohelixhelp.proj | 2 + 41 files changed, 1028 insertions(+), 228 deletions(-) delete mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json create mode 100644 src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json create mode 100644 src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs diff --git a/src/libraries/Common/tests/System/Net/Configuration.Http.cs b/src/libraries/Common/tests/System/Net/Configuration.Http.cs index e43c2c0e385..ae1481c5606 100644 --- a/src/libraries/Common/tests/System/Net/Configuration.Http.cs +++ b/src/libraries/Common/tests/System/Net/Configuration.Http.cs @@ -45,12 +45,15 @@ namespace System.Net.Test.Common public static string EchoClientCertificateRemoteServer => GetValue("DOTNET_TEST_HTTPHOST_ECHOCLIENTCERT", "https://corefx-net-tls.azurewebsites.net/EchoClientCertificate.ashx"); public static string Http2ForceUnencryptedLoopback => GetValue("DOTNET_TEST_HTTP2_FORCEUNENCRYPTEDLOOPBACK"); + public static string RemoteLoopHost => GetValue("DOTNET_TEST_REMOTE_LOOP_HOST"); + private const string EchoHandler = "Echo.ashx"; private const string EmptyContentHandler = "EmptyContent.ashx"; private const string RedirectHandler = "Redirect.ashx"; private const string VerifyUploadHandler = "VerifyUpload.ashx"; private const string DeflateHandler = "Deflate.ashx"; private const string GZipHandler = "GZip.ashx"; + private const string RemoteLoopHandler = "RemoteLoop"; public static readonly Uri RemoteEchoServer = new Uri("http://" + Host + "/" + EchoHandler); public static readonly Uri SecureRemoteEchoServer = new Uri("https://" + SecureHost + "/" + EchoHandler); @@ -67,6 +70,7 @@ namespace System.Net.Test.Common public static readonly Uri RemoteGZipServer = new Uri("http://" + Host + "/" + GZipHandler); public static readonly Uri Http2RemoteDeflateServer = new Uri("https://" + Http2Host + "/" + DeflateHandler); public static readonly Uri Http2RemoteGZipServer = new Uri("https://" + Http2Host + "/" + GZipHandler); + public static Uri RemoteLoopServer => new Uri("ws://" + RemoteLoopHost + "/" + RemoteLoopHandler); public static readonly object[][] EchoServers = EchoServerList.Select(x => new object[] { x }).ToArray(); public static readonly object[][] VerifyUploadServers = { new object[] { RemoteVerifyUploadServer }, new object[] { SecureRemoteVerifyUploadServer }, new object[] { Http2RemoteVerifyUploadServer } }; diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs index d3095bd7a20..c145b6aa3d4 100644 --- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs @@ -9,6 +9,8 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.IO; using System.Net.Sockets; +using System.Net.WebSockets; +using System.Threading; namespace System.Net.Test.Common { @@ -20,7 +22,7 @@ namespace System.Net.Test.Common public abstract GenericLoopbackServer CreateServer(GenericLoopbackOptions options = null); public abstract Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null); - public abstract Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null); + public abstract Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null); public abstract Version Version { get; } @@ -59,6 +61,54 @@ namespace System.Net.Test.Common } } + public sealed class SocketWrapper : IDisposable + { + private Socket _socket; + private WebSocket _websocket; + + public SocketWrapper(Socket socket) + { + _socket = socket; + } + public SocketWrapper(WebSocket websocket) + { + _websocket = websocket; + } + + public void Dispose() + { + _socket?.Dispose(); + _websocket?.Dispose(); + } + public void Close() + { + _socket?.Close(); + CloseWebSocket(); + } + + public void Shutdown(SocketShutdown how) + { + _socket?.Shutdown(how); + CloseWebSocket(); + } + + private void CloseWebSocket() + { + if (_websocket != null && (_websocket.State == WebSocketState.Open || _websocket.State == WebSocketState.Connecting || _websocket.State == WebSocketState.None)) + { + try + { + var task = _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None); + // Block and wait for the task to complete synchronously + Task.WaitAll(task); + } + catch (Exception) + { + } + } + } + } + public abstract class GenericLoopbackConnection : IDisposable { public abstract void Dispose(); diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs index 01fa9d4e697..5dcbc2d3863 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs @@ -20,7 +20,7 @@ namespace System.Net.Test.Common { public const string Http2Prefix = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; - private Socket _connectionSocket; + private SocketWrapper _connectionSocket; private Stream _connectionStream; private TaskCompletionSource _ignoredSettingsAckPromise; private bool _ignoreWindowUpdates; @@ -34,19 +34,19 @@ namespace System.Net.Test.Common public Stream Stream => _connectionStream; public Task SettingAckWaiter => _ignoredSettingsAckPromise?.Task; - private Http2LoopbackConnection(Socket socket, Stream stream, TimeSpan timeout) + private Http2LoopbackConnection(SocketWrapper socket, Stream stream, TimeSpan timeout) { _connectionSocket = socket; _connectionStream = stream; _timeout = timeout; } - public static Task CreateAsync(Socket socket, Stream stream, Http2Options httpOptions) + public static Task CreateAsync(SocketWrapper socket, Stream stream, Http2Options httpOptions) { return CreateAsync(socket, stream, httpOptions, Http2LoopbackServer.Timeout); } - public static async Task CreateAsync(Socket socket, Stream stream, Http2Options httpOptions, TimeSpan timeout) + public static async Task CreateAsync(SocketWrapper socket, Stream stream, Http2Options httpOptions, TimeSpan timeout) { if (httpOptions.UseSsl) { @@ -230,9 +230,9 @@ namespace System.Net.Test.Common } // Reset and return underlying networking objects. - public (Socket, Stream) ResetNetwork() + public (SocketWrapper, Stream) ResetNetwork() { - Socket oldSocket = _connectionSocket; + SocketWrapper oldSocket = _connectionSocket; Stream oldStream = _connectionStream; _connectionSocket = null; _connectionStream = null; diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs index 45c2edf3459..45df14c0380 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs @@ -91,9 +91,10 @@ namespace System.Net.Test.Common Socket connectionSocket = await _listenSocket.AcceptAsync().ConfigureAwait(false); var stream = new NetworkStream(connectionSocket, ownsSocket: true); + var wrapper = new SocketWrapper(connectionSocket); Http2LoopbackConnection connection = - timeout != null ? await Http2LoopbackConnection.CreateAsync(connectionSocket, stream, _options, timeout.Value).ConfigureAwait(false) : - await Http2LoopbackConnection.CreateAsync(connectionSocket, stream, _options).ConfigureAwait(false); + timeout != null ? await Http2LoopbackConnection.CreateAsync(wrapper, stream, _options, timeout.Value).ConfigureAwait(false) : + await Http2LoopbackConnection.CreateAsync(wrapper, stream, _options).ConfigureAwait(false); _connections.Add(connection); return connection; @@ -201,7 +202,7 @@ namespace System.Net.Test.Common return Http2LoopbackServer.CreateServer(CreateOptions(options)); } - public override async Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override async Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { return await Http2LoopbackConnection.CreateAsync(socket, stream, CreateOptions(options)).ConfigureAwait(false); } diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index cf83b893ff7..b84393a88b1 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -97,7 +97,7 @@ namespace System.Net.Test.Common await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); } - public override Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { // TODO: make a new overload that takes a MultiplexedConnection. // This method is always unacceptable to call for HTTP/3. diff --git a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs index ae436df429a..88a8071153e 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpAgnosticLoopbackServer.cs @@ -87,13 +87,13 @@ namespace System.Net.Test.Common if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2) { // Do not pass original options so the CreateConnectionAsync won't try to do ALPN again. - return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http11 || sslStream.NegotiatedApplicationProtocol == default) { // Do not pass original options so the CreateConnectionAsync won't try to do ALPN again. - return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else { @@ -103,11 +103,11 @@ namespace System.Net.Test.Common if (_options.ClearTextVersion == HttpVersion.Version11) { - return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http11LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else if (_options.ClearTextVersion == HttpVersion.Version20) { - return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(socket, stream, options).ConfigureAwait(false); + return connection = await Http2LoopbackServerFactory.Singleton.CreateConnectionAsync(new SocketWrapper(socket), stream, options).ConfigureAwait(false); } else { @@ -187,7 +187,7 @@ namespace System.Net.Test.Common return HttpAgnosticLoopbackServer.CreateServer(CreateOptions(options)); } - public override Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { // This method is always unacceptable to call for an agnostic server. throw new NotImplementedException("HttpAgnosticLoopbackServerFactory cannot create connection."); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs index 4df4100a8ed..f7ccc3127e9 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs @@ -104,7 +104,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(Authentication_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_Authentication_Succeeds(string authenticateHeader, bool result) { if (PlatformDetection.IsWindowsNanoServer) @@ -144,7 +143,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\n")] [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: Basic realm=\"hello\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_MultipleAuthenticateHeaders_Succeeds(string authenticateHeader) { if (PlatformDetection.IsWindowsNanoServer) @@ -164,7 +162,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: NTLM\r\n", "Basic", "Negotiate")] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: NTLM\r\n", "Digest", "Negotiate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth) { if (PlatformDetection.IsWindowsNanoServer) @@ -190,7 +187,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\n")] [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task HttpClientHandler_IncorrectCredentials_Fails(string authenticateHeader) { var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password }; @@ -228,7 +224,6 @@ namespace System.Net.Http.Functional.Tests [InlineData("NTLM")] [InlineData("Kerberos")] [InlineData("Negotiate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_NoPreviousAuthenticatedRequests_NoCredentialsSent(string credCacheScheme) { const int NumRequests = 3; @@ -271,7 +266,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(null, "WWW-Authenticate: Basic realm=\"hello\"\r\n")] [InlineData("Basic", "WWW-Authenticate: Basic realm=\"hello\"\r\n")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_FirstRequestNoHeaderAndAuthenticates_SecondRequestPreauthenticates(string credCacheScheme, string authResponse) { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -364,7 +358,6 @@ namespace System.Net.Http.Functional.Tests [InlineData((HttpStatusCode)508)] // LoopDetected [InlineData((HttpStatusCode)510)] // NotExtended [InlineData((HttpStatusCode)511)] // NetworkAuthenticationRequired - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_FirstRequestNoHeader_SecondRequestVariousStatusCodes_ThirdRequestPreauthenticates(HttpStatusCode statusCode) { const string AuthResponse = "WWW-Authenticate: Basic realm=\"hello\"\r\n"; @@ -408,7 +401,6 @@ namespace System.Net.Http.Functional.Tests [InlineData("/something/hello.html", "/world.html", false)] [InlineData("/something/hello.html", "/another/", false)] [InlineData("/something/hello.html", "/another/hello.html", false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_AuthenticatedUrl_ThenTryDifferentUrl_SendsAuthHeaderOnlyIfPrefixMatches( string originalRelativeUri, string secondRelativeUri, bool expectedAuthHeader) { @@ -448,7 +440,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_SuccessfulBasicButThenFails_DoesntLoopInfinitely() { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -487,7 +478,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task PreAuthenticate_SuccessfulBasic_ThenDigestChallenged() { if (IsWinHttpHandler) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index d246ca53e93..410d2a6987b 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -329,6 +329,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "MaxConnectionsPerServer is not supported on Browser")] public async Task MaxConnectionsPerServer_WaitingConnectionsAreCancelable() { if (LoopbackServerFactory.Version >= HttpVersion20.Value) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index e722e8bb37c..dbb1382c9aa 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -63,6 +63,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(CookieNamesValuesAndUseCookies))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainer_CookieSent(string cookieName, string cookieValue, bool useCookies) { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -92,6 +93,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerMultipleCookies_CookiesSent() { var cookies = new Cookie[] @@ -211,6 +213,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndCookieHeader_BothCookiesSent() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -238,6 +241,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndMultipleCookieHeaders_BothCookiesSent() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -289,6 +293,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithRedirect_SetCookieContainer_CorrectCookiesSent() { const string path1 = "/foo"; @@ -329,6 +334,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(CookieNamesValuesAndUseCookies))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieAdded(string cookieName, string cookieValue, bool useCookies) { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -360,6 +366,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveMultipleSetCookieHeaders_CookieAdded() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -399,6 +406,7 @@ namespace System.Net.Http.Functional.Tests // the cookie should be added with Path=/path. // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_NoPathDefined_CookieAddedWithDefaultPath() { await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => @@ -428,6 +436,7 @@ namespace System.Net.Http.Functional.Tests // these cookies should be accepted by the client. // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_CookiePathDoesNotMatchRequestPath_CookieAccepted() { await LoopbackServerFactory.CreateServerAsync(async (server, serverUrl) => @@ -459,6 +468,7 @@ namespace System.Net.Http.Functional.Tests // https://github.com/dotnet/runtime/issues/26141#issuecomment-612097147 // ConditionalFact: CookieContainer does not follow RFC6265 on .NET Framework, therefore the (WinHttpHandler) test is expected to fail [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_Redirect_CookiesArePreserved() { HttpClientHandler handler = CreateHttpClientHandler(); @@ -501,6 +511,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieUpdated() { const string newCookieValue = "789"; @@ -528,6 +539,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveSetCookieHeader_CookieRemoved() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -551,6 +563,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveInvalidSetCookieHeader_ValidCookiesAdded() { await LoopbackServerFactory.CreateServerAsync(async (server, url) => @@ -585,6 +598,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithRedirect_ReceiveSetCookie_CookieSent() { const string path1 = "/foo"; @@ -638,6 +652,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsyncWithBasicAuth_ReceiveSetCookie_CookieSent() { if (IsWinHttpHandler) @@ -757,6 +772,7 @@ namespace System.Net.Http.Functional.Tests public HttpClientHandlerTest_Cookies_Http11(ITestOutputHelper output) : base(output) { } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_ReceiveMultipleSetCookieHeaders_CookieAdded() { await LoopbackServer.CreateServerAsync(async (server, url) => diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index 6cf46633cc6..f53b6fcb910 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -46,7 +46,7 @@ namespace System.Net.Http.Functional.Tests [InlineData("deflate", true)] [InlineData("br", false)] [InlineData("br", true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(string encodingName, bool all) { Func compress; @@ -136,7 +136,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(DecompressedResponse_MethodNotSpecified_OriginalContentReturned_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task DecompressedResponse_MethodNotSpecified_OriginalContentReturned( string encodingName, Func compress, DecompressionMethods methods) { @@ -263,7 +263,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(DecompressionMethods.Deflate, "deflate", "gzip")] [InlineData(DecompressionMethods.Deflate, "deflate", "br")] [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip, deflate", "gzip, deflate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task GetAsync_SetAutomaticDecompression_AcceptEncodingHeaderSentWithNoDuplicates( DecompressionMethods methods, string encodings, @@ -316,7 +316,7 @@ namespace System.Net.Http.Functional.Tests #endif [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip; q=1.0, deflate; q=1.0", "")] [InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip; q=1.0", "deflate")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] public async Task GetAsync_SetAutomaticDecompression_AcceptEncodingHeaderSentWithQualityWeightingsNoDuplicates( DecompressionMethods methods, string manualAcceptEncodingHeaderValues, diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 06e80b66b00..33392864210 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -39,7 +39,7 @@ namespace System.Net.Http.Functional.Tests } } - [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public void Ctor_ExpectedDefaultPropertyValues_CommonPlatform() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -62,6 +62,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "MaxResponseHeadersLength is not supported on Browser")] public void Ctor_ExpectedDefaultPropertyValues() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -79,6 +80,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public void Credentials_SetGet_Roundtrips() { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -99,6 +101,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(-1)] [InlineData(0)] + [SkipOnPlatform(TestPlatforms.Browser, "MaxAutomaticRedirections not supported on Browser")] public void MaxAutomaticRedirections_InvalidValue_Throws(int redirects) { using (HttpClientHandler handler = CreateHttpClientHandler()) @@ -148,6 +151,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "ServerCertificateCustomValidationCallback not supported on Browser")] public async Task GetAsync_IPv6LinkLocalAddressUri_Success() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -195,7 +199,10 @@ namespace System.Net.Http.Functional.Tests } using HttpClientHandler handler = CreateHttpClientHandler(); - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + if (PlatformDetection.IsNotBrowser) + { + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + } using HttpClient client = CreateHttpClient(handler); @@ -224,6 +231,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("[::1234]")] [InlineData("[::1234]:8080")] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")] public async Task GetAsync_IPv6AddressInHostHeader_CorrectlyFormatted(string host) { string ipv6Address = "http://" + host; @@ -254,7 +262,9 @@ namespace System.Net.Http.Functional.Tests public static IEnumerable SecureAndNonSecure_IPBasedUri_MemberData() => from address in new[] { IPAddress.Loopback, IPAddress.IPv6Loopback } - from useSsl in BoolValues + from useSsl in BoolValues + // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] + where PlatformDetection.IsNotBrowser || !useSsl select new object[] { address, useSsl }; [Theory] @@ -267,7 +277,7 @@ namespace System.Net.Http.Functional.Tests return; } - var options = new LoopbackServer.Options { Address = address, UseSsl= useSsl }; + var options = new LoopbackServer.Options { Address = address, UseSsl = useSsl }; bool connectionAccepted = false; string host = ""; @@ -277,8 +287,9 @@ namespace System.Net.Http.Functional.Tests using (HttpClientHandler handler = CreateHttpClientHandler()) using (HttpClient client = CreateHttpClient(handler)) { - if (useSsl) + if (useSsl && PlatformDetection.IsNotBrowser) { + // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } try { await client.GetAsync(url); } catch { } @@ -296,6 +307,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("WWW-Authenticate", "CustomAuth")] [InlineData("", "")] // RFC7235 requires servers to send this header with 401 but some servers don't. + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public async Task GetAsync_ServerNeedsNonStandardAuthAndSetCredential_StatusCodeUnauthorized(string authHeadrName, string authHeaderValue) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -328,6 +340,7 @@ namespace System.Net.Http.Functional.Tests [InlineData("nocolon")] [InlineData("no colon")] [InlineData("Content-Length ")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidHeaderNameValue_ThrowsHttpRequestException(string invalidHeader) { if (UseVersion == HttpVersion30) @@ -341,7 +354,7 @@ namespace System.Net.Http.Functional.Tests { await Assert.ThrowsAsync(() => client.GetStringAsync(uri)); } - }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{invalidHeader}\r\nContent-Length: 11\r\n\r\nhello world")); + }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{invalidHeader}\r\n{LoopbackServer.CorsHeaders}Content-Length: 11\r\n\r\nhello world")); } [Theory] @@ -349,6 +362,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(true, false)] [InlineData(false, true)] [InlineData(true, true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_IncompleteData_ThrowsHttpRequestException(bool failDuringHeaders, bool getString) { if (IsWinHttpHandler) @@ -372,8 +386,8 @@ namespace System.Net.Http.Functional.Tests } }, server => failDuringHeaders ? - server.AcceptConnectionSendCustomResponseAndCloseAsync("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n") : - server.AcceptConnectionSendCustomResponseAndCloseAsync("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhe")); + server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{LoopbackServer.CorsHeaders}Content-Length: 5\r\n") : + server.AcceptConnectionSendCustomResponseAndCloseAsync($"HTTP/1.1 200 OK\r\n{LoopbackServer.CorsHeaders}Content-Length: 5\r\n\r\nhe")); } [Fact] @@ -396,10 +410,13 @@ namespace System.Net.Http.Functional.Tests var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = new ByteArrayContent(contentArray), Version = UseVersion }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - request.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); - request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); - request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); - request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US")); + if (PlatformDetection.IsNotBrowser) + { + request.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); + request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); + request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); + request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US")); + } request.Headers.Add("Accept-Datetime", "Thu, 31 May 2007 20:35:00 GMT"); request.Headers.Add("Access-Control-Request-Method", "GET"); request.Headers.Add("Access-Control-Request-Headers", "GET"); @@ -409,7 +426,10 @@ namespace System.Net.Http.Functional.Tests request.Headers.Connection.Add("close"); request.Headers.Add("Cookie", "$Version=1; Skin=new"); request.Content.Headers.ContentLength = contentArray.Length; - request.Content.Headers.ContentMD5 = MD5.Create().ComputeHash(contentArray); + if (PlatformDetection.IsNotBrowser) + { + request.Content.Headers.ContentMD5 = MD5.Create().ComputeHash(contentArray); + } request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); request.Headers.Date = DateTimeOffset.Parse("Tue, 15 Nov 1994 08:12:31 GMT"); request.Headers.Expect.Add(new NameValueWithParametersHeaderValue("100-continue")); @@ -431,11 +451,14 @@ namespace System.Net.Http.Functional.Tests request.Headers.TE.Add(new TransferCodingWithQualityHeaderValue("deflate")); request.Headers.Trailer.Add("MyTrailer"); request.Headers.TransferEncoding.Add(new TransferCodingHeaderValue("chunked")); - request.Headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("Mozilla", "5.0"))); - request.Headers.Upgrade.Add(new ProductHeaderValue("HTTPS", "1.3")); - request.Headers.Upgrade.Add(new ProductHeaderValue("IRC", "6.9")); - request.Headers.Upgrade.Add(new ProductHeaderValue("RTA", "x11")); - request.Headers.Upgrade.Add(new ProductHeaderValue("websocket")); + if (PlatformDetection.IsNotBrowser) + { + request.Headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("Mozilla", "5.0"))); + request.Headers.Upgrade.Add(new ProductHeaderValue("HTTPS", "1.3")); + request.Headers.Upgrade.Add(new ProductHeaderValue("IRC", "6.9")); + request.Headers.Upgrade.Add(new ProductHeaderValue("RTA", "x11")); + request.Headers.Upgrade.Add(new ProductHeaderValue("websocket")); + } request.Headers.Via.Add(new ViaHeaderValue("1.0", "fred")); request.Headers.Via.Add(new ViaHeaderValue("1.1", "example.com", null, "(Apache/1.1)")); request.Headers.Warning.Add(new WarningHeaderValue(199, "-", "\"Miscellaneous warning\"")); @@ -471,18 +494,31 @@ namespace System.Net.Http.Functional.Tests Assert.Equal(content, Encoding.ASCII.GetString(requestData.Body)); - Assert.Equal("utf-8", requestData.GetSingleHeaderValue("Accept-Charset")); - Assert.Equal("gzip, deflate", requestData.GetSingleHeaderValue("Accept-Encoding")); - Assert.Equal("en-US", requestData.GetSingleHeaderValue("Accept-Language")); - Assert.Equal("Thu, 31 May 2007 20:35:00 GMT", requestData.GetSingleHeaderValue("Accept-Datetime")); - Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Method")); - Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Headers")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal("utf-8", requestData.GetSingleHeaderValue("Accept-Charset")); + Assert.Equal("gzip, deflate", requestData.GetSingleHeaderValue("Accept-Encoding")); + Assert.Equal("en-US", requestData.GetSingleHeaderValue("Accept-Language")); + Assert.Equal("Thu, 31 May 2007 20:35:00 GMT", requestData.GetSingleHeaderValue("Accept-Datetime")); + Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Method")); + Assert.Equal("GET", requestData.GetSingleHeaderValue("Access-Control-Request-Headers")); + } Assert.Equal("12", requestData.GetSingleHeaderValue("Age")); Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Authorization")); Assert.Equal("no-cache", requestData.GetSingleHeaderValue("Cache-Control")); - Assert.Equal("$Version=1; Skin=new", requestData.GetSingleHeaderValue("Cookie")); - Assert.Equal("Tue, 15 Nov 1994 08:12:31 GMT", requestData.GetSingleHeaderValue("Date")); - Assert.Equal("100-continue", requestData.GetSingleHeaderValue("Expect")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal("$Version=1; Skin=new", requestData.GetSingleHeaderValue("Cookie")); + Assert.Equal("Tue, 15 Nov 1994 08:12:31 GMT", requestData.GetSingleHeaderValue("Date")); + Assert.Equal("100-continue", requestData.GetSingleHeaderValue("Expect")); + Assert.Equal("http://www.example-social-network.com", requestData.GetSingleHeaderValue("Origin")); + Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Proxy-Authorization")); + Assert.Equal("Mozilla/5.0", requestData.GetSingleHeaderValue("User-Agent")); + Assert.Equal("http://en.wikipedia.org/wiki/Main_Page", requestData.GetSingleHeaderValue("Referer")); + Assert.Equal("MyTrailer", requestData.GetSingleHeaderValue("Trailer")); + Assert.Equal("1.0 fred, 1.1 example.com (Apache/1.1)", requestData.GetSingleHeaderValue("Via")); + Assert.Equal("1 (Do Not Track Enabled)", requestData.GetSingleHeaderValue("DNT")); + } Assert.Equal("for=192.0.2.60;proto=http;by=203.0.113.43", requestData.GetSingleHeaderValue("Forwarded")); Assert.Equal("User Name ", requestData.GetSingleHeaderValue("From")); Assert.Equal("\"37060cd8c284d8af7ad3082f209582d\"", requestData.GetSingleHeaderValue("If-Match")); @@ -491,17 +527,10 @@ namespace System.Net.Http.Functional.Tests Assert.Equal("Wed, 21 Oct 2015 07:28:00 GMT", requestData.GetSingleHeaderValue("If-Range")); Assert.Equal("Sat, 29 Oct 1994 19:43:31 GMT", requestData.GetSingleHeaderValue("If-Unmodified-Since")); Assert.Equal("10", requestData.GetSingleHeaderValue("Max-Forwards")); - Assert.Equal("http://www.example-social-network.com", requestData.GetSingleHeaderValue("Origin")); Assert.Equal("no-cache", requestData.GetSingleHeaderValue("Pragma")); - Assert.Equal("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", requestData.GetSingleHeaderValue("Proxy-Authorization")); Assert.Equal("bytes=500-999", requestData.GetSingleHeaderValue("Range")); - Assert.Equal("http://en.wikipedia.org/wiki/Main_Page", requestData.GetSingleHeaderValue("Referer")); - Assert.Equal("MyTrailer", requestData.GetSingleHeaderValue("Trailer")); - Assert.Equal("Mozilla/5.0", requestData.GetSingleHeaderValue("User-Agent")); - Assert.Equal("1.0 fred, 1.1 example.com (Apache/1.1)", requestData.GetSingleHeaderValue("Via")); Assert.Equal("199 - \"Miscellaneous warning\"", requestData.GetSingleHeaderValue("Warning")); Assert.Equal("XMLHttpRequest", requestData.GetSingleHeaderValue("X-Requested-With")); - Assert.Equal("1 (Do Not Track Enabled)", requestData.GetSingleHeaderValue("DNT")); Assert.Equal("client1, proxy1, proxy2", requestData.GetSingleHeaderValue("X-Forwarded-For")); Assert.Equal("en.wikipedia.org:8080", requestData.GetSingleHeaderValue("X-Forwarded-Host")); Assert.Equal("https", requestData.GetSingleHeaderValue("X-Forwarded-Proto")); @@ -527,7 +556,7 @@ namespace System.Net.Http.Functional.Tests Assert.Equal(0, requestData.GetHeaderValueCount("Connection")); Assert.Equal(0, requestData.GetHeaderValueCount("Transfer-Encoding")); } - else + else if (PlatformDetection.IsNotBrowser) { // Verify HTTP/1.x headers Assert.Equal("close", requestData.GetSingleHeaderValue("Connection"), StringComparer.OrdinalIgnoreCase); // NetFxHandler uses "Close" vs "close" @@ -565,7 +594,10 @@ namespace System.Net.Http.Functional.Tests { Assert.Equal("1.1", resp.Version.ToString()); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); - Assert.Contains("*", resp.Headers.GetValues("Access-Control-Allow-Origin")); + if (PlatformDetection.IsNotBrowser) + { + Assert.Contains("*", resp.Headers.GetValues("Access-Control-Allow-Origin")); + } Assert.Contains("text/example;charset=utf-8", resp.Headers.GetValues("Accept-Patch")); Assert.Contains("bytes", resp.Headers.AcceptRanges); Assert.Equal(TimeSpan.FromSeconds(12), resp.Headers.Age.GetValueOrDefault()); @@ -581,7 +613,10 @@ namespace System.Net.Http.Functional.Tests Assert.Contains("gzip", resp.Content.Headers.ContentEncoding); Assert.Contains("da", resp.Content.Headers.ContentLanguage); Assert.Equal(new Uri("/index.htm", UriKind.Relative), resp.Content.Headers.ContentLocation); - Assert.Equal(Convert.FromBase64String("Q2hlY2sgSW50ZWdyaXR5IQ=="), resp.Content.Headers.ContentMD5); + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal(Convert.FromBase64String("Q2hlY2sgSW50ZWdyaXR5IQ=="), resp.Content.Headers.ContentMD5); + } Assert.Equal("bytes", resp.Content.Headers.ContentRange.Unit); Assert.Equal(21010, resp.Content.Headers.ContentRange.From.GetValueOrDefault()); Assert.Equal(47021, resp.Content.Headers.ContentRange.To.GetValueOrDefault()); @@ -600,7 +635,11 @@ namespace System.Net.Http.Functional.Tests Assert.Contains("max-age=2592000; pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"", resp.Headers.GetValues("Public-Key-Pins")); Assert.Equal(TimeSpan.FromSeconds(120), resp.Headers.RetryAfter.Delta.GetValueOrDefault()); Assert.Contains(new ProductInfoHeaderValue("Apache", "2.4.1"), resp.Headers.Server); - Assert.Contains("UserID=JohnDoe; Max-Age=3600; Version=1", resp.Headers.GetValues("Set-Cookie")); + + if (PlatformDetection.IsNotBrowser) + { + Assert.Contains("UserID=JohnDoe; Max-Age=3600; Version=1", resp.Headers.GetValues("Set-Cookie")); + } Assert.Contains("max-age=16070400; includeSubDomains", resp.Headers.GetValues("Strict-Transport-Security")); Assert.Contains("Max-Forwards", resp.Headers.Trailer); Assert.Contains("?", resp.Headers.GetValues("Tk")); @@ -627,6 +666,7 @@ namespace System.Net.Http.Functional.Tests }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( $"HTTP/1.1 200 OK{newline}" + $"Access-Control-Allow-Origin:{fold} *{newline}" + + $"Access-Control-Expose-Headers:{fold} *{newline}" + $"Accept-Patch:{fold} text/example;charset=utf-8{newline}" + $"Accept-Ranges:{fold} bytes{newline}" + $"Age: {fold}12{newline}" + @@ -682,6 +722,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_NonTraditionalChunkSizes_Accepted() { if (LoopbackServerFactory.Version >= HttpVersion20.Value) @@ -700,6 +741,7 @@ namespace System.Net.Http.Functional.Tests server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + "4 \r\n" + // whitespace after size @@ -733,6 +775,7 @@ namespace System.Net.Http.Functional.Tests [InlineData("xyz")] // non-hex [InlineData("7gibberish")] // valid size then gibberish [InlineData("7\v\f")] // unacceptable whitespace + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidChunkSize_ThrowsHttpRequestException(string chunkSize) { if (UseVersion != HttpVersion.Version11) @@ -745,6 +788,7 @@ namespace System.Net.Http.Functional.Tests using (HttpClient client = CreateHttpClient()) { string partialResponse = "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + $"{chunkSize}\r\n"; @@ -764,6 +808,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InvalidChunkTerminator_ThrowsHttpRequestException() { if (UseVersion != HttpVersion.Version11) @@ -780,6 +825,7 @@ namespace System.Net.Http.Functional.Tests }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\n" + @@ -791,6 +837,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_InfiniteChunkSize_ThrowsHttpRequestException() { if (UseVersion != HttpVersion.Version11) @@ -808,7 +855,7 @@ namespace System.Net.Http.Functional.Tests var tcs = new TaskCompletionSource(); Task serverTask = server.AcceptConnectionAsync(async connection => { - await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); + await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\n" + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n\r\n"); try { while (!cts.IsCancellationRequested) // infinite to make sure implementation doesn't OOM @@ -828,6 +875,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "CORS is required on Browser")] public async Task SendAsync_TransferEncodingSetButNoRequestContent_Throws() { var req = new HttpRequestMessage(HttpMethod.Post, "http://bing.com") { Version = UseVersion }; @@ -841,6 +889,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Slow response")] [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "This is blocking forever on Browser, there is infinite default timeout")] public async Task SendAsync_ReadFromSlowStreamingServer_PartialDataReturned() { if (UseVersion != HttpVersion.Version11) @@ -856,9 +905,14 @@ namespace System.Net.Http.Functional.Tests await server.AcceptConnectionAsync(async connection => { - await connection.ReadRequestHeaderAndSendCustomResponseAsync( + HttpRequestData requestData = await connection.ReadRequestDataAsync(); +#if TARGET_BROWSER + await connection.HandleCORSPreFlight(requestData); +#endif + await connection.WriteStringAsync( "HTTP/1.1 200 OK\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + LoopbackServer.CorsHeaders + "Content-Length: 16000\r\n" + "\r\n" + "less than 16000 bytes"); @@ -882,6 +936,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(true)] [InlineData(false)] [InlineData(null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1047,6 +1102,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1211,6 +1267,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(99)] [InlineData(1000)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task GetAsync_StatusCodeOutOfRange_ExpectedException(int statusCode) { if (UseVersion == HttpVersion30) @@ -1227,6 +1284,7 @@ namespace System.Net.Http.Functional.Tests await server.AcceptConnectionSendCustomResponseAndCloseAsync( $"HTTP/1.1 {statusCode}\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "\r\n"); @@ -1255,6 +1313,7 @@ namespace System.Net.Http.Functional.Tests #region Post Methods Tests [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53876", TestPlatforms.Browser)] public async Task GetAsync_ExpectContinueTrue_NoContent_StillSendsHeader() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1306,6 +1365,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(Interim1xxStatusCode))] + [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task SendAsync_1xxResponsesWithHeaders_InterimResponsesHeadersIgnored(HttpStatusCode responseStatusCode) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1480,6 +1540,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_Expect100Continue_RequestBodyFails_ThrowsContentException() { if (IsWinHttpHandler) @@ -1576,6 +1637,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "Switching protocol is not supported on Browser")] public async Task SendAsync_101SwitchingProtocolsResponse_Success() { // WinHttpHandler and CurlHandler will hang, waiting for additional response. @@ -1616,6 +1678,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task PostAsync_ThrowFromContentCopy_RequestFails(bool syncFailure) { if (UseVersion == HttpVersion30) @@ -1629,7 +1692,7 @@ namespace System.Net.Http.Functional.Tests Task responseTask = server.AcceptConnectionAsync(async connection => { var buffer = new byte[1000]; - while (await connection.Socket.ReceiveAsync(new ArraySegment(buffer, 0, buffer.Length), SocketFlags.None) != 0); + while (await connection.ReadAsync(new Memory(buffer), 0, buffer.Length) != 0) ; }); using (HttpClient client = CreateHttpClient()) @@ -1651,6 +1714,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(HttpStatusCode.MethodNotAllowed, "Custom description")] [InlineData(HttpStatusCode.MethodNotAllowed, "")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54163", TestPlatforms.Browser)] public async Task GetAsync_CallMethod_ExpectedStatusLine(HttpStatusCode statusCode, string reasonPhrase) { if (LoopbackServerFactory.Version >= HttpVersion20.Value) @@ -1668,13 +1732,14 @@ namespace System.Net.Http.Functional.Tests Assert.Equal(reasonPhrase, response.ReasonPhrase); } }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( - $"HTTP/1.1 {(int)statusCode} {reasonPhrase}\r\nContent-Length: 0\r\n\r\n")); + $"HTTP/1.1 {(int)statusCode} {reasonPhrase}\r\n{LoopbackServer.CorsHeaders}Content-Length: 0\r\n\r\n")); } #endregion #region Version tests + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersion10_ServerReceivesVersion10Request() { @@ -1688,6 +1753,7 @@ namespace System.Net.Http.Functional.Tests Assert.Equal(new Version(1, 0), receivedRequestVersion); } + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersion11_ServerReceivesVersion11Request() { @@ -1695,6 +1761,7 @@ namespace System.Net.Http.Functional.Tests Assert.Equal(new Version(1, 1), receivedRequestVersion); } + [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] [Fact] public async Task SendAsync_RequestVersionNotSpecified_ServerReceivesVersion11Request() { diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs index b44ae2d7f68..5685e79cdb6 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs @@ -11,6 +11,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Net.WebSockets; using Xunit; namespace System.Net.Test.Common @@ -20,55 +21,83 @@ namespace System.Net.Test.Common private static readonly byte[] s_newLineBytes = new byte[] { (byte)'\r', (byte)'\n' }; private static readonly byte[] s_colonSpaceBytes = new byte[] { (byte)':', (byte)' ' }; + private SocketWrapper _socketWrapper; +#if TARGET_BROWSER + private ClientWebSocket _listenSocket; +#else private Socket _listenSocket; +#endif private Options _options; private Uri _uri; public LoopbackServer(Options options = null) { _options = options ??= new Options(); + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public async Task ListenAsync() +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { try { - _listenSocket = new Socket(options.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - _listenSocket.Bind(new IPEndPoint(options.Address, 0)); - _listenSocket.Listen(options.ListenBacklog); + IPEndPoint localEndPoint; +#if TARGET_BROWSER + _listenSocket = new ClientWebSocket(); - var localEndPoint = (IPEndPoint)_listenSocket.LocalEndPoint; - string host = options.Address.AddressFamily == AddressFamily.InterNetworkV6 ? + await _listenSocket.ConnectAsync(Configuration.Http.RemoteLoopServer, CancellationToken.None); + + byte[] buffer = new byte[128 * 1024]; + var message = Encoding.ASCII.GetBytes($"{_options.ListenBacklog},{_options.Address}"); + await _listenSocket.SendAsync(message, WebSocketMessageType.Binary, true, CancellationToken.None); + var first = await _listenSocket.ReceiveAsync(buffer, CancellationToken.None); + localEndPoint = IPEndPoint.Parse(Encoding.ASCII.GetString(buffer, 0, first.Count)); +#else + _listenSocket = new Socket(_options.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _listenSocket.Bind(new IPEndPoint(_options.Address, 0)); + _listenSocket.Listen(_options.ListenBacklog); + localEndPoint = (IPEndPoint)_listenSocket.LocalEndPoint; +#endif + + string host = _options.Address.AddressFamily == AddressFamily.InterNetworkV6 ? $"[{localEndPoint.Address}]" : localEndPoint.Address.ToString(); - string scheme = options.UseSsl ? "https" : "http"; - if (options.WebSocketEndpoint) + string scheme = _options.UseSsl ? "https" : "http"; + if (_options.WebSocketEndpoint) { - scheme = options.UseSsl ? "wss" : "ws"; + scheme = _options.UseSsl ? "wss" : "ws"; } _uri = new Uri($"{scheme}://{host}:{localEndPoint.Port}/"); + _socketWrapper = new SocketWrapper(_listenSocket); } catch { _listenSocket?.Dispose(); + _socketWrapper?.Dispose(); throw; } } public override void Dispose() { - if (_listenSocket != null) + _listenSocket = null; + if (_socketWrapper != null) { - _listenSocket.Dispose(); - _listenSocket = null; + _socketWrapper.Dispose(); + _socketWrapper = null; } } - public Socket ListenSocket => _listenSocket; + public SocketWrapper ListenSocket => _socketWrapper; public override Uri Address => _uri; public static async Task CreateServerAsync(Func funcAsync, Options options = null) { using (var server = new LoopbackServer(options)) { + await server.ListenAsync(); await funcAsync(server).ConfigureAwait(false); } } @@ -96,23 +125,31 @@ namespace System.Net.Test.Common public async Task EstablishConnectionAsync() { - Socket s = await _listenSocket.AcceptAsync().ConfigureAwait(false); + SocketWrapper closableWrapper = null; try { + Stream stream = null; +#if TARGET_BROWSER + closableWrapper = new SocketWrapper(_listenSocket); + stream = new WebSocketStream(_listenSocket, ownsSocket: true); +#else + var socket = await _listenSocket.AcceptAsync().ConfigureAwait(false); + closableWrapper = new SocketWrapper(socket); + try { - s.NoDelay = true; + socket.NoDelay = true; } // OSX can throw if socket is in weird state during close or cancellation catch (SocketException ex) when (ex.SocketErrorCode == SocketError.InvalidArgument && PlatformDetection.IsOSXLike) { } - Stream stream = new NetworkStream(s, ownsSocket: false); - - return await Connection.CreateAsync(s, stream, _options); + stream = new NetworkStream(socket, ownsSocket: false); +#endif + return await Connection.CreateAsync(closableWrapper, stream, _options).ConfigureAwait(false); } catch (Exception) { - s.Close(); + closableWrapper?.Close(); throw; } } @@ -332,11 +369,19 @@ namespace System.Net.Test.Common public static string GetHttpResponseHeaders(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, string content = null, bool connectionClose = false) => GetHttpResponseHeaders(statusCode, additionalHeaders, content == null ? 0 : content.Length, connectionClose); + public static string CorsHeaders = PlatformDetection.IsBrowser + ? "Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE\r\n" + + "Access-Control-Expose-Headers: *\r\n" + + "Access-Control-Allow-Headers: *\r\n" + + "Access-Control-Allow-Origin: *\r\n" + : ""; + public static string GetHttpResponseHeaders(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, int contentLength = 0, bool connectionClose = false) => $"HTTP/1.1 {(int)statusCode} {GetStatusDescription(statusCode)}\r\n" + (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + $"Content-Length: {contentLength}\r\n" + + CorsHeaders + additionalHeaders + "\r\n"; @@ -345,6 +390,7 @@ namespace System.Net.Test.Common (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Transfer-Encoding: chunked\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + (string.IsNullOrEmpty(content) ? "" : @@ -358,6 +404,7 @@ namespace System.Net.Test.Common (connectionClose ? "Connection: close\r\n" : "") + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Transfer-Encoding: chunked\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + (string.IsNullOrEmpty(content) ? "" : string.Concat(content.Select(c => $"1\r\n{c}\r\n"))) + @@ -368,6 +415,7 @@ namespace System.Net.Test.Common $"HTTP/1.1 {(int)statusCode} {GetStatusDescription(statusCode)}\r\n" + "Connection: close\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + + CorsHeaders + additionalHeaders + "\r\n" + content; @@ -395,7 +443,7 @@ namespace System.Net.Test.Common public sealed class Connection : GenericLoopbackConnection { private const int BufferSize = 4000; - private Socket _socket; + private SocketWrapper _socket; private Stream _stream; private byte[] _readBuffer; private int _readStart; @@ -403,7 +451,7 @@ namespace System.Net.Test.Common private int _contentLength = 0; private bool _bodyRead = false; - public Connection(Socket socket, Stream stream) + public Connection(SocketWrapper socket, Stream stream) { _socket = socket; _stream = stream; @@ -413,10 +461,10 @@ namespace System.Net.Test.Common _readEnd = 0; } - public Socket Socket => _socket; + public SocketWrapper Socket => _socket; public Stream Stream => _stream; - public static async Task CreateAsync(Socket socket, Stream stream, Options httpOptions) + public static async Task CreateAsync(SocketWrapper socket, Stream stream, Options httpOptions) { if (httpOptions.UseSsl) { @@ -706,6 +754,11 @@ namespace System.Net.Test.Common public async Task> ReadRequestHeaderAndSendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, string additionalHeaders = null, string content = null) { List lines = await ReadRequestHeaderAsync().ConfigureAwait(false); + +#if TARGET_BROWSER + lines = await HandleCORSPreFlight(lines); +#endif + await SendResponseAsync(statusCode, additionalHeaders, content).ConfigureAwait(false); return lines; } @@ -850,6 +903,12 @@ namespace System.Net.Test.Common } } + if (PlatformDetection.IsBrowser) + { + byte[] corsBytes = Encoding.ASCII.GetBytes(CorsHeaders); + headerBytes.Write(corsBytes, 0, corsBytes.Length); + } + bool endHeaders = content != null || isFinal; if (statusCode != null) { @@ -890,6 +949,7 @@ namespace System.Net.Test.Common headerString = headerString + $"{headerData.Name}: {headerData.Value}\r\n"; } } + headerString += CorsHeaders; headerString = GetHttpResponseHeaders(statusCode, headerString, 0, connectionClose: true); @@ -901,10 +961,52 @@ namespace System.Net.Test.Common await SendResponseAsync(body).ConfigureAwait(false); } + public async Task HandleCORSPreFlight(HttpRequestData requestData) + { + if (PlatformDetection.IsBrowser && requestData.Method == "OPTIONS" && requestData.Headers.Any(h => h.Name.StartsWith("Access-Control-Request-Method"))) + { + // handle CORS pre-flight + await SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false); + + // reset state + _bodyRead = false; + _contentLength = 0; + _readStart = 0; + _readEnd = 0; + + // wait for real request + return await ReadRequestDataAsync().ConfigureAwait(false); + } + return requestData; + } + + public async Task> HandleCORSPreFlight(List lines) + { + if (PlatformDetection.IsBrowser && lines[0].Contains("OPTIONS") && lines.Any(h => h.StartsWith("Access-Control-Request-Method"))) + { + // handle CORS pre-flight + await SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false); + + // reset state + _bodyRead = false; + _contentLength = 0; + _readStart = 0; + _readEnd = 0; + + // wait for real request + return await ReadRequestHeaderAsync().ConfigureAwait(false); + } + return lines; + } + public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { HttpRequestData requestData = await ReadRequestDataAsync().ConfigureAwait(false); +#if TARGET_BROWSER + requestData = await HandleCORSPreFlight(requestData); +#endif + // For historical reasons, we added Date and "Connection: close" (to improve test reliability) bool hasDate = false; List newHeaders = new List(); @@ -970,7 +1072,9 @@ namespace System.Net.Test.Common public override GenericLoopbackServer CreateServer(GenericLoopbackOptions options = null) { - return new LoopbackServer(CreateOptions(options)); + var loopbackServer = new LoopbackServer(CreateOptions(options)); + Task.WaitAll(loopbackServer.ListenAsync()); + return loopbackServer; } public override Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = 60_000, GenericLoopbackOptions options = null) @@ -978,7 +1082,7 @@ namespace System.Net.Test.Common return LoopbackServer.CreateServerAsync((server, uri) => funcAsync(server, uri), options: CreateOptions(options)); } - public override async Task CreateConnectionAsync(Socket socket, Stream stream, GenericLoopbackOptions options = null) + public override async Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) { return await LoopbackServer.Connection.CreateAsync(socket, stream, CreateOptions(options)); } diff --git a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs index 1a6cc656e92..f6cc44f67e7 100644 --- a/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs @@ -228,11 +228,11 @@ namespace System.Net.Http.Functional.Tests } } #if NETCOREAPP - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.ContentLength, TransferError.ContentLengthTooLarge)] [InlineData(TransferType.Chunked, TransferError.MissingChunkTerminator)] [InlineData(TransferType.Chunked, TransferError.ChunkSizeTooLarge)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_InvalidServerResponse_ThrowsIOException( TransferType transferType, TransferError transferError) @@ -243,7 +243,6 @@ namespace System.Net.Http.Functional.Tests }); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.None, TransferError.None)] [InlineData(TransferType.ContentLength, TransferError.None)] @@ -258,7 +257,6 @@ namespace System.Net.Http.Functional.Tests }); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [Theory] [InlineData(TransferType.None, TransferError.None)] [InlineData(TransferType.ContentLength, TransferError.None)] @@ -327,6 +325,7 @@ namespace System.Net.Http.Functional.Tests // Write response header await connection.WriteStringAsync("HTTP/1.1 200 OK\r\n").ConfigureAwait(false); await connection.WriteStringAsync($"Date: {DateTimeOffset.UtcNow:R}\r\n").ConfigureAwait(false); + await connection.WriteStringAsync(LoopbackServer.CorsHeaders).ConfigureAwait(false); await connection.WriteStringAsync("Content-Type: text/plain\r\n").ConfigureAwait(false); if (!string.IsNullOrEmpty(transferHeader)) { diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props b/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props index 97bdbf298b8..aeed390ba53 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props +++ b/src/libraries/Common/tests/System/Net/Prerequisites/LocalEchoServer.props @@ -9,9 +9,14 @@ <_TestEchoMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' != 'Windows_NT'">$HELIX_CORRELATION_PAYLOAD/xharness/TestEchoMiddleware <_TestEchoMiddleware Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(ArtifactsDir)bin/NetCoreServer/$(NetCoreAppCurrent)-$(Configuration) + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' == 'Windows_NT'">%HELIX_CORRELATION_PAYLOAD%/xharness/RemoteLoopMiddleware + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' == 'true' and '$(OS)' != 'Windows_NT'">$HELIX_CORRELATION_PAYLOAD/xharness/RemoteLoopMiddleware + <_RemoteLoopMiddleware Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(ArtifactsDir)bin/RemoteLoopServer/$(NetCoreAppCurrent)-$(Configuration) + $(WasmXHarnessArgs) --web-server-use-cors --web-server-use-https - $(WasmXHarnessArgs) --set-web-server-http-env=DOTNET_TEST_WEBSOCKETHOST,DOTNET_TEST_HTTPHOST + $(WasmXHarnessArgs) --set-web-server-http-env=DOTNET_TEST_WEBSOCKETHOST,DOTNET_TEST_HTTPHOST,DOTNET_TEST_REMOTE_LOOP_HOST $(WasmXHarnessArgs) --set-web-server-https-env=DOTNET_TEST_SECUREWEBSOCKETHOST,DOTNET_TEST_SECUREHTTPHOST,DOTNET_TEST_HTTP2HOST + $(WasmXHarnessArgs) --web-server-middleware=$(_RemoteLoopMiddleware)/RemoteLoopServer.dll,GenericHandler $(WasmXHarnessArgs) --web-server-middleware=$(_TestEchoMiddleware)/NetCoreServer.dll,GenericHandler diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln deleted file mode 100644 index 8e5d44de5cd..00000000000 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.438 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2F9A0637-452E-4FB9-9403-CB52944982DA} - EndGlobalSection -EndGlobal diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln b/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln new file mode 100644 index 00000000000..4075b414beb --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetTestServers.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteLoopServer", "RemoteLoopServer\RemoteLoopServer.csproj", "{86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{2BB687CC-3F0C-43A9-8F38-140E91892EB0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86E9A13D-9F4A-45DE-B0BB-CBB6A6533868}.Release|Any CPU.Build.0 = Release|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BB687CC-3F0C-43A9-8F38-140E91892EB0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2F9A0637-452E-4FB9-9403-CB52944982DA} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs new file mode 100644 index 00000000000..3f015301038 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/GenericHandler.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; + +namespace RemoteLoopServer +{ + public class GenericHandler + { + RequestDelegate _next; + ILogger _logger; + public GenericHandler(RequestDelegate next, ILogger logger) + { + this._next = next; + this._logger = logger; + } + + public async Task Invoke(HttpContext context) + { + PathString path = context.Request.Path; + if (path.Equals(new PathString("/RemoteLoop"))) + { + await RemoteLoopHandler.InvokeAsync(context, _logger); + return; + } + + await _next(context); + } + } + + public static class GenericHandlerExtensions + { + public static IApplicationBuilder UseGenericHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + + public static void SetStatusDescription(this HttpResponse response, string description) + { + response.HttpContext.Features.Get().ReasonPhrase = description; + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs new file mode 100644 index 00000000000..4f83c67b0b3 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Handlers/RemoteLoopHandler.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Net; +using System.Net.Sockets; +using System.Net.WebSockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace RemoteLoopServer +{ + public partial class RemoteLoopHandler + { + public static async Task InvokeAsync(HttpContext context, ILogger logger) + { + try + { + if (!context.WebSockets.IsWebSocketRequest) + { + context.Response.StatusCode = 400; + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Not a websocket request"); + + return; + } + + using (WebSocket socket = await context.WebSockets.AcceptWebSocketAsync()) + { + await ProcessWebSocketRequest(context, socket, logger); + } + + } + catch (Exception ex) + { + logger.LogError("RemoteLoopHandler failed", ex); + } + } + + private static async Task ProcessWebSocketRequest(HttpContext context, WebSocket control, ILogger logger) + { + byte[] controlBuffer = new byte[128 * 1024]; + byte[] testedBuffer = new byte[128 * 1024]; + Socket listenSocket = null; + Socket tested = null; + CancellationTokenSource cts = new CancellationTokenSource(); + try + { + WebSocketReceiveResult first = await control.ReceiveAsync(controlBuffer, cts.Token).ConfigureAwait(false); + if (first.Count <= 0 || first.MessageType != WebSocketMessageType.Binary || control.State != WebSocketState.Open) + { + throw new Exception("Unexpected close"); + } + + // parse setup request + var message = Encoding.ASCII.GetString(controlBuffer, 0, first.Count); + var split = message.Split(','); + var listenBacklog = int.Parse(split[0]); + var address = IPAddress.Parse(split[1]); + + listenSocket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + listenSocket.Bind(new IPEndPoint(address, 0)); + listenSocket.Listen(listenBacklog); + EndPoint endPoint = listenSocket.LocalEndPoint; + + // respond with what we have done + await control.SendAsync(Encoding.ASCII.GetBytes(endPoint.ToString()), WebSocketMessageType.Binary, true, cts.Token).ConfigureAwait(false); + + // wait for the tested client to connect + tested = await listenSocket.AcceptAsync().ConfigureAwait(false); + + // now we are connected, pump messages + bool close = false; + + Task testedNext = tested.ReceiveAsync(new Memory(testedBuffer), SocketFlags.None, cts.Token).AsTask(); + Task controlNext = control.ReceiveAsync(controlBuffer, cts.Token); + while (!close) + { + // wait for either message + await Task.WhenAny(testedNext, controlNext).ConfigureAwait(false); + + if (testedNext.IsCompleted) + { + if (testedNext.IsCanceled || testedNext.IsFaulted) + { + close = true; + } + else + { + if (!tested.Connected) + { + close = true; + } + if (testedNext.Result > 0) + { + var slice = new ArraySegment(testedBuffer, 0, testedNext.Result); + await control.SendAsync(slice, WebSocketMessageType.Binary, true, cts.Token).ConfigureAwait(false); + } + if (!close) + { + testedNext = tested.ReceiveAsync(new Memory(testedBuffer), SocketFlags.None, cts.Token).AsTask(); + } + } + } + if (controlNext.IsCompleted) + { + if (controlNext.IsCanceled || controlNext.IsFaulted) + { + close = true; + } + else + { + if (controlNext.Result.MessageType == WebSocketMessageType.Close) + { + close = true; + } + if (controlNext.Result.Count > 0) + { + var slice = new ArraySegment(controlBuffer, 0, controlNext.Result.Count); + await tested.SendAsync(slice, SocketFlags.None, cts.Token).ConfigureAwait(false); + } + if (!close) + { + controlNext = control.ReceiveAsync(new ArraySegment(controlBuffer), cts.Token); + } + } + } + } + if (tested.Connected) + { + tested.Disconnect(false); + } + if (control.State == WebSocketState.Open || control.State == WebSocketState.Connecting || control.State == WebSocketState.None) + { + try + { + await control.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", cts.Token).ConfigureAwait(false); + } + catch (WebSocketException ex) + { + logger.LogWarning("ProcessWebSocketRequest closing failed", ex); + } + } + cts.Cancel(); + } + catch (Exception ex) + { + logger.LogError("ProcessWebSocketRequest failed", ex); + } + finally + { + tested?.Dispose(); + control?.Dispose(); + } + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs new file mode 100644 index 00000000000..f18d257efb9 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Program.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace RemoteLoopServer +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj new file mode 100644 index 00000000000..ba8a85a059c --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/RemoteLoopServer.csproj @@ -0,0 +1,15 @@ + + + + $(AspNetCoreAppCurrent) + InProcess + Exe + + + + + + + + + diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs new file mode 100644 index 00000000000..bbf7b7bb79e --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/Startup.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace RemoteLoopServer +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseWebSockets(); + app.UseGenericHandler(); + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json new file mode 100644 index 00000000000..e203e9407e7 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json new file mode 100644 index 00000000000..def9159a7d9 --- /dev/null +++ b/src/libraries/Common/tests/System/Net/Prerequisites/RemoteLoopServer/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs new file mode 100644 index 00000000000..322f5d2a00c --- /dev/null +++ b/src/libraries/Common/tests/System/Net/WebSockets/WebSocketStream.cs @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using System.IO; +using System.Threading; + +namespace System.Net.WebSockets +{ + public class WebSocketStream : Stream + { + // Used by the class to hold the underlying socket the stream uses. + private readonly WebSocket _streamSocket; + + // Whether the stream should dispose of the socket when the stream is disposed + private readonly bool _ownsSocket; + + // Used by the class to indicate that the stream is m_Readable. + private bool _readable; + + // Used by the class to indicate that the stream is writable. + private bool _writeable; + + // Whether Dispose has been called. 0 == false, 1 == true + private int _disposed; + + public WebSocketStream(WebSocket socket) + : this(socket, FileAccess.ReadWrite, ownsSocket: false) + { + } + + public WebSocketStream(WebSocket socket, bool ownsSocket) + : this(socket, FileAccess.ReadWrite, ownsSocket) + { + } + + public WebSocketStream(WebSocket socket, FileAccess access) + : this(socket, access, ownsSocket: false) + { + } + + public WebSocketStream(WebSocket socket, FileAccess access, bool ownsSocket) + { + if (socket == null) + { + throw new ArgumentNullException(nameof(socket)); + } + if (socket.State != WebSocketState.Open) + { + throw new IOException("The operation is not allowed on non-connected sockets."); + } + + _streamSocket = socket; + _ownsSocket = ownsSocket; + + switch (access) + { + case FileAccess.Read: + _readable = true; + break; + case FileAccess.Write: + _writeable = true; + break; + case FileAccess.ReadWrite: + default: // assume FileAccess.ReadWrite + _readable = true; + _writeable = true; + break; + } + } + + public WebSocket Socket => _streamSocket; + + protected bool Readable + { + get { return _readable; } + set { _readable = value; } + } + + protected bool Writeable + { + get { return _writeable; } + set { _writeable = value; } + } + + public override bool CanRead => _readable; + public override bool CanSeek => false; + public override bool CanWrite => _writeable; + public override bool CanTimeout => true; + public override long Length => throw new NotSupportedException("This stream does not support seek operations."); + + public override long Position + { + get + { + throw new NotSupportedException("This stream does not support seek operations."); + } + set + { + throw new NotSupportedException("This stream does not support seek operations."); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("This stream does not support seek operations."); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override int Read(Span buffer) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + public override void Write(ReadOnlySpan buffer) + { + throw new IOException("The operation is not allowed on a non-blocking Socket."); + } + + private int _closeTimeout = -1; + + public void Close(int timeout) + { + if (timeout < -1) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + _closeTimeout = timeout; + Dispose(); + } + + protected override void Dispose(bool disposing) + { + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + if (disposing) + { + _readable = false; + _writeable = false; + if (_ownsSocket) + { + if (_streamSocket != null && (_streamSocket.State == WebSocketState.Open || _streamSocket.State == WebSocketState.Connecting || _streamSocket.State == WebSocketState.None)) + { + try + { + var task = _streamSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "closing remoteLoop", CancellationToken.None); + Task.WaitAll(task); + } + catch (Exception) + { + } + finally + { + _streamSocket.Dispose(); + } + } + } + } + + base.Dispose(disposing); + } + + ~WebSocketStream() => Dispose(false); + + public async override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + ThrowIfDisposed(); + if (!CanRead) + { + throw new InvalidOperationException("The stream does not support reading."); + } + + try + { + var res = await _streamSocket.ReceiveAsync(new Memory(buffer, offset, count), cancellationToken); + return res.Count; + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to read data from the transport connection", exception); + } + } + + public async override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) + { + bool canRead = CanRead; // Prevent race with Dispose. + ThrowIfDisposed(); + if (!canRead) + { + throw new InvalidOperationException("The stream does not support reading."); + } + + try + { + var res = await _streamSocket.ReceiveAsync(buffer, + cancellationToken); + return res.Count; + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to read data from the transport connection", exception); + } + } + + public async override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + ThrowIfDisposed(); + if (!CanWrite) + { + throw new InvalidOperationException("The stream does not support writing."); + } + + try + { + await _streamSocket.SendAsync(new ReadOnlyMemory(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken); + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to write data to the transport connection", exception); + } + } + + public async override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + { + bool canWrite = CanWrite; // Prevent race with Dispose. + ThrowIfDisposed(); + if (!canWrite) + { + throw new InvalidOperationException("The stream does not support writing."); + } + + try + { + await _streamSocket.SendAsync(buffer, WebSocketMessageType.Binary, true, cancellationToken); + } + catch (Exception exception) when (!(exception is OutOfMemoryException)) + { + throw WrapException("Unable to write data to the transport connection", exception); + } + } + + public override void Flush() + { + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public override void SetLength(long value) + { + throw new NotSupportedException("This stream does not support seek operations."); + } + + private void ThrowIfDisposed() + { + if (_disposed != 0) + { + ThrowObjectDisposedException(); + } + + void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().FullName); + } + + private static IOException WrapException(string resourceFormatString, Exception innerException) + { + return new IOException(resourceFormatString, innerException); + } + } +} diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs index 76d80f21450..6cb8bbc075c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs @@ -41,7 +41,7 @@ namespace System.Net.Http.Functional.Tests using GenericLoopbackServer firstServer = fromVersion.Major switch { - 1 => new LoopbackServer(new LoopbackServer.Options { UseSsl = true }), + 1 => Http11LoopbackServerFactory.Singleton.CreateServer(new LoopbackServer.Options { UseSsl = true }), 2 => Http2LoopbackServer.CreateServer(), _ => throw new Exception("Unknown HTTP version.") }; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index 73a543ac12a..7a1ceaf88c7 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -23,7 +23,6 @@ namespace System.Net.Http.Functional.Tests private sealed class DerivedHttpHeaders : HttpHeaders { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_RequestWithSimpleHeader_ResponseReferencesUnmodifiedRequestHeaders() { const string HeaderKey = "some-header-123", HeaderValue = "this is the expected header value"; @@ -47,7 +46,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "User-Agent is not supported on Browser")] public async Task SendAsync_UserAgent_CorrectlyWritten() { string userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.18 Safari/537.36"; @@ -71,7 +70,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_LargeHeaders_CorrectlyWritten() { if (UseVersion == HttpVersion.Version30) @@ -107,7 +105,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_DefaultHeaders_CorrectlyWritten() { const string Version = "2017-04-17"; @@ -137,7 +134,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("\u05D1\u05F1")] [InlineData("jp\u30A5")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_InvalidCharactersInHeader_Throw(string value) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -169,9 +166,14 @@ namespace System.Net.Http.Functional.Tests [InlineData("Accept-CharSet", "text/plain, text/json", false)] // invalid format for header but added with TryAddWithoutValidation [InlineData("Content-Location", "", false)] // invalid format for header but added with TryAddWithoutValidation [InlineData("Max-Forwards", "NotAnInteger", false)] // invalid format for header but added with TryAddWithoutValidation - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_SpecialHeaderKeyOrValue_Success(string key, string value, bool parsable) { + if (PlatformDetection.IsBrowser && (key == "Content-Location" || key == "Date" || key == "Accept-CharSet")) + { + // https://fetch.spec.whatwg.org/#forbidden-header-name + return; + } + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { bool contentHeader = false; @@ -209,7 +211,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("Content-Security-Policy", 4618)] [InlineData("RandomCustomHeader", 12345)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_LargeHeader_Success(string headerName, int headerValueLength) { var rand = new Random(42); @@ -234,7 +235,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_EmptyResponseHeader_Success() { IList headers = new HttpHeaderData[] { @@ -246,8 +246,12 @@ namespace System.Net.Http.Functional.Tests { using (HttpClient client = CreateHttpClient()) { - HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false); - Assert.Equal(headers.Count, response.Headers.Count()); + HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false); + // browser sends more headers + if (PlatformDetection.IsNotBrowser) + { + Assert.Equal(headers.Count, response.Headers.Count()); + } Assert.NotNull(response.Headers.GetValues("x-empty")); } }, @@ -262,7 +266,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsync_MissingExpires_ReturnNull() { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -283,7 +286,6 @@ namespace System.Net.Http.Functional.Tests [InlineData("Thu, 01 Dec 1994 16:00:00 GMT", true)] [InlineData("-1", false)] [InlineData("0", false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_Expires_Success(string value, bool isValid) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -322,7 +324,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData("Accept-Encoding", "identity,gzip")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task SendAsync_RequestHeaderInResponse_Success(string name, string value) { await LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -400,7 +401,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_WithZeroLengthHeaderName_Throws() { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -443,7 +444,7 @@ namespace System.Net.Http.Functional.Tests }; [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public async Task SendAsync_CustomRequestEncodingSelector_CanSendNonAsciiHeaderValues() { await LoopbackServerFactory.CreateClientAndServerAsync( @@ -498,7 +499,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public async Task SendAsync_CustomResponseEncodingSelector_CanReceiveNonAsciiHeaderValues() { await LoopbackServerFactory.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs index c7579cd114b..f0e58b32ca3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http1.cs @@ -17,7 +17,6 @@ namespace System.Net.Http.Functional.Tests public HttpClientHandlerTest_Http1(ITestOutputHelper output) : base(output) { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] public async Task SendAsync_HostHeader_First() { // RFC 7230 3.2.2. Field Order diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs index ced259294f8..4e2426b958e 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.RequestRetry.cs @@ -19,7 +19,6 @@ namespace System.Net.Http.Functional.Tests public HttpClientHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetAsyncOnNewConnection_RetryOnConnectionClosed_Success() { await LoopbackServer.CreateClientAndServerAsync(async url => diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs index 6d68042a733..af3bdd764bc 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs @@ -98,6 +98,7 @@ namespace System.Net.Http.Functional.Tests } [Collection(nameof(HttpClientMiniStress))] + [SkipOnPlatform(TestPlatforms.Browser, "System.Net.Security is not supported on Browser")] public abstract class HttpClientMiniStress : HttpClientHandlerTestBase { public HttpClientMiniStress(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 94d59d89b03..451bb20d58f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -357,8 +357,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetStringAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -378,8 +376,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -404,8 +401,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -440,7 +436,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(1, 0)] [InlineData(1, 1)] [InlineData(1, 2)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetAsync_ContentCanBeCanceled(int getMode, int cancelMode) { // cancelMode: @@ -530,8 +526,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetByteArrayAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -552,8 +546,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -578,8 +571,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -607,8 +599,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task GetStreamAsync_Success() { string content = Guid.NewGuid().ToString(); @@ -632,8 +622,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -658,8 +647,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -916,8 +904,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(HttpCompletionOption.ResponseContentRead)] [InlineData(HttpCompletionOption.ResponseHeadersRead)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_SingleThread_Loopback_Succeeds(HttpCompletionOption completionOption) { string content = "Test content"; @@ -970,7 +957,7 @@ namespace System.Net.Http.Functional.Tests [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_CancelledRequestContent_Throws() { CancellationTokenSource cts = new CancellationTokenSource(); @@ -1017,7 +1004,6 @@ namespace System.Net.Http.Functional.Tests [Fact] [OuterLoop] [ActiveIssue("https://github.com/dotnet/runtime/issues/39056")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task Send_TimeoutRequestContent_Throws() { await LoopbackServer.CreateClientAndServerAsync( @@ -1060,7 +1046,7 @@ namespace System.Net.Http.Functional.Tests [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] public async Task Send_CancelledResponseContent_Throws() { string content = "Test content"; @@ -1110,12 +1096,13 @@ namespace System.Net.Http.Functional.Tests [Fact] [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] - public void Send_TimeoutResponseContent_Throws() + [SkipOnPlatform(TestPlatforms.Browser, "Synchronous Send is not supported on Browser")] + public async Task Send_TimeoutResponseContent_Throws() { const string Content = "Test content"; using var server = new LoopbackServer(); + await server.ListenAsync(); // Ignore all failures from the server. This includes being disposed of before ever accepting a connection, // which is possible if the client times out so quickly that it hasn't initiated a connection yet. @@ -1163,8 +1150,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(VersionSelectionMemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Version is ignored on Browser")] public async Task SendAsync_CorrectVersionSelected_LoopbackServer(Version requestVersion, HttpVersionPolicy versionPolicy, Version serverVersion, bool useSsl, object expectedResult) { await HttpAgnosticLoopbackServer.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index 214042a1850..b09b0d2dc1a 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -613,8 +613,6 @@ namespace System.Net.Http.Functional.Tests [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Buffered_IgnoresCancellationToken() { string content = Guid.NewGuid().ToString(); @@ -641,8 +639,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts() { await LoopbackServer.CreateClientAndServerAsync( @@ -670,8 +666,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -706,8 +700,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Buffered_IgnoresCancellationToken() { string content = Guid.NewGuid().ToString(); @@ -735,8 +727,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts() { await LoopbackServer.CreateClientAndServerAsync( @@ -764,8 +754,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -802,8 +790,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_Buffered_IgnoresCancellationToken(bool readStreamAsync) { string content = Guid.NewGuid().ToString(); @@ -835,10 +821,13 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39187", TestPlatforms.Browser)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_Unbuffered_IgnoresCancellationToken(bool readStreamAsync) { + if(PlatformDetection.IsBrowser && !readStreamAsync) + { + // syncronous operations are not supported on Browser + return; + } string content = Guid.NewGuid().ToString(); await LoopbackServer.CreateClientAndServerAsync( diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs index 167d5a0504c..18b7fa9fe47 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http.Headers; using System.Net.Test.Common; using System.Threading.Tasks; @@ -12,7 +13,6 @@ using Xunit.Abstractions; namespace System.Net.Http.Functional.Tests { - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] public class HttpRequestMessageTest : HttpClientHandlerTestBase { private readonly Version _expectedRequestMessageVersion = HttpVersion.Version11; @@ -240,8 +240,12 @@ namespace System.Net.Http.Functional.Tests Task requestTask = client.SendAsync(request); await server.AcceptConnectionAsync(async connection => { - List headers = await connection.ReadRequestHeaderAsync(); - Assert.DoesNotContain(headers, line => line.StartsWith("Content-length")); + var requestData = await connection.ReadRequestDataAsync().ConfigureAwait(false); +#if TARGET_BROWSER + requestData = await connection.HandleCORSPreFlight(requestData); +#endif + + Assert.DoesNotContain(requestData.Headers, line => line.Name.StartsWith("Content-length")); await connection.SendResponseAsync(); await requestTask; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 0ac38b25727..cf7edc98814 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -170,19 +170,19 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandler_HttpClientHandler_Decompression_Tests(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test : HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test { public SocketsHttpHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test : HttpClientHandler_ClientCertificates_Test { public SocketsHttpHandler_HttpClientHandler_ClientCertificates_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test : HttpClientHandler_DefaultProxyCredentials_Test { public SocketsHttpHandler_HttpClientHandler_DefaultProxyCredentials_Test(ITestOutputHelper output) : base(output) { } @@ -248,13 +248,13 @@ namespace System.Net.Http.Functional.Tests } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Certificates are not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test : HttpClientHandler_ServerCertificates_Test { public SocketsHttpHandler_HttpClientHandler_ServerCertificates_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "ResponseDrainTimeout is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ResponseDrain_Test : HttpClientHandler_ResponseDrain_Test { protected override void SetResponseDrainTimeout(HttpClientHandler handler, TimeSpan time) @@ -560,13 +560,13 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandler_ResponseStreamTest(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] public sealed class SocketsHttpHandler_HttpClientHandler_SslProtocols_Test : HttpClientHandler_SslProtocols_Test { public SocketsHttpHandler_HttpClientHandler_SslProtocols_Test(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Proxy_Test : HttpClientHandler_Proxy_Test { public SocketsHttpHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { } @@ -588,8 +588,7 @@ namespace System.Net.Http.Functional.Tests new DataFrame(data, (endStream ? FrameFlags.EndStream : FrameFlags.None), 0, streamId); } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54156", TestPlatforms.Browser)] public class SocketsHttpHandler_Http1_TrailingHeaders_Test : SocketsHttpHandler_TrailingHeaders_Test { public SocketsHttpHandler_Http1_TrailingHeaders_Test(ITestOutputHelper output) : base(output) { } @@ -609,6 +608,7 @@ namespace System.Net.Http.Functional.Tests getResponseTask, server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + (includeTrailerHeader ? "Trailer: MyCoolTrailerHeader, Hello\r\n" : "") + @@ -662,6 +662,7 @@ namespace System.Net.Http.Functional.Tests getResponseTask, server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + "Trailer: MyCoolTrailerHeader\r\n" + @@ -751,6 +752,7 @@ namespace System.Net.Http.Functional.Tests } }, server => server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + + LoopbackServer.CorsHeaders + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n" + $"Trailer: Set-Cookie, MyCoolTrailerHeader, {name}, Hello\r\n" + @@ -780,6 +782,7 @@ namespace System.Net.Http.Functional.Tests server.AcceptConnectionSendCustomResponseAndCloseAsync( "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n" + "Trailer: MyCoolTrailerHeader\r\n" + "\r\n" + @@ -987,7 +990,6 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } } - [SkipOnPlatform(TestPlatforms.Browser, "Tests hang with chrome. To be investigated")] public sealed class SocketsHttpHandler_HttpClientHandlerTest : HttpClientHandlerTest { public SocketsHttpHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { } @@ -1017,19 +1019,19 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandlerTest_RequestRetry(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")] public sealed class SocketsHttpHandlerTest_Cookies : HttpClientHandlerTest_Cookies { public SocketsHttpHandlerTest_Cookies(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseCookies is not supported on Browser")] public sealed class SocketsHttpHandlerTest_Cookies_Http11 : HttpClientHandlerTest_Cookies_Http11 { public SocketsHttpHandlerTest_Cookies_Http11(ITestOutputHelper output) : base(output) { } } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "ConnectTimeout is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test : SocketsHttpHandler_Cancellation_Test { public SocketsHttpHandler_HttpClientHandler_Http11_Cancellation_Test(ITestOutputHelper output) : base(output) { } @@ -1130,15 +1132,13 @@ namespace System.Net.Http.Functional.Tests } } - // BrowserHttpHandler.set_MaxResponseHeadersLength - Not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "MaxResponseHeadersLength is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test : HttpClientHandler_MaxResponseHeadersLength_Test { public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Test(ITestOutputHelper output) : base(output) { } } - //System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_Authentication_Test : HttpClientHandler_Authentication_Test { public SocketsHttpHandler_HttpClientHandler_Authentication_Test(ITestOutputHelper output) : base(output) { } @@ -1158,7 +1158,7 @@ namespace System.Net.Http.Functional.Tests { // We need to use ResponseHeadersRead here, otherwise we will hang trying to buffer the response body. Task getResponseTask = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); - await server.AcceptConnectionAsync(async connection => + await server.AcceptConnectionAsync(async (LoopbackServer.Connection connection) => { Task> serverTask = connection.ReadRequestHeaderAndSendCustomResponseAsync($"HTTP/1.1 101 Switching Protocols\r\nDate: {DateTimeOffset.UtcNow:R}\r\n\r\n"); @@ -1252,7 +1252,7 @@ namespace System.Net.Http.Functional.Tests Task copyTask = clientStream.CopyToAsync(ms); string bigString = string.Concat(Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", 1000)); - Task lotsOfDataSent = connection.Socket.SendAsync(Encoding.ASCII.GetBytes(bigString), SocketFlags.None); + Task lotsOfDataSent = connection.SendResponseAsync(Encoding.ASCII.GetBytes(bigString)); connection.Socket.Shutdown(SocketShutdown.Send); await copyTask; await lotsOfDataSent; @@ -1270,8 +1270,7 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandler_Connect_Test(ITestOutputHelper output) : base(output) { } } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test : HttpClientHandlerTestBase { public SocketsHttpHandler_HttpClientHandler_ConnectionPooling_Test(ITestOutputHelper output) : base(output) { } @@ -1468,7 +1467,7 @@ namespace System.Net.Http.Functional.Tests // Wait a small amount of time before making the second request, to give the first request time to timeout. await Task.Delay(100); // Grab reference to underlying socket and stream to make sure they are not disposed and closed. - (Socket socket, Stream stream) = connection.ResetNetwork(); + (SocketWrapper socket, Stream stream) = connection.ResetNetwork(); // Make second request and expect it to be served from a different connection. Task request2 = client.GetStringAsync(url); @@ -1997,8 +1996,7 @@ namespace System.Net.Http.Functional.Tests } } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Headers.Location are not supported on Browser")] public sealed class SocketsHttpHandlerTest_LocationHeader { private static readonly byte[] s_redirectResponseBefore = Encoding.ASCII.GetBytes( @@ -2671,8 +2669,7 @@ namespace System.Net.Http.Functional.Tests private static bool PlatformSupportsUnixDomainSockets => Socket.OSSupportsUnixDomainSockets; } - // System.Net.Sockets is not supported on this platform - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")] public sealed class SocketsHttpHandlerTest_ConnectCallback_Http11 : SocketsHttpHandlerTest_ConnectCallback { public SocketsHttpHandlerTest_ConnectCallback_Http11(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index 633f562c949..821794e9f82 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -113,14 +113,14 @@ namespace System.Net.Http.Functional.Tests } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http1_Async : SocksProxyTest { public SocksProxyTest_Http1_Async(ITestOutputHelper helper) : base(helper) { } protected override Version UseVersion => HttpVersion.Version11; } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http1_Sync : SocksProxyTest { public SocksProxyTest_Http1_Sync(ITestOutputHelper helper) : base(helper) { } @@ -128,7 +128,7 @@ namespace System.Net.Http.Functional.Tests protected override bool TestAsync => false; } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [SkipOnPlatform(TestPlatforms.Browser, "UseProxy not supported on Browser")] public sealed class SocksProxyTest_Http2 : SocksProxyTest { public SocksProxyTest_Http2(ITestOutputHelper helper) : base(helper) { } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 7c9b9d6d157..672ce364f70 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -13,12 +13,17 @@ WasmTestOnBrowser $(TestArchiveRoot)browseronly/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ + $(DefineConstants);TARGET_BROWSER + + + diff --git a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs index 68773696bfc..a10e9e4639e 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs @@ -374,7 +374,7 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)] public async Task CloseAsync_CancelableEvenWhenPendingReceive_Throws() { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index 443166cafec..0fb1314a606 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -58,7 +58,7 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - [SkipOnPlatform(TestPlatforms.Browser, "CustomHeaders not supported on browser")] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")] public async Task ConnectAsync_AddCustomHeaders_Success(Uri server) { using (var cws = new ClientWebSocket()) @@ -96,7 +96,7 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on browser")] public async Task ConnectAsync_AddHostHeader_Success() { string expectedHost = null; @@ -218,7 +218,7 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "SetRequestHeader not supported on Browser")] public async Task ConnectAsync_NonStandardRequestHeaders_HeadersAddedWithoutValidation() { await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -237,7 +237,7 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")] public async Task ConnectAndCloseAsync_UseProxyServer_ExpectedClosedState(Uri server) { using (var cws = new ClientWebSocket()) @@ -282,7 +282,7 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54152", TestPlatforms.Browser)] public async Task ConnectAsync_CancellationRequestedAfterConnect_ThrowsOperationCanceledException() { var releaseServer = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs index 1f12d767433..e0a0e1e59fd 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/DeflateTests.cs @@ -22,7 +22,6 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalTheory(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] [InlineData(15, true, 15, true, "permessage-deflate; client_max_window_bits")] [InlineData(14, true, 15, true, "permessage-deflate; client_max_window_bits=14")] [InlineData(15, true, 14, true, "permessage-deflate; client_max_window_bits; server_max_window_bits=14")] diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index d3a04bdbeef..6266f01e8ed 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -389,7 +389,7 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalFact(nameof(WebSocketsSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/42852", TestPlatforms.Browser)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54153", TestPlatforms.Browser)] public async Task SendReceive_ConnectionClosedPrematurely_ReceiveAsyncFailsAndWebSocketStateUpdated() { var options = new LoopbackServer.Options { WebSocketEndpoint = true }; diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index 22b43cc0edc..956ee4d4146 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -9,12 +9,17 @@ WasmTestOnBrowser $(TestArchiveRoot)browseronly/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ + $(DefineConstants);TARGET_BROWSER + + + diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 574bee9c7dc..25d7168bdc4 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -277,6 +277,7 @@ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'RemoteLoopServer', '$(NetCoreAppCurrent)-$(Configuration)')) @@ -311,6 +312,7 @@ + From 183c4d100f68fb6c177a1fe71809d581aa25e47b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 22 Jun 2021 09:44:28 -0400 Subject: [PATCH 040/926] Fix XML in Version.Details.xml --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4177e280edf..99fb28cb6e9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -216,7 +216,7 @@ 25b814e010cd4796cedfbcce72a274c26928f496 - https://github.com/dotnet/runtime-assets + https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e From 85aebc422ee12c13a15c94aedd5a2e7627e47db7 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 22 Jun 2021 09:00:43 -0700 Subject: [PATCH 041/926] Fix deadlock (#54426) --- src/coreclr/gc/gc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 909e5580c72..f4fdf3177ef 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -20587,6 +20587,7 @@ void gc_heap::garbage_collect (int n) update_collection_counts_for_no_gc(); #ifdef MULTIPLE_HEAPS + gc_start_event.Reset(); gc_t_join.restart(); #endif //MULTIPLE_HEAPS } From 2a61c25b26814431163b53e9f9f2e47a750c48e7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jun 2021 09:32:53 -0700 Subject: [PATCH 042/926] Fail COM CoClass creation during construction instead of during jitting (#54508) --- src/coreclr/vm/ecall.cpp | 20 +++++++------------ src/coreclr/vm/ecall.h | 2 -- src/coreclr/vm/gchelpers.cpp | 12 ++++++++++- .../COM/NETClients/ComDisabled/Program.cs | 16 ++++++--------- src/tests/issues.targets | 3 --- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp index 974556232f6..c6d1b6d2f1e 100644 --- a/src/coreclr/vm/ecall.cpp +++ b/src/coreclr/vm/ecall.cpp @@ -434,12 +434,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /* #endif // FEATURE_COMINTEROP ) { -#ifdef FEATURE_COMINTEROP - if (g_pBaseCOMObject == NULL) - { - COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); - } - if (pfSharedOrDynamicFCallImpl) *pfSharedOrDynamicFCallImpl = TRUE; @@ -448,9 +442,6 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /* // FCComCtor does not need to be in the fcall hashtable since it does not erect frame. return GetEEFuncEntryPoint(FCComCtor); -#else - COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); -#endif // FEATURE_COMINTEROP } if (!pMD->GetModule()->IsSystem()) @@ -571,9 +562,7 @@ BOOL ECall::IsSharedFCallImpl(PCODE pImpl) PCODE pNativeCode = pImpl; return -#ifdef FEATURE_COMINTEROP (pNativeCode == GetEEFuncEntryPoint(FCComCtor)) || -#endif (pNativeCode == GetEEFuncEntryPoint(COMDelegate::DelegateConstruct)); } @@ -619,7 +608,12 @@ BOOL ECall::CheckUnusedECalls(SetSHash& usedIDs) } -#if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE) +#if !defined(CROSSGEN_COMPILE) +// This function is a stub implementation for the constructor of a ComImport class. +// The actual work to implement COM Activation (and built-in COM support checks) is done as part +// of the implementation of object allocation. As a result, the constructor itself has no extra +// work to do once the object has been allocated. As a result, we just provide a dummy implementation +// here since a constructor has to have an implementation. FCIMPL1(VOID, FCComCtor, LPVOID pV) { FCALL_CONTRACT; @@ -627,7 +621,7 @@ FCIMPL1(VOID, FCComCtor, LPVOID pV) FCUnique(0x34); } FCIMPLEND -#endif // FEATURE_COMINTEROP && !CROSSGEN_COMPILE +#endif // !CROSSGEN_COMPILE diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h index 387bbc2e9dd..9b946ad1982 100644 --- a/src/coreclr/vm/ecall.h +++ b/src/coreclr/vm/ecall.h @@ -134,8 +134,6 @@ class ECall static LPVOID GetQCallImpl(MethodDesc * pMD); }; -#ifdef FEATURE_COMINTEROP extern "C" FCDECL1(VOID, FCComCtor, LPVOID pV); -#endif #endif // _ECALL_H_ diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 74412a9e06d..0cecfc624a7 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -930,15 +930,25 @@ OBJECTREF AllocateObject(MethodTable *pMT #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION if (fHandleCom && pMT->IsComObjectType()) { + if (!g_pConfig->IsBuiltInCOMSupported()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_COM")); + } + // Create a instance of __ComObject here is not allowed as we don't know what COM object to create if (pMT == g_pBaseCOMObject) COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); oref = OBJECTREF_TO_UNCHECKED_OBJECTREF(AllocateComObject_ForManaged(pMT)); } - else #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION +#else // FEATURE_COMINTEROP + if (pMT->IsComObjectType()) + { + COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM); + } #endif // FEATURE_COMINTEROP + else { GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS; if (pMT->ContainsPointers()) diff --git a/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs b/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs index d7f6a83983a..8f25b3dbcf8 100644 --- a/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs +++ b/src/tests/Interop/COM/NETClients/ComDisabled/Program.cs @@ -20,22 +20,18 @@ namespace NetClient try { - ActivateServer(); + var server = (Server.Contract.Servers.NumericTesting)new Server.Contract.Servers.NumericTestingClass(); } - catch (PlatformNotSupportedException ex) + catch (NotSupportedException) when (OperatingSystem.IsWindows()) + { + return 100; + } + catch (PlatformNotSupportedException) when (!OperatingSystem.IsWindows()) { return 100; } return 101; } - - // Mark as NoInlining to make sure the failure is observed while running Main, - // not while JITing Main and trying to resolve the target of the constructor call. - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ActivateServer() - { - var server = (Server.Contract.Servers.NumericTesting)new Server.Contract.Servers.NumericTestingClass(); - } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 25b94c7c75c..1daa9fda46d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,9 +123,6 @@ https://github.com/dotnet/runtime/issues/48727 - - https://github.com/dotnet/runtime/issues/54379 - https://github.com/dotnet/runtime/issues/54316 From 5f778101c6867f1490889658cd27095cded236fe Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 22 Jun 2021 12:35:38 -0400 Subject: [PATCH 043/926] [wasm] Collect, and process satellite assemblies automatically (#53656) --- eng/testing/tests.wasm.targets | 21 +- src/mono/wasm/build/WasmApp.Native.targets | 4 +- src/mono/wasm/build/WasmApp.targets | 16 +- .../aot-tests/ProxyProjectForAOTOnHelix.proj | 8 +- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 7 + .../Wasm.Build.Tests/BuildTestBase.cs | 57 ++++-- .../InvariantGlobalizationTests.cs | 6 +- .../Wasm.Build.Tests/MainWithArgsTests.cs | 2 +- .../Wasm.Build.Tests/NativeBuildTests.cs | 4 +- .../Wasm.Build.Tests/RebuildTests.cs | 2 +- .../SatelliteAssembliesTests.cs | 179 ++++++++++++++++++ .../SharedBuildPerTestClassFixture.cs | 4 +- .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 5 +- .../Wasm.Build.Tests/WasmBuildAppTest.cs | 9 +- .../testassets/LibraryWithResources/Class1.cs | 11 ++ .../Directory.Build.props | 1 + .../Directory.Build.targets | 1 + .../LibraryWithResources.csproj | 10 + .../testassets/resx/words.es-ES.resx | 127 +++++++++++++ .../testassets/resx/words.ja-JP.resx | 127 +++++++++++++ .../BuildWasmApps/testassets/resx/words.resx | 127 +++++++++++++ 21 files changed, 673 insertions(+), 55 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.resx diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index 1e1c8669579..de4432bee9f 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -69,8 +69,7 @@ - - + @@ -131,17 +130,17 @@ -1 + + <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" /> + <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" /> + + + + - - - - - + - <_CopyLocalPaths Include="@(PublishItemsOutputGroupOutputs)" diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index b16195a618a..b6897fbd2b4 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -353,6 +353,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ @(MonoAOTCompilerDefaultAotArguments, ';') @(MonoAOTCompilerDefaultProcessArguments, ';') + <_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" /> <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> @@ -422,9 +423,6 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ <_WasmNativeFileForLinking Include="@(_BitcodeFile)" /> - - diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index b886f327d71..03428743689 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -61,7 +61,6 @@ Public items: - @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir). (relative path can be set via %(TargetPath) metadata) - - @(WasmSatelliteAssemblies) - @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs - @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle. @@ -115,6 +114,13 @@ <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> + + <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" /> + <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" /> + + <_WasmSatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + + <_WasmAssembliesInternal Remove="@(_WasmSatelliteAssemblies)" /> @@ -142,7 +148,7 @@ MainJS="$(WasmMainJSPath)" Assemblies="@(_WasmAssembliesInternal)" InvariantGlobalization="$(InvariantGlobalization)" - SatelliteAssemblies="@(WasmSatelliteAssemblies)" + SatelliteAssemblies="@(_WasmSatelliteAssemblies)" FilesToIncludeInFileSystem="@(WasmFilesToIncludeInFileSystem)" IcuDataFileName="$(WasmIcuDataFileName)" RemoteSources="@(WasmRemoteSources)" @@ -193,11 +199,5 @@ - - - diff --git a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index 0a0b915ad43..cdd5e95a4e2 100644 --- a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -33,17 +33,15 @@ - - - - + <_ExtraFiles Include="$(ExtraFilesPath)**\*" /> - <_SatelliteAssembliesForVFS Include="@(WasmSatelliteAssemblies)" /> + <_SatelliteAssembliesForVFS Include="$(OriginalPublishDir)**\*.resources.dll" /> + <_SatelliteAssembliesForVFS CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> <_SatelliteAssembliesForVFS TargetPath="%(CultureName)\%(FileName)%(Extension)" /> diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 49d984490a6..660272e268a 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -189,6 +189,13 @@ public class WasmAppBuilder : Task { string culture = assembly.GetMetadata("CultureName") ?? string.Empty; string fullPath = assembly.GetMetadata("Identity"); + if (string.IsNullOrEmpty(culture)) + { + Log.LogWarning($"Missing CultureName metadata for satellite assembly {fullPath}"); + continue; + } + // FIXME: validate the culture? + string name = Path.GetFileName(fullPath); string directory = Path.Combine(AppDir, config.AssemblyRoot, culture); Directory.CreateDirectory(directory); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 99e721dfa65..b87ff5c70bf 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -8,9 +8,11 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -29,7 +31,6 @@ namespace Wasm.Build.Tests protected static readonly string s_emsdkPath; protected static readonly bool s_skipProjectCleanup; protected static readonly string s_xharnessRunnerCommand; - protected string? _projectDir; protected readonly ITestOutputHelper _testOutput; protected string _logPath; @@ -139,10 +140,17 @@ namespace Wasm.Build.Tests .WithRunHosts(host) .UnwrapItemsAsArrays(); - protected void RunAndTestWasmApp(BuildArgs buildArgs, RunHost host, string id, Action test, string? buildDir=null, int expectedExitCode=0, string? args=null) + protected string RunAndTestWasmApp(BuildArgs buildArgs, + RunHost host, + string id, + Action? test=null, + string? buildDir = null, + int expectedExitCode = 0, + string? args = null, + Dictionary? envVars = null) { buildDir ??= _projectDir; - Dictionary? envVars = new(); + envVars ??= new(); envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { @@ -180,6 +188,11 @@ namespace Wasm.Build.Tests Assert.DoesNotContain("AOT: image 'System.Private.CoreLib' found.", output); Assert.DoesNotContain($"AOT: image '{buildArgs.ProjectName}' found.", output); } + + if (test != null) + test(output); + + return output; } protected static string RunWithXHarness(string testCommand, string testLogPath, string projectName, string bundleDir, @@ -209,6 +222,8 @@ namespace Wasm.Build.Tests args.Append($" --run {projectName}.dll"); args.Append($" {appArgs ?? string.Empty}"); + _testOutput.WriteLine(string.Empty); + _testOutput.WriteLine($"---------- Running with {testCommand} ---------"); var (exitCode, output) = RunProcess("dotnet", _testOutput, args: args.ToString(), workingDir: bundleDir, @@ -251,14 +266,21 @@ namespace Wasm.Build.Tests runtime-test.js ##EXTRA_PROPERTIES## + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## "; - protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraProperties=null, string projectTemplate=SimpleProjectTemplate) + protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProperties="", string extraItems="", string insertAtEnd="", string projectTemplate=SimpleProjectTemplate) { if (buildArgs.AOT) - extraProperties = $"{extraProperties}\ntrue\n"; + extraProperties = $"{extraProperties}\ntrue\nfalse\n"; - string projectContents = projectTemplate.Replace("##EXTRA_PROPERTIES##", extraProperties ?? string.Empty); + string projectContents = projectTemplate + .Replace("##EXTRA_PROPERTIES##", extraProperties) + .Replace("##EXTRA_ITEMS##", extraItems) + .Replace("##INSERT_AT_END##", insertAtEnd); return buildArgs with { ProjectFileContents = projectContents }; } @@ -273,10 +295,10 @@ namespace Wasm.Build.Tests { if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) { - Console.WriteLine ($"Using existing build found at {product.BuildPath}, with build log at {product.LogFile}"); + Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}"); - Assert.True(product.Result, $"Found existing build at {product.BuildPath}, but it had failed. Check build log at {product.LogFile}"); - _projectDir = product.BuildPath; + Assert.True(product.Result, $"Found existing build at {product.ProjectDir}, but it had failed. Check build log at {product.LogFile}"); + _projectDir = product.ProjectDir; // use this test's id for the run logs _logPath = Path.Combine(s_logRoot, id); @@ -297,7 +319,6 @@ namespace Wasm.Build.Tests throw new Exception("_projectDir should be set, to use createProject=false"); } - StringBuilder sb = new(); sb.Append("publish"); sb.Append(s_defaultBuildArgs); @@ -305,6 +326,7 @@ namespace Wasm.Build.Tests sb.Append($" /p:Configuration={buildArgs.Config}"); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog"); + _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); if (buildArgs.ExtraBuildArgs != null) @@ -378,8 +400,14 @@ namespace Wasm.Build.Tests { string nativeDir = GetRuntimeNativeDir(); - AssertFile(Path.Combine(nativeDir, "dotnet.wasm"), Path.Combine(bundleDir, "dotnet.wasm"), "Expected dotnet.wasm to be same as the runtime pack", same: fromRuntimePack); - AssertFile(Path.Combine(nativeDir, "dotnet.js"), Path.Combine(bundleDir, "dotnet.js"), "Expected dotnet.js to be same as the runtime pack", same: fromRuntimePack); + AssertNativeFile("dotnet.wasm"); + AssertNativeFile("dotnet.js"); + + void AssertNativeFile(string file) + => AssertFile(Path.Combine(nativeDir, file), + Path.Combine(bundleDir, file), + $"Expected {file} to be {(fromRuntimePack ? "the same as" : "different from")} the runtime pack", + same: fromRuntimePack); } protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) @@ -464,6 +492,7 @@ namespace Wasm.Build.Tests _testOutput.WriteLine($"Running {path} {args}"); Console.WriteLine($"Running: {path}: {args}"); Console.WriteLine($"WorkingDirectory: {workingDir}"); + _testOutput.WriteLine($"WorkingDirectory: {workingDir}"); StringBuilder outputBuilder = new (); var processStartInfo = new ProcessStartInfo { @@ -584,7 +613,7 @@ namespace Wasm.Build.Tests - + @@ -594,5 +623,5 @@ namespace Wasm.Build.Tests } public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs); - public record BuildProduct(string BuildPath, string LogFile, bool Result); + public record BuildProduct(string ProjectDir, string LogFile, bool Result); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 6d01f20a842..753f8c40c2a 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -49,7 +49,7 @@ namespace Wasm.Build.Tests extraProperties = $"{extraProperties}{invariantGlobalization}"; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); @@ -76,8 +76,8 @@ namespace Wasm.Build.Tests hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false); string expectedOutputString = invariantGlobalization == true - ? "False - en (ES)" - : "True - Invariant Language (Invariant Country)"; + ? "True - Invariant Language (Invariant Country)" + : "False - es (ES)"; RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains(expectedOutputString, output), host: host, id: id); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs index df920577cea..b97a0490d2e 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs @@ -77,7 +77,7 @@ namespace Wasm.Build.Tests string programText = projectContents.Replace("##CODE##", code); buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = programText }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index da7b33d0a0c..9f87757175c 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -39,7 +39,7 @@ namespace Wasm.Build.Tests ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -54,7 +54,7 @@ namespace Wasm.Build.Tests string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index aebaa43ba5d..ecbcbdd75c0 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -27,7 +27,7 @@ namespace Wasm.Build.Tests bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, $"{(nativeRelink ? "true" : "false")}"); + buildArgs = ExpandBuildArgs(buildArgs, $"{(nativeRelink ? "true" : "false")}"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs new file mode 100644 index 00000000000..e9d973650dc --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class SatelliteAssembliesTests : BuildTestBase + { + public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + public static IEnumerable SatelliteAssemblyTestData(bool aot, bool relinking, RunHost host) + => ConfigWithAOTData(aot) + .Multiply( + new object?[] { relinking, "es-ES", "got: hola" }, + new object?[] { relinking, null, "got: hello" }, + new object?[] { relinking, "ja-JP", "got: \u3053\u3093\u306B\u3061\u306F" }) + .WithRunHosts(host) + .UnwrapItemsAsArrays(); + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromMainAssembly(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_from_main_asm"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp( + buildArgs, expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromProjectReference(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_proj_ref"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest("LibraryWithResources.words", "LibraryWithResources.Class1"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp(buildArgs, + expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + +#pragma warning disable xUnit1026 + [Theory] + [BuildAndRun(host: RunHost.None, aot: true)] + public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string id) + { + string projectName = $"check_sat_asm_not_aot"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $@" + -O0 + -O0", + extraItems: $""); + + System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + dotnetWasmFromRuntimePack: false, + id: id); + + var bitCodeFileNames = Directory.GetFileSystemEntries(Path.Combine(_projectDir!, "obj"), "*.dll.bc", SearchOption.AllDirectories) + .Select(path => Path.GetFileName(path)) + .ToArray(); + + // sanity check, in case we change file extensions + Assert.Contains($"{projectName}.dll.bc", bitCodeFileNames); + + Assert.Empty(bitCodeFileNames.Where(file => file.EndsWith(".resources.dll.bc"))); + } +#pragma warning restore xUnit1026 + + private void CreateProgramForCultureTest(string resourceName, string typeName) + => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), + s_cultureResourceTestProgram + .Replace("##RESOURCE_NAME##", resourceName) + .Replace("##TYPE_NAME##", typeName)); + + private const string s_resourcesProjectTemplate = + @$" + + {s_targetFramework} + Exe + true + runtime-test.js + ##EXTRA_PROPERTIES## + + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## + "; + + private static string s_cultureResourceTestProgram = @" +using System; +using System.Runtime.CompilerServices; +using System.Globalization; +using System.Resources; +using System.Threading; + +namespace ResourcesTest +{ + public class TestClass + { + public static int Main(string[] args) + { + if (args.Length == 1) + { + string cultureToTest = args[0]; + var newCulture = new CultureInfo(cultureToTest); + Thread.CurrentThread.CurrentCulture = newCulture; + Thread.CurrentThread.CurrentUICulture = newCulture; + } + + var currentCultureName = Thread.CurrentThread.CurrentCulture.Name; + + var rm = new ResourceManager(""##RESOURCE_NAME##"", typeof(##TYPE_NAME##).Assembly); + Console.WriteLine($""For '{currentCultureName}' got: {rm.GetString(""hello"")}""); + + return 42; + } + } +}"; + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index a491d972af2..e84a151bd5b 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -20,7 +20,7 @@ namespace Wasm.Build.Tests public void RemoveFromCache(string buildPath) { - KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.BuildPath == buildPath).SingleOrDefault(); + KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault(); if (foundKvp == null) throw new Exception($"Could not find build path {buildPath} in cache to remove."); @@ -36,7 +36,7 @@ namespace Wasm.Build.Tests Console.WriteLine ($"============== DELETING THE BUILDS ============="); foreach (var kvp in _buildPaths.Values) { - RemoveDirectory(kvp.BuildPath); + RemoveDirectory(kvp.ProjectDir); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 9ed56506d79..20a19724307 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -9,6 +9,7 @@ xunit false true + false TEST_DEBUG_CONFIG_ALSO false @@ -20,8 +21,6 @@ <_PreCommand Condition="'$(OS)' != 'Windows_NT'">WasmBuildSupportDir=%24{HELIX_CORRELATION_PAYLOAD}/build <_PreCommand Condition="'$(OS)' == 'Windows_NT'">set WasmBuildSupportDir=%HELIX_CORRELATION_PAYLOAD%/build & - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 & @@ -38,5 +37,7 @@ + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 3778d1fcdaa..e426c9523d9 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -72,7 +72,7 @@ namespace Wasm.Build.Tests ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -88,6 +88,9 @@ namespace Wasm.Build.Tests public static int Main() { Console.WriteLine($""tc: {Environment.TickCount}, tc64: {Environment.TickCount64}""); + + // if this gets printed, then we didn't crash! + Console.WriteLine(""Hello, World!""); return 42; } }"; @@ -109,11 +112,11 @@ namespace Wasm.Build.Tests BuildArgs buildArgs, RunHost host, string id, - string? extraProperties = null, + string extraProperties = "", bool? dotnetWasmFromRuntimePack = null) { buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs new file mode 100644 index 00000000000..4674bfd7d0b --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace LibraryWithResources +{ + public class Class1 + { + } +} diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props new file mode 100644 index 00000000000..058246e4086 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets new file mode 100644 index 00000000000..058246e4086 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj new file mode 100644 index 00000000000..91961dd7d6b --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj @@ -0,0 +1,10 @@ + + + + net5.0 + + + + + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx new file mode 100644 index 00000000000..775397b15a2 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx new file mode 100644 index 00000000000..c843811244c --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ã•よã†ãªã‚‰ + + + + ã“ã‚“ã«ã¡ã¯ + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.resx b/src/tests/BuildWasmApps/testassets/resx/words.resx new file mode 100644 index 00000000000..c3d5a787420 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + From 98db254b38c71713ca7a2f7e1240f01de73c022c Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Tue, 22 Jun 2021 12:44:55 -0400 Subject: [PATCH 044/926] Import the correct MacCatalyst workload pack (#54558) In https://github.com/dotnet/runtime/pull/54361 there was an incorrect import of the maccatalyst aot workload pack. This fix corrects the problem. Fixes https://github.com/dotnet/runtime/issues/54494 --- .../WorkloadManifest.targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 0e454c9bd50..ca7309a6612 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -26,8 +26,7 @@ - - + From 62b8d02fdbf4288bb1cb3ea60d31a96b0f9dd14f Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 09:57:09 -0700 Subject: [PATCH 045/926] Disable failing tests under GCStress (#54532) https://github.com/dotnet/runtime/issues/51477 https://github.com/dotnet/runtime/issues/53359 --- .../ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj | 2 ++ .../eventpipe/processenvironment/processenvironment.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj index 9a81c001eb8..7ae26166948 100644 --- a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj +++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj @@ -4,6 +4,8 @@ true true + + true diff --git a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj index 8633e6ca188..97976e33e50 100644 --- a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj +++ b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj @@ -8,6 +8,8 @@ true true true + + true From 245dddc95ce85ee810cdcbd0727662d4c716dbe6 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Tue, 22 Jun 2021 13:36:57 -0400 Subject: [PATCH 046/926] [mono]Re-enable runtime tests on Android arm64 (#49662) * Re-enable runtime tests on Android arm64 * disable irrelevant CI lanes * Disable more dotnet-linker-tests and runtime-dev-innerloop CI lanes * Comment out definition for DISABLE_LOGGING * Remove defining logging for android * Extend app installation timeout limit * Fix command prefix for windows * Enable tests excluded only for Android arm64 * Disable failed test on arm64 * Revert hacks * Revert unintended changes * More... * Only run tests on arm64 for rolling build * Fix merge error --- eng/pipelines/runtime-staging.yml | 14 +++++++----- .../Coreclr.TestWrapper/MobileAppHandler.cs | 16 +++++++++++--- src/tests/issues.targets | 22 ++----------------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 5def476efef..36ff5c79c00 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -347,7 +347,7 @@ jobs: buildConfig: Release runtimeFlavor: mono platforms: - #- Android_arm64 # disabled due to https://github.com/dotnet/runtime/issues/47850 + - Android_arm64 variables: - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}: - name: _HelixSource @@ -369,11 +369,13 @@ jobs: eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), eq(variables['isFullMatrix'], true)) - # extra steps, run tests - extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml - extraStepsParameters: - creator: dotnet-bot - testRunNamePrefixSuffix: Mono_$(_BuildConfig) + # don't run tests on PRs until we can get significantly more devices + ${{ if eq(variables['isFullMatrix'], true) }}: + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) # Run disabled installer tests on Linux x64 - template: /eng/pipelines/common/platform-matrix.yml diff --git a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs index 7a80d1e2b3b..b712246c6f3 100644 --- a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs +++ b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs @@ -29,7 +29,7 @@ namespace CoreclrTestLib string xharnessCmd; string cmdStr; string appExtension; - int timeout = 240000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 4 mins on CI + int timeout = 600000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI if(String.IsNullOrEmpty(dotnetCmd_raw)) { @@ -120,8 +120,18 @@ namespace CoreclrTestLib private static string ConvertCmd2Arg(string cmd) { cmd.Replace("\"", "\"\""); - var result = $"-c \"{cmd}\""; - return result; + + string cmdPrefix; + if(OperatingSystem.IsWindows()) + { + cmdPrefix = "/c"; + } + else + { + cmdPrefix = "-c"; + } + + return $"{cmdPrefix} \"{cmd}\""; } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 1daa9fda46d..e34d49e1455 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3146,26 +3146,8 @@ - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.HardwareIntrinsics) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.Methodical*) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.IL_Conformance) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT) - - - https://github.com/dotnet/runtime/issues/45568 (workitem JIT.Regression.CLR-x86-JIT.V1-M12-M13) - - - https://github.com/dotnet/runtime/issues/45568 (workitem PayloadGroup0) + + https://github.com/dotnet/runtime/issues/52781 From 4fd380a71666afb8572b5e6559cafcbd72a469b9 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Tue, 22 Jun 2021 15:00:17 -0400 Subject: [PATCH 047/926] [Android] Fix AndroidAppBuilder to work w/ AOT+LLVM (#53643) We were missing a few key additions to make sure we can test against AOT+LLVM. This change will make sure we link against all the .dll-llvm.o files produced from the AOT compiler and include the right mtriple for each architecture. Fixes #53628 --- src/mono/mono/mini/aot-compiler.c | 16 ++++++----- src/mono/mono/mini/mini-arm.c | 2 ++ src/tasks/AndroidAppBuilder/ApkBuilder.cs | 27 ++++++++++++------- .../Templates/CMakeLists-android.txt | 9 ++++++- .../AotCompilerTask/MonoAOTCompiler.props | 5 +++- ...droid.Device_Emulator.Aot_Llvm.Test.csproj | 17 ++++++++++++ .../Device_Emulator/AOT_LLVM/Program.cs | 13 +++++++++ 7 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 358c4e60c53..b36fea0cdf4 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -1115,17 +1115,15 @@ arch_init (MonoAotCompile *acfg) acfg->llvm_label_prefix = ""; acfg->user_symbol_prefix = ""; -#if defined(TARGET_X86) -#ifdef TARGET_ANDROID - g_string_append_printf (acfg->llc_args, " -mtriple=i686-none-linux-android21"); -#else +#if TARGET_X86 || TARGET_AMD64 const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu; - g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic"); #endif + +#if defined(TARGET_X86) + g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic"); #endif #if defined(TARGET_AMD64) - const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu; g_string_append_printf (acfg->llc_args, " -march=x86-64 %s", has_custom_args ? "" : "-mcpu=generic"); /* NOP */ acfg->align_pad_value = 0x90; @@ -1159,7 +1157,13 @@ arch_init (MonoAotCompile *acfg) g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16 -float-abi=hard"); g_string_append (acfg->as_args, " -mfpu=vfp3"); #elif defined(ARM_FPU_VFP) + +#if defined(TARGET_ARM) + // +d16 triggers a warning on arm + g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon"); +#else g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16"); +#endif g_string_append (acfg->as_args, " -mfpu=vfp3"); #else g_string_append (acfg->llc_args, " -mattr=+soft-float"); diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index 1f4ec81d761..4fc43ee3071 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -873,6 +873,8 @@ mono_arch_init (void) have a way to properly detect CPU features on it. */ thumb_supported = TRUE; iphone_abi = TRUE; +#elif defined(TARGET_ANDROID) + thumb_supported = TRUE; #else thumb_supported = mono_hwcap_arm_has_thumb; thumb2_supported = mono_hwcap_arm_has_thumb2; diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index ca7c4922ddb..346f854c383 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using Microsoft.Build.Framework; public class ApkBuilder @@ -122,18 +123,29 @@ public class ApkBuilder throw new ArgumentException($"{buildToolsFolder} was not found."); } - var assemblerFiles = new List(); + var assemblerFiles = new StringBuilder(); + var assemblerFilesToLink = new StringBuilder(); foreach (ITaskItem file in Assemblies) { // use AOT files if available var obj = file.GetMetadata("AssemblerFile"); + var llvmObj = file.GetMetadata("LlvmObjectFile"); + if (!string.IsNullOrEmpty(obj)) { - assemblerFiles.Add(obj); + var name = Path.GetFileNameWithoutExtension(obj); + assemblerFiles.AppendLine($"add_library({name} OBJECT {obj})"); + assemblerFilesToLink.AppendLine($" {name}"); + } + + if (!string.IsNullOrEmpty(llvmObj)) + { + var name = Path.GetFileNameWithoutExtension(llvmObj); + assemblerFilesToLink.AppendLine($" {llvmObj}"); } } - if (ForceAOT && !assemblerFiles.Any()) + if (ForceAOT && assemblerFiles.Length == 0) { throw new InvalidOperationException("Need list of AOT files."); } @@ -261,12 +273,9 @@ public class ApkBuilder nativeLibraries += $" {monoRuntimeLib}{Environment.NewLine}"; } - string aotSources = ""; - foreach (string asm in assemblerFiles) - { - // these libraries are linked via modules.c - aotSources += $" {asm}{Environment.NewLine}"; - } + nativeLibraries += assemblerFilesToLink.ToString(); + + string aotSources = assemblerFiles.ToString(); string cmakeLists = Utils.GetEmbeddedResource("CMakeLists-android.txt") .Replace("%MonoInclude%", monoRuntimeHeaders) diff --git a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt index c74d4625af0..7b602ad6a30 100644 --- a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt +++ b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt @@ -9,12 +9,19 @@ if(NOT USE_LLVM) add_compile_options(-no-integrated-as) endif() +# Prevent the warning: shared library text segment is not shareable which is treated as an error +if (NOT ANDROID_ABI STREQUAL "arm64-v8a") + add_link_options(-Wl,--no-warn-shared-textrel) +endif() + add_library( monodroid SHARED monodroid.c %AotModulesSource% - %AotSources%) +) + +%AotSources% %Defines% diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index 097758ae0c7..159c01ff3f2 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -14,7 +14,10 @@ - + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj new file mode 100644 index 00000000000..3ec5bf5e4b6 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj @@ -0,0 +1,17 @@ + + + Exe + false + true + true + $(NetCoreAppCurrent) + Android.Device_Emulator.Aot_Llvm.Test.dll + 42 + true + true + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs new file mode 100644 index 00000000000..7dcc0f375db --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +public static class Program +{ + public static int Main(string[] args) + { + Console.WriteLine("Hello, Android!"); // logcat + return 42; + } +} From de0a408d34045a5e831225b81d5860a8b59ddfdd Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 13:12:05 -0700 Subject: [PATCH 048/926] Disable failing test BasicTestWithMcj under GCStress (#54566) Tracking: https://github.com/dotnet/runtime/issues/54203 --- .../baseservices/TieredCompilation/BasicTestWithMcj.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj b/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj index d4ed48b297d..a25cf031089 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTestWithMcj.csproj @@ -4,6 +4,8 @@ true true 0 + + true true From 26857bff9b62e87083ef1ba4ec0c45b3b58f3095 Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Tue, 22 Jun 2021 13:27:31 -0700 Subject: [PATCH 049/926] get rid of an unnecessary join (#54312) I think this join was left there by accident when we were doing the write watch feature. I'm puzzled why I didn't notice it. the comment was also inconsistent which strengthens my belief that this was a mistake. we don't need a join here (there used to not be a join) and disabling the dirty pages tracking can be done anywhere while the EE is stopped since write barriers cannot be invoked. --- src/coreclr/gc/gc.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index f4fdf3177ef..6f99f1ad78d 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -641,6 +641,10 @@ process_sync_log_stats() #ifndef DACCESS_COMPILE uint32_t g_num_active_processors = 0; +// Note that when a join is no longer used we still keep the values here because +// tooling already recognized them as having the meaning they were assigned originally. +// It doesn't break tooling if we stop using them but does if we assign a new meaning +// to them. enum gc_join_stage { gc_join_init_cpu_mapping = 0, @@ -681,6 +685,7 @@ enum gc_join_stage gc_join_after_commit_soh_no_gc = 35, gc_join_expand_loh_no_gc = 36, gc_join_final_no_gc = 37, + // No longer in use but do not remove, see comments for this enum. gc_join_disable_software_write_watch = 38, gc_join_max = 39 }; @@ -31805,23 +31810,6 @@ void gc_heap::background_mark_phase () //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH"); concurrent_print_time_delta ("NRre LOH"); -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_disable_software_write_watch); - if (bgc_t_join.joined()) -#endif // MULTIPLE_HEAPS - { - // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to - // avoid further perf penalty after the runtime is restarted - SoftwareWriteWatch::DisableForGCHeap(); - -#ifdef MULTIPLE_HEAPS - dprintf(3, ("Restarting BGC threads after disabling software write watch")); - bgc_t_join.restart(); -#endif // MULTIPLE_HEAPS - } -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count)); bgc_overflow_count = 0; @@ -31846,6 +31834,12 @@ void gc_heap::background_mark_phase () if (bgc_t_join.joined()) #endif //MULTIPLE_HEAPS { +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // The runtime is suspended, take this opportunity to pause tracking written pages to + // avoid further perf penalty after the runtime is restarted + SoftwareWriteWatch::DisableForGCHeap(); +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc); #ifdef MULTIPLE_HEAPS From d0a102843040a3950827c498fa05da6e9f55a2b9 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 22 Jun 2021 13:28:59 -0700 Subject: [PATCH 050/926] Fix a memory corruption caused by an integer overflow (#54510) --- src/coreclr/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 6f99f1ad78d..6a6c63d1ac2 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -10813,7 +10813,7 @@ void gc_heap::return_free_region (heap_segment* region) heap_segment_mem (region), num_basic_regions, num_free_regions)); for (int i = 0; i < num_basic_regions; i++) { - uint8_t* basic_region_start = region_start + (i << min_segment_size_shr); + uint8_t* basic_region_start = region_start + ((size_t)i << min_segment_size_shr); heap_segment* basic_region = get_region_info (basic_region_start); heap_segment_allocated (basic_region) = 0; #ifdef MULTIPLE_HEAPS From 9da9bfcaffca3329ad9efdfccd89ef9e9aaf2930 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 22 Jun 2021 14:07:54 -0700 Subject: [PATCH 051/926] Improve performance of IsRootScope check (#54555) - Stash a field instead of doing an equality comparison. Fixes #54351 --- .../src/ServiceLookup/ServiceProviderEngineScope.cs | 5 +++-- .../src/ServiceProvider.cs | 4 ++-- .../tests/DI.Tests/ServiceProviderEngineScopeTests.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index 06498ff98ba..4ad8b1579a0 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -16,10 +16,11 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup private bool _disposed; private List _disposables; - public ServiceProviderEngineScope(ServiceProvider provider) + public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope) { ResolvedServices = new Dictionary(); RootProvider = provider; + IsRootScope = isRootScope; } internal Dictionary ResolvedServices { get; } @@ -29,7 +30,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // For other scopes, it protects ResolvedServices and the list of disposables internal object Sync => ResolvedServices; - public bool IsRootScope => this == RootProvider.Root; + public bool IsRootScope { get; } internal ServiceProvider RootProvider { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index d21d9807f5f..fb7052ec8ea 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -36,7 +36,7 @@ namespace Microsoft.Extensions.DependencyInjection _createServiceAccessor = CreateServiceAccessor; _realizedServices = new ConcurrentDictionary>(); - Root = new ServiceProviderEngineScope(this); + Root = new ServiceProviderEngineScope(this, isRootScope: true); CallSiteFactory = new CallSiteFactory(serviceDescriptors); // The list of built in services that aren't part of the list of service descriptors // keep this in sync with CallSiteFactory.IsService @@ -173,7 +173,7 @@ namespace Microsoft.Extensions.DependencyInjection ThrowHelper.ThrowObjectDisposedException(); } - return new ServiceProviderEngineScope(this); + return new ServiceProviderEngineScope(this, isRootScope: false); } private ServiceProviderEngine GetEngine() diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs index 5c19caa922e..d43752db21e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup public void DoubleDisposeWorks() { var provider = new ServiceProvider(new ServiceCollection(), ServiceProviderOptions.Default); - var serviceProviderEngineScope = new ServiceProviderEngineScope(provider); + var serviceProviderEngineScope = new ServiceProviderEngineScope(provider, isRootScope: true); serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null); serviceProviderEngineScope.Dispose(); serviceProviderEngineScope.Dispose(); From 7a3343f22f7caab6cd04c94e8ce8e34971e791d3 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 22 Jun 2021 17:25:36 -0400 Subject: [PATCH 052/926] Add `SkipEnabledCheck` on `LoggerMessageAttribute` (#54305) * Add SkipEnabledCheck on LoggerMessageAttribute * Make logging generator more robust with null input * Adds LoggerMessageAttribute ctor overload * properly identify misconfigured input --- .../gen/LoggerMessageGenerator.Emitter.cs | 35 ++++-- .../gen/LoggerMessageGenerator.Parser.cs | 114 +++++++++++++----- ...crosoft.Extensions.Logging.Abstractions.cs | 2 + .../src/LoggerMessageAttribute.cs | 19 +++ .../TestWithDefaultValues.generated.txt | 57 +++++++++ .../TestWithSkipEnabledCheck.generated.txt | 18 +++ .../LoggerMessageGeneratedCodeTests.cs | 58 +++++++++ .../LoggerMessageGeneratorEmitterTests.cs | 42 ++++++- .../LoggerMessageGeneratorParserTests.cs | 104 ++++++++++++++++ .../TestClasses/EventNameTestExtensions.cs | 3 + .../TestClasses/LevelTestExtensions.cs | 3 + .../TestClasses/MessageTestExtensions.cs | 6 + .../TestClasses/SkipEnabledCheckExtensions.cs | 15 +++ 13 files changed, 428 insertions(+), 48 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs index 2b0bc4eca00..908efaf6afe 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs @@ -381,34 +381,45 @@ namespace {lc.Namespace} GenParameters(lm); _builder.Append($@") - {nestedIndentation}{{ + {nestedIndentation}{{"); + + string enabledCheckIndentation = lm.SkipEnabledCheck ? "" : " "; + if (!lm.SkipEnabledCheck) + { + _builder.Append($@" {nestedIndentation}if ({logger}.IsEnabled({level})) {nestedIndentation}{{"); + } if (UseLoggerMessageDefine(lm)) { _builder.Append($@" - {nestedIndentation}__{lm.Name}Callback({logger}, "); + {nestedIndentation}{enabledCheckIndentation}__{lm.Name}Callback({logger}, "); GenCallbackArguments(lm); - _builder.Append(@$"{exceptionArg});"); + _builder.Append($"{exceptionArg});"); } else { _builder.Append($@" - {nestedIndentation}{logger}.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - "); - GenHolder(lm); - _builder.Append($@", - {exceptionArg}, - __{lm.Name}Struct.Format);"); + {nestedIndentation}{enabledCheckIndentation}{logger}.Log( + {nestedIndentation}{enabledCheckIndentation}{level}, + {nestedIndentation}{enabledCheckIndentation}new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {nestedIndentation}{enabledCheckIndentation}"); + GenHolder(lm); + _builder.Append($@", + {nestedIndentation}{enabledCheckIndentation}{exceptionArg}, + {nestedIndentation}{enabledCheckIndentation}__{lm.Name}Struct.Format);"); + } + + if (!lm.SkipEnabledCheck) + { + _builder.Append($@" + {nestedIndentation}}}"); } _builder.Append($@" - {nestedIndentation}}} {nestedIndentation}}}"); static string GetException(LoggerMethod lm) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index 534249c2326..4a995039104 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -9,6 +9,7 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; namespace Microsoft.Extensions.Logging.Generators { @@ -28,7 +29,7 @@ namespace Microsoft.Extensions.Logging.Generators } /// - /// Gets the set of logging classes or structs containing methods to output. + /// Gets the set of logging classes containing methods to output. /// public IReadOnlyList GetLogClasses(IEnumerable classes) { @@ -83,7 +84,7 @@ namespace Microsoft.Extensions.Logging.Generators bool multipleLoggerFields = false; ids.Clear(); - foreach (var member in classDec.Members) + foreach (MemberDeclarationSyntax member in classDec.Members) { var method = member as MethodDeclarationSyntax; if (method == null) @@ -93,6 +94,9 @@ namespace Microsoft.Extensions.Logging.Generators } sm ??= _compilation.GetSemanticModel(classDec.SyntaxTree); + IMethodSymbol logMethodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol; + Debug.Assert(logMethodSymbol != null, "log method is present."); + (int eventId, int? level, string message, string? eventName, bool skipEnabledCheck) = (-1, null, string.Empty, null, false); foreach (AttributeListSyntax mal in method.AttributeLists) { @@ -105,7 +109,78 @@ namespace Microsoft.Extensions.Logging.Generators continue; } - (int eventId, int? level, string message, string? eventName) = ExtractAttributeValues(ma.ArgumentList!, sm); + bool hasMisconfiguredInput = false; + ImmutableArray? boundAttrbutes = logMethodSymbol?.GetAttributes(); + + if (boundAttrbutes == null) + { + continue; + } + + foreach (AttributeData attributeData in boundAttrbutes) + { + // supports: [LoggerMessage(0, LogLevel.Warning, "custom message")] + // supports: [LoggerMessage(eventId: 0, level: LogLevel.Warning, message: "custom message")] + if (attributeData.ConstructorArguments.Any()) + { + foreach (TypedConstant typedConstant in attributeData.ConstructorArguments) + { + if (typedConstant.Kind == TypedConstantKind.Error) + { + hasMisconfiguredInput = true; + } + } + + ImmutableArray items = attributeData.ConstructorArguments; + Debug.Assert(items.Length == 3); + + eventId = items[0].IsNull ? -1 : (int)GetItem(items[0]); + level = items[1].IsNull ? null : (int?)GetItem(items[1]); + message = items[2].IsNull ? "" : (string)GetItem(items[2]); + } + + // argument syntax takes parameters. e.g. EventId = 0 + // supports: e.g. [LoggerMessage(EventId = 0, Level = LogLevel.Warning, Message = "custom message")] + if (attributeData.NamedArguments.Any()) + { + foreach (KeyValuePair namedArgument in attributeData.NamedArguments) + { + TypedConstant typedConstant = namedArgument.Value; + if (typedConstant.Kind == TypedConstantKind.Error) + { + hasMisconfiguredInput = true; + } + else + { + TypedConstant value = namedArgument.Value; + switch (namedArgument.Key) + { + case "EventId": + eventId = (int)GetItem(value); + break; + case "Level": + level = value.IsNull ? null : (int?)GetItem(value); + break; + case "SkipEnabledCheck": + skipEnabledCheck = (bool)GetItem(value); + break; + case "EventName": + eventName = (string?)GetItem(value); + break; + case "Message": + message = value.IsNull ? "" : (string)GetItem(value); + break; + } + } + } + } + } + + if (hasMisconfiguredInput) + { + // skip further generator execution and let compiler generate the errors + break; + } IMethodSymbol? methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken); if (methodSymbol != null) @@ -119,6 +194,7 @@ namespace Microsoft.Extensions.Logging.Generators EventName = eventName, IsExtensionMethod = methodSymbol.IsExtensionMethod, Modifiers = method.Modifiers.ToString(), + SkipEnabledCheck = skipEnabledCheck }; ExtractTemplates(message, lm.TemplateMap, lm.TemplateList); @@ -435,35 +511,6 @@ namespace Microsoft.Extensions.Logging.Generators return (loggerField, false); } - private (int eventId, int? level, string message, string? eventName) ExtractAttributeValues(AttributeArgumentListSyntax args, SemanticModel sm) - { - int eventId = 0; - int? level = null; - string? eventName = null; - string message = string.Empty; - foreach (AttributeArgumentSyntax a in args.Arguments) - { - // argument syntax takes parameters. e.g. EventId = 0 - Debug.Assert(a.NameEquals != null); - switch (a.NameEquals.Name.ToString()) - { - case "EventId": - eventId = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!; - break; - case "EventName": - eventName = sm.GetConstantValue(a.Expression, _cancellationToken).ToString(); - break; - case "Level": - level = (int)sm.GetConstantValue(a.Expression, _cancellationToken).Value!; - break; - case "Message": - message = sm.GetConstantValue(a.Expression, _cancellationToken).ToString(); - break; - } - } - return (eventId, level, message, eventName); - } - private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) { _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); @@ -580,6 +627,8 @@ namespace Microsoft.Extensions.Logging.Generators return string.Empty; } + + private static object GetItem(TypedConstant arg) => arg.Kind == TypedConstantKind.Array ? arg.Values : arg.Value; } /// @@ -612,6 +661,7 @@ namespace Microsoft.Extensions.Logging.Generators public bool IsExtensionMethod; public string Modifiers = string.Empty; public string LoggerField = string.Empty; + public bool SkipEnabledCheck; } /// diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index e7b9034ea81..ef9d46564ec 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -119,10 +119,12 @@ namespace Microsoft.Extensions.Logging public sealed partial class LoggerMessageAttribute : System.Attribute { public LoggerMessageAttribute() { } + public LoggerMessageAttribute(int eventId, Microsoft.Extensions.Logging.LogLevel level, string message) { } public int EventId { get { throw null; } set { } } public string? EventName { get { throw null; } set { } } public Microsoft.Extensions.Logging.LogLevel Level { get { throw null; } set { } } public string Message { get { throw null; } set { } } + public bool SkipEnabledCheck { get { throw null; } set { } } } public partial class Logger : Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.ILogger { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs index b103ef31a4e..acb9af3d860 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessageAttribute.cs @@ -37,6 +37,20 @@ namespace Microsoft.Extensions.Logging /// public LoggerMessageAttribute() { } + /// + /// Initializes a new instance of the class + /// which is used to guide the production of a strongly-typed logging method. + /// + /// The log event Id. + /// The log level. + /// Format string of the log message. + public LoggerMessageAttribute(int eventId, LogLevel level, string message) + { + EventId = eventId; + Level = level; + Message = message; + } + /// /// Gets the logging event id for the logging method. /// @@ -59,5 +73,10 @@ namespace Microsoft.Extensions.Logging /// Gets the message text for the logging method. /// public string Message { get; set; } = ""; + + /// + /// Gets the flag to skip IsEnabled check for the logging method. + /// + public bool SkipEnabledCheck { get; set; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt new file mode 100644 index 00000000000..c61da37d27b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDefaultValues.generated.txt @@ -0,0 +1,57 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithDefaultValues + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __M0Struct : global::System.Collections.Generic.IReadOnlyList> + { + + public override string ToString() + { + + return $""; + } + + public static string Format(__M0Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => 1; + + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", ""), + + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 1; i++) + { + yield return this[i]; + } + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::Microsoft.Extensions.Logging.LogLevel level) + { + if (logger.IsEnabled(level)) + { + logger.Log( + level, + new global::Microsoft.Extensions.Logging.EventId(-1, nameof(M0)), + new __M0Struct(), + null, + __M0Struct.Format); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt new file mode 100644 index 00000000000..c4b242a0a9e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt @@ -0,0 +1,18 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithSkipEnabledCheck + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", true); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger) + { + __M0Callback(logger, null); + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index c9180b70f23..33d1ab7c901 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -178,6 +178,22 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Equal(string.Empty, logger.LastFormattedString); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M5(logger, LogLevel.Trace); + Assert.Null(logger.LastException); + Assert.Equal(string.Empty, logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(-1, logger.LastEventId.Id); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M6(logger, LogLevel.Trace); + Assert.Null(logger.LastException); + Assert.Equal(string.Empty, logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(6, logger.LastEventId.Id); + Assert.Equal(1, logger.CallCount); } [Fact] @@ -309,6 +325,14 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Equal("M9", logger.LastFormattedString); Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M10vs11(logger); + Assert.Null(logger.LastException); + Assert.Equal("event ID 10 vs. 11", logger.LastFormattedString); + Assert.Equal(LogLevel.Warning, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal(11, logger.LastEventId.Id); } [Fact] @@ -343,6 +367,40 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); Assert.Equal("CustomEventName", logger.LastEventId.Name); + + logger.Reset(); + EventNameTestExtensions.CustomEventName(logger); + Assert.Null(logger.LastException); + Assert.Equal("CustomEventName", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal("CustomEventName", logger.LastEventId.Name); + } + + [Fact] + public void SkipEnabledCheckTests() + { + var logger = new MockLogger(); + + logger.Reset(); + logger.Enabled = false; + Assert.False(logger.IsEnabled(LogLevel.Information)); + SkipEnabledCheckExtensions.LoggerMethodWithFalseSkipEnabledCheck(logger); + Assert.Null(logger.LastException); + Assert.Null(logger.LastFormattedString); + Assert.Equal((LogLevel)(-1), logger.LastLogLevel); + Assert.Equal(0, logger.CallCount); + Assert.Equal(default, logger.LastEventId); + + logger.Reset(); + logger.Enabled = false; + Assert.False(logger.IsEnabled(LogLevel.Debug)); + SkipEnabledCheckExtensions.LoggerMethodWithTrueSkipEnabledCheck(logger); + Assert.Null(logger.LastException); + Assert.Equal("Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + Assert.Equal("LoggerMethodWithTrueSkipEnabledCheck", logger.LastEventId.Name); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 433d601c3d9..917d87cd127 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -35,17 +35,51 @@ namespace Microsoft.Extensions.Logging.Generators.Tests } [Fact] - public async Task TestBaseline_TestWithTwoParams_Success() + public async Task TestBaseline_TestWithSkipEnabledCheck_Success() { string testSourceCode = @" namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { - internal static partial class TestWithTwoParams + internal static partial class TestWithSkipEnabledCheck { - [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {a1} {a2}"")] - public static partial void M0(ILogger logger, int a1, System.Collections.Generic.IEnumerable a2); + [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = ""Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check."", SkipEnabledCheck = true)] + public static partial void M0(ILogger logger); } }"; + await VerifyAgainstBaselineUsingFile("TestWithSkipEnabledCheck.generated.txt", testSourceCode); + } + + [Fact] + public async Task TestBaseline_TestWithDefaultValues_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class TestWithDefaultValues + { + [LoggerMessage] + public static partial void M0(ILogger logger, LogLevel level); + } +}"; + await VerifyAgainstBaselineUsingFile("TestWithDefaultValues.generated.txt", testSourceCode); + } + + [Theory] + [InlineData("EventId = 0, Level = LogLevel.Error, Message = \"M0 {a1} {a2}\"")] + [InlineData("eventId: 0, level: LogLevel.Error, message: \"M0 {a1} {a2}\"")] + [InlineData("0, LogLevel.Error, \"M0 {a1} {a2}\"")] + [InlineData("0, LogLevel.Error, \"M0 {a1} {a2}\", SkipEnabledCheck = false")] + public async Task TestBaseline_TestWithTwoParams_Success(string argumentList) + { + string testSourceCode = $@" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{{ + internal static partial class TestWithTwoParams + {{ + [LoggerMessage({argumentList})] + public static partial void M0(ILogger logger, int a1, System.Collections.Generic.IEnumerable a2); + }} +}}"; await VerifyAgainstBaselineUsingFile("TestWithTwoParams.generated.txt", testSourceCode); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 4991e416cad..8e5df4972cf 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -64,6 +64,90 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[0].Id); } + [Theory] + [InlineData("EventId = 0, Level = null, Message = \"This is a message with {foo}\"")] + [InlineData("eventId: 0, level: null, message: \"This is a message with {foo}\"")] + [InlineData("0, null, \"This is a message with {foo}\"")] + public async Task WithNullLevel_GeneratorWontFail(string argumentList) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + [LoggerMessage({argumentList})] + static partial void M1(ILogger logger, string foo); + + [LoggerMessage({argumentList})] + static partial void M2(ILogger logger, LogLevel level, string foo); + }} + "); + + Assert.Empty(diagnostics); + } + + [Theory] + [InlineData("EventId = null, Level = LogLevel.Debug, Message = \"This is a message with {foo}\"")] + [InlineData("eventId: null, level: LogLevel.Debug, message: \"This is a message with {foo}\"")] + [InlineData("null, LogLevel.Debug, \"This is a message with {foo}\"")] + public async Task WithNullEventId_GeneratorWontFail(string argumentList) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + [LoggerMessage({argumentList})] + static partial void M1(ILogger logger, string foo); + }} + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task WithNullMessage_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = null)] + static partial void M1(ILogger logger, string foo); + } + "); + + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); + Assert.Contains("Argument 'foo' is not referenced from the logging message", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + } + + [Fact] + public async Task WithNullSkipEnabledCheck_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""This is a message with {foo}"", SkipEnabledCheck = null)] + static partial void M1(ILogger logger, string foo); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task WithBadMisconfiguredInput_GeneratorWontFail() + { + IReadOnlyList diagnostics = await RunGenerator(@" + public static partial class C + { + [LoggerMessage(SkipEnabledCheck = 6)] + public static partial void M0(ILogger logger, LogLevel level); + + [LoggerMessage(eventId: true, level: LogLevel.Debug, message: ""misconfigured eventId as bool"")] + public static partial void M1(ILogger logger); + } + "); + + Assert.Empty(diagnostics); + } + [Fact] public async Task MissingTemplate() { @@ -266,6 +350,26 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Empty(diagnostics); } + [Theory] + [InlineData("false")] + [InlineData("true")] + [InlineData("null")] + public async Task UsingSkipEnabledCheck(string skipEnabledCheckValue) + { + IReadOnlyList diagnostics = await RunGenerator($@" + partial class C + {{ + public partial class WithLoggerMethodUsingSkipEnabledCheck + {{ + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"", SkipEnabledCheck = {skipEnabledCheckValue})] + static partial void M1(ILogger logger); + }} + }} + "); + + Assert.Empty(diagnostics); + } + [Fact] public async Task MissingExceptionType() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index 4c0ddf320aa..f41d615d043 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -7,5 +7,8 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0", EventName = "CustomEventName")] public static partial void M0(ILogger logger); + + [LoggerMessage(EventId = 2, Level = LogLevel.Trace, Message = "CustomEventName")] // EventName inferred from method name + public static partial void CustomEventName(ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index 5726aa02a4c..2251d198a2a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -34,5 +34,8 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses [LoggerMessage(EventId = 9, Message = "M9")] public static partial void M9(LogLevel level, ILogger logger); + + [LoggerMessage(eventId: 10, level: LogLevel.Warning, message: "event ID 10 vs. 11", EventId = 11)] + public static partial void M10vs11(ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index a30849288b5..8cad8db64cd 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -29,5 +29,11 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = "{p1}")] public static partial void M4(ILogger logger, string p1, int p2, int p3); #endif + + [LoggerMessage] + public static partial void M5(ILogger logger, LogLevel level); + + [LoggerMessage(EventId = 6, Message = "")] + public static partial void M6(ILogger logger, LogLevel level); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs new file mode 100644 index 00000000000..397acdf080c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SkipEnabledCheckExtensions.cs @@ -0,0 +1,15 @@ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class SkipEnabledCheckExtensions + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", SkipEnabledCheck = true)] + internal static partial void LoggerMethodWithTrueSkipEnabledCheck(ILogger logger); + + [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "M1", SkipEnabledCheck = false)] + internal static partial void LoggerMethodWithFalseSkipEnabledCheck(ILogger logger); + } +} \ No newline at end of file From ac106e87e1224bc81f28d82f1f1507dfd65ad975 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Tue, 22 Jun 2021 15:45:36 -0700 Subject: [PATCH 053/926] Set DLL flag on R2R binaries (#54533) This is required for R2R relocations to be processed by the OS loader on Windows 7. --- .../CodeGen/ReadyToRunObjectWriter.cs | 8 +--- .../ObjectWriter/R2RPEBuilder.cs | 48 +++++-------------- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index e54207ff00f..74da51246b6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -198,11 +198,7 @@ namespace ILCompiler.DependencyAnalysis if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { - headerBuilder = PEHeaderProvider.Create( - imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll, - dllCharacteristics: default(DllCharacteristics), - Subsystem.Unknown, - _nodeFactory.Target); + headerBuilder = PEHeaderProvider.Create(Subsystem.Unknown, _nodeFactory.Target); peIdProvider = new Func, BlobContentId>(content => BlobContentId.FromHash(CryptographicHashProvider.ComputeSourceHash(content))); timeDateStamp = null; r2rHeaderExportSymbol = _nodeFactory.Header; @@ -210,7 +206,7 @@ namespace ILCompiler.DependencyAnalysis else { PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); - headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target); + headerBuilder = PEHeaderProvider.Create(inputPeReader.PEHeaders.PEHeader.Subsystem, _nodeFactory.Target); timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; r2rHeaderExportSymbol = null; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 3f6764b0e80..c7212368ace 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -489,7 +489,7 @@ namespace ILCompiler.PEWriter DosHeaderSize + PESignatureSize + sizeof(short) + // Machine - sizeof(short); //NumberOfSections + sizeof(short); // NumberOfSections outputStream.Seek(seekSize, SeekOrigin.Begin); outputStream.Write(patchedTimestamp, 0, patchedTimestamp.Length); @@ -664,52 +664,28 @@ namespace ILCompiler.PEWriter } /// - /// Simple helper for filling in PE header information by either copying over - /// data from a pre-existing input PE header (used for single-assembly R2R files) - /// or by explicitly specifying the image characteristics (for composite R2R). + /// Simple helper for filling in PE header information. /// static class PEHeaderProvider { - /// - /// Copy PE headers into a PEHeaderBuilder used by PEBuilder. - /// - /// Headers to copy - /// Target architecture to set in the header - public static PEHeaderBuilder Copy(PEHeaders peHeaders, TargetDetails target) - { - return Create( - peHeaders.CoffHeader.Characteristics, - peHeaders.PEHeader.DllCharacteristics, - peHeaders.PEHeader.Subsystem, - target); - } - /// /// Fill in PE header information into a PEHeaderBuilder used by PEBuilder. /// - /// Relocs are not present in the PE executable - /// Extra DLL characteristics to apply /// Targeting subsystem /// Target architecture to set in the header - public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCharacteristics dllCharacteristics, Subsystem subsystem, TargetDetails target) + public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target) { bool is64BitTarget = target.PointerSize == sizeof(long); - imageCharacteristics &= ~(Characteristics.Bit32Machine | Characteristics.LargeAddressAware); - imageCharacteristics |= (is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine); + Characteristics imageCharacteristics = Characteristics.ExecutableImage | Characteristics.Dll; + imageCharacteristics |= is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine; - ulong imageBase = PE32HeaderConstants.ImageBase; - if (target.IsWindows && is64BitTarget && (imageBase <= uint.MaxValue)) - { - // Base addresses below 4 GiB are reserved for WoW on x64 and disallowed on ARM64. - // If the input assembly was compiled for anycpu, its base address is 32-bit and we need to fix it. - imageBase = (imageCharacteristics & Characteristics.Dll) != 0 ? PE64HeaderConstants.DllImageBase : PE64HeaderConstants.ExeImageBase; - } + ulong imageBase = is64BitTarget ? PE64HeaderConstants.DllImageBase : PE32HeaderConstants.ImageBase; int fileAlignment = 0x200; if (!target.IsWindows && !is64BitTarget) { - // To minimize wasted VA space on 32 bit systems align file to page bounaries (presumed to be 4K). + // To minimize wasted VA space on 32-bit systems, align file to page boundaries (presumed to be 4K) fileAlignment = 0x1000; } @@ -721,13 +697,11 @@ namespace ILCompiler.PEWriter sectionAlignment = fileAlignment; } - dllCharacteristics &= DllCharacteristics.AppContainer; - - // In Crossgen1, this is under a debug-specific condition 'if (0 == CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NoASLRForNgen))' - dllCharacteristics |= DllCharacteristics.DynamicBase; - // Without NxCompatible the PE executable cannot execute on Windows ARM64 - dllCharacteristics |= DllCharacteristics.NxCompatible | DllCharacteristics.TerminalServerAware; + DllCharacteristics dllCharacteristics = + DllCharacteristics.DynamicBase | + DllCharacteristics.NxCompatible | + DllCharacteristics.TerminalServerAware; if (is64BitTarget) { From 1f5033c333370334286861368205293abb0abee6 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 22 Jun 2021 19:08:12 -0600 Subject: [PATCH 054/926] Disable another failing MemoryCache test (#54578) --- .../tests/MemoryCacheSetAndRemoveTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs index e9e7fb4ebfc..fb15d973206 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/MemoryCacheSetAndRemoveTests.cs @@ -594,6 +594,7 @@ namespace Microsoft.Extensions.Caching.Memory } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/33993")] public void AddAndReplaceEntries_AreThreadSafe() { var cache = new MemoryCache(new MemoryCacheOptions From 9701ef9e6cf8844e2fb0452177affc1e70e1c108 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Tue, 22 Jun 2021 18:58:23 -0700 Subject: [PATCH 055/926] Keep obj node for ArrayIndex. (#54584) --- src/coreclr/jit/morph.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2d0d108a334..6d7f8c7bc6f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5509,8 +5509,16 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) fgSetRngChkTarget(indexAddr); } - // Change `tree` into an indirection and return. - tree->ChangeOper(GT_IND); + if (!tree->TypeIs(TYP_STRUCT)) + { + tree->ChangeOper(GT_IND); + } + else + { + DEBUG_DESTROY_NODE(tree); + tree = gtNewObjNode(elemStructType, indexAddr); + INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + } GenTreeIndir* const indir = tree->AsIndir(); indir->Addr() = indexAddr; bool canCSE = indir->CanCSE(); @@ -5520,9 +5528,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) indir->SetDoNotCSE(); } -#ifdef DEBUG - indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif // DEBUG + INDEBUG(indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); return indir; } From 8026e74e3d7c45f34dbea1da9dd25ff394f1afd6 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 22 Jun 2021 23:46:33 -0400 Subject: [PATCH 056/926] [wasm] Move AOT builds from `runtime-staging` to `runtime` (#54577) These builds have had ~2-3 failures in the last 14 days (~90 builds). --- eng/pipelines/runtime-staging.yml | 42 ------------------------------- eng/pipelines/runtime.yml | 42 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 36ff5c79c00..2352995df91 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -256,48 +256,6 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) -# -# Build the whole product using Mono and run libraries tests -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/common/global-build-job.yml - helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml - buildConfig: Release - runtimeFlavor: mono - platforms: - - Browser_wasm - variables: - # map dependencies variables to local variables - - name: librariesContainsChange - value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] - - name: monoContainsChange - value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] - jobParameters: - testGroup: innerloop - nameSuffix: AllSubsets_Mono_AOT - buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true - timeoutInMinutes: 180 - condition: >- - or( - eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), - eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), - eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), - eq(variables['isFullMatrix'], true)) - # extra steps, run tests - extraStepsTemplate: /eng/pipelines/libraries/helix.yml - extraStepsParameters: - creator: dotnet-bot - testRunNamePrefixSuffix: Mono_$(_BuildConfig) - extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true - scenarios: - - normal - condition: >- - or( - eq(variables['librariesContainsChange'], true), - eq(variables['monoContainsChange'], true), - eq(variables['isFullMatrix'], true)) - # # Build the whole product using Mono for Android and run runtime tests with interpreter # diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 148566ea7a2..931a65a05e1 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -347,6 +347,48 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# +# Build for Browser/wasm with RunAOTCompilation=true +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Browser_wasm + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_AOT + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true + scenarios: + - normal + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) + # Build and test libraries under single-file publishing - template: /eng/pipelines/common/platform-matrix.yml parameters: From f3556b35c66198ac054bb51bc29820d6a83d7c4f Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 Jun 2021 21:42:14 -0700 Subject: [PATCH 057/926] Disable failing System.Reflection.Tests.ModuleTests.GetMethods (#54564) Tracking: https://github.com/dotnet/runtime/issues/50831 --- .../System.Runtime/tests/System/Reflection/ModuleTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs index a481ee253a5..29fe532e6c0 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs @@ -232,6 +232,7 @@ namespace System.Reflection.Tests [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/51912", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/50831")] public void GetMethods() { var methodNames = TestModule.GetMethods().Select(m => m.Name).ToArray(); From d87b78342c5d88741eb1f194b4c9968baf192ddc Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Wed, 23 Jun 2021 00:46:38 -0400 Subject: [PATCH 058/926] Added runtime dependency to fix the intermittent test failures (#54587) * Added runtime dependency to hopefully fix the intermittent test failures * addressed comments * cleanup * cleanup accidental spaces and tabs cleanup * added Larry's comments --- src/mono/wasm/runtime/library_mono.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 36837f62cf7..e9cb8cd2b40 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -2365,12 +2365,14 @@ var MonoSupportLib = { }, /** - * Loads the mono config file (typically called mono-config.json) + * Loads the mono config file (typically called mono-config.json) asynchroniously + * Note: the run dependencies are so emsdk actually awaits it in order. * * @param {string} configFilePath - relative path to the config file * @throws Will throw an error if the config file loading fails */ - mono_wasm_load_config: async function (configFilePath) { + mono_wasm_load_config: async function (configFilePath) { + Module.addRunDependency(configFilePath); try { let config = null; // NOTE: when we add nodejs make sure to include the nodejs fetch package @@ -2385,6 +2387,8 @@ var MonoSupportLib = { return config; } catch(e) { return {message: "failed to load config file", error: e}; + } finally { + Module.removeRunDependency(configFilePath); } } }, From cd0b3ef6695e520e8c4757fed34891e6860dd7bd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 23 Jun 2021 02:34:38 -0400 Subject: [PATCH 059/926] Update NetAnalyzers version (#54511) * Update NetAnalyzers version * Add NetAnalyzers to dependency flow Co-authored-by: Viktor Hofer --- eng/CodeAnalysis.ruleset | 1 + eng/Version.Details.xml | 4 ++++ eng/Versions.props | 3 ++- .../src/System/Data/Common/DbConnectionOptions.Common.cs | 3 +-- .../src/Microsoft/Win32/RegistryKey.cs | 3 +-- .../src/System.Configuration.ConfigurationManager.csproj | 1 + .../src/System.Diagnostics.EventLog.csproj | 2 +- .../src/System.Diagnostics.PerformanceCounter.csproj | 1 + .../src/System/IO/DriveInfo.UnixOrBrowser.cs | 2 +- src/libraries/System.Speech/src/System.Speech.csproj | 2 +- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 2 +- 11 files changed, 15 insertions(+), 9 deletions(-) diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index 46314145995..8faa50e9a6f 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -126,6 +126,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 99fb28cb6e9..5c1f457ae40 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -219,5 +219,9 @@ https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e + + https://github.com/dotnet/roslyn-analyzers + fcddb771f42866f9521f23f093b1f30e129018bb + diff --git a/eng/Versions.props b/eng/Versions.props index b62b9b4854c..2295b6e440d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,9 +45,10 @@ 3.8.0 - 6.0.0-preview6.21281.1 + 3.10.0-2.final 3.10.0-2.final + 6.0.0-rc1.21320.2 6.0.0-beta.21311.3 6.0.0-beta.21311.3 diff --git a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs index fda8cc871d9..80a092104ae 100644 --- a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs +++ b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs @@ -406,8 +406,7 @@ namespace System.Data.Common bool compValue = s_connectionStringValidValueRegex.IsMatch(keyvalue); Debug.Assert((-1 == keyvalue.IndexOf('\u0000')) == compValue, "IsValueValid mismatch with regex"); #endif - // string.Contains(char) is .NetCore2.1+ specific - return (-1 == keyvalue.IndexOf('\u0000')); + return (-1 == keyvalue.IndexOf('\u0000')); // string.Contains(char) is .NetCore2.1+ specific } return true; } diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs index 54054d809c2..ea5a8331521 100644 --- a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs +++ b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs @@ -628,8 +628,7 @@ namespace Microsoft.Win32 { Debug.Assert(name != null, "[FixupName]name!=null"); - // string.Contains(char) is .NetCore2.1+ specific - if (name.IndexOf('\\') == -1) + if (!name.Contains('\\')) { return name; } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj index 104386de5ed..209d36e49c5 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + $(NoWarn);CA1847 diff --git a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj index 14a4d335616..a093eb35ac7 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj +++ b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj @@ -2,7 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 - $(NoWarn);CA1838 + $(NoWarn);CA1838;CA1847 diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index f66b352c9c1..600f6fc58b9 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -2,6 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 + $(NoWarn);CA1847 diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs index 51892986a5d..b39b2232288 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.UnixOrBrowser.cs @@ -24,7 +24,7 @@ namespace System.IO private static string NormalizeDriveName(string driveName) { - if (driveName.Contains("\0")) // string.Contains(char) is .NetCore2.1+ specific + if (driveName.Contains('\0')) { throw new ArgumentException(SR.Format(SR.Arg_InvalidDriveChars, driveName), nameof(driveName)); } diff --git a/src/libraries/System.Speech/src/System.Speech.csproj b/src/libraries/System.Speech/src/System.Speech.csproj index a38b4519356..8493864d2f6 100644 --- a/src/libraries/System.Speech/src/System.Speech.csproj +++ b/src/libraries/System.Speech/src/System.Speech.csproj @@ -4,7 +4,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0 - $(NoWarn);CS0649;SA1129 + $(NoWarn);CS0649;SA1129;CA1847 false diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 346f854c383..b979ba316b3 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -495,7 +495,7 @@ public class ApkBuilder { string? buildTools = Directory.GetDirectories(Path.Combine(androidSdkDir, "build-tools")) .Select(Path.GetFileName) - .Where(file => !file!.Contains("-")) + .Where(file => !file!.Contains('-')) .Select(file => { Version.TryParse(Path.GetFileName(file), out Version? version); return version; }) .OrderByDescending(v => v) .FirstOrDefault()?.ToString(); From 0aafceb47a2a26783e3b3ece891e45bf49354fe6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 23 Jun 2021 08:53:55 +0200 Subject: [PATCH 060/926] [FileStream] handle UNC and device paths (#54483) * stop using NtCreateFile as there is no public and reliable way of mapping DOS to NT paths --- .../Kernel32/Interop.FILE_ALLOCATION_INFO.cs | 16 ++ .../SafeHandles/SafeFileHandle.Windows.cs | 137 ++++++++++++------ .../System.Private.CoreLib.Shared.projitems | 6 + 3 files changed, 111 insertions(+), 48 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs new file mode 100644 index 00000000000..0233626ee73 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // Value taken from https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle#remarks: + internal const int FileAllocationInfo = 5; + + internal struct FILE_ALLOCATION_INFO + { + internal long AllocationSize; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index 36710dcdbde..5aa7a2c7f4d 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -4,7 +4,8 @@ using System; using System.Diagnostics; using System.IO; -using System.Text; +using System.IO.Strategies; +using System.Runtime.InteropServices; using System.Threading; namespace Microsoft.Win32.SafeHandles @@ -24,13 +25,6 @@ namespace Microsoft.Win32.SafeHandles SetHandle(preexistingHandle); } - private SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle, FileOptions fileOptions) : base(ownsHandle) - { - SetHandle(preexistingHandle); - - _fileOptions = fileOptions; - } - public bool IsAsync => (GetFileOptions() & FileOptions.Asynchronous) != 0; internal bool CanSeek => !IsClosed && GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; @@ -43,10 +37,14 @@ namespace Microsoft.Win32.SafeHandles { using (DisableMediaInsertionPrompt.Create()) { - SafeFileHandle fileHandle = new SafeFileHandle( - NtCreateFile(fullPath, mode, access, share, options, preallocationSize), - ownsHandle: true, - options); + // we don't use NtCreateFile as there is no public and reliable way + // of converting DOS to NT file paths (RtlDosPathNameToRelativeNtPathName_U_WithStatus is not documented) + SafeFileHandle fileHandle = CreateFile(fullPath, mode, access, share, options); + + if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode)) + { + Preallocate(fullPath, preallocationSize, fileHandle); + } fileHandle.InitThreadPoolBindingIfNeeded(); @@ -54,48 +52,91 @@ namespace Microsoft.Win32.SafeHandles } } - private static IntPtr NtCreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) + private static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options) { - uint ntStatus; - IntPtr fileHandle; - - const string MandatoryNtPrefix = @"\??\"; - if (fullPath.StartsWith(MandatoryNtPrefix, StringComparison.Ordinal)) + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; + if ((share & FileShare.Inheritable) != 0) { - (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(fullPath, mode, access, share, options, preallocationSize); - } - else - { - var vsb = new ValueStringBuilder(stackalloc char[256]); - vsb.Append(MandatoryNtPrefix); - - if (fullPath.StartsWith(@"\\?\", StringComparison.Ordinal)) // NtCreateFile does not support "\\?\" prefix, only "\??\" + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES { - vsb.Append(fullPath.AsSpan(4)); - } - else - { - vsb.Append(fullPath); - } - - (ntStatus, fileHandle) = Interop.NtDll.NtCreateFile(vsb.AsSpan(), mode, access, share, options, preallocationSize); - vsb.Dispose(); + nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), + bInheritHandle = Interop.BOOL.TRUE + }; } - switch (ntStatus) + int fAccess = + ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | + ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); + + // Our Inheritable bit was stolen from Windows, but should be set in + // the security attributes class. Don't leave this bit set. + share &= ~FileShare.Inheritable; + + // Must use a valid Win32 constant here... + if (mode == FileMode.Append) { - case Interop.StatusOptions.STATUS_SUCCESS: - return fileHandle; - case Interop.StatusOptions.STATUS_DISK_FULL: - throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize)); - // NtCreateFile has a bug and it reports STATUS_INVALID_PARAMETER for files - // that are too big for the current file system. Example: creating a 4GB+1 file on a FAT32 drive. - case Interop.StatusOptions.STATUS_INVALID_PARAMETER when preallocationSize > 0: - case Interop.StatusOptions.STATUS_FILE_TOO_LARGE: - throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); - default: - int error = (int)Interop.NtDll.RtlNtStatusToDosError((int)ntStatus); - throw Win32Marshal.GetExceptionForWin32Error(error, fullPath); + mode = FileMode.OpenOrCreate; + } + + int flagsAndAttributes = (int)options; + + // For mitigating local elevation of privilege attack through named pipes + // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the + // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) + flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); + + SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(fullPath, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero); + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastPInvokeError(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && fullPath!.Length == PathInternal.GetRootLength(fullPath)) + { + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + } + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); + } + + fileHandle._fileOptions = options; + return fileHandle; + } + + private static unsafe void Preallocate(string fullPath, long preallocationSize, SafeFileHandle fileHandle) + { + var allocationInfo = new Interop.Kernel32.FILE_ALLOCATION_INFO + { + AllocationSize = preallocationSize + }; + + if (!Interop.Kernel32.SetFileInformationByHandle( + fileHandle, + Interop.Kernel32.FileAllocationInfo, + &allocationInfo, + (uint)sizeof(Interop.Kernel32.FILE_ALLOCATION_INFO))) + { + int errorCode = Marshal.GetLastPInvokeError(); + + // we try to mimic the atomic NtCreateFile here: + // if preallocation fails, close the handle and delete the file + fileHandle.Dispose(); + Interop.Kernel32.DeleteFile(fullPath); + + switch (errorCode) + { + case Interop.Errors.ERROR_DISK_FULL: + throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize)); + case Interop.Errors.ERROR_FILE_TOO_LARGE: + throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a7ce085d9a0..2af41ff1342 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1426,6 +1426,9 @@ Common\Interop\Windows\Kernel32\Interop.FILE_BASIC_INFO.cs + + Common\Interop\Windows\Kernel32\Interop.FILE_ALLOCATION_INFO.cs + Common\Interop\Windows\Kernel32\Interop.FILE_END_OF_FILE_INFO.cs @@ -1612,6 +1615,9 @@ Common\Interop\Windows\Interop.UNICODE_STRING.cs + + Common\Interop\Windows\Kernel32\Interop.SecurityOptions.cs + Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs From d783a8c812b387a9b8befa0ec5172857c04d845c Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Wed, 23 Jun 2021 00:00:47 -0700 Subject: [PATCH 061/926] Update library testing docs page to reduce confusion (#54324) * Add warning on unmaintained testing doc page * Update testing.md Some example text that seems more clear to me, but only offered as a suggestion. Feel free to adjust it or if you want to use it as-is please double check what I wrote is accurate : ) I think the useful elements are: 1. Being explicit about what workflow steps need to happen in total 2. Being explicit about which commands are covering the entire workflow and which ones are only covering a part of it 3. Show the simple "do-it-all" options first before showing more complex partial options. Glancing at the first example and blindly copying it should land in the pit of success. Co-authored-by: Viktor Hofer --- docs/workflow/testing/libraries/testing.md | 85 +++++++++++++--------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/docs/workflow/testing/libraries/testing.md b/docs/workflow/testing/libraries/testing.md index fe4d76255a7..a78a3d04098 100644 --- a/docs/workflow/testing/libraries/testing.md +++ b/docs/workflow/testing/libraries/testing.md @@ -1,54 +1,71 @@ # Testing Libraries -We use the OSS testing framework [xunit](https://github.com/xunit/xunit). +## Full Build and Test Run -To build the tests and run them you can call the libraries build script. For libraries tests to work, you must have built the coreclr or mono runtime for them to run on. +These example commands automate the test run and all pre-requisite build steps in a single command from a clean enlistment. -**Examples** -- The following shows how to build only the tests but not run them: -``` -build.cmd/sh -subset libs.tests -``` - -- The following builds and runs all tests using clr: -``` -build.cmd/sh -subset clr+libs.tests -test -``` - -- The following builds and runs all tests using mono: -``` -build.cmd/sh -subset mono+libs.tests -test -``` - -- The following builds and runs all tests in release configuration: -``` -build.cmd/sh -subset libs.tests -test -c Release -``` - -- The following builds clr in release, libs in debug and runs all tests: +- Run all tests - Builds clr in release, libs+tests in debug: ``` build.cmd/sh -subset clr+libs+libs.tests -test -rc Release ``` -- The following builds mono and libs for x86 architecture and runs all tests: +- Run all tests - Builds Mono in release, libs+tests in debug: +``` +build.cmd/sh -subset mono+libs+libs.tests -test -rc Release +``` + +- Run all tests - Build Mono and libs for x86 architecture in debug (choosing debug for runtime will run very slowly): ``` build.cmd/sh -subset mono+libs+libs.tests -test -arch x86 ``` -- The following example shows how to pass extra msbuild properties to ignore tests ignored in CI: +## Partial Build and Test Runs + +Doing full build and test runs takes a long time and is very inefficient if you need to iterate on a change. +For greater control and efficiency individual parts of the build + testing workflow can be run in isolation. +See the [Building instructions](../../building/libraries/README.md) for more info on build options. + +### Test Run Pre-requisites +Before any tests can run we need a complete build to run them on. This requires building (1) a runtime, and +(2) all the libraries. Examples: + +- Build release clr + debug libraries ``` -build.cmd/sh -subset libs.tests -test /p:WithoutCategories=IgnoreForCI +build.cmd/sh -subset clr+libs -rc Release ``` -Unless you specifiy `-testnobuild`, test assemblies are implicitly built when invoking the `Test` action. -- The following shows how to only test the libraries without building them +- Build release mono + debug libraries +``` +build.cmd/sh -subset mono+libs -rc Release +``` + +Building the `libs` subset or any of individual library projects automatically copies product binaries into the testhost folder +in the bin directory. This is where the tests will load the binaries from during the run. However System.Private.CorLib is an +exception - the build does not automatically copy it to the testhost folder. If you [rebuild System.Private.CoreLib](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/libraries/README.md#iterating-on-systemprivatecorelib-changes) you must also build the `libs.pretest` subset to ensure S.P.C is copied before running tests. + +### Running tests for all libraries + +- Build and run all tests in release configuration. +``` +build.cmd/sh -subset libs.tests -test -c Release +``` + +- Build the tests without running them +``` +build.cmd/sh -subset libs.tests +``` + +- Run the tests without building them ``` build.cmd/sh -subset libs.tests -test -testnobuild ``` -## Running tests on the command line +- The following example shows how to pass extra msbuild properties to ignore tests ignored in CI. +``` +build.cmd/sh -subset libs.tests -test /p:WithoutCategories=IgnoreForCI +``` -To build tests you need to specify the `test` subset when invoking build.cmd/sh: `build.cmd/sh -subset libs.tests`. +### Running tests for a single library The easiest (and recommended) way to build and run the tests for a specific library, is to invoke the `Test` target on that library: ```cmd @@ -68,21 +85,21 @@ dotnet build /t:Test /p:TargetArchitecture=x86 There may be multiple projects in some directories so you may need to specify the path to a specific test project to get it to build and run the tests. -#### Running a single test on the command line +### Running a single test on the command line To quickly run or debug a single test from the command line, set the XunitMethodName property, e.g.: ```cmd dotnet build /t:Test /p:XunitMethodName={FullyQualifiedNamespace}.{ClassName}.{MethodName} ``` -#### Running outer loop tests +### Running outer loop tests To run all tests, including "outer loop" tests (which are typically slower and in some test suites less reliable, but which are more comprehensive): ```cmd dotnet build /t:Test /p:Outerloop=true ``` -#### Running tests on a different target framework +### Running tests on a different target framework Each test project can potentially have multiple target frameworks. There are some tests that might be OS-specific, or might be testing an API that is available only on some target frameworks, so the `TargetFrameworks` property specifies the valid target frameworks. By default we will build and run only the default build target framework which is `net5.0`. The rest of the `TargetFrameworks` will need to be built and ran by specifying the `BuildTargetFramework` option, e.g.: ```cmd From 97de5c5aff67892ae66fe65d1da971affe59bd76 Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Wed, 23 Jun 2021 00:19:10 -0700 Subject: [PATCH 062/926] Add support for multi-arch install locations (#53763) * Add support for multiple architectures inside install_locations * Add install_location tests * Fallback to DOTNET_ROOT on win32 --- .../HostActivation.Tests/CommandExtensions.cs | 5 +- .../InstallLocationCommandResultExtensions.cs | 58 ++++++ .../MultiArchInstallLocation.cs | 184 ++++++++++++++++++ .../MultilevelSDKLookup.cs | 2 +- .../NativeHosting/Nethost.cs | 121 ++++++++++-- .../PortableAppActivation.cs | 14 +- .../RegisteredInstallLocationOverride.cs | 16 +- .../StandaloneAppActivation.cs | 3 +- .../tests/TestUtils/TestProjectFixture.cs | 4 +- src/native/corehost/deps_format.cpp | 2 +- src/native/corehost/fxr/command_line.cpp | 2 +- src/native/corehost/fxr_resolver.cpp | 10 +- src/native/corehost/hostmisc/pal.h | 53 +++-- src/native/corehost/hostmisc/pal.unix.cpp | 181 ++++++++++------- src/native/corehost/hostmisc/pal.windows.cpp | 11 +- src/native/corehost/hostmisc/utils.cpp | 40 +++- src/native/corehost/hostmisc/utils.h | 11 +- .../corehost/test/nativehost/nativehost.cpp | 4 +- 18 files changed, 565 insertions(+), 156 deletions(-) create mode 100644 src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs create mode 100644 src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs diff --git a/src/installer/tests/HostActivation.Tests/CommandExtensions.cs b/src/installer/tests/HostActivation.Tests/CommandExtensions.cs index 054e6c39d14..d8460c1368b 100644 --- a/src/installer/tests/HostActivation.Tests/CommandExtensions.cs +++ b/src/installer/tests/HostActivation.Tests/CommandExtensions.cs @@ -35,8 +35,11 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation .CaptureStdErr(); } - public static Command DotNetRoot(this Command command, string dotNetRoot) + public static Command DotNetRoot(this Command command, string dotNetRoot, string architecture = null) { + if (!string.IsNullOrEmpty(architecture)) + return command.EnvironmentVariable($"DOTNET_ROOT_{architecture.ToUpper()}", dotNetRoot); + return command .EnvironmentVariable("DOTNET_ROOT", dotNetRoot) .EnvironmentVariable("DOTNET_ROOT(x86)", dotNetRoot); diff --git a/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs b/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs new file mode 100644 index 00000000000..c85b7d3e56c --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/InstallLocationCommandResultExtensions.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime.InteropServices; +using FluentAssertions; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using Xunit; + +namespace HostActivation.Tests +{ + internal static class InstallLocationCommandResultExtensions + { + private static bool IsRunningInWoW64(string rid) => OperatingSystem.IsWindows() && Environment.Is64BitOperatingSystem && rid.Equals("win-x86"); + + public static AndConstraint HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion, string installLocation, string rid) + { + return assertion.HaveUsedDotNetRootInstallLocation(installLocation, rid, null); + } + + public static AndConstraint HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion, + string installLocation, + string rid, + string arch) + { + // If no arch is passed and we are on Windows, we need the used RID for determining whether or not we are running on WoW64. + if (string.IsNullOrEmpty(arch)) + Assert.NotNull(rid); + + string expectedEnvironmentVariable = !string.IsNullOrEmpty(arch) ? $"DOTNET_ROOT_{arch.ToUpper()}" : + IsRunningInWoW64(rid) ? "DOTNET_ROOT(x86)" : "DOTNET_ROOT"; + + return assertion.HaveStdErrContaining($"Using environment variable {expectedEnvironmentVariable}=[{installLocation}] as runtime location."); + } + + public static AndConstraint HaveUsedConfigFileInstallLocation(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Using install location '{installLocation}'."); + } + + public static AndConstraint HaveUsedGlobalInstallLocation(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Using global installation location [{installLocation}]"); + } + + public static AndConstraint HaveFoundDefaultInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation) + { + return assertion.HaveStdErrContaining($"Found install location path '{installLocation}'."); + } + + public static AndConstraint HaveFoundArchSpecificInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation, string arch) + { + return assertion.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{arch}')."); + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs new file mode 100644 index 00000000000..c1b00cafd4e --- /dev/null +++ b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.DotNet.CoreSetup.Test.HostActivation; +using Xunit; + +namespace HostActivation.Tests +{ + public class MultiArchInstallLocation : IClassFixture + { + private SharedTestState sharedTestState; + + public MultiArchInstallLocation(SharedTestState fixture) + { + sharedTestState = fixture; + } + + [Fact] + public void EnvironmentVariable_CurrentArchitectureIsUsedIfEnvVarSet() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(fixture.BuiltDotnet.BinPath, arch) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid, arch); + } + + [Fact] + public void EnvironmentVariable_IfNoArchSpecificEnvVarIsFoundDotnetRootIsUsed() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(fixture.BuiltDotnet.BinPath) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid); + } + + [Fact] + public void EnvironmentVariable_ArchSpecificDotnetRootIsUsedOverDotnetRoot() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper(); + var dotnet = fixture.BuiltDotnet.BinPath; + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot("non_existent_path") + .DotNetRoot(dotnet, arch) + .Execute() + .Should().Pass() + .And.HaveUsedDotNetRootInstallLocation(dotnet, fixture.CurrentRid, arch) + .And.NotHaveStdErrContaining("Using environment variable DOTNET_ROOT="); + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_ArchSpecificLocationIsPickedFirst() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var arch1 = "someArch"; + var path1 = "x/y/z"; + var arch2 = fixture.RepoDirProvider.BuildArchitecture; + var path2 = "a/b/c"; + + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, path1), + (arch1, path1), + (arch2, path2) + }); + + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile(path1) + .And.HaveFoundArchSpecificInstallLocationInConfigFile(path1, arch1) + .And.HaveFoundArchSpecificInstallLocationInConfigFile(path2, arch2) + .And.HaveUsedGlobalInstallLocation(path2); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_OnlyFirstLineMayNotSpecifyArchitecture() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, "a/b/c"), + (string.Empty, "x/y/z"), + }); + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile("a/b/c") + .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.") + .And.HaveUsedConfigFileInstallLocation("a/b/c"); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) + { + var reallyLongPath = + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" + + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" + + "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongpath"; + registeredInstallLocationOverride.SetInstallLocation((string.Empty, reallyLongPath)); + + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .DotNetRoot(null) + .Execute() + .Should().HaveFoundDefaultInstallLocationInConfigFile(reallyLongPath) + .And.HaveUsedConfigFileInstallLocation(reallyLongPath); + } + } + + public class SharedTestState : IDisposable + { + public string BaseDirectory { get; } + public TestProjectFixture PortableAppFixture { get; } + public RepoDirectoriesProvider RepoDirectories { get; } + public string InstallLocation { get; } + + public SharedTestState() + { + RepoDirectories = new RepoDirectoriesProvider(); + var fixture = new TestProjectFixture("PortableApp", RepoDirectories); + fixture + .EnsureRestored() + // App Host generation is turned off by default on macOS + .PublishProject(extraArgs: "/p:UseAppHost=true"); + + PortableAppFixture = fixture; + BaseDirectory = Path.GetDirectoryName(PortableAppFixture.SdkDotnet.GreatestVersionHostFxrFilePath); + } + + public void Dispose() + { + PortableAppFixture.Dispose(); + } + } + } +} diff --git a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs index 97b3387604b..76d11337c03 100644 --- a/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs +++ b/src/installer/tests/HostActivation.Tests/MultilevelSDKLookup.cs @@ -501,7 +501,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(DotNet.GreatestVersionHostFxrFilePath)) { - registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (RepoDirectories.BuildArchitecture, _regDir) }); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4"); diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs index 0f18161b026..0af2ce34774 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Nethost.cs @@ -116,7 +116,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting { if (useRegisteredLocation) { - registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, installLocation)); } result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {explicitLoad} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}") @@ -180,21 +180,32 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting [Theory] [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] - [InlineData("{0}", true)] - [InlineData("{0}\n", true)] - [InlineData("{0}\nSome other text", true)] - [InlineData("", false)] - [InlineData("\n{0}", false)] - [InlineData(" {0}", false)] - [InlineData("{0} \n", false)] - [InlineData("{0} ", false)] - public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass) + [InlineData("{0}", false, true)] + [InlineData("{0}\n", false, true)] + [InlineData("{0}\nSome other text", false, true)] + [InlineData("", false, false)] + [InlineData("\n{0}", false, false)] + [InlineData(" {0}", false, false)] + [InlineData("{0} \n", false, false)] + [InlineData("{0} ", false, false)] + [InlineData("{0}", true, true)] + [InlineData("{0}\n", true, true)] + [InlineData("{0}\nSome other text", true, true)] + [InlineData("", true, false)] + [InlineData("\n{0}", true, false)] + [InlineData(" {0}", true, false)] + [InlineData("{0} \n", true, false)] + [InlineData("{0} ", true, false)] + public void GetHostFxrPath_InstallLocationFile(string value, bool shouldUseArchSpecificInstallLocation, bool shouldPass) { string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) { - File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation)); + if (shouldUseArchSpecificInstallLocation) + registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, string.Format(value, installLocation))); + else + registeredInstallLocationOverride.SetInstallLocation((string.Empty, string.Format(value, installLocation))); CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) .EnableTracingAndCaptureOutputs() @@ -223,6 +234,94 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting } } + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_HasMoreThanOneDefaultInstallationPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, installLocation), (string.Empty, installLocation) + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found install location path '{installLocation}'.") + .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_HasNoDefaultInstallationPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (sharedState.RepoDirectories.BuildArchitecture, installLocation), + ("someOtherArch", $"{installLocation}/invalid") + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void GetHostFxrPath_GlobalInstallation_ArchitectureSpecificPathIsPickedOverDefaultPath() + { + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath)) + { + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { + (string.Empty, $"{installLocation}/a/b/c"), + (sharedState.RepoDirectories.BuildArchitecture, installLocation) + }); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .EnableTracingAndCaptureOutputs() + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .DotNetRoot(null) + .Execute(); + + result.Should().Pass() + .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.") + .And.HaveStdErrContaining($"Found install location path '{installLocation}/a/b/c'.") + .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').") + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + } + [Fact] public void GetHostFxrPath_InvalidParameters() { diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index 14060b5e944..a2a0d963e1f 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -257,7 +257,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation Command.Create(appExe) .CaptureStdErr() .CaptureStdOut() - .DotNetRoot(builtDotnet) + .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .Execute() .Should().Pass() @@ -268,7 +268,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation // Verify running from within the working directory Command.Create(appExe) .WorkingDirectory(fixture.TestProject.OutputDirectory) - .DotNetRoot(builtDotnet) + .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .CaptureStdErr() .CaptureStdOut() @@ -297,10 +297,10 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe)) { - string architecture = fixture.CurrentRid.Split('-')[1]; + string architecture = fixture.RepoDirProvider.BuildArchitecture; if (useRegisteredLocation) { - registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture); + registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (architecture, builtDotnet) }); } // Verify running with the default working directory @@ -357,7 +357,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation if (useAppHost) { command = Command.Create(sharedTestState.MockApp.AppExe) - .DotNetRoot(sharedTestState.BuiltDotNet.BinPath); + .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture); } else { @@ -386,7 +386,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation if (useAppHost) { command = Command.Create(app.AppExe) - .DotNetRoot(sharedTestState.BuiltDotNet.BinPath); + .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture); } else { @@ -540,7 +540,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation Command command = Command.Create(appExe) .EnableTracingAndCaptureOutputs() - .DotNetRoot(dotnet.BinPath) + .DotNetRoot(dotnet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture) .MultilevelLookup(false) .Start(); diff --git a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs index 7a30c5d6fd0..d37cfafe649 100644 --- a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs +++ b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs @@ -4,8 +4,10 @@ using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation @@ -61,18 +63,24 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation } } - public void SetInstallLocation(string installLocation, string architecture) + public void SetInstallLocation(params (string Architecture, string Path)[] locations) { + Debug.Assert(locations.Length >= 1); if (OperatingSystem.IsWindows()) { - using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}")) + foreach (var location in locations) { - dotnetLocationKey.SetValue("InstallLocation", installLocation); + using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{location.Architecture}")) + { + dotnetLocationKey.SetValue("InstallLocation", location.Path); + } } } else { - File.WriteAllText(PathValueOverride, installLocation); + File.WriteAllText(PathValueOverride, string.Join(Environment.NewLine, + locations.Select(l => string.Format("{0}{1}", + (!string.IsNullOrWhiteSpace(l.Architecture) ? l.Architecture + "=" : ""), l.Path)))); } } diff --git a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs index b7ec6eb9688..352abb04f0d 100644 --- a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs @@ -204,8 +204,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation // self-contained layout since a flat layout of the shared framework is not supported. Command.Create(appExe) .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable("DOTNET_ROOT", newOutDir) - .EnvironmentVariable("DOTNET_ROOT(x86)", newOutDir) + .DotNetRoot(newOutDir) .CaptureStdErr() .CaptureStdOut() .Execute(fExpectedToFail: true) diff --git a/src/installer/tests/TestUtils/TestProjectFixture.cs b/src/installer/tests/TestUtils/TestProjectFixture.cs index 2ccce4dded1..7b9da29d1a9 100644 --- a/src/installer/tests/TestUtils/TestProjectFixture.cs +++ b/src/installer/tests/TestUtils/TestProjectFixture.cs @@ -358,7 +358,7 @@ namespace Microsoft.DotNet.CoreSetup.Test public TestProjectFixture EnsureRestored(params string[] fallbackSources) { - if ( ! TestProject.IsRestored()) + if (!TestProject.IsRestored()) { RestoreProject(fallbackSources); } @@ -368,7 +368,7 @@ namespace Microsoft.DotNet.CoreSetup.Test public TestProjectFixture EnsureRestoredForRid(string rid, params string[] fallbackSources) { - if ( ! TestProject.IsRestored()) + if (!TestProject.IsRestored()) { string extraMSBuildProperties = $"/p:TestTargetRid={rid}"; RestoreProject(fallbackSources, extraMSBuildProperties); diff --git a/src/native/corehost/deps_format.cpp b/src/native/corehost/deps_format.cpp index f0beb6ed7e5..8ab0f22caff 100644 --- a/src/native/corehost/deps_format.cpp +++ b/src/native/corehost/deps_format.cpp @@ -84,7 +84,7 @@ void deps_json_t::reconcile_libraries_with_targets( size_t pos = lib_name.find(_X("/")); entry.library_name = lib_name.substr(0, pos); entry.library_version = lib_name.substr(pos + 1); - entry.library_type = pal::to_lower(library.value[_X("type")].GetString()); + entry.library_type = to_lower(library.value[_X("type")].GetString()); entry.library_hash = hash; entry.library_path = library_path; entry.library_hash_path = library_hash_path; diff --git a/src/native/corehost/fxr/command_line.cpp b/src/native/corehost/fxr/command_line.cpp index 082f81d802c..deb80b38b7f 100644 --- a/src/native/corehost/fxr/command_line.cpp +++ b/src/native/corehost/fxr/command_line.cpp @@ -86,7 +86,7 @@ namespace while (arg_i < argc) { const pal::char_t* arg = argv[arg_i]; - pal::string_t arg_lower = pal::to_lower(arg); + pal::string_t arg_lower = to_lower(arg); const auto &iter = std::find_if(known_opts.cbegin(), known_opts.cend(), [&](const known_options &opt) { return arg_lower == get_host_option(opt).option; }); if (iter == known_opts.cend()) diff --git a/src/native/corehost/fxr_resolver.cpp b/src/native/corehost/fxr_resolver.cpp index d457047ddc8..6e1356457d4 100644 --- a/src/native/corehost/fxr_resolver.cpp +++ b/src/native/corehost/fxr_resolver.cpp @@ -65,10 +65,10 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o return true; } - // For framework-dependent apps, use DOTNET_ROOT + // For framework-dependent apps, use DOTNET_ROOT_ pal::string_t default_install_location; - pal::string_t dotnet_root_env_var_name = get_dotnet_root_env_var_name(); - if (get_file_path_from_env(dotnet_root_env_var_name.c_str(), out_dotnet_root)) + pal::string_t dotnet_root_env_var_name; + if (get_dotnet_root_from_env(&dotnet_root_env_var_name, out_dotnet_root)) { trace::info(_X("Using environment variable %s=[%s] as runtime location."), dotnet_root_env_var_name.c_str(), out_dotnet_root->c_str()); } @@ -134,7 +134,7 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o #endif // !FEATURE_APPHOST && !FEATURE_LIBHOST } -bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t &dotnet_root, pal::string_t *out_fxr_path) +bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t& dotnet_root, pal::string_t* out_fxr_path) { pal::string_t fxr_dir = dotnet_root; append_path(&fxr_dir, _X("host")); @@ -148,7 +148,7 @@ bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t &dotnet_roo return get_latest_fxr(std::move(fxr_dir), out_fxr_path); } -bool fxr_resolver::try_get_existing_fxr(pal::dll_t *out_fxr, pal::string_t *out_fxr_path) +bool fxr_resolver::try_get_existing_fxr(pal::dll_t* out_fxr, pal::string_t* out_fxr_path) { if (!pal::get_loaded_library(LIBFXR_NAME, "hostfxr_main", out_fxr, out_fxr_path)) return false; diff --git a/src/native/corehost/hostmisc/pal.h b/src/native/corehost/hostmisc/pal.h index 27daf76c727..b6622cd5dff 100644 --- a/src/native/corehost/hostmisc/pal.h +++ b/src/native/corehost/hostmisc/pal.h @@ -104,13 +104,13 @@ namespace pal { #if defined(_WIN32) - #ifdef EXPORT_SHARED_API - #define SHARED_API extern "C" __declspec(dllexport) - #else - #define SHARED_API extern "C" - #endif +#ifdef EXPORT_SHARED_API +#define SHARED_API extern "C" __declspec(dllexport) +#else +#define SHARED_API extern "C" +#endif - #define STDMETHODCALLTYPE __stdcall +#define STDMETHODCALLTYPE __stdcall typedef wchar_t char_t; typedef std::wstring string_t; @@ -151,13 +151,13 @@ namespace pal inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, size_t len) { return ::wcsncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, size_t len) { return ::_wcsnicmp(str1, str2, len); } - inline int pathcmp(const pal::string_t &path1, const pal::string_t &path2) { return strcasecmp(path1.c_str(), path2.c_str()); } + inline int pathcmp(const pal::string_t& path1, const pal::string_t& path2) { return strcasecmp(path1.c_str(), path2.c_str()); } inline string_t to_string(int value) { return std::to_wstring(value); } inline size_t strlen(const char_t* str) { return ::wcslen(str); } #pragma warning(suppress : 4996) // error C4996: '_wfopen': This function or variable may be unsafe. - inline FILE * file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); } + inline FILE* file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); } inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfwprintf(f, format, vl); ::fputwc(_X('\n'), f); } inline void err_fputs(const char_t* message) { ::fputws(message, stderr); ::fputwc(_X('\n'), stderr); } @@ -170,32 +170,32 @@ namespace pal // Suppressing warning since the 'safe' version requires an input buffer that is unnecessary for // uses of this function. #pragma warning(suppress : 4996) // error C4996: '_wcserror': This function or variable may be unsafe. - inline const char_t* strerror(int errnum){ return ::_wcserror(errnum); } + inline const char_t* strerror(int errnum) { return ::_wcserror(errnum); } bool pal_utf8string(const string_t& str, std::vector* out); bool pal_clrstring(const string_t& str, std::vector* out); bool clr_palstring(const char* cstr, string_t* out); inline bool mkdir(const char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; } - inline bool rmdir (const char_t* path) { return RemoveDirectoryW(path) != 0; } + inline bool rmdir(const char_t* path) { return RemoveDirectoryW(path) != 0; } inline int rename(const char_t* old_name, const char_t* new_name) { return ::_wrename(old_name, new_name); } inline int remove(const char_t* path) { return ::_wremove(path); } inline bool munmap(void* addr, size_t length) { return UnmapViewOfFile(addr) != 0; } inline int get_pid() { return GetCurrentProcessId(); } inline void sleep(uint32_t milliseconds) { Sleep(milliseconds); } #else - #ifdef EXPORT_SHARED_API - #define SHARED_API extern "C" __attribute__((__visibility__("default"))) - #else - #define SHARED_API extern "C" - #endif +#ifdef EXPORT_SHARED_API +#define SHARED_API extern "C" __attribute__((__visibility__("default"))) +#else +#define SHARED_API extern "C" +#endif - #define __cdecl /* nothing */ - #define __stdcall /* nothing */ - #if !defined(TARGET_FREEBSD) - #define __fastcall /* nothing */ - #endif - #define STDMETHODCALLTYPE __stdcall +#define __cdecl /* nothing */ +#define __stdcall /* nothing */ +#if !defined(TARGET_FREEBSD) +#define __fastcall /* nothing */ +#endif +#define STDMETHODCALLTYPE __stdcall typedef char char_t; typedef std::string string_t; @@ -219,7 +219,7 @@ namespace pal inline string_t to_string(int value) { return std::to_string(value); } inline size_t strlen(const char_t* str) { return ::strlen(str); } - inline FILE * file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); } + inline FILE* file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); } inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfprintf(f, format, vl); ::fputc('\n', f); } inline void err_fputs(const char_t* message) { ::fputs(message, stderr); ::fputc(_X('\n'), stderr); } inline void out_vprintf(const char_t* format, va_list vl) { ::vfprintf(stdout, format, vl); ::fputc('\n', stdout); } @@ -237,7 +237,6 @@ namespace pal inline bool munmap(void* addr, size_t length) { return ::munmap(addr, length) == 0; } inline int get_pid() { return getpid(); } inline void sleep(uint32_t milliseconds) { usleep(milliseconds * 1000); } - #endif inline int snwprintf(char_t* buffer, size_t count, const char_t* format, ...) @@ -252,10 +251,8 @@ namespace pal string_t get_timestamp(); bool getcwd(string_t* recv); - string_t to_lower(const char_t* in); - - inline void file_flush(FILE *f) { std::fflush(f); } + inline void file_flush(FILE* f) { std::fflush(f); } inline void err_flush() { std::fflush(stderr); } inline void out_flush() { std::fflush(stdout); } @@ -283,7 +280,7 @@ namespace pal bool get_own_module_path(string_t* recv); bool get_method_module_path(string_t* recv, void* method); bool get_module_path(dll_t mod, string_t* recv); - bool get_current_module(dll_t *mod); + bool get_current_module(dll_t* mod); bool getenv(const char_t* name, string_t* recv); bool get_default_servicing_directory(string_t* recv); @@ -307,7 +304,7 @@ namespace pal int xtoi(const char_t* input); - bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path); + bool get_loaded_library(const char_t* library_name, const char* symbol_name, /*out*/ dll_t* dll, /*out*/ string_t* path); bool load_library(const string_t* path, dll_t* dll); proc_t get_symbol(dll_t library, const char* name); void unload_library(dll_t library); diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index 2db2dd8488f..411298a08f4 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -50,13 +50,6 @@ #error "Don't know how to obtain max path on this platform" #endif -pal::string_t pal::to_lower(const pal::char_t* in) -{ - pal::string_t ret = in; - std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); - return ret; -} - pal::string_t pal::get_timestamp() { std::time_t t = std::time(nullptr); @@ -75,7 +68,7 @@ bool pal::touch_file(const pal::string_t& path) trace::warning(_X("open(%s) failed in %s"), path.c_str(), _STRINGIFY(__FUNCTION__)); return false; } - (void) close(fd); + (void)close(fd); return true; } @@ -146,12 +139,12 @@ bool pal::getcwd(pal::string_t* recv) namespace { - bool get_loaded_library_from_proc_maps(const pal::char_t *library_name, pal::dll_t *dll, pal::string_t *path) + bool get_loaded_library_from_proc_maps(const pal::char_t* library_name, pal::dll_t* dll, pal::string_t* path) { - char *line = nullptr; + char* line = nullptr; size_t lineLen = 0; ssize_t read; - FILE *file = pal::file_open(_X("/proc/self/maps"), _X("r")); + FILE* file = pal::file_open(_X("/proc/self/maps"), _X("r")); if (file == nullptr) return false; @@ -192,10 +185,10 @@ namespace } bool pal::get_loaded_library( - const char_t *library_name, - const char *symbol_name, - /*out*/ dll_t *dll, - /*out*/ pal::string_t *path) + const char_t* library_name, + const char* symbol_name, + /*out*/ dll_t* dll, + /*out*/ pal::string_t* path) { pal::string_t library_name_local; #if defined(TARGET_OSX) @@ -340,7 +333,7 @@ bool pal::get_default_servicing_directory(string_t* recv) bool is_read_write_able_directory(pal::string_t& dir) { return pal::realpath(&dir) && - (access(dir.c_str(), R_OK | W_OK | X_OK) == 0); + (access(dir.c_str(), R_OK | W_OK | X_OK) == 0); } bool get_extraction_base_parent_directory(pal::string_t& directory) @@ -388,18 +381,41 @@ bool pal::get_global_dotnet_dirs(std::vector* recv) bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv) { - *recv = _X("/etc/dotnet/install_location"); + recv->assign(_X("/etc/dotnet/install_location")); // ***Used only for testing*** pal::string_t environment_install_location_override; if (test_only_getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override)) { - *recv = environment_install_location_override; + recv->assign(environment_install_location_override); } return true; } +namespace +{ + bool get_line_from_file(FILE* pFile, pal::string_t& line) + { + line = pal::string_t(); + char buffer[256]; + while (fgets(buffer, sizeof(buffer), pFile)) + { + line += (pal::char_t*)buffer; + size_t len = line.length(); + + // fgets includes the newline character in the string - so remove it. + if (len > 0 && line[len - 1] == '\n') + { + line.pop_back(); + break; + } + } + + return !line.empty(); + } +} + bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) { recv->clear(); @@ -424,35 +440,60 @@ bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) FILE* install_location_file = pal::file_open(install_location_file_path, "r"); if (install_location_file == nullptr) { - trace::verbose(_X("The install_location file failed to open.")); + trace::error(_X("The install_location file ['%s'] failed to open: %s."), install_location_file_path.c_str(), pal::strerror(errno)); return false; } - bool result = false; + pal::string_t install_location; + int current_line = 0; + bool is_first_line = true, install_location_found = false; - char buf[PATH_MAX]; - char* install_location = fgets(buf, sizeof(buf), install_location_file); - if (install_location != nullptr) + while (get_line_from_file(install_location_file, install_location)) { - size_t len = pal::strlen(install_location); - - // fgets includes the newline character in the string - so remove it. - if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n') + current_line++; + size_t arch_sep = install_location.find(_X('=')); + if (arch_sep == pal::string_t::npos) { - install_location[len - 1] = '\0'; + if (is_first_line) + { + recv->assign(install_location); + install_location_found = true; + trace::verbose(_X("Found install location path '%s'."), install_location.c_str()); + } + else + { + trace::warning(_X("Found unprefixed install location path '%s' on line %d."), install_location.c_str(), current_line); + trace::warning(_X("Only the first line in '%s' may not have an architecture prefix."), install_location_file_path.c_str()); + } + + is_first_line = false; + continue; } - trace::verbose(_X("Using install location '%s'."), install_location); - *recv = install_location; - result = true; - } - else - { - trace::verbose(_X("The install_location file first line could not be read.")); + pal::string_t arch_prefix = install_location.substr(0, arch_sep); + pal::string_t path_to_location = install_location.substr(arch_sep + 1); + + trace::verbose(_X("Found architecture-specific install location path: '%s' ('%s')."), path_to_location.c_str(), arch_prefix.c_str()); + if (pal::strcasecmp(arch_prefix.c_str(), get_arch()) == 0) + { + recv->assign(path_to_location); + install_location_found = true; + trace::verbose(_X("Found architecture-specific install location path matching the current host architecture ('%s'): '%s'."), arch_prefix.c_str(), path_to_location.c_str()); + break; + } + + is_first_line = false; } fclose(install_location_file); - return result; + if (!install_location_found) + { + trace::warning(_X("Did not find any install location in '%s'."), install_location_file_path.c_str()); + return false; + } + + trace::verbose(_X("Using install location '%s'."), recv->c_str()); + return true; } bool pal::get_default_installation_dir(pal::string_t* recv) @@ -467,17 +508,17 @@ bool pal::get_default_installation_dir(pal::string_t* recv) // *************************** #if defined(TARGET_OSX) - recv->assign(_X("/usr/local/share/dotnet")); + recv->assign(_X("/usr/local/share/dotnet")); #else - recv->assign(_X("/usr/share/dotnet")); + recv->assign(_X("/usr/share/dotnet")); #endif - return true; + return true; } pal::string_t trim_quotes(pal::string_t stringToCleanup) { - pal::char_t quote_array[2] = {'\"', '\''}; - for (size_t index = 0; index < sizeof(quote_array)/sizeof(quote_array[0]); index++) + pal::char_t quote_array[2] = { '\"', '\'' }; + for (size_t index = 0; index < sizeof(quote_array) / sizeof(quote_array[0]); index++) { size_t pos = stringToCleanup.find(quote_array[index]); while (pos != std::string::npos) @@ -553,11 +594,11 @@ pal::string_t pal::get_current_os_rid_platform() if (ret == 0) { - char *pos = strchr(str, '.'); + char* pos = strchr(str, '.'); if (pos) { ridOS.append(_X("freebsd.")) - .append(str, pos - str); + .append(str, pos - str); } } @@ -589,7 +630,7 @@ pal::string_t pal::get_current_os_rid_platform() if (strncmp(utsname_obj.version, "omnios", strlen("omnios")) == 0) { ridOS.append(_X("omnios.")) - .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15 + .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15 } else if (strncmp(utsname_obj.version, "illumos-", strlen("illumos-")) == 0) { @@ -598,7 +639,7 @@ pal::string_t pal::get_current_os_rid_platform() else if (strncmp(utsname_obj.version, "joyent_", strlen("joyent_")) == 0) { ridOS.append(_X("smartos.")) - .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020 + .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020 } return ridOS; @@ -621,11 +662,11 @@ pal::string_t pal::get_current_os_rid_platform() return ridOS; } - char *pos = strchr(utsname_obj.version, '.'); + char* pos = strchr(utsname_obj.version, '.'); if (pos) { ridOS.append(_X("solaris.")) - .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11 + .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11 } return ridOS; @@ -771,7 +812,7 @@ bool pal::get_own_executable_path(pal::string_t* recv) bool pal::get_own_module_path(string_t* recv) { Dl_info info; - if (dladdr((void *)&pal::get_own_module_path, &info) == 0) + if (dladdr((void*)&pal::get_own_module_path, &info) == 0) return false; recv->assign(info.dli_fname); @@ -793,7 +834,7 @@ bool pal::get_module_path(dll_t module, string_t* recv) return false; } -bool pal::get_current_module(dll_t *mod) +bool pal::get_current_module(dll_t* mod) { return false; } @@ -876,31 +917,31 @@ static void readdir(const pal::string_t& path, const pal::string_t& pattern, boo } break; - // Handle symlinks and file systems that do not support d_type + // Handle symlinks and file systems that do not support d_type case DT_LNK: case DT_UNKNOWN: + { + struct stat sb; + + if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1) { - struct stat sb; - - if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1) - { - continue; - } - - if (onlydirectories) - { - if (!S_ISDIR(sb.st_mode)) - { - continue; - } - break; - } - else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) - { - continue; - } + continue; } - break; + + if (onlydirectories) + { + if (!S_ISDIR(sb.st_mode)) + { + continue; + } + break; + } + else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) + { + continue; + } + } + break; default: continue; diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index 8a9e35a3236..c992f92da82 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -26,7 +26,7 @@ bool GetModuleFileNameWrapper(HMODULE hModule, pal::string_t* recv) return false; path.resize(dwModuleFileName); - *recv = path; + recv->assign(path); return true; } @@ -40,13 +40,6 @@ bool GetModuleHandleFromAddress(void *addr, HMODULE *hModule) return (res != FALSE); } -pal::string_t pal::to_lower(const pal::char_t* in) -{ - pal::string_t ret = in; - std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower); - return ret; -} - pal::string_t pal::get_timestamp() { std::time_t t = std::time(nullptr); @@ -339,7 +332,7 @@ bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv) const pal::char_t* value; get_dotnet_install_location_registry_path(&key_hive, &sub_key, &value); - *recv = (key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value; + recv->assign((key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value); return true; #endif } diff --git a/src/native/corehost/hostmisc/utils.cpp b/src/native/corehost/hostmisc/utils.cpp index 055b4f6a31d..abf1aeee89e 100644 --- a/src/native/corehost/hostmisc/utils.cpp +++ b/src/native/corehost/hostmisc/utils.cpp @@ -161,7 +161,7 @@ void remove_trailing_dir_seperator(pal::string_t* dir) void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl) { - size_t pos = 0; + size_t pos = 0; while ((pos = path->find(match, pos)) != pal::string_t::npos) { (*path)[pos] = repl; @@ -170,7 +170,7 @@ void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl) pal::string_t get_replaced_char(const pal::string_t& path, pal::char_t match, pal::char_t repl) { - size_t pos = path.find(match); + size_t pos = path.find(match); if (pos == pal::string_t::npos) { return path; @@ -241,7 +241,7 @@ bool get_env_shared_store_dirs(std::vector* dirs, const pal::stri return true; } -bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm) +bool get_global_shared_store_dirs(std::vector* dirs, const pal::string_t& arch, const pal::string_t& tfm) { std::vector global_dirs; if (!pal::get_global_dotnet_dirs(&global_dirs)) @@ -348,14 +348,26 @@ bool try_stou(const pal::string_t& str, unsigned* num) return true; } -pal::string_t get_dotnet_root_env_var_name() +bool get_dotnet_root_from_env(pal::string_t* dotnet_root_env_var_name, pal::string_t* recv) { + *dotnet_root_env_var_name = _X("DOTNET_ROOT_"); + dotnet_root_env_var_name->append(to_upper(get_arch())); + if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + return true; + +#if defined(WIN32) if (pal::is_running_in_wow64()) { - return pal::string_t(_X("DOTNET_ROOT(x86)")); + *dotnet_root_env_var_name = _X("DOTNET_ROOT(x86)"); + if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv)) + return true; } +#endif - return pal::string_t(_X("DOTNET_ROOT")); + // If no architecture-specific environment variable was set + // fallback to the default DOTNET_ROOT. + *dotnet_root_env_var_name = _X("DOTNET_ROOT"); + return get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv); } /** @@ -402,7 +414,7 @@ void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& na trace::verbose(_X("Runtime config is cfg=%s dev=%s"), cfg->c_str(), dev_cfg->c_str()); } -pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path) +pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path) { // If coreclr exists next to hostfxr, assume everything is local (e.g. self-contained) pal::string_t fxr_dir = get_directory(fxr_path); @@ -414,7 +426,7 @@ pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path) return get_directory(get_directory(fxr_root)); } -pal::string_t get_download_url(const pal::char_t *framework_name, const pal::char_t *framework_version) +pal::string_t get_download_url(const pal::char_t* framework_name, const pal::char_t* framework_version) { pal::string_t url = DOTNET_CORE_APPLAUNCH_URL _X("?"); if (framework_name != nullptr && pal::strlen(framework_name) > 0) @@ -441,6 +453,18 @@ pal::string_t get_download_url(const pal::char_t *framework_name, const pal::cha return url; } +pal::string_t to_lower(const pal::char_t* in) { + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + +pal::string_t to_upper(const pal::char_t* in) { + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); + return ret; +} + #define TEST_ONLY_MARKER "d38cc827-e34f-4453-9df4-1e796e9f1d07" // Retrieves environment variable which is only used for testing. diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index d0dc381b69c..b4e31b599fa 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -43,16 +43,19 @@ void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vecto bool get_file_path_from_env(const pal::char_t* env_key, pal::string_t* recv); size_t index_of_non_numeric(const pal::string_t& str, size_t i); bool try_stou(const pal::string_t& str, unsigned* num); -pal::string_t get_dotnet_root_env_var_name(); +bool get_dotnet_root_from_env(pal::string_t* used_dotnet_root_env_var_name, pal::string_t* recv); pal::string_t get_deps_from_app_binary(const pal::string_t& app_base, const pal::string_t& app); pal::string_t get_runtime_config_path(const pal::string_t& path, const pal::string_t& name); pal::string_t get_runtime_config_dev_path(const pal::string_t& path, const pal::string_t& name); void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& name, pal::string_t* cfg, pal::string_t* dev_cfg); -pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path); +pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path); // Get a download URL for a specific framework and version // If no framework is specified, a download URL for the runtime is returned -pal::string_t get_download_url(const pal::char_t *framework_name = nullptr, const pal::char_t *framework_version = nullptr); +pal::string_t get_download_url(const pal::char_t* framework_name = nullptr, const pal::char_t* framework_version = nullptr); + +pal::string_t to_lower(const pal::char_t* in); +pal::string_t to_upper(const pal::char_t* in); // Retrieves environment variable which is only used for testing. // This will return the value of the variable only if the product binary is stamped @@ -63,7 +66,7 @@ bool test_only_getenv(const pal::char_t* name, pal::string_t* recv); class propagate_error_writer_t { public: - typedef trace::error_writer_fn(__cdecl *set_error_writer_fn)(trace::error_writer_fn error_writer); + typedef trace::error_writer_fn(__cdecl* set_error_writer_fn)(trace::error_writer_fn error_writer); private: set_error_writer_fn m_set_error_writer; diff --git a/src/native/corehost/test/nativehost/nativehost.cpp b/src/native/corehost/test/nativehost/nativehost.cpp index b323f95a829..80280b7bdbb 100644 --- a/src/native/corehost/test/nativehost/nativehost.cpp +++ b/src/native/corehost/test/nativehost/nativehost.cpp @@ -40,7 +40,7 @@ int main(const int argc, const pal::char_t *argv[]) // args: ... [] [] [] [] bool explicit_load = false; if (argc >= 3) - explicit_load = pal::strcmp(pal::to_lower(argv[2]).c_str(), _X("true")) == 0; + explicit_load = pal::strcmp(to_lower(argv[2]).c_str(), _X("true")) == 0; const pal::char_t *assembly_path = nullptr; if (argc >= 4 && pal::strcmp(argv[3], _X("nullptr")) != 0) @@ -117,7 +117,7 @@ int main(const int argc, const pal::char_t *argv[]) if (static_cast(res) == StatusCode::Success) { std::cout << "get_hostfxr_path succeeded" << std::endl; - std::cout << "hostfxr_path: " << tostr(pal::to_lower(fxr_path.c_str())).data() << std::endl; + std::cout << "hostfxr_path: " << tostr(to_lower(fxr_path.c_str())).data() << std::endl; return EXIT_SUCCESS; } else From ea3f4034314481acde910dc571da5006a571dbfa Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Wed, 23 Jun 2021 12:09:38 +0200 Subject: [PATCH 063/926] Fix finalizer issue with regions (#54550) This fixes an issue in Server GC where an item in the finalizer queue became stale due to not being relocated. The problem was that a finalizable object was allocated on one heap, but registered in the finalizer queue of another heap (this is possible due to heap balancing). In CFinalize::UpdatePromotedGenerations, we ask for the generation of an object, and move the object to the correct section of the finalizer queue. In the error case, we obtained the wrong result for the generation of the object because it lived on another heap, and that heap hadn't set the final generation for the region containing the object yet. So we ended up moving the finalizer entry to the section corresponding to gen 2, and missed a relocation of the object occurring in a gen 1 collection afterwards. Fix: It seems best to make sure an object is always registered for finalization on the heap it's allocated from, so the fix simply fetches the heap from the alloc context after the allocation in the case of SOH, or determines it by calling gc_heap::heap_of in the case of LOH and POH. In the case of SOH, I added an assert to ensure that the heap obtained agrees with the result of calling gc_heap::heap_of. I also added some dprintf calls to the finalizer logic to aid in future investigations. --- src/coreclr/gc/gc.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 6a6c63d1ac2..9cb439e6504 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -42257,6 +42257,15 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, gen_num, acontext->alloc_bytes_uoh); ASSERT(((size_t)newAlloc & 7) == 0); +#ifdef MULTIPLE_HEAPS + if (flags & GC_ALLOC_FINALIZE) + { + // the heap may have changed due to heap balancing - it's important + // to register the object for finalization on the heap it was allocated on + hp = gc_heap::heap_of ((uint8_t*)newAlloc); + } +#endif //MULTIPLE_HEAPS + #ifdef FEATURE_STRUCTALIGN newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size); #endif // FEATURE_STRUCTALIGN @@ -42276,6 +42285,16 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_ newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext, flags); } +#ifdef MULTIPLE_HEAPS + if (flags & GC_ALLOC_FINALIZE) + { + // the heap may have changed due to heap balancing - it's important + // to register the object for finalization on the heap it was allocated on + hp = acontext->get_alloc_heap()->pGenGCHeap; + assert ((newAlloc == nullptr) || (hp == gc_heap::heap_of ((uint8_t*)newAlloc))); + } +#endif //MULTIPLE_HEAPS + #ifdef FEATURE_STRUCTALIGN newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext); #endif // FEATURE_STRUCTALIGN @@ -44404,6 +44423,9 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp) unsigned int Seg = gen_segment (gen); Object** startIndex = SegQueue (Seg); + + dprintf (3, ("RelocateFinalizationData gen=%d, [%Ix,%Ix[", gen, startIndex, SegQueue (FreeList))); + for (Object** po = startIndex; po < SegQueue (FreeList);po++) { GCHeap::Relocate (po, &sc); @@ -44413,6 +44435,8 @@ CFinalize::RelocateFinalizationData (int gen, gc_heap* hp) void CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) { + dprintf(3, ("UpdatePromotedGenerations gen=%d, gen_0_empty_p=%d", gen, gen_0_empty_p)); + // update the generation fill pointers. // if gen_0_empty is FALSE, test each object to find out if // it was promoted or not @@ -44437,6 +44461,8 @@ CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) int new_gen = g_theGCHeap->WhichGeneration (*po); if (new_gen != i) { + dprintf (3, ("Moving object %Ix->%Ix from gen %d to gen %d", po, *po, i, new_gen)); + if (new_gen > i) { //promotion @@ -44468,6 +44494,8 @@ CFinalize::GrowArray() } memcpy (newArray, m_Array, oldArraySize*sizeof(Object*)); + dprintf (3, ("Grow finalizer array [%Ix,%Ix[ -> [%Ix,%Ix[", m_Array, m_EndArray, newArray, &m_Array[newArraySize])); + //adjust the fill pointers for (int i = 0; i < FreeList; i++) { From 2b7927faff7d51c577f82f3abfe3bc110cdde1e7 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Jun 2021 14:02:37 +0200 Subject: [PATCH 064/926] Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder (#54280) * Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder * address some feedback * Make GetEvents() safe * make GetProperties safe * Mark GetProperties with RUC --- .../ref/System.Data.Common.cs | 2 + .../src/ILLink/ILLink.Suppressions.xml | 54 ------ .../Data/Common/DbConnectionStringBuilder.cs | 26 ++- .../DbConnectionStringBuilder.cs | 159 ++++++++++++++++++ .../System.Data.Common.TrimmingTests.proj | 5 + 5 files changed, 191 insertions(+), 55 deletions(-) create mode 100644 src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index a4bc1f651f2..ce7de38a7ae 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -2095,6 +2095,7 @@ namespace System.Data.Common System.Data.IDbTransaction System.Data.IDbConnection.BeginTransaction(System.Data.IsolationLevel isolationLevel) { throw null; } System.Data.IDbCommand System.Data.IDbConnection.CreateCommand() { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public partial class DbConnectionStringBuilder : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.ComponentModel.ICustomTypeDescriptor { public DbConnectionStringBuilder() { } @@ -2130,6 +2131,7 @@ namespace System.Data.Common protected internal void ClearPropertyDescriptors() { } public virtual bool ContainsKey(string keyword) { throw null; } public virtual bool EquivalentTo(System.Data.Common.DbConnectionStringBuilder connectionStringBuilder) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] protected virtual void GetProperties(System.Collections.Hashtable propertyDescriptors) { } public virtual bool Remove(string keyword) { throw null; } public virtual bool ShouldSerialize(string keyword) { throw null; } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index ffcb7d78381..3e47be79791 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -103,59 +103,5 @@ member M:System.Data.ProviderBase.SchemaMapping.SetupSchemaWithoutKeyInfo(System.Data.MissingMappingAction,System.Data.MissingSchemaAction,System.Boolean,System.Data.DataColumn,System.Object) - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.GetProperties(System.Collections.Hashtable) - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetAttributes - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetClassName - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetComponentName - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetConverter - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultEvent - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetDefaultProperty - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEditor(System.Type) - - - ILLink - IL2026 - member - M:System.Data.Common.DbConnectionStringBuilder.System#ComponentModel#ICustomTypeDescriptor#GetEvents - diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 3c3ac63548e..027fe42f0b0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -12,6 +12,7 @@ using System.Diagnostics.CodeAnalysis; namespace System.Data.Common { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public class DbConnectionStringBuilder : IDictionary, ICustomTypeDescriptor { // keyword->value currently listed in the connection string @@ -387,6 +388,7 @@ namespace System.Data.Common return attributes; } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] private PropertyDescriptorCollection GetProperties() { PropertyDescriptorCollection? propertyDescriptors = _propertyDescriptors; @@ -412,11 +414,16 @@ namespace System.Data.Common return propertyDescriptors; } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] protected virtual void GetProperties(Hashtable propertyDescriptors) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}", ObjectID); try { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); + // show all strongly typed properties (not already added) // except ConnectionString iff BrowsableConnectionString Attribute[]? attributes; @@ -562,16 +569,28 @@ namespace System.Data.Common return new PropertyDescriptorCollection(filteredPropertiesArray); } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated + // TODO-NULLABLE: Enable after System.ComponentModel.TypeConverter is annotated #nullable disable + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string ICustomTypeDescriptor.GetClassName() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetClassName(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string ICustomTypeDescriptor.GetComponentName() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetComponentName(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); @@ -606,8 +625,13 @@ namespace System.Data.Common { return TypeDescriptor.GetDefaultEvent(this, true); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { + // Below call is necessary to tell the trimmer that it should mark derived types appropriately. + // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. + Type thisType = GetType(); return TypeDescriptor.GetEvents(this, true); } [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs new file mode 100644 index 00000000000..78ac56e9f75 --- /dev/null +++ b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Data.Common; +using System.ComponentModel; +using System.Collections; + +namespace DbConnectionStringBuilderTrimmingTests +{ + class Program + { + static int Main(string[] args) + { + DbConnectionStringBuilder2 dcsb2 = new(); + ICustomTypeDescriptor td = dcsb2; + + if (td.GetClassName() != "DbConnectionStringBuilderTrimmingTests.DbConnectionStringBuilder2") + { + throw new Exception("Class name got trimmed"); + } + + if (td.GetComponentName() != "Test Component Name") + { + throw new Exception("Component name got trimmed"); + } + + bool foundAttr = false; + + foreach (Attribute attr in td.GetAttributes()) + { + if (attr.GetType().Name == "TestAttribute") + { + if (attr.ToString() != "Test Attribute Value") + { + throw new Exception("Test attribute value differs"); + } + + if (foundAttr) + { + throw new Exception("More than one attribute found"); + } + + foundAttr = true; + } + } + + if (!foundAttr) + { + throw new Exception("Attribute not found"); + } + + bool foundEvent = false; + bool foundEventWithDisplayName = false; + + foreach (EventDescriptor ev in td.GetEvents()) + { + if (ev.DisplayName == "TestEvent") + { + if (foundEvent) + { + throw new Exception("More than one event TestEvent found."); + } + + foundEvent = true; + } + + if (ev.DisplayName == "Event With DisplayName") + { + if (foundEventWithDisplayName) + { + throw new Exception("More than one event with display name found."); + } + + foundEventWithDisplayName = true; + } + } + + if (!foundEvent) + { + throw new Exception("Event not found"); + } + + if (!foundEventWithDisplayName) + { + throw new Exception("Event with DisplayName not found"); + } + + bool propertyFound = false; + foreach (DictionaryEntry kv in dcsb2.GetProperties2()) + { + PropertyDescriptor val = (PropertyDescriptor)kv.Value; + if (val.Name == "TestProperty") + { + if (propertyFound) + { + throw new Exception("More than one property TestProperty found."); + } + + propertyFound = true; + } + } + + if (!propertyFound) + { + throw new Exception("Property not found"); + } + + return 100; + } + } + + [Test("Test Attribute Value")] + class DbConnectionStringBuilder2 : DbConnectionStringBuilder, IComponent + { +#pragma warning disable CS0067 // The event is never used + public event EventHandler Disposed; + public event Action TestEvent; + [DisplayName("Event With DisplayName")] + public event Action TestEvent2; +#pragma warning restore CS0067 + + public string TestProperty { get; set; } + public ISite Site { get => new TestSite(); set => throw new NotImplementedException(); } + public void Dispose() { } + + public Hashtable GetProperties2() + { + Hashtable propertyDescriptors = new Hashtable(); + GetProperties(propertyDescriptors); + return propertyDescriptors; + } + } + + class TestSite : INestedSite + { + public string FullName => null; + public IComponent Component => throw new NotImplementedException(); + public IContainer Container => throw new NotImplementedException(); + public bool DesignMode => throw new NotImplementedException(); + public string Name { get => "Test Component Name"; set => throw new NotImplementedException(); } + public object GetService(Type serviceType) => null; + } + + class TestAttribute : Attribute + { + public string Test { get; private set; } + + public TestAttribute(string test) + { + Test = test; + } + + public override string ToString() + { + return Test; + } + } +} diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj index da4a46f2ae1..7254c33fc9c 100644 --- a/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj +++ b/src/libraries/System.Data.Common/tests/TrimmingTests/System.Data.Common.TrimmingTests.proj @@ -1,5 +1,10 @@ + + + + + From 56b3751de114202a44324014196102efc3102775 Mon Sep 17 00:00:00 2001 From: Lakshan Fernando Date: Wed, 23 Jun 2021 05:11:42 -0700 Subject: [PATCH 065/926] Root ComActivator for hosting (#54524) * rooting ComActivator that is needed for hosting * FB --- .../src/ILLink/ILLink.Descriptors.Windows.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml index 0648aed9763..52ab4af9bef 100644 --- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml @@ -4,5 +4,11 @@ + + + + + + From 9039c7764e989d97e59db4982cb2b73c5179b471 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Wed, 23 Jun 2021 14:56:44 +0200 Subject: [PATCH 066/926] Respect EventSource::IsSupported setting in more codepaths (#51977) --- .../src/System/Diagnostics/Tracing/EventSource.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 321d60de739..d6093b4fb66 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -495,6 +495,9 @@ namespace System.Diagnostics.Tracing /// public override string ToString() { + if (!IsSupported) + return base.ToString()!; + return SR.Format(SR.EventSource_ToString, Name, Guid); } From fa86c81b61b10daf3da4438b894a7d4405e229e8 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 23 Jun 2021 09:38:02 -0400 Subject: [PATCH 067/926] Make mono_polling_required a public symbol (#54592) Resolves build failure with the Android aot+llvm functional test --- src/mono/mono/utils/mono-threads-coop.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/utils/mono-threads-coop.h b/src/mono/mono/utils/mono-threads-coop.h index 47c20c14025..97591581294 100644 --- a/src/mono/mono/utils/mono-threads-coop.h +++ b/src/mono/mono/utils/mono-threads-coop.h @@ -20,7 +20,7 @@ #include "mono/metadata/icalls.h" /* JIT specific interface */ -extern volatile size_t mono_polling_required; +MONO_API_DATA volatile size_t mono_polling_required; /* Internal API */ From c0ed319ba3164bfbd9c2c4d66f68f29f8cd78b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 23 Jun 2021 09:44:41 -0400 Subject: [PATCH 068/926] Change PathInternal.IsCaseSensitive to a constant (#54340) * Return constants in PathInternal.GetIsCaseSensitive() on mobile platforms In particular on Android probing using I/O is slow and contributes to slow app startup. Fixes https://github.com/dotnet/runtime/issues/54339 * Implement Path.IsCaseSensitive as PathInternal.IsCaseSensitive Also Path.StringComparison => PathInternal.StringComparison * Add test for PathInternal.IsCaseSensitive Move GetIsCaseSensitiveByProbing to FileSystemTest * Drop PathInternal.s_isCaseSensitive cache field * Delete Path.IsCaseSensitive and Path.StringComparison update callers to use PathInternal.IsCaseSensitive and PathInternal.StringComparison * Remove catch clause from GetIsCaseSensitiveByProbing * Mark new test [OuterLoop] * Apply suggestions from code review Co-authored-by: Stephen Toub Co-authored-by: Jan Kotas Co-authored-by: Adam Sitnik Co-authored-by: Stephen Toub --- .../System/IO/PathInternal.CaseSensitivity.cs | 36 +++++-------------- .../tests/FileSystemTest.cs | 19 ++++++++++ ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 2 ++ .../tests/PathInternalTests.cs | 18 ++++++++++ .../tests/System.IO.FileSystem.Tests.csproj | 3 ++ .../src/System/IO/Path.Unix.cs | 12 ------- .../src/System/IO/Path.Windows.cs | 3 -- .../src/System/IO/Path.cs | 8 +---- .../Runtime/Loader/AssemblyLoadContext.cs | 2 +- 9 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs diff --git a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs index 5611e9c6969..3e3486157e8 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs @@ -12,47 +12,27 @@ namespace System.IO /// Contains internal path helpers that are shared between many projects. internal static partial class PathInternal { - private static readonly bool s_isCaseSensitive = GetIsCaseSensitive(); - /// Returns a comparison that can be used to compare file and directory names for equality. internal static StringComparison StringComparison { get { - return s_isCaseSensitive ? + return IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; } } /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive { get { return s_isCaseSensitive; } } - - /// - /// Determines whether the file system is case sensitive. - /// - /// - /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable, - /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters - /// and then tests for its existence with lower-case letters. This could return invalid results in corner - /// cases where, for example, different file systems are mounted with differing sensitivities. - /// - private static bool GetIsCaseSensitive() + internal static bool IsCaseSensitive { - try + get { - string pathWithUpperCase = Path.Combine(Path.GetTempPath(), "CASESENSITIVETEST" + Guid.NewGuid().ToString("N")); - using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) - { - string lowerCased = pathWithUpperCase.ToLowerInvariant(); - return !File.Exists(lowerCased); - } - } - catch - { - // In case something goes wrong (e.g. temp pointing to a privilieged directory), we don't - // want to fail just because of a casing test, so we assume case-insensitive-but-preserving. - return false; +#if MS_IO_REDIST + return false; // Windows is always case-insensitive +#else + return !(OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()); +#endif } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs index 2ba15d31b41..d82346f6f4f 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs @@ -127,5 +127,24 @@ namespace System.IO.Tests Assert.Equal(0, AdminHelpers.RunAsSudo($"umount {readOnlyDirectory}")); } } + + /// + /// Determines whether the file system is case sensitive by creating a file in the specified folder and observing the result. + /// + /// + /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable, + /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters + /// and then tests for its existence with lower-case letters. This could return invalid results in corner + /// cases where, for example, different file systems are mounted with differing sensitivities. + /// + protected static bool GetIsCaseSensitiveByProbing(string probingDirectory) + { + string pathWithUpperCase = Path.Combine(probingDirectory, "CASESENSITIVETEST" + Guid.NewGuid().ToString("N")); + using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) + { + string lowerCased = pathWithUpperCase.ToLowerInvariant(); + return !File.Exists(lowerCased); + } + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 628bbb4324a..58669657e51 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -37,6 +37,8 @@ + diff --git a/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs new file mode 100644 index 00000000000..c908601862e --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.Tests +{ + public class PathInternalTests : FileSystemTest + { + [Fact] + [OuterLoop] + public void PathInternalIsCaseSensitiveMatchesProbing() + { + string probingDirectory = TestDirectory; + Assert.Equal(GetIsCaseSensitiveByProbing(probingDirectory), PathInternal.IsCaseSensitive); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index a271109ce2f..94cec86ce3a 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -51,6 +51,7 @@ + @@ -191,6 +192,8 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs index ecc990b07c6..5a7f0c8d1bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs @@ -129,17 +129,5 @@ namespace System.IO return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan.Empty; } - /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive - { - get - { - #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS - return false; - #else - return true; - #endif - } - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs index 5e641806926..5f4b8e11a19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs @@ -230,9 +230,6 @@ namespace System.IO return pathRoot <= 0 ? ReadOnlySpan.Empty : path.Slice(0, pathRoot); } - /// Gets whether the system is case-sensitive. - internal static bool IsCaseSensitive => false; - /// /// Returns the volume name for dos, UNC and device paths. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index 7601e45e074..104704de7b7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -860,7 +860,7 @@ namespace System.IO /// Thrown if or is null or an empty string. public static string GetRelativePath(string relativeTo, string path) { - return GetRelativePath(relativeTo, path, StringComparison); + return GetRelativePath(relativeTo, path, PathInternal.StringComparison); } private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) @@ -957,12 +957,6 @@ namespace System.IO return sb.ToString(); } - /// Returns a comparison that can be used to compare file and directory names for equality. - internal static StringComparison StringComparison => - IsCaseSensitive ? - StringComparison.Ordinal : - StringComparison.OrdinalIgnoreCase; - /// /// Trims one trailing directory separator beyond the root of the path. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 6c733ef6ca1..2a5a3c1da07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -781,7 +781,7 @@ namespace System.Runtime.Loader string assemblyPath = Path.Combine(parentDirectory, assemblyName.CultureName!, $"{assemblyName.Name}.dll"); bool exists = System.IO.FileSystem.FileExists(assemblyPath); - if (!exists && Path.IsCaseSensitive) + if (!exists && PathInternal.IsCaseSensitive) { #if CORECLR if (AssemblyLoadContext.IsTracingEnabled()) From 0416c3469b7bccff087b89db3d9967dfd36070a3 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 23 Jun 2021 12:53:56 -0400 Subject: [PATCH 069/926] [wasm] Compile .bc->.o in parallel, before passing to the linker (#54053) --- eng/Versions.props | 2 +- src/libraries/tests.proj | 5 + src/mono/wasm/build/WasmApp.Native.targets | 76 ++++++--- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 13 +- src/tasks/Common/Utils.cs | 110 ++++++++++-- src/tasks/WasmAppBuilder/EmccCompile.cs | 158 ++++++++++++++++++ src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 5 - .../WasmAppBuilder/WasmAppBuilder.csproj | 2 + 8 files changed, 323 insertions(+), 48 deletions(-) create mode 100644 src/tasks/WasmAppBuilder/EmccCompile.cs diff --git a/eng/Versions.props b/eng/Versions.props index 2295b6e440d..a194baa33de 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,7 +140,7 @@ These are used as reference assemblies only, so they must not take a ProdCon/source-build version. Insert "RefOnly" to avoid assignment via PVP. --> - 16.8.0 + 16.9.0 $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b1ca6c3a1dd..5cad3c54088 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -255,6 +255,8 @@ + + @@ -276,6 +278,9 @@ + + + diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index b6897fbd2b4..c034763ca0f 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -3,6 +3,7 @@ + <_WasmBuildNativeCoreDependsOn> @@ -165,7 +166,7 @@ <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz $(_EmccOptimizationFlagDefault) - $(_EmccOptimizationFlagDefault) + -O0 @@ -173,10 +174,6 @@ <_EmccCommonFlags Include="$(EmccFlags)" /> <_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" /> <_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" /> - <_EmccCommonFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCommonFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCommonFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> - <_EmccCommonFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> @@ -186,11 +183,7 @@ - <_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" /> - <_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" /> - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> - <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" /> @@ -227,54 +220,84 @@ <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> <_EmccCFlags Include="@(_EmccCommonFlags)" /> + + <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> + <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DCORE_BINDINGS" /> <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> <_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'"%(Identity)"', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" /> <_EmccCFlags Include="$(EmccExtraCFlags)" /> + + <_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" /> + <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" /> + <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + <_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" /> + <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat + <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp + + + - + - <_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> - <_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" /> - <_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" /> - <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" /> <_EmccLDFlags Include="@(_EmccCommonFlags)" /> + <_EmccLDFlags Include="-s TOTAL_MEMORY=536870912" /> <_EmccLDFlags Include="$(EmccExtraLDFlags)" /> + - <_EmccLDFlags Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> - <_EmccLDFlags Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> + - <_EmccLDFlags Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" /> - <_EmccLDFlags Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" /> + + + <_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" /> + <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" /> - <_EmccLDFlags Include=""%(_WasmNativeFileForLinking.Identity)"" /> - <_EmccLDFlags Include=""%(_WasmObjects.Identity)"" /> - <_EmccLDFlags Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" /> + + <_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> + + <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> + <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> + <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> + + <_EmccLinkStepArgs Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" /> + <_EmccLinkStepArgs Include="--post-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" /> + + <_EmccLinkStepArgs Include=""%(_WasmNativeFileForLinking.Identity)"" /> + <_EmccLinkStepArgs Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" /> <_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp - - + + + + @@ -289,7 +312,7 @@ - $(EmccFlags) -DDRIVER_GEN=1 + $(EmccExtraCFlags) -DDRIVER_GEN=1 void mono_profiler_init_aot (const char *desc)%3B EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_profiler_init_aot (desc)%3B } @@ -405,7 +428,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ Profilers="$(WasmProfilers)" AotModulesTablePath="$(_WasmIntermediateOutputPath)driver-gen.c" UseLLVM="true" - DisableParallelAot="true" + DisableParallelAot="$(DisableParallelAot)" DedupAssembly="$(_WasmDedupAssembly)" LLVMDebug="dwarfdebug" LLVMPath="$(EmscriptenUpstreamBinPath)" > @@ -420,8 +443,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" /> <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> - - <_WasmNativeFileForLinking Include="@(_BitcodeFile)" /> + <_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 52d37fe900e..cb3876cfc9c 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -417,8 +417,17 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task try { // run the AOT compiler - Utils.RunProcess(CompilerBinaryPath, string.Join(" ", processArgs), envVariables, assemblyDir, silent: false, - outputMessageImportance: MessageImportance.Low, debugMessageImportance: MessageImportance.Low); + (int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath, + string.Join(" ", processArgs), + envVariables, + assemblyDir, + silent: false, + debugMessageImportance: MessageImportance.Low); + if (exitCode != 0) + { + Log.LogError($"Precompiling failed for {assembly}: {output}"); + return false; + } } catch (Exception ex) { diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 696ef14d85e..ea2aba607dd 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -21,6 +22,49 @@ internal static class Utils return reader.ReadToEnd(); } + public static (int exitCode, string output) RunShellCommand(string command, + IDictionary envVars, + string workingDir, + MessageImportance debugMessageImportance=MessageImportance.Low) + { + string scriptFileName = CreateTemporaryBatchFile(command); + (string shell, string args) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? ("cmd", $"/c \"{scriptFileName}\"") + : ("/bin/sh", $"\"{scriptFileName}\""); + + Logger?.LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:"); + Logger?.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName)); + + return TryRunProcess(shell, + args, + envVars, + workingDir, + silent: false, + debugMessageImportance); + + static string CreateTemporaryBatchFile(string command) + { + string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh"; + string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}"); + + using StreamWriter sw = new(file); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sw.WriteLine("setlocal"); + sw.WriteLine("set errorlevel=dummy"); + sw.WriteLine("set errorlevel="); + } + else + { + // Use sh rather than bash, as not all 'nix systems necessarily have Bash installed + sw.WriteLine("#!/bin/sh"); + } + + sw.WriteLine(command); + return file; + } + } + public static string RunProcess( string path, string args = "", @@ -28,10 +72,31 @@ internal static class Utils string? workingDir = null, bool ignoreErrors = false, bool silent = true, - MessageImportance outputMessageImportance=MessageImportance.High, MessageImportance debugMessageImportance=MessageImportance.High) { - LogInfo($"Running: {path} {args}", debugMessageImportance); + (int exitCode, string output) = TryRunProcess( + path, + args, + envVars, + workingDir, + silent, + debugMessageImportance); + + if (exitCode != 0 && !ignoreErrors) + throw new Exception("Error: Process returned non-zero exit code: " + output); + + return output; + } + + public static (int, string) TryRunProcess( + string path, + string args = "", + IDictionary? envVars = null, + string? workingDir = null, + bool silent = true, + MessageImportance debugMessageImportance=MessageImportance.High) + { + Logger?.LogMessage(debugMessageImportance, $"Running: {path} {args}"); var outputBuilder = new StringBuilder(); var processStartInfo = new ProcessStartInfo { @@ -46,7 +111,7 @@ internal static class Utils if (workingDir != null) processStartInfo.WorkingDirectory = workingDir; - LogInfo($"Using working directory: {workingDir ?? Environment.CurrentDirectory}", debugMessageImportance); + Logger?.LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}"); if (envVars != null) { @@ -68,10 +133,11 @@ internal static class Utils { lock (s_SyncObj) { + if (string.IsNullOrEmpty(e.Data)) + return; + if (!silent) - { LogWarning(e.Data); - } outputBuilder.AppendLine(e.Data); } }; @@ -79,10 +145,11 @@ internal static class Utils { lock (s_SyncObj) { + if (string.IsNullOrEmpty(e.Data)) + return; + if (!silent) - { - LogInfo(e.Data, outputMessageImportance); - } + Logger?.LogMessage(debugMessageImportance, e.Data); outputBuilder.AppendLine(e.Data); } }; @@ -90,14 +157,31 @@ internal static class Utils process.BeginErrorReadLine(); process.WaitForExit(); - if (process.ExitCode != 0) + Logger?.LogMessage(debugMessageImportance, $"Exit code: {process.ExitCode}"); + return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); + } + + internal static string CreateTemporaryBatchFile(string command) + { + string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh"; + string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}"); + + using StreamWriter sw = new(file); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Logger?.LogMessage(MessageImportance.High, $"Exit code: {process.ExitCode}"); - if (!ignoreErrors) - throw new Exception("Error: Process returned non-zero exit code: " + outputBuilder); + sw.WriteLine("setlocal"); + sw.WriteLine("set errorlevel=dummy"); + sw.WriteLine("set errorlevel="); + } + else + { + // Use sh rather than bash, as not all 'nix systems necessarily have Bash installed + sw.WriteLine("#!/bin/sh"); } - return silent ? string.Empty : outputBuilder.ToString().Trim('\r', '\n'); + sw.WriteLine(command); + + return file; } #if NETCOREAPP diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs new file mode 100644 index 00000000000..b175c62c2ee --- /dev/null +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable enable + +namespace Microsoft.WebAssembly.Build.Tasks +{ + /// + /// This is meant to *compile* source files only. It is *not* a general purpose + /// `emcc` invocation task. + /// + /// It runs `emcc` for each source file, and with output to `%(SourceFiles.ObjectFile)` + /// + /// + public class EmccCompile : Microsoft.Build.Utilities.Task + { + [NotNull] + [Required] + public ITaskItem[]? SourceFiles { get; set; } + + public ITaskItem[]? EnvironmentVariables { get; set; } + public bool DisableParallelCompile { get; set; } + public string Arguments { get; set; } = string.Empty; + public string? WorkingDirectory { get; set; } + + [Output] + public ITaskItem[]? OutputFiles { get; private set; } + + private string? _tempPath; + + public override bool Execute() + { + if (SourceFiles.Length == 0) + { + Log.LogError($"No SourceFiles to compile"); + return false; + } + + ITaskItem? badItem = SourceFiles.FirstOrDefault(sf => string.IsNullOrEmpty(sf.GetMetadata("ObjectFile"))); + if (badItem != null) + { + Log.LogError($"Source file {badItem.ItemSpec} is missing ObjectFile metadata."); + return false; + } + + IDictionary envVarsDict = GetEnvironmentVariablesDict(); + ConcurrentBag outputItems = new(); + try + { + Log.LogMessage(MessageImportance.Low, "Using environment variables:"); + foreach (var kvp in envVarsDict) + Log.LogMessage(MessageImportance.Low, $"\t{kvp.Key} = {kvp.Value}"); + + string workingDir = Environment.CurrentDirectory; + Log.LogMessage(MessageImportance.Low, $"Using working directory: {workingDir}"); + + _tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(_tempPath); + + int allowedParallelism = Math.Min(SourceFiles.Length, Environment.ProcessorCount); +#if false // Enable this when we bump msbuild to 16.1.0 + if (BuildEngine is IBuildEngine9 be9) + allowedParallelism = be9.RequestCores(allowedParallelism); +#endif + + if (DisableParallelCompile || allowedParallelism == 1) + { + foreach (ITaskItem srcItem in SourceFiles) + { + if (!ProcessSourceFile(srcItem)) + return false; + } + } + else + { + ParallelLoopResult result = Parallel.ForEach(SourceFiles, + new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism }, + (srcItem, state) => + { + if (!ProcessSourceFile(srcItem)) + state.Stop(); + }); + + if (!result.IsCompleted && !Log.HasLoggedErrors) + Log.LogError("Unknown failed occured while compiling"); + } + } + finally + { + if (!string.IsNullOrEmpty(_tempPath)) + Directory.Delete(_tempPath, true); + } + + OutputFiles = outputItems.ToArray(); + return !Log.HasLoggedErrors; + + bool ProcessSourceFile(ITaskItem srcItem) + { + string srcFile = srcItem.ItemSpec; + string objFile = srcItem.GetMetadata("ObjectFile"); + + try + { + string command = $"emcc {Arguments} -c -o {objFile} {srcFile}"; + (int exitCode, string output) = Utils.RunShellCommand(command, envVarsDict, workingDir: Environment.CurrentDirectory); + + if (exitCode != 0) + { + Log.LogError($"Failed to compile {srcFile} -> {objFile}: {output}"); + return false; + } + + ITaskItem newItem = new TaskItem(objFile); + newItem.SetMetadata("SourceFile", srcFile); + outputItems.Add(newItem); + + return true; + } + catch (Exception ex) + { + Log.LogError($"Failed to compile {srcFile} -> {objFile}: {ex.Message}"); + return false; + } + } + } + + private IDictionary GetEnvironmentVariablesDict() + { + Dictionary envVarsDict = new(); + if (EnvironmentVariables == null) + return envVarsDict; + + foreach (var item in EnvironmentVariables) + { + var parts = item.ItemSpec.Split(new char[] {'='}, 2, StringSplitOptions.None); + if (parts.Length == 0) + continue; + + string key = parts[0]; + string value = parts.Length > 1 ? parts[1] : string.Empty; + + envVarsDict[key] = value; + } + + return envVarsDict; + } + } +} diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 660272e268a..46c0174148b 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -3,16 +3,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Text; -using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; -using System.Reflection; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 23074621b89..b27176e2774 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -14,6 +14,8 @@ + + From 89603ecdd466f8a01f5ef59d9a4d2f2c13813e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Wed, 23 Jun 2021 23:24:06 +0200 Subject: [PATCH 070/926] Move setting fHasVirtualStaticMethods out of sanity check section (#54574) --- src/coreclr/vm/methodtablebuilder.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 24dfe4c1240..ad827323c69 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2906,6 +2906,13 @@ MethodTableBuilder::EnumerateClassMethods() IDS_CLASSLOAD_BADSPECIALMETHOD, tok); } + + // Check for the presence of virtual static methods + if (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs)) + { + bmtProp->fHasVirtualStaticMethods = TRUE; + } + // // But first - minimal flags validity checks // @@ -2972,11 +2979,7 @@ MethodTableBuilder::EnumerateClassMethods() } if(IsMdStatic(dwMemberAttrs)) { - if (fIsClassInterface) - { - bmtProp->fHasVirtualStaticMethods = TRUE; - } - else + if (!fIsClassInterface) { // Static virtual methods are only allowed to exist in interfaces BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD); From 707cf4fee594c19ed0dbf78349116f3e99bbbb55 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 24 Jun 2021 00:37:17 +0300 Subject: [PATCH 071/926] Move System.Object serialization to ObjectConverter (#54436) * move System.Object serialization to ObjectConverter * simply customized object converter test --- .../Converters/Value/ObjectConverter.cs | 5 ++- .../Json/Serialization/JsonConverterOfT.cs | 34 ++++++++++++------- .../Metadata/JsonPropertyInfoOfT.cs | 2 +- .../CustomConverterTests.Object.cs | 22 +++++++++++- .../Serialization/OptionsTests.cs | 33 ++++++------------ 5 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs index 597dce6b7ef..65b47d65074 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs @@ -24,7 +24,10 @@ namespace System.Text.Json.Serialization.Converters public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) { - throw new InvalidOperationException(); + Debug.Assert(value?.GetType() == typeof(object)); + + writer.WriteStartObject(); + writer.WriteEndObject(); } internal override object ReadWithQuotes(ref Utf8JsonReader reader) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 543fbe2f522..33f45fd3ced 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -18,12 +18,11 @@ namespace System.Text.Json.Serialization /// protected internal JsonConverter() { - // Today only typeof(object) can have polymorphic writes. - // In the future, this will be check for !IsSealed (and excluding value types). - CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType; + IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; + // Today only the internal JsonConverter can have polymorphic writes. + CanBePolymorphic = IsInternalConverter && TypeToConvert == JsonTypeInfo.ObjectType; IsValueType = TypeToConvert.IsValueType; CanBeNull = default(T) is null; - IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; if (HandleNull) { @@ -332,7 +331,7 @@ namespace System.Text.Json.Serialization bool ignoreCyclesPopReference = false; if ( -#if NET6_0_OR_GREATER +#if NET5_0_OR_GREATER !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. #else !IsValueType && @@ -368,15 +367,11 @@ namespace System.Text.Json.Serialization if (CanBePolymorphic) { - Type type = value.GetType(); - if (type == JsonTypeInfo.ObjectType) - { - writer.WriteStartObject(); - writer.WriteEndObject(); - return true; - } + Debug.Assert(IsInternalConverter); - if (type != TypeToConvert && IsInternalConverter) + Type type = value.GetType(); + + if (type != TypeToConvert) { // For internal converter only: Handle polymorphic case and get the new converter. // Custom converter, even though polymorphic converter, get called for reading AND writing. @@ -420,6 +415,19 @@ namespace System.Text.Json.Serialization } VerifyWrite(originalPropertyDepth, writer); + + if ( +#if NET5_0_OR_GREATER + !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. +#endif + ignoreCyclesPopReference) + { + // should only be entered if we're serializing instances + // of type object using the internal object converter. + Debug.Assert(value?.GetType() == typeof(object) && IsInternalConverter); + state.ReferenceResolver.PopReferenceForCycleDetection(); + } + return true; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index 0f2ff8574f0..8e268c25099 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -235,7 +235,7 @@ namespace System.Text.Json.Serialization.Metadata T value = Get!(obj); if ( -#if NET6_0_OR_GREATER +#if NET5_0_OR_GREATER !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT. #else !Converter.IsValueType && diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs index 0ab7ad020de..36309616eaf 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs @@ -307,7 +307,9 @@ namespace System.Text.Json.Serialization.Tests public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - throw new InvalidOperationException("Should not get here."); + Assert.IsType(value); + writer.WriteStartObject(); + writer.WriteEndObject(); } } @@ -732,5 +734,23 @@ namespace System.Text.Json.Serialization.Tests options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter()); Verify(options); } + + [Fact] + public static void CanCustomizeSystemObjectSerialization() + { + var options = new JsonSerializerOptions { Converters = { new CustomSystemObjectConverter() } }; + + string expectedJson = "42"; + string actualJson = JsonSerializer.Serialize(new object(), options); + Assert.Equal(expectedJson, actualJson); + } + + private class CustomSystemObjectConverter : JsonConverter + { + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + => writer.WriteNumberValue(42); + } } + } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index 06747c87786..f7d80c2a01e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -328,12 +328,12 @@ namespace System.Text.Json.Serialization.Tests [Fact] public static void Options_GetConverterForObjectJsonElement_GivesCorrectConverter() { - GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "[3]", true); + GenericObjectOrJsonElementConverterTestHelper("ObjectConverter", new object(), "{}"); JsonElement element = JsonDocument.Parse("[3]").RootElement; - GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]", false); + GenericObjectOrJsonElementConverterTestHelper("JsonElementConverter", element, "[3]"); } - private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue, bool throws) + private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue) { var options = new JsonSerializerOptions(); @@ -347,10 +347,7 @@ namespace System.Text.Json.Serialization.Tests if (readValue is JsonElement element) { - Assert.Equal(JsonValueKind.Array, element.ValueKind); - JsonElement.ArrayEnumerator iterator = element.EnumerateArray(); - Assert.True(iterator.MoveNext()); - Assert.Equal(3, iterator.Current.GetInt32()); + JsonTestHelper.AssertJsonEqual(stringValue, element.ToString()); } else { @@ -360,22 +357,14 @@ namespace System.Text.Json.Serialization.Tests using (var stream = new MemoryStream()) using (var writer = new Utf8JsonWriter(stream)) { - if (throws) - { - Assert.Throws(() => converter.Write(writer, (T)objectValue, options)); - Assert.Throws(() => converter.Write(writer, (T)objectValue, null)); - } - else - { - converter.Write(writer, (T)objectValue, options); - writer.Flush(); - Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray())); + converter.Write(writer, (T)objectValue, options); + writer.Flush(); + Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray())); - writer.Reset(stream); - converter.Write(writer, (T)objectValue, null); // Test with null option - writer.Flush(); - Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray())); - } + writer.Reset(stream); + converter.Write(writer, (T)objectValue, null); // Test with null option + writer.Flush(); + Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray())); } } From d7a5b899807329db2cf2d4afeef0aaa8edca055c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Wed, 23 Jun 2021 23:39:28 +0200 Subject: [PATCH 072/926] Put Crossgen2 in sync with #54235 (#54438) --- .../Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs | 5 ++--- .../tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs | 4 ++-- .../MarshalUtilsTests.cs | 4 ++-- src/tests/issues.targets | 3 --- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 2bf40935a38..4109bb701aa 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -782,10 +782,9 @@ namespace Internal.TypeSystem if (!type.IsValueType && type.HasBaseType) { cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; - if (!type.BaseType.InstanceByteCountUnaligned.IsIndeterminate) + if (!cumulativeInstanceFieldPos.IsIndeterminate) { - cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; - if (type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout()) + if (requiresAlignedBase && type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout()) { cumulativeInstanceFieldPos += LayoutInt.One; } diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs index 84dcc80be13..33c609ee23b 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs @@ -23,8 +23,8 @@ namespace Internal.TypeSystem.Interop && !baseType.IsWellKnownType(WellKnownType.Object) && !baseType.IsWellKnownType(WellKnownType.ValueType); - // Type is blittable only if parent is also blittable and is not empty. - if (hasNonTrivialParent && (!IsBlittableType(baseType) || baseType.IsZeroSizedReferenceType)) + // Type is blittable only if parent is also blittable. + if (hasNonTrivialParent && !IsBlittableType(baseType)) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs index c3befa87af7..3523f5d62f8 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs @@ -74,10 +74,10 @@ namespace TypeSystemTests [InlineData("ClassWithExplicitEmptyBase")] [InlineData("ClassWithExplicitEmptySizeZeroBase")] [InlineData("ClassWithSequentialEmptyBase")] - public void IsBlittableType_TypeWithEmptyBase_ReturnsFalse(string className) + public void IsBlittableType_TypeWithEmptyBase_ReturnsTrue(string className) { TypeDesc classWithEmptyBase = _testModule.GetType("Marshalling", className); - Assert.False(MarshalUtils.IsBlittableType(classWithEmptyBase)); + Assert.True(MarshalUtils.IsBlittableType(classWithEmptyBase)); } } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index e34d49e1455..06867ff7ee8 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -123,9 +123,6 @@ https://github.com/dotnet/runtime/issues/48727 - - https://github.com/dotnet/runtime/issues/54316 - From 4f4f0dbd5e674f4d19d8d51fd6717b9316224852 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Jun 2021 20:06:58 -0300 Subject: [PATCH 073/926] [wasm][debugger] Reuse debugger-agent on wasm debugger (#52300) * Trying to reuse debugger-agent on wasm debugger. This will remove a lot of code that does the same thing on mini-wasm-debugger. * Replace remove_breakpoint and clear_all_breakpoints with the ones on debugger-agent and remove unused code. * Stepping and callstack using debugger-agent. * Remove more code. * Get frame values using debugger-agent. * Working valuetypes and call function on valuetypes. make -C src/mono/wasm/ run-debugger-tests TEST_FILTER=DebuggerTests.SteppingTests.InspectValueTypeMethodArgsWhileStepping is working without use_cfo. * Failed: 316, Passed: 175 * Failed: 307, Passed: 184, Skipped: 0, Total: 491 * Failed: 280, Passed: 211 * Failed: 277, Passed: 214 * Implemented boxed value. Failed: 271, Passed: 220 * Implementing get properties on objects. Implementing handling error on debugger-agent. * Implementing callfunctionon object. Failed: 248, Passed: 243 * Implementing get pointer values. Failed: 243, Passed: 248 * Fixing pointer values and implement call on function with pointers. Failed: 226, Passed: 265 * Reimplement call function on, and implement set values. Failed: 192, Passed: 299 * Failed: 192, Passed: 299 * Fixing valuetype with null values. Failed: 184, Passed: 307 * Implemented Evaluate expressions, conditional breakpoints, all breakpoints tests are passing. Failed: 172, Passed: 319 * Fixing evaluate with value type. Failed: 156, Passed: 335 * Trim part and add cache. Failed: 148, Passed: 343 * Fixing evaluate expression. Failed: 99, Passed: 392 * GetPropertiesTests working. Failed: 53, Passed: 438 * Passing delegate tests. Failed: 31, Passed: 460 * Removing unused code. * Implementing exception handler. Failed: 30, Passed: 461 * Fixing cfo returning array. Removing more code. Removing 2 tests that tests functions which does not exist anymore. Failed: 18, Passed: 471 * Fix CallFunctionOn returning primitive types and null. Failed: 9, Passed: 480 * Failed: 7, Passed: 482 * Fixing some tests. Failed: 2, Passed: 488 * Removing a lot of code. Failed: 4, Passed: 485 * 0 ERRORS! * Removing more code. No errors. * Fixing added tests. * Return javascript callstack after managed callstack. Step out from managed code return to native wasm or javascript. Adding debug info to Wasm.Browser.Sample to help testing debugger with sample. * Change what Ankit suggested. Clear cache with valuetypes and pointers after resume or step. * Fixing suggestions. * Fix error on wasm build. * Apply suggestions from code review Co-authored-by: Larry Ewing * Changing what was suggested by @lewing. * Fix pointer tests. * Refactoring CreateJObjectForVariableValue * Changing what @lewing suggested. * Apply suggestions from code review Co-authored-by: Larry Ewing * Update src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs Co-authored-by: Larry Ewing * Update src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs Co-authored-by: Larry Ewing * Fixing @lewing changes. * Trying to fix CI. Co-authored-by: Larry Ewing --- src/mono/mono/mini/debugger-agent.c | 479 +++-- src/mono/mono/mini/debugger-agent.h | 66 + src/mono/mono/mini/debugger-engine.c | 7 - src/mono/mono/mini/debugger-engine.h | 13 +- src/mono/mono/mini/debugger-protocol.c | 1 - src/mono/mono/mini/debugger-protocol.h | 14 +- src/mono/mono/mini/mini-wasm-debugger.c | 1720 +--------------- .../wasm/browser/Wasm.Browser.Sample.csproj | 4 + .../debugger/BrowserDebugProxy/DebugStore.cs | 3 +- .../BrowserDebugProxy/DevToolsHelper.cs | 42 +- .../MemberReferenceResolver.cs | 115 +- .../debugger/BrowserDebugProxy/MonoProxy.cs | 553 ++--- .../BrowserDebugProxy/MonoSDBHelper.cs | 1779 +++++++++++++++++ .../DebuggerTestSuite/AssignmentTests.cs | 2 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 1 - .../debugger/DebuggerTestSuite/MonoJsTests.cs | 79 - .../wasm/debugger/DebuggerTestSuite/Tests.cs | 39 +- src/mono/wasm/runtime/library_mono.js | 1199 +---------- 18 files changed, 2754 insertions(+), 3362 deletions(-) create mode 100644 src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs diff --git a/src/mono/mono/mini/debugger-agent.c b/src/mono/mono/mini/debugger-agent.c index eb0e2a248dc..b1efc0d246c 100644 --- a/src/mono/mono/mini/debugger-agent.c +++ b/src/mono/mono/mini/debugger-agent.c @@ -109,8 +109,7 @@ #define DISABLE_SOCKET_TRANSPORT #endif -#ifndef DISABLE_SDB - +#if !defined (DISABLE_SDB) || defined(TARGET_WASM) #include #include @@ -145,28 +144,6 @@ typedef struct { gboolean using_icordbg; } AgentConfig; -typedef struct _InvokeData InvokeData; - -struct _InvokeData -{ - int id; - int flags; - guint8 *p; - guint8 *endp; - /* This is the context which needs to be restored after the invoke */ - MonoContext ctx; - gboolean has_ctx; - /* - * If this is set, invoke this method with the arguments given by ARGS. - */ - MonoMethod *method; - gpointer *args; - guint32 suspend_count; - int nmethods; - - InvokeData *last_invoke; -}; - struct _DebuggerTlsData { MonoThreadUnwindState context; @@ -262,15 +239,6 @@ struct _DebuggerTlsData { gboolean gc_finalizing; }; -typedef struct { - const char *name; - void (*connect) (const char *address); - void (*close1) (void); - void (*close2) (void); - gboolean (*send) (void *buf, int len); - int (*recv) (void *buf, int len); -} DebuggerTransport; - /* Buffered reply packets */ static ReplyPacket reply_packets [128]; static int nreply_packets; @@ -314,7 +282,9 @@ typedef struct { /* * Globals */ - +#ifdef TARGET_WASM +static DebuggerTlsData debugger_wasm_thread; +#endif static AgentConfig agent_config; /* @@ -397,6 +367,28 @@ static gint32 suspend_count; /* Whenever to buffer reply messages and send them together */ static gboolean buffer_replies; + +#ifndef TARGET_WASM +#define GET_TLS_DATA_FROM_THREAD(thread) \ + DebuggerTlsData *tls = NULL; \ + mono_loader_lock(); \ + if (thread_to_tls != NULL) \ + tls = (DebuggerTlsData*)mono_g_hash_table_lookup(thread_to_tls, thread); \ + mono_loader_unlock(); +#define GET_DEBUGGER_TLS() \ + DebuggerTlsData *tls; \ + tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); +#else +#define GET_TLS_DATA_FROM_THREAD(thread) \ + DebuggerTlsData *tls; \ + tls = &debugger_wasm_thread; +#define GET_DEBUGGER_TLS() \ + DebuggerTlsData *tls; \ + tls = &debugger_wasm_thread; +#endif + +//mono_native_tls_get_value (debugger_tls_id); + #define dbg_lock mono_de_lock #define dbg_unlock mono_de_unlock @@ -460,6 +452,7 @@ static void objrefs_init (void); static void objrefs_cleanup (void); static void ids_init (void); + static void ids_cleanup (void); static void suspend_init (void); @@ -478,19 +471,13 @@ static MonoContext* tls_get_restore_state (void *the_tls); static gboolean try_process_suspend (void *tls, MonoContext *ctx, gboolean from_breakpoint); static gboolean begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal); static void begin_single_step_processing (MonoContext *ctx, gboolean from_signal); -static void ss_discard_frame_context (void *the_tls); -static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); static gboolean ensure_jit (DbgEngineStackFrame* the_frame); static int ensure_runtime_is_suspended (void); -static int get_this_async_id (DbgEngineStackFrame *frame); -static void* create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind); -static void process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); -static int ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); -static void ss_args_destroy (SingleStepArgs *ss_args); static int handle_multiple_ss_requests (void); static GENERATE_TRY_GET_CLASS_WITH_CACHE (fixed_buffer, "System.Runtime.CompilerServices", "FixedBufferAttribute") + #ifndef DISABLE_SOCKET_TRANSPORT static void register_socket_transport (void); @@ -710,17 +697,17 @@ debugger_agent_init (void) cbs.try_process_suspend = try_process_suspend; cbs.begin_breakpoint_processing = begin_breakpoint_processing; cbs.begin_single_step_processing = begin_single_step_processing; - cbs.ss_discard_frame_context = ss_discard_frame_context; - cbs.ss_calculate_framecount = ss_calculate_framecount; + cbs.ss_discard_frame_context = mono_ss_discard_frame_context; + cbs.ss_calculate_framecount = mono_ss_calculate_framecount; cbs.ensure_jit = ensure_jit; cbs.ensure_runtime_is_suspended = ensure_runtime_is_suspended; - cbs.get_this_async_id = get_this_async_id; + cbs.get_this_async_id = mono_get_this_async_id; cbs.set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag; cbs.get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method; - cbs.create_breakpoint_events = create_breakpoint_events; - cbs.process_breakpoint_events = process_breakpoint_events; - cbs.ss_create_init_args = ss_create_init_args; - cbs.ss_args_destroy = ss_args_destroy; + cbs.create_breakpoint_events = mono_dbg_create_breakpoint_events; + cbs.process_breakpoint_events = mono_dbg_process_breakpoint_events; + cbs.ss_create_init_args = mono_ss_create_init_args; + cbs.ss_args_destroy = mono_ss_args_destroy; cbs.handle_multiple_ss_requests = handle_multiple_ss_requests; mono_de_init (&cbs); @@ -1287,9 +1274,6 @@ static DebuggerTransport *transport; static DebuggerTransport transports [MAX_TRANSPORTS]; static int ntransports; -MONO_API void -mono_debugger_agent_register_transport (DebuggerTransport *trans); - void mono_debugger_agent_register_transport (DebuggerTransport *trans) { @@ -1590,6 +1574,24 @@ static GHashTable *obj_to_objref; /* Protected by the dbg lock */ static MonoGHashTable *suspended_objs; +#ifdef TARGET_WASM +void mono_init_debugger_agent_for_wasm (int log_level_parm) +{ + if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1) + return; + + ids_init(); + objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); + obj_to_objref = g_hash_table_new (NULL, NULL); + + log_level = log_level; + event_requests = g_ptr_array_new (); + vm_start_event_sent = TRUE; + transport = &transports [0]; + memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData)); + agent_config.enabled = TRUE; +} +#endif static void @@ -1986,7 +1988,6 @@ static int buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val) { int id = get_id (domain, type, val); - buffer_add_id (buf, id); return id; } @@ -2176,6 +2177,21 @@ save_thread_context (MonoContext *ctx) mono_thread_state_init_from_current (&tls->context); } +#ifdef TARGET_WASM +void +mono_wasm_save_thread_context (void) +{ + debugger_wasm_thread.really_suspended = TRUE; + mono_thread_state_init_from_current (&debugger_wasm_thread.context); +} + +DebuggerTlsData* +mono_wasm_get_tls (void) +{ + return &debugger_wasm_thread; +} +#endif + static MonoCoopMutex suspend_mutex; /* Cond variable used to wait for suspend_count becoming 0 */ @@ -2700,7 +2716,8 @@ static int count_threads_to_wait_for (void) { int count = 0; - + if (thread_to_tls == NULL) + return 0; mono_loader_lock (); mono_g_hash_table_foreach (thread_to_tls, count_thread, &count); mono_loader_unlock (); @@ -3049,7 +3066,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f tls->frames = new_frames; tls->frame_count = new_frame_count; tls->frames_up_to_date = TRUE; - +#ifndef TARGET_WASM if (CHECK_PROTOCOL_VERSION (2, 52)) { MonoJitTlsData *jit_data = thread->thread_info->jit_data; gboolean has_interp_resume_state = FALSE; @@ -3064,6 +3081,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f } } } +#endif } /* @@ -3494,7 +3512,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx return; } } - + if (event == EVENT_KIND_VM_START) suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE; @@ -3552,12 +3570,10 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx break; case EVENT_KIND_BREAKPOINT: case EVENT_KIND_STEP: { - DebuggerTlsData *tls; - tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + GET_DEBUGGER_TLS(); g_assert (tls); mono_stopwatch_stop (&tls->step_time); MonoMethod *method = (MonoMethod *)arg; - buffer_add_methodid (&buf, domain, method); buffer_add_long (&buf, il_offset); break; @@ -3578,6 +3594,9 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_EXCEPTION: { EventInfo *ei = (EventInfo *)arg; buffer_add_objid (&buf, ei->exc); +#ifdef TARGET_WASM + buffer_add_byte (&buf, ei->caught); +#endif /* * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it * later after the suspension. (#12494). @@ -3586,8 +3605,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx break; } case EVENT_KIND_USER_BREAK: { - DebuggerTlsData *tls; - tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); + GET_DEBUGGER_TLS(); g_assert (tls); // We are already processing a breakpoint event if (tls->disable_breakpoints) @@ -4042,14 +4060,18 @@ event_requests_cleanup (void) * * Ensure DebuggerTlsData fields are filled out. */ -static void -ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes) +void +mono_ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes) { DebuggerTlsData *tls = (DebuggerTlsData*)the_tls; - +#ifndef TARGET_WASM if (force_use_ctx || !tls->context.valid) mono_thread_state_init_from_monoctx (&tls->context, ctx); compute_frame_info (tls->thread, tls, FALSE); +#else + compute_frame_info (tls->thread, tls, TRUE); +#endif + if (frames) *frames = (DbgEngineStackFrame**)tls->frames; if (nframes) @@ -4061,8 +4083,8 @@ ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx * * Discard frame data and invalidate any context */ -static void -ss_discard_frame_context (void *the_tls) +void +mono_ss_discard_frame_context (void *the_tls) { DebuggerTlsData *tls = (DebuggerTlsData*)the_tls; tls->context.valid = FALSE; @@ -4107,8 +4129,8 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) //This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not //since thread probably changed... -static int -get_this_async_id (DbgEngineStackFrame *frame) +int +mono_get_this_async_id (DbgEngineStackFrame *frame) { MonoClassField *builder_field; gpointer builder; @@ -4161,8 +4183,11 @@ begin_breakpoint_processing (void *the_tls, MonoContext *ctx, MonoJitInfo *ji, g * Skip the instruction causing the breakpoint signal. */ if (from_signal) +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_breakpoint (ctx, ji); - +#else + NOT_IMPLEMENTED; +#endif if (tls->disable_breakpoints) return FALSE; return TRUE; @@ -4174,8 +4199,8 @@ typedef struct { int suspend_policy; } BreakPointEvents; -static void* -create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) +void* +mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) { int suspend_policy = 0; BreakPointEvents *evts = g_new0 (BreakPointEvents, 1); @@ -4191,8 +4216,8 @@ create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *j return evts; } -static void -process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset) +void +mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset) { BreakPointEvents *evts = (BreakPointEvents*)_evts; /* @@ -4301,8 +4326,8 @@ user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data) /* * Called by System.Diagnostics.Debugger:Break (). */ -static void -debugger_agent_user_break (void) +void +mono_dbg_debugger_agent_user_break (void) { if (agent_config.enabled) { MonoContext ctx; @@ -4332,7 +4357,11 @@ static void begin_single_step_processing (MonoContext *ctx, gboolean from_signal) { if (from_signal) +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_single_step (ctx); +#else + NOT_IMPLEMENTED; +#endif } static void @@ -4364,7 +4393,11 @@ debugger_agent_single_step_event (void *sigctx) MonoContext ctx; mono_sigctx_to_monoctx (sigctx, &ctx); +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_skip_single_step (&ctx); +#else + NOT_IMPLEMENTED; +#endif mono_monoctx_to_sigctx (&ctx, sigctx); return; } @@ -4444,8 +4477,8 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx) if (MONO_CONTEXT_GET_IP (ctx) == orig_ip - 1) MONO_CONTEXT_SET_IP (ctx, orig_ip); } -static void -ss_args_destroy (SingleStepArgs *ss_args) +void +mono_ss_args_destroy (SingleStepArgs *ss_args) { if (ss_args->frames) free_frames ((StackFrame**)ss_args->frames, ss_args->nframes); @@ -4470,8 +4503,8 @@ ensure_runtime_is_suspended (void) return ERR_NONE; } -static int -ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) +int +mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) { MonoSeqPointInfo *info = NULL; gboolean found_sp; @@ -4481,10 +4514,9 @@ ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args) gboolean set_ip = FALSE; StackFrame **frames = NULL; int nframes = 0; - - mono_loader_lock (); - DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, ss_req->thread); - mono_loader_unlock (); + + GET_TLS_DATA_FROM_THREAD (ss_req->thread); + g_assert (tls); if (!tls->context.valid) { PRINT_DEBUG_MSG (1, "Received a single step request on a thread with no managed frames.\n"); @@ -4727,8 +4759,8 @@ debugger_agent_unhandled_exception (MonoException *exc) process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy); } -static void -debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, +void +mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) { if (catch_ctx == NULL && catch_frame == NULL && mini_debug_options.suspend_on_unhandled && mono_object_class (exc) != mono_defaults.threadabortexception_class) { @@ -4736,23 +4768,15 @@ debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, while (1) ; } - int i, j, suspend_policy; GSList *events; MonoJitInfo *ji, *catch_ji; EventInfo ei; - DebuggerTlsData *tls = NULL; - - if (thread_to_tls != NULL) { - MonoInternalThread *thread = mono_thread_internal_current (); - - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); - - if (tls && tls->abort_requested) + GET_TLS_DATA_FROM_THREAD (mono_thread_internal_current ()); + if (tls != NULL) { + if (tls->abort_requested) return; - if (tls && tls->disable_breakpoints) + if (tls->disable_breakpoints) return; } @@ -4965,7 +4989,10 @@ buffer_add_info_for_null_value (Buffer* buf, MonoType* t, MonoDomain* domain) buffer_add_int (buf, m_class_get_rank (mono_class_from_mono_type_internal (t))); if (m_class_get_byval_arg (m_class_get_element_class (mono_class_from_mono_type_internal (t)))->type == MONO_TYPE_CLASS) buffer_add_typeid (buf, domain, m_class_get_element_class (mono_class_from_mono_type_internal (t))); + buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t)); break; + default: + buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal (t)); } } /* @@ -5149,6 +5176,9 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, buffer_add_byte (buf, MONO_TYPE_VALUETYPE); buffer_add_byte (buf, m_class_is_enumtype (klass)); + + if (CHECK_PROTOCOL_VERSION(2, 61)) + buffer_add_byte(buf, boxed_vtype); buffer_add_typeid (buf, domain, klass); nfields = 0; @@ -5222,9 +5252,8 @@ decode_vtype (MonoType *t, MonoDomain *domain, gpointer void_addr, gpointer void ErrorCode err; is_enum = decode_byte (buf, &buf, limit); - /* Enums are sent as a normal vtype */ - if (is_enum) - return ERR_NOT_IMPLEMENTED; + if (CHECK_PROTOCOL_VERSION(2, 61)) + decode_byte (buf, &buf, limit); klass = decode_typeid (buf, &buf, limit, &d, &err); if (err != ERR_NONE) return err; @@ -5413,7 +5442,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, handle_ref: default: if (MONO_TYPE_IS_REFERENCE (t)) { - if (type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) { + if (type == MONO_TYPE_CLASS || type == MONO_TYPE_OBJECT || type == MONO_TYPE_STRING) { int objid = decode_objid (buf, &buf, limit); ErrorCode err; MonoObject *obj; @@ -5435,7 +5464,12 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, mono_gc_wbarrier_generic_store_internal (addr, obj); } else if (type == VALUE_TYPE_ID_NULL) { + if (CHECK_PROTOCOL_VERSION (2, 59)) { + decode_byte (buf, &buf, limit); + decode_int (buf, &buf, limit); //not used + } *(MonoObject**)addr = NULL; + } else if (type == MONO_TYPE_VALUETYPE) { ERROR_DECL (error); guint8 *buf2; @@ -5452,8 +5486,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, */ buf2 = buf; is_enum = decode_byte (buf, &buf, limit); - if (is_enum) - return ERR_NOT_IMPLEMENTED; + decode_byte (buf, &buf, limit); //ignore is boxed klass = decode_typeid (buf, &buf, limit, &d, &err); if (err != ERR_NONE) return err; @@ -5913,8 +5946,8 @@ add_thread (gpointer key, gpointer value, gpointer user_data) } -static ErrorCode -do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp) +ErrorCode +mono_do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp) { ERROR_DECL (error); guint8 *end = invoke->endp; @@ -5981,7 +6014,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 return err; } } else { - if (!(m->flags & METHOD_ATTRIBUTE_STATIC && CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later + if (!(m->flags & METHOD_ATTRIBUTE_STATIC) || (m->flags & METHOD_ATTRIBUTE_STATIC && !CHECK_PROTOCOL_VERSION (2, 59))) { //on icordbg I couldn't find an object when invoking a static method maybe I can change this later err = decode_value(m_class_get_byval_arg(m->klass), domain, this_buf, p, &p, end, FALSE); if (err != ERR_NONE) return err; @@ -6236,7 +6269,7 @@ invoke_method (void) if (err) { /* Fail the other invokes as well */ } else { - err = do_invoke_method (tls, &buf, invoke, p, &p); + err = mono_do_invoke_method (tls, &buf, invoke, p, &p); } if (tls->abort_requested) { @@ -6929,6 +6962,39 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) buffer_add_byte_array (buf, memory, size); break; } + case MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME: { + int i; + char* assembly_name = decode_string (p, &p, end); + //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot + char *lookup_name = g_strdup (assembly_name); + for (i = strlen (lookup_name) - 1; i >= 0; --i) { + if (lookup_name [i] == '.') { + lookup_name [i] = 0; + break; + } + } + + //resolve the assembly + MonoImageOpenStatus status; + MonoAssemblyName* aname = mono_assembly_name_new (lookup_name); + if (!aname) { + PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); + buffer_add_int(buf, -1); + break; + } + MonoAssemblyByNameRequest byname_req; + mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ()); + MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status); + g_free (lookup_name); + mono_assembly_name_free_internal (aname); + if (!assembly) { + PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); + buffer_add_int(buf, -1); + break; + } + buffer_add_assemblyid (buf, mono_get_root_domain (), assembly); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -7091,10 +7157,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (req); return err; } + + GET_TLS_DATA_FROM_THREAD (THREAD_TO_INTERNAL(step_thread)); - mono_loader_lock (); - DebuggerTlsData *tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL(step_thread)); - mono_loader_unlock (); g_assert (tls); if (tls->terminated) { @@ -7108,6 +7173,22 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (req); return err; } +#ifdef TARGET_WASM + int isBPOnManagedCode = 0; + SingleStepReq *ss_req = req->info; + if (ss_req && ss_req->bps) { + GSList *l; + + for (l = ss_req->bps; l; l = l->next) { + if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) + isBPOnManagedCode = 1; + } + } + if (!isBPOnManagedCode) { + mono_de_cancel_all_ss (); + } + buffer_add_byte (buf, isBPOnManagedCode); +#endif } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) { req->info = mono_de_set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req, NULL); } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) { @@ -7738,7 +7819,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_string (buf, m_class_get_name_space (klass)); buffer_add_string (buf, m_class_get_name (klass)); // FIXME: byref - name = mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); + + MonoTypeNameFormat format = MONO_TYPE_NAME_FORMAT_FULL_NAME; + if (CHECK_PROTOCOL_VERSION(2, 61)) + format = (MonoTypeNameFormat) decode_int (p, &p, end); + name = mono_type_get_name_full (m_class_get_byval_arg (klass), format); buffer_add_string (buf, name); g_free (name); buffer_add_assemblyid (buf, domain, m_class_get_image (klass)->assembly); @@ -8182,6 +8267,23 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_int (buf, value_size); break; } + case MDBGPROT_CMD_TYPE_GET_PARENTS: { + MonoClass *parent_klass = m_class_get_parent (klass); + int count = 0; + while (parent_klass != NULL) + { + count++; + parent_klass = m_class_get_parent (parent_klass); + } + buffer_add_int (buf, count); + parent_klass = m_class_get_parent (klass); + while (parent_klass != NULL) + { + buffer_add_typeid (buf, domain, parent_klass); + parent_klass = m_class_get_parent (parent_klass); + } + break; + } default: err = ERR_NOT_IMPLEMENTED; goto exit; @@ -8237,7 +8339,11 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g switch (command) { case CMD_METHOD_GET_NAME: { buffer_add_string (buf, method->name); - break; + break; + } + case MDBGPROT_CMD_METHOD_GET_NAME_FULL: { + buffer_add_string (buf, mono_method_full_name (method, FALSE)); + break; } case MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN: { buffer_add_int (buf, m_class_get_type_token (method->klass)); @@ -8676,6 +8782,16 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g buffer_add_assemblyid(buf, mono_domain_get (), m_class_get_image(method->klass)->assembly); break; } + case MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO: { + MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method); + if (async_method) { + buffer_add_byte(buf, TRUE); + mono_debug_free_method_async_debug_info (async_method); + } + else + buffer_add_byte(buf, FALSE); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -8761,7 +8877,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) break; } case MDBGPROT_CMD_THREAD_GET_CONTEXT: { - DebuggerTlsData *tls; int start_frame; while (!is_suspended ()) { if (suspend_count) @@ -8769,9 +8884,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } start_frame = decode_int (p, &p, end); - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); if (tls == NULL) return ERR_UNLOADED; @@ -8784,7 +8897,6 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) break; } case CMD_THREAD_GET_FRAME_INFO: { - DebuggerTlsData *tls; int i, start_frame, length; // Wait for suspending if it already started @@ -8805,10 +8917,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (start_frame != 0 || length != -1) return ERR_NOT_IMPLEMENTED; - - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); if (tls == NULL) return ERR_UNLOADED; @@ -8980,7 +9089,6 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoThread *thread_obj; MonoInternalThread *thread; int pos, i, len, frame_idx; - DebuggerTlsData *tls; StackFrame *frame; MonoDebugMethodJitInfo *jit; MonoMethodSignature *sig; @@ -8997,9 +9105,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) id = decode_id (p, &p, end); - mono_loader_lock (); - tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); - mono_loader_unlock (); + GET_TLS_DATA_FROM_THREAD (thread); g_assert (tls); for (i = 0; i < tls->frame_count; ++i) { @@ -9010,7 +9116,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_INVALID_FRAMEID; /* The thread is still running native code, can't get frame variables info */ - if (!tls->really_suspended && !tls->async_state.valid) + if (!tls->really_suspended && !tls->async_state.valid) return ERR_NOT_SUSPENDED; frame_idx = i; frame = tls->frames [frame_idx]; @@ -9574,6 +9680,17 @@ get_field_value: buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type_internal (((MonoReflectionType*)obj->vtable->type)->type)); buffer_add_domainid (buf, obj->vtable->domain); break; + case MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD: + buffer_add_methodid (buf, obj->vtable->domain, ((MonoDelegate *)obj)->method); + break; + case MDBGPROT_CMD_OBJECT_IS_DELEGATE: { + MonoType *type = m_class_get_byval_arg (obj_type); + if (m_class_is_delegate (obj_type) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) + buffer_add_byte (buf, TRUE); + else + buffer_add_byte (buf, FALSE); + break; + } default: err = ERR_NOT_IMPLEMENTED; goto exit; @@ -9890,6 +10007,62 @@ wait_for_attach (void) return TRUE; } +ErrorCode +mono_process_dbg_packet (int id, CommandSet command_set, int command, gboolean *no_reply, guint8 *buf, guint8 *end, Buffer *ret_buf) +{ + ErrorCode err; + /* Process the request */ + switch (command_set) { + case CMD_SET_VM: + err = vm_commands (command, id, buf, end, ret_buf); + if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD) + /* Sent after the invoke is complete */ + *no_reply = TRUE; + break; + case CMD_SET_EVENT_REQUEST: + err = event_commands (command, buf, end, ret_buf); + break; + case CMD_SET_APPDOMAIN: + err = domain_commands (command, buf, end, ret_buf); + break; + case CMD_SET_ASSEMBLY: + err = assembly_commands (command, buf, end, ret_buf); + break; + case CMD_SET_MODULE: + err = module_commands (command, buf, end, ret_buf); + break; + case CMD_SET_FIELD: + err = field_commands (command, buf, end, ret_buf); + break; + case CMD_SET_TYPE: + err = type_commands (command, buf, end, ret_buf); + break; + case CMD_SET_METHOD: + err = method_commands (command, buf, end, ret_buf); + break; + case CMD_SET_THREAD: + err = thread_commands (command, buf, end, ret_buf); + break; + case CMD_SET_STACK_FRAME: + err = frame_commands (command, buf, end, ret_buf); + break; + case CMD_SET_ARRAY_REF: + err = array_commands (command, buf, end, ret_buf); + break; + case CMD_SET_STRING_REF: + err = string_commands (command, buf, end, ret_buf); + break; + case CMD_SET_POINTER: + err = pointer_commands (command, buf, end, ret_buf); + break; + case CMD_SET_OBJECT_REF: + err = object_commands (command, buf, end, ret_buf); + break; + default: + err = ERR_NOT_IMPLEMENTED; + } + return err; +} /* * debugger_thread: * @@ -9985,57 +10158,7 @@ debugger_thread (void *arg) err = ERR_NONE; no_reply = FALSE; - - /* Process the request */ - switch (command_set) { - case CMD_SET_VM: - err = vm_commands (command, id, p, end, &buf); - if (err == ERR_NONE && command == CMD_VM_INVOKE_METHOD) - /* Sent after the invoke is complete */ - no_reply = TRUE; - break; - case CMD_SET_EVENT_REQUEST: - err = event_commands (command, p, end, &buf); - break; - case CMD_SET_APPDOMAIN: - err = domain_commands (command, p, end, &buf); - break; - case CMD_SET_ASSEMBLY: - err = assembly_commands (command, p, end, &buf); - break; - case CMD_SET_MODULE: - err = module_commands (command, p, end, &buf); - break; - case CMD_SET_FIELD: - err = field_commands (command, p, end, &buf); - break; - case CMD_SET_TYPE: - err = type_commands (command, p, end, &buf); - break; - case CMD_SET_METHOD: - err = method_commands (command, p, end, &buf); - break; - case CMD_SET_THREAD: - err = thread_commands (command, p, end, &buf); - break; - case CMD_SET_STACK_FRAME: - err = frame_commands (command, p, end, &buf); - break; - case CMD_SET_ARRAY_REF: - err = array_commands (command, p, end, &buf); - break; - case CMD_SET_STRING_REF: - err = string_commands (command, p, end, &buf); - break; - case CMD_SET_POINTER: - err = pointer_commands (command, p, end, &buf); - break; - case CMD_SET_OBJECT_REF: - err = object_commands (command, p, end, &buf); - break; - default: - err = ERR_NOT_IMPLEMENTED; - } + err = mono_process_dbg_packet (id, command_set, command, &no_reply, p, end, &buf); if (command_set == CMD_SET_VM && command == CMD_VM_START_BUFFERING) { buffer_replies = TRUE; @@ -10106,10 +10229,10 @@ mono_debugger_agent_init (void) cbs.breakpoint_from_context = debugger_agent_breakpoint_from_context; cbs.free_mem_manager = debugger_agent_free_mem_manager; cbs.unhandled_exception = debugger_agent_unhandled_exception; - cbs.handle_exception = debugger_agent_handle_exception; + cbs.handle_exception = mono_debugger_agent_handle_exception; cbs.begin_exception_filter = debugger_agent_begin_exception_filter; cbs.end_exception_filter = debugger_agent_end_exception_filter; - cbs.user_break = debugger_agent_user_break; + cbs.user_break = mono_dbg_debugger_agent_user_break; cbs.debug_log = debugger_agent_debug_log; cbs.debug_log_is_enabled = debugger_agent_debug_log_is_enabled; cbs.send_crash = mono_debugger_agent_send_crash; diff --git a/src/mono/mono/mini/debugger-agent.h b/src/mono/mono/mini/debugger-agent.h index 500e8e610c3..bf0a06e2056 100644 --- a/src/mono/mono/mini/debugger-agent.h +++ b/src/mono/mono/mini/debugger-agent.h @@ -6,12 +6,45 @@ #define __MONO_DEBUGGER_AGENT_H__ #include "mini.h" +#include "debugger-protocol.h" + #include #define MONO_DBG_CALLBACKS_VERSION (4) // 2. debug_log parameters changed from MonoString* to MonoStringHandle // 3. debug_log parameters changed from MonoStringHandle back to MonoString* +typedef struct _InvokeData InvokeData; + +struct _InvokeData +{ + int id; + int flags; + guint8 *p; + guint8 *endp; + /* This is the context which needs to be restored after the invoke */ + MonoContext ctx; + gboolean has_ctx; + /* + * If this is set, invoke this method with the arguments given by ARGS. + */ + MonoMethod *method; + gpointer *args; + guint32 suspend_count; + int nmethods; + + InvokeData *last_invoke; +}; + +typedef struct { + const char *name; + void (*connect) (const char *address); + void (*close1) (void); + void (*close2) (void); + gboolean (*send) (void *buf, int len); + int (*recv) (void *buf, int len); +} DebuggerTransport; + struct _MonoDebuggerCallbacks { int version; void (*parse_options) (char *options); @@ -46,4 +79,37 @@ mono_debugger_agent_stub_init (void); MONO_API MONO_RT_EXTERNAL_ONLY gboolean mono_debugger_agent_transport_handshake (void); +MONO_API void +mono_debugger_agent_register_transport (DebuggerTransport *trans); + +MdbgProtErrorCode +mono_process_dbg_packet (int id, MdbgProtCommandSet command_set, int command, gboolean *no_reply, guint8 *p, guint8 *end, MdbgProtBuffer *buf); + +void +mono_init_debugger_agent_for_wasm (int log_level); + +void* +mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, MdbgProtEventKind kind); + +void +mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); + +void +mono_dbg_debugger_agent_user_break (void); + +void +mono_wasm_save_thread_context (void); + +DebuggerTlsData* +mono_wasm_get_tls (void); + +MdbgProtErrorCode +mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp); + +void +mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); + +void +mono_ss_discard_frame_context (void *the_tls); + #endif diff --git a/src/mono/mono/mini/debugger-engine.c b/src/mono/mono/mini/debugger-engine.c index c9d85b9099c..8c85227e842 100644 --- a/src/mono/mono/mini/debugger-engine.c +++ b/src/mono/mono/mini/debugger-engine.c @@ -388,13 +388,6 @@ collect_domain_bp (gpointer key, gpointer value, gpointer user_data) jit_mm_unlock (jit_mm); } -void -mono_de_clear_all_breakpoints (void) -{ - while (breakpoints->len) - mono_de_clear_breakpoint ((MonoBreakpoint*)g_ptr_array_index (breakpoints, 0)); -} - /* * mono_de_set_breakpoint: * diff --git a/src/mono/mono/mini/debugger-engine.h b/src/mono/mono/mini/debugger-engine.h index 24ad575093e..20fe88fedb8 100644 --- a/src/mono/mono/mini/debugger-engine.h +++ b/src/mono/mono/mini/debugger-engine.h @@ -497,7 +497,6 @@ MonoBreakpoint* mono_de_set_breakpoint (MonoMethod *method, long il_offset, Even void mono_de_collect_breakpoints_by_sp (SeqPoint *sp, MonoJitInfo *ji, GPtrArray *ss_reqs, GPtrArray *bp_reqs); void mono_de_clear_breakpoints_for_domain (MonoDomain *domain); void mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji); -void mono_de_clear_all_breakpoints (void); MonoBreakpoint * mono_de_get_breakpoint_by_id (int id); //single stepping @@ -545,3 +544,15 @@ void win32_debugger_log(FILE *stream, const gchar *format, ...); #define PRINT_ERROR_MSG(...) g_printerr (__VA_ARGS__) #define PRINT_MSG(...) g_print (__VA_ARGS__) #endif + +int +mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); + +void +mono_ss_args_destroy (SingleStepArgs *ss_args); + +int +mono_get_this_async_id (DbgEngineStackFrame *frame); + +void +mono_ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); diff --git a/src/mono/mono/mini/debugger-protocol.c b/src/mono/mono/mini/debugger-protocol.c index 1402dae3d36..2fe3096608a 100644 --- a/src/mono/mono/mini/debugger-protocol.c +++ b/src/mono/mono/mini/debugger-protocol.c @@ -56,7 +56,6 @@ m_dbgprot_decode_int (uint8_t *buf, uint8_t **endbuf, uint8_t *limit) { *endbuf = buf + 4; g_assert (*endbuf <= limit); - return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0); } diff --git a/src/mono/mono/mini/debugger-protocol.h b/src/mono/mono/mini/debugger-protocol.h index 87d9e232c85..24e8a2e42c2 100644 --- a/src/mono/mono/mini/debugger-protocol.h +++ b/src/mono/mono/mini/debugger-protocol.h @@ -34,7 +34,8 @@ typedef enum { MDBGPROT_CMD_VM_START_BUFFERING = 14, MDBGPROT_CMD_VM_STOP_BUFFERING = 15, MDBGPROT_CMD_VM_READ_MEMORY = 16, - MDBGPROT_CMD_VM_WRITE_MEMORY = 17 + MDBGPROT_CMD_VM_WRITE_MEMORY = 17, + MDBGPROT_CMD_GET_ASSEMBLY_BY_NAME = 18 } MdbgProtCmdVM; typedef enum { @@ -173,7 +174,9 @@ typedef enum { MDBGPROT_CMD_METHOD_MAKE_GENERIC_METHOD = 10, MDBGPROT_CMD_METHOD_TOKEN = 11, MDBGPROT_CMD_METHOD_ASSEMBLY = 12, - MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13 + MDBGPROT_CMD_METHOD_GET_CLASS_TOKEN = 13, + MDBGPROT_CMD_METHOD_HAS_ASYNC_DEBUG_INFO = 14, + MDBGPROT_CMD_METHOD_GET_NAME_FULL = 15 } MdbgProtCmdMethod; typedef enum { @@ -197,7 +200,8 @@ typedef enum { MDBGPROT_CMD_TYPE_IS_INITIALIZED = 18, MDBGPROT_CMD_TYPE_CREATE_INSTANCE = 19, MDBGPROT_CMD_TYPE_GET_VALUE_SIZE = 20, - MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21 + MDBGPROT_CMD_TYPE_GET_VALUES_ICORDBG = 21, + MDBGPROT_CMD_TYPE_GET_PARENTS = 22 } MdbgProtCmdType; typedef enum { @@ -235,7 +239,9 @@ typedef enum { MDBGPROT_CMD_OBJECT_REF_GET_DOMAIN = 5, MDBGPROT_CMD_OBJECT_REF_SET_VALUES = 6, MDBGPROT_CMD_OBJECT_REF_GET_INFO = 7, - MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8 + MDBGPROT_CMD_OBJECT_REF_GET_VALUES_ICORDBG = 8, + MDBGPROT_CMD_OBJECT_REF_DELEGATE_GET_METHOD = 9, + MDBGPROT_CMD_OBJECT_IS_DELEGATE = 10 } MdbgProtCmdObject; typedef enum { diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/mini/mini-wasm-debugger.c index 352bb81deb9..fd19c6a8462 100644 --- a/src/mono/mono/mini/mini-wasm-debugger.c +++ b/src/mono/mono/mini/mini-wasm-debugger.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle #include @@ -24,84 +26,29 @@ static int log_level = 1; -enum { - EXCEPTION_MODE_NONE, - EXCEPTION_MODE_UNCAUGHT, - EXCEPTION_MODE_ALL -}; - -// Flags for get_*_properties -#define GPFLAG_NONE 0x0000 -#define GPFLAG_OWN_PROPERTIES 0x0001 -#define GPFLAG_ACCESSORS_ONLY 0x0002 -#define GPFLAG_EXPAND_VALUETYPES 0x0004 - //functions exported to be used by JS G_BEGIN_DECLS -EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset); -EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id); -EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void); -EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_local_vars (int scope, int* pos, int len); -EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void); -EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind); -EMSCRIPTEN_KEEPALIVE int mono_wasm_pause_on_exceptions (int state); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, int gpflags); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_object (int object_id, const char* name); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass); EMSCRIPTEN_KEEPALIVE void mono_wasm_set_is_debugger_attached (gboolean is_attached); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value); -EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_set_value_on_object (int object_id, const char* name, const char* value); +EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size); +EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue); + //JS functions imported that we use -extern void mono_wasm_add_frame (int il_offset, int method_token, int frame_id, const char *assembly_name, const char *method_name); -extern void mono_wasm_fire_bp (void); -extern void mono_wasm_fire_exception (int exception_obj_id, const char* message, const char* class_name, gboolean uncaught); -extern void mono_wasm_add_obj_var (const char*, const char*, guint64); -extern void mono_wasm_add_enum_var (const char*, const char*, guint64); -extern void mono_wasm_add_func_var (const char*, const char*, guint64); -extern void mono_wasm_add_properties_var (const char*, gint32); -extern void mono_wasm_add_array_item (int); -extern void mono_wasm_set_is_async_method (guint64); -extern void mono_wasm_add_typed_value (const char *type, const char *str_value, double value); +extern void mono_wasm_fire_debugger_agent_message (void); extern void mono_wasm_asm_loaded (const char *asm_name, const char *assembly_data, guint32 assembly_len, const char *pdb_data, guint32 pdb_len); G_END_DECLS -static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags); static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); +static gboolean receive_debugger_agent_message (void *data, int len); static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly); -static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error); //FIXME move all of those fields to the profiler object static gboolean debugger_enabled; static gboolean has_pending_lazy_loaded_assemblies; -static int event_request_id; -static GHashTable *objrefs; -static GHashTable *obj_to_objref; -static int objref_id = 0; -static int pause_on_exc = EXCEPTION_MODE_NONE; -static MonoObject* exception_on_runtime_invoke = NULL; - -static const char* -all_getters_allowed_class_names[] = { - "System.DateTime", - "System.DateTimeOffset", - "System.TimeSpan" -}; - -static const char* -to_string_as_descr_names[] = { - "System.DateTime", - "System.DateTimeOffset", - "System.Decimal", - "System.TimeSpan" -}; #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread @@ -129,14 +76,6 @@ void wasm_debugger_log (int level, const gchar *format, ...) g_free (mesg); } -static void -inplace_tolower (char *c) -{ - int i; - for (i = strlen (c) - 1; i >= 0; --i) - c [i] = tolower (c [i]); -} - static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) { @@ -149,78 +88,6 @@ appdomain_load (MonoProfiler *prof, MonoDomain *domain) mono_de_domain_add (domain); } -/* Frame state handling */ -static GPtrArray *frames; - -static void -free_frame (DbgEngineStackFrame *frame) -{ - g_free (frame); -} - -static gboolean -collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data) -{ - SeqPoint sp; - MonoMethod *method; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) - return FALSE; - - if (info->ji) - method = jinfo_get_method (info->ji); - else - method = info->method; - - if (!method) - return FALSE; - - PRINT_DEBUG_MSG (2, "collect_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type); - - if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp)) - PRINT_DEBUG_MSG (2, "collect_frames: Failed to lookup sequence point. method: %s, native_offset: %d \n", method->name, info->native_offset); - - - StackFrame *frame = g_new0 (StackFrame, 1); - frame->de.ji = info->ji; - frame->de.domain = mono_get_root_domain (); - frame->de.method = method; - frame->de.native_offset = info->native_offset; - - frame->il_offset = info->il_offset; - frame->interp_frame = info->interp_frame; - frame->frame_addr = info->frame_addr; - - g_ptr_array_add (frames, frame); - - return FALSE; -} - -static void -free_frame_state (void) -{ - if (frames) { - int i; - for (i = 0; i < frames->len; ++i) - free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i)); - g_ptr_array_set_size (frames, 0); - } -} - -static void -compute_frames (void) { - if (frames) { - int i; - for (i = 0; i < frames->len; ++i) - free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i)); - g_ptr_array_set_size (frames, 0); - } else { - frames = g_ptr_array_new (); - } - - mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL); -} static MonoContext* tls_get_restore_state (void *tls) { @@ -247,17 +114,14 @@ begin_single_step_processing (MonoContext *ctx, gboolean from_signal) static void ss_discard_frame_context (void *the_tls) { - free_frame_state (); + mono_ss_discard_frame_context (mono_wasm_get_tls()); } static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes) { - compute_frames (); - if (out_frames) - *out_frames = (DbgEngineStackFrame **)frames->pdata; - if (nframes) - *nframes = frames->len; + mono_wasm_save_thread_context(); + mono_ss_calculate_framecount(mono_wasm_get_tls(), NULL, force_use_ctx, out_frames, nframes); } static gboolean @@ -272,132 +136,8 @@ ensure_runtime_is_suspended (void) return DE_ERR_NONE; } -static int -get_object_id (MonoObject *obj) -{ - ObjRef *ref; - if (!obj) - return 0; - - ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); - if (ref) - return ref->id; - ref = g_new0 (ObjRef, 1); - ref->id = mono_atomic_inc_i32 (&objref_id); - ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE); - g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref); - g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); - return ref->id; -} - - -static int -get_this_async_id (DbgEngineStackFrame *frame) -{ - MonoClassField *builder_field; - gpointer builder; - MonoMethod *method; - MonoObject *ex; - ERROR_DECL (error); - MonoObject *obj; - - /* - * FRAME points to a method in a state machine class/struct. - * Call the ObjectIdForDebugger method of the associated method builder type. - */ - builder = get_async_method_builder (frame); - if (!builder) - return 0; - - builder_field = mono_class_get_field_from_name_full (get_class_to_get_builder_field(frame), "<>t__builder", NULL); - if (!builder_field) - return 0; - - method = get_object_id_for_debugger_method (mono_class_from_mono_type_internal (builder_field->type)); - if (!method) { - return 0; - } - - obj = mono_runtime_try_invoke_internal (method, builder, NULL, &ex, error); - mono_error_assert_ok (error); - - return get_object_id (obj); -} - -typedef struct { - gboolean is_ss; //do I need this? -} BpEvents; - -static void* -create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind) -{ - PRINT_DEBUG_MSG (1, "ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len); - if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) { - BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events - evts->is_ss = (ss_reqs && ss_reqs->len); - return evts; - } - return NULL; -} - -static void -process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets) -{ - BpEvents *evts = (BpEvents*)_evts; - if (evts) { - if (evts->is_ss) - mono_de_cancel_all_ss (); - mono_wasm_fire_bp (); - g_free (evts); - } -} - -static void -no_seq_points_found (MonoMethod *method, int offset) -{ - /* - * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space. - */ - PRINT_DEBUG_MSG (1, "Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset); -} - #define DBG_NOT_SUSPENDED 1 -static int -ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args) -{ - PRINT_DEBUG_MSG (1, "ss_create_init_args\n"); - int dummy = 0; - ss_req->start_sp = ss_req->last_sp = &dummy; - compute_frames (); - memset (ss_args, 0, sizeof (*ss_args)); - - // This shouldn't happen - maybe should assert here ? - if (frames->len == 0) { - PRINT_DEBUG_MSG (1, "SINGLE STEPPING FOUND NO FRAMES"); - return DBG_NOT_SUSPENDED; - } - - DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0); - ss_req->start_method = ss_args->method = frame->method; - gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->method, frame->native_offset, &ss_args->info, &ss_args->sp); - if (!found_sp) - no_seq_points_found (frame->method, frame->native_offset); - g_assert (found_sp); - - ss_args->frames = (DbgEngineStackFrame**)frames->pdata; - ss_args->nframes = frames->len; - //XXX do sp - - return DE_ERR_NONE; -} - -static void -ss_args_destroy (SingleStepArgs *ss_args) -{ - //nothing to do -} - static int handle_multiple_ss_requests (void) { mono_de_cancel_all_ss (); @@ -419,13 +159,13 @@ mono_wasm_debugger_init (void) .ss_calculate_framecount = ss_calculate_framecount, .ensure_jit = ensure_jit, .ensure_runtime_is_suspended = ensure_runtime_is_suspended, - .get_this_async_id = get_this_async_id, + .get_this_async_id = mono_get_this_async_id, .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag, .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method, - .create_breakpoint_events = create_breakpoint_events, - .process_breakpoint_events = process_breakpoint_events, - .ss_create_init_args = ss_create_init_args, - .ss_args_destroy = ss_args_destroy, + .create_breakpoint_events = mono_dbg_create_breakpoint_events, + .process_breakpoint_events = mono_dbg_process_breakpoint_events, + .ss_create_init_args = mono_ss_create_init_args, + .ss_args_destroy = mono_ss_args_destroy, .handle_multiple_ss_requests = handle_multiple_ss_requests, }; @@ -444,11 +184,16 @@ mono_wasm_debugger_init (void) mono_profiler_set_domain_loaded_callback (prof, appdomain_load); mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded); - obj_to_objref = g_hash_table_new (NULL, NULL); - objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); + mini_get_dbg_callbacks ()->handle_exception = mono_debugger_agent_handle_exception; + mini_get_dbg_callbacks ()->user_break = mono_dbg_debugger_agent_user_break; - mini_get_dbg_callbacks ()->handle_exception = handle_exception; - mini_get_dbg_callbacks ()->user_break = mono_wasm_user_break; +//debugger-agent initialization + DebuggerTransport trans; + trans.name = "buffer-wasm-communication"; + trans.send = receive_debugger_agent_message; + + mono_debugger_agent_register_transport (&trans); + mono_init_debugger_agent_for_wasm (log_level); } MONO_API void @@ -459,69 +204,6 @@ mono_wasm_enable_debugging (int debug_level) log_level = debug_level; } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_pause_on_exceptions (int state) -{ - pause_on_exc = state; - PRINT_DEBUG_MSG (1, "setting pause on exception: %d\n", pause_on_exc); - return 1; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_setup_single_step (int kind) -{ - int nmodifiers = 1; - - PRINT_DEBUG_MSG (2, ">>>> mono_wasm_setup_single_step %d\n", kind); - EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier))); - req->id = ++event_request_id; - req->event_kind = EVENT_KIND_STEP; - // DE doesn't care about suspend_policy - // req->suspend_policy = SUSPEND_POLICY_ALL; - req->nmodifiers = nmodifiers; - - StepSize size = STEP_SIZE_MIN; - - //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things. - StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE); - req->modifiers [0].data.filter = filter; - - StepDepth depth; - switch (kind) { - case 0: //into - depth = STEP_DEPTH_INTO; - break; - case 1: //out - depth = STEP_DEPTH_OUT; - break; - case 2: //over - depth = STEP_DEPTH_OVER; - break; - default: - g_error ("[dbg] unknown step kind %d", kind); - } - - DbgEngineErrorCode err = mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req); - if (err != DE_ERR_NONE) { - PRINT_DEBUG_MSG (1, "[dbg] Failed to setup single step request"); - } - PRINT_DEBUG_MSG (1, "[dbg] single step is in place, now what?\n"); - SingleStepReq *ss_req = req->info; - int isBPOnNativeCode = 0; - if (ss_req && ss_req->bps) { - GSList *l; - - for (l = ss_req->bps; l; l = l->next) { - if (((MonoBreakpoint *)l->data)->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) - isBPOnNativeCode = 1; - } - } - if (!isBPOnNativeCode) { - mono_de_cancel_all_ss (); - } - return isBPOnNativeCode; -} - static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) { @@ -551,1019 +233,46 @@ assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) } } -static void -handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) -{ - ERROR_DECL (error); - const char *default_error_message = "Failed to get exception message."; - - PRINT_DEBUG_MSG (1, "handle exception - %d - %p - %p - %p\n", pause_on_exc, exc, throw_ctx, catch_ctx); - - //normal mono_runtime_try_invoke does not capture the exception and this is a temporary workaround. - exception_on_runtime_invoke = (MonoObject*)exc; - - if (pause_on_exc == EXCEPTION_MODE_NONE) - return; - if (pause_on_exc == EXCEPTION_MODE_UNCAUGHT && catch_ctx != NULL) - return; - - int obj_id = get_object_id ((MonoObject *)exc); - char *error_message = mono_string_to_utf8_checked_internal (exc->message, error); - - const char *class_name = mono_class_full_name (mono_object_class (exc)); - PRINT_DEBUG_MSG (2, "handle exception - calling mono_wasm_fire_exc(): %d - message - %s, class_name: %s\n", obj_id, !is_ok (error) ? error_message : default_error_message, class_name); - - mono_wasm_fire_exception (obj_id, !is_ok (error) ? error_message : default_error_message, class_name, !catch_ctx); - - if (error_message != NULL) - g_free (error_message); - - PRINT_DEBUG_MSG (2, "handle exception - done\n"); -} - - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_clear_all_breakpoints (void) -{ - PRINT_DEBUG_MSG (1, "CLEAR BREAKPOINTS\n"); - mono_de_clear_all_breakpoints (); -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset) -{ - int i; - ERROR_DECL (error); - PRINT_DEBUG_MSG (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset); - - - //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot - char *lookup_name = g_strdup (assembly_name); - for (i = strlen (lookup_name) - 1; i >= 0; --i) { - if (lookup_name [i] == '.') { - lookup_name [i] = 0; - break; - } - } - - //resolve the assembly - MonoImageOpenStatus status; - MonoAssemblyName* aname = mono_assembly_name_new (lookup_name); - MonoAssemblyByNameRequest byname_req; - mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ()); - MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status); - g_free (lookup_name); - if (!assembly) { - PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); - return -1; - } - - mono_assembly_name_free_internal (aname); - - MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error); - if (!method) { - //FIXME don't swallow the error - PRINT_DEBUG_MSG (1, "Could not find method due to %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - return -1; - } - - //FIXME right now none of the EventRequest fields are used by debugger-engine - EventRequest *req = g_new0 (EventRequest, 1); - req->id = ++event_request_id; - req->event_kind = EVENT_KIND_BREAKPOINT; - //DE doesn't care about suspend_policy - // req->suspend_policy = SUSPEND_POLICY_ALL; - req->nmodifiers = 0; //funny thing, - - // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset); - MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, req, error); - - if (!bp) { - PRINT_DEBUG_MSG (1, "Could not set breakpoint to %s\n", mono_error_get_message (error)); - mono_error_cleanup (error); - return 0; - } - - PRINT_DEBUG_MSG (1, "NEW BP %p has id %d\n", req, req->id); - return req->id; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_remove_breakpoint (int bp_id) -{ - MonoBreakpoint *bp = mono_de_get_breakpoint_by_id (bp_id); - if (!bp) - return 0; - - mono_de_clear_breakpoint (bp); - return 1; -} void mono_wasm_single_step_hit (void) { - mono_de_process_single_step (NULL, FALSE); + mono_de_process_single_step (mono_wasm_get_tls(), FALSE); } void mono_wasm_breakpoint_hit (void) { - mono_de_process_breakpoint (NULL, FALSE); - // mono_wasm_fire_bp (); -} - -void -mono_wasm_user_break (void) -{ - mono_wasm_fire_bp (); -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_current_bp_id (void) -{ - PRINT_DEBUG_MSG (2, "COMPUTING breakpoint ID\n"); - //FIXME handle compiled case - - /* Interpreter */ - MonoLMF *lmf = mono_get_lmf (); - - g_assert (((guint64)lmf->previous_lmf) & 2); - MonoLMFExt *ext = (MonoLMFExt*)lmf; - - g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX); - MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data; - MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame); - guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame); - - g_assert (ji && !ji->is_trampoline); - MonoMethod *method = jinfo_get_method (ji); - - /* Compute the native offset of the breakpoint from the ip */ - guint32 native_offset = ip - (guint8*)ji->code_start; - - MonoSeqPointInfo *info = NULL; - SeqPoint sp; - gboolean found_sp = mono_find_prev_seq_point_for_native_offset (method, native_offset, &info, &sp); - if (!found_sp) - PRINT_DEBUG_MSG (1, "Could not find SP\n"); - - - GPtrArray *bp_reqs = g_ptr_array_new (); - mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs); - - if (bp_reqs->len == 0) { - PRINT_DEBUG_MSG (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset); - return -1; - } - - if (bp_reqs->len > 1) - PRINT_DEBUG_MSG (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len); - - EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0); - g_ptr_array_free (bp_reqs, TRUE); - - PRINT_DEBUG_MSG (1, "Found BP %p with id %d\n", evt, evt->id); - return evt->id; -} - -static MonoObject* -get_object_from_id (int objectId) -{ - ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId)); - if (!ref) { - PRINT_DEBUG_MSG (2, "get_object_from_id !ref: %d\n", objectId); - return NULL; - } - - MonoObject *obj = mono_gchandle_get_target_internal (ref->handle); - if (!obj) - PRINT_DEBUG_MSG (2, "get_object_from_id !obj: %d\n", objectId); - - return obj; + mono_de_process_breakpoint (mono_wasm_get_tls(), FALSE); } static gboolean -list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data) -{ - SeqPoint sp; - MonoMethod *method; - char *method_full_name; - - int* frame_id_p = (int*)data; - (*frame_id_p)++; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) - return FALSE; - - if (info->ji) - method = jinfo_get_method (info->ji); - else - method = info->method; - - if (!method || method->wrapper_type != MONO_WRAPPER_NONE) - return FALSE; - - PRINT_DEBUG_MSG (2, "list_frames: Reporting method %s native_offset %d, wrapper_type: %d\n", method->name, info->native_offset, method->wrapper_type); - - if (!mono_find_prev_seq_point_for_native_offset (method, info->native_offset, NULL, &sp)) - PRINT_DEBUG_MSG (2, "list_frames: Failed to lookup sequence point. method: %s, native_offset: %d\n", method->name, info->native_offset); - - method_full_name = mono_method_full_name (method, FALSE); - while (method->is_inflated) - method = ((MonoMethodInflated*)method)->declaring; - - char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name); - inplace_tolower (assembly_name); - - PRINT_DEBUG_MSG (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name); - mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), *frame_id_p, assembly_name, method_full_name); - - g_free (assembly_name); - - return FALSE; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_enum_frames (void) -{ - int frame_id = -1; - mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, &frame_id); -} - -static char* -invoke_to_string (const char *class_name, MonoClass *klass, gpointer addr) -{ - MonoObject *exc; - MonoString *mstr; - char *ret_str; - ERROR_DECL (error); - MonoObject *obj; - - // TODO: this is for a specific use case right now, - // (invoke ToString() get a preview/description for *some* types) - // and we don't want to report errors for that. - if (m_class_is_valuetype (klass)) { - MonoMethod *method; - - MONO_STATIC_POINTER_INIT (MonoMethod, to_string) - to_string = mono_class_get_method_from_name_checked (mono_get_object_class (), "ToString", 0, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC, error); - mono_error_assert_ok (error); - MONO_STATIC_POINTER_INIT_END (MonoMethod, to_string) - - method = mono_class_get_virtual_method (klass, to_string, error); - if (!method) - return NULL; - - MonoString *mstr = (MonoString*) mono_runtime_try_invoke_internal (method, addr , NULL, &exc, error); - if (exc || !is_ok (error)) { - PRINT_DEBUG_MSG (1, "Failed to invoke ToString for %s\n", class_name); - return NULL; - } - - return mono_string_to_utf8_checked_internal (mstr, error); - } - - obj = *(MonoObject**)addr; - if (!obj) - return NULL; - - mstr = mono_object_try_to_string (obj, &exc, error); - if (exc || !is_ok (error)) - return NULL; - - ret_str = mono_string_to_utf8_checked_internal (mstr, error); - if (!is_ok (error)) - return NULL; - - return ret_str; -} - -static char* -get_to_string_description (const char* class_name, MonoClass *klass, gpointer addr) -{ - if (!class_name || !klass || !addr) - return NULL; - - if (strcmp (class_name, "System.Guid") == 0) - return mono_guid_to_string (addr); - - for (int i = 0; i < G_N_ELEMENTS (to_string_as_descr_names); i ++) { - if (strcmp (to_string_as_descr_names [i], class_name) == 0) { - return invoke_to_string (class_name, klass, addr); - } - } - - return NULL; -} - -typedef struct { - int cur_frame; - int target_frame; - int len; - int *pos; - gboolean found; -} FrameDescData; - - -typedef struct { - int cur_frame; - int target_frame; - int pos; - const char* new_value; - gboolean found; - gboolean error; -} SetVariableValueData; - -/* - * this returns a string formatted like - * - * :[]: - * - * .. which is consumed by `mono_wasm_add_func_var`. It is used for - * generating this for the delegate, and it's target. - */ -static char* -mono_method_to_desc_for_js (MonoMethod *method, gboolean include_namespace) -{ - MonoMethodSignature *sig = mono_method_signature_internal (method); - char *ret_desc = mono_type_full_name (sig->ret); - char *args_desc = mono_signature_get_desc (sig, include_namespace); - - char *sig_desc = g_strdup_printf ("%s:%s:%s", ret_desc, args_desc, method->name); - - g_free (ret_desc); - g_free (args_desc); - return sig_desc; -} - -static guint64 -read_enum_value (const char *mem, int type) -{ - switch (type) { - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_U1: - return *(guint8*)mem; - case MONO_TYPE_I1: - return *(gint8*)mem; - case MONO_TYPE_CHAR: - case MONO_TYPE_U2: - return read16 (mem); - case MONO_TYPE_I2: - return (gint16) read16 (mem); - case MONO_TYPE_U4: - case MONO_TYPE_R4: - return read32 (mem); - case MONO_TYPE_I4: - return (gint32) read32 (mem); - case MONO_TYPE_U8: - case MONO_TYPE_I8: - case MONO_TYPE_R8: - return read64 (mem); - case MONO_TYPE_U: - case MONO_TYPE_I: -#if SIZEOF_REGISTER == 8 - return read64 (mem); -#else - return read32 (mem); -#endif - default: - g_assert_not_reached (); - } - return 0; -} - -static gboolean -nullable_try_get_value (guint8 *nullable, MonoClass *klass, gpointer* out_value) -{ - mono_class_setup_fields (klass); - g_assert (m_class_is_fields_inited (klass)); - - *out_value = NULL; - MonoClassField *klass_fields = m_class_get_fields (klass); - gpointer addr_for_has_value = mono_vtype_get_field_addr (nullable, &klass_fields[0]); - if (0 == *(guint8*)addr_for_has_value) - return FALSE; - - *out_value = mono_vtype_get_field_addr (nullable, &klass_fields[1]); - return TRUE; -} - -static gboolean -describe_value(MonoType * type, gpointer addr, int gpflags) -{ - ERROR_DECL (error); - switch (type->type) { - case MONO_TYPE_BOOLEAN: - mono_wasm_add_typed_value ("bool", NULL, *(gint8*)addr); - break; - case MONO_TYPE_I1: - mono_wasm_add_typed_value ("number", NULL, *(gint8*)addr); - break; - case MONO_TYPE_U1: - mono_wasm_add_typed_value ("number", NULL, *(guint8*)addr); - break; - case MONO_TYPE_CHAR: - mono_wasm_add_typed_value ("char", NULL, *(guint16*)addr); - break; - case MONO_TYPE_U2: - mono_wasm_add_typed_value ("number", NULL, *(guint16*)addr); - break; - case MONO_TYPE_I2: - mono_wasm_add_typed_value ("number", NULL, *(gint16*)addr); - break; - case MONO_TYPE_I4: - case MONO_TYPE_I: - mono_wasm_add_typed_value ("number", NULL, *(gint32*)addr); - break; - case MONO_TYPE_U4: - case MONO_TYPE_U: - mono_wasm_add_typed_value ("number", NULL, *(guint32*)addr); - break; - case MONO_TYPE_I8: - mono_wasm_add_typed_value ("number", NULL, *(gint64*)addr); - break; - case MONO_TYPE_U8: - mono_wasm_add_typed_value ("number", NULL, *(guint64*)addr); - break; - case MONO_TYPE_R4: - mono_wasm_add_typed_value ("number", NULL, *(float*)addr); - break; - case MONO_TYPE_R8: - mono_wasm_add_typed_value ("number", NULL, *(double*)addr); - break; - case MONO_TYPE_PTR: - case MONO_TYPE_FNPTR: { - char *class_name = mono_type_full_name (type); - const void *val = *(const void **)addr; - char *descr = g_strdup_printf ("(%s) %p", class_name, val); - - EM_ASM ({ - MONO.mono_wasm_add_typed_value ('pointer', $0, { ptr_addr: $1, klass_addr: $2 }); - }, descr, val ? addr : 0, val ? mono_class_from_mono_type_internal (type) : 0); - - g_free (descr); - g_free (class_name); - break; - } - - case MONO_TYPE_STRING: { - MonoString *str_obj = *(MonoString **)addr; - if (!str_obj) { - mono_wasm_add_typed_value ("string", NULL, 0); - } else { - char *str = mono_string_to_utf8_checked_internal (str_obj, error); - mono_error_assert_ok (error); /* FIXME report error */ - mono_wasm_add_typed_value ("string", str, 0); - g_free (str); - } - break; - } - - case MONO_TYPE_OBJECT: { - MonoObject *obj = *(MonoObject**)addr; - if (!obj) { - mono_wasm_add_obj_var ("object", NULL, 0); - break; - } - MonoClass *klass = obj->vtable->klass; - if (!klass) { - // boxed null - mono_wasm_add_obj_var ("object", NULL, 0); - break; - } - - type = m_class_get_byval_arg (klass); - if (type->type == MONO_TYPE_OBJECT) { - mono_wasm_add_obj_var ("object", "object", get_object_id (obj)); - break; - } - - // Boxed valuetype - if (m_class_is_valuetype (klass)) - addr = mono_object_unbox_internal (obj); - - return describe_value (type, addr, gpflags); - } - - case MONO_TYPE_GENERICINST: { - MonoClass *klass = mono_class_from_mono_type_internal (type); - if (mono_class_is_nullable (klass)) { - MonoType *targ = type->data.generic_class->context.class_inst->type_argv [0]; - - gpointer nullable_value = NULL; - if (nullable_try_get_value (addr, klass, &nullable_value)) { - return describe_value (targ, nullable_value, gpflags); - } else { - char* class_name = mono_type_full_name (type); - mono_wasm_add_obj_var (class_name, NULL, 0); - g_free (class_name); - break; - } - } - - if (mono_type_generic_inst_is_valuetype (type)) - goto handle_vtype; - /* - * else fallthrough - */ - } - - case MONO_TYPE_SZARRAY: - case MONO_TYPE_ARRAY: - case MONO_TYPE_CLASS: { - MonoObject *obj = *(MonoObject**)addr; - if (!obj) { - char *class_name = mono_type_full_name (type); - mono_wasm_add_func_var (class_name, NULL, 0); - g_free (class_name); - return TRUE; - } - MonoClass *klass = type->data.klass; - - if (m_class_is_valuetype (mono_object_class (obj))) { - addr = mono_object_unbox_internal (obj); - type = m_class_get_byval_arg (mono_object_class (obj)); - goto handle_vtype; - } - - char *class_name = mono_type_full_name (type); - int obj_id = get_object_id (obj); - - if (type-> type == MONO_TYPE_ARRAY || type->type == MONO_TYPE_SZARRAY) { - MonoArray *array = (MonoArray *)obj; - EM_ASM ({ - MONO.mono_wasm_add_typed_value ('array', $0, { objectId: $1, length: $2 }); - }, class_name, obj_id, mono_array_length_internal (array)); - } else if (m_class_is_delegate (klass) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) { - MonoMethod *method; - - if (type->type == MONO_TYPE_GENERICINST) - klass = type->data.generic_class->container_class; - - method = mono_get_delegate_invoke_internal (klass); - if (!method) { - mono_wasm_add_func_var (class_name, NULL, -1); - } else { - MonoMethod *tm = ((MonoDelegate *)obj)->method; - char *tm_desc = NULL; - if (tm) - tm_desc = mono_method_to_desc_for_js (tm, FALSE); - - mono_wasm_add_func_var (class_name, tm_desc, obj_id); - g_free (tm_desc); - } - } else { - char *to_string_val = get_to_string_description (class_name, klass, addr); - mono_wasm_add_obj_var (class_name, to_string_val, obj_id); - g_free (to_string_val); - } - g_free (class_name); - break; - } - - handle_vtype: - case MONO_TYPE_VALUETYPE: { - g_assert (addr); - MonoClass *klass = mono_class_from_mono_type_internal (type); - char *class_name = mono_type_full_name (type); - - if (m_class_is_enumtype (klass)) { - MonoClassField *field; - gpointer iter = NULL; - const char *p; - MonoTypeEnum def_type; - guint64 field_value; - guint64 value__ = 0xDEAD; - GString *enum_members = g_string_new (""); - int base_type = mono_class_enum_basetype_internal (klass)->type; - - while ((field = mono_class_get_fields_internal (klass, &iter))) { - if (strcmp ("value__", mono_field_get_name (field)) == 0) { - value__ = read_enum_value (mono_vtype_get_field_addr (addr, field), base_type); - continue; - } - - if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) - continue; - if (mono_field_is_deleted (field)) - continue; - - p = mono_class_get_field_default_value (field, &def_type); - /* this is to correctly increment `p` in the blob */ - /* len = */ mono_metadata_decode_blob_size (p, &p); - - field_value = read_enum_value (p, base_type); - - g_string_append_printf (enum_members, ",%s:%llu", mono_field_get_name (field), field_value); - } - - mono_wasm_add_enum_var (class_name, enum_members->str, value__); - g_string_free (enum_members, TRUE); - } else { - char *to_string_val = get_to_string_description (class_name, klass, addr); - - if (gpflags & GPFLAG_EXPAND_VALUETYPES) { - int32_t size = mono_class_value_size (klass, NULL); - void *value_buf = g_malloc0 (size); - mono_value_copy_internal (value_buf, addr, klass); - - EM_ASM ({ - MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2, value_addr: $3, value_size: $4, klass: $5 }); - }, "begin_vt", class_name, to_string_val, value_buf, size, klass); - - g_free (value_buf); - - // FIXME: isAsyncLocalThis - describe_object_properties_for_klass (addr, klass, FALSE, gpflags); - mono_wasm_add_typed_value ("end_vt", NULL, 0); - } else { - EM_ASM ({ - MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2 }); - }, "unexpanded_vt", class_name, to_string_val); - } - g_free (to_string_val); - } - g_free (class_name); - break; - } - default: { - char *type_name = mono_type_full_name (type); - char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type); - mono_wasm_add_typed_value ("string", msg, 0); - g_free (msg); - g_free (type_name); - } - } - return TRUE; -} - -static gboolean -are_getters_allowed (const char *class_name) -{ - for (int i = 0; i < G_N_ELEMENTS (all_getters_allowed_class_names); i ++) { - if (strcmp (class_name, all_getters_allowed_class_names [i]) == 0) - return TRUE; - } - - return FALSE; -} - -static gboolean -invoke_and_describe_getter_value (MonoObject *obj, MonoProperty *p) -{ - ERROR_DECL (error); - MonoObject *res; - MonoObject *exc; - - MonoMethodSignature *sig = mono_method_signature_internal (p->get); - - res = mono_runtime_try_invoke_internal (p->get, obj, NULL, &exc, error); - if (!is_ok (error) && exc == NULL) - exc = (MonoObject *) mono_error_convert_to_exception (error); - if (exc) - { - const char *class_name = mono_class_full_name (mono_object_class (exc)); - ERROR_DECL (local_error); - char *str = mono_string_to_utf8_checked_internal (((MonoException*)exc)->message, local_error); - mono_error_assert_ok (local_error); /* FIXME report error */ - char *msg = g_strdup_printf("%s: %s", class_name, str); - mono_wasm_add_typed_value ("string", msg, 0); - g_free (msg); - return TRUE; - } - else if (!res || !m_class_is_valuetype (mono_object_class (res))) - return describe_value (sig->ret, &res, GPFLAG_EXPAND_VALUETYPES); - else - return describe_value (sig->ret, mono_object_unbox_internal (res), GPFLAG_EXPAND_VALUETYPES); -} - -static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error) -{ - exception_on_runtime_invoke = NULL; - MonoObject* res = mono_runtime_try_invoke (method, obj, params, exc, error); - if (exception_on_runtime_invoke != NULL) - *exc = exception_on_runtime_invoke; - exception_on_runtime_invoke = NULL; - return res; -} - -static void -describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags) -{ - MonoClassField *f; - MonoProperty *p; - MonoMethodSignature *sig; - gboolean is_valuetype; - int pnum; - char *klass_name; - gboolean auto_invoke_getters; - gboolean is_own; - gboolean only_backing_fields; - - g_assert (klass); - MonoClass *start_klass = klass; - - only_backing_fields = gpflags & GPFLAG_ACCESSORS_ONLY; - is_valuetype = m_class_is_valuetype(klass); - if (is_valuetype) - gpflags |= GPFLAG_EXPAND_VALUETYPES; - -handle_parent: - is_own = (start_klass == klass); - klass_name = mono_class_full_name (klass); - gpointer iter = NULL; - while (obj && (f = mono_class_get_fields_internal (klass, &iter))) { - if (isAsyncLocalThis && f->name[0] == '<' && f->name[1] == '>') { - if (g_str_has_suffix (f->name, "__this")) { - mono_wasm_add_properties_var ("this", f->offset); - gpointer field_value = (guint8*)obj + f->offset; - - describe_value (f->type, field_value, gpflags); - } - - continue; - } - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) - continue; - if (mono_field_is_deleted (f)) - continue; - - if (only_backing_fields && !g_str_has_suffix(f->name, "k__BackingField")) - continue; - - EM_ASM ({ - MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 }); - }, f->name, f->offset, is_own, f->type->attrs, klass_name); - - gpointer field_addr; - if (is_valuetype) - field_addr = mono_vtype_get_field_addr (obj, f); - else - field_addr = (guint8*)obj + f->offset; - - describe_value (f->type, field_addr, gpflags); - } - - auto_invoke_getters = are_getters_allowed (klass_name); - iter = NULL; - pnum = 0; - while ((p = mono_class_get_properties (klass, &iter))) { - if (p->get->name) { //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug - if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>'))) - continue; - - sig = mono_method_signature_internal (p->get); - if (sig->param_count != 0) { - // getters with params are not shown - continue; - } - - if (p->get->flags & METHOD_ATTRIBUTE_STATIC) - continue; - - EM_ASM ({ - MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 }); - }, p->name, pnum, is_own, p->attrs, klass_name); - - gboolean vt_self_type_getter = is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass; - if (auto_invoke_getters && !vt_self_type_getter) { - invoke_and_describe_getter_value (obj, p); - } else { - // not allowed to call the getter here - char *ret_class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret)); - - mono_wasm_add_typed_value ("getter", ret_class_name, -1); - - g_free (ret_class_name); - continue; - } - } - pnum ++; - } - - g_free (klass_name); - - // ownProperties - // Note: ownProperties should mean that we return members of the klass itself, - // but we are going to ignore that here, because otherwise vscode/chrome don't - // seem to ask for inherited fields at all. - // if (!is_valuetype && !(gpflags & GPFLAG_OWN_PROPERTIES) && (klass = m_class_get_parent (klass))) - if (!is_valuetype && (klass = m_class_get_parent (klass))) - goto handle_parent; -} - -/* - * We return a `Target` property only for now. - * In future, we could add a `MethodInfo` too. - */ -static gboolean -describe_delegate_properties (MonoObject *obj) -{ - MonoClass *klass = mono_object_class(obj); - if (!m_class_is_delegate (klass)) - return FALSE; - - // Target, like in VS - what is this field supposed to be, anyway?? - MonoMethod *tm = ((MonoDelegate *)obj)->method; - char * sig_desc = mono_method_to_desc_for_js (tm, FALSE); - - mono_wasm_add_properties_var ("Target", -1); - mono_wasm_add_func_var (NULL, sig_desc, -1); - - g_free (sig_desc); - return TRUE; -} - -static gboolean -describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, int gpflags) -{ - PRINT_DEBUG_MSG (2, "describe_object_properties %llu, gpflags: %d\n", objectId, gpflags); - - MonoObject *obj = get_object_from_id (objectId); - if (!obj) - return FALSE; - - if (m_class_is_delegate (mono_object_class (obj))) { - // delegates get the same id format as regular objects - describe_delegate_properties (obj); - } else { - describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, gpflags); - } - - return TRUE; -} - -static gboolean -invoke_getter (void *obj_or_value, MonoClass *klass, const char *name) -{ - if (!obj_or_value || !klass || !name) { - PRINT_DEBUG_MSG (2, "invoke_getter: none of the arguments can be null"); - return FALSE; - } - - gpointer iter; -handle_parent: - iter = NULL; - MonoProperty *p; - while ((p = mono_class_get_properties (klass, &iter))) { - //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug - if (!p->get->name || strcasecmp (p->name, name) != 0) - continue; - - invoke_and_describe_getter_value (obj_or_value, p); - return TRUE; - } - - if ((klass = m_class_get_parent(klass))) - goto handle_parent; - - return FALSE; -} - -static gboolean -describe_array_values (guint64 objectId, int startIdx, int count, int gpflags) -{ - if (count == 0) - return TRUE; - - int esize; - gpointer elem; - MonoArray *arr = (MonoArray*) get_object_from_id (objectId); - if (!arr) - return FALSE; - - MonoClass *klass = mono_object_class (arr); - MonoTypeEnum type = m_class_get_byval_arg (klass)->type; - if (type != MONO_TYPE_SZARRAY && type != MONO_TYPE_ARRAY) { - PRINT_DEBUG_MSG (1, "describe_array_values: object is not an array. type: 0x%x\n", type); - return FALSE; - } - - int len = arr->max_length; - if (len == 0 && startIdx == 0 && count <= 0) { - // Nothing to do - return TRUE; - } - - if (startIdx < 0 || (len > 0 && startIdx >= len)) { - PRINT_DEBUG_MSG (1, "describe_array_values: invalid startIdx (%d) for array of length %d\n", startIdx, len); - return FALSE; - } - - if (count > 0 && (startIdx + count) > len) { - PRINT_DEBUG_MSG (1, "describe_array_values: invalid count (%d) for startIdx: %d, and array of length %d\n", count, startIdx, len); - return FALSE; - } - - esize = mono_array_element_size (klass); - int endIdx = count < 0 ? len : startIdx + count; - - for (int i = startIdx; i < endIdx; i ++) { - mono_wasm_add_array_item(i); - elem = (gpointer*)((char*)arr->vector + (i * esize)); - describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, gpflags); - } - return TRUE; -} - -static void -describe_async_method_locals (InterpFrame *frame, MonoMethod *method) -{ - //Async methods are special in the way that local variables can be lifted to generated class fields - gpointer addr = NULL; - if (mono_debug_lookup_method_async_debug_info (method)) { - addr = mini_get_interp_callbacks ()->frame_get_this (frame); - MonoObject *obj = *(MonoObject**)addr; - int objId = get_object_id (obj); - mono_wasm_set_is_async_method (objId); - describe_object_properties (objId, TRUE, GPFLAG_NONE); - } -} - -static void -describe_non_async_this (InterpFrame *frame, MonoMethod *method) -{ - gpointer addr = NULL; - if (mono_debug_lookup_method_async_debug_info (method)) - return; - - if (mono_method_signature_internal (method)->hasthis) { - addr = mini_get_interp_callbacks ()->frame_get_this (frame); - MonoObject *obj = *(MonoObject**)addr; - MonoClass *klass = method->klass; - MonoType *type = m_class_get_byval_arg (method->klass); - - mono_wasm_add_properties_var ("this", -1); - - if (m_class_is_valuetype (klass)) { - describe_value (type, obj, GPFLAG_EXPAND_VALUETYPES); - } else { - // this is an object, and we can retrieve the valuetypes in it later - // through the object id - describe_value (type, addr, GPFLAG_NONE); - } - } -} - -static gboolean -describe_variable (InterpFrame *frame, MonoMethod *method, MonoMethodHeader *header, int pos, int gpflags) -{ - MonoType *type = NULL; - gpointer addr = NULL; - if (pos < 0) { - MonoMethodSignature *sig = mono_method_signature_internal (method); - pos = -pos - 1; - - if (pos >= sig->param_count) { - PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access param indexed %d, but the method (%s) has only %d params\n", pos, method->name, sig->param_count); - return FALSE; - } - - type = sig->params [pos]; - addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos); - } else { - if (pos >= header->num_locals) { - PRINT_DEBUG_MSG(1, "BUG: describe_variable, trying to access local indexed %d, but the method (%s) has only %d locals\n", pos, method->name, header->num_locals); - return FALSE; - } - - type = header->locals [pos]; - addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos); - } - - PRINT_DEBUG_MSG (2, "adding val %p type 0x%x %s\n", addr, type->type, mono_type_full_name (type)); - - return describe_value(type, addr, gpflags); -} - -static gboolean -decode_value (MonoType *t, guint8 *addr, const char* variableValue) +write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* variableValue) { char* endptr; errno = 0; - switch (t->type) { + buffer_add_byte (buf, type); + switch (type) { case MONO_TYPE_BOOLEAN: if (!strcasecmp (variableValue, "True")) - *(guint8*)addr = 1; + buffer_add_int (buf, 1); else if (!strcasecmp (variableValue, "False")) - *(guint8*)addr = 0; - else + buffer_add_int (buf, 0); + else return FALSE; break; case MONO_TYPE_CHAR: if (strlen (variableValue) > 1) return FALSE; - *(gunichar2*)addr = variableValue [0]; + buffer_add_int (buf, (variableValue [0])); break; case MONO_TYPE_I1: { intmax_t val = strtoimax (variableValue, &endptr, 10); if (errno != 0) return FALSE; if (val >= -128 && val <= 127) - *(gint8*)addr = val; - else + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1572,8 +281,8 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) if (errno != 0) return FALSE; if (val >= 0 && val <= 255) - *(guint8*)addr = val; - else + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1582,8 +291,8 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) if (errno != 0) return FALSE; if (val >= -32768 && val <= 32767) - *(gint16*)addr = val; - else + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1592,8 +301,8 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) if (errno != 0) return FALSE; if (val >= 0 && val <= 65535) - *(guint16*)addr = val; - else + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1602,8 +311,8 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) if (errno != 0) return FALSE; if (val >= -2147483648 && val <= 2147483647) - *(gint32*)addr = val; - else + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1611,9 +320,9 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) intmax_t val = strtoimax (variableValue, &endptr, 10); if (errno != 0) return FALSE; - if (val >= 0 && val <= 4294967295) - *(guint32*)addr = val; - else + if (val >= 0 && val <= 4294967295) + buffer_add_int (buf, val); + else return FALSE; break; } @@ -1621,28 +330,28 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) long long val = strtoll (variableValue, &endptr, 10); if (errno != 0) return FALSE; - *(gint64*)addr = val; + buffer_add_long (buf, val); break; } case MONO_TYPE_U8: { long long val = strtoll (variableValue, &endptr, 10); if (errno != 0) return FALSE; - *(guint64*)addr = val; + buffer_add_long (buf, val); break; } case MONO_TYPE_R4: { gfloat val = strtof (variableValue, &endptr); if (errno != 0) return FALSE; - *(gfloat*)addr = val; + buffer_add_int (buf, *((gint32*)(&val))); break; } case MONO_TYPE_R8: { gdouble val = strtof (variableValue, &endptr); if (errno != 0) return FALSE; - *(gdouble*)addr = val; + buffer_add_long (buf, *((guint64*)(&val))); break; } default: @@ -1651,280 +360,6 @@ decode_value (MonoType *t, guint8 *addr, const char* variableValue) return TRUE; } -static gboolean -set_variable_value_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud) -{ - ERROR_DECL (error); - SetVariableValueData *data = (SetVariableValueData*)ud; - gboolean is_arg = FALSE; - MonoType *t = NULL; - guint8 *val_buf = NULL; - - ++data->cur_frame; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { - return FALSE; - } - - if (data->cur_frame != data->target_frame) - return FALSE; - - data->found = TRUE; - - InterpFrame *frame = (InterpFrame*)info->interp_frame; - MonoMethod *method = frame->imethod->method; - MonoMethodSignature *sig = mono_method_signature_internal (method); - MonoMethodHeader *header = mono_method_get_header_checked (method, error); - - if (!header) { - mono_error_cleanup(error); - data->error = TRUE; - return TRUE; - } - - if (!sig) - goto exit_with_error; - - int pos = data->pos; - - if (pos < 0) { - pos = - pos - 1; - if (pos >= sig->param_count) - goto exit_with_error; - is_arg = TRUE; - t = sig->params [pos]; - } - else { - if (pos >= header->num_locals) - goto exit_with_error; - t = header->locals [pos]; - } - - guint8 *addr; - if (is_arg) - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_arg (frame, pos); - else - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_local (frame, pos); - - val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (t))); - - if (!decode_value(t, val_buf, data->new_value)) - goto exit_with_error; - - DbgEngineErrorCode errorCode = mono_de_set_interp_var (t, addr, val_buf); - if (errorCode != ERR_NONE) { - goto exit_with_error; - } - - mono_metadata_free_mh (header); - return TRUE; - -exit_with_error: - data->error = TRUE; - mono_metadata_free_mh (header); - return TRUE; -} - -static gboolean -describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud) -{ - ERROR_DECL (error); - FrameDescData *data = (FrameDescData*)ud; - - ++data->cur_frame; - - //skip wrappers - if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { - return FALSE; - } - - if (data->cur_frame != data->target_frame) - return FALSE; - - data->found = TRUE; - - InterpFrame *frame = (InterpFrame*)info->interp_frame; - g_assert (frame); - MonoMethod *method = frame->imethod->method; - g_assert (method); - - MonoMethodHeader *header = mono_method_get_header_checked (method, error); - mono_error_assert_ok (error); /* FIXME report error */ - - for (int i = 0; i < data->len; i++) - { - if (!describe_variable (frame, method, header, data->pos[i], GPFLAG_EXPAND_VALUETYPES)) - mono_wasm_add_typed_value("symbol", "", 0); - } - - describe_async_method_locals (frame, method); - describe_non_async_this (frame, method); - - mono_metadata_free_mh (header); - return TRUE; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_set_variable_on_frame (int scope, int index, const char* name, const char* value) -{ - if (scope < 0) - return FALSE; - - SetVariableValueData data; - data.target_frame = scope; - data.cur_frame = -1; - data.pos = index; - data.found = FALSE; - data.new_value = value; - data.error = FALSE; - - mono_walk_stack_with_ctx (set_variable_value_on_frame, NULL, MONO_UNWIND_NONE, &data); - return !data.error; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass) -{ - MonoType *type = m_class_get_byval_arg (klass); - if (type->type != MONO_TYPE_PTR && type->type != MONO_TYPE_FNPTR) { - PRINT_DEBUG_MSG (2, "BUG: mono_wasm_get_deref_ptr_value: Expected to get a ptr type, but got 0x%x\n", type->type); - return FALSE; - } - - mono_wasm_add_properties_var ("deref", -1); - return describe_value (type->data.type, value_addr, GPFLAG_EXPAND_VALUETYPES); -} - -//FIXME this doesn't support getting the return value pseudo-var -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_local_vars (int scope, int* pos, int len) -{ - if (scope < 0) - return FALSE; - - FrameDescData data; - data.target_frame = scope; - data.cur_frame = -1; - data.len = len; - data.pos = pos; - data.found = FALSE; - - mono_walk_stack_with_ctx (describe_variables_on_frame, NULL, MONO_UNWIND_NONE, &data); - - return data.found; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_object_properties (int object_id, int gpflags) -{ - PRINT_DEBUG_MSG (2, "getting properties of object %d, gpflags: %d\n", object_id, gpflags); - - return describe_object_properties (object_id, FALSE, gpflags); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags) -{ - PRINT_DEBUG_MSG (2, "getting array values %d, startIdx: %d, count: %d, gpflags: 0x%x\n", object_id, start_idx, count, gpflags); - - return describe_array_values (object_id, start_idx, count, gpflags); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_invoke_getter_on_object (int object_id, const char* name) -{ - MonoObject *obj = get_object_from_id (object_id); - if (!obj) - return FALSE; - - return invoke_getter (obj, mono_object_class (obj), name); -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_set_value_on_object (int object_id, const char* name, const char* value) -{ - PRINT_DEBUG_MSG (1, "mono_wasm_set_value_on_object %d, name: %s, value: %s\n", object_id, name, value); - MonoObject *obj = get_object_from_id (object_id); - - if (!obj || !name) { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object: none of the arguments can be null"); - return FALSE; - } - MonoClass* klass = mono_object_class (obj); - - gpointer iter; -handle_parent: - iter = NULL; - MonoClassField *f; - while ((f = mono_class_get_fields_internal (klass, &iter))) { - if (!f->name || strcasecmp (f->name, name) != 0) - continue; - guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (f->type))); - - if (!decode_value(f->type, val_buf, value)) { - return FALSE; - } - DbgEngineErrorCode errorCode = mono_de_set_interp_var (f->type, (guint8*)obj + f->offset, val_buf); - if (errorCode != ERR_NONE) { - return FALSE; - } - return TRUE; - } - - iter = NULL; - MonoProperty *p; - MonoObject *exc; - ERROR_DECL (error); - while ((p = mono_class_get_properties (klass, &iter))) { - if (!p->name || strcasecmp (p->name, name) != 0) - continue; - if (!p->set) - break; - MonoType *type = mono_method_signature_internal (p->set)->params [0]; - guint8 *val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type_internal (type))); - - if (!decode_value(type, val_buf, value)) { - return FALSE; - } - mono_runtime_try_invoke (p->set, obj, (void **)&val_buf, &exc, error); - if (!is_ok (error) && exc == NULL) - exc = (MonoObject*) mono_error_convert_to_exception (error); - if (exc) { - char *error_message = mono_string_to_utf8_checked_internal (((MonoException *)exc)->message, error); - if (is_ok (error)) { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception: %s\n", error_message); - g_free (error_message); - mono_error_cleanup (error); - } - else { - PRINT_DEBUG_MSG (2, "mono_wasm_set_value_on_object exception\n"); - } - return FALSE; - } - return TRUE; - } - - if ((klass = m_class_get_parent(klass))) - goto handle_parent; - return FALSE; -} - -EMSCRIPTEN_KEEPALIVE gboolean -mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name) -{ - PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: v: %p klass: %p, name: %s\n", value, klass, name); - if (!klass || !value) - return FALSE; - - if (!m_class_is_valuetype (klass)) { - PRINT_DEBUG_MSG (2, "mono_wasm_invoke_getter_on_value: klass is not a valuetype. name: %s\n", mono_class_full_name (klass)); - return FALSE; - } - - return invoke_getter (value, klass, name); -} - EMSCRIPTEN_KEEPALIVE void mono_wasm_set_is_debugger_attached (gboolean is_attached) { @@ -1941,11 +376,58 @@ mono_wasm_set_is_debugger_attached (gboolean is_attached) } } -// Functions required by debugger-state-machine. -gsize -mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData) +EMSCRIPTEN_KEEPALIVE gboolean +mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size, int valtype, char* newvalue) { - return 1; + MdbgProtBuffer bufWithParms; + buffer_init (&bufWithParms, 128); + m_dbgprot_buffer_add_data (&bufWithParms, data, size); + if (!write_value_to_buffer(&bufWithParms, valtype, newvalue)) { + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); + }, 0, id, 0, 0); + return TRUE; + } + mono_wasm_send_dbg_command(id, command_set, command, bufWithParms.buf, m_dbgprot_buffer_len(&bufWithParms)); + buffer_free (&bufWithParms); + return TRUE; +} + +EMSCRIPTEN_KEEPALIVE gboolean +mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, guint8* data, unsigned int size) +{ + ss_calculate_framecount (NULL, NULL, TRUE, NULL, NULL); + MdbgProtBuffer buf; + buffer_init (&buf, 128); + gboolean no_reply; + MdbgProtErrorCode error = 0; + if (command_set == MDBGPROT_CMD_SET_VM && command == MDBGPROT_CMD_VM_INVOKE_METHOD ) + { + DebuggerTlsData* tls = mono_wasm_get_tls(); + InvokeData invoke_data; + memset(&invoke_data, 0, sizeof(InvokeData)); + invoke_data.endp = data + size; + error = mono_do_invoke_method(tls, &buf, &invoke_data, data, &data); + } + else + error = mono_process_dbg_packet(id, command_set, command, &no_reply, data, data + size, &buf); + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); + }, error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf); + + buffer_free (&buf); + return TRUE; +} + +static gboolean +receive_debugger_agent_message (void *data, int len) +{ + EM_ASM ({ + MONO.mono_wasm_add_dbg_command_received (1, -1, $0, $1); + }, data, len); + mono_wasm_save_thread_context(); + mono_wasm_fire_debugger_agent_message (); + return FALSE; } #else // HOST_WASM diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj index 2b058366276..15386dece90 100644 --- a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj +++ b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj @@ -1,7 +1,11 @@ + Debug true runtime.js + true + embedded + 1 diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 1f31e044653..81c419e4c58 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -317,6 +317,7 @@ namespace Microsoft.WebAssembly.Diagnostics public SourceId SourceId => source.SourceId; + public int DebuggerId { get; set; } public string Name { get; } public MethodDebugInformation DebugInformation; public MethodDefinitionHandle methodDefHandle; @@ -325,7 +326,7 @@ namespace Microsoft.WebAssembly.Diagnostics public SourceLocation EndLocation { get; } public AssemblyInfo Assembly { get; } public int Token { get; } - + public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type) { this.Assembly = assembly; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index efb2a7b4a2a..f39f2736caa 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -172,50 +172,33 @@ namespace Microsoft.WebAssembly.Diagnostics public MonoCommands(string expression) => this.expression = expression; - public static MonoCommands GetCallStack() => new MonoCommands("MONO.mono_wasm_get_call_stack()"); - public static MonoCommands GetExceptionObject() => new MonoCommands("MONO.mono_wasm_get_exception_object()"); + public static MonoCommands GetDebuggerAgentBufferReceived() => new MonoCommands("MONO.mono_wasm_get_dbg_command_info()"); + public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready"); - public static MonoCommands StartSingleStepping(StepKind kind) => new MonoCommands($"MONO.mono_wasm_start_single_stepping ({(int)kind})"); - public static MonoCommands GetLoadedFiles() => new MonoCommands("MONO.mono_wasm_get_loaded_files()"); - public static MonoCommands ClearAllBreakpoints() => new MonoCommands("MONO.mono_wasm_clear_all_breakpoints()"); - - public static MonoCommands GetDetails(DotnetObjectId objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ('{objectId}', {(args ?? "{ }")})"); - - public static MonoCommands GetScopeVariables(int scopeId, params VarInfo[] vars) + public static MonoCommands SendDebuggerAgentCommand(int id, int command_set, int command, string command_parameters) { - var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray(); - return new MonoCommands($"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(var_ids)})"); + return new MonoCommands($"MONO.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')"); } - public static MonoCommands SetVariableValue(int scopeId, int index, string name, string newValue) + public static MonoCommands SendDebuggerAgentCommandWithParms(int id, int command_set, int command, string command_parameters, int len, int type, string parm) { - return new MonoCommands($"MONO.mono_wasm_set_variable_value({scopeId}, {index}, '{name}', '{newValue}')"); + return new MonoCommands($"MONO.mono_wasm_send_dbg_command_with_parms ({id}, {command_set}, {command},'{command_parameters}', {len}, {type}, '{parm}')"); } - public static MonoCommands EvaluateMemberAccess(int scopeId, string expr, params VarInfo[] vars) - { - var var_ids = vars.Select(v => new { index = v.Index, name = v.Name }).ToArray(); - return new MonoCommands($"MONO.mono_wasm_eval_member_access({scopeId}, {JsonConvert.SerializeObject(var_ids)}, '', '{expr}')"); - } - - public static MonoCommands SetBreakpoint(string assemblyName, int methodToken, int ilOffset) => new MonoCommands($"MONO.mono_wasm_set_breakpoint (\"{assemblyName}\", {methodToken}, {ilOffset})"); - - public static MonoCommands RemoveBreakpoint(int breakpointId) => new MonoCommands($"MONO.mono_wasm_remove_breakpoint({breakpointId})"); - - public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')"); - public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString()})"); + public static MonoCommands GetDetails(int objectId, JToken args = null) => new MonoCommands($"MONO.mono_wasm_get_details ({objectId}, {(args ?? "{ }")})"); + public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()"); - public static MonoCommands SetPauseOnExceptions(string state) => new MonoCommands($"MONO.mono_wasm_set_pause_on_exceptions(\"{state}\")"); - public static MonoCommands DetachDebugger() => new MonoCommands($"MONO.mono_wasm_detach_debugger()"); + + public static MonoCommands ReleaseObject(DotnetObjectId objectId) => new MonoCommands($"MONO.mono_wasm_release_object('{objectId}')"); } internal enum MonoErrorCodes @@ -280,8 +263,8 @@ namespace Microsoft.WebAssembly.Diagnostics internal enum StepKind { Into, - Out, - Over + Over, + Out } internal class ExecutionContext @@ -292,6 +275,7 @@ namespace Microsoft.WebAssembly.Diagnostics public TaskCompletionSource ready; public bool IsRuntimeReady => ready != null && ready.Task.IsCompleted; + public int ThreadId { get; set; } public int Id { get; set; } public object AuxData { get; set; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index 21f8dabdb37..c4e578a0a2c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.IO; namespace Microsoft.WebAssembly.Diagnostics { @@ -18,7 +19,6 @@ namespace Microsoft.WebAssembly.Diagnostics private MonoProxy proxy; private ExecutionContext ctx; private PerScopeCache scopeCache; - private VarInfo[] varIds; private ILogger logger; private bool locals_fetched; @@ -31,44 +31,99 @@ namespace Microsoft.WebAssembly.Diagnostics this.logger = logger; scopeCache = ctx.GetCacheForScope(scope_id); } + public async Task GetValueFromObject(JToken objRet, CancellationToken token) + { + if (objRet["value"]?["className"]?.Value() == "System.Exception") + { + if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var exceptionObject = await proxy.sdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token); + var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("_message")); + exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value() + ": " + exceptionObjectMessage["value"]?["value"]?.Value(); + return exceptionObjectMessage["value"]?.Value(); + } + return objRet["value"]?.Value(); + } + if (objRet["value"]?.Value() != null) + return objRet["value"]?.Value(); + if (objRet["get"]?.Value() != null) + { + if (DotnetObjectId.TryParse(objRet?["get"]?["objectIdValue"]?.Value(), out DotnetObjectId objectId)) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteObj(objectId, proxy.sdbHelper); + var ret = await proxy.sdbHelper.InvokeMethod(sessionId, command_params.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token); + return await GetValueFromObject(ret, token); + } + + } + return null; + } // Checks Locals, followed by `this` public async Task Resolve(string var_name, CancellationToken token) { - if (scopeCache.Locals.Count == 0 && !locals_fetched) - { - Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token); - if (scope_res.IsErr) - throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}"); - locals_fetched = true; - } + string[] parts = var_name.Split("."); + JObject rootObject = null; - if (scopeCache.Locals.TryGetValue(var_name, out JObject obj)) - { - return obj["value"]?.Value(); - } - - if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) + if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) { return ret; - - if (varIds == null) - { - Frame scope = ctx.CallStack.FirstOrDefault(s => s.Id == scopeId); - varIds = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); } - - Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.EvaluateMemberAccess(scopeId, var_name, varIds), token); - if (res.IsOk) + foreach (string part in parts) { - ret = res.Value?["result"]?["value"]?["value"]?.Value(); - scopeCache.MemberReferences[var_name] = ret; - } - else - { - logger.LogDebug(res.Error.ToString()); - } + string partTrimmed = part.Trim(); + if (partTrimmed == "") + return null; + if (rootObject != null) + { + if (rootObject?["subtype"]?.Value() == "null") + return null; + if (DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + if (objRet == null) + return null; - return ret; + rootObject = await GetValueFromObject(objRet, token); + } + continue; + } + if (scopeCache.Locals.Count == 0 && !locals_fetched) + { + Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token); + if (scope_res.IsErr) + throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}"); + locals_fetched = true; + } + if (scopeCache.Locals.TryGetValue(partTrimmed, out JObject obj)) + { + rootObject = obj["value"]?.Value(); + } + else if (scopeCache.Locals.TryGetValue("this", out JObject objThis)) + { + if (partTrimmed == "this") + { + rootObject = objThis?["value"].Value(); + } + else if (DotnetObjectId.TryParse(objThis?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) + { + var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + if (objRet != null) + { + rootObject = await GetValueFromObject(objRet, token); + } + else + { + return null; + } + } + } + } + scopeCache.MemberReferences[var_name] = rootObject; + return rootObject; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index b80a4483ca0..689904740d3 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -18,6 +18,7 @@ namespace Microsoft.WebAssembly.Diagnostics { internal class MonoProxy : DevToolsProxy { + internal MonoSDBHelper sdbHelper; private IList urlSymbolServerList; private static HttpClient client = new HttpClient(); private HashSet sessions = new HashSet(); @@ -26,6 +27,7 @@ namespace Microsoft.WebAssembly.Diagnostics public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { this.urlSymbolServerList = urlSymbolServerList ?? new List(); + sdbHelper = new MonoSDBHelper(this); } internal ExecutionContext GetContext(SessionId sessionId) @@ -132,11 +134,15 @@ namespace Microsoft.WebAssembly.Diagnostics await SendCommand(sessionId, "Debugger.resume", new JObject(), token); return true; } - case "mono_wasm_fire_bp": - case "_mono_wasm_fire_bp": - case "_mono_wasm_fire_exception": + case "_mono_wasm_fire_debugger_agent_message": { - return await OnPause(sessionId, args, token); + try { + return await OnReceiveDebuggerAgentEvent(sessionId, args, token); + } + catch (Exception) //if the page is refreshed maybe it stops here. + { + return false; + } } } break; @@ -370,8 +376,12 @@ namespace Microsoft.WebAssembly.Diagnostics if (!DotnetObjectId.TryParse(args?["objectId"], out DotnetObjectId objectId)) break; - Result result = await RuntimeGetProperties(id, objectId, args, token); - SendResponse(id, result, token); + var ret = await RuntimeGetPropertiesInternal(id, objectId, args, token); + if (ret == null) { + SendResponse(id, Result.Err($"Unable to RuntimeGetProperties '{objectId}'"), token); + } + else + SendResponse(id, Result.OkFromObject(new { result = ret }), token); return true; } @@ -388,7 +398,7 @@ namespace Microsoft.WebAssembly.Diagnostics case "Debugger.setPauseOnExceptions": { string state = args["state"].Value(); - await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions(state), token); + await sdbHelper.EnableExceptions(id, state, token); // Pass this on to JS too return false; } @@ -403,8 +413,6 @@ namespace Microsoft.WebAssembly.Diagnostics } case "DotnetDebugger.getMethodLocation": { - Console.WriteLine("set-breakpoint-by-method: " + id + " " + args); - DebugStore store = await RuntimeReady(id, token); string aname = args["assemblyName"]?.Value(); string typeName = args["typeName"]?.Value(); @@ -460,31 +468,80 @@ namespace Microsoft.WebAssembly.Diagnostics } case "Runtime.callFunctionOn": { - if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) - return false; - - if (objectId.Scheme == "scope") - { + try { + return await CallOnFunction(id, args, token); + } + catch (Exception){ SendResponse(id, Result.Exception(new ArgumentException( - $"Runtime.callFunctionOn not supported with scope ({objectId}).")), + $"Runtime.callFunctionOn not supported with ({args["objectId"]}).")), token); return true; } - - Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); - JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type; - - if (res.IsOk && res_value_type == JTokenType.Object || res_value_type == JTokenType.Object) - res = Result.OkFromObject(new { result = res.Value["result"]["value"] }); - - SendResponse(id, res, token); - return true; } } return false; } + private async Task CallOnFunction(MessageId id, JObject args, CancellationToken token) + { + if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) { + return false; + } + switch (objectId.Scheme) + { + case "object": + args["details"] = await sdbHelper.GetObjectProxy(id, int.Parse(objectId.Value), token); + break; + case "valuetype": + args["details"] = await sdbHelper.GetValueTypeProxy(id, int.Parse(objectId.Value), token); + break; + case "pointer": + args["details"] = await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token); + break; + case "array": + args["details"] = await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + break; + case "cfo_res": + { + Result cfo_res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); + cfo_res = Result.OkFromObject(new { result = cfo_res.Value?["result"]?["value"]}); + SendResponse(id, cfo_res, token); + return true; + } + case "scope": + { + SendResponse(id, + Result.Exception(new ArgumentException( + $"Runtime.callFunctionOn not supported with scope ({objectId}).")), + token); + return true; + } + default: + return false; + } + Result res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token); + if (res.IsErr) + { + SendResponse(id, res, token); + return true; + } + if (res.Value?["result"]?["value"]?["type"] == null) //it means that is not a buffer returned from the debugger-agent + { + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + ret_debugger_cmd_reader.ReadByte(); //number of objects returned. + var obj = await sdbHelper.CreateJObjectForVariableValue(id, ret_debugger_cmd_reader, "ret", false, -1, token); + /*JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;*/ + res = Result.OkFromObject(new { result = obj["value"]}); + SendResponse(id, res, token); + return true; + } + res = Result.OkFromObject(new { result = res.Value?["result"]?["value"]}); + SendResponse(id, res, token); + return true; + } private async Task OnSetVariableValue(MessageId id, int scopeId, string varName, JToken varValue, CancellationToken token) { @@ -498,50 +555,59 @@ namespace Microsoft.WebAssembly.Diagnostics var varToSetValue = varIds.FirstOrDefault(v => v.Name == varName); if (varToSetValue == null) return false; - Result res = await SendMonoCommand(id, MonoCommands.SetVariableValue(scopeId, varToSetValue.Index, varName, varValue["value"].Value()), token); - if (res.IsOk) + var res = await sdbHelper.SetVariableValue(id, ctx.ThreadId, scopeId, varToSetValue.Index, varValue["value"].Value(), token); + if (res) SendResponse(id, Result.Ok(new JObject()), token); else SendResponse(id, Result.Err($"Unable to set '{varValue["value"].Value()}' to variable '{varName}'"), token); return true; } - private async Task RuntimeGetProperties(MessageId id, DotnetObjectId objectId, JToken args, CancellationToken token) + internal async Task RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token) { - if (objectId.Scheme == "scope") + var accessorPropertiesOnly = false; + var ownProperties = false; + if (args != null) { - return await GetScopeProperties(id, int.Parse(objectId.Value), token); + if (args["accessorPropertiesOnly"] != null) + accessorPropertiesOnly = args["accessorPropertiesOnly"].Value(); + if (args["ownProperties"] != null) + ownProperties = args["ownProperties"].Value(); } - - Result res = await SendMonoCommand(id, MonoCommands.GetDetails(objectId, args), token); - if (res.IsErr) - return res; - - if (objectId.Scheme == "cfo_res") - { - // Runtime.callFunctionOn result object - string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value(); - if (value_json_str != null) + //Console.WriteLine($"RuntimeGetProperties - {args}"); + try { + switch (objectId.Scheme) { - res = Result.OkFromObject(new + case "scope": { - result = JArray.Parse(value_json_str) - }); - } - else - { - res = Result.OkFromObject(new { result = new { } }); - } - } - else - { - res = Result.Ok(JObject.FromObject(new { result = res.Value["result"]["value"] })); - } + var res = await GetScopeProperties(id, int.Parse(objectId.Value), token); + return res.Value?["result"]; + } + case "valuetype": + return await sdbHelper.GetValueTypeValues(id, int.Parse(objectId.Value), accessorPropertiesOnly, token); + case "array": + return await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + case "object": + return await sdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token); + case "pointer": + return new JArray{await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)}; + case "cfo_res": + { + Result res = await SendMonoCommand(id, MonoCommands.GetDetails(int.Parse(objectId.Value), args), token); + string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value(); + return value_json_str != null ? JArray.Parse(value_json_str) : null; + } + default: + return null; - return res; + } + } + catch (Exception) { + return null; + } } - private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, JObject mono_frame, Breakpoint bp, CancellationToken token) + private async Task EvaluateCondition(SessionId sessionId, ExecutionContext context, Frame mono_frame, Breakpoint bp, CancellationToken token) { if (string.IsNullOrEmpty(bp?.Condition) || mono_frame == null) return true; @@ -551,8 +617,7 @@ namespace Microsoft.WebAssembly.Diagnostics if (bp.ConditionAlreadyEvaluatedWithError) return false; try { - var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame["frame_id"].Value(), logger); - + var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame.Id, logger); JObject retValue = await resolver.Resolve(condition, token); if (retValue == null) retValue = await EvaluateExpression.CompileAndRunTheExpression(condition, resolver, token); @@ -573,172 +638,124 @@ namespace Microsoft.WebAssembly.Diagnostics } return false; } - - private async Task OnPause(SessionId sessionId, JObject args, CancellationToken token) + private async Task SendCallStack(SessionId sessionId, ExecutionContext context, string reason, int thread_id, Breakpoint bp, JObject data, IEnumerable orig_callframes, CancellationToken token) { - //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime - Result res = await SendMonoCommand(sessionId, MonoCommands.GetCallStack(), token); - IEnumerable orig_callframes = args?["callFrames"]?.Values(); - ExecutionContext context = GetContext(sessionId); - JObject data = null; - string reason = "other";//other means breakpoint - - if (res.IsErr) - { - //Give up and send the original call stack - return false; - } - - //step one, figure out where did we hit - JToken res_value = res.Value?["result"]?["value"]; - if (res_value == null || res_value is JValue) - { - //Give up and send the original call stack - return false; - } - - Log("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}"); - int? bp_id = res_value?["breakpoint_id"]?.Value(); - Log("verbose", $"We just hit bp {bp_id}"); - if (!bp_id.HasValue) - { - //Give up and send the original call stack - return false; - } - - Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == bp_id.Value); - var callFrames = new List(); - foreach (JObject frame in orig_callframes) - { - string function_name = frame["functionName"]?.Value(); - string url = frame["url"]?.Value(); - if ("mono_wasm_fire_bp" == function_name || "_mono_wasm_fire_bp" == function_name || - "_mono_wasm_fire_exception" == function_name) + var frames = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(thread_id); + command_params_writer.Write(0); + command_params_writer.Write(-1); + var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdThread.GetFrameInfo, command_params, token); + var frame_count = ret_debugger_cmd_reader.ReadInt32(); + //Console.WriteLine("frame_count - " + frame_count); + for (int j = 0; j < frame_count; j++) { + var frame_id = ret_debugger_cmd_reader.ReadInt32(); + var method_id = ret_debugger_cmd_reader.ReadInt32(); + var il_pos = ret_debugger_cmd_reader.ReadInt32(); + var flags = ret_debugger_cmd_reader.ReadByte(); + var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); + var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); + var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + if (asm == null) { - if ("_mono_wasm_fire_exception" == function_name) + assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly + asm = store.GetAssemblyByName(assembly_name); + if (asm == null) { - Result exception_obj_id = await SendMonoCommand(sessionId, MonoCommands.GetExceptionObject(), token); - JToken res_val = exception_obj_id.Value?["result"]?["value"]; - var exception_dotnet_obj_id = new DotnetObjectId("object", res_val?["exception_id"]?.Value()); - data = JObject.FromObject(new - { - type = "object", - subtype = "error", - className = res_val?["class_name"]?.Value(), - uncaught = res_val?["uncaught"]?.Value(), - description = res_val?["message"]?.Value() + "\n", - objectId = exception_dotnet_obj_id.ToString() - }); - reason = "exception"; + Log("debug", $"Unable to find assembly: {assembly_name}"); + continue; } + } - var frames = new List(); - IEnumerable the_mono_frames = res.Value?["result"]?["value"]?["frames"]?.Values(); + MethodInfo method = asm.GetMethodByToken(method_token); - foreach (JObject mono_frame in the_mono_frames) + if (method == null && !asm.HasSymbols) + { + try { - int frame_id = mono_frame["frame_id"].Value(); - int il_pos = mono_frame["il_pos"].Value(); - int method_token = mono_frame["method_token"].Value(); - string assembly_name = mono_frame["assembly_name"].Value(); + method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token); + } + catch (Exception e) + { + Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}"); + continue; + } + } - // This can be different than `method.Name`, like in case of generic methods - string method_name = mono_frame["method_name"]?.Value(); + if (method == null) + { + Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}"); + continue; + } - DebugStore store = await LoadStore(sessionId, token); - AssemblyInfo asm = store.GetAssemblyByName(assembly_name); - if (asm == null) + method.DebuggerId = method_id; + + SourceLocation location = method?.GetLocationByIl(il_pos); + + // When hitting a breakpoint on the "IncrementCount" method in the standard + // Blazor project template, one of the stack frames is inside mscorlib.dll + // and we get location==null for it. It will trigger a NullReferenceException + // if we don't skip over that stack frame. + if (location == null) + { + continue; + } + + Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); + Log("debug", $"\tmethod {method_name} location: {location}"); + frames.Add(new Frame(method, location, frame_id)); + + callFrames.Add(new + { + functionName = method_name, + callFrameId = $"dotnet:scope:{frame_id}", + functionLocation = method.StartLocation.AsLocation(), + + location = location.AsLocation(), + + url = store.ToUrl(location), + + scopeChain = new[] { - Log("debug", $"Unable to find assembly: {assembly_name}"); - continue; - } - - MethodInfo method = asm.GetMethodByToken(method_token); - - if (method == null && !asm.HasSymbols) - { - try + new { - method = await LoadSymbolsOnDemand(asm, method_token, sessionId, token); - } - catch (Exception e) - { - Log("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name} exception: {e}"); - continue; - } - } - - if (method == null) - { - Log("debug", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}"); - continue; - } - - SourceLocation location = method?.GetLocationByIl(il_pos); - - // When hitting a breakpoint on the "IncrementCount" method in the standard - // Blazor project template, one of the stack frames is inside mscorlib.dll - // and we get location==null for it. It will trigger a NullReferenceException - // if we don't skip over that stack frame. - if (location == null) - { - continue; - } - - Log("debug", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); - Log("debug", $"\tmethod {method_name} location: {location}"); - frames.Add(new Frame(method, location, frame_id)); - - callFrames.Add(new - { - functionName = method_name, - callFrameId = $"dotnet:scope:{frame_id}", - functionLocation = method.StartLocation.AsLocation(), - - location = location.AsLocation(), - - url = store.ToUrl(location), - - scopeChain = new[] - { - new + type = "local", + @object = new { - type = "local", - @object = new - { - @type = "object", - className = "Object", - description = "Object", - objectId = $"dotnet:scope:{frame_id}", - }, - name = method_name, - startLocation = method.StartLocation.AsLocation(), - endLocation = method.EndLocation.AsLocation(), - } - } - }); + @type = "object", + className = "Object", + description = "Object", + objectId = $"dotnet:scope:{frame_id}", + }, + name = method_name, + startLocation = method.StartLocation.AsLocation(), + endLocation = method.EndLocation.AsLocation(), + } + } + }); - context.CallStack = frames; - - } - if (!await EvaluateCondition(sessionId, context, the_mono_frames?.First(), bp, token)) - { - await SendCommand(sessionId, "Debugger.resume", new JObject(), token); - return true; - } - } - else if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) || - url.StartsWith("wasm://wasm/", StringComparison.Ordinal))) - { - callFrames.Add(frame); - } + context.CallStack = frames; + context.ThreadId = thread_id; } - string[] bp_list = new string[bp == null ? 0 : 1]; if (bp != null) bp_list[0] = bp.StackId; + foreach (JObject frame in orig_callframes) + { + string function_name = frame["functionName"]?.Value(); + string url = frame["url"]?.Value(); + if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) || + url.StartsWith("wasm://wasm/", StringComparison.Ordinal) || function_name == "_mono_wasm_fire_debugger_agent_message")) + { + callFrames.Add(frame); + } + } var o = JObject.FromObject(new { callFrames, @@ -746,10 +763,72 @@ namespace Microsoft.WebAssembly.Diagnostics data, hitBreakpoints = bp_list, }); - + if (!await EvaluateCondition(sessionId, context, context.CallStack.First(), bp, token)) + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return true; + } SendEvent(sessionId, "Debugger.paused", o, token); + return true; } + private async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObject args, CancellationToken token) + { + Result res = await SendMonoCommand(sessionId, MonoCommands.GetDebuggerAgentBufferReceived(), token); + if (res.IsErr) + return false; + + ExecutionContext context = GetContext(sessionId); + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + ret_debugger_cmd_reader.ReadBytes(11); //skip HEADER_LEN + ret_debugger_cmd_reader.ReadByte(); //suspend_policy + var number_of_events = ret_debugger_cmd_reader.ReadInt32(); //number of events -> should be always one + for (int i = 0 ; i < number_of_events; i++) { + var event_kind = (EventKind)ret_debugger_cmd_reader.ReadByte(); //event kind + var request_id = ret_debugger_cmd_reader.ReadInt32(); //request id + if (event_kind == EventKind.Step) + await sdbHelper.ClearSingleStep(sessionId, request_id, token); + int thread_id = ret_debugger_cmd_reader.ReadInt32(); + switch (event_kind) + { + case EventKind.Exception: + { + string reason = "exception"; + int object_id = ret_debugger_cmd_reader.ReadInt32(); + var caught = ret_debugger_cmd_reader.ReadByte(); + var exceptionObject = await sdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token); + var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("message")); + var data = JObject.FromObject(new + { + type = "object", + subtype = "error", + className = await sdbHelper.GetClassNameFromObject(sessionId, object_id, token), + uncaught = caught == 0, + description = exceptionObjectMessage["value"]["value"].Value(), + objectId = $"dotnet:object:{object_id}" + }); + + var ret = await SendCallStack(sessionId, context, reason, thread_id, null, data, args?["callFrames"]?.Values(), token); + return ret; + } + case EventKind.UserBreak: + case EventKind.Step: + case EventKind.Breakpoint: + { + Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id); + string reason = "other";//other means breakpoint + int method_id = 0; + if (event_kind != EventKind.UserBreak) + method_id = ret_debugger_cmd_reader.ReadInt32(); + var ret = await SendCallStack(sessionId, context, reason, thread_id, bp, null, args?["callFrames"]?.Values(), token); + return ret; + } + } + } + return false; + } private async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method_token, SessionId sessionId, CancellationToken token) { @@ -826,6 +905,7 @@ namespace Microsoft.WebAssembly.Diagnostics } //discard managed frames + sdbHelper.ClearCache(); GetContext(msg_id).ClearState(); } @@ -838,12 +918,9 @@ namespace Microsoft.WebAssembly.Diagnostics if (context.CallStack.Count <= 1 && kind == StepKind.Out) return false; - Result res = await SendMonoCommand(msg_id, MonoCommands.StartSingleStepping(kind), token); - - int? ret_code = res.Value?["result"]?["value"]?.Value(); - - if (ret_code.HasValue && ret_code.Value == 0) - { + var step = await sdbHelper.Step(msg_id, context.ThreadId, kind, token); + if (step == false) { + sdbHelper.ClearCache(); context.ClearState(); await SendCommand(msg_id, "Debugger.stepOut", new JObject(), token); return false; @@ -971,24 +1048,21 @@ namespace Microsoft.WebAssembly.Diagnostics return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scope_id}" })); VarInfo[] var_ids = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); - Result res = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token); - //if we fail we just buble that to the IDE (and let it panic over it) - if (res.IsErr) - return res; - - JObject[] values = res.Value?["result"]?["value"]?.Values().ToArray(); - - if (values == null || values.Length == 0) - return Result.OkFromObject(new { result = Array.Empty() }); - - PerScopeCache frameCache = ctx.GetCacheForScope(scope_id); - foreach (JObject value in values) + var values = await sdbHelper.StackFrameGetValues(msg_id, scope.Method, ctx.ThreadId, scope_id, var_ids, token); + if (values != null) { - frameCache.Locals[value["name"]?.Value()] = value; - } + if (values == null || values.Count == 0) + return Result.OkFromObject(new { result = Array.Empty() }); - return Result.OkFromObject(new { result = values }); + PerScopeCache frameCache = ctx.GetCacheForScope(scope_id); + foreach (JObject value in values) + { + frameCache.Locals[value["name"]?.Value()] = value; + } + return Result.OkFromObject(new { result = values }); + } + return Result.OkFromObject(new { result = Array.Empty() }); } catch (Exception exception) { @@ -1004,16 +1078,16 @@ namespace Microsoft.WebAssembly.Diagnostics int method_token = bp.Location.CliLocation.Method.Token; int il_offset = bp.Location.CliLocation.Offset; - Result res = await SendMonoCommand(sessionId, MonoCommands.SetBreakpoint(asm_name, method_token, il_offset), token); - int? ret_code = res.Value?["result"]?["value"]?.Value(); + var assembly_id = await sdbHelper.GetAssemblyId(sessionId, asm_name, token); + var method_id = await sdbHelper.GetMethodIdByToken(sessionId, assembly_id, method_token, token); + var breakpoint_id = await sdbHelper.SetBreakpoint(sessionId, method_id, il_offset, token); - if (ret_code.HasValue) + if (breakpoint_id > 0) { - bp.RemoteId = ret_code.Value; + bp.RemoteId = breakpoint_id; bp.State = BreakpointState.Active; //Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}"); } - return bp; } @@ -1021,7 +1095,6 @@ namespace Microsoft.WebAssembly.Diagnostics { JObject scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData)); Log("debug", $"sending {source.Url} {context.Id} {sessionId.sessionId}"); - SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token); foreach (var req in context.BreakpointRequests.Values) @@ -1033,7 +1106,7 @@ namespace Microsoft.WebAssembly.Diagnostics } } - private async Task LoadStore(SessionId sessionId, CancellationToken token) + internal async Task LoadStore(SessionId sessionId, CancellationToken token) { ExecutionContext context = GetContext(sessionId); @@ -1072,12 +1145,16 @@ namespace Microsoft.WebAssembly.Diagnostics if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource(), null) != null) return await context.ready.Task; - Result clear_result = await SendMonoCommand(sessionId, MonoCommands.ClearAllBreakpoints(), token); - if (clear_result.IsErr) + var command_params = new MemoryStream(); + var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdEventRequest.ClearAllBreakpoints, command_params, token); + if (ret_debugger_cmd_reader == null) { - Log("verbose", $"Failed to clear breakpoints due to {clear_result}"); + Log("verbose", $"Failed to clear breakpoints"); } + await sdbHelper.SetProtocolVersion(sessionId, token); + await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); + DebugStore store = await LoadStore(sessionId, token); context.ready.SetResult(store); @@ -1095,10 +1172,8 @@ namespace Microsoft.WebAssembly.Diagnostics foreach (Breakpoint bp in breakpointRequest.Locations) { - Result res = await SendMonoCommand(msg_id, MonoCommands.RemoveBreakpoint(bp.RemoteId), token); - int? ret_code = res.Value?["result"]?["value"]?.Value(); - - if (ret_code.HasValue) + var breakpoint_removed = await sdbHelper.RemoveBreakpoint(msg_id, bp.RemoteId, token); + if (breakpoint_removed) { bp.RemoteId = -1; bp.State = BreakpointState.Disabled; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs new file mode 100644 index 00000000000..c296c850147 --- /dev/null +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -0,0 +1,1779 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal enum TokenType + { + MdtModule = 0x00000000, // + MdtTypeRef = 0x01000000, // + MdtTypeDef = 0x02000000, // + MdtFieldDef = 0x04000000, // + MdtMethodDef = 0x06000000, // + MdtParamDef = 0x08000000, // + MdtInterfaceImpl = 0x09000000, // + MdtMemberRef = 0x0a000000, // + MdtCustomAttribute = 0x0c000000, // + MdtPermission = 0x0e000000, // + MdtSignature = 0x11000000, // + MdtEvent = 0x14000000, // + MdtProperty = 0x17000000, // + MdtModuleRef = 0x1a000000, // + MdtTypeSpec = 0x1b000000, // + MdtAssembly = 0x20000000, // + MdtAssemblyRef = 0x23000000, // + MdtFile = 0x26000000, // + MdtExportedType = 0x27000000, // + MdtManifestResource = 0x28000000, // + MdtGenericParam = 0x2a000000, // + MdtMethodSpec = 0x2b000000, // + MdtGenericParamConstraint = 0x2c000000, + MdtString = 0x70000000, // + MdtName = 0x71000000, // + MdtBaseType = 0x72000000, // Leave this on the high end value. This does not correspond to metadata table + } + + internal enum CommandSet { + Vm = 1, + ObjectRef = 9, + StringRef = 10, + Thread = 11, + ArrayRef = 13, + EventRequest = 15, + StackFrame = 16, + AppDomain = 20, + Assembly = 21, + Method = 22, + Type = 23, + Module = 24, + Field = 25, + Event = 64, + Pointer = 65 + } + + internal enum EventKind { + VmStart = 0, + VmDeath = 1, + ThreadStart = 2, + ThreadDeath = 3, + AppDomainCreate = 4, + AppDomainUnload = 5, + MethodEntry = 6, + MethodExit = 7, + AssemblyLoad = 8, + AssemblyUnload = 9, + Breakpoint = 10, + Step = 11, + TypeLoad = 12, + Exception = 13, + KeepAlive = 14, + UserBreak = 15, + UserLog = 16, + Crash = 17 + } + + internal enum ModifierKind { + Count = 1, + ThreadOnly = 3, + LocationOnly = 7, + ExceptionOnly = 8, + Step = 10, + AssemblyOnly = 11, + SourceFileOnly = 12, + TypeNameOnly = 13 + } + + + internal enum SuspendPolicy { + None = 0, + EventThread = 1, + All = 2 + } + + internal enum CmdVM { + Version = 1, + AllThreads = 2, + Suspend = 3, + Resume = 4, + Exit = 5, + Dispose = 6, + InvokeMethod = 7, + SetProtocolVersion = 8, + AbortInvoke = 9, + SetKeepAlive = 10, + GetTypesForSourceFile = 11, + GetTypes = 12, + InvokeMethods = 13, + StartBuffering = 14, + StopBuffering = 15, + VmReadMemory = 16, + VmWriteMemory = 17, + GetAssemblyByName = 18 + } + + internal enum CmdFrame { + GetValues = 1, + GetThis = 2, + SetValues = 3, + GetDomain = 4, + SetThis = 5, + GetArgument = 6, + GetArguments = 7 + } + + internal enum CmdEvent { + Composite = 100 + } + + internal enum CmdThread { + GetFrameInfo = 1, + GetName = 2, + GetState = 3, + GetInfo = 4, + /* FIXME: Merge into GetInfo when the major protocol version is increased */ + GetId = 5, + /* Ditto */ + GetTid = 6, + SetIp = 7, + GetElapsedTime = 8 + } + + internal enum CmdEventRequest { + Set = 1, + Clear = 2, + ClearAllBreakpoints = 3 + } + + internal enum CmdAppDomain { + GetRootDomain = 1, + GetFriendlyName = 2, + GetAssemblies = 3, + GetEntryAssembly = 4, + CreateString = 5, + GetCorLib = 6, + CreateBoxedValue = 7, + CreateByteArray = 8, + } + + internal enum CmdAssembly { + GetLocation = 1, + GetEntryPoint = 2, + GetManifestModule = 3, + GetObject = 4, + GetType = 5, + GetName = 6, + GetDomain = 7, + GetMetadataBlob = 8, + GetIsDynamic = 9, + GetPdbBlob = 10, + GetTypeFromToken = 11, + GetMethodFromToken = 12, + HasDebugInfo = 13, + } + + internal enum CmdModule { + GetInfo = 1, + ApplyChanges = 2, + } + + internal enum CmdPointer{ + GetValue = 1 + } + + internal enum CmdMethod { + GetName = 1, + GetDeclaringType = 2, + GetDebugInfo = 3, + GetParamInfo = 4, + GetLocalsInfo = 5, + GetInfo = 6, + GetBody = 7, + ResolveToken = 8, + GetCattrs = 9, + MakeGenericMethod = 10, + Token = 11, + Assembly = 12, + ClassToken = 13, + AsyncDebugInfo = 14, + GetNameFull = 15 + } + + internal enum CmdType { + GetInfo = 1, + GetMethods = 2, + GetFields = 3, + GetValues = 4, + GetObject = 5, + GetSourceFiles = 6, + SetValues = 7, + IsAssignableFrom = 8, + GetProperties = 9, + GetCattrs = 10, + GetFieldCattrs = 11, + GetPropertyCattrs = 12, + /* FIXME: Merge into GetSourceFiles when the major protocol version is increased */ + GetSourceFiles2 = 13, + /* FIXME: Merge into GetValues when the major protocol version is increased */ + GetValues2 = 14, + GetMethodsByNameFlags = 15, + GetInterfaces = 16, + GetInterfacesMap = 17, + IsInitialized = 18, + CreateInstance = 19, + GetValueSize = 20, + GetValuesICorDbg = 21, + GetParents = 22 + } + + internal enum CmdArray { + GetLength = 1, + GetValues = 2, + SetValues = 3, + RefGetType = 4 + } + + + internal enum CmdField { + GetInfo = 1 + } + + internal enum CmdString { + GetValue = 1, + GetLength = 2, + GetChars = 3 + } + + internal enum CmdObject { + RefGetType = 1, + RefGetValues = 2, + RefIsCollected = 3, + RefGetAddress = 4, + RefGetDomain = 5, + RefSetValues = 6, + RefGetInfo = 7, + GetValuesICorDbg = 8, + RefDelegateGetMethod = 9, + RefIsDelegate = 10 + } + + internal enum ElementType { + End = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + I1 = 0x04, + U1 = 0x05, + I2 = 0x06, + U2 = 0x07, + I4 = 0x08, + U4 = 0x09, + I8 = 0x0a, + U8 = 0x0b, + R4 = 0x0c, + R8 = 0x0d, + String = 0x0e, + Ptr = 0x0f, + ByRef = 0x10, + ValueType = 0x11, + Class = 0x12, + Var = 0x13, + Array = 0x14, + GenericInst = 0x15, + TypedByRef = 0x16, + I = 0x18, + U = 0x19, + FnPtr = 0x1b, + Object = 0x1c, + SzArray = 0x1d, + MVar = 0x1e, + CModReqD = 0x1f, + CModOpt = 0x20, + Internal = 0x21, + Modifier = 0x40, + Sentinel = 0x41, + Pinned = 0x45, + + Type = 0x50, + Boxed = 0x51, + Enum = 0x55 + } + + internal enum ValueTypeId { + Null = 0xf0, + Type = 0xf1, + VType = 0xf2, + FixedArray = 0xf3 + } + internal enum MonoTypeNameFormat{ + FormatIL, + FormatReflection, + FullName, + AssemblyQualified + } + + internal enum StepFilter { + None = 0, + StaticCtor = 1, + DebuggerHidden = 2, + DebuggerStepThrough = 4, + DebuggerNonUserCode = 8 + } + + internal class MonoBinaryReader : BinaryReader + { + public MonoBinaryReader(Stream stream) : base(stream) {} + + internal static unsafe void PutBytesBE (byte *dest, byte *src, int count) + { + int i = 0; + + if (BitConverter.IsLittleEndian){ + dest += count; + for (; i < count; i++) + *(--dest) = *src++; + } else { + for (; i < count; i++) + *dest++ = *src++; + } + } + + public override string ReadString() + { + var valueLen = ReadInt32(); + char[] value = new char[valueLen]; + Read(value, 0, valueLen); + return new string(value); + } + public unsafe long ReadLong() + { + byte[] data = new byte[8]; + Read(data, 0, 8); + + long ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 8); + } + + return ret; + } + public override unsafe sbyte ReadSByte() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (sbyte)ret; + } + + public unsafe byte ReadUByte() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (byte)ret; + } + + public override unsafe int ReadInt32() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + int ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return ret; + } + + public override unsafe double ReadDouble() + { + byte[] data = new byte[8]; + Read(data, 0, 8); + + double ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 8); + } + return ret; + } + + public override unsafe uint ReadUInt32() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + uint ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return ret; + } + public unsafe ushort ReadUShort() + { + byte[] data = new byte[4]; + Read(data, 0, 4); + + uint ret; + fixed (byte *src = &data[0]){ + PutBytesBE ((byte *) &ret, src, 4); + } + return (ushort)ret; + } + } + + internal class MonoBinaryWriter : BinaryWriter + { + public MonoBinaryWriter(Stream stream) : base(stream) {} + public void WriteString(string val) + { + Write(val.Length); + Write(val.ToCharArray()); + } + public void WriteLong(long val) + { + Write((int)((val >> 32) & 0xffffffff)); + Write((int)((val >> 0) & 0xffffffff)); + } + public override void Write(int val) + { + byte[] bytes = BitConverter.GetBytes(val); + Array.Reverse(bytes, 0, bytes.Length); + Write(bytes); + } + public void WriteObj(DotnetObjectId objectId, MonoSDBHelper sdbHelper) + { + if (objectId.Scheme == "object") + { + Write((byte)ElementType.Class); + Write(int.Parse(objectId.Value)); + } + if (objectId.Scheme == "valuetype") + { + Write(sdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer); + } + } + } + internal class FieldTypeClass + { + public int Id { get; } + public string Name { get; } + public int TypeId { get; } + public FieldTypeClass(int id, string name, int typeId) + { + Id = id; + Name = name; + TypeId = typeId; + } + } + internal class ValueTypeClass + { + public byte[] valueTypeBuffer; + public JArray valueTypeJson; + public JArray valueTypeJsonProps; + public int typeId; + public JArray valueTypeProxy; + public string valueTypeVarName; + public bool valueTypeAutoExpand; + public int Id; + public ValueTypeClass(string varName, byte[] buffer, JArray json, int id, bool expand_properties, int valueTypeId) + { + valueTypeBuffer = buffer; + valueTypeJson = json; + typeId = id; + valueTypeJsonProps = null; + valueTypeProxy = null; + valueTypeVarName = varName; + valueTypeAutoExpand = expand_properties; + Id = valueTypeId; + } + } + internal class PointerValue + { + public long address; + public int typeId; + public string varName; + public PointerValue(long address, int typeId, string varName) + { + this.address = address; + this.typeId = typeId; + this.varName = varName; + } + + } + internal class MonoSDBHelper + { + internal Dictionary valueTypes = new Dictionary(); + internal Dictionary pointerValues = new Dictionary(); + private static int debugger_object_id; + private static int cmd_id; + private static int GetId() {return cmd_id++;} + private MonoProxy proxy; + private static int MINOR_VERSION = 61; + private static int MAJOR_VERSION = 2; + public MonoSDBHelper(MonoProxy proxy) + { + this.proxy = proxy; + } + + public void ClearCache() + { + valueTypes = new Dictionary(); + pointerValues = new Dictionary(); + } + + public async Task SetProtocolVersion(SessionId sessionId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(MAJOR_VERSION); + command_params_writer.Write(MINOR_VERSION); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); + return true; + } + public async Task EnableReceiveUserBreakRequest(SessionId sessionId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.UserBreak); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)0); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return true; + } + internal async Task SendDebuggerAgentCommandInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, CancellationToken token) + { + Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommand(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray())), token); + if (res.IsErr) { + throw new Exception($"SendDebuggerAgentCommand Error - {(CommandSet)command_set} - {command}"); + } + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + return ret_debugger_cmd_reader; + } + + internal CommandSet GetCommandSetForCommand(T command) => + command switch { + CmdVM => CommandSet.Vm, + CmdObject => CommandSet.ObjectRef, + CmdString => CommandSet.StringRef, + CmdThread => CommandSet.Thread, + CmdArray => CommandSet.ArrayRef, + CmdEventRequest => CommandSet.EventRequest, + CmdFrame => CommandSet.StackFrame, + CmdAppDomain => CommandSet.AppDomain, + CmdAssembly => CommandSet.Assembly, + CmdMethod => CommandSet.Method, + CmdType => CommandSet.Type, + CmdModule => CommandSet.Module, + CmdField => CommandSet.Field, + CmdEvent => CommandSet.Event, + CmdPointer => CommandSet.Pointer, + _ => throw new Exception ("Unknown CommandSet") + }; + + internal Task SendDebuggerAgentCommand(SessionId sessionId, T command, MemoryStream parms, CancellationToken token) where T : Enum => + SendDebuggerAgentCommandInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, token); + + internal Task SendDebuggerAgentCommandWithParms(SessionId sessionId, T command, MemoryStream parms, int type, string extraParm, CancellationToken token) where T : Enum => + SendDebuggerAgentCommandWithParmsInternal(sessionId, (int)GetCommandSetForCommand(command), (int)(object)command, parms, type, extraParm, token); + + internal async Task SendDebuggerAgentCommandWithParmsInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, int type, string extraParm, CancellationToken token) + { + Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommandWithParms(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray()), parms.ToArray().Length, type, extraParm), token); + if (res.IsErr) { + throw new Exception("SendDebuggerAgentCommandWithParms Error"); + } + byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); + var ret_debugger_cmd = new MemoryStream(newBytes); + var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); + return ret_debugger_cmd_reader; + } + + public async Task GetMethodToken(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Token, command_params, token); + return ret_debugger_cmd_reader.ReadInt32() & 0xffffff; //token + } + + public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, int method_token, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + command_params_writer.Write(method_token | (int)TokenType.MdtMethodDef); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetMethodFromToken, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetAssemblyIdFromMethod(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Assembly, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); //assembly_id + } + + public async Task GetAssemblyId(SessionId sessionId, string asm_name, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteString(asm_name); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetAssemblyByName, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetLocation, command_params, token); + return ret_debugger_cmd_reader.ReadString(); + } + + + public async Task GetAssemblyNameFull(SessionId sessionId, int assembly_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(assembly_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, command_params, token); + var name = ret_debugger_cmd_reader.ReadString(); + return name.Remove(name.IndexOf(",")) + ".dll"; + } + + public async Task GetMethodName(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetNameFull, command_params, token); + var methodName = ret_debugger_cmd_reader.ReadString(); + return methodName.Substring(methodName.IndexOf(":")+1); + } + + public async Task MethodIsStatic(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetInfo, command_params, token); + var flags = ret_debugger_cmd_reader.ReadInt32(); + return (flags & 0x0010) > 0; //check method is static + } + + public async Task GetParamCount(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + int param_count = ret_debugger_cmd_reader.ReadInt32(); + return param_count; + } + + public async Task GetReturnType(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + var retType = ret_debugger_cmd_reader.ReadInt32(); + var ret = await GetTypeName(sessionId, retType, token); + return ret; + } + + public async Task GetParameters(SessionId sessionId, int method_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(method_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); + ret_debugger_cmd_reader.ReadInt32(); + var paramCount = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); + var retType = ret_debugger_cmd_reader.ReadInt32(); + var parameters = "("; + for (int i = 0 ; i < paramCount; i++) + { + var paramType = ret_debugger_cmd_reader.ReadInt32(); + parameters += await GetTypeName(sessionId, paramType, token); + parameters = parameters.Replace("System.Func", "Func"); + if (i + 1 < paramCount) + parameters += ","; + } + parameters += ")"; + return parameters; + } + + public async Task SetBreakpoint(SessionId sessionId, int method_id, long il_offset, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Breakpoint); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.LocationOnly); + command_params_writer.Write(method_id); + command_params_writer.WriteLong(il_offset); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task RemoveBreakpoint(SessionId sessionId, int breakpoint_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Breakpoint); + command_params_writer.Write((int) breakpoint_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + + if (ret_debugger_cmd_reader != null) + return true; + return false; + } + + public async Task Step(SessionId sessionId, int thread_id, StepKind kind, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Step); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.Step); + command_params_writer.Write(thread_id); + command_params_writer.Write((int)0); + command_params_writer.Write((int)kind); + command_params_writer.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + if (ret_debugger_cmd_reader == null) + return false; + var isBPOnManagedCode = ret_debugger_cmd_reader.ReadInt32(); + if (isBPOnManagedCode == 0) + return false; + return true; + } + + public async Task ClearSingleStep(SessionId sessionId, int req_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Step); + command_params_writer.Write((int) req_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + + if (ret_debugger_cmd_reader != null) + return true; + return false; + } + + public async Task> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetFields, command_params, token); + var nFields = ret_debugger_cmd_reader.ReadInt32(); + + for (int i = 0 ; i < nFields; i++) + { + int fieldId = ret_debugger_cmd_reader.ReadInt32(); //fieldId + string fieldNameStr = ret_debugger_cmd_reader.ReadString(); + int typeId = ret_debugger_cmd_reader.ReadInt32(); //typeId + ret_debugger_cmd_reader.ReadInt32(); //attrs + if (fieldNameStr.Contains("k__BackingField")) + { + fieldNameStr = fieldNameStr.Replace("k__BackingField", ""); + fieldNameStr = fieldNameStr.Replace("<", ""); + fieldNameStr = fieldNameStr.Replace(">", ""); + } + ret.Add(new FieldTypeClass(fieldId, fieldNameStr, typeId)); + } + return ret; + } + public string ReplaceCommonClassNames(string className) + { + className = className.Replace("System.String", "string"); + className = className.Replace("System.Boolean", "bool"); + className = className.Replace("System.Char", "char"); + className = className.Replace("System.Int32", "int"); + className = className.Replace("System.Object", "object"); + className = className.Replace("System.Void", "void"); + className = className.Replace("System.Byte", "byte"); + return className; + } + public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + command_params_writer.Write((int) MonoTypeNameFormat.FormatReflection); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, command_params, token); + + ret_debugger_cmd_reader.ReadString(); + + ret_debugger_cmd_reader.ReadString(); + + string className = ret_debugger_cmd_reader.ReadString(); + + className = className.Replace("+", "."); + className = Regex.Replace(className, @"`\d+", ""); + className = className.Replace("[]", "__SQUARED_BRACKETS__"); + className = className.Replace("[", "<"); + className = className.Replace("]", ">"); + className = className.Replace("__SQUARED_BRACKETS__", "[]"); + className = className.Replace(",", ", "); + className = ReplaceCommonClassNames(className); + return className; + } + + public async Task GetStringValue(SessionId sessionId, int string_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(string_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdString.GetValue, command_params, token); + var isUtf16 = ret_debugger_cmd_reader.ReadByte(); + if (isUtf16 == 0) { + return ret_debugger_cmd_reader.ReadString(); + } + return null; + } + public async Task GetArrayLength(SessionId sessionId, int object_id, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(object_id); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetLength, command_params, token); + var length = ret_debugger_cmd_reader.ReadInt32(); + length = ret_debugger_cmd_reader.ReadInt32(); + return length; + } + public async Task> GetTypeIdFromObject(SessionId sessionId, int object_id, bool withParents, CancellationToken token) + { + List ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(object_id); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetType, command_params, token); + var type_id = ret_debugger_cmd_reader.ReadInt32(); + ret.Add(type_id); + if (withParents) + { + command_params = new MemoryStream(); + command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(type_id); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); + var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < parentsCount; i++) + { + ret.Add(ret_debugger_cmd_reader.ReadInt32()); + } + } + return ret; + } + + public async Task GetClassNameFromObject(SessionId sessionId, int object_id, CancellationToken token) + { + var type_id = await GetTypeIdFromObject(sessionId, object_id, false, token); + return await GetTypeName(sessionId, type_id[0], token); + } + + public async Task GetMethodIdByName(SessionId sessionId, int type_id, string method_name, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)type_id); + command_params_writer.WriteString(method_name); + command_params_writer.Write((int)(0x10 | 4)); //instance methods + command_params_writer.Write((int)1); //case sensitive + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, command_params, token); + var nMethods = ret_debugger_cmd_reader.ReadInt32(); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task IsDelegate(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)objectId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefIsDelegate, command_params, token); + return ret_debugger_cmd_reader.ReadByte() == 1; + } + + public async Task GetDelegateMethod(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((int)objectId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefDelegateGetMethod, command_params, token); + return ret_debugger_cmd_reader.ReadInt32(); + } + + public async Task GetDelegateMethodDescription(SessionId sessionId, int objectId, CancellationToken token) + { + var methodId = await GetDelegateMethod(sessionId, objectId, token); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(methodId); + //Console.WriteLine("methodId - " + methodId); + if (methodId == 0) + return ""; + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetName, command_params, token); + var methodName = ret_debugger_cmd_reader.ReadString(); + + var returnType = await GetReturnType(sessionId, methodId, token); + var parameters = await GetParameters(sessionId, methodId, token); + + return $"{returnType} {methodName} {parameters}"; + } + public async Task InvokeMethod(SessionId sessionId, byte[] valueTypeBuffer, int method_id, string varName, CancellationToken token) + { + MemoryStream parms = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(parms); + command_params_writer.Write(method_id); + command_params_writer.Write(valueTypeBuffer); + command_params_writer.Write(0); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.InvokeMethod, parms, token); + ret_debugger_cmd_reader.ReadByte(); //number of objects returned. + return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, varName, false, -1, token); + } + public async Task CreateJArrayForProperties(SessionId sessionId, int typeId, byte[] object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token) + { + JArray ret = new JArray(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); //setmethod + var attrs = ret_debugger_cmd_reader.ReadInt32(); //attrs + if (getMethodId == 0 || await GetParamCount(sessionId, getMethodId, token) != 0 || await MethodIsStatic(sessionId, getMethodId, token)) + continue; + JObject propRet = null; + if (attributes.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any()) + continue; + if (isAutoExpandable) + { + try { + propRet = await InvokeMethod(sessionId, object_buffer, getMethodId, propertyNameStr, token); + } + catch (Exception) + { + continue; + } + } + else + { + propRet = JObject.FromObject(new { + get = new + { + type = "function", + objectId = $"{objectId}:method_id:{getMethodId}", + className = "Function", + description = "get " + propertyNameStr + " ()", + methodId = getMethodId, + objectIdValue = objectId + }, + name = propertyNameStr + }); + } + if (isOwn) + propRet["isOwn"] = true; + ret.Add(propRet); + } + return ret; + } + public async Task GetPointerContent(SessionId sessionId, int pointerId, CancellationToken token) + { + var ret = new List(); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.WriteLong(pointerValues[pointerId].address); + command_params_writer.Write(pointerValues[pointerId].typeId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdPointer.GetValue, command_params, token); + var varName = pointerValues[pointerId].varName; + if (int.TryParse(varName, out _)) + varName = $"[{varName}]"; + return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "*" + varName, false, -1, token); + } + public async Task GetPropertiesValuesOfValueType(SessionId sessionId, int valueTypeId, CancellationToken token) + { + JArray ret = new JArray(); + var valueType = valueTypes[valueTypeId]; + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(valueType.typeId); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); + var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + List typesToGetProperties = new List(); + typesToGetProperties.Add(valueType.typeId); + for (int i = 0 ; i < parentsCount; i++) + { + typesToGetProperties.Add(ret_debugger_cmd_reader.ReadInt32()); + } + for (int i = 0 ; i < typesToGetProperties.Count; i++) + { + var properties = await CreateJArrayForProperties(sessionId, typesToGetProperties[i], valueType.valueTypeBuffer, valueType.valueTypeJson, valueType.valueTypeAutoExpand, $"dotnet:valuetype:{valueType.Id}", i == 0, token); + ret = new JArray(ret.Union(properties)); + } + + return ret; + } + + public bool AutoExpandable(string className) { + if (className == "System.DateTime" || + className == "System.DateTimeOffset" || + className == "System.TimeSpan") + return true; + return false; + } + + public bool AutoInvokeToString(string className) { + if (className == "System.DateTime" || + className == "System.DateTimeOffset" || + className == "System.TimeSpan" || + className == "System.Decimal" || + className == "System.Guid") + return true; + return false; + } + + public JObject CreateJObject(T value, string type, string description, bool writable, string className = null, string objectId = null, string __custom_type = null, string subtype = null, bool isValueType = false, bool expanded = false, bool isEnum = false) + { + var ret = JObject.FromObject(new { + value = new + { + type, + value, + description + }, + writable + }); + if (__custom_type != null) + ret["value"]["__custom_type"] = __custom_type; + if (className != null) + ret["value"]["className"] = className; + if (objectId != null) + ret["value"]["objectId"] = objectId; + if (subtype != null) + ret["value"]["subtype"] = subtype; + if (isValueType) + ret["value"]["isValueType"] = isValueType; + if (expanded) + ret["value"]["expanded"] = expanded; + if (isEnum) + ret["value"]["isEnum"] = isEnum; + return ret; + + } + public JObject CreateJObjectForBoolean(int value) + { + return CreateJObject(value == 0 ? false : true, "boolean", value == 0 ? "false" : "true", true); + } + + public JObject CreateJObjectForNumber(T value) + { + return CreateJObject(value, "number", value.ToString(), true); + } + + public JObject CreateJObjectForChar(int value) + { + var description = $"{value.ToString()} '{Convert.ToChar(value)}'"; + return CreateJObject(description, "symbol", description, true); + } + + public async Task CreateJObjectForPtr(SessionId sessionId, ElementType etype, MonoBinaryReader ret_debugger_cmd_reader, string name, CancellationToken token) + { + string type; + string value; + long valueAddress = ret_debugger_cmd_reader.ReadLong(); + var typeId = ret_debugger_cmd_reader.ReadInt32(); + var className = ""; + if (etype == ElementType.FnPtr) + className = "(*())"; //to keep the old behavior + else + className = "(" + await GetTypeName(sessionId, typeId, token) + ")"; + + int pointerId = 0; + if (valueAddress != 0 && className != "(void*)") + { + pointerId = Interlocked.Increment(ref debugger_object_id); + type = "object"; + value = className; + pointerValues[pointerId] = new PointerValue(valueAddress, typeId, name); + } + else + { + type = "symbol"; + value = className + " " + valueAddress; + } + return CreateJObject(value, type, value, false, className, $"dotnet:pointer:{pointerId}", "pointer"); + } + + public async Task CreateJObjectForString(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var string_id = ret_debugger_cmd_reader.ReadInt32(); + var value = await GetStringValue(sessionId, string_id, token); + return CreateJObject(value, "string", value, false); + } + + public async Task CreateJObjectForArray(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var value = await GetClassNameFromObject(sessionId, objectId, token); + var length = await GetArrayLength(sessionId, objectId, token); + return CreateJObject(null, "object", $"{value.ToString()}({length})", false, value.ToString(), "dotnet:array:" + objectId, null, "array"); + } + + public async Task CreateJObjectForObject(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, int typeIdFromAttribute, CancellationToken token) + { + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var className = ""; + var type_id = await GetTypeIdFromObject(sessionId, objectId, false, token); + className = await GetTypeName(sessionId, type_id[0], token); + var description = className.ToString(); + if (await IsDelegate(sessionId, objectId, token)) + { + if (typeIdFromAttribute != -1) + { + className = await GetTypeName(sessionId, typeIdFromAttribute, token); + } + + description = await GetDelegateMethodDescription(sessionId, objectId, token); + if (description == "") + { + return CreateJObject(className.ToString(), "symbol", className.ToString(), false); + } + } + return CreateJObject(null, "object", description, false, className, $"dotnet:object:{objectId}"); + } + + public async Task CreateJObjectForValueType(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, long initialPos, CancellationToken token) + { + JObject fieldValueType = null; + var isEnum = ret_debugger_cmd_reader.ReadByte(); + var isBoxed = ret_debugger_cmd_reader.ReadByte() == 1; + var typeId = ret_debugger_cmd_reader.ReadInt32(); + var className = await GetTypeName(sessionId, typeId, token); + var description = className; + var numFields = ret_debugger_cmd_reader.ReadInt32(); + var fields = await GetTypeFields(sessionId, typeId, token); + JArray valueTypeFields = new JArray(); + if (className.IndexOf("System.Nullable<") == 0) //should we call something on debugger-agent to check??? + { + ret_debugger_cmd_reader.ReadByte(); //ignoring the boolean type + var isNull = ret_debugger_cmd_reader.ReadInt32(); + var value = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, name, false, -1, token); + if (isNull != 0) + return value; + else + return CreateJObject(null, "object", className, false, className, null, null, "null", true); + } + for (int i = 0; i < numFields ; i++) + { + fieldValueType = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, fields.ElementAt(i).Name, true, fields.ElementAt(i).TypeId, token); + valueTypeFields.Add(fieldValueType); + } + + long endPos = ret_debugger_cmd_reader.BaseStream.Position; + var valueTypeId = Interlocked.Increment(ref debugger_object_id); + + ret_debugger_cmd_reader.BaseStream.Position = initialPos; + byte[] valueTypeBuffer = new byte[endPos - initialPos]; + ret_debugger_cmd_reader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos)); + ret_debugger_cmd_reader.BaseStream.Position = endPos; + valueTypes[valueTypeId] = new ValueTypeClass(name, valueTypeBuffer, valueTypeFields, typeId, AutoExpandable(className), valueTypeId); + if (AutoInvokeToString(className) || isEnum == 1) { + int method_id = await GetMethodIdByName(sessionId, typeId, "ToString", token); + var retMethod = await InvokeMethod(sessionId, valueTypeBuffer, method_id, "methodRet", token); + description = retMethod["value"]?["value"].Value(); + if (className.Equals("System.Guid")) + description = description.ToUpper(); //to keep the old behavior + } + else if (isBoxed && numFields == 1) { + return fieldValueType; + } + return CreateJObject(null, "object", description, false, className, $"dotnet:valuetype:{valueTypeId}", null, null, true, true, isEnum == 1); + } + + public async Task CreateJObjectForNull(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + string className = ""; + ElementType variableType = (ElementType)ret_debugger_cmd_reader.ReadByte(); + switch (variableType) + { + case ElementType.String: + case ElementType.Class: + { + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + + } + case ElementType.SzArray: + case ElementType.Array: + { + ElementType byte_type = (ElementType)ret_debugger_cmd_reader.ReadByte(); + var rank = ret_debugger_cmd_reader.ReadInt32(); + if (byte_type == ElementType.Class) { + var internal_type_id = ret_debugger_cmd_reader.ReadInt32(); + } + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + } + default: + { + var type_id = ret_debugger_cmd_reader.ReadInt32(); + className = await GetTypeName(sessionId, type_id, token); + break; + } + } + return CreateJObject(null, "object", className, false, className, null, null, "null"); + } + + public async Task CreateJObjectForVariableValue(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, bool isOwn, int typeIdFromAttribute, CancellationToken token) + { + long initialPos = ret_debugger_cmd_reader == null ? 0 : ret_debugger_cmd_reader.BaseStream.Position; + ElementType etype = (ElementType)ret_debugger_cmd_reader.ReadByte(); + JObject ret = null; + switch (etype) { + case ElementType.I: + case ElementType.U: + case ElementType.Void: + case (ElementType)ValueTypeId.Type: + case (ElementType)ValueTypeId.VType: + case (ElementType)ValueTypeId.FixedArray: + ret = new JObject{{"Type", "void"}}; + break; + case ElementType.Boolean: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForBoolean(value); + break; + } + case ElementType.I1: + { + var value = ret_debugger_cmd_reader.ReadSByte(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.I2: + case ElementType.I4: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U1: + { + var value = ret_debugger_cmd_reader.ReadUByte(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U2: + { + var value = ret_debugger_cmd_reader.ReadUShort(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U4: + { + var value = ret_debugger_cmd_reader.ReadUInt32(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.R4: + { + float value = BitConverter.Int32BitsToSingle(ret_debugger_cmd_reader.ReadInt32()); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.Char: + { + var value = ret_debugger_cmd_reader.ReadInt32(); + ret = CreateJObjectForChar(value); + break; + } + case ElementType.I8: + { + long value = ret_debugger_cmd_reader.ReadLong(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.U8: + { + ulong high = (ulong) ret_debugger_cmd_reader.ReadInt32(); + ulong low = (ulong) ret_debugger_cmd_reader.ReadInt32(); + var value = ((high << 32) | low); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.R8: + { + double value = ret_debugger_cmd_reader.ReadDouble(); + ret = CreateJObjectForNumber(value); + break; + } + case ElementType.FnPtr: + case ElementType.Ptr: + { + ret = await CreateJObjectForPtr(sessionId, etype, ret_debugger_cmd_reader, name, token); + break; + } + case ElementType.String: + { + ret = await CreateJObjectForString(sessionId, ret_debugger_cmd_reader, token); + break; + } + case ElementType.SzArray: + case ElementType.Array: + { + ret = await CreateJObjectForArray(sessionId, ret_debugger_cmd_reader, token); + break; + } + case ElementType.Class: + case ElementType.Object: + { + ret = await CreateJObjectForObject(sessionId, ret_debugger_cmd_reader, typeIdFromAttribute, token); + break; + } + case ElementType.ValueType: + { + ret = await CreateJObjectForValueType(sessionId, ret_debugger_cmd_reader, name, initialPos, token); + break; + } + case (ElementType)ValueTypeId.Null: + { + ret = await CreateJObjectForNull(sessionId, ret_debugger_cmd_reader, token); + break; + } + } + if (isOwn) + ret["isOwn"] = true; + ret["name"] = name; + return ret; + } + + public async Task IsAsyncMethod(SessionId sessionId, int methodId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(methodId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.AsyncDebugInfo, command_params, token); + return ret_debugger_cmd_reader.ReadByte() == 1 ; //token + } + + public async Task StackFrameGetValues(SessionId sessionId, MethodInfo method, int thread_id, int frame_id, VarInfo[] var_ids, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + MonoBinaryReader ret_debugger_cmd_reader = null; + command_params_writer.Write(thread_id); + command_params_writer.Write(frame_id); + command_params_writer.Write(var_ids.Length); + foreach (var var in var_ids) + { + command_params_writer.Write(var.Index); + } + + if (await IsAsyncMethod(sessionId, method.DebuggerId, token)) + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); + ret_debugger_cmd_reader.ReadByte(); //ignore type + var objectId = ret_debugger_cmd_reader.ReadInt32(); + var asyncLocals = await GetObjectValues(sessionId, objectId, true, false, false, false, token); + asyncLocals = new JArray(asyncLocals.Where( asyncLocal => !asyncLocal["name"].Value().Contains("<>") || asyncLocal["name"].Value().EndsWith("__this"))); + foreach (var asyncLocal in asyncLocals) + { + if (asyncLocal["name"].Value().EndsWith("__this")) + asyncLocal["name"] = "this"; + else if (asyncLocal["name"].Value().Contains("<")) + asyncLocal["name"] = Regex.Match(asyncLocal["name"].Value(), @"\<([^)]*)\>").Groups[1].Value; + } + return asyncLocals; + } + + JArray locals = new JArray(); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); + foreach (var var in var_ids) + { + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, var.Name, false, -1, token); + locals.Add(var_json); + } + if (!method.IsStatic()) + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "this", false, -1, token); + var_json.Add("fieldOffset", -1); + locals.Add(var_json); + } + return locals; + + } + + public async Task GetValueTypeValues(SessionId sessionId, int valueTypeId, bool accessorPropertiesOnly, CancellationToken token) + { + if (valueTypes[valueTypeId].valueTypeJsonProps == null) + { + valueTypes[valueTypeId].valueTypeJsonProps = await GetPropertiesValuesOfValueType(sessionId, valueTypeId, token); + } + if (accessorPropertiesOnly) + return valueTypes[valueTypeId].valueTypeJsonProps; + var ret = new JArray(valueTypes[valueTypeId].valueTypeJson.Union(valueTypes[valueTypeId].valueTypeJsonProps)); + return ret; + } + + public async Task GetValueTypeProxy(SessionId sessionId, int valueTypeId, CancellationToken token) + { + if (valueTypes[valueTypeId].valueTypeProxy != null) + return valueTypes[valueTypeId].valueTypeProxy; + valueTypes[valueTypeId].valueTypeProxy = new JArray(valueTypes[valueTypeId].valueTypeJson); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(valueTypes[valueTypeId].typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + ret_debugger_cmd_reader.ReadInt32(); //setmethod + ret_debugger_cmd_reader.ReadInt32(); //attrs + if (await MethodIsStatic(sessionId, getMethodId, token)) + continue; + var command_params_to_proxy = new MemoryStream(); + var command_params_writer_to_proxy = new MonoBinaryWriter(command_params_to_proxy); + command_params_writer_to_proxy.Write(getMethodId); + command_params_writer_to_proxy.Write(valueTypes[valueTypeId].valueTypeBuffer); + command_params_writer_to_proxy.Write(0); + valueTypes[valueTypeId].valueTypeProxy.Add(JObject.FromObject(new { + get = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_proxy.ToArray()), + length = command_params_to_proxy.ToArray().Length + }), + name = propertyNameStr + })); + } + return valueTypes[valueTypeId].valueTypeProxy; + } + + public async Task GetArrayValues(SessionId sessionId, int arrayId, CancellationToken token) + { + var length = await GetArrayLength(sessionId, arrayId, token); + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(arrayId); + command_params_writer.Write(0); + command_params_writer.Write(length); + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetValues, command_params, token); + JArray array = new JArray(); + for (int i = 0 ; i < length ; i++) + { + var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, i.ToString(), false, -1, token); + array.Add(var_json); + } + return array; + } + public async Task EnableExceptions(SessionId sessionId, string state, CancellationToken token) + { + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write((byte)EventKind.Exception); + command_params_writer.Write((byte)SuspendPolicy.None); + command_params_writer.Write((byte)1); + command_params_writer.Write((byte)ModifierKind.ExceptionOnly); + command_params_writer.Write(0); //exc_class + if (state == "all") + command_params_writer.Write((byte)1); //caught + else + command_params_writer.Write((byte)0); //caught + if (state == "uncaught" || state == "all") + command_params_writer.Write((byte)1); //uncaught + else + command_params_writer.Write((byte)0); //uncaught + command_params_writer.Write((byte)1);//subclasses + command_params_writer.Write((byte)0);//not_filtered_feature + command_params_writer.Write((byte)0);//everything_else + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + return true; + } + public async Task GetObjectValues(SessionId sessionId, int objectId, bool withProperties, bool withSetter, bool accessorPropertiesOnly, bool ownProperties, CancellationToken token) + { + var typeId = await GetTypeIdFromObject(sessionId, objectId, true, token); + var className = await GetTypeName(sessionId, typeId[0], token); + JArray ret = new JArray(); + if (await IsDelegate(sessionId, objectId, token)) + { + var description = await GetDelegateMethodDescription(sessionId, objectId, token); + + var obj = JObject.FromObject(new { + value = new + { + type = "symbol", + value = description, + description + }, + name = "Target" + }); + ret.Add(obj); + return ret; + } + for (int i = 0; i < typeId.Count; i++) + { + if (!accessorPropertiesOnly) + { + var fields = await GetTypeFields(sessionId, typeId[i], token); + JArray objectFields = new JArray(); + + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(objectId); + command_params_writer.Write(fields.Count); + foreach (var field in fields) + { + command_params_writer.Write(field.Id); + } + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetValues, command_params, token); + + foreach (var field in fields) + { + long initialPos = ret_debugger_cmd_reader.BaseStream.Position; + int valtype = ret_debugger_cmd_reader.ReadByte(); + ret_debugger_cmd_reader.BaseStream.Position = initialPos; + var fieldValue = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, field.Name, i == 0, field.TypeId, token); + + if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { + continue; + } + if (withSetter) + { + var command_params_to_set = new MemoryStream(); + var command_params_writer_to_set = new MonoBinaryWriter(command_params_to_set); + command_params_writer_to_set.Write(objectId); + command_params_writer_to_set.Write(1); + command_params_writer_to_set.Write(field.Id); + + fieldValue.Add("set", JObject.FromObject(new { + commandSet = CommandSet.ObjectRef, + command = CmdObject.RefSetValues, + buffer = Convert.ToBase64String(command_params_to_set.ToArray()), + valtype, + length = command_params_to_set.ToArray().Length + })); + } + objectFields.Add(fieldValue); + } + ret = new JArray(ret.Union(objectFields)); + } + if (!withProperties) + return ret; + var command_params_obj = new MemoryStream(); + var command_params_obj_writer = new MonoBinaryWriter(command_params_obj); + command_params_obj_writer.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); + var props = await CreateJArrayForProperties(sessionId, typeId[i], command_params_obj.ToArray(), ret, false, $"dotnet:object:{objectId}", i == 0, token); + ret = new JArray(ret.Union(props)); + + // ownProperties + // Note: ownProperties should mean that we return members of the klass itself, + // but we are going to ignore that here, because otherwise vscode/chrome don't + // seem to ask for inherited fields at all. + //if (ownProperties) + //break; + /*if (accessorPropertiesOnly) + break;*/ + } + if (accessorPropertiesOnly) + { + var retAfterRemove = new JArray(); + List> allFields = new List>(); + for (int i = 0; i < typeId.Count; i++) + { + var fields = await GetTypeFields(sessionId, typeId[i], token); + allFields.Add(fields); + } + foreach (var item in ret) + { + bool foundField = false; + for (int j = 0 ; j < allFields.Count; j++) + { + foreach (var field in allFields[j]) + { + if (field.Name.Equals(item["name"].Value())) { + if (item["isOwn"] == null || (item["isOwn"].Value() && j == 0) || !item["isOwn"].Value()) + foundField = true; + break; + } + } + if (foundField) + break; + } + if (!foundField) { + retAfterRemove.Add(item); + } + } + ret = retAfterRemove; + } + return ret; + } + + public async Task GetObjectProxy(SessionId sessionId, int objectId, CancellationToken token) + { + var ret = await GetObjectValues(sessionId, objectId, false, true, false, false, token); + var typeIds = await GetTypeIdFromObject(sessionId, objectId, true, token); + foreach (var typeId in typeIds) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(typeId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); + var nProperties = ret_debugger_cmd_reader.ReadInt32(); + for (int i = 0 ; i < nProperties; i++) + { + ret_debugger_cmd_reader.ReadInt32(); //propertyId + string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + var getMethodId = ret_debugger_cmd_reader.ReadInt32(); + var setMethodId = ret_debugger_cmd_reader.ReadInt32(); //setmethod + var attrValue = ret_debugger_cmd_reader.ReadInt32(); //attrs + //Console.WriteLine($"{propertyNameStr} - {attrValue}"); + if (ret.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any()) + { + var attr = ret.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).First(); + + var command_params_to_set = new MemoryStream(); + var command_params_writer_to_set = new MonoBinaryWriter(command_params_to_set); + command_params_writer_to_set.Write(setMethodId); + command_params_writer_to_set.Write((byte)ElementType.Class); + command_params_writer_to_set.Write(objectId); + command_params_writer_to_set.Write(1); + if (attr["set"] != null) + { + attr["set"] = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_set.ToArray()), + valtype = attr["set"]["valtype"], + length = command_params_to_set.ToArray().Length + }); + } + continue; + } + else + { + var command_params_to_get = new MemoryStream(); + var command_params_writer_to_get = new MonoBinaryWriter(command_params_to_get); + command_params_writer_to_get.Write(getMethodId); + command_params_writer_to_get.Write((byte)ElementType.Class); + command_params_writer_to_get.Write(objectId); + command_params_writer_to_get.Write(0); + + ret.Add(JObject.FromObject(new { + get = JObject.FromObject(new { + commandSet = CommandSet.Vm, + command = CmdVM.InvokeMethod, + buffer = Convert.ToBase64String(command_params_to_get.ToArray()), + length = command_params_to_get.ToArray().Length + }), + name = propertyNameStr + })); + } + if (await MethodIsStatic(sessionId, getMethodId, token)) + continue; + } + } + return ret; + } + + public async Task SetVariableValue(SessionId sessionId, int thread_id, int frame_id, int varId, string newValue, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + MonoBinaryReader ret_debugger_cmd_reader = null; + command_params_writer.Write(thread_id); + command_params_writer.Write(frame_id); + command_params_writer.Write(1); + command_params_writer.Write(varId); + JArray locals = new JArray(); + ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); + int etype = ret_debugger_cmd_reader.ReadByte(); + try + { + ret_debugger_cmd_reader = await SendDebuggerAgentCommandWithParms(sessionId, CmdFrame.SetValues, command_params, etype, newValue, token); + } + catch (Exception) + { + return false; + } + + return true; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs index 00f917c8dc5..1afa97939d5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs @@ -33,7 +33,7 @@ namespace DebuggerTests { "MONO_TYPE_U2", TNumber(0), TNumber(1) }, { "MONO_TYPE_U4", TNumber(0), TNumber(1) }, { "MONO_TYPE_U8", TNumber(0), TNumber(1) }, - { "MONO_TYPE_R4", TNumber(0), TNumber("3.1414999961853027") }, + { "MONO_TYPE_R4", TNumber(0), TNumber("3.1415") }, //this is also the value that we see if we debug using VS { "MONO_TYPE_R8", TNumber(0), TNumber("3.1415") }, }; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 9b419838120..3d9106959c8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -706,7 +706,6 @@ namespace DebuggerTests var exp_val_str = jp.Value.Value(); bool null_or_empty_exp_val = String.IsNullOrEmpty(exp_val_str); - var actual_field_val = actual_val?.Values()?.FirstOrDefault(a_jp => a_jp.Name == jp.Name); var actual_field_val_str = actual_field_val?.Value?.Value(); if (null_or_empty_exp_val && String.IsNullOrEmpty(actual_field_val_str)) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index e2b796062e3..4054c6a2cea 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -13,85 +13,6 @@ namespace DebuggerTests { public class MonoJsTests : DebuggerTestBase { - [Fact] - public async Task FixupNameValueObjectsWithMissingParts() - { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - - var names = new JObject[] - { - JObject.FromObject(new { name = "Abc" }), - JObject.FromObject(new { name = "Def" }), - JObject.FromObject(new { name = "Xyz" }) - }; - - var values = new JObject[] - { - JObject.FromObject(new { value = TObject("testclass") }), - JObject.FromObject(new { value = TString("test string") }), - }; - - var getters = new JObject[] - { - GetterRes("xyz"), - GetterRes("unattached") - }; - - var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); - Assert.True(res.IsOk); - - await CheckProps(res.Value["result"]["value"], new - { - Abc = TSymbol(""), - Def = TObject("testclass"), - Xyz = TGetter("xyz") - }, "#1", num_fields: 4); - - JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values().ToArray()[3]); - - static JObject GetterRes(string name) => JObject.FromObject(new - { - get = new - { - className = "Function", - description = $"get {name} () {{}}", - type = "function" - } - }); - } - - [Fact] - public async Task GetParamsAndLocalsWithInvalidIndices() - { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", - null, -1, -1, "IntAdd"); - - var scope_id = pause_location["callFrames"][0]["callFrameId"].Value(); - var scope = int.Parse(scope_id.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[2]); - - var var_ids = new[] - { - new { index = 0, name = "one" }, - new { index = -12, name = "bad0" }, - new { index = 1231, name = "bad1" } - }; - - var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; - - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); - Assert.True(res.IsOk); - - await CheckProps(res.Value["result"]?["value"], new - { - one = TNumber(3), - bad0 = TSymbol(""), - bad1 = TSymbol("") - }, "results"); - } - [Fact] public async Task InvalidScopeId() { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index a671ed2b7c1..468ca4d628d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -641,43 +641,6 @@ namespace DebuggerTests }); - [Fact] - public async Task InvalidValueTypeData() - { - await CheckInspectLocalsAtBreakpointSite( - "dotnet://debugger-test.dll/debugger-test.cs", 85, 8, - "OuterMethod", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] Math:OuterMethod'); })", - wait_for_event_fn: async (pause_location) => - { - var new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3, value64: 4 }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - - new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3 }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - - new_id = await CreateNewId(@"MONO._new_or_add_id_props ({ scheme: 'valuetype', idArgs: { containerId: 1 }, props: { klass: 3, value64: 'AA' }});"); - await _invoke_getter(new_id, "NonExistant", expect_ok: false); - }); - - async Task CreateNewId(string expr) - { - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); - Assert.True(res.IsOk, "Expected Runtime.evaluate to succeed"); - AssertEqual("string", res.Value["result"]?["type"]?.Value(), "Expected Runtime.evaluate to return a string type result"); - return res.Value["result"]?["value"]?.Value(); - } - - async Task _invoke_getter(string obj_id, string property_name, bool expect_ok) - { - var expr = $"MONO._invoke_getter ('{obj_id}', '{property_name}')"; - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); - AssertEqual(expect_ok, res.IsOk, "Runtime.evaluate result not as expected for {expr}"); - - return res; - } - } - [Fact] public async Task MulticastDelegateTest() => await CheckInspectLocalsAtBreakpointSite( "MulticastDelegateTestClass", "Test", 5, "Test", @@ -746,7 +709,7 @@ namespace DebuggerTests await CheckProps(frame_locals, new { - mi = TObject("System.Reflection.MethodInfo"), + mi = TObject("System.Reflection.RuntimeMethodInfo"), //this is what is returned when debugging desktop apps using VS dt = TDateTime(new DateTime(4210, 3, 4, 5, 6, 7)), i = TNumber(4), strings = TArray("string[]", 1), diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index e9cb8cd2b40..84fb5684abd 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -565,484 +565,52 @@ var MonoSupportLib = { }, }, - mono_wasm_get_exception_object: function() { - var exception_obj = MONO.active_exception; - MONO.active_exception = null; - return exception_obj ; - }, - - mono_wasm_get_call_stack: function() { - if (!this.mono_wasm_current_bp_id) - this.mono_wasm_current_bp_id = Module.cwrap ("mono_wasm_current_bp_id", 'number'); - if (!this.mono_wasm_enum_frames) - this.mono_wasm_enum_frames = Module.cwrap ("mono_wasm_enum_frames", null); - - var bp_id = this.mono_wasm_current_bp_id (); - this.active_frames = []; - this.mono_wasm_enum_frames (); - - var the_frames = this.active_frames; - this.active_frames = []; - return { - "breakpoint_id": bp_id, - "frames": the_frames, - }; - }, - - _fixup_name_value_objects: function (var_list) { - let out_list = []; - - var i = 0; - while (i < var_list.length) { - let o = var_list [i]; - const this_has_name = o.name !== undefined; - let next_has_value_or_get_set = false; - - if (i + 1 < var_list.length) { - const next = var_list [i+1]; - next_has_value_or_get_set = next.value !== undefined || next.get !== undefined || next.set !== undefined; + mono_wasm_add_dbg_command_received: function(res_ok, id, buffer, buffer_len) { + const assembly_data = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len); + const base64String = MONO._base64Converter.toBase64StringImpl(assembly_data); + const buffer_obj = { + res_ok, + res: { + id, + value: base64String } - - if (!this_has_name) { - // insert the object as-is - // Eg. in case of locals, the names are added - // later - i ++; - } else if (next_has_value_or_get_set) { - // found a {name} followed by a {value/get} - o = Object.assign (o, var_list [i + 1]); - i += 2; - } else { - // missing value/get, so add a placeholder one - o.value = { - type: "symbol", - value: "", - description: "" - }; - i ++; - } - - out_list.push (o); } - - return out_list; + MONO.commands_received = buffer_obj; }, - _filter_automatic_properties: function (props, accessors_only=false) { - // Note: members in @props, have derived class members, followed by - // those from parent classes - - // Note: Auto-properties have backing fields, named with a special suffix. - // @props here will have the backing field, *and* the getter. - // - // But we want to return only one name/value pair: - // [name of the auto-property] = value of the backing field - - let getters = {}; - let all_fields_except_backing_fields = {}; - let backing_fields = {}; - - // Split props into the 3 groups - backing_fields, getters, and all_fields_except_backing_fields - props.forEach(p => { - if (p.name === undefined) { - console.debug(`Bug: Found a member with no name. Skipping it. p: ${JSON.stringify(p)}`); - return; - } - - if (p.name.endsWith('k__BackingField')) { - const auto_prop_name = p.name.replace ('k__BackingField', '') - .replace ('<', '') - .replace ('>', ''); - - // Only take the first one, as that is overriding others - if (!(auto_prop_name in backing_fields)) - backing_fields[auto_prop_name] = Object.assign(p, { name: auto_prop_name }); - - } else if (p.get !== undefined) { - // if p wasn't overridden by a getter or a field, - // from a more derived class - if (!(p.name in getters) && !(p.name in all_fields_except_backing_fields)) - getters[p.name] = p; - - } else if (!(p.name in all_fields_except_backing_fields)) { - all_fields_except_backing_fields[p.name] = p; - } - }); - - // Filter/merge backing fields, and getters - Object.values(backing_fields).forEach(backing_field => { - const auto_prop_name = backing_field.name; - const getter = getters[auto_prop_name]; - - if (getter === undefined) { - // backing field with no getter - // eg. when a field overrides/`new string foo=..` - // an autoproperty - return; - } - - if (auto_prop_name in all_fields_except_backing_fields) { - delete getters[auto_prop_name]; - } else if (getter.__args.owner_class === backing_field.__args.owner_class) { - // getter+backing_field are from the same class. - // Add the backing_field value as a field - all_fields_except_backing_fields[auto_prop_name] = backing_field; - - // .. and drop the auto-prop getter - delete getters[auto_prop_name]; - } - }); - - if (accessors_only) - return Object.values(getters); - - return Object.values(all_fields_except_backing_fields).concat(Object.values(getters)); - }, - - /** Given `dotnet:object:foo:bar`, - * returns { scheme:'object', value: 'foo:bar' } - * - * Given `dotnet:pointer:{ b: 3 }` - * returns { scheme:'object', value: '{b:3}`, o: {b:3} - * - * @param {string} idStr - * @param {boolean} [throwOnError=false] - * - * @returns {WasmId} - */ - _parse_object_id: function (idStr, throwOnError = false) { - if (idStr === undefined || idStr == "" || !idStr.startsWith ('dotnet:')) { - if (throwOnError) - throw new Error (`Invalid id: ${idStr}`); - - return undefined; - } - - const [, scheme, ...rest] = idStr.split(':'); - let res = { - scheme, - value: rest.join (':'), - idStr, - o: {} - }; - - try { - res.o = JSON.parse(res.value); - // eslint-disable-next-line no-empty - } catch (e) {} - - return res; - }, - - _resolve_member_by_name: function (base_object, base_name, expr_parts) { - if (base_object === undefined || base_object.value === undefined) - throw new Error(`Bug: base_object is undefined`); - - if (base_object.value.type === 'object' && base_object.value.subtype === 'null') - throw new ReferenceError(`Null reference: ${base_name} is null`); - - if (base_object.value.type !== 'object') - throw new ReferenceError(`'.' is only supported on non-primitive types. Failed on '${base_name}'`); - - if (expr_parts.length == 0) - throw new Error(`Invalid member access expression`);//FIXME: need the full expression here - - const root = expr_parts[0]; - const props = this.mono_wasm_get_details(base_object.value.objectId, {}); - let resObject = props.find(l => l.name == root); - if (resObject !== undefined) { - if (resObject.value === undefined && resObject.get !== undefined) - resObject = this._invoke_getter(base_object.value.objectId, root); - } - - if (resObject === undefined || expr_parts.length == 1) - return resObject; - else { - expr_parts.shift(); - return this._resolve_member_by_name(resObject, root, expr_parts); - } - }, - - mono_wasm_eval_member_access: function (scope, var_list, rootObjectId, expr) { - if (expr === undefined || expr.length == 0) - throw new Error(`expression argument required`); - - let parts = expr.split('.'); - if (parts.length == 0) - throw new Error(`Invalid member access expression: ${expr}`); - - const root = parts[0]; - - const locals = this.mono_wasm_get_variables(scope, var_list); - let rootObject = locals.find(l => l.name === root); - if (rootObject === undefined) { - // check `this` - const thisObject = locals.find(l => l.name == "this"); - if (thisObject === undefined) - throw new ReferenceError(`Could not find ${root} in locals, and no 'this' found.`); - - const thisProps = this.mono_wasm_get_details(thisObject.value.objectId, {}); - rootObject = thisProps.find(tp => tp.name == root); - if (rootObject === undefined) - throw new ReferenceError(`Could not find ${root} in locals, or in 'this'`); - - if (rootObject.value === undefined && rootObject.get !== undefined) - rootObject = this._invoke_getter(thisObject.value.objectId, root); - } - - parts.shift(); - - if (parts.length == 0) - return rootObject; - - if (rootObject === undefined || rootObject.value === undefined) - throw new Error(`Could not get a value for ${root}`); - - return this._resolve_member_by_name(rootObject, root, parts); - }, - - mono_wasm_set_variable_value: function (scope, index, name, newValue) { - console.debug (">> mono_wasm_set_variable_value " + name + " - " + newValue); - var ret = this._c_fn_table.mono_wasm_set_variable_on_frame_wrapper(scope, index, name, newValue); - if (ret == false) - throw new Error(`Could not get a value for ${name}`); - return ret; - }, - - /** - * @param {WasmId} id - * @returns {object[]} - */ - _get_vt_properties: function (id, args={}) { - let entry = this._get_id_props (id.idStr); - - if (entry === undefined || entry.members === undefined) { - if (!isNaN (id.o.containerId)) { - // We are expanding, so get *all* the members. - // Which ones to return based on @args, can be determined - // at the time of return - this._get_object_properties (id.o.containerId, { expandValueTypes: true }); - } else if (!isNaN (id.o.arrayId)) - this._get_array_values (id, Number (id.o.arrayIdx), 1, true); - else - throw new Error (`Invalid valuetype id (${id.idStr}). Can't get properties for it.`); - } - - // Let's try again - entry = this._get_id_props (id.idStr); - - if (entry !== undefined && entry.members !== undefined) { - if (args.accessorPropertiesOnly === true) - return entry.accessors; - - return entry.members; - } - - throw new Error (`Unknown valuetype id: ${id.idStr}. Failed to get properties for it.`); - }, - - /** - * - * @callback GetIdArgsCallback - * @param {object} var - * @param {number} idx - * @returns {object} - */ - - /** - * @param {object[]} vars - * @param {GetIdArgsCallback} getIdArgs - * @returns {object} - */ - _assign_vt_ids: function (vars, getIdArgs) + mono_wasm_send_dbg_command_with_parms: function (id, command_set, command, command_parameters, length, valtype, newvalue) { - vars.forEach ((v, i) => { - // we might not have a `.value`, like in case of getters which have a `.get` instead - const value = v.value; - if (value === undefined || !value.isValueType) - return; - - if (value.objectId !== undefined) - throw new Error (`Bug: Trying to assign valuetype id, but the var already has one: ${v}`); - - value.objectId = this._new_or_add_id_props ({ scheme: 'valuetype', idArgs: getIdArgs (v, i), props: value._props }); - delete value._props; - }); - - return vars; - }, - - // - // @var_list: [ { index: , name: }, .. ] - mono_wasm_get_variables: function(scope, var_list) { - const numBytes = var_list.length * Int32Array.BYTES_PER_ELEMENT; - const ptr = Module._malloc(numBytes); - let heapBytes = new Int32Array(Module.HEAP32.buffer, ptr, numBytes); - for (let i=0; i ({ containerId: this._async_method_objectId, fieldOffset: v.fieldOffset })); - - for (let i in res) { - const res_name = res [i].name; - if (this._async_method_objectId != 0) { - //Async methods are special in the way that local variables can be lifted to generated class fields - //value of "this" comes here either - if (res_name !== undefined && res_name.indexOf ('>') > 0) { - // For async methods, we get the names too, so use that - // ALTHOUGH, the name wouldn't have `<>` for method args - res [i].name = res_name.substring (1, res_name.indexOf ('>')); - } - } else if (res_name === undefined && var_list [i] !== undefined) { - // For non-async methods, we just have the var id, but we have the name - // from the caller - res [i].name = var_list [i].name; - } - } - - this._post_process_details(res); + throw new Error (`Failed on mono_wasm_invoke_method_debugger_agent_with_parms`); return res; }, - // Keep in sync with the flags in mini-wasm-debugger.c - _get_properties_args_to_gpflags: function (args) { - let gpflags =0; - /* - Disabled for now. Instead, we ask debugger.c to return - ~all~ the members, and then handle the filtering in mono.js . + mono_wasm_send_dbg_command: function (id, command_set, command, command_parameters) + { + const dataHeap = new Uint8Array (Module.HEAPU8.buffer, command_parameters, command_parameters.length); + dataHeap.set (new Uint8Array (this._base64_to_uint8 (command_parameters))); - if (args.ownProperties) - gpflags |= 1; - if (args.accessorPropertiesOnly) - gpflags |= 2; - */ - if (args.expandValueTypes) - gpflags |= 4; + this._c_fn_table.mono_wasm_send_dbg_command_wrapper (id, command_set, command, dataHeap.byteOffset, command_parameters.length); - return gpflags; - }, - - /** - * @param {number} idNum - * @param {boolean} expandValueTypes - * @returns {object} - */ - _get_object_properties: function(idNum, args={}) { - let gpflags = this._get_properties_args_to_gpflags (args); - - let { res_ok, res } = this.mono_wasm_get_object_properties_info (idNum, gpflags); + let { res_ok, res } = MONO.commands_received; if (!res_ok) - throw new Error (`Failed to get properties for ${idNum}`); - - res = MONO._filter_automatic_properties (res, args.accessorPropertiesOnly === true); - res = this._assign_vt_ids (res, v => ({ containerId: idNum, fieldOffset: v.fieldOffset })); - res = this._post_process_details (res); - + throw new Error (`Failed on mono_wasm_send_dbg_command`); return res; + }, - /** - * @param {WasmId} id - * @param {number} [startIdx=0] - * @param {number} [count=-1] - * @param {boolean} [expandValueTypes=false] - * @returns {object[]} - */ - _get_array_values: function (id, startIdx = 0, count = -1, expandValueTypes = false) { - if (isNaN (id.o.arrayId) || isNaN (startIdx)) - throw new Error (`Invalid array id: ${id.idStr}`); - - let gpflags = this._get_properties_args_to_gpflags({ expandValueTypes }); - let { res_ok, res } = this.mono_wasm_get_array_values_info (id.o.arrayId, startIdx, count, gpflags); + mono_wasm_get_dbg_command_info: function () + { + let { res_ok, res } = MONO.commands_received; if (!res_ok) - throw new Error (`Failed to get properties for array id ${id.idStr}`); - - res = this._assign_vt_ids (res, (_, i) => ({ arrayId: id.o.arrayId, arrayIdx: Number (startIdx) + i})); - - for (let i = 0; i < res.length; i ++) { - let value = res [i].value; - if (value.objectId !== undefined && value.objectId.startsWith("dotnet:pointer")) - this._new_or_add_id_props ({ objectId: value.objectId, props: { varName: `[${i}]` } }); - } - res = this._post_process_details (res); + throw new Error (`Failed on mono_wasm_get_dbg_command_info`); return res; }, - _post_process_details: function (details) { - if (details == undefined) - return {}; - - if (details.length > 0) - this._extract_and_cache_value_types(details); - - // remove __args added by add_properties_var - details.forEach(d => delete d.__args); - return details; - }, - - /** - * Gets the next id number to use for generating ids - * - * @returns {number} - */ - _next_id: function () { - return ++this._next_id_var; - }, - - _extract_and_cache_value_types: function (var_list) { - if (var_list == undefined || !Array.isArray (var_list) || var_list.length == 0) - return var_list; - - for (let i in var_list) { - let value = var_list [i].value; - if (value === undefined) - continue; - - if (value.objectId !== undefined && value.objectId.startsWith ("dotnet:pointer:")) { - let ptr_args = this._get_id_props (value.objectId); - if (ptr_args === undefined) - throw new Error (`Bug: Expected to find an entry for pointer id: ${value.objectId}`); - - // It might have been already set in some cases, like arrays - // where the name would be `0`, but we want `[0]` for pointers, - // so the deref would look like `*[0]` - ptr_args.varName = ptr_args.varName || var_list [i].name; - } - - if (value.type != "object" || value.isValueType != true || value.expanded != true) // undefined would also give us false - continue; - - if (value.members === undefined) { - // this could happen for valuetypes that maybe - // we were not able to describe, like `ref` parameters - // So, skip that - continue; - } - - // Generate objectId for expanded valuetypes - value.objectId = value.objectId || this._new_or_add_id_props ({ scheme: 'valuetype' }); - - this._extract_and_cache_value_types (value.members); - - const accessors = value.members.filter(m => m.get !== undefined); - const new_props = Object.assign ({ members: value.members, accessors }, value.__extra_vt_props); - - this._new_or_add_id_props ({ objectId: value.objectId, props: new_props }); - delete value.members; - delete value.__extra_vt_props; - } - - return var_list; - }, - _get_cfo_res_details: function (objectId, args) { if (!(objectId in this._call_function_res_cache)) throw new Error(`Could not find any object with id ${objectId}`); @@ -1101,116 +669,8 @@ var MonoSupportLib = { return { __value_as_json_string__: JSON.stringify (res_details) }; }, - /** - * Generates a new id, and a corresponding entry for associated properties - * like `dotnet:pointer:{ a: 4 }` - * The third segment of that `{a:4}` is the idArgs parameter - * - * Only `scheme` or `objectId` can be set. - * if `scheme`, then a new id is generated, and it's properties set - * if `objectId`, then it's properties are updated - * - * @param {object} args - * @param {string} [args.scheme=undefined] scheme second part of `dotnet:pointer:..` - * @param {string} [args.objectId=undefined] objectId - * @param {object} [args.idArgs={}] The third segment of the objectId - * @param {object} [args.props={}] Properties for the generated id - * - * @returns {string} generated/updated id string - */ - _new_or_add_id_props: function ({ scheme = undefined, objectId = undefined, idArgs = {}, props = {} }) { - if (scheme === undefined && objectId === undefined) - throw new Error (`Either scheme or objectId must be given`); - - if (scheme !== undefined && objectId !== undefined) - throw new Error (`Both scheme, and objectId cannot be given`); - - if (objectId !== undefined && Object.entries (idArgs).length > 0) - throw new Error (`Both objectId, and idArgs cannot be given`); - - if (Object.entries (idArgs).length == 0) { - // We want to generate a new id, only if it doesn't have other - // attributes that it can use to uniquely identify. - // Eg, we don't do this for `dotnet:valuetype:{containerId:4, fieldOffset: 24}` - idArgs.num = this._next_id (); - } - - let idStr; - if (objectId !== undefined) { - idStr = objectId; - const old_props = this._id_table [idStr]; - if (old_props === undefined) - throw new Error (`ObjectId not found in the id table: ${idStr}`); - - this._id_table [idStr] = Object.assign (old_props, props); - } else { - idStr = `dotnet:${scheme}:${JSON.stringify (idArgs)}`; - this._id_table [idStr] = props; - } - - return idStr; - }, - - /** - * @param {string} objectId - * @returns {object} - */ - _get_id_props: function (objectId) { - return this._id_table [objectId]; - }, - - _get_deref_ptr_value: function (objectId) { - const ptr_args = this._get_id_props (objectId); - if (ptr_args === undefined) - throw new Error (`Unknown pointer id: ${objectId}`); - - if (ptr_args.ptr_addr == 0 || ptr_args.klass_addr == 0) - throw new Error (`Both ptr_addr and klass_addr need to be non-zero, to dereference a pointer. objectId: ${objectId}`); - - const value_addr = new DataView (Module.HEAPU8.buffer).getUint32 (ptr_args.ptr_addr, /* littleEndian */ true); - let { res_ok, res } = this.mono_wasm_get_deref_ptr_value_info (value_addr, ptr_args.klass_addr); - if (!res_ok) - throw new Error (`Failed to dereference pointer ${objectId}`); - - if (res.length > 0) { - if (ptr_args.varName === undefined) - throw new Error (`Bug: no varName found for the pointer. objectId: ${objectId}`); - - res [0].name = `*${ptr_args.varName}`; - } - - res = this._post_process_details (res); - return res; - }, - mono_wasm_get_details: function (objectId, args={}) { - let id = this._parse_object_id (objectId, true); - - switch (id.scheme) { - case "object": { - if (isNaN (id.value)) - throw new Error (`Invalid objectId: ${objectId}. Expected a numeric id.`); - - args.expandValueTypes = false; - return this._get_object_properties(id.value, args); - } - - case "array": - return this._get_array_values (id); - - case "valuetype": - return this._get_vt_properties(id, args); - - case "cfo_res": - return this._get_cfo_res_details (objectId, args); - - case "pointer": { - return this._get_deref_ptr_value (objectId); - } - - default: - throw new Error(`Unknown object id format: ${objectId}`); - } + return this._get_cfo_res_details (`dotnet:cfo_res:${objectId}`, args); }, _cache_call_function_res: function (obj) { @@ -1224,105 +684,33 @@ var MonoSupportLib = { delete this._cache_call_function_res[objectId]; }, - /** - * @param {string} objectIdStr objectId - * @param {string} name property name - * @returns {object} return value - */ - _invoke_getter: function (objectIdStr, name) { - const id = this._parse_object_id (objectIdStr); - if (id === undefined) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let getter_res; - if (id.scheme == 'object') { - if (isNaN (id.o) || id.o < 0) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let { res_ok, res } = this.mono_wasm_invoke_getter_on_object_info (id.o, name); - if (!res_ok) - throw new Error (`Invoking getter on ${objectIdStr} failed`); - - getter_res = res; - } else if (id.scheme == 'valuetype') { - const id_props = this._get_id_props (objectIdStr); - if (id_props === undefined) - throw new Error (`Unknown valuetype id: ${objectIdStr}`); - - if (typeof id_props.value64 !== 'string' || isNaN (id_props.klass)) - throw new Error (`Bug: Cannot invoke getter on ${objectIdStr}, because of missing or invalid klass/value64 fields. idProps: ${JSON.stringify (id_props)}`); - - const dataPtr = Module._malloc (id_props.value64.length); - const dataHeap = new Uint8Array (Module.HEAPU8.buffer, dataPtr, id_props.value64.length); - dataHeap.set (new Uint8Array (this._base64_to_uint8 (id_props.value64))); - - let { res_ok, res } = this.mono_wasm_invoke_getter_on_value_info (dataHeap.byteOffset, id_props.klass, name); - Module._free (dataHeap.byteOffset); - - if (!res_ok) { - console.debug (`Invoking getter on valuetype ${objectIdStr}, with props: ${JSON.stringify (id_props)} failed`); - throw new Error (`Invoking getter on valuetype ${objectIdStr} failed`); - } - getter_res = res; - } else { - throw new Error (`Only object, and valuetypes supported for getters, id: ${objectIdStr}`); - } - - getter_res = MONO._post_process_details (getter_res); - return getter_res.length > 0 ? getter_res [0] : {}; - }, - - /** - * @param {string} objectIdStr objectId - * @param {string} name property name - * @returns {object} return true if it works and false if it doesn't - */ - _set_value_on_object: function (objectIdStr, name, newvalue) { - const id = this._parse_object_id (objectIdStr); - if (id === undefined) - throw new Error (`Invalid object id: ${objectIdStr}`); - - let setter_res; - if (id.scheme == 'object') { - if (isNaN (id.o) || id.o < 0) - throw new Error (`Invalid object id: ${objectIdStr}`); - - var ret = this._c_fn_table.mono_wasm_set_value_on_object_wrapper (id.o, name, newvalue); - if (!ret) - throw new Error (`Invoking setter on ${objectIdStr} failed`); - - setter_res = ret; - } - else - throw new Error (`Only object is supported for setters, id: ${objectIdStr}`); - return setter_res; - }, - - _create_proxy_from_object_id: function (objectId) { - const details = this.mono_wasm_get_details(objectId); - + _create_proxy_from_object_id: function (objectId, details) { if (objectId.startsWith ('dotnet:array:')) - return details.map (p => p.value); + { + let ret = details.map (p => p.value); + return ret; + } let proxy = {}; Object.keys (details).forEach (p => { var prop = details [p]; if (prop.get !== undefined) { - // TODO: `set` - Object.defineProperty (proxy, prop.name, - { get () { return MONO._invoke_getter (objectId, prop.name); } } + { get () { return MONO.mono_wasm_send_dbg_command(-1, prop.get.commandSet, prop.get.command, prop.get.buffer, prop.get.length); }, + set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} + ); + } else if (prop.set !== undefined ){ + Object.defineProperty (proxy, + prop.name, + { get () { return prop.value; }, + set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} ); } else { proxy [prop.name] = prop.value; } }); - - const handler1 = { - set (obj, prop, newValue) {return MONO._set_value_on_object (objectId, prop, newValue.toString());}, - }; - return new Proxy(proxy, handler1); + return proxy; }, mono_wasm_call_function_on: function (request) { @@ -1330,6 +718,7 @@ var MonoSupportLib = { throw new Error (`"arguments" should be an array, but was ${request.arguments}`); const objId = request.objectId; + const details = request.details; let proxy; if (objId.startsWith ('dotnet:cfo_res:')) { @@ -1338,7 +727,7 @@ var MonoSupportLib = { else throw new Error (`Unknown object id ${objId}`); } else { - proxy = this._create_proxy_from_object_id (objId); + proxy = this._create_proxy_from_object_id (objId, details); } const fn_args = request.arguments != undefined ? request.arguments.map(a => JSON.stringify(a.value)) : []; @@ -1348,22 +737,19 @@ var MonoSupportLib = { if (fn_res === undefined) return { type: "undefined" }; - if (fn_res === null || (fn_res.subtype === 'null' && fn_res.value === undefined)) - return fn_res; - - // primitive type if (Object (fn_res) !== fn_res) - return fn_res; + { + if (typeof(fn_res) == "object" && fn_res == null) + return { type: typeof(fn_res), subtype: `${fn_res}`, value: null }; + return { type: typeof(fn_res), description: `${fn_res}`, value: `${fn_res}`}; + } - // return .value, if it is a primitive type - if (fn_res.value !== undefined && Object (fn_res.value.value) !== fn_res.value.value) - return fn_res.value; - - if (request.returnByValue) + if (request.returnByValue && fn_res.subtype == undefined) return {type: "object", value: fn_res}; - - const fn_res_id = this._cache_call_function_res (fn_res); if (Object.getPrototypeOf (fn_res) == Array.prototype) { + + const fn_res_id = this._cache_call_function_res (fn_res); + return { type: "object", subtype: "array", @@ -1371,9 +757,15 @@ var MonoSupportLib = { description: `Array(${fn_res.length})`, objectId: fn_res_id }; - } else { - return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; } + if (fn_res.value !== undefined || fn_res.subtype !== undefined) { + return fn_res; + } + + if (fn_res == proxy) + return { type: "object", className: "Object", description: "Object", objectId: objId }; + const fn_res_id = this._cache_call_function_res (fn_res); + return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; }, _clear_per_step_state: function () { @@ -1385,31 +777,6 @@ var MonoSupportLib = { this._clear_per_step_state (); }, - mono_wasm_start_single_stepping: function (kind) { - console.debug (">> mono_wasm_start_single_stepping " + kind); - if (!this.mono_wasm_setup_single_step) - this.mono_wasm_setup_single_step = Module.cwrap ("mono_wasm_setup_single_step", 'number', [ 'number']); - - this._clear_per_step_state (); - - return this.mono_wasm_setup_single_step (kind); - }, - - mono_wasm_set_pause_on_exceptions: function (state) { - if (!this.mono_wasm_pause_on_exceptions) - this.mono_wasm_pause_on_exceptions = Module.cwrap ("mono_wasm_pause_on_exceptions", 'number', [ 'number']); - var state_enum = 0; - switch (state) { - case 'uncaught': - state_enum = 1; //EXCEPTION_MODE_UNCAUGHT - break; - case 'all': - state_enum = 2; //EXCEPTION_MODE_ALL - break; - } - return this.mono_wasm_pause_on_exceptions (state_enum); - }, - mono_wasm_detach_debugger: function () { if (!this.mono_wasm_set_is_debugger_attached) this.mono_wasm_set_is_debugger_attached = Module.cwrap ('mono_wasm_set_is_debugger_attached', 'void', ['bool']); @@ -1461,14 +828,9 @@ var MonoSupportLib = { this._call_function_res_cache = {}; this._c_fn_table = {}; - this._register_c_var_fn ('mono_wasm_get_object_properties', 'bool', [ 'number', 'number' ]); - this._register_c_var_fn ('mono_wasm_get_array_values', 'bool', [ 'number', 'number', 'number', 'number' ]); - this._register_c_var_fn ('mono_wasm_invoke_getter_on_object', 'bool', [ 'number', 'string' ]); - this._register_c_var_fn ('mono_wasm_invoke_getter_on_value', 'bool', [ 'number', 'number', 'string' ]); - this._register_c_var_fn ('mono_wasm_get_local_vars', 'bool', [ 'number', 'number', 'number']); - this._register_c_var_fn ('mono_wasm_get_deref_ptr_value', 'bool', [ 'number', 'number']); - this._register_c_fn ('mono_wasm_set_value_on_object', 'bool', [ 'number', 'string', 'string' ]); - this._register_c_fn ('mono_wasm_set_variable_on_frame', 'bool', [ 'number', 'number', 'string', 'string']); + this._register_c_fn ('mono_wasm_send_dbg_command', 'bool', [ 'number', 'number', 'number', 'number', 'number' ]); + this._register_c_fn ('mono_wasm_send_dbg_command_with_parms', 'bool', [ 'number', 'number', 'number', 'number', 'number', 'number', 'string' ]); + // DO NOT REMOVE - magic debugger init function if (globalThis.dotnetDebugger) debugger; @@ -1476,20 +838,6 @@ var MonoSupportLib = { console.debug ("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28"); }, - mono_wasm_set_breakpoint: function (assembly, method_token, il_offset) { - if (!this.mono_wasm_set_bp) - this.mono_wasm_set_bp = Module.cwrap ('mono_wasm_set_breakpoint', 'number', ['string', 'number', 'number']); - - return this.mono_wasm_set_bp (assembly, method_token, il_offset) - }, - - mono_wasm_remove_breakpoint: function (breakpoint_id) { - if (!this.mono_wasm_del_bp) - this.mono_wasm_del_bp = Module.cwrap ('mono_wasm_remove_breakpoint', 'number', ['number']); - - return this.mono_wasm_del_bp (breakpoint_id); - }, - // Set environment variable NAME to VALUE // Should be called before mono_load_runtime_and_bcl () in most cases mono_wasm_setenv: function (name, value) { @@ -2000,78 +1348,6 @@ var MonoSupportLib = { return MONO.loaded_assets; }, - mono_wasm_clear_all_breakpoints: function() { - if (!this.mono_clear_bps) - this.mono_clear_bps = Module.cwrap ('mono_wasm_clear_all_breakpoints', null); - - this.mono_clear_bps (); - }, - - mono_wasm_add_null_var: function(className) - { - let fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - if (!fixed_class_name) { - // Eg, when a @className is passed from js itself, like - // mono_wasm_add_null_var ("string") - fixed_class_name = className; - } - MONO.var_info.push ({value: { - type: "object", - className: fixed_class_name, - description: fixed_class_name, - subtype: "null" - }}); - }, - - _mono_wasm_add_string_var: function(var_value) { - if (var_value === 0) { - MONO.mono_wasm_add_null_var ("string"); - return; - } - - MONO.var_info.push({ - value: { - type: "string", - value: var_value, - description: var_value - } - }); - }, - - _mono_wasm_add_getter_var: function(className) { - const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); - var name; - if (MONO.var_info.length > 0) - name = MONO.var_info [MONO.var_info.length - 1].name; - name = (name === undefined) ? "" : name; - - MONO.var_info.push({ - get: { - className: "Function", - description: `get ${name} () {}`, - type: "function", - } - }); - }, - - _mono_wasm_add_array_var: function(className, objectId, length) { - const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); - if (objectId == 0) { - MONO.mono_wasm_add_null_var (fixed_class_name); - return; - } - - MONO.var_info.push({ - value: { - type: "object", - subtype: "array", - className: fixed_class_name, - description: `${fixed_class_name}(${length})`, - objectId: this._new_or_add_id_props ({ scheme: 'array', idArgs: { arrayId: objectId } }) - } - }); - }, - // FIXME: improve _base64_to_uint8: function (base64String) { const byteCharacters = atob (base64String); @@ -2083,215 +1359,6 @@ var MonoSupportLib = { return new Uint8Array (byteNumbers); }, - _begin_value_type_var: function(className, args) { - if (args === undefined || (typeof args !== 'object')) { - console.debug (`_begin_value_type_var: Expected an args object`); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); - const toString = args.toString; - const base64String = btoa (String.fromCharCode (...new Uint8Array (Module.HEAPU8.buffer, args.value_addr, args.value_size))); - const vt_obj = { - value: { - type : "object", - className : fixed_class_name, - description : (toString === 0 ? fixed_class_name: Module.UTF8ToString (toString)), - expanded : true, - isValueType : true, - __extra_vt_props: { klass: args.klass, value64: base64String }, - members : [] - } - }; - if (MONO._vt_stack.length == 0) - MONO._old_var_info = MONO.var_info; - - MONO.var_info = vt_obj.value.members; - MONO._vt_stack.push (vt_obj); - }, - - _end_value_type_var: function() { - let top_vt_obj_popped = MONO._vt_stack.pop (); - top_vt_obj_popped.value.members = MONO._filter_automatic_properties ( - MONO._fixup_name_value_objects (top_vt_obj_popped.value.members)); - - if (MONO._vt_stack.length == 0) { - MONO.var_info = MONO._old_var_info; - MONO.var_info.push(top_vt_obj_popped); - } else { - var top_obj = MONO._vt_stack [MONO._vt_stack.length - 1]; - top_obj.value.members.push (top_vt_obj_popped); - MONO.var_info = top_obj.value.members; - } - }, - - _add_valuetype_unexpanded_var: function(className, args) { - if (args === undefined || (typeof args !== 'object')) { - console.debug (`_add_valuetype_unexpanded_var: Expected an args object`); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); - const toString = args.toString; - - MONO.var_info.push ({ - value: { - type: "object", - className: fixed_class_name, - description: (toString === 0 ? fixed_class_name : Module.UTF8ToString (toString)), - isValueType: true - } - }); - }, - - mono_wasm_add_properties_var: function (name, args) { - if (typeof args !== 'object') - args = { field_offset: args }; - - if (args.owner_class !== undefined && args.owner_class !== 0) - args.owner_class = Module.UTF8ToString(args.owner_class); - - let name_obj = { - name: Module.UTF8ToString (name), - fieldOffset: args.field_offset, - __args: args - }; - if (args.is_own) - name_obj.isOwn = true; - - MONO.var_info.push(name_obj); - }, - - mono_wasm_add_typed_value: function (type, str_value, value) { - let type_str = type; - if (typeof type != 'string') - type_str = Module.UTF8ToString (type); - - if (str_value !== 0) - str_value = Module.UTF8ToString (str_value); - - switch (type_str) { - case "bool": { - const v = value != 0; - MONO.var_info.push ({ - value: { - type: "boolean", - value: v, - description: v.toString () - }, - writable:true - }); - break; - } - - case "char": { - const v = `${value} '${String.fromCharCode (value)}'`; - MONO.var_info.push ({ - value: { - type: "symbol", - value: v, - description: v - }, - writable:true - }); - break; - } - - case "number": - MONO.var_info.push ({ - value: { - type: "number", - value: value, - description: '' + value - }, - writable:true - }); - break; - - case "string": - MONO._mono_wasm_add_string_var (str_value); - break; - - case "getter": - MONO._mono_wasm_add_getter_var (str_value); - break; - - case "array": - MONO._mono_wasm_add_array_var (str_value, value.objectId, value.length); - break; - - case "begin_vt": - MONO._begin_value_type_var (str_value, value); - break; - - case "end_vt": - MONO._end_value_type_var (); - break; - - case "unexpanded_vt": - MONO._add_valuetype_unexpanded_var (str_value, value); - break; - - case "pointer": { - const fixed_value_str = MONO._mono_csharp_fixup_class_name (str_value); - if (value.klass_addr == 0 || value.ptr_addr == 0 || fixed_value_str.startsWith ('(void*')) { - // null or void*, which we can't deref - MONO.var_info.push({ - value: { - type: "symbol", - value: fixed_value_str, - description: fixed_value_str - } - }); - } else { - MONO.var_info.push({ - value: { - type: "object", - className: fixed_value_str, - description: fixed_value_str, - objectId: this._new_or_add_id_props ({ scheme: 'pointer', props: value }) - } - }); - } - } - break; - - case "symbol": { - if (typeof value === 'object' && value.isClassName) - str_value = MONO._mono_csharp_fixup_class_name (str_value); - - MONO.var_info.push ({ - value: { - type: "symbol", - value: str_value, - description: str_value - } - }); - } - break; - - default: { - const msg = `'${str_value}' ${value}`; - - MONO.var_info.push ({ - value: { - type: "symbol", - value: msg, - description: msg - } - }); - break; - } - } - }, - - _mono_csharp_fixup_class_name: function(className) - { - // Fix up generic names like Foo`2 to Foo - // and nested class names like Foo/Bar to Foo.Bar - return className.replace(/\//g, '.').replace(/`\d+/g, ''); - }, - mono_wasm_load_data_archive: function (data, prefix) { if (data.length < 8) return false; @@ -2392,132 +1459,6 @@ var MonoSupportLib = { } } }, - - mono_wasm_add_typed_value: function (type, str_value, value) { - MONO.mono_wasm_add_typed_value (type, str_value, value); - }, - - mono_wasm_add_properties_var: function(name, args) { - MONO.mono_wasm_add_properties_var (name, args); - }, - - mono_wasm_set_is_async_method: function(objectId) { - MONO._async_method_objectId = objectId; - }, - - mono_wasm_add_enum_var: function(className, members, value) { - // FIXME: flags - // - - // group0: Monday:0 - // group1: Monday - // group2: 0 - const re = new RegExp (`[,]?([^,:]+):(${value}(?=,)|${value}$)`, 'g') - const members_str = Module.UTF8ToString (members); - - const match = re.exec(members_str); - const member_name = match == null ? ('' + value) : match [1]; - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "object", - className: fixed_class_name, - description: member_name, - isEnum: true - } - }); - }, - - mono_wasm_add_array_item: function(position) { - MONO.var_info.push({ - name: `${position}` - }); - }, - - mono_wasm_add_obj_var: function(className, toString, objectId) { - if (objectId == 0) { - MONO.mono_wasm_add_null_var (className); - return; - } - - const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "object", - className: fixed_class_name, - description: (toString === 0 ? fixed_class_name : Module.UTF8ToString (toString)), - objectId: "dotnet:object:"+ objectId, - } - }); - }, - - /* - * @className, and @targetName are in the following format: - * - * :[]: - */ - mono_wasm_add_func_var: function (className, targetName, objectId) { - if (objectId == 0) { - MONO.mono_wasm_add_null_var ( - MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className))); - return; - } - - function args_to_sig (args_str) { - var parts = args_str.split (":"); - // TODO: min length = 3? - parts = parts.map (a => MONO._mono_csharp_fixup_class_name (a)); - - // method name at the end - var method_name = parts.pop (); - - // ret type at the beginning - var ret_sig = parts [0]; - var args_sig = parts.splice (1).join (', '); - return `${ret_sig} ${method_name} (${args_sig})`; - } - let tgt_sig; - if (targetName != 0) - tgt_sig = args_to_sig (Module.UTF8ToString (targetName)); - - const type_name = MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className)); - if (tgt_sig === undefined) - tgt_sig = type_name; - - if (objectId == -1 || targetName === 0) { - // Target property - MONO.var_info.push ({ - value: { - type: "symbol", - value: tgt_sig, - description: tgt_sig, - } - }); - } else { - MONO.var_info.push ({ - value: { - type: "object", - className: type_name, - description: tgt_sig, - objectId: "dotnet:object:" + objectId, - } - }); - } - }, - - mono_wasm_add_frame: function(il, method, frame_id, assembly_name, method_full_name) { - var parts = Module.UTF8ToString (method_full_name).split (":", 2); - MONO.active_frames.push( { - il_pos: il, - method_token: method, - assembly_name: Module.UTF8ToString (assembly_name), - // Extract just the method name from `{class_name}:{method_name}` - method_name: parts [parts.length - 1], - frame_id - }); - }, - schedule_background_exec: function () { ++MONO.pump_count; if (typeof globalThis.setTimeout === 'function') { @@ -2541,21 +1482,11 @@ var MonoSupportLib = { } }, - mono_wasm_fire_bp: function () { + mono_wasm_fire_debugger_agent_message: function () { // eslint-disable-next-line no-debugger debugger; }, - mono_wasm_fire_exception: function (exception_id, message, class_name, uncaught) { - MONO.active_exception = { - exception_id: exception_id, - message : Module.UTF8ToString (message), - class_name : Module.UTF8ToString (class_name), - uncaught : uncaught - }; - debugger; - }, - mono_wasm_asm_loaded: function (assembly_name, assembly_ptr, assembly_len, pdb_ptr, pdb_len) { // Only trigger this codepath for assemblies loaded after app is ready if (MONO.mono_wasm_runtime_is_ready !== true) From 93f407d231e31687decdc1f5b7f466af2fb94737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 23 Jun 2021 20:13:37 -0400 Subject: [PATCH 074/926] [wasm] Build static components; include hot_reload in runtime (#54568) * [wasm] Build static components; include hot_reload in runtime Workaround until https://github.com/dotnet/runtime/issues/54565 is fixed Build the runtime always with support for hot_reload, and without diagnostics_tracing * Update wasm.proj * Add a browser functional test for hot reload Just check that the capabilities are non-empty which is a good proxy for hot reload being enabled in the runtime. * Turn off trimming for hot reload functional test * Disable test on browser AOT * fix whitespace Co-authored-by: Thays Grazia --- ...tadata.ApplyUpdate.Test.MethodBody1.csproj | 1 + .../tests/ApplyUpdateTest.cs | 1 + src/mono/mono.proj | 1 - src/mono/wasm/Makefile | 8 ++ src/mono/wasm/wasm.proj | 3 + .../ApplyUpdateReferencedAssembly.csproj | 30 +++++++ .../MethodBody1.cs | 11 +++ .../MethodBody1_v1.cs | 11 +++ .../MethodBody1_v2.cs | 11 +++ .../deltascript.json | 7 ++ .../WebAssembly/Browser/HotReload/Program.cs | 81 +++++++++++++++++++ .../WebAssembly.Browser.HotReload.Test.csproj | 52 ++++++++++++ .../WebAssembly/Browser/HotReload/index.html | 55 +++++++++++++ .../WebAssembly/Browser/HotReload/runtime.js | 47 +++++++++++ 14 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html create mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj index f05397e884c..57ba4f3ec52 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent) true deltascript.json + true diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index e533377e673..2a953d19eec 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -18,6 +18,7 @@ namespace System.Reflection.Metadata public class ApplyUpdateTest { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] void StaticMethodBodyUpdate() { ApplyUpdateUtil.TestCase(static () => diff --git a/src/mono/mono.proj b/src/mono/mono.proj index e5d4bb2970a..e459f872177 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -294,7 +294,6 @@ <_MonoCMakeArgs Include="-DENABLE_INTERP_LIB=1"/> <_MonoCMakeArgs Include="-DDISABLE_ICALL_TABLES=1"/> <_MonoCMakeArgs Include="-DDISABLE_CRASH_REPORTING=1"/> - <_MonoCMakeArgs Include="-DDISABLE_COMPONENTS=1"/> <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_LLVM_RUNTIME=1"/> diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 9f6d0555111..86288cd674e 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -48,12 +48,20 @@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION) @echo "----------------------------------------------------------" @echo "Installed emsdk into EMSDK_PATH=$(TOP)/src/mono/wasm/emsdk" +# FIXME: When https://github.com/dotnet/runtime/issues/54565 is fixed, and the WasmApp targets are updated to use mono runtime components, remove this +MONO_COMPONENT_LIBS= \ + $(MONO_BIN_DIR)/libmono-component-hot_reload-static.a \ + $(MONO_BIN_DIR)/libmono-component-diagnostics_tracing-stub-static.a + MONO_OBJ_DIR=$(OBJDIR)/mono/Browser.wasm.$(CONFIG) MONO_INCLUDE_DIR=$(MONO_BIN_DIR)/include/mono-2.0 BUILDS_OBJ_DIR=$(MONO_OBJ_DIR)/wasm +# libmonosgen-2.0 is in MONO_LIBS twice because the components and the runtime are depend on each other MONO_LIBS = \ $(MONO_BIN_DIR)/libmono-ee-interp.a \ $(MONO_BIN_DIR)/libmonosgen-2.0.a \ + $(MONO_COMPONENT_LIBS) \ + $(MONO_BIN_DIR)/libmonosgen-2.0.a \ $(MONO_BIN_DIR)/libmono-ilgen.a \ $(MONO_BIN_DIR)/libmono-icall-table.a \ $(MONO_BIN_DIR)/libmono-profiler-aot.a \ diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 489d88259f5..ffc2fb92825 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -189,6 +189,8 @@ + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj new file mode 100644 index 00000000000..46e260f24e1 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -0,0 +1,30 @@ + + + true + deltascript.json + library + false + true + + false + true + + false + false + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs new file mode 100644 index 00000000000..9e98604b921 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "OLD STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs new file mode 100644 index 00000000000..4aab1e81dad --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "NEW STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs new file mode 100644 index 00000000000..83f0142e55f --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + return "NEWEST STRING"; + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json new file mode 100644 index 00000000000..8e738364bc7 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json @@ -0,0 +1,7 @@ +{ + "changes": [ + {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"}, + {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"}, + ] +} + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs new file mode 100644 index 00000000000..cb005b09088 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Sample +{ + public class Test + { + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TestMeaning() + { + const int success = 42; + const int failure = 1; + + var ty = typeof(System.Reflection.Metadata.AssemblyExtensions); + var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); + + if (mi == null) + return failure; + + var caps = mi.Invoke(null, null) as string; + + if (String.IsNullOrEmpty(caps)) + return failure; + + var assm = typeof (ApplyUpdateReferencedAssembly.MethodBody1).Assembly; + + var r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("OLD STRING" != r) + return failure; + + ApplyUpdate(assm); + + r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("NEW STRING" != r) + return failure; + + ApplyUpdate(assm); + + r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1(); + if ("NEWEST STRING" != r) + return failure; + + return success; + } + + private static System.Collections.Generic.Dictionary assembly_count = new(); + + internal static void ApplyUpdate (System.Reflection.Assembly assm) + { + int count; + if (!assembly_count.TryGetValue(assm, out count)) + count = 1; + else + count++; + assembly_count [assm] = count; + + /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {count}"); + + string dmeta_name = $"{basename}.{count}.dmeta"; + string dil_name = $"{basename}.{count}.dil"; + byte[] dmeta_data = System.IO.File.ReadAllBytes(dmeta_name); + byte[] dil_data = System.IO.File.ReadAllBytes(dil_name); + byte[] dpdb_data = null; // TODO also use the dpdb data + + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); + } + } +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj new file mode 100644 index 00000000000..2ba05527c2a --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj @@ -0,0 +1,52 @@ + + + true + false + false + true + WasmTestOnBrowser + 42 + runtime.js + false + + + + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html new file mode 100644 index 00000000000..ad7cc2164ba --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html @@ -0,0 +1,55 @@ + + + + + + TESTS + + + + + + Result from Sample.Test.TestMeaning: + + + + + + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js new file mode 100644 index 00000000000..4859991a626 --- /dev/null +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +var Module = { + + config: null, + + preInit: async function() { + Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + }, + + onRuntimeInitialized: function () { + if (!Module.config || Module.config.error) { + console.log("No config found"); + test_exit(1); + throw(Module.config.error); + } + + Module.config.loaded_cb = function () { + try { + App.init (); + } catch (error) { + test_exit(1); + throw (error); + } + }; + Module.config.fetch_file_cb = function (asset) { + return fetch (asset, { credentials: 'same-origin' }); + } + + if (Module.config.environment_variables !== undefined) { + console.log ("expected environment variables to be undefined, but they're: ", Module.config.environment_variables); + test_exit(1); + } + Module.config.environment_variables = { + "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" + }; + + try + { + MONO.mono_load_runtime_and_bcl_args (Module.config); + } catch (error) { + test_exit(1); + throw(error); + } + }, +}; From f3af8d8b1468f55dea7b82c6bea3bbe0c956e72e Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Wed, 23 Jun 2021 17:21:42 -0700 Subject: [PATCH 075/926] Fix telemetry for Socket connects to Dns endpoints (#54071) --- .../Net/Sockets/SocketAsyncEventArgs.cs | 21 +-- .../System/Net/Sockets/SocketsTelemetry.cs | 2 +- .../tests/FunctionalTests/TelemetryTest.cs | 172 +++++++++--------- 3 files changed, 90 insertions(+), 105 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs index bda1217e01f..699a447837e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs @@ -74,7 +74,6 @@ namespace System.Net.Sockets private Socket? _currentSocket; private bool _userSocket; // if false when performing Connect, _currentSocket should be disposed private bool _disposeCalled; - private protected bool _disableTelemetry; // Controls thread safety via Interlocked. private const int Configuring = -1; @@ -202,7 +201,7 @@ namespace System.Net.Sockets private void OnCompletedInternal() { - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry(); + if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry(); OnCompleted(this); } @@ -813,11 +812,8 @@ namespace System.Net.Sockets } // Complete the operation. - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) - { - LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred); - AfterConnectAcceptTelemetry(); - } + if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_connectSocket?.SocketType, SocketAsyncOperation.Connect, internalArgs.BytesTransferred); + Complete(); // Clean up after our temporary arguments. @@ -842,12 +838,7 @@ namespace System.Net.Sockets private ManualResetValueTaskSourceCore _mrvtsc; private int _isCompleted; - public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) - { - // Instances of this type are an implementation detail of an overarching connect operation. - // We don't want to emit telemetry specific to operations on this inner instance. - _disableTelemetry = true; - } + public MultiConnectSocketAsyncEventArgs() : base(unsafeSuppressExecutionContextFlow: false) { } public void GetResult(short token) => _mrvtsc.GetResult(token); public ValueTaskSourceStatus GetStatus(short token) => _mrvtsc.GetStatus(token); @@ -968,7 +959,7 @@ namespace System.Net.Sockets break; } - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred); + if (SocketsTelemetry.Log.IsEnabled()) LogBytesTransferEvents(_currentSocket?.SocketType, _completedOperation, bytesTransferred); Complete(); } @@ -1003,7 +994,7 @@ namespace System.Net.Sockets FinishOperationSyncFailure(socketError, bytesTransferred, flags); } - if (SocketsTelemetry.Log.IsEnabled() && !_disableTelemetry) AfterConnectAcceptTelemetry(); + if (SocketsTelemetry.Log.IsEnabled()) AfterConnectAcceptTelemetry(); } private static void LogBytesTransferEvents(SocketType? socketType, SocketAsyncOperation operation, int bytesTransferred) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs index 7b7641f2a40..cb1e77f17ae 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs @@ -113,7 +113,7 @@ namespace System.Net.Sockets { if (IsEnabled(EventLevel.Informational, EventKeywords.All)) { - AcceptStart(address.ToString()); + AcceptStart(address.Serialize().ToString()); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index ad17579f90e..82bed83eabe 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; -using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; @@ -117,14 +116,9 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - VerifyStartStopEvents(events, connect: false, expectedCount: 1); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); - Assert.DoesNotContain(events, e => e.Event.EventName == "AcceptFailed"); + VerifyEvents(events, connect: true, expectedCount: 1); + VerifyEvents(events, connect: false, expectedCount: 1); VerifyEventCounters(events, connectCount: 1); }, connectMethod, acceptMethod).Dispose(); } @@ -153,12 +147,8 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); + VerifyEvents(events, connect: true, expectedCount: 1); VerifyEventCounters(events, connectCount: 1, connectOnly: true); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } @@ -169,12 +159,6 @@ namespace System.Net.Sockets.Tests [MemberData(nameof(SocketMethods_WithBools_MemberData))] public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { - if (useDnsEndPoint) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/43931")] - throw new SkipTestException("https://github.com/dotnet/runtime/issues/43931"); - } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -207,7 +191,10 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - VerifyConnectFailureEvents(events); + // For DNS endpoints, we may see multiple Start/Failure/Stop events + int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; + VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); + VerifyEventCounters(events, connectCount: 0); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } @@ -216,12 +203,6 @@ namespace System.Net.Sockets.Tests [MemberData(nameof(SocketMethods_MemberData))] public void EventSource_SocketAcceptFailure_LogsAcceptFailed(string acceptMethod) { - if (acceptMethod == "Sync" && PlatformDetection.IsRedHatFamily7) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/42686")] - throw new SkipTestException("Disposing a Socket performing a sync operation can hang on RedHat7 systems"); - } - RemoteExecutor.Invoke(async acceptMethod => { using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1); @@ -246,18 +227,8 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: false, expectedCount: 1); - - (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "AcceptFailed"); - Assert.Equal(2, failed.Event.Payload.Count); - Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0])); - Assert.IsType(failed.Event.Payload[1]); - - (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "AcceptStart"); - Assert.Equal(startActivityId, failed.ActivityId); + VerifyEvents(events, connect: false, expectedCount: 1, shouldHaveFailures: true); VerifyEventCounters(events, connectCount: 0); }, acceptMethod).Dispose(); } @@ -270,12 +241,6 @@ namespace System.Net.Sockets.Tests [InlineData("Eap", false)] public void EventSource_ConnectAsyncCanceled_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { - if (useDnsEndPoint) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/46030")] - throw new SkipTestException("https://github.com/dotnet/runtime/issues/46030"); - } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -326,27 +291,13 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - VerifyConnectFailureEvents(events); + // For DNS endpoints, we may see multiple Start/Failure/Stop events + int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; + VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); + VerifyEventCounters(events, connectCount: 0); }, connectMethod, useDnsEndPoint.ToString()).Dispose(); } - private static void VerifyConnectFailureEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events) - { - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 1); - - (EventWrittenEventArgs Event, Guid ActivityId) failed = Assert.Single(events, e => e.Event.EventName == "ConnectFailed"); - Assert.Equal(2, failed.Event.Payload.Count); - Assert.True(Enum.IsDefined((SocketError)failed.Event.Payload[0])); - Assert.IsType(failed.Event.Payload[1]); - - (_, Guid startActivityId) = Assert.Single(events, e => e.Event.EventName == "ConnectStart"); - Assert.Equal(startActivityId, failed.ActivityId); - - VerifyEventCounters(events, connectCount: 0); - } - [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void EventSource_EventsRaisedAsExpected() @@ -382,40 +333,13 @@ namespace System.Net.Sockets.Tests await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - - VerifyStartStopEvents(events, connect: true, expectedCount: 10); - - Assert.DoesNotContain(events, e => e.Event.EventName == "ConnectFailed"); + VerifyEvents(events, connect: true, expectedCount: 10); VerifyEventCounters(events, connectCount: 10, shouldHaveTransferedBytes: true, shouldHaveDatagrams: true); } }).Dispose(); } - private static void VerifyStartStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int expectedCount) - { - string startName = connect ? "ConnectStart" : "AcceptStart"; - (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == startName).ToArray(); - Assert.Equal(expectedCount, starts.Length); - foreach ((EventWrittenEventArgs Event, _) in starts) - { - object startPayload = Assert.Single(Event.Payload); - Assert.False(string.IsNullOrWhiteSpace(startPayload as string)); - } - - string stopName = connect ? "ConnectStop" : "AcceptStop"; - (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == stopName).ToArray(); - Assert.Equal(expectedCount, stops.Length); - Assert.All(stops, stop => Assert.Empty(stop.Event.Payload)); - - for (int i = 0; i < expectedCount; i++) - { - Assert.NotEqual(Guid.Empty, starts[i].ActivityId); - Assert.Equal(starts[i].ActivityId, stops[i].ActivityId); - } - } - private static async Task WaitForEventAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string name) { DateTime startTime = DateTime.UtcNow; @@ -452,6 +376,76 @@ namespace System.Net.Sockets.Tests } } + private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool connect, int? expectedCount, bool shouldHaveFailures = false) + { + bool start = false; + Guid startGuid = Guid.Empty; + bool seenFailures = false; + bool seenFailureAfterStart = false; + int numberOfStops = 0; + + foreach ((EventWrittenEventArgs Event, Guid ActivityId) in events) + { + Assert.False(Event.EventId == 0, $"Received an error event from EventSource: {Event.Message}"); + + if (Event.EventName.Contains("Connect") != connect) + { + continue; + } + + switch (Event.EventName) + { + case "ConnectStart": + case "AcceptStart": + Assert.False(start, "Start without a Stop"); + Assert.NotEqual(Guid.Empty, ActivityId); + startGuid = ActivityId; + seenFailureAfterStart = false; + start = true; + + string startAddress = Assert.IsType(Assert.Single(Event.Payload)); + Assert.Matches(@"^InterNetwork.*?:\d\d:{(?:\d{1,3},?)+}$", startAddress); + break; + + case "ConnectStop": + case "AcceptStop": + Assert.True(start, "Stop without a Start"); + Assert.Equal(startGuid, ActivityId); + startGuid = Guid.Empty; + numberOfStops++; + start = false; + + Assert.Empty(Event.Payload); + break; + + case "ConnectFailed": + case "AcceptFailed": + Assert.True(start, "Failed should come between Start and Stop"); + Assert.False(seenFailureAfterStart, "Start may only have one Failed event"); + Assert.Equal(startGuid, ActivityId); + seenFailureAfterStart = true; + seenFailures = true; + + Assert.Equal(2, Event.Payload.Count); + Assert.True(Enum.IsDefined((SocketError)Event.Payload[0])); + Assert.IsType(Event.Payload[1]); + break; + } + } + + Assert.False(start, "Start without a Stop"); + Assert.Equal(shouldHaveFailures, seenFailures); + + if (expectedCount.HasValue) + { + Assert.Equal(expectedCount, numberOfStops); + } + else + { + Assert.NotEqual(0, numberOfStops); + } + } + private static void VerifyEventCounters(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, int connectCount, bool connectOnly = false, bool shouldHaveTransferedBytes = false, bool shouldHaveDatagrams = false) { Dictionary eventCounters = events From 4f3e30e6d343d5eb47e77e978c74c235d0bfcf4c Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 23 Jun 2021 22:49:43 -0300 Subject: [PATCH 076/926] [mono][wasm] Fix compilation error on wasm (#54659) --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index c296c850147..b3eebcd7e38 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -1446,7 +1446,7 @@ namespace Microsoft.WebAssembly.Diagnostics { if (asyncLocal["name"].Value().EndsWith("__this")) asyncLocal["name"] = "this"; - else if (asyncLocal["name"].Value().Contains("<")) + else if (asyncLocal["name"].Value().Contains('<')) asyncLocal["name"] = Regex.Match(asyncLocal["name"].Value(), @"\<([^)]*)\>").Groups[1].Value; } return asyncLocals; From dae9156dd0d5b82c9f3fc98baeb5a75cc3f66211 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 23 Jun 2021 22:29:00 -0400 Subject: [PATCH 077/926] [wasm] Fix blazor/aot builds (#54651) `dotnet\packs\Microsoft.NET.Runtime.WebAssembly.Sdk\6.0.0-preview.7.21321.15\Sdk\WasmApp.Native.targets(342,5): error : Could not find AOT cross compiler at $(_MonoAotCrossCompilerPath)=` Make sure this is set for the aot path. --- src/mono/wasm/build/WasmApp.Native.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index c034763ca0f..e1de26e4a4c 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -359,6 +359,10 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ --> + + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) + + From 914a42217a6370c33364265da305e1765f8aae33 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 24 Jun 2021 05:52:40 +0200 Subject: [PATCH 078/926] [wasm] Enable fixed libraries tests (#54641) Fixed by https://github.com/dotnet/runtime/pull/52705 and https://github.com/dotnet/runtime/pull/52707 --- src/libraries/tests.proj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 5cad3c54088..b5638f2a00d 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -322,10 +322,8 @@ - - From 4facc363c445b51b63af8df2d278ce582546130e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 24 Jun 2021 00:00:01 -0400 Subject: [PATCH 079/926] Remove Version.Clone from AssemblyName.Clone (#54621) Version is immutable. --- .../src/System/Reflection/AssemblyName.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 0853bfff068..4291dded861 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -126,7 +126,7 @@ namespace System.Reflection _publicKey = (byte[]?)_publicKey?.Clone(), _publicKeyToken = (byte[]?)_publicKeyToken?.Clone(), _cultureInfo = _cultureInfo, - _version = (Version?)_version?.Clone(), + _version = _version, _flags = _flags, _codeBase = _codeBase, _hashAlgorithm = _hashAlgorithm, From f55390a2e57cfe38fa692032195ed5a9f073e1d9 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Wed, 23 Jun 2021 22:35:39 -0600 Subject: [PATCH 080/926] Fix sporadic double fd close (#54660) Fix https://github.com/dotnet/runtime/issues/54589 --- src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs index 8d876f30174..45d0547a391 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs @@ -46,7 +46,7 @@ namespace System.IO.Tests public void ThrowsNotSupportedExceptionForUnseekableFile() { using (var server = new AnonymousPipeServerStream(PipeDirection.Out)) - using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true)) + using (SafeFileHandle handle = new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), ownsHandle: false)) { Assert.Throws(() => MethodUnderTest(handle, Array.Empty(), 0)); } From 7833828914a42f8c99dfa6f18ccd47f99dc2b56e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 24 Jun 2021 08:58:33 +0200 Subject: [PATCH 081/926] [FileStream] add tests for device and UNC paths (#54545) * add a test for unseekable device by using a path to named pipe * add a test for seekable device by using DeviceID instead of drive letter * add a test for a UNC file path (local file share) --- .../System/IO/FileCleanupTestBase.cs | 2 +- .../FileStreamConformanceTests.Windows.cs | 179 ++++++++++++++++++ ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 1 + .../tests/System.IO.FileSystem.Tests.csproj | 2 + 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index a0542ef930c..02ffa607c94 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -79,7 +79,7 @@ namespace System.IO /// An optional index value to use as a suffix on the file name. Typically a loop index. /// The member name of the function calling this method. /// The line number of the function calling this method. - protected string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) => + protected virtual string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) => Path.Combine(TestDirectory, GetTestFileName(index, memberName, lineNumber)); /// Gets a test file name that is associated with the call site. diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs new file mode 100644 index 00000000000..1cfbd351824 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.ComponentModel; +using System.IO.Pipes; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.ServiceProcess; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public class UnseekableDeviceFileStreamConnectedConformanceTests : ConnectedStreamConformanceTests + { + protected override async Task CreateConnectedStreamsAsync() + { + string pipeName = FileSystemTest.GetNamedPipeServerStreamName(); + string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}"); + + var server = new NamedPipeServerStream(pipeName, PipeDirection.In); + var clienStream = new FileStream(File.OpenHandle(pipePath, FileMode.Open, FileAccess.Write, FileShare.None), FileAccess.Write); + + await server.WaitForConnectionAsync(); + + var serverStrean = new FileStream(new SafeFileHandle(server.SafePipeHandle.DangerousGetHandle(), true), FileAccess.Read); + + server.SafePipeHandle.SetHandleAsInvalid(); + + return (serverStrean, clienStream); + } + + protected override Type UnsupportedConcurrentExceptionType => null; + protected override bool UsableAfterCanceledReads => false; + protected override bool FullyCancelableOperations => false; + protected override bool BlocksOnZeroByteReads => OperatingSystem.IsWindows(); + protected override bool SupportsConcurrentBidirectionalUse => false; + } + + [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + public class SeekableDeviceFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests + { + protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) + { + string filePath = Path.GetFullPath(base.GetTestFilePath(index, memberName, lineNumber)); + string drive = Path.GetPathRoot(filePath); + StringBuilder volumeNameBuffer = new StringBuilder(filePath.Length + 1024); + + // the following method maps drive letter like "C:\" to a DeviceID (a DOS device path) + // example: "\\?\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\" + if (!GetVolumeNameForVolumeMountPoint(drive, volumeNameBuffer, volumeNameBuffer.Capacity)) + { + throw new Win32Exception(Marshal.GetLastPInvokeError(), "GetVolumeNameForVolumeMountPoint failed"); + } + + // instead of: + // 'C:\Users\x\AppData\Local\Temp\y\z + // we want something like: + // '\\.\Volume{724edb31-eaa5-4728-a4e3-f2474fd34ae2}\Users\x\AppData\Local\Temp\y\z + string devicePath = filePath.Replace(drive, volumeNameBuffer.ToString()); + Assert.StartsWith(@"\\?\", devicePath); +#if DEBUG + // we do want to test \\.\ prefix as well + devicePath = devicePath.Replace(@"\\?\", @"\\.\"); +#endif + + return devicePath; + } + + [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetVolumeNameForVolumeMountPointW", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)] + private static extern bool GetVolumeNameForVolumeMountPoint(string volumeName, StringBuilder uniqueVolumeName, int uniqueNameBufferCapacity); + } + + [PlatformSpecific(TestPlatforms.Windows)] // the test setup is Windows-specifc + [Collection("NoParallelTests")] // don't run in parallel, as file sharing logic is not thread-safe + [OuterLoop("Requires admin privileges to create a file share")] + [ConditionalClass(typeof(UncFilePathFileStreamStandaloneConformanceTests), nameof(CanShareFiles))] + public class UncFilePathFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests + { + public static bool CanShareFiles => _canShareFiles.Value; + + private static Lazy _canShareFiles = new Lazy(() => + { + if (!PlatformDetection.IsWindowsAndElevated || PlatformDetection.IsWindowsNanoServer) + { + return false; + } + + // the "Server Service" allows for file sharing. It can be disabled on some of our CI machines. + using (ServiceController sharingService = new ServiceController("Server")) + { + return sharingService.Status == ServiceControllerStatus.Running; + } + }); + + protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) + { + string testDirectoryPath = Path.GetFullPath(TestDirectory); + string shareName = new DirectoryInfo(testDirectoryPath).Name; + string fileName = GetTestFileName(index, memberName, lineNumber); + + SHARE_INFO_502 shareInfo = default; + shareInfo.shi502_netname = shareName; + shareInfo.shi502_path = testDirectoryPath; + shareInfo.shi502_remark = "folder created to test UNC file paths"; + shareInfo.shi502_max_uses = -1; + + int infoSize = Marshal.SizeOf(shareInfo); + IntPtr infoBuffer = Marshal.AllocCoTaskMem(infoSize); + + try + { + Marshal.StructureToPtr(shareInfo, infoBuffer, false); + + int shareResult = NetShareAdd(string.Empty, 502, infoBuffer, IntPtr.Zero); + + if (shareResult != 0 && shareResult != 2118) // is a failure that is not a NERR_DuplicateShare + { + throw new Exception($"Failed to create a file share, NetShareAdd returned {shareResult}"); + } + } + finally + { + Marshal.FreeCoTaskMem(infoBuffer); + } + + // now once the folder has been shared we can use "localhost" to access it: + // both type of slashes are valid, so let's test one for Debug and another for other configs +#if DEBUG + return @$"//localhost/{shareName}/{fileName}"; +#else + return @$"\\localhost\{shareName}\{fileName}"; +#endif + } + + protected override void Dispose(bool disposing) + { + string testDirectoryPath = Path.GetFullPath(TestDirectory); + string shareName = new DirectoryInfo(testDirectoryPath).Name; + + try + { + NetShareDel(string.Empty, shareName, 0); + } + finally + { + base.Dispose(disposing); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SHARE_INFO_502 + { + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_netname; + public uint shi502_type; + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_remark; + public int shi502_permissions; + public int shi502_max_uses; + public int shi502_current_uses; + [MarshalAs(UnmanagedType.LPWStr)] + public string shi502_path; + public IntPtr shi502_passwd; + public int shi502_reserved; + public IntPtr shi502_security_descriptor; + } + + [DllImport(Interop.Libraries.Netapi32)] + public static extern int NetShareAdd([MarshalAs(UnmanagedType.LPWStr)]string servername, int level, IntPtr buf, IntPtr parm_err); + + [DllImport(Interop.Libraries.Netapi32)] + public static extern int NetShareDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int reserved); + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 58669657e51..04d57626857 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -25,6 +25,7 @@ + diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 94cec86ce3a..bf845ce77b5 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -72,6 +72,7 @@ + @@ -80,6 +81,7 @@ + From 72deae88e543b815339a7ba5a3d83a649e90b555 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 24 Jun 2021 12:38:53 +0200 Subject: [PATCH 082/926] get last error before calling a method that might fail as well (#54667) --- .../src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 7a13a80610d..40c1e186668 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -44,8 +44,8 @@ namespace Microsoft.Win32.SafeHandles if (handle.IsInvalid) { - handle.Dispose(); Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + handle.Dispose(); // If we fail to open the file due to a path not existing, we need to know whether to blame // the file itself or its directory. If we're creating the file, then we blame the directory, @@ -70,8 +70,9 @@ namespace Microsoft.Win32.SafeHandles Interop.Sys.FileStatus status; if (Interop.Sys.FStat(handle, out status) != 0) { + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); handle.Dispose(); - throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), path); + throw Interop.GetExceptionForIoErrno(error, path); } if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) { From 1773f1666b548a17ea019fce8b62390c9713c089 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 24 Jun 2021 14:06:06 +0200 Subject: [PATCH 083/926] exclude fragile tests (#54671) --- .../Common/tests/System/Net/Http/HttpClientHandlerTest.cs | 1 + .../System.Net.WebSockets.Client/tests/SendReceiveTest.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 33392864210..912f356c5d6 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -577,6 +577,7 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly_MemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54655", TestPlatforms.Browser)] public async Task GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly(string newline, string fold, bool dribble) { if (LoopbackServerFactory.Version >= HttpVersion20.Value) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 6266f01e8ed..bc70d2a4fdf 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -217,6 +217,7 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task ReceiveAsync_MultipleOutstandingReceiveOperations_Throws(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) @@ -320,6 +321,7 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task SendReceive_VaryingLengthBuffers_Success(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) From b317d0624f36caf7b7c6f61058e9f6663803f30c Mon Sep 17 00:00:00 2001 From: LateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com> Date: Thu, 24 Jun 2021 20:30:55 +0800 Subject: [PATCH 084/926] UdpClient with span support (#53429) Add API from #864 --- .../ref/System.Net.Sockets.cs | 7 + .../src/System/Net/Sockets/UDPClient.cs | 188 ++++++++++++++--- .../System/Net/Sockets/UdpReceiveResult.cs | 2 +- .../SendReceive/SendReceiveUdpClient.cs | 14 +- .../tests/FunctionalTests/TelemetryTest.cs | 4 +- .../tests/FunctionalTests/UdpClientTest.cs | 194 ++++++++++++++---- 6 files changed, 339 insertions(+), 70 deletions(-) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index 0ea7f6b7ce7..8ed4a71a4e8 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -760,12 +760,19 @@ namespace System.Net.Sockets public void JoinMulticastGroup(System.Net.IPAddress multicastAddr, System.Net.IPAddress localAddress) { } public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; } public System.Threading.Tasks.Task ReceiveAsync() { throw null; } + public System.Threading.Tasks.ValueTask ReceiveAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public int Send(byte[] dgram, int bytes) { throw null; } + public int Send(System.ReadOnlySpan datagram) {throw null; } public int Send(byte[] dgram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public int Send(System.ReadOnlySpan datagram, System.Net.IPEndPoint? endPoint) { throw null; } public int Send(byte[] dgram, int bytes, string? hostname, int port) { throw null; } + public int Send(System.ReadOnlySpan datagram, string? hostname, int port) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, System.Net.IPEndPoint? endPoint) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, System.Net.IPEndPoint? endPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) { throw null; } + public System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory datagram, string? hostname, int port, System.Threading.CancellationToken cancellationToken = default) { throw null; } } public partial struct UdpReceiveResult : System.IEquatable { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs index e27a77da1f8..246483d0815 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using System.Runtime.Versioning; +using System.Threading; namespace System.Net.Sockets { @@ -600,9 +601,46 @@ namespace System.Net.Sockets public Task SendAsync(byte[] datagram, int bytes) => SendAsync(datagram, bytes, null); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, CancellationToken cancellationToken = default) => + SendAsync(datagram, null, cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, string? hostname, int port) => SendAsync(datagram, bytes, GetEndpoint(hostname, port)); + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, string? hostname, int port, CancellationToken cancellationToken = default) => + SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken); + public Task SendAsync(byte[] datagram, int bytes, IPEndPoint? endPoint) { ValidateDatagram(datagram, bytes, endPoint); @@ -618,6 +656,39 @@ namespace System.Net.Sockets } } + /// + /// Sends a UDP datagram asynchronously to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// + /// The token to monitor for cancellation requests. The default value is None. + /// + /// A that represents the asynchronous send operation. The value of its Result property contains the number of bytes sent. + /// has already established a default remote host and is not . + /// The is closed. + /// An error occurred when accessing the socket. + public ValueTask SendAsync(ReadOnlyMemory datagram, IPEndPoint? endPoint, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + if (endPoint is null) + { + return _clientSocket.SendAsync(datagram, SocketFlags.None, cancellationToken); + } + if (_active) + { + // Do not allow sending packets to arbitrary host when connected. + throw new InvalidOperationException(SR.net_udpconnected); + } + CheckForBroadcast(endPoint.Address); + return _clientSocket.SendToAsync(datagram, SocketFlags.None, endPoint, cancellationToken); + } + public Task ReceiveAsync() { ThrowIfDisposed(); @@ -639,6 +710,36 @@ namespace System.Net.Sockets } } + /// + /// Returns a UDP datagram asynchronously that was sent by a remote host. + /// + /// + /// The token to monitor for cancellation requests. + /// + /// A representing the asynchronous operation. + /// The underlying has been closed. + /// An error occurred when accessing the socket. + public ValueTask ReceiveAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + + return WaitAndWrap(_clientSocket.ReceiveFromAsync( + _buffer, + SocketFlags.None, + _family == AddressFamily.InterNetwork ? IPEndPointStatics.Any : IPEndPointStatics.IPv6Any, cancellationToken)); + + async ValueTask WaitAndWrap(ValueTask task) + { + SocketReceiveFromResult result = await task.ConfigureAwait(false); + + byte[] buffer = result.ReceivedBytes < MaxUDPSize ? + _buffer.AsSpan(0, result.ReceivedBytes).ToArray() : + _buffer; + + return new UdpReceiveResult(buffer, (IPEndPoint)result.RemoteEndPoint); + } + } + private void CreateClientSocket() { // Common initialization code. @@ -892,45 +993,59 @@ namespace System.Net.Sockets return Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint); } - - // Sends a UDP datagram to the specified port on the specified remote host. - public int Send(byte[] dgram, int bytes, string? hostname, int port) + /// + /// Sends a UDP datagram to the host at the specified remote endpoint. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// An that represents the host and port to which to send the datagram. + /// + /// The number of bytes sent. + /// has already established a default remote host and is not . + /// is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram, IPEndPoint? endPoint) { ThrowIfDisposed(); - if (dgram == null) - { - throw new ArgumentNullException(nameof(dgram)); - } - if (_active && ((hostname != null) || (port != 0))) + if (_active && endPoint != null) { // Do not allow sending packets to arbitrary host when connected throw new InvalidOperationException(SR.net_udpconnected); } - if (hostname == null || port == 0) + if (endPoint == null) { - return Client.Send(dgram, 0, bytes, SocketFlags.None); + return Client.Send(datagram, SocketFlags.None); } - IPAddress[] addresses = Dns.GetHostAddresses(hostname); + CheckForBroadcast(endPoint.Address); - int i = 0; - for (; i < addresses.Length && !IsAddressFamilyCompatible(addresses[i].AddressFamily); i++) - { - ; // just count the addresses - } - - if (addresses.Length == 0 || i == addresses.Length) - { - throw new ArgumentException(SR.net_invalidAddressList, nameof(hostname)); - } - - CheckForBroadcast(addresses[i]); - IPEndPoint ipEndPoint = new IPEndPoint(addresses[i], port); - return Client.SendTo(dgram, 0, bytes, SocketFlags.None, ipEndPoint); + return Client.SendTo(datagram, SocketFlags.None, endPoint); } + // Sends a UDP datagram to the specified port on the specified remote host. + public int Send(byte[] dgram, int bytes, string? hostname, int port) => Send(dgram, bytes, GetEndpoint(hostname, port)); + + /// + /// Sends a UDP datagram to a specified port on a specified remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// + /// The name of the remote host to which you intend to send the datagram. + /// + /// + /// The remote port number with which you intend to communicate. + /// + /// The number of bytes sent. + /// The has already established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram, string? hostname, int port) => Send(datagram, GetEndpoint(hostname, port)); // Sends a UDP datagram to a remote host. public int Send(byte[] dgram, int bytes) @@ -950,6 +1065,29 @@ namespace System.Net.Sockets return Client.Send(dgram, 0, bytes, SocketFlags.None); } + /// + /// Sends a UDP datagram to a remote host. + /// + /// + /// An of Type that specifies the UDP datagram that you intend to send. + /// + /// The number of bytes sent. + /// The has not established a default remote host. + /// The is closed. + /// An error occurred when accessing the socket. + public int Send(ReadOnlySpan datagram) + { + ThrowIfDisposed(); + + if (!_active) + { + // only allowed on connected socket + throw new InvalidOperationException(SR.net_notconnected); + } + + return Client.Send(datagram, SocketFlags.None); + } + private void ThrowIfDisposed() { if (_disposed) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs index 25af4a45bba..55535f05830 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; namespace System.Net.Sockets { /// - /// Presents UDP receive result information from a call to the method + /// Presents UDP receive result information from a call to the and method /// public struct UdpReceiveResult : IEquatable { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs index ce83af9bb67..252b862bce1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs @@ -11,8 +11,8 @@ namespace System.Net.Sockets.Tests { [OuterLoop] [Theory] - [MemberData(nameof(Loopbacks))] - public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress) + [MemberData(nameof(LoopbacksAndUseMemory))] + public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress, bool useMemoryOverload) { IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; @@ -66,7 +66,7 @@ namespace System.Net.Sockets.Tests random.NextBytes(sendBuffer); sendBuffer[0] = (byte)sentDatagrams; - int sent = await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); + int sent = useMemoryOverload ? await right.SendAsync(new ReadOnlyMemory(sendBuffer), leftEndpoint) : await right.SendAsync(sendBuffer, DatagramSize, leftEndpoint); Assert.True(receiverAck.Wait(AckTimeout)); receiverAck.Reset(); @@ -85,5 +85,13 @@ namespace System.Net.Sockets.Tests } } } + + public static readonly object[][] LoopbacksAndUseMemory = new object[][] + { + new object[] { IPAddress.IPv6Loopback, true }, + new object[] { IPAddress.IPv6Loopback, false }, + new object[] { IPAddress.Loopback, true }, + new object[] { IPAddress.Loopback, false }, + }; } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index 82bed83eabe..7b507a18f40 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -325,8 +325,8 @@ namespace System.Net.Sockets.Tests await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, false).ConfigureAwait(false); await new SendReceive_Apm(null).SendRecv_Stream_TCP(IPAddress.Loopback, true).ConfigureAwait(false); - await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false); - await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback).ConfigureAwait(false); + await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false); + await new SendReceiveUdpClient().SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress.Loopback, false).ConfigureAwait(false); await new NetworkStreamTest().CopyToAsync_AllDataCopied(4096, true).ConfigureAwait(false); await new NetworkStreamTest().Timeout_Roundtrips().ConfigureAwait(false); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 3b447962950..66f69e1ce70 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -59,7 +59,6 @@ namespace System.Net.Sockets.Tests AssertExtensions.Throws("localEP", () => new UdpClient(null)); } - [OuterLoop] [Fact] public void Ctor_CanSend() { @@ -70,7 +69,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Ctor_Int_CanSend() { @@ -88,7 +86,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv4_CanSend() { @@ -106,7 +103,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Ctor_IntAddressFamily_IPv6_CanSend() { @@ -124,7 +120,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Ctor_IPEndPoint_CanSend() { @@ -142,7 +137,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Ctor_StringInt_CanSend() { @@ -191,6 +185,21 @@ namespace System.Net.Sockets.Tests Assert.Throws(() => udpClient.Send(null, 0, remoteEP)); Assert.Throws(() => udpClient.Send(null, 0)); Assert.Throws(() => udpClient.Send(null, 0, "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), remoteEP)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan())); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(), "localhost", 0)); + + Assert.Throws(() => {udpClient.SendAsync(null, 0, remoteEP);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0);}); + Assert.Throws(() => {udpClient.SendAsync(null, 0, "localhost", 0);}); + + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), remoteEP)); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory())); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(), "localhost", 0)); + + Assert.Throws(() => {udpClient.ReceiveAsync();}); + Assert.Throws(() => udpClient.ReceiveAsync(default)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] @@ -321,7 +330,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void BeginSend_NegativeBytes_Throws() { @@ -337,7 +345,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void BeginSend_BytesMoreThanArrayLength_Throws() { @@ -353,7 +360,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void BeginSend_AsyncOperationCompletes_Success() { @@ -377,8 +383,12 @@ namespace System.Net.Sockets.Tests AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, "localhost", 0)); AssertExtensions.Throws("dgram", () => udpClient.Send(null, 0, new IPEndPoint(IPAddress.Loopback, 0))); Assert.Throws(() => udpClient.Send(new byte[1], 1)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]))); udpClient.Active = true; Assert.Throws(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); } } @@ -389,10 +399,17 @@ namespace System.Net.Sockets.Tests using (var udpClient = new UdpClient("localhost", 0)) { Assert.Throws(() => udpClient.Send(new byte[1], 1, "localhost", 0)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), "localhost", 0)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, "localhost", 0);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", 0)); + + Assert.Throws(() => udpClient.Send(new byte[1], 1, null, UnusedPort)); + Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), null, UnusedPort)); + Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, null, UnusedPort);}); + Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), null, UnusedPort)); } } - [OuterLoop] [Fact] public void Client_Idempotent() { @@ -421,7 +438,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public async Task ConnectAsync_StringHost_Success() { @@ -431,7 +447,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public async Task ConnectAsync_IPAddressHost_Success() { @@ -441,7 +456,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Connect_StringHost_Success() { @@ -451,7 +465,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Fact] public void Connect_IPAddressHost_Success() { @@ -468,7 +481,6 @@ namespace System.Net.Sockets.Tests _waitHandle.Set(); } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.Windows)] // Udp.AllowNatTraversal only supported on Windows [InlineData(true, IPProtectionLevel.Unrestricted)] @@ -482,7 +494,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Theory] [PlatformSpecific(TestPlatforms.AnyUnix)] // Udp.AllowNatTraversal throws PNSE on Unix [InlineData(true)] @@ -495,7 +506,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -507,32 +517,55 @@ namespace System.Net.Sockets.Tests using (var sender = new UdpClient(new IPEndPoint(address, 0))) { sender.Send(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + AssertReceive(receiver); - IPEndPoint remoteEP = null; - byte[] data = receiver.Receive(ref remoteEP); - Assert.NotNull(remoteEP); - Assert.InRange(data.Length, 1, int.MaxValue); + sender.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + AssertReceive(receiver); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public void Send_Receive_With_HostName_Success(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + sender.Send(new byte[1], 1, "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + AssertReceive(receiver); + + sender.Send(new ReadOnlySpan(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + AssertReceive(receiver); } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public void Send_Receive_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) { sender.Send(new byte[1], 1); + AssertReceive(receiver); - IPEndPoint remoteEP = null; - byte[] data = receiver.Receive(ref remoteEP); - Assert.NotNull(remoteEP); - Assert.InRange(data.Length, 1, int.MaxValue); + sender.Send(new ReadOnlySpan(new byte[1])); + AssertReceive(receiver); } } - [OuterLoop] + private static void AssertReceive(UdpClient receiver) + { + IPEndPoint remoteEP = null; + byte[] data = receiver.Receive(ref remoteEP); + Assert.NotNull(remoteEP); + Assert.InRange(data.Length, 1, int.MaxValue); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -549,7 +582,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -571,7 +603,6 @@ namespace System.Net.Sockets.Tests [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public void BeginEndSend_BeginEndReceive_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) @@ -586,7 +617,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] [Theory] [InlineData(false)] [InlineData(true)] @@ -598,28 +628,114 @@ namespace System.Net.Sockets.Tests using (var sender = new UdpClient(new IPEndPoint(address, 0))) { await sender.SendAsync(new byte[1], 1, new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); + await AssertReceiveAsync(receiver); + } + } - UdpReceiveResult result = await receiver.ReceiveAsync(); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_ReceiveAsync_With_HostName_Success(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await sender.SendAsync(new byte[1], "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port); + await AssertReceiveAsync(receiver); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ReceiveAsync_Cancel_Throw(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + { + using (var timeoutCts = new CancellationTokenSource(1)) + { + await Assert.ThrowsAnyAsync(() => receiver.ReceiveAsync(timeoutCts.Token).AsTask()); + } } } [Fact] [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix - [OuterLoop] public async Task SendAsync_ReceiveAsync_Connected_Success() { using (var receiver = new UdpClient("localhost", 0)) using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) { await sender.SendAsync(new byte[1], 1); + await AssertReceiveAsync(receiver); - UdpReceiveResult result = await receiver.ReceiveAsync(); - Assert.NotNull(result.RemoteEndPoint); - Assert.NotNull(result.Buffer); - Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + await sender.SendAsync(new ReadOnlyMemory(new byte[1])); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null); + await AssertReceiveAsync(receiver); + + await sender.SendAsync(new ReadOnlyMemory(new byte[1]), null, 0); + await AssertReceiveAsync(receiver); + } + } + + private static async Task AssertReceiveAsync(UdpClient receiver) + { + UdpReceiveResult result = await receiver.ReceiveAsync(); + Assert.NotNull(result.RemoteEndPoint); + Assert.NotNull(result.Buffer); + Assert.InRange(result.Buffer.Length, 1, int.MaxValue); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_Connected_PreCanceled_Throws() + { + using (var receiver = new UdpClient("localhost", 0)) + using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new CancellationToken(true)).AsTask()); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix + public async Task SendAsync_With_HostName_PreCanceled_Throws(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), "localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port, new CancellationToken(true)).AsTask()); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SendAsync_PreCanceled_Throws(bool ipv4) + { + IPAddress address = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback; + + using (var receiver = new UdpClient(new IPEndPoint(address, 0))) + using (var sender = new UdpClient(new IPEndPoint(address, 0))) + { + await Assert.ThrowsAnyAsync(() => sender.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port), new CancellationToken(true)).AsTask()); } } From 5c2ff179be30c6618baa6473feda19fcaebe5116 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 24 Jun 2021 10:24:47 -0400 Subject: [PATCH 085/926] Add PeriodicTimer (#53899) --- .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/Threading/PeriodicTimer.cs | 220 ++++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 6 + .../tests/System.Runtime.Tests.csproj | 1 + .../System/Threading/PeriodicTimerTests.cs | 203 ++++++++++++++++ 5 files changed, 431 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs create mode 100644 src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 2af41ff1342..1186b2c606f 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1069,6 +1069,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs new file mode 100644 index 00000000000..b16c4d067b7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PeriodicTimer.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.ExceptionServices; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; + +namespace System.Threading +{ + /// Provides a periodic timer that enables waiting asynchronously for timer ticks. + /// + /// This timer is intended to be used only by a single consumer at a time: only one call to + /// may be in flight at any given moment. may be used concurrently with an active + /// to interrupt it and cause it to return false. + /// + public sealed class PeriodicTimer : IDisposable + { + /// The underlying timer. + private readonly TimerQueueTimer _timer; + /// All state other than the _timer, so that the rooted timer's callback doesn't indirectly root itself by referring to _timer. + private readonly State _state; + + /// Initializes the timer. + /// The time interval between invocations of callback.. + /// must be represent a number of milliseconds larger than 0 and smaller than . + public PeriodicTimer(TimeSpan period) + { + long ms = (long)period.TotalMilliseconds; + if (ms < 1 || ms > Timer.MaxSupportedTimeout) + { + GC.SuppressFinalize(this); + throw new ArgumentOutOfRangeException(nameof(period)); + } + + _state = new State(); + _timer = new TimerQueueTimer(s => ((State)s!).Signal(), _state, (uint)ms, (uint)ms, flowExecutionContext: false); + } + + /// Wait for the next tick of the timer, or for the timer to be stopped. + /// + /// A to use to cancel the asynchronous wait. If cancellation is requested, it affects only the single wait operation; + /// the underlying timer continues firing. + /// + /// A task that will be completed due to the timer firing, being called to stop the timer, or cancellation being requested. + /// + /// The behaves like an auto-reset event, in that multiple ticks are coalesced into a single tick if they occur between + /// calls to . Similarly, a call to will void any tick not yet consumed. + /// may only be used by one consumer at a time, and may be used concurrently with a single call to . + /// + public ValueTask WaitForNextTickAsync(CancellationToken cancellationToken = default) => + _state.WaitForNextTickAsync(this, cancellationToken); + + /// Stops the timer and releases associated managed resources. + /// + /// will cause an active wait with to complete with a value of false. + /// All subsequent invocations will produce a value of false. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + _timer.Close(); + _state.Signal(stopping: true); + } + + ~PeriodicTimer() => Dispose(); + + /// Core implementation for the periodic timer. + private sealed class State : IValueTaskSource + { + /// The associated . + /// + /// This should refer to the parent instance only when there's an active waiter, and be null when there + /// isn't. The TimerQueueTimer in the PeriodicTimer strongly roots itself, and it references this State + /// object: + /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --ref--> null + /// If this State object then references the PeriodicTimer, it creates a strongly-rooted cycle that prevents anything from + /// being GC'd: + /// PeriodicTimer (finalizable) --ref--> TimerQueueTimer (rooted) --ref--> State --v + /// ^--ref-------------------------------------------------------------------| + /// When this field is null, the cycle is broken, and dropping all references to the PeriodicTimer allows the + /// PeriodicTimer to be finalized and unroot the TimerQueueTimer. Thus, we keep this field set during + /// so that the timer roots any async continuation chain awaiting it, and then keep it unset otherwise so that everything + /// can be GC'd appropriately. + /// + private PeriodicTimer? _owner; + /// Core of the implementation. + private ManualResetValueTaskSourceCore _mrvtsc; + /// Cancellation registration for any active call. + private CancellationTokenRegistration _ctr; + /// Whether the timer has been stopped. + private bool _stopped; + /// Whether there's a pending notification to be received. This could be due to the timer firing, the timer being stopped, or cancellation being requested. + private bool _signaled; + /// Whether there's a call in flight. + private bool _activeWait; + + /// Wait for the next tick of the timer, or for the timer to be stopped. + public ValueTask WaitForNextTickAsync(PeriodicTimer owner, CancellationToken cancellationToken) + { + lock (this) + { + if (_activeWait) + { + // WaitForNextTickAsync should only be used by one consumer at a time. Failing to do so is an error. + ThrowHelper.ThrowInvalidOperationException(); + } + + // If cancellation has already been requested, short-circuit. + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + // If the timer has a pending tick or has been stopped, we can complete synchronously. + if (_signaled) + { + // Reset the signal for subsequent consumers, but only if we're not stopped. Since. + // stopping the timer is one way, any subsequent calls should also complete synchronously + // with false, and thus we leave _signaled pinned at true. + if (!_stopped) + { + _signaled = false; + } + + return new ValueTask(!_stopped); + } + + Debug.Assert(!_stopped, "Unexpectedly stopped without _signaled being true."); + + // Set up for the wait and return a task that will be signaled when the + // timer fires, stop is called, or cancellation is requested. + _owner = owner; + _activeWait = true; + _ctr = cancellationToken.UnsafeRegister(static (state, cancellationToken) => ((State)state!).Signal(cancellationToken: cancellationToken), this); + + return new ValueTask(this, _mrvtsc.Version); + } + } + + /// Signal that the timer has either fired or been stopped. + public void Signal(bool stopping = false, CancellationToken cancellationToken = default) + { + bool completeTask = false; + + lock (this) + { + _stopped |= stopping; + if (!_signaled) + { + _signaled = true; + completeTask = _activeWait; + } + } + + if (completeTask) + { + if (cancellationToken.IsCancellationRequested) + { + // If cancellation is requested just before the UnsafeRegister call, it's possible this will end up being invoked + // as part of the WaitForNextTickAsync call and thus as part of holding the lock. The goal of completeTask + // was to escape that lock, so that we don't invoke any synchronous continuations from the ValueTask as part + // of completing _mrvtsc. However, in that case, we also haven't returned the ValueTask to the caller, so there + // won't be any continuations yet, which makes this safe. + _mrvtsc.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException(cancellationToken))); + } + else + { + Debug.Assert(!Monitor.IsEntered(this)); + _mrvtsc.SetResult(true); + } + } + } + + /// + bool IValueTaskSource.GetResult(short token) + { + // Dispose of the cancellation registration. This is done outside of the below lock in order + // to avoid a potential deadlock due to waiting for a concurrent cancellation callback that might + // in turn try to take the lock. For valid usage, GetResult is only called once _ctr has been + // successfully initialized before WaitForNextTickAsync returns to its synchronous caller, and + // there should be no race conditions accessing it, as concurrent consumption is invalid. If there + // is invalid usage, with GetResult used erroneously/concurrently, the worst that happens is cancellation + // may not take effect for the in-flight operation, with its registration erroneously disposed. + // Note we use Dispose rather than Unregister (which wouldn't risk deadlock) so that we know that thecancellation callback associated with this operation + // won't potentially still fire after we've completed this GetResult and a new operation + // has potentially started. + _ctr.Dispose(); + + lock (this) + { + try + { + _mrvtsc.GetResult(token); + } + finally + { + _mrvtsc.Reset(); + _ctr = default; + _activeWait = false; + _owner = null; + if (!_stopped) + { + _signaled = false; + } + } + + return !_stopped; + } + } + + /// + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _mrvtsc.GetStatus(token); + + /// + void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _mrvtsc.OnCompleted(continuation, state, token, flags); + } + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 39a1cfc4b81..4f9afb90503 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11651,6 +11651,12 @@ namespace System.Threading PublicationOnly = 1, ExecutionAndPublication = 2, } + public sealed class PeriodicTimer : System.IDisposable + { + public PeriodicTimer(System.TimeSpan period) { } + public System.Threading.Tasks.ValueTask WaitForNextTickAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } + public void Dispose() { } + } public static partial class Timeout { public const int Infinite = -1; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 2ca6739e9c6..db3b3dcd1b5 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -233,6 +233,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs new file mode 100644 index 00000000000..92367b8a467 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Tests +{ + public class PeriodicTimerTests + { + [Fact] + public void Ctor_InvalidArguments_Throws() + { + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(-1))); + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.Zero)); + AssertExtensions.Throws("period", () => new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue))); + } + + [Theory] + [InlineData(1)] + [InlineData(uint.MaxValue - 1)] + public void Ctor_ValidArguments_Succeeds(uint milliseconds) + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(milliseconds)); + } + + [Fact] + public async Task Dispose_Idempotent() + { + var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + Assert.True(await timer.WaitForNextTickAsync()); + + for (int i = 0; i < 2; i++) + { + timer.Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + + ((IDisposable)timer).Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + } + } + + [Fact] + public async Task WaitForNextTickAsync_TimerFires_ReturnsTrue() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + await Task.Delay(100); + for (int i = 0; i < 3; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + timer.Dispose(); + Assert.False(timer.WaitForNextTickAsync().Result); + } + + [Fact] + public async Task WaitForNextTickAsync_Dispose_ReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + ValueTask task = timer.WaitForNextTickAsync(); + timer.Dispose(); + Assert.False(await task); + } + + [Fact] + public async Task WaitForNextTickAsync_ConcurrentDispose_ReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + _ = Task.Run(async delegate + { + await Task.Delay(1); + timer.Dispose(); + }); + + Assert.False(await timer.WaitForNextTickAsync()); + } + + [Fact] + public async Task WaitForNextTickAsync_ConcurrentDisposeAfterTicks_EventuallyReturnsFalse() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + for (int i = 0; i < 5; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + + _ = Task.Run(async delegate + { + await Task.Delay(1); + timer.Dispose(); + }); + + while (await timer.WaitForNextTickAsync()); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void PeriodicTimer_NoActiveOperations_TimerNotRooted() + { + WeakReference timer = Create(); + + WaitForTimerToBeCollected(timer, expected: true); + + [MethodImpl(MethodImplOptions.NoInlining)] + static WeakReference Create() => + new WeakReference(new PeriodicTimer(TimeSpan.FromMilliseconds(1))); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public async Task PeriodicTimer_ActiveOperations_TimerRooted() + { + (WeakReference timer, ValueTask task) = Create(); + + WaitForTimerToBeCollected(timer, expected: false); + + Assert.True(await task); + + WaitForTimerToBeCollected(timer, expected: true); + + [MethodImpl(MethodImplOptions.NoInlining)] + static (WeakReference, ValueTask) Create() + { + var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + ValueTask task = timer.WaitForNextTickAsync(); + return (new WeakReference(timer), task); + } + } + + [Fact] + public void WaitForNextTickAsync_WaitAlreadyInProgress_Throws() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + ValueTask task = timer.WaitForNextTickAsync(); + Assert.False(task.IsCompleted); + + Assert.Throws(() => timer.WaitForNextTickAsync()); + + Assert.False(task.IsCompleted); + + timer.Dispose(); + Assert.True(task.IsCompleted); + Assert.False(task.Result); + } + + [Fact] + public void WaitForNextTickAsync_CanceledBeforeWait_CompletesSynchronously() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + ValueTask task = timer.WaitForNextTickAsync(cts.Token); + Assert.True(task.IsCanceled); + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + } + + [Fact] + public void WaitForNextTickAsync_CanceledAfterWait_CancelsOperation() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(uint.MaxValue - 1)); + + var cts = new CancellationTokenSource(); + + ValueTask task = timer.WaitForNextTickAsync(cts.Token); + cts.Cancel(); + + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + } + + [Fact] + public async Task WaitForNextTickAsync_CanceledWaitThenWaitAgain_Succeeds() + { + using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); + + ValueTask task = timer.WaitForNextTickAsync(new CancellationToken(true)); + Assert.ThrowsAny(() => task.Result); + + var cts = new CancellationTokenSource(); + task = timer.WaitForNextTickAsync(cts.Token); + cts.Cancel(); + Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); + + for (int i = 0; i < 10; i++) + { + Assert.True(await timer.WaitForNextTickAsync()); + } + } + + private static void WaitForTimerToBeCollected(WeakReference timer, bool expected) + { + Assert.Equal(expected, SpinWait.SpinUntil(() => + { + GC.Collect(); + return !timer.TryGetTarget(out _); + }, TimeSpan.FromSeconds(expected ? 5 : 0.5))); + } + } +} From 2ac023c8e9df61fe4557212cbd13956042fb47c5 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Thu, 24 Jun 2021 16:31:07 +0200 Subject: [PATCH 086/926] process more TLS frames at one when available (#50815) * process more TLS frames when available * add SslStream.Implementation.cs * remove extra comment * add back DefaultRequestHeaders_SentUnparsed * feedback from review * fix winhttp * fix linker test * feedback from review * feedback from review * feedback from review * make EnsureFullTlsFrame async * feedback from review --- .../Interop.Ssl.cs | 2 +- .../Interop.OpenSsl.cs | 3 - .../Net/Http/SchSendAuxRecordHttpTest.cs | 127 ------- .../FunctionalTests/PlatformHandlerTest.cs | 12 - ...ttp.WinHttpHandler.Functional.Tests.csproj | 2 - .../FunctionalTests/SocketsHttpHandlerTest.cs | 5 - .../System.Net.Http.Functional.Tests.csproj | 2 - .../src/System/Net/Logging/NetEventSource.cs | 2 +- .../src/System/Net/Security/SecureChannel.cs | 20 +- .../Net/Security/SslStream.Implementation.cs | 316 ++++++++++++------ .../Net/Security/SslStreamPal.Android.cs | 13 +- .../System/Net/Security/SslStreamPal.OSX.cs | 18 +- .../System/Net/Security/SslStreamPal.Unix.cs | 7 +- .../Net/Security/SslStreamPal.Windows.cs | 8 +- .../src/System/Net/Security/TlsFrameHelper.cs | 5 + .../SslStreamSchSendAuxRecordTest.cs | 125 ------- .../System.Net.Security.Tests.csproj | 1 - 17 files changed, 248 insertions(+), 420 deletions(-) delete mode 100644 src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs delete mode 100644 src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs index 762d352a6f6..6f0c53bac51 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs @@ -166,7 +166,7 @@ internal static partial class Interop out int bytesRead); internal static unsafe PAL_SSLStreamStatus SSLStreamRead( SafeSslHandle sslHandle, - Span buffer, + ReadOnlySpan buffer, out int bytesRead) { fixed (byte* bufferPtr = buffer) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 42276ab2b04..604ac8b4c79 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -2,19 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Net.Http; using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; internal static partial class Interop diff --git a/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs b/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs deleted file mode 100644 index 0348db4d357..00000000000 --- a/src/libraries/Common/tests/System/Net/Http/SchSendAuxRecordHttpTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Net.Test.Common; -using System.Security.Authentication; -using System.Threading.Tasks; - -using Xunit; -using Xunit.Abstractions; - -namespace System.Net.Http.Functional.Tests -{ -#if WINHTTPHANDLER_TEST - using HttpClientHandler = System.Net.Http.WinHttpClientHandler; -#endif - - public abstract class SchSendAuxRecordHttpTest : HttpClientHandlerTestBase - { - public SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } - - private class CircularBuffer - { - public CircularBuffer(int size) => _buffer = new char[size]; - - private char[] _buffer; - private int _lastBytesWriteIndex = 0; - private int _size = 0; - - public void Add(string value) - { - foreach (char ch in value) - { - _buffer[_lastBytesWriteIndex] = ch; - - _lastBytesWriteIndex = ++_lastBytesWriteIndex % _buffer.Length; - _size = Math.Min(_buffer.Length, ++_size); - } - } - - public bool Equals(string value) - { - if (value.Length != _size) - return false; - - for (int i = 0; i < _size; i++) - { - if (_buffer[(_lastBytesWriteIndex + i) % _buffer.Length] != value[i]) - return false; - } - - return true; - } - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task HttpClient_ClientUsesAuxRecord_Ok() - { - var options = new HttpsTestServer.Options(); - options.AllowedProtocols = SslProtocols.Tls; - - using (var server = new HttpsTestServer(options)) - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (HttpClient client = CreateHttpClient(handler)) - { - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; - server.Start(); - - var tasks = new Task[2]; - - bool serverAuxRecordDetected = false; - bool serverAuxRecordDetectedInconclusive = false; - int serverTotalBytesReceived = 0; - int serverChunks = 0; - - CircularBuffer buffer = new CircularBuffer(4); - - tasks[0] = server.AcceptHttpsClientAsync((requestString) => - { - - buffer.Add(requestString); - - serverTotalBytesReceived += requestString.Length; - - if (serverTotalBytesReceived == 1 && serverChunks == 0) - { - serverAuxRecordDetected = true; - } - - serverChunks++; - - // Test is inconclusive if any non-CBC cipher is used: - if (server.Stream.CipherAlgorithm == CipherAlgorithmType.None || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - serverAuxRecordDetectedInconclusive = true; - } - - // Detect end of HTML request - if (buffer.Equals("\r\n\r\n")) - { - return Task.FromResult(HttpsTestServer.Options.DefaultResponseString); - } - else - { - return Task.FromResult(null); - } - }); - - string requestUriString = "https://localhost:" + server.Port.ToString(); - tasks[1] = client.GetStringAsync(requestUriString); - - await tasks.WhenAllOrAnyFailed(15 * 1000); - - if (serverAuxRecordDetectedInconclusive) - { - _output.WriteLine("Test inconclusive: The Operating system preferred a non-CBC or Null cipher."); - } - else - { - Assert.True(serverAuxRecordDetected, "Server reports: Client auxiliary record not detected."); - } - } - } - } -} diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs index 9e384c292c8..9e6d360d9ac 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/PlatformHandlerTest.cs @@ -135,11 +135,6 @@ namespace System.Net.Http.Functional.Tests public PlatformHandler_HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { } } - public sealed class PlatformHandler_SchSendAuxRecordHttpTest : SchSendAuxRecordHttpTest - { - public PlatformHandler_SchSendAuxRecordHttpTest(ITestOutputHelper output) : base(output) { } - } - public sealed class PlatformHandler_HttpClientHandlerTest : HttpClientHandlerTest { public PlatformHandler_HttpClientHandlerTest(ITestOutputHelper output) : base(output) { } @@ -299,13 +294,6 @@ namespace System.Net.Http.Functional.Tests public PlatformHandler_HttpClientHandler_Proxy_Http2_Test(ITestOutputHelper output) : base(output) { } } - public sealed class PlatformHandler_SchSendAuxRecordHttp_Http2_Test : SchSendAuxRecordHttpTest - { - protected override Version UseVersion => HttpVersion20.Value; - - public PlatformHandler_SchSendAuxRecordHttp_Http2_Test(ITestOutputHelper output) : base(output) { } - } - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))] public sealed class PlatformHandler_HttpClientHandler_Http2_Test : HttpClientHandlerTest { diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj index c72ec6b0acc..4b6596d1432 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj @@ -127,8 +127,6 @@ Link="Common\System\Net\Http\RepeatedFlushContent.cs" /> - - diff --git a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs index 63af2603db4..f71c6a90544 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Logging/NetEventSource.cs @@ -13,7 +13,7 @@ namespace System.Net /// The buffer to be logged. /// The calling member. [NonEvent] - public static void DumpBuffer(object thisOrContextObject, ReadOnlyMemory buffer, [CallerMemberName] string? memberName = null) + public static void DumpBuffer(object thisOrContextObject, ReadOnlySpan buffer, [CallerMemberName] string? memberName = null) { if (Log.IsEnabled()) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 5d234b58862..2cbc44c5857 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -879,7 +879,7 @@ namespace System.Net.Security --*/ internal SecurityStatusPal Encrypt(ReadOnlyMemory buffer, ref byte[] output, out int resultSize) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer); + if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer.Span); byte[] writeBuffer = output; @@ -903,24 +903,12 @@ namespace System.Net.Security return secStatus; } - internal SecurityStatusPal Decrypt(byte[]? payload, ref int offset, ref int count) + internal SecurityStatusPal Decrypt(Span buffer, out int outputOffset, out int outputCount) { - if ((uint)offset > (uint)(payload == null ? 0 : payload.Length)) - { - Debug.Fail("Argument 'offset' out of range."); - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if ((uint)count > (uint)(payload == null ? 0 : payload.Length - offset)) - { - Debug.Fail("Argument 'count' out of range."); - throw new ArgumentOutOfRangeException(nameof(count)); - } - - SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, payload!, ref offset, ref count); + SecurityStatusPal status = SslStreamPal.DecryptMessage(_securityContext!, buffer, out outputOffset, out outputCount); if (NetEventSource.Log.IsEnabled() && status.ErrorCode == SecurityStatusPalErrorCode.OK) { - NetEventSource.DumpBuffer(this, payload!, offset, count); + NetEventSource.DumpBuffer(this, buffer.Slice(outputOffset, outputCount)); } return status; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 81f2c92880b..17858bb0e68 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -43,6 +43,7 @@ namespace System.Net.Security private const int ReadBufferSize = 4096 * 4 + FrameOverhead; // We read in 16K chunks + headers. private const int InitialHandshakeBufferSize = 4096 + FrameOverhead; // try to fit at least 4K ServerCertificate private ArrayBuffer _handshakeBuffer; + private bool _receivedEOF; // Used by Telemetry to ensure we log connection close exactly once // 0 = no handshake @@ -183,17 +184,6 @@ namespace System.Net.Security } } - private SecurityStatusPal DecryptData() - { - ThrowIfExceptionalOrNotAuthenticated(); - return PrivateDecryptData(_internalBuffer, ref _decryptedBytesOffset, ref _decryptedBytesCount); - } - - private SecurityStatusPal PrivateDecryptData(byte[]? buffer, ref int offset, ref int count) - { - return _context!.Decrypt(buffer, ref offset, ref count); - } - // // This method assumes that a SSPI context is already in a good shape. // For example it is either a fresh context or already authenticated context that needs renegotiation. @@ -813,6 +803,117 @@ namespace System.Net.Security _decryptedBytesCount = 0; _decryptedBytesOffset = 0; } + else if (_decryptedBytesCount == 0) + { + _decryptedBytesOffset = 0; + } + } + + + private bool HaveFullTlsFrame(out int frameSize) + { + if (_internalBufferCount < SecureChannel.ReadHeaderSize) + { + frameSize = int.MaxValue; + return false; + } + + frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); + return _internalBufferCount >= frameSize; + } + + + private async ValueTask EnsureFullTlsFrameAsync(TIOAdapter adapter) + where TIOAdapter : IReadWriteAdapter + { + int frameSize; + if (HaveFullTlsFrame(out frameSize)) + { + return frameSize; + } + + // We may have enough space to complete frame, but we may still do extra IO if the frame is small. + // So we will attempt larger read - that is trade of with extra copy. + // This may be updated at some point based on size of existing chunk, rented buffer and size of 'buffer'. + ResetReadBuffer(); + + // _internalOffset is 0 after ResetReadBuffer and we use _internalBufferCount to determined where to read. + while (_internalBufferCount < frameSize) + { + // We either don't have full frame or we don't have enough data to even determine the size. + int bytesRead = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false); + if (bytesRead == 0) + { + if (_internalBufferCount != 0) + { + // we got EOF in middle of TLS frame. Treat that as error. + throw new IOException(SR.net_io_eof); + } + + return 0; + } + + _internalBufferCount += bytesRead; + if (frameSize == int.MaxValue && _internalBufferCount > SecureChannel.ReadHeaderSize) + { + // recalculate frame size if needed e.g. we could not get it before. + frameSize = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); + } + } + + return frameSize; + } + + private SecurityStatusPal DecryptData(int frameSize) + { + Debug.Assert(_decryptedBytesCount == 0); + + // Set _decryptedBytesOffset/Count to the current frame we have (including header) + // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. + _decryptedBytesOffset = _internalOffset; + _decryptedBytesCount = frameSize; + SecurityStatusPal status; + + lock (_handshakeLock) + { + ThrowIfExceptionalOrNotAuthenticated(); + status = _context!.Decrypt(new Span(_internalBuffer, _internalOffset, frameSize), out int decryptedOffset, out int decryptedCount); + _decryptedBytesCount = decryptedCount; + if (decryptedCount > 0) + { + _decryptedBytesOffset = _internalOffset + decryptedOffset; + } + + if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate) + { + // The status indicates that peer wants to renegotiate. (Windows only) + // In practice, there can be some other reasons too - like TLS1.3 session creation + // of alert handling. We need to pass the data to lsass and it is not safe to do parallel + // write any more as that can change TLS state and the EncryptData() can fail in strange ways. + + // To handle this we call DecryptData() under lock and we create TCS waiter. + // EncryptData() checks that under same lock and if it exist it will not call low-level crypto. + // Instead it will wait synchronously or asynchronously and it will try again after the wait. + // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over. + // If that happen before EncryptData() runs, _handshakeWaiter will be set to null + // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData() + + + if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0) + { + // create TCS only if we plan to proceed. If not, we will throw later outside of the lock. + // Tls1.3 does not have renegotiation. However on Windows this error code is used + // for session management e.g. anything lsass needs to see. + // We also allow it when explicitly requested using RenegotiateAsync(). + _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + } + } + + // Treat the bytes we just decrypted as consumed + ConsumeBufferedBytes(frameSize); + + return status; } private async ValueTask ReadAsyncInternal(TIOAdapter adapter, Memory buffer, bool renegotiation = false) @@ -826,109 +927,63 @@ namespace System.Net.Security } } + ThrowIfExceptionalOrNotAuthenticated(); + Debug.Assert(_internalBuffer is null || _internalBufferCount > 0 || _decryptedBytesCount > 0, "_internalBuffer allocated when no data is buffered."); + int processedLength = 0; + int payloadBytes = 0; try { + if (_decryptedBytesCount != 0) + { + if (renegotiation) + { + throw new InvalidOperationException(SR.net_ssl_renegotiate_data); + } + + processedLength = CopyDecryptedData(buffer); + if (processedLength == buffer.Length || !HaveFullTlsFrame(out payloadBytes)) + { + // We either filled whole buffer or used all buffered frames. + return processedLength; + } + + buffer = buffer.Slice(processedLength); + } + + if (_receivedEOF) + { + Debug.Assert(_internalBufferCount == 0); + // We received EOF during previous read but had buffered data to return. + return 0; + } + + if (buffer.Length == 0 && _internalBuffer is null) + { + // User requested a zero-byte read, and we have no data available in the buffer for processing. + // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read + // for reduced memory consumption when data is not immediately available. + // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation + // until data is actually available from the underlying stream. + // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will + // complete immediately and won't save any memory, but will still function correctly. + await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false); + } + + Debug.Assert(_decryptedBytesCount == 0); + Debug.Assert(_decryptedBytesOffset == 0); + while (true) { - if (_decryptedBytesCount != 0) + payloadBytes = await EnsureFullTlsFrameAsync(adapter).ConfigureAwait(false); + if (payloadBytes == 0) { - if (renegotiation) - { - throw new InvalidOperationException(SR.net_ssl_renegotiate_data); - } - - return CopyDecryptedData(buffer); + _receivedEOF = true; + break; } - if (buffer.Length == 0 && _internalBuffer is null && !renegotiation) - { - // User requested a zero-byte read, and we have no data available in the buffer for processing. - // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read - // for reduced memory consumption when data is not immediately available. - // So, we will issue our own zero-byte read against the underlying stream and defer buffer allocation - // until data is actually available from the underlying stream. - // Note that if the underlying stream does not supporting blocking on zero byte reads, then this will - // complete immediately and won't save any memory, but will still function correctly. - await adapter.ReadAsync(Memory.Empty).ConfigureAwait(false); - } - - ResetReadBuffer(); - - // Read the next frame header. - if (_internalBufferCount < SecureChannel.ReadHeaderSize) - { - // We don't have enough bytes buffered, so issue an initial read to try to get enough. This is - // done in this method both to better consolidate error handling logic (the first read is the special - // case that needs to differentiate reading 0 from > 0, and everything else needs to throw if it - // doesn't read enough), and to minimize the chances that in the common case the FillBufferAsync - // helper needs to yield and allocate a state machine. - int readBytes = await adapter.ReadAsync(_internalBuffer.AsMemory(_internalBufferCount)).ConfigureAwait(false); - if (readBytes == 0) - { - return 0; - } - - _internalBufferCount += readBytes; - if (_internalBufferCount < SecureChannel.ReadHeaderSize) - { - await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false); - } - } - Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize); - - // Parse the frame header to determine the payload size (which includes the header size). - int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); - if (payloadBytes < 0) - { - throw new IOException(SR.net_frame_read_size); - } - - // Read in the rest of the payload if we don't have it. - if (_internalBufferCount < payloadBytes) - { - await FillBufferAsync(adapter, payloadBytes).ConfigureAwait(false); - } - - // Set _decrytpedBytesOffset/Count to the current frame we have (including header) - // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. - _decryptedBytesOffset = _internalOffset; - _decryptedBytesCount = payloadBytes; - - SecurityStatusPal status; - lock (_handshakeLock) - { - status = DecryptData(); - if (status.ErrorCode == SecurityStatusPalErrorCode.Renegotiate) - { - // The status indicates that peer wants to renegotiate. (Windows only) - // In practice, there can be some other reasons too - like TLS1.3 session creation - // of alert handling. We need to pass the data to lsass and it is not safe to do parallel - // write any more as that can change TLS state and the EncryptData() can fail in strange ways. - - // To handle this we call DecryptData() under lock and we create TCS waiter. - // EncryptData() checks that under same lock and if it exist it will not call low-level crypto. - // Instead it will wait synchronously or asynchronously and it will try again after the wait. - // The result will be set when ReplyOnReAuthenticationAsync() is finished e.g. lsass business is over. - // If that happen before EncryptData() runs, _handshakeWaiter will be set to null - // and EncryptData() will work normally e.g. no waiting, just exclusion with DecryptData() - - if (_sslAuthenticationOptions!.AllowRenegotiation || SslProtocol == SslProtocols.Tls13 || _nestedAuth != 0) - { - // create TCS only if we plan to proceed. If not, we will throw in block bellow outside of the lock. - // Tls1.3 does not have renegotiation. However on Windows this error code is used - // for session management e.g. anything lsass needs to see. - // We also allow it when explicitly requested using RenegotiateAsync(). - _handshakeWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - } - } - } - - // Treat the bytes we just decrypted as consumed - // Note, we won't do another buffer read until the decrypted bytes are processed - ConsumeBufferedBytes(payloadBytes); - + SecurityStatusPal status = DecryptData(payloadBytes); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { byte[]? extraBuffer = null; @@ -962,12 +1017,50 @@ namespace System.Net.Security if (status.ErrorCode == SecurityStatusPalErrorCode.ContextExpired) { - return 0; + _receivedEOF = true; + break; } throw new IOException(SR.net_io_decrypt, SslStreamPal.GetException(status)); } + + if (_decryptedBytesCount > 0) + { + // This will either copy data from rented buffer or adjust final buffer as needed. + // In both cases _decryptedBytesOffset and _decryptedBytesCount will be updated as needed. + int copyLength = CopyDecryptedData(buffer); + processedLength += copyLength; + if (copyLength == buffer.Length) + { + // We have more decrypted data after we filled provided buffer. + break; + } + + buffer = buffer.Slice(copyLength); + } + + if (processedLength == 0) + { + // We did not get any real data so far. + continue; + } + + if (!HaveFullTlsFrame(out payloadBytes)) + { + // We don't have another frame to process but we have some data to return to caller. + break; + } + + TlsFrameHelper.TryGetFrameHeader(_internalBuffer.AsSpan(_internalOffset), ref _lastFrame.Header); + if (_lastFrame.Header.Type != TlsContentType.AppData) + { + // Alerts, handshake and anything else will be processed separately. + // This may not be necessary but it improves compatibility with older versions. + break; + } } + + return processedLength; } catch (Exception e) { @@ -1101,6 +1194,10 @@ namespace System.Net.Security _internalOffset += byteCount; _internalBufferCount -= byteCount; + if (_internalBufferCount == 0) + { + _internalOffset = 0; + } } private int CopyDecryptedData(Memory buffer) @@ -1116,6 +1213,11 @@ namespace System.Net.Security _decryptedBytesCount -= copyBytes; } + if (_decryptedBytesCount == 0) + { + _decryptedBytesOffset = 0; + } + return copyBytes; } @@ -1333,7 +1435,7 @@ namespace System.Net.Security payloadSize = ((buffer[3] << 8) | buffer[4]) + 5; break; default: - break; + throw new IOException(SR.net_frame_read_size); } return payloadSize; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 24f1e8e0e01..27c854c9916 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -112,17 +112,20 @@ namespace System.Net.Security public static SecurityStatusPal DecryptMessage( SafeDeleteSslContext securityContext, - byte[] buffer, - ref int offset, - ref int count) + Span buffer, + out int offset, + out int count) { + offset = 0; + count = 0; + try { SafeSslHandle sslHandle = securityContext.SslContext; - securityContext.Write(buffer.AsSpan(offset, count)); + securityContext.Write(buffer); - PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer.AsSpan(offset, count), out int read); + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read); if (ret == PAL_SSLStreamStatus.Error) return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index 53c4d12c99a..0414082781c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -143,22 +143,23 @@ namespace System.Net.Security public static SecurityStatusPal DecryptMessage( SafeDeleteSslContext securityContext, - byte[] buffer, - ref int offset, - ref int count) + Span buffer, + out int offset, + out int count) { + offset = 0; + count = 0; + try { SafeSslHandle sslHandle = securityContext.SslContext; - - securityContext.Write(buffer.AsSpan(offset, count)); + securityContext.Write(buffer); unsafe { - fixed (byte* offsetInput = &buffer[offset]) + fixed (byte* ptr = buffer) { - PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out int written); - + PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, ptr, buffer.Length, out int written); if (status < 0) { return new SecurityStatusPal( @@ -167,6 +168,7 @@ namespace System.Net.Security } count = written; + offset = 0; switch (status) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 98a21e12856..02045613170 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -62,11 +62,14 @@ namespace System.Net.Security } } - public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, byte[] buffer, ref int offset, ref int count) + public static SecurityStatusPal DecryptMessage(SafeDeleteSslContext securityContext, Span buffer, out int offset, out int count) { + offset = 0; + count = 0; + try { - int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, new Span(buffer, offset, count), out Interop.Ssl.SslErrorCode errorCode); + int resultSize = Interop.OpenSsl.Decrypt(securityContext.SslContext, buffer, out Interop.Ssl.SslErrorCode errorCode); SecurityStatusPal retVal = MapNativeErrorCode(errorCode); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 59a781b470a..4060296c4ed 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -313,7 +313,7 @@ namespace System.Net.Security } } - public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, byte[] buffer, ref int offset, ref int count) + public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? securityContext, Span buffer, out int offset, out int count) { const int NumSecBuffers = 4; // data + empty + empty + empty fixed (byte* bufferPtr = buffer) @@ -321,8 +321,8 @@ namespace System.Net.Security Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers]; Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[0]; dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA; - dataBuffer->pvBuffer = (IntPtr)bufferPtr + offset; - dataBuffer->cbBuffer = count; + dataBuffer->pvBuffer = (IntPtr)bufferPtr; + dataBuffer->cbBuffer = buffer.Length; for (int i = 1; i < NumSecBuffers; i++) { @@ -341,6 +341,7 @@ namespace System.Net.Security // Decrypt may repopulate the sec buffers, likely with header + data + trailer + empty. // We need to find the data. count = 0; + offset = 0; for (int i = 0; i < NumSecBuffers; i++) { // Successfully decoded data and placed it at the following position in the buffer, @@ -351,6 +352,7 @@ namespace System.Net.Security offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferPtr); count = unmanagedBuffer[i].cbBuffer; + // output is ignored on Windows. We always decrypt in place and we set outputOffset to indicate where the data start. Debug.Assert(offset >= 0 && count >= 0, $"Expected offset and count greater than 0, got {offset} and {count}"); Debug.Assert(checked(offset + count) <= buffer.Length, $"Expected offset+count <= buffer.Length, got {offset}+{count}>={buffer.Length}"); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs index 8104387dc07..8f5b5ed2a07 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -90,6 +90,11 @@ namespace System.Net.Security public int Length; public override string ToString() => $"{Version}:{Type}[{Length}]"; + + public int GetFrameSize() + { + return Length + TlsFrameHelper.HeaderSize; + } } internal static class TlsFrameHelper diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs deleted file mode 100644 index 2389188bbef..00000000000 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSchSendAuxRecordTest.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Net.Test.Common; -using System.Security.Authentication; -using System.Threading.Tasks; - -using Xunit; -using Xunit.Abstractions; - -namespace System.Net.Security.Tests -{ - using Configuration = System.Net.Test.Common.Configuration; - - public class SchSendAuxRecordTest - { - readonly ITestOutputHelper _output; - - public SchSendAuxRecordTest(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task SslStream_ClientAndServerUsesAuxRecord_Ok() - { - using (var server = new HttpsTestServer()) - { - server.Start(); - - var tasks = new Task[2]; - - bool serverAuxRecordDetected = false; - bool serverAuxRecordDetectedInconclusive = false; - int serverTotalBytesReceived = 0; - int serverChunks = 0; - - tasks[0] = server.AcceptHttpsClientAsync((requestString) => - { - serverTotalBytesReceived += requestString.Length; - - if (serverTotalBytesReceived == 1 && serverChunks == 0) - { - serverAuxRecordDetected = true; - } - - serverChunks++; - - // Test is inconclusive if any non-CBC cipher is used: - if (server.Stream.CipherAlgorithm == CipherAlgorithmType.None || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - server.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - serverAuxRecordDetectedInconclusive = true; - } - - if (serverTotalBytesReceived < 5) - { - return Task.FromResult(null); - } - else - { - return Task.FromResult(HttpsTestServer.Options.DefaultResponseString); - } - }); - - - var clientOptions = new HttpsTestClient.Options(new IPEndPoint(IPAddress.Loopback, server.Port)); - clientOptions.AllowedProtocols = SslProtocols.Tls; - - clientOptions.IgnoreSslPolicyErrors = - SslPolicyErrors.RemoteCertificateNameMismatch | SslPolicyErrors.RemoteCertificateChainErrors; - - var client = new HttpsTestClient(clientOptions); - - bool clientAuxRecordDetected = false; - bool clientAuxRecordDetectedInconclusive = false; - int clientTotalBytesReceived = 0; - int clientChunks = 0; - - tasks[1] = client.HttpsRequestAsync((responseString) => - { - if (responseString == null) - { - string requestString = string.Format( - HttpsTestClient.Options.DefaultRequestStringTemplate, - clientOptions.ServerName); - - return Task.FromResult(requestString); - } - - clientTotalBytesReceived += responseString.Length; - - if (clientTotalBytesReceived == 1 && clientChunks == 0) - { - clientAuxRecordDetected = true; - } - - // Test is inconclusive if any non-CBC cipher is used: - if (client.Stream.CipherAlgorithm == CipherAlgorithmType.None || - client.Stream.CipherAlgorithm == CipherAlgorithmType.Null || - client.Stream.CipherAlgorithm == CipherAlgorithmType.Rc4) - { - clientAuxRecordDetectedInconclusive = true; - } - - return Task.FromResult(null); - }); - - await Task.WhenAll(tasks).WaitAsync(TestConfiguration.PassingTestTimeout); - - if (serverAuxRecordDetectedInconclusive || clientAuxRecordDetectedInconclusive) - { - _output.WriteLine("Test inconclusive: The Operating system preferred a non-CBC or Null cipher."); - } - else - { - Assert.True(serverAuxRecordDetected, "Server reports: Client auxiliary record not detected."); - Assert.True(clientAuxRecordDetected, "Client reports: Server auxiliary record not detected."); - } - } - } - } -} diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index bb131408de3..3dc537ad91d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -100,7 +100,6 @@ - From 458bb9efd1de6544aa2a1645ba699ef7e59889df Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 24 Jun 2021 16:46:39 +0200 Subject: [PATCH 087/926] Fix compiler references when building inside VS (#54614) If for a source project a contract project exists, then the contract project's TargetPath should be passed to the compiler. This is handled by the SDK by default when `ProduceReferenceAssembly` is true. As dotnet/runtime doesn't use the `ProduceReferenceAssembly` feature yet, a custom target adds the necessary `ReferenceAssembly` metadata to the `TargetPathWithTargetPlatformMoniker` item which then is transformed to references for the compiler. That works fine on the CLI as the `GetTargetPathWithTargetPlatformMoniker` target runs after the ProjectReference to the ContractProject is resolved and its target path is available. Inside VS the target ordering is different and the `ResolvedMatchingContract` item was empty as the ProjectReference to the contract wasn't yet resolved. The fix for that is to add a dependency onto the `ResolveProjectReferences` target to guarantee that the `ResolvedMatchingContract` item is populated in time. Noticed this when the build of System.ComponentModel.Composition.Registration failed because the implementation assembly of System.ComponentModel.Composition was passed to the compiler instead of the reference assembly. --- eng/resolveContract.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/resolveContract.targets b/eng/resolveContract.targets index d98ef8dffb7..8c99a9059de 100644 --- a/eng/resolveContract.targets +++ b/eng/resolveContract.targets @@ -25,6 +25,7 @@ From 055a38a9062c4c9ec3a811eec02f7e37fb1fbbd5 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 24 Jun 2021 17:20:26 +0200 Subject: [PATCH 088/926] [wasm] Bump emscripten to 2.0.23 (#53603) Bumps emscripten to 2.0.23 The Browser AOT tests now use `-Wl,-lto-O0` option to reduce memory usage of `wasm-ld` tool, which was in some cases going over avaiable 8GB on helix machines. * Revert "Add ActiveIssue to the MemoryMappedFiles tests" This reverts commit ec1ae530606ef1061680600fc046226cc1c4cbc3. * Revert "Add ActiveIssue attr to the FileSystem tests" This reverts commit 356b3ff2a703980ac01b9df697a594e8c341c436. * Bump emscripten version to 2.0.23 * Use newer docker images with 2.0.23 * Update docs * Use 2.0.23 emscripten nuget packages * Revert "Revert "Add ActiveIssue attr to the FileSystem tests"" This reverts commit eb2f9548b08c114b359fab8d867ba50de098fe48. The fix is not present in 2.0.23 * Revert "Revert "Add ActiveIssue to the MemoryMappedFiles tests"" This reverts commit 8be39f583499a8d8451034c65260a785330b0795. The fix is not present in 2.0.23 * Increase timeout for AOT tests * Add description of emscripten bump to README * Try to get information about resources * Get all limits * Escape & chars * Reduce platform matrix * Lets try one more build with doubled timeout * Revert "Lets try one more build with doubled timeout" This reverts commit 67dd7754bb79218b2c6b687034162d041715093e. * Try -Wl,-O0 on CI To be sure it behaves the same as in local build * Use -Wl,-lto-O0 do lower link time optimization It looks like it reduces the memory load a lot * Set EmccLinkOptimizationFlag for AOT tests And reset the default value * Escape commas * Revert "Reduce platform matrix" This reverts commit fec0e557208eb165824e75cd57b895a74d164de4. * Remove resource info retrieval * Bump emsdk versions Co-authored-by: Larry Ewing --- .../libraries/webassembly-instructions.md | 2 +- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 4 ++-- eng/pipelines/common/platform-matrix.yml | 2 +- .../libraries/helix-queues-setup.yml | 2 +- eng/testing/tests.wasm.targets | 2 +- src/libraries/sendtohelixhelp.proj | 1 + .../WorkloadManifest.json.in | 22 +++++++++---------- src/mono/wasm/README.md | 13 ++++++++++- src/mono/wasm/emscripten-version.txt | 2 +- 10 files changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/workflow/building/libraries/webassembly-instructions.md b/docs/workflow/building/libraries/webassembly-instructions.md index 80af1a508b1..fdc5dc31d22 100644 --- a/docs/workflow/building/libraries/webassembly-instructions.md +++ b/docs/workflow/building/libraries/webassembly-instructions.md @@ -7,7 +7,7 @@ If you haven't already done so, please read [this document](../../README.md#Buil The **correct version** of Emscripten SDK (emsdk) needs to be installed. * Run `make -C src/mono/wasm provision-wasm` to install emsdk into `src/mono/wasm/emsdk`. * Alternatively follow the [installation guide](https://emscripten.org/docs/getting_started/downloads.html#sdk-download-and-install). -Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.21`. See [emscripten-version.txt](..\..\..\..\src\mono\wasm\emscripten-version.txt) +Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.23`. See [emscripten-version.txt](..\..\..\..\src\mono\wasm\emscripten-version.txt) Once installed the `EMSDK_PATH` environment variable needs to be set: diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5c1f457ae40..9c44cabfca8 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -207,9 +207,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f - + https://github.com/dotnet/emsdk - defa37b05c734e025292c5747664e970cd2ac444 + 617928847d1e11458527b8bbafb5577982291847 https://github.com/dotnet/hotreload-utils diff --git a/eng/Versions.props b/eng/Versions.props index a194baa33de..3b887fae24a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -176,7 +176,7 @@ 11.1.0-alpha.1.21308.1 11.1.0-alpha.1.21308.1 - 6.0.0-preview.6.21275.1 - $(MicrosoftNETRuntimeEmscripten2021Nodewinx64Version) + 6.0.0-preview.7.21323.1 + $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index daedd73b0e4..defd34be84a 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -219,7 +219,7 @@ jobs: targetRid: browser-wasm platform: Browser_wasm container: - image: ubuntu-18.04-webassembly-20210519131124-ba00c14 + image: ubuntu-18.04-webassembly-20210531091624-f5c7a43 registry: mcr jobParameters: runtimeFlavor: ${{ parameters.runtimeFlavor }} diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index b766f1af96a..7d33d26edb0 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -189,6 +189,6 @@ jobs: # WebAssembly windows - ${{ if eq(parameters.platform, 'Browser_wasm_win') }}: - - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210519130955-ba00c14 + - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210531091615-f5c7a43 ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index de4432bee9f..aa4fb305dbd 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -40,7 +40,7 @@ <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) - <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) + <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) /p:EmccLinkOptimizationFlag='-Oz -Wl%252C-O0 -Wl%252C-lto-O0' <_AOTBuildCommand>$(_AOTBuildCommand) && cd wasm_build/AppBundle $(_AOTBuildCommand) diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 25d7168bdc4..12d402fcb7e 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -29,6 +29,7 @@ <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">00:45:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:00:00 + <_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == ''">00:30:00 diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index d783615a908..4c50a08f007 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -270,29 +270,29 @@ "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Node.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64", + "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64" } }, "Microsoft.NET.Runtime.Emscripten.Python" : { "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.win-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Python.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64" } }, "Microsoft.NET.Runtime.Emscripten.Sdk" : { "kind": "Sdk", "version": "${EmscriptenVersion}", "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.21.Sdk.osx-x64" + "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64", + "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64", + "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64", + "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64" } } } diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md index a44edd53dc3..67cf64b8c05 100644 --- a/src/mono/wasm/README.md +++ b/src/mono/wasm/README.md @@ -13,7 +13,7 @@ Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building ta you are directly using the `dotnet build`, or `build.sh`. * Alternatively you can install **correct version** yourself from the [Emscripten SDK guide](https://emscripten.org/docs/getting_started/downloads.html). -Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.21`. See [emscripten-version.txt](./emscripten-version.txt) +Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.23`. See [emscripten-version.txt](./emscripten-version.txt) Make sure to set `EMSDK_PATH` variable, whenever building, or running tests for wasm. @@ -154,3 +154,14 @@ The samples in `src/mono/sample/wasm` can be build and run like this: `dotnet build /t:RunSample browser/Wasm.Browser.Sample.csproj` To build and run the samples with AOT, add `/p:RunAOTCompilation=true` to the above command lines. + +### Upgrading Emscripten + +Bumping Emscripten version involves these steps: + +* update https://github.com/dotnet/runtime/blob/main/src/mono/wasm/emscripten-version.txt +* bump emscripten versions in docker images in https://github.com/dotnet/dotnet-buildtools-prereqs-docker +* bump emscripten in https://github.com/dotnet/emsdk +* update version number in docs +* update `Microsoft.NET.Runtime.Emscripten..Node.win-x64` package name, version and sha hash in https://github.com/dotnet/runtime/blob/main/eng/Version.Details.xml and in https://github.com/dotnet/runtime/blob/main/eng/Versions.props. the sha is the commit hash in https://github.com/dotnet/emsdk and the package version can be found at https://dev.azure.com/dnceng/public/_packaging?_a=feed&feed=dotnet6 +* update packages in the workload manifest https://github.com/dotnet/runtime/blob/main/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in diff --git a/src/mono/wasm/emscripten-version.txt b/src/mono/wasm/emscripten-version.txt index eb4d1bc7a34..8fe1046e01f 100644 --- a/src/mono/wasm/emscripten-version.txt +++ b/src/mono/wasm/emscripten-version.txt @@ -1 +1 @@ -2.0.21 \ No newline at end of file +2.0.23 \ No newline at end of file From 74322d7c4d2d5db16825bb8500c757c91f0fecb9 Mon Sep 17 00:00:00 2001 From: monojenkins Date: Thu, 24 Jun 2021 11:33:14 -0400 Subject: [PATCH 089/926] Fix for heap_use_after_free flagged by sanitizer (#54679) Copy of https://github.com/mono/mono/pull/21120 Co-authored-by: dseshadri --- src/mono/mono/mini/mini-posix.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c index 6ff3228ca06..b273a96a5fa 100644 --- a/src/mono/mono/mini/mini-posix.c +++ b/src/mono/mono/mini/mini-posix.c @@ -135,18 +135,31 @@ mono_runtime_shutdown_handlers (void) static GHashTable *mono_saved_signal_handlers = NULL; static struct sigaction * -get_saved_signal_handler (int signo, gboolean remove) +get_saved_signal_handler (int signo) { if (mono_saved_signal_handlers) { /* The hash is only modified during startup, so no need for locking */ struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); - if (remove && handler) - g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); return handler; } return NULL; } + +static void +remove_saved_signal_handler (int signo) +{ + if (mono_saved_signal_handlers) { + /* The hash is only modified during startup, so no need for locking */ + struct sigaction *handler = (struct sigaction*)g_hash_table_lookup (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); + if (handler) + g_hash_table_remove (mono_saved_signal_handlers, GINT_TO_POINTER (signo)); + } + return; +} + + + static void save_old_signal_handler (int signo, struct sigaction *old_action) { @@ -181,7 +194,7 @@ gboolean MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal) { int signal = MONO_SIG_HANDLER_GET_SIGNO (); - struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal, FALSE); + struct sigaction *saved_handler = (struct sigaction *)get_saved_signal_handler (signal); if (saved_handler && saved_handler->sa_handler) { if (!(saved_handler->sa_flags & SA_SIGINFO)) { @@ -376,7 +389,7 @@ static void remove_signal_handler (int signo) { struct sigaction sa; - struct sigaction *saved_action = get_saved_signal_handler (signo, TRUE); + struct sigaction *saved_action = get_saved_signal_handler (signo); if (!saved_action) { sa.sa_handler = SIG_DFL; @@ -387,6 +400,7 @@ remove_signal_handler (int signo) } else { g_assert (sigaction (signo, saved_action, NULL) != -1); } + remove_saved_signal_handler(signo); } void From ea1707cf3a4a513de37a41d89591cca88460e9fa Mon Sep 17 00:00:00 2001 From: Daniel Genkin Date: Thu, 24 Jun 2021 11:39:04 -0400 Subject: [PATCH 090/926] [WASM] Fix async/await in config loading (#54652) * Fixed config issue * Updated Hot Reload test --- src/mono/sample/mbr/browser/runtime.js | 2 +- src/mono/sample/wasm/browser-bench/runtime.js | 2 +- src/mono/sample/wasm/browser-profile/runtime.js | 2 +- src/mono/sample/wasm/browser/runtime.js | 2 +- .../wasm/debugger/tests/debugger-test/runtime-debugger.js | 2 +- src/mono/wasm/runtime-test.js | 2 +- src/mono/wasm/runtime/library_mono.js | 4 ++-- src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js | 2 +- .../FunctionalTests/WebAssembly/Browser/HotReload/runtime.js | 2 +- .../WebAssembly/Browser/NormalInterp/runtime.js | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mono/sample/mbr/browser/runtime.js b/src/mono/sample/mbr/browser/runtime.js index 32918aa573a..74781179a31 100644 --- a/src/mono/sample/mbr/browser/runtime.js +++ b/src/mono/sample/mbr/browser/runtime.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser-bench/runtime.js b/src/mono/sample/wasm/browser-bench/runtime.js index 60c383d13cb..f9bdf8b3af7 100644 --- a/src/mono/sample/wasm/browser-bench/runtime.js +++ b/src/mono/sample/wasm/browser-bench/runtime.js @@ -4,7 +4,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser-profile/runtime.js b/src/mono/sample/wasm/browser-profile/runtime.js index 2c83ff54ef9..f0636985d03 100644 --- a/src/mono/sample/wasm/browser-profile/runtime.js +++ b/src/mono/sample/wasm/browser-profile/runtime.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/sample/wasm/browser/runtime.js b/src/mono/sample/wasm/browser/runtime.js index e97feef745a..816136acf36 100644 --- a/src/mono/sample/wasm/browser/runtime.js +++ b/src/mono/sample/wasm/browser/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 360aa9b8b30..8fb1e86211a 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -5,7 +5,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, // Called when the runtime is initialized and wasm is ready diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 8b3395f4ec5..89b9c5d70cc 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -211,7 +211,7 @@ var Module = { printErr, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onAbort: function(x) { diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 84fb5684abd..8f8d4cbcfe5 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -1451,9 +1451,9 @@ var MonoSupportLib = { } else { // shell or worker config = JSON.parse(read(configFilePath)); // read is a v8 debugger command } - return config; + Module.config = config; } catch(e) { - return {message: "failed to load config file", error: e}; + Module.config = {message: "failed to load config file", error: e}; } finally { Module.removeRunDependency(configFilePath); } diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js index 65cba13a9b1..11f8d64f60c 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js index 4859991a626..94f1a36a410 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js index 1a8abf503fb..b5227472674 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js @@ -6,7 +6,7 @@ var Module = { config: null, preInit: async function() { - Module.config = await MONO.mono_wasm_load_config("./mono-config.json"); + await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly }, onRuntimeInitialized: function () { From abccfadbf570033efee8ac9a6992f6f7ee51f474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 24 Jun 2021 17:43:55 +0200 Subject: [PATCH 091/926] Disable MacCatalyst arm64 PR test runs on staging pipeline (#54678) We don't have enough capacity right now on Helix to handle the load. --- eng/pipelines/runtime-staging.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 2352995df91..666641354bf 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -107,7 +107,9 @@ jobs: runtimeFlavor: mono platforms: - MacCatalyst_x64 - - MacCatalyst_arm64 + # don't run tests on MacCatalyst_arm64 PRs until we can get significantly more devices + - ${{ if eq(variables['isFullMatrix'], true) }}: + - MacCatalyst_arm64 variables: # map dependencies variables to local variables - name: librariesContainsChange From 5a238f8f727e79b157837ba0ee3a183217314898 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 24 Jun 2021 19:20:36 +0200 Subject: [PATCH 092/926] Fix Activator.CreateInstance(GetType(), ...) related ILLink warnings in (#54680) System.Data.Common --- .../System.Data.Common/ref/System.Data.Common.cs | 2 ++ .../src/ILLink/ILLink.Suppressions.xml | 12 ------------ .../src/System/Data/Common/DataAdapter.cs | 3 ++- .../System.Data.Common/src/System/Data/DataTable.cs | 3 +++ 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index ce7de38a7ae..0caa4703edb 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -676,6 +676,7 @@ namespace System.Data [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DataTableEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [System.ComponentModel.ToolboxItemAttribute(false)] [System.Xml.Serialization.XmlSchemaProviderAttribute("GetDataTableSchema")] + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors)] public partial class DataTable : System.ComponentModel.MarshalByValueComponent, System.ComponentModel.IListSource, System.ComponentModel.ISupportInitialize, System.ComponentModel.ISupportInitializeNotification, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable { protected internal bool fInitInProgress; @@ -1743,6 +1744,7 @@ namespace System.Data.Common Start = 1, End = 2, } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public partial class DataAdapter : System.ComponentModel.Component, System.Data.IDataAdapter { protected DataAdapter() { } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 3e47be79791..46c0140e4ac 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -13,12 +13,6 @@ member M:System.Data.DataRowExtensions.UnboxT`1.Create - - ILLink - IL2072 - member - M:System.Data.Common.DataAdapter.CloneInternals - ILLink IL2026 @@ -49,12 +43,6 @@ member M:System.Data.DataTable.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) - - ILLink - IL2072 - member - M:System.Data.DataTable.CreateInstance() - ILLink IL2026 diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs index 48e44921c91..0ef33036cb5 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs @@ -9,6 +9,7 @@ using System.Globalization; namespace System.Data.Common { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public class DataAdapter : Component, IDataAdapter { private static readonly object s_eventFillError = new object(); @@ -202,7 +203,7 @@ namespace System.Data.Common [Obsolete("CloneInternals() has been deprecated. Use the DataAdapter(DataAdapter from) constructor. https://go.microsoft.com/fwlink/?linkid=14202")] protected virtual DataAdapter CloneInternals() { - DataAdapter clone = (DataAdapter)Activator.CreateInstance(GetType(), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, null, CultureInfo.InvariantCulture, null)!; + DataAdapter clone = (DataAdapter)Activator.CreateInstance(GetType())!; clone.CloneFrom(this); return clone; } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index f3e89e19b64..c0300defa1b 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -31,6 +31,7 @@ namespace System.Data [XmlSchemaProvider(nameof(GetDataTableSchema))] [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public class DataTable : MarshalByValueComponent, IListSource, ISupportInitializeNotification, ISerializable, IXmlSerializable { private DataSet? _dataSet; @@ -2304,6 +2305,8 @@ namespace System.Data // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Only parameterless constructors are used here. Warning is about serialization related constructors.")] protected virtual DataTable CreateInstance() => (DataTable)Activator.CreateInstance(GetType(), true)!; public virtual DataTable Clone() => Clone(null); From 8ecdf98273ac279871fb27da996c402f73188156 Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Thu, 24 Jun 2021 19:24:56 +0200 Subject: [PATCH 093/926] [QUIC] Add Windows version check to QUIC initialization (#54488) Add Windows version check to QUIC initialization, log min supported and current Windows version --- .../MsQuic/Internal/MsQuicApi.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index 4483920196a..2bf1b9b1955 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -9,6 +9,8 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal unsafe sealed class MsQuicApi { + private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000); + public SafeMsQuicRegistrationHandle Registration { get; } // This is workaround for a bug in ILTrimmer. @@ -123,6 +125,18 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal static MsQuicApi() { + if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported()) + { + IsQuicSupported = false; + + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); + } + + return; + } + if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { try @@ -149,6 +163,9 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal } } + private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major, + MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision); + // TODO: Consider updating all of these delegates to instead use function pointers. internal RegistrationOpenDelegate RegistrationOpenDelegate { get; } internal RegistrationCloseDelegate RegistrationCloseDelegate { get; } From 5dbbae5f3b6e96ac113d20029ae3be7d3afd70d8 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 24 Jun 2021 19:25:54 +0200 Subject: [PATCH 094/926] Fix instruction hex display (#54675) --- src/coreclr/jit/emitxarch.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 6578232a929..a0a5e3283d4 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -8676,8 +8676,10 @@ void emitter::emitDispIns( if (code != nullptr) { /* Display the instruction hex code */ + assert(((code >= emitCodeBlock) && (code < emitCodeBlock + emitTotalHotCodeSize)) || + ((code >= emitColdCodeBlock) && (code < emitColdCodeBlock + emitTotalColdCodeSize))); - emitDispInsHex(id, code, sz); + emitDispInsHex(id, code + writeableOffset, sz); } /* Display the instruction name */ From 1c81556da6058abe2fe2d9c9e96a58933af980fe Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Thu, 24 Jun 2021 10:46:31 -0700 Subject: [PATCH 095/926] Add non-LLVM iOS size scenario (#54585) * Add non-LLVM iOS size scenario * add category * remove parens * variables in the wrong place. * move setting configs to before it is written --- eng/pipelines/coreclr/perf.yml | 40 ++++++++++++++++++- .../templates/build-perf-sample-apps.yml | 24 ++++++++++- eng/pipelines/coreclr/templates/perf-job.yml | 32 +++++++++++---- .../coreclr/templates/run-performance-job.yml | 3 +- eng/testing/performance/ios_scenarios.proj | 34 ++++++++++++++++ eng/testing/performance/performance-setup.ps1 | 18 ++++++++- src/mono/sample/iOS/Makefile | 13 ++++-- 7 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 eng/testing/performance/ios_scenarios.proj diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index e125f17c36f..32ddf224a9d 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -215,7 +215,28 @@ jobs: archiveExtension: '.tar.gz' archiveType: tar tarCompression: gz - + + # build mono iOS scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - iOS_arm64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: iOSMono + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/coreclr/templates/build-perf-sample-apps.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: iOS Mono Artifacts + artifactName: iOSMonoarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz # build mono - template: /eng/pipelines/common/platform-matrix.yml @@ -242,6 +263,23 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml logicalmachine: 'perfpixel4a' + # run mono iOS scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Windows_x64 + jobParameters: + testGroup: perf + runtimeType: iOSMono + projectFile: ios_scenarios.proj + runKind: ios_scenarios + runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml + logicalmachine: 'perfpixel4a' + iosLlvmBuild: False + # run mono microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml index 2b556af6d91..6bfd8637bfa 100644 --- a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -23,6 +23,27 @@ steps: - script: make run MONO_ARCH=arm64 DEPLOY_AND_RUN=false workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/Android displayName: Build HelloAndroid sample app + - ${{ if eq(parameters.osGroup, 'iOS') }}: + - script: make build-appbundle TARGET=iOS MONO_ARCH=arm64 MONO_CONFIG=Release AOT=True USE_LLVM=False + env: + DevTeamProvisioning: '-' + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS + displayName: Build HelloiOS AOT sample app LLVM=False + - task: PublishBuildArtifacts@1 + condition: succeededOrFailed() + displayName: 'Publish binlog' + inputs: + pathtoPublish: $(Build.SourcesDirectory)/src/mono/sample/iOS/msbuild.binlog + artifactName: ${{ parameters.artifactName }} + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app + includeRootFolder: true + displayName: iOS Sample App NoLLVM + artifactName: iOSSampleAppNoLLVM + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz - template: /eng/pipelines/common/upload-artifact-step.yml parameters: @@ -42,4 +63,5 @@ steps: artifactName: ${{ parameters.artifactName }} archiveExtension: ${{ parameters.archiveExtension }} archiveType: ${{ parameters.archiveType }} - tarCompression: ${{ parameters.tarCompression }} \ No newline at end of file + tarCompression: ${{ parameters.tarCompression }} + diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 5347d8578b5..3dee80bb370 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -18,6 +18,7 @@ parameters: logicalMachine: '' pgoRunType: '' javascriptEngine: 'NoJS' + iOSLlvmBuild: 'False' ### Perf job @@ -28,8 +29,8 @@ jobs: - template: ${{ parameters.runJobTemplate }} parameters: # Compute job name from template parameters - jobName: ${{ format('perfbuild_{0}{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.runtimeType, parameters.codeGenType, parameters.runKind, parameters.logicalMachine, parameters.javascriptEngine, parameters.pgoRunType) }} - displayName: ${{ format('Performance {0}{1} {2} {3} {4} {5} {6} {7} {8} {9}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.runtimeType, parameters.codeGenType, parameters.runKind, parameters.logicalMachine, parameters.javascriptEngine, parameters.pgoRunType) }} + jobName: ${{ format('perfbuild_{0}{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.runtimeType, parameters.codeGenType, parameters.runKind, parameters.logicalMachine, parameters.javascriptEngine, parameters.pgoRunType, parameters.iosLlvmBuild) }} + displayName: ${{ format('Performance {0}{1} {2} {3} {4} {5} {6} {7} {8} {9} {10}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.runtimeType, parameters.codeGenType, parameters.runKind, parameters.logicalMachine, parameters.javascriptEngine, parameters.pgoRunType, parameters.iosLlvmBuild) }} pool: ${{ parameters.pool }} buildConfig: ${{ parameters.buildConfig }} archType: ${{ parameters.archType }} @@ -46,9 +47,10 @@ jobs: logicalmachine: ${{ parameters.logicalmachine }} pgoRunType: ${{ parameters.pgoRunType }} javascriptEngine: ${{ parameters.javascriptEngine }} + iosLlvmBuild: ${{ parameters.iosLlvmBuild }} # Test job depends on the corresponding build job dependsOn: - - ${{ if ne(parameters.runtimeType, 'AndroidMono')}}: + - ${{ if not(in(parameters.runtimeType, 'AndroidMono', 'iOSMono')) }}: - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - ${{ if ne(parameters.liveLibrariesBuildConfig, '') }}: - ${{ format('libraries_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.liveLibrariesBuildConfig) }} @@ -60,13 +62,15 @@ jobs: - ${{ format('build_{0}{1}_{2}_{3}_{4}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.codeGenType) }} - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: - ${{ 'build_Android_arm64_release_AndroidMono' }} + - ${{ if eq(parameters.runtimeType, 'iOSMono')}}: + - ${{ 'build_iOS_arm64_release_iOSMono' }} - ${{ if and(eq(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: + ${{ if and(eq(parameters.osGroup, 'windows'), not(in(parameters.runtimeType, 'AndroidMono', 'iOSMono'))) }}: ${{ if eq(parameters.runtimeType, 'mono') }}: extraSetupParameters: -Architecture ${{ parameters.archType }} -MonoDotnet $(Build.SourcesDirectory)\.dotnet-mono ${{ if eq(parameters.runtimeType, 'coreclr') }}: extraSetupParameters: -CoreRootDirectory $(Build.SourcesDirectory)\artifacts\tests\coreclr\${{ parameters.osGroup }}.${{ parameters.archType }}.Release\Tests\Core_Root -Architecture ${{ parameters.archType }} - ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: + ${{ if and(ne(parameters.osGroup, 'windows'), not(in(parameters.runtimeType, 'AndroidMono', 'iOSMono'))) }}: ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: extraSetupParameters: --architecture ${{ parameters.archType }} --monodotnet $(Build.SourcesDirectory)/.dotnet-mono ${{ if eq(parameters.runtimeType, 'wasm') }}: @@ -79,6 +83,8 @@ jobs: extraSetupParameters: --corerootdirectory $(Build.SourcesDirectory)/artifacts/tests/coreclr/${{ parameters.osGroup }}.${{ parameters.archType }}.Release/Tests/Core_Root --architecture ${{ parameters.archType }} --alpine ${{ if eq(parameters.runtimeType, 'AndroidMono') }}: extraSetupParameters: -Architecture ${{ parameters.archType }} -AndroidMono + ${{ if eq(parameters.runtimeType, 'iosMono') }}: + extraSetupParameters: -Architecture ${{ parameters.archType }} -iOSMono -iOSLlvmBuild:$${{ parameters.iOSLlvmBuild }} variables: ${{ parameters.variables }} @@ -98,7 +104,7 @@ jobs: displayName: 'live-built libraries' # Download coreclr - - ${{ if ne(parameters.runtimeType, 'AndroidMono') }}: + - ${{ if not(in(parameters.runtimeType, 'AndroidMono', 'iOSMono')) }}: - template: /eng/pipelines/common/download-artifact-step.yml parameters: unpackFolder: $(buildProductRootFolderPath) @@ -149,6 +155,16 @@ jobs: artifactFileName: 'AndroidMonoarm64.tar.gz' artifactName: 'AndroidMonoarm64' displayName: 'Mono Android runtime' + + # Download iOSMono tests + - ${{ if eq(parameters.runtimeType, 'iOSMono') }}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory)/iosHelloWorld/nollvm + cleanUnpackFolder: false + artifactFileName: 'iOSSampleAppNoLLVM.tar.gz' + artifactName: 'iOSSampleAppNoLLVM' + displayName: 'iOS Sample App NoLLVM' # Create Core_Root @@ -159,8 +175,8 @@ jobs: # Copy the runtime directory into the testhost folder to include OOBs. - script: "build.cmd -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)\\bin\\mono\\$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\runtime\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\shared\\Microsoft.NETCore.App\\6.0.0 /E /I /Y;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\.dotnet-mono /E /I /Y;copy $(Build.SourcesDirectory)\\artifacts\\bin\\coreclr\\$(osGroup).$(archType).$(buildConfigUpper)\\corerun.exe $(Build.SourcesDirectory)\\.dotnet-mono\\shared\\Microsoft.NETCore.App\\6.0.0\\corerun.exe" displayName: "Create mono dotnet (Windows)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows'), not(in('${{ parameters.runtimeType }}', 'AndroidMono', 'iOSMono'))) - script: "mkdir $(Build.SourcesDirectory)/.dotnet-mono;./build.sh -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)/bin/mono/$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;cp $(Build.SourcesDirectory)/artifacts/bin/runtime/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/shared/Microsoft.NETCore.App/6.0.0 -rf;cp $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/.dotnet-mono -r;cp $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(buildConfigUpper)/corerun $(Build.SourcesDirectory)/.dotnet-mono/shared/Microsoft.NETCore.App/6.0.0/corerun" displayName: "Create mono dotnet (Linux)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows'), not(in('${{ parameters.runtimeType }}', 'AndroidMono', 'iOSMono'))) diff --git a/eng/pipelines/coreclr/templates/run-performance-job.yml b/eng/pipelines/coreclr/templates/run-performance-job.yml index de523bb2d99..e031d0fa1ce 100644 --- a/eng/pipelines/coreclr/templates/run-performance-job.yml +++ b/eng/pipelines/coreclr/templates/run-performance-job.yml @@ -23,6 +23,7 @@ parameters: runKind: '' # required -- test category logicalMachine: '' # required -- Used to specify a which pool of machines the test should run against javascriptEngine: 'NoJS' + iosLlvmBuild: 'False' jobs: - template: xplat-pipeline-job.yml @@ -142,6 +143,6 @@ jobs: displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}' + artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}_${{ parameters.iosLlvmBuild }}' continueOnError: true condition: always() diff --git a/eng/testing/performance/ios_scenarios.proj b/eng/testing/performance/ios_scenarios.proj new file mode 100644 index 00000000000..c7fbf1e5a58 --- /dev/null +++ b/eng/testing/performance/ios_scenarios.proj @@ -0,0 +1,34 @@ + + + python3 + $(HelixPreCommands);chmod +x $HELIX_WORKITEM_PAYLOAD/SOD/SizeOnDisk + + + + + %(Identity) + + + + + nollvm + llvm + + + + %HELIX_CORRELATION_PAYLOAD%\performance\src\scenarios\ + + + + $HELIX_CORRELATION_PAYLOAD/performance/src/scenarios/ + + + + + $(WorkItemDirectory) + cd $(ScenarioDirectory)helloios;xcopy %HELIX_CORRELATION_PAYLOAD%\iosHelloWorld\$(LlvmPath) .\app\/e;$(Python) pre.py + $(Python) test.py sod --scenario-name "%(Identity)" + $(Python) post.py + + + \ No newline at end of file diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index 3fc5a44a52b..03aff42d25f 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -21,9 +21,11 @@ Param( [string] $Configurations="CompilationMode=$CompilationMode RunKind=$Kind", [string] $LogicalMachine="", [switch] $AndroidMono, + [switch] $iOSMono, [switch] $NoPGO, [switch] $DynamicPGO, - [switch] $FullPGO + [switch] $FullPGO, + [switch] $iOSLlvmBuild ) $RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") -or ($Repository -eq "dotnet-performance") @@ -90,6 +92,10 @@ elseif($FullPGO) $Configurations += " PGOType=fullpgo" } +if ($iOSMono) { + $Configurations += " iOSLlvmBuild=$iOSLlvmBuild" +} + # FIX ME: This is a workaround until we get this from the actual pipeline $CommonSetupArguments="--channel main --queue $Queue --build-number $BuildNumber --build-configs $Configurations --architecture $Architecture" $SetupArguments = "--repository https://github.com/$Repository --branch $Branch --get-perf-hash --commit-sha $CommitSha $CommonSetupArguments" @@ -141,6 +147,15 @@ if ($AndroidMono) { $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' } +if ($iOSMono) { + if(!(Test-Path $WorkItemDirectory)) + { + mkdir $WorkItemDirectory + } + Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' +} + $DocsDir = (Join-Path $PerformanceDirectory "docs") robocopy $DocsDir $WorkItemDirectory @@ -168,6 +183,7 @@ Write-PipelineSetVariable -Name 'UseBaselineCoreRun' -Value "$UseBaselineCoreRun Write-PipelineSetVariable -Name 'RunFromPerfRepo' -Value "$RunFromPerformanceRepo" -IsMultiJobVariable $false Write-PipelineSetVariable -Name 'Compare' -Value "$Compare" -IsMultiJobVariable $false Write-PipelineSetVariable -Name 'MonoDotnet' -Value "$UsingMono" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'iOSLlvmBuild' -Value "$iOSLlvmBuild" -IsMultiJobVariable $false # Helix Arguments Write-PipelineSetVariable -Name 'Creator' -Value "$Creator" -IsMultiJobVariable $false diff --git a/src/mono/sample/iOS/Makefile b/src/mono/sample/iOS/Makefile index 0cea32121e6..e3b7578629b 100644 --- a/src/mono/sample/iOS/Makefile +++ b/src/mono/sample/iOS/Makefile @@ -1,8 +1,9 @@ -MONO_CONFIG=Debug -MONO_ARCH=x64 +MONO_CONFIG?=Debug +MONO_ARCH?=x64 DOTNET := ../../../../dotnet.sh USE_LLVM=true -AOT=false +AOT?=false +TARGET?=iOSSimulator #If DIAGNOSTIC_PORTS is enabled, RUNTIME_COMPONENTS must also be enabled. #If RUNTIME_COMPONENTS is enabled, DIAGNOSTIC_PORTS is optional. @@ -20,7 +21,7 @@ appbuilder: $(DOTNET) build -c Debug $(TOOLS_DIR)/AppleAppBuilder/AppleAppBuilder.csproj runtimepack: - ../../../../build.sh Mono+Libs -os iOSSimulator -arch $(MONO_ARCH) -c $(MONO_CONFIG) + ../../../../build.sh Mono+Libs -os $(TARGET) -arch $(MONO_ARCH) -c $(MONO_CONFIG) run: clean appbuilder $(DOTNET) publish \ @@ -41,6 +42,10 @@ run-sim: clean appbuilder '/p:RuntimeComponents="$(RUNTIME_COMPONENTS)"' \ '/p:DiagnosticPorts="$(DIAGNOSTIC_PORTS)"' +build-appbundle: clean appbuilder + $(DOTNET) publish -c $(MONO_CONFIG) /p:TargetOS=$(TARGET) /p:TargetArchitecture=$(MONO_ARCH) \ + /p:UseLLVM=$(USE_LLVM) /p:ForceAOT=$(AOT) /bl \ + run-catalyst: $(DOTNET) publish \ -c $(MONO_CONFIG) \ From c6a7e2f37bec3635d33993fe3ab388deb52988ae Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 24 Jun 2021 15:36:56 -0300 Subject: [PATCH 096/926] [wasm][debugger] Fixing debugger tests errors (#54664) * Fixing new debugger tests errors. Errno is not being assigned anymore. DebuggerTests.SetVariableValueTests * Passing icordbg flag as false. * Adding new tests. --- src/mono/mono/mini/mini-wasm-debugger.c | 23 ++++++++++--------- .../BrowserDebugProxy/MonoSDBHelper.cs | 1 + .../SetVariableValueTests.cs | 2 ++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/mini/mini-wasm-debugger.c index fd19c6a8462..8c5951cf104 100644 --- a/src/mono/mono/mini/mini-wasm-debugger.c +++ b/src/mono/mono/mini/mini-wasm-debugger.c @@ -249,7 +249,8 @@ mono_wasm_breakpoint_hit (void) static gboolean write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* variableValue) { - char* endptr; + char* endptr = NULL; + const char *variableValueEnd = variableValue + strlen(variableValue); errno = 0; buffer_add_byte (buf, type); switch (type) { @@ -268,7 +269,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia break; case MONO_TYPE_I1: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= -128 && val <= 127) buffer_add_int (buf, val); @@ -278,7 +279,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_U1: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= 0 && val <= 255) buffer_add_int (buf, val); @@ -288,7 +289,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_I2: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= -32768 && val <= 32767) buffer_add_int (buf, val); @@ -298,7 +299,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_U2: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= 0 && val <= 65535) buffer_add_int (buf, val); @@ -308,7 +309,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_I4: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= -2147483648 && val <= 2147483647) buffer_add_int (buf, val); @@ -318,7 +319,7 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_U4: { intmax_t val = strtoimax (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; if (val >= 0 && val <= 4294967295) buffer_add_int (buf, val); @@ -328,28 +329,28 @@ write_value_to_buffer (MdbgProtBuffer *buf, MonoTypeEnum type, const char* varia } case MONO_TYPE_I8: { long long val = strtoll (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; buffer_add_long (buf, val); break; } case MONO_TYPE_U8: { long long val = strtoll (variableValue, &endptr, 10); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; buffer_add_long (buf, val); break; } case MONO_TYPE_R4: { gfloat val = strtof (variableValue, &endptr); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; buffer_add_int (buf, *((gint32*)(&val))); break; } case MONO_TYPE_R8: { gdouble val = strtof (variableValue, &endptr); - if (errno != 0) + if (errno != 0 || variableValue == endptr || endptr != variableValueEnd) return FALSE; buffer_add_long (buf, *((guint64*)(&val))); break; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index b3eebcd7e38..48ed3a9992b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -547,6 +547,7 @@ namespace Microsoft.WebAssembly.Diagnostics var command_params_writer = new MonoBinaryWriter(command_params); command_params_writer.Write(MAJOR_VERSION); command_params_writer.Write(MINOR_VERSION); + command_params_writer.Write((byte)0); var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); return true; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SetVariableValueTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SetVariableValueTests.cs index 5f4a5a4f93b..9214a4fe6e8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SetVariableValueTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SetVariableValueTests.cs @@ -212,6 +212,7 @@ namespace DebuggerTests [InlineData(1, "b", 20, "wrongValue")] [InlineData(2, "c", 30, "wrongValue")] [InlineData(3, "d", 50, "wrongValue")] + [InlineData(3, "d", 50, "123wrongValue")] public async Task SetVariableValuesAtBreakpointSiteFail(int offset, string variableName, int originalValue, string invalidValue){ await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", offset); var pause_location = await EvaluateAndCheck( @@ -263,6 +264,7 @@ namespace DebuggerTests [InlineData("A", 10, "error", false)] [InlineData("d", 15, "20", true)] [InlineData("d", 15, "error", false)] + [InlineData("d", 15, "123error", false)] public async Task TestSetValueOnObject(string prop_name, int prop_value, string prop_new_value, bool expect_ok) { var bp = await SetBreakpointInMethod("debugger-test.dll", "Math", "UseComplex", 5); From 3b3099f00add243f7ee453e7fd6975a00a9871ed Mon Sep 17 00:00:00 2001 From: Ryan Lucia Date: Thu, 24 Jun 2021 14:56:04 -0400 Subject: [PATCH 097/926] Remove CoffeeFlux from CODEOWNERS (#54696) --- .github/CODEOWNERS | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b727ef80af0..b4f0685cf0d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -19,17 +19,17 @@ /src/mono/llvm @vargaz @SamMonoRT @imhameed @EgorBo /src/mono/mono/arch @vargaz -/src/mono/mono/eglib @vargaz @lambdageek @CoffeeFlux +/src/mono/mono/eglib @vargaz @lambdageek -/src/mono/mono/metadata @vargaz @lambdageek @thaystg @CoffeeFlux -/src/mono/mono/metadata/*-win* @lateralusX @lambdageek @CoffeeFlux +/src/mono/mono/metadata @vargaz @lambdageek @thaystg +/src/mono/mono/metadata/*-win* @lateralusX @lambdageek /src/mono/mono/metadata/handle* @lambdageek @vargaz /src/mono/mono/metadata/monitor* @brzvlad @vargaz /src/mono/mono/metadata/sgen* @brzvlad @vargaz @naricc /src/mono/mono/metadata/thread* @lateralusX @lambdageek -/src/mono/mono/metadata/w32* @lateralusX @lambdageek @CoffeeFlux +/src/mono/mono/metadata/w32* @lateralusX @lambdageek -/src/mono/mono/mini @vargaz @lambdageek @SamMonoRT @CoffeeFlux @imhameed +/src/mono/mono/mini @vargaz @lambdageek @SamMonoRT @imhameed /src/mono/mono/mini/*cfgdump* @vargaz /src/mono/mono/mini/*exceptions* @vargaz @BrzVlad @imhameed /src/mono/mono/mini/*llvm* @vargaz @imhameed @EgorBo @@ -43,8 +43,8 @@ /src/mono/mono/profiler @BrzVlad @lambdageek /src/mono/mono/sgen @BrzVlad @lambdageek @naricc -/src/mono/mono/utils @vargaz @lambdageek @CoffeeFlux -/src/mono/mono/utils/*-win* @lateralusX @lambdageek @CoffeeFlux +/src/mono/mono/utils @vargaz @lambdageek +/src/mono/mono/utils/*-win* @lateralusX @lambdageek /src/mono/mono/utils/atomic* @vargaz /src/mono/mono/utils/mono-hwcap* @vargaz /src/mono/mono/utils/mono-mem* @vargaz @@ -53,5 +53,3 @@ /src/mono/mono/utils/mono-threads* @lambdageek @vargaz /src/mono/dlls @thaystg @lambdageek - -/src/mono/netcore @marek-safar @akoeplinger @vargaz @steveisok From d4fc723b4982a49deb4a43b5d145cbd667e6077d Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 24 Jun 2021 15:05:15 -0400 Subject: [PATCH 098/926] [mono][wasm] Reenable the remove finally pass. (#54450) * [mono][wasm] Reenable the remove finally pass. * [mono][wasm] Run methods which need stack walks using the interpreter instead of pushing an LMF. * Disable vtype based sharing for vtypes with explicit sizes. --- src/mono/mono/mini/method-to-ir.c | 22 +++++++++++++---- src/mono/mono/mini/mini-generic-sharing.c | 3 +++ src/mono/mono/mini/mini.c | 30 ++++++++++++++++------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 964d31d4126..8ca12378c2d 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -6498,9 +6498,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (cfg->llvm_only && cfg->interp && cfg->method == method) { - if (!cfg->method->wrapper_type && header->num_clauses) - cfg->interp_entry_only = TRUE; - + if (!cfg->method->wrapper_type && header->num_clauses) { + for (int i = 0; i < header->num_clauses; ++i) { + MonoExceptionClause *clause = &header->clauses [i]; + /* Finally clauses are checked after the remove_finally pass */ + if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY) + cfg->interp_entry_only = TRUE; + } + } if (cfg->interp_entry_only) emit_llvmonly_interp_entry (cfg, header); } @@ -7237,8 +7242,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b emit_method_access_failure (cfg, method, cil_method); } - if (cfg->llvm_only && cmethod && method_needs_stack_walk (cfg, cmethod)) - needs_stack_walk = TRUE; + if (cfg->llvm_only && cmethod && method_needs_stack_walk (cfg, cmethod)) { + if (cfg->interp && !cfg->interp_entry_only) { + /* Use the interpreter instead */ + cfg->exception_message = g_strdup ("stack walk"); + cfg->disable_llvm = TRUE; + } else { + needs_stack_walk = TRUE; + } + } if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) { if (!mono_class_is_interface (method->klass)) diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 8b0da4a3f4e..7cf73897551 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -1174,6 +1174,9 @@ get_wrapper_shared_vtype (MonoType *t) if (mono_class_has_failure (klass)) return NULL; + if (m_class_get_type_token (klass) && mono_metadata_packing_from_typedef (m_class_get_image (klass), m_class_get_type_token (klass), NULL, NULL)) + return NULL; + int num_fields = mono_class_get_field_count (klass); MonoClassField *klass_fields = m_class_get_fields (klass); diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index b54bb2cb2a9..76051818cf3 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -2940,13 +2940,18 @@ remove_empty_finally_pass (MonoCompile *cfg) } } if (empty) { - bb->flags &= ~BB_EXCEPTION_HANDLER; + /* Nullify OP_START_HANDLER */ NULLIFY_INS (first); last = mono_bb_last_inst (bb, 0); if (last->opcode == OP_ENDFINALLY) NULLIFY_INS (last); if (cfg->verbose_level > 1) g_print ("removed empty finally clause %d.\n", i); + + /* Mark the handler bb as not used anymore */ + bb = cfg->cil_offset_to_bb [clause->handler_offset]; + bb->flags &= ~BB_EXCEPTION_HANDLER; + cfg->clause_is_dead [i] = TRUE; remove_call_handler = TRUE; } @@ -2956,18 +2961,14 @@ remove_empty_finally_pass (MonoCompile *cfg) if (remove_call_handler) { /* Remove OP_CALL_HANDLER opcodes pointing to the removed finally blocks */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - MonoInst *handler_ins = NULL; MONO_BB_FOR_EACH_INS (bb, ins) { if (ins->opcode == OP_CALL_HANDLER && ins->inst_target_bb && !(ins->inst_target_bb->flags & BB_EXCEPTION_HANDLER)) { - handler_ins = ins; + NULLIFY_INS (ins); + for (MonoInst *ins2 = ins->next; ins2; ins2 = ins2->next) + NULLIFY_INS (ins2); break; } } - if (handler_ins) { - handler_ins->opcode = OP_BR; - for (ins = handler_ins->next; ins; ins = ins->next) - NULLIFY_INS (ins); - } } } } @@ -3596,9 +3597,20 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts mono_cfg_dump_ir (cfg, "if_conversion"); } - // This still causes failures remove_empty_finally_pass (cfg); + if (cfg->llvm_only && cfg->interp && !cfg->method->wrapper_type && !interp_entry_only) { + /* Disable llvm if there are still finally clauses left */ + for (int i = 0; i < cfg->header->num_clauses; ++i) { + MonoExceptionClause *clause = &header->clauses [i]; + if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY && !cfg->clause_is_dead [i]) { + cfg->exception_message = g_strdup ("finally clause."); + cfg->disable_llvm = TRUE; + break; + } + } + } + mono_threads_safepoint (); MONO_TIME_TRACK (mono_jit_stats.jit_bb_ordering, mono_bb_ordering (cfg)); From 7cb47629a859e24e04431010dcd95d68fa49a035 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Thu, 24 Jun 2021 12:09:36 -0700 Subject: [PATCH 099/926] Ensure relocation blocks are 4-byte aligned (#54668) --- .../ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index 608ba52d59b..1e02a7868d4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs @@ -703,6 +703,13 @@ namespace ILCompiler.PEWriter /// 16-bit entries encoding offset relative to the base RVA (low 12 bits) and relocation type (top 4 bite) private static void FlushRelocationBlock(BlobBuilder builder, int baseRVA, List offsetsAndTypes) { + // Ensure blocks are 4-byte aligned. This is required by kernel memory manager + // on Windows 8.1 and earlier. + if ((offsetsAndTypes.Count & 1) == 1) + { + offsetsAndTypes.Add(0); + } + // First, emit the block header: 4 bytes starting RVA, builder.WriteInt32(baseRVA); // followed by the total block size comprising this header From 9bbf90582fcfb98c8c9fd279e384c7cf630bfad6 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Thu, 24 Jun 2021 22:34:08 +0300 Subject: [PATCH 100/926] Add args descriptions for VNF_MapStore and VNF_MapSelect (#54108) --- src/coreclr/jit/valuenumfuncs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 23364910c1e..aa13273c877 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -6,8 +6,8 @@ // ) // clang-format off -ValueNumFuncDef(MapStore, 3, false, false, false) -ValueNumFuncDef(MapSelect, 2, false, false, false) +ValueNumFuncDef(MapStore, 3, false, false, false) // Args: 0: map, 1: index (e. g. field handle), 2: value being stored. +ValueNumFuncDef(MapSelect, 2, false, false, false) // Args: 0: map, 1: key. ValueNumFuncDef(FieldSeq, 2, false, false, false) // Sequence (VN of null == empty) of (VN's of) field handles. ValueNumFuncDef(NotAField, 0, false, false, false) // Value number function for FieldSeqStore::NotAField. From 8faef776655a6b8054972950d5bb043f1d00e830 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 24 Jun 2021 14:35:35 -0500 Subject: [PATCH 101/926] Support synchronous Stream-based serialization methods (#54632) --- .../Text/Json/PooledByteBufferWriter.cs | 10 + .../src/System/Net/Http/Json/JsonContent.cs | 15 +- .../System/Net/Http/Json/JsonContentOfT.cs | 10 +- .../System.Text.Json/ref/System.Text.Json.cs | 14 +- .../src/System.Text.Json.csproj | 2 +- .../JsonSerializer.Read.Stream.cs | 258 +++++++++++++- .../JsonSerializer.Write.Stream.cs | 202 ++++++++++- ...AsyncBufferState.cs => ReadBufferState.cs} | 6 +- .../ConstructorTests.ParameterMatching.cs | 17 +- .../Serialization/DeserializationWrapper.cs | 139 -------- .../Serialization/InvalidTypeTests.cs | 23 +- .../JsonSerializationWrapperForStream.cs | 94 +++++ .../JsonSerializerWrapperForString.cs | 334 ++++++++++++++++++ .../MetadataTests.JsonSerializer.cs | 4 +- .../MetadataTests/MetadataTests.cs | 22 +- .../Serialization/NumberHandlingTests.cs | 15 +- .../Serialization/PolymorphicTests.cs | 23 +- .../Serialization/SerializationWrapper.cs | 166 --------- .../Serialization/Stream.Collections.cs | 20 +- .../Serialization/Stream.ReadTests.cs | 64 ++-- .../Serialization/Stream.WriteTests.cs | 67 ++-- .../Serialization/StreamTests.cs | 28 ++ .../Serialization/WriteValueTests.cs | 4 +- .../System.Text.Json.Tests.csproj | 5 +- 24 files changed, 1071 insertions(+), 471 deletions(-) rename src/libraries/System.Text.Json/src/System/Text/Json/Serialization/{ReadAsyncBufferState.cs => ReadBufferState.cs} (84%) delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DeserializationWrapper.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SerializationWrapper.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs diff --git a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs index 6d6feee2aec..80e7bc26ae3 100644 --- a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs +++ b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs @@ -116,11 +116,21 @@ namespace System.Text.Json { return destination.WriteAsync(WrittenMemory, cancellationToken); } + + internal void WriteToStream(Stream destination) + { + destination.Write(WrittenMemory.Span); + } #else internal Task WriteToStreamAsync(Stream destination, CancellationToken cancellationToken) { return destination.WriteAsync(_rentedBuffer, 0, _index, cancellationToken); } + + internal void WriteToStream(Stream destination) + { + destination.Write(_rentedBuffer, 0, _index); + } #endif private void CheckAndResizeBuffer(int sizeHint) diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs index 03a9cefd3b0..f1430c31ea5 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs @@ -79,10 +79,7 @@ namespace System.Net.Http.Json } else { - // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. - // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 - using var writer = new Utf8JsonWriter(transcodingStream); - SerializeHelper(writer, Value, ObjectType, _jsonSerializerOptions); + SerializeSyncHelper(transcodingStream, Value, ObjectType, _jsonSerializerOptions); } } finally @@ -120,21 +117,17 @@ namespace System.Net.Http.Json else { #if NETCOREAPP - // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. - // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 - using var writer = new Utf8JsonWriter(targetStream); - SerializeHelper(writer, Value, ObjectType, _jsonSerializerOptions); + SerializeSyncHelper(targetStream, Value, ObjectType, _jsonSerializerOptions); #else Debug.Fail("Synchronous serialization is only supported since .NET 5.0"); #endif } } - #if NETCOREAPP [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")] - static void SerializeHelper(Utf8JsonWriter writer, object? value, Type inputType, JsonSerializerOptions? options) - => JsonSerializer.Serialize(writer, value, inputType, options); + static void SerializeSyncHelper(Stream utf8Json, object? value, Type inputType, JsonSerializerOptions? options) + => JsonSerializer.Serialize(utf8Json, value, inputType, options); #endif [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs index 5225f412d06..a676e425c43 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs @@ -58,10 +58,7 @@ namespace System.Net.Http.Json } else { - // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. - // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 - using var writer = new Utf8JsonWriter(transcodingStream); - JsonSerializer.Serialize(writer, _typedValue, _typeInfo); + JsonSerializer.Serialize(transcodingStream, _typedValue, _typeInfo); } } finally @@ -99,10 +96,7 @@ namespace System.Net.Http.Json else { #if NETCOREAPP - // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. - // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 - using var writer = new Utf8JsonWriter(targetStream); - JsonSerializer.Serialize(writer, _typedValue, _typeInfo); + JsonSerializer.Serialize(targetStream, _typedValue, _typeInfo); #else Debug.Fail("Synchronous serialization is only supported since .NET 5.0"); #endif diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index ed8880c117f..e68bffd9205 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -186,6 +186,9 @@ namespace System.Text.Json } public static partial class JsonSerializer { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static object? Deserialize(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static object? Deserialize(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static object? Deserialize(System.ReadOnlySpan utf8Json, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static object? Deserialize(System.ReadOnlySpan utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } @@ -202,11 +205,14 @@ namespace System.Text.Json public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] - public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable< TValue>(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static TValue? Deserialize(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static TValue? Deserialize(System.IO.Stream utf8Json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static TValue? Deserialize(System.ReadOnlySpan utf8Json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize(System.ReadOnlySpan utf8Json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] @@ -219,6 +225,9 @@ namespace System.Text.Json public static TValue? Deserialize< TValue>(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static void Serialize(System.IO.Stream utf8Json, object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { } + public static void Serialize(System.IO.Stream utf8Json, object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static string Serialize(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static string Serialize(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] @@ -237,6 +246,9 @@ namespace System.Text.Json public static byte[] SerializeToUtf8Bytes(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static byte[] SerializeToUtf8Bytes(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static void Serialize(System.IO.Stream utf8Json, TValue value, System.Text.Json.JsonSerializerOptions? options = null) { } + public static void Serialize(System.IO.Stream utf8Json, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static void Serialize(System.Text.Json.Utf8JsonWriter writer, TValue value, System.Text.Json.JsonSerializerOptions? options = null) { } public static void Serialize(System.Text.Json.Utf8JsonWriter writer, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index e3ba73d6b6c..bc957caea23 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -212,7 +212,7 @@ - + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs index 4f8a5e91b6d..617485fed7d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs @@ -53,6 +53,38 @@ namespace System.Text.Json return ReadAllUsingOptionsAsync(utf8Json, typeof(TValue), options, cancellationToken); } + /// + /// Read the UTF-8 encoded text representing a single JSON value into a . + /// The Stream will be read to completion. + /// + /// A representation of the JSON value. + /// JSON data to parse. + /// Options to control the behavior during reading. + /// + /// is . + /// + /// + /// Thrown when the JSON is invalid, + /// is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static TValue? Deserialize( + Stream utf8Json, + JsonSerializerOptions? options = null) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + return ReadAllUsingOptions(utf8Json, typeof(TValue), options); + } + /// /// Read the UTF-8 encoded text representing a single JSON value into a . /// The Stream will be read to completion. @@ -96,6 +128,45 @@ namespace System.Text.Json return ReadAllUsingOptionsAsync(utf8Json, returnType, options, cancellationToken); } + /// + /// Read the UTF-8 encoded text representing a single JSON value into a . + /// The Stream will be read to completion. + /// + /// A representation of the JSON value. + /// JSON data to parse. + /// The type of the object to convert to and return. + /// Options to control the behavior during reading. + /// + /// or is . + /// + /// + /// Thrown when the JSON is invalid, + /// the is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static object? Deserialize( + Stream utf8Json, + Type returnType, + JsonSerializerOptions? options = null) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + return ReadAllUsingOptions(utf8Json, returnType, options); + } + /// /// Read the UTF-8 encoded text representing a single JSON value into a . /// The Stream will be read to completion. @@ -133,7 +204,43 @@ namespace System.Text.Json throw new ArgumentNullException(nameof(jsonTypeInfo)); } - return ReadAllAsync(utf8Json, typeof(TValue), jsonTypeInfo, cancellationToken); + return ReadAllAsync(utf8Json, jsonTypeInfo, cancellationToken); + } + + /// + /// Read the UTF-8 encoded text representing a single JSON value into a . + /// The Stream will be read to completion. + /// + /// A representation of the JSON value. + /// JSON data to parse. + /// Metadata about the type to convert. + /// + /// or is . + /// + /// + /// Thrown when the JSON is invalid, + /// is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static TValue? Deserialize( + Stream utf8Json, + JsonTypeInfo jsonTypeInfo) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return ReadAll(utf8Json, jsonTypeInfo); } /// @@ -184,7 +291,54 @@ namespace System.Text.Json throw new ArgumentNullException(nameof(context)); } - return ReadAllAsync(utf8Json, returnType, GetTypeInfo(context, returnType), cancellationToken); + return ReadAllAsync(utf8Json, GetTypeInfo(context, returnType), cancellationToken); + } + + /// + /// Read the UTF-8 encoded text representing a single JSON value into a . + /// The Stream will be read to completion. + /// + /// A representation of the JSON value. + /// JSON data to parse. + /// The type of the object to convert to and return. + /// A metadata provider for serializable types. + /// + /// , , or is . + /// + /// + /// Thrown when the JSON is invalid, + /// the is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method on the provided + /// did not return a compatible for . + /// + public static object? Deserialize( + Stream utf8Json, + Type returnType, + JsonSerializerContext context) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ReadAll(utf8Json, GetTypeInfo(context, returnType)); } /// @@ -221,7 +375,7 @@ namespace System.Text.Json JsonSerializerOptions options, [EnumeratorCancellation] CancellationToken cancellationToken) { - var bufferState = new ReadAsyncBufferState(options.DefaultBufferSize); + var bufferState = new ReadBufferState(options.DefaultBufferSize); // Hardcode the queue converter to avoid accidental use of custom converters JsonConverter converter = QueueOfTConverter, TValue>.Instance; JsonTypeInfo jsonTypeInfo = CreateQueueJsonTypeInfo(converter, options); @@ -253,18 +407,17 @@ namespace System.Text.Json } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")] + Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")] private static JsonTypeInfo CreateQueueJsonTypeInfo(JsonConverter queueConverter, JsonSerializerOptions queueOptions) => - new JsonTypeInfo(typeof(Queue), queueConverter, typeof(Queue), queueOptions); + new JsonTypeInfo(typeof(Queue), queueConverter, typeof(Queue), queueOptions); internal static async ValueTask ReadAllAsync( Stream utf8Json, - Type inputType, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken) { JsonSerializerOptions options = jsonTypeInfo.Options; - var asyncState = new ReadAsyncBufferState(options.DefaultBufferSize); + var bufferState = new ReadBufferState(options.DefaultBufferSize); ReadStack readStack = default; readStack.Initialize(jsonTypeInfo, supportContinuation: true); JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase; @@ -274,10 +427,10 @@ namespace System.Text.Json { while (true) { - asyncState = await ReadFromStreamAsync(utf8Json, asyncState, cancellationToken).ConfigureAwait(false); - TValue value = ContinueDeserialize(ref asyncState, ref jsonReaderState, ref readStack, converter, options); + bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false); + TValue value = ContinueDeserialize(ref bufferState, ref jsonReaderState, ref readStack, converter, options); - if (asyncState.IsFinalBlock) + if (bufferState.IsFinalBlock) { return value!; } @@ -285,7 +438,37 @@ namespace System.Text.Json } finally { - asyncState.Dispose(); + bufferState.Dispose(); + } + } + + internal static TValue? ReadAll( + Stream utf8Json, + JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + var bufferState = new ReadBufferState(options.DefaultBufferSize); + ReadStack readStack = default; + readStack.Initialize(jsonTypeInfo, supportContinuation: true); + JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase; + var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); + + try + { + while (true) + { + bufferState = ReadFromStream(utf8Json, bufferState); + TValue value = ContinueDeserialize(ref bufferState, ref jsonReaderState, ref readStack, converter, options); + + if (bufferState.IsFinalBlock) + { + return value!; + } + } + } + finally + { + bufferState.Dispose(); } } @@ -294,9 +477,9 @@ namespace System.Text.Json /// Calling ReadCore is relatively expensive, so we minimize the number of times /// we need to call it. /// - internal static async ValueTask ReadFromStreamAsync( + internal static async ValueTask ReadFromStreamAsync( Stream utf8Json, - ReadAsyncBufferState bufferState, + ReadBufferState bufferState, CancellationToken cancellationToken) { while (true) @@ -326,8 +509,43 @@ namespace System.Text.Json return bufferState; } + /// + /// Read from the stream until either our buffer is filled or we hit EOF. + /// Calling ReadCore is relatively expensive, so we minimize the number of times + /// we need to call it. + /// + internal static ReadBufferState ReadFromStream( + Stream utf8Json, + ReadBufferState bufferState) + { + while (true) + { + int bytesRead = utf8Json.Read( +#if BUILDING_INBOX_LIBRARY + bufferState.Buffer.AsSpan(bufferState.BytesInBuffer)); +#else + bufferState.Buffer, bufferState.BytesInBuffer, bufferState.Buffer.Length - bufferState.BytesInBuffer); +#endif + + if (bytesRead == 0) + { + bufferState.IsFinalBlock = true; + break; + } + + bufferState.BytesInBuffer += bytesRead; + + if (bufferState.BytesInBuffer == bufferState.Buffer.Length) + { + break; + } + } + + return bufferState; + } + internal static TValue ContinueDeserialize( - ref ReadAsyncBufferState bufferState, + ref ReadBufferState bufferState, ref JsonReaderState jsonReaderState, ref ReadStack readStack, JsonConverter converter, @@ -406,7 +624,17 @@ namespace System.Text.Json CancellationToken cancellationToken) { JsonTypeInfo jsonTypeInfo = GetTypeInfo(returnType, options); - return ReadAllAsync(utf8Json, returnType, jsonTypeInfo, cancellationToken); + return ReadAllAsync(utf8Json, jsonTypeInfo, cancellationToken); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static TValue? ReadAllUsingOptions( + Stream utf8Json, + Type returnType, + JsonSerializerOptions? options) + { + JsonTypeInfo jsonTypeInfo = GetTypeInfo(returnType, options); + return ReadAll(utf8Json, jsonTypeInfo); } private static TValue ReadCore( diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs index 7550dd9a600..b5acccd9e42 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.Json.Serialization; @@ -12,6 +13,14 @@ namespace System.Text.Json { public static partial class JsonSerializer { + // We flush the Stream when the buffer is >=90% of capacity. + // This threshold is a compromise between buffer utilization and minimizing cases where the buffer + // needs to be expanded\doubled because it is not large enough to write the current property or element. + // We check for flush after each JSON property and element is written to the buffer. + // Once the buffer is expanded to contain the largest single element\property, a 90% thresold + // means the buffer may be expanded a maximum of 4 times: 1-(1\(2^4))==.9375. + private const float FlushThreshold = .9f; + /// /// Convert the provided value to UTF-8 encoded JSON text and write it to the . /// @@ -47,6 +56,38 @@ namespace System.Text.Json cancellationToken); } + + /// + /// Convert the provided value to UTF-8 encoded JSON text and write it to the . + /// + /// The UTF-8 to write to. + /// The value to convert. + /// Options to control the conversion behavior. + /// + /// is . + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static void Serialize( + Stream utf8Json, + TValue value, + JsonSerializerOptions? options = null) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + Write( + utf8Json, + value, + GetRuntimeType(value), + options); + } + /// /// Convert the provided value to UTF-8 encoded JSON text and write it to the . /// @@ -87,6 +128,42 @@ namespace System.Text.Json cancellationToken); } + /// + /// Convert the provided value to UTF-8 encoded JSON text and write it to the . + /// + /// The UTF-8 to write to. + /// The value to convert. + /// The type of the to convert. + /// Options to control the conversion behavior. + /// + /// is not compatible with . + /// + /// + /// or is . + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static void Serialize( + Stream utf8Json, + object? value, + Type inputType, + JsonSerializerOptions? options = null) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + Write( + utf8Json, + value!, + GetRuntimeTypeAndValidateInputType(value, inputType), + options); + } + /// /// Convert the provided value to UTF-8 encoded JSON text and write it to the . /// @@ -102,7 +179,11 @@ namespace System.Text.Json /// There is no compatible /// for or its serializable members. /// - public static Task SerializeAsync(Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + public static Task SerializeAsync( + Stream utf8Json, + TValue value, + JsonTypeInfo jsonTypeInfo, + CancellationToken cancellationToken = default) { if (utf8Json == null) { @@ -117,6 +198,37 @@ namespace System.Text.Json return WriteAsyncCore(utf8Json, value, jsonTypeInfo, cancellationToken); } + /// + /// Convert the provided value to UTF-8 encoded JSON text and write it to the . + /// + /// The UTF-8 to write to. + /// The value to convert. + /// Metadata about the type to convert. + /// + /// is . + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static void Serialize( + Stream utf8Json, + TValue value, + JsonTypeInfo jsonTypeInfo) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + WriteCore(utf8Json, value, jsonTypeInfo); + } + /// /// Convert the provided value to UTF-8 encoded JSON text and write it to the . /// @@ -130,7 +242,7 @@ namespace System.Text.Json /// is not compatible with . /// /// - /// or is . + /// , or is . /// /// /// There is no compatible @@ -161,6 +273,43 @@ namespace System.Text.Json cancellationToken); } + /// + /// Convert the provided value to UTF-8 encoded JSON text and write it to the . + /// + /// The UTF-8 to write to. + /// The value to convert. + /// The type of the to convert. + /// A metadata provider for serializable types. + /// + /// is not compatible with . + /// + /// + /// , or is . + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static void Serialize( + Stream utf8Json, + object? value, + Type inputType, + JsonSerializerContext context) + { + if (utf8Json == null) + { + throw new ArgumentNullException(nameof(utf8Json)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Type runtimeType = GetRuntimeTypeAndValidateInputType(value, inputType); + WriteCore(utf8Json, value!, GetTypeInfo(context, runtimeType)); + } + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] private static Task WriteAsync( Stream utf8Json, @@ -173,20 +322,23 @@ namespace System.Text.Json return WriteAsyncCore(utf8Json, value!, jsonTypeInfo, cancellationToken); } + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static void Write( + Stream utf8Json, + in TValue value, + Type runtimeType, + JsonSerializerOptions? options) + { + JsonTypeInfo jsonTypeInfo = GetTypeInfo(runtimeType, options); + WriteCore(utf8Json, value!, jsonTypeInfo); + } + private static async Task WriteAsyncCore( Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken) { - // We flush the Stream when the buffer is >=90% of capacity. - // This threshold is a compromise between buffer utilization and minimizing cases where the buffer - // needs to be expanded\doubled because it is not large enough to write the current property or element. - // We check for flush after each object property and array element is written to the buffer. - // Once the buffer is expanded to contain the largest single element\property, a 90% thresold - // means the buffer may be expanded a maximum of 4 times: 1-(1\(2^4))==.9375. - const float FlushThreshold = .9f; - JsonSerializerOptions options = jsonTypeInfo.Options; JsonWriterOptions writerOptions = options.GetWriterOptions(); @@ -241,5 +393,35 @@ namespace System.Text.Json } } } + + private static void WriteCore( + Stream utf8Json, + in TValue value, + JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + JsonWriterOptions writerOptions = options.GetWriterOptions(); + + using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize)) + using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) + { + WriteStack state = default; + JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true); + + bool isFinalBlock; + + do + { + state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); + + isFinalBlock = WriteCore(converter, writer, value, options, ref state); + + bufferWriter.WriteToStream(utf8Json); + bufferWriter.Clear(); + + Debug.Assert(state.PendingTask == null); + } while (!isFinalBlock); + } + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs similarity index 84% rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs rename to src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs index 2fac132521f..395c5f9db1c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadAsyncBufferState.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; -using System.Diagnostics; -using System.Threading; namespace System.Text.Json.Serialization { - internal struct ReadAsyncBufferState : IDisposable + internal struct ReadBufferState : IDisposable { public byte[] Buffer; public int BytesInBuffer; @@ -15,7 +13,7 @@ namespace System.Text.Json.Serialization public bool IsFirstIteration; public bool IsFinalBlock; - public ReadAsyncBufferState(int defaultBufferSize) + public ReadBufferState(int defaultBufferSize) { Buffer = ArrayPool.Shared.Rent(Math.Max(defaultBufferSize, JsonConstants.Utf8Bom.Length)); BytesInBuffer = ClearMax = 0; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ConstructorTests/ConstructorTests.ParameterMatching.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ConstructorTests/ConstructorTests.ParameterMatching.cs index 5a249bcd6bc..54e407cc9fc 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ConstructorTests/ConstructorTests.ParameterMatching.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ConstructorTests/ConstructorTests.ParameterMatching.cs @@ -11,24 +11,29 @@ namespace System.Text.Json.Serialization.Tests { public class ConstructorTests_String : ConstructorTests { - public ConstructorTests_String() : base(DeserializationWrapper.StringDeserializer) { } + public ConstructorTests_String() : base(JsonSerializerWrapperForString.StringSerializer) { } } - public class ConstructorTests_Stream : ConstructorTests + public class ConstructorTests_AsyncStream : ConstructorTests { - public ConstructorTests_Stream() : base(DeserializationWrapper.StreamDeserializer) { } + public ConstructorTests_AsyncStream() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } + } + + public class ConstructorTests_SyncStream : ConstructorTests + { + public ConstructorTests_SyncStream() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } } public class ConstructorTests_Span : ConstructorTests { - public ConstructorTests_Span() : base(DeserializationWrapper.SpanDeserializer) { } + public ConstructorTests_Span() : base(JsonSerializerWrapperForString.SpanSerializer) { } } public abstract partial class ConstructorTests { - private DeserializationWrapper Serializer { get; } + private JsonSerializerWrapperForString Serializer { get; } - public ConstructorTests(DeserializationWrapper serializer) + public ConstructorTests(JsonSerializerWrapperForString serializer) { Serializer = serializer; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DeserializationWrapper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DeserializationWrapper.cs deleted file mode 100644 index 790d0f115db..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DeserializationWrapper.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; - -namespace System.Text.Json.Serialization.Tests -{ - /// - /// Base class for wrapping serialization calls which allows tests to run under different configurations. - /// - public abstract class DeserializationWrapper - { - private static readonly JsonSerializerOptions _optionsWithSmallBuffer = new JsonSerializerOptions { DefaultBufferSize = 1 }; - - public static DeserializationWrapper StringDeserializer => new StringDeserializerWrapper(); - public static DeserializationWrapper StreamDeserializer => new StreamDeserializerWrapper(); - public static DeserializationWrapper SpanDeserializer => new SpanDeserializerWrapper(); - - public static DeserializationWrapper ReaderDeserializer => new ReaderDeserializerWrapper(); - - protected internal abstract Task DeserializeWrapper(string json, JsonSerializerOptions options = null); - - protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null); - - protected internal abstract Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo); - - protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerContext context); - - private class StringDeserializerWrapper : DeserializationWrapper - { - protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Deserialize(json, options)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Deserialize(json, type, options)); - } - - protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) - { - return Task.FromResult(JsonSerializer.Deserialize(json, jsonTypeInfo)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) - { - return Task.FromResult(JsonSerializer.Deserialize(json, type, context)); - } - } - - private class StreamDeserializerWrapper : DeserializationWrapper - { - protected internal override async Task DeserializeWrapper(string json, JsonSerializerOptions options = null) - { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - return await JsonSerializer.DeserializeAsync(stream, options ?? _optionsWithSmallBuffer); - } - } - - protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) - { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - return await JsonSerializer.DeserializeAsync(stream, type, options ?? _optionsWithSmallBuffer); - } - } - - protected internal override async Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) - { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - return await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo); - } - } - - protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) - { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - return await JsonSerializer.DeserializeAsync(stream, type, context); - } - } - } - - private class SpanDeserializerWrapper : DeserializationWrapper - { - protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), options)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), type, options)); - } - - protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) - { - return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), jsonTypeInfo)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) - { - return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), type, context)); - } - } - - private class ReaderDeserializerWrapper : DeserializationWrapper - { - protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) - { - Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); - return Task.FromResult(JsonSerializer.Deserialize(ref reader, options)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) - { - Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); - return Task.FromResult(JsonSerializer.Deserialize(ref reader, type, options)); - } - - protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) - { - Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); - return Task.FromResult(JsonSerializer.Deserialize(ref reader, jsonTypeInfo)); - } - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) - { - Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); - return Task.FromResult(JsonSerializer.Deserialize(ref reader, type, context)); - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs index 6a39e9d1985..c449367cdad 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/InvalidTypeTests.cs @@ -9,34 +9,39 @@ namespace System.Text.Json.Serialization.Tests { public class InvalidTypeTests_Span : InvalidTypeTests { - public InvalidTypeTests_Span() : base(SerializationWrapper.SpanSerializer) { } + public InvalidTypeTests_Span() : base(JsonSerializerWrapperForString.SpanSerializer) { } } public class InvalidTypeTests_String : InvalidTypeTests { - public InvalidTypeTests_String() : base(SerializationWrapper.StringSerializer) { } + public InvalidTypeTests_String() : base(JsonSerializerWrapperForString.StringSerializer) { } } - public class InvalidTypeTests_Stream : InvalidTypeTests + public class InvalidTypeTests_AsyncStream : InvalidTypeTests { - public InvalidTypeTests_Stream() : base(SerializationWrapper.StreamSerializer) { } + public InvalidTypeTests_AsyncStream() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } } - public class InvalidTypeTests_StreamWithSmallBuffer : InvalidTypeTests + public class InvalidTypeTests_AsyncStreamWithSmallBuffer : InvalidTypeTests { - public InvalidTypeTests_StreamWithSmallBuffer() : base(SerializationWrapper.StreamSerializerWithSmallBuffer) { } + public InvalidTypeTests_AsyncStreamWithSmallBuffer() : base(JsonSerializerWrapperForString.AsyncStreamSerializerWithSmallBuffer) { } + } + + public class InvalidTypeTests_SyncStream : InvalidTypeTests + { + public InvalidTypeTests_SyncStream() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } } public class InvalidTypeTests_Writer : InvalidTypeTests { - public InvalidTypeTests_Writer() : base(SerializationWrapper.WriterSerializer) { } + public InvalidTypeTests_Writer() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } } public abstract class InvalidTypeTests { - private SerializationWrapper Serializer { get; } + private JsonSerializerWrapperForString Serializer { get; } - public InvalidTypeTests(SerializationWrapper serializer) + public InvalidTypeTests(JsonSerializerWrapperForString serializer) { Serializer = serializer; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs new file mode 100644 index 00000000000..365bf23a1d2 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace System.Text.Json.Serialization.Tests +{ + /// + /// Base class for wrapping Stream-based JsonSerializer methods which allows tests to run under different configurations. + /// + public abstract class JsonSerializationWrapperForStream + { + public static JsonSerializationWrapperForStream AsyncStreamSerializer => new AsyncStreamSerializerWrapper(); + public static JsonSerializationWrapperForStream SyncStreamSerializer => new SyncStreamSerializerWrapper(); + + protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null); + protected internal abstract Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null); + protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo); + + private class AsyncStreamSerializerWrapper : JsonSerializationWrapperForStream + { + protected internal override async Task SerializeWrapper(Stream utf8Json, T value, JsonSerializerOptions options = null) + { + await JsonSerializer.SerializeAsync(utf8Json, value, options); + } + + protected internal override async Task SerializeWrapper(Stream utf8Json, object value, Type inputType, JsonSerializerOptions options = null) + { + await JsonSerializer.SerializeAsync(utf8Json, value, inputType, options); + } + + protected internal override async Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo) + { + await JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null) + { + return await JsonSerializer.DeserializeAsync(utf8Json, options); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null) + { + return await JsonSerializer.DeserializeAsync(utf8Json, returnType, options); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo) + { + return await JsonSerializer.DeserializeAsync(utf8Json, jsonTypeInfo); + } + } + + private class SyncStreamSerializerWrapper : JsonSerializationWrapperForStream + { + protected internal override Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null) + { + JsonSerializer.Serialize(stream, value, options); + return Task.FromResult(false); + } + + protected internal override Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null) + { + JsonSerializer.Serialize(stream, value, inputType, options); + return Task.FromResult(false); + } + + protected internal override Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo) + { + JsonSerializer.Serialize(stream, value, jsonTypeInfo); + return Task.FromResult(false); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null) + { + return await Task.FromResult(JsonSerializer.Deserialize(utf8Json, options)); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null) + { + return await Task.FromResult(JsonSerializer.Deserialize(utf8Json, returnType, options)); + } + + protected internal override async Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo) + { + return await Task.FromResult(JsonSerializer.Deserialize(utf8Json, jsonTypeInfo)); + } + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs new file mode 100644 index 00000000000..b01738c7a96 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs @@ -0,0 +1,334 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + /// + /// Base class for wrapping string-based JsonSerializer methods which allows tests to run under different configurations. + /// + public abstract class JsonSerializerWrapperForString + { + private static readonly JsonSerializerOptions _optionsWithSmallBuffer = new JsonSerializerOptions { DefaultBufferSize = 1 }; + + public static JsonSerializerWrapperForString SpanSerializer => new SpanSerializerWrapper(); + public static JsonSerializerWrapperForString StringSerializer => new StringSerializerWrapper(); + public static JsonSerializerWrapperForString AsyncStreamSerializer => new AsyncStreamSerializerWrapper(); + public static JsonSerializerWrapperForString AsyncStreamSerializerWithSmallBuffer => new AsyncStreamSerializerWrapperWithSmallBuffer(); + public static JsonSerializerWrapperForString SyncStreamSerializer => new SyncStreamSerializerWrapper(); + public static JsonSerializerWrapperForString ReaderWriterSerializer => new ReaderWriterSerializerWrapper(); + + protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null); + + protected internal abstract Task SerializeWrapper(T value, JsonSerializerOptions options = null); + + protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context); + + protected internal abstract Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo); + + protected internal abstract Task DeserializeWrapper(string json, JsonSerializerOptions options = null); + + protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null); + + protected internal abstract Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo); + + protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerContext context); + + private class SpanSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, inputType, options); + return Task.FromResult(Encoding.UTF8.GetString(result)); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, options); + return Task.FromResult(Encoding.UTF8.GetString(result)); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, inputType, context); + return Task.FromResult(Encoding.UTF8.GetString(result)); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, jsonTypeInfo); + return Task.FromResult(Encoding.UTF8.GetString(result)); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + return Task.FromResult(JsonSerializer.Deserialize(json.AsSpan(), type, context)); + } + } + + private class StringSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Serialize(value, inputType, options)); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Serialize(value, options)); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + return Task.FromResult(JsonSerializer.Serialize(value, inputType, context)); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + return Task.FromResult(JsonSerializer.Serialize(value, jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Deserialize(json, options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + return Task.FromResult(JsonSerializer.Deserialize(json, type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + return Task.FromResult(JsonSerializer.Deserialize(json, jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + return Task.FromResult(JsonSerializer.Deserialize(json, type, context)); + } + } + + private class AsyncStreamSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, value, inputType, options); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override async Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, value, options); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, value, inputType, context); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override async Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override async Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return await JsonSerializer.DeserializeAsync(stream, options ?? _optionsWithSmallBuffer); + } + } + + protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return await JsonSerializer.DeserializeAsync(stream, type, options ?? _optionsWithSmallBuffer); + } + } + + protected internal override async Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo); + } + } + + protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return await JsonSerializer.DeserializeAsync(stream, type, context); + } + } + } + + private class AsyncStreamSerializerWrapperWithSmallBuffer : AsyncStreamSerializerWrapper + { + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + if (options == null) + { + options = _optionsWithSmallBuffer; + } + + return base.SerializeWrapper(value, inputType, options); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + return base.SerializeWrapper(value, options); + } + } + + private class SyncStreamSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + using var stream = new MemoryStream(); + JsonSerializer.Serialize(stream, value, inputType, options); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + using var stream = new MemoryStream(); + JsonSerializer.Serialize(stream, value, options); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + using var stream = new MemoryStream(); + JsonSerializer.Serialize(stream, value, inputType, context); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + using var stream = new MemoryStream(); + JsonSerializer.Serialize(stream, value, jsonTypeInfo); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return Task.FromResult(JsonSerializer.Deserialize(stream, options ?? _optionsWithSmallBuffer)); + } + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return Task.FromResult(JsonSerializer.Deserialize(stream, type, options ?? _optionsWithSmallBuffer)); + } + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return Task.FromResult(JsonSerializer.Deserialize(stream, jsonTypeInfo)); + } + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return Task.FromResult(JsonSerializer.Deserialize(stream, type, context)); + } + } + } + + private class ReaderWriterSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + using MemoryStream stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + JsonSerializer.Serialize(writer, value, inputType, options); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + using MemoryStream stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + JsonSerializer.Serialize(writer, value, options); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + using MemoryStream stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + JsonSerializer.Serialize(writer, value, inputType, context); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + using MemoryStream stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + JsonSerializer.Serialize(writer, value, jsonTypeInfo); + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(ref reader, options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(ref reader, type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(ref reader, jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(ref reader, type, context)); + } + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs index 40137337b65..b4db06777a3 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs @@ -15,11 +15,11 @@ namespace System.Text.Json.Tests.Serialization { WeatherForecastWithPOCOs expected = CreateWeatherForecastWithPOCOs(); string json = await Serializer.SerializeWrapper(expected, JsonContext.Default.WeatherForecastWithPOCOs); - WeatherForecastWithPOCOs actual = await Deserializer.DeserializeWrapper(json, JsonContext.Default.WeatherForecastWithPOCOs); + WeatherForecastWithPOCOs actual = await Serializer.DeserializeWrapper(json, JsonContext.Default.WeatherForecastWithPOCOs); VerifyWeatherForecastWithPOCOs(expected, actual); json = await Serializer.SerializeWrapper(actual, typeof(WeatherForecastWithPOCOs), JsonContext.Default); - actual = (WeatherForecastWithPOCOs)await Deserializer.DeserializeWrapper(json, typeof(WeatherForecastWithPOCOs), JsonContext.Default); + actual = (WeatherForecastWithPOCOs)await Serializer.DeserializeWrapper(json, typeof(WeatherForecastWithPOCOs), JsonContext.Default); VerifyWeatherForecastWithPOCOs(expected, actual); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs index 2587c571d32..7212b051e73 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs @@ -8,34 +8,36 @@ namespace System.Text.Json.Tests.Serialization { public sealed class MetadataTests_Span : MetadataTests { - public MetadataTests_Span() : base(SerializationWrapper.SpanSerializer, DeserializationWrapper.SpanDeserializer) { } + public MetadataTests_Span() : base(JsonSerializerWrapperForString.SpanSerializer) { } } public sealed class MetadataTests_String : MetadataTests { - public MetadataTests_String() : base(SerializationWrapper.StringSerializer, DeserializationWrapper.StringDeserializer) { } + public MetadataTests_String() : base(JsonSerializerWrapperForString.StringSerializer) { } } - public sealed class MetadataTests_Stream : MetadataTests + public sealed class MetadataTests_AsyncStream : MetadataTests { - public MetadataTests_Stream() : base(SerializationWrapper.StreamSerializer, DeserializationWrapper.StreamDeserializer) { } + public MetadataTests_AsyncStream() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } + } + + public sealed class MetadataTests_SyncStream : MetadataTests + { + public MetadataTests_SyncStream() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } } public sealed class MetadataTests_LowLevel : MetadataTests { - public MetadataTests_LowLevel() : base(SerializationWrapper.WriterSerializer, DeserializationWrapper.ReaderDeserializer) { } + public MetadataTests_LowLevel() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } } public abstract partial class MetadataTests { - protected SerializationWrapper Serializer { get; } + protected JsonSerializerWrapperForString Serializer { get; } - protected DeserializationWrapper Deserializer { get; } - - public MetadataTests(SerializationWrapper serializer, DeserializationWrapper deserializer) + public MetadataTests(JsonSerializerWrapperForString serializer) { Serializer = serializer; - Deserializer = deserializer; } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs index fb6dca863e0..ada72d48ac6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs @@ -1629,21 +1629,26 @@ namespace System.Text.Json.Serialization.Tests } } - public class NumberHandlingTests_StreamOverload : NumberHandlingTests_OverloadSpecific + public class NumberHandlingTests_AsyncStreamOverload : NumberHandlingTests_OverloadSpecific { - public NumberHandlingTests_StreamOverload() : base(DeserializationWrapper.StreamDeserializer) { } + public NumberHandlingTests_AsyncStreamOverload() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } + } + + public class NumberHandlingTests_SyncStreamOverload : NumberHandlingTests_OverloadSpecific + { + public NumberHandlingTests_SyncStreamOverload() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } } public class NumberHandlingTests_SyncOverload : NumberHandlingTests_OverloadSpecific { - public NumberHandlingTests_SyncOverload() : base(DeserializationWrapper.StringDeserializer) { } + public NumberHandlingTests_SyncOverload() : base(JsonSerializerWrapperForString.StringSerializer) { } } public abstract class NumberHandlingTests_OverloadSpecific { - private DeserializationWrapper Deserializer { get; } + private JsonSerializerWrapperForString Deserializer { get; } - public NumberHandlingTests_OverloadSpecific(DeserializationWrapper deserializer) + public NumberHandlingTests_OverloadSpecific(JsonSerializerWrapperForString deserializer) { Deserializer = deserializer; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs index 00cb5ad1946..dfd167d4407 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs @@ -11,34 +11,39 @@ namespace System.Text.Json.Serialization.Tests { public class PolymorphicTests_Span : PolymorphicTests { - public PolymorphicTests_Span() : base(SerializationWrapper.SpanSerializer) { } + public PolymorphicTests_Span() : base(JsonSerializerWrapperForString.SpanSerializer) { } } public class PolymorphicTests_String : PolymorphicTests { - public PolymorphicTests_String() : base(SerializationWrapper.StringSerializer) { } + public PolymorphicTests_String() : base(JsonSerializerWrapperForString.StringSerializer) { } } - public class PolymorphicTests_Stream : PolymorphicTests + public class PolymorphicTests_AsyncStream : PolymorphicTests { - public PolymorphicTests_Stream() : base(SerializationWrapper.StreamSerializer) { } + public PolymorphicTests_AsyncStream() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } } - public class PolymorphicTests_StreamWithSmallBuffer : PolymorphicTests + public class PolymorphicTests_AsyncStreamWithSmallBuffer : PolymorphicTests { - public PolymorphicTests_StreamWithSmallBuffer() : base(SerializationWrapper.StreamSerializerWithSmallBuffer) { } + public PolymorphicTests_AsyncStreamWithSmallBuffer() : base(JsonSerializerWrapperForString.AsyncStreamSerializerWithSmallBuffer) { } + } + + public class PolymorphicTests_SyncStream : PolymorphicTests + { + public PolymorphicTests_SyncStream() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } } public class PolymorphicTests_Writer : PolymorphicTests { - public PolymorphicTests_Writer() : base(SerializationWrapper.WriterSerializer) { } + public PolymorphicTests_Writer() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } } public abstract class PolymorphicTests { - private SerializationWrapper Serializer { get; } + private JsonSerializerWrapperForString Serializer { get; } - public PolymorphicTests(SerializationWrapper serializer) + public PolymorphicTests(JsonSerializerWrapperForString serializer) { Serializer = serializer; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SerializationWrapper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SerializationWrapper.cs deleted file mode 100644 index 02c5952b4ea..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/SerializationWrapper.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; - -namespace System.Text.Json.Serialization.Tests -{ - /// - /// Base class for wrapping serialization calls which allows tests to run under different configurations. - /// - public abstract class SerializationWrapper - { - private static readonly JsonSerializerOptions _optionsWithSmallBuffer = new JsonSerializerOptions { DefaultBufferSize = 1 }; - - public static SerializationWrapper SpanSerializer => new SpanSerializerWrapper(); - public static SerializationWrapper StringSerializer => new StringSerializerWrapper(); - public static SerializationWrapper StreamSerializer => new StreamSerializerWrapper(); - public static SerializationWrapper StreamSerializerWithSmallBuffer => new StreamSerializerWrapperWithSmallBuffer(); - public static SerializationWrapper WriterSerializer => new WriterSerializerWrapper(); - - protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null); - - protected internal abstract Task SerializeWrapper(T value, JsonSerializerOptions options = null); - - protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context); - - protected internal abstract Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo); - - - private class SpanSerializerWrapper : SerializationWrapper - { - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - { - byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, inputType, options); - return Task.FromResult(Encoding.UTF8.GetString(result)); - } - - protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) - { - byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, options); - return Task.FromResult(Encoding.UTF8.GetString(result)); - } - - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) - { - byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, inputType, context); - return Task.FromResult(Encoding.UTF8.GetString(result)); - } - - protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) - { - byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, jsonTypeInfo); - return Task.FromResult(Encoding.UTF8.GetString(result)); - } - } - - private class StringSerializerWrapper : SerializationWrapper - { - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Serialize(value, inputType, options)); - } - - protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) - { - return Task.FromResult(JsonSerializer.Serialize(value, options)); - } - - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) - { - return Task.FromResult(JsonSerializer.Serialize(value, inputType, context)); - } - - protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) - { - return Task.FromResult(JsonSerializer.Serialize(value, jsonTypeInfo)); - } - } - - private class StreamSerializerWrapper : SerializationWrapper - { - protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, value, inputType, options); - return Encoding.UTF8.GetString(stream.ToArray()); - } - - protected internal override async Task SerializeWrapper(T value, JsonSerializerOptions options = null) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, value, options); - return Encoding.UTF8.GetString(stream.ToArray()); - } - - protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, value, inputType, context); - return Encoding.UTF8.GetString(stream.ToArray()); - } - - protected internal override async Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) - { - using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - private class StreamSerializerWrapperWithSmallBuffer : StreamSerializerWrapper - { - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - { - if (options == null) - { - options = _optionsWithSmallBuffer; - } - - return base.SerializeWrapper(value, inputType, options); - } - - protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) - { - return base.SerializeWrapper(value, options); - } - } - - private class WriterSerializerWrapper : SerializationWrapper - { - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - { - using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, inputType, options); - return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); - } - - protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) - { - using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, options); - return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); - } - - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) - { - using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, inputType, context); - return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); - } - - protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) - { - using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, jsonTypeInfo); - return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs index 3efdfde6350..91d8f7a667b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs @@ -14,19 +14,19 @@ using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class StreamTests + public partial class StreamTests { [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/35927", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] [ActiveIssue("https://github.com/dotnet/runtime/issues/35927", TestPlatforms.Browser)] - public static async Task HandleCollectionsAsync() + public async Task HandleCollectionsAsync() { await RunTestAsync(); await RunTestAsync(); await RunTestAsync(); } - private static async Task RunTestAsync() + private async Task RunTestAsync() { foreach ((Type, int) pair in CollectionTestData()) { @@ -56,12 +56,12 @@ namespace System.Text.Json.Serialization.Tests } } - private static async Task PerformSerialization(object obj, Type type, JsonSerializerOptions options) + private async Task PerformSerialization(object obj, Type type, JsonSerializerOptions options) { string expectedjson = JsonSerializer.Serialize(obj, options); using var memoryStream = new MemoryStream(); - await JsonSerializer.SerializeAsync(memoryStream, obj, options); + await Serializer.SerializeWrapper(memoryStream, obj, options); string serialized = Encoding.UTF8.GetString(memoryStream.ToArray()); JsonTestHelper.AssertJsonEqual(expectedjson, serialized); @@ -78,7 +78,7 @@ namespace System.Text.Json.Serialization.Tests } } - private static async Task TestDeserialization( + private async Task TestDeserialization( Stream memoryStream, string expectedJson, Type type, @@ -86,7 +86,7 @@ namespace System.Text.Json.Serialization.Tests { try { - object deserialized = await JsonSerializer.DeserializeAsync(memoryStream, type, options); + object deserialized = await Serializer.DeserializeWrapper(memoryStream, type, options); string serialized = JsonSerializer.Serialize(deserialized, options); // Stack elements reversed during serialization. @@ -361,7 +361,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData("}")] [InlineData("[")] [InlineData("]")] - public static void DeserializeDictionaryStartsWithInvalidJson(string json) + public void DeserializeDictionaryStartsWithInvalidJson(string json) { foreach (Type type in CollectionTestTypes.DictionaryTypes()) { @@ -369,14 +369,14 @@ namespace System.Text.Json.Serialization.Tests { using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json))) { - await JsonSerializer.DeserializeAsync(memoryStream, type); + await Serializer.DeserializeWrapper(memoryStream, type); } }); } } [Fact] - public static void SerializeEmptyCollection() + public void SerializeEmptyCollection() { foreach (Type type in CollectionTestTypes.EnumerableTypes()) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs index 1881fc82aab..5112265009e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs @@ -8,19 +8,19 @@ using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class StreamTests + public partial class StreamTests { [Fact] - public static async Task ReadNullArgumentFail() + public async Task ReadNullArgumentFail() { - await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync((Stream)null)); - await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync((Stream)null, (Type)null)); - await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync((Stream)null, typeof(string))); - await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync(new MemoryStream(), (Type)null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper((Stream)null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper((Stream)null, (Type)null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper((Stream)null, typeof(string))); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(new MemoryStream(), (Type)null)); } [Fact] - public static async Task ReadSimpleObjectAsync() + public async Task ReadSimpleObjectAsync() { using (MemoryStream stream = new MemoryStream(SimpleTestClass.s_data)) { @@ -29,13 +29,13 @@ namespace System.Text.Json.Serialization.Tests DefaultBufferSize = 1 }; - SimpleTestClass obj = await JsonSerializer.DeserializeAsync(stream, options); + SimpleTestClass obj = await Serializer.DeserializeWrapper(stream, options); obj.Verify(); } } [Fact] - public static async Task ReadSimpleObjectWithTrailingTriviaAsync() + public async Task ReadSimpleObjectWithTrailingTriviaAsync() { byte[] data = Encoding.UTF8.GetBytes(SimpleTestClass.s_json + " /* Multi\r\nLine Comment */\t"); using (MemoryStream stream = new MemoryStream(data)) @@ -46,13 +46,13 @@ namespace System.Text.Json.Serialization.Tests ReadCommentHandling = JsonCommentHandling.Skip, }; - SimpleTestClass obj = await JsonSerializer.DeserializeAsync(stream, options); + SimpleTestClass obj = await Serializer.DeserializeWrapper(stream, options); obj.Verify(); } } [Fact] - public static async Task ReadPrimitivesAsync() + public async Task ReadPrimitivesAsync() { using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(@"1"))) { @@ -61,13 +61,13 @@ namespace System.Text.Json.Serialization.Tests DefaultBufferSize = 1 }; - int i = await JsonSerializer.DeserializeAsync(stream, options); + int i = await Serializer.DeserializeWrapper(stream, options); Assert.Equal(1, i); } } [Fact] - public static async Task ReadPrimitivesWithTrailingTriviaAsync() + public async Task ReadPrimitivesWithTrailingTriviaAsync() { using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(" 1\t// Comment\r\n/* Multi\r\nLine */"))) { @@ -77,27 +77,27 @@ namespace System.Text.Json.Serialization.Tests ReadCommentHandling = JsonCommentHandling.Skip, }; - int i = await JsonSerializer.DeserializeAsync(stream, options); + int i = await Serializer.DeserializeWrapper(stream, options); Assert.Equal(1, i); } } [Fact] - public static async Task ReadReferenceTypeCollectionPassingNullValueAsync() + public async Task ReadReferenceTypeCollectionPassingNullValueAsync() { using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("null"))) { - IList referenceTypeCollection = await JsonSerializer.DeserializeAsync>(stream); + IList referenceTypeCollection = await Serializer.DeserializeWrapper>(stream); Assert.Null(referenceTypeCollection); } } [Fact] - public static async Task ReadValueTypeCollectionPassingNullValueAsync() + public async Task ReadValueTypeCollectionPassingNullValueAsync() { using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("null"))) { - IList valueTypeCollection = await JsonSerializer.DeserializeAsync>(stream); + IList valueTypeCollection = await Serializer.DeserializeWrapper>(stream); Assert.Null(valueTypeCollection); } } @@ -120,24 +120,24 @@ namespace System.Text.Json.Serialization.Tests [Theory] [MemberData(nameof(BOMTestData))] - public static async Task TestBOMWithSingleJsonValue(byte[] utf8BomAndValueArray, JsonSerializerOptions options, ulong expected) + public async Task TestBOMWithSingleJsonValue(byte[] utf8BomAndValueArray, JsonSerializerOptions options, ulong expected) { ulong value; using (Stream stream = new MemoryStream(utf8BomAndValueArray)) { - value = await JsonSerializer.DeserializeAsync(stream, options); + value = await Serializer.DeserializeWrapper(stream, options); } Assert.Equal(expected, value); } [Fact] - public static async Task TestBOMWithNoJsonValue() + public async Task TestBOMWithNoJsonValue() { byte[] utf8BomAndValueArray = new byte[] { 0xEF, 0xBB, 0xBF }; using (Stream stream = new MemoryStream(utf8BomAndValueArray)) { await Assert.ThrowsAsync( - async () => await JsonSerializer.DeserializeAsync(stream)); + async () => await Serializer.DeserializeWrapper(stream)); } } @@ -192,7 +192,7 @@ namespace System.Text.Json.Serialization.Tests [Theory] [MemberData(nameof(BOMWithStreamTestData))] - public static async Task TestBOMWithShortAndLongBuffers(Stream stream, int count, int expectedStreamLength, int bufferSize) + public async Task TestBOMWithShortAndLongBuffers(Stream stream, int count, int expectedStreamLength, int bufferSize) { JsonElement[] value; @@ -202,7 +202,7 @@ namespace System.Text.Json.Serialization.Tests }; stream.Position = 0; - value = await JsonSerializer.DeserializeAsync(stream, options); + value = await Serializer.DeserializeWrapper(stream, options); // Verify each element. for (int i = 0; i < count; i++) @@ -236,7 +236,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(1)] [InlineData(16)] [InlineData(32000)] - public static async Task ReadPrimitiveWithWhitespace(int bufferSize) + public async Task ReadPrimitiveWithWhitespace(int bufferSize) { byte[] data = Encoding.UTF8.GetBytes("42" + new string(' ', 16 * 1024)); @@ -245,7 +245,7 @@ namespace System.Text.Json.Serialization.Tests using (MemoryStream stream = new MemoryStream(data)) { - int i = await JsonSerializer.DeserializeAsync(stream, options); + int i = await Serializer.DeserializeWrapper(stream, options); Assert.Equal(42, i); Assert.Equal(16386, stream.Position); } @@ -255,7 +255,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(1)] [InlineData(16)] [InlineData(32000)] - public static async Task ReadObjectWithWhitespace(int bufferSize) + public async Task ReadObjectWithWhitespace(int bufferSize) { byte[] data = Encoding.UTF8.GetBytes("{}" + new string(' ', 16 * 1024)); @@ -264,7 +264,7 @@ namespace System.Text.Json.Serialization.Tests using (MemoryStream stream = new MemoryStream(data)) { - SimpleTestClass obj = await JsonSerializer.DeserializeAsync(stream, options); + SimpleTestClass obj = await Serializer.DeserializeWrapper(stream, options); Assert.Equal(16386, stream.Position); } } @@ -273,7 +273,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(1)] [InlineData(16)] [InlineData(32000)] - public static async Task ReadPrimitiveWithWhitespaceAndThenInvalid(int bufferSize) + public async Task ReadPrimitiveWithWhitespaceAndThenInvalid(int bufferSize) { byte[] data = Encoding.UTF8.GetBytes("42" + new string(' ', 16 * 1024) + "!"); @@ -282,7 +282,7 @@ namespace System.Text.Json.Serialization.Tests using (MemoryStream stream = new MemoryStream(data)) { - JsonException ex = await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync(stream, options)); + JsonException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(stream, options)); Assert.Equal(16387, stream.Position); // We should get an exception like: '!' is invalid after a single JSON value. @@ -294,7 +294,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(1)] [InlineData(16)] [InlineData(32000)] - public static async Task ReadObjectWithWhitespaceAndThenInvalid(int bufferSize) + public async Task ReadObjectWithWhitespaceAndThenInvalid(int bufferSize) { byte[] data = Encoding.UTF8.GetBytes("{}" + new string(' ', 16 * 1024) + "!"); @@ -303,7 +303,7 @@ namespace System.Text.Json.Serialization.Tests using (MemoryStream stream = new MemoryStream(data)) { - JsonException ex = await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync(stream, options)); + JsonException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(stream, options)); Assert.Equal(16387, stream.Position); // We should get an exception like: '!' is invalid after a single JSON value. diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs index 06481bf70fe..35d96103569 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs @@ -9,13 +9,15 @@ using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class StreamTests + public partial class StreamTests { [Fact] public static async Task WriteNullArgumentFail() { await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync((Stream)null, 1)); await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync((Stream)null, 1, typeof(int))); + Assert.Throws(() => JsonSerializer.Serialize((Stream)null)); + Assert.Throws(() => JsonSerializer.Serialize((Stream)null, 1, typeof(int))); } [Fact] @@ -23,6 +25,7 @@ namespace System.Text.Json.Serialization.Tests { MemoryStream stream = new MemoryStream(); await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync(stream, "", (Type)null)); + Assert.Throws(() => JsonSerializer.Serialize(stream, "", (Type)null)); } [Fact] @@ -30,13 +33,15 @@ namespace System.Text.Json.Serialization.Tests { MemoryStream stream = new MemoryStream(); await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync(stream, 1, typeof(string))); + Assert.Throws(() => JsonSerializer.Serialize(stream, 1, typeof(string))); } [Fact] - public static async Task NullObjectValue() + public async Task NullObjectValue() { MemoryStream stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, (object)null); + + await Serializer.SerializeWrapper(stream, (object)null); stream.Seek(0, SeekOrigin.Begin); @@ -50,7 +55,7 @@ namespace System.Text.Json.Serialization.Tests [Fact] [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/45464", RuntimeConfiguration.Checked)] - public static async Task RoundTripAsync() + public async Task RoundTripAsync() { byte[] buffer; @@ -69,7 +74,8 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static async Task RoundTripLargeJsonViaJsonElementAsync() + + public async Task RoundTripLargeJsonViaJsonElementAsync() { // Generating tailored json int i = 0; @@ -88,18 +94,20 @@ namespace System.Text.Json.Serialization.Tests JsonElement root = JsonSerializer.Deserialize(json.ToString()); var ms = new MemoryStream(); - await JsonSerializer.SerializeAsync(ms, root, root.GetType()); + + await Serializer.SerializeWrapper(ms, root, root.GetType()); } [Fact] - public static async Task RoundTripLargeJsonViaPocoAsync() + public async Task RoundTripLargeJsonViaPocoAsync() { byte[] array = JsonSerializer.Deserialize(JsonSerializer.Serialize(new byte[11056])); var ms = new MemoryStream(); - await JsonSerializer.SerializeAsync(ms, array, array.GetType()); + + await Serializer.SerializeWrapper(ms, array, array.GetType()); } - private static async Task WriteAsync(TestStream stream) + private async Task WriteAsync(TestStream stream) { JsonSerializerOptions options = new JsonSerializerOptions { @@ -112,7 +120,7 @@ namespace System.Text.Json.Serialization.Tests obj.Initialize(); obj.Verify(); - await JsonSerializer.SerializeAsync(stream, obj, options: options); + await Serializer.SerializeWrapper(stream, obj, options: options); } // Must be changed if the test classes change: @@ -125,7 +133,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(0, stream.TestFlushCount); } - private static async Task ReadAsync(TestStream stream) + private async Task ReadAsync(TestStream stream) { JsonSerializerOptions options = new JsonSerializerOptions { @@ -133,7 +141,7 @@ namespace System.Text.Json.Serialization.Tests DefaultBufferSize = 1 }; - LargeDataTestClass obj = await JsonSerializer.DeserializeAsync(stream, options); + LargeDataTestClass obj = await Serializer.DeserializeWrapper(stream, options); // Must be changed if the test classes change; may be > since last read may not have filled buffer. Assert.InRange(stream.TestRequestedReadBytesCount, 551368, int.MaxValue); @@ -147,16 +155,17 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static async Task WritePrimitivesAsync() + public async Task WritePrimitivesAsync() { - MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(@"1")); + MemoryStream stream = new MemoryStream(); JsonSerializerOptions options = new JsonSerializerOptions { DefaultBufferSize = 1 }; - int i = await JsonSerializer.DeserializeAsync(stream, options); - Assert.Equal(1, i); + await Serializer.SerializeWrapper(stream, 1, options); + string jsonSerialized = Encoding.UTF8.GetString(stream.ToArray()); + Assert.Equal("1", jsonSerialized); } private class Session @@ -199,7 +208,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(16000)] [InlineData(32000)] [InlineData(64000)] - public static async Task LargeJsonFile(int bufferSize) + public async Task LargeJsonFile(int bufferSize) { const int SessionResponseCount = 100; @@ -259,12 +268,12 @@ namespace System.Text.Json.Serialization.Tests // Async case. using (var memoryStream = new MemoryStream()) { - await JsonSerializer.SerializeAsync(memoryStream, list, options); + await Serializer.SerializeWrapper(memoryStream, list, options); string jsonSerialized = Encoding.UTF8.GetString(memoryStream.ToArray()); Assert.Equal(json, jsonSerialized); memoryStream.Position = 0; - List deserializedList = await JsonSerializer.DeserializeAsync>(memoryStream, options); + List deserializedList = await Serializer.DeserializeWrapper>(memoryStream, options); Assert.Equal(SessionResponseCount, deserializedList.Count); } } @@ -278,7 +287,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(10, false, false)] [InlineData(100, false, false)] [InlineData(1000, false, false)] - public static async Task VeryLargeJsonFileTest(int payloadSize, bool ignoreNull, bool writeIndented) + public async Task VeryLargeJsonFileTest(int payloadSize, bool ignoreNull, bool writeIndented) { List list = PopulateLargeObject(payloadSize); @@ -302,12 +311,12 @@ namespace System.Text.Json.Serialization.Tests // Async case. using (var memoryStream = new MemoryStream()) { - await JsonSerializer.SerializeAsync(memoryStream, list, options); + await Serializer.SerializeWrapper(memoryStream, list, options); string jsonSerialized = Encoding.UTF8.GetString(memoryStream.ToArray()); Assert.Equal(json, jsonSerialized); memoryStream.Position = 0; - List deserializedList = await JsonSerializer.DeserializeAsync>(memoryStream, options); + List deserializedList = await Serializer.DeserializeWrapper>(memoryStream, options); Assert.Equal(payloadSize, deserializedList.Count); } } @@ -322,7 +331,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(4, false, false)] [InlineData(8, false, false)] [InlineData(16, false, false)] // This results a reader\writer depth of 324 which currently works on all test platforms. - public static async Task DeepNestedJsonFileTest(int depthFactor, bool ignoreNull, bool writeIndented) + public async Task DeepNestedJsonFileTest(int depthFactor, bool ignoreNull, bool writeIndented) { const int ListLength = 10; @@ -354,19 +363,19 @@ namespace System.Text.Json.Serialization.Tests // Async case. using (var memoryStream = new MemoryStream()) { - await JsonSerializer.SerializeAsync(memoryStream, orders[0], options); + await Serializer.SerializeWrapper(memoryStream, orders[0], options); string jsonSerialized = Encoding.UTF8.GetString(memoryStream.ToArray()); Assert.Equal(json, jsonSerialized); memoryStream.Position = 0; - List deserializedList = await JsonSerializer.DeserializeAsync>(memoryStream, options); + List deserializedList = await Serializer.DeserializeWrapper>(memoryStream, options); } } [Theory] [InlineData(1)] [InlineData(4)] - public static async Task NestedJsonFileCircularDependencyTest(int depthFactor) + public async Task NestedJsonFileCircularDependencyTest(int depthFactor) { const int ListLength = 2; @@ -394,7 +403,7 @@ namespace System.Text.Json.Serialization.Tests using (var memoryStream = new MemoryStream()) { - await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync(memoryStream, orders[0], options)); + await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(memoryStream, orders[0], options)); } } @@ -405,7 +414,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(8192)] [InlineData(16384)] [InlineData(65536)] - public static async Task FlushThresholdTest(int bufferSize) + public async Task FlushThresholdTest(int bufferSize) { // bufferSize * 0.9 is the threshold size from codebase, subtract 2 for [" characters, then create a // string containing (threshold - 2) amount of char 'a' which when written into output buffer produces buffer @@ -425,7 +434,7 @@ namespace System.Text.Json.Serialization.Tests using (var memoryStream = new MemoryStream()) { - await JsonSerializer.SerializeAsync(memoryStream, list, options); + await Serializer.SerializeWrapper(memoryStream, list, options); string jsonSerialized = Encoding.UTF8.GetString(memoryStream.ToArray()); Assert.Equal(json, jsonSerialized); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs new file mode 100644 index 00000000000..fc85e180dfe --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Tests +{ + public sealed class StreamTests_Async : StreamTests + { + public StreamTests_Async() : base(JsonSerializationWrapperForStream.AsyncStreamSerializer) { } + } + + public sealed class StreamTests_Sync : StreamTests + { + public StreamTests_Sync() : base(JsonSerializationWrapperForStream.SyncStreamSerializer) { } + } + + public abstract partial class StreamTests + { + /// + /// Wrapper for JsonSerializer's Serialize() and Deserialize() methods. + /// + private JsonSerializationWrapperForStream Serializer { get; } + + public StreamTests(JsonSerializationWrapperForStream serializer) + { + Serializer = serializer; + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs index 84a305bbc60..ceb0e33fe8e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs @@ -16,10 +16,10 @@ namespace System.Text.Json.Serialization.Tests { ArgumentNullException ex; - ex = Assert.Throws(() => JsonSerializer.Serialize(null, 1)); + ex = Assert.Throws(() => JsonSerializer.Serialize(writer: null, 1)); Assert.Contains("writer", ex.ToString()); - ex = Assert.Throws(() => JsonSerializer.Serialize(null, 1, typeof(int))); + ex = Assert.Throws(() => JsonSerializer.Serialize(writer: null, 1, typeof(int))); Assert.Contains("writer", ex.ToString()); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index ee45bdc5e97..ab9c1fbd7a8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -109,7 +109,6 @@ - @@ -119,6 +118,8 @@ + + @@ -152,8 +153,8 @@ - + From 1d84e014ac2b6d11b1ddc452ce1a7706395abcb6 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Thu, 24 Jun 2021 13:17:39 -0700 Subject: [PATCH 102/926] Add CustomAttributes ApplyUpdate Capability (#54619) * Add CustomAttributes ApplyUpdate Capability * Rename CustomAttributes ChangeCustomAttributes per #54284 --- .../src/System/Reflection/Metadata/AssemblyExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index 15ee7bd82bf..97b908ab259 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -84,7 +84,7 @@ namespace System.Reflection.Metadata internal static string GetApplyUpdateCapabilities() { - return "Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition"; + return "Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes"; } } } From c88da2905317c0b199c5814594f1ea5d079e0760 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 24 Jun 2021 13:45:52 -0700 Subject: [PATCH 103/926] Add ActivitySource support to DiagnosticsHandler (#54437) * Add ActivitySource support to DiagnosticsHandler * Use ActivitySource as another enabled condition * Make IsGloballyEnabled a property * Simplify enabled check * Revert using the exception filter * Update HTTP ILLink.Substitutions.xml --- .../src/ILLink/ILLink.Substitutions.xml | 2 +- .../src/System.Net.Http.csproj | 3 +- .../src/System/Net/Http/DiagnosticsHandler.cs | 284 ++++++++---------- .../Http/DiagnosticsHandlerLoggingStrings.cs | 25 -- .../src/System/Net/Http/HttpClientHandler.cs | 26 +- .../tests/FunctionalTests/DiagnosticsTests.cs | 158 +++++++--- 6 files changed, 242 insertions(+), 256 deletions(-) delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml index 314469c96d7..d6aea3dbf37 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml @@ -1,7 +1,7 @@ - + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 0ce5ec1aa17..a6034a21e0f 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -1,4 +1,4 @@ - + win true @@ -494,7 +494,6 @@ - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index 4e4d730c4c1..14c1464e77a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -15,103 +15,121 @@ namespace System.Net.Http /// internal sealed class DiagnosticsHandler : DelegatingHandler { - /// - /// DiagnosticHandler constructor - /// - /// Inner handler: Windows or Unix implementation of HttpMessageHandler. - /// Note that DiagnosticHandler is the latest in the pipeline + private const string Namespace = "System.Net.Http"; + private const string RequestWriteNameDeprecated = Namespace + ".Request"; + private const string ResponseWriteNameDeprecated = Namespace + ".Response"; + private const string ExceptionEventName = Namespace + ".Exception"; + private const string ActivityName = Namespace + ".HttpRequestOut"; + private const string ActivityStartName = ActivityName + ".Start"; + private const string ActivityStopName = ActivityName + ".Stop"; + + private static readonly DiagnosticListener s_diagnosticListener = new("HttpHandlerDiagnosticListener"); + private static readonly ActivitySource s_activitySource = new(Namespace); + + public static bool IsGloballyEnabled { get; } = GetEnableActivityPropagationValue(); + + private static bool GetEnableActivityPropagationValue() + { + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(Namespace + ".EnableActivityPropagation", out bool enableActivityPropagation)) + { + return enableActivityPropagation; + } + + // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"); + if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) + { + // Suppress Activity propagation. + return false; + } + + // Defaults to enabling Activity propagation. + return true; + } + public DiagnosticsHandler(HttpMessageHandler innerHandler) : base(innerHandler) { + Debug.Assert(IsGloballyEnabled); } - internal static bool IsEnabled() + private static bool ShouldLogDiagnostics(HttpRequestMessage request, out Activity? activity) { - // check if there is a parent Activity (and propagation is not suppressed) - // or if someone listens to HttpHandlerDiagnosticListener - return IsGloballyEnabled() && (Activity.Current != null || Settings.s_diagnosticListener.IsEnabled()); - } - - internal static bool IsGloballyEnabled() - { - return Settings.s_activityPropagationEnabled; - } - - // SendAsyncCore returns already completed ValueTask for when async: false is passed. - // Internally, it calls the synchronous Send method of the base class. - protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: false, cancellationToken).AsTask().GetAwaiter().GetResult(); - - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: true, cancellationToken).AsTask(); - - private async ValueTask SendAsyncCore(HttpRequestMessage request, bool async, - CancellationToken cancellationToken) - { - // HttpClientHandler is responsible to call static DiagnosticsHandler.IsEnabled() before forwarding request here. - // It will check if propagation is on (because parent Activity exists or there is a listener) or off (forcibly disabled) - // This code won't be called unless consumer unsubscribes from DiagnosticListener right after the check. - // So some requests happening right after subscription starts might not be instrumented. Similarly, - // when consumer unsubscribes, extra requests might be instrumented - - if (request == null) + if (request is null) { throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } - Activity? activity = null; - DiagnosticListener diagnosticListener = Settings.s_diagnosticListener; + activity = null; - // if there is no listener, but propagation is enabled (with previous IsEnabled() check) - // do not write any events just start/stop Activity and propagate Ids - if (!diagnosticListener.IsEnabled()) + if (s_activitySource.HasListeners()) { - activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); - activity.Start(); - InjectHeaders(activity, request); - - try - { - return async ? - await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : - base.Send(request, cancellationToken); - } - finally - { - activity.Stop(); - } + activity = s_activitySource.CreateActivity(ActivityName, ActivityKind.Client); } - Guid loggingRequestId = Guid.Empty; - - // There is a listener. Check if listener wants to be notified about HttpClient Activities - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityName, request)) + if (activity is null) { - activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); + bool diagnosticListenerEnabled = s_diagnosticListener.IsEnabled(); - // Only send start event to users who subscribed for it, but start activity anyway - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityStartName)) + if (Activity.Current is not null || (diagnosticListenerEnabled && s_diagnosticListener.IsEnabled(ActivityName, request))) { - StartActivity(diagnosticListener, activity, new ActivityStartData(request)); + // If a diagnostics listener is enabled for the Activity, always create one + activity = new Activity(ActivityName); } else { - activity.Start(); + // There is no Activity, but we may still want to use the instrumented SendAsyncCore if diagnostic listeners are interested in other events + return diagnosticListenerEnabled; } } - // try to write System.Net.Http.Request event (deprecated) - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated)) + + activity.Start(); + + if (s_diagnosticListener.IsEnabled(ActivityStartName)) { - long timestamp = Stopwatch.GetTimestamp(); - loggingRequestId = Guid.NewGuid(); - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, - new RequestData(request, loggingRequestId, timestamp)); + Write(ActivityStartName, new ActivityStartData(request)); } - // If we are on at all, we propagate current activity information - Activity? currentActivity = Activity.Current; - if (currentActivity != null) + InjectHeaders(activity, request); + + return true; + } + + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (ShouldLogDiagnostics(request, out Activity? activity)) { - InjectHeaders(currentActivity, request); + ValueTask sendTask = SendAsyncCore(request, activity, async: false, cancellationToken); + return sendTask.IsCompleted ? + sendTask.Result : + sendTask.AsTask().GetAwaiter().GetResult(); + } + else + { + return base.Send(request, cancellationToken); + } + } + + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (ShouldLogDiagnostics(request, out Activity? activity)) + { + return SendAsyncCore(request, activity, async: true, cancellationToken).AsTask(); + } + else + { + return base.SendAsync(request, cancellationToken); + } + } + + private async ValueTask SendAsyncCore(HttpRequestMessage request, Activity? activity, bool async, CancellationToken cancellationToken) + { + Guid loggingRequestId = default; + + if (s_diagnosticListener.IsEnabled(RequestWriteNameDeprecated)) + { + loggingRequestId = Guid.NewGuid(); + Write(RequestWriteNameDeprecated, new RequestData(request, loggingRequestId, Stopwatch.GetTimestamp())); } HttpResponseMessage? response = null; @@ -126,52 +144,39 @@ namespace System.Net.Http catch (OperationCanceledException) { taskStatus = TaskStatus.Canceled; - - // we'll report task status in HttpRequestOut.Stop throw; } catch (Exception ex) { - taskStatus = TaskStatus.Faulted; - - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ExceptionEventName)) + if (s_diagnosticListener.IsEnabled(ExceptionEventName)) { - // If request was initially instrumented, Activity.Current has all necessary context for logging - // Request is passed to provide some context if instrumentation was disabled and to avoid - // extensive Activity.Tags usage to tunnel request properties - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); + Write(ExceptionEventName, new ExceptionData(ex, request)); } + + taskStatus = TaskStatus.Faulted; throw; } finally { - // always stop activity if it was started - if (activity != null) + if (activity is not null) { - StopActivity(diagnosticListener, activity, new ActivityStopData( - response, - // If request is failed or cancelled, there is no response, therefore no information about request; - // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests - // and not retain all requests in Start - request, - taskStatus)); + activity.SetEndTime(DateTime.UtcNow); + + if (s_diagnosticListener.IsEnabled(ActivityStopName)) + { + Write(ActivityStopName, new ActivityStopData(response, request, taskStatus)); + } + + activity.Stop(); } - // Try to write System.Net.Http.Response event (deprecated) - if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated)) + + if (s_diagnosticListener.IsEnabled(ResponseWriteNameDeprecated)) { - long timestamp = Stopwatch.GetTimestamp(); - Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, - new ResponseData( - response, - loggingRequestId, - timestamp, - taskStatus)); + Write(ResponseWriteNameDeprecated, new ResponseData(response, loggingRequestId, Stopwatch.GetTimestamp(), taskStatus)); } } } - #region private - private sealed class ActivityStartData { // matches the properties selected in https://github.com/dotnet/diagnostics/blob/ffd0254da3bcc47847b1183fa5453c0877020abd/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/HttpRequestSourceConfiguration.cs#L36-L40 @@ -269,55 +274,29 @@ namespace System.Net.Http public override string ToString() => $"{{ {nameof(Response)} = {Response}, {nameof(LoggingRequestId)} = {LoggingRequestId}, {nameof(Timestamp)} = {Timestamp}, {nameof(RequestTaskStatus)} = {RequestTaskStatus} }}"; } - private static class Settings - { - private const string EnableActivityPropagationEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"; - private const string EnableActivityPropagationAppCtxSettingName = "System.Net.Http.EnableActivityPropagation"; - - public static readonly bool s_activityPropagationEnabled = GetEnableActivityPropagationValue(); - - private static bool GetEnableActivityPropagationValue() - { - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(EnableActivityPropagationAppCtxSettingName, out bool enableActivityPropagation)) - { - return enableActivityPropagation; - } - - // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. - string? envVar = Environment.GetEnvironmentVariable(EnableActivityPropagationEnvironmentVariableSettingName); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Suppress Activity propagation. - return false; - } - - // Defaults to enabling Activity propagation. - return true; - } - - public static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); - } - private static void InjectHeaders(Activity currentActivity, HttpRequestMessage request) { + const string TraceParentHeaderName = "traceparent"; + const string TraceStateHeaderName = "tracestate"; + const string RequestIdHeaderName = "Request-Id"; + const string CorrelationContextHeaderName = "Correlation-Context"; + if (currentActivity.IdFormat == ActivityIdFormat.W3C) { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName)) + if (!request.Headers.Contains(TraceParentHeaderName)) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(TraceParentHeaderName, currentActivity.Id); if (currentActivity.TraceStateString != null) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceStateHeaderName, currentActivity.TraceStateString); + request.Headers.TryAddWithoutValidation(TraceStateHeaderName, currentActivity.TraceStateString); } } } else { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName)) + if (!request.Headers.Contains(RequestIdHeaderName)) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(RequestIdHeaderName, currentActivity.Id); } } @@ -333,41 +312,16 @@ namespace System.Net.Http baggage.Add(new NameValueHeaderValue(WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)).ToString()); } while (e.MoveNext()); - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.CorrelationContextHeaderName, baggage); + request.Headers.TryAddWithoutValidation(CorrelationContextHeaderName, baggage); } } } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] - private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - string name, - T value) + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string name, T value) { - diagnosticSource.Write(name, value); + s_diagnosticListener.Write(name, value); } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", - Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency.")] - private static Activity StartActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - Activity activity, - T? args) - { - return diagnosticSource.StartActivity(activity, args); - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", - Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency.")] - private static void StopActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( - DiagnosticSource diagnosticSource, - Activity activity, - T? args) - { - diagnosticSource.StopActivity(activity, args); - } - - #endregion } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs deleted file mode 100644 index 0fa57394c1c..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Http -{ - /// - /// Defines names of DiagnosticListener and Write events for DiagnosticHandler - /// - internal static class DiagnosticsHandlerLoggingStrings - { - public const string DiagnosticListenerName = "HttpHandlerDiagnosticListener"; - public const string RequestWriteNameDeprecated = "System.Net.Http.Request"; - public const string ResponseWriteNameDeprecated = "System.Net.Http.Response"; - - public const string ExceptionEventName = "System.Net.Http.Exception"; - public const string ActivityName = "System.Net.Http.HttpRequestOut"; - public const string ActivityStartName = "System.Net.Http.HttpRequestOut.Start"; - - public const string RequestIdHeaderName = "Request-Id"; - public const string CorrelationContextHeaderName = "Correlation-Context"; - - public const string TraceParentHeaderName = "traceparent"; - public const string TraceStateHeaderName = "tracestate"; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 2e3289643cf..bfb8f6cae78 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -20,17 +20,17 @@ namespace System.Net.Http public partial class HttpClientHandler : HttpMessageHandler { private readonly HttpHandlerType _underlyingHandler; - private readonly DiagnosticsHandler? _diagnosticsHandler; + private readonly HttpMessageHandler _handler; private ClientCertificateOption _clientCertificateOptions; private volatile bool _disposed; public HttpClientHandler() { - _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled()) + _handler = _underlyingHandler = new HttpHandlerType(); + if (DiagnosticsHandler.IsGloballyEnabled) { - _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); + _handler = new DiagnosticsHandler(_handler); } ClientCertificateOptions = ClientCertificateOption.Manual; } @@ -288,21 +288,11 @@ namespace System.Net.Http public IDictionary Properties => _underlyingHandler.Properties; [UnsupportedOSPlatform("browser")] - protected internal override HttpResponseMessage Send(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.Send(request, cancellationToken) : - _underlyingHandler.Send(request, cancellationToken); - } + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => + _handler.Send(request, cancellationToken); - protected internal override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.SendAsync(request, cancellationToken) : - _underlyingHandler.SendAsync(request, cancellationToken); - } + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + _handler.SendAsync(request, cancellationToken); // lazy-load the validator func so it can be trimmed by the ILLinker if it isn't used. private static Func? s_dangerousAcceptAnyServerCertificateValidator; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 1fb6fd925fd..66b60e800b8 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Tracing; +using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Net.Test.Common; @@ -256,7 +257,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus status = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Canceled, status); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -344,7 +345,7 @@ namespace System.Net.Http.Functional.Tests activityStopResponseLogged = GetProperty(kvp.Value, "Response"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -409,7 +410,7 @@ namespace System.Net.Http.Functional.Tests Assert.Contains("goodkey=bad%2Fvalue", correlationContext); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -467,7 +468,7 @@ namespace System.Net.Http.Functional.Tests Assert.False(request.Headers.TryGetValues("traceparent", out var _)); Assert.False(request.Headers.TryGetValues("tracestate", out var _)); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -519,7 +520,7 @@ namespace System.Net.Http.Functional.Tests } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Stop")) { - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -608,7 +609,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -647,7 +648,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -796,44 +797,6 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListener() - { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - Activity parent = new Activity("parent").Start(); - - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertHeadersAreInjected(requestData, parent); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); - } - - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListenerOrParentActivity() - { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertNoHeadersAreInjected(requestData); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); - } - [ConditionalTheory(nameof(EnableActivityPropagationEnvironmentVariableIsNotSetAndRemoteExecutorSupported))] [InlineData("true")] [InlineData("1")] @@ -899,6 +862,111 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).Dispose(); } + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(true, true, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, false, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, null, true)] // Activity was set and ActivitySource created an Activity + [InlineData(true, true, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, false, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, null, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(true, true, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(true, false, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(true, null, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created + [InlineData(false, true, true)] // DiagnosticListener requested an Activity and ActivitySource created an Activity + [InlineData(false, true, false)] // DiagnosticListener requested an Activity, ActivitySource chose not to create an Activity, so one was manually created + [InlineData(false, true, null)] // DiagnosticListener requested an Activity, ActivitySource had no listeners, so an Activity was manually created + [InlineData(false, false, true)] // No Activity is set, DiagnosticListener does not want one, but ActivitySource created an Activity + [InlineData(false, false, false)] // No Activity is set, DiagnosticListener does not want one and ActivitySource chose not to create an Activity + [InlineData(false, false, null)] // No Activity is set, DiagnosticListener does not want one and ActivitySource has no listeners + [InlineData(false, null, true)] // No Activity is set, there is no DiagnosticListener, but ActivitySource created an Activity + [InlineData(false, null, false)] // No Activity is set, there is no DiagnosticListener and ActivitySource chose not to create an Activity + [InlineData(false, null, null)] // No Activity is set, there is no DiagnosticListener and ActivitySource has no listeners + public void SendAsync_ActivityIsCreatedIfRequested(bool currentActivitySet, bool? diagnosticListenerActivityEnabled, bool? activitySourceCreatesActivity) + { + string parameters = $"{currentActivitySet},{diagnosticListenerActivityEnabled},{activitySourceCreatesActivity}"; + + RemoteExecutor.Invoke(async (useVersion, testAsync, parametersString) => + { + bool?[] parameters = parametersString.Split(',').Select(p => p.Length == 0 ? (bool?)null : bool.Parse(p)).ToArray(); + bool currentActivitySet = parameters[0].Value; + bool? diagnosticListenerActivityEnabled = parameters[1]; + bool? activitySourceCreatesActivity = parameters[2]; + + bool madeASamplingDecision = false; + if (activitySourceCreatesActivity.HasValue) + { + ActivitySource.AddActivityListener(new ActivityListener + { + ShouldListenTo = _ => true, + Sample = (ref ActivityCreationOptions _) => + { + madeASamplingDecision = true; + return activitySourceCreatesActivity.Value ? ActivitySamplingResult.AllData : ActivitySamplingResult.None; + } + }); + } + + bool listenerCallbackWasCalled = false; + IDisposable listenerSubscription = new MemoryStream(); // Dummy disposable + if (diagnosticListenerActivityEnabled.HasValue) + { + var diagnosticListenerObserver = new FakeDiagnosticListenerObserver(_ => listenerCallbackWasCalled = true); + + diagnosticListenerObserver.Enable(name => !name.Contains("HttpRequestOut") || diagnosticListenerActivityEnabled.Value); + + listenerSubscription = DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver); + } + + Activity activity = currentActivitySet ? new Activity("parent").Start() : null; + + if (!currentActivitySet) + { + // Listen to new activity creations if an Activity was created without a parent + // (when a DiagnosticListener forced one to be created) + ActivitySource.AddActivityListener(new ActivityListener + { + ShouldListenTo = _ => true, + ActivityStarted = created => + { + Assert.Null(activity); + activity = created; + } + }); + } + + using (listenerSubscription) + { + await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(useVersion, testAsync, uri); + }, + async server => + { + HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); + + if (currentActivitySet || diagnosticListenerActivityEnabled == true || activitySourceCreatesActivity == true) + { + Assert.NotNull(activity); + AssertHeadersAreInjected(requestData, activity); + } + else + { + AssertNoHeadersAreInjected(requestData); + + if (!currentActivitySet) + { + Assert.Null(activity); + } + } + }); + } + + Assert.Equal(activitySourceCreatesActivity.HasValue, madeASamplingDecision); + Assert.Equal(diagnosticListenerActivityEnabled.HasValue, listenerCallbackWasCalled); + }, UseVersion.ToString(), TestAsync.ToString(), parameters).Dispose(); + } + private static T GetProperty(object obj, string propertyName) { Type t = obj.GetType(); From db9cfd2d35c0672cc841c63f431831af5f63da7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 24 Jun 2021 16:56:12 -0400 Subject: [PATCH 104/926] Pass the default ALC gchandle to the v3 preload hook (#54686) * Pass the default ALC gchandle to the v3 preload hook Fixes crashes early during startup when the gchandle is then used by the hooks to call mono API functions. The default ALC exists, but its managed object doesn't, so the gchandle target is null. The mono APIs detect the the special gchandle and resolve it to the default ALC Example crash https://gist.github.com/grendello/b4ab24587a055725cc5e1416b86ad7ca * [alc] Assert that we never see a null target from a managed ALC gchandle --- src/mono/mono/metadata/assembly-load-context.c | 1 + src/mono/mono/metadata/assembly.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/assembly-load-context.c b/src/mono/mono/metadata/assembly-load-context.c index 85c9b3313bf..d3e4df84d2d 100644 --- a/src/mono/mono/metadata/assembly-load-context.c +++ b/src/mono/mono/metadata/assembly-load-context.c @@ -424,6 +424,7 @@ mono_alc_from_gchandle (MonoGCHandle alc_gchandle) HANDLE_FUNCTION_ENTER (); MonoManagedAssemblyLoadContextHandle managed_alc = MONO_HANDLE_CAST (MonoManagedAssemblyLoadContext, mono_gchandle_get_target_handle (alc_gchandle)); + g_assert (!MONO_HANDLE_IS_NULL (managed_alc)); MonoAssemblyLoadContext *alc = MONO_HANDLE_GETVAL (managed_alc, native_assembly_load_context); HANDLE_FUNCTION_RETURN_VAL (alc); } diff --git a/src/mono/mono/metadata/assembly.c b/src/mono/mono/metadata/assembly.c index a40e2c623d3..e98264a2f93 100644 --- a/src/mono/mono/metadata/assembly.c +++ b/src/mono/mono/metadata/assembly.c @@ -1579,9 +1579,21 @@ invoke_assembly_preload_hook (MonoAssemblyLoadContext *alc, MonoAssemblyName *an if (hook->version == 2) assembly = hook->func.v2 (alc, aname, apath, hook->user_data, error); else { // v3 - MonoGCHandle strong_gchandle = mono_gchandle_from_handle (mono_gchandle_get_target_handle (alc->gchandle), TRUE); + /* + * For the default ALC, pass the globally known gchandle (since it's never collectible, it's always a strong handle). + * For other ALCs, make a new strong handle that is passed to the caller. + * Early at startup, when the default ALC exists, but its managed object doesn't, so the default ALC gchandle points to null. + */ + gboolean needs_free = TRUE; + MonoGCHandle strong_gchandle; + if (mono_alc_is_default (alc)) { + needs_free = FALSE; + strong_gchandle = alc->gchandle; + } else + strong_gchandle = mono_gchandle_from_handle (mono_gchandle_get_target_handle (alc->gchandle), TRUE); assembly = hook->func.v3 (strong_gchandle, aname, apath, hook->user_data, error); - mono_gchandle_free_internal (strong_gchandle); + if (needs_free) + mono_gchandle_free_internal (strong_gchandle); } /* TODO: propagage error out to callers */ mono_error_assert_ok (error); From 4aeef0d982096d68318bf4ea2b8d12dd438acf8a Mon Sep 17 00:00:00 2001 From: imhameed Date: Thu, 24 Jun 2021 13:56:27 -0700 Subject: [PATCH 105/926] [mono] Implement Rdm and Dp (#49737) See https://github.com/dotnet/runtime/issues/42322 and https://github.com/dotnet/runtime/issues/42280. Tested manually on an arm64 Linux VM running on an M1 Mac Mini. Does not enable RDM or DP when AOT-compiling the associated runtime tests; our CI hardware doesn't support these extensions yet. --- src/mono/mono/mini/aot-compiler.c | 6 ++- src/mono/mono/mini/llvm-intrinsics.h | 6 +++ src/mono/mono/mini/mini-llvm.c | 68 ++++++++++++++++++++++++++-- src/mono/mono/mini/mini-ops.h | 8 ++++ src/mono/mono/mini/simd-intrinsics.c | 67 ++++++++++++++++++++++++++- src/mono/mono/mini/simd-methods.h | 14 +++++- 6 files changed, 162 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index b36fea0cdf4..3df6d4b8b14 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -8240,7 +8240,7 @@ parse_cpu_features (const gchar *attr) // if we disable a feature from the SSE-AVX tree we also need to disable all dependencies if (!enabled && (feature & MONO_CPU_X86_FULL_SSEAVX_COMBINED)) feature = (MonoCPUFeatures) (MONO_CPU_X86_FULL_SSEAVX_COMBINED & ~feature); - + #elif defined(TARGET_ARM64) // MONO_CPU_ARM64_BASE is unconditionally set in mini_get_cpu_features. if (!strcmp (attr + prefix, "crc")) @@ -8249,6 +8249,10 @@ parse_cpu_features (const gchar *attr) feature = MONO_CPU_ARM64_CRYPTO; else if (!strcmp (attr + prefix, "neon")) feature = MONO_CPU_ARM64_NEON; + else if (!strcmp (attr + prefix, "rdm")) + feature = MONO_CPU_ARM64_RDM; + else if (!strcmp (attr + prefix, "dotprod")) + feature = MONO_CPU_ARM64_DP; #elif defined(TARGET_WASM) if (!strcmp (attr + prefix, "simd")) feature = MONO_CPU_WASM_SIMD; diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index f12bb53d873..042f1074b32 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -25,6 +25,7 @@ #define Widen INTRIN_kind_widen #define WidenAcross INTRIN_kind_widen_across #define Across INTRIN_kind_across +#define Arm64DotProd INTRIN_kind_arm64_dot_prod #if !defined(Generic) #define Generic #endif @@ -466,6 +467,10 @@ INTRINS_OVR_TAG(AARCH64_ADV_SIMD_SRI, aarch64_neon_vsri, Arm64, V64 | V128 | I1 INTRINS_OVR_TAG(AARCH64_ADV_SIMD_TBX1, aarch64_neon_tbx1, Arm64, V64 | V128 | I1) INTRINS_OVR_TAG(AARCH64_ADV_SIMD_TBL1, aarch64_neon_tbl1, Arm64, V64 | V128 | I1) + +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SDOT, aarch64_neon_sdot, Arm64, Arm64DotProd, V64 | V128 | I4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UDOT, aarch64_neon_udot, Arm64, Arm64DotProd, V64 | V128 | I4) + #endif #undef INTRINS @@ -486,6 +491,7 @@ INTRINS_OVR_TAG(AARCH64_ADV_SIMD_TBL1, aarch64_neon_tbl1, Arm64, V64 | V128 | I1 #undef Ftoi #undef WidenAcross #undef Across +#undef Arm64DotProd #undef Generic #undef X86 #undef Arm64 diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index b19cf2fe572..7d563842c16 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -360,6 +360,7 @@ enum { INTRIN_kind_widen, INTRIN_kind_widen_across, INTRIN_kind_across, + INTRIN_kind_arm64_dot_prod, }; static const uint8_t intrin_kind [] = { @@ -9660,6 +9661,21 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) values [ins->dreg] = result; break; } + case OP_ARM64_SELECT_QUAD: { + LLVMTypeRef src_type = simd_class_to_llvm_type (ctx, ins->data.op [1].klass); + LLVMTypeRef ret_type = simd_class_to_llvm_type (ctx, ins->klass); + unsigned int src_type_bits = mono_llvm_get_prim_size_bits (src_type); + unsigned int ret_type_bits = mono_llvm_get_prim_size_bits (ret_type); + unsigned int src_intermediate_elems = src_type_bits / 32; + unsigned int ret_intermediate_elems = ret_type_bits / 32; + LLVMTypeRef intermediate_type = LLVMVectorType (i4_t, src_intermediate_elems); + LLVMValueRef result = LLVMBuildBitCast (builder, lhs, intermediate_type, "arm64_select_quad"); + result = LLVMBuildExtractElement (builder, result, rhs, "arm64_select_quad"); + result = broadcast_element (ctx, result, ret_intermediate_elems); + result = LLVMBuildBitCast (builder, result, ret_type, "arm64_select_quad"); + values [ins->dreg] = result; + break; + } case OP_LSCNT32: case OP_LSCNT64: { // %shr = ashr i32 %x, 31 @@ -9683,6 +9699,43 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, ins->opcode == OP_LSCNT32 ? INTRINS_CTLZ_I32 : INTRINS_CTLZ_I64), args, 2, ""); break; } + case OP_ARM64_SQRDMLAH: + case OP_ARM64_SQRDMLAH_BYSCALAR: + case OP_ARM64_SQRDMLAH_SCALAR: + case OP_ARM64_SQRDMLSH: + case OP_ARM64_SQRDMLSH_BYSCALAR: + case OP_ARM64_SQRDMLSH_SCALAR: { + gboolean byscalar = FALSE; + gboolean scalar = FALSE; + gboolean subtract = FALSE; + switch (ins->opcode) { + case OP_ARM64_SQRDMLAH_BYSCALAR: byscalar = TRUE; break; + case OP_ARM64_SQRDMLAH_SCALAR: scalar = TRUE; break; + case OP_ARM64_SQRDMLSH: subtract = TRUE; break; + case OP_ARM64_SQRDMLSH_BYSCALAR: subtract = TRUE; byscalar = TRUE; break; + case OP_ARM64_SQRDMLSH_SCALAR: subtract = TRUE; scalar = TRUE; break; + } + int acc_iid = subtract ? INTRINS_AARCH64_ADV_SIMD_SQSUB : INTRINS_AARCH64_ADV_SIMD_SQADD; + LLVMTypeRef ret_t = simd_class_to_llvm_type (ctx, ins->klass); + llvm_ovr_tag_t ovr_tag = ovr_tag_from_llvm_type (ret_t); + ScalarOpFromVectorOpCtx sctx = scalar_op_from_vector_op (ctx, ret_t, ins); + LLVMValueRef args [] = { lhs, rhs, arg3 }; + if (byscalar) { + unsigned int elems = LLVMGetVectorSize (ret_t); + args [2] = broadcast_element (ctx, scalar_from_vector (ctx, args [2]), elems); + } + if (scalar) { + ovr_tag = sctx.ovr_tag; + scalar_op_from_vector_op_process_args (&sctx, args, 3); + } + LLVMValueRef result = call_overloaded_intrins (ctx, INTRINS_AARCH64_ADV_SIMD_SQRDMULH, ovr_tag, &args [1], "arm64_sqrdmlxh"); + args [1] = result; + result = call_overloaded_intrins (ctx, acc_iid, ovr_tag, &args [0], "arm64_sqrdmlxh"); + if (scalar) + result = scalar_op_from_vector_op_process_result (&sctx, result); + values [ins->dreg] = result; + break; + } case OP_ARM64_SMULH: case OP_ARM64_UMULH: { LLVMValueRef op1, op2; @@ -12136,6 +12189,13 @@ add_intrinsic (LLVMModuleRef module, int id) int associated_prim = MAX(ew, 2); LLVMTypeRef associated_scalar_type = intrin_types [0][associated_prim]; intrins = add_intrins2 (module, id, associated_scalar_type, distinguishing_type); + } else if (kind == INTRIN_kind_arm64_dot_prod) { + /* + * @llvm.aarch64.neon.sdot.v2i32.v8i8 + * @llvm.aarch64.neon.sdot.v4i32.v16i8 + */ + LLVMTypeRef associated_type = intrin_types [vw][0]; + intrins = add_intrins2 (module, id, distinguishing_type, associated_type); } else intrins = add_intrins1 (module, id, distinguishing_type); int key = key_from_id_and_tag (id, test); @@ -13530,9 +13590,11 @@ MonoCPUFeatures mono_llvm_get_cpu_features (void) { "bmi2", MONO_CPU_X86_BMI2 }, #endif #if defined(TARGET_ARM64) - { "crc", MONO_CPU_ARM64_CRC }, - { "crypto", MONO_CPU_ARM64_CRYPTO }, - { "neon", MONO_CPU_ARM64_NEON } + { "crc", MONO_CPU_ARM64_CRC }, + { "crypto", MONO_CPU_ARM64_CRYPTO }, + { "neon", MONO_CPU_ARM64_NEON }, + { "rdm", MONO_CPU_ARM64_RDM }, + { "dotprod", MONO_CPU_ARM64_DP }, #endif #if defined(TARGET_WASM) { "simd", MONO_CPU_WASM_SIMD }, diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 5fadd703be0..25dd6d8fd6e 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1740,6 +1740,7 @@ MINI_OP(OP_ARM64_UQXTN2, "arm64_uqxtn2", XREG, XREG, XREG) MINI_OP(OP_ARM64_SQXTUN2, "arm64_sqxtun2", XREG, XREG, XREG) MINI_OP(OP_ARM64_SELECT_SCALAR, "arm64_select_scalar", XREG, XREG, IREG) +MINI_OP(OP_ARM64_SELECT_QUAD, "arm64_select_quad", XREG, XREG, IREG) MINI_OP(OP_ARM64_FCVTZU, "arm64_fcvtzu", XREG, XREG, NONE) MINI_OP(OP_ARM64_FCVTZS, "arm64_fcvtzs", XREG, XREG, NONE) @@ -1807,4 +1808,11 @@ MINI_OP(OP_ARM64_XNARROW_SCALAR, "arm64_xnarrow_scalar", XREG, XREG, NONE) MINI_OP3(OP_ARM64_EXT, "arm64_ext", XREG, XREG, XREG, IREG) +MINI_OP3(OP_ARM64_SQRDMLAH, "arm64_sqrdmlah", XREG, XREG, XREG, XREG) +MINI_OP3(OP_ARM64_SQRDMLAH_BYSCALAR, "arm64_sqrdmlah_byscalar", XREG, XREG, XREG, XREG) +MINI_OP3(OP_ARM64_SQRDMLAH_SCALAR, "arm64_sqrdmlah_scalar", XREG, XREG, XREG, XREG) +MINI_OP3(OP_ARM64_SQRDMLSH, "arm64_sqrdmlsh", XREG, XREG, XREG, XREG) +MINI_OP3(OP_ARM64_SQRDMLSH_BYSCALAR, "arm64_sqrdmlsh_byscalar", XREG, XREG, XREG, XREG) +MINI_OP3(OP_ARM64_SQRDMLSH_SCALAR, "arm64_sqrdmlsh_scalar", XREG, XREG, XREG, XREG) + #endif // TARGET_ARM64 diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index b65d8d887ce..9b4fce55bc0 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -1428,13 +1428,31 @@ static SimdIntrinsic advsimd_methods [] = { {SN_get_IsSupported}, }; +static const SimdIntrinsic rdm_methods [] = { + {SN_MultiplyRoundedDoublingAndAddSaturateHigh, OP_ARM64_SQRDMLAH}, + {SN_MultiplyRoundedDoublingAndAddSaturateHighScalar, OP_ARM64_SQRDMLAH_SCALAR}, + {SN_MultiplyRoundedDoublingAndSubtractSaturateHigh, OP_ARM64_SQRDMLSH}, + {SN_MultiplyRoundedDoublingAndSubtractSaturateHighScalar, OP_ARM64_SQRDMLSH_SCALAR}, + {SN_MultiplyRoundedDoublingBySelectedScalarAndAddSaturateHigh}, + {SN_MultiplyRoundedDoublingBySelectedScalarAndSubtractSaturateHigh}, + {SN_MultiplyRoundedDoublingScalarBySelectedScalarAndAddSaturateHigh}, + {SN_MultiplyRoundedDoublingScalarBySelectedScalarAndSubtractSaturateHigh}, + {SN_get_IsSupported}, +}; + +static const SimdIntrinsic dp_methods [] = { + {SN_DotProduct, OP_XOP_OVR_X_X_X_X, INTRINS_AARCH64_ADV_SIMD_SDOT, OP_XOP_OVR_X_X_X_X, INTRINS_AARCH64_ADV_SIMD_UDOT}, + {SN_DotProductBySelectedQuadruplet}, + {SN_get_IsSupported}, +}; + static const IntrinGroup supported_arm_intrinsics [] = { { "AdvSimd", MONO_CPU_ARM64_NEON, advsimd_methods, sizeof (advsimd_methods) }, { "Aes", MONO_CPU_ARM64_CRYPTO, crypto_aes_methods, sizeof (crypto_aes_methods) }, { "ArmBase", MONO_CPU_ARM64_BASE, armbase_methods, sizeof (armbase_methods) }, { "Crc32", MONO_CPU_ARM64_CRC, crc32_methods, sizeof (crc32_methods) }, - { "Dp", MONO_CPU_ARM64_DP, unsupported, sizeof (unsupported) }, - { "Rdm", MONO_CPU_ARM64_RDM, unsupported, sizeof (unsupported) }, + { "Dp", MONO_CPU_ARM64_DP, dp_methods, sizeof (dp_methods) }, + { "Rdm", MONO_CPU_ARM64_RDM, rdm_methods, sizeof (rdm_methods) }, { "Sha1", MONO_CPU_ARM64_CRYPTO, sha1_methods, sizeof (sha1_methods) }, { "Sha256", MONO_CPU_ARM64_CRYPTO, sha256_methods, sizeof (sha256_methods) }, }; @@ -1740,6 +1758,51 @@ emit_arm64_intrinsics ( } } + if (feature == MONO_CPU_ARM64_RDM) { + switch (id) { + case SN_MultiplyRoundedDoublingBySelectedScalarAndAddSaturateHigh: + case SN_MultiplyRoundedDoublingBySelectedScalarAndSubtractSaturateHigh: + case SN_MultiplyRoundedDoublingScalarBySelectedScalarAndAddSaturateHigh: + case SN_MultiplyRoundedDoublingScalarBySelectedScalarAndSubtractSaturateHigh: { + MonoClass *ret_klass = mono_class_from_mono_type_internal (fsig->ret); + int opcode = 0; + switch (id) { + case SN_MultiplyRoundedDoublingBySelectedScalarAndAddSaturateHigh: opcode = OP_ARM64_SQRDMLAH_BYSCALAR; break; + case SN_MultiplyRoundedDoublingBySelectedScalarAndSubtractSaturateHigh: opcode = OP_ARM64_SQRDMLSH_BYSCALAR; break; + case SN_MultiplyRoundedDoublingScalarBySelectedScalarAndAddSaturateHigh: opcode = OP_ARM64_SQRDMLAH_SCALAR; break; + case SN_MultiplyRoundedDoublingScalarBySelectedScalarAndSubtractSaturateHigh: opcode = OP_ARM64_SQRDMLSH_SCALAR; break; + } + MonoInst *scalar = emit_simd_ins (cfg, ret_klass, OP_ARM64_SELECT_SCALAR, args [2]->dreg, args [3]->dreg); + MonoInst *ret = emit_simd_ins (cfg, ret_klass, opcode, args [0]->dreg, args [1]->dreg); + ret->inst_c1 = arg0_type; + ret->sreg3 = scalar->dreg; + return ret; + } + default: + g_assert_not_reached (); + } + } + + if (feature == MONO_CPU_ARM64_DP) { + switch (id) { + case SN_DotProductBySelectedQuadruplet: { + MonoClass *ret_klass = mono_class_from_mono_type_internal (fsig->ret); + MonoClass *arg_klass = mono_class_from_mono_type_internal (fsig->params [1]); + MonoClass *quad_klass = mono_class_from_mono_type_internal (fsig->params [2]); + gboolean is_unsigned = type_is_unsigned (fsig->ret); + int iid = is_unsigned ? INTRINS_AARCH64_ADV_SIMD_UDOT : INTRINS_AARCH64_ADV_SIMD_SDOT; + MonoInst *quad = emit_simd_ins (cfg, arg_klass, OP_ARM64_SELECT_QUAD, args [2]->dreg, args [3]->dreg); + quad->data.op [1].klass = quad_klass; + MonoInst *ret = emit_simd_ins (cfg, ret_klass, OP_XOP_OVR_X_X_X_X, args [0]->dreg, args [1]->dreg); + ret->sreg3 = quad->dreg; + ret->inst_c0 = iid; + return ret; + } + default: + g_assert_not_reached (); + } + } + return NULL; } #endif // TARGET_ARM64 diff --git a/src/mono/mono/mini/simd-methods.h b/src/mono/mono/mini/simd-methods.h index 673543d4389..4abd0ebb7e6 100644 --- a/src/mono/mono/mini/simd-methods.h +++ b/src/mono/mono/mini/simd-methods.h @@ -248,7 +248,7 @@ METHOD(HashUpdate2) METHOD(ScheduleUpdate0) METHOD(ScheduleUpdate1) METHOD(MixColumns) -//AdvSimd +// AdvSimd METHOD(AbsSaturate) METHOD(AbsSaturateScalar) METHOD(AbsScalar) @@ -559,3 +559,15 @@ METHOD(ZeroExtendWideningLower) METHOD(ZeroExtendWideningUpper) METHOD(ZipHigh) METHOD(ZipLow) +// Arm.Rdm +METHOD(MultiplyRoundedDoublingAndAddSaturateHigh) +METHOD(MultiplyRoundedDoublingAndSubtractSaturateHigh) +METHOD(MultiplyRoundedDoublingBySelectedScalarAndAddSaturateHigh) +METHOD(MultiplyRoundedDoublingBySelectedScalarAndSubtractSaturateHigh) +// Arm.Rdm.Arm64 +METHOD(MultiplyRoundedDoublingAndAddSaturateHighScalar) +METHOD(MultiplyRoundedDoublingAndSubtractSaturateHighScalar) +METHOD(MultiplyRoundedDoublingScalarBySelectedScalarAndAddSaturateHigh) +METHOD(MultiplyRoundedDoublingScalarBySelectedScalarAndSubtractSaturateHigh) +// Arm.Dp +METHOD(DotProductBySelectedQuadruplet) From 5306472296c5c174be9379dac3920880de64c4a0 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 25 Jun 2021 00:01:06 +0300 Subject: [PATCH 106/926] Mark AssemblyDependencyResolver as unsupported on Browser and mobile (#54601) * Mark AssemblyDependencyResolver as unsupported on Browser and mobile * Apply AlekseyK's workaround to fix android CI builds * Add an annotation for maccatalyst --- .../System.Private.CoreLib.Shared.projitems | 14 ++++++--- ...DependencyResolver.PlatformNotSupported.cs | 31 +++++++++++++++++++ .../Loader/AssemblyDependencyResolver.cs | 6 ++++ .../ref/System.Runtime.Loader.cs | 5 +++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.PlatformNotSupported.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 1186b2c606f..de5bc44f1ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -12,6 +12,7 @@ enable true true + true true true $(MSBuildThisFileDirectory)ILLink\ @@ -856,7 +857,6 @@ - @@ -1222,6 +1222,15 @@ Common\System\Threading\Tasks\TaskToApm.cs + + + Common\Interop\Interop.HostPolicy.cs + + + + + + Common\Interop\Windows\Advapi32\Interop.ActivityControl.cs @@ -1232,9 +1241,6 @@ Common\Interop\Windows\Advapi32\Interop.EVENT_INFO_CLASS.cs - - Common\Interop\Interop.HostPolicy.cs - diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.PlatformNotSupported.cs new file mode 100644 index 00000000000..06df366a1c7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.PlatformNotSupported.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.Versioning; + +namespace System.Runtime.Loader +{ + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] + public sealed class AssemblyDependencyResolver + { + public AssemblyDependencyResolver(string componentAssemblyPath) + { + throw new PlatformNotSupportedException(); + } + + public string? ResolveAssemblyToPath(AssemblyName assemblyName) + { + throw new PlatformNotSupportedException(); + } + + public string? ResolveUnmanagedDllToPath(string unmanagedDllName) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs index 644e536757b..e2c18627675 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs @@ -5,10 +5,16 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; namespace System.Runtime.Loader { + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] public sealed class AssemblyDependencyResolver { /// diff --git a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs index e42a0642b32..3aaa591f624 100644 --- a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs +++ b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs @@ -22,6 +22,11 @@ namespace System.Reflection.Metadata } namespace System.Runtime.Loader { + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public sealed partial class AssemblyDependencyResolver { public AssemblyDependencyResolver(string componentAssemblyPath) { } From 20ff641973d5d66f6eda52bccaf3b86786d11f4e Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 24 Jun 2021 19:19:11 -0400 Subject: [PATCH 107/926] Fix MonoCrossAOT.UnixFilePermissions for wasm (#54693) * Fix MonoCrossAOT.UnixFilePermissions for wasm We introduced Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml in https://github.com/dotnet/runtime/pull/54501 to make sure the right permissions are set when installing AOT compiler workload packs. We hardcoded the list to include mono-aot-cross, llc, and opt. However, in wasm's case they only have mono-aot-cross. This change makes the xml file a template and only includes mono-aot-cross for browser and all three for the other configurations. Fixes https://github.com/dotnet/runtime/issues/54612 * Use property instead of item for the file nodes * Ankit suggestion Co-authored-by: Steve Pfister --- ...re.App.MonoCrossAOT.UnixFilePermissions.xml | 5 ----- ...App.MonoCrossAOT.UnixFilePermissions.xml.in | 3 +++ .../Microsoft.NETCore.App.MonoCrossAOT.sfxproj | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml create mode 100644 src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml.in diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml deleted file mode 100644 index 9437f7953f3..00000000000 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml.in b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml.in new file mode 100644 index 00000000000..6ad7487cb04 --- /dev/null +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml.in @@ -0,0 +1,3 @@ + + ${PermissionsProperties} + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj index 3c0e7fac2d8..a951a58b643 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj @@ -18,17 +18,29 @@ - + <_ToolFile Include="$(MonoAotCrossDir)$(TargetCrossRid)\**" /> + - + - + <_SdkPropsProperties Condition="!$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value="" /> <_SdkPropsProperties Condition="$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value=".exe" /> <_SdkPropsProperties Include="TargetRid" Value="$(TargetCrossRid)" /> + + <_PermissionsFiles>@(_ToolFile -> '<File Path="tools/%(RecursiveDir)%(FileName)%(Extension)" Permission="755" />', ' ') + + + <_PermissionsProperties Include="PermissionsProperties" Value="$(_PermissionsFiles)" /> + + Date: Thu, 24 Jun 2021 16:40:40 -0700 Subject: [PATCH 108/926] defMAC construction up a scope to make it live long enough. (#54702) Fixes #54649 --- src/coreclr/jit/morph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 6d7f8c7bc6f..e53c2d9e8ff 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6046,6 +6046,10 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) } #endif + // Create a default MorphAddrContext early so it doesn't go out of scope + // before it is used. + MorphAddrContext defMAC(MACK_Ind); + /* Is this an instance data member? */ if (objRef) @@ -6136,7 +6140,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) // NULL mac means we encounter the GT_FIELD first. This denotes a dereference of the field, // and thus is equivalent to a MACK_Ind with zero offset. - MorphAddrContext defMAC(MACK_Ind); if (mac == nullptr) { mac = &defMAC; From 881e90231b93652acde39bea946c6075a71d5ddc Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 24 Jun 2021 22:21:57 -0300 Subject: [PATCH 109/926] [wasm][debugger] Detect initial status of pause on exceptions. (#54040) * Detect initial status of pause on exceptions. * Changing what @radical suggested. * Changing more things. * Test case created. I could not test the pause on "all" exceptions because if I enable the pause on caught exceptions and reload the page it will stop in a lot of exceptions other then the one that I inserted in AttachToTarget. * Adding a test for Reload page with ALL set. * Fixing merge conflicts. * setting icordebug = false. * Removing unrelated change. --- .../BrowserDebugProxy/DevToolsHelper.cs | 3 + .../debugger/BrowserDebugProxy/MonoProxy.cs | 62 ++++++++++- .../DebuggerTestSuite/ExceptionTests.cs | 105 ++++++++++++++++++ 3 files changed, 168 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index f39f2736caa..6f1ec09317e 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -279,6 +279,9 @@ namespace Microsoft.WebAssembly.Diagnostics public int Id { get; set; } public object AuxData { get; set; } + public bool PauseOnUncaught { get; set; } + public bool PauseOnCaught { get; set; } + public List CallStack { get; set; } public string[] LoadedFiles { get; set; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 689904740d3..f45d31ca1c8 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -23,6 +23,8 @@ namespace Microsoft.WebAssembly.Diagnostics private static HttpClient client = new HttpClient(); private HashSet sessions = new HashSet(); private Dictionary contexts = new Dictionary(); + private const string sPauseOnUncaught = "pause_on_uncaught"; + private const string sPauseOnCaught = "pause_on_caught"; public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { @@ -122,8 +124,41 @@ namespace Microsoft.WebAssembly.Diagnostics return true; } + case "Runtime.exceptionThrown": + { + if (!GetContext(sessionId).IsRuntimeReady) + { + string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value(); + if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught) + { + return true; + } + } + break; + } + case "Debugger.paused": { + if (!GetContext(sessionId).IsRuntimeReady) + { + string reason = args?["reason"]?.Value(); + if (reason == "exception") + { + string exceptionError = args?["data"]?["value"]?.Value(); + if (exceptionError == sPauseOnUncaught) + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + GetContext(sessionId).PauseOnUncaught = true; + return true; + } + if (exceptionError == sPauseOnCaught) + { + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + GetContext(sessionId).PauseOnCaught = true; + return true; + } + } + } //TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack string top_func = args?["callFrames"]?[0]?["functionName"]?.Value(); switch (top_func) { @@ -398,7 +433,23 @@ namespace Microsoft.WebAssembly.Diagnostics case "Debugger.setPauseOnExceptions": { string state = args["state"].Value(); - await sdbHelper.EnableExceptions(id, state, token); + if (!context.IsRuntimeReady) + { + context.PauseOnCaught = false; + context.PauseOnUncaught = false; + switch (state) + { + case "all": + context.PauseOnCaught = true; + context.PauseOnUncaught = true; + break; + case "uncaught": + context.PauseOnUncaught = true; + break; + } + } + else + await sdbHelper.EnableExceptions(id, state, token); // Pass this on to JS too return false; } @@ -1152,6 +1203,11 @@ namespace Microsoft.WebAssembly.Diagnostics Log("verbose", $"Failed to clear breakpoints"); } + if (context.PauseOnCaught && context.PauseOnUncaught) + await sdbHelper.EnableExceptions(sessionId, "all", token); + else if (context.PauseOnUncaught) + await sdbHelper.EnableExceptions(sessionId, "uncaught", token); + await sdbHelper.SetProtocolVersion(sessionId, token); await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); @@ -1289,10 +1345,12 @@ namespace Microsoft.WebAssembly.Diagnostics // see https://github.com/mono/mono/issues/19549 for background if (sessions.Add(sessionId)) { + string checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";"; + string checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}"; await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token); Result res = await SendCommand(sessionId, "Page.addScriptToEvaluateOnNewDocument", - JObject.FromObject(new { source = "globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver" }), + JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkCaughtExceptions} {checkUncaughtExceptions}" }), token); if (sessionId != SessionId.Null && !res.IsOk) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index 1be0b18f325..eb0ebe0cc0d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.WebAssembly.Diagnostics; using Newtonsoft.Json.Linq; +using System.Threading; using Xunit; namespace DebuggerTests @@ -191,6 +192,110 @@ namespace DebuggerTests CheckString(exception_members, "message", exception_message); } + [Fact] + public async Task ExceptionTestUncaughtWithReload() + { + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + + await SetPauseOnException("uncaught"); + + await SendCommand("Page.enable", null); + await SendCommand("Page.reload", JObject.FromObject(new + { + ignoreCache = true + })); + Thread.Sleep(1000); + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); + + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); + + //stop in the uncaught exception + CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); + } + + [Fact] + public async Task ExceptionTestAllWithReload() + { + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + + await SetPauseOnException("all"); + + await SendCommand("Page.enable", null); + var pause_location = await SendCommandAndCheck(JObject.FromObject(new + { + ignoreCache = true + }), "Page.reload",null, 0, 0, null); + Thread.Sleep(1000); + + //send a lot of resumes to "skip" all the pauses on caught exception and completely reload the page + int i = 0; + while (i < 100) + { + Result res = await cli.SendCommand("Debugger.resume", null, token); + i++; + } + + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); + + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause0"); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = false + }), "exception0.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented caught"); + + pause_location = await WaitForManagedException(null); + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); + + //stop in the uncaught exception + CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); + } + + async Task WaitForManagedException(JObject pause_location) { while (true) From e129e327b60d49df7770333a3ced94e5085cf43d Mon Sep 17 00:00:00 2001 From: Egor Chesakov Date: Thu, 24 Jun 2021 20:26:08 -0700 Subject: [PATCH 110/926] Disallow promotion of HVA structs when their fields of TYP_SIMD8 were promoted as plain structs (#54694) --- src/coreclr/jit/lclvars.cpp | 4 ++- .../JitBlue/Runtime_54647/Runtime_54647.cs | 34 +++++++++++++++++++ .../Runtime_54647/Runtime_54647.csproj | 10 ++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.csproj diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 9f1c01042a4..916cb93e187 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1946,9 +1946,11 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) var_types fieldType = structPromotionInfo.fields[i].fldType; // Non-HFA structs are always passed in general purpose registers. // If there are any floating point fields, don't promote for now. + // Likewise, since HVA structs are passed in SIMD registers + // promotion of non FP or SIMD type fields is disallowed. // TODO-1stClassStructs: add support in Lowering and prolog generation // to enable promoting these types. - if (varDsc->lvIsParam && !varDsc->lvIsHfa() && varTypeUsesFloatReg(fieldType)) + if (varDsc->lvIsParam && (varDsc->lvIsHfa() != varTypeUsesFloatReg(fieldType))) { canPromote = false; } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.cs new file mode 100644 index 00000000000..d066fd1f063 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace Runtime_54647 +{ + struct Vector64x2 + { + Vector64 _fld1; + Vector64 _fld2; + } + + class Program + { + static int Main(string[] args) + { + var val1 = new Vector64x2(); + var val2 = new Vector64x2(); + + Copy(ref val1, val2); + + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Copy(ref Vector64x2 dst, Vector64x2 src) + { + dst = src; + } + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.csproj new file mode 100644 index 00000000000..1100f420532 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54647/Runtime_54647.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 7ed5a67c0bbb48bdce3cfd962e333c8f7db047a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Fri, 25 Jun 2021 06:37:07 +0200 Subject: [PATCH 111/926] S.N.Quic made private (#54610) Note this will need reaction on ASP.NET side. --- .../System.Net.Http/ref/System.Net.Http.cs | 1 - .../System.Net.Http/src/System.Net.Http.csproj | 2 +- .../Http/BrowserHttpHandler/SocketsHttpHandler.cs | 6 ------ .../Http/SocketsHttpHandler/SocketsHttpHandler.cs | 15 --------------- ...ttpClientHandlerTestBase.SocketsHttpHandler.cs | 11 +++++++++-- .../System.Net.Http.Functional.Tests.csproj | 1 + .../System.Net.Quic/Directory.Build.props | 1 + .../System.Net.Quic/ref/System.Net.Quic.csproj | 1 + .../System.Net.Quic.Functional.Tests.csproj | 1 + 9 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index 02486ab68b1..ea669da03d3 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -388,7 +388,6 @@ namespace System.Net.Http public bool EnableMultipleHttp2Connections { get { throw null; } set { } } public Func>? ConnectCallback { get { throw null; } set { } } public Func>? PlaintextStreamFilter { get { throw null; } set { } } - public System.Net.Quic.Implementations.QuicImplementationProvider? QuicImplementationProvider { get { throw null; } set { } } } public sealed class SocketsHttpConnectionContext { diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index a6034a21e0f..1423025f7c1 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -615,6 +615,7 @@ Link="Common\System\Net\Http\HttpHandlerDefaults.cs" /> + @@ -628,7 +629,6 @@ - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index f85f0a3521e..4a29ff1ea2b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -188,11 +188,5 @@ namespace System.Net.Http get => throw new PlatformNotSupportedException(); set => throw new PlatformNotSupportedException(); } - - public QuicImplementationProvider? QuicImplementationProvider - { - get => throw new PlatformNotSupportedException(); - set => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 6f392af09ac..76080c6cfc5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -394,21 +394,6 @@ namespace System.Net.Http } } - /// - /// Gets or sets the QUIC implementation to be used for HTTP3 requests. - /// - public QuicImplementationProvider? QuicImplementationProvider - { - // !!! NOTE !!! - // This is temporary and will not ship. - get => _settings._quicImplementationProvider; - set - { - CheckDisposedOrStarted(); - _settings._quicImplementationProvider = value; - } - } - public IDictionary Properties => _settings._properties ?? (_settings._properties = new Dictionary()); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs index 81a4438a83b..581a8bb5298 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs @@ -49,7 +49,7 @@ namespace System.Net.Http.Functional.Tests if (quicImplementationProvider != null) { SocketsHttpHandler socketsHttpHandler = (SocketsHttpHandler)GetUnderlyingSocketsHttpHandler(handler); - socketsHttpHandler.QuicImplementationProvider = quicImplementationProvider; + SetQuicImplementationProvider(socketsHttpHandler, quicImplementationProvider); socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => true; } @@ -66,13 +66,20 @@ namespace System.Net.Http.Functional.Tests protected static HttpClientHandler CreateHttpClientHandler(string useVersionString) => CreateHttpClientHandler(Version.Parse(useVersionString)); - protected static SocketsHttpHandler GetUnderlyingSocketsHttpHandler(HttpClientHandler handler) { FieldInfo field = typeof(HttpClientHandler).GetField("_underlyingHandler", BindingFlags.Instance | BindingFlags.NonPublic); return (SocketsHttpHandler)field?.GetValue(handler); } + protected static void SetQuicImplementationProvider(SocketsHttpHandler handler, QuicImplementationProvider quicImplementationProvider) + { + FieldInfo settingsField = typeof(SocketsHttpHandler).GetField("_settings", BindingFlags.Instance | BindingFlags.NonPublic); + object settings = settingsField.GetValue(handler); + FieldInfo field = settings.GetType().GetField("_quicImplementationProvider", BindingFlags.Instance | BindingFlags.NonPublic); + field.SetValue(settings, quicImplementationProvider); + } + protected static HttpRequestMessage CreateRequest(HttpMethod method, Uri uri, Version version, bool exactVersion = false) => new HttpRequestMessage(method, uri) { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 82246f634e5..7a33b8cac5c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -257,6 +257,7 @@ + diff --git a/src/libraries/System.Net.Quic/Directory.Build.props b/src/libraries/System.Net.Quic/Directory.Build.props index faeadef3efc..94b050a4b2a 100644 --- a/src/libraries/System.Net.Quic/Directory.Build.props +++ b/src/libraries/System.Net.Quic/Directory.Build.props @@ -3,6 +3,7 @@ Microsoft true + true windows;linux;macos diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj index d5dc6b1b204..507e003a13e 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent) enable + false diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index 4803a745431..2a17894a555 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -29,5 +29,6 @@ + From cef40a107ff2f810219b3b28af709b0b778a3472 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 24 Jun 2021 22:54:28 -0700 Subject: [PATCH 112/926] Move the metadata update related APIs to the MetadataUpdater class (#54590) Move the metadata update related APIs to the MetadataUpdater class The old APIs AssemblyExtensions.ApplyUpdate() and AssemblyExtensions.GetApplyUpdateCapabilities() will be removed after all the references to them have been changed to the new APIs. Add the new IsSupported API described in issue https://github.com/dotnet/runtime/issues/51159. Change the tests to use the MetadataUpdater APIs. Fix the ApplyUpdate qcalls and icalls Add the ILLink substitutions for MetadataUpdater.IsSupported property Change the old APIs to call the new ones Update mono's MetadataUpdater.IsSupported property Update feature switch doc Fixed the argument checking in coreclr's MetadataUpdater.ApplyUpdate(). --- docs/workflow/trimming/feature-switches.md | 1 + .../System.Private.CoreLib.csproj | 1 + .../Reflection/Metadata/AssemblyExtensions.cs | 25 +------ .../Reflection/Metadata/MetadataUpdater.cs | 63 +++++++++++++++++ src/coreclr/vm/assemblynative.cpp | 18 +++++ src/coreclr/vm/assemblynative.hpp | 1 + src/coreclr/vm/ecalllist.h | 8 ++- .../ILLink/ILLink.LinkAttributes.Shared.xml | 3 +- .../ILLink/ILLink.Substitutions.Shared.xml | 3 + .../ref/System.Runtime.Loader.cs | 5 ++ .../tests/ApplyUpdateTest.cs | 46 +++++++++++++ .../tests/ApplyUpdateUtil.cs | 10 +-- .../tests/AssemblyExtensionsTest.cs | 48 ------------- .../tests/System.Runtime.Loader.Tests.csproj | 1 - .../System.Private.CoreLib.csproj | 1 + .../Reflection/Metadata/AssemblyExtensions.cs | 35 +--------- .../Reflection/Metadata/MetadataUpdater.cs | 67 +++++++++++++++++++ src/mono/mono/metadata/icall-def-netcore.h | 6 +- src/mono/mono/metadata/icall.c | 2 +- 19 files changed, 226 insertions(+), 118 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs delete mode 100644 src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md index c95bec10877..9836a8375db 100644 --- a/docs/workflow/trimming/feature-switches.md +++ b/docs/workflow/trimming/feature-switches.md @@ -22,6 +22,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif | EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | BinaryFormatter serialization support is trimmed when set to false. | | BuiltInComInteropSupport | System.Runtime.InteropServices.BuiltInComInterop.IsSupported | Built-in COM support is trimmed when set to false. | | EnableCppCLIHostActivation | System.Runtime.InteropServices.EnableCppCLIHostActivation | C++/CLI host activation code is disabled when set to false and related functionality can be trimmed. | +| MetadataUpdaterSupport | System.Reflection.Metadata.MetadataUpdater.IsSupported | Metadata update related code to be trimmed when set to false | | _EnableConsumingManagedCodeFromNativeHosting | System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting | Getting a managed function from native hosting is disabled when set to false and related functionality can be trimmed. | Any feature-switch which defines property can be set in csproj file or diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 626f0eec24b..97671f56e5d 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -189,6 +189,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index 97b908ab259..55e897de3c0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -41,9 +41,6 @@ namespace System.Reflection.Metadata return InternalTryGetRawMetadata(new QCallAssembly(ref rtAsm), ref blob, ref length); } - [DllImport(RuntimeHelpers.QCall)] - private static extern unsafe void ApplyUpdate(QCallAssembly assembly, byte* metadataDelta, int metadataDeltaLength, byte* ilDelta, int ilDeltaLength, byte* pdbDelta, int pdbDeltaLength); - /// /// Updates the specified assembly using the provided metadata, IL and PDB deltas. /// @@ -61,30 +58,12 @@ namespace System.Reflection.Metadata /// The update could not be applied. public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) { - if (assembly == null) - { - throw new ArgumentNullException(nameof(assembly)); - } - - RuntimeAssembly? runtimeAssembly = assembly as RuntimeAssembly; - if (runtimeAssembly == null) - { - throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); - } - - unsafe - { - RuntimeAssembly rtAsm = runtimeAssembly; - fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta) - { - ApplyUpdate(new QCallAssembly(ref rtAsm), metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length); - } - } + MetadataUpdater.ApplyUpdate(assembly, metadataDelta, ilDelta, pdbDelta); } internal static string GetApplyUpdateCapabilities() { - return "Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes"; + return MetadataUpdater.GetCapabilities(); } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs new file mode 100644 index 00000000000..a24fa6710f7 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Reflection.Metadata +{ + public static class MetadataUpdater + { + [DllImport(RuntimeHelpers.QCall)] + private static extern unsafe void ApplyUpdate(QCallAssembly assembly, byte* metadataDelta, int metadataDeltaLength, byte* ilDelta, int ilDeltaLength, byte* pdbDelta, int pdbDeltaLength); + + [DllImport(RuntimeHelpers.QCall)] + private static extern unsafe bool IsApplyUpdateSupported(); + + /// + /// Updates the specified assembly using the provided metadata, IL and PDB deltas. + /// + /// + /// Currently executing methods will continue to use the existing IL. New executions of modified methods will + /// use the new IL. Different runtimes may have different limitations on what kinds of changes are supported, + /// and runtimes make no guarantees as to the state of the assembly and process if the delta includes + /// unsupported changes. + /// + /// The assembly to update. + /// The metadata changes to be applied. + /// The IL changes to be applied. + /// The PDB changes to be applied. + /// The assembly argument is not a runtime assembly. + /// The assembly argument is null. + /// The assembly is not editable. + /// The update could not be applied. + public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) + { + if (assembly is not RuntimeAssembly runtimeAssembly) + { + if (assembly is null) throw new ArgumentNullException(nameof(assembly)); + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + } + + unsafe + { + RuntimeAssembly rtAsm = runtimeAssembly; + fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta) + { + ApplyUpdate(new QCallAssembly(ref rtAsm), metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length); + } + } + } + + /// + /// Returns the metadata update capabilities. + /// + internal static string GetCapabilities() => "Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes"; + + /// + /// Returns true if the apply assembly update is enabled and available. + /// + public static bool IsSupported { get; } = IsApplyUpdateSupported(); + } +} diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 19e23c44c5b..b74038fa377 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1450,3 +1450,21 @@ void QCALLTYPE AssemblyNative::ApplyUpdate( END_QCALL; } + +// static +BOOL QCALLTYPE AssemblyNative::IsApplyUpdateSupported() +{ + QCALL_CONTRACT; + + BOOL result = false; + + BEGIN_QCALL; + +#ifdef EnC_SUPPORTED + BOOL result = CORDebuggerAttached() || g_pConfig->ForceEnc() || g_pConfig->DebugAssembliesModifiable(); +#endif + + END_QCALL; + + return result; +} diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index 25ebf74d5c8..54ca987a86f 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -129,6 +129,7 @@ public: static void QCALLTYPE TraceSatelliteSubdirectoryPathProbed(LPCWSTR filePath, HRESULT hr); static void QCALLTYPE ApplyUpdate(QCall::AssemblyHandle assembly, UINT8* metadataDelta, INT32 metadataDeltaLength, UINT8* ilDelta, INT32 ilDeltaLength, UINT8* pdbDelta, INT32 pdbDeltaLength); + static BOOL QCALLTYPE IsApplyUpdateSupported(); }; #endif diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 1822422f596..5cc1d5827fb 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -438,7 +438,11 @@ FCFuncEnd() FCFuncStart(gAssemblyExtensionsFuncs) QCFuncElement("InternalTryGetRawMetadata", AssemblyNative::InternalTryGetRawMetadata) +FCFuncEnd() + +FCFuncStart(gMetadataUpdaterFuncs) QCFuncElement("ApplyUpdate", AssemblyNative::ApplyUpdate) + QCFuncElement("IsApplyUpdateSupported", AssemblyNative::IsApplyUpdateSupported) FCFuncEnd() FCFuncStart(gAssemblyLoadContextFuncs) @@ -1121,11 +1125,8 @@ FCClassElement("ArgIterator", "System", gVarArgFuncs) FCClassElement("Array", "System", gArrayFuncs) FCClassElement("Assembly", "System.Reflection", gAssemblyFuncs) FCClassElement("AssemblyBuilder", "System.Reflection.Emit", gAssemblyBuilderFuncs) - FCClassElement("AssemblyExtensions", "System.Reflection.Metadata", gAssemblyExtensionsFuncs) - FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs) - FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs) FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CLRConfig", "System", gClrConfig) @@ -1168,6 +1169,7 @@ FCClassElement("Math", "System", gMathFuncs) FCClassElement("MathF", "System", gMathFFuncs) FCClassElement("MdUtf8String", "System", gMdUtf8String) FCClassElement("MetadataImport", "System.Reflection", gMetaDataImport) +FCClassElement("MetadataUpdater", "System.Reflection.Metadata", gMetadataUpdaterFuncs) FCClassElement("MissingMemberException", "System", gMissingMemberExceptionFuncs) FCClassElement("MngdFixedArrayMarshaler", "System.StubHelpers", gMngdFixedArrayMarshalerFuncs) FCClassElement("MngdNativeArrayMarshaler", "System.StubHelpers", gMngdNativeArrayMarshalerFuncs) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml index d910ad1eb64..b6b2046f557 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml @@ -29,8 +29,9 @@ + - + diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index 629afffc730..ba2db281981 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -27,5 +27,8 @@ + + + diff --git a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs index 3aaa591f624..6555a3093ee 100644 --- a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs +++ b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs @@ -12,6 +12,11 @@ namespace System.Reflection.Metadata public unsafe static bool TryGetRawMetadata(this System.Reflection.Assembly assembly, out byte* blob, out int length) { throw null; } public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) { throw null; } } + public static partial class MetadataUpdater + { + public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) { throw null; } + public static bool IsSupported { get { throw null; } } + } [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] public sealed class MetadataUpdateHandlerAttribute : System.Attribute { diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index 2a953d19eec..e09eaa0a141 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -83,5 +83,51 @@ namespace System.Reflection.Metadata Assert.Equal(attrType, cattrs[0].GetType()); }); } + + class NonRuntimeAssembly : Assembly + { + } + + [Fact] + public static void ApplyUpdateInvalidParameters() + { + // Dummy delta arrays + var metadataDelta = new byte[20]; + var ilDelta = new byte[20]; + + // Assembly can't be null + Assert.Throws("assembly", () => + MetadataUpdater.ApplyUpdate(null, new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); + + // Tests fail on non-runtime assemblies + Assert.Throws(() => + MetadataUpdater.ApplyUpdate(new NonRuntimeAssembly(), new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); + + // Tests that this assembly isn't not editable + Assert.Throws(() => + MetadataUpdater.ApplyUpdate(typeof(AssemblyExtensions).Assembly, new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); + } + + [Fact] + public static void GetCapabilities() + { + var ty = typeof(System.Reflection.Metadata.MetadataUpdater); + var mi = ty.GetMethod("GetCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); + + Assert.NotNull(mi); + + var result = mi.Invoke(null, null); + + Assert.NotNull(result); + Assert.Equal(typeof(string), result.GetType()); + } + + [Fact] + [ActiveIssue("Returns true on mono", TestRuntimes.Mono)] + public static void IsSupported() + { + bool result = MetadataUpdater.IsSupported; + Assert.False(result); + } } } diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs index ac734d35456..235e94bc2d8 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs @@ -48,8 +48,8 @@ namespace System.Reflection.Metadata internal static bool HasApplyUpdateCapabilities() { - var ty = typeof(AssemblyExtensions); - var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); + var ty = typeof(MetadataUpdater); + var mi = ty.GetMethod("GetCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); if (mi == null) return false; @@ -83,15 +83,15 @@ namespace System.Reflection.Metadata byte[] dil_data = System.IO.File.ReadAllBytes(dil_name); byte[] dpdb_data = null; // TODO also use the dpdb data - AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); + MetadataUpdater.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); } internal static bool UseRemoteExecutor => !IsModifiableAssembliesSet; internal static void AddRemoteInvokeOptions (ref RemoteInvokeOptions options) { - options = options ?? new RemoteInvokeOptions(); - options.StartInfo.EnvironmentVariables.Add(DotNetModifiableAssembliesSwitch, DotNetModifiableAssembliesValue); + options = options ?? new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables.Add(DotNetModifiableAssembliesSwitch, DotNetModifiableAssembliesValue); } /// Run the given test case, which applies updates to the given assembly. diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs deleted file mode 100644 index 5208b573c75..00000000000 --- a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -namespace System.Reflection.Metadata -{ - public class AssemblyExtensionsTest - { - class NonRuntimeAssembly : Assembly - { - } - - [Fact] - public static void ApplyUpdateInvalidParameters() - { - // Dummy delta arrays - var metadataDelta = new byte[20]; - var ilDelta = new byte[20]; - - // Assembly can't be null - Assert.Throws("assembly", () => - AssemblyExtensions.ApplyUpdate(null, new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); - - // Tests fail on non-runtime assemblies - Assert.Throws(() => - AssemblyExtensions.ApplyUpdate(new NonRuntimeAssembly(), new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); - - // Tests that this assembly isn't not editable - Assert.Throws(() => - AssemblyExtensions.ApplyUpdate(typeof(AssemblyExtensions).Assembly, new ReadOnlySpan(metadataDelta), new ReadOnlySpan(ilDelta), ReadOnlySpan.Empty)); - } - - [Fact] - public void GetApplyUpdateCapabilitiesIsCallable() - { - var ty = typeof(System.Reflection.Metadata.AssemblyExtensions); - var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty()); - - Assert.NotNull(mi); - - var result = mi.Invoke(null, null); - - Assert.NotNull(result); - Assert.Equal(typeof(string), result.GetType()); - } - } -} diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index dc1a2cccd21..eeae81e75cc 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 61ad598697e..803d83464ea 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -239,6 +239,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index c211c5d68f5..6d9fba32a96 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -27,40 +27,9 @@ namespace System.Reflection.Metadata /// The update could not be applied. public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) { - if (assembly is not RuntimeAssembly runtimeAssembly) - { - if (assembly is null) throw new ArgumentNullException(nameof(assembly)); - throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); - } - - // System.Private.CoreLib is not editable - if (runtimeAssembly == typeof(AssemblyExtensions).Assembly) - throw new InvalidOperationException (SR.InvalidOperation_AssemblyNotEditable); - - unsafe - { - IntPtr monoAssembly = runtimeAssembly.GetUnderlyingNativeHandle (); - fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta) - { - ApplyUpdate_internal(monoAssembly, metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length); - } - } + MetadataUpdater.ApplyUpdate(assembly, metadataDelta, ilDelta, pdbDelta); } - private static Lazy s_ApplyUpdateCapabilities = new Lazy(() => InitializeApplyUpdateCapabilities()); - - internal static string GetApplyUpdateCapabilities() => s_ApplyUpdateCapabilities.Value; - - private static string InitializeApplyUpdateCapabilities() - { - return ApplyUpdateEnabled() != 0 ? "Baseline" : string.Empty ; - } - - - [MethodImpl (MethodImplOptions.InternalCall)] - private static extern int ApplyUpdateEnabled (); - - [MethodImpl (MethodImplOptions.InternalCall)] - private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length); + internal static string GetApplyUpdateCapabilities() => MetadataUpdater.GetCapabilities(); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs new file mode 100644 index 00000000000..ea7035ee10a --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Reflection.Metadata +{ + public static class MetadataUpdater + { + /// + /// Updates the specified assembly using the provided metadata, IL and PDB deltas. + /// + /// + /// Currently executing methods will continue to use the existing IL. New executions of modified methods will + /// use the new IL. Different runtimes may have different limitations on what kinds of changes are supported, + /// and runtimes make no guarantees as to the state of the assembly and process if the delta includes + /// unsupported changes. + /// + /// The assembly to update. + /// The metadata changes to be applied. + /// The IL changes to be applied. + /// The PDB changes to be applied. + /// The assembly argument is not a runtime assembly. + /// The assembly argument is null. + /// The assembly is not editable. + /// The update could not be applied. + public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) + { + if (assembly is not RuntimeAssembly runtimeAssembly) + { + if (assembly is null) throw new ArgumentNullException(nameof(assembly)); + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + } + + // System.Private.CoreLib is not editable + if (runtimeAssembly == typeof(AssemblyExtensions).Assembly) + throw new InvalidOperationException (SR.InvalidOperation_AssemblyNotEditable); + + unsafe + { + IntPtr monoAssembly = runtimeAssembly.GetUnderlyingNativeHandle (); + fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta) + { + ApplyUpdate_internal(monoAssembly, metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length); + } + } + } + + internal static string GetCapabilities() => s_ApplyUpdateCapabilities.Value; + + public static bool IsSupported { get; } = ApplyUpdateEnabled() != 0; + + private static Lazy s_ApplyUpdateCapabilities = new Lazy(() => InitializeApplyUpdateCapabilities()); + + private static string InitializeApplyUpdateCapabilities() + { + return ApplyUpdateEnabled() != 0 ? "Baseline" : string.Empty ; + } + + [MethodImpl (MethodImplOptions.InternalCall)] + private static extern int ApplyUpdateEnabled (); + + [MethodImpl (MethodImplOptions.InternalCall)] + private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length); + } +} diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 6fb0ddd4712..56b5804d153 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -245,9 +245,9 @@ ICALL_TYPE(FIELDI, "System.Reflection.FieldInfo", FILEDI_1) HANDLES(FILEDI_1, "get_marshal_info", ves_icall_System_Reflection_FieldInfo_get_marshal_info, MonoReflectionMarshalAsAttribute, 1, (MonoReflectionField)) HANDLES(FILEDI_2, "internal_from_handle_type", ves_icall_System_Reflection_FieldInfo_internal_from_handle_type, MonoReflectionField, 2, (MonoClassField_ref, MonoType_ref)) -ICALL_TYPE(ASSMEXT, "System.Reflection.Metadata.AssemblyExtensions", ASSMEXT_2) -NOHANDLES(ICALL(ASSMEXT_2, "ApplyUpdateEnabled", ves_icall_AssemblyExtensions_ApplyUpdateEnabled)) -NOHANDLES(ICALL(ASSMEXT_1, "ApplyUpdate_internal", ves_icall_AssemblyExtensions_ApplyUpdate)) +ICALL_TYPE(MDUP, "System.Reflection.Metadata.MetadataUpdater", MDUP_1) +NOHANDLES(ICALL(MDUP_1, "ApplyUpdateEnabled", ves_icall_AssemblyExtensions_ApplyUpdateEnabled)) +NOHANDLES(ICALL(MDUP_2, "ApplyUpdate_internal", ves_icall_AssemblyExtensions_ApplyUpdate)) ICALL_TYPE(MBASE, "System.Reflection.MethodBase", MBASE_1) HANDLES(MBASE_1, "GetCurrentMethod", ves_icall_GetCurrentMethod, MonoReflectionMethod, 0, ()) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 014b69b0433..c6e3905e2da 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5789,7 +5789,7 @@ ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gint32 ves_icall_AssemblyExtensions_ApplyUpdateEnabled (void) { - return mono_metadata_update_available (); + return mono_metadata_update_available () && mono_metadata_update_enabled (NULL); } MonoBoolean From b5b8863005d0d95cc470be458108b5176396a522 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 25 Jun 2021 09:32:44 +0300 Subject: [PATCH 113/926] Optimize `CAST(int <- long)` on 32 bit targets (#53040) * Optimize CAST(int <- long) on 32 bit targets * Revert "Optimize CAST(int <- long) on 32 bit targets" Revert the implementation in lowering * Optimize CAST(int <- long) on 32 bit targets Move the code from lowering to long decomposition. * Fixed the "Arguments" note for DecomposeNode * Added the function header For OptimizeCastFromDecomposedLong. * Remove the TODO comment While correct, it has questionable value. * Add a more detailed dump output * Do not try to optimize checked casts It is easy to get it wrong. Let the frontend handle this. * Do not depend on tree order Previous version of the code assumed that there could be no nodes between the cast and its operand. That is not a correct assumption to make in LIR. --- src/coreclr/jit/decomposelongs.cpp | 103 ++++++++++++++++++++++++++++- src/coreclr/jit/decomposelongs.h | 2 + 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index f563b448abe..7e17db1ae91 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -114,7 +114,7 @@ void DecomposeLongs::DecomposeRangeHelper() // DecomposeNode: Decompose long-type trees into lower and upper halves. // // Arguments: -// use - the LIR::Use object for the def that needs to be decomposed. +// tree - the tree that will, if needed, be decomposed. // // Return Value: // The next node to process. @@ -279,6 +279,13 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree) } #endif + // When casting from a decomposed long to a smaller integer we can discard the high part. + if (m_compiler->opts.OptimizationEnabled() && !use.IsDummyUse() && use.User()->OperIs(GT_CAST) && + use.User()->TypeIs(TYP_INT) && use.Def()->OperIs(GT_LONG)) + { + nextNode = OptimizeCastFromDecomposedLong(use.User()->AsCast(), nextNode); + } + return nextNode; } @@ -1784,6 +1791,100 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW #endif // FEATURE_HW_INTRINSICS +//------------------------------------------------------------------------ +// OptimizeCastFromDecomposedLong: optimizes a cast from GT_LONG by discarding +// the high part of the source and, if the cast is to INT, the cast node itself. +// Accounts for side effects and marks nodes unused as neccessary. +// +// Only accepts casts to integer types that are not long. +// Does not optimize checked casts. +// +// Arguments: +// cast - the cast tree that has a GT_LONG node as its operand. +// nextNode - the next candidate for decomposition. +// +// Return Value: +// The next node to process in DecomposeRange: "nextNode->gtNext" if +// "cast == nextNode", simply "nextNode" otherwise. +// +// Notes: +// Because "nextNode" usually is "cast", and this method may remove "cast" +// from the linear order, it needs to return the updated "nextNode". Instead +// of receiving it as an argument, it could assume that "nextNode" is always +// "cast->CastOp()->gtNext", but not making that assumption seems better. +// +GenTree* DecomposeLongs::OptimizeCastFromDecomposedLong(GenTreeCast* cast, GenTree* nextNode) +{ + GenTreeOp* src = cast->CastOp()->AsOp(); + var_types dstType = cast->CastToType(); + + assert(src->OperIs(GT_LONG)); + assert(genActualType(dstType) == TYP_INT); + + if (cast->gtOverflow()) + { + return nextNode; + } + + GenTree* loSrc = src->gtGetOp1(); + GenTree* hiSrc = src->gtGetOp2(); + + JITDUMP("Optimizing a truncating cast [%06u] from decomposed LONG [%06u]\n", cast->gtTreeID, src->gtTreeID); + INDEBUG(GenTree* treeToDisplay = cast); + + // TODO-CQ: we could go perform this removal transitively. + // See also identical code in shift decomposition. + if ((hiSrc->gtFlags & (GTF_ALL_EFFECT | GTF_SET_FLAGS)) == 0) + { + JITDUMP("Removing the HI part of [%06u] and marking its operands unused:\n", src->gtTreeID); + DISPNODE(hiSrc); + Range().Remove(hiSrc, /* markOperandsUnused */ true); + } + else + { + JITDUMP("The HI part of [%06u] has side effects, marking it unused\n", src->gtTreeID); + hiSrc->SetUnusedValue(); + } + + JITDUMP("Removing the LONG source:\n"); + DISPNODE(src); + Range().Remove(src); + + if (varTypeIsSmall(dstType)) + { + JITDUMP("Cast is to a small type, keeping it, the new source is [%06u]\n", loSrc->gtTreeID); + cast->CastOp() = loSrc; + } + else + { + LIR::Use useOfCast; + if (Range().TryGetUse(cast, &useOfCast)) + { + useOfCast.ReplaceWith(m_compiler, loSrc); + } + else + { + loSrc->SetUnusedValue(); + } + + if (nextNode == cast) + { + nextNode = nextNode->gtNext; + } + + INDEBUG(treeToDisplay = loSrc); + JITDUMP("Removing the cast:\n"); + DISPNODE(cast); + + Range().Remove(cast); + } + + JITDUMP("Final result:\n") + DISPTREERANGE(Range(), treeToDisplay); + + return nextNode; +} + //------------------------------------------------------------------------ // StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't, // store the node to a var. Then decompose the new LclVar. diff --git a/src/coreclr/jit/decomposelongs.h b/src/coreclr/jit/decomposelongs.h index 4a7320553bd..7b5ae19be4f 100644 --- a/src/coreclr/jit/decomposelongs.h +++ b/src/coreclr/jit/decomposelongs.h @@ -63,6 +63,8 @@ private: GenTree* DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHWIntrinsic* node); #endif // FEATURE_HW_INTRINSICS + GenTree* OptimizeCastFromDecomposedLong(GenTreeCast* cast, GenTree* nextNode); + // Helper functions GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult, GenTree* insertResultAfter); GenTree* RepresentOpAsLocalVar(GenTree* op, GenTree* user, GenTree** edge); From ec42090f311ae3239abe61830e75bddc761fc828 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 25 Jun 2021 09:32:54 +0300 Subject: [PATCH 114/926] Eliminate chained casts to small types (#52561) We can take advantage of the implicit zero/sign-extension for small integer types and eliminate some casts. --- src/coreclr/jit/morph.cpp | 46 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index e53c2d9e8ff..946fe11cbec 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -730,14 +730,54 @@ OPTIMIZECAST: break; case GT_CAST: - /* Check for two consecutive casts into the same dstType */ + // Check for two consecutive casts into the same dstType. + // Also check for consecutive casts to small types. if (!tree->gtOverflow()) { - var_types dstType2 = oper->CastToType(); - if (dstType == dstType2) + var_types dstCastToType = dstType; + var_types srcCastToType = oper->CastToType(); + if (dstCastToType == srcCastToType) { goto REMOVE_CAST; } + // We can take advantage of the implicit zero/sign-extension for + // small integer types and eliminate some casts. + if (opts.OptimizationEnabled() && !oper->gtOverflow() && !gtIsActiveCSE_Candidate(oper) && + varTypeIsSmall(dstCastToType) && varTypeIsSmall(srcCastToType)) + { + // Gather some facts about our casts. + bool srcZeroExtends = varTypeIsUnsigned(srcCastToType); + bool dstZeroExtends = varTypeIsUnsigned(dstCastToType); + unsigned srcCastToSize = genTypeSize(srcCastToType); + unsigned dstCastToSize = genTypeSize(dstCastToType); + + // If the previous cast to a smaller type was zero-extending, + // this cast will also always be zero-extending. Example: + // CAST(ubyte): 000X => CAST(short): Sign-extend(0X) => 000X. + if (srcZeroExtends && (dstCastToSize > srcCastToSize)) + { + dstZeroExtends = true; + } + + // Case #1: cast to a smaller or equal in size type. + // We can discard the intermediate cast. Examples: + // CAST(short): --XX => CAST(ubyte): 000X. + // CAST(ushort): 00XX => CAST(short): --XX. + if (dstCastToSize <= srcCastToSize) + { + tree->AsCast()->CastOp() = oper->AsCast()->CastOp(); + DEBUG_DESTROY_NODE(oper); + } + // Case #2: cast to a larger type with the same effect. + // Here we can eliminate the outer cast. Example: + // CAST(byte): ---X => CAST(short): Sign-extend(-X) => ---X. + // Example of a sequence where this does not hold: + // CAST(byte): ---X => CAST(ushort): Zero-extend(-X) => 00-X. + else if (srcZeroExtends == dstZeroExtends) + { + goto REMOVE_CAST; + } + } } break; From 17828d199b0794f1902611e1f2fffa72d734e489 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 25 Jun 2021 09:37:09 +0300 Subject: [PATCH 115/926] Fix CQ regression & correctness bug in morphing of long muls (#53566) * Add a test covering GTF_MUL_64RSLT transform * Disable the test on Mono * Add genActualTypeIsInt helper * Add some gtFlags helpers * Prepare decomposition for new long muls * Update gtIsValid64RsltMul To understand the new format for long muls. * Rework morphing of long muls Previously, morph was looking for the exact pattern of MUL(CAST(long <- int), CAST(long <- int)) when assessing candidacy of GT_MUL for being marked with "GTF_MUL_64RSLT" and emitted as "long mul". This worked fine, until the importer was changed to fold all casts with constant operands. This broke the pattern matching and thus all MULs in the form of (long)value * 10 started being emitted as helper calls. This change updates morph to understand the new folded casts and in general updates the "format" of long mul from "CAST * CAST" to "CAST * (CAST | CONST)". In the process, new helper functions have been introduced, to avoid bloating fgMorphSmpOp with the new sizeable logic. Recognition of overflowing cases has been upgraded, and a correctness bug, where "checked((long)uint.MaxValue * (long)uint.MaxValue)" was wrongly treated as non-overflowing, fixed. Additionally, the logic to emit intermediate NOPs has been changed to instead always skip morphing the casts themselves, even when remorphing. * Add the script to generate the longmul test The test itself has been regenerated using it and there were no diffs, as expected. --- src/coreclr/jit/compiler.h | 6 + src/coreclr/jit/decomposelongs.cpp | 30 +- src/coreclr/jit/gentree.cpp | 28 +- src/coreclr/jit/gentree.h | 57 + src/coreclr/jit/morph.cpp | 275 +- src/coreclr/jit/vartype.h | 6 + .../JIT/Methodical/int64/misc/longmul.il | 5869 +++++++++++++++++ .../JIT/Methodical/int64/misc/longmul.ilproj | 12 + .../JIT/Methodical/int64/misc/longmul_gen.csx | 324 + src/tests/issues.targets | 3 + 10 files changed, 6487 insertions(+), 123 deletions(-) create mode 100644 src/tests/JIT/Methodical/int64/misc/longmul.il create mode 100644 src/tests/JIT/Methodical/int64/misc/longmul.ilproj create mode 100644 src/tests/JIT/Methodical/int64/misc/longmul_gen.csx diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 117d7d28a16..fe3f987667d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5833,6 +5833,12 @@ private: GenTree* fgRecognizeAndMorphBitwiseRotation(GenTree* tree); bool fgOperIsBitwiseRotationRoot(genTreeOps oper); +#if !defined(TARGET_64BIT) + // Recognize and morph a long multiplication with 32 bit operands. + GenTreeOp* fgRecognizeAndMorphLongMul(GenTreeOp* mul); + GenTreeOp* fgMorphLongMul(GenTreeOp* mul); +#endif + //-------- Determine the order in which the trees will be evaluated ------- unsigned fgTreeSeqNum; diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index 7e17db1ae91..39a4d01acc4 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -608,12 +608,12 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) { // // This int->long cast is used by a GT_MUL that will be transformed by DecomposeMul into a - // GT_LONG_MUL and as a result the high operand produced by the cast will become dead. + // GT_MUL_LONG and as a result the high operand produced by the cast will become dead. // Skip cast decomposition so DecomposeMul doesn't need to bother with dead code removal, // especially in the case of sign extending casts that also introduce new lclvars. // - assert((use.User()->gtFlags & GTF_MUL_64RSLT) != 0); + assert(use.User()->Is64RsltMul()); skipDecomposition = true; } @@ -1541,19 +1541,29 @@ GenTree* DecomposeLongs::DecomposeMul(LIR::Use& use) { assert(use.IsInitialized()); - GenTree* tree = use.Def(); - genTreeOps oper = tree->OperGet(); + GenTree* tree = use.Def(); - assert(oper == GT_MUL); - assert((tree->gtFlags & GTF_MUL_64RSLT) != 0); + assert(tree->OperIs(GT_MUL)); + assert(tree->Is64RsltMul()); GenTree* op1 = tree->gtGetOp1(); GenTree* op2 = tree->gtGetOp2(); - // We expect both operands to be int->long casts. DecomposeCast specifically - // ignores such casts when they are used by GT_MULs. - assert((op1->OperGet() == GT_CAST) && (op1->TypeGet() == TYP_LONG)); - assert((op2->OperGet() == GT_CAST) && (op2->TypeGet() == TYP_LONG)); + assert(op1->TypeIs(TYP_LONG) && op2->TypeIs(TYP_LONG)); + + // We expect the first operand to be an int->long cast. + // DecomposeCast specifically ignores such casts when they are used by GT_MULs. + assert(op1->OperIs(GT_CAST)); + + // The second operand can be a cast or a constant. + if (!op2->OperIs(GT_CAST)) + { + assert(op2->OperIs(GT_LONG)); + assert(op2->gtGetOp1()->IsIntegralConst()); + assert(op2->gtGetOp2()->IsIntegralConst()); + + Range().Remove(op2->gtGetOp2()); + } Range().Remove(op1); Range().Remove(op2); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3596de621d6..bb088e9dae9 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2438,15 +2438,15 @@ GenTree* Compiler::gtReverseCond(GenTree* tree) bool GenTree::gtIsValid64RsltMul() { - if ((gtOper != GT_MUL) || !(gtFlags & GTF_MUL_64RSLT)) + if (!OperIs(GT_MUL) || !Is64RsltMul()) { return false; } - GenTree* op1 = AsOp()->gtOp1; - GenTree* op2 = AsOp()->gtOp2; + GenTree* op1 = AsOp()->gtGetOp1(); + GenTree* op2 = AsOp()->gtGetOp2(); - if (TypeGet() != TYP_LONG || op1->TypeGet() != TYP_LONG || op2->TypeGet() != TYP_LONG) + if (!TypeIs(TYP_LONG) || !op1->TypeIs(TYP_LONG) || !op2->TypeIs(TYP_LONG)) { return false; } @@ -2456,26 +2456,30 @@ bool GenTree::gtIsValid64RsltMul() return false; } - // op1 has to be conv.i8(i4Expr) - if ((op1->gtOper != GT_CAST) || (genActualType(op1->CastFromType()) != TYP_INT)) + // op1 has to be CAST(long <- int). + if (!(op1->OperIs(GT_CAST) && genActualTypeIsInt(op1->AsCast()->CastOp()))) { return false; } - // op2 has to be conv.i8(i4Expr) - if ((op2->gtOper != GT_CAST) || (genActualType(op2->CastFromType()) != TYP_INT)) + // op2 has to be CAST(long <- int) or a suitably small constant. + if (!(op2->OperIs(GT_CAST) && genActualTypeIsInt(op2->AsCast()->CastOp())) && + !(op2->IsIntegralConst() && FitsIn(op2->AsIntConCommon()->IntegralValue()))) { return false; } - // The signedness of both casts must be the same - if (((op1->gtFlags & GTF_UNSIGNED) != 0) != ((op2->gtFlags & GTF_UNSIGNED) != 0)) + // Both operands must extend the same way. + bool op1ZeroExtends = op1->IsUnsigned(); + bool op2ZeroExtends = op2->OperIs(GT_CAST) ? op2->IsUnsigned() : op2->AsIntConCommon()->IntegralValue() >= 0; + bool op2AnyExtensionIsSuitable = op2->IsIntegralConst() && op2ZeroExtends; + if ((op1ZeroExtends != op2ZeroExtends) && !op2AnyExtensionIsSuitable) { return false; } - // Do unsigned mul iff both the casts are unsigned - if (((op1->gtFlags & GTF_UNSIGNED) != 0) != ((gtFlags & GTF_UNSIGNED) != 0)) + // Do unsigned mul iff both operands are zero-extending. + if (op1->IsUnsigned() != IsUnsigned()) { return false; } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e2a5045e906..e46e33de6fe 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2117,6 +2117,63 @@ public: return ((gtFlags & GTF_UNSIGNED) != 0); } + void SetUnsigned() + { + assert(OperIs(GT_ADD, GT_SUB, GT_MUL, GT_CAST)); + gtFlags |= GTF_UNSIGNED; + } + + void ClearUnsigned() + { + assert(OperIs(GT_ADD, GT_SUB, GT_MUL, GT_CAST)); + gtFlags &= ~GTF_UNSIGNED; + } + + void SetOverflow() + { + assert(OperMayOverflow()); + gtFlags |= GTF_OVERFLOW; + } + + void ClearOverflow() + { + assert(OperMayOverflow()); + gtFlags &= ~GTF_OVERFLOW; + } + + bool Is64RsltMul() const + { + return (gtFlags & GTF_MUL_64RSLT) != 0; + } + + void Set64RsltMul() + { + gtFlags |= GTF_MUL_64RSLT; + } + + void Clear64RsltMul() + { + gtFlags &= ~GTF_MUL_64RSLT; + } + + void SetAllEffectsFlags(GenTree* source) + { + SetAllEffectsFlags(source->gtFlags & GTF_ALL_EFFECT); + } + + void SetAllEffectsFlags(GenTree* source, GenTree* otherSource) + { + SetAllEffectsFlags((source->gtFlags | otherSource->gtFlags) & GTF_ALL_EFFECT); + } + + void SetAllEffectsFlags(GenTreeFlags sourceFlags) + { + assert((sourceFlags & ~GTF_ALL_EFFECT) == 0); + + gtFlags &= ~GTF_ALL_EFFECT; + gtFlags |= sourceFlags; + } + inline bool IsCnsIntOrI() const; inline bool IsIntegralConst() const; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 946fe11cbec..9dc3020c15d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -12382,6 +12382,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) return fgMorphCast(tree); case GT_MUL: + noway_assert(op2 != nullptr); + if (opts.OptimizationEnabled() && !optValnumCSE_phase && !tree->gtOverflow()) { // MUL(NEG(a), C) => MUL(a, NEG(C)) @@ -12401,119 +12403,39 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #ifndef TARGET_64BIT if (typ == TYP_LONG) { - /* For (long)int1 * (long)int2, we dont actually do the - casts, and just multiply the 32 bit values, which will - give us the 64 bit result in edx:eax */ + // For (long)int1 * (long)int2, we dont actually do the + // casts, and just multiply the 32 bit values, which will + // give us the 64 bit result in edx:eax. - noway_assert(op2); - if ((op1->gtOper == GT_CAST && op2->gtOper == GT_CAST && - genActualType(op1->CastFromType()) == TYP_INT && genActualType(op2->CastFromType()) == TYP_INT) && - !op1->gtOverflow() && !op2->gtOverflow()) + if (tree->Is64RsltMul()) { - // The casts have to be of the same signedness. - if ((op1->gtFlags & GTF_UNSIGNED) != (op2->gtFlags & GTF_UNSIGNED)) - { - // We see if we can force an int constant to change its signedness - GenTree* constOp; - if (op1->AsCast()->CastOp()->gtOper == GT_CNS_INT) - constOp = op1; - else if (op2->AsCast()->CastOp()->gtOper == GT_CNS_INT) - constOp = op2; - else - goto NO_MUL_64RSLT; - - if (((unsigned)(constOp->AsCast()->CastOp()->AsIntCon()->gtIconVal) < (unsigned)(0x80000000))) - constOp->gtFlags ^= GTF_UNSIGNED; - else - goto NO_MUL_64RSLT; - } - - // The only combination that can overflow - if (tree->gtOverflow() && (tree->gtFlags & GTF_UNSIGNED) && !(op1->gtFlags & GTF_UNSIGNED)) - goto NO_MUL_64RSLT; - - /* Remaining combinations can never overflow during long mul. */ - - tree->gtFlags &= ~GTF_OVERFLOW; - - /* Do unsigned mul only if the casts were unsigned */ - - tree->gtFlags &= ~GTF_UNSIGNED; - tree->gtFlags |= op1->gtFlags & GTF_UNSIGNED; - - /* Since we are committing to GTF_MUL_64RSLT, we don't want - the casts to be folded away. So morph the castees directly */ - - op1->AsOp()->gtOp1 = fgMorphTree(op1->AsOp()->gtOp1); - op2->AsOp()->gtOp1 = fgMorphTree(op2->AsOp()->gtOp1); - - // Propagate side effect flags up the tree - op1->gtFlags &= ~GTF_ALL_EFFECT; - op1->gtFlags |= (op1->AsOp()->gtOp1->gtFlags & GTF_ALL_EFFECT); - op2->gtFlags &= ~GTF_ALL_EFFECT; - op2->gtFlags |= (op2->AsOp()->gtOp1->gtFlags & GTF_ALL_EFFECT); - - // If the GT_MUL can be altogether folded away, we should do that. - - if ((op1->AsCast()->CastOp()->OperKind() & op2->AsCast()->CastOp()->OperKind() & GTK_CONST) && - opts.OptEnabled(CLFLG_CONSTANTFOLD)) - { - tree->AsOp()->gtOp1 = op1 = gtFoldExprConst(op1); - tree->AsOp()->gtOp2 = op2 = gtFoldExprConst(op2); - noway_assert(op1->OperKind() & op2->OperKind() & GTK_CONST); - tree = gtFoldExprConst(tree); - noway_assert(tree->OperIsConst()); - return tree; - } - - tree->gtFlags |= GTF_MUL_64RSLT; - - // If op1 and op2 are unsigned casts, we need to do an unsigned mult - tree->gtFlags |= (op1->gtFlags & GTF_UNSIGNED); - - // Insert GT_NOP nodes for the cast operands so that they do not get folded - // And propagate the new flags. We don't want to CSE the casts because - // codegen expects GTF_MUL_64RSLT muls to have a certain layout. - - if (op1->AsCast()->CastOp()->OperGet() != GT_NOP) - { - op1->AsOp()->gtOp1 = gtNewOperNode(GT_NOP, TYP_INT, op1->AsCast()->CastOp()); - op1->gtFlags &= ~GTF_ALL_EFFECT; - op1->gtFlags |= (op1->AsCast()->CastOp()->gtFlags & GTF_ALL_EFFECT); - } - - if (op2->AsCast()->CastOp()->OperGet() != GT_NOP) - { - op2->AsOp()->gtOp1 = gtNewOperNode(GT_NOP, TYP_INT, op2->AsCast()->CastOp()); - op2->gtFlags &= ~GTF_ALL_EFFECT; - op2->gtFlags |= (op2->AsCast()->CastOp()->gtFlags & GTF_ALL_EFFECT); - } - - op1->gtFlags |= GTF_DONT_CSE; - op2->gtFlags |= GTF_DONT_CSE; - - tree->gtFlags &= ~GTF_ALL_EFFECT; - tree->gtFlags |= ((op1->gtFlags | op2->gtFlags) & GTF_ALL_EFFECT); + // We are seeing this node again. + // Morph only the children of casts, + // so as to avoid losing them. + assert(tree->gtIsValid64RsltMul()); + tree = fgMorphLongMul(tree->AsOp()); goto DONE_MORPHING_CHILDREN; } - else if ((tree->gtFlags & GTF_MUL_64RSLT) == 0) + + tree = fgRecognizeAndMorphLongMul(tree->AsOp()); + + if (tree->Is64RsltMul()) + { + op1 = tree->AsOp()->gtGetOp1(); + op2 = tree->AsOp()->gtGetOp2(); + + goto DONE_MORPHING_CHILDREN; + } + else { - NO_MUL_64RSLT: if (tree->gtOverflow()) - helper = (tree->gtFlags & GTF_UNSIGNED) ? CORINFO_HELP_ULMUL_OVF : CORINFO_HELP_LMUL_OVF; + helper = tree->IsUnsigned() ? CORINFO_HELP_ULMUL_OVF : CORINFO_HELP_LMUL_OVF; else helper = CORINFO_HELP_LMUL; goto USE_HELPER_FOR_ARITH; } - else - { - /* We are seeing this node again. We have decided to use - GTF_MUL_64RSLT, so leave it alone. */ - - assert(tree->gtIsValid64RsltMul()); - } } #endif // !TARGET_64BIT break; @@ -15993,6 +15915,157 @@ GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree) return tree; } +#if !defined(TARGET_64BIT) +//------------------------------------------------------------------------------ +// fgRecognizeAndMorphLongMul : Check for and morph long multiplication with 32 bit operands. +// +// Recognizes the following tree: MUL(CAST(long <- int) or CONST, CAST(long <- int) or CONST), +// where CONST must be an integer constant that fits in 32 bits. Note that if both operands are +// constants, the original tree is returned unmodified, i. e. the caller is responsible for +// folding or correct code generation (e. g. for overflow cases). May swap operands if the +// first one is a constant and the second one is not. +// +// Arguments: +// mul - GT_MUL tree to check for a long multiplication opportunity +// +// Return Value: +// The original tree unmodified if it is not eligible for long multiplication. +// Tree with GTF_MUL_64RSLT set, side effect flags propagated, and children morphed if it is. +// +GenTreeOp* Compiler::fgRecognizeAndMorphLongMul(GenTreeOp* mul) +{ + assert(mul->OperIs(GT_MUL)); + assert(mul->TypeIs(TYP_LONG)); + + GenTree* op1 = mul->gtGetOp1(); + GenTree* op2 = mul->gtGetOp2(); + + assert(op1->TypeIs(TYP_LONG) && op2->TypeIs(TYP_LONG)); + + if (!(op1->OperIs(GT_CAST) && genActualTypeIsInt(op1->AsCast()->CastOp())) && + !(op1->IsIntegralConst() && FitsIn(op1->AsIntConCommon()->IntegralValue()))) + { + return mul; + } + + if (!(op2->OperIs(GT_CAST) && genActualTypeIsInt(op2->AsCast()->CastOp())) && + !(op2->IsIntegralConst() && FitsIn(op2->AsIntConCommon()->IntegralValue()))) + { + return mul; + } + + // Let fgMorphSmpOp take care of folding. + if (op1->IsIntegralConst() && op2->IsIntegralConst()) + { + return mul; + } + + // We don't handle checked casts. + if (op1->gtOverflowEx() || op2->gtOverflowEx()) + { + return mul; + } + + // Move the constant operand to the right to make the logic below more straightfoward. + if (op2->OperIs(GT_CAST) && op1->IsIntegralConst()) + { + std::swap(op1, op2); + mul->gtOp1 = op1; + mul->gtOp2 = op2; + } + + // The operands must have the same extending behavior, since the instruction + // used to compute the result will sign/zero-extend both operands at once. + bool op1ZeroExtends = op1->IsUnsigned(); + bool op2ZeroExtends = op2->OperIs(GT_CAST) ? op2->IsUnsigned() : op2->AsIntConCommon()->IntegralValue() >= 0; + bool op2AnyExtensionIsSuitable = op2->IsIntegralConst() && op2ZeroExtends; + if ((op1ZeroExtends != op2ZeroExtends) && !op2AnyExtensionIsSuitable) + { + return mul; + } + + if (mul->gtOverflow()) + { + auto getMaxValue = [mul](GenTree* op) -> int64_t { + if (op->OperIs(GT_CAST)) + { + if (op->IsUnsigned()) + { + switch (op->AsCast()->CastOp()->TypeGet()) + { + case TYP_UBYTE: + return UINT8_MAX; + case TYP_USHORT: + return UINT16_MAX; + default: + return UINT32_MAX; + } + } + + return mul->IsUnsigned() ? static_cast(UINT64_MAX) : INT32_MIN; + } + + return op->AsIntConCommon()->IntegralValue(); + }; + + int64_t maxOp1 = getMaxValue(op1); + int64_t maxOp2 = getMaxValue(op2); + + if (CheckedOps::MulOverflows(maxOp1, maxOp2, mul->IsUnsigned())) + { + return mul; + } + + mul->ClearOverflow(); + } + + // MUL_LONG needs to do the work the casts would have done. + mul->ClearUnsigned(); + if (op1->IsUnsigned()) + { + mul->SetUnsigned(); + } + + mul->Set64RsltMul(); + + return fgMorphLongMul(mul); +} + +//------------------------------------------------------------------------------ +// fgMorphLongMul : Morphs GT_MUL nodes marked with GTF_MUL_64RSLT. +// +// Morphs *only* the operands of casts that compose the long mul to +// avoid them being folded aways. +// +// Arguments: +// mul - GT_MUL tree to morph operands of +// +// Return Value: +// The original tree, with operands morphed and flags propagated. +// +GenTreeOp* Compiler::fgMorphLongMul(GenTreeOp* mul) +{ + GenTree* op1 = mul->gtGetOp1(); + GenTree* op2 = mul->gtGetOp2(); + + // Morph the operands. We cannot allow the casts to go away, so we morph their operands directly. + op1->AsCast()->CastOp() = fgMorphTree(op1->AsCast()->CastOp()); + op1->SetAllEffectsFlags(op1->AsCast()->CastOp()); + + if (op2->OperIs(GT_CAST)) + { + op2->AsCast()->CastOp() = fgMorphTree(op2->AsCast()->CastOp()); + op2->SetAllEffectsFlags(op2->AsCast()->CastOp()); + } + + mul->SetAllEffectsFlags(op1, op2); + op1->SetDoNotCSE(); + op2->SetDoNotCSE(); + + return mul; +} +#endif // !defined(TARGET_64BIT) + /***************************************************************************** * * Transform the given tree for code generation and return an equivalent tree. diff --git a/src/coreclr/jit/vartype.h b/src/coreclr/jit/vartype.h index 35abfb53b59..d43e02a0a30 100644 --- a/src/coreclr/jit/vartype.h +++ b/src/coreclr/jit/vartype.h @@ -229,6 +229,12 @@ inline bool varTypeIsIntOrI(T vt) ); } +template +inline bool genActualTypeIsInt(T vt) +{ + return ((TypeGet(vt) >= TYP_BOOL) && (TypeGet(vt) <= TYP_UINT)); +} + template inline bool genActualTypeIsIntOrI(T vt) { diff --git a/src/tests/JIT/Methodical/int64/misc/longmul.il b/src/tests/JIT/Methodical/int64/misc/longmul.il new file mode 100644 index 00000000000..03d2a47e309 --- /dev/null +++ b/src/tests/JIT/Methodical/int64/misc/longmul.il @@ -0,0 +1,5869 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Console { auto } +.assembly extern System.Runtime { auto } +.assembly LongMulOn32BitTest { } + +.class auto LongMulOn32BitTest extends [System.Runtime]System.Object +{ + .method private hidebysig static void PrintErrorWithResult(string message, int64 result) cil managed + { + call class [System.Runtime]System.IO.TextWriter [System.Console]System.Console::get_Error() + ldarg message + ldarg result + box [System.Runtime]System.Int64 + callvirt instance void [System.Runtime]System.IO.TextWriter::WriteLine(string, object) + ret + } + + .method private hidebysig static void PrintError(string message) cil managed + { + call class [System.Runtime]System.IO.TextWriter [System.Console]System.Console::get_Error() + ldarg message + callvirt instance void [System.Runtime]System.IO.TextWriter::WriteLine(string) + ret + } + + .method private hidebysig static int32 Main() cil managed + { + .entrypoint + .locals ( int64 result ) + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_1(int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT1 + ldstr "'LongMulOn32BitTest::LongMul_1' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT1: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_1(int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT2 + ldstr "'LongMulOn32BitTest::LongMul_1' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT2: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_1(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT3 + ldstr "'LongMulOn32BitTest::LongMul_1' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT3: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_1(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT4 + ldstr "'LongMulOn32BitTest::LongMul_1' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT4: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_2(int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT5 + ldstr "'LongMulOn32BitTest::LongMul_2' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT5: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_2(int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT6 + ldstr "'LongMulOn32BitTest::LongMul_2' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT6: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_2(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT7 + ldstr "'LongMulOn32BitTest::LongMul_2' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT7: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_2(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT8 + ldstr "'LongMulOn32BitTest::LongMul_2' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT8: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_3(int32) + ldstr "'LongMulOn32BitTest::LongMul_3' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT9 + } + NEXT9: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_3(int32) + ldstr "'LongMulOn32BitTest::LongMul_3' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT10 + } + NEXT10: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_3(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT11 + ldstr "'LongMulOn32BitTest::LongMul_3' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT11: + + .try + { + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_3(int32) + ldstr "'LongMulOn32BitTest::LongMul_3' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT12 + } + NEXT12: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_4(int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT13 + ldstr "'LongMulOn32BitTest::LongMul_4' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT13: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_4(int32) + stloc result + ldloc result + ldc.i8 1 + ceq + brtrue NEXT14 + ldstr "'LongMulOn32BitTest::LongMul_4' returned: '{0}'. Expected: '1'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT14: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_4(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT15 + ldstr "'LongMulOn32BitTest::LongMul_4' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT15: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_4(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT16 + ldstr "'LongMulOn32BitTest::LongMul_4' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT16: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_5(int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT17 + ldstr "'LongMulOn32BitTest::LongMul_5' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT17: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_5(int32) + stloc result + ldloc result + ldc.i8 1 + ceq + brtrue NEXT18 + ldstr "'LongMulOn32BitTest::LongMul_5' returned: '{0}'. Expected: '1'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT18: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_5(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT19 + ldstr "'LongMulOn32BitTest::LongMul_5' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT19: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_5(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT20 + ldstr "'LongMulOn32BitTest::LongMul_5' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT20: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_6(int32) + ldstr "'LongMulOn32BitTest::LongMul_6' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT21 + } + NEXT21: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_6(int32) + ldstr "'LongMulOn32BitTest::LongMul_6' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT22 + } + NEXT22: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_6(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT23 + ldstr "'LongMulOn32BitTest::LongMul_6' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT23: + + .try + { + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_6(int32) + ldstr "'LongMulOn32BitTest::LongMul_6' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT24 + } + NEXT24: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_7(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT25 + ldstr "'LongMulOn32BitTest::LongMul_7' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT25: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_7(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT26 + ldstr "'LongMulOn32BitTest::LongMul_7' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT26: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_7(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT27 + ldstr "'LongMulOn32BitTest::LongMul_7' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT27: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_7(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT28 + ldstr "'LongMulOn32BitTest::LongMul_7' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT28: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_8(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT29 + ldstr "'LongMulOn32BitTest::LongMul_8' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT29: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_8(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT30 + ldstr "'LongMulOn32BitTest::LongMul_8' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT30: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_8(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT31 + ldstr "'LongMulOn32BitTest::LongMul_8' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT31: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_8(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT32 + ldstr "'LongMulOn32BitTest::LongMul_8' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT32: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_9(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT33 + ldstr "'LongMulOn32BitTest::LongMul_9' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT33: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_9(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT34 + ldstr "'LongMulOn32BitTest::LongMul_9' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT34: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_9(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT35 + ldstr "'LongMulOn32BitTest::LongMul_9' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT35: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_9(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT36 + ldstr "'LongMulOn32BitTest::LongMul_9' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT36: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_10(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT37 + ldstr "'LongMulOn32BitTest::LongMul_10' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT37: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_10(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT38 + ldstr "'LongMulOn32BitTest::LongMul_10' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT38: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_10(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT39 + ldstr "'LongMulOn32BitTest::LongMul_10' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT39: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_10(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT40 + ldstr "'LongMulOn32BitTest::LongMul_10' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT40: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_11(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT41 + ldstr "'LongMulOn32BitTest::LongMul_11' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT41: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_11(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT42 + ldstr "'LongMulOn32BitTest::LongMul_11' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT42: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_11(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT43 + ldstr "'LongMulOn32BitTest::LongMul_11' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT43: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_11(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT44 + ldstr "'LongMulOn32BitTest::LongMul_11' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT44: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_12(int32) + ldstr "'LongMulOn32BitTest::LongMul_12' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT45 + } + NEXT45: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_12(int32) + ldstr "'LongMulOn32BitTest::LongMul_12' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT46 + } + NEXT46: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_12(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT47 + ldstr "'LongMulOn32BitTest::LongMul_12' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT47: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_12(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT48 + ldstr "'LongMulOn32BitTest::LongMul_12' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT48: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT49 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT49: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT50 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT50: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT51 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT51: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT52 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT52: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT53 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT53: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 1 + ceq + brtrue NEXT54 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '1'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT54: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT55 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT55: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT56 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT56: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT57 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT57: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT58 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT58: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT59 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT59: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT60 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT60: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT61 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT61: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT62 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT62: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT63 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT63: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_13(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT64 + ldstr "'LongMulOn32BitTest::LongMul_13' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT64: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT65 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT65: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT66 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT66: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT67 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT67: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT68 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT68: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 2147483648 + ceq + brtrue NEXT69 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT69: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 1 + ceq + brtrue NEXT70 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '1'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT70: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT71 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT71: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT72 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT72: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT73 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT73: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT74 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT74: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT75 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT75: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT76 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT76: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT77 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT77: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT78 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT78: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT79 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT79: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_14(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT80 + ldstr "'LongMulOn32BitTest::LongMul_14' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT80: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT81 + } + NEXT81: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT82 + } + NEXT82: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT83 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT83: + + .try + { + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT84 + } + NEXT84: + + .try + { + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT85 + } + NEXT85: + + .try + { + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT86 + } + NEXT86: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT87 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT87: + + .try + { + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT88 + } + NEXT88: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT89 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT89: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT90 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT90: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT91 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT91: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT92 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT92: + + .try + { + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT93 + } + NEXT93: + + .try + { + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_15' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT94 + } + NEXT94: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT95 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT95: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_15(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT96 + ldstr "'LongMulOn32BitTest::LongMul_15' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT96: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_16(int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT97 + ldstr "'LongMulOn32BitTest::LongMul_16' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT97: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_16(int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT98 + ldstr "'LongMulOn32BitTest::LongMul_16' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT98: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_16(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT99 + ldstr "'LongMulOn32BitTest::LongMul_16' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT99: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_16(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT100 + ldstr "'LongMulOn32BitTest::LongMul_16' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT100: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_17(int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT101 + ldstr "'LongMulOn32BitTest::LongMul_17' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT101: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_17(int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT102 + ldstr "'LongMulOn32BitTest::LongMul_17' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT102: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_17(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT103 + ldstr "'LongMulOn32BitTest::LongMul_17' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT103: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_17(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT104 + ldstr "'LongMulOn32BitTest::LongMul_17' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT104: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_18(int32) + ldstr "'LongMulOn32BitTest::LongMul_18' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT105 + } + NEXT105: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_18(int32) + ldstr "'LongMulOn32BitTest::LongMul_18' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT106 + } + NEXT106: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_18(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT107 + ldstr "'LongMulOn32BitTest::LongMul_18' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT107: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_18(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT108 + ldstr "'LongMulOn32BitTest::LongMul_18' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT108: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_19(int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT109 + ldstr "'LongMulOn32BitTest::LongMul_19' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT109: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_19(int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT110 + ldstr "'LongMulOn32BitTest::LongMul_19' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT110: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_19(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT111 + ldstr "'LongMulOn32BitTest::LongMul_19' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT111: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_19(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT112 + ldstr "'LongMulOn32BitTest::LongMul_19' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT112: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_20(int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT113 + ldstr "'LongMulOn32BitTest::LongMul_20' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT113: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_20(int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT114 + ldstr "'LongMulOn32BitTest::LongMul_20' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT114: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_20(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT115 + ldstr "'LongMulOn32BitTest::LongMul_20' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT115: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_20(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT116 + ldstr "'LongMulOn32BitTest::LongMul_20' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT116: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_21(int32) + ldstr "'LongMulOn32BitTest::LongMul_21' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT117 + } + NEXT117: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_21(int32) + ldstr "'LongMulOn32BitTest::LongMul_21' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT118 + } + NEXT118: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_21(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT119 + ldstr "'LongMulOn32BitTest::LongMul_21' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT119: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_21(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT120 + ldstr "'LongMulOn32BitTest::LongMul_21' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT120: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_22(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT121 + ldstr "'LongMulOn32BitTest::LongMul_22' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT121: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_22(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT122 + ldstr "'LongMulOn32BitTest::LongMul_22' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT122: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_22(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT123 + ldstr "'LongMulOn32BitTest::LongMul_22' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT123: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_22(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT124 + ldstr "'LongMulOn32BitTest::LongMul_22' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT124: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_23(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT125 + ldstr "'LongMulOn32BitTest::LongMul_23' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT125: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_23(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT126 + ldstr "'LongMulOn32BitTest::LongMul_23' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT126: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_23(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT127 + ldstr "'LongMulOn32BitTest::LongMul_23' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT127: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_23(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT128 + ldstr "'LongMulOn32BitTest::LongMul_23' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT128: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_24(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT129 + ldstr "'LongMulOn32BitTest::LongMul_24' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT129: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_24(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT130 + ldstr "'LongMulOn32BitTest::LongMul_24' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT130: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_24(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT131 + ldstr "'LongMulOn32BitTest::LongMul_24' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT131: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_24(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT132 + ldstr "'LongMulOn32BitTest::LongMul_24' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT132: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_25(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT133 + ldstr "'LongMulOn32BitTest::LongMul_25' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT133: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_25(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT134 + ldstr "'LongMulOn32BitTest::LongMul_25' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT134: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_25(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT135 + ldstr "'LongMulOn32BitTest::LongMul_25' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT135: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_25(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT136 + ldstr "'LongMulOn32BitTest::LongMul_25' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT136: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_26(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT137 + ldstr "'LongMulOn32BitTest::LongMul_26' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT137: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_26(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT138 + ldstr "'LongMulOn32BitTest::LongMul_26' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT138: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_26(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT139 + ldstr "'LongMulOn32BitTest::LongMul_26' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT139: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_26(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT140 + ldstr "'LongMulOn32BitTest::LongMul_26' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT140: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_27(int32) + ldstr "'LongMulOn32BitTest::LongMul_27' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT141 + } + NEXT141: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_27(int32) + ldstr "'LongMulOn32BitTest::LongMul_27' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT142 + } + NEXT142: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_27(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT143 + ldstr "'LongMulOn32BitTest::LongMul_27' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT143: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_27(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT144 + ldstr "'LongMulOn32BitTest::LongMul_27' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT144: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT145 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT145: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT146 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT146: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT147 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT147: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT148 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT148: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT149 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT149: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT150 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT150: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT151 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT151: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT152 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT152: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT153 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT153: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT154 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT154: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT155 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT155: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT156 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT156: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT157 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT157: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT158 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT158: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT159 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT159: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_28(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT160 + ldstr "'LongMulOn32BitTest::LongMul_28' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT160: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT161 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT161: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT162 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT162: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT163 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT163: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT164 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT164: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT165 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT165: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT166 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT166: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT167 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT167: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT168 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT168: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT169 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT169: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT170 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT170: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT171 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT171: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT172 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT172: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT173 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT173: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT174 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT174: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT175 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT175: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_29(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT176 + ldstr "'LongMulOn32BitTest::LongMul_29' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT176: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT177 + } + NEXT177: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT178 + } + NEXT178: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT179 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT179: + + .try + { + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT180 + } + NEXT180: + + .try + { + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT181 + } + NEXT181: + + .try + { + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT182 + } + NEXT182: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT183 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT183: + + .try + { + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_30' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT184 + } + NEXT184: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT185 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT185: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT186 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT186: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT187 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT187: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT188 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT188: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT189 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT189: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT190 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT190: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT191 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT191: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_30(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT192 + ldstr "'LongMulOn32BitTest::LongMul_30' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT192: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_31(int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT193 + ldstr "'LongMulOn32BitTest::LongMul_31' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT193: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_31(int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT194 + ldstr "'LongMulOn32BitTest::LongMul_31' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT194: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_31(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT195 + ldstr "'LongMulOn32BitTest::LongMul_31' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT195: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_31(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT196 + ldstr "'LongMulOn32BitTest::LongMul_31' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT196: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_32(int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT197 + ldstr "'LongMulOn32BitTest::LongMul_32' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT197: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_32(int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT198 + ldstr "'LongMulOn32BitTest::LongMul_32' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT198: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_32(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT199 + ldstr "'LongMulOn32BitTest::LongMul_32' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT199: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_32(int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT200 + ldstr "'LongMulOn32BitTest::LongMul_32' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT200: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_33(int32) + ldstr "'LongMulOn32BitTest::LongMul_33' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT201 + } + NEXT201: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_33(int32) + ldstr "'LongMulOn32BitTest::LongMul_33' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT202 + } + NEXT202: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_33(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT203 + ldstr "'LongMulOn32BitTest::LongMul_33' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT203: + + .try + { + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_33(int32) + ldstr "'LongMulOn32BitTest::LongMul_33' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT204 + } + NEXT204: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_34(int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT205 + ldstr "'LongMulOn32BitTest::LongMul_34' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT205: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_34(int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT206 + ldstr "'LongMulOn32BitTest::LongMul_34' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT206: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_34(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT207 + ldstr "'LongMulOn32BitTest::LongMul_34' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT207: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_34(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT208 + ldstr "'LongMulOn32BitTest::LongMul_34' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT208: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_35(int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT209 + ldstr "'LongMulOn32BitTest::LongMul_35' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT209: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_35(int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT210 + ldstr "'LongMulOn32BitTest::LongMul_35' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT210: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_35(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT211 + ldstr "'LongMulOn32BitTest::LongMul_35' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT211: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_35(int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT212 + ldstr "'LongMulOn32BitTest::LongMul_35' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT212: + + .try + { + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_36(int32) + ldstr "'LongMulOn32BitTest::LongMul_36' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT213 + } + NEXT213: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_36(int32) + ldstr "'LongMulOn32BitTest::LongMul_36' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT214 + } + NEXT214: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_36(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT215 + ldstr "'LongMulOn32BitTest::LongMul_36' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT215: + + .try + { + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_36(int32) + ldstr "'LongMulOn32BitTest::LongMul_36' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT216 + } + NEXT216: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_37(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT217 + ldstr "'LongMulOn32BitTest::LongMul_37' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT217: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_37(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT218 + ldstr "'LongMulOn32BitTest::LongMul_37' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT218: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_37(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT219 + ldstr "'LongMulOn32BitTest::LongMul_37' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT219: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_37(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT220 + ldstr "'LongMulOn32BitTest::LongMul_37' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT220: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_38(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT221 + ldstr "'LongMulOn32BitTest::LongMul_38' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT221: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_38(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT222 + ldstr "'LongMulOn32BitTest::LongMul_38' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT222: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_38(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT223 + ldstr "'LongMulOn32BitTest::LongMul_38' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT223: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_38(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT224 + ldstr "'LongMulOn32BitTest::LongMul_38' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT224: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_39(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT225 + ldstr "'LongMulOn32BitTest::LongMul_39' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT225: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_39(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT226 + ldstr "'LongMulOn32BitTest::LongMul_39' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT226: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_39(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT227 + ldstr "'LongMulOn32BitTest::LongMul_39' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT227: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_39(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT228 + ldstr "'LongMulOn32BitTest::LongMul_39' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT228: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_40(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT229 + ldstr "'LongMulOn32BitTest::LongMul_40' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT229: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_40(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT230 + ldstr "'LongMulOn32BitTest::LongMul_40' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT230: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_40(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT231 + ldstr "'LongMulOn32BitTest::LongMul_40' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT231: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_40(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT232 + ldstr "'LongMulOn32BitTest::LongMul_40' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT232: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_41(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT233 + ldstr "'LongMulOn32BitTest::LongMul_41' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT233: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_41(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT234 + ldstr "'LongMulOn32BitTest::LongMul_41' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT234: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_41(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT235 + ldstr "'LongMulOn32BitTest::LongMul_41' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT235: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_41(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT236 + ldstr "'LongMulOn32BitTest::LongMul_41' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT236: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_42(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT237 + ldstr "'LongMulOn32BitTest::LongMul_42' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT237: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_42(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT238 + ldstr "'LongMulOn32BitTest::LongMul_42' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT238: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_42(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT239 + ldstr "'LongMulOn32BitTest::LongMul_42' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT239: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_42(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT240 + ldstr "'LongMulOn32BitTest::LongMul_42' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT240: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT241 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT241: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT242 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT242: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT243 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT243: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT244 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT244: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT245 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT245: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT246 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT246: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT247 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT247: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT248 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT248: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT249 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT249: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT250 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT250: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT251 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT251: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT252 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT252: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT253 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT253: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT254 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT254: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT255 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT255: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_43(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT256 + ldstr "'LongMulOn32BitTest::LongMul_43' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT256: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686018427387904 + ceq + brtrue NEXT257 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT257: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483648 + ceq + brtrue NEXT258 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-2147483648'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT258: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT259 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT259: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT260 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT260: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -9223372034707292160 + ceq + brtrue NEXT261 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT261: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -4294967295 + ceq + brtrue NEXT262 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-4294967295'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT262: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT263 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT263: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT264 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT264: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT265 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT265: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT266 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT266: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT267 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT267: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT268 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT268: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -4611686016279904256 + ceq + brtrue NEXT269 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT269: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 -2147483647 + ceq + brtrue NEXT270 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '-2147483647'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT270: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT271 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT271: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_44(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT272 + ldstr "'LongMulOn32BitTest::LongMul_44' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT272: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT273 + } + NEXT273: + + .try + { + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT274 + } + NEXT274: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT275 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT275: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT276 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT276: + + .try + { + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT277 + } + NEXT277: + + .try + { + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT278 + } + NEXT278: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT279 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT279: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT280 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT280: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT281 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT281: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT282 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT282: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT283 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT283: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT284 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT284: + + .try + { + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT285 + } + NEXT285: + + .try + { + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_45' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT286 + } + NEXT286: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT287 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT287: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_45(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT288 + ldstr "'LongMulOn32BitTest::LongMul_45' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT288: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_46(int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT289 + ldstr "'LongMulOn32BitTest::LongMul_46' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT289: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_46(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT290 + ldstr "'LongMulOn32BitTest::LongMul_46' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT290: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_46(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT291 + ldstr "'LongMulOn32BitTest::LongMul_46' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT291: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_46(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT292 + ldstr "'LongMulOn32BitTest::LongMul_46' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT292: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_47(int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT293 + ldstr "'LongMulOn32BitTest::LongMul_47' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT293: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_47(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT294 + ldstr "'LongMulOn32BitTest::LongMul_47' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT294: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_47(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT295 + ldstr "'LongMulOn32BitTest::LongMul_47' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT295: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_47(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT296 + ldstr "'LongMulOn32BitTest::LongMul_47' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT296: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_48(int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT297 + ldstr "'LongMulOn32BitTest::LongMul_48' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT297: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_48(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT298 + ldstr "'LongMulOn32BitTest::LongMul_48' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT298: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_48(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT299 + ldstr "'LongMulOn32BitTest::LongMul_48' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT299: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_48(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT300 + ldstr "'LongMulOn32BitTest::LongMul_48' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT300: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_49(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT301 + ldstr "'LongMulOn32BitTest::LongMul_49' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT301: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_49(int32) + stloc result + ldloc result + ldc.i8 -8589934591 + ceq + brtrue NEXT302 + ldstr "'LongMulOn32BitTest::LongMul_49' returned: '{0}'. Expected: '-8589934591'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT302: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_49(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT303 + ldstr "'LongMulOn32BitTest::LongMul_49' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT303: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_49(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT304 + ldstr "'LongMulOn32BitTest::LongMul_49' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT304: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_50(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT305 + ldstr "'LongMulOn32BitTest::LongMul_50' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT305: + + .try + { + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_50(int32) + ldstr "'LongMulOn32BitTest::LongMul_50' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT306 + } + NEXT306: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_50(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT307 + ldstr "'LongMulOn32BitTest::LongMul_50' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT307: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_50(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT308 + ldstr "'LongMulOn32BitTest::LongMul_50' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT308: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_51(int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT309 + ldstr "'LongMulOn32BitTest::LongMul_51' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT309: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_51(int32) + stloc result + ldloc result + ldc.i8 -8589934591 + ceq + brtrue NEXT310 + ldstr "'LongMulOn32BitTest::LongMul_51' returned: '{0}'. Expected: '-8589934591'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT310: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_51(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT311 + ldstr "'LongMulOn32BitTest::LongMul_51' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT311: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_51(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT312 + ldstr "'LongMulOn32BitTest::LongMul_51' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT312: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_52(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT313 + ldstr "'LongMulOn32BitTest::LongMul_52' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT313: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_52(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT314 + ldstr "'LongMulOn32BitTest::LongMul_52' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT314: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_52(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT315 + ldstr "'LongMulOn32BitTest::LongMul_52' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT315: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_52(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT316 + ldstr "'LongMulOn32BitTest::LongMul_52' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT316: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_53(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT317 + ldstr "'LongMulOn32BitTest::LongMul_53' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT317: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_53(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT318 + ldstr "'LongMulOn32BitTest::LongMul_53' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT318: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_53(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT319 + ldstr "'LongMulOn32BitTest::LongMul_53' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT319: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_53(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT320 + ldstr "'LongMulOn32BitTest::LongMul_53' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT320: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_54(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT321 + ldstr "'LongMulOn32BitTest::LongMul_54' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT321: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_54(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT322 + ldstr "'LongMulOn32BitTest::LongMul_54' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT322: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_54(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT323 + ldstr "'LongMulOn32BitTest::LongMul_54' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT323: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_54(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT324 + ldstr "'LongMulOn32BitTest::LongMul_54' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT324: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_55(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT325 + ldstr "'LongMulOn32BitTest::LongMul_55' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT325: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_55(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT326 + ldstr "'LongMulOn32BitTest::LongMul_55' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT326: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_55(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT327 + ldstr "'LongMulOn32BitTest::LongMul_55' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT327: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_55(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT328 + ldstr "'LongMulOn32BitTest::LongMul_55' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT328: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_56(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT329 + ldstr "'LongMulOn32BitTest::LongMul_56' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT329: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_56(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT330 + ldstr "'LongMulOn32BitTest::LongMul_56' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT330: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_56(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT331 + ldstr "'LongMulOn32BitTest::LongMul_56' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT331: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_56(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT332 + ldstr "'LongMulOn32BitTest::LongMul_56' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT332: + + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_57(int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT333 + ldstr "'LongMulOn32BitTest::LongMul_57' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT333: + + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_57(int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT334 + ldstr "'LongMulOn32BitTest::LongMul_57' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT334: + + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_57(int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT335 + ldstr "'LongMulOn32BitTest::LongMul_57' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT335: + + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_57(int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT336 + ldstr "'LongMulOn32BitTest::LongMul_57' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT336: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT337 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT337: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT338 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT338: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT339 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT339: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT340 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT340: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT341 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT341: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 -8589934591 + ceq + brtrue NEXT342 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '-8589934591'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT342: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT343 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT343: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT344 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT344: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT345 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT345: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT346 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT346: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT347 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT347: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT348 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT348: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT349 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT349: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT350 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT350: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT351 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT351: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_58(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT352 + ldstr "'LongMulOn32BitTest::LongMul_58' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT352: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT353 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT353: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT354 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT354: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT355 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT355: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT356 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT356: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT357 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT357: + + .try + { + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + ldstr "'LongMulOn32BitTest::LongMul_59' failed to throw OverflowException" + call void LongMulOn32BitTest::PrintError(string) + leave FAIL + } + catch [System.Runtime]System.OverflowException + { + leave NEXT358 + } + NEXT358: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT359 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT359: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT360 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT360: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT361 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT361: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT362 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT362: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT363 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT363: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT364 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT364: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT365 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT365: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT366 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT366: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT367 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT367: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_59(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT368 + ldstr "'LongMulOn32BitTest::LongMul_59' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT368: + + ldc.i4 -2147483648 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 4611686018427387904 + ceq + brtrue NEXT369 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '4611686018427387904'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT369: + + ldc.i4 -2147483648 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT370 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT370: + + ldc.i4 -2147483648 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT371 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT371: + + ldc.i4 -2147483648 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT372 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT372: + + ldc.i4 -1 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 9223372034707292160 + ceq + brtrue NEXT373 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '9223372034707292160'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT373: + + ldc.i4 -1 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 -8589934591 + ceq + brtrue NEXT374 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '-8589934591'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT374: + + ldc.i4 -1 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT375 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT375: + + ldc.i4 -1 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT376 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT376: + + ldc.i4 0 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT377 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT377: + + ldc.i4 0 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT378 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT378: + + ldc.i4 0 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT379 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT379: + + ldc.i4 0 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT380 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT380: + + ldc.i4 2147483647 + ldc.i4 -2147483648 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 4611686016279904256 + ceq + brtrue NEXT381 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '4611686016279904256'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT381: + + ldc.i4 2147483647 + ldc.i4 -1 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 9223372030412324865 + ceq + brtrue NEXT382 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '9223372030412324865'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT382: + + ldc.i4 2147483647 + ldc.i4 0 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 0 + ceq + brtrue NEXT383 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '0'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT383: + + ldc.i4 2147483647 + ldc.i4 2147483647 + call int64 LongMulOn32BitTest::LongMul_60(int32, int32) + stloc result + ldloc result + ldc.i8 4611686014132420609 + ceq + brtrue NEXT384 + ldstr "'LongMulOn32BitTest::LongMul_60' returned: '{0}'. Expected: '4611686014132420609'" + ldloc result + call void LongMulOn32BitTest::PrintErrorWithResult(string, int64) + br FAIL + NEXT384: + + ldstr "SUCCESS" + call void [System.Console]System.Console::WriteLine(string) + ldc.i4 100 + ret +FAIL: + ldstr "FAILED" + call void LongMulOn32BitTest::PrintError(string) + ldc.i4 1 + ret + } + + .method private hidebysig static int64 LongMul_1(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_2(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_3(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_4(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_5(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_6(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_7(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_8(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_9(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_10(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_11(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_12(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_13(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_14(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_15(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_16(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_17(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_18(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -2147483648 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_19(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_20(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_21(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 -1 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_22(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_23(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_24(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 0 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_25(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_26(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_27(int32 left) cil managed noinlining + { + ldarg left + conv.i8 + ldc.i4 2147483647 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_28(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_29(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_30(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.i8 + ldarg right + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_31(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_32(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_33(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_34(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_35(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_36(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_37(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_38(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_39(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_40(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_41(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_42(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_43(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.i8 + mul + ret + } + + .method private hidebysig static int64 LongMul_44(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.i8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_45(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.i8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_46(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_47(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_48(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -2147483648 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_49(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_50(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_51(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 -1 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_52(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_53(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_54(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 0 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_55(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_56(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_57(int32 left) cil managed noinlining + { + ldarg left + conv.u8 + ldc.i4 2147483647 + conv.u8 + mul.ovf.un + ret + } + + .method private hidebysig static int64 LongMul_58(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.u8 + mul + ret + } + + .method private hidebysig static int64 LongMul_59(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.u8 + mul.ovf + ret + } + + .method private hidebysig static int64 LongMul_60(int32 left, int32 right) cil managed noinlining + { + ldarg left + conv.u8 + ldarg right + conv.u8 + mul.ovf.un + ret + } +} diff --git a/src/tests/JIT/Methodical/int64/misc/longmul.ilproj b/src/tests/JIT/Methodical/int64/misc/longmul.ilproj new file mode 100644 index 00000000000..2d20f0d42db --- /dev/null +++ b/src/tests/JIT/Methodical/int64/misc/longmul.ilproj @@ -0,0 +1,12 @@ + + + Exe + 0 + + + PdbOnly + + + + + diff --git a/src/tests/JIT/Methodical/int64/misc/longmul_gen.csx b/src/tests/JIT/Methodical/int64/misc/longmul_gen.csx new file mode 100644 index 00000000000..5dbdbb43ba5 --- /dev/null +++ b/src/tests/JIT/Methodical/int64/misc/longmul_gen.csx @@ -0,0 +1,324 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file contains the code that generates "longmul.il", on stdout. +// It can be executed via the global "dotnet-script" tool (dotnet tool install -g dotnet-script). + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using static TestsGen; + +var testName = "LongMulOn32BitTest"; + +OutLicenseHeader(); +Out(".assembly extern System.Console { auto }"); +Out(".assembly extern System.Runtime { auto }"); +Out($".assembly {testName} {{ }}"); +Out(); + +OpenScope($".class auto {testName} extends [System.Runtime]System.Object"); + +var inputConsts = new int[] +{ + int.MinValue, + -1, + 0, + int.MaxValue +}; + +var inputValues = new List(); +foreach (var inputConst in inputConsts) +{ + inputValues.Add($"ldc.i4 {inputConst}"); +} +inputValues.Add("ldarg"); + +var casts = new[] { "conv.i8", "conv.u8" }; +var inputs = new List(); +foreach (var cast in casts) +{ + foreach (var value in inputValues) + { + inputs.Add(new InputKind { Cast = cast, Value = value }); + } +} + +var muls = new HashSet(); +foreach (var left in inputs) +{ + foreach (var right in inputs) + { + // Don't include folded cases to keep the size of the test down. + if (left.IsConst && right.IsConst) + { + break; + } + + var leftInput = left; + var rightInput = right; + if (left.IsArg) + { + leftInput = new InputKind { Cast = left.Cast, Value = "ldarg left" }; + } + if (right.IsArg) + { + rightInput = new InputKind { Cast = right.Cast, Value = "ldarg right" }; + } + + var index = muls.Count; + var mul = new LongMul { Left = leftInput, Right = rightInput }; + + muls.Add(new LongMul { Left = mul.Left, Right = mul.Right, MethodName = $"LongMul_{++index}" }); + muls.Add(new LongMul { Left = mul.Left, Right = mul.Right, MethodName = $"LongMul_{++index}", IsOverflow = true }); + muls.Add(new LongMul { Left = mul.Left, Right = mul.Right, MethodName = $"LongMul_{++index}", IsOverflow = true, IsUnsigned = true }); + } +} + +OpenScope(".method private hidebysig static void PrintErrorWithResult(string message, int64 result) cil managed"); +Out("call class [System.Runtime]System.IO.TextWriter [System.Console]System.Console::get_Error()"); +Out("ldarg message"); +Out("ldarg result"); +Out("box [System.Runtime]System.Int64"); +Out("callvirt instance void [System.Runtime]System.IO.TextWriter::WriteLine(string, object)"); +Out("ret"); +CloseScope(); + +Out(); +OpenScope(".method private hidebysig static void PrintError(string message) cil managed"); +Out($"call class [System.Runtime]System.IO.TextWriter [System.Console]System.Console::get_Error()"); +Out($"ldarg message"); +Out($"callvirt instance void [System.Runtime]System.IO.TextWriter::WriteLine(string)"); +Out("ret"); +CloseScope(); + +void Print(string msg) +{ + Out($@"ldstr ""{msg}"""); + Out($"call void [System.Console]System.Console::WriteLine(string)"); +} + +void PrintError(string msg) +{ + Out($@"ldstr ""{msg}"""); + Out($"call void {testName}::PrintError(string)"); +} + +void PrintErrorWithResult(string fmt) +{ + Out($@"ldstr ""{fmt}"""); + Out("ldloc result"); + Out($"call void {testName}::PrintErrorWithResult(string, int64)"); +} + +Out(); +OpenScope(".method private hidebysig static int32 Main() cil managed"); +Out(".entrypoint"); +Out(".locals ( int64 result )"); + +var nextBranchIndex = 0; +foreach (var mul in muls) +{ + var leftValues = mul.Left.IsConst ? new[] { mul.Left.Const } : inputConsts; + var rightValues = mul.Right.IsConst ? new[] { mul.Right.Const } : inputConsts; + var scenarios = new List<(bool EmitLeft, bool EmitRight, int LeftValue, int RightValue, long? Expected)>(); + + foreach (var leftValue in leftValues) + { + foreach (var rightValue in rightValues) + { + var longLeftValue = mul.Left.SignExtends ? SignExtend(leftValue) : ZeroExtend(leftValue); + var longRightValue = mul.Right.SignExtends ? SignExtend(rightValue) : ZeroExtend(rightValue); + + long? expected = longLeftValue * longRightValue; + var emitLeft = mul.Left.IsArg; + var emitRight = mul.Right.IsArg; + + if (mul.IsOverflow) + { + bool overflow = mul.IsUnsigned ? + (ulong)expected != new BigInteger((ulong)longLeftValue) * new BigInteger((ulong)longRightValue) : + expected != new BigInteger(longLeftValue) * new BigInteger(longRightValue); + + if (overflow) + { + expected = null; + } + } + + scenarios.Add((emitLeft, emitRight, leftValue, rightValue, expected)); + } + } + + foreach (var (emitLeft, emitRight, leftValue, rightValue, expected) in scenarios) + { + Out(); + + var expectOverflow = expected is null; + if (expectOverflow) + { + OpenScope(".try"); + } + + if (emitLeft) + { + Out($"ldc.i4 {leftValue}"); + } + if (emitRight) + { + Out($"ldc.i4 {rightValue}"); + } + var fullMethodName = $"{testName}::{mul.MethodName}"; + Out($"call int64 {fullMethodName}({string.Join(", ", Enumerable.Repeat("int32", mul.ArgCount))})"); + + if (expectOverflow) + { + PrintError($"'{fullMethodName}' failed to throw OverflowException"); + Out("leave FAIL"); + CloseScope(); + OpenScope("catch [System.Runtime]System.OverflowException"); + Out($"leave NEXT{++nextBranchIndex}"); + CloseScope(); + } + else + { + Out("stloc result"); + Out("ldloc result"); + Out($"ldc.i8 {expected}"); + Out($"ceq"); + Out($"brtrue NEXT{++nextBranchIndex}"); + PrintErrorWithResult($"'{fullMethodName}' returned: '{{0}}'. Expected: '{expected}'"); + Out("br FAIL"); + } + + Out($"NEXT{nextBranchIndex}:", $"{TestsGen.LastIndent}"); + } +} + +Out(); +Print("SUCCESS"); +Out("ldc.i4 100"); +Out("ret"); + +Out("FAIL:", ""); +PrintError("FAILED"); +Out("ldc.i4 1"); +Out("ret"); + +CloseScope(); + +foreach (var mul in muls) +{ + var parameters = new List(); + if (mul.Left.IsArg) + { + parameters.Add("int32 left"); + } + if (mul.Right.IsArg) + { + parameters.Add("int32 right"); + } + + Out(); + OpenScope($".method private hidebysig static int64 {mul.MethodName}({string.Join(", ", parameters)}) cil managed noinlining"); + Out(mul.Left.Value); + Out(mul.Left.Cast); + Out(mul.Right.Value); + Out(mul.Right.Cast); + Out(mul.Instruction()); + Out("ret"); + CloseScope(); +} + +CloseScope(); + +[MethodImpl(MethodImplOptions.NoInlining)] +static long SignExtend(int value) => value; + +[MethodImpl(MethodImplOptions.NoInlining)] +static long ZeroExtend(int value) => (uint)value; + +public sealed class LongMul +{ + public InputKind Left { get; set; } + public InputKind Right { get; set; } + public bool IsOverflow { get; set; } + public bool IsUnsigned { get; set; } + + public string MethodName { get; set; } + + public int ArgCount => (Left.IsArg, Right.IsArg) switch + { + (true, true) => 2, + (false, false) => 0, + _ => 1 + }; + + public string Instruction() => $"mul{(IsOverflow ? ".ovf" : "")}{(IsUnsigned ? ".un" : "")}"; + + public override int GetHashCode() => HashCode.Combine(Left, Right, IsOverflow, IsUnsigned); + + public bool Equals(LongMul other) => + other != null && + Left.Equals(other.Left) && + Right.Equals(other.Right) && + IsOverflow == other.IsOverflow && + IsUnsigned == other.IsUnsigned; +} + +public sealed class InputKind : IEquatable +{ + public string Value { get; set; } + public string Cast { get; set; } + + public bool SignExtends => Cast.Contains("i8"); + public bool IsArg => Value.Contains("ldarg"); + public bool IsConst => Value.Contains("ldc"); + + public int Const => int.Parse(Value.Split()[1]); + + public override bool Equals(object obj) => Equals(obj as InputKind); + public bool Equals(InputKind other) => other != null && Value == other.Value && Cast == other.Cast; + public override int GetHashCode() => HashCode.Combine(Value, Cast); +} + +public static class TestsGen +{ + public static string IndentBase { get; set; } = " "; + public static string Indent { get; private set; } + public static string LastIndent => Indent.Substring(0, Indent.Length - IndentBase.Length); + + public static void OpenScope(string header) + { + Out(header); + Out("{"); + Indent += IndentBase; + } + + public static void CloseScope() + { + Indent = Indent.Substring(0, Indent.Length - IndentBase.Length); + Out("}"); + } + + public static void Out(string line = "", string indent = null) + { + if (indent is null) + { + indent = Indent; + } + + line = string.IsNullOrWhiteSpace(line) ? "" : indent + line; + + Console.WriteLine(line); + } + + public static void OutLicenseHeader() + { + Out("// Licensed to the .NET Foundation under one or more agreements."); + Out("// The .NET Foundation licenses this file to you under the MIT license."); + Out(); + } +} diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 06867ff7ee8..bac25dd4863 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1017,6 +1017,9 @@ https://github.com/dotnet/runtime/issues/51323 + + https://github.com/dotnet/runtime/issues/51323 + Mono does not define out of range fp to int conversions From 419506b13f45ebfd4d5459accde27a20c5482f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emek=20Vysok=C3=BD?= Date: Fri, 25 Jun 2021 09:07:51 +0200 Subject: [PATCH 116/926] [main] Update dependencies from 9 repositories (#54543) --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 201 +++++++++--------- eng/Versions.props | 88 ++++---- eng/common/generate-locproject.ps1 | 13 +- .../templates/job/source-index-stage1.yml | 4 + global.json | 10 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 20 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 26 +-- src/libraries/pkg/test/testPackages.proj | 2 +- src/tests/Common/CLRTest.Execute.Bash.targets | 2 +- .../Common/CLRTest.Execute.Batch.targets | 2 +- .../Coreclr.TestWrapper/MobileAppHandler.cs | 36 +--- 15 files changed, 210 insertions(+), 202 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 02e0543e98b..14ccd5e0cfd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21314.1", + "version": "1.0.0-prerelease.21324.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9c44cabfca8..f47e2fe1e06 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,219 +1,218 @@ - + https://github.com/dotnet/icu - d7db669b70f4dd67ec001c192f9809c218cab88b + 59588c1257a842089d0b7df3bad1cdd69ac720e1 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 - + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - 4a2b475948d498b89fedef7cf890883f49bc1ea3 + 36b148348ee8312f6369c0c56b0d0fe07deec603 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 0612b036e67746930105231b605c4df9ac6ed47e + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - a76e596b96a1b9b4bc7a213f9a8335bcd9189b67 + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - af5c238556e204583b129cc8f5c7338f84dc2c40 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/mono/linker - caeaf2a3fb3f636805fdd4881df4f9a539fff8f6 + c739a81ba553b00df1cb2f5b9974deae996b757a - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 - + https://github.com/dotnet/xharness - d6f8a4ad30908fb210390380eae97264e4fbe8ce + 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 - + https://github.com/dotnet/arcade - 85a65ea1fca1d0867f699fed44d191358270bf6a + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 4e5bea15eb5a9c8cf9142195b1c9c78437a5b27f + f291c7f87a563f29ff2a9af7378495769d97389c https://github.com/dotnet/emsdk 617928847d1e11458527b8bbafb5577982291847 - + https://github.com/dotnet/hotreload-utils - 25b814e010cd4796cedfbcce72a274c26928f496 + 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index 3b887fae24a..da6691e1fb6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,28 +50,28 @@ 3.10.0-2.final 6.0.0-rc1.21320.2 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 2.5.1-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 - 6.0.0-beta.21311.3 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 2.5.1-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 3.1.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 5.0.0 4.3.0 @@ -105,27 +105,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.6.21314.1 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 4.5.4 4.5.0 - 6.0.0-preview.6.21314.1 + 6.0.0-preview.7.21321.2 6.0.0-beta.21314.1 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21307.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 - 1.0.0-prerelease.21313.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -149,9 +149,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21314.1 - 1.0.0-prerelease.21314.1 - 1.0.1-alpha.0.21311.1 + 1.0.0-prerelease.21324.2 + 1.0.0-prerelease.21324.2 + 1.0.1-alpha.0.21314.1 2.4.1 2.4.2 1.3.0 @@ -162,19 +162,19 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21310.3 + 6.0.100-preview.6.21317.4 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.6.21307.1 + 6.0.0-preview.7.21315.3 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 - 11.1.0-alpha.1.21308.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 6.0.0-preview.7.21323.1 $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index de348a2e225..25e97ac0077 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -25,8 +25,15 @@ Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to # Template files $jsonFiles = @() -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern -$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$jsonTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\.+\.en\.json" } # .NET templating pattern +$jsonTemplateFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.json" # matches '[filename].[langcode].json + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).json" + $jsonFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern $xlfFiles = @() @@ -44,7 +51,7 @@ $langXlfFiles | ForEach-Object { $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru } -$locFiles = $jsonFiles + $xlfFiles +$locFiles = $jsonFiles + $jsonWinformsTemplateFiles + $xlfFiles $locJson = @{ Projects = @( diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index 6e8aa9f7f21..b58d42364b9 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -7,9 +7,13 @@ parameters: binlogPath: artifacts/log/Debug/Build.binlog pool: vmImage: vs2017-win2016 + condition: '' + dependsOn: '' jobs: - job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} variables: - name: SourceIndexPackageVersion value: ${{ parameters.sourceIndexPackageVersion }} diff --git a/global.json b/global.json index df8f7ead637..433797e8116 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21311.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21311.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.6.21314.1" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" } } diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 533fc1cd0ed..0504df00e76 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:Interop.Odbc.SQLSetConnectAttrW(System.Data.Odbc.OdbcConnectionHandle,System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) + M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) ILLink diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 2e52a33c8a9..5386e4bd294 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,25 @@ ILLink IL2050 member - M:System.Data.Common.UnsafeNativeMethods.GetErrorInfo(System.Int32,System.Data.Common.UnsafeNativeMethods.IErrorInfo@) + M:System.Data.OleDb.DBPropSet.SetLastErrorInfo(System.Data.OleDb.OleDbHResult) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbConnection.ProcessResults(System.Data.OleDb.OleDbHResult,System.Data.OleDb.OleDbConnection,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillClose(System.Boolean,System.Object) + + + ILLink + IL2050 + member + M:System.Data.OleDb.OleDbDataAdapter.FillFromADODB(System.Object,System.Object,System.String,System.Boolean) ILLink diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml index ae9db1185dd..7572cc7074a 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.AccountManagement.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml index 0e87e736269..ca9681a8e8f 100644 --- a/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.DirectoryServices/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2050 member - M:System.DirectoryServices.Interop.UnsafeNativeMethods.IntADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) + M:System.DirectoryServices.Interop.UnsafeNativeMethods.ADsOpenObject(System.String,System.String,System.String,System.Int32,System.Guid@,System.Object@) ILLink diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index d5734aab1a2..3f367cccd24 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -5,67 +5,61 @@ ILLink IL2050 member - M:System.Drawing.Icon.OleCreatePictureIndirect(System.Drawing.Icon.PICTDESC,System.Guid@,System.Boolean) + M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Icon.Save(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipCreateMetafileFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream,System.IntPtr) + M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(Interop.Ole32.IStream,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.RectangleF@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStream(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.IntPtr,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) + M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) ILLink IL2050 member - M:System.Drawing.SafeNativeMethods.Gdip.GdipRecordMetafileStreamI(Interop.Ole32.IStream,System.IntPtr,System.Drawing.Imaging.EmfType,System.Drawing.Rectangle@,System.Drawing.Imaging.MetafileFrameUnit,System.String,System.IntPtr@) - - - ILLink - IL2050 - member - M:System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(System.Runtime.InteropServices.HandleRef,Interop.Ole32.IStream,System.Guid@,System.Runtime.InteropServices.HandleRef) + M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 03492c30ee0..83ad6233b64 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -75,7 +75,7 @@ - + diff --git a/src/tests/Common/CLRTest.Execute.Bash.targets b/src/tests/Common/CLRTest.Execute.Bash.targets index cb28cbfac5e..d44f83d4292 100644 --- a/src/tests/Common/CLRTest.Execute.Bash.targets +++ b/src/tests/Common/CLRTest.Execute.Bash.targets @@ -361,7 +361,7 @@ else HARNESS_RUNNER="xharness" fi -$__Command $HARNESS_RUNNER android run --instrumentation="net.dot.MonoRunner" --package-name="net.dot.$__Category" --app="$__TestBinaryBase/$__Category.apk" --output-directory="$__OutputDir" --arg=entrypoint:libname=$(MsBuildProjectName).dll --expected-exit-code=100 -v +$__Command $HARNESS_RUNNER android run --instrumentation="net.dot.MonoRunner" --package-name="net.dot.$__Category" --output-directory="$__OutputDir" --arg=entrypoint:libname=$(MsBuildProjectName).dll --expected-exit-code=100 -v CLRTestExitCode=$? # Exist code of xharness is zero when tests finished successfully diff --git a/src/tests/Common/CLRTest.Execute.Batch.targets b/src/tests/Common/CLRTest.Execute.Batch.targets index bdebae3acda..24081762e9f 100644 --- a/src/tests/Common/CLRTest.Execute.Batch.targets +++ b/src/tests/Common/CLRTest.Execute.Batch.targets @@ -329,7 +329,7 @@ IF NOT "%XHARNESS_CLI_PATH%"=="" ( set HARNESS_RUNNER=xharness ) -%__Command% %HARNESS_RUNNER% android run --instrumentation="net.dot.MonoRunner" --package-name="net.dot.%__Category%" --app="%__TestBinaryBase%\%__Category%.apk" --output-directory="%__OutputDir%" --arg=entrypoint:libname=$(MsBuildProjectName).dll --expected-exit-code=100 -v +%__Command% %HARNESS_RUNNER% android run --instrumentation="net.dot.MonoRunner" --package-name="net.dot.%__Category%" --output-directory="%__OutputDir%" --arg=entrypoint:libname=$(MsBuildProjectName).dll --expected-exit-code=100 -v set CLRTestExitCode=!ERRORLEVEL! set CLRTestExpectedExitCode=0 ]]> diff --git a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs index b712246c6f3..b594455f107 100644 --- a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs +++ b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs @@ -24,41 +24,27 @@ namespace CoreclrTestLib string outputFile = Path.Combine(reportBase, action, $"{category}_{action}.output.txt"); string errorFile = Path.Combine(reportBase, action, $"{category}_{action}.error.txt"); string dotnetCmd_raw = System.Environment.GetEnvironmentVariable("__TestDotNetCmd"); - string dotnetCmd; string xharnessCmd_raw = System.Environment.GetEnvironmentVariable("XHARNESS_CLI_PATH"); - string xharnessCmd; - string cmdStr; - string appExtension; int timeout = 600000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI - if(String.IsNullOrEmpty(dotnetCmd_raw)) + string dotnetCmd = string.IsNullOrEmpty(dotnetCmd_raw) ? "dotnet" : dotnetCmd_raw; + string xharnessCmd = string.IsNullOrEmpty(xharnessCmd_raw) ? "xharness" : $"exec {xharnessCmd_raw}"; + string appExtension = platform == "android" ? "apk" : "app"; + string cmdStr = $"{dotnetCmd} {xharnessCmd} {platform} {action} --output-directory={reportBase}/{action}"; + + if (action == "install") { - dotnetCmd = "dotnet"; + cmdStr += $" --app={testBinaryBase}/{category}.{appExtension}"; } - else + else if (platform != "android") { - dotnetCmd = dotnetCmd_raw; + cmdStr += $" --app=net.dot.{category}"; } - if(String.IsNullOrEmpty(xharnessCmd_raw)) + if (platform == "android") { - xharnessCmd = "xharness"; + cmdStr += $" --package-name=net.dot.{category}"; } - else - { - xharnessCmd = $"exec {xharnessCmd_raw}"; - } - - if(platform == "android") - { - appExtension = "apk"; - } - else - { - appExtension = "app"; - } - - cmdStr = $"{dotnetCmd} {xharnessCmd} {platform} {action} --package-name=net.dot.{category} --app={testBinaryBase}/{category}.{appExtension} --output-directory={reportBase}/install"; Directory.CreateDirectory(Path.Combine(reportBase, action)); var outputStream = new FileStream(outputFile, FileMode.Create); From e9f101c4c7a5d6fc4d96fd6edd0419dbc954a9f1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 25 Jun 2021 03:35:29 -0400 Subject: [PATCH 117/926] Override more Stream members on System.IO.Compression streams (#54518) --- .../DeflateManaged/DeflateManagedStream.cs | 31 ++-- .../DeflateManaged/InflaterManaged.cs | 4 +- .../DeflateManaged/OutputWindow.cs | 12 +- .../System/IO/Compression/ZipArchiveEntry.cs | 43 ++++++ .../System/IO/Compression/ZipCustomStreams.cs | 143 ++++++++++++++++++ 5 files changed, 211 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs index f24e15d2f3b..c3a27659397 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -97,19 +97,22 @@ namespace System.IO.Compression public override int Read(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); + return Read(new Span(buffer, offset, count)); + } + + public override int Read(Span buffer) + { EnsureNotDisposed(); - int bytesRead; - int currentOffset = offset; - int remainingCount = count; + int initialLength = buffer.Length; + int bytesRead; while (true) { - bytesRead = _inflater.Inflate(buffer, currentOffset, remainingCount); - currentOffset += bytesRead; - remainingCount -= bytesRead; + bytesRead = _inflater.Inflate(buffer); + buffer = buffer.Slice(bytesRead); - if (remainingCount == 0) + if (buffer.Length == 0) { break; } @@ -136,7 +139,13 @@ namespace System.IO.Compression _inflater.SetInput(_buffer, 0, bytes); } - return count - remainingCount; + return initialLength - buffer.Length; + } + + public override int ReadByte() + { + byte b = default; + return Read(MemoryMarshal.CreateSpan(ref b, 1)) == 1 ? b : -1; } private void EnsureNotDisposed() @@ -169,7 +178,7 @@ namespace System.IO.Compression try { // Try to read decompressed data in output buffer - int bytesRead = _inflater.Inflate(buffer); + int bytesRead = _inflater.Inflate(buffer.Span); if (bytesRead != 0) { // If decompression output buffer is not empty, return immediately. @@ -224,7 +233,7 @@ namespace System.IO.Compression // Feed the data from base stream into decompression engine _inflater.SetInput(_buffer, 0, bytesRead); - bytesRead = _inflater.Inflate(buffer); + bytesRead = _inflater.Inflate(buffer.Span); if (bytesRead == 0 && !_inflater.Finished()) { diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs index 8372368cfe3..78b93e3aa32 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs @@ -95,7 +95,7 @@ namespace System.IO.Compression public int AvailableOutput => _output.AvailableBytes; - public int Inflate(Memory bytes) + public int Inflate(Span bytes) { // copy bytes from output to outputbytes if we have available bytes // if buffer is not filled up. keep decoding until no input are available @@ -139,7 +139,7 @@ namespace System.IO.Compression return count; } - public int Inflate(byte[] bytes, int offset, int length) => Inflate(bytes.AsMemory(offset, length)); + public int Inflate(byte[] bytes, int offset, int length) => Inflate(bytes.AsSpan(offset, length)); //Each block of compressed data begins with 3 header bits // containing the following data: diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputWindow.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputWindow.cs index 2cdcf7d66fb..471e8667ec0 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputWindow.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputWindow.cs @@ -118,7 +118,7 @@ namespace System.IO.Compression public int AvailableBytes => _bytesUsed; /// Copy the decompressed bytes to output buffer. - public int CopyTo(Memory output) + public int CopyTo(Span output) { int copy_end; @@ -140,19 +140,13 @@ namespace System.IO.Compression { // this means we need to copy two parts separately // copy the taillen bytes from the end of the output window - _window.AsSpan(WindowSize - tailLen, tailLen).CopyTo(output.Span); + _window.AsSpan(WindowSize - tailLen, tailLen).CopyTo(output); output = output.Slice(tailLen, copy_end); } - _window.AsSpan(copy_end - output.Length, output.Length).CopyTo(output.Span); + _window.AsSpan(copy_end - output.Length, output.Length).CopyTo(output); _bytesUsed -= copied; Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have"); return copied; } - - /// Copy the decompressed bytes to output array. - public int CopyTo(byte[] output, int offset, int length) - { - return CopyTo(output.AsMemory(offset, length)); - } } } diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index 4f0072b507c..0481ee770df 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -4,7 +4,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace System.IO.Compression { @@ -1222,6 +1225,38 @@ namespace System.IO.Compression _position += source.Length; } + public override void WriteByte(byte value) => + Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + Debug.Assert(CanWrite); + + return !buffer.IsEmpty ? + Core(buffer, cancellationToken) : + default; + + async ValueTask Core(ReadOnlyMemory buffer, CancellationToken cancellationToken) + { + if (!_everWritten) + { + _everWritten = true; + // write local header, we are good to go + _usedZip64inLH = _entry.WriteLocalFileHeader(isEmptyFile: false); + } + + await _crcSizeStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); + _position += buffer.Length; + } + } + public override void Flush() { ThrowIfDisposed(); @@ -1230,6 +1265,14 @@ namespace System.IO.Compression _crcSizeStream.Flush(); } + public override Task FlushAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + Debug.Assert(CanWrite); + + return _crcSizeStream.FlushAsync(cancellationToken); + } + protected override void Dispose(bool disposing) { if (disposing && !_isDisposed) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs index dbc4f56d83e..7f43043785c 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace System.IO.Compression { @@ -95,6 +98,38 @@ namespace System.IO.Compression return _baseStream.Read(buffer, offset, count); } + public override int Read(Span buffer) + { + ThrowIfDisposed(); + ThrowIfCantRead(); + + return _baseStream.Read(buffer); + } + + public override int ReadByte() + { + ThrowIfDisposed(); + ThrowIfCantRead(); + + return _baseStream.ReadByte(); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ThrowIfDisposed(); + ThrowIfCantRead(); + + return _baseStream.ReadAsync(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + ThrowIfCantRead(); + + return _baseStream.ReadAsync(buffer, cancellationToken); + } + public override long Seek(long offset, SeekOrigin origin) { ThrowIfDisposed(); @@ -128,6 +163,30 @@ namespace System.IO.Compression _baseStream.Write(source); } + public override void WriteByte(byte value) + { + ThrowIfDisposed(); + ThrowIfCantWrite(); + + _baseStream.WriteByte(value); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ThrowIfDisposed(); + ThrowIfCantWrite(); + + return _baseStream.WriteAsync(buffer, offset, count, cancellationToken); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + ThrowIfCantWrite(); + + return _baseStream.WriteAsync(buffer, cancellationToken); + } + public override void Flush() { ThrowIfDisposed(); @@ -136,6 +195,14 @@ namespace System.IO.Compression _baseStream.Flush(); } + public override Task FlushAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + ThrowIfCantWrite(); + + return _baseStream.FlushAsync(cancellationToken); + } + protected override void Dispose(bool disposing) { if (disposing && !_isDisposed) @@ -259,6 +326,43 @@ namespace System.IO.Compression return ret; } + public override int ReadByte() + { + byte b = default; + return Read(MemoryMarshal.CreateSpan(ref b, 1)) == 1 ? b : -1; + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + ThrowIfCantRead(); + return Core(buffer, cancellationToken); + + async ValueTask Core(Memory buffer, CancellationToken cancellationToken) + { + if (_superStream.Position != _positionInSuperStream) + { + _superStream.Seek(_positionInSuperStream, SeekOrigin.Begin); + } + + if (_positionInSuperStream > _endInSuperStream - buffer.Length) + { + buffer = buffer.Slice(0, (int)(_endInSuperStream - _positionInSuperStream)); + } + + int ret = await _superStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + + _positionInSuperStream += ret; + return ret; + } + } + public override long Seek(long offset, SeekOrigin origin) { ThrowIfDisposed(); @@ -437,6 +541,39 @@ namespace System.IO.Compression _position += source.Length; } + public override void WriteByte(byte value) => + Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + Debug.Assert(CanWrite); + + return !buffer.IsEmpty ? + Core(buffer, cancellationToken) : + default; + + async ValueTask Core(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (!_everWritten) + { + _initialPosition = _baseBaseStream.Position; + _everWritten = true; + } + + _checksum = Crc32Helper.UpdateCrc32(_checksum, buffer.Span); + + await _baseStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); + _position += buffer.Length; + } + } + public override void Flush() { ThrowIfDisposed(); @@ -447,6 +584,12 @@ namespace System.IO.Compression _baseStream.Flush(); } + public override Task FlushAsync(CancellationToken cancellationToken) + { + ThrowIfDisposed(); + return _baseStream.FlushAsync(cancellationToken); + } + protected override void Dispose(bool disposing) { if (disposing && !_isDisposed) From c139d00640485e8c231c54d1573dc02d7c9e8daf Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 25 Jun 2021 10:56:28 +0200 Subject: [PATCH 118/926] [wasm] browser http response stream could be seekable (#54603) - browser http response stream could be seekable - test WebAssemblyEnableStreamingResponse --- .../System/Net/Http/HttpClientHandlerTest.cs | 151 ++++++++++++------ 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 912f356c5d6..44dfc1a5ca3 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -937,7 +937,6 @@ namespace System.Net.Http.Functional.Tests [InlineData(true)] [InlineData(false)] [InlineData(null)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -960,6 +959,11 @@ namespace System.Net.Http.Functional.Tests await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; + if (PlatformDetection.IsBrowser) + { + request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); + } + using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) using (HttpResponseMessage response = await client.SendAsync(TestAsync, request, CancellationToken.None)) { @@ -974,17 +978,20 @@ namespace System.Net.Http.Functional.Tests // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK Assert.Throws(() => responseStream.Write(new Span(new byte[1]))); - Assert.Throws(() => { responseStream.WriteAsync(new Memory(new byte[1])); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new Memory(new byte[1]))); #endif - Assert.Throws(() => { responseStream.WriteAsync(new byte[1], 0, 1); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new byte[1], 0, 1)); Assert.Throws(() => responseStream.WriteByte(1)); // Invalid arguments @@ -998,11 +1005,14 @@ namespace System.Net.Http.Functional.Tests Assert.Throws(() => { responseStream.CopyToAsync(Stream.Null, -1, default); }); Assert.Throws(() => { responseStream.CopyToAsync(nonWritableStream, 100, default); }); Assert.Throws(() => { responseStream.CopyToAsync(disposedStream, 100, default); }); - Assert.Throws(() => responseStream.Read(null, 0, 100)); - Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); - Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + if (PlatformDetection.IsNotBrowser) + { + Assert.Throws(() => responseStream.Read(null, 0, 100)); + Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); + Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + } Assert.Throws(() => responseStream.BeginRead(null, 0, 100, null, null)); Assert.Throws(() => responseStream.BeginRead(new byte[1], -1, 1, null, null)); Assert.ThrowsAny(() => responseStream.BeginRead(new byte[1], 2, 1, null, null)); @@ -1018,62 +1028,97 @@ namespace System.Net.Http.Functional.Tests // Various forms of reading var buffer = new byte[1]; - Assert.Equal('h', responseStream.ReadByte()); + if (PlatformDetection.IsBrowser) + { + Assert.Equal('h', await responseStream.ReadByteAsync()); + Assert.Equal('e', await responseStream.ReadByteAsync()); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer)); + Assert.Equal((byte)'o', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); + + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); + } + else + { + Assert.Equal('h', responseStream.ReadByte()); + Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal((byte)'e', buffer[0]); - Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); - Assert.Equal((byte)'e', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal((byte)'l', buffer[0]); - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, responseStream.Read(new Span(buffer))); + Assert.Equal(1, responseStream.Read(new Span(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'o', buffer[0]); + Assert.Equal((byte)'o', buffer[0]); - Assert.Equal(1, responseStream.Read(buffer, 0, 1)); - Assert.Equal((byte)' ', buffer[0]); + Assert.Equal(1, responseStream.Read(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); - // Doing any of these 0-byte reads causes the connection to fail. - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); #endif - Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(Span.Empty)); + Assert.Equal(0, responseStream.Read(Span.Empty)); #endif - Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); + Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); - // And copying - var ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); - // Read and copy again once we've exhausted all data - ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - responseStream.CopyTo(ms); - Assert.Equal(0, ms.Length); - Assert.Equal(-1, responseStream.ReadByte()); - Assert.Equal(0, responseStream.Read(buffer, 0, 1)); + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + responseStream.CopyTo(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(-1, responseStream.ReadByte()); + Assert.Equal(0, responseStream.Read(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(new Span(buffer))); + Assert.Equal(0, responseStream.Read(new Span(buffer))); #endif - Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); #endif - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + } } } }, async server => @@ -1103,7 +1148,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1123,14 +1167,17 @@ namespace System.Net.Http.Functional.Tests // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.False(responseStream.CanSeek); + Assert.Equal(PlatformDetection.IsBrowser, responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK From fc4a42746d9cf65ad3db4074207069ab9c168bc9 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 25 Jun 2021 16:14:54 +0200 Subject: [PATCH 119/926] Revert "[wasm] browser http response stream could be seekable (#54603)" (#54742) This reverts commit c139d00640485e8c231c54d1573dc02d7c9e8daf. --- .../System/Net/Http/HttpClientHandlerTest.cs | 151 ++++++------------ 1 file changed, 52 insertions(+), 99 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 44dfc1a5ca3..912f356c5d6 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -937,6 +937,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(true)] [InlineData(false)] [InlineData(null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -959,11 +960,6 @@ namespace System.Net.Http.Functional.Tests await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; - if (PlatformDetection.IsBrowser) - { - request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); - } - using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) using (HttpResponseMessage response = await client.SendAsync(TestAsync, request, CancellationToken.None)) { @@ -978,20 +974,17 @@ namespace System.Net.Http.Functional.Tests // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - if (!responseStream.CanSeek) - { - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); - } + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK Assert.Throws(() => responseStream.Write(new Span(new byte[1]))); - await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new Memory(new byte[1]))); + Assert.Throws(() => { responseStream.WriteAsync(new Memory(new byte[1])); }); #endif - await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new byte[1], 0, 1)); + Assert.Throws(() => { responseStream.WriteAsync(new byte[1], 0, 1); }); Assert.Throws(() => responseStream.WriteByte(1)); // Invalid arguments @@ -1005,14 +998,11 @@ namespace System.Net.Http.Functional.Tests Assert.Throws(() => { responseStream.CopyToAsync(Stream.Null, -1, default); }); Assert.Throws(() => { responseStream.CopyToAsync(nonWritableStream, 100, default); }); Assert.Throws(() => { responseStream.CopyToAsync(disposedStream, 100, default); }); - if (PlatformDetection.IsNotBrowser) - { - Assert.Throws(() => responseStream.Read(null, 0, 100)); - Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); - Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); - } + Assert.Throws(() => responseStream.Read(null, 0, 100)); + Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); + Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); Assert.Throws(() => responseStream.BeginRead(null, 0, 100, null, null)); Assert.Throws(() => responseStream.BeginRead(new byte[1], -1, 1, null, null)); Assert.ThrowsAny(() => responseStream.BeginRead(new byte[1], 2, 1, null, null)); @@ -1028,97 +1018,62 @@ namespace System.Net.Http.Functional.Tests // Various forms of reading var buffer = new byte[1]; - if (PlatformDetection.IsBrowser) - { - Assert.Equal('h', await responseStream.ReadByteAsync()); - Assert.Equal('e', await responseStream.ReadByteAsync()); - Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); - Assert.Equal((byte)'l', buffer[0]); - - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)'l', buffer[0]); - - Assert.Equal(1, await responseStream.ReadAsync(buffer)); - Assert.Equal((byte)'o', buffer[0]); - - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)' ', buffer[0]); - - // Doing any of these 0-byte reads causes the connection to fail. - Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); - Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); - - // And copying - var ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); - - // Read and copy again once we've exhausted all data - ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal(0, ms.Length); - Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); - } - else - { - Assert.Equal('h', responseStream.ReadByte()); - Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); - Assert.Equal((byte)'e', buffer[0]); + Assert.Equal('h', responseStream.ReadByte()); + Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal((byte)'e', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal((byte)'l', buffer[0]); - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, responseStream.Read(new Span(buffer))); + Assert.Equal(1, responseStream.Read(new Span(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'o', buffer[0]); + Assert.Equal((byte)'o', buffer[0]); - Assert.Equal(1, responseStream.Read(buffer, 0, 1)); - Assert.Equal((byte)' ', buffer[0]); + Assert.Equal(1, responseStream.Read(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); - // Doing any of these 0-byte reads causes the connection to fail. - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); #endif - Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(Span.Empty)); + Assert.Equal(0, responseStream.Read(Span.Empty)); #endif - Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); + Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); - // And copying - var ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); - // Read and copy again once we've exhausted all data - ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - responseStream.CopyTo(ms); - Assert.Equal(0, ms.Length); - Assert.Equal(-1, responseStream.ReadByte()); - Assert.Equal(0, responseStream.Read(buffer, 0, 1)); + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + responseStream.CopyTo(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(-1, responseStream.ReadByte()); + Assert.Equal(0, responseStream.Read(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(new Span(buffer))); + Assert.Equal(0, responseStream.Read(new Span(buffer))); #endif - Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); #endif - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); - } + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); } } }, async server => @@ -1148,6 +1103,7 @@ namespace System.Net.Http.Functional.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1167,17 +1123,14 @@ namespace System.Net.Http.Functional.Tests // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.Equal(PlatformDetection.IsBrowser, responseStream.CanSeek); + Assert.False(responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - if (!responseStream.CanSeek) - { - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); - } + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK From de5dd0d370cda957cf36b687a066e34411bc4f7c Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 25 Jun 2021 12:55:02 -0400 Subject: [PATCH 120/926] [mono][jit] Refactor the generic sharing code a bit. NFC. (#54705) * [jit] Refactor the emit_rgctx code a bit. * Add an enum to specify the way the rgctx is accessed from a gshared method. Use it to simplify some logic. --- src/mono/mono/metadata/marshal.h | 3 +- src/mono/mono/mini/aot-compiler.c | 6 +- src/mono/mono/mini/method-to-ir.c | 128 ++++++++++++++++-------------- src/mono/mono/mini/mini-llvm.c | 70 ++++++++-------- src/mono/mono/mini/mini.c | 19 +++++ src/mono/mono/mini/mini.h | 13 +++ 6 files changed, 137 insertions(+), 102 deletions(-) diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index a30c67d289b..6ce138c1170 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -209,7 +209,8 @@ typedef enum { AOT_INIT_METHOD = 0, AOT_INIT_METHOD_GSHARED_MRGCTX = 1, AOT_INIT_METHOD_GSHARED_THIS = 2, - AOT_INIT_METHOD_GSHARED_VTABLE = 3 + AOT_INIT_METHOD_GSHARED_VTABLE = 3, + AOT_INIT_METHOD_NUM = 4 } MonoAotInitSubtype; typedef struct { diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 3df6d4b8b14..1574acda348 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -4284,10 +4284,8 @@ add_jit_icall_wrapper (MonoAotCompile *acfg, MonoJitICallInfo *callinfo) static void add_lazy_init_wrappers (MonoAotCompile *acfg) { - add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD)); - add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_MRGCTX)); - add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_VTABLE)); - add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_THIS)); + for (int i = 0; i < AOT_INIT_METHOD_NUM; ++i) + add_method (acfg, mono_marshal_get_aot_init_wrapper ((MonoAotInitSubtype)i)); } #endif diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 8ca12378c2d..d011f3b6da0 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2482,29 +2482,23 @@ context_used_is_mrgctx (MonoCompile *cfg, int context_used) /* * emit_get_rgctx: * - * Emit IR to return either the this pointer for instance method, - * or the mrgctx for static methods. + * Emit IR to return either the vtable or the mrgctx. */ static MonoInst* emit_get_rgctx (MonoCompile *cfg, int context_used) { - MonoInst *this_ins = NULL; MonoMethod *method = cfg->method; g_assert (cfg->gshared); - if (!(method->flags & METHOD_ATTRIBUTE_STATIC) && - !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) && - !m_class_is_valuetype (method->klass)) - EMIT_NEW_VARLOAD (cfg, this_ins, cfg->this_arg, mono_get_object_type ()); - + /* Data whose context contains method type vars is stored in the mrgctx */ if (context_used_is_mrgctx (cfg, context_used)) { MonoInst *mrgctx_loc, *mrgctx_var; - if (!mini_method_is_default_method (method)) { - g_assert (!this_ins); + g_assert (cfg->rgctx_access == MONO_RGCTX_ACCESS_MRGCTX); + + if (!mini_method_is_default_method (method)) g_assert (method->is_inflated && mono_method_get_context (method)->method_inst); - } if (cfg->llvm_only) { mrgctx_var = mono_get_mrgctx_var (cfg); @@ -2515,10 +2509,34 @@ emit_get_rgctx (MonoCompile *cfg, int context_used) EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0); } return mrgctx_var; - } else if (method->flags & METHOD_ATTRIBUTE_STATIC || m_class_is_valuetype (method->klass)) { + } + + /* + * The rest of the entries are stored in vtable->runtime_generic_context so + * have to return a vtable. + */ + if (cfg->rgctx_access == MONO_RGCTX_ACCESS_MRGCTX) { + MonoInst *mrgctx_loc, *mrgctx_var, *vtable_var; + int vtable_reg; + + /* We are passed an mrgctx, return mrgctx->class_vtable */ + + if (cfg->llvm_only) { + mrgctx_var = mono_get_mrgctx_var (cfg); + } else { + mrgctx_loc = mono_get_mrgctx_var (cfg); + g_assert (mrgctx_loc->flags & MONO_INST_VOLATILE); + EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0); + } + + vtable_reg = alloc_preg (cfg); + EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable)); + vtable_var->type = STACK_PTR; + return vtable_var; + } else if (cfg->rgctx_access == MONO_RGCTX_ACCESS_VTABLE) { MonoInst *vtable_loc, *vtable_var; - g_assert (!this_ins); + /* We are passed a vtable, return it */ if (cfg->llvm_only) { vtable_var = mono_get_vtable_var (cfg); @@ -2527,20 +2545,15 @@ emit_get_rgctx (MonoCompile *cfg, int context_used) g_assert (vtable_loc->flags & MONO_INST_VOLATILE); EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0); } - - if (method->is_inflated && mono_method_get_context (method)->method_inst) { - MonoInst *mrgctx_var = vtable_var; - int vtable_reg; - - vtable_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable)); - vtable_var->type = STACK_PTR; - } - + vtable_var->type = STACK_PTR; return vtable_var; } else { - MonoInst *ins; + MonoInst *ins, *this_ins; int vtable_reg; + + /* We are passed a this pointer, return this->vtable */ + + EMIT_NEW_VARLOAD (cfg, this_ins, cfg->this_arg, mono_get_object_type ()); vtable_reg = alloc_preg (cfg); EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this_ins->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable)); @@ -2675,12 +2688,13 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt /* * emit_rgctx_fetch: * - * Emit IR to load the value of the rgctx entry ENTRY from the rgctx - * given by RGCTX. + * Emit IR to load the value of the rgctx entry ENTRY from the rgctx. */ static MonoInst* -emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry) +emit_rgctx_fetch (MonoCompile *cfg, int context_used, MonoJumpInfoRgctxEntry *entry) { + MonoInst *rgctx = emit_get_rgctx (cfg, context_used); + if (cfg->llvm_only) return emit_rgctx_fetch_inline (cfg, rgctx, entry); else @@ -2704,15 +2718,23 @@ mini_emit_get_rgctx_klass (MonoCompile *cfg, int context_used, case MONO_RGCTX_INFO_KLASS: EMIT_NEW_CLASSCONST (cfg, ins, klass); return ins; + case MONO_RGCTX_INFO_VTABLE: { + MonoVTable *vtable = mono_class_vtable_checked (klass, cfg->error); + CHECK_CFG_ERROR; + EMIT_NEW_VTABLECONST (cfg, ins, vtable); + return ins; + } default: g_assert_not_reached (); } } MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_CLASS, klass, rgctx_type); - MonoInst *rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); + +mono_error_exit: + return NULL; } static MonoInst* @@ -2720,9 +2742,8 @@ emit_get_rgctx_sig (MonoCompile *cfg, int context_used, MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type) { MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_SIGNATURE, sig, rgctx_type); - MonoInst *rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } static MonoInst* @@ -2731,16 +2752,14 @@ emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used, { MonoJumpInfoGSharedVtCall *call_info; MonoJumpInfoRgctxEntry *entry; - MonoInst *rgctx; call_info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall)); call_info->sig = sig; call_info->method = cmethod; entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_GSHAREDVT_CALL, call_info, rgctx_type); - rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } /* @@ -2754,16 +2773,14 @@ emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used, { MonoJumpInfoVirtMethod *info; MonoJumpInfoRgctxEntry *entry; - MonoInst *rgctx; info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod)); info->klass = klass; info->method = virt_method; entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type); - rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } static MonoInst* @@ -2771,12 +2788,10 @@ emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoGSharedVtMethodInfo *info) { MonoJumpInfoRgctxEntry *entry; - MonoInst *rgctx; entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_GSHAREDVT_METHOD, info, MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO); - rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } /* @@ -2810,9 +2825,8 @@ emit_get_rgctx_method (MonoCompile *cfg, int context_used, } } else { MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type); - MonoInst *rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } } @@ -2821,9 +2835,8 @@ emit_get_rgctx_field (MonoCompile *cfg, int context_used, MonoClassField *field, MonoRgctxInfoType rgctx_type) { MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_FIELD, field, rgctx_type); - MonoInst *rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } MonoInst* @@ -3522,7 +3535,6 @@ emit_get_rgctx_dele_tramp (MonoCompile *cfg, int context_used, { MonoDelegateClassMethodPair *info; MonoJumpInfoRgctxEntry *entry; - MonoInst *rgctx; info = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair)); info->klass = klass; @@ -3530,12 +3542,10 @@ emit_get_rgctx_dele_tramp (MonoCompile *cfg, int context_used, info->is_virtual = _virtual; entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, info, rgctx_type); - rgctx = emit_get_rgctx (cfg, context_used); - return emit_rgctx_fetch (cfg, rgctx, entry); + return emit_rgctx_fetch (cfg, context_used, entry); } - /* * Returns NULL and set the cfg exception on error. */ @@ -5563,25 +5573,23 @@ handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fs { MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins; - if (m_class_is_valuetype (cmethod->klass) && mono_class_generic_sharing_enabled (cmethod->klass) && - mono_method_is_generic_sharable (cmethod, TRUE)) { - if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) { + if (mono_class_generic_sharing_enabled (cmethod->klass) && mono_method_is_generic_sharable (cmethod, TRUE)) { + MonoRgctxAccess access = mini_get_rgctx_access_for_method (cmethod); + + if (access == MONO_RGCTX_ACCESS_MRGCTX) { mono_class_vtable_checked (cmethod->klass, cfg->error); CHECK_CFG_ERROR; CHECK_TYPELOAD (cmethod->klass); vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); + } else if (access == MONO_RGCTX_ACCESS_VTABLE) { + vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, + cmethod->klass, MONO_RGCTX_INFO_VTABLE); + CHECK_CFG_ERROR; + CHECK_TYPELOAD (cmethod->klass); } else { - if (context_used) { - vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, - cmethod->klass, MONO_RGCTX_INFO_VTABLE); - } else { - MonoVTable *vtable = mono_class_vtable_checked (cmethod->klass, cfg->error); - CHECK_CFG_ERROR; - CHECK_TYPELOAD (cmethod->klass); - EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable); - } + g_assert (access == MONO_RGCTX_ACCESS_THIS); } } diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 7d563842c16..fd88f9cf163 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -89,7 +89,7 @@ typedef struct { MonoAotFileInfo aot_info; const char *eh_frame_symbol; LLVMValueRef get_method, get_unbox_tramp, init_aotconst_func; - LLVMValueRef init_method, init_method_gshared_mrgctx, init_method_gshared_this, init_method_gshared_vtable; + LLVMValueRef init_methods [AOT_INIT_METHOD_NUM]; LLVMValueRef code_start, code_end; LLVMValueRef inited_var; LLVMValueRef unbox_tramp_indexes; @@ -3393,27 +3393,14 @@ emit_icall_cold_wrapper (MonoLLVMModule *module, LLVMModuleRef lmodule, MonoJitI static void emit_init_funcs (MonoLLVMModule *module) { - module->init_method = emit_init_func (module, AOT_INIT_METHOD); - module->init_method_gshared_mrgctx = emit_init_func (module, AOT_INIT_METHOD_GSHARED_MRGCTX); - module->init_method_gshared_this = emit_init_func (module, AOT_INIT_METHOD_GSHARED_THIS); - module->init_method_gshared_vtable = emit_init_func (module, AOT_INIT_METHOD_GSHARED_VTABLE); + for (int i = 0; i < AOT_INIT_METHOD_NUM; ++i) + module->init_methods [i] = emit_init_func (module, i); } static LLVMValueRef get_init_func (MonoLLVMModule *module, MonoAotInitSubtype subtype) { - switch (subtype) { - case AOT_INIT_METHOD: - return module->init_method; - case AOT_INIT_METHOD_GSHARED_MRGCTX: - return module->init_method_gshared_mrgctx; - case AOT_INIT_METHOD_GSHARED_THIS: - return module->init_method_gshared_this; - case AOT_INIT_METHOD_GSHARED_VTABLE: - return module->init_method_gshared_vtable; - default: - g_assert_not_reached (); - } + return module->init_methods [subtype]; } static void @@ -3576,11 +3563,12 @@ emit_div_check (EmitContext *ctx, LLVMBuilderRef builder, MonoBasicBlock *bb, Mo static void emit_method_init (EmitContext *ctx) { - LLVMValueRef indexes [16], args [16], callee; + LLVMValueRef indexes [16], args [16]; LLVMValueRef inited_var, cmp, call; LLVMBasicBlockRef inited_bb, notinited_bb; LLVMBuilderRef builder = ctx->builder; MonoCompile *cfg = ctx->cfg; + MonoAotInitSubtype subtype; ctx->module->max_inited_idx = MAX (ctx->module->max_inited_idx, cfg->method_index); @@ -3608,27 +3596,35 @@ emit_method_init (EmitContext *ctx) g_free (symbol); cfg->llvm_dummy_info_var = info_var; - args [0] = convert (ctx, info_var, ctx->module->ptr_type); + int nargs = 0; + args [nargs ++] = convert (ctx, info_var, ctx->module->ptr_type); - // FIXME: Cache - if (ctx->rgctx_arg && ((cfg->method->is_inflated && mono_method_get_context (cfg->method)->method_inst) || - mini_method_is_default_method (cfg->method))) { - args [1] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); - callee = ctx->module->init_method_gshared_mrgctx; - call = LLVMBuildCall (builder, callee, args, 2, ""); - } else if (ctx->rgctx_arg) { - /* A vtable is passed as the rgctx argument */ - args [1] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); - callee = ctx->module->init_method_gshared_vtable; - call = LLVMBuildCall (builder, callee, args, 2, ""); - } else if (cfg->gshared) { - args [1] = convert (ctx, ctx->this_arg, ObjRefType ()); - callee = ctx->module->init_method_gshared_this; - call = LLVMBuildCall (builder, callee, args, 2, ""); - } else { - callee = ctx->module->init_method; - call = LLVMBuildCall (builder, callee, args, 1, ""); + switch (cfg->rgctx_access) { + case MONO_RGCTX_ACCESS_MRGCTX: + if (ctx->rgctx_arg) { + args [nargs ++] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); + subtype = AOT_INIT_METHOD_GSHARED_MRGCTX; + } else { + g_assert (ctx->this_arg); + args [nargs ++] = convert (ctx, ctx->this_arg, ObjRefType ()); + subtype = AOT_INIT_METHOD_GSHARED_THIS; + } + break; + case MONO_RGCTX_ACCESS_VTABLE: + args [nargs ++] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); + subtype = AOT_INIT_METHOD_GSHARED_VTABLE; + break; + case MONO_RGCTX_ACCESS_THIS: + args [nargs ++] = convert (ctx, ctx->this_arg, ObjRefType ()); + subtype = AOT_INIT_METHOD_GSHARED_THIS; + break; + case MONO_RGCTX_ACCESS_NONE: + subtype = AOT_INIT_METHOD; + break; + default: + g_assert_not_reached (); } + call = LLVMBuildCall (builder, ctx->module->init_methods [subtype], args, nargs, ""); /* * This enables llvm to keep arguments in their original registers/ diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 76051818cf3..d03efcac914 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3062,6 +3062,23 @@ is_simd_supported (MonoCompile *cfg) return TRUE; } +/* Determine how an rgctx is passed to a method */ +MonoRgctxAccess +mini_get_rgctx_access_for_method (MonoMethod *method) +{ + /* gshared dim methods use an mrgctx */ + if (mini_method_is_default_method (method)) + return MONO_RGCTX_ACCESS_MRGCTX; + + if (mono_method_get_context (method)->method_inst) + return MONO_RGCTX_ACCESS_MRGCTX; + + if (method->flags & METHOD_ATTRIBUTE_STATIC || m_class_is_valuetype (method->klass)) + return MONO_RGCTX_ACCESS_VTABLE; + + return MONO_RGCTX_ACCESS_THIS; +} + /* * mini_method_compile: * @method: the method to compile @@ -3207,6 +3224,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts cfg->interp_entry_only = interp_entry_only; if (try_generic_shared) cfg->gshared = TRUE; + if (cfg->gshared) + cfg->rgctx_access = mini_get_rgctx_access_for_method (cfg->method); cfg->compile_llvm = try_llvm; cfg->token_info_hash = g_hash_table_new (NULL, NULL); if (cfg->compile_aot) diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 8d55a61d123..c865bf5b293 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1070,6 +1070,17 @@ typedef enum { MONO_RGCTX_INFO_CLASS_SIZEOF = 34 } MonoRgctxInfoType; +/* How an rgctx is passed to a method */ +typedef enum { + MONO_RGCTX_ACCESS_NONE = 0, + /* Loaded from this->vtable->rgctx */ + MONO_RGCTX_ACCESS_THIS = 1, + /* Loaded from an additional mrgctx argument */ + MONO_RGCTX_ACCESS_MRGCTX = 2, + /* Loaded from an additional vtable argument */ + MONO_RGCTX_ACCESS_VTABLE = 3 +} MonoRgctxAccess; + typedef struct _MonoRuntimeGenericContextInfoTemplate { MonoRgctxInfoType info_type; gpointer data; @@ -1353,6 +1364,7 @@ typedef struct { /* The current virtual register number */ guint32 next_vreg; + MonoRgctxAccess rgctx_access; MonoGenericSharingContext gsctx; MonoGenericContext *gsctx_context; @@ -2309,6 +2321,7 @@ MonoInst* mini_handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoMethod* mini_get_memcpy_method (void); MonoMethod* mini_get_memset_method (void); int mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass); +MonoRgctxAccess mini_get_rgctx_access_for_method (MonoMethod *method); CompRelation mono_opcode_to_cond (int opcode); CompType mono_opcode_to_type (int opcode, int cmp_opcode); From 3924d0308110f6365e47e784b8c7cf1e8f7efa2b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 25 Jun 2021 19:06:37 +0200 Subject: [PATCH 121/926] Fix BackoutJitData (#54711) * Fix BackoutJitData The RemoveJitData that the BackoutJitData calls requires the code header to be copied to the final location. This change fixes it. I've also found that in one of my previous changes, I've accidentally enabled jitting into a scratch buffer by default by adding the FEATURE_WXORX define unconditionally. So I am removing it in this change for non Apple Silicon, it will be replaced by a dynamic check whether W^X is enabled in the final W^X change. --- src/coreclr/vm/codeman.cpp | 6 ++++- src/coreclr/vm/jitinterface.cpp | 48 ++++++++++++++++++++------------- src/coreclr/vm/jitinterface.h | 2 ++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 349a76039cd..37220786fed 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -2685,7 +2685,11 @@ void EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, size_t reserveFo pCodeHdr = ((CodeHeader *)pCode) - 1; *pAllocatedSize = sizeof(CodeHeader) + totalSize; -#define FEATURE_WXORX + +#if defined(HOST_OSX) && defined(HOST_ARM64) +#define FEATURE_WXORX +#endif + #ifdef FEATURE_WXORX pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize]; #else diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f4b155a500a..8483c1fb1dc 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11212,25 +11212,9 @@ void CEEJitInfo::GetProfilingHandle(bool *pbHookFunction, } /*********************************************************************/ -void CEEJitInfo::BackoutJitData(EEJitManager * jitMgr) +void CEEJitInfo::WriteCodeBytes() { - CONTRACTL { - NOTHROW; - GC_TRIGGERS; - } CONTRACTL_END; - - CodeHeader* pCodeHeader = m_CodeHeader; - if (pCodeHeader) - jitMgr->RemoveJitData(pCodeHeader, m_GCinfo_len, m_EHinfo_len); -} - -/*********************************************************************/ -void CEEJitInfo::WriteCode(EEJitManager * jitMgr) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } CONTRACTL_END; + LIMITED_METHOD_CONTRACT; #ifdef USE_INDIRECT_CODEHEADER if (m_pRealCodeHeader != NULL) @@ -11246,6 +11230,34 @@ void CEEJitInfo::WriteCode(EEJitManager * jitMgr) ExecutableWriterHolder codeWriterHolder((void *)m_CodeHeader, m_codeWriteBufferSize); memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, m_codeWriteBufferSize); } +} + +/*********************************************************************/ +void CEEJitInfo::BackoutJitData(EEJitManager * jitMgr) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + // The RemoveJitData call below requires the m_CodeHeader to be valid, so we need to write + // the code bytes to the target memory location. + WriteCodeBytes(); + + CodeHeader* pCodeHeader = m_CodeHeader; + if (pCodeHeader) + jitMgr->RemoveJitData(pCodeHeader, m_GCinfo_len, m_EHinfo_len); +} + +/*********************************************************************/ +void CEEJitInfo::WriteCode(EEJitManager * jitMgr) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + WriteCodeBytes(); // Now that the code header was written to the final location, publish the code via the nibble map jitMgr->NibbleMapSet(m_pCodeHeap, m_CodeHeader->GetCodeStartAddress(), TRUE); diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 29e566a7342..ca9d03c2141 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -941,6 +941,8 @@ public: protected : + void WriteCodeBytes(); + #ifdef FEATURE_PGO // PGO data struct ComputedPgoData From 57bfe474518ab5b7cfe6bf7424a79ce3af9d6657 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 25 Jun 2021 11:10:50 -0700 Subject: [PATCH 122/926] Tweak classcompat loader to skip MethodImpls associated with static methods (#54658) --- src/coreclr/vm/classcompat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/classcompat.cpp b/src/coreclr/vm/classcompat.cpp index 3c9556a6623..1870e6f3dd0 100644 --- a/src/coreclr/vm/classcompat.cpp +++ b/src/coreclr/vm/classcompat.cpp @@ -939,7 +939,7 @@ VOID MethodTableBuilder::BuildInteropVTable_PlaceMembers( } } - if(Classification & mdcMethodImpl) + if (Classification & mdcMethodImpl) { // If this method serves as the BODY of a MethodImpl specification, then // we should iterate all the MethodImpl's for this class and see just how many // of them this method participates in as the BODY. @@ -2807,7 +2807,7 @@ VOID MethodTableBuilder::EnumerateClassMethods() // on this type so we can just compare the tok with the body token found // from the overrides. for(DWORD impls = 0; impls < bmtMethodImpl->dwNumberMethodImpls; impls++) { - if(bmtMethodImpl->rgMethodImplTokens[impls].methodBody == tok) { + if ((bmtMethodImpl->rgMethodImplTokens[impls].methodBody == tok) && !IsMdStatic(dwMemberAttrs)) { Classification |= mdcMethodImpl; break; } From 24adc911242af6b99a4fc9a6c7f234e6e4df7d53 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 25 Jun 2021 15:05:56 -0700 Subject: [PATCH 123/926] Fix thread safety of _typeRefsInCompilationModuleSet (#54760) --- .../ReadyToRunCompilationModuleGroupBase.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 08cdc64d098..4ba66a5ca9c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Threading; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Internal.TypeSystem.Interop; @@ -370,22 +371,29 @@ namespace ILCompiler if (_typeRefsInCompilationModuleSet == null) { - _typeRefsInCompilationModuleSet = new Dictionary(); - - foreach (var module in _compilationModuleSet) + lock(_compilationModuleSet) { - EcmaModule ecmaModule = (EcmaModule)module; - foreach (var typeRefHandle in ecmaModule.MetadataReader.TypeReferences) + if (_typeRefsInCompilationModuleSet == null) { - try + var typeRefsInCompilationModuleSet = new Dictionary(); + + foreach (var module in _compilationModuleSet) { - TypeDesc typeFromTypeRef = ecmaModule.GetType(typeRefHandle); - if (!_typeRefsInCompilationModuleSet.ContainsKey(typeFromTypeRef)) + EcmaModule ecmaModule = (EcmaModule)module; + foreach (var typeRefHandle in ecmaModule.MetadataReader.TypeReferences) { - _typeRefsInCompilationModuleSet.Add(typeFromTypeRef, new ModuleToken(ecmaModule, typeRefHandle)); + try + { + TypeDesc typeFromTypeRef = ecmaModule.GetType(typeRefHandle); + if (!typeRefsInCompilationModuleSet.ContainsKey(typeFromTypeRef)) + { + typeRefsInCompilationModuleSet.Add(typeFromTypeRef, new ModuleToken(ecmaModule, typeRefHandle)); + } + } + catch (TypeSystemException) { } } } - catch (TypeSystemException) { } + Volatile.Write(ref _typeRefsInCompilationModuleSet, typeRefsInCompilationModuleSet); } } } From 333c4e72b83a08288db929ac56c0e336c936b655 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 25 Jun 2021 15:06:22 -0700 Subject: [PATCH 124/926] Fix handling of static virtual method implementation checking (#54710) - It turns out that GetMethodDescFromMemberDefOrRefOrSpec and FindOrCreateAssociatedMethodDesc are not safe to use on a MemberRef whent the associated MethodTable is not fully loaded. - Instead only use that feature when working with a MethodDef or a fully loaded type, and when working with a not fully loaded type, use MemberLoader::FindMethod instead. - When running the resolution algorithm for doing constraint validation, it also is not necessary to fully resolve to the exact correct MethodDesc, which as that process uses FindOrCreateAssociatedMethodDesc needs to be avoided. - The above was not evident as in many cases, the validation algorithm did not run as it was misplaced and located directly before the call to SetIsFullyLoaded. That code path is only followed if the type is able to fully load without circular dependencies. (Test case CuriouslyRecurringGenericWithUnimplementedMethod added to cover that scenario) - In addition, while investigating these issues, I realized we were lacking checks that the constraints on the impl and decl method were not checked at during type load, but that work was instead deferred to dispatch time. Along with the constraint check there was also a set of accessibility checks that had been missed that are common to all MethodImpl handling. Fix by adding tweaking the logic to share most of that code. --- src/coreclr/vm/methodtable.cpp | 84 ++++++++++++------- src/coreclr/vm/methodtable.h | 8 +- src/coreclr/vm/methodtablebuilder.cpp | 80 +++++++++++++++--- src/coreclr/vm/methodtablebuilder.h | 16 +++- src/coreclr/vm/runtimehandles.cpp | 2 +- src/coreclr/vm/typedesc.cpp | 2 +- ...RecurringGenericWithUnimplementedMethod.il | 57 +++++++++++++ ...rringGenericWithUnimplementedMethod.ilproj | 12 +++ .../MethodConstraintMismatch.il | 66 +++++++++++++++ .../MethodConstraintMismatch.ilproj | 12 +++ 10 files changed, 290 insertions(+), 49 deletions(-) create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 58e64187bc4..bb44e829905 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5658,6 +5658,22 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const } + // Validate implementation of virtual static methods on all implemented interfaces unless: + // 1) The type resides in a module where sanity checks are disabled (such as System.Private.CoreLib, or an + // R2R module with type checks disabled) + // 2) There are no virtual static methods defined on any of the interfaces implemented by this type; + // 3) The type is abstract in which case it's allowed to leave some virtual static methods unimplemented + // akin to equivalent behavior of virtual instance method overriding in abstract classes; + // 4) The type is a not the typical type definition. (The typical type is always checked) + + if (fNeedsSanityChecks && + IsTypicalTypeDefinition() && + !IsAbstract()) + { + if (HasVirtualStaticMethods()) + VerifyThatAllVirtualStaticMethodsAreImplemented(); + } + if (locals.fBailed) { // We couldn't complete security checks on some dependency because it is already being processed by one of our callers. @@ -5671,22 +5687,6 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const } else { - // Validate implementation of virtual static methods on all implemented interfaces unless: - // 1) The type resides in the system module (System.Private.CoreLib); we own this module and ensure - // its consistency by other means not requiring runtime checks; - // 2) There are no virtual static methods defined on any of the interfaces implemented by this type; - // 3) The method is abstract in which case it's allowed to leave some virtual static methods unimplemented - // akin to equivalent behavior of virtual instance method overriding in abstract classes; - // 4) The type is a shared generic in which case we generally don't have enough information to perform - // the validation. - if (!GetModule()->IsSystem() && - HasVirtualStaticMethods() && - !IsAbstract() && - !IsSharedByGenericInstantiations()) - { - VerifyThatAllVirtualStaticMethodsAreImplemented(); - } - // Finally, mark this method table as fully loaded SetIsFullyLoaded(); } @@ -9201,7 +9201,7 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA //========================================================================================== // Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD. MethodDesc * -MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates, BOOL allowVariantMatches) +MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches) { if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { @@ -9231,7 +9231,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, checkDuplicates); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -9273,7 +9273,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, checkDuplicates); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -9295,7 +9295,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -9347,13 +9347,39 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType { continue; } - MethodDesc *pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( + MethodDesc *pMethodDecl; + + if ((TypeFromToken(methodDecl) == mdtMethodDef) || pInterfaceMT->IsFullyLoaded()) + { + pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( GetModule(), methodDecl, &sigTypeContext, /* strictMetadataChecks */ FALSE, /* allowInstParam */ FALSE, /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); + } + else if (TypeFromToken(methodDecl) == mdtMemberRef) + { + LPCUTF8 szMember; + PCCOR_SIGNATURE pSig; + DWORD cSig; + + IfFailThrow(pMDInternalImport->GetNameAndSigOfMemberRef(methodDecl, &pSig, &cSig, &szMember)); + + // Do a quick name check to avoid excess use of FindMethod + if (strcmp(szMember, pInterfaceMD->GetName()) != 0) + { + continue; + } + + pMethodDecl = MemberLoader::FindMethod(pInterfaceMT, szMember, pSig, cSig, GetModule()); + } + else + { + COMPlusThrow(kTypeLoadException, E_FAIL); + } + if (pMethodDecl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9369,13 +9395,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType COMPlusThrow(kTypeLoadException, E_FAIL); } - MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( + MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMethodDef( GetModule(), methodBody, - &sigTypeContext, - /* strictMetadataChecks */ FALSE, - /* allowInstParam */ FALSE, - /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); + FALSE, + CLASS_LOAD_EXACTPARENTS); if (pMethodImpl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9388,7 +9412,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType COMPlusThrow(kTypeLoadException, E_FAIL); } - if (pInterfaceMD->HasMethodInstantiation() || pMethodImpl->HasMethodInstantiation() || HasInstantiation()) + if (!verifyImplemented) { pMethodImpl = pMethodImpl->FindOrCreateAssociatedMethodDesc( pMethodImpl, @@ -9398,11 +9422,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType /* allowInstParam */ FALSE, /* forceRemotableMethod */ FALSE, /* allowCreate */ TRUE, - /* level */ CLASS_LOAD_EXACTPARENTS); + /* level */ CLASS_LOADED); } if (pMethodImpl != nullptr) { - if (!checkDuplicates) + if (!verifyImplemented) { return pMethodImpl; } @@ -9432,7 +9456,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE, /* allowVariantMatches */ FALSE)) + !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE)) { IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 326bcce2c42..3d0ee06b37f 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2285,7 +2285,11 @@ public: // Resolve virtual static interface method pInterfaceMD on this type. - MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates = FALSE, BOOL allowVariantMatches = TRUE); + // + // Specify allowNullResult to return NULL instead of throwing if the there is no implementation + // Specify verifyImplemented to verify that there is a match, but do not actually return a final useable MethodDesc + // Specify allowVariantMatches to permit generic interface variance + MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2402,7 +2406,7 @@ public: // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index ad827323c69..9ccb3d78f86 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1186,12 +1186,29 @@ MethodTableBuilder::bmtInterfaceEntry::CreateSlotTable( CONSISTENCY_CHECK(m_pImplTable == NULL); SLOT_INDEX cSlots = (SLOT_INDEX)GetInterfaceType()->GetMethodTable()->GetNumVirtuals(); - bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlots]; + SLOT_INDEX cSlotsTotal = cSlots; + + if (GetInterfaceType()->GetMethodTable()->HasVirtualStaticMethods()) + { + MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable()); + for (; it.IsValid(); it.Next()) + { + MethodDesc *pDeclMD = it.GetDeclMethodDesc(); + if (pDeclMD->IsStatic() && pDeclMD->IsVirtual()) + { + cSlotsTotal++; + } + } + } + + bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlotsTotal]; + MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable()); for (; it.IsValid(); it.Next()) { - if (!it.IsVirtual()) + MethodDesc *pDeclMD = it.GetDeclMethodDesc(); + if (!pDeclMD->IsVirtual()) { break; } @@ -1199,8 +1216,15 @@ MethodTableBuilder::bmtInterfaceEntry::CreateSlotTable( bmtRTMethod * pCurMethod = new (pStackingAllocator) bmtRTMethod(GetInterfaceType(), it.GetDeclMethodDesc()); - CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber()); - pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + if (pDeclMD->IsStatic()) + { + pST[cSlots + m_cImplTableStatics++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + } + else + { + CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber()); + pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + } } m_pImplTable = pST; @@ -4808,16 +4832,16 @@ VOID MethodTableBuilder::TestMethodImpl( { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL); } - if (!IsMdVirtual(dwImplAttrs)) + if ((IsMdVirtual(dwImplAttrs) && IsMdStatic(dwImplAttrs)) || (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs))) { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL); } - // Virtual methods cannot be static - if (IsMdStatic(dwDeclAttrs)) + // Virtual methods on classes/valuetypes cannot be static + if (IsMdStatic(dwDeclAttrs) && !hDeclMethod.GetOwningType().IsInterface()) { BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL); } - if (IsMdStatic(dwImplAttrs)) + if ((!!IsMdStatic(dwImplAttrs)) != (!!IsMdStatic(dwDeclAttrs))) { BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL); } @@ -5421,14 +5445,14 @@ MethodTableBuilder::PlaceVirtualMethods() // that the name+signature corresponds to. Used by ProcessMethodImpls and ProcessInexactMethodImpls // Always returns the first match that it finds. Affects the ambiguities in code:#ProcessInexactMethodImpls_Ambiguities MethodTableBuilder::bmtMethodHandle -MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig) +MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods) { STANDARD_VM_CONTRACT; bmtMethodHandle declMethod; bmtInterfaceEntry::InterfaceSlotIterator slotIt = - pItfEntry->IterateInterfaceSlots(GetStackingAllocator()); + pItfEntry->IterateInterfaceSlots(GetStackingAllocator(), searchForStaticMethods); // Check for exact match for (; !slotIt.AtEnd(); slotIt.Next()) { @@ -5656,7 +5680,7 @@ MethodTableBuilder::ProcessMethodImpls() DeclaredMethodIterator it(*this); while (it.Next()) { - if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl()) + if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && bmtProp->fNoSanityChecks) { // Non-virtual methods can only be classified as methodImpl when implementing // static virtual methods. @@ -5839,7 +5863,7 @@ MethodTableBuilder::ProcessMethodImpls() } // 3. Find the matching method. - declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig); + declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, !IsMdVirtual(it.Attrs())); // Search for statics when the impl is non-virtual } else { @@ -5874,6 +5898,14 @@ MethodTableBuilder::ProcessMethodImpls() BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token()); } + if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && IsMdStatic(it.Attrs())) + { + // Non-virtual methods can only be classified as methodImpl when implementing + // static virtual methods. + ValidateStaticMethodImpl(declMethod, *it);//bmtMethodHandle(pCurImplMethod)); + continue; + } + if (bmtMetaData->rgMethodImplTokens[m].fRequiresCovariantReturnTypeChecking) { it->GetMethodDesc()->SetRequiresCovariantReturnTypeChecking(); @@ -6744,6 +6776,30 @@ MethodTableBuilder::PlaceParentDeclarationOnClass( (*pSlotIndex)++; } // MethodTableBuilder::PlaceParentDeclarationOnClass +VOID MethodTableBuilder::ValidateStaticMethodImpl( + bmtMethodHandle hDecl, + bmtMethodHandle hImpl) +{ + // While we don't want to place the static method impl declarations on the class/interface, we do + // need to validate the method constraints and signature are compatible + if (!bmtProp->fNoSanityChecks) + { + /////////////////////////////// + // Verify the signatures match + + MethodImplCompareSignatures( + hDecl, + hImpl, + FALSE /* allowCovariantReturn */, + IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL); + + /////////////////////////////// + // Validate the method impl. + + TestMethodImpl(hDecl, hImpl); + } +} + //******************************************************************************* // This will validate that all interface methods that were matched during // layout also validate against type constraints. diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index c44568a2f96..63cc9c856c1 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -1725,6 +1725,7 @@ private: : m_pType(pItfType), m_pImplTable(NULL), // Lazily created m_cImplTable(0), + m_cImplTableStatics(0), m_declScope(declScope), m_equivalenceSet(0), m_fEquivalenceSetWithMultipleEntries(false) @@ -1762,13 +1763,14 @@ private: typedef IteratorUtil::ArrayIterator InterfaceSlotIterator; + // Iterate the interface virtual methods that can be implemented. The static methods can be iterated if statics is set to true InterfaceSlotIterator IterateInterfaceSlots( - StackingAllocator * pStackingAllocator) + StackingAllocator * pStackingAllocator, bool statics = false) { WRAPPER_NO_CONTRACT; CheckCreateSlotTable(pStackingAllocator); - return InterfaceSlotIterator(m_pImplTable, m_cImplTable); + return InterfaceSlotIterator(m_pImplTable + (statics ? m_cImplTable : 0), statics ? m_cImplTableStatics : m_cImplTable); } //----------------------------------------------------------------------------------------- @@ -1850,6 +1852,7 @@ private: bmtRTType * m_pType; bmtInterfaceSlotImpl * m_pImplTable; SLOT_INDEX m_cImplTable; + SLOT_INDEX m_cImplTableStatics; InterfaceDeclarationScope m_declScope; UINT32 m_equivalenceSet; bool m_fEquivalenceSetWithMultipleEntries; @@ -2734,7 +2737,7 @@ private: // Find the decl method on a given interface entry that matches the method name+signature specified // If none is found, return a null method handle bmtMethodHandle - FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig); + FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods = false); // -------------------------------------------------------------------------------------------- // Find the decl method within the class hierarchy method name+signature specified @@ -2814,6 +2817,13 @@ private: DWORD* pSlotIndex, DWORD dwMaxSlotSize); + // -------------------------------------------------------------------------------------------- + // Validate that the methodimpl is handled correctly + VOID + ValidateStaticMethodImpl( + bmtMethodHandle hDecl, + bmtMethodHandle hImpl); + // -------------------------------------------------------------------------------------------- // This will validate that all interface methods that were matched during // layout also validate against type constraints. diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index d3c21535cc4..736f36758ee 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1190,7 +1190,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(QCall: if (pMD->IsStatic()) { - pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod(thOwnerOfMD.GetMethodTable(), pMD, /* allowNullResult */ TRUE, /* checkDuplicates*/ FALSE, /*allowVariantMatches */ TRUE); + pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod(thOwnerOfMD.GetMethodTable(), pMD, /* allowNullResult */ TRUE, /* verifyImplemented*/ FALSE, /*allowVariantMatches */ TRUE); } else { diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index 657170b3bbc..fbc6ff0170e 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -1922,7 +1922,7 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE)) + !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE)) { virtualStaticResolutionCheckFailed = true; break; diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il new file mode 100644 index 00000000000..e4a952e1c13 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Console {} +.assembly extern mscorlib {} +.assembly extern System.Runtime {} +.assembly extern TypeHierarchyCommonCs {} +.assembly TypeHierarchyTest {} +.class interface public abstract auto ansi InterfaceScenario1`1 +{ + .method public newslot virtual abstract static int32 Method() cil managed noinlining + { + } // end of method Method +} // end of class InterfaceScenario1 + +.class public auto ansi BaseScenario1`1 + extends [System.Runtime]System.Object + implements class InterfaceScenario1`1> +{ +} // end of class BaseScenario1 + +.class public auto ansi TestEntrypoint + extends [System.Runtime]System.Object +{ + .method public static class [System.Runtime]System.Type Test_Scenario1() cil managed noinlining + { + ldtoken class BaseScenario1`1 + call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle) + ret + } // end of method Test_Scenario1 + .method public static int32 Main() cil managed noinlining + { + .entrypoint + .locals init (class [System.Runtime]System.Exception V_0) + .try { + call class [System.Runtime]System.Type TestEntrypoint::Test_Scenario1() + pop + ldstr "CuriouslyRecurringGenericWithUnimplementedMethod" + ldstr "TypeLoadException" + ldstr "Did not throw exception" + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s CuriouslyRecurringGenericWithUnimplementedMethodDone + } catch [System.Runtime]System.Exception { + stloc.0 + ldstr "CuriouslyRecurringGenericWithUnimplementedMethod" + ldstr "TypeLoadException" + ldloc.0 + callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Exception::GetType() + callvirt instance string [System.Runtime]System.Reflection.MemberInfo::get_Name() + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s CuriouslyRecurringGenericWithUnimplementedMethodDone + } +CuriouslyRecurringGenericWithUnimplementedMethodDone: nop + + call int32 [TypeHierarchyCommonCs]Statics::ReportResults() + ret } // end of method Main +} // end of class TestEntrypoint diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj new file mode 100644 index 00000000000..19680b9e18b --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + Full + + + + + + diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il new file mode 100644 index 00000000000..bc957e1ff4d --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Console {} +.assembly extern mscorlib {} +.assembly extern System.Runtime {} +.assembly extern TypeHierarchyCommonCs {} +.assembly TypeHierarchyTest {} +.class interface public abstract auto ansi InterfaceScenario1 +{ + .method public newslot virtual abstract static int32 GenericMethod() cil managed noinlining + { + } // end of method Method +} // end of class InterfaceScenario1 + +.class public auto ansi BaseScenario1 + extends [System.Runtime]System.Object + implements InterfaceScenario1 +{ + .method public static int32 GenericMethod<(class InterfaceScenario1) U>() cil managed noinlining + { + .override method int32 InterfaceScenario1::GenericMethod<[1]>() + .locals init (int32 V_O) + ldloca.s 0 + initobj int32 + ldloc.0 + ret + } // end of method Method +} // end of class BaseScenario1 + +.class public auto ansi TestEntrypoint + extends [System.Runtime]System.Object +{ + .method public static class [System.Runtime]System.Type Test_Scenario1() cil managed noinlining + { + ldtoken BaseScenario1 + call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle) + ret + } // end of method Test_Scenario1 + .method public static int32 Main() cil managed noinlining + { + .entrypoint + .locals init (class [System.Runtime]System.Exception V_0) + .try { + call class [System.Runtime]System.Type TestEntrypoint::Test_Scenario1() + pop + ldstr "MethodConstraintMismatch" + ldstr "TypeLoadException" + ldstr "Did not throw exception" + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s MethodConstraintMismatchDone + } catch [System.Runtime]System.Exception { + stloc.0 + ldstr "MethodConstraintMismatch" + ldstr "TypeLoadException" + ldloc.0 + callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Exception::GetType() + callvirt instance string [System.Runtime]System.Reflection.MemberInfo::get_Name() + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s MethodConstraintMismatchDone + } +MethodConstraintMismatchDone: nop + + call int32 [TypeHierarchyCommonCs]Statics::ReportResults() + ret } // end of method Main +} // end of class TestEntrypoint diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj new file mode 100644 index 00000000000..19680b9e18b --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + Full + + + + + + From 118c530033b43d47fcabab00efce414d5e4fd19c Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 25 Jun 2021 16:09:23 -0700 Subject: [PATCH 125/926] Clean up JSON source gen APIs (#54527) * Clean up JSON source gen APIs * Address review feedback * Simplify GenerateX computation --- .../Common/JsonKnownNamingPolicy.cs | 2 +- .../JsonSerializableAttribute.cs | 13 +- .../Common/JsonSourceGenerationMode.cs | 12 +- ...> JsonSourceGenerationOptionsAttribute.cs} | 9 +- .../gen/ContextGenerationSpec.cs | 4 +- .../gen/JsonSourceGenerator.Emitter.cs | 14 +- .../gen/JsonSourceGenerator.Parser.cs | 206 ++++++++++++------ .../gen/JsonSourceGenerator.cs | 1 - .../System.Text.Json.SourceGeneration.csproj | 3 +- .../gen/TypeGenerationSpec.cs | 17 +- .../System.Text.Json/ref/System.Text.Json.cs | 11 +- .../src/System.Text.Json.csproj | 4 +- .../Serialization/JsonSerializerContext.cs | 2 +- .../JsonMetadataServices.Collections.cs | 6 +- .../Metadata/JsonMetadataServices.cs | 2 +- .../Serialization/Metadata/JsonTypeInfoOfT.cs | 2 +- .../MetadataContextTests.cs | 49 +++++ .../MixedModeContextTests.cs | 32 +-- .../SerializationContextTests.cs | 96 ++++++-- 19 files changed, 338 insertions(+), 147 deletions(-) rename src/libraries/System.Text.Json/{src/System/Text/Json/Serialization/Attributes => Common}/JsonSerializableAttribute.cs (79%) rename src/libraries/System.Text.Json/Common/{JsonSerializerOptionsAttribute.cs => JsonSourceGenerationOptionsAttribute.cs} (80%) diff --git a/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs b/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs index 99290715f31..10a4be38653 100644 --- a/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs +++ b/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs @@ -21,6 +21,6 @@ namespace System.Text.Json.Serialization /// /// Specifies that the built-in be used to convert JSON property names. /// - BuiltInCamelCase = 1 + CamelCase = 1 } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs similarity index 79% rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs rename to src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs index b223fada10c..1b689ba6cfb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs +++ b/src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if !BUILDING_SOURCE_GENERATOR using System.Text.Json.Serialization.Metadata; +#endif namespace System.Text.Json.Serialization { @@ -10,7 +12,13 @@ namespace System.Text.Json.Serialization /// when serializing and deserializing instances of the specified type and types in its object graph. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public sealed class JsonSerializableAttribute : JsonAttribute + +#if BUILDING_SOURCE_GENERATOR + internal +#else + public +#endif + sealed class JsonSerializableAttribute : JsonAttribute { /// /// Initializes a new instance of with the specified type. @@ -28,7 +36,8 @@ namespace System.Text.Json.Serialization public string? TypeInfoPropertyName { get; set; } /// - /// Determines what the source generator should generate for the type. + /// Determines what the source generator should generate for the type. If the value is , + /// then the setting specified on will be used. /// public JsonSourceGenerationMode GenerationMode { get; set; } } diff --git a/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs index b46292baaee..99785d8be52 100644 --- a/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs +++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs @@ -15,13 +15,11 @@ namespace System.Text.Json.Serialization enum JsonSourceGenerationMode { /// - /// Instructs the JSON source generator to generate serialization logic and type metadata to fallback to - /// when the run-time options are not compatible with the indicated . + /// When specified on , indicates that both type-metadata initialization logic + /// and optimized serialization logic should be generated for all types. When specified on , + /// indicates that the setting on should be used. /// - /// - /// This mode supports all features. - /// - MetadataAndSerialization = 0, + Default = 0, /// /// Instructs the JSON source generator to generate type-metadata initialization logic. @@ -32,7 +30,7 @@ namespace System.Text.Json.Serialization Metadata = 1, /// - /// Instructs the JSON source generator to generate serialization logic. + /// Instructs the JSON source generator to generate optimized serialization logic. /// /// /// This mode supports only a subset of features. diff --git a/src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs similarity index 80% rename from src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs rename to src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs index a1b4d099e32..334e5226a5c 100644 --- a/src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs +++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs @@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization #else public #endif - class JsonSerializerOptionsAttribute : JsonAttribute + class JsonSourceGenerationOptionsAttribute : JsonAttribute { /// /// Specifies the default ignore condition. @@ -43,11 +43,16 @@ namespace System.Text.Json.Serialization /// /// Specifies a built-in naming polices to convert JSON property names with. /// - public JsonKnownNamingPolicy NamingPolicy { get; set; } + public JsonKnownNamingPolicy PropertyNamingPolicy { get; set; } /// /// Specifies whether JSON output should be pretty-printed. /// public bool WriteIndented { get; set; } + + /// + /// Specifies the source generation mode for types that don't explicitly set the mode with . + /// + public JsonSourceGenerationMode GenerationMode { get; set; } } } diff --git a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs index 7ba239fba49..f4b27c2db11 100644 --- a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs @@ -13,11 +13,11 @@ namespace System.Text.Json.SourceGeneration /// internal sealed class ContextGenerationSpec { - public JsonSerializerOptionsAttribute SerializerOptions { get; init; } + public JsonSourceGenerationOptionsAttribute GenerationOptions { get; init; } public Type ContextType { get; init; } - public List? RootSerializableTypes { get; init; } + public List RootSerializableTypes { get; } = new(); public List ContextClassDeclarationList { get; init; } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 251aa2a18f8..e00a0ecfa63 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -637,10 +637,10 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC bool canBeNull, List? properties) { - JsonSerializerOptionsAttribute options = _currentContext.SerializerOptions; + JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions; // Add the property names to the context-wide cache; we'll generate the source to initialize them at the end of generation. - string[] runtimePropNames = GetRuntimePropNames(properties, options.NamingPolicy); + string[] runtimePropNames = GetRuntimePropNames(properties, options.PropertyNamingPolicy); _currentContext.RuntimePropertyNames.UnionWith(runtimePropNames); StringBuilder sb = new(); @@ -855,7 +855,7 @@ private static void {serializeMethodName}({Utf8JsonWriterTypeRef} {WriterVarName { runtimePropName = jsonPropName; } - else if (namingPolicy == JsonKnownNamingPolicy.BuiltInCamelCase) + else if (namingPolicy == JsonKnownNamingPolicy.CamelCase) { runtimePropName = JsonNamingPolicy.CamelCase.ConvertName(clrPropName); } @@ -890,7 +890,7 @@ public {typeInfoPropertyTypeRef} {typeFriendlyName} private string WrapWithCheckForCustomConverterIfRequired(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg) { - if (_currentContext.SerializerOptions.IgnoreRuntimeCustomConverters) + if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters) { return source; } @@ -933,9 +933,9 @@ public {contextTypeName}({JsonSerializerOptionsTypeRef} options) : base(options, private string GetLogicForDefaultSerializerOptionsInit() { - JsonSerializerOptionsAttribute options = _currentContext.SerializerOptions; + JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions; - string? namingPolicyInit = options.NamingPolicy == JsonKnownNamingPolicy.BuiltInCamelCase + string? namingPolicyInit = options.PropertyNamingPolicy == JsonKnownNamingPolicy.CamelCase ? $@" PropertyNamingPolicy = {JsonNamingPolicyTypeRef}.CamelCase" : null; @@ -953,7 +953,7 @@ private static {JsonSerializerOptionsTypeRef} {DefaultOptionsStaticVarName} {{ g private string GetFetchLogicForRuntimeSpecifiedCustomConverter() { - if (_currentContext.SerializerOptions.IgnoreRuntimeCustomConverters) + if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters) { return ""; } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index b909e1e42e0..386a79914cf 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -98,9 +98,9 @@ namespace System.Text.Json.SourceGeneration Compilation compilation = _executionContext.Compilation; INamedTypeSymbol jsonSerializerContextSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerContext"); INamedTypeSymbol jsonSerializableAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute"); - INamedTypeSymbol jsonSerializerOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerOptionsAttribute"); + INamedTypeSymbol jsonSourceGenerationOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute"); - if (jsonSerializerContextSymbol == null || jsonSerializableAttributeSymbol == null || jsonSerializerOptionsAttributeSymbol == null) + if (jsonSerializerContextSymbol == null || jsonSerializableAttributeSymbol == null || jsonSourceGenerationOptionsAttributeSymbol == null) { return null; } @@ -117,8 +117,8 @@ namespace System.Text.Json.SourceGeneration continue; } - List? rootTypes = null; - JsonSerializerOptionsAttribute? options = null; + JsonSourceGenerationOptionsAttribute? options = null; + List? serializableAttributeList = null; foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists) { @@ -133,19 +133,15 @@ namespace System.Text.Json.SourceGeneration if (jsonSerializableAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default)) { - TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attributeSyntax); - if (metadata != null) - { - (rootTypes ??= new List()).Add(metadata); - } + (serializableAttributeList ??= new List()).Add(attributeSyntax); } - else if (jsonSerializerOptionsAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default)) + else if (jsonSourceGenerationOptionsAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default)) { options = GetSerializerOptions(attributeSyntax); } } - if (rootTypes == null) + if (serializableAttributeList == null) { // No types were indicated with [JsonSerializable] continue; @@ -161,14 +157,29 @@ namespace System.Text.Json.SourceGeneration continue; } - contextGenSpecList ??= new List(); - contextGenSpecList.Add(new ContextGenerationSpec + ContextGenerationSpec contextGenSpec = new() { - SerializerOptions = options ?? new JsonSerializerOptionsAttribute(), + GenerationOptions = options ?? new JsonSourceGenerationOptionsAttribute(), ContextType = contextTypeSymbol.AsType(_metadataLoadContext), - RootSerializableTypes = rootTypes, ContextClassDeclarationList = classDeclarationList - }); + }; + + foreach(AttributeSyntax attribute in serializableAttributeList) + { + TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attribute, contextGenSpec.GenerationOptions.GenerationMode); + if (metadata != null) + { + contextGenSpec.RootSerializableTypes.Add(metadata); + } + } + + if (contextGenSpec.RootSerializableTypes.Count == 0) + { + continue; + } + + contextGenSpecList ??= new List(); + contextGenSpecList.Add(contextGenSpec); // Clear the cache of generated metadata between the processing of context classes. _typeGenerationSpecCache.Clear(); @@ -220,11 +231,10 @@ namespace System.Text.Json.SourceGeneration return match != null; } - private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List classDeclarationList) + private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List? classDeclarationList) { - classDeclarationList = new(); - INamedTypeSymbol currentSymbol = typeSymbol; + classDeclarationList = null; while (currentSymbol != null) { @@ -259,7 +269,7 @@ namespace System.Text.Json.SourceGeneration declarationElements[tokenCount] = "class"; declarationElements[tokenCount + 1] = currentSymbol.Name; - classDeclarationList.Add(string.Join(" ", declarationElements)); + (classDeclarationList ??= new List()).Add(string.Join(" ", declarationElements)); } currentSymbol = currentSymbol.ContainingType; @@ -269,13 +279,15 @@ namespace System.Text.Json.SourceGeneration return true; } - private TypeGenerationSpec? GetRootSerializableType(SemanticModel compilationSemanticModel, AttributeSyntax attributeSyntax) + private TypeGenerationSpec? GetRootSerializableType( + SemanticModel compilationSemanticModel, + AttributeSyntax attributeSyntax, + JsonSourceGenerationMode generationMode) { IEnumerable attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax); ITypeSymbol? typeSymbol = null; string? typeInfoPropertyName = null; - JsonSourceGenerationMode generationMode = default; bool seenFirstArg = false; foreach (AttributeArgumentSyntax node in attributeArguments) @@ -298,15 +310,20 @@ namespace System.Text.Json.SourceGeneration NameEqualsSyntax? propertyNameNode = childNodes.First() as NameEqualsSyntax; Debug.Assert(propertyNameNode != null); - SyntaxNode? propertyValueMode = childNodes.ElementAtOrDefault(1); - if (propertyNameNode.Name.Identifier.ValueText == "TypeInfoPropertyName") + SyntaxNode? propertyValueNode = childNodes.ElementAtOrDefault(1); + string optionName = propertyNameNode.Name.Identifier.ValueText; + + if (optionName == nameof(JsonSerializableAttribute.TypeInfoPropertyName)) { - typeInfoPropertyName = propertyValueMode.GetFirstToken().ValueText; + typeInfoPropertyName = propertyValueNode.GetFirstToken().ValueText; } - else + else if (optionName == nameof(JsonSerializableAttribute.GenerationMode)) { - Debug.Assert(propertyNameNode.Name.Identifier.ValueText == "GenerationMode"); - generationMode = (JsonSourceGenerationMode)Enum.Parse(typeof(JsonSourceGenerationMode), propertyValueMode.GetLastToken().ValueText); + JsonSourceGenerationMode? mode = GetJsonSourceGenerationModeEnumVal(propertyValueNode); + if (mode.HasValue) + { + generationMode = mode.Value; + } } } } @@ -325,37 +342,49 @@ namespace System.Text.Json.SourceGeneration return null; } - TypeGenerationSpec typeGenerationSpec = GetOrAddTypeGenerationSpec(type); + TypeGenerationSpec typeGenerationSpec = GetOrAddTypeGenerationSpec(type, generationMode); if (typeInfoPropertyName != null) { typeGenerationSpec.TypeInfoPropertyName = typeInfoPropertyName; } - ClassType classType = typeGenerationSpec.ClassType; - CollectionType collectionType = typeGenerationSpec.CollectionType; - switch (generationMode) + if (generationMode != default) { - case JsonSourceGenerationMode.MetadataAndSerialization: - break; - case JsonSourceGenerationMode.Metadata: - typeGenerationSpec.GenerateSerializationLogic = false; - break; - case JsonSourceGenerationMode.Serialization: - typeGenerationSpec.GenerateMetadata = false; - break; - default: - throw new InvalidOperationException(); + typeGenerationSpec.GenerationMode = generationMode; } return typeGenerationSpec; } - private static JsonSerializerOptionsAttribute? GetSerializerOptions(AttributeSyntax attributeSyntax) + private static JsonSourceGenerationMode? GetJsonSourceGenerationModeEnumVal(SyntaxNode propertyValueMode) { + IEnumerable enumTokens = propertyValueMode + .DescendantTokens() + .Where(token => IsValidEnumIdentifier(token.ValueText)) + .Select(token => token.ValueText); + string enumAsStr = string.Join(",", enumTokens); + + if (Enum.TryParse(enumAsStr, out JsonSourceGenerationMode value)) + { + return value; + } + + return null; + + static bool IsValidEnumIdentifier(string token) => token != nameof(JsonSourceGenerationMode) && token != "." && token != "|"; + } + + private static JsonSourceGenerationOptionsAttribute? GetSerializerOptions(AttributeSyntax? attributeSyntax) + { + if (attributeSyntax == null) + { + return null; + } + IEnumerable attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax); - JsonSerializerOptionsAttribute options = new(); + JsonSourceGenerationOptionsAttribute options = new(); foreach (AttributeArgumentSyntax node in attributeArguments) { @@ -369,26 +398,70 @@ namespace System.Text.Json.SourceGeneration switch (propertyNameNode.Name.Identifier.ValueText) { - case "DefaultIgnoreCondition": - options.DefaultIgnoreCondition = (JsonIgnoreCondition)Enum.Parse(typeof(JsonIgnoreCondition), propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.DefaultIgnoreCondition): + { + if (Enum.TryParse(propertyValueStr, out JsonIgnoreCondition value)) + { + options.DefaultIgnoreCondition = value; + } + } break; - case "IgnoreReadOnlyFields": - options.IgnoreReadOnlyFields = bool.Parse(propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.IgnoreReadOnlyFields): + { + if (bool.TryParse(propertyValueStr, out bool value)) + { + options.IgnoreReadOnlyFields = value; + } + } break; - case "IgnoreReadOnlyProperties": - options.IgnoreReadOnlyProperties = bool.Parse(propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.IgnoreReadOnlyProperties): + { + if (bool.TryParse(propertyValueStr, out bool value)) + { + options.IgnoreReadOnlyProperties = value; + } + } break; - case "IgnoreRuntimeCustomConverters": - options.IgnoreRuntimeCustomConverters = bool.Parse(propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.IgnoreRuntimeCustomConverters): + { + if (bool.TryParse(propertyValueStr, out bool value)) + { + options.IgnoreRuntimeCustomConverters = value; + } + } break; - case "IncludeFields": - options.IncludeFields = bool.Parse(propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.IncludeFields): + { + if (bool.TryParse(propertyValueStr, out bool value)) + { + options.IncludeFields = value; + } + } break; - case "NamingPolicy": - options.NamingPolicy = (JsonKnownNamingPolicy)Enum.Parse(typeof(JsonKnownNamingPolicy), propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.PropertyNamingPolicy): + { + if (Enum.TryParse(propertyValueStr, out JsonKnownNamingPolicy value)) + { + options.PropertyNamingPolicy = value; + } + } break; - case "WriteIndented": - options.WriteIndented = bool.Parse(propertyValueStr); + case nameof(JsonSourceGenerationOptionsAttribute.WriteIndented): + { + if (bool.TryParse(propertyValueStr, out bool value)) + { + options.WriteIndented = value; + } + } + break; + case nameof(JsonSourceGenerationOptionsAttribute.GenerationMode): + { + JsonSourceGenerationMode? mode = GetJsonSourceGenerationModeEnumVal(propertyValueNode); + if (mode.HasValue) + { + options.GenerationMode = mode.Value; + } + } break; default: throw new InvalidOperationException(); @@ -398,7 +471,7 @@ namespace System.Text.Json.SourceGeneration return options; } - private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type) + private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGenerationMode generationMode) { if (_typeGenerationSpecCache.TryGetValue(type, out TypeGenerationSpec? typeMetadata)) { @@ -514,7 +587,7 @@ namespace System.Text.Json.SourceGeneration foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags)) { - PropertyGenerationSpec metadata = GetPropertyGenerationSpec(propertyInfo); + PropertyGenerationSpec metadata = GetPropertyGenerationSpec(propertyInfo, generationMode); // Ignore indexers. if (propertyInfo.GetIndexParameters().Length > 0) @@ -530,7 +603,7 @@ namespace System.Text.Json.SourceGeneration foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags)) { - PropertyGenerationSpec metadata = GetPropertyGenerationSpec(fieldInfo); + PropertyGenerationSpec metadata = GetPropertyGenerationSpec(fieldInfo, generationMode); if (metadata.CanUseGetter || metadata.CanUseSetter) { @@ -541,6 +614,7 @@ namespace System.Text.Json.SourceGeneration } typeMetadata.Initialize( + generationMode, typeRef: type.GetUniqueCompilableTypeName(), typeInfoPropertyName: type.GetFriendlyTypeName(), type, @@ -549,16 +623,16 @@ namespace System.Text.Json.SourceGeneration numberHandling, propertiesMetadata, collectionType, - collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeGenerationSpec(collectionKeyType) : null, - collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeGenerationSpec(collectionValueType) : null, + collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeGenerationSpec(collectionKeyType, generationMode) : null, + collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeGenerationSpec(collectionValueType, generationMode) : null, constructionStrategy, - nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeGenerationSpec(nullableUnderlyingType) : null, + nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeGenerationSpec(nullableUnderlyingType, generationMode) : null, converterInstatiationLogic); return typeMetadata; } - private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo) + private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo, JsonSourceGenerationMode generationMode) { IList attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo); @@ -669,7 +743,7 @@ namespace System.Text.Json.SourceGeneration DefaultIgnoreCondition = ignoreCondition, NumberHandling = numberHandling, HasJsonInclude = hasJsonInclude, - TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType), + TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode), DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}", ConverterInstantiationLogic = converterInstantiationLogic }; diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs index d49dfd0e759..0966d4eed80 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs @@ -33,7 +33,6 @@ namespace System.Text.Json.SourceGeneration /// public void Execute(GeneratorExecutionContext executionContext) { - //if (!Diagnostics.Debugger.IsAttached) { Diagnostics.Debugger.Launch(); }; SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver; List? contextClasses = receiver.ClassDeclarationSyntaxList; if (contextClasses == null) diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj index 5ed7281c65d..ec46f798ab3 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj @@ -28,8 +28,9 @@ - + + diff --git a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs index 20155f0e5b4..0d457cd01b1 100644 --- a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs @@ -23,14 +23,11 @@ namespace System.Text.Json.SourceGeneration /// public string TypeInfoPropertyName { get; set; } - public bool GenerateMetadata { get; set; } = true; + public JsonSourceGenerationMode GenerationMode { get; set; } - private bool? _generateSerializationLogic; - public bool GenerateSerializationLogic - { - get => _generateSerializationLogic ??= FastPathIsSupported(); - set => _generateSerializationLogic = value; - } + public bool GenerateMetadata => GenerationModeIsSpecified(JsonSourceGenerationMode.Metadata); + + public bool GenerateSerializationLogic => GenerationModeIsSpecified(JsonSourceGenerationMode.Serialization) && FastPathIsSupported(); public Type Type { get; private set; } @@ -57,6 +54,7 @@ namespace System.Text.Json.SourceGeneration public string? ConverterInstantiationLogic { get; private set; } public void Initialize( + JsonSourceGenerationMode generationMode, string typeRef, string typeInfoPropertyName, Type type, @@ -71,6 +69,7 @@ namespace System.Text.Json.SourceGeneration TypeGenerationSpec? nullableUnderlyingTypeMetadata, string? converterInstantiationLogic) { + GenerationMode = generationMode; TypeRef = $"global::{typeRef}"; TypeInfoPropertyName = typeInfoPropertyName; Type = type; @@ -87,7 +86,7 @@ namespace System.Text.Json.SourceGeneration ConverterInstantiationLogic = converterInstantiationLogic; } - public bool FastPathIsSupported() + private bool FastPathIsSupported() { if (ClassType == ClassType.Object) { @@ -106,5 +105,7 @@ namespace System.Text.Json.SourceGeneration return false; } + + private bool GenerationModeIsSpecified(JsonSourceGenerationMode mode) => GenerationMode == JsonSourceGenerationMode.Default || (mode & GenerationMode) != 0; } } diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index e68bffd9205..f22feb4a517 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -792,7 +792,7 @@ namespace System.Text.Json.Serialization public enum JsonKnownNamingPolicy { Unspecified = 0, - BuiltInCamelCase = 1, + CamelCase = 1, } [System.FlagsAttribute] public enum JsonNumberHandling @@ -828,21 +828,22 @@ namespace System.Text.Json.Serialization public abstract System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type); } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false)] - public partial class JsonSerializerOptionsAttribute : System.Text.Json.Serialization.JsonAttribute + public partial class JsonSourceGenerationOptionsAttribute : System.Text.Json.Serialization.JsonAttribute { - public JsonSerializerOptionsAttribute() { } + public JsonSourceGenerationOptionsAttribute() { } public System.Text.Json.Serialization.JsonIgnoreCondition DefaultIgnoreCondition { get { throw null; } set { } } public bool IgnoreReadOnlyFields { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool IgnoreRuntimeCustomConverters { get { throw null; } set { } } public bool IncludeFields { get { throw null; } set { } } - public System.Text.Json.Serialization.JsonKnownNamingPolicy NamingPolicy { get { throw null; } set { } } + public System.Text.Json.Serialization.JsonKnownNamingPolicy PropertyNamingPolicy { get { throw null; } set { } } public bool WriteIndented { get { throw null; } set { } } + public JsonSourceGenerationMode GenerationMode { get { throw null; } set { } } } [System.FlagsAttribute] public enum JsonSourceGenerationMode { - MetadataAndSerialization = 0, + Default = 0, Metadata = 1, Serialization = 2, } diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index bc957caea23..142d9f8c4fd 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -25,8 +25,9 @@ - + + @@ -89,7 +90,6 @@ - diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs index 77f723b59fe..a302d92878b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs @@ -84,7 +84,7 @@ namespace System.Text.Json.Serialization /// Creates an instance of and binds it with the indicated . /// /// The run-time provided options for the context instance. - /// The default run-time options for the context. It's values are defined at design-time via . + /// The default run-time options for the context. It's values are defined at design-time via . /// /// If no instance options are passed, then no options are set until the context is bound using , /// or until is called, where a new options instance is created and bound. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs index 12f3717ddfb..e0f846ddaf2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs @@ -15,7 +15,7 @@ namespace System.Text.Json.Serialization.Metadata /// The to use. /// A instance representing the element type. /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// An optimized serialization implementation assuming pre-determined defaults. /// public static JsonTypeInfo CreateArrayInfo( JsonSerializerOptions options, @@ -40,7 +40,7 @@ namespace System.Text.Json.Serialization.Metadata /// A to create an instance of the list when deserializing. /// A instance representing the element type. /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// An optimized serialization implementation assuming pre-determined defaults. /// public static JsonTypeInfo CreateListInfo( JsonSerializerOptions options, @@ -69,7 +69,7 @@ namespace System.Text.Json.Serialization.Metadata /// A instance representing the key type. /// A instance representing the value type. /// The option to apply to number collection elements. - /// An optimized serialization implementation assuming pre-determined defaults. + /// An optimized serialization implementation assuming pre-determined defaults. /// public static JsonTypeInfo CreateDictionaryInfo( JsonSerializerOptions options, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs index 14fd4034cea..5268573219d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs @@ -93,7 +93,7 @@ namespace System.Text.Json.Serialization.Metadata /// The to initialize the metadata with. /// Provides a mechanism to create an instance of the class or struct when deserializing. /// Provides a mechanism to initialize metadata for properties and fields of the class or struct. - /// Provides a serialization implementation for instances of the class or struct which assumes options specified by . + /// Provides a serialization implementation for instances of the class or struct which assumes options specified by . /// Specifies how number properties and fields should be processed when serializing and deserializing. /// The type of the class or struct. /// Thrown when and are both null. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs index 208ac31539c..13bdbc8259e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs @@ -24,7 +24,7 @@ namespace System.Text.Json.Serialization.Metadata /// /// A method that serializes an instance of using - /// values specified at design time. + /// values specified at design time. /// public Action? Serialize { get; private protected set; } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index 5d4b469c8da..0bb57ab5721 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -23,6 +23,55 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata)] + internal partial class MetadataWithPerTypeAttributeContext : JsonSerializerContext, ITestContext + { + } + + public sealed class MetadataWithPerTypeAttributeContextTests : RealWorldContextTests + { + public MetadataWithPerTypeAttributeContextTests() : base(MetadataWithPerTypeAttributeContext.Default, (options) => new MetadataWithPerTypeAttributeContext(options)) { } + + [Fact] + public override void EnsureFastPathGeneratedAsExpected() + { + Assert.Null(MetadataWithPerTypeAttributeContext.Default.Location.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.IndexViewModel.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.EmptyPoco.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTemps.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType2.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedClass.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); + } + } + + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(Location))] + [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")] + [JsonSerializable(typeof(ActiveOrUpcomingEvent))] + [JsonSerializable(typeof(CampaignSummaryViewModel))] + [JsonSerializable(typeof(IndexViewModel))] + [JsonSerializable(typeof(WeatherForecastWithPOCOs))] + [JsonSerializable(typeof(EmptyPoco))] + [JsonSerializable(typeof(HighLowTemps))] + [JsonSerializable(typeof(MyType))] + [JsonSerializable(typeof(MyType2))] + [JsonSerializable(typeof(MyIntermediateType))] + [JsonSerializable(typeof(HighLowTempsImmutable))] + [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] + [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] + [JsonSerializable(typeof(object[]))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))] internal partial class MetadataContext : JsonSerializerContext, ITestContext { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index a92759a9ec8..f8317b590ec 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -14,15 +14,15 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(WeatherForecastWithPOCOs), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(EmptyPoco), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTemps), GenerationMode = JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)] - [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)] - [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)] + [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Default)] + [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] - [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)] - [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)] + [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] internal partial class MixedModeContext : JsonSerializerContext, ITestContext { } @@ -60,7 +60,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.IndexViewModel); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.IndexViewModel), typeof(CampaignSummaryViewModel)); - IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).IndexViewModel); + IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel); VerifyIndexViewModel(expected, obj); } @@ -72,7 +72,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.CampaignSummaryViewModel); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.CampaignSummaryViewModel), typeof(CampaignSummaryViewModel)); - CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel); + CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel); VerifyCampaignSummaryViewModel(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.CampaignSummaryViewModel); @@ -86,7 +86,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.WeatherForecastWithPOCOs); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.WeatherForecastWithPOCOs), typeof(HighLowTemps)); - WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).WeatherForecastWithPOCOs); + WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).WeatherForecastWithPOCOs); VerifyWeatherForecastWithPOCOs(expected, obj); } @@ -98,7 +98,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.EmptyPoco); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.EmptyPoco), typeof(EmptyPoco)); - EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).EmptyPoco); + EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).EmptyPoco); VerifyEmptyPoco(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.EmptyPoco); @@ -112,7 +112,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.RepeatedLocation); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.RepeatedLocation), typeof(RepeatedTypes.Location)); - RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).RepeatedLocation); + RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).RepeatedLocation); VerifyRepeatedLocation(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.RepeatedLocation); @@ -122,11 +122,11 @@ namespace System.Text.Json.SourceGeneration.Tests public override void HandlesNestedTypes() { string json = @"{""MyInt"":5}"; - MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedClass); + MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass); Assert.Equal(5, obj.MyInt); Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass)); - MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass); + MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass); Assert.Equal(5, obj2.MyInt); Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass)); } @@ -138,12 +138,12 @@ namespace System.Text.Json.SourceGeneration.Tests CampaignSummaryViewModel campaignSummary = CreateCampaignSummaryViewModel(); string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, DefaultContext.ObjectArray); - object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray); + object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray); JsonElement indexAsJsonElement = (JsonElement)arr[0]; JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1]; - VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).IndexViewModel)); - VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel)); + VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel)); + VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel)); } [Fact] @@ -156,7 +156,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.Same(JsonNamingPolicy.CamelCase, ((JsonSerializerContext)context).Options.PropertyNamingPolicy); string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, context.ObjectArray); - object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray); + object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray); JsonElement indexAsJsonElement = (JsonElement)arr[0]; JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1]; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index 45ce3f358df..02567c4b8ae 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -6,6 +6,28 @@ using Xunit; namespace System.Text.Json.SourceGeneration.Tests { + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(Location))] + [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")] + [JsonSerializable(typeof(ActiveOrUpcomingEvent))] + [JsonSerializable(typeof(CampaignSummaryViewModel))] + [JsonSerializable(typeof(IndexViewModel))] + [JsonSerializable(typeof(WeatherForecastWithPOCOs))] + [JsonSerializable(typeof(EmptyPoco))] + [JsonSerializable(typeof(HighLowTemps))] + [JsonSerializable(typeof(MyType))] + [JsonSerializable(typeof(MyType2))] + [JsonSerializable(typeof(MyIntermediateType))] + [JsonSerializable(typeof(HighLowTempsImmutable))] + [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] + [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] + [JsonSerializable(typeof(object[]))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))] + internal partial class SerializationContext : JsonSerializerContext, ITestContext + { + } + [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")] [JsonSerializable(typeof(ActiveOrUpcomingEvent), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -23,11 +45,11 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)] - internal partial class SerializationContext : JsonSerializerContext, ITestContext + internal partial class SerializationWithPerTypeAttributeContext : JsonSerializerContext, ITestContext { } - [JsonSerializerOptions(NamingPolicy = JsonKnownNamingPolicy.BuiltInCamelCase)] + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")] [JsonSerializable(typeof(ActiveOrUpcomingEvent), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -49,9 +71,14 @@ namespace System.Text.Json.SourceGeneration.Tests { } - public sealed class SerializationContextTests : RealWorldContextTests + public class SerializationContextTests : RealWorldContextTests { - public SerializationContextTests() : base(SerializationContext.Default, (options) => new SerializationContext(options)) { } + public SerializationContextTests() : this(SerializationContext.Default, (options) => new SerializationContext(options)) { } + + internal SerializationContextTests(ITestContext defaultContext, Func contextCreator) + : base(defaultContext, contextCreator) + { + } [Fact] public override void EnsureFastPathGeneratedAsExpected() @@ -83,7 +110,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.Location); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.Location), typeof(Location)); - Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).Location); + Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).Location); VerifyLocation(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.Location); @@ -97,7 +124,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.IndexViewModel); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.IndexViewModel), typeof(IndexViewModel)); - IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).IndexViewModel); + IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel); VerifyIndexViewModel(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.IndexViewModel); @@ -111,7 +138,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.CampaignSummaryViewModel); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.CampaignSummaryViewModel), typeof(CampaignSummaryViewModel)); - CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel); + CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel); VerifyCampaignSummaryViewModel(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.CampaignSummaryViewModel); @@ -125,7 +152,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.ActiveOrUpcomingEvent); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.ActiveOrUpcomingEvent), typeof(ActiveOrUpcomingEvent)); - ActiveOrUpcomingEvent obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ActiveOrUpcomingEvent); + ActiveOrUpcomingEvent obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ActiveOrUpcomingEvent); VerifyActiveOrUpcomingEvent(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.ActiveOrUpcomingEvent); @@ -139,7 +166,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.WeatherForecastWithPOCOs); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.WeatherForecastWithPOCOs), typeof(WeatherForecastWithPOCOs)); - WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).WeatherForecastWithPOCOs); + WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).WeatherForecastWithPOCOs); VerifyWeatherForecastWithPOCOs(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.WeatherForecastWithPOCOs); @@ -153,7 +180,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.EmptyPoco); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.EmptyPoco), typeof(EmptyPoco)); - EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).EmptyPoco); + EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).EmptyPoco); VerifyEmptyPoco(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.EmptyPoco); @@ -167,7 +194,7 @@ namespace System.Text.Json.SourceGeneration.Tests string json = JsonSerializer.Serialize(expected, DefaultContext.RepeatedLocation); JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.RepeatedLocation), typeof(RepeatedTypes.Location)); - RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).RepeatedLocation); + RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).RepeatedLocation); VerifyRepeatedLocation(expected, obj); AssertFastPathLogicCorrect(json, obj, DefaultContext.RepeatedLocation); @@ -178,12 +205,12 @@ namespace System.Text.Json.SourceGeneration.Tests { MyType myType = new() { Type = new() }; string json = JsonSerializer.Serialize(myType, DefaultContext.MyType); - myType = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyType); + myType = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyType); AssertFastPathLogicCorrect(json, myType, DefaultContext.MyType); MyType2 myType2 = new() { Type = new MyIntermediateType() { Type = myType } }; json = JsonSerializer.Serialize(myType2, DefaultContext.MyType2); - myType2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyType2); + myType2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyType2); AssertFastPathLogicCorrect(json, myType2, DefaultContext.MyType2); } @@ -194,12 +221,12 @@ namespace System.Text.Json.SourceGeneration.Tests CampaignSummaryViewModel campaignSummary = CreateCampaignSummaryViewModel(); string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, DefaultContext.ObjectArray); - object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray); + object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray); JsonElement indexAsJsonElement = (JsonElement)arr[0]; JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1]; - VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).IndexViewModel)); - VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel)); + VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel)); + VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel)); } [Fact] @@ -218,7 +245,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.Contains("description", json); Assert.Contains("organizationName", json); - object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray); + object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray); JsonElement indexAsJsonElement = (JsonElement)arr[0]; JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1]; @@ -235,7 +262,7 @@ namespace System.Text.Json.SourceGeneration.Tests ITestContext context = new SerializationContext(options); string json = JsonSerializer.Serialize(new object[] { "Hello", "World" }, typeof(object[]), (JsonSerializerContext)context); - object[] arr = (object[])JsonSerializer.Deserialize(json, typeof(object[]), (JsonSerializerContext)((ITestContext)MetadataContext.Default)); + object[] arr = (object[])JsonSerializer.Deserialize(json, typeof(object[]), (JsonSerializerContext)((ITestContext)MetadataWithPerTypeAttributeContext.Default)); JsonElement hello = (JsonElement)arr[0]; JsonElement world = (JsonElement)arr[1]; @@ -247,11 +274,11 @@ namespace System.Text.Json.SourceGeneration.Tests public override void HandlesNestedTypes() { string json = @"{""MyInt"":5}"; - MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedClass); + MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass); Assert.Equal(5, obj.MyInt); Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass)); - MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass); + MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass); Assert.Equal(5, obj2.MyInt); Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass)); } @@ -265,7 +292,7 @@ namespace System.Text.Json.SourceGeneration.Tests void RunTest(ClassWithEnumAndNullable expected) { string json = JsonSerializer.Serialize(expected, DefaultContext.ClassWithEnumAndNullable); - ClassWithEnumAndNullable actual = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ClassWithEnumAndNullable); + ClassWithEnumAndNullable actual = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ClassWithEnumAndNullable); Assert.Equal(expected.Day, actual.Day); Assert.Equal(expected.NullableDay, actual.NullableDay); } @@ -281,4 +308,31 @@ namespace System.Text.Json.SourceGeneration.Tests JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.HighLowTempsImmutable), typeof(HighLowTempsImmutable)); } } + + public sealed class SerializationWithPerTypeAttributeContextTests : SerializationContextTests + { + public SerializationWithPerTypeAttributeContextTests() : base(SerializationWithPerTypeAttributeContext.Default, (options) => new SerializationContext(options)) { } + + [Fact] + public override void EnsureFastPathGeneratedAsExpected() + { + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.Location.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.IndexViewModel.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTemps.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType2.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedClass.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); + } + } } From 7527d93c0f4452a23e9bbc3fda57c69c46b03ae7 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Fri, 25 Jun 2021 18:45:42 -0700 Subject: [PATCH 126/926] HTTP3: Re-enable cookie and cancellation tests (#54727) * fix an issue with GOAWAY handling and enable cookie tests * disable ConnectTimeout test for HTTP3 * fix Expect 100 continue handling in HTTP3 * add and use GenericLoopbackServer.SendPartialResponseHeadersAsync * enable cancellation tests for HTTP3 * disable failing interop tests Co-authored-by: Geoffrey Kizer --- .../System/Net/Http/GenericLoopbackServer.cs | 11 ++- .../Net/Http/Http2LoopbackConnection.cs | 24 ++++--- .../Net/Http/Http3LoopbackConnection.cs | 25 ++++++- .../System/Net/Http/Http3LoopbackServer.cs | 5 +- .../System/Net/Http/Http3LoopbackStream.cs | 69 ++++++++++++++++--- .../HttpClientHandlerTest.Cancellation.cs | 2 +- .../System/Net/Http/HttpClientHandlerTest.cs | 2 +- .../tests/System/Net/Http/LoopbackServer.cs | 47 ++++++++----- .../SocketsHttpHandler/Http3Connection.cs | 6 +- .../SocketsHttpHandler/Http3RequestStream.cs | 11 ++- .../HttpClientHandlerTest.Http2.cs | 2 +- .../HttpClientHandlerTest.Http3.cs | 12 ++++ .../SocketsHttpHandlerTest.Cancellation.cs | 6 ++ .../FunctionalTests/SocketsHttpHandlerTest.cs | 6 -- 14 files changed, 166 insertions(+), 62 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs index c145b6aa3d4..ecff49e7201 100644 --- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs @@ -120,10 +120,13 @@ namespace System.Net.Test.Common /// Read complete request body if not done by ReadRequestData. public abstract Task ReadRequestBodyAsync(); - /// Sends Response back with provided statusCode, headers and content. Can be called multiple times on same response if isFinal was set to false before. - public abstract Task SendResponseAsync(HttpStatusCode? statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0); + /// Sends Response back with provided statusCode, headers and content. + /// If isFinal is false, the body is not completed and you can call SendResponseBodyAsync to send more. + public abstract Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0); /// Sends response headers. public abstract Task SendResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0); + /// Sends valid but incomplete headers. Once called, there is no way to continue the response past this point. + public abstract Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0); /// Sends Response body after SendResponse was called with isFinal: false. public abstract Task SendResponseBodyAsync(byte[] content, bool isFinal = true, int requestId = 0); @@ -184,7 +187,7 @@ namespace System.Net.Test.Common public string Path; public Version Version; public List Headers { get; } - public int RequestId; // Generic request ID. Currently only used for HTTP/2 to hold StreamId. + public int RequestId; // HTTP/2 StreamId. public HttpRequestData() { @@ -246,5 +249,7 @@ namespace System.Net.Test.Common { return Headers.Where(h => h.Name.Equals(headerName, StringComparison.OrdinalIgnoreCase)).Count(); } + + public override string ToString() => $"{Method} {Path} HTTP/{Version}\r\n{string.Join("\r\n", Headers)}\r\n\r\n"; } } diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs index 5dcbc2d3863..97787e65849 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs @@ -845,11 +845,8 @@ namespace System.Net.Test.Common return ReadBodyAsync(); } - public override async Task SendResponseAsync(HttpStatusCode? statusCode = null, IList headers = null, string body = null, bool isFinal = true, int requestId = 0) + public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0) { - // TODO: Header continuation support. - Assert.NotNull(statusCode); - if (headers != null) { bool hasDate = false; @@ -886,16 +883,15 @@ namespace System.Net.Test.Common } int streamId = requestId == 0 ? _lastStreamId : requestId; - bool endHeaders = body != null || isFinal; - if (string.IsNullOrEmpty(body)) + if (string.IsNullOrEmpty(content)) { - await SendResponseHeadersAsync(streamId, endStream: isFinal, (HttpStatusCode)statusCode, endHeaders: endHeaders, headers: headers); + await SendResponseHeadersAsync(streamId, endStream: isFinal, (HttpStatusCode)statusCode, endHeaders: true, headers: headers); } else { - await SendResponseHeadersAsync(streamId, endStream: false, (HttpStatusCode)statusCode, endHeaders: endHeaders, headers: headers); - await SendResponseBodyAsync(body, isFinal: isFinal, requestId: streamId); + await SendResponseHeadersAsync(streamId, endStream: false, (HttpStatusCode)statusCode, endHeaders: true, headers: headers); + await SendResponseBodyAsync(content, isFinal: isFinal, requestId: streamId); } } @@ -905,10 +901,16 @@ namespace System.Net.Test.Common return SendResponseHeadersAsync(streamId, endStream: false, statusCode, isTrailingHeader: false, endHeaders: true, headers); } - public override Task SendResponseBodyAsync(byte[] body, bool isFinal = true, int requestId = 0) + public override Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0) { int streamId = requestId == 0 ? _lastStreamId : requestId; - return SendResponseBodyAsync(streamId, body, isFinal); + return SendResponseHeadersAsync(streamId, endStream: false, statusCode, isTrailingHeader: false, endHeaders: false, headers); + } + + public override Task SendResponseBodyAsync(byte[] content, bool isFinal = true, int requestId = 0) + { + int streamId = requestId == 0 ? _lastStreamId : requestId; + return SendResponseBodyAsync(streamId, content, isFinal); } public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs index 4c74a50b210..555a54895e7 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs @@ -33,6 +33,7 @@ namespace System.Net.Test.Common private readonly QuicConnection _connection; private readonly Dictionary _openStreams = new Dictionary(); + private Http3LoopbackStream _controlStream; // Our outbound control stream private Http3LoopbackStream _currentStream; private bool _closed; @@ -138,6 +139,13 @@ namespace System.Net.Test.Common } } + public async Task EstablishControlStreamAsync() + { + _controlStream = OpenUnidirectionalStream(); + await _controlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream); + await _controlStream.SendSettingsFrameAsync(); + } + public override async Task ReadRequestBodyAsync() { return await _currentStream.ReadRequestBodyAsync().ConfigureAwait(false); @@ -149,7 +157,7 @@ namespace System.Net.Test.Common return await stream.ReadRequestDataAsync(readBody).ConfigureAwait(false); } - public override Task SendResponseAsync(HttpStatusCode? statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0) + public override Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0) { return GetOpenRequest(requestId).SendResponseAsync(statusCode, headers, content, isFinal); } @@ -164,12 +172,25 @@ namespace System.Net.Test.Common return GetOpenRequest(requestId).SendResponseHeadersAsync(statusCode, headers); } + public override Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0) + { + return GetOpenRequest(requestId).SendPartialResponseHeadersAsync(statusCode, headers); + } + public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { Http3LoopbackStream stream = await AcceptRequestStreamAsync().ConfigureAwait(false); - HttpRequestData request = await stream.HandleRequestAsync(statusCode, headers, content); + + HttpRequestData request = await stream.ReadRequestDataAsync().ConfigureAwait(false); + + // We are about to close the connection, after we send the response. + // So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection. + await _controlStream.SendGoAwayFrameAsync(stream.StreamId + 4); + + await stream.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false); // closing the connection here causes bytes written to streams to go missing. + // Regardless, we told the client we are closing so it shouldn't matter -- they should not use this connection anymore. //await CloseAsync(H3_NO_ERROR).ConfigureAwait(false); return request; diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index b84393a88b1..6e511915f04 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -57,7 +57,10 @@ namespace System.Net.Test.Common public override async Task EstablishGenericConnectionAsync() { QuicConnection con = await _listener.AcceptConnectionAsync().ConfigureAwait(false); - return new Http3LoopbackConnection(con); + Http3LoopbackConnection connection = new Http3LoopbackConnection(con); + + await connection.EstablishControlStreamAsync(); + return connection; } public override async Task AcceptConnectionAsync(Func funcAsync) diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs index faccfab64b2..8bccc13cce2 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs @@ -23,6 +23,7 @@ namespace System.Net.Test.Common private const long DataFrame = 0x0; private const long HeadersFrame = 0x1; private const long SettingsFrame = 0x4; + private const long GoAwayFrame = 0x7; public const long ControlStream = 0x0; public const long PushStream = 0x1; @@ -43,6 +44,9 @@ namespace System.Net.Test.Common { _stream.Dispose(); } + + public long StreamId => _stream.StreamId; + public async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { HttpRequestData request = await ReadRequestDataAsync().ConfigureAwait(false); @@ -57,8 +61,10 @@ namespace System.Net.Test.Common await _stream.WriteAsync(buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false); } - public async Task SendSettingsFrameAsync(ICollection<(long settingId, long settingValue)> settings) + public async Task SendSettingsFrameAsync(ICollection<(long settingId, long settingValue)> settings = null) { + settings ??= Array.Empty<(long settingId, long settingValue)>(); + var buffer = new byte[settings.Count * MaximumVarIntBytes * 2]; int bytesWritten = 0; @@ -72,7 +78,7 @@ namespace System.Net.Test.Common await SendFrameAsync(SettingsFrame, buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false); } - public async Task SendHeadersFrameAsync(IEnumerable headers) + private Memory ConstructHeadersPayload(IEnumerable headers) { int bufferLength = QPackTestEncoder.MaxPrefixLength; @@ -95,7 +101,24 @@ namespace System.Net.Test.Common bytesWritten += QPackTestEncoder.EncodeHeader(buffer.AsSpan(bytesWritten), header.Name, header.Value, header.ValueEncoding, header.HuffmanEncoded ? QPackFlags.HuffmanEncode : QPackFlags.None); } - await SendFrameAsync(HeadersFrame, buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false); + return buffer.AsMemory(0, bytesWritten); + } + + private async Task SendHeadersFrameAsync(IEnumerable headers) + { + await SendFrameAsync(HeadersFrame, ConstructHeadersPayload(headers)).ConfigureAwait(false); + } + + private async Task SendPartialHeadersFrameAsync(IEnumerable headers) + { + Memory payload = ConstructHeadersPayload(headers); + + await SendFrameHeaderAsync(HeadersFrame, payload.Length); + + // Slice off final byte so the payload is not complete + payload = payload.Slice(0, payload.Length - 1); + + await _stream.WriteAsync(payload).ConfigureAwait(false); } public async Task SendDataFrameAsync(ReadOnlyMemory data) @@ -103,16 +126,31 @@ namespace System.Net.Test.Common await SendFrameAsync(DataFrame, data).ConfigureAwait(false); } - public async Task SendFrameAsync(long frameType, ReadOnlyMemory framePayload) + // Note that unlike HTTP2, the stream ID here indicates the *first invalid* stream. + public async Task SendGoAwayFrameAsync(long firstInvalidStreamId) + { + var buffer = new byte[QPackTestEncoder.MaxVarIntLength]; + int bytesWritten = 0; + + bytesWritten += EncodeHttpInteger(firstInvalidStreamId, buffer); + await SendFrameAsync(GoAwayFrame, buffer.AsMemory(0, bytesWritten)); + } + + private async Task SendFrameHeaderAsync(long frameType, int payloadLength) { var buffer = new byte[MaximumVarIntBytes * 2]; int bytesWritten = 0; bytesWritten += EncodeHttpInteger(frameType, buffer.AsSpan(bytesWritten)); - bytesWritten += EncodeHttpInteger(framePayload.Length, buffer.AsSpan(bytesWritten)); + bytesWritten += EncodeHttpInteger(payloadLength, buffer.AsSpan(bytesWritten)); await _stream.WriteAsync(buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false); + } + + public async Task SendFrameAsync(long frameType, ReadOnlyMemory framePayload) + { + await SendFrameHeaderAsync(frameType, framePayload.Length).ConfigureAwait(false); await _stream.WriteAsync(framePayload).ConfigureAwait(false); } @@ -189,7 +227,7 @@ namespace System.Net.Test.Common return requestData; } - public async Task SendResponseAsync(HttpStatusCode? statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true) + public async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true) { IEnumerable newHeaders = headers ?? Enumerable.Empty(); @@ -202,21 +240,30 @@ namespace System.Net.Test.Common await SendResponseBodyAsync(Encoding.UTF8.GetBytes(content ?? ""), isFinal).ConfigureAwait(false); } - public async Task SendResponseHeadersAsync(HttpStatusCode? statusCode = HttpStatusCode.OK, IEnumerable headers = null) + private IEnumerable PrepareHeaders(HttpStatusCode statusCode, IEnumerable headers) { headers ??= Enumerable.Empty(); // Some tests use Content-Length with a null value to indicate Content-Length should not be set. headers = headers.Where(x => x.Name != "Content-Length" || x.Value != null); - if (statusCode != null) - { - headers = headers.Prepend(new HttpHeaderData(":status", ((int)statusCode).ToString(CultureInfo.InvariantCulture))); - } + headers = headers.Prepend(new HttpHeaderData(":status", ((int)statusCode).ToString(CultureInfo.InvariantCulture))); + return headers; + } + + public async Task SendResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IEnumerable headers = null) + { + headers = PrepareHeaders(statusCode, headers); await SendHeadersFrameAsync(headers).ConfigureAwait(false); } + public async Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IEnumerable headers = null) + { + headers = PrepareHeaders(statusCode, headers); + await SendPartialHeadersFrameAsync(headers).ConfigureAwait(false); + } + public async Task SendResponseBodyAsync(byte[] content, bool isFinal = true) { if (content?.Length != 0) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index 410d2a6987b..d4c4a571ad3 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -109,7 +109,7 @@ namespace System.Net.Http.Functional.Tests Task serverTask = server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(); - await connection.SendResponseAsync(HttpStatusCode.OK, content: null, isFinal: false); + await connection.SendPartialResponseHeadersAsync(HttpStatusCode.OK); partialResponseHeadersSent.TrySetResult(true); await clientFinished.Task; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 912f356c5d6..d9958e9c520 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -1226,7 +1226,7 @@ namespace System.Net.Http.Functional.Tests Task serverTask2 = server2.AcceptConnectionAsync(async connection2 => { await connection2.ReadRequestDataAsync(); - await connection2.SendResponseAsync(HttpStatusCode.OK, content: null, isFinal : false); + await connection2.SendPartialResponseHeadersAsync(HttpStatusCode.OK); await unblockServers.Task; }); diff --git a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs index 5685e79cdb6..7594b490801 100644 --- a/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs @@ -866,7 +866,7 @@ namespace System.Net.Test.Common return buffer; } - public override async Task SendResponseAsync(HttpStatusCode? statusCode = HttpStatusCode.OK, IList headers = null, string content = null, bool isFinal = true, int requestId = 0) + public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true, int requestId = 0) { MemoryStream headerBytes = new MemoryStream(); int contentLength = -1; @@ -909,25 +909,18 @@ namespace System.Net.Test.Common headerBytes.Write(corsBytes, 0, corsBytes.Length); } - bool endHeaders = content != null || isFinal; - if (statusCode != null) - { - byte[] temp = headerBytes.ToArray(); + byte[] temp = headerBytes.ToArray(); - headerBytes.SetLength(0); + headerBytes.SetLength(0); - byte[] headerStartBytes = Encoding.ASCII.GetBytes( - $"HTTP/1.1 {(int)statusCode} {GetStatusDescription((HttpStatusCode)statusCode)}\r\n" + - (!hasContentLength && !isChunked && content != null ? $"Content-length: {content.Length}\r\n" : "")); + byte[] headerStartBytes = Encoding.ASCII.GetBytes( + $"HTTP/1.1 {(int)statusCode} {GetStatusDescription(statusCode)}\r\n" + + (!hasContentLength && !isChunked && content != null ? $"Content-length: {content.Length}\r\n" : "")); - headerBytes.Write(headerStartBytes, 0, headerStartBytes.Length); - headerBytes.Write(temp, 0, temp.Length); + headerBytes.Write(headerStartBytes, 0, headerStartBytes.Length); + headerBytes.Write(temp, 0, temp.Length); - if (endHeaders) - { - headerBytes.Write(s_newLineBytes, 0, s_newLineBytes.Length); - } - } + headerBytes.Write(s_newLineBytes, 0, s_newLineBytes.Length); headerBytes.Position = 0; await headerBytes.CopyToAsync(_stream).ConfigureAwait(false); @@ -938,7 +931,7 @@ namespace System.Net.Test.Common } } - public override async Task SendResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0) + private string GetResponseHeaderString(HttpStatusCode statusCode, IList headers) { string headerString = null; @@ -953,12 +946,28 @@ namespace System.Net.Test.Common headerString = GetHttpResponseHeaders(statusCode, headerString, 0, connectionClose: true); + return headerString; + } + + public override async Task SendResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0) + { + string headerString = GetResponseHeaderString(statusCode, headers); await SendResponseAsync(headerString).ConfigureAwait(false); } - public override async Task SendResponseBodyAsync(byte[] body, bool isFinal = true, int requestId = 0) + public override async Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, int requestId = 0) { - await SendResponseAsync(body).ConfigureAwait(false); + string headerString = GetResponseHeaderString(statusCode, headers); + + // Lop off the final \r\n so the headers are not complete. + headerString = headerString.Substring(0, headerString.Length - 2); + + await SendResponseAsync(headerString).ConfigureAwait(false); + } + + public override async Task SendResponseBodyAsync(byte[] content, bool isFinal = true, int requestId = 0) + { + await SendResponseAsync(content).ConfigureAwait(false); } public async Task HandleCORSPreFlight(HttpRequestData requestData) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index c0911273500..ce4f3e74169 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -568,7 +568,7 @@ namespace System.Net.Http switch (frameType) { case Http3FrameType.GoAway: - await ProcessGoAwayFameAsync(payloadLength).ConfigureAwait(false); + await ProcessGoAwayFrameAsync(payloadLength).ConfigureAwait(false); break; case Http3FrameType.Settings: // If an endpoint receives a second SETTINGS frame on the control stream, the endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED. @@ -685,12 +685,12 @@ namespace System.Net.Http } } - async ValueTask ProcessGoAwayFameAsync(long goawayPayloadLength) + async ValueTask ProcessGoAwayFrameAsync(long goawayPayloadLength) { long lastStreamId; int bytesRead; - while (!VariableLengthIntegerHelper.TryRead(buffer.AvailableSpan, out lastStreamId, out bytesRead)) + while (!VariableLengthIntegerHelper.TryRead(buffer.ActiveSpan, out lastStreamId, out bytesRead)) { buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength); bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 3d469f06c3b..4b00bb31f65 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -55,6 +55,9 @@ namespace System.Net.Http // Keep track of how much is remaining in that frame. private long _requestContentLengthRemaining; + // For the precomputed length case, we need to add the DATA framing for the first write only. + private bool _singleDataFrameWritten; + public long StreamId { get => Volatile.Read(ref _streamId); @@ -393,9 +396,9 @@ namespace System.Net.Http } _requestContentLengthRemaining -= buffer.Length; - if (_sendBuffer.ActiveLength != 0) + if (!_singleDataFrameWritten) { - // We haven't sent out headers yet, so write them together with the user's content buffer. + // Note we may not have sent headers yet; if so, _sendBuffer.ActiveLength will be > 0, and we will write them in a single write. // Because we have a Content-Length, we can write it in a single DATA frame. BufferFrameEnvelope(Http3FrameType.Data, remaining); @@ -405,10 +408,12 @@ namespace System.Net.Http await _stream.WriteAsync(_gatheredSendBuffer, cancellationToken).ConfigureAwait(false); _sendBuffer.Discard(_sendBuffer.ActiveLength); + + _singleDataFrameWritten = true; } else { - // Headers already sent, send just the content buffer directly. + // DATA frame already sent, send just the content buffer directly. await _stream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index 668d194ebef..b24c7037c2d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -206,7 +206,7 @@ namespace System.Net.Http.Functional.Tests HttpRequestData requestData = await connection.ReadRequestDataAsync(); string requestContent = requestData.Body is null ? (string)null : Encoding.ASCII.GetString(requestData.Body); Assert.Equal(clientContent, requestContent); - await connection.SendResponseAsync(HttpStatusCode.OK, body: serverContent); + await connection.SendResponseAsync(HttpStatusCode.OK, content: serverContent); }, new Http2Options() { UseSsl = false }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 6a41bfb0195..d4d806f6881 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -317,8 +317,14 @@ namespace System.Net.Http.Functional.Tests [OuterLoop] [ConditionalTheory(nameof(IsMsQuicSupported))] [MemberData(nameof(InteropUris))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54726")] public async Task Public_Interop_ExactVersion_Success(string uri) { + if (UseQuicImplementationProvider == QuicImplementationProviders.Mock) + { + return; + } + using HttpClient client = CreateHttpClient(); using HttpRequestMessage request = new HttpRequestMessage { @@ -336,8 +342,14 @@ namespace System.Net.Http.Functional.Tests [OuterLoop] [ConditionalTheory(nameof(IsMsQuicSupported))] [MemberData(nameof(InteropUris))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54726")] public async Task Public_Interop_Upgrade_Success(string uri) { + if (UseQuicImplementationProvider == QuicImplementationProviders.Mock) + { + return; + } + using HttpClient client = CreateHttpClient(); // First request uses HTTP/1 or HTTP/2 and receives an Alt-Svc either by header or (with HTTP/2) by frame. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index 53fcb8fe229..bc4213b39a1 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -48,6 +48,12 @@ namespace System.Net.Http.Functional.Tests [Fact] public async Task ConnectTimeout_ConnectCallbackTimesOut_Throws() { + if (UseVersion == HttpVersion.Version30) + { + // HTTP3 does not support ConnectCallback + return; + } + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using (var handler = CreateHttpClientHandler()) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 4f18763f458..7d006162ef0 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3098,8 +3098,6 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.Mock; } - // TODO: Many Cookie tests are failing for HTTP3. - [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMsQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3_MsQuic : HttpClientHandlerTest_Cookies { @@ -3108,7 +3106,6 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.MsQuic; } - [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMockQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3_Mock : HttpClientHandlerTest_Cookies { @@ -3133,8 +3130,6 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.Mock; } - // TODO: Many cancellation tests are failing for HTTP3. - [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3_MsQuic : SocketsHttpHandler_Cancellation_Test { @@ -3143,7 +3138,6 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.MsQuic; } - [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMockQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3_Mock : SocketsHttpHandler_Cancellation_Test { From 4be7e678ce50971a7fe6e89ece827119f1a5a991 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 25 Jun 2021 20:21:40 -0700 Subject: [PATCH 127/926] Fix gc hole in IO thread pool (#54769) --- src/coreclr/vm/win32threadpool.cpp | 3 ++- .../eventpipe/processenvironment/processenvironment.csproj | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/win32threadpool.cpp b/src/coreclr/vm/win32threadpool.cpp index 4bdbca4dbbf..4f8646d73fa 100644 --- a/src/coreclr/vm/win32threadpool.cpp +++ b/src/coreclr/vm/win32threadpool.cpp @@ -1278,8 +1278,9 @@ void ThreadpoolMgr::ManagedWaitIOCompletionCallback_Worker(LPVOID state) DestroyHandle(completeWaitWorkItemObjectHandle); completeWaitWorkItemObjectHandle = NULL; + MethodDescCallSite completeAwait(METHOD__COMPLETE_WAIT_THREAD_POOL_WORK_ITEM__COMPLETE_WAIT, &completeWaitWorkItemObject); ARG_SLOT args[] = { ObjToArgSlot(completeWaitWorkItemObject) }; - MethodDescCallSite(METHOD__COMPLETE_WAIT_THREAD_POOL_WORK_ITEM__COMPLETE_WAIT, &completeWaitWorkItemObject).Call(args); + completeAwait.Call(args); GCPROTECT_END(); } diff --git a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj index 97976e33e50..8633e6ca188 100644 --- a/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj +++ b/src/tests/tracing/eventpipe/processenvironment/processenvironment.csproj @@ -8,8 +8,6 @@ true true true - - true From 31de746c6e2de0a5fecf52f39b3a74fb5e2fbdd6 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Fri, 25 Jun 2021 21:10:32 -0700 Subject: [PATCH 128/926] Move remote server redirect and decompression tests to HttpClientHandlerTest.RemoteServer.cs (#54738) * move remote redirect and decompression tests to HttpClientHandler_RemoteServerTest Co-authored-by: Geoffrey Kizer --- .../HttpClientHandlerTest.AutoRedirect.cs | 321 +------------- .../HttpClientHandlerTest.Decompression.cs | 90 ---- .../HttpClientHandlerTest.RemoteServer.cs | 398 ++++++++++++++++++ 3 files changed, 400 insertions(+), 409 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs index 75214845cc8..c9bbdbc0c13 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Net.Http.Headers; using System.Net.Sockets; using System.Net.Test.Common; using System.Threading.Tasks; @@ -12,32 +11,13 @@ using Xunit.Abstractions; namespace System.Net.Http.Functional.Tests { - using Configuration = System.Net.Test.Common.Configuration; - #if WINHTTPHANDLER_TEST using HttpClientHandler = System.Net.Http.WinHttpClientHandler; #endif public abstract class HttpClientHandlerTest_AutoRedirect : HttpClientHandlerTestBase { - private const string ExpectedContent = "Test content"; - private const string Username = "testuser"; - private const string Password = "password"; - - private readonly NetworkCredential _credential = new NetworkCredential(Username, Password); - - public static IEnumerable RemoteServersAndRedirectStatusCodes() - { - foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) - { - yield return new object[] { remoteServer, 300 }; - yield return new object[] { remoteServer, 301 }; - yield return new object[] { remoteServer, 302 }; - yield return new object[] { remoteServer, 303 }; - yield return new object[] { remoteServer, 307 }; - yield return new object[] { remoteServer, 308 }; - } - } + public HttpClientHandlerTest_AutoRedirect(ITestOutputHelper output) : base(output) { } public static IEnumerable RedirectStatusCodesOldMethodsNewMethods() { @@ -55,34 +35,6 @@ namespace System.Net.Http.Functional.Tests yield return new object[] { statusCode, "MYCUSTOMMETHOD", statusCode == 303 ? "GET" : "MYCUSTOMMETHOD" }; } } - public HttpClientHandlerTest_AutoRedirect(ITestOutputHelper output) : base(output) { } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = false; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(statusCode, (int)response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } [Theory, MemberData(nameof(RedirectStatusCodesOldMethodsNewMethods))] public async Task AllowAutoRedirect_True_ValidateNewMethodUsedOnRedirection( @@ -143,7 +95,7 @@ namespace System.Net.Http.Functional.Tests await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => { var request = new HttpRequestMessage(HttpMethod.Post, origUrl) { Version = UseVersion }; - request.Content = new StringContent(ExpectedContent); + request.Content = new StringContent("hello world"); request.Headers.TransferEncodingChunked = true; Task getResponseTask = client.SendAsync(TestAsync, request); @@ -192,76 +144,6 @@ namespace System.Net.Http.Functional.Tests } } - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClient(handler)) - { - Uri uri = Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteSecureHttp11Server.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.SecureRemoteEchoServer, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClient(handler)) - { - Uri uri = Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - [Fact] public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithoutLocation_ReturnsOriginalResponse() { @@ -288,99 +170,6 @@ namespace System.Net.Http.Functional.Tests } } - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersMemberData))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - Uri targetUri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: targetUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - Assert.Equal(targetUri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory] - [InlineData(3, 2)] - [InlineData(3, 3)] - [InlineData(3, 4)] - public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) - { - if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1703OrGreater) - { - // Skip this test if using WinHttpHandler but on a release prior to Windows 10 Creators Update. - _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.MaxAutomaticRedirections = maxHops; - using (HttpClient client = CreateHttpClient(handler)) - { - Task t = client.GetAsync(Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, - hops: hops)); - - if (hops <= maxHops) - { - using (HttpResponseMessage response = await t) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); - } - } - else - { - if (!IsWinHttpHandler) - { - using (HttpResponseMessage response = await t) - { - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - } - } - else - { - await Assert.ThrowsAsync(() => t); - } - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersMemberData))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: 302, - destinationUri: remoteServer.EchoUri, - hops: 1, - relative: true); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); - } - } - } - [Theory] [InlineData(200)] [InlineData(201)] @@ -476,111 +265,5 @@ namespace System.Net.Http.Functional.Tests }); } } - - [Theory, MemberData(nameof(RemoteServersMemberData))] - [OuterLoop("Uses external servers")] - public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: 302, - userName: Username, - password: Password); - using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); - } - } - } - - [Theory, MemberData(nameof(RemoteServersMemberData))] - [OuterLoop("Uses external servers")] - public async Task HttpClientHandler_CredentialIsNotCredentialCacheAfterRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: 302, - userName: Username, - password: Password); - using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); - } - - // Use the same handler to perform get request, authentication should succeed after redirect. - Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - using (HttpResponseMessage authResponse = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, authResponse.StatusCode); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); - Uri redirectUri = remoteServer.RedirectUriForCreds( - statusCode: statusCode, - userName: Username, - password: Password); - _output.WriteLine(uri.AbsoluteUri); - _output.WriteLine(redirectUri.AbsoluteUri); - var credentialCache = new CredentialCache(); - credentialCache.Add(uri, "Basic", _credential); - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = credentialCache; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - using (HttpResponseMessage response = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] - public async Task DefaultHeaders_SetCredentials_ClearedOnRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) - { - if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) - { - // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - string credentialString = _credential.UserName + ":" + _credential.Password; - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialString); - Uri uri = remoteServer.RedirectUriForDestinationUri( - statusCode: statusCode, - destinationUri: remoteServer.EchoUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - string responseText = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.False(TestHelper.JsonMessageContainsKey(responseText, "Authorization")); - } - } - } } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index f53b6fcb910..88ac334a06d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -13,8 +13,6 @@ using Xunit.Abstractions; namespace System.Net.Http.Functional.Tests { - using Configuration = System.Net.Test.Common.Configuration; - #if WINHTTPHANDLER_TEST using HttpClientHandler = System.Net.Http.WinHttpClientHandler; #endif @@ -28,17 +26,6 @@ namespace System.Net.Http.Functional.Tests #endif public HttpClientHandler_Decompression_Test(ITestOutputHelper output) : base(output) { } - public static IEnumerable RemoteServersAndCompressionUris() - { - foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) - { - yield return new object[] { remoteServer, remoteServer.GZipUri }; - - // Remote deflate endpoint isn't correctly following the deflate protocol. - //yield return new object[] { remoteServer, remoteServer.DeflateUri }; - } - } - [Theory] [InlineData("gzip", false)] [InlineData("gzip", true)] @@ -169,83 +156,6 @@ namespace System.Net.Http.Functional.Tests }); } - [OuterLoop("Uses external servers")] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] - public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_GZip(Configuration.Http.RemoteServer remoteServer, Uri uri) - { - // Sync API supported only up to HTTP/1.1 - if (!TestAsync && remoteServer.HttpVersion.Major >= 2) - { - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - { - using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion))) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - } - - // The remote server endpoint was written to use DeflateStream, which isn't actually a correct - // implementation of the deflate protocol (the deflate protocol requires the zlib wrapper around - // deflate). Until we can get that updated (and deal with previous releases still testing it - // via a DeflateStream-based implementation), we utilize httpbin.org to help validate behavior. - [OuterLoop("Uses external servers")] - [Theory] - [InlineData("http://httpbin.org/deflate", "\"deflated\": true")] - [InlineData("https://httpbin.org/deflate", "\"deflated\": true")] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_Deflate(string uri, string expectedContent) - { - if (IsWinHttpHandler) - { - // WinHttpHandler targets netstandard2.0 and still erroneously uses DeflateStream rather than ZlibStream for deflate. - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClient(handler)) - { - Assert.Contains(expectedContent, await client.GetStringAsync(uri)); - } - } - - [OuterLoop("Uses external servers")] - [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] - [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] - public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuration.Http.RemoteServer remoteServer, Uri uri) - { - // Sync API supported only up to HTTP/1.1 - if (!TestAsync && remoteServer.HttpVersion.Major >= 2) - { - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) - using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion), HttpCompletionOption.ResponseHeadersRead)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - Assert.False(response.Content.Headers.Contains("Content-Encoding"), "Content-Encoding unexpectedly found"); - Assert.False(response.Content.Headers.Contains("Content-Length"), "Content-Length unexpectedly found"); - } - } - [Theory] #if NETCOREAPP [InlineData(DecompressionMethods.Brotli, "br", "")] diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs index 732384e64ed..36d732d79ff 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -912,6 +913,403 @@ namespace System.Net.Http.Functional.Tests } } + public static IEnumerable RemoteServersAndRedirectStatusCodes() + { + foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) + { + yield return new object[] { remoteServer, 300 }; + yield return new object[] { remoteServer, 301 }; + yield return new object[] { remoteServer, 302 }; + yield return new object[] { remoteServer, 303 }; + yield return new object[] { remoteServer, 307 }; + yield return new object[] { remoteServer, 308 }; + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = false; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(statusCode, (int)response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Fact] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClient(handler)) + { + Uri uri = Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteSecureHttp11Server.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(Configuration.Http.SecureRemoteEchoServer, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Fact] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClient(handler)) + { + Uri uri = Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersMemberData))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + Uri targetUri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: targetUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Equal(targetUri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory] + [InlineData(3, 2)] + [InlineData(3, 3)] + [InlineData(3, 4)] + public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) + { + if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1703OrGreater) + { + // Skip this test if using WinHttpHandler but on a release prior to Windows 10 Creators Update. + _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.MaxAutomaticRedirections = maxHops; + using (HttpClient client = CreateHttpClient(handler)) + { + Task t = client.GetAsync(Configuration.Http.RemoteHttp11Server.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: Configuration.Http.RemoteHttp11Server.EchoUri, + hops: hops)); + + if (hops <= maxHops) + { + using (HttpResponseMessage response = await t) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); + } + } + else + { + if (!IsWinHttpHandler) + { + using (HttpResponseMessage response = await t) + { + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + } + } + else + { + await Assert.ThrowsAsync(() => t); + } + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersMemberData))] + public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: 302, + destinationUri: remoteServer.EchoUri, + hops: 1, + relative: true); + _output.WriteLine("Uri: {0}", uri); + + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(remoteServer.EchoUri, response.RequestMessage.RequestUri); + } + } + } + + [Theory, MemberData(nameof(RemoteServersMemberData))] + [OuterLoop("Uses external servers")] + public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = _credential; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: 302, + userName: Username, + password: Password); + using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); + } + } + } + + [Theory, MemberData(nameof(RemoteServersMemberData))] + [OuterLoop("Uses external servers")] + public async Task HttpClientHandler_CredentialIsNotCredentialCacheAfterRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer) + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = _credential; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: 302, + userName: Username, + password: Password); + using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); + } + + // Use the same handler to perform get request, authentication should succeed after redirect. + Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + using (HttpResponseMessage authResponse = await client.GetAsync(uri)) + { + Assert.Equal(HttpStatusCode.OK, authResponse.StatusCode); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + Uri uri = remoteServer.BasicAuthUriForCreds(userName: Username, password: Password); + Uri redirectUri = remoteServer.RedirectUriForCreds( + statusCode: statusCode, + userName: Username, + password: Password); + _output.WriteLine(uri.AbsoluteUri); + _output.WriteLine(redirectUri.AbsoluteUri); + var credentialCache = new CredentialCache(); + credentialCache.Add(uri, "Basic", _credential); + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.Credentials = credentialCache; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + using (HttpResponseMessage response = await client.GetAsync(redirectUri)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(uri, response.RequestMessage.RequestUri); + } + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + public async Task DefaultHeaders_SetCredentials_ClearedOnRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) + { + if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) + { + // 308 redirects are not supported on old versions of WinHttp, or on .NET Framework. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + string credentialString = _credential.UserName + ":" + _credential.Password; + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialString); + Uri uri = remoteServer.RedirectUriForDestinationUri( + statusCode: statusCode, + destinationUri: remoteServer.EchoUri, + hops: 1); + _output.WriteLine("Uri: {0}", uri); + using (HttpResponseMessage response = await client.GetAsync(uri)) + { + string responseText = await response.Content.ReadAsStringAsync(); + _output.WriteLine(responseText); + Assert.False(TestHelper.JsonMessageContainsKey(responseText, "Authorization")); + } + } + } + + public static IEnumerable RemoteServersAndCompressionUris() + { + foreach (Configuration.Http.RemoteServer remoteServer in Configuration.Http.RemoteServers) + { + yield return new object[] { remoteServer, remoteServer.GZipUri }; + + // Remote deflate endpoint isn't correctly following the deflate protocol. + //yield return new object[] { remoteServer, remoteServer.DeflateUri }; + } + } + + [OuterLoop("Uses external servers")] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] + public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_GZip(Configuration.Http.RemoteServer remoteServer, Uri uri) + { + // Sync API supported only up to HTTP/1.1 + if (!TestAsync && remoteServer.HttpVersion.Major >= 2) + { + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + { + using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion))) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseContent = await response.Content.ReadAsStringAsync(); + _output.WriteLine(responseContent); + TestHelper.VerifyResponseBody( + responseContent, + response.Content.Headers.ContentMD5, + false, + null); + } + } + } + + // The remote server endpoint was written to use DeflateStream, which isn't actually a correct + // implementation of the deflate protocol (the deflate protocol requires the zlib wrapper around + // deflate). Until we can get that updated (and deal with previous releases still testing it + // via a DeflateStream-based implementation), we utilize httpbin.org to help validate behavior. + [OuterLoop("Uses external servers")] + [Theory] + [InlineData("http://httpbin.org/deflate", "\"deflated\": true")] + [InlineData("https://httpbin.org/deflate", "\"deflated\": true")] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed_Deflate(string uri, string expectedContent) + { + if (IsWinHttpHandler) + { + // WinHttpHandler targets netstandard2.0 and still erroneously uses DeflateStream rather than ZlibStream for deflate. + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClient(handler)) + { + Assert.Contains(expectedContent, await client.GetStringAsync(uri)); + } + } + + [OuterLoop("Uses external servers")] + [Theory, MemberData(nameof(RemoteServersAndCompressionUris))] + [SkipOnPlatform(TestPlatforms.Browser, "AutomaticDecompression not supported on Browser")] + public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Configuration.Http.RemoteServer remoteServer, Uri uri) + { + // Sync API supported only up to HTTP/1.1 + if (!TestAsync && remoteServer.HttpVersion.Major >= 2) + { + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using (HttpClient client = CreateHttpClientForRemoteServer(remoteServer, handler)) + using (HttpResponseMessage response = await client.SendAsync(TestAsync, CreateRequest(HttpMethod.Get, uri, remoteServer.HttpVersion), HttpCompletionOption.ResponseHeadersRead)) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + Assert.False(response.Content.Headers.Contains("Content-Encoding"), "Content-Encoding unexpectedly found"); + Assert.False(response.Content.Headers.Contains("Content-Length"), "Content-Length unexpectedly found"); + } + } + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [Theory] [MemberData(nameof(Http2Servers))] From 15c2cc44d48dc25b71dd94fa700152790f80b643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Sat, 26 Jun 2021 03:30:00 -0400 Subject: [PATCH 129/926] Miscellaneous MetadataUpdater cleanups (#54751) --- .../tests/ApplyUpdateTest.cs | 15 +++++++---- .../tests/ApplyUpdateUtil.cs | 25 +++++++++++-------- .../Reflection/Metadata/MetadataUpdater.cs | 6 ++--- src/mono/mono/metadata/icall-decl.h | 2 +- src/mono/mono/metadata/icall.c | 6 +++-- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index e09eaa0a141..c2f06b86b93 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -14,10 +14,9 @@ namespace System.Reflection.Metadata /// testsuite runs each test in sequence, loading the corresponding /// assembly, applying an update to it and observing the results. [Collection(nameof(ApplyUpdateUtil.NoParallelTests))] - [ConditionalClass(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))] public class ApplyUpdateTest { - [Fact] + [ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] void StaticMethodBodyUpdate() { @@ -40,7 +39,7 @@ namespace System.Reflection.Metadata }); } - [Fact] + [ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/52993", TestRuntimes.Mono)] void ClassWithCustomAttributes() { @@ -122,12 +121,18 @@ namespace System.Reflection.Metadata Assert.Equal(typeof(string), result.GetType()); } - [Fact] - [ActiveIssue("Returns true on mono", TestRuntimes.Mono)] + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.TestUsingRemoteExecutor))] public static void IsSupported() { bool result = MetadataUpdater.IsSupported; Assert.False(result); } + + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.TestUsingLaunchEnvironment))] + public static void IsSupported2() + { + bool result = MetadataUpdater.IsSupported; + Assert.True(result); + } } } diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs index 235e94bc2d8..36d04c42604 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateUtil.cs @@ -19,20 +19,27 @@ namespace System.Reflection.Metadata /// /// We need: /// 1. Either DOTNET_MODIFIABLE_ASSEMBLIES=debug is set, or we can use the RemoteExecutor to run a child process with that environment; and, - /// 2. Either Mono in a supported configuration (interpreter as the execution engine, and the hot reload feature enabled), or CoreCLR; and, + /// 2. Either Mono in a supported configuration (interpreter as the execution engine, and the hot reload component enabled), or CoreCLR; and, /// 3. The test assemblies are compiled with Debug information (this is configured by setting EmitDebugInformation in ApplyUpdate\Directory.Build.props) public static bool IsSupported => (IsModifiableAssembliesSet || IsRemoteExecutorSupported) && (!IsMonoRuntime || IsSupportedMonoConfiguration); + /// true if the current runtime was not launched with the apropriate settings for applying + /// updates (DOTNET_MODIFIABLE_ASSEMBLIES unset), but we can use the remote executor to + /// launch a child process that has the right setting. + public static bool TestUsingRemoteExecutor => IsRemoteExecutorSupported && !IsModifiableAssembliesSet; + + /// true if the current runtime was launched with the appropriate settings for applying + /// updates (DOTNET_MODIFIABLE_ASSEMBLIES set, and if Mono, the interpreter is enabled). + public static bool TestUsingLaunchEnvironment => (!IsMonoRuntime || IsSupportedMonoConfiguration) && IsModifiableAssembliesSet; + public static bool IsModifiableAssembliesSet => String.Equals(DotNetModifiableAssembliesValue, Environment.GetEnvironmentVariable(DotNetModifiableAssembliesSwitch), StringComparison.InvariantCultureIgnoreCase); // static cctor for RemoteExecutor throws on wasm. public static bool IsRemoteExecutorSupported => !RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")) && RemoteExecutor.IsSupported; - // copied from https://github.com/dotnet/arcade/blob/6cc4c1e9e23d5e65e88a8a57216b3d91e9b3d8db/src/Microsoft.DotNet.XUnitExtensions/src/DiscovererHelpers.cs#L16-L17 - private static readonly Lazy s_isMonoRuntime = new Lazy(() => Type.GetType("Mono.RuntimeStructs") != null); - public static bool IsMonoRuntime => s_isMonoRuntime.Value; + public static bool IsMonoRuntime => PlatformDetection.IsMonoRuntime; private static readonly Lazy s_isSupportedMonoConfiguration = new Lazy(CheckSupportedMonoConfiguration); @@ -41,7 +48,7 @@ namespace System.Reflection.Metadata // Not every build of Mono supports ApplyUpdate internal static bool CheckSupportedMonoConfiguration() { - // check that interpreter is enabled, and the build has hot reload capabilities enabled. + // check that interpreter is enabled, and the build has hot reload capabilities enabled. var isInterp = RuntimeFeature.IsDynamicCodeSupported && !RuntimeFeature.IsDynamicCodeCompiled; return isInterp && HasApplyUpdateCapabilities(); } @@ -75,7 +82,7 @@ namespace System.Reflection.Metadata string basename = assm.Location; if (basename == "") basename = assm.GetName().Name + ".dll"; - Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {count}"); + Console.Error.WriteLine($"Applying metadata update for {basename}, revision {count}"); string dmeta_name = $"{basename}.{count}.dmeta"; string dil_name = $"{basename}.{count}.dil"; @@ -86,8 +93,6 @@ namespace System.Reflection.Metadata MetadataUpdater.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); } - internal static bool UseRemoteExecutor => !IsModifiableAssembliesSet; - internal static void AddRemoteInvokeOptions (ref RemoteInvokeOptions options) { options = options ?? new RemoteInvokeOptions(); @@ -101,7 +106,7 @@ namespace System.Reflection.Metadata public static void TestCase(Action testBody, RemoteInvokeOptions options = null) { - if (UseRemoteExecutor) + if (TestUsingRemoteExecutor) { Console.Error.WriteLine ($"Running test using RemoteExecutor"); AddRemoteInvokeOptions(ref options); @@ -123,7 +128,7 @@ namespace System.Reflection.Metadata string arg1, RemoteInvokeOptions options = null) { - if (UseRemoteExecutor) + if (TestUsingRemoteExecutor) { AddRemoteInvokeOptions(ref options); RemoteExecutor.Invoke(testBody, arg1, options).Dispose(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs index ea7035ee10a..973eb43d91c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs @@ -49,17 +49,17 @@ namespace System.Reflection.Metadata internal static string GetCapabilities() => s_ApplyUpdateCapabilities.Value; - public static bool IsSupported { get; } = ApplyUpdateEnabled() != 0; + public static bool IsSupported { get; } = ApplyUpdateEnabled(justComponentCheck: 0) != 0; private static Lazy s_ApplyUpdateCapabilities = new Lazy(() => InitializeApplyUpdateCapabilities()); private static string InitializeApplyUpdateCapabilities() { - return ApplyUpdateEnabled() != 0 ? "Baseline" : string.Empty ; + return ApplyUpdateEnabled(justComponentCheck: 1) != 0 ? "Baseline" : string.Empty ; } [MethodImpl (MethodImplOptions.InternalCall)] - private static extern int ApplyUpdateEnabled (); + private static extern int ApplyUpdateEnabled (int justComponentCheck); [MethodImpl (MethodImplOptions.InternalCall)] private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length); diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index b78d7c6a06d..d6d052b0d88 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -243,6 +243,6 @@ ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int #endif ICALL_EXPORT void ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len, gconstpointer dpdb_bytes, int32_t dpdb_len); -ICALL_EXPORT gint32 ves_icall_AssemblyExtensions_ApplyUpdateEnabled (void); +ICALL_EXPORT gint32 ves_icall_AssemblyExtensions_ApplyUpdateEnabled (gint32 just_component_check); #endif // __MONO_METADATA_ICALL_DECL_H__ diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index c6e3905e2da..51a8fc0a607 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5787,9 +5787,11 @@ ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, mono_error_set_pending_exception (error); } -gint32 ves_icall_AssemblyExtensions_ApplyUpdateEnabled (void) +gint32 ves_icall_AssemblyExtensions_ApplyUpdateEnabled (gint32 just_component_check) { - return mono_metadata_update_available () && mono_metadata_update_enabled (NULL); + // if just_component_check is true, we only care whether the hot_reload component is enabled, + // not whether the environment is appropriately setup to apply updates. + return mono_metadata_update_available () && (just_component_check || mono_metadata_update_enabled (NULL)); } MonoBoolean From c7e37b1f4101cab534b2f0d87652154274cb30d3 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 26 Jun 2021 00:52:38 -0700 Subject: [PATCH 130/926] Clean up some DI code (#54732) - Remove empty CreateInstanceCallSite file - Removed ServiceScopeFactoryCallsite and used ConstantCallsite instead. - Added fast path if the value is already cached on the callsite. --- .../src/CallSiteJsonFormatter.cs | 5 ----- .../src/ServiceLookup/CallSiteKind.cs | 4 ---- .../ServiceLookup/CallSiteRuntimeResolver.cs | 11 ++++++----- .../src/ServiceLookup/CallSiteValidator.cs | 4 +--- .../src/ServiceLookup/CallSiteVisitor.cs | 5 ----- .../src/ServiceLookup/ConstantCallSite.cs | 2 +- .../ServiceLookup/CreateInstanceCallSite.cs | 9 --------- .../Expressions/ExpressionResolverBuilder.cs | 5 ----- .../ILEmit/ILEmitResolverBuilder.cs | 6 ------ .../ServiceScopeFactoryCallSite.cs | 19 ------------------- .../src/ServiceProvider.cs | 2 +- 11 files changed, 9 insertions(+), 63 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CreateInstanceCallSite.cs delete mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs index a853a9fe5b5..a2b3d5f0952 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs @@ -81,11 +81,6 @@ namespace Microsoft.Extensions.DependencyInjection return null; } - protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, CallSiteFormatterContext argument) - { - return null; - } - protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, CallSiteFormatterContext argument) { argument.WriteProperty("itemType", enumerableCallSite.ItemType); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteKind.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteKind.cs index 83ba197ad88..112e9afc8fc 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteKind.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteKind.cs @@ -19,10 +19,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup Transient, - CreateInstance, - - ServiceScopeFactory, - Singleton } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs index e7ddb41fb08..fabaea9fbe7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs @@ -20,6 +20,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) { + // Fast path to avoid virtual calls if we already have the cached value in the root scope + if (scope.IsRootScope && callSite.Value is object cached) + { + return cached; + } + return VisitCallSite(callSite, new RuntimeResolverContext { Scope = scope @@ -153,11 +159,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup return context.Scope; } - protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context) - { - return serviceScopeFactoryCallSite.Value; - } - protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context) { var array = Array.CreateInstance( diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs index f836ba78b41..29a95f507d3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs @@ -78,7 +78,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup protected override Type VisitScopeCache(ServiceCallSite scopedCallSite, CallSiteValidatorState state) { // We are fine with having ServiceScopeService requested by singletons - if (scopedCallSite is ServiceScopeFactoryCallSite) + if (scopedCallSite.ServiceType == typeof(IServiceScopeFactory)) { return null; } @@ -100,8 +100,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup protected override Type VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, CallSiteValidatorState state) => null; - protected override Type VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, CallSiteValidatorState state) => null; - protected override Type VisitFactory(FactoryCallSite factoryCallSite, CallSiteValidatorState state) => null; internal struct CallSiteValidatorState diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteVisitor.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteVisitor.cs index 5308501c89d..b7143a2c577 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteVisitor.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteVisitor.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Runtime.CompilerServices; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { @@ -51,8 +50,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup return VisitConstant((ConstantCallSite)callSite, argument); case CallSiteKind.ServiceProvider: return VisitServiceProvider((ServiceProviderCallSite)callSite, argument); - case CallSiteKind.ServiceScopeFactory: - return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument); default: throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType())); } @@ -84,8 +81,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup protected abstract TResult VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, TArgument argument); - protected abstract TResult VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, TArgument argument); - protected abstract TResult VisitIEnumerable(IEnumerableCallSite enumerableCallSite, TArgument argument); protected abstract TResult VisitFactory(FactoryCallSite factoryCallSite, TArgument argument); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs index a27cb110389..bb822437a0e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs @@ -21,7 +21,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup Value = defaultValue; } - public override Type ServiceType => DefaultValue?.GetType() ?? _serviceType; + public override Type ServiceType => _serviceType; public override Type ImplementationType => DefaultValue?.GetType() ?? _serviceType; public override CallSiteKind Kind { get; } = CallSiteKind.Constant; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CreateInstanceCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CreateInstanceCallSite.cs deleted file mode 100644 index 6532a5a7728..00000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CreateInstanceCallSite.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.ExceptionServices; - -namespace Microsoft.Extensions.DependencyInjection.ServiceLookup -{ -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index 07ff50a44d6..76e54ec2814 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -117,11 +117,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup return ScopeParameter; } - protected override Expression VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, object context) - { - return Expression.Constant(serviceScopeFactoryCallSite.Value); - } - protected override Expression VisitFactory(FactoryCallSite factoryCallSite, object context) { return Expression.Invoke(Expression.Constant(factoryCallSite.Factory), ScopeParameter); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs index a9015e12a99..61690bdd451 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs @@ -194,12 +194,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup return null; } - protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ILEmitResolverBuilderContext argument) - { - AddConstant(argument, serviceScopeFactoryCallSite.Value); - return null; - } - protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, ILEmitResolverBuilderContext argument) { if (enumerableCallSite.ServiceCallSites.Length == 0) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs deleted file mode 100644 index 116c6037cab..00000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Extensions.DependencyInjection.ServiceLookup -{ - internal sealed class ServiceScopeFactoryCallSite : ServiceCallSite - { - public ServiceScopeFactoryCallSite(IServiceScopeFactory value) : base(ResultCache.None) - { - Value = value; - } - - public override Type ServiceType { get; } = typeof(IServiceScopeFactory); - public override Type ImplementationType { get; } = typeof(ServiceProviderEngine); - public override CallSiteKind Kind { get; } = CallSiteKind.ServiceScopeFactory; - } -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index fb7052ec8ea..b93a11d9566 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -41,7 +41,7 @@ namespace Microsoft.Extensions.DependencyInjection // The list of built in services that aren't part of the list of service descriptors // keep this in sync with CallSiteFactory.IsService CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); - CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite(Root)); + CallSiteFactory.Add(typeof(IServiceScopeFactory), new ConstantCallSite(typeof(IServiceScopeFactory), Root)); CallSiteFactory.Add(typeof(IServiceProviderIsService), new ConstantCallSite(typeof(IServiceProviderIsService), CallSiteFactory)); if (options.ValidateScopes) From 6cccee5f73dba2df84fb6b5ec28e4171a01578d2 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Sat, 26 Jun 2021 01:24:54 -0700 Subject: [PATCH 131/926] Extract fgMorph(Init/Copy)Block into their own classes. (#53983) * Rewrite fgMorph(Copy/Init)Block. * fix a last squash error. * fix nits --- src/coreclr/jit/CMakeLists.txt | 1 + src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 14 +- src/coreclr/jit/fgdiagnostic.cpp | 6 + src/coreclr/jit/gentree.cpp | 52 +- src/coreclr/jit/gentree.h | 8 +- src/coreclr/jit/morph.cpp | 1278 +----------------------- src/coreclr/jit/morphblock.cpp | 1551 ++++++++++++++++++++++++++++++ 8 files changed, 1623 insertions(+), 1289 deletions(-) create mode 100644 src/coreclr/jit/morphblock.cpp diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index f06c011e5b9..629ea1ae4bc 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -122,6 +122,7 @@ set( JIT_SOURCES lsra.cpp lsrabuild.cpp morph.cpp + morphblock.cpp objectalloc.cpp optcse.cpp optimizer.cpp diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 0093b0e0dbb..23aed4e317d 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -4684,7 +4684,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // local variable allocation on the stack. ObjectAllocator objectAllocator(this); // PHASE_ALLOCATE_OBJECTS - if (JitConfig.JitObjectStackAllocation() && opts.OptimizationEnabled()) + if (compObjectStackAllocation() && opts.OptimizationEnabled()) { objectAllocator.EnableObjectStackAllocation(); } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index fe3f987667d..fbafd6e9a84 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2333,6 +2333,8 @@ class Compiler friend class ObjectAllocator; friend class LocalAddressVisitor; friend struct GenTree; + friend class MorphInitBlockHelper; + friend class MorphCopyBlockHelper; #ifdef FEATURE_HW_INTRINSICS friend struct HWIntrinsicInfo; @@ -2850,8 +2852,8 @@ public: GenTree* ctxTree, void* compileTimeHandle); - GenTree* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); - GenTree* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); + GenTreeLclVar* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); + GenTreeLclVar* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET)); GenTreeLclVar* gtNewLclVarAddrNode(unsigned lclNum, var_types type = TYP_I_IMPL); GenTreeLclFld* gtNewLclFldAddrNode(unsigned lclNum, @@ -6017,8 +6019,7 @@ private: GenTree* fgMorphInitBlock(GenTree* tree); GenTree* fgMorphPromoteLocalInitBlock(GenTreeLclVar* destLclNode, GenTree* initVal, unsigned blockSize); GenTree* fgMorphGetStructAddr(GenTree** pTree, CORINFO_CLASS_HANDLE clsHnd, bool isRValue = false); - GenTree* fgMorphBlkNode(GenTree* tree, bool isDest); - GenTree* fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigned blockWidth, bool isDest); + GenTree* fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigned blockWidth, bool isBlkReqd); GenTree* fgMorphCopyBlock(GenTree* tree); GenTree* fgMorphForRegisterFP(GenTree* tree); GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr); @@ -9782,6 +9783,11 @@ public: return (JitConfig.JitEnregStructLocals() != 0); } + bool compObjectStackAllocation() + { + return (JitConfig.JitObjectStackAllocation() != 0); + } + // Returns true if the method returns a value in more than one return register, // it should replace/be merged with compMethodReturnsMultiRegRetType when #36868 is fixed. // The difference from original `compMethodReturnsMultiRegRetType` is in ARM64 SIMD* handling, diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index d284f075bd3..65934aad6ad 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -2992,6 +2992,12 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) } break; + case GT_ASG: + { + // Can't CSE dst. + assert((tree->gtGetOp1()->gtFlags & GTF_DONT_CSE) != 0); + break; + } default: break; } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index bb088e9dae9..a10afeb758b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6494,7 +6494,7 @@ GenTreeCall* Compiler::gtNewCallNode( return node; } -GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) +GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) { assert(type != TYP_VOID); // We need to ensure that all struct values are normalized. @@ -6514,7 +6514,7 @@ GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSE assert((type == varDsc->lvType) || simd12ToSimd16Widening || (lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (varDsc->lvType == TYP_BYREF))); } - GenTree* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs)); + GenTreeLclVar* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs)); /* Cannot have this assert because the inliner uses this function * to add temporaries */ @@ -6524,7 +6524,7 @@ GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSE return node; } -GenTree* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) +GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs)) { // We need to ensure that all struct values are normalized. // It might be nice to assert this in general, but we have assignments of int to long. @@ -6538,7 +6538,7 @@ GenTree* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSE } // This local variable node may later get transformed into a large node assert(GenTree::s_gtNodeSizes[LargeOpOpcode()] > GenTree::s_gtNodeSizes[GT_LCL_VAR]); - GenTree* node = + GenTreeLclVar* node = new (this, LargeOpOpcode()) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs) DEBUGARG(/*largeNode*/ true)); return node; } @@ -16611,7 +16611,27 @@ GenTreeLclVarCommon* GenTree::IsLocalAddrExpr() return nullptr; } -bool GenTree::IsLocalAddrExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, FieldSeqNode** pFldSeq) +//------------------------------------------------------------------------ +// IsLocalAddrExpr: finds if "this" is an address of a local var/fld. +// +// Arguments: +// comp - a compiler instance; +// pLclVarTree - [out] sets to the node indicating the local variable if found; +// pFldSeq - [out] sets to the field sequence representing the field, else null; +// pOffset - [out](optional) sets to the sum offset of the lcl/fld if found, +// note it does not include pLclVarTree->GetLclOffs(). +// +// Returns: +// Returns true if "this" represents the address of a local, or a field of a local. +// +// Notes: +// It is mostly used for optimizations but assertion propogation depends on it for correctness. +// So if this function does not recognize a def of a LCL_VAR we can have an incorrect optimization. +// +bool GenTree::IsLocalAddrExpr(Compiler* comp, + GenTreeLclVarCommon** pLclVarTree, + FieldSeqNode** pFldSeq, + ssize_t* pOffset /* = nullptr */) { if (OperGet() == GT_ADDR) { @@ -16645,23 +16665,33 @@ bool GenTree::IsLocalAddrExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, { if (AsOp()->gtOp1->OperGet() == GT_CNS_INT) { - if (AsOp()->gtOp1->AsIntCon()->gtFieldSeq == nullptr) + GenTreeIntCon* cnst = AsOp()->gtOp1->AsIntCon(); + if (cnst->gtFieldSeq == nullptr) { return false; } // Otherwise, prepend this field to whatever we've already accumulated outside in. - *pFldSeq = comp->GetFieldSeqStore()->Append(AsOp()->gtOp1->AsIntCon()->gtFieldSeq, *pFldSeq); - return AsOp()->gtOp2->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq); + *pFldSeq = comp->GetFieldSeqStore()->Append(cnst->gtFieldSeq, *pFldSeq); + if (pOffset != nullptr) + { + *pOffset += cnst->IconValue(); + } + return AsOp()->gtOp2->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq, pOffset); } else if (AsOp()->gtOp2->OperGet() == GT_CNS_INT) { - if (AsOp()->gtOp2->AsIntCon()->gtFieldSeq == nullptr) + GenTreeIntCon* cnst = AsOp()->gtOp2->AsIntCon(); + if (cnst->gtFieldSeq == nullptr) { return false; } // Otherwise, prepend this field to whatever we've already accumulated outside in. - *pFldSeq = comp->GetFieldSeqStore()->Append(AsOp()->gtOp2->AsIntCon()->gtFieldSeq, *pFldSeq); - return AsOp()->gtOp1->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq); + *pFldSeq = comp->GetFieldSeqStore()->Append(cnst->gtFieldSeq, *pFldSeq); + if (pOffset != nullptr) + { + *pOffset += cnst->IconValue(); + } + return AsOp()->gtOp1->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq, pOffset); } } // Otherwise... diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e46e33de6fe..be5d7769cbb 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1982,10 +1982,10 @@ public: // variable, or just a portion of it. bool DefinesLocal(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, bool* pIsEntire = nullptr); - // Returns true if "this" represents the address of a local, or a field of a local. If returns true, sets - // "*pLclVarTree" to the node indicating the local variable. If the address is that of a field of this node, - // sets "*pFldSeq" to the field sequence representing that field, else null. - bool IsLocalAddrExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, FieldSeqNode** pFldSeq); + bool IsLocalAddrExpr(Compiler* comp, + GenTreeLclVarCommon** pLclVarTree, + FieldSeqNode** pFldSeq, + ssize_t* pOffset = nullptr); // Simpler variant of the above which just returns the local node if this is an expression that // yields an address into a local diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 9dc3020c15d..7cc40e6144e 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8482,7 +8482,7 @@ GenTree* Compiler::getRuntimeLookupTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, ArrayStack stmts(getAllocator(CMK_ArrayStack)); - auto cloneTree = [&](GenTree** tree DEBUGARG(const char* reason)) { + auto cloneTree = [&](GenTree** tree DEBUGARG(const char* reason)) -> GenTree* { if (!((*tree)->gtFlags & GTF_GLOB_EFFECT)) { GenTree* clone = gtClone(*tree, true); @@ -9669,6 +9669,7 @@ GenTreeLclVar* Compiler::fgMorphTryFoldObjAsLclVar(GenTreeObj* obj) if (opts.OptimizationEnabled()) { GenTree* op1 = obj->Addr(); + assert(!op1->OperIs(GT_LCL_VAR_ADDR) && "missed an opt opportunity"); if (op1->OperIs(GT_ADDR)) { GenTreeUnOp* addr = op1->AsUnOp(); @@ -9833,7 +9834,7 @@ void Compiler::fgAssignSetVarDef(GenTree* tree) // Notes: // If successful, this method always returns the incoming tree, modifying only // its arguments. - +// GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree) { // This must be a block assignment. @@ -9883,6 +9884,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree) if (isCopyBlock && destLclVarTree == nullptr && !src->OperIs(GT_LCL_VAR)) { fgMorphBlockOperand(src, asgType, genTypeSize(asgType), false /*isBlkReqd*/); + dest->gtFlags |= GTF_DONT_CSE; return tree; } } @@ -10233,136 +10235,6 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree) return nullptr; } -//------------------------------------------------------------------------ -// fgMorphInitBlock: Morph a block initialization assignment tree. -// -// Arguments: -// tree - A GT_ASG tree that performs block initialization -// -// Return Value: -// A single assignment, when fgMorphOneAsgBlockOp transforms it. -// -// If the destination is a promoted struct local variable then we will try to -// perform a field by field assignment for each of the promoted struct fields. -// This is not always possible (e.g. if the struct has holes and custom layout). -// -// Otherwise the orginal GT_ASG tree is returned unmodified (always correct but -// least desirable because it prevents enregistration and/or blocks independent -// struct promotion). -// -// Assumptions: -// GT_ASG's children have already been morphed. -// -GenTree* Compiler::fgMorphInitBlock(GenTree* tree) -{ - // We must have the GT_ASG form of InitBlkOp. - noway_assert((tree->OperGet() == GT_ASG) && tree->OperIsInitBlkOp()); -#ifdef DEBUG - bool morphed = false; -#endif // DEBUG - - GenTree* src = tree->gtGetOp2(); - GenTree* origDest = tree->gtGetOp1(); - - GenTree* dest = fgMorphBlkNode(origDest, true); - if (dest != origDest) - { - tree->AsOp()->gtOp1 = dest; - } - tree->gtType = dest->TypeGet(); - JITDUMP("\nfgMorphInitBlock:"); - - GenTree* oneAsgTree = fgMorphOneAsgBlockOp(tree); - if (oneAsgTree) - { - JITDUMP(" using oneAsgTree.\n"); - tree = oneAsgTree; - } - else - { - GenTreeLclVarCommon* destLclNode = nullptr; - unsigned destLclNum = BAD_VAR_NUM; - LclVarDsc* destLclVar = nullptr; - GenTree* initVal = src->OperIsInitVal() ? src->gtGetOp1() : src; - unsigned blockSize = 0; - - if (dest->IsLocal()) - { - destLclNode = dest->AsLclVarCommon(); - destLclNum = destLclNode->GetLclNum(); - destLclVar = lvaGetDesc(destLclNum); - blockSize = varTypeIsStruct(destLclVar) ? destLclVar->lvExactSize : genTypeSize(destLclVar->TypeGet()); - } - else - { - blockSize = dest->AsBlk()->Size(); - - FieldSeqNode* destFldSeq = nullptr; - if (dest->AsIndir()->Addr()->IsLocalAddrExpr(this, &destLclNode, &destFldSeq)) - { - destLclNum = destLclNode->GetLclNum(); - destLclVar = lvaGetDesc(destLclNum); - } - } - - bool destDoFldAsg = false; - - if (destLclNum != BAD_VAR_NUM) - { -#if LOCAL_ASSERTION_PROP - // Kill everything about destLclNum (and its field locals) - if (optLocalAssertionProp && (optAssertionCount > 0)) - { - fgKillDependentAssertions(destLclNum DEBUGARG(tree)); - } -#endif // LOCAL_ASSERTION_PROP - - // If we have already determined that a promoted TYP_STRUCT lclVar will not be enregistered, - // we are better off doing a block init. - if (destLclVar->lvPromoted && (!destLclVar->lvDoNotEnregister || !destLclNode->TypeIs(TYP_STRUCT))) - { - GenTree* newTree = fgMorphPromoteLocalInitBlock(destLclNode->AsLclVar(), initVal, blockSize); - - if (newTree != nullptr) - { - tree = newTree; - destDoFldAsg = true; - INDEBUG(morphed = true); - } - } - - // If destLclVar is not a reg-sized non-field-addressed struct, set it as DoNotEnregister. - if (!destDoFldAsg && !destLclVar->lvRegStruct) - { - lvaSetVarDoNotEnregister(destLclNum DEBUGARG(DNER_BlockOp)); - } - } - - if (!destDoFldAsg) - { - // For an InitBlock we always require a block operand. - dest = fgMorphBlockOperand(dest, dest->TypeGet(), blockSize, true /*isBlkReqd*/); - tree->AsOp()->gtOp1 = dest; - tree->gtFlags |= (dest->gtFlags & GTF_ALL_EFFECT); - } - } - -#ifdef DEBUG - if (morphed) - { - tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; - - if (verbose) - { - printf("fgMorphInitBlock (after):\n"); - gtDispTree(tree); - } - } -#endif - - return tree; -} - //------------------------------------------------------------------------ // fgMorphPromoteLocalInitBlock: Attempts to promote a local block init tree // to a tree of promoted field initialization assignments. @@ -10426,6 +10298,7 @@ GenTree* Compiler::fgMorphPromoteLocalInitBlock(GenTreeLclVar* destLclNode, GenT if (destLclVar->lvCustomLayout && destLclVar->lvContainsHoles) { + // TODO-1stClassStructs: there are no reasons for this pessimization, delete it. JITDUMP(" dest has custom layout and contains holes.\n"); return nullptr; } @@ -10616,143 +10489,6 @@ GenTree* Compiler::fgMorphGetStructAddr(GenTree** pTree, CORINFO_CLASS_HANDLE cl return addr; } -//------------------------------------------------------------------------ -// fgMorphBlkNode: Morph a block node preparatory to morphing a block assignment -// -// Arguments: -// tree - The struct type node -// isDest - True if this is the destination of the assignment -// -// Return Value: -// Returns the possibly-morphed node. The caller is responsible for updating -// the parent of this node.. - -GenTree* Compiler::fgMorphBlkNode(GenTree* tree, bool isDest) -{ - JITDUMP("fgMorphBlkNode for %s tree, before:\n", (isDest ? "dst" : "src")); - DISPTREE(tree); - GenTree* handleTree = nullptr; - GenTree* addr = nullptr; - if (tree->OperIs(GT_COMMA)) - { - // In order to CSE and value number array index expressions and bounds checks, - // the commas in which they are contained need to match. - // The pattern is that the COMMA should be the address expression. - // Therefore, we insert a GT_ADDR just above the node, and wrap it in an obj or ind. - // TODO-1stClassStructs: Consider whether this can be improved. - // Example: - // before: [3] comma struct <- [2] comma struct <- [1] LCL_VAR struct - // after: [3] comma byref <- [2] comma byref <- [4] addr byref <- [1] LCL_VAR struct - - addr = tree; - GenTree* effectiveVal = tree->gtEffectiveVal(); - - GenTreePtrStack commas(getAllocator(CMK_ArrayStack)); - for (GenTree* comma = tree; comma != nullptr && comma->gtOper == GT_COMMA; comma = comma->gtGetOp2()) - { - commas.Push(comma); - } - - GenTree* lastComma = commas.Top(); - noway_assert(lastComma->gtGetOp2() == effectiveVal); - GenTree* effectiveValAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal); -#ifdef DEBUG - effectiveValAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif - lastComma->AsOp()->gtOp2 = effectiveValAddr; - - while (!commas.Empty()) - { - GenTree* comma = commas.Pop(); - comma->gtType = TYP_BYREF; - gtUpdateNodeSideEffects(comma); - } - - handleTree = effectiveVal; - } - else if (tree->OperIs(GT_IND) && tree->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR)) - { - handleTree = tree; - addr = tree->AsIndir()->Addr(); - } - - if (addr != nullptr) - { - var_types structType = handleTree->TypeGet(); - if (structType == TYP_STRUCT) - { - CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(handleTree); - if (structHnd == NO_CLASS_HANDLE) - { - tree = gtNewOperNode(GT_IND, structType, addr); - } - else - { - tree = gtNewObjNode(structHnd, addr); - gtSetObjGcInfo(tree->AsObj()); - } - } - else - { - tree = new (this, GT_BLK) GenTreeBlk(GT_BLK, structType, addr, typGetBlkLayout(genTypeSize(structType))); - } - - gtUpdateNodeSideEffects(tree); -#ifdef DEBUG - tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif - } - - if (!tree->OperIsBlk()) - { - JITDUMP("fgMorphBlkNode after:\n"); - DISPTREE(tree); - return tree; - } - GenTreeBlk* blkNode = tree->AsBlk(); - if (blkNode->OperGet() == GT_DYN_BLK) - { - if (blkNode->AsDynBlk()->gtDynamicSize->IsCnsIntOrI()) - { - unsigned size = (unsigned)blkNode->AsDynBlk()->gtDynamicSize->AsIntConCommon()->IconValue(); - // A GT_BLK with size of zero is not supported, - // so if we encounter such a thing we just leave it as a GT_DYN_BLK - if (size != 0) - { - blkNode->AsDynBlk()->gtDynamicSize = nullptr; - blkNode->ChangeOper(GT_BLK); - blkNode->SetLayout(typGetBlkLayout(size)); - } - else - { - JITDUMP("fgMorphBlkNode after, DYN_BLK with zero size can't be morphed:\n"); - DISPTREE(blkNode); - return blkNode; - } - } - else - { - JITDUMP("fgMorphBlkNode after, DYN_BLK with non-const size can't be morphed:\n"); - DISPTREE(blkNode); - return blkNode; - } - } - GenTree* blkSrc = blkNode->Addr(); - assert(blkSrc != nullptr); - if (!blkNode->TypeIs(TYP_STRUCT) && blkSrc->OperIs(GT_ADDR) && blkSrc->gtGetOp1()->OperIs(GT_LCL_VAR)) - { - GenTreeLclVarCommon* lclVarNode = blkSrc->gtGetOp1()->AsLclVarCommon(); - if ((genTypeSize(blkNode) != genTypeSize(lclVarNode)) || (!isDest && !varTypeIsStruct(lclVarNode))) - { - lvaSetVarDoNotEnregister(lclVarNode->GetLclNum() DEBUG_ARG(DNER_VMNeedsStackAddr)); - } - } - - JITDUMP("fgMorphBlkNode after:\n"); - DISPTREE(tree); - return tree; -} - //------------------------------------------------------------------------ // fgMorphBlockOperand: Canonicalize an operand of a block assignment // @@ -10903,1003 +10639,6 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne return tree; } -//------------------------------------------------------------------------ -// fgMorphCopyBlock: Perform the Morphing of block copy -// -// Arguments: -// tree - a block copy (i.e. an assignment with a block op on the lhs). -// -// Return Value: -// We can return the orginal block copy unmodified (least desirable, but always correct) -// We can return a single assignment, when fgMorphOneAsgBlockOp transforms it (most desirable). -// If we have performed struct promotion of the Source() or the Dest() then we will try to -// perform a field by field assignment for each of the promoted struct fields. -// -// Assumptions: -// The child nodes for tree have already been Morphed. -// -// Notes: -// If we leave it as a block copy we will call lvaSetVarDoNotEnregister() on both Source() and Dest(). -// When performing a field by field assignment we can have one of Source() or Dest treated as a blob of bytes -// and in such cases we will call lvaSetVarDoNotEnregister() on the one treated as a blob of bytes. -// if the Source() or Dest() is a a struct that has a "CustomLayout" and "ConstainsHoles" then we -// can not use a field by field assignment and must leave the orginal block copy unmodified. - -GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) -{ - noway_assert(tree->OperIsCopyBlkOp()); - - JITDUMP("fgMorphCopyBlock:\n"); - - bool isLateArg = (tree->gtFlags & GTF_LATE_ARG) != 0; - - GenTreeOp* asg = tree->AsOp(); - GenTree* src = asg->gtGetOp2(); - GenTree* dest = asg->gtGetOp1(); - -#if FEATURE_MULTIREG_RET - // If this is a multi-reg return, we will not do any morphing of this node. - if (src->IsMultiRegCall()) - { - assert(dest->OperGet() == GT_LCL_VAR); - JITDUMP(" not morphing a multireg call return\n"); - return tree; - } - else if (dest->IsMultiRegLclVar() && !src->IsMultiRegNode()) - { - dest->AsLclVar()->ClearMultiReg(); - } -#endif // FEATURE_MULTIREG_RET - - if (src->IsCall()) - { - if (dest->OperIs(GT_OBJ)) - { - GenTreeLclVar* lclVar = fgMorphTryFoldObjAsLclVar(dest->AsObj()); - if (lclVar != nullptr) - { - dest = lclVar; - asg->gtOp1 = lclVar; - } - } - - if (dest->OperIs(GT_LCL_VAR)) - { - LclVarDsc* varDsc = lvaGetDesc(dest->AsLclVar()); - if (varTypeIsStruct(varDsc) && varDsc->CanBeReplacedWithItsField(this)) - { - JITDUMP(" not morphing a single reg call return\n"); - return tree; - } - } - } - - // If we have an array index on the lhs, we need to create an obj node. - - dest = fgMorphBlkNode(dest, true); - if (dest != asg->gtGetOp1()) - { - asg->gtOp1 = dest; - if (dest->IsLocal()) - { - dest->gtFlags |= GTF_VAR_DEF; - } - } -#ifdef DEBUG - if (asg->TypeGet() != dest->TypeGet()) - { - JITDUMP("changing type of dest from %-6s to %-6s\n", varTypeName(asg->TypeGet()), varTypeName(dest->TypeGet())); - } -#endif - asg->ChangeType(dest->TypeGet()); - src = fgMorphBlkNode(src, false); - - asg->gtOp2 = src; - - GenTree* oldTree = tree; - GenTree* oneAsgTree = fgMorphOneAsgBlockOp(tree); - - if (oneAsgTree) - { - JITDUMP(" using oneAsgTree.\n"); - tree = oneAsgTree; - } - else - { - unsigned blockWidth; - bool blockWidthIsConst = false; - GenTreeLclVarCommon* lclVarTree = nullptr; - GenTreeLclVarCommon* srcLclVarTree = nullptr; - unsigned destLclNum = BAD_VAR_NUM; - unsigned modifiedLclNum = BAD_VAR_NUM; - LclVarDsc* destLclVar = nullptr; - FieldSeqNode* destFldSeq = nullptr; - unsigned destLclOffset = 0; - bool destDoFldAsg = false; - GenTree* destAddr = nullptr; - GenTree* srcAddr = nullptr; - bool destOnStack = false; - bool hasGCPtrs = false; - - JITDUMP("block assignment to morph:\n"); - DISPTREE(asg); - - if (dest->IsLocal()) - { - blockWidthIsConst = true; - destOnStack = true; - modifiedLclNum = dest->AsLclVarCommon()->GetLclNum(); - if (dest->gtOper == GT_LCL_VAR) - { - lclVarTree = dest->AsLclVarCommon(); - destLclNum = modifiedLclNum; - destLclVar = &lvaTable[destLclNum]; - if (destLclVar->lvType == TYP_STRUCT) - { - // It would be nice if lvExactSize always corresponded to the size of the struct, - // but it doesn't always for the temps that the importer creates when it spills side - // effects. - // TODO-Cleanup: Determine when this happens, and whether it can be changed. - blockWidth = info.compCompHnd->getClassSize(destLclVar->GetStructHnd()); - } - else - { - blockWidth = genTypeSize(destLclVar->lvType); - } - hasGCPtrs = destLclVar->HasGCPtr(); - } - else - { - assert(dest->TypeGet() != TYP_STRUCT); - assert(dest->gtOper == GT_LCL_FLD); - GenTreeLclFld* destFld = dest->AsLclFld(); - blockWidth = genTypeSize(destFld->TypeGet()); - destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, destFld); - destFldSeq = destFld->GetFieldSeq(); - destLclOffset = destFld->GetLclOffs(); - } - } - else - { - GenTree* effectiveDest = dest->gtEffectiveVal(); - if (effectiveDest->OperGet() == GT_IND) - { - assert(dest->TypeGet() != TYP_STRUCT); - blockWidth = genTypeSize(effectiveDest->TypeGet()); - blockWidthIsConst = true; - if ((dest == effectiveDest) && ((dest->gtFlags & GTF_IND_ARR_INDEX) == 0)) - { - destAddr = dest->gtGetOp1(); - } - } - else - { - assert(effectiveDest->OperIsBlk()); - GenTreeBlk* blk = effectiveDest->AsBlk(); - - blockWidth = blk->Size(); - blockWidthIsConst = (blk->gtOper != GT_DYN_BLK); - if ((dest == effectiveDest) && ((dest->gtFlags & GTF_IND_ARR_INDEX) == 0)) - { - destAddr = blk->Addr(); - } - } - if (destAddr != nullptr) - { - noway_assert(destAddr->TypeGet() == TYP_BYREF || destAddr->TypeGet() == TYP_I_IMPL); - if (destAddr->IsLocalAddrExpr(this, &lclVarTree, &destFldSeq)) - { - destOnStack = true; - destLclNum = lclVarTree->GetLclNum(); - modifiedLclNum = destLclNum; - destLclVar = &lvaTable[destLclNum]; - destLclOffset = lclVarTree->GetLclOffs(); - } - } - } - -#if LOCAL_ASSERTION_PROP - // Kill everything about modifiedLclNum (and its field locals) - if ((modifiedLclNum != BAD_VAR_NUM) && optLocalAssertionProp) - { - if (optAssertionCount > 0) - { - fgKillDependentAssertions(modifiedLclNum DEBUGARG(tree)); - } - } -#endif // LOCAL_ASSERTION_PROP - - if (destLclVar != nullptr) - { - if (destLclVar->lvPromoted && blockWidthIsConst) - { - noway_assert(varTypeIsStruct(destLclVar)); - noway_assert(!opts.MinOpts()); - - if (blockWidth == destLclVar->lvExactSize) - { - JITDUMP(" (destDoFldAsg=true)"); - // We may decide later that a copyblk is required when this struct has holes - destDoFldAsg = true; - } - else - { - JITDUMP(" with mismatched dest size"); - } - } - } - - FieldSeqNode* srcFldSeq = nullptr; - unsigned srcLclNum = BAD_VAR_NUM; - LclVarDsc* srcLclVar = nullptr; - unsigned srcLclOffset = 0; - bool srcDoFldAsg = false; - - bool srcUseLclFld = false; - bool destUseLclFld = false; - - if (src->IsLocal()) - { - srcLclVarTree = src->AsLclVarCommon(); - srcLclNum = srcLclVarTree->GetLclNum(); - if (src->OperGet() == GT_LCL_FLD) - { - srcFldSeq = src->AsLclFld()->GetFieldSeq(); - } - } - else if (src->OperIsIndir()) - { - if (src->AsOp()->gtOp1->IsLocalAddrExpr(this, &srcLclVarTree, &srcFldSeq)) - { - srcLclNum = srcLclVarTree->GetLclNum(); - } - else - { - srcAddr = src->AsOp()->gtOp1; - } - } - - if (srcLclNum != BAD_VAR_NUM) - { - srcLclOffset = srcLclVarTree->GetLclOffs(); - srcLclVar = &lvaTable[srcLclNum]; - - if (srcLclVar->lvPromoted && blockWidthIsConst) - { - noway_assert(varTypeIsStruct(srcLclVar)); - noway_assert(!opts.MinOpts()); - - if (blockWidth == srcLclVar->lvExactSize) - { - JITDUMP(" (srcDoFldAsg=true)"); - // We may decide later that a copyblk is required when this struct has holes - srcDoFldAsg = true; - } - else - { - JITDUMP(" with mismatched src size"); - } - } - } - - // Check to see if we are doing a copy to/from the same local block. - // If so, morph it to a nop. - if ((destLclVar != nullptr) && (srcLclVar == destLclVar) && (destFldSeq == srcFldSeq) && - destFldSeq != FieldSeqStore::NotAField()) - { - JITDUMP("Self-copy; replaced with a NOP.\n"); - GenTree* nop = gtNewNothingNode(); - INDEBUG(nop->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - return nop; - } - - // Check to see if we are required to do a copy block because the struct contains holes - // and either the src or dest is externally visible - // - bool requiresCopyBlock = false; - bool srcSingleLclVarAsg = false; - bool destSingleLclVarAsg = false; - - // If either src or dest is a reg-sized non-field-addressed struct, keep the copyBlock. - if ((destLclVar != nullptr && destLclVar->lvRegStruct) || (srcLclVar != nullptr && srcLclVar->lvRegStruct)) - { - requiresCopyBlock = true; - } - - // Can we use field by field assignment for the dest? - if (destDoFldAsg && destLclVar->lvCustomLayout && destLclVar->lvContainsHoles) - { - JITDUMP(" dest contains custom layout and contains holes"); - // C++ style CopyBlock with holes - requiresCopyBlock = true; - } - - // Can we use field by field assignment for the src? - if (srcDoFldAsg && srcLclVar->lvCustomLayout && srcLclVar->lvContainsHoles) - { - JITDUMP(" src contains custom layout and contains holes"); - // C++ style CopyBlock with holes - requiresCopyBlock = true; - } - -#if defined(TARGET_ARM) - if ((src->OperIsIndir()) && (src->gtFlags & GTF_IND_UNALIGNED)) - { - JITDUMP(" src is unaligned"); - requiresCopyBlock = true; - } - - if (asg->gtFlags & GTF_BLK_UNALIGNED) - { - JITDUMP(" asg is unaligned"); - requiresCopyBlock = true; - } -#endif // TARGET_ARM - - // Don't use field by field assignment if the src is a call, - // lowering will handle it without spilling the call result into memory - // to access the individual fields. - // - if (src->OperGet() == GT_CALL) - { - JITDUMP(" src is a call"); - requiresCopyBlock = true; - } - - // If we passed the above checks, then we will check these two - if (!requiresCopyBlock) - { - // It is not always profitable to do field by field init for structs that are allocated to memory. - // A struct with 8 bool fields will require 8 moves instead of one if we do this transformation. - // A simple heuristic when field by field copy is prefered: - // - if fields can be enregistered; - // - if the struct has GCPtrs (block copy would be done via helper that is expensive); - // - if the struct has only one field. - bool dstFldIsProfitable = - ((destLclVar != nullptr) && - (!destLclVar->lvDoNotEnregister || destLclVar->HasGCPtr() || (destLclVar->lvFieldCnt == 1))); - bool srcFldIsProfitable = - ((srcLclVar != nullptr) && - (!srcLclVar->lvDoNotEnregister || srcLclVar->HasGCPtr() || (srcLclVar->lvFieldCnt == 1))); - // Are both dest and src promoted structs? - if (destDoFldAsg && srcDoFldAsg && (dstFldIsProfitable || srcFldIsProfitable)) - { - // Both structs should be of the same type, or have the same number of fields of the same type. - // If not we will use a copy block. - bool misMatchedTypes = false; - if (destLclVar->GetStructHnd() != srcLclVar->GetStructHnd()) - { - if (destLclVar->lvFieldCnt != srcLclVar->lvFieldCnt) - { - misMatchedTypes = true; - } - else - { - for (int i = 0; i < destLclVar->lvFieldCnt; i++) - { - LclVarDsc* destFieldVarDsc = lvaGetDesc(destLclVar->lvFieldLclStart + i); - LclVarDsc* srcFieldVarDsc = lvaGetDesc(srcLclVar->lvFieldLclStart + i); - if ((destFieldVarDsc->lvType != srcFieldVarDsc->lvType) || - (destFieldVarDsc->lvFldOffset != srcFieldVarDsc->lvFldOffset)) - { - misMatchedTypes = true; - break; - } - } - } - if (misMatchedTypes) - { - requiresCopyBlock = true; // Mismatched types, leave as a CopyBlock - JITDUMP(" with mismatched types"); - } - } - } - else if (destDoFldAsg && dstFldIsProfitable) - { - // Match the following kinds of trees: - // fgMorphTree BB01, stmt 9 (before) - // [000052] ------------ const int 8 - // [000053] -A--G------- copyBlk void - // [000051] ------------ addr byref - // [000050] ------------ lclVar long V07 loc5 - // [000054] --------R--- void - // [000049] ------------ addr byref - // [000048] ------------ lclVar struct(P) V06 loc4 - // long V06.h (offs=0x00) -> V17 tmp9 - // Yields this transformation - // fgMorphCopyBlock (after): - // [000050] ------------ lclVar long V07 loc5 - // [000085] -A---------- = long - // [000083] D------N---- lclVar long V17 tmp9 - // - if (blockWidthIsConst && (destLclVar->lvFieldCnt == 1) && (srcLclVar != nullptr) && - (blockWidth == genTypeSize(srcLclVar->TypeGet()))) - { - // Reject the following tree: - // - seen on x86chk jit\jit64\hfa\main\hfa_sf3E_r.exe - // - // fgMorphTree BB01, stmt 6 (before) - // [000038] ------------- const int 4 - // [000039] -A--G-------- copyBlk void - // [000037] ------------- addr byref - // [000036] ------------- lclVar int V05 loc3 - // [000040] --------R---- void - // [000035] ------------- addr byref - // [000034] ------------- lclVar struct(P) V04 loc2 - // float V04.f1 (offs=0x00) -> V13 tmp6 - // As this would framsform into - // float V13 = int V05 - // - unsigned fieldLclNum = lvaTable[destLclNum].lvFieldLclStart; - var_types destType = lvaTable[fieldLclNum].TypeGet(); - if (srcLclVar->TypeGet() == destType) - { - srcSingleLclVarAsg = true; - } - } - } - else if (srcDoFldAsg && srcFldIsProfitable) - { - // Check for the symmetric case (which happens for the _pointer field of promoted spans): - // - // [000240] -----+------ /--* lclVar struct(P) V18 tmp9 - // /--* byref V18._value (offs=0x00) -> V30 tmp21 - // [000245] -A------R--- * = struct (copy) - // [000244] -----+------ \--* obj(8) struct - // [000243] -----+------ \--* addr byref - // [000242] D----+-N---- \--* lclVar byref V28 tmp19 - // - if (blockWidthIsConst && (srcLclVar->lvFieldCnt == 1) && (destLclVar != nullptr) && - (blockWidth == genTypeSize(destLclVar->TypeGet()))) - { - // Check for type agreement - unsigned fieldLclNum = lvaTable[srcLclNum].lvFieldLclStart; - var_types srcType = lvaTable[fieldLclNum].TypeGet(); - if (destLclVar->TypeGet() == srcType) - { - destSingleLclVarAsg = true; - } - } - } - // Are neither dest or src promoted structs? - else - { - assert(!(destDoFldAsg && dstFldIsProfitable) && !(srcDoFldAsg && srcFldIsProfitable)); - requiresCopyBlock = true; // Leave as a CopyBlock - JITDUMP(" with no promoted structs"); - } - } - - // If we require a copy block the set both of the field assign bools to false - if (requiresCopyBlock) - { - // If a copy block is required then we won't do field by field assignments - destDoFldAsg = false; - srcDoFldAsg = false; - } - - JITDUMP(requiresCopyBlock ? " this requires a CopyBlock.\n" : " using field by field assignments.\n"); - - // Mark the dest/src structs as DoNotEnreg when they are not being fully referenced as the same type. - // - if (!destDoFldAsg && (destLclVar != nullptr) && !destSingleLclVarAsg) - { - if (!destLclVar->lvRegStruct || (destLclVar->lvType != dest->TypeGet())) - { - if (!dest->IsMultiRegLclVar() || (blockWidth != destLclVar->lvExactSize) || - (destLclVar->lvCustomLayout && destLclVar->lvContainsHoles)) - { - // Mark it as DoNotEnregister. - lvaSetVarDoNotEnregister(destLclNum DEBUGARG(DNER_BlockOp)); - } - else if (dest->IsMultiRegLclVar()) - { - // Handle this as lvIsMultiRegRet; this signals to SSA that it can't consider these fields - // SSA candidates (we don't have a way to represent multiple SSANums on MultiRegLclVar nodes). - destLclVar->lvIsMultiRegRet = true; - } - } - } - - if (!srcDoFldAsg && (srcLclVar != nullptr) && !srcSingleLclVarAsg) - { - if (!srcLclVar->lvRegStruct || (srcLclVar->lvType != dest->TypeGet())) - { - lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DNER_BlockOp)); - } - } - - var_types asgType = dest->TypeGet(); - if (requiresCopyBlock) - { - bool isBlkReqd = (asgType == TYP_STRUCT); - dest = fgMorphBlockOperand(dest, asgType, blockWidth, isBlkReqd); - asg->AsOp()->gtOp1 = dest; - asg->gtFlags |= (dest->gtFlags & GTF_ALL_EFFECT); - - // Eliminate the "OBJ or BLK" node on the src. - src = fgMorphBlockOperand(src, asgType, blockWidth, false /*!isBlkReqd*/); - asg->AsOp()->gtOp2 = src; - - goto _Done; - } - - // - // Otherwise we convert this CopyBlock into individual field by field assignments - // - tree = nullptr; - - GenTree* addrSpill = nullptr; - unsigned addrSpillTemp = BAD_VAR_NUM; - bool addrSpillIsStackDest = false; // true if 'addrSpill' represents the address in our local stack frame - - unsigned fieldCnt = DUMMY_INIT(0); - - if (destDoFldAsg && srcDoFldAsg) - { - // To do fieldwise assignments for both sides. - // The structs do not have to be the same exact types but have to have same field types - // at the same offsets. - assert(destLclNum != BAD_VAR_NUM && srcLclNum != BAD_VAR_NUM); - assert(destLclVar != nullptr && srcLclVar != nullptr && destLclVar->lvFieldCnt == srcLclVar->lvFieldCnt); - - fieldCnt = destLclVar->lvFieldCnt; - goto _AssignFields; // No need to spill the address to the temp. Go ahead to morph it into field - // assignments. - } - else if (destDoFldAsg) - { - fieldCnt = destLclVar->lvFieldCnt; - src = fgMorphBlockOperand(src, asgType, blockWidth, false /*isBlkReqd*/); - - srcUseLclFld = fgMorphCanUseLclFldForCopy(destLclNum, srcLclNum); - - if (!srcUseLclFld && srcAddr == nullptr) - { - srcAddr = fgMorphGetStructAddr(&src, destLclVar->GetStructHnd(), true /* rValue */); - } - } - else - { - assert(srcDoFldAsg); - fieldCnt = srcLclVar->lvFieldCnt; - dest = fgMorphBlockOperand(dest, asgType, blockWidth, false /*isBlkReqd*/); - if (dest->OperIsBlk()) - { - dest->SetOper(GT_IND); - dest->gtType = TYP_STRUCT; - } - destUseLclFld = fgMorphCanUseLclFldForCopy(srcLclNum, destLclNum); - if (!destUseLclFld) - { - destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest); - } - } - - if (destDoFldAsg) - { - noway_assert(!srcDoFldAsg); - if (!srcUseLclFld) - { - if (gtClone(srcAddr)) - { - // srcAddr is simple expression. No need to spill. - noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - } - else - { - // srcAddr is complex expression. Clone and spill it (unless the destination is - // a struct local that only has one field, in which case we'd only use the - // address value once...) - if (destLclVar->lvFieldCnt > 1) - { - // We will spill srcAddr (i.e. assign to a temp "BlockOp address local") - // no need to clone a new copy as it is only used once - // - addrSpill = srcAddr; // addrSpill represents the 'srcAddr' - } - } - } - } - - if (srcDoFldAsg) - { - noway_assert(!destDoFldAsg); - - // If we're doing field-wise stores, to an address within a local, and we copy - // the address into "addrSpill", do *not* declare the original local var node in the - // field address as GTF_VAR_DEF and GTF_VAR_USEASG; we will declare each of the - // field-wise assignments as an "indirect" assignment to the local. - // ("lclVarTree" is a subtree of "destAddr"; make sure we remove the flags before - // we clone it.) - if (lclVarTree != nullptr) - { - lclVarTree->gtFlags &= ~(GTF_VAR_DEF | GTF_VAR_USEASG); - } - - if (!destUseLclFld) - { - if (gtClone(destAddr)) - { - // destAddr is simple expression. No need to spill - noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - } - else - { - // destAddr is complex expression. Clone and spill it (unless - // the source is a struct local that only has one field, in which case we'd only - // use the address value once...) - if (srcLclVar->lvFieldCnt > 1) - { - // We will spill destAddr (i.e. assign to a temp "BlockOp address local") - // no need to clone a new copy as it is only used once - // - addrSpill = destAddr; // addrSpill represents the 'destAddr' - } - } - } - } - - // TODO-CQ: this should be based on a more general - // "BaseAddress" method, that handles fields of structs, before or after - // morphing. - if ((addrSpill != nullptr) && addrSpill->OperIs(GT_ADDR)) - { - GenTree* addrSpillOp = addrSpill->AsOp()->gtGetOp1(); - if (addrSpillOp->IsLocal()) - { - // We will *not* consider this to define the local, but rather have each individual field assign - // be a definition. - addrSpillOp->gtFlags &= ~(GTF_LIVENESS_MASK); - assert(lvaGetPromotionType(addrSpillOp->AsLclVarCommon()->GetLclNum()) != PROMOTION_TYPE_INDEPENDENT); - addrSpillIsStackDest = true; // addrSpill represents the address of LclVar[varNum] in our - // local stack frame - } - } - - if (addrSpill != nullptr) - { - // 'addrSpill' is already morphed - - // Spill the (complex) address to a BYREF temp. - // Note, at most one address may need to be spilled. - addrSpillTemp = lvaGrabTemp(true DEBUGARG("BlockOp address local")); - - lvaTable[addrSpillTemp].lvType = TYP_BYREF; - - if (addrSpillIsStackDest) - { - lvaTable[addrSpillTemp].lvStackByref = true; - } - - tree = gtNewAssignNode(gtNewLclvNode(addrSpillTemp, TYP_BYREF), addrSpill); - - // If we are assigning the address of a LclVar here - // liveness does not account for this kind of address taken use. - // - // We have to mark this local as address exposed so - // that we don't delete the definition for this LclVar - // as a dead store later on. - // - if (addrSpill->OperGet() == GT_ADDR) - { - GenTree* addrOp = addrSpill->AsOp()->gtOp1; - if (addrOp->IsLocal()) - { - unsigned lclVarNum = addrOp->AsLclVarCommon()->GetLclNum(); - lvaTable[lclVarNum].lvAddrExposed = true; - lvaSetVarDoNotEnregister(lclVarNum DEBUGARG(DNER_AddrExposed)); - } - } - } - - _AssignFields: - - // We may have allocated a temp above, and that may have caused the lvaTable to be expanded. - // So, beyond this point we cannot rely on the old values of 'srcLclVar' and 'destLclVar'. - for (unsigned i = 0; i < fieldCnt; ++i) - { - GenTree* dstFld; - if (destDoFldAsg) - { - noway_assert(destLclNum != BAD_VAR_NUM); - unsigned dstFieldLclNum = lvaTable[destLclNum].lvFieldLclStart + i; - dstFld = gtNewLclvNode(dstFieldLclNum, lvaTable[dstFieldLclNum].TypeGet()); - // If it had been labeled a "USEASG", assignments to the individual promoted fields are not. - if (destAddr != nullptr) - { - noway_assert(destAddr->AsOp()->gtOp1->gtOper == GT_LCL_VAR); - dstFld->gtFlags |= destAddr->AsOp()->gtOp1->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG); - } - else - { - noway_assert(lclVarTree != nullptr); - dstFld->gtFlags |= lclVarTree->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG); - } - // Don't CSE the lhs of an assignment. - dstFld->gtFlags |= GTF_DONT_CSE; - } - else - { - noway_assert(srcDoFldAsg); - - if (destSingleLclVarAsg) - { - noway_assert(fieldCnt == 1); - noway_assert(destLclVar != nullptr); - noway_assert(addrSpill == nullptr); - - dstFld = gtNewLclvNode(destLclNum, destLclVar->TypeGet()); - } - else - { - GenTree* dstAddrClone = nullptr; - if (!destUseLclFld) - { - // Need address of the destination. - if (addrSpill) - { - assert(addrSpillTemp != BAD_VAR_NUM); - dstAddrClone = gtNewLclvNode(addrSpillTemp, TYP_BYREF); - } - else - { - if (i == 0) - { - // Use the orginal destAddr tree when i == 0 - dstAddrClone = destAddr; - } - else - { - // We can't clone multiple copies of a tree with persistent side effects - noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - - dstAddrClone = gtCloneExpr(destAddr); - noway_assert(dstAddrClone != nullptr); - - JITDUMP("dstAddr - Multiple Fields Clone created:\n"); - DISPTREE(dstAddrClone); - - // Morph the newly created tree - dstAddrClone = fgMorphTree(dstAddrClone); - } - - // Is the address of a local? - GenTreeLclVarCommon* lclVarTree = nullptr; - bool isEntire = false; - bool* pIsEntire = (blockWidthIsConst ? &isEntire : nullptr); - if (dstAddrClone->DefinesLocalAddr(this, blockWidth, &lclVarTree, pIsEntire)) - { - lclVarTree->gtFlags |= GTF_VAR_DEF; - if (!isEntire) - { - lclVarTree->gtFlags |= GTF_VAR_USEASG; - } - } - } - } - - LclVarDsc* srcVarDsc = lvaGetDesc(srcLclNum); - unsigned srcFieldLclNum = srcVarDsc->lvFieldLclStart + i; - LclVarDsc* srcFieldVarDsc = lvaGetDesc(srcFieldLclNum); - - // Have to set the field sequence -- which means we need the field handle. - CORINFO_CLASS_HANDLE classHnd = srcVarDsc->GetStructHnd(); - CORINFO_FIELD_HANDLE fieldHnd = - info.compCompHnd->getFieldInClass(classHnd, srcFieldVarDsc->lvFldOrdinal); - FieldSeqNode* curFieldSeq = GetFieldSeqStore()->CreateSingleton(fieldHnd); - - unsigned srcFieldOffset = lvaGetDesc(srcFieldLclNum)->lvFldOffset; - var_types srcType = srcFieldVarDsc->TypeGet(); - - if (!destUseLclFld) - { - - if (srcFieldOffset == 0) - { - fgAddFieldSeqForZeroOffset(dstAddrClone, curFieldSeq); - } - else - { - GenTree* fieldOffsetNode = gtNewIconNode(srcFieldVarDsc->lvFldOffset, curFieldSeq); - dstAddrClone = gtNewOperNode(GT_ADD, TYP_BYREF, dstAddrClone, fieldOffsetNode); - } - - dstFld = gtNewIndir(srcType, dstAddrClone); - } - else - { - assert(dstAddrClone == nullptr); - assert((destLclOffset == 0) || (destFldSeq != nullptr)); - // If the dst was a struct type field "B" in a struct "A" then we add - // add offset of ("B" in "A") + current offset in "B". - unsigned summOffset = destLclOffset + srcFieldOffset; - dstFld = gtNewLclFldNode(destLclNum, srcType, summOffset); - FieldSeqNode* dstFldFldSeq = GetFieldSeqStore()->Append(destFldSeq, curFieldSeq); - dstFld->AsLclFld()->SetFieldSeq(dstFldFldSeq); - - // TODO-1stClassStructs: remove this and implement storing to a field in a struct in a reg. - lvaSetVarDoNotEnregister(destLclNum DEBUGARG(DNER_LocalField)); - } - - // !!! The destination could be on stack. !!! - // This flag will let us choose the correct write barrier. - dstFld->gtFlags |= GTF_IND_TGTANYWHERE; - } - } - - GenTree* srcFld = nullptr; - if (srcDoFldAsg) - { - noway_assert(srcLclNum != BAD_VAR_NUM); - unsigned srcFieldLclNum = lvaTable[srcLclNum].lvFieldLclStart + i; - srcFld = gtNewLclvNode(srcFieldLclNum, lvaTable[srcFieldLclNum].TypeGet()); - - noway_assert(srcLclVarTree != nullptr); - srcFld->gtFlags |= srcLclVarTree->gtFlags & ~GTF_NODE_MASK; - } - else - { - noway_assert(destDoFldAsg); - noway_assert(destLclNum != BAD_VAR_NUM); - unsigned dstFieldLclNum = lvaTable[destLclNum].lvFieldLclStart + i; - - if (srcSingleLclVarAsg) - { - noway_assert(fieldCnt == 1); - noway_assert(srcLclNum != BAD_VAR_NUM); - noway_assert(addrSpill == nullptr); - - srcFld = gtNewLclvNode(srcLclNum, lvaGetDesc(srcLclNum)->TypeGet()); - } - else - { - GenTree* srcAddrClone = nullptr; - if (!srcUseLclFld) - { - // Need address of the source. - if (addrSpill) - { - assert(addrSpillTemp != BAD_VAR_NUM); - srcAddrClone = gtNewLclvNode(addrSpillTemp, TYP_BYREF); - } - else - { - if (i == 0) - { - // Use the orginal srcAddr tree when i == 0 - srcAddrClone = srcAddr; - } - else - { - // We can't clone multiple copies of a tree with persistent side effects - noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - - srcAddrClone = gtCloneExpr(srcAddr); - noway_assert(srcAddrClone != nullptr); - - JITDUMP("srcAddr - Multiple Fields Clone created:\n"); - DISPTREE(srcAddrClone); - - // Morph the newly created tree - srcAddrClone = fgMorphTree(srcAddrClone); - } - } - } - - CORINFO_CLASS_HANDLE classHnd = lvaTable[destLclNum].GetStructHnd(); - CORINFO_FIELD_HANDLE fieldHnd = - info.compCompHnd->getFieldInClass(classHnd, lvaTable[dstFieldLclNum].lvFldOrdinal); - FieldSeqNode* curFieldSeq = GetFieldSeqStore()->CreateSingleton(fieldHnd); - var_types destType = lvaGetDesc(dstFieldLclNum)->lvType; - - bool done = false; - if (lvaGetDesc(dstFieldLclNum)->lvFldOffset == 0) - { - // If this is a full-width use of the src via a different type, we need to create a GT_LCL_FLD. - // (Note that if it was the same type, 'srcSingleLclVarAsg' would be true.) - if (srcLclNum != BAD_VAR_NUM) - { - noway_assert(srcLclVarTree != nullptr); - assert(destType != TYP_STRUCT); - unsigned destSize = genTypeSize(destType); - srcLclVar = lvaGetDesc(srcLclNum); - unsigned srcSize = - (srcLclVar->lvType == TYP_STRUCT) ? srcLclVar->lvExactSize : genTypeSize(srcLclVar); - if (destSize == srcSize) - { - srcLclVarTree->gtFlags |= GTF_VAR_CAST; - srcLclVarTree->ChangeOper(GT_LCL_FLD); - srcLclVarTree->gtType = destType; - srcLclVarTree->AsLclFld()->SetFieldSeq(curFieldSeq); - srcFld = srcLclVarTree; - done = true; - } - } - } - if (!done) - { - unsigned fldOffset = lvaGetDesc(dstFieldLclNum)->lvFldOffset; - if (!srcUseLclFld) - { - assert(srcAddrClone != nullptr); - if (fldOffset == 0) - { - fgAddFieldSeqForZeroOffset(srcAddrClone, curFieldSeq); - } - else - { - GenTreeIntCon* fldOffsetNode = gtNewIconNode(fldOffset, curFieldSeq); - srcAddrClone = gtNewOperNode(GT_ADD, TYP_BYREF, srcAddrClone, fldOffsetNode); - } - srcFld = gtNewIndir(destType, srcAddrClone); - } - else - { - assert((srcLclOffset == 0) || (srcFldSeq != 0)); - // If the src was a struct type field "B" in a struct "A" then we add - // add offset of ("B" in "A") + current offset in "B". - unsigned summOffset = srcLclOffset + fldOffset; - srcFld = gtNewLclFldNode(srcLclNum, destType, summOffset); - FieldSeqNode* srcFldFldSeq = GetFieldSeqStore()->Append(srcFldSeq, curFieldSeq); - srcFld->AsLclFld()->SetFieldSeq(srcFldFldSeq); - // TODO-1stClassStructs: remove this and implement reading a field from a struct in a reg. - lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DNER_LocalField)); - } - } - } - } - assert(srcFld != nullptr); - noway_assert(dstFld->TypeGet() == srcFld->TypeGet()); - - asg = gtNewAssignNode(dstFld, srcFld); - - // If we spilled the address, and we didn't do individual field assignments to promoted fields, - // and it was of a local, ensure that the destination local variable has been marked as address - // exposed. Neither liveness nor SSA are able to track this kind of indirect assignments. - if (addrSpill && !destDoFldAsg && destLclNum != BAD_VAR_NUM) - { - noway_assert(lvaGetDesc(destLclNum)->lvAddrExposed); - } - -#if LOCAL_ASSERTION_PROP - if (optLocalAssertionProp) - { - optAssertionGen(asg); - } -#endif // LOCAL_ASSERTION_PROP - - if (tree) - { - tree = gtNewOperNode(GT_COMMA, TYP_VOID, tree, asg); - } - else - { - tree = asg; - } - } - } - - if (isLateArg) - { - tree->gtFlags |= GTF_LATE_ARG; - } - -#ifdef DEBUG - if (tree != oldTree) - { - tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; - } -#endif - -_Done: - - JITDUMP("\nfgMorphCopyBlock (after):\n"); - DISPTREE(tree); - - return tree; -} - //------------------------------------------------------------------------ // fgMorphCanUseLclFldForCopy: check if we can access LclVar2 using LclVar1's fields. // @@ -14794,7 +13533,8 @@ DONE_MORPHING_CHILDREN: } tree = op1; GenTree* addr = commaNode->AsOp()->gtOp2; - op1 = gtNewIndir(typ, addr); + // TODO-1stClassStructs: we often create a struct IND without a handle, fix it. + op1 = gtNewIndir(typ, addr); // This is very conservative op1->gtFlags |= treeFlags & ~GTF_ALL_EFFECT & ~GTF_IND_NONFAULTING; op1->gtFlags |= (addr->gtFlags & GTF_ALL_EFFECT); @@ -19370,8 +18110,8 @@ bool Compiler::fgMorphCombineSIMDFieldAssignments(BasicBlock* block, Statement* { copyBlkAddr = copyBlkAddr->AsAddrMode()->Base(); } - GenTreeLclVarCommon* localDst = nullptr; - if (copyBlkAddr->IsLocalAddrExpr(this, &localDst, nullptr)) + GenTreeLclVarCommon* localDst = copyBlkAddr->IsLocalAddrExpr(); + if (localDst != nullptr) { setLclRelatedToSIMDIntrinsic(localDst); } diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp new file mode 100644 index 00000000000..5e8d47a671b --- /dev/null +++ b/src/coreclr/jit/morphblock.cpp @@ -0,0 +1,1551 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "jitpch.h" + +class MorphInitBlockHelper +{ +public: + static GenTree* MorphInitBlock(Compiler* comp, GenTree* tree); + +protected: + MorphInitBlockHelper(Compiler* comp, GenTree* asg, bool initBlock); + + GenTree* Morph(); + + void PrepareDst(); + virtual void PrepareSrc(); + + virtual void TrySpecialCases(); + virtual void MorphStructCases(); + + virtual const char* GetHelperName() const + { + return "MorphInitBlock"; + } + + static GenTree* MorphBlock(Compiler* comp, GenTree* tree, bool isDest); + static GenTree* MorphCommaBlock(Compiler* comp, GenTreeOp* firstComma); + static GenTreeBlk* MorphDynamicBlock(Compiler* comp, GenTreeDynBlk* dynBlock); + +protected: + Compiler* m_comp; + bool m_initBlock; + + GenTreeOp* m_asg = nullptr; + GenTree* m_dst = nullptr; + GenTree* m_src = nullptr; + + unsigned m_blockSize = 0; + bool m_blockSizeIsConst = false; + + unsigned m_dstLclNum = BAD_VAR_NUM; + GenTreeLclVarCommon* m_dstLclNode = nullptr; + LclVarDsc* m_dstVarDsc = nullptr; + FieldSeqNode* m_dstFldSeq = nullptr; + unsigned m_dstLclOffset = 0; + bool m_dstUseLclFld = false; + bool m_dstSingleLclVarAsg = false; + GenTree* m_dstAddr = nullptr; + ssize_t m_dstAddOff = 0; + +#if defined(DEBUG) + bool m_isLateArg = false; +#endif // DEBUG + + enum class BlockTransformation + { + Undefined, + FieldByField, + OneAsgBlock, + StructBlock, + SkipCallSrc, + Nop + }; + + BlockTransformation m_transformationDecision = BlockTransformation::Undefined; + GenTree* m_result = nullptr; +}; + +//------------------------------------------------------------------------ +// MorphInitBlock: Morph a block initialization assignment tree. +// +// Arguments: +// comp - a compiler instance; +// tree - A GT_ASG tree that performs block initialization. +// +// Return Value: +// A possibly modified tree to perfom the initializetion. +// +// static +GenTree* MorphInitBlockHelper::MorphInitBlock(Compiler* comp, GenTree* tree) +{ + const bool initBlock = true; + MorphInitBlockHelper helper(comp, tree, initBlock); + return helper.Morph(); +} + +//------------------------------------------------------------------------ +// MorphInitBlockHelper: helper's constructor. +// +// Arguments: +// comp - a compiler instance; +// initBlock - true if this is init block op, false if it is a copy block; +// asg - GT_ASG node to morph. +// +// Notes: +// Most class members are initialized via in-class member initializers. +// +MorphInitBlockHelper::MorphInitBlockHelper(Compiler* comp, GenTree* asg, bool initBlock = true) + : m_comp(comp), m_initBlock(initBlock) +{ + assert(asg->OperIs(GT_ASG)); +#if defined(DEBUG) + if (m_initBlock) + { + assert(asg->OperIsInitBlkOp()); + } + else + { + assert(asg->OperIsCopyBlkOp()); + } +#endif // DEBUG + m_asg = asg->AsOp(); +} + +//------------------------------------------------------------------------ +// Morph: transform the asg to a possible better form and changes its children +// to an appropriate form for later phases, for example, adds SIMD_INIT nodes +// or sets lvDoNotEnregister on locals. +// +// Return Value: +// A possibly modified tree to perfom the block operation. +// +// Notes: +// It is used for both init and copy block. +// +GenTree* MorphInitBlockHelper::Morph() +{ + JITDUMP("%s:\n", GetHelperName()); + + PrepareDst(); + PrepareSrc(); + + INDEBUG(m_isLateArg = (m_asg->gtFlags & GTF_LATE_ARG) != 0); + + TrySpecialCases(); + + if (m_transformationDecision == BlockTransformation::Undefined) + { + GenTree* oneAsgTree = m_comp->fgMorphOneAsgBlockOp(m_asg); + if (oneAsgTree != nullptr) + { + assert((m_asg == oneAsgTree) && "fgMorphOneAsgBlock must return the incoming tree."); + + m_transformationDecision = BlockTransformation::OneAsgBlock; + m_result = oneAsgTree; + } + else + { + MorphStructCases(); + } + } + + assert(m_transformationDecision != BlockTransformation::Undefined); + assert(m_result != nullptr); + + if (m_result != m_asg) + { + const bool isLateArg = ((m_asg->gtFlags & GTF_LATE_ARG) != 0); + assert(m_isLateArg == isLateArg); + if (isLateArg) + { + assert(!m_initBlock && "do not expect a block init as a late arg."); + m_result->gtFlags |= GTF_LATE_ARG; + } + } + +#ifdef DEBUG + if (m_result != m_asg) + { + m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; + } + if (m_comp->verbose) + { + printf("%s (after):\n", GetHelperName()); + m_comp->gtDispTree(m_result); + } +#endif // DEBUG + + return m_result; +} + +//------------------------------------------------------------------------ +// PrepareDst: Transform the asg destination to an appropriate form and initialize member fields +// with information about it. +// +// Notes: +// When assertion propogation is enabled this method kills assertions about the dst local, +// so the correctness depends on `IsLocalAddrExpr` recognizing all patterns. +// +void MorphInitBlockHelper::PrepareDst() +{ + GenTree* origDst = m_asg->gtGetOp1(); + m_dst = MorphBlock(m_comp, origDst, true); + if (m_dst != origDst) + { + m_asg->gtOp1 = m_dst; + } + + if (m_asg->TypeGet() != m_dst->TypeGet()) + { + assert(!m_initBlock && "the asg type should be final for an init block."); + JITDUMP("changing type of asignment from %-6s to %-6s\n", varTypeName(m_asg->TypeGet()), + varTypeName(m_dst->TypeGet())); + + m_asg->ChangeType(m_dst->TypeGet()); + } + + if (m_dst->IsLocal()) + { + m_dstLclNode = m_dst->AsLclVarCommon(); + m_dstVarDsc = m_comp->lvaGetDesc(m_dstLclNode); + m_blockSizeIsConst = true; + + if (m_dst->OperIs(GT_LCL_VAR)) + { + if (m_dstVarDsc->TypeGet() == TYP_STRUCT) + { +#ifdef DEBUG + const bool isSizeMistmatch = + (m_dstVarDsc->lvExactSize != m_comp->info.compCompHnd->getClassSize(m_dstVarDsc->GetStructHnd())); + const bool isStackAllocCandidate = + m_comp->compObjectStackAllocation() && !m_dstVarDsc->GetLayout()->IsValueClass(); + // There were cases where for temps lvExactSize did not correspond to the struct size + // so we were using `getClassSize` result here, however, now this cases are gone and the only + // scenario when `getClassSize` != `lvExactSize` it is a class object optimized to be on stack + assert(!isSizeMistmatch || isStackAllocCandidate); +#endif // DEBUG + m_blockSize = m_dstVarDsc->lvExactSize; + } + else + { + m_blockSize = genTypeSize(m_dstVarDsc); + } + } + else + { + assert(m_dst->OperIs(GT_LCL_FLD) && !m_dst->TypeIs(TYP_STRUCT)); + GenTreeLclFld* destFld = m_dst->AsLclFld(); + m_blockSize = genTypeSize(destFld->TypeGet()); + m_dstFldSeq = destFld->GetFieldSeq(); + } + } + else + { + assert(m_dst == m_dst->gtEffectiveVal() && "the commas were skipped in MorphBlock"); + assert(m_dst->OperIs(GT_IND, GT_BLK, GT_OBJ, GT_DYN_BLK)); + + GenTree* dstAddr = m_dst->AsIndir()->Addr(); + if (m_dst->OperGet() == GT_IND) + { + assert(m_dst->TypeGet() != TYP_STRUCT); + m_blockSize = genTypeSize(m_dst); + m_blockSizeIsConst = true; + } + else + { + assert(m_dst->OperIsBlk()); + GenTreeBlk* blk = m_dst->AsBlk(); + m_blockSize = blk->Size(); + m_blockSizeIsConst = !blk->OperIs(GT_DYN_BLK); + } + + noway_assert(dstAddr->TypeIs(TYP_BYREF, TYP_I_IMPL)); + if (dstAddr->IsLocalAddrExpr(m_comp, &m_dstLclNode, &m_dstFldSeq, &m_dstAddOff)) + { + // Don't expect `IsLocalAddrExpr` to pass `INDEX_ADDR`. + assert((m_dst->gtFlags & GTF_IND_ARR_INDEX) == 0); + + // Note that lclNode can be a field, like `BLK<4> struct(ADD(ADDR(LCL_FLD int), CNST_INT))`. + m_dstVarDsc = m_comp->lvaGetDesc(m_dstLclNode); + } + } + + if (m_dstLclNode != nullptr) + { + m_dstLclNum = m_dstLclNode->GetLclNum(); + m_dstLclOffset = m_dstLclNode->GetLclOffs(); + +#if LOCAL_ASSERTION_PROP + + // Kill everything about m_dstLclNum (and its field locals) + if (m_comp->optLocalAssertionProp && (m_comp->optAssertionCount > 0)) + { + m_comp->fgKillDependentAssertions(m_dstLclNum DEBUGARG(m_asg)); + } + +#endif // LOCAL_ASSERTION_PROP + } + +#if defined(DEBUG) + if (m_comp->verbose) + { + printf("PrepareDst for [%06u] ", m_comp->dspTreeID(origDst)); + if (m_dstLclNode != nullptr) + { + printf("have found a local var V%02u.\n", m_dstLclNum); + } + else + { + printf("have not found a local var.\n"); + } + } +#endif // DEBUG +} + +//------------------------------------------------------------------------ +// PrepareSrc: Transform the asg src to an appropriate form and initialize member fields +// with information about it. +// +void MorphInitBlockHelper::PrepareSrc() +{ + m_src = m_asg->gtGetOp2(); +} + +//------------------------------------------------------------------------ +// TrySpecialCases: check special cases that require special transformations. +// We don't have any for for init block. +// +void MorphInitBlockHelper::TrySpecialCases() +{ + return; +} + +//------------------------------------------------------------------------ +// MorphStructCases: transforms the asg as field by field init or keeps it as a block init +// but sets appropriate flags for the involved lclVars. +// +// Assumptions: +// we have already checked that it is not a special case and can't be transformed as OneAsgBlock. +// +void MorphInitBlockHelper::MorphStructCases() +{ + GenTree* initVal = m_src->OperIsInitVal() ? m_src->gtGetOp1() : m_src; + + if (m_dstLclNum != BAD_VAR_NUM) + { + // If we have already determined that a promoted TYP_STRUCT lclVar will not be enregistered, + // we are better off doing a block init. + bool tryFieldByField = false; + if (m_dstVarDsc->lvPromoted) + { + assert(m_dstLclNode->OperIs(GT_LCL_VAR)); // Don't expect a promoted LCL_VAR with a field reference. + if (m_dstVarDsc->lvDoNotEnregister) + { + tryFieldByField = false; + } + else + { + tryFieldByField = true; + } + } + + if (tryFieldByField) + { + GenTreeLclVar* dstLclVar = m_dstLclNode->AsLclVar(); + GenTree* newTree = m_comp->fgMorphPromoteLocalInitBlock(dstLclVar, initVal, m_blockSize); + + if (newTree != nullptr) + { + m_transformationDecision = BlockTransformation::FieldByField; + m_result = newTree; + } + } + + if (m_transformationDecision != BlockTransformation::FieldByField) + { + if (m_dst != m_dstLclNode) + { + // If we access the dst as a whole but not directly, for example, with OBJ(ADDR(LCL_VAR)) + // then set doNotEnreg. + // TODO-1stClassStructs: remove it when we can represent narowing struct cast + // without taking address of the lcl. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_dstVarDsc->lvPromoted) + { + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + } + } + + if (m_transformationDecision == BlockTransformation::Undefined) + { + // For an InitBlock we always require a block operand. + m_dst = m_comp->fgMorphBlockOperand(m_dst, m_dst->TypeGet(), m_blockSize, true /*isBlkReqd*/); + m_transformationDecision = BlockTransformation::StructBlock; + m_dst->gtFlags |= GTF_DONT_CSE; + m_result = m_asg; + m_result->AsOp()->gtOp1 = m_dst; + m_result->gtFlags |= (m_dst->gtFlags & GTF_ALL_EFFECT); + +#if FEATURE_SIMD + if (varTypeIsSIMD(m_asg) && (m_dst == m_dstLclNode)) + { + // For a SIMD local init we need to call SIMDIntrinsic init. + // We need this block becuase morph does not create SIMD init for promoted lclVars. + assert(m_src->IsIntegralConst(0) || m_src->IsFPZero()); + assert(m_dstVarDsc != nullptr); + const var_types asgType = m_asg->TypeGet(); + CorInfoType simdBaseJitType = m_dstVarDsc->GetSimdBaseJitType(); + m_src = m_comp->gtNewSIMDNode(asgType, m_src, SIMDIntrinsicInit, simdBaseJitType, m_blockSize); + m_result->AsOp()->gtOp2 = m_src; + } +#endif // FEATURE_SIMD + } +} + +//------------------------------------------------------------------------ +// MorphBlock: Morph a block node preparatory to morphing a block assignment. +// +// Arguments: +// comp - a compiler instance; +// tree - a struct type node; +// isDest - true if this is the destination of an assignment; +// +// Return Value: +// Returns the possibly-morphed node. The caller is responsible for updating +// the parent of this node. +// +// static +GenTree* MorphInitBlockHelper::MorphBlock(Compiler* comp, GenTree* tree, bool isDest) +{ + JITDUMP("MorphBlock for %s tree, before:\n", (isDest ? "dst" : "src")); + DISPTREE(tree); + + // Src can be a primitive type. + assert(!isDest || varTypeIsStruct(tree)); + + GenTree* handleTree = nullptr; + GenTree* addr = nullptr; + + if (tree->OperIs(GT_COMMA)) + { + // TODO-Cleanup: this block is not needed for not struct nodes, but + // fgMorphOneAsgBlockOp works wrong without this transformation. + tree = MorphCommaBlock(comp, tree->AsOp()); + } + + if (!tree->OperIsBlk()) + { + JITDUMP("MorphBlock after:\n"); + DISPTREE(tree); + return tree; + } + + GenTreeBlk* blkNode = tree->AsBlk(); + if (blkNode->OperIs(GT_DYN_BLK)) + { + blkNode = MorphDynamicBlock(comp, blkNode->AsDynBlk()); + if (blkNode->OperIs(GT_DYN_BLK)) + { + JITDUMP("MorphBlock after:\n"); + DISPTREE(blkNode); + return blkNode; + } + } + + GenTree* blkAddr = blkNode->Addr(); + assert(blkAddr != nullptr); + assert(blkAddr->TypeIs(TYP_I_IMPL, TYP_BYREF, TYP_REF)); + // GT_ADDR, GT_LCL_VAR/FLD, GT_ADD, GT_COMMA, GT_CALL, GT_CNST_INT, GT_LCL_VAR/FLD_ADDR + + JITDUMP("MorphBlock after:\n"); + DISPTREE(tree); + return tree; +} + +//------------------------------------------------------------------------ +// MorphCommaBlock: transform COMMA(X) as OBJ(COMMA byref(ADDR(X)). +// +// Notes: +// In order to CSE and value number array index expressions and bounds checks, +// the commas in which they are contained need to match. +// The pattern is that the COMMA should be the address expression. +// Therefore, we insert a GT_ADDR just above the node, and wrap it in an obj or ind. +// TODO-1stClassStructs: Consider whether this can be improved. +// Example: +// before: [3] comma struct <- [2] comma struct <- [1] LCL_VAR struct +// after: [5] obj <- [3] comma byref <- [2] comma byref <- [4] addr byref <- [1] LCL_VAR struct +// +// static +GenTree* MorphInitBlockHelper::MorphCommaBlock(Compiler* comp, GenTreeOp* firstComma) +{ + assert(firstComma->OperIs(GT_COMMA)); + + Compiler::GenTreePtrStack commas(comp->getAllocator(CMK_ArrayStack)); + for (GenTree* currComma = firstComma; currComma != nullptr && currComma->OperIs(GT_COMMA); + currComma = currComma->gtGetOp2()) + { + commas.Push(currComma); + } + + GenTree* lastComma = commas.Top(); + + GenTree* effectiveVal = lastComma->gtGetOp2(); + + assert(effectiveVal == firstComma->gtEffectiveVal()); + + GenTree* effectiveValAddr = comp->gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal); + + INDEBUG(effectiveValAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + + lastComma->AsOp()->gtOp2 = effectiveValAddr; + + while (!commas.Empty()) + { + GenTree* comma = commas.Pop(); + comma->gtType = TYP_BYREF; + comp->gtUpdateNodeSideEffects(comma); + } + + const var_types blockType = effectiveVal->TypeGet(); + GenTree* addr = firstComma; + + GenTree* res; + + if (blockType == TYP_STRUCT) + { + CORINFO_CLASS_HANDLE structHnd = comp->gtGetStructHandleIfPresent(effectiveVal); + if (structHnd == NO_CLASS_HANDLE) + { + // TODO-1stClassStructs: get rid of all such cases. + res = comp->gtNewIndir(blockType, addr); + } + else + { + res = comp->gtNewObjNode(structHnd, addr); + comp->gtSetObjGcInfo(res->AsObj()); + } + } + else + { + res = comp->gtNewIndir(blockType, addr); + } + + comp->gtUpdateNodeSideEffects(res); + INDEBUG(res->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + return res; +} + +//------------------------------------------------------------------------ +// MorphDynamicBlock: tries to transform a dynamic block as a const block. +// +// static +GenTreeBlk* MorphInitBlockHelper::MorphDynamicBlock(Compiler* comp, GenTreeDynBlk* dynBlock) +{ + if (dynBlock->gtDynamicSize->IsCnsIntOrI()) + { + GenTreeIntCon* dynSize = dynBlock->gtDynamicSize->AsIntCon(); + assert(dynSize->FitsInI32()); + unsigned size = static_cast(dynSize->IconValue()); + // A GT_BLK with size of zero is not supported, + // so if we encounter such a thing we just leave it as a GT_DYN_BLK + if (size != 0) + { + dynBlock->gtDynamicSize = nullptr; + GenTreeBlk* blkNode = dynBlock; + blkNode->ChangeOper(GT_BLK); + blkNode->SetLayout(comp->typGetBlkLayout(size)); + JITDUMP("MorphDynamicBlock: DYN_BLK was morphed into BLK:\n"); + return blkNode; + } + else + { + JITDUMP("MorphDynamicBlock: DYN_BLK with zero size can't be morphed:\n"); + return dynBlock; + } + } + else + { + JITDUMP("MorphDynamicBlock: DYN_BLK with non-const size can't be morphed:\n"); + return dynBlock; + } +} + +class MorphCopyBlockHelper : public MorphInitBlockHelper +{ +public: + static GenTree* MorphCopyBlock(Compiler* comp, GenTree* tree); + +protected: + MorphCopyBlockHelper(Compiler* comp, GenTree* asg); + + void PrepareSrc() override; + + void TrySpecialCases() override; + + void MorphStructCases() override; + GenTree* CopyFieldByField(); + + const char* GetHelperName() const override + { + return "MorphCopyBlock"; + } + +protected: + unsigned m_srcLclNum = BAD_VAR_NUM; + LclVarDsc* m_srcVarDsc = nullptr; + GenTreeLclVarCommon* m_srcLclNode = nullptr; + FieldSeqNode* m_srcFldSeq = nullptr; + bool m_srcUseLclFld = false; + unsigned m_srcLclOffset = 0; + bool m_srcSingleLclVarAsg = false; + GenTree* m_srcAddr = nullptr; + ssize_t m_srcAddOff = 0; + + bool m_dstDoFldAsg = false; + bool m_srcDoFldAsg = false; +}; + +//------------------------------------------------------------------------ +// MorphCopyBlock: Morph a block copy assignment tree. +// +// Arguments: +// comp - a compiler instance; +// tree - A GT_ASG tree that performs block copy. +// +// Return Value: +// A possibly modified tree to perfom the copy. +// +// static +GenTree* MorphCopyBlockHelper::MorphCopyBlock(Compiler* comp, GenTree* tree) +{ + MorphCopyBlockHelper helper(comp, tree); + return helper.Morph(); +} + +//------------------------------------------------------------------------ +// MorphCopyBlockHelper: helper's constructor. +// +// Arguments: +// comp - a compiler instance; +// asg - GT_ASG node to morph. +// +// Notes: +// Most class members are initialized via in-class member initializers. +// +MorphCopyBlockHelper::MorphCopyBlockHelper(Compiler* comp, GenTree* asg) : MorphInitBlockHelper(comp, asg, false) +{ +} + +//------------------------------------------------------------------------ +// PrepareSrc: Transform the asg src to an appropriate form and initialize member fields +// with information about it. +// +void MorphCopyBlockHelper::PrepareSrc() +{ + GenTree* origSrc = m_asg->gtGetOp2(); + m_src = MorphBlock(m_comp, origSrc, false); + if (m_src != origSrc) + { + m_asg->gtOp2 = m_src; + } + + if (m_src->IsLocal()) + { + m_srcLclNode = m_src->AsLclVarCommon(); + m_srcLclNum = m_srcLclNode->GetLclNum(); + if (m_src->OperGet() == GT_LCL_FLD) + { + m_srcFldSeq = m_src->AsLclFld()->GetFieldSeq(); + } + } + else if (m_src->OperIsIndir()) + { + if (m_src->AsOp()->gtOp1->IsLocalAddrExpr(m_comp, &m_srcLclNode, &m_srcFldSeq, &m_srcAddOff)) + { + m_srcLclNum = m_srcLclNode->GetLclNum(); + } + else + { + m_srcAddr = m_src->AsOp()->gtOp1; + } + } + if (m_srcLclNum != BAD_VAR_NUM) + { + m_srcLclOffset = m_srcLclNode->GetLclOffs(); + m_srcVarDsc = m_comp->lvaGetDesc(m_srcLclNum); + } +} + +// TrySpecialCases: check special cases that require special transformations. +// The current special cases include asignments with calls in RHS. +// +// Notes: +// It could change multiReg flags or change m_dst node. +// +void MorphCopyBlockHelper::TrySpecialCases() +{ +#if FEATURE_MULTIREG_RET + // If this is a multi-reg return, we will not do any morphing of this node. + if (m_src->IsMultiRegCall()) + { + assert(m_dst->OperGet() == GT_LCL_VAR); + JITDUMP("Not morphing a multireg call return\n"); + m_transformationDecision = BlockTransformation::SkipCallSrc; + m_result = m_asg; + } + else if (m_dst->IsMultiRegLclVar() && !m_src->IsMultiRegNode()) + { + m_dst->AsLclVar()->ClearMultiReg(); + } +#endif // FEATURE_MULTIREG_RET + + if (m_transformationDecision == BlockTransformation::Undefined) + { + if (m_src->IsCall()) + { + if (m_dst->OperIs(GT_OBJ)) + { + GenTreeLclVar* lclVar = m_comp->fgMorphTryFoldObjAsLclVar(m_dst->AsObj()); + if (lclVar != nullptr) + { + m_dst = lclVar; + m_asg->gtOp1 = lclVar; + } + } + if (m_dst->OperIs(GT_LCL_VAR)) + { + LclVarDsc* varDsc = m_comp->lvaGetDesc(m_dst->AsLclVar()); + if (varTypeIsStruct(varDsc) && varDsc->CanBeReplacedWithItsField(m_comp)) + { + m_dst->gtFlags |= GTF_DONT_CSE; + JITDUMP("Not morphing a single reg call return\n"); + m_transformationDecision = BlockTransformation::SkipCallSrc; + m_result = m_asg; + } + } + } + } +} + +//------------------------------------------------------------------------ +// MorphStructCases: transforms the asg as field by field copy or keeps it as a block init +// but sets appropriate flags for the involved lclVars. +// +// Assumptions: +// we have already checked that it is not a special case and can't be transformed as OneAsgBlock. +// +void MorphCopyBlockHelper::MorphStructCases() +{ + JITDUMP("block assignment to morph:\n"); + DISPTREE(m_asg); + + if (m_dstVarDsc != nullptr) + { + if (m_dstVarDsc->lvPromoted && m_blockSizeIsConst) + { + noway_assert(varTypeIsStruct(m_dstVarDsc)); + noway_assert(!m_comp->opts.MinOpts()); + + if (m_blockSize == m_dstVarDsc->lvExactSize) + { + JITDUMP(" (m_dstDoFldAsg=true)"); + // We may decide later that a copyblk is required when this struct has holes + m_dstDoFldAsg = true; + } + else + { + JITDUMP(" with mismatched dest size"); + } + } + } + + if (m_srcVarDsc != nullptr) + { + if (m_srcVarDsc->lvPromoted && m_blockSizeIsConst) + { + noway_assert(varTypeIsStruct(m_srcVarDsc)); + noway_assert(!m_comp->opts.MinOpts()); + + if (m_blockSize == m_srcVarDsc->lvExactSize) + { + JITDUMP(" (m_srcDoFldAsg=true)"); + // We may decide later that a copyblk is required when this struct has holes + m_srcDoFldAsg = true; + } + else + { + JITDUMP(" with mismatched src size"); + } + } + } + + // Check to see if we are doing a copy to/from the same local block. + // If so, morph it to a nop. + if ((m_dstVarDsc != nullptr) && (m_srcVarDsc == m_dstVarDsc) && (m_dstFldSeq == m_srcFldSeq) && + m_dstFldSeq != FieldSeqStore::NotAField()) + { + JITDUMP("Self-copy; replaced with a NOP.\n"); + m_transformationDecision = BlockTransformation::Nop; + GenTree* nop = m_comp->gtNewNothingNode(); + m_result = nop; + return; + } + + // Check to see if we are required to do a copy block because the struct contains holes + // and either the src or dest is externally visible + // + bool requiresCopyBlock = false; + + // If either src or dest is a reg-sized non-field-addressed struct, keep the copyBlock. + if ((m_dstVarDsc != nullptr && m_dstVarDsc->lvRegStruct) || (m_srcVarDsc != nullptr && m_srcVarDsc->lvRegStruct)) + { + requiresCopyBlock = true; + } + + // Can we use field by field assignment for the dest? + if (m_dstDoFldAsg && m_dstVarDsc->lvCustomLayout && m_dstVarDsc->lvContainsHoles) + { + JITDUMP(" dest contains custom layout and contains holes"); + // C++ style CopyBlock with holes + requiresCopyBlock = true; + } + + // Can we use field by field assignment for the src? + if (m_srcDoFldAsg && m_srcVarDsc->lvCustomLayout && m_srcVarDsc->lvContainsHoles) + { + JITDUMP(" src contains custom layout and contains holes"); + // C++ style CopyBlock with holes + requiresCopyBlock = true; + } + +#if defined(TARGET_ARM) + if ((m_src->OperIsIndir()) && (m_src->gtFlags & GTF_IND_UNALIGNED)) + { + JITDUMP(" src is unaligned"); + requiresCopyBlock = true; + } + + if (m_asg->gtFlags & GTF_BLK_UNALIGNED) + { + JITDUMP(" m_asg is unaligned"); + requiresCopyBlock = true; + } +#endif // TARGET_ARM + + // Don't use field by field assignment if the src is a call, + // lowering will handle it without spilling the call result into memory + // to access the individual fields. + // + if (m_src->OperGet() == GT_CALL) + { + JITDUMP(" src is a call"); + requiresCopyBlock = true; + } + + // If we passed the above checks, then we will check these two + if (!requiresCopyBlock) + { + // It is not always profitable to do field by field init for structs that are allocated to memory. + // A struct with 8 bool fields will require 8 moves instead of one if we do this transformation. + // A simple heuristic when field by field copy is prefered: + // - if fields can be enregistered; + // - if the struct has GCPtrs (block copy would be done via helper that is expensive); + // - if the struct has only one field. + bool dstFldIsProfitable = + ((m_dstVarDsc != nullptr) && + (!m_dstVarDsc->lvDoNotEnregister || m_dstVarDsc->HasGCPtr() || (m_dstVarDsc->lvFieldCnt == 1))); + bool srcFldIsProfitable = + ((m_srcVarDsc != nullptr) && + (!m_srcVarDsc->lvDoNotEnregister || m_srcVarDsc->HasGCPtr() || (m_srcVarDsc->lvFieldCnt == 1))); + // Are both dest and src promoted structs? + if (m_dstDoFldAsg && m_srcDoFldAsg && (dstFldIsProfitable || srcFldIsProfitable)) + { + // Both structs should be of the same type, or have the same number of fields of the same type. + // If not we will use a copy block. + bool misMatchedTypes = false; + if (m_dstVarDsc->GetStructHnd() != m_srcVarDsc->GetStructHnd()) + { + if (m_dstVarDsc->lvFieldCnt != m_srcVarDsc->lvFieldCnt) + { + misMatchedTypes = true; + } + else + { + for (int i = 0; i < m_dstVarDsc->lvFieldCnt; i++) + { + LclVarDsc* destFieldVarDsc = m_comp->lvaGetDesc(m_dstVarDsc->lvFieldLclStart + i); + LclVarDsc* srcFieldVarDsc = m_comp->lvaGetDesc(m_srcVarDsc->lvFieldLclStart + i); + if ((destFieldVarDsc->lvType != srcFieldVarDsc->lvType) || + (destFieldVarDsc->lvFldOffset != srcFieldVarDsc->lvFldOffset)) + { + misMatchedTypes = true; + break; + } + } + } + if (misMatchedTypes) + { + requiresCopyBlock = true; // Mismatched types, leave as a CopyBlock + JITDUMP(" with mismatched types"); + } + } + } + else if (m_dstDoFldAsg && dstFldIsProfitable) + { + // Match the following kinds of trees: + // fgMorphTree BB01, stmt 9 (before) + // [000052] ------------ const int 8 + // [000053] -A--G------- copyBlk void + // [000051] ------------ addr byref + // [000050] ------------ lclVar long V07 loc5 + // [000054] --------R--- void + // [000049] ------------ addr byref + // [000048] ------------ lclVar struct(P) V06 loc4 + // long V06.h (offs=0x00) -> V17 tmp9 + // Yields this transformation + // fgMorphCopyBlock (after): + // [000050] ------------ lclVar long V07 loc5 + // [000085] -A---------- = long + // [000083] D------N---- lclVar long V17 tmp9 + // + if (m_blockSizeIsConst && (m_dstVarDsc->lvFieldCnt == 1) && (m_srcVarDsc != nullptr) && + (m_blockSize == genTypeSize(m_srcVarDsc->TypeGet()))) + { + // Reject the following tree: + // - seen on x86chk jit\jit64\hfa\main\hfa_sf3E_r.exe + // + // fgMorphTree BB01, stmt 6 (before) + // [000038] ------------- const int 4 + // [000039] -A--G-------- copyBlk void + // [000037] ------------- addr byref + // [000036] ------------- lclVar int V05 loc3 + // [000040] --------R---- void + // [000035] ------------- addr byref + // [000034] ------------- lclVar struct(P) V04 loc2 + // float V04.f1 (offs=0x00) -> V13 tmp6 + // As this would framsform into + // float V13 = int V05 + // + unsigned fieldLclNum = m_comp->lvaGetDesc(m_dstLclNum)->lvFieldLclStart; + var_types destType = m_comp->lvaGetDesc(fieldLclNum)->TypeGet(); + if (m_srcVarDsc->TypeGet() == destType) + { + m_srcSingleLclVarAsg = true; + } + } + } + else if (m_srcDoFldAsg && srcFldIsProfitable) + { + // Check for the symmetric case (which happens for the _pointer field of promoted spans): + // + // [000240] -----+------ /--* lclVar struct(P) V18 tmp9 + // /--* byref V18._value (offs=0x00) -> V30 + // tmp21 + // [000245] -A------R--- * = struct (copy) + // [000244] -----+------ \--* obj(8) struct + // [000243] -----+------ \--* addr byref + // [000242] D----+-N---- \--* lclVar byref V28 tmp19 + // + if (m_blockSizeIsConst && (m_srcVarDsc->lvFieldCnt == 1) && (m_dstVarDsc != nullptr) && + (m_blockSize == genTypeSize(m_dstVarDsc->TypeGet()))) + { + // Check for type agreement + unsigned fieldLclNum = m_comp->lvaGetDesc(m_srcLclNum)->lvFieldLclStart; + var_types srcType = m_comp->lvaGetDesc(fieldLclNum)->TypeGet(); + if (m_dstVarDsc->TypeGet() == srcType) + { + m_dstSingleLclVarAsg = true; + } + } + } + // Are neither dest or src promoted structs? + else + { + assert(!(m_dstDoFldAsg && dstFldIsProfitable) && !(m_srcDoFldAsg && srcFldIsProfitable)); + requiresCopyBlock = true; // Leave as a CopyBlock + JITDUMP(" with no promoted structs"); + } + } + + // If we require a copy block the set both of the field assign bools to false + if (requiresCopyBlock) + { + // If a copy block is required then we won't do field by field assignments + m_dstDoFldAsg = false; + m_srcDoFldAsg = false; + } + + JITDUMP(requiresCopyBlock ? " this requires a CopyBlock.\n" : " using field by field assignments.\n"); + + // Mark the dest/src structs as DoNotEnreg when they are not being fully referenced as the same type. + // + if (!m_dstDoFldAsg && (m_dstVarDsc != nullptr) && !m_dstSingleLclVarAsg) + { + if (!m_dstVarDsc->lvRegStruct || (m_dstVarDsc->lvType != m_dst->TypeGet())) + { + if (!m_dst->IsMultiRegLclVar() || (m_blockSize != m_dstVarDsc->lvExactSize) || + (m_dstVarDsc->lvCustomLayout && m_dstVarDsc->lvContainsHoles)) + { + // Mark it as DoNotEnregister. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_dst->IsMultiRegLclVar()) + { + // Handle this as lvIsMultiRegRet; this signals to SSA that it can't consider these fields + // SSA candidates (we don't have a way to represent multiple SSANums on MultiRegLclVar nodes). + m_dstVarDsc->lvIsMultiRegRet = true; + } + } + } + + if (!m_srcDoFldAsg && (m_srcVarDsc != nullptr) && !m_srcSingleLclVarAsg) + { + if (!m_srcVarDsc->lvRegStruct || (m_srcVarDsc->lvType != m_dst->TypeGet())) + { + m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + } + + if (requiresCopyBlock) + { + const var_types asgType = m_dst->TypeGet(); + bool isBlkReqd = (asgType == TYP_STRUCT); + m_dst = m_comp->fgMorphBlockOperand(m_dst, asgType, m_blockSize, isBlkReqd); + m_dst->gtFlags |= GTF_DONT_CSE; + m_asg->gtOp1 = m_dst; + m_asg->gtFlags |= (m_dst->gtFlags & GTF_ALL_EFFECT); + + // Eliminate the "OBJ or BLK" node on the src. + m_src = m_comp->fgMorphBlockOperand(m_src, asgType, m_blockSize, false /*!isBlkReqd*/); + m_asg->gtOp2 = m_src; + + m_result = m_asg; + m_transformationDecision = BlockTransformation::StructBlock; + } + else + { + m_result = CopyFieldByField(); + m_transformationDecision = BlockTransformation::FieldByField; + } +} + +//------------------------------------------------------------------------ +// CopyFieldByField: transform the copy block to a field by field asignment. +// +// Notes: +// We do it for promoted lclVars which fields can be enregistered. +// +GenTree* MorphCopyBlockHelper::CopyFieldByField() +{ + GenTreeOp* asgFields = nullptr; + + GenTree* addrSpill = nullptr; + unsigned addrSpillTemp = BAD_VAR_NUM; + bool addrSpillIsStackDest = false; // true if 'addrSpill' represents the address in our local stack frame + + GenTree* addrSpillAsg = nullptr; + + unsigned fieldCnt = DUMMY_INIT(0); + + if (m_dstDoFldAsg && m_srcDoFldAsg) + { + // To do fieldwise assignments for both sides. + // The structs do not have to be the same exact types but have to have same field types + // at the same offsets. + assert(m_dstLclNum != BAD_VAR_NUM && m_srcLclNum != BAD_VAR_NUM); + assert(m_dstVarDsc != nullptr && m_srcVarDsc != nullptr && m_dstVarDsc->lvFieldCnt == m_srcVarDsc->lvFieldCnt); + + fieldCnt = m_dstVarDsc->lvFieldCnt; + } + else if (m_dstDoFldAsg) + { + fieldCnt = m_dstVarDsc->lvFieldCnt; + m_src = m_comp->fgMorphBlockOperand(m_src, m_asg->TypeGet(), m_blockSize, false /*isBlkReqd*/); + + m_srcUseLclFld = m_comp->fgMorphCanUseLclFldForCopy(m_dstLclNum, m_srcLclNum); + + if (!m_srcUseLclFld && m_srcAddr == nullptr) + { + m_srcAddr = m_comp->fgMorphGetStructAddr(&m_src, m_dstVarDsc->GetStructHnd(), true /* rValue */); + } + + if (!m_srcUseLclFld) + { + if (m_comp->gtClone(m_srcAddr)) + { + // m_srcAddr is simple expression. No need to spill. + noway_assert((m_srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + } + else + { + // m_srcAddr is complex expression. Clone and spill it (unless the destination is + // a struct local that only has one field, in which case we'd only use the + // address value once...) + if (m_dstVarDsc->lvFieldCnt > 1) + { + // We will spill m_srcAddr (i.e. assign to a temp "BlockOp address local") + // no need to clone a new copy as it is only used once + // + addrSpill = m_srcAddr; // addrSpill represents the 'm_srcAddr' + } + } + } + } + else + { + assert(m_srcDoFldAsg); + fieldCnt = m_srcVarDsc->lvFieldCnt; + m_dst = m_comp->fgMorphBlockOperand(m_dst, m_dst->TypeGet(), m_blockSize, false /*isBlkReqd*/); + if (m_dst->OperIsBlk()) + { + m_dst->SetOper(GT_IND); + m_dst->gtType = TYP_STRUCT; + } + m_dstUseLclFld = m_comp->fgMorphCanUseLclFldForCopy(m_srcLclNum, m_dstLclNum); + if (!m_dstUseLclFld) + { + m_dstAddr = m_comp->gtNewOperNode(GT_ADDR, TYP_BYREF, m_dst); + } + + // If we're doing field-wise stores, to an address within a local, and we copy + // the address into "addrSpill", do *not* declare the original local var node in the + // field address as GTF_VAR_DEF and GTF_VAR_USEASG; we will declare each of the + // field-wise assignments as an "indirect" assignment to the local. + // ("m_dstLclNode" is a subtree of "m_dstAddr"; make sure we remove the flags before + // we clone it.) + if (m_dstLclNode != nullptr) + { + m_dstLclNode->gtFlags &= ~(GTF_VAR_DEF | GTF_VAR_USEASG); + } + + if (!m_dstUseLclFld) + { + if (m_comp->gtClone(m_dstAddr)) + { + // m_dstAddr is simple expression. No need to spill + noway_assert((m_dstAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + } + else + { + // m_dstAddr is complex expression. Clone and spill it (unless + // the source is a struct local that only has one field, in which case we'd only + // use the address value once...) + if (m_srcVarDsc->lvFieldCnt > 1) + { + // We will spill m_dstAddr (i.e. assign to a temp "BlockOp address local") + // no need to clone a new copy as it is only used once + // + addrSpill = m_dstAddr; // addrSpill represents the 'm_dstAddr' + } + } + } + } + + // TODO-CQ: this should be based on a more general + // "BaseAddress" method, that handles fields of structs, before or after + // morphing. + if ((addrSpill != nullptr) && addrSpill->OperIs(GT_ADDR)) + { + GenTree* addrSpillOp = addrSpill->AsOp()->gtGetOp1(); + if (addrSpillOp->IsLocal()) + { + // We will *not* consider this to define the local, but rather have each individual field assign + // be a definition. + addrSpillOp->gtFlags &= ~(GTF_LIVENESS_MASK); + assert(m_comp->lvaGetPromotionType(addrSpillOp->AsLclVarCommon()->GetLclNum()) != + Compiler::PROMOTION_TYPE_INDEPENDENT); + addrSpillIsStackDest = true; // addrSpill represents the address of LclVar[varNum] in our + // local stack frame + } + } + + if (addrSpill != nullptr) + { + // 'addrSpill' is already morphed + + // Spill the (complex) address to a BYREF temp. + // Note, at most one address may need to be spilled. + addrSpillTemp = m_comp->lvaGrabTemp(true DEBUGARG("BlockOp address local")); + + LclVarDsc* addrSpillDsc = m_comp->lvaGetDesc(addrSpillTemp); + + addrSpillDsc->lvType = TYP_BYREF; + + if (addrSpillIsStackDest) + { + addrSpillDsc->lvStackByref = true; + } + + GenTreeLclVar* addrSpillNode = m_comp->gtNewLclvNode(addrSpillTemp, TYP_BYREF); + addrSpillAsg = m_comp->gtNewAssignNode(addrSpillNode, addrSpill); + + // If we are assigning the address of a LclVar here + // liveness does not account for this kind of address taken use. + // + // We have to mark this local as address exposed so + // that we don't delete the definition for this LclVar + // as a dead store later on. + // + if (addrSpill->OperGet() == GT_ADDR) + { + GenTree* addrOp = addrSpill->AsOp()->gtOp1; + if (addrOp->IsLocal()) + { + unsigned lclVarNum = addrOp->AsLclVarCommon()->GetLclNum(); + m_comp->lvaGetDesc(lclVarNum)->lvAddrExposed = true; + m_comp->lvaSetVarDoNotEnregister(lclVarNum DEBUGARG(Compiler::DNER_AddrExposed)); + } + } + } + + // We may have allocated a temp above, and that may have caused the lvaTable to be expanded. + // So, beyond this point we cannot rely on the old values of 'm_srcVarDsc' and 'm_dstVarDsc'. + for (unsigned i = 0; i < fieldCnt; ++i) + { + + GenTree* dstFld; + if (m_dstDoFldAsg) + { + noway_assert(m_dstLclNum != BAD_VAR_NUM); + unsigned dstFieldLclNum = m_comp->lvaGetDesc(m_dstLclNum)->lvFieldLclStart + i; + dstFld = m_comp->gtNewLclvNode(dstFieldLclNum, m_comp->lvaGetDesc(dstFieldLclNum)->TypeGet()); + // If it had been labeled a "USEASG", assignments to the individual promoted fields are not. + if (m_dstAddr != nullptr) + { + noway_assert(m_dstAddr->AsOp()->gtOp1->gtOper == GT_LCL_VAR); + dstFld->gtFlags |= m_dstAddr->AsOp()->gtOp1->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG); + } + else + { + noway_assert(m_dstLclNode != nullptr); + dstFld->gtFlags |= m_dstLclNode->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG); + } + // Don't CSE the lhs of an assignment. + dstFld->gtFlags |= GTF_DONT_CSE; + } + else + { + noway_assert(m_srcDoFldAsg); + + if (m_dstSingleLclVarAsg) + { + noway_assert(fieldCnt == 1); + noway_assert(m_dstVarDsc != nullptr); + noway_assert(addrSpill == nullptr); + + dstFld = m_comp->gtNewLclvNode(m_dstLclNum, m_dstVarDsc->TypeGet()); + } + else + { + GenTree* dstAddrClone = nullptr; + if (!m_dstUseLclFld) + { + // Need address of the destination. + if (addrSpill) + { + assert(addrSpillTemp != BAD_VAR_NUM); + dstAddrClone = m_comp->gtNewLclvNode(addrSpillTemp, TYP_BYREF); + } + else + { + if (i == 0) + { + // Use the orginal m_dstAddr tree when i == 0 + dstAddrClone = m_dstAddr; + } + else + { + // We can't clone multiple copies of a tree with persistent side effects + noway_assert((m_dstAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + + dstAddrClone = m_comp->gtCloneExpr(m_dstAddr); + noway_assert(dstAddrClone != nullptr); + + JITDUMP("dstAddr - Multiple Fields Clone created:\n"); + DISPTREE(dstAddrClone); + + // Morph the newly created tree + dstAddrClone = m_comp->fgMorphTree(dstAddrClone); + } + + // Is the address of a local? + GenTreeLclVarCommon* lclVarTree = nullptr; + bool isEntire = false; + bool* pIsEntire = (m_blockSizeIsConst ? &isEntire : nullptr); + if (dstAddrClone->DefinesLocalAddr(m_comp, m_blockSize, &lclVarTree, pIsEntire)) + { + lclVarTree->gtFlags |= GTF_VAR_DEF; + if (!isEntire) + { + lclVarTree->gtFlags |= GTF_VAR_USEASG; + } + } + } + } + + LclVarDsc* srcVarDsc = m_comp->lvaGetDesc(m_srcLclNum); + unsigned srcFieldLclNum = srcVarDsc->lvFieldLclStart + i; + LclVarDsc* srcFieldVarDsc = m_comp->lvaGetDesc(srcFieldLclNum); + + // Have to set the field sequence -- which means we need the field handle. + CORINFO_CLASS_HANDLE classHnd = srcVarDsc->GetStructHnd(); + CORINFO_FIELD_HANDLE fieldHnd = + m_comp->info.compCompHnd->getFieldInClass(classHnd, srcFieldVarDsc->lvFldOrdinal); + FieldSeqNode* curFieldSeq = m_comp->GetFieldSeqStore()->CreateSingleton(fieldHnd); + + unsigned srcFieldOffset = m_comp->lvaGetDesc(srcFieldLclNum)->lvFldOffset; + var_types srcType = srcFieldVarDsc->TypeGet(); + + if (!m_dstUseLclFld) + { + + if (srcFieldOffset == 0) + { + m_comp->fgAddFieldSeqForZeroOffset(dstAddrClone, curFieldSeq); + } + else + { + GenTree* fieldOffsetNode = m_comp->gtNewIconNode(srcFieldVarDsc->lvFldOffset, curFieldSeq); + dstAddrClone = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, dstAddrClone, fieldOffsetNode); + } + + dstFld = m_comp->gtNewIndir(srcType, dstAddrClone); + } + else + { + assert(m_dstAddOff == 0); + assert(dstAddrClone == nullptr); + assert((m_dstLclOffset == 0) || (m_dstFldSeq != nullptr)); + // If the dst was a struct type field "B" in a struct "A" then we add + // add offset of ("B" in "A") + current offset in "B". + unsigned summOffset = m_dstLclOffset + srcFieldOffset; + dstFld = m_comp->gtNewLclFldNode(m_dstLclNum, srcType, summOffset); + FieldSeqNode* dstFldFldSeq = m_comp->GetFieldSeqStore()->Append(m_dstFldSeq, curFieldSeq); + dstFld->AsLclFld()->SetFieldSeq(dstFldFldSeq); + + // TODO-1stClassStructs: remove this and implement storing to a field in a struct in a reg. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_LocalField)); + } + + // !!! The destination could be on stack. !!! + // This flag will let us choose the correct write barrier. + dstFld->gtFlags |= GTF_IND_TGTANYWHERE; + } + } + + GenTree* srcFld = nullptr; + if (m_srcDoFldAsg) + { + noway_assert(m_srcLclNum != BAD_VAR_NUM); + unsigned srcFieldLclNum = m_comp->lvaGetDesc(m_srcLclNum)->lvFieldLclStart + i; + srcFld = m_comp->gtNewLclvNode(srcFieldLclNum, m_comp->lvaGetDesc(srcFieldLclNum)->TypeGet()); + + noway_assert(m_srcLclNode != nullptr); + srcFld->gtFlags |= m_srcLclNode->gtFlags & ~GTF_NODE_MASK; + } + else + { + noway_assert(m_dstDoFldAsg); + noway_assert(m_dstLclNum != BAD_VAR_NUM); + unsigned dstFieldLclNum = m_comp->lvaGetDesc(m_dstLclNum)->lvFieldLclStart + i; + + if (m_srcSingleLclVarAsg) + { + noway_assert(fieldCnt == 1); + noway_assert(m_srcLclNum != BAD_VAR_NUM); + noway_assert(addrSpill == nullptr); + + srcFld = m_comp->gtNewLclvNode(m_srcLclNum, m_comp->lvaGetDesc(m_srcLclNum)->TypeGet()); + } + else + { + GenTree* srcAddrClone = nullptr; + if (!m_srcUseLclFld) + { + // Need address of the source. + if (addrSpill) + { + assert(addrSpillTemp != BAD_VAR_NUM); + srcAddrClone = m_comp->gtNewLclvNode(addrSpillTemp, TYP_BYREF); + } + else + { + if (i == 0) + { + // Use the orginal m_srcAddr tree when i == 0 + srcAddrClone = m_srcAddr; + } + else + { + // We can't clone multiple copies of a tree with persistent side effects + noway_assert((m_srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + + srcAddrClone = m_comp->gtCloneExpr(m_srcAddr); + noway_assert(srcAddrClone != nullptr); + + JITDUMP("m_srcAddr - Multiple Fields Clone created:\n"); + DISPTREE(srcAddrClone); + + // Morph the newly created tree + srcAddrClone = m_comp->fgMorphTree(srcAddrClone); + } + } + } + + CORINFO_CLASS_HANDLE classHnd = m_comp->lvaGetDesc(m_dstLclNum)->GetStructHnd(); + CORINFO_FIELD_HANDLE fieldHnd = + m_comp->info.compCompHnd->getFieldInClass(classHnd, + m_comp->lvaGetDesc(dstFieldLclNum)->lvFldOrdinal); + FieldSeqNode* curFieldSeq = m_comp->GetFieldSeqStore()->CreateSingleton(fieldHnd); + var_types destType = m_comp->lvaGetDesc(dstFieldLclNum)->lvType; + + bool done = false; + if (m_comp->lvaGetDesc(dstFieldLclNum)->lvFldOffset == 0) + { + // If this is a full-width use of the src via a different type, we need to create a + // GT_LCL_FLD. + // (Note that if it was the same type, 'm_srcSingleLclVarAsg' would be true.) + if (m_srcLclNum != BAD_VAR_NUM) + { + noway_assert(m_srcLclNode != nullptr); + assert(destType != TYP_STRUCT); + unsigned destSize = genTypeSize(destType); + m_srcVarDsc = m_comp->lvaGetDesc(m_srcLclNum); + unsigned srcSize = + (m_srcVarDsc->lvType == TYP_STRUCT) ? m_srcVarDsc->lvExactSize : genTypeSize(m_srcVarDsc); + if (destSize == srcSize) + { + m_srcLclNode->gtFlags |= GTF_VAR_CAST; + m_srcLclNode->ChangeOper(GT_LCL_FLD); + m_srcLclNode->gtType = destType; + m_srcLclNode->AsLclFld()->SetFieldSeq(curFieldSeq); + srcFld = m_srcLclNode; + done = true; + } + } + } + if (!done) + { + unsigned fldOffset = m_comp->lvaGetDesc(dstFieldLclNum)->lvFldOffset; + if (!m_srcUseLclFld) + { + assert(srcAddrClone != nullptr); + if (fldOffset == 0) + { + m_comp->fgAddFieldSeqForZeroOffset(srcAddrClone, curFieldSeq); + } + else + { + GenTreeIntCon* fldOffsetNode = m_comp->gtNewIconNode(fldOffset, curFieldSeq); + srcAddrClone = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, srcAddrClone, fldOffsetNode); + } + srcFld = m_comp->gtNewIndir(destType, srcAddrClone); + } + else + { + assert((m_srcLclOffset == 0) || (m_srcFldSeq != 0)); + assert(m_srcAddOff == 0); + // If the src was a struct type field "B" in a struct "A" then we add + // add offset of ("B" in "A") + current offset in "B". + unsigned summOffset = m_srcLclOffset + fldOffset; + srcFld = m_comp->gtNewLclFldNode(m_srcLclNum, destType, summOffset); + FieldSeqNode* srcFldFldSeq = m_comp->GetFieldSeqStore()->Append(m_srcFldSeq, curFieldSeq); + srcFld->AsLclFld()->SetFieldSeq(srcFldFldSeq); + // TODO-1stClassStructs: remove this and implement reading a field from a struct in a + // reg. + m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_LocalField)); + } + } + } + } + assert(srcFld != nullptr); + noway_assert(dstFld->TypeGet() == srcFld->TypeGet()); + + GenTreeOp* asgOneFld = m_comp->gtNewAssignNode(dstFld, srcFld); + + // If we spilled the address, and we didn't do individual field assignments to promoted fields, + // and it was of a local, ensure that the destination local variable has been marked as address + // exposed. Neither liveness nor SSA are able to track this kind of indirect assignments. + if (addrSpill && !m_dstDoFldAsg && m_dstLclNum != BAD_VAR_NUM) + { + noway_assert(m_comp->lvaGetDesc(m_dstLclNum)->lvAddrExposed); + } + +#if LOCAL_ASSERTION_PROP + if (m_comp->optLocalAssertionProp) + { + m_comp->optAssertionGen(asgOneFld); + } +#endif // LOCAL_ASSERTION_PROP + + if (addrSpillAsg != nullptr) + { + asgFields = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, addrSpillAsg, asgOneFld)->AsOp(); + addrSpillAsg = nullptr; + } + else if (asgFields != nullptr) + { + asgFields = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, asgFields, asgOneFld)->AsOp(); + } + else + { + asgFields = asgOneFld; + } + } + return asgFields; +} + +//------------------------------------------------------------------------ +// fgMorphCopyBlock: Perform the morphing of a block copy. +// +// Arguments: +// tree - a block copy (i.e. an assignment with a block op on the lhs). +// +// Return Value: +// We can return the orginal block copy unmodified (least desirable, but always correct) +// We can return a single assignment, when fgMorphOneAsgBlockOp transforms it (most desirable). +// If we have performed struct promotion of the Source() or the Dest() then we will try to +// perform a field by field assignment for each of the promoted struct fields. +// +// Assumptions: +// The child nodes for tree have already been Morphed. +// +// Notes: +// If we leave it as a block copy we will call lvaSetVarDoNotEnregister() on Source() or Dest() +// if they cannot be enregistered. +// When performing a field by field assignment we can have one of Source() or Dest treated as a blob of bytes +// and in such cases we will call lvaSetVarDoNotEnregister() on the one treated as a blob of bytes. +// If the Source() or Dest() is a struct that has a "CustomLayout" and "ConstainsHoles" then we +// can not use a field by field assignment and must leave the orginal block copy unmodified. +// +GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) +{ + return MorphCopyBlockHelper::MorphCopyBlock(this, tree); +} + +//------------------------------------------------------------------------ +// fgMorphInitBlock: Morph a block initialization assignment tree. +// +// Arguments: +// tree - A GT_ASG tree that performs block initialization. +// +// Return Value: +// A single assignment, when fgMorphOneAsgBlockOp transforms it. +// If the destination is a promoted struct local variable then we will try to +// perform a field by field assignment for each of the promoted struct fields. +// This is not always possible (e.g. if the struct is address exposed). +// +// Otherwise the orginal GT_ASG tree is returned unmodified, note that the +// nodes can still be changed. +// +// Assumptions: +// GT_ASG's children have already been morphed. +// +GenTree* Compiler::fgMorphInitBlock(GenTree* tree) +{ + return MorphInitBlockHelper::MorphInitBlock(this, tree); +} From a76664767cee488582ba511046a1f8bfd13f89dc Mon Sep 17 00:00:00 2001 From: Oded Hanson Date: Sat, 26 Jun 2021 16:37:59 +0300 Subject: [PATCH 132/926] minor optimization: move creation of X509 Subject Summary into catch block on MacOS. (#54774) --- .../src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index 07959c3e77b..5334d9eb31a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -399,7 +399,6 @@ namespace Internal.Cryptography.Pal return; Debug.Assert(!_certHandle.IsInvalid); - string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle); try { @@ -407,6 +406,7 @@ namespace Internal.Cryptography.Pal } catch (CryptographicException e) { + string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle); if (subjectSummary is null) { throw; From 32b58a1c800e1ce4e76c3fdf2458728d08f317a1 Mon Sep 17 00:00:00 2001 From: "Kevin Ransom (msft)" Date: Sat, 26 Jun 2021 06:51:19 -0700 Subject: [PATCH 133/926] Add metadatatoken override to SymbolMethod (#54656) * Add metadatatoken override to SymbolMethod * Add test Co-authored-by: Jan Kotas --- .../src/System/Reflection/Emit/SymbolMethod.cs | 2 ++ .../tests/ModuleBuilder/ModuleBuilderGetArrayMethod.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SymbolMethod.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SymbolMethod.cs index 10ae4bca5f7..b0be6da1ff0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SymbolMethod.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SymbolMethod.cs @@ -67,6 +67,8 @@ namespace System.Reflection.Emit #endregion #region MemberInfo Overrides + public override int MetadataToken => m_token; + public override Module Module => m_module; public override Type? ReflectedType => m_containingType; diff --git a/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderGetArrayMethod.cs b/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderGetArrayMethod.cs index fd4a9adae75..7161e362a9c 100644 --- a/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderGetArrayMethod.cs +++ b/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderGetArrayMethod.cs @@ -137,6 +137,7 @@ namespace System.Reflection.Emit.Tests Assert.Equal(methodName, method.Name); Assert.Equal(callingConvention, method.CallingConvention); Assert.Equal(returnType, method.ReturnType); + Assert.NotEqual(0, method.MetadataToken); } } } From db143a077217bc9848d1a6ca287bf5dc0f6a0eca Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Sat, 26 Jun 2021 10:52:23 -0700 Subject: [PATCH 134/926] Revert "Fix 54025 (#54070)" (#54728) This reverts commit b2fe6678282503374aeea7ab5f468f7a3cc85a2f. --- src/coreclr/debug/ee/controller.cpp | 3 +-- src/coreclr/debug/ee/controller.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index eed3c453e78..831dc2dd5b4 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -4412,9 +4412,8 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, } else { - _ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass); // Copy the data into our buffer. - memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); + memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, SharedPatchBypassBuffer::cbBufferBypass); if (m_instrAttrib.m_fIsWrite) { diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h index 9fe0a67e3ea..9bcfc8682f7 100644 --- a/src/coreclr/debug/ee/controller.h +++ b/src/coreclr/debug/ee/controller.h @@ -288,7 +288,7 @@ public: // "PatchBypass" must be the first field of this class for alignment to be correct. BYTE PatchBypass[MAX_INSTRUCTION_LENGTH]; #if defined(TARGET_AMD64) - const static int cbBufferBypass = 0x20; + const static int cbBufferBypass = 0x10; BYTE BypassBuffer[cbBufferBypass]; UINT_PTR RipTargetFixup; From 11967d4ceac2272e0e9624e41340a0fa53ca3c1a Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 26 Jun 2021 21:15:41 +0300 Subject: [PATCH 135/926] [interp] Don't cprop between vt vars of different sizes (#54734) Via unsafe code, getting the lower Vector128 from a Vector256 ends up as a move of `sizeof (Vector128)` from a Vector256 var. However, the destination var is not a valid copy of the source var, having a different type --- src/mono/mono/mini/interp/transform.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index e4651250385..0c4776ac0be 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -8508,17 +8508,30 @@ retry: } else if (opcode == MINT_LDOBJ_VT) { InterpInst *ldloca = local_defs [sregs [0]].ins; if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int ldsize = ins->data [0]; int local = ldloca->sregs [0]; - // Replace LDLOCA + LDOBJ_VT with MOV_VT - ins->opcode = MINT_MOV_VT; local_ref_count [sregs [0]]--; - sregs [0] = local; + if (ldsize == td->locals [local].size) { + // Replace LDLOCA + LDOBJ_VT with MOV_VT + ins->opcode = MINT_MOV_VT; + sregs [0] = local; + needs_retry = TRUE; + } else { + // This loads just a part of the local valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_OFF); + interp_ins_set_dreg (ins, ins->prev->dreg); + interp_ins_set_sreg (ins, local); + ins->data [0] = 0; + ins->data [1] = MINT_TYPE_VT; + ins->data [2] = ldsize; + + interp_clear_ins (ins->prev); + } if (td->verbose_level) { g_print ("Replace ldloca/ldobj_vt pair :\n\t"); dump_interp_inst (ins); } - needs_retry = TRUE; } } else if (MINT_IS_STFLD (opcode) && ins->data [0] == 0) { InterpInst *ldloca = local_defs [sregs [0]].ins; From 3b3d20de81f35392fd78a344eb9f7795d73f6bc2 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sat, 26 Jun 2021 20:20:39 +0200 Subject: [PATCH 136/926] add pre-cancellation check and enable ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException for QUIC (#54540) --- .../Implementations/MsQuic/MsQuicStream.cs | 27 +++++++++++++++++++ ...icStreamConnectedStreamConformanceTests.cs | 2 -- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index e796496bcf1..a0fea559d6c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -215,6 +215,7 @@ namespace System.Net.Quic.Implementations.MsQuic internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, CancellationToken cancellationToken = default) { ThrowIfDisposed(); + using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); @@ -229,6 +230,19 @@ namespace System.Net.Quic.Implementations.MsQuic throw new InvalidOperationException(SR.net_quic_writing_notallowed); } + if (cancellationToken.IsCancellationRequested) + { + lock (_state) + { + if (_state.SendState == SendState.None || _state.SendState == SendState.Pending) + { + _state.SendState = SendState.Aborted; + } + } + + throw new System.OperationCanceledException(cancellationToken); + } + // Make sure start has completed if (!_started) { @@ -304,6 +318,19 @@ namespace System.Net.Quic.Implementations.MsQuic throw new InvalidOperationException(SR.net_quic_reading_notallowed); } + if (cancellationToken.IsCancellationRequested) + { + lock (_state) + { + if (_state.ReadState == ReadState.None) + { + _state.ReadState = ReadState.Aborted; + } + } + + throw new System.OperationCanceledException(cancellationToken); + } + if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"[Stream#{_state.GetHashCode()}] reading into Memory of '{destination.Length}' bytes."); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index cd83662a1ba..908fdb3a3d3 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -40,8 +40,6 @@ namespace System.Net.Quic.Tests // TODO: new additions, find out the actual reason for hanging [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException() => base.ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException(); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); From c78d79058892cb3888b7c5d531ed685e2f3dbc31 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com> Date: Sat, 26 Jun 2021 22:11:08 +0300 Subject: [PATCH 137/926] Use an untyped `Task` instead of a `Task` in `TimerQueueTimer`. (#54455) --- .../src/System/Threading/Timer.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs index e2766f270c4..6b7e928cd5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs @@ -440,7 +440,7 @@ namespace System.Threading // A timer in our TimerQueue. [DebuggerDisplay("{DisplayString,nq}")] [DebuggerTypeProxy(typeof(TimerDebuggerTypeProxy))] - internal sealed partial class TimerQueueTimer : IThreadPoolWorkItem + internal sealed class TimerQueueTimer : IThreadPoolWorkItem { // The associated timer queue. private readonly TimerQueue _associatedTimerQueue; @@ -473,12 +473,12 @@ namespace System.Threading // after all pending callbacks are complete. We set _canceled to prevent any callbacks that // are already queued from running. We track the number of callbacks currently executing in // _callbacksRunning. We set _notifyWhenNoCallbacksRunning only when _callbacksRunning - // reaches zero. Same applies if Timer.DisposeAsync() is used, except with a Task + // reaches zero. Same applies if Timer.DisposeAsync() is used, except with a Task // instead of with a provided WaitHandle. private int _callbacksRunning; private bool _canceled; internal bool _everQueued; - private object? _notifyWhenNoCallbacksRunning; // may be either WaitHandle or Task + private object? _notifyWhenNoCallbacksRunning; // may be either WaitHandle or Task internal TimerQueueTimer(TimerCallback timerCallback, object? state, uint dueTime, uint period, bool flowExecutionContext) { @@ -626,20 +626,20 @@ namespace System.Threading Debug.Assert( notifyWhenNoCallbacksRunning == null || - notifyWhenNoCallbacksRunning is Task); + notifyWhenNoCallbacksRunning is Task); - // There are callbacks queued or running, so we need to store a Task + // There are callbacks queued or running, so we need to store a Task // that'll be used to signal the caller when all callbacks complete. Do so as long as // there wasn't a previous CloseAsync call that did. if (notifyWhenNoCallbacksRunning == null) { - var t = new Task((object?)null, TaskCreationOptions.RunContinuationsAsynchronously); + var t = new Task((object?)null, TaskCreationOptions.RunContinuationsAsynchronously, true); _notifyWhenNoCallbacksRunning = t; return new ValueTask(t); } // A previous CloseAsync call already hooked up a task. Just return it. - return new ValueTask((Task)notifyWhenNoCallbacksRunning); + return new ValueTask((Task)notifyWhenNoCallbacksRunning); } } @@ -675,7 +675,7 @@ namespace System.Threading internal void SignalNoCallbacksRunning() { object? toSignal = _notifyWhenNoCallbacksRunning; - Debug.Assert(toSignal is WaitHandle || toSignal is Task); + Debug.Assert(toSignal is WaitHandle || toSignal is Task); if (toSignal is WaitHandle wh) { @@ -683,7 +683,7 @@ namespace System.Threading } else { - ((Task)toSignal).TrySetResult(true); + ((Task)toSignal).TrySetResult(); } } From c2e8973c625dfb890dab3aa86d7d5a6be636cbde Mon Sep 17 00:00:00 2001 From: Danny Friar Date: Sat, 26 Jun 2021 20:29:30 +0100 Subject: [PATCH 138/926] Expose underlying unix file descriptor in SafeMemoryMappedFileHandle (#53538) * expose unix file descriptor safe memory map * store handle after DangerousAddRef * add test verifying handle matches filestream handle * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs Co-authored-by: Stephen Toub * Dispose MemoryMappedFile in MapHandleMatchesFileStreamHandle test * Update src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs Co-authored-by: Stephen Toub * Update MemoryMappedFile.CreateFromFile.Tests.cs * Move mmf in test to using block * Fix test Co-authored-by: Danny Friar Co-authored-by: Stephen Toub --- .../Win32/SafeMemoryMappedFileHandle.Unix.cs | 38 +++++++++++++++---- .../MemoryMappedView.Unix.cs | 4 +- .../MemoryMappedFile.CreateFromFile.Tests.cs | 17 +++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs index 32e76bd9467..3428b012182 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs @@ -11,15 +11,14 @@ namespace Microsoft.Win32.SafeHandles { public sealed partial class SafeMemoryMappedFileHandle : SafeHandleZeroOrMinusOneIsInvalid { - /// Counter used to produce a unique handle value. - private static long s_counter; - /// /// The underlying FileStream. May be null. We hold onto the stream rather than just /// onto the underlying handle to ensure that logic associated with disposing the stream /// (e.g. deleting the file for DeleteOnClose) happens at the appropriate time. /// - internal readonly FileStream? _fileStream; + private readonly FileStream? _fileStream; + /// The FileStream's handle, cached to avoid repeated accesses to FileStream.SafeFileHandle that could, in theory, change. + internal SafeFileHandle? _fileStreamHandle; /// Whether this SafeHandle owns the _fileStream and should Dispose it when disposed. internal readonly bool _ownsFileStream; @@ -59,9 +58,22 @@ namespace Microsoft.Win32.SafeHandles _options = options; _capacity = capacity; - // Fake a unique int handle value > 0. - int nextHandleValue = (int)((Interlocked.Increment(ref s_counter) % (int.MaxValue - 1)) + 1); - SetHandle(new IntPtr(nextHandleValue)); + IntPtr handlePtr; + + if (fileStream != null) + { + bool ignored = false; + SafeFileHandle handle = fileStream.SafeFileHandle; + handle.DangerousAddRef(ref ignored); + _fileStreamHandle = handle; + handlePtr = handle.DangerousGetHandle(); + } + else + { + handlePtr = IntPtr.MaxValue; + } + + SetHandle(handlePtr); } protected override void Dispose(bool disposing) @@ -74,7 +86,17 @@ namespace Microsoft.Win32.SafeHandles base.Dispose(disposing); } - protected override bool ReleaseHandle() => true; // Nothing to clean up. We unlinked immediately after creating the backing store. + protected override bool ReleaseHandle() + { + if (_fileStreamHandle != null) + { + SetHandle((IntPtr) (-1)); + _fileStreamHandle.DangerousRelease(); + _fileStreamHandle = null; + } + + return true; + } public override bool IsInvalid => (long)handle <= 0; } diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs index b5e5a54c56c..917058aee93 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs @@ -56,10 +56,10 @@ namespace System.IO.MemoryMappedFiles // If we have a file handle, get the file descriptor from it. If the handle is null, // we'll use an anonymous backing store for the map. SafeFileHandle fd; - if (memMappedFileHandle._fileStream != null) + if (memMappedFileHandle._fileStreamHandle != null) { // Get the file descriptor from the SafeFileHandle - fd = memMappedFileHandle._fileStream.SafeFileHandle; + fd = memMappedFileHandle._fileStreamHandle; Debug.Assert(!fd.IsInvalid); } else diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs index 58cc8b3fb25..824f9b9fd17 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs @@ -951,5 +951,22 @@ namespace System.IO.MemoryMappedFiles.Tests // ENODEV Operation not supported by device. catch (IOException ex) when (ex.HResult == 19) { }; } + + /// + /// Test to verify that the MemoryMappedFile has the same underlying handle as the FileStream it's created from + /// + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void MapHandleMatchesFileStreamHandle() + { + using (FileStream fs = File.OpenWrite(GetTestFilePath())) + { + using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, null, 4096, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false)) + { + SafeMemoryMappedFileHandle handle = mmf.SafeMemoryMappedFileHandle; + Assert.Equal(fs.SafeFileHandle.DangerousGetHandle(), handle.DangerousGetHandle()); + } + } + } } } From 46b5fdc8fc5855a9d78ba9bb4273c409a87841cd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 26 Jun 2021 15:30:56 -0400 Subject: [PATCH 139/926] Fix WaitForNextTickAsync_CanceledWaitThenWaitAgain_Succeeds test (#54775) There's a race condition in the test between the timer firing and cancellation being requested. It repros more on Linux because there's a smaller quantum on Linux than on Windows. --- .../System/Threading/PeriodicTimerTests.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs index 92367b8a467..099096ac4f5 100644 --- a/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs +++ b/src/libraries/System.Runtime/tests/System/Threading/PeriodicTimerTests.cs @@ -177,18 +177,24 @@ namespace System.Threading.Tests { using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1)); - ValueTask task = timer.WaitForNextTickAsync(new CancellationToken(true)); - Assert.ThrowsAny(() => task.Result); - var cts = new CancellationTokenSource(); - task = timer.WaitForNextTickAsync(cts.Token); + ValueTask task = timer.WaitForNextTickAsync(cts.Token); cts.Cancel(); - Assert.Equal(cts.Token, Assert.ThrowsAny(() => task.Result).CancellationToken); - for (int i = 0; i < 10; i++) + try { - Assert.True(await timer.WaitForNextTickAsync()); + // If the task happens to succeed because the operation completes fast enough + // that it beats the cancellation request, then make sure it completed successfully. + Assert.True(await task); } + catch (OperationCanceledException oce) + { + // Otherwise, it must have been canceled with the relevant token. + Assert.Equal(cts.Token, oce.CancellationToken); + } + + // We should be able to await the next tick either way. + Assert.True(await timer.WaitForNextTickAsync()); } private static void WaitForTimerToBeCollected(WeakReference timer, bool expected) From aca3754a17de70453dd51d94b86dea54ae1b221b Mon Sep 17 00:00:00 2001 From: Joseph Da Silva <39675835+jfd16@users.noreply.github.com> Date: Sat, 26 Jun 2021 12:33:01 -0700 Subject: [PATCH 140/926] Fix BigInteger hex string parsing bug (#54251) (#54262) --- .../src/System/Numerics/BigNumber.cs | 2 +- .../tests/BigInteger/parse.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 957bd732fed..5ce7919e830 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -467,8 +467,8 @@ namespace System.Numerics if ((!isNegative && sign < 0) || sign == int.MinValue) { - sign = isNegative ? -1 : 1; bits = new[] { (uint)sign }; + sign = isNegative ? -1 : 1; } } else diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs index 23440b41d95..115a61366d0 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs @@ -105,6 +105,19 @@ namespace System.Numerics.Tests Assert.Equal(0, result); } + [Fact] + public void Parse_Hex32Bits() + { + // Regression test for: https://github.com/dotnet/runtime/issues/54251 + BigInteger result; + + Assert.True(BigInteger.TryParse("80000000", NumberStyles.HexNumber, null, out result)); + Assert.Equal(int.MinValue, result); + + Assert.True(BigInteger.TryParse("080000001", NumberStyles.HexNumber, null, out result)); + Assert.Equal(0x80000001u, result); + } + private static void RunFormatProviderParseStrings() { NumberFormatInfo nfi = new NumberFormatInfo(); From 2abd4878e2e356389352a909baa15043399cd0ca Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 26 Jun 2021 23:20:32 +0200 Subject: [PATCH 141/926] More file stream options tests (#53982) * add more tests * StreamWriter and StreamReader require FileStreamOptions with necessary access * allow for bufferSize == 0 * extend the test * refactor the tests * Revert "allow for bufferSize == 0" This reverts commit f1259be18a3446c1b8939883f484cc28347c74cf. --- .../tests/FileStream/FileStreamOptions.cs | 56 ++++++++++++++++++- .../StreamReader.StringCtorTests.cs | 10 ++++ .../StreamWriter.StringCtorTests.cs | 9 +++ .../src/System/IO/StreamReader.cs | 9 ++- .../src/System/IO/StreamWriter.cs | 9 ++- 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs index d078deb07e5..9be492d21bd 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; +using System.Text; using Xunit; namespace System.IO.Tests @@ -134,5 +134,59 @@ namespace System.IO.Tests Assert.Throws(() => new FileStreamOptions { BufferSize = -1 }); } + + [Theory] + [InlineData(FileMode.Create, FileAccess.Write, FileOptions.None)] + [InlineData(FileMode.Create, FileAccess.Write, FileOptions.Asynchronous)] + [InlineData(FileMode.Open, FileAccess.Read, FileOptions.None)] + [InlineData(FileMode.Open, FileAccess.Read, FileOptions.Asynchronous)] + [InlineData(FileMode.Create, FileAccess.ReadWrite, FileOptions.None)] + [InlineData(FileMode.Create, FileAccess.ReadWrite, FileOptions.Asynchronous)] + public void SettingsArePropagated(FileMode mode, FileAccess access, FileOptions fileOptions) + { + string filePath = GetTestFilePath(); + if (mode == FileMode.Open) + { + File.Create(filePath).Dispose(); + } + + bool canRead = (access & FileAccess.Read) != 0; + bool canWrite = (access & FileAccess.Write) != 0; + bool isAsync = (fileOptions & FileOptions.Asynchronous) != 0; + + var options = new FileStreamOptions + { + Mode = mode, + Access = access, + Options = fileOptions + }; + + Validate(new FileStream(filePath, options), filePath, isAsync, canRead, canWrite); + Validate(File.Open(filePath, options), filePath, isAsync, canRead, canWrite); + Validate(new FileInfo(filePath).Open(options), filePath, isAsync, canRead, canWrite); + + if (canWrite) + { + Validate((FileStream)new StreamWriter(filePath, options).BaseStream, filePath, isAsync, canRead, canWrite); + Validate((FileStream)new StreamWriter(filePath, Encoding.UTF8, options).BaseStream, filePath, isAsync, canRead, canWrite); + } + + if (canRead) + { + Validate((FileStream)new StreamReader(filePath, options).BaseStream, filePath, isAsync, canRead, canWrite); + Validate((FileStream)new StreamReader(filePath, Encoding.UTF8, false, options).BaseStream, filePath, isAsync, canRead, canWrite); + } + + static void Validate(FileStream fs, string expectedPath, bool expectedAsync, bool expectedCanRead, bool expectedCanWrite) + { + using (fs) + { + Assert.Equal(expectedPath, fs.Name); + Assert.Equal(expectedAsync, fs.IsAsync); + Assert.Equal(expectedCanRead, fs.CanRead); + Assert.Equal(expectedCanWrite, fs.CanWrite); + } + } + } } } diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReader.StringCtorTests.cs b/src/libraries/System.IO/tests/StreamReader/StreamReader.StringCtorTests.cs index d4c7ac4ad84..2e19dd283eb 100644 --- a/src/libraries/System.IO/tests/StreamReader/StreamReader.StringCtorTests.cs +++ b/src/libraries/System.IO/tests/StreamReader/StreamReader.StringCtorTests.cs @@ -47,6 +47,16 @@ namespace System.IO.Tests AssertExtensions.Throws("bufferSize", () => new StreamReader("path", Encoding.UTF8, true, 0)); } + [Fact] + public static void LackOfReadAccess_ThrowsArgumentException() + { + var noReadAccess = new FileStreamOptions { Access = FileAccess.Write }; + + AssertExtensions.Throws("options", () => new StreamReader("path", noReadAccess)); + AssertExtensions.Throws("options", () => new StreamReader("path", Encoding.UTF8, false, noReadAccess)); + AssertExtensions.Throws("options", () => new StreamReader("path", Encoding.UTF8, true, noReadAccess)); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/libraries/System.IO/tests/StreamWriter/StreamWriter.StringCtorTests.cs b/src/libraries/System.IO/tests/StreamWriter/StreamWriter.StringCtorTests.cs index 95041cd5650..841ff591aef 100644 --- a/src/libraries/System.IO/tests/StreamWriter/StreamWriter.StringCtorTests.cs +++ b/src/libraries/System.IO/tests/StreamWriter/StreamWriter.StringCtorTests.cs @@ -46,6 +46,15 @@ namespace System.IO.Tests AssertExtensions.Throws("bufferSize", () => new StreamWriter("path", true, Encoding.UTF8, 0)); } + [Fact] + public static void LackOfWriteAccess_ThrowsArgumentException() + { + var readOptions = new FileStreamOptions { Access = FileAccess.Read }; + + AssertExtensions.Throws("options", () => new StreamWriter("path", readOptions)); + AssertExtensions.Throws("options", () => new StreamWriter("path", Encoding.UTF8, readOptions)); + } + [Fact] public static void CreateStreamWriter() { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index af35711a682..3b3e03a806f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -211,8 +211,15 @@ namespace System.IO private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, FileStreamOptions options) { ValidateArgs(path, encoding); - if (options == null) + + if (options is null) + { throw new ArgumentNullException(nameof(options)); + } + if ((options.Access & FileAccess.Read) == 0) + { + throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(options)); + } return new FileStream(path, options); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 96ac62fd51d..7ba362604d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -167,8 +167,15 @@ namespace System.IO private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, FileStreamOptions options) { ValidateArgs(path, encoding); - if (options == null) + + if (options is null) + { throw new ArgumentNullException(nameof(options)); + } + if ((options.Access & FileAccess.Write) == 0) + { + throw new ArgumentException(SR.Argument_StreamNotWritable, nameof(options)); + } return new FileStream(path, options); } From 02f70d0b903422282cd7ba8037de6b66ea0b7a2d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 26 Jun 2021 23:22:09 +0200 Subject: [PATCH 142/926] Make DependentHandle public (#54246) * Move DependentHandle to System.Runtime * Update DependentHandle APIs to follow review * Make DependentHandle type public * Update DependentHandle on Mono runtime * Add allocation checks to DependentHandle APIs This avoids throwing ExecutionEngineException-s if one of the public APIs is called on a non-allocated DependentHandle instance * Add more unit tests for new public DependentHandle APIs * Add faster, unsafe internal APIs versions to DependentHandle * Naming improvements to Ephemeron type The ephemeron type is checked in the Mono runtime in "object.c" as follows: m_class_get_image (klass) == mono_defaults.corlib && !strcmp ("Ephemeron", m_class_get_name (klass)) As such, the namespace it belongs to in the managed runtime doesn't matter: the VM will just check that the type name matches, and that the type is in fact defined in corelib. This means we can just move it to System.Runtime without worrying about it being properly managed in the VM. Additionally, the type is defined in "sgen-mono.c" as follows: typedef struct { GCObject* key; GCObject* value; } Ephemeron; So as long as the layout matches the one of the type defined in C# (which it does), we're also free to rename the fields to better follow the naming guidelines, and the VM will have no issues with it. * Code style tweaks, improved nullability annotations * Remove incorrect DependentHandle comment on Mono * Add default Dispose test for DependentHandle Co-authored-by: Stephen Toub * Fix race condition in DependentHandle on Mono * Optimize DependentHandle.nGetPrimary on CoreCLR Removed internal call, same optimization as GCHandle * Small IL codegen improvement in DependentHandle.nGetPrimary * Simplify comments, add #ifdef for using directive * Minor code style tweaks * Change nGetPrimaryAndSecondary to nGetSecondary * Minor code refactoring to DependentHandle on Mono * Rename DependentHandle FCalls * Remove DependentHandle.UnsafeGetTargetAndDependent * Remove DependentHandle.GetTargetAndDependent * Fix FCall path for internal DependentHandle APIs * Add more DependentHandle unit tests * Reintroduce DependentHandle.GetTargetAndDependent() This fixes a bug due to a race condition in ConditionalWeakTable, which relies on this method which atomically retrieves both target and dependent with respect to target being set to null concurrently by other threads. This also exposes the same API publically to allow consumers to potentially implement custom conditional weak tables in the same manner. * Minor IL tweaks to produce smaller IR in the JIT * Add DependentHandle.StopTracking() API This also fixes two potential GC holes when setting DependentHandle.Target (see conversation from https://github.com/dotnet/runtime/pull/54246#issuecomment-863285327 onwards) * Rename InternalSetTarget to StopTracking, remove redundant param * Remove FCUnique from InternalStopTracking This was added in https://github.com/dotnet/runtime/pull/39810 to avoid a collision with MarshalNative::GCHandleInternalSet, as the two FCalls had identical implementations and their entry points were not unique. This should no longer be needed after 099fc478551f46cc54e7a18a32d9a9ac73727c73, as that changed both the signature and the implementation of this FCall. * Update API surface to match approved specs from API review * Update DependentHandle XML docs Co-authored-by: Stephen Toub --- .../System.Private.CoreLib.csproj | 2 +- .../CompilerServices/DependentHandle.cs | 84 ----- .../src/System/Runtime/DependentHandle.cs | 267 ++++++++++++++ .../InteropServices/GCHandle.CoreCLR.cs | 2 +- src/coreclr/vm/comdependenthandle.cpp | 128 +++---- src/coreclr/vm/comdependenthandle.h | 28 +- src/coreclr/vm/ecalllist.h | 15 +- .../CompilerServices/ConditionalWeakTable.cs | 23 +- .../Runtime/InteropServices/GCHandle.cs | 8 +- .../System.Runtime/ref/System.Runtime.cs | 11 + .../tests/System.Runtime.Tests.csproj | 1 + .../System/Runtime/DependentHandleTests.cs | 334 ++++++++++++++++++ .../System.Private.CoreLib.csproj | 2 +- .../src/System/GC.Mono.cs | 1 + .../CompilerServices/DependentHandle.cs | 73 ---- .../src/System/Runtime/DependentHandle.cs | 155 ++++++++ 16 files changed, 878 insertions(+), 256 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs create mode 100644 src/libraries/System.Runtime/tests/System/Runtime/DependentHandleTests.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 97671f56e5d..b42232633a7 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -206,13 +206,13 @@ - + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs deleted file mode 100644 index d0aff34f2bc..00000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.CompilerServices -{ - // ========================================================================================= - // This struct collects all operations on native DependentHandles. The DependentHandle - // merely wraps an IntPtr so this struct serves mainly as a "managed typedef." - // - // DependentHandles exist in one of two states: - // - // IsAllocated == false - // No actual handle is allocated underneath. Illegal to call GetPrimary - // or GetPrimaryAndSecondary(). Ok to call Free(). - // - // Initializing a DependentHandle using the nullary ctor creates a DependentHandle - // that's in the !IsAllocated state. - // (! Right now, we get this guarantee for free because (IntPtr)0 == NULL unmanaged handle. - // ! If that assertion ever becomes false, we'll have to add an _isAllocated field - // ! to compensate.) - // - // - // IsAllocated == true - // There's a handle allocated underneath. You must call Free() on this eventually - // or you cause a native handle table leak. - // - // This struct intentionally does no self-synchronization. It's up to the caller to - // to use DependentHandles in a thread-safe way. - // ========================================================================================= - internal struct DependentHandle - { - private IntPtr _handle; - - public DependentHandle(object primary, object? secondary) => - // no need to check for null result: nInitialize expected to throw OOM. - _handle = nInitialize(primary, secondary); - - public bool IsAllocated => _handle != IntPtr.Zero; - - // Getting the secondary object is more expensive than getting the first so - // we provide a separate primary-only accessor for those times we only want the - // primary. - public object? GetPrimary() => nGetPrimary(_handle); - - public object? GetPrimaryAndSecondary(out object? secondary) => - nGetPrimaryAndSecondary(_handle, out secondary); - - public void SetPrimary(object? primary) => - nSetPrimary(_handle, primary); - - public void SetSecondary(object? secondary) => - nSetSecondary(_handle, secondary); - - // Forces dependentHandle back to non-allocated state (if not already there) - // and frees the handle if needed. - public void Free() - { - if (_handle != IntPtr.Zero) - { - IntPtr handle = _handle; - _handle = IntPtr.Zero; - nFree(handle); - } - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr nInitialize(object primary, object? secondary); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object? nGetPrimary(IntPtr dependentHandle); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object? nGetPrimaryAndSecondary(IntPtr dependentHandle, out object? secondary); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void nSetPrimary(IntPtr dependentHandle, object? primary); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void nSetSecondary(IntPtr dependentHandle, object? secondary); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void nFree(IntPtr dependentHandle); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs new file mode 100644 index 00000000000..f080ba28dda --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +#if !DEBUG +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Runtime +{ + /// + /// Represents a dependent GC handle, which will conditionally keep a dependent object instance alive as long as + /// a target object instance is alive as well, without representing a strong reference to the target instance. + /// + /// + /// A value with a given object instance as target will not cause the target + /// to be kept alive if there are no other strong references to it, but it will do so for the dependent + /// object instance as long as the target is alive. + /// + /// Using this type is conceptually equivalent to having a weak reference to a given target object instance A, + /// with that object having a field or property (or some other strong reference) to a dependent object instance B. + /// + /// + /// The type is not thread-safe, and consumers are responsible for ensuring that + /// is not called concurrently with other APIs. Not doing so results in undefined behavior. + /// + /// + /// The , , and + /// properties are instead thread-safe, and safe to use if is not concurrently invoked as well. + /// + /// + public struct DependentHandle : IDisposable + { + // ========================================================================================= + // This struct collects all operations on native DependentHandles. The DependentHandle + // merely wraps an IntPtr so this struct serves mainly as a "managed typedef." + // + // DependentHandles exist in one of two states: + // + // IsAllocated == false + // No actual handle is allocated underneath. Illegal to get Target, Dependent + // or GetTargetAndDependent(). Ok to call Dispose(). + // + // Initializing a DependentHandle using the nullary ctor creates a DependentHandle + // that's in the !IsAllocated state. + // (! Right now, we get this guarantee for free because (IntPtr)0 == NULL unmanaged handle. + // ! If that assertion ever becomes false, we'll have to add an _isAllocated field + // ! to compensate.) + // + // + // IsAllocated == true + // There's a handle allocated underneath. You must call Dispose() on this eventually + // or you cause a native handle table leak. + // + // This struct intentionally does no self-synchronization. It's up to the caller to + // to use DependentHandles in a thread-safe way. + // ========================================================================================= + + private IntPtr _handle; + + /// + /// Initializes a new instance of the struct with the specified arguments. + /// + /// The target object instance to track. + /// The dependent object instance to associate with . + public DependentHandle(object? target, object? dependent) + { + // no need to check for null result: InternalInitialize expected to throw OOM. + _handle = InternalInitialize(target, dependent); + } + + /// + /// Gets a value indicating whether this instance was constructed with + /// and has not yet been disposed. + /// + /// This property is thread-safe. + public bool IsAllocated => (nint)_handle != 0; + + /// + /// Gets or sets the target object instance for the current handle. The target can only be set to a value + /// once the instance has been created. Doing so will cause to start + /// returning as well, and to become eligible for collection even if the previous target is still alive. + /// + /// + /// Thrown if is or if the input value is not . + /// This property is thread-safe. + public object? Target + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + return InternalGetTarget(handle); + } + set + { + IntPtr handle = _handle; + + if ((nint)handle == 0 || value is not null) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + InternalSetTargetToNull(handle); + } + } + + /// + /// Gets or sets the dependent object instance for the current handle. + /// + /// + /// If it is needed to retrieve both and , it is necessary + /// to ensure that the returned instance from will be kept alive until + /// is retrieved as well, or it might be collected and result in unexpected behavior. This can be done by storing the + /// target in a local and calling on it after is accessed. + /// + /// Thrown if is . + /// This property is thread-safe. + public object? Dependent + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + return InternalGetDependent(handle); + } + set + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + InternalSetDependent(handle, value); + } + } + + /// + /// Gets the values of both and (if available) as an atomic operation. + /// That is, even if is concurrently set to , calling this method + /// will either return for both target and dependent, or return both previous values. + /// If and were used sequentially in this scenario instead, it + /// would be possible to sometimes successfully retrieve the previous target, but then fail to get the dependent. + /// + /// The values of and . + /// Thrown if is . + /// This property is thread-safe. + public (object? Target, object? Dependent) TargetAndDependent + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + object? target = InternalGetTargetAndDependent(handle, out object? dependent); + + return (target, dependent); + } + } + + /// + /// Gets the target object instance for the current handle. + /// + /// The target object instance, if present. + /// This method mirrors , but without the allocation check. + internal object? UnsafeGetTarget() + { + return InternalGetTarget(_handle); + } + + /// + /// Atomically retrieves the values of both and , if available. + /// + /// The dependent instance, if available. + /// The values of and . + /// + /// This method mirrors the property, but without the allocation check. + /// The signature is also kept the same as the one for the internal call, to improve the codegen. + /// Note that is required to be on the stack (or it might not be tracked). + /// + internal object? UnsafeGetTargetAndDependent(out object? dependent) + { + return InternalGetTargetAndDependent(_handle, out dependent); + } + + /// + /// Sets the dependent object instance for the current handle to . + /// + /// This method mirrors the setter, but without allocation and input checks. + internal void UnsafeSetTargetToNull() + { + InternalSetTargetToNull(_handle); + } + + /// + /// Sets the dependent object instance for the current handle. + /// + /// This method mirrors , but without the allocation check. + internal void UnsafeSetDependent(object? dependent) + { + InternalSetDependent(_handle, dependent); + } + + /// + /// This method is not thread-safe. + public void Dispose() + { + // Forces the DependentHandle back to non-allocated state + // (if not already there) and frees the handle if needed. + IntPtr handle = _handle; + + if ((nint)handle != 0) + { + _handle = IntPtr.Zero; + + InternalFree(handle); + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr InternalInitialize(object? target, object? dependent); + +#if DEBUG + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object? InternalGetTarget(IntPtr dependentHandle); +#else + private static unsafe object? InternalGetTarget(IntPtr dependentHandle) + { + // This optimization is the same that is used in GCHandle in RELEASE mode. + // This is not used in DEBUG builds as the runtime performs additional checks. + // The logic below is the inlined copy of ObjectFromHandle in the unmanaged runtime. + return Unsafe.As(ref *(IntPtr*)(nint)dependentHandle); + } +#endif + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object? InternalGetDependent(IntPtr dependentHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object? InternalGetTargetAndDependent(IntPtr dependentHandle, out object? dependent); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void InternalSetDependent(IntPtr dependentHandle, object? dependent); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void InternalSetTargetToNull(IntPtr dependentHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void InternalFree(IntPtr dependentHandle); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs index 8cfaff78e93..fa342d4db70 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs @@ -22,7 +22,7 @@ namespace System.Runtime.InteropServices internal static extern object? InternalGet(IntPtr handle); #else internal static unsafe object? InternalGet(IntPtr handle) => - Unsafe.As(ref *(IntPtr*)handle); + Unsafe.As(ref *(IntPtr*)(nint)handle); #endif [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/vm/comdependenthandle.cpp b/src/coreclr/vm/comdependenthandle.cpp index e261f16064a..b2221908102 100644 --- a/src/coreclr/vm/comdependenthandle.cpp +++ b/src/coreclr/vm/comdependenthandle.cpp @@ -14,20 +14,18 @@ #include "common.h" #include "comdependenthandle.h" - - -FCIMPL2(OBJECTHANDLE, DependentHandle::nInitialize, Object *_primary, Object *_secondary) +FCIMPL2(OBJECTHANDLE, DependentHandle::InternalInitialize, Object *_target, Object *_dependent) { FCALL_CONTRACT; - OBJECTREF primary(_primary); - OBJECTREF secondary(_secondary); + OBJECTREF target(_target); + OBJECTREF dependent(_dependent); OBJECTHANDLE result = NULL; HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); // Create the handle. - result = GetAppDomain()->CreateDependentHandle(primary, secondary); + result = GetAppDomain()->CreateDependentHandle(target, dependent); HELPER_METHOD_FRAME_END_POLL(); @@ -35,9 +33,71 @@ FCIMPL2(OBJECTHANDLE, DependentHandle::nInitialize, Object *_primary, Object *_s } FCIMPLEND +FCIMPL1(Object*, DependentHandle::InternalGetTarget, OBJECTHANDLE handle) +{ + FCALL_CONTRACT; + FCUnique(0x54); + _ASSERTE(handle != NULL); -FCIMPL1(VOID, DependentHandle::nFree, OBJECTHANDLE handle) + return OBJECTREFToObject(ObjectFromHandle(handle)); +} +FCIMPLEND + +FCIMPL1(Object*, DependentHandle::InternalGetDependent, OBJECTHANDLE handle) +{ + FCALL_CONTRACT; + + _ASSERTE(handle != NULL); + + OBJECTREF target = ObjectFromHandle(handle); + + IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); + + // The dependent is tracked only if target is non-null + return (target != NULL) ? mgr->GetDependentHandleSecondary(handle) : NULL; +} +FCIMPLEND + +FCIMPL2(Object*, DependentHandle::InternalGetTargetAndDependent, OBJECTHANDLE handle, Object **outDependent) +{ + FCALL_CONTRACT; + + _ASSERTE(handle != NULL && outDependent != NULL); + + OBJECTREF target = ObjectFromHandle(handle); + IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); + + // The dependent is tracked only if target is non-null + *outDependent = (target != NULL) ? mgr->GetDependentHandleSecondary(handle) : NULL; + + return OBJECTREFToObject(target); +} +FCIMPLEND + +FCIMPL1(VOID, DependentHandle::InternalSetTargetToNull, OBJECTHANDLE handle) +{ + FCALL_CONTRACT; + + _ASSERTE(handle != NULL); + + IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); + mgr->StoreObjectInHandle(handle, NULL); +} +FCIMPLEND + +FCIMPL2(VOID, DependentHandle::InternalSetDependent, OBJECTHANDLE handle, Object *_dependent) +{ + FCALL_CONTRACT; + + _ASSERTE(handle != NULL); + + IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); + mgr->SetDependentHandleSecondary(handle, _dependent); +} +FCIMPLEND + +FCIMPL1(VOID, DependentHandle::InternalFree, OBJECTHANDLE handle) { FCALL_CONTRACT; @@ -48,59 +108,5 @@ FCIMPL1(VOID, DependentHandle::nFree, OBJECTHANDLE handle) DestroyDependentHandle(handle); HELPER_METHOD_FRAME_END(); - -} -FCIMPLEND - - - -FCIMPL1(Object*, DependentHandle::nGetPrimary, OBJECTHANDLE handle) -{ - FCALL_CONTRACT; - FCUnique(0x54); - _ASSERTE(handle != NULL); - return OBJECTREFToObject(ObjectFromHandle(handle)); -} -FCIMPLEND - - - -FCIMPL2(Object*, DependentHandle::nGetPrimaryAndSecondary, OBJECTHANDLE handle, Object **outSecondary) -{ - FCALL_CONTRACT; - _ASSERTE(handle != NULL && outSecondary != NULL); - - OBJECTREF primary = ObjectFromHandle(handle); - - IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - // Secondary is tracked only if primary is non-null - *outSecondary = (primary != NULL) ? mgr->GetDependentHandleSecondary(handle) : NULL; - - return OBJECTREFToObject(primary); -} -FCIMPLEND - -FCIMPL2(VOID, DependentHandle::nSetPrimary, OBJECTHANDLE handle, Object *_primary) -{ - FCALL_CONTRACT; - - _ASSERTE(handle != NULL); - - // Avoid collision with MarshalNative::GCHandleInternalSet - FCUnique(0x12); - - IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - mgr->StoreObjectInHandle(handle, _primary); -} -FCIMPLEND - -FCIMPL2(VOID, DependentHandle::nSetSecondary, OBJECTHANDLE handle, Object *_secondary) -{ - FCALL_CONTRACT; - - _ASSERTE(handle != NULL); - - IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - mgr->SetDependentHandleSecondary(handle, _secondary); } FCIMPLEND diff --git a/src/coreclr/vm/comdependenthandle.h b/src/coreclr/vm/comdependenthandle.h index 7c80d0bebe4..06786e62577 100644 --- a/src/coreclr/vm/comdependenthandle.h +++ b/src/coreclr/vm/comdependenthandle.h @@ -16,16 +16,16 @@ // A dependent handle is conceputally a tuple containing two object reference // -// * A Primary object (think key) -// * A Secondary Object (think value) +// * A Target object (think key) +// * A Dependent Object (think value) // -// The reference to both the primary object is (long) weak (will not keep the object alive). However the -// reference to the secondary object is (long) weak if the primary object is dead, and strong if the primary -// object is alive. (Hence it is a 'Dependent' handle since the strength of the secondary reference depends -// on the primary). +// The reference to both the target object is (long) weak (will not keep the object alive). However the +// reference to the dependent object is (long) weak if the target object is dead, and strong if the target +// object is alive. (Hence it is a 'Dependent' handle since the strength of the dependent reference depends +// on the target). // // The effect of this semantics is that it seems that while the DependentHandle exists, the system behaves as -// if there was a normal strong reference from the primary object to the secondary one. +// if there was a normal strong reference from the target object to the dependent one. // // The usefulness of a DependentHandle is to allow other objects to be 'attached' to a given object. By // having a hash table where the entries are dependent handles you can attach arbitrary objects to another @@ -40,13 +40,13 @@ class DependentHandle { public: - static FCDECL2(OBJECTHANDLE, nInitialize, Object *primary, Object *secondary); - static FCDECL1(Object *, nGetPrimary, OBJECTHANDLE handle); - static FCDECL2(Object *, nGetPrimaryAndSecondary, OBJECTHANDLE handle, Object **outSecondary); - static FCDECL1(VOID, nFree, OBJECTHANDLE handle); - static FCDECL2(VOID, nSetPrimary, OBJECTHANDLE handle, Object *primary); - static FCDECL2(VOID, nSetSecondary, OBJECTHANDLE handle, Object *secondary); + static FCDECL2(OBJECTHANDLE, InternalInitialize, Object *target, Object *dependent); + static FCDECL1(Object *, InternalGetTarget, OBJECTHANDLE handle); + static FCDECL1(Object *, InternalGetDependent, OBJECTHANDLE handle); + static FCDECL2(Object *, InternalGetTargetAndDependent, OBJECTHANDLE handle, Object **outDependent); + static FCDECL1(VOID, InternalSetTargetToNull, OBJECTHANDLE handle); + static FCDECL2(VOID, InternalSetDependent, OBJECTHANDLE handle, Object *dependent); + static FCDECL1(VOID, InternalFree, OBJECTHANDLE handle); }; #endif - diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 5cc1d5827fb..f77dc75c80b 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -61,12 +61,13 @@ FCFuncStart(gDependentHandleFuncs) - FCFuncElement("nInitialize", DependentHandle::nInitialize) - FCFuncElement("nGetPrimary", DependentHandle::nGetPrimary) - FCFuncElement("nGetPrimaryAndSecondary", DependentHandle::nGetPrimaryAndSecondary) - FCFuncElement("nFree", DependentHandle::nFree) - FCFuncElement("nSetPrimary", DependentHandle::nSetPrimary) - FCFuncElement("nSetSecondary", DependentHandle::nSetSecondary) + FCFuncElement("InternalInitialize", DependentHandle::InternalInitialize) + FCFuncElement("InternalGetTarget", DependentHandle::InternalGetTarget) + FCFuncElement("InternalGetDependent", DependentHandle::InternalGetDependent) + FCFuncElement("InternalGetTargetAndDependent", DependentHandle::InternalGetTargetAndDependent) + FCFuncElement("InternalSetTargetToNull", DependentHandle::InternalSetTargetToNull) + FCFuncElement("InternalSetDependent", DependentHandle::InternalSetDependent) + FCFuncElement("InternalFree", DependentHandle::InternalFree) FCFuncEnd() @@ -1139,7 +1140,7 @@ FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) FCClassElement("Debugger", "System.Diagnostics", gDiagnosticsDebugger) FCClassElement("Delegate", "System", gDelegateFuncs) -FCClassElement("DependentHandle", "System.Runtime.CompilerServices", gDependentHandleFuncs) +FCClassElement("DependentHandle", "System.Runtime", gDependentHandleFuncs) FCClassElement("Enum", "System", gEnumFuncs) FCClassElement("Environment", "System", gEnvironmentFuncs) #if defined(FEATURE_PERFTRACING) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index b3c72604977..88b8187f3df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -515,6 +515,7 @@ namespace System.Runtime.CompilerServices /// Returns -1 if not found (if key expires during FindEntry, this can be treated as "not found."). /// Must hold _lock, or be prepared to retry the search while holding _lock. /// + /// This method requires to be on the stack to be properly tracked. internal int FindEntry(TKey key, out object? value) { Debug.Assert(key != null); // Key already validated as non-null. @@ -523,14 +524,15 @@ namespace System.Runtime.CompilerServices int bucket = hashCode & (_buckets.Length - 1); for (int entriesIndex = Volatile.Read(ref _buckets[bucket]); entriesIndex != -1; entriesIndex = _entries[entriesIndex].Next) { - if (_entries[entriesIndex].HashCode == hashCode && _entries[entriesIndex].depHnd.GetPrimaryAndSecondary(out value) == key) + if (_entries[entriesIndex].HashCode == hashCode && _entries[entriesIndex].depHnd.UnsafeGetTargetAndDependent(out value) == key) { - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. + GC.KeepAlive(this); // Ensure we don't get finalized while accessing DependentHandle + return entriesIndex; } } - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. + GC.KeepAlive(this); // Ensure we don't get finalized while accessing DependentHandle value = null; return -1; } @@ -540,8 +542,9 @@ namespace System.Runtime.CompilerServices { if (index < _entries.Length) { - object? oKey = _entries[index].depHnd.GetPrimaryAndSecondary(out object? oValue); - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. + object? oKey = _entries[index].depHnd.UnsafeGetTargetAndDependent(out object? oValue); + + GC.KeepAlive(this); // Ensure we don't get finalized while accessing DependentHandle if (oKey != null) { @@ -592,7 +595,7 @@ namespace System.Runtime.CompilerServices Volatile.Write(ref entry.HashCode, -1); // Also, clear the key to allow GC to collect objects pointed to by the entry - entry.depHnd.SetPrimary(null); + entry.depHnd.UnsafeSetTargetToNull(); } internal void UpdateValue(int entryIndex, TValue newValue) @@ -602,7 +605,7 @@ namespace System.Runtime.CompilerServices VerifyIntegrity(); _invalid = true; - _entries[entryIndex].depHnd.SetSecondary(newValue); + _entries[entryIndex].depHnd.UnsafeSetDependent(newValue); _invalid = false; } @@ -634,7 +637,7 @@ namespace System.Runtime.CompilerServices break; } - if (entry.depHnd.IsAllocated && entry.depHnd.GetPrimary() is null) + if (entry.depHnd.IsAllocated && entry.depHnd.UnsafeGetTarget() is null) { // the entry has expired hasExpiredEntries = true; @@ -699,7 +702,7 @@ namespace System.Runtime.CompilerServices DependentHandle depHnd = oldEntry.depHnd; if (hashCode != -1 && depHnd.IsAllocated) { - if (depHnd.GetPrimary() != null) + if (depHnd.UnsafeGetTarget() is not null) { ref Entry newEntry = ref newEntries[newEntriesIndex]; @@ -795,7 +798,7 @@ namespace System.Runtime.CompilerServices // another container, removed entries are not, therefore this container must free them. if (_oldKeepAlive is null || entries[entriesIndex].HashCode == -1) { - entries[entriesIndex].depHnd.Free(); + entries[entriesIndex].depHnd.Dispose(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs index 78aa52682e4..579633db55c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs @@ -142,7 +142,7 @@ namespace System.Runtime.InteropServices } /// Determine whether this handle has been allocated or not. - public bool IsAllocated => _handle != IntPtr.Zero; + public bool IsAllocated => (nint)_handle != 0; /// /// Used to create a GCHandle from an int. This is intended to @@ -165,9 +165,9 @@ namespace System.Runtime.InteropServices public override bool Equals([NotNullWhen(true)] object? o) => o is GCHandle && _handle == ((GCHandle)o)._handle; - public static bool operator ==(GCHandle a, GCHandle b) => a._handle == b._handle; + public static bool operator ==(GCHandle a, GCHandle b) => (nint)a._handle == (nint)b._handle; - public static bool operator !=(GCHandle a, GCHandle b) => a._handle != b._handle; + public static bool operator !=(GCHandle a, GCHandle b) => (nint)a._handle != (nint)b._handle; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static IntPtr GetHandleValue(IntPtr handle) => new IntPtr((nint)handle & ~(nint)1); // Remove Pin flag @@ -179,7 +179,7 @@ namespace System.Runtime.InteropServices private static void ThrowIfInvalid(IntPtr handle) { // Check if the handle was never initialized or was freed. - if (handle == IntPtr.Zero) + if ((nint)handle == 0) { ThrowHelper.ThrowInvalidOperationException_HandleIsNotInitialized(); } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 4f9afb90503..47e45170b40 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9593,6 +9593,17 @@ namespace System.Runtime public AssemblyTargetedPatchBandAttribute(string targetedPatchBand) { } public string TargetedPatchBand { get { throw null; } } } + public partial struct DependentHandle : System.IDisposable + { + private object _dummy; + private int _dummyPrimitive; + public DependentHandle(object? target, object? dependent) { throw null; } + public object? Dependent { get { throw null; } set { } } + public bool IsAllocated { get { throw null; } } + public object? Target { get { throw null; } set { } } + public (object? Target, object? Dependent) TargetAndDependent { get { throw null; } } + public void Dispose() { } + } public enum GCLargeObjectHeapCompactionMode { Default = 1, diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index db3b3dcd1b5..780fcbfc161 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -215,6 +215,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Runtime/DependentHandleTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/DependentHandleTests.cs new file mode 100644 index 00000000000..2ee4eca51d4 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Runtime/DependentHandleTests.cs @@ -0,0 +1,334 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Xunit; + +namespace System.Runtime.Tests +{ + // NOTE: DependentHandle is already heavily tested indirectly through ConditionalWeakTable<,>. + // This class contains some specific tests for APIs that are only relevant when used directly. + public class DependentHandleTests + { + [Fact] + public void GetNullTarget() + { + object target = new(); + DependentHandle handle = new(null, null); + + Assert.True(handle.IsAllocated); + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + + handle.Dispose(); + } + + [Fact] + public void GetNotNullTarget() + { + object target = new(); + DependentHandle handle = new(target, null); + + // A handle with a set target and no dependent is valid + Assert.True(handle.IsAllocated); + Assert.Same(target, handle.Target); + Assert.Null(handle.Dependent); + + handle.Dispose(); + } + + [Fact] + public void SetTargetToNull_StateIsConsistent() + { + object target = new(), dependent = new(); + DependentHandle handle = new(target, dependent); + + Assert.True(handle.IsAllocated); + Assert.Same(handle.Target, target); + Assert.Same(handle.Dependent, dependent); + + handle.Target = null; + + Assert.True(handle.IsAllocated); + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + + handle.Dispose(); + } + + [Fact] + public void SetTargetToNull_RepeatedCallsAreFine() + { + object target = new(), dependent = new(); + DependentHandle handle = new(target, dependent); + + handle.Target = null; + + Assert.True(handle.IsAllocated); + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + + handle.Target = null; + handle.Target = null; + handle.Target = null; + + Assert.True(handle.IsAllocated); + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + + handle.Dispose(); + } + + [Fact] + public void GetSetDependent() + { + object target = new(), dependent = new(); + DependentHandle handle = new(target, null); + + // The target can be retrieved correctly + Assert.True(handle.IsAllocated); + Assert.Same(target, handle.Target); + Assert.Null(handle.Dependent); + + handle.Dependent = dependent; + + // The dependent can also be retrieved correctly + Assert.Same(target, handle.Target); + Assert.Same(dependent, handle.Dependent); + + handle.Dispose(); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void TargetKeepsDependentAlive() + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(out object target, out WeakReference weakDependent) + { + target = new object(); + + object dependent = new(); + + weakDependent = new WeakReference(dependent); + + return new DependentHandle(target, dependent); + } + + DependentHandle handle = Initialize(out object target, out WeakReference dependent); + + GC.Collect(); + + // The dependent has to still be alive as the target has a strong reference + Assert.Same(target, handle.Target); + Assert.True(dependent.IsAlive); + Assert.Same(dependent.Target, handle.Dependent); + + GC.KeepAlive(target); + + handle.Dispose(); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void DependentDoesNotKeepTargetAlive() + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(out WeakReference weakTarget, out object dependent) + { + dependent = new object(); + + object target = new(); + + weakTarget = new WeakReference(target); + + return new DependentHandle(target, dependent); + } + + DependentHandle handle = Initialize(out WeakReference target, out object dependent); + + GC.Collect(); + + // The target has to be collected, as there were no strong references to it + Assert.Null(handle.Target); + Assert.False(target.IsAlive); + + GC.KeepAlive(target); + + handle.Dispose(); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void DependentIsCollectedOnTargetNotReachable() + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(out WeakReference weakTarget, out WeakReference weakDependent) + { + object target = new(), dependent = new(); + + weakTarget = new WeakReference(target); + weakDependent = new WeakReference(dependent); + + return new DependentHandle(target, dependent); + } + + DependentHandle handle = Initialize(out WeakReference target, out WeakReference dependent); + + GC.Collect(); + + // Both target and dependent have to be collected, as there were no strong references to either + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + Assert.False(target.IsAlive); + Assert.False(dependent.IsAlive); + + handle.Dispose(); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void DependentIsCollectedOnTargetNotReachable_EvenWithReferenceCycles() + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(out WeakReference weakTarget, out WeakReference weakDependent) + { + object target = new(); + ObjectWithReference dependent = new() { Reference = target }; + + weakTarget = new WeakReference(target); + weakDependent = new WeakReference(dependent); + + return new DependentHandle(target, dependent); + } + + DependentHandle handle = Initialize(out WeakReference target, out WeakReference dependent); + + GC.Collect(); + + // Both target and dependent have to be collected, as there were no strong references to either. + // The fact that the dependent has a strong reference back to the target should not affect this. + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + Assert.False(target.IsAlive); + Assert.False(dependent.IsAlive); + + handle.Dispose(); + } + + private sealed class ObjectWithReference + { + public object Reference; + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void DependentIsCollectedAfterTargetIsSetToNull() + { + [MethodImpl(MethodImplOptions.NoInlining)] + static DependentHandle Initialize(out object target, out WeakReference weakDependent) + { + target = new(); + + object dependent = new(); + + weakDependent = new WeakReference(dependent); + + return new DependentHandle(target, dependent); + } + + DependentHandle handle = Initialize(out object target, out WeakReference dependent); + + handle.Target = null; + + GC.Collect(); + + // After calling StopTracking, the dependent is collected even if + // target is still alive and the handle itself has not been disposed + Assert.True(handle.IsAllocated); + Assert.Null(handle.Target); + Assert.Null(handle.Dependent); + Assert.False(dependent.IsAlive); + + GC.KeepAlive(target); + + handle.Dispose(); + } + + [Fact] + public void GetTarget_ThrowsInvalidOperationException() + { + Assert.Throws(() => default(DependentHandle).Target); + } + + [Fact] + public void GetDependent_ThrowsInvalidOperationException() + { + Assert.Throws(() => default(DependentHandle).Dependent); + } + + [Fact] + public void SetTarget_NotAllocated_ThrowsInvalidOperationException() + { + Assert.Throws(() => + { + DependentHandle handle = default; + handle.Target = new(); + }); + } + + [Fact] + public void SetTarget_NotNullObject_ThrowsInvalidOperationException() + { + Assert.Throws(() => + { + DependentHandle handle = default; + + try + { + handle.Target = new(); + } + finally + { + handle.Dispose(); + } + }); + } + + [Fact] + public void SetDependent_ThrowsInvalidOperationException() + { + Assert.Throws(() => + { + DependentHandle handle = default; + handle.Dependent = new(); + }); + } + + [Fact] + public void Dispose_RepeatedCallsAreFine() + { + object target = new(), dependent = new(); + DependentHandle handle = new(target, dependent); + + Assert.True(handle.IsAllocated); + + handle.Dispose(); + + Assert.False(handle.IsAllocated); + + handle.Dispose(); + + Assert.False(handle.IsAllocated); + + handle.Dispose(); + handle.Dispose(); + handle.Dispose(); + + Assert.False(handle.IsAllocated); + } + + [Fact] + public void Dispose_ValidOnDefault() + { + DependentHandle handle = default; + Assert.False(handle.IsAllocated); + handle.Dispose(); + } + } +} diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 803d83464ea..21d6492b718 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -241,8 +241,8 @@ + - diff --git a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs index ee874822c56..a92ebab4992 100644 --- a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime; using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; using System.Diagnostics.Tracing; diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs deleted file mode 100644 index 1b976a5906a..00000000000 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.CompilerServices -{ - internal struct Ephemeron - { - public object? key; - public object? value; - } - - // - // Instead of dependent handles, mono uses arrays of Ephemeron objects. - // - internal struct DependentHandle - { - private Ephemeron[] data; - - public DependentHandle(object? primary, object? secondary) - { - data = new Ephemeron[1]; - data[0].key = primary; - data[0].value = secondary; - GC.register_ephemeron_array(data); - } - - public bool IsAllocated => data != null; - - // Getting the secondary object is more expensive than getting the first so - // we provide a separate primary-only accessor for those times we only want the - // primary. - public object? GetPrimary() - { - if (!IsAllocated) - throw new NotSupportedException(); - if (data[0].key == GC.EPHEMERON_TOMBSTONE) - return null; - return data[0].key; - } - - public object? GetPrimaryAndSecondary(out object? secondary) - { - if (!IsAllocated) - throw new NotSupportedException(); - if (data[0].key == GC.EPHEMERON_TOMBSTONE) - { - secondary = null; - return null; - } - secondary = data[0].value; - return data[0].key; - } - - public void SetPrimary(object? primary) - { - if (!IsAllocated) - throw new NotSupportedException(); - data[0].key = primary; - } - - public void SetSecondary(object? secondary) - { - if (!IsAllocated) - throw new NotSupportedException(); - data[0].value = secondary; - } - - public void Free() - { - data = null!; - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs new file mode 100644 index 00000000000..8cfd9b013e5 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + internal struct Ephemeron + { + public object? Key; + public object? Value; + } + + // + // Instead of dependent handles, mono uses arrays of Ephemeron objects. + // + public struct DependentHandle : IDisposable + { + private Ephemeron[]? _data; + + public DependentHandle(object? target, object? dependent) + { + _data = new Ephemeron[1]; + _data[0].Key = target; + _data[0].Value = dependent; + GC.register_ephemeron_array(_data); + } + + public bool IsAllocated => _data is not null; + + public object? Target + { + get => UnsafeGetTarget(); + set + { + Ephemeron[]? data = _data; + + if (data is null || value is not null) + { + ThrowHelper.ThrowInvalidOperationException(); + + return; + } + + data[0].Key = null; + } + } + + public object? Dependent + { + get => UnsafeGetDependent(); + set => UnsafeSetDependent(value); + } + + public (object? Target, object? Dependent) TargetAndDependent + { + get + { + object? target = UnsafeGetTargetAndDependent(out object? dependent); + + return (target, dependent); + } + } + + internal object? UnsafeGetTarget() + { + Ephemeron[]? data = _data; + + if (data is null) + { + ThrowHelper.ThrowInvalidOperationException(); + + return default; + } + + object? key = data[0].Key; + + return key != GC.EPHEMERON_TOMBSTONE ? key : null; + } + + internal object? UnsafeGetDependent() + { + Ephemeron[]? data = _data; + + if (data is null) + { + ThrowHelper.ThrowInvalidOperationException(); + + return default; + } + + Ephemeron e = data[0]; + + return e.Key != GC.EPHEMERON_TOMBSTONE && e.Key is not null ? e.Value : null; + } + + internal object? UnsafeGetTargetAndDependent(out object? dependent) + { + Ephemeron[]? data = _data; + + if (data is null) + { + ThrowHelper.ThrowInvalidOperationException(); + + dependent = null; + + return null; + } + + Ephemeron e = data[0]; + + if (e.Key != GC.EPHEMERON_TOMBSTONE && e.Key is not null) + { + dependent = e.Value; + + return e.Key; + } + + dependent = null; + + return null; + } + + internal void UnsafeSetTargetToNull() + { + Ephemeron[]? data = _data; + + if (data is null) + { + ThrowHelper.ThrowInvalidOperationException(); + + return; + } + + data[0].Key = null; + } + + internal void UnsafeSetDependent(object? dependent) + { + Ephemeron[]? data = _data; + + if (data is null) + { + ThrowHelper.ThrowInvalidOperationException(); + + return; + } + + data[0].Value = dependent; + } + + public void Dispose() + { + _data = null; + } + } +} From 385b6f3e5fd6fbf08a8a82bde5a5f57f18018c2a Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sun, 27 Jun 2021 17:13:46 +0200 Subject: [PATCH 143/926] Fix MacOS build on 11.x SDK and Catalyst build (#54506) * Fix MacOS build on 11.x SDK and Catalyst build The configure.cmake was not getting the minimum supported OS version because it was being set via set_compile_options and the config functions can only get options from CMAKE_XXX_FLAGS. * Add comment explaining why we set the macOS options via CMAKE_XXX_FLAGS --- eng/native/configurecompiler.cmake | 19 +++++++++++-------- src/libraries/Native/Unix/CMakeLists.txt | 14 +++++++++++--- .../Native/Unix/System.Native/CMakeLists.txt | 4 ++++ src/libraries/Native/build-native.sh | 6 +++++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 99a8013d7cf..685949ca33d 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -393,30 +393,33 @@ if (CLR_CMAKE_HOST_UNIX) # replaced with a default value, and always gets expanded to an OS version. # https://gitlab.kitware.com/cmake/cmake/-/issues/20132 # We need to disable the warning that -tagret replaces -mmacosx-version-min - add_compile_options(-Wno-overriding-t-option) + set(DISABLE_OVERRIDING_MIN_VERSION_ERROR -Wno-overriding-t-option) add_link_options(-Wno-overriding-t-option) if(CLR_CMAKE_HOST_ARCH_ARM64) - add_compile_options(-target arm64-apple-ios14.2-macabi) + set(MACOS_VERSION_MIN_FLAGS "-target arm64-apple-ios14.2-macabi") add_link_options(-target arm64-apple-ios14.2-macabi) elseif(CLR_CMAKE_HOST_ARCH_AMD64) - add_compile_options(-target x86_64-apple-ios13.5-macabi) + set(MACOS_VERSION_MIN_FLAGS "-target x86_64-apple-ios13.5-macabi") add_link_options(-target x86_64-apple-ios13.5-macabi) else() clr_unknown_arch() endif() + # These options are intentionally set using the CMAKE_XXX_FLAGS instead of + # add_compile_options so that they take effect on the configuration functions + # in various configure.cmake files. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") else() if(CLR_CMAKE_HOST_ARCH_ARM64) - # 'pthread_jit_write_protect_np' is only available on macOS 11.0 or newer - set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=11.0) + set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0") add_compile_options(-arch arm64) elseif(CLR_CMAKE_HOST_ARCH_AMD64) - set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=10.13) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") add_compile_options(-arch x86_64) else() clr_unknown_arch() endif() - add_compile_options(${MACOS_VERSION_MIN_FLAGS}) - add_linker_flag(${MACOS_VERSION_MIN_FLAGS}) endif(CLR_CMAKE_TARGET_MACCATALYST) endif(CLR_CMAKE_HOST_OSX OR CLR_CMAKE_HOST_MACCATALYST) diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index 24bb6264f35..fcd7964aacb 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -82,15 +82,23 @@ endif() if(CLR_CMAKE_TARGET_MACCATALYST) # -target overrides -mmacosx-version-min so suppress warning about that # https://gitlab.kitware.com/cmake/cmake/-/issues/20132 - add_compile_options(-Wno-overriding-t-option) + set(DISABLE_OVERRIDING_MIN_VERSION_ERROR -Wno-overriding-t-option) add_link_options(-Wno-overriding-t-option) if (CLR_CMAKE_TARGET_ARCH_AMD64) - add_compile_options(-target x86_64-apple-ios13.5-macabi) + set(MACOS_VERSION_MIN_FLAGS "-target x86_64-apple-ios13.5-macabi") add_link_options(-target x86_64-apple-ios13.5-macabi) elseif (CLR_CMAKE_TARGET_ARCH_ARM64) - add_compile_options(-target arm64-apple-ios14.2-macabi) + set(MACOS_VERSION_MIN_FLAGS "-target arm64-apple-ios14.2-macabi") add_link_options(-target arm64-apple-ios14.2-macabi) endif() + + # These options are intentionally set using the CMAKE_XXX_FLAGS instead of + # add_compile_options so that they take effect on the configuration functions + # in various configure.cmake files. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${MACOS_VERSION_MIN_FLAGS} ${DISABLE_OVERRIDING_MIN_VERSION_ERROR}") + endif() if(CLR_CMAKE_TARGET_TVOS) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index d21e27a561b..a28952750b3 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -4,6 +4,10 @@ if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CM add_definitions(-DHAS_CONSOLE_SIGNALS) endif () +if (CLR_CMAKE_TARGET_OSX) + add_definitions(-D_DARWIN_C_SOURCE) +endif () + include_directories("${CLR_SRC_NATIVE_DIR}/common") set(NATIVE_SOURCES diff --git a/src/libraries/Native/build-native.sh b/src/libraries/Native/build-native.sh index f95f1f622f0..ba3e88b4260 100755 --- a/src/libraries/Native/build-native.sh +++ b/src/libraries/Native/build-native.sh @@ -77,7 +77,11 @@ fi if [[ "$__TargetOS" == OSX ]]; then # set default OSX deployment target - __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 $__CMakeArgs" + if [[ "$__BuildArch" == x64 ]]; then + __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 $__CMakeArgs" + else + __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 $__CMakeArgs" + fi elif [[ "$__TargetOS" == Android && -z "$ROOTFS_DIR" ]]; then if [[ -z "$ANDROID_NDK_ROOT" ]]; then echo "Error: You need to set the ANDROID_NDK_ROOT environment variable pointing to the Android NDK root." From 49ee95870faa371cdb46ec336f5ab107ec1fdf9c Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Sun, 27 Jun 2021 18:21:13 +0100 Subject: [PATCH 144/926] Do not timeout HostingListener when debugging (#54791) Do not timeout waiting for the host to start when running with the debugger attached. Addresses dotnet/aspnetcore#33886. --- .../src/HostFactoryResolver.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs index 12516f600b6..49ef8b69517 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs @@ -21,7 +21,7 @@ namespace Microsoft.Extensions.Hosting public const string CreateHostBuilder = nameof(CreateHostBuilder); // The amount of time we wait for the diagnostic source events to fire - private static readonly TimeSpan s_defaultWaitTimeout = TimeSpan.FromSeconds(5); + private static readonly TimeSpan s_defaultWaitTimeout = Debugger.IsAttached ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(5); public static Func? ResolveWebHostFactory(Assembly assembly) { @@ -44,10 +44,10 @@ namespace Microsoft.Extensions.Hosting // 3. Give the caller a chance to execute logic to mutate the IHostBuilder // 4. Resolve the instance of the applications's IHost // 5. Allow the caller to determine if the entry point has completed - public static Func? ResolveHostFactory(Assembly assembly, - TimeSpan? waitTimeout = null, - bool stopApplication = true, - Action? configureHostBuilder = null, + public static Func? ResolveHostFactory(Assembly assembly, + TimeSpan? waitTimeout = null, + bool stopApplication = true, + Action? configureHostBuilder = null, Action? entrypointCompleted = null) { if (assembly.EntryPoint is null) @@ -64,7 +64,7 @@ namespace Microsoft.Extensions.Hosting { return null; } - + // We're using a version >= 6 so the events can fire. If they don't fire // then it's because the application isn't using the hosting APIs } @@ -178,8 +178,8 @@ namespace Microsoft.Extensions.Hosting private readonly TaskCompletionSource _hostTcs = new(); private IDisposable? _disposable; - private Action? _configure; - private Action? _entrypointCompleted; + private readonly Action? _configure; + private readonly Action? _entrypointCompleted; private static readonly AsyncLocal _currentListener = new(); public HostingListener(string[] args, MethodInfo entryPoint, TimeSpan waitTimeout, bool stopApplication, Action? configure, Action? entrypointCompleted) From 15b4d9a50e91d048d904c66e02d7879e1187baa6 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sun, 27 Jun 2021 21:04:20 -0400 Subject: [PATCH 145/926] Fix missing entrypoints and add build-time validation (#54785) * Fix missing entrypoints and add build-time validation * Fix missing include * Update src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt Co-authored-by: Jan Kotas --- .../CMakeLists.txt | 11 +++++++++ .../entrypoints.c | 23 ++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt index a9029ec6290..77aa423237c 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -73,6 +73,17 @@ if (GEN_SHARED_LIB) target_link_libraries(System.Security.Cryptography.Native.Apple ${NATIVE_LIBS_EXTRA} ) + + if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS) + add_custom_command(TARGET System.Security.Cryptography.Native.Apple POST_BUILD + COMMENT "Verifying System.Security.Cryptography.Native.Apple points against entrypoints.c " + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../verify-entrypoints.sh + $ + ${CMAKE_CURRENT_SOURCE_DIR}/entrypoints.c + ${CMAKE_NM} + VERBATIM + ) + endif() endif() if (GEN_SHARED_LIB) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c index 1833d4a2161..5162b7a2193 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -21,6 +21,7 @@ #include "pal_x509_macos.h" #include "pal_x509chain.h" #include "pal_keyderivation_macos.h" +#include "pal_keyagree.h" static const Entry s_cryptoAppleNative[] = { @@ -30,8 +31,11 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_DigestFinal) DllImportEntry(AppleCryptoNative_DigestCurrent) DllImportEntry(AppleCryptoNative_DigestOneShot) + DllImportEntry(AppleCryptoNative_DigestReset) DllImportEntry(AppleCryptoNative_EccGenerateKey) DllImportEntry(AppleCryptoNative_EccGetKeySizeInBits) + DllImportEntry(AppleCryptoNative_EcdhKeyAgree) + DllImportEntry(AppleCryptoNative_GetRandomBytes) DllImportEntry(AppleCryptoNative_HmacFree) DllImportEntry(AppleCryptoNative_HmacCreate) DllImportEntry(AppleCryptoNative_HmacInit) @@ -40,14 +44,22 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_HmacCurrent) DllImportEntry(AppleCryptoNative_HmacOneShot) DllImportEntry(AppleCryptoNative_SecKeychainItemCopyKeychain) + DllImportEntry(AppleCryptoNative_SecKeychainCopyDefault) DllImportEntry(AppleCryptoNative_SecKeychainCreate) DllImportEntry(AppleCryptoNative_SecKeychainDelete) - DllImportEntry(AppleCryptoNative_SecKeychainCopyDefault) + DllImportEntry(AppleCryptoNative_SecKeychainEnumerateCerts) DllImportEntry(AppleCryptoNative_SecKeychainOpen) DllImportEntry(AppleCryptoNative_SecKeychainUnlock) - DllImportEntry(AppleCryptoNative_SetKeychainNeverLock) DllImportEntry(AppleCryptoNative_SecKeychainEnumerateIdentities) - DllImportEntry(AppleCryptoNative_GetRandomBytes) + DllImportEntry(AppleCryptoNative_SetKeychainNeverLock) + DllImportEntry(AppleCryptoNative_SslCopyCADistinguishedNames) + DllImportEntry(AppleCryptoNative_SslCopyCertChain) + DllImportEntry(AppleCryptoNative_SslIsHostnameMatch) + DllImportEntry(AppleCryptoNative_SslRead) + DllImportEntry(AppleCryptoNative_SslSetBreakOnClientAuth) + DllImportEntry(AppleCryptoNative_SslSetBreakOnServerAuth) + DllImportEntry(AppleCryptoNative_SslSetIoCallbacks) + DllImportEntry(AppleCryptoNative_SslWrite) DllImportEntry(AppleCryptoNative_RsaGenerateKey) DllImportEntry(AppleCryptoNative_RsaDecryptOaep) DllImportEntry(AppleCryptoNative_RsaDecryptPkcs) @@ -87,7 +99,10 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_StoreEnumerateMachineRoot) DllImportEntry(AppleCryptoNative_StoreEnumerateUserDisallowed) DllImportEntry(AppleCryptoNative_StoreEnumerateMachineDisallowed) + DllImportEntry(AppleCryptoNative_X509ChainCreate) + DllImportEntry(AppleCryptoNative_X509DemuxAndRetainHandle) DllImportEntry(AppleCryptoNative_X509GetContentType) + DllImportEntry(AppleCryptoNative_X509GetPublicKey) DllImportEntry(AppleCryptoNative_X509CopyCertFromIdentity) DllImportEntry(AppleCryptoNative_X509CopyPrivateKeyFromIdentity) DllImportEntry(AppleCryptoNative_X509ImportCollection) @@ -105,6 +120,8 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_X509ChainGetStatusAtIndex) DllImportEntry(AppleCryptoNative_GetOSStatusForChainStatus) DllImportEntry(AppleCryptoNative_X509ChainSetTrustAnchorCertificates) + DllImportEntry(AppleCryptoNative_X509StoreAddCertificate) + DllImportEntry(AppleCryptoNative_X509StoreRemoveCertificate) DllImportEntry(AppleCryptoNative_Pbkdf2) DllImportEntry(AppleCryptoNative_X509GetSubjectSummary) }; From 899764bccb67a70ff1a6c1a19c0460f988e22ed8 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Sun, 27 Jun 2021 18:24:35 -0700 Subject: [PATCH 146/926] Use Empty instead of null for DCS deserializing namespace. (#54670) --- .../Serialization/XmlObjectSerializer.cs | 2 +- .../tests/DataContractSerializer.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 011d03454e7..97e134fe4e3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -355,7 +355,7 @@ namespace System.Runtime.Serialization reader.MoveToElement(); if (name != null) // root name set explicitly { - return reader.IsStartElement(name, ns!); // https://github.com/dotnet/runtime/issues/41395 + return reader.IsStartElement(name, ns ?? XmlDictionaryString.Empty); } else { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index a576fcf1c87..2a39358aa09 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -1393,6 +1393,30 @@ public static partial class DataContractSerializerTests Assert.Equal("Hello", result.Str); } + [Fact] + public static void DCS_RootNameAndNamespaceThroughSettings() + { + var xmlDictionary = new XmlDictionary(); + var obj = new MyOtherType() { Str = "Hello" }; + var settings = new DataContractSerializerSettings() { RootName = xmlDictionary.Add("ChangedRoot"), RootNamespace = xmlDictionary.Add("http://changedNamespace") }; + Func serializerFactory = () => new DataContractSerializer(typeof(MyOtherType), settings); + string baselineXml = @"Hello"; + var result = DataContractSerializerHelper.SerializeAndDeserialize(obj, baselineXml, serializerFactory: serializerFactory); + Assert.Equal("Hello", result.Str); + } + + [Fact] + public static void DCS_RootNameWithoutNamespaceThroughSettings() + { + var xmlDictionary = new XmlDictionary(); + var obj = new MyOtherType() { Str = "Hello" }; + var settings = new DataContractSerializerSettings() { RootName = xmlDictionary.Add("ChangedRoot") }; + Func serializerFactory = () => new DataContractSerializer(typeof(MyOtherType), settings); + string baselineXml = @"Hello"; + var result = DataContractSerializerHelper.SerializeAndDeserialize(obj, baselineXml, serializerFactory: serializerFactory); + Assert.Equal("Hello", result.Str); + } + [Fact] public static void DCS_KnownTypesThroughConstructor() { From e9286801d6edf5fe45bf102f174667c6aae1997a Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 28 Jun 2021 09:39:32 +0300 Subject: [PATCH 147/926] [mono] Intrinsify inequality comparision between runtime types (#54602) This is needed because, when inlining op_Inequality, the information on whether the arguments are runtime types gets lost. We should track that information instead when inlining, but it is a more invasive change. --- src/mono/System.Private.CoreLib/src/System/Type.Mono.cs | 1 + src/mono/mono/mini/interp/transform.c | 3 +++ src/mono/mono/mini/intrinsics.c | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs index 41791273e42..bd6f2f792af 100644 --- a/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs @@ -152,6 +152,7 @@ namespace System return left.Equals(right); } + [Intrinsic] public static bool operator !=(Type? left, Type? right) { return !(left == right); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0c4776ac0be..252ff88c451 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2429,6 +2429,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // We do a reference comparison only if we know both operands are runtime type // (they originate from object.GetType or ldftn + GetTypeFromHandle) *op = MINT_CEQ_P; + } else if (in_corlib && target_method->klass == mono_defaults.systemtype_class && !strcmp (target_method->name, "op_Inequality") && + td->sp [-1].klass == mono_defaults.runtimetype_class && td->sp [-2].klass == mono_defaults.runtimetype_class) { + *op = MINT_CNE_P; } else if (in_corlib && target_method->klass == mono_defaults.object_class) { if (!strcmp (tm, "InternalGetHashCode")) { *op = MINT_INTRINS_GET_HASHCODE; diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 5ad1eb487a1..20d6f8b5a28 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -1813,6 +1813,14 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); return ins; + } else if (cmethod->klass == mono_defaults.systemtype_class && !strcmp (cmethod->name, "op_Inequality") && + args [0]->klass == mono_defaults.runtimetype_class && args [1]->klass == mono_defaults.runtimetype_class) { + EMIT_NEW_BIALU (cfg, ins, OP_COMPARE, -1, args [0]->dreg, args [1]->dreg); + MONO_INST_NEW (cfg, ins, OP_ICNEQ); + ins->dreg = alloc_preg (cfg); + ins->type = STACK_I4; + MONO_ADD_INS (cfg->cbb, ins); + return ins; } else if (((!strcmp (cmethod_klass_image->assembly->aname.name, "MonoMac") || !strcmp (cmethod_klass_image->assembly->aname.name, "monotouch")) && !strcmp (cmethod_klass_name_space, "XamCore.ObjCRuntime") && From 91599fff26619cb8bccb7b39c39590e7cd9a4379 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 28 Jun 2021 04:31:45 -0400 Subject: [PATCH 148/926] Avoid per-XmlSchemaValidator PositionInfo allocation (#54356) It can easily use a static singleton, as the instance is immutable and exists purely to provide a "I don't have line info" / (0,0) value if anyone queries for position. --- .../src/System/Xml/Schema/XmlSchemaValidator.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs index 2ffdd1d6fb0..0ca348043a0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs @@ -113,7 +113,7 @@ namespace System.Xml.Schema private object _validationEventSender; private readonly XmlNameTable _nameTable; private IXmlLineInfo _positionInfo; - private IXmlLineInfo _dummyPositionInfo; + private static readonly IXmlLineInfo s_dummyPositionInfo = new PositionInfo(); private XmlResolver? _xmlResolver; private Uri? _sourceUri; @@ -198,7 +198,6 @@ namespace System.Xml.Schema [MemberNotNull(nameof(_validationStack))] [MemberNotNull(nameof(_attPresence))] [MemberNotNull(nameof(_positionInfo))] - [MemberNotNull(nameof(_dummyPositionInfo))] [MemberNotNull(nameof(_validationEventSender))] [MemberNotNull(nameof(_currentState))] [MemberNotNull(nameof(_textValue))] @@ -219,8 +218,7 @@ namespace System.Xml.Schema _attPresence = new Hashtable(); Push(XmlQualifiedName.Empty); - _dummyPositionInfo = new PositionInfo(); //Dummy position info, will return (0,0) if user does not set the LineInfoProvider property - _positionInfo = _dummyPositionInfo; + _positionInfo = s_dummyPositionInfo; //Dummy position info, will return (0,0) if user does not set the LineInfoProvider property _validationEventSender = this; _currentState = ValidatorState.None; _textValue = new StringBuilder(100); @@ -282,7 +280,7 @@ namespace System.Xml.Schema { if (value == null) { //If value is null, retain the default dummy line info - _positionInfo = _dummyPositionInfo; + _positionInfo = s_dummyPositionInfo; } else { From 084392d5d642dcb42edfd8f168042ad3695d5d3f Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Mon, 28 Jun 2021 09:44:05 -0300 Subject: [PATCH 149/926] [mono][debugger] Insert and search seq_point in the same MonoJitMemoryManager (#54757) * [mono][debugger] Fixing side effect of #48483 When starting XAML Hot Reload, in transform.c the seq_point was saved using jit_mm_for_method: https://github.com/thaystg/runtime/blob/abccfadbf570033efee8ac9a6992f6f7ee51f474/src/mono/mono/mini/interp/transform.c#L9760 When trying to search was trying on get_default_jit_mm. * Fixing everywhere that uses seq_points. * Not used. * Revert "Not used." This reverts commit fd7f2061b40436300abdcc919705b56af53f4ca0. * Revert "Fixing everywhere that uses seq_points." This reverts commit 6faffb8c92a1c9be17d8c53d5f735075b9212220. * Revert "[mono][debugger] Fixing side effect of #48483" This reverts commit 44601d310bd5ca1929604af5e0b96436b416ced8. * Always use get_default_jit_mm for seq_points. * Adding a test case and fixing wasm debugging on ALC. * Moving implementation to avoid changing line number * Update src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs Co-authored-by: Larry Ewing * Update src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs Co-authored-by: Larry Ewing Co-authored-by: Larry Ewing --- src/mono/mono/mini/debugger-agent.c | 20 ++++++++++--- src/mono/mono/mini/interp/transform.c | 2 +- src/mono/mono/mini/mini-runtime.c | 6 ++++ .../DebuggerTestSuite/DebuggerTestBase.cs | 30 +++++++++++++++++++ .../wasm/debugger/DebuggerTestSuite/Tests.cs | 19 +++++++++++- .../tests/debugger-test/debugger-test.cs | 22 ++++++++++++++ 6 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/debugger-agent.c b/src/mono/mono/mini/debugger-agent.c index b1efc0d246c..f61ffce5d22 100644 --- a/src/mono/mono/mini/debugger-agent.c +++ b/src/mono/mono/mini/debugger-agent.c @@ -6986,12 +6986,24 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) mono_assembly_request_prepare_byname (&byname_req, MONO_ASMCTX_DEFAULT, mono_alc_get_default ()); MonoAssembly *assembly = mono_assembly_request_byname (aname, &byname_req, &status); g_free (lookup_name); - mono_assembly_name_free_internal (aname); if (!assembly) { - PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); - buffer_add_int(buf, -1); - break; + GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); + for (int i = 0; i < assemblies->len; ++i) { + MonoAssembly *assemblyOnALC = (MonoAssembly*)g_ptr_array_index (assemblies, i); + if (!strcmp(assemblyOnALC->aname.name, aname->name)) { + assembly = assemblyOnALC; + break; + } + } + g_ptr_array_free (assemblies, TRUE); + if (!assembly) { + PRINT_DEBUG_MSG (1, "Could not resolve assembly %s\n", assembly_name); + buffer_add_int(buf, -1); + mono_assembly_name_free_internal (aname); + break; + } } + mono_assembly_name_free_internal (aname); buffer_add_assemblyid (buf, mono_get_root_domain (), assembly); break; } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 252ff88c451..4a17c9410b7 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9773,7 +9773,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon mono_runtime_print_stats (); } - MonoJitMemoryManager *jit_mm = jit_mm_for_method (imethod->method); + MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); if (!g_hash_table_lookup (jit_mm->seq_points, imethod->method)) g_hash_table_insert (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 09944b33a72..ce8930ff6c8 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -2738,6 +2738,12 @@ mono_jit_free_method (MonoMethod *method) mono_debug_remove_method (method, NULL); mono_lldb_remove_method (method, ji); + + //seq_points are always on get_default_jit_mm + jit_mm = get_default_jit_mm (); + jit_mm_lock (jit_mm); + g_hash_table_remove (jit_mm->seq_points, method); + jit_mm_unlock (jit_mm); jit_mm = jit_mm_for_method (method); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 3d9106959c8..a6af28d8e45 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -1010,6 +1010,36 @@ namespace DebuggerTests Assert.True(load_assemblies_res.IsOk); } + internal async Task LoadAssemblyDynamicallyALCAndRunMethod(string asm_file, string pdb_file, string type_name, string method_name) + { + // Simulate loading an assembly into the framework + byte[] bytes = File.ReadAllBytes(asm_file); + string asm_base64 = Convert.ToBase64String(bytes); + + string pdb_base64 = null; + if (pdb_file != null) + { + bytes = File.ReadAllBytes(pdb_file); + pdb_base64 = Convert.ToBase64String(bytes); + } + + var load_assemblies = JObject.FromObject(new + { + expression = $"{{ let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}'; invoke_static_method('[debugger-test] LoadDebuggerTestALC:LoadLazyAssemblyInALC', asm_b64, pdb_b64); }}" + }); + + Result load_assemblies_res = await cli.SendCommand("Runtime.evaluate", load_assemblies, token); + + Assert.True(load_assemblies_res.IsOk); + Thread.Sleep(1000); + var run_method = JObject.FromObject(new + { + expression = "window.setTimeout(function() { invoke_static_method('[debugger-test] LoadDebuggerTestALC:RunMethodInALC', '" + type_name + "', '" + method_name + "'); }, 1);" + }); + + await cli.SendCommand("Runtime.evaluate", run_method, token); + return await insp.WaitFor(Inspector.PAUSE); + } } class DotnetObjectId diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 468ca4d628d..35bc79fcb0a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -742,7 +742,7 @@ namespace DebuggerTests var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; Assert.Contains(source_location, scripts.Values); - + System.Threading.Thread.Sleep(1000); var pause_location = await EvaluateAndCheck( "window.setTimeout(function () { invoke_static_method('[lazy-debugger-test] LazyMath:IntAdd', 5, 10); }, 1);", source_location, line, 8, @@ -773,6 +773,23 @@ namespace DebuggerTests CheckNumber(locals, "b", 10); } + [Fact] + public async Task DebugLazyLoadedAssemblyWithEmbeddedPdbALC() + { + int line = 9; + await SetBreakpoint(".*/lazy-debugger-test-embedded.cs$", line, 0, use_regex: true); + var pause_location = await LoadAssemblyDynamicallyALCAndRunMethod( + Path.Combine(DebuggerTestAppPath, "lazy-debugger-test-embedded.dll"), + null, "LazyMath", "IntAdd"); + + var source_location = "dotnet://lazy-debugger-test-embedded.dll/lazy-debugger-test-embedded.cs"; + Assert.Contains(source_location, scripts.Values); + + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 5); + CheckNumber(locals, "b", 10); + } + [Fact] public async Task CannotDebugLazyLoadedAssemblyWithoutPdb() { diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 4cee8f374fd..32177bbc0c5 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -556,3 +556,25 @@ public class HiddenSequencePointTest { } #line default } + +public class LoadDebuggerTestALC { + static System.Reflection.Assembly loadedAssembly; + public static void LoadLazyAssemblyInALC(string asm_base64, string pdb_base64) + { + var context = new System.Runtime.Loader.AssemblyLoadContext("testContext", true); + byte[] asm_bytes = Convert.FromBase64String(asm_base64); + byte[] pdb_bytes = null; + if (pdb_base64 != null) + pdb_bytes = Convert.FromBase64String(pdb_base64); + + loadedAssembly = context.LoadFromStream(new System.IO.MemoryStream(asm_bytes), new System.IO.MemoryStream(pdb_bytes)); + Console.WriteLine($"Loaded - {loadedAssembly}"); + } + public static void RunMethodInALC(string type_name, string method_name) + { + var myType = loadedAssembly.GetType(type_name); + var myMethod = myType.GetMethod(method_name); + myMethod.Invoke(null, new object[] { 5, 10 }); + } +} + From 78be35936a48414df83460dadf9003e053499f84 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 28 Jun 2021 14:57:30 +0200 Subject: [PATCH 150/926] Fix EnumMemberRefs always returning NULL (#54805) --- src/coreclr/md/compiler/import.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/md/compiler/import.cpp b/src/coreclr/md/compiler/import.cpp index 6559b2b31e9..ac5472c1e4c 100644 --- a/src/coreclr/md/compiler/import.cpp +++ b/src/coreclr/md/compiler/import.cpp @@ -762,7 +762,7 @@ STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; - *ppmdEnum = 0; + pEnum = NULL; } // fill the output token buffer From 6fdb82aea93465ee046c7f903a96d5c2027a3ecd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 28 Jun 2021 10:42:53 -0400 Subject: [PATCH 151/926] Reduce overhead of Enumerable.Chunk (#54782) Avoid passing the array by ref and yielding inside the loop, which defeat various optimizations (e.g. bounds checking elimination). --- .../System.Linq/src/System/Linq/Chunk.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Linq/src/System/Linq/Chunk.cs b/src/libraries/System.Linq/src/System/Linq/Chunk.cs index 661edaab569..2a2ddd44961 100644 --- a/src/libraries/System.Linq/src/System/Linq/Chunk.cs +++ b/src/libraries/System.Linq/src/System/Linq/Chunk.cs @@ -55,20 +55,23 @@ namespace System.Linq TSource[] chunk = new TSource[size]; chunk[0] = e.Current; - for (int i = 1; i < size; i++) + int i = 1; + for (; i < chunk.Length && e.MoveNext(); i++) { - if (!e.MoveNext()) - { - Array.Resize(ref chunk, i); - yield return chunk; - yield break; - } - chunk[i] = e.Current; } - yield return chunk; + if (i == chunk.Length) + { + yield return chunk; + } + else + { + Array.Resize(ref chunk, i); + yield return chunk; + yield break; + } } } } -} \ No newline at end of file +} From 985eb81cf38f2055adcf22860d8a39f9034a5987 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 28 Jun 2021 10:46:21 -0400 Subject: [PATCH 152/926] Remove unnecessary char[] allocation from Uri.GetRelativeSerializationString (#54799) --- src/libraries/System.Private.Uri/src/System/UriExt.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index 72c266d0487..a644dfa5666 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -744,11 +744,9 @@ namespace System if (_string.Length == 0) return string.Empty; - char[] dest = new char[_string.Length]; - int position = 0; - dest = UriHelper.UnescapeString(_string, 0, _string.Length, dest, ref position, c_DummyChar, - c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape, null, false); - return new string(dest, 0, position); + var vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]); + UriHelper.UnescapeString(_string, ref vsb, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape, null, false); + return vsb.ToString(); } else { From bde0322a9e11fba40a7a65c6d38200d6514efc72 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Mon, 28 Jun 2021 10:50:46 -0400 Subject: [PATCH 153/926] Move iOS/tvOS simulator AOT imports in the Mono workload (#54821) The condition on the import group for the iOS and tvOS simulators was not valid since iOS/tvOSSimulator is not a `TargetPlatformIdentifier`. To simplify, the simulator imports were moved under iOS/tvOS. Fixes https://github.com/dotnet/runtime/issues/53427 Co-authored-by: Steve Pfister --- .../WorkloadManifest.targets | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index ca7309a6612..2102eabc36c 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -22,6 +22,7 @@ + @@ -32,15 +33,6 @@ - - - - - - - - - From daeee34715ea012d7a42af19137fd73f03b1f151 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Mon, 28 Jun 2021 18:19:52 +0300 Subject: [PATCH 154/926] [Mono] MSBuild Task housekeeping (#54485) - added TargetFrameworkForNETCoreTasks property similar to NetCoreAppToolCurrent one as NetCoreAppToolCurrent will likely bump more aggressively between new sdks; - renamed TargetFrameworkForNETFramework to TargetFrameworkForNETFrameworkTasks. --- .../AndroidAppBuilder/AndroidAppBuilder.csproj | 2 +- src/tasks/AotCompilerTask/MonoAOTCompiler.csproj | 2 +- src/tasks/AppleAppBuilder/AppleAppBuilder.csproj | 2 +- src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj | 2 +- src/tasks/Directory.Build.props | 3 ++- .../RuntimeConfigParser/RuntimeConfigParser.csproj | 2 +- src/tasks/WasmAppBuilder/WasmAppBuilder.csproj | 14 +++++++------- src/tasks/WasmBuildTasks/WasmBuildTasks.csproj | 2 +- .../WorkloadBuildTasks/WorkloadBuildTasks.csproj | 2 +- src/tasks/installer.tasks/installer.tasks.csproj | 4 ++-- 10 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj index 46df0de8638..de6a04a95f0 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent) + $(TargetFrameworkForNETCoreTasks) Library true false diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj index 79baa5b8b39..338ba17ab82 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent);$(TargetFrameworkForNETFramework) + $(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks) Library true false diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj b/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj index 04da7561d0d..7ceb4480e1c 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent) + $(TargetFrameworkForNETCoreTasks) Library true false diff --git a/src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj b/src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj index 63d7016359c..fcd5b20ab2c 100644 --- a/src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj +++ b/src/tasks/Crossgen2Tasks/Crossgen2Tasks.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent) + $(TargetFrameworkForNETCoreTasks) Library true $(NoWarn),CA1050 diff --git a/src/tasks/Directory.Build.props b/src/tasks/Directory.Build.props index 7ed8a459d2f..695833da3db 100644 --- a/src/tasks/Directory.Build.props +++ b/src/tasks/Directory.Build.props @@ -2,6 +2,7 @@ - net472 + net472 + net6.0 diff --git a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj index 5b491a31fc3..5c9ac48afda 100644 --- a/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj +++ b/src/tasks/RuntimeConfigParser/RuntimeConfigParser.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent);$(TargetFrameworkForNETFramework) + $(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks) Library true false diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index b27176e2774..3c93ab5d643 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent);$(TargetFrameworkForNETFramework) + $(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks) enable $(NoWarn),CA1050 @@ -39,14 +39,14 @@ <_PublishFramework Include="$(TargetFrameworks)" /> - - + + - + diff --git a/src/tasks/WasmBuildTasks/WasmBuildTasks.csproj b/src/tasks/WasmBuildTasks/WasmBuildTasks.csproj index 3dc1369a7fc..0f8b6c93993 100644 --- a/src/tasks/WasmBuildTasks/WasmBuildTasks.csproj +++ b/src/tasks/WasmBuildTasks/WasmBuildTasks.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent) + $(TargetFrameworkForNETCoreTasks) Library enable $(NoWarn),CA1050 diff --git a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj index 112f8f17fb1..328672b4514 100644 --- a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj +++ b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent) + $(TargetFrameworkForNETCoreTasks) Library enable $(NoWarn),CA1050 diff --git a/src/tasks/installer.tasks/installer.tasks.csproj b/src/tasks/installer.tasks/installer.tasks.csproj index 04f5997217e..45fe8463b5b 100644 --- a/src/tasks/installer.tasks/installer.tasks.csproj +++ b/src/tasks/installer.tasks/installer.tasks.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppToolCurrent);net461 + $(TargetFrameworkForNETCoreTasks);net461 false false @@ -10,7 +10,7 @@ - + From 6b5dbf6bdb95aef88262b1eb949f1028c59dc5e2 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 28 Jun 2021 11:55:27 -0400 Subject: [PATCH 155/926] Add one-shot ECB methods This change adds SymmetricAlgorithm.EncryptEcb, SymmetricAlgorithm.DecryptEcb, their respective Try- and -Core methods, derived type implementations thereof, and tests. There's an open question of should these members on on the base class throw or "succeed if the Mode property is in agreement with the algorithm". While the latter is "nicer", just throwing is easier to reason about, and that's the current behavior. Co-authored-by: Jeremy Barton --- .../BasicSymmetricCipherBCrypt.cs | 27 +- .../src/Internal/Cryptography/Helpers.cs | 4 +- .../Cryptography/UniversalCryptoDecryptor.cs | 56 ++ .../Cryptography/UniversalCryptoEncryptor.cs | 27 + .../Cryptography/UniversalCryptoTransform.cs | 4 +- .../AES/AesCipherTests.OneShot.cs | 638 ++++++++++++++++++ .../AES/AesCipherTests.cs | 30 + .../DES/DESCipherTests.OneShot.cs | 624 +++++++++++++++++ .../DES/DESCipherTests.cs | 2 +- .../RC2/RC2CipherTests.OneShot.cs | 627 +++++++++++++++++ .../RC2/RC2CipherTests.cs | 2 +- .../TripleDES/TripleDESCipherTests.OneShot.cs | 633 +++++++++++++++++ .../TripleDES/TripleDESCipherTests.cs | 2 +- .../Cryptography/AesImplementation.OSX.cs | 2 +- .../Cryptography/AesImplementation.Unix.cs | 2 +- .../Cryptography/AesImplementation.Windows.cs | 2 +- .../Cryptography/AesImplementation.cs | 54 +- .../Internal/Cryptography/AppleCCCryptor.cs | 22 +- .../Cryptography/DesImplementation.Android.cs | 2 +- .../Cryptography/DesImplementation.OSX.cs | 2 +- .../Cryptography/DesImplementation.Unix.cs | 2 +- .../Cryptography/DesImplementation.Windows.cs | 2 +- .../Cryptography/DesImplementation.cs | 54 +- .../Cryptography/RC2Implementation.Android.cs | 2 +- .../Cryptography/RC2Implementation.OSX.cs | 2 +- .../Cryptography/RC2Implementation.Unix.cs | 2 +- .../Cryptography/RC2Implementation.Windows.cs | 2 +- .../Cryptography/RC2Implementation.cs | 71 +- .../TripleDesImplementation.OSX.cs | 2 +- .../TripleDesImplementation.Unix.cs | 2 +- .../TripleDesImplementation.Windows.cs | 2 +- .../Cryptography/TripleDesImplementation.cs | 54 +- ...urity.Cryptography.Algorithms.Tests.csproj | 8 + .../BasicSymmetricCipherNCrypt.cs | 2 +- .../Cryptography/CngSymmetricAlgorithmCore.cs | 49 +- .../Cryptography/ICngSymmetricAlgorithm.cs | 4 +- .../System/Security/Cryptography/AesCng.cs | 44 +- .../Security/Cryptography/TripleDESCng.cs | 44 +- .../tests/AesCngTests.cs | 4 + .../tests/SymmetricCngTestHelpers.cs | 27 + ...tem.Security.Cryptography.Cng.Tests.csproj | 4 + .../tests/TripleDESCngTests.cs | 4 + .../DESCryptoServiceProvider.Windows.cs | 12 +- .../tests/ShimHelpers.cs | 3 + ...System.Security.Cryptography.Primitives.cs | 10 + .../src/Resources/Strings.resx | 6 + .../Cryptography/SymmetricAlgorithm.cs | 297 ++++++++ .../tests/SymmetricAlgorithmTests.cs | 162 +++++ 48 files changed, 3580 insertions(+), 59 deletions(-) create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs diff --git a/src/libraries/Common/src/Internal/Cryptography/BasicSymmetricCipherBCrypt.cs b/src/libraries/Common/src/Internal/Cryptography/BasicSymmetricCipherBCrypt.cs index 59d984f5fec..348f1a3a2c9 100644 --- a/src/libraries/Common/src/Internal/Cryptography/BasicSymmetricCipherBCrypt.cs +++ b/src/libraries/Common/src/Internal/Cryptography/BasicSymmetricCipherBCrypt.cs @@ -64,15 +64,27 @@ namespace Internal.Cryptography Debug.Assert(input.Length > 0); Debug.Assert((input.Length % PaddingSizeInBytes) == 0); - int numBytesWritten; + int numBytesWritten = 0; - if (_encrypting) + // BCryptEncrypt and BCryptDecrypt can do in place encryption, but if the buffers overlap + // the offset must be zero. In that case, we need to copy to a temporary location. + if (input.Overlaps(output, out int offset) && offset != 0) { - numBytesWritten = Interop.BCrypt.BCryptEncrypt(_hKey, input, _currentIv, output); + byte[] rented = CryptoPool.Rent(output.Length); + + try + { + numBytesWritten = BCryptTransform(input, rented); + rented.AsSpan(0, numBytesWritten).CopyTo(output); + } + finally + { + CryptoPool.Return(rented, clearSize: numBytesWritten); + } } else { - numBytesWritten = Interop.BCrypt.BCryptDecrypt(_hKey, input, _currentIv, output); + numBytesWritten = BCryptTransform(input, output); } if (numBytesWritten != input.Length) @@ -84,6 +96,13 @@ namespace Internal.Cryptography } return numBytesWritten; + + int BCryptTransform(ReadOnlySpan input, Span output) + { + return _encrypting ? + Interop.BCrypt.BCryptEncrypt(_hKey, input, _currentIv, output) : + Interop.BCrypt.BCryptDecrypt(_hKey, input, _currentIv, output); + } } public override int TransformFinal(ReadOnlySpan input, Span output) diff --git a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs index c2998444ea1..2ee5ffc39c4 100644 --- a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs +++ b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs @@ -30,11 +30,11 @@ namespace Internal.Cryptography return (byte[])(src.Clone()); } - public static int GetPaddingSize(this SymmetricAlgorithm algorithm) + public static int GetPaddingSize(this SymmetricAlgorithm algorithm, CipherMode mode, int feedbackSizeBits) { // CFB8 does not require any padding at all // otherwise, it is always required to pad for block size - if (algorithm.Mode == CipherMode.CFB && algorithm.FeedbackSize == 8) + if (mode == CipherMode.CFB && feedbackSizeBits == 8) return 1; return algorithm.BlockSize / 8; diff --git a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoDecryptor.cs b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoDecryptor.cs index 3f050ff1eda..0578bb3d5d0 100644 --- a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoDecryptor.cs +++ b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoDecryptor.cs @@ -177,6 +177,62 @@ namespace Internal.Cryptography base.Dispose(disposing); } + public override unsafe bool TransformOneShot(ReadOnlySpan input, Span output, out int bytesWritten) + { + if (input.Length % PaddingSizeBytes != 0) + throw new CryptographicException(SR.Cryptography_PartialBlock); + + // If there is no padding that needs to be removed, and the output buffer is large enough to hold + // the resulting plaintext, we can decrypt directly in to the output buffer. + // We do not do this for modes that require padding removal. + // + // This is not done for padded ciphertexts because we don't know if the padding is valid + // until it's been decrypted. We don't want to decrypt in to a user-supplied buffer and then throw + // a padding exception after we've already filled the user buffer with plaintext. We should only + // release the plaintext to the caller once we know the padding is valid. + if (!DepaddingRequired) + { + if (output.Length >= input.Length) + { + bytesWritten = BasicSymmetricCipher.TransformFinal(input, output); + return true; + } + + // If no padding is going to be removed, we know the buffer is too small and we can bail out. + bytesWritten = 0; + return false; + } + + byte[] rentedBuffer = CryptoPool.Rent(input.Length); + Span buffer = rentedBuffer.AsSpan(0, input.Length); + Span decryptedBuffer = default; + + fixed (byte* pBuffer = buffer) + { + try + { + int transformWritten = BasicSymmetricCipher.TransformFinal(input, buffer); + decryptedBuffer = buffer.Slice(0, transformWritten); + int unpaddedLength = GetPaddingLength(decryptedBuffer); // validates padding + + if (unpaddedLength > output.Length) + { + bytesWritten = 0; + return false; + } + + decryptedBuffer.Slice(0, unpaddedLength).CopyTo(output); + bytesWritten = unpaddedLength; + return true; + } + finally + { + CryptographicOperations.ZeroMemory(decryptedBuffer); + CryptoPool.Return(rentedBuffer, clearSize: 0); // ZeroMemory clears the part of the buffer that was written to. + } + } + } + private void Reset() { if (_heldoverCipher != null) diff --git a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoEncryptor.cs b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoEncryptor.cs index af88c7e6c27..b9139789f97 100644 --- a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoEncryptor.cs +++ b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoEncryptor.cs @@ -56,6 +56,33 @@ namespace Internal.Cryptography return buffer; } + public override bool TransformOneShot(ReadOnlySpan input, Span output, out int bytesWritten) + { + int ciphertextLength = GetCiphertextLength(input.Length); + + if (output.Length < ciphertextLength) + { + bytesWritten = 0; + return false; + } + + // Copy the input to the output, and apply padding if required. This will not throw since the + // output length has already been checked, and PadBlock will not copy from input to output + // until it has checked that it will be able to apply padding correctly. + int padWritten = PadBlock(input, output); + + // Do an in-place encrypt. All of our implementations support this, either natively + // or making a temporary buffer themselves if in-place is not supported by the native + // implementation. + Span paddedOutput = output.Slice(0, padWritten); + bytesWritten = BasicSymmetricCipher.TransformFinal(paddedOutput, paddedOutput); + + // After padding, we should have an even number of blocks, and the same applies + // to the transform. + Debug.Assert(padWritten == bytesWritten); + return true; + } + private int GetCiphertextLength(int plaintextLength) { Debug.Assert(plaintextLength >= 0); diff --git a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoTransform.cs b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoTransform.cs index 23966df2969..a3a22165c6c 100644 --- a/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoTransform.cs +++ b/src/libraries/Common/src/Internal/Cryptography/UniversalCryptoTransform.cs @@ -17,7 +17,7 @@ namespace Internal.Cryptography // internal abstract class UniversalCryptoTransform : ICryptoTransform { - public static ICryptoTransform Create( + public static UniversalCryptoTransform Create( PaddingMode paddingMode, BasicSymmetricCipher cipher, bool encrypting) @@ -108,6 +108,8 @@ namespace Internal.Cryptography return output; } + public abstract bool TransformOneShot(ReadOnlySpan input, Span output, out int bytesWritten); + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs new file mode 100644 index 00000000000..fd82160f1a7 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs @@ -0,0 +1,638 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Encryption.Aes.Tests +{ + using Aes = System.Security.Cryptography.Aes; + + public partial class AesCipherTests + { + private static byte[] s_aes128OneShotKey = + new byte[] { 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12 }; + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + // Even though we have set the instance to use CFB, the Ecb one shots should + // always be done in ECB. + aes.FeedbackSize = 8; + aes.Mode = CipherMode.CFB; + aes.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; + + byte[] encrypted = aes.EncryptEcb(plaintext, padding); + byte[] decrypted = aes.DecryptEcb(encrypted, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted[..plaintext.Length]); + AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + + decrypted = aes.DecryptEcb(ciphertext, padding); + encrypted = aes.EncryptEcb(decrypted, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (plaintext.Length == 0) + { + // Can't have a ciphertext length shorter than zero. + return; + } + + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + Span destinationBuffer = new byte[plaintext.Length - 1]; + + bool result = aes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (ciphertext.Length == 0) + { + // Can't have a too small buffer for zero. + return; + } + + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + Span destinationBuffer = new byte[ciphertext.Length - 1]; + + bool result = aes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + Span destinationBuffer = new byte[expectedPlaintextSize]; + + bool result = aes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + int expectedCiphertextSize = aes.GetCiphertextLengthEcb(plaintext.Length, padding); + Span destinationBuffer = new byte[expectedCiphertextSize]; + + bool result = aes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(expectedCiphertextSize, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + // Padding is random so we can't validate the last block. + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + + Span largeBuffer = new byte[expectedPlaintextSize + 10]; + Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); + largeBuffer.Fill(0xCC); + + bool result = aes.TryDecryptEcb( + ciphertext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + + Span excess = largeBuffer.Slice(destinationBuffer.Length); + AssertFilledWith(0xCC, excess); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + Span largeBuffer = new byte[ciphertext.Length + 10]; + Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); + largeBuffer.Fill(0xCC); + + bool result = aes.TryEncryptEcb( + plaintext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + + AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); + Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + ciphertext.AsSpan().CopyTo(ciphertextBuffer); + + bool result = aes.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + + int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); + plaintext.AsSpan().CopyTo(plaintextBuffer); + + bool result = aes.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + byte[] decrypted = aes.DecryptEcb(ciphertext.AsSpan(), padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + byte[] encrypted = aes.EncryptEcb(plaintext.AsSpan(), padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + byte[] decrypted = aes.DecryptEcb(ciphertext, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (Aes aes = AesFactory.Create()) + { + aes.Key = s_aes128OneShotKey; + byte[] encrypted = aes.EncryptEcb(plaintext, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = aes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + private static void AssertFilledWith(byte value, ReadOnlySpan span) + { + for (int i = 0; i < span.Length; i++) + { + Assert.Equal(value, span[i]); + } + } + + public static IEnumerable EcbTestCases + { + get + { + // plaintext requires no padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, + 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + }, + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0xC1, 0xCA, 0x44, 0xE8, 0x05, 0xFF, 0xCB, 0x6F, + 0x4D, 0x7F, 0xE9, 0x17, 0x12, 0xFE, 0xBB, 0xAC, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0xD3, 0xAA, 0x33, 0x5B, 0x93, 0xC2, 0x3D, 0x96, + 0xFD, 0x89, 0xB1, 0x8C, 0x47, 0x75, 0x65, 0xA8, + }, + + PaddingMode.ISO10126, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x82, 0x8D, 0x60, 0xDC, 0x44, 0x26, 0xCF, 0xDE, + 0xC9, 0x54, 0x33, 0x47, 0xE2, 0x9E, 0xF0, 0x8C, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x49, 0x39, 0x1B, 0x69, 0xA1, 0xF3, 0x66, 0xE4, + 0x3E, 0x40, 0x51, 0xB8, 0x05, 0x60, 0xDC, 0xFD, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0xCD, 0x0D, 0xCD, 0xEA, 0xA2, 0x1F, 0xC1, 0xC3, + 0x81, 0xEE, 0x8A, 0x63, 0x94, 0x5F, 0x85, 0x43, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x9C, 0xE4, 0x0D, 0x2F, 0xCD, 0x82, 0x25, 0x0E, + 0x13, 0xAB, 0x4B, 0x6B, 0xC0, 0x9A, 0x21, 0x2E, + }, + + PaddingMode.ISO10126, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, + 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, + }, + + PaddingMode.PKCS7, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs index e1f5da5f40f..2d7e23749d4 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs @@ -1066,6 +1066,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests int? feedbackSize = default) { byte[] decryptedBytes; + byte[] oneShotDecryptedBytes = null; using (Aes aes = AesFactory.Create()) { @@ -1089,10 +1090,20 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests cryptoStream.CopyTo(output); decryptedBytes = output.ToArray(); } + + if (mode == CipherMode.ECB) + { + oneShotDecryptedBytes = aes.DecryptEcb(encryptedBytes, aes.Padding); + } } Assert.NotEqual(encryptedBytes, decryptedBytes); Assert.Equal(expectedAnswer, decryptedBytes); + + if (oneShotDecryptedBytes is not null) + { + Assert.Equal(expectedAnswer, oneShotDecryptedBytes); + } } private static void TestAesTransformDirectKey( @@ -1106,6 +1117,8 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { byte[] liveEncryptBytes; byte[] liveDecryptBytes; + byte[] liveOneShotDecryptBytes = null; + byte[] liveOneShotEncryptBytes = null; using (Aes aes = AesFactory.Create()) { @@ -1119,10 +1132,27 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests liveEncryptBytes = AesEncryptDirectKey(aes, key, iv, plainBytes); liveDecryptBytes = AesDecryptDirectKey(aes, key, iv, cipherBytes); + + if (cipherMode == CipherMode.ECB) + { + aes.Key = key; + liveOneShotDecryptBytes = aes.DecryptEcb(cipherBytes, paddingMode); + liveOneShotEncryptBytes = aes.EncryptEcb(plainBytes, paddingMode); + } } Assert.Equal(cipherBytes, liveEncryptBytes); Assert.Equal(plainBytes, liveDecryptBytes); + + if (liveOneShotDecryptBytes is not null) + { + Assert.Equal(plainBytes, liveOneShotDecryptBytes); + } + + if (liveOneShotEncryptBytes is not null) + { + Assert.Equal(cipherBytes, liveOneShotEncryptBytes); + } } private static byte[] AesEncryptDirectKey(Aes aes, byte[] key, byte[] iv, byte[] plainBytes) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs new file mode 100644 index 00000000000..6f5106816bc --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs @@ -0,0 +1,624 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Encryption.Des.Tests +{ + public partial class DesCipherTests + { + private static byte[] s_desOneShotKey = new byte[] + { + 0x74, 0x4B, 0x93, 0x3A, 0x96, 0x33, 0x61, 0xD6 + }; + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + byte[] encrypted = des.EncryptEcb(plaintext, padding); + byte[] decrypted = des.DecryptEcb(encrypted, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted[..plaintext.Length]); + AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + + decrypted = des.DecryptEcb(ciphertext, padding); + encrypted = des.EncryptEcb(decrypted, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (plaintext.Length == 0) + { + // Can't have a ciphertext length shorter than zero. + return; + } + + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + Span destinationBuffer = new byte[plaintext.Length - 1]; + + bool result = des.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (ciphertext.Length == 0) + { + // Can't have a too small buffer for zero. + return; + } + + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + Span destinationBuffer = new byte[ciphertext.Length - 1]; + + bool result = des.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + Span destinationBuffer = new byte[expectedPlaintextSize]; + + bool result = des.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + int expectedCiphertextSize = des.GetCiphertextLengthEcb(plaintext.Length, padding); + Span destinationBuffer = new byte[expectedCiphertextSize]; + + bool result = des.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(expectedCiphertextSize, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + // Padding is random so we can't validate the last block. + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + + Span largeBuffer = new byte[expectedPlaintextSize + 10]; + Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); + largeBuffer.Fill(0xCC); + + bool result = des.TryDecryptEcb( + ciphertext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + + Span excess = largeBuffer.Slice(destinationBuffer.Length); + AssertFilledWith(0xCC, excess); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + Span largeBuffer = new byte[ciphertext.Length + 10]; + Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); + largeBuffer.Fill(0xCC); + + bool result = des.TryEncryptEcb( + plaintext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + + AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); + Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + ciphertext.AsSpan().CopyTo(ciphertextBuffer); + + bool result = des.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + + int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); + plaintext.AsSpan().CopyTo(plaintextBuffer); + + bool result = des.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + byte[] decrypted = des.DecryptEcb(ciphertext.AsSpan(), padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + byte[] encrypted = des.EncryptEcb(plaintext.AsSpan(), padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + byte[] decrypted = des.DecryptEcb(ciphertext, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (DES des = DESFactory.Create()) + { + des.Key = s_desOneShotKey; + byte[] encrypted = des.EncryptEcb(plaintext, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = des.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + private static void AssertFilledWith(byte value, ReadOnlySpan span) + { + for (int i = 0; i < span.Length; i++) + { + Assert.Equal(value, span[i]); + } + } + + public static IEnumerable EcbTestCases + { + get + { + // plaintext requires no padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + }, + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0xEC, 0x52, 0xA1, 0x7E, 0x52, 0x54, 0x6E, 0x9E, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0x44, 0x4C, 0xA5, 0xC2, 0xCC, 0x54, 0xAC, 0xF9, + }, + + PaddingMode.ISO10126, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0x60, 0x8E, 0xC3, 0xB8, 0x09, 0x84, 0xCF, 0x3B, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0xE7, 0xA4, 0x10, 0xF1, 0x7B, 0xFF, 0x32, 0x4A, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0x92, 0x9A, 0x36, 0xFE, 0xA4, 0xB3, 0xEC, 0xA0, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0xDB, 0x86, 0xA4, 0xAB, 0xDE, 0x05, 0xE4, 0xE7, + }, + + PaddingMode.ISO10126, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, + }, + + PaddingMode.PKCS7, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs index d007a41167e..dae50482fc0 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.Security.Cryptography.Encryption.Des.Tests { [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] - public static class DesCipherTests + public static partial class DesCipherTests { // These are the expected output of many decryptions. Changing these values requires re-generating test input. private static readonly string s_multiBlockString = new ASCIIEncoding().GetBytes( diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs new file mode 100644 index 00000000000..4a22728224d --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs @@ -0,0 +1,627 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Encryption.RC2.Tests +{ + using RC2 = System.Security.Cryptography.RC2; + + public partial class RC2CipherTests + { + private static byte[] s_rc2OneShotKey = new byte[] + { + 0x83, 0x2F, 0x81, 0x1B, 0x61, 0x02, 0xCC, 0x8F, + 0x2F, 0x78, 0x10, 0x68, 0x06, 0xA6, 0x35, 0x50, + }; + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + byte[] encrypted = rc2.EncryptEcb(plaintext, padding); + byte[] decrypted = rc2.DecryptEcb(encrypted, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted[..plaintext.Length]); + AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + + decrypted = rc2.DecryptEcb(ciphertext, padding); + encrypted = rc2.EncryptEcb(decrypted, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (plaintext.Length == 0) + { + // Can't have a ciphertext length shorter than zero. + return; + } + + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + Span destinationBuffer = new byte[plaintext.Length - 1]; + + bool result = rc2.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (ciphertext.Length == 0) + { + // Can't have a too small buffer for zero. + return; + } + + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + Span destinationBuffer = new byte[ciphertext.Length - 1]; + + bool result = rc2.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + Span destinationBuffer = new byte[expectedPlaintextSize]; + + bool result = rc2.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + int expectedCiphertextSize = rc2.GetCiphertextLengthEcb(plaintext.Length, padding); + Span destinationBuffer = new byte[expectedCiphertextSize]; + + bool result = rc2.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(expectedCiphertextSize, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + // Padding is random so we can't validate the last block. + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + + Span largeBuffer = new byte[expectedPlaintextSize + 10]; + Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); + largeBuffer.Fill(0xCC); + + bool result = rc2.TryDecryptEcb( + ciphertext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + + Span excess = largeBuffer.Slice(destinationBuffer.Length); + AssertFilledWith(0xCC, excess); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + Span largeBuffer = new byte[ciphertext.Length + 10]; + Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); + largeBuffer.Fill(0xCC); + + bool result = rc2.TryEncryptEcb( + plaintext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + + AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); + Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + ciphertext.AsSpan().CopyTo(ciphertextBuffer); + + bool result = rc2.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + + int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); + plaintext.AsSpan().CopyTo(plaintextBuffer); + + bool result = rc2.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + byte[] decrypted = rc2.DecryptEcb(ciphertext.AsSpan(), padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + byte[] encrypted = rc2.EncryptEcb(plaintext.AsSpan(), padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + byte[] decrypted = rc2.DecryptEcb(ciphertext, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (RC2 rc2 = RC2Factory.Create()) + { + rc2.Key = s_rc2OneShotKey; + byte[] encrypted = rc2.EncryptEcb(plaintext, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = rc2.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + private static void AssertFilledWith(byte value, ReadOnlySpan span) + { + for (int i = 0; i < span.Length; i++) + { + Assert.Equal(value, span[i]); + } + } + + public static IEnumerable EcbTestCases + { + get + { + // plaintext that is block aligned + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + }, + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0x72, 0x8A, 0x57, 0x94, 0x2D, 0x79, 0xBD, 0xAA, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0xEB, 0x5E, 0x2E, 0xB9, 0x1A, 0x1E, 0x1B, 0xE4, + }, + + PaddingMode.ISO10126, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0xF2, 0x94, 0x11, 0xA3, 0xE8, 0xAD, 0xA7, 0xE6, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0xE3, 0xB2, 0x3D, 0xAA, 0x91, 0x6A, 0xD0, 0x06, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0x17, 0x97, 0x3A, 0x77, 0x69, 0x5E, 0x79, 0xE9, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0x22, 0xC0, 0x50, 0x52, 0x56, 0x5A, 0x15, 0xFD, + }, + + PaddingMode.ISO10126, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, + }, + + PaddingMode.PKCS7, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs index 7823cc8b1c5..7b065d22b47 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs @@ -12,7 +12,7 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] [ConditionalClass(typeof(RC2Factory), nameof(RC2Factory.IsSupported))] - public static class RC2CipherTests + public static partial class RC2CipherTests { // These are the expected output of many decryptions. Changing these values requires re-generating test input. private static readonly string s_multiBlockString = new ASCIIEncoding().GetBytes( diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs new file mode 100644 index 00000000000..3469f4c6fec --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs @@ -0,0 +1,633 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Encryption.TripleDes.Tests +{ + public partial class TripleDESCipherTests + { + private static byte[] s_tdes192OneShotKey = new byte[] + { + 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xA0, + }; + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + // Even though we have set the instance to use CFB, the Ecb one shots should + // always be done in ECB. + tdes.FeedbackSize = 8; + tdes.Mode = CipherMode.CFB; + tdes.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; + + byte[] encrypted = tdes.EncryptEcb(plaintext, padding); + byte[] decrypted = tdes.DecryptEcb(encrypted, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted[..plaintext.Length]); + AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + + decrypted = tdes.DecryptEcb(ciphertext, padding); + encrypted = tdes.EncryptEcb(decrypted, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (plaintext.Length == 0) + { + // Can't have a ciphertext length shorter than zero. + return; + } + + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + Span destinationBuffer = new byte[plaintext.Length - 1]; + + bool result = tdes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + if (ciphertext.Length == 0) + { + // Can't have a too small buffer for zero. + return; + } + + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + Span destinationBuffer = new byte[ciphertext.Length - 1]; + + bool result = tdes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.False(result, "TryDecryptEcb"); + Assert.Equal(0, bytesWritten); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + Span destinationBuffer = new byte[expectedPlaintextSize]; + + bool result = tdes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + int expectedCiphertextSize = tdes.GetCiphertextLengthEcb(plaintext.Length, padding); + Span destinationBuffer = new byte[expectedCiphertextSize]; + + bool result = tdes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(expectedCiphertextSize, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + // Padding is random so we can't validate the last block. + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + + Span largeBuffer = new byte[expectedPlaintextSize + 10]; + Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); + largeBuffer.Fill(0xCC); + + bool result = tdes.TryDecryptEcb( + ciphertext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + } + + Span excess = largeBuffer.Slice(destinationBuffer.Length); + AssertFilledWith(0xCC, excess); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + Span largeBuffer = new byte[ciphertext.Length + 10]; + Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); + largeBuffer.Fill(0xCC); + + bool result = tdes.TryEncryptEcb( + plaintext, + destinationBuffer, + padding, + out int bytesWritten); + + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + } + + AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); + Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + ciphertext.AsSpan().CopyTo(ciphertextBuffer); + + bool result = tdes.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryDecryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); + AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + + int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); + plaintext.AsSpan().CopyTo(plaintextBuffer); + + bool result = tdes.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); + Assert.True(result, "TryEncryptEcb"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); + } + else + { + Assert.Equal(ciphertext, destinationBuffer.ToArray()); + Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + byte[] decrypted = tdes.DecryptEcb(ciphertext.AsSpan(), padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + byte[] encrypted = tdes.EncryptEcb(plaintext.AsSpan(), padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + byte[] decrypted = tdes.DecryptEcb(ciphertext, padding); + + if (padding == PaddingMode.Zeros) + { + Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); + AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); + } + else + { + Assert.Equal(plaintext, decrypted); + } + } + } + + [Theory] + [MemberData(nameof(EcbTestCases))] + public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + tdes.Key = s_tdes192OneShotKey; + byte[] encrypted = tdes.EncryptEcb(plaintext, padding); + + if (padding == PaddingMode.ISO10126) + { + int blockSizeBytes = tdes.BlockSize / 8; + Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); + } + else + { + Assert.Equal(ciphertext, encrypted); + } + } + } + + private static void AssertFilledWith(byte value, ReadOnlySpan span) + { + for (int i = 0; i < span.Length; i++) + { + Assert.Equal(value, span[i]); + } + } + + public static IEnumerable EcbTestCases + { + get + { + // plaintext requires no padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, + }, + + PaddingMode.PKCS7, + }; + + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + }, + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x34, 0xE6, 0x86, 0x6D, 0x94, 0x2E, 0x98, 0x0F, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x5E, 0xEE, 0x73, 0xBB, 0x94, 0xED, 0x29, 0x7A, + }, + + PaddingMode.ISO10126, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0xB1, 0x3D, 0x05, 0x93, 0x98, 0xE6, 0x2C, 0xDF, + }, + + PaddingMode.PKCS7, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0xC9, 0x52, 0x8F, 0xC1, 0x30, 0xC0, 0x7C, 0x63, + }, + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0x6A, 0x97, 0x38, 0x85, 0x3B, 0x48, 0x81, 0x5E, + }, + + PaddingMode.ANSIX923, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0x33, 0x58, 0x09, 0x2C, 0xD8, 0xB5, 0x36, 0xAD, + }, + + PaddingMode.ISO10126, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, + }, + + PaddingMode.PKCS7, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs index ce92a62f029..9bc2b80f14c 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs @@ -9,7 +9,7 @@ using Xunit; namespace System.Security.Cryptography.Encryption.TripleDes.Tests { [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] - public static class TripleDESCipherTests + public static partial class TripleDESCipherTests { [Fact] public static void TripleDESDefaults() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs index 1c7fbb1ea71..94392093387 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { internal sealed partial class AesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs index 378b4cf0c11..1fd8e00cd3d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { internal sealed partial class AesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs index 953bcde1cc1..9bb0a9efc79 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { internal sealed partial class AesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs index 9041682804d..f00ee5582d3 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs @@ -43,6 +43,50 @@ namespace Internal.Cryptography base.Dispose(disposing); } + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + private ICryptoTransform CreateTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting) { // note: rbgIV is guaranteed to be cloned before this method, so no need to clone it again @@ -66,7 +110,15 @@ namespace Internal.Cryptography ValidateCFBFeedbackSize(FeedbackSize); } - return CreateTransformCore(Mode, Padding, rgbKey, rgbIV, BlockSize / BitsPerByte, this.GetPaddingSize(), FeedbackSize / BitsPerByte, encrypting); + return CreateTransformCore( + Mode, + Padding, + rgbKey, + rgbIV, + BlockSize / BitsPerByte, + this.GetPaddingSize(Mode, FeedbackSize), + FeedbackSize / BitsPerByte, + encrypting); } private static void ValidateCFBFeedbackSize(int feedback) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AppleCCCryptor.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AppleCCCryptor.cs index 0400c5c5c1c..9c406f8f107 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AppleCCCryptor.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AppleCCCryptor.cs @@ -70,7 +70,27 @@ namespace Internal.Cryptography Debug.Assert((input.Length % PaddingSizeInBytes) == 0); Debug.Assert(input.Length <= output.Length); - int written = ProcessFinalBlock(input, output); + int written = 0; + + if (input.Overlaps(output, out int offset) && offset != 0) + { + byte[] rented = CryptoPool.Rent(output.Length); + + try + { + written = ProcessFinalBlock(input, rented); + rented.AsSpan(0, written).CopyTo(output); + } + finally + { + CryptoPool.Return(rented, clearSize: written); + } + } + else + { + written = ProcessFinalBlock(input, output); + } + Reset(); return written; } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Android.cs index 7e8ebb82a22..759665214ee 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Android.cs @@ -9,7 +9,7 @@ namespace Internal.Cryptography { internal sealed partial class DesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs index 72714b4cf3e..015c3a023d4 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { internal sealed partial class DesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs index f9f21194f7c..d4f7d6d5858 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs @@ -9,7 +9,7 @@ namespace Internal.Cryptography { internal sealed partial class DesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs index dba5858591c..cdc8e28ac12 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { internal sealed partial class DesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs index 9c1bf00bbf4..34f0fc6367f 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs @@ -76,7 +76,59 @@ namespace Internal.Cryptography ValidateCFBFeedbackSize(FeedbackSize); } - return CreateTransformCore(Mode, Padding, rgbKey, rgbIV, BlockSize / BitsPerByte, FeedbackSize / BitsPerByte, this.GetPaddingSize(), encrypting); + return CreateTransformCore( + Mode, + Padding, + rgbKey, + rgbIV, + BlockSize / BitsPerByte, + FeedbackSize / BitsPerByte, + this.GetPaddingSize(Mode, FeedbackSize), + encrypting); + } + + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } } private static void ValidateCFBFeedbackSize(int feedback) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Android.cs index fa5604a7126..39448908dc2 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Android.cs @@ -9,7 +9,7 @@ namespace Internal.Cryptography [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "We are providing the implementation for RC2, not consuming it.")] internal sealed partial class RC2Implementation : RC2 { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs index 1473dc7ca3b..af2340f60fb 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { internal sealed partial class RC2Implementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs index 4f85d080c64..d33174d8f89 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs @@ -9,7 +9,7 @@ namespace Internal.Cryptography { internal sealed partial class RC2Implementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs index 5c038c325d5..1a934ec03a7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs @@ -9,7 +9,7 @@ namespace Internal.Cryptography { internal sealed partial class RC2Implementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs index f4f1ed12315..b4c8029f7a5 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs @@ -61,8 +61,7 @@ namespace Internal.Cryptography if (rgbKey == null) throw new ArgumentNullException(nameof(rgbKey)); - long keySize = rgbKey.Length * (long)BitsPerByte; - if (keySize > int.MaxValue || !((int)keySize).IsLegalSize(LegalKeySizes)) + if (!ValidKeySize(rgbKey.Length, out int keySize)) throw new ArgumentException(SR.Cryptography_InvalidKeySize, nameof(rgbKey)); if (rgbIV != null) @@ -77,10 +76,64 @@ namespace Internal.Cryptography ValidateCFBFeedbackSize(FeedbackSize); } - int effectiveKeySize = EffectiveKeySizeValue == 0 ? (int)keySize : EffectiveKeySize; + int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; return CreateTransformCore(Mode, Padding, rgbKey, effectiveKeySize, rgbIV, BlockSize / BitsPerByte, FeedbackSize / BitsPerByte, GetPaddingSize(), encrypting); } + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + if (!ValidKeySize(Key.Length, out int keySize)) + throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); + + int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + effectiveKeyLength: effectiveKeySize, + iv: null, + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + if (!ValidKeySize(Key.Length, out int keySize)) + throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); + + int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + effectiveKeyLength: effectiveKeySize, + iv: null, + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // CFB not supported at all @@ -91,5 +144,17 @@ namespace Internal.Cryptography { return BlockSize / BitsPerByte; } + + private bool ValidKeySize(int keySizeBytes, out int keySizeBits) + { + if (keySizeBytes > (int.MaxValue / BitsPerByte)) + { + keySizeBits = 0; + return false; + } + + keySizeBits = keySizeBytes << 3; + return keySizeBits.IsLegalSize(LegalKeySizes); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs index 34d3dcf4978..461a07a6550 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { internal sealed partial class TripleDesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs index 2044b3f5963..b9335a18537 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { internal sealed partial class TripleDesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs index b6ea4fca905..577c771b001 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { internal sealed partial class TripleDesImplementation { - private static ICryptoTransform CreateTransformCore( + private static UniversalCryptoTransform CreateTransformCore( CipherMode cipherMode, PaddingMode paddingMode, byte[] key, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs index 691c5736ea0..8c0e2825a73 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs @@ -81,7 +81,59 @@ namespace Internal.Cryptography ValidateCFBFeedbackSize(FeedbackSize); } - return CreateTransformCore(Mode, Padding, rgbKey, rgbIV, BlockSize / BitsPerByte, this.GetPaddingSize(), FeedbackSize / BitsPerByte, encrypting); + return CreateTransformCore( + Mode, + Padding, + rgbKey, + rgbIV, + BlockSize / BitsPerByte, + this.GetPaddingSize(Mode, FeedbackSize), + FeedbackSize / BitsPerByte, + encrypting); + } + + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.ECB, + paddingMode, + Key, + iv: null, + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } } private static void ValidateCFBFeedbackSize(int feedback) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 5577ac649d2..01c484e98d2 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -15,6 +15,8 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\AES\AesCipherTests.Data.cs" /> + + + + cngKeyFactory, CipherMode cipherMode, int blockSizeInBytes, byte[] iv, bool encrypting, int feedbackSizeInBytes, int paddingSize) + public BasicSymmetricCipherNCrypt(Func cngKeyFactory, CipherMode cipherMode, int blockSizeInBytes, byte[]? iv, bool encrypting, int feedbackSizeInBytes, int paddingSize) : base(iv, blockSizeInBytes, paddingSize) { _encrypting = encrypting; diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs index 6aa654e1eea..6b323ccf460 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs @@ -111,25 +111,35 @@ namespace Internal.Cryptography public ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV) { - return CreateCryptoTransform(rgbKey, rgbIV, encrypting: true); + return CreateCryptoTransform(rgbKey, rgbIV, encrypting: true, _outer.Padding, _outer.Mode); } public ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV) { - return CreateCryptoTransform(rgbKey, rgbIV, encrypting: false); + return CreateCryptoTransform(rgbKey, rgbIV, encrypting: false, _outer.Padding, _outer.Mode); } private ICryptoTransform CreateCryptoTransform(bool encrypting) { if (KeyInPlainText) { - return CreateCryptoTransform(_outer.BaseKey, _outer.IV, encrypting); + return CreateCryptoTransform(_outer.BaseKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode); } - return CreatePersistedCryptoTransformCore(ProduceCngKey, _outer.IV, encrypting); + return CreatePersistedCryptoTransformCore(ProduceCngKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode); } - private ICryptoTransform CreateCryptoTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting) + public UniversalCryptoTransform CreateCryptoTransform(byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) + { + if (KeyInPlainText) + { + return CreateCryptoTransform(_outer.BaseKey, iv, encrypting, padding, mode); + } + + return CreatePersistedCryptoTransformCore(ProduceCngKey, iv, encrypting, padding, mode); + } + + private UniversalCryptoTransform CreateCryptoTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting, PaddingMode padding, CipherMode mode) { if (rgbKey == null) throw new ArgumentNullException(nameof(rgbKey)); @@ -148,39 +158,46 @@ namespace Internal.Cryptography // CloneByteArray is null-preserving. So even when GetCipherIv returns null the iv variable // is correct, and detached from the input parameter. - byte[]? iv = _outer.Mode.GetCipherIv(rgbIV).CloneByteArray(); + byte[]? iv = mode.GetCipherIv(rgbIV).CloneByteArray(); key = _outer.PreprocessKey(key); - return CreateEphemeralCryptoTransformCore(key, iv, encrypting); + return CreateEphemeralCryptoTransformCore(key, iv, encrypting, padding, mode); } - private ICryptoTransform CreateEphemeralCryptoTransformCore(byte[] key, byte[]? iv, bool encrypting) + private UniversalCryptoTransform CreateEphemeralCryptoTransformCore(byte[] key, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) { int blockSizeInBytes = _outer.BlockSize.BitSizeToByteSize(); - SafeAlgorithmHandle algorithmModeHandle = _outer.GetEphemeralModeHandle(); + SafeAlgorithmHandle algorithmModeHandle = _outer.GetEphemeralModeHandle(mode); BasicSymmetricCipher cipher = new BasicSymmetricCipherBCrypt( algorithmModeHandle, - _outer.Mode, + mode, blockSizeInBytes, - _outer.GetPaddingSize(), + _outer.GetPaddingSize(mode, _outer.FeedbackSize), key, - false, + ownsParentHandle: false, iv, encrypting); - return UniversalCryptoTransform.Create(_outer.Padding, cipher, encrypting); + return UniversalCryptoTransform.Create(padding, cipher, encrypting); } - private ICryptoTransform CreatePersistedCryptoTransformCore(Func cngKeyFactory, byte[] iv, bool encrypting) + private UniversalCryptoTransform CreatePersistedCryptoTransformCore(Func cngKeyFactory, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) { // note: iv is guaranteed to be cloned before this method, so no need to clone it again int blockSizeInBytes = _outer.BlockSize.BitSizeToByteSize(); int feedbackSizeInBytes = _outer.FeedbackSize; - BasicSymmetricCipher cipher = new BasicSymmetricCipherNCrypt(cngKeyFactory, _outer.Mode, blockSizeInBytes, iv, encrypting, feedbackSizeInBytes, _outer.GetPaddingSize()); - return UniversalCryptoTransform.Create(_outer.Padding, cipher, encrypting); + BasicSymmetricCipher cipher = new BasicSymmetricCipherNCrypt( + cngKeyFactory, + mode, + blockSizeInBytes, + iv, + encrypting, + feedbackSizeInBytes, + _outer.GetPaddingSize(mode, _outer.FeedbackSize)); + return UniversalCryptoTransform.Create(padding, cipher, encrypting); } private CngKey ProduceCngKey() diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs index 8fdf656d87c..1e1edf7c792 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs @@ -28,9 +28,9 @@ namespace Internal.Cryptography // Other members. bool IsWeakKey(byte[] key); - SafeAlgorithmHandle GetEphemeralModeHandle(); + SafeAlgorithmHandle GetEphemeralModeHandle(CipherMode mode); string GetNCryptAlgorithmIdentifier(); byte[] PreprocessKey(byte[] key); - int GetPaddingSize(); + int GetPaddingSize(CipherMode mode, int feedbackSizeBits); } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs index 44553eaeeb7..7a19ca06de9 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs @@ -92,6 +92,42 @@ namespace System.Security.Cryptography _core.GenerateIV(); } + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: null, + encrypting: false, + paddingMode, + CipherMode.ECB); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: null, + encrypting: true, + paddingMode, + CipherMode.ECB); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -105,16 +141,16 @@ namespace System.Security.Cryptography return false; } - int ICngSymmetricAlgorithm.GetPaddingSize() + int ICngSymmetricAlgorithm.GetPaddingSize(CipherMode mode, int feedbackSizeBits) { - return this.GetPaddingSize(); + return this.GetPaddingSize(mode, feedbackSizeBits); } - SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle() + SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode) { try { - return AesBCryptModes.GetSharedHandle(Mode, FeedbackSize / 8); + return AesBCryptModes.GetSharedHandle(mode, FeedbackSize / 8); } catch (NotSupportedException) { diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs index 708acee372a..3687df173b5 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs @@ -93,6 +93,42 @@ namespace System.Security.Cryptography _core.GenerateIV(); } + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: null, + encrypting: false, + paddingMode, + CipherMode.ECB); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: null, + encrypting: true, + paddingMode, + CipherMode.ECB); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -106,14 +142,14 @@ namespace System.Security.Cryptography return TripleDES.IsWeakKey(key); } - int ICngSymmetricAlgorithm.GetPaddingSize() + int ICngSymmetricAlgorithm.GetPaddingSize(CipherMode mode, int feedbackSizeBits) { - return this.GetPaddingSize(); + return this.GetPaddingSize(mode, feedbackSizeBits); } - SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle() + SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode) { - return TripleDesBCryptModes.GetSharedHandle(Mode, FeedbackSize / 8); + return TripleDesBCryptModes.GetSharedHandle(mode, FeedbackSize / 8); } string ICngSymmetricAlgorithm.GetNCryptAlgorithmIdentifier() diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs index 981242fd983..dc6d484263c 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs @@ -15,6 +15,10 @@ namespace System.Security.Cryptography.Cng.Tests [ConditionalTheory(nameof(SupportsPersistedSymmetricKeys))] // AES128-ECB-NoPadding 2 blocks. [InlineData(128, 2 * BlockSizeBytes, CipherMode.ECB, PaddingMode.None)] + // AES128-ECB-Zeros 2 blocks. + [InlineData(128, 2 * BlockSizeBytes, CipherMode.ECB, PaddingMode.Zeros)] + // AES128-ECB-Zeros 1.5 blocks. + [InlineData(128, BlockSizeBytes + BlockSizeBytes / 2, CipherMode.ECB, PaddingMode.Zeros)] // AES128-CBC-NoPadding at 2 blocks [InlineData(128, 2 * BlockSizeBytes, CipherMode.CBC, PaddingMode.None)] // AES256-CBC-Zeros at 1.5 blocks diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs index 2b714471e65..9f1a604a74a 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs @@ -100,6 +100,33 @@ namespace System.Security.Cryptography.Cng.Tests Assert.Equal(expectedBytes, persistedDecrypted); } + + byte[] oneShotPersistedEncrypted = null; + byte[] oneShotEphemeralEncrypted = null; + byte[] oneShotPersistedDecrypted = null; + + if (cipherMode == CipherMode.ECB) + { + oneShotPersistedEncrypted = persisted.EncryptEcb(plainBytes, paddingMode); + oneShotEphemeralEncrypted = ephemeral.EncryptEcb(plainBytes, paddingMode); + oneShotPersistedDecrypted = persisted.DecryptEcb(oneShotEphemeralEncrypted, paddingMode); + } + + if (oneShotPersistedEncrypted is not null) + { + Assert.Equal(oneShotEphemeralEncrypted, oneShotPersistedEncrypted); + + if (paddingMode == PaddingMode.Zeros) + { + byte[] plainPadded = new byte[oneShotPersistedDecrypted.Length]; + plainBytes.AsSpan().CopyTo(plainPadded); + Assert.Equal(plainPadded, oneShotPersistedDecrypted); + } + else + { + Assert.Equal(plainBytes, oneShotPersistedDecrypted); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 1e494d236e1..1761b8d4a2e 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -146,12 +146,16 @@ Link="CommonTest\AlgorithmImplementations\AES\AesContractTests.cs" /> + + diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs b/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs index 078601871d0..d68aa596c27 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs @@ -29,6 +29,10 @@ namespace System.Security.Cryptography.Cng.Tests [ConditionalTheory(nameof(SupportsPersistedSymmetricKeys))] // 3DES192-ECB-NoPadding 2 blocks. [InlineData(2 * BlockSizeBytes, CipherMode.ECB, PaddingMode.None)] + // 3DES192-ECB-Zeros 2 blocks. + [InlineData(2 * BlockSizeBytes, CipherMode.ECB, PaddingMode.Zeros)] + // 3DES192-ECB-Zeros 1.5 blocks. + [InlineData(BlockSizeBytes + BlockSizeBytes / 2, CipherMode.ECB, PaddingMode.Zeros)] // 3DES192-CBC-NoPadding at 2 blocks [InlineData(2 * BlockSizeBytes, CipherMode.CBC, PaddingMode.None)] // 3DES192-CBC-Zeros at 1.5 blocks diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Windows.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Windows.cs index d2536fd6791..2bdd859a691 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Windows.cs @@ -95,7 +95,17 @@ namespace System.Security.Cryptography throw new CryptographicException(SR.Cryptography_InvalidIVSize); } - BasicSymmetricCipher cipher = new BasicSymmetricCipherCsp(CapiHelper.CALG_DES, Mode, BlockSize / BitsPerByte, rgbKey, 0, false, rgbIV, encrypting, FeedbackSize, this.GetPaddingSize()); + BasicSymmetricCipher cipher = new BasicSymmetricCipherCsp( + CapiHelper.CALG_DES, + Mode, + BlockSize / BitsPerByte, + rgbKey, + effectiveKeyLength: 0, + addNoSaltFlag: false, + rgbIV, + encrypting, + FeedbackSize, + this.GetPaddingSize(Mode, FeedbackSize)); return UniversalCryptoTransform.Create(Padding, cipher, encrypting); } } diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs index b2019d7061a..00a71bd1687 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs @@ -77,6 +77,9 @@ namespace System.Security.Cryptography.Csp.Tests "TrySignDataCore", "VerifyDataCore", "VerifySignatureCore", + // CryptoServiceProviders will not get one-shot APIs as they are being deprecated + "TryEncryptEcbCore", + "TryDecryptEcbCore", }; IEnumerable baseMethods = shimType. diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index d363d1df183..49510d6484c 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -253,13 +253,23 @@ namespace System.Security.Cryptography public abstract System.Security.Cryptography.ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV); public virtual System.Security.Cryptography.ICryptoTransform CreateEncryptor() { throw null; } public abstract System.Security.Cryptography.ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV); + public byte[] DecryptEcb(byte[] ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public byte[] DecryptEcb(System.ReadOnlySpan ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public int DecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public byte[] EncryptEcb(byte[] plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public byte[] EncryptEcb(System.ReadOnlySpan plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public int EncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public abstract void GenerateIV(); public abstract void GenerateKey(); public int GetCiphertextLengthCbc(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public int GetCiphertextLengthCfb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } public int GetCiphertextLengthEcb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public bool TryDecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + protected virtual bool TryDecryptEcbCore(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + public bool TryEncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + protected virtual bool TryEncryptEcbCore(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool ValidKeySize(int bitLength) { throw null; } } } diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Primitives/src/Resources/Strings.resx index 75639b42000..250a6eceed2 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Primitives/src/Resources/Strings.resx @@ -60,6 +60,9 @@ Error occurred during a cryptographic operation. + + Destination is too short. + Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. @@ -120,6 +123,9 @@ The specified plaintext size is too large. + + {0} unexpectedly produced a ciphertext with the incorrect length. + Method not supported. Derived class must override. diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs index c140062b697..a79a220a707 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs @@ -428,6 +428,303 @@ namespace System.Security.Cryptography } } + /// + /// Decrypts data using ECB mode with the specified padding mode. + /// + /// The data to decrypt. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The decrypted plaintext data. + /// + /// is . + /// + /// + /// is not a valid padding mode. + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptEcb(byte[] ciphertext, PaddingMode paddingMode) + { + // Padding mode is validated by callee. + if (ciphertext is null) + throw new ArgumentNullException(nameof(ciphertext)); + + return DecryptEcb(new ReadOnlySpan(ciphertext), paddingMode); + } + + /// + /// Decrypts data using ECB mode with the specified padding mode. + /// + /// The data to decrypt. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The decrypted plaintext data. + /// + /// is not a valid padding mode. + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptEcb(ReadOnlySpan ciphertext, PaddingMode paddingMode) + { + CheckPaddingMode(paddingMode); + + // This could get returned directly to the caller if we there was no padding + // that needed to get removed, so don't rent from a pool. + byte[] decryptBuffer = GC.AllocateUninitializedArray(ciphertext.Length); + + if (!TryDecryptEcbCore(ciphertext, decryptBuffer, paddingMode, out int written) + || (uint)written > decryptBuffer.Length) + { + // This means decrypting the ciphertext grew in to a larger plaintext or overflowed. + // A user-derived class could do this, but it is not expected in any of the + // implementations that we ship. + + throw new CryptographicException(SR.Argument_DestinationTooShort); + } + + // Array.Resize will no-op if the array does not need to be resized. + Array.Resize(ref decryptBuffer, written); + return decryptBuffer; + } + + /// + /// Decrypts data into the specified buffer, using ECB mode with the specified padding mode. + /// + /// The data to decrypt. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The total number of bytes written to + /// + /// is not a valid padding mode. + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// The buffer in is too small to hold the plaintext data. + /// + /// + /// This method's behavior is defined by . + /// + public int DecryptEcb(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode) + { + CheckPaddingMode(paddingMode); + + if (!TryDecryptEcbCore(ciphertext, destination, paddingMode, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to decrypt data into the specified buffer, using ECB mode with the specified padding mode. + /// + /// The data to decrypt. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// is not a valid padding mode. + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public bool TryDecryptEcb(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + CheckPaddingMode(paddingMode); + return TryDecryptEcbCore(ciphertext, destination, paddingMode, out bytesWritten); + } + + /// + /// Encrypts data using ECB mode with the specified padding mode. + /// + /// The data to encrypt. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The encrypted ciphertext data. + /// + /// is . + /// + /// + /// is not a valid padding mode. + /// + /// + /// could not encrypt the plaintext. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptEcb(byte[] plaintext, PaddingMode paddingMode) + { + // paddingMode is validated by callee + if (plaintext is null) + throw new ArgumentNullException(nameof(plaintext)); + + return EncryptEcb(new ReadOnlySpan(plaintext), paddingMode); + } + + /// + /// Encrypts data using ECB mode with the specified padding mode. + /// + /// The data to encrypt. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The encrypted ciphertext data. + /// + /// is not a valid padding mode. + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptEcb(ReadOnlySpan plaintext, PaddingMode paddingMode) + { + CheckPaddingMode(paddingMode); + + int ciphertextLength = GetCiphertextLengthEcb(plaintext.Length, paddingMode); + + // We expect most if not all uses to encrypt to exactly the ciphertextLength + byte[] buffer = GC.AllocateUninitializedArray(ciphertextLength); + + if (!TryEncryptEcbCore(plaintext, buffer, paddingMode, out int written) || + written != ciphertextLength) + { + // This means a user-derived imiplementation added more padding than we expected or + // did something non-standard (encrypt to a partial block). This can't happen for + // multiple padding blocks since the buffer would have been too small in the first + // place. It doesn't make sense to try and support partial block encryption, likely + // something went very wrong. So throw. + throw new CryptographicException(SR.Format(SR.Cryptography_EncryptedIncorrectLength, nameof(TryEncryptEcbCore))); + } + + return buffer; + } + + /// + /// Encrypts data into the specified buffer, using ECB mode with the specified padding mode. + /// + /// The data to encrypt. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The total number of bytes written to . + /// + /// is not a valid padding mode. + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// The buffer in is too small to hold the ciphertext data. + /// + /// + /// This method's behavior is defined by . + /// + public int EncryptEcb(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode) + { + CheckPaddingMode(paddingMode); + + if (!TryEncryptEcbCore(plaintext, destination, paddingMode, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to encrypt data into the specified buffer, using ECB mode with the specified padding mode. + /// + /// The data to encrypt. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// is not a valid padding mode. + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public bool TryEncryptEcb(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + CheckPaddingMode(paddingMode); + return TryEncryptEcbCore(plaintext, destination, paddingMode, out bytesWritten); + } + + /// + /// When overridden in a derived class, attempts to encrypt data into the specified + /// buffer, using ECB mode with the specified padding mode. + /// + /// The data to encrypt. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + /// Implementations of this method must write precisely + /// GetCiphertextLengthEcb(plaintext.Length, paddingMode) bytes to + /// and report that via . + /// + /// + protected virtual bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + + /// + /// When overridden in a derived class, attempts to decrypt data + /// into the specified buffer, using ECB mode with the specified padding mode. + /// + /// The data to decrypt. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + protected virtual bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + + private static void CheckPaddingMode(PaddingMode paddingMode) + { + if (paddingMode < PaddingMode.None || paddingMode > PaddingMode.ISO10126) + throw new ArgumentOutOfRangeException(nameof(paddingMode), SR.Cryptography_InvalidPaddingMode); + } + protected CipherMode ModeValue; protected PaddingMode PaddingValue; protected byte[]? KeyValue; diff --git a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs index 8edfa2b0650..65fa6db5699 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs @@ -133,6 +133,138 @@ namespace System.Security.Cryptography.Primitives.Tests alg.GetCiphertextLengthCfb(17, PaddingMode.None, feedbackSizeInBits: 128)); } + [Fact] + public static void EncryptEcb_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.EncryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void DecryptEcb_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.DecryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void EncryptEcb_EncryptProducesIncorrectlyPaddedValue() + { + static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptEcbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void DecryptEcb_DecryptBytesWrittenLies() + { + static bool DecryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptEcbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptEcb(new byte[128 / 8], PaddingMode.None)); + } + + [Fact] + public static void EncryptEcb_EncryptCoreFails() + { + static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptEcbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void EncryptEcb_EncryptCoreOverflowWritten() + { + static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptEcbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void DecryptEcb_DecryptCoreFails() + { + static bool DecryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptEcbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptEcb(Array.Empty(), PaddingMode.None)); + } + + [Fact] + public static void DecryptEcb_DecryptCoreOverflowWritten() + { + static bool DecryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptEcbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptEcb(Array.Empty(), PaddingMode.None)); + } + public static IEnumerable CiphertextLengthTheories { get @@ -231,5 +363,35 @@ namespace System.Security.Cryptography.Primitives.Tests public override void GenerateIV() => throw new NotImplementedException(); public override void GenerateKey() => throw new NotImplementedException(); } + + private class EcbSymmetricAlgorithm : AnySizeAlgorithm + { + public delegate bool TryEncryptEcbCoreFunc( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten); + + public delegate bool TryDecryptEcbCoreFunc( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten); + + public TryEncryptEcbCoreFunc TryEncryptEcbCoreImpl { get; set; } + public TryDecryptEcbCoreFunc TryDecryptEcbCoreImpl { get; set; } + + protected override bool TryEncryptEcbCore( + ReadOnlySpan plaintext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) => TryEncryptEcbCoreImpl(plaintext, destination, paddingMode, out bytesWritten); + + protected override bool TryDecryptEcbCore( + ReadOnlySpan ciphertext, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) => TryDecryptEcbCoreImpl(ciphertext, destination, paddingMode, out bytesWritten); + } } } From d0adff8186cbd58f4f63fe2ffdb26077a11390fd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 28 Jun 2021 09:32:11 -0700 Subject: [PATCH 156/926] Fix alloc-dealloc mismatches (#54701) --- src/coreclr/vm/ilstubresolver.cpp | 3 +-- src/coreclr/vm/methodtable.cpp | 9 ++----- src/coreclr/vm/methodtable.h | 44 ++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 2df8f543e9e..74cd62a7015 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -344,8 +344,7 @@ ILStubResolver::AllocGeneratedIL( if (!UseLoaderHeap()) { NewArrayHolder pNewILCodeBuffer = new BYTE[cbCode]; - NewArrayHolder pNewCompileTimeState = (CompileTimeState*)new BYTE[sizeof(CompileTimeState)]; - memset(pNewCompileTimeState, 0, sizeof(CompileTimeState)); + NewHolder pNewCompileTimeState = new CompileTimeState{}; NewArrayHolder pNewLocalSig = NULL; if (0 != cbLocalSig) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index bb44e829905..a81a0117e6f 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -8504,10 +8504,7 @@ MethodTable::GetMethodDataHelper( MethodDataWrapper hDecl(GetMethodData(pMTDecl, FALSE)); MethodDataWrapper hImpl(GetMethodData(pMTImpl, FALSE)); - UINT32 cb = MethodDataInterfaceImpl::GetObjectSize(pMTDecl); - NewArrayHolder pb(new BYTE[cb]); - MethodDataInterfaceImpl * pData = new (pb.GetValue()) MethodDataInterfaceImpl(rgDeclTypeIDs, cDeclTypeIDs, hDecl, hImpl); - pb.SuppressRelease(); + MethodDataInterfaceImpl * pData = new ({ pMTDecl }) MethodDataInterfaceImpl(rgDeclTypeIDs, cDeclTypeIDs, hDecl, hImpl); return pData; } // MethodTable::GetMethodDataHelper @@ -8548,10 +8545,8 @@ MethodTable::MethodData *MethodTable::GetMethodDataHelper(MethodTable *pMTDecl, } else { UINT32 cb = MethodDataObject::GetObjectSize(pMTDecl); - NewArrayHolder pb(new BYTE[cb]); MethodDataHolder h(FindParentMethodDataHelper(pMTDecl)); - pData = new (pb.GetValue()) MethodDataObject(pMTDecl, h.GetValue()); - pb.SuppressRelease(); + pData = new ({ pMTDecl }) MethodDataObject(pMTDecl, h.GetValue()); } } else { diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 3d0ee06b37f..13ff58836fe 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -3157,7 +3157,7 @@ public: protected: //-------------------------------------------------------------------------------------- - class MethodDataObject : public MethodData + class MethodDataObject final : public MethodData { public: // Static method that returns the amount of memory to allocate for a particular type. @@ -3237,19 +3237,32 @@ protected: { LIMITED_METHOD_CONTRACT; return m_pMDImpl; } }; - // - // At the end of this object is an array, so you cannot derive from this class. - // inline MethodDataObjectEntry *GetEntryData() - { LIMITED_METHOD_CONTRACT; return (MethodDataObjectEntry *)(this + 1); } + { LIMITED_METHOD_CONTRACT; return &m_rgEntries[0]; } inline MethodDataObjectEntry *GetEntry(UINT32 i) { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK(i < GetNumMethods()); return GetEntryData() + i; } void FillEntryDataForAncestor(MethodTable *pMT); - // MethodDataObjectEntry m_rgEntries[...]; + // + // At the end of this object is an array + // + MethodDataObjectEntry m_rgEntries[0]; + + public: + struct TargetMethodTable + { + MethodTable* pMT; + }; + + static void* operator new(size_t size, TargetMethodTable targetMT) + { + _ASSERTE(size <= GetObjectSize(targetMT.pMT)); + return ::operator new(GetObjectSize(targetMT.pMT)); + } + static void* operator new(size_t size) = delete; }; // class MethodDataObject //-------------------------------------------------------------------------------------- @@ -3303,7 +3316,7 @@ protected: }; // class MethodDataInterface //-------------------------------------------------------------------------------------- - class MethodDataInterfaceImpl : public MethodData + class MethodDataInterfaceImpl final : public MethodData { public: // Object construction-related methods @@ -3377,12 +3390,25 @@ protected: // inline MethodDataEntry *GetEntryData() - { LIMITED_METHOD_CONTRACT; return (MethodDataEntry *)(this + 1); } + { LIMITED_METHOD_CONTRACT; return &m_rgEntries[0]; } inline MethodDataEntry *GetEntry(UINT32 i) { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK(i < GetNumMethods()); return GetEntryData() + i; } - // MethodDataEntry m_rgEntries[...]; + MethodDataEntry m_rgEntries[0]; + + public: + struct TargetMethodTable + { + MethodTable* pMT; + }; + + static void* operator new(size_t size, TargetMethodTable targetMT) + { + _ASSERTE(size <= GetObjectSize(targetMT.pMT)); + return ::operator new(GetObjectSize(targetMT.pMT)); + } + static void* operator new(size_t size) = delete; }; // class MethodDataInterfaceImpl //-------------------------------------------------------------------------------------- From 8707275824c2524fa670312dbc1c78551ce78ef4 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 28 Jun 2021 10:39:17 -0600 Subject: [PATCH 157/926] First round of converting System.Drawing.Common to COMWrappers (#54636) * First round of converting System.Drawing.Common to COMWrappers Using COM Wrappers makes the library trim compatible. * Add Trimming Test for Icon.Save * Add support for OS specific trimming tests * Use function pointers instead of delegates * Rename Guid to IID * Better interop to closely match the native side * Release any COM pointer that was QueryInterface * Use pointers instead of Marshal.PtrToStructure/StructureToPtr * No need for a VTable struct, just set each function pointer right into the table * Wrap all managed calls in try-catch and return HResult * Use COM naming * Fix method signature to use pointer instead of out. * CheckStatus => ThrowExceptionForHR * Pass -1 to Marshal.GetExceptionForHR so it doesn't query GetErrorInfo, and always returns the correct exception type * Create the PictureWrapper with UniqueInstance, so it doesn't get cached. Caching it causes lifetime issues. --- eng/testing/linker/project.csproj.template | 2 +- eng/testing/linker/trimmingTests.targets | 14 +- .../src/Interop/Windows/Interop.HRESULT.cs | 1 + .../Interop/Windows/Ole32/Interop.IStream.cs | 4 +- .../Interop/Windows/Ole32/Interop.STATFLAG.cs | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 6 - .../src/System.Drawing.Common.csproj | 7 + .../src/System/Drawing/DrawingComWrappers.cs | 314 ++++++++++++++++++ .../Drawing/Icon.Windows.COMWrappers.cs | 92 +++++ .../Drawing/Icon.Windows.NoCOMWrappers.cs | 115 +++++++ .../src/System/Drawing/Icon.Windows.cs | 101 ------ .../src/System/Drawing/Internal/GPStream.cs | 12 +- .../System.Drawing.Common/tests/IconTests.cs | 13 +- .../tests/TrimmingTests/IconSave.cs | 28 ++ .../System.Drawing.Common.TrimmingTests.proj | 15 + 15 files changed, 603 insertions(+), 123 deletions(-) create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs create mode 100644 src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs create mode 100644 src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 7f8ccaba794..38c6d68f901 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -8,7 +8,7 @@ {WasmAppBuilderTasksAssemblyPath} {MicrosoftNetCoreAppRuntimePackRidDir} {RepositoryEngineeringDir} - {NetCoreAppCurrent} + {TestTargetFramework} {RuntimeIdentifier} {UseMonoRuntime} {TargetingPackDir} diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index f43dbee3b4e..6f084b6ddb4 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -25,14 +25,16 @@ $([MSBuild]::NormalizeDirectory('$(TrimmingTestProjectsDir)', '$(MSBuildProjectName)', '%(Filename)', '$(PackageRID)')) $(PackageRID) + $(NetCoreAppCurrent) + $(NetCoreAppCurrent)-%(TestConsoleAppSourceFiles.TargetOS) - + %(ProjectDir)project.csproj - $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'publish', 'project')) - $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'AppBundle', 'run-v8.sh')) - $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'publish')) - $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '$(NetCoreAppCurrent)', '%(TestRuntimeIdentifier)', 'AppBundle')) + $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'publish', 'project')) + $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'AppBundle', 'run-v8.sh')) + $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'publish')) + $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TestTargetFramework)', '%(TestRuntimeIdentifier)', 'AppBundle')) @@ -70,7 +72,7 @@ . + /// Stat flags for . /// /// internal enum STATFLAG : uint diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml index 3f367cccd24..a0b7401fb0a 100644 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml @@ -7,12 +7,6 @@ member M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) - - ILLink - IL2050 - member - M:System.Drawing.Icon.Save(System.IO.Stream) - ILLink IL2050 diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index 092ea6af165..b02d55a46c6 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -317,6 +317,13 @@ + + + + + + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs new file mode 100644 index 00000000000..7895381853c --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs @@ -0,0 +1,314 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// The ComWrappers implementation for System.Drawing.Common's COM interop usages. + /// + /// Supports IStream and IPicture COM interfaces. + /// + internal unsafe class DrawingComWrappers : ComWrappers + { + private const int S_OK = (int)Interop.HRESULT.S_OK; + private static readonly Guid IID_IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + + private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry(); + + internal static DrawingComWrappers Instance { get; } = new DrawingComWrappers(); + + private DrawingComWrappers() { } + + private static ComInterfaceEntry* InitializeComInterfaceEntry() + { + GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); + + IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInteface, fpAddRef, fpRelease); + + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); + wrapperEntry->IID = IID_IStream; + wrapperEntry->Vtable = iStreamVtbl; + return wrapperEntry; + } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + Debug.Assert(obj is Interop.Ole32.IStream); + Debug.Assert(s_wrapperEntry != null); + + // Always return the same table mappings. + count = 1; + return s_wrapperEntry; + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + Debug.Assert(flags == CreateObjectFlags.UniqueInstance); + + Guid pictureIID = IPicture.IID; + int hr = Marshal.QueryInterface(externalComObject, ref pictureIID, out IntPtr comObject); + if (hr == S_OK) + { + return new PictureWrapper(comObject); + } + + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + internal static class IStreamVtbl + { + public static IntPtr Create(IntPtr fpQueryInteface, IntPtr fpAddRef, IntPtr fpRelease) + { + IntPtr* vtblRaw = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), IntPtr.Size * 14); + vtblRaw[0] = fpQueryInteface; + vtblRaw[1] = fpAddRef; + vtblRaw[2] = fpRelease; + vtblRaw[3] = (IntPtr)(delegate* unmanaged)&Read; + vtblRaw[4] = (IntPtr)(delegate* unmanaged)&Write; + vtblRaw[5] = (IntPtr)(delegate* unmanaged)&Seek; + vtblRaw[6] = (IntPtr)(delegate* unmanaged)&SetSize; + vtblRaw[7] = (IntPtr)(delegate* unmanaged)&CopyTo; + vtblRaw[8] = (IntPtr)(delegate* unmanaged)&Commit; + vtblRaw[9] = (IntPtr)(delegate* unmanaged)&Revert; + vtblRaw[10] = (IntPtr)(delegate* unmanaged)&LockRegion; + vtblRaw[11] = (IntPtr)(delegate* unmanaged)&UnlockRegion; + vtblRaw[12] = (IntPtr)(delegate* unmanaged)&Stat; + vtblRaw[13] = (IntPtr)(delegate* unmanaged)&Clone; + + return (IntPtr)vtblRaw; + } + + [UnmanagedCallersOnly] + private static int Read(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Read(pv, cb, pcbRead); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Write(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Write(pv, cb, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Seek(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Seek(dlibMove, dwOrigin, plibNewPosition); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int SetSize(IntPtr thisPtr, ulong libNewSize) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.SetSize(libNewSize); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + Interop.Ole32.IStream pstmStream = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)pstm); + + inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Commit(IntPtr thisPtr, uint grfCommitFlags) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Commit(grfCommitFlags); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Revert(IntPtr thisPtr) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Revert(); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int LockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.LockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int UnlockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.UnlockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int Stat(IntPtr thisPtr, Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Stat(pstatstg, grfStatFlag); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Clone(IntPtr thisPtr, IntPtr* ppstm) + { + if (ppstm == null) + { + return (int)Interop.HRESULT.STG_E_INVALIDPOINTER; + } + + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *ppstm = Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + } + + internal interface IPicture : IDisposable + { + static readonly Guid IID = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0, 0xAA, 0x00, 0x30, 0x0C, 0xAB); + + // NOTE: Only SaveAsFile is invoked. The other methods on IPicture are not necessary + + int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize); + } + + private class PictureWrapper : IPicture + { + private readonly IntPtr _wrappedInstance; + + public PictureWrapper(IntPtr wrappedInstance) + { + _wrappedInstance = wrappedInstance; + } + + public void Dispose() + { + Marshal.Release(_wrappedInstance); + } + + public unsafe int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize) + { + // Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation + Guid streamIID = IID_IStream; + Marshal.ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); + + try + { + return ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 15 /* IPicture.SaveAsFile slot */))) + (_wrappedInstance, pstmImpl, fSaveMemCopy, pcbSize); + } + finally + { + Marshal.Release(pstmImpl); + } + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs new file mode 100644 index 00000000000..a03ad5ed133 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + public unsafe void Save(Stream outputStream) + { + if (_iconData != null) + { + outputStream.Write(_iconData, 0, _iconData.Length); + } + else + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + + // Ideally, we would pick apart the icon using + // GetIconInfo, and then pull the individual bitmaps out, + // converting them to DIBS and saving them into the file. + // But, in the interest of simplicity, we just call to + // OLE to do it for us. + PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); + Guid iid = DrawingComWrappers.IPicture.IID; + IntPtr lpPicture; + Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); + + IntPtr streamPtr = IntPtr.Zero; + try + { + // Use UniqueInstance here because we never want to cache the wrapper. It only gets used once and then disposed. + using DrawingComWrappers.IPicture picture = (DrawingComWrappers.IPicture)DrawingComWrappers.Instance + .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); + + var gpStream = new GPStream(outputStream, makeSeekable: false); + streamPtr = DrawingComWrappers.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + + CheckSaveAsFileResult(picture.SaveAsFile(streamPtr, -1, null)); + } + finally + { + if (streamPtr != IntPtr.Zero) + { + int count = Marshal.Release(streamPtr); + Debug.Assert(count == 0); + } + + if (lpPicture != IntPtr.Zero) + { + int count = Marshal.Release(lpPicture); + Debug.Assert(count == 0); + } + } + } + } + + private static void CheckSaveAsFileResult(int errorCode) + { + // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only + // throw the Exception corresponding to the specified errorCode. + Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); + } + + [DllImport(Interop.Libraries.Oleaut32)] + private static unsafe extern int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); + + [StructLayout(LayoutKind.Sequential)] + private readonly struct PICTDESC + { + public readonly int SizeOfStruct; + public readonly int PicType; + public readonly IntPtr Icon; + + private unsafe PICTDESC(int picType, IntPtr hicon) + { + SizeOfStruct = sizeof(PICTDESC); + PicType = picType; + Icon = hicon; + } + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) => + new PICTDESC(Ole.PICTYPE_ICON, hicon); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs new file mode 100644 index 00000000000..34a5d19a0d9 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.NoCOMWrappers.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + public void Save(Stream outputStream) + { + if (_iconData != null) + { + outputStream.Write(_iconData, 0, _iconData.Length); + } + else + { + // Ideally, we would pick apart the icon using + // GetIconInfo, and then pull the individual bitmaps out, + // converting them to DIBS and saving them into the file. + // But, in the interest of simplicity, we just call to + // OLE to do it for us. + PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); + Guid g = typeof(IPicture).GUID; + IPicture picture = OleCreatePictureIndirect(pictdesc, ref g, false); + + if (picture != null) + { + try + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + + picture.SaveAsFile(new GPStream(outputStream, makeSeekable: false), -1, out int temp); + } + finally + { + Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + Marshal.ReleaseComObject(picture); + } + } + } + } + + [DllImport(Interop.Libraries.Oleaut32, PreserveSig = false)] + internal static extern IPicture OleCreatePictureIndirect(PICTDESC pictdesc, [In]ref Guid refiid, bool fOwn); + + [ComImport] + [Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IPicture + { + IntPtr GetHandle(); + + IntPtr GetHPal(); + + [return: MarshalAs(UnmanagedType.I2)] + short GetPictureType(); + + int GetWidth(); + + int GetHeight(); + + void Render(); + + void SetHPal([In] IntPtr phpal); + + IntPtr GetCurDC(); + + void SelectPicture([In] IntPtr hdcIn, + [Out, MarshalAs(UnmanagedType.LPArray)] int[] phdcOut, + [Out, MarshalAs(UnmanagedType.LPArray)] int[] phbmpOut); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetKeepOriginalFormat(); + + void SetKeepOriginalFormat([In, MarshalAs(UnmanagedType.Bool)] bool pfkeep); + + void PictureChanged(); + + [PreserveSig] + int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] Interop.Ole32.IStream pstm, + [In] int fSaveMemCopy, + [Out] out int pcbSize); + + int GetAttributes(); + + void SetHdc([In] IntPtr hdc); + } + + [StructLayout(LayoutKind.Sequential)] + internal sealed class PICTDESC + { + internal int cbSizeOfStruct; + public int picType; + internal IntPtr union1; + internal int union2; + internal int union3; + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) + { + return new PICTDESC() + { + cbSizeOfStruct = 12, + picType = Ole.PICTYPE_ICON, + union1 = hicon + }; + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs index 6bd26a336c1..4acd6376e4c 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs @@ -637,41 +637,6 @@ namespace System.Drawing } } - public void Save(Stream outputStream) - { - if (_iconData != null) - { - outputStream.Write(_iconData, 0, _iconData.Length); - } - else - { - // Ideally, we would pick apart the icon using - // GetIconInfo, and then pull the individual bitmaps out, - // converting them to DIBS and saving them into the file. - // But, in the interest of simplicity, we just call to - // OLE to do it for us. - PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); - Guid g = typeof(IPicture).GUID; - IPicture picture = OleCreatePictureIndirect(pictdesc, ref g, false); - - if (picture != null) - { - try - { - if (outputStream == null) - throw new ArgumentNullException(nameof(outputStream)); - - picture.SaveAsFile(new GPStream(outputStream, makeSeekable: false), -1, out int temp); - } - finally - { - Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); - Marshal.ReleaseComObject(picture); - } - } - } - } - private unsafe void CopyBitmapData(BitmapData sourceData, BitmapData targetData) { byte* srcPtr = (byte*)sourceData.Scan0; @@ -905,75 +870,9 @@ namespace System.Drawing public override string ToString() => SR.toStringIcon; - [DllImport(Interop.Libraries.Oleaut32, PreserveSig = false)] - internal static extern IPicture OleCreatePictureIndirect(PICTDESC pictdesc, [In]ref Guid refiid, bool fOwn); - - [ComImport] - [Guid("7BF80980-BF32-101A-8BBB-00AA00300CAB")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IPicture - { - IntPtr GetHandle(); - - IntPtr GetHPal(); - - [return: MarshalAs(UnmanagedType.I2)] - short GetPictureType(); - - int GetWidth(); - - int GetHeight(); - - void Render(); - - void SetHPal([In] IntPtr phpal); - - IntPtr GetCurDC(); - - void SelectPicture([In] IntPtr hdcIn, - [Out, MarshalAs(UnmanagedType.LPArray)] int[] phdcOut, - [Out, MarshalAs(UnmanagedType.LPArray)] int[] phbmpOut); - - [return: MarshalAs(UnmanagedType.Bool)] - bool GetKeepOriginalFormat(); - - void SetKeepOriginalFormat([In, MarshalAs(UnmanagedType.Bool)] bool pfkeep); - - void PictureChanged(); - - [PreserveSig] - int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] Interop.Ole32.IStream pstm, - [In] int fSaveMemCopy, - [Out] out int pcbSize); - - int GetAttributes(); - - void SetHdc([In] IntPtr hdc); - } - internal static class Ole { public const int PICTYPE_ICON = 3; } - - [StructLayout(LayoutKind.Sequential)] - internal sealed class PICTDESC - { - internal int cbSizeOfStruct; - public int picType; - internal IntPtr union1; - internal int union2; - internal int union3; - - public static PICTDESC CreateIconPICTDESC(IntPtr hicon) - { - return new PICTDESC() - { - cbSizeOfStruct = 12, - picType = Ole.PICTYPE_ICON, - union1 = hicon - }; - } - } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs index 6040cd85c98..dd6a8a16350 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs @@ -3,7 +3,6 @@ using System.Buffers; using System.IO; -using System.Runtime.InteropServices; namespace System.Drawing.Internal { @@ -177,9 +176,14 @@ namespace System.Drawing.Internal _dataStream.SetLength(checked((long)value)); } - public void Stat(out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + public unsafe void Stat(Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) { - pstatstg = new Interop.Ole32.STATSTG + if (pstatstg == null) + { + throw new ArgumentNullException(nameof(pstatstg)); + } + + *pstatstg = new Interop.Ole32.STATSTG { cbSize = (ulong)_dataStream.Length, type = Interop.Ole32.STGTY.STGTY_STREAM, @@ -195,7 +199,7 @@ namespace System.Drawing.Internal if (grfStatFlag == Interop.Ole32.STATFLAG.STATFLAG_DEFAULT) { // Caller wants a name - pstatstg.AllocName(_dataStream is FileStream fs ? fs.Name : _dataStream.ToString()); + pstatstg->AllocName(_dataStream is FileStream fs ? fs.Name : _dataStream.ToString()); } } diff --git a/src/libraries/System.Drawing.Common/tests/IconTests.cs b/src/libraries/System.Drawing.Common/tests/IconTests.cs index 2ad23d5f18d..108e277aee2 100644 --- a/src/libraries/System.Drawing.Common/tests/IconTests.cs +++ b/src/libraries/System.Drawing.Common/tests/IconTests.cs @@ -528,7 +528,7 @@ namespace System.Drawing.Tests [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalFact(Helpers.IsDrawingSupported)] - public void Save_ClosedOutputStreamNoIconData_DoesNothing() + public void Save_ClosedOutputStreamNoIconData() { using (var source = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) using (var icon = Icon.FromHandle(source.Handle)) @@ -536,7 +536,16 @@ namespace System.Drawing.Tests var stream = new MemoryStream(); stream.Close(); - icon.Save(stream); + if (PlatformDetection.IsNetFramework) + { + // The ObjectDisposedException is ignored in previous .NET versions, + // so the following does nothing. + icon.Save(stream); + } + else + { + Assert.Throws(() => icon.Save(stream)); + } } } diff --git a/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs b/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs new file mode 100644 index 00000000000..0e15a645c51 --- /dev/null +++ b/src/libraries/System.Drawing.Common/tests/TrimmingTests/IconSave.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.IO; + +/// +/// Tests that Icon.Save works when the Icon is loaded from an IntPtr. +/// This causes COM to be used to save the Icon. +/// +class Program +{ + static int Main(string[] args) + { + Icon i = SystemIcons.WinLogo; + using MemoryStream stream = new(); + + i.Save(stream); + + // ensure something was written to the stream + if (stream.Position == 0) + { + return -1; + } + + return 100; + } +} diff --git a/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj b/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj new file mode 100644 index 00000000000..71883b846cb --- /dev/null +++ b/src/libraries/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj @@ -0,0 +1,15 @@ + + + + + System.Drawing.Common + + + + + + + + From af18e93a2bb205f4d8ea5a42b5477b8476274933 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 28 Jun 2021 12:59:06 -0400 Subject: [PATCH 158/926] Obsolete X509Certificate2.PrivateKey and PublicKey.Key. (#54562) The two properties got different diagnostic IDs so that the messages could better reflect the caller recovery action. --- docs/project/list-of-diagnostics.md | 2 ++ src/libraries/Common/src/System/Obsoletions.cs | 6 ++++++ .../System.Net.Security/tests/FunctionalTests/TestHelper.cs | 2 +- .../tests/EnvelopedCms/DecryptTests.cs | 2 +- .../ref/System.Security.Cryptography.X509Certificates.cs | 2 ++ .../Security/Cryptography/X509Certificates/PublicKey.cs | 2 ++ .../Cryptography/X509Certificates/X509Certificate2.cs | 1 + ...stem.Security.Cryptography.X509Certificates.Tests.csproj | 2 +- .../System.Security.Cryptography.Xml/tests/SignedXmlTest.cs | 6 +++--- 9 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index fc946ddb03b..9497fc7b692 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -81,6 +81,8 @@ The PR that reveals the implementation of the `().Where((r) => r.RecipientIdentifier.MatchesCertificate(cert)).Single(); - ecms.Decrypt(recipient, cert.PrivateKey); + ecms.Decrypt(recipient, cert.GetRSAPrivateKey()); } } else diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index e3ade1a368c..6adbdfb1160 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -64,6 +64,7 @@ namespace System.Security.Cryptography.X509Certificates public PublicKey(System.Security.Cryptography.Oid oid, System.Security.Cryptography.AsnEncodedData parameters, System.Security.Cryptography.AsnEncodedData keyValue) { } public System.Security.Cryptography.AsnEncodedData EncodedKeyValue { get { throw null; } } public System.Security.Cryptography.AsnEncodedData EncodedParameters { get { throw null; } } + [System.ObsoleteAttribute("PublicKey.Key is obsolete. Use the appropriate method to get the public key, such as GetRSAPublicKey.", DiagnosticId = "SYSLIB0027", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public System.Security.Cryptography.AsymmetricAlgorithm Key { get { throw null; } } public System.Security.Cryptography.Oid Oid { get { throw null; } } public static System.Security.Cryptography.X509Certificates.PublicKey CreateFromSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } @@ -253,6 +254,7 @@ namespace System.Security.Cryptography.X509Certificates public System.Security.Cryptography.X509Certificates.X500DistinguishedName IssuerName { get { throw null; } } public System.DateTime NotAfter { get { throw null; } } public System.DateTime NotBefore { get { throw null; } } + [System.ObsoleteAttribute("X509Certificate2.PrivateKey is obsolete. Use the appropriate method to get the private key, such as GetRSAPrivateKey, or use the CopyWithPrivateKey method to create a new instance with a private key.", DiagnosticId = "SYSLIB0028", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } } public System.Security.Cryptography.X509Certificates.PublicKey PublicKey { get { throw null; } } public byte[] RawData { get { throw null; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs index a641fe9f0c4..009242d85f8 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Buffers; using System.Formats.Asn1; using System.Runtime.InteropServices; @@ -61,6 +62,7 @@ namespace System.Security.Cryptography.X509Certificates public AsnEncodedData EncodedParameters { get; private set; } + [Obsolete(Obsoletions.PublicKeyPropertyMessage, DiagnosticId = Obsoletions.PublicKeyPropertyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public AsymmetricAlgorithm Key { get diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index d4ef8220853..967d2915b5c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -238,6 +238,7 @@ namespace System.Security.Cryptography.X509Certificates } } + [Obsolete(Obsoletions.X509CertificatePrivateKeyMessage, DiagnosticId = Obsoletions.X509CertificatePrivateKeyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public AsymmetricAlgorithm? PrivateKey { get diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index ee68bbc75bc..18b91ea8472 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -4,7 +4,7 @@ $(DefineConstants);HAVE_THUMBPRINT_OVERLOADS $(DefineConstants);Unix true - $(NoWarn);SYSLIB0026 + $(NoWarn);SYSLIB0026;SYSLIB0027;SYSLIB0028 $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs index c6ad9ba72e8..c85f82b12ec 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs @@ -665,7 +665,7 @@ namespace System.Security.Cryptography.Xml.Tests X509Certificate2 cert = new X509Certificate2(_pkcs12, "mono"); SignedXml signedXml = new SignedXml(doc); - signedXml.SigningKey = cert.PrivateKey; + signedXml.SigningKey = cert.GetRSAPrivateKey(); signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url; @@ -725,7 +725,7 @@ namespace System.Security.Cryptography.Xml.Tests X509Certificate2 cert = new X509Certificate2(_pkcs12, "mono"); SignedXml signedXml = new SignedXml(doc); - signedXml.SigningKey = cert.PrivateKey; + signedXml.SigningKey = cert.GetRSAPrivateKey(); signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url; signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; @@ -970,7 +970,7 @@ namespace System.Security.Cryptography.Xml.Tests XmlDocument doc = CreateSomeXml(lineFeed); SignedXml signedXml = new SignedXml(doc); - signedXml.SigningKey = cert.PrivateKey; + signedXml.SigningKey = cert.GetRSAPrivateKey(); signedXml.SignedInfo.CanonicalizationMethod = canonicalizationMethod; signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url; From 291e28426c8810aa2e0b3952a4f824371099c4c4 Mon Sep 17 00:00:00 2001 From: Wei Zheng <13881045+wzchua@users.noreply.github.com> Date: Tue, 29 Jun 2021 01:51:46 +0800 Subject: [PATCH 159/926] Fix load exception on generic covariant return type (#54790) --- src/coreclr/vm/class.cpp | 7 ++-- .../coreclr/GitHub_54719/test54719.cs | 32 +++++++++++++++++++ .../coreclr/GitHub_54719/test54719.csproj | 10 ++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/tests/Regressions/coreclr/GitHub_54719/test54719.cs create mode 100644 src/tests/Regressions/coreclr/GitHub_54719/test54719.csproj diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 6f2da9db3c2..7fdf25133c1 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -1170,12 +1170,13 @@ void ClassLoader::ValidateMethodsWithCovariantReturnTypes(MethodTable* pMT) continue; // Locate the MethodTable defining the pParentMD. - while (pParentMT->GetCanonicalMethodTable() != pParentMD->GetMethodTable()) + MethodTable* pDefinitionParentMT = pParentMT; + while (pDefinitionParentMT->GetCanonicalMethodTable() != pParentMD->GetMethodTable()) { - pParentMT = pParentMT->GetParentMethodTable(); + pDefinitionParentMT = pDefinitionParentMT->GetParentMethodTable(); } - SigTypeContext context1(pParentMT->GetInstantiation(), pMD->GetMethodInstantiation()); + SigTypeContext context1(pDefinitionParentMT->GetInstantiation(), pMD->GetMethodInstantiation()); MetaSig methodSig1(pParentMD); TypeHandle hType1 = methodSig1.GetReturnProps().GetTypeHandleThrowing(pParentMD->GetModule(), &context1, ClassLoader::LoadTypesFlag::LoadTypes, CLASS_LOAD_EXACTPARENTS); diff --git a/src/tests/Regressions/coreclr/GitHub_54719/test54719.cs b/src/tests/Regressions/coreclr/GitHub_54719/test54719.cs new file mode 100644 index 00000000000..64e60e59800 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_54719/test54719.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +public class IA { } +public class IB { } + +public abstract class Base +{ + public abstract IA Key { get; } + public abstract IB Value { get; } +} +public sealed class Derived : Base +{ + public class A : IA { } + public sealed override A Key => default; +} +public abstract class Base : Base where B : IB +{ + public sealed override B Value => null; +} + +class Program +{ + static int Main() + { + new Derived(); + + return 100; + } +} diff --git a/src/tests/Regressions/coreclr/GitHub_54719/test54719.csproj b/src/tests/Regressions/coreclr/GitHub_54719/test54719.csproj new file mode 100644 index 00000000000..543d7356bbb --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_54719/test54719.csproj @@ -0,0 +1,10 @@ + + + Exe + BuildAndRun + 1 + + + + + From 6e045f4acbe9fae1c7dbf903e6b992a5310dbb81 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 28 Jun 2021 13:04:55 -0500 Subject: [PATCH 160/926] Remove ActiveIssue for #51723 (#54830) --- .../System/Collections/IEnumerable.NonGeneric.Tests.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libraries/Common/tests/System/Collections/IEnumerable.NonGeneric.Tests.cs b/src/libraries/Common/tests/System/Collections/IEnumerable.NonGeneric.Tests.cs index a9152629a5f..5760cbef906 100644 --- a/src/libraries/Common/tests/System/Collections/IEnumerable.NonGeneric.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/IEnumerable.NonGeneric.Tests.cs @@ -133,7 +133,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_MoveNext_ModifiedBeforeEnumeration_ThrowsInvalidOperationException(int count) { @@ -147,7 +146,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_MoveNext_ModifiedDuringEnumeration_ThrowsInvalidOperationException(int count) { @@ -163,7 +161,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_MoveNext_ModifiedAfterEnumeration_ThrowsInvalidOperationException(int count) { @@ -251,7 +248,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public virtual void Enumerator_Current_ModifiedDuringEnumeration_UndefinedBehavior(int count) { @@ -286,7 +282,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_Reset_ModifiedBeforeEnumeration_ThrowsInvalidOperationException(int count) { @@ -300,7 +295,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_Reset_ModifiedDuringEnumeration_ThrowsInvalidOperationException(int count) { @@ -316,7 +310,6 @@ namespace System.Collections.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51723", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] [MemberData(nameof(ValidCollectionSizes))] public void IEnumerable_NonGeneric_Enumerator_Reset_ModifiedAfterEnumeration_ThrowsInvalidOperationException(int count) { From 5066c83e6336b2f079b5b4219dcd43de2dcbde5e Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 28 Jun 2021 13:11:30 -0500 Subject: [PATCH 161/926] Enable System.Text.Json tests for Wasm AOT (#54833) --- src/libraries/tests.proj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b5638f2a00d..c4054686952 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -283,9 +283,6 @@ - - - From 26f0ade1b7e51d928362fbb6fee2b9257bae4580 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 28 Jun 2021 13:12:08 -0500 Subject: [PATCH 162/926] Remove ActiveIssue for #50968 (#54831) --- .../AsyncTaskMethodBuilderTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs index 177696010f8..647f3cbdcc6 100644 --- a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs @@ -407,7 +407,6 @@ namespace System.Threading.Tasks.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50968", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void Tcs_ValidateFaultedTask() { var tcs = new TaskCompletionSource(); @@ -417,7 +416,6 @@ namespace System.Threading.Tasks.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50968", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void TaskMethodBuilder_ValidateFaultedTask() { var atmb = AsyncTaskMethodBuilder.Create(); @@ -427,7 +425,6 @@ namespace System.Threading.Tasks.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50968", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void TaskMethodBuilderT_ValidateFaultedTask() { var atmbtr = AsyncTaskMethodBuilder.Create(); @@ -437,7 +434,6 @@ namespace System.Threading.Tasks.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50968", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void TrackedSyncContext_ValidateException() { SynchronizationContext previousContext = SynchronizationContext.Current; From 909b1b29806de8adcd67b115c219b14acb3a61e3 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Mon, 28 Jun 2021 21:16:44 +0300 Subject: [PATCH 163/926] Add YieldProcessor implementation for arm (#54829) --- src/coreclr/gc/env/gcenv.base.h | 9 ++------- src/coreclr/pal/inc/pal.h | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index 0aa1d0bd673..59eb74862f5 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -227,15 +227,10 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #endif // defined(__i386__) || defined(__x86_64__) -#ifdef __aarch64__ +#if defined(__arm__) || defined(__aarch64__) #define YieldProcessor() asm volatile ("yield") #define MemoryBarrier __sync_synchronize -#endif // __aarch64__ - -#ifdef __arm__ - #define YieldProcessor() - #define MemoryBarrier __sync_synchronize -#endif // __arm__ +#endif // __arm__ || __aarch64__ #endif // _MSC_VER diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 8e0553e02e8..532c3e82d3d 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -3582,7 +3582,7 @@ YieldProcessor() __asm__ __volatile__( "rep\n" "nop"); -#elif defined(HOST_ARM64) +#elif defined(HOST_ARM) || defined(HOST_ARM64) __asm__ __volatile__( "yield"); #else return; From 317783916d5695d86b087b45220a28c5d8db9fc6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 28 Jun 2021 12:41:39 -0700 Subject: [PATCH 164/926] Make sure we consider buffer length when marshalling back Unicode ByValTStr fields (#54695) * Use string constructor that takes length instead of the one that searches for a null terminator. Fixes #54662 * Marshal back buffer size or string to first null terminator, whichever is shorter * Add tests. * Add unicode test. * Use the same implementation style for the wstr case case as the cstr case * Fix accidental deletion from test. --- .../src/System/StubHelpers.cs | 11 +++++++++ src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/ilmarshalers.cpp | 7 ++---- .../StringMarshalling/LPTSTR/LPTSTRTest.cs | 18 ++++++++++++++- .../LPTSTR/LPTStrTestNative.cpp | 12 ++++++++++ .../LPTSTR/LPTStrTestNative.cs | 23 +++++++++++++++++++ 6 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index 33435681e9e..54f5764989c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -508,6 +508,17 @@ namespace System.StubHelpers managed.Slice(0, numChars).CopyTo(native); native[numChars] = '\0'; } + + internal static unsafe string ConvertToManaged(IntPtr nativeHome, int length) + { + int end = SpanHelpers.IndexOf(ref *(char*)nativeHome, '\0', length); + if (end != -1) + { + length = end; + } + + return new string((char*)nativeHome, 0, length); + } } // class WSTRBufferMarshaler #if FEATURE_COMINTEROP diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 73ce65fd2d7..8f6afcc3f73 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1069,6 +1069,7 @@ DEFINE_METHOD(CSTRMARSHALER, CLEAR_NATIVE, ClearNative, DEFINE_CLASS(FIXEDWSTRMARSHALER, StubHelpers, FixedWSTRMarshaler) DEFINE_METHOD(FIXEDWSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Str_IntPtr_Int_RetVoid) +DEFINE_METHOD(FIXEDWSTRMARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, SM_IntPtr_Int_RetStr) DEFINE_CLASS(BSTRMARSHALER, StubHelpers, BSTRMarshaler) DEFINE_METHOD(BSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Str_IntPtr_RetIntPtr) diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 163222d423d..8f348468b7a 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -1906,11 +1906,8 @@ void ILFixedWSTRMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmi STANDARD_VM_CONTRACT; EmitLoadNativeHomeAddr(pslILEmit); - pslILEmit->EmitDUP(); - pslILEmit->EmitCALL(METHOD__STRING__WCSLEN, 1, 1); - pslILEmit->EmitCALL(METHOD__STUBHELPERS__CHECK_STRING_LENGTH, 1, 0); - - pslILEmit->EmitNEWOBJ(METHOD__STRING__CTOR_CHARPTR, 1); + pslILEmit->EmitLDC(m_pargs->fs.fixedStringLength); + pslILEmit->EmitCALL(METHOD__FIXEDWSTRMARSHALER__CONVERT_TO_MANAGED, 2, 1); EmitStoreManagedValue(pslILEmit); } diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs b/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs index 8c789d180c6..54054435fed 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTSTRTest.cs @@ -12,6 +12,8 @@ using static LPTStrTestNative; class LPTStrTest { private static readonly string InitialString = "Hello World"; + private static readonly string LongString = "0123456789abcdefghi"; + private static readonly string LongUnicodeString = "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘§ðŸ±â€ðŸ‘¤"; public static int Main() { @@ -58,7 +60,21 @@ class LPTStrTest }; ReverseByValStringUni(ref uniStr); - Assert.AreEqual(Helpers.Reverse(InitialString), uniStr.str); + + ReverseCopyByValStringAnsi(new ByValStringInStructAnsi { str = LongString }, out ByValStringInStructSplitAnsi ansiStrSplit); + + Assert.AreEqual(Helpers.Reverse(LongString[^10..]), ansiStrSplit.str1); + Assert.AreEqual(Helpers.Reverse(LongString[..^10]), ansiStrSplit.str2); + + ReverseCopyByValStringUni(new ByValStringInStructUnicode { str = LongString }, out ByValStringInStructSplitUnicode uniStrSplit); + + Assert.AreEqual(Helpers.Reverse(LongString[^10..]), uniStrSplit.str1); + Assert.AreEqual(Helpers.Reverse(LongString[..^10]), uniStrSplit.str2); + + ReverseCopyByValStringUni(new ByValStringInStructUnicode { str = LongUnicodeString }, out ByValStringInStructSplitUnicode uniStrSplit2); + + Assert.AreEqual(Helpers.Reverse(LongUnicodeString[^10..]), uniStrSplit2.str1); + Assert.AreEqual(Helpers.Reverse(LongUnicodeString[..^10]), uniStrSplit2.str2); } } diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp index 428ce488ddd..c618a5db4fb 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cpp @@ -47,3 +47,15 @@ extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseByValStringUni(ByValStringIn { StringMarshalingTests::ReverseInplace(str->str); } + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseCopyByValStringAnsi(ByValStringInStructAnsi str, ByValStringInStructAnsi* out) +{ + *out = str; + StringMarshalingTests::ReverseInplace(out->str); +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReverseCopyByValStringUni(ByValStringInStructUnicode str, ByValStringInStructUnicode* out) +{ + *out = str; + StringMarshalingTests::ReverseInplace(out->str); +} diff --git a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs index 022c3dc250e..443bc7615f3 100644 --- a/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs +++ b/src/tests/Interop/StringMarshalling/LPTSTR/LPTStrTestNative.cs @@ -14,6 +14,15 @@ class LPTStrTestNative public string str; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ByValStringInStructSplitAnsi + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str2; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct ByValStringInStructUnicode { @@ -21,6 +30,15 @@ class LPTStrTestNative public string str; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct ByValStringInStructSplitUnicode + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] + public string str2; + } + [DllImport(nameof(LPTStrTestNative), CharSet = CharSet.Unicode)] public static extern bool Verify_NullTerminators_PastEnd(StringBuilder builder, int length); @@ -36,4 +54,9 @@ class LPTStrTestNative public static extern void ReverseByValStringAnsi(ref ByValStringInStructAnsi str); [DllImport(nameof(LPTStrTestNative))] public static extern void ReverseByValStringUni(ref ByValStringInStructUnicode str); + + [DllImport(nameof(LPTStrTestNative))] + public static extern void ReverseCopyByValStringAnsi(ByValStringInStructAnsi str, out ByValStringInStructSplitAnsi strOut); + [DllImport(nameof(LPTStrTestNative))] + public static extern void ReverseCopyByValStringUni(ByValStringInStructUnicode str, out ByValStringInStructSplitUnicode strOut); } From 314418846a25ce94d003b9a32d60daa3a2c020c0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 28 Jun 2021 13:10:56 -0700 Subject: [PATCH 165/926] Faster type load for scenarios made more common by generic math (#54588) Change interface map layout in two interesting ways 1. For interface maps defined in System.Private.CoreLib, rely on the C# compiler to prevent any ambiguous duplicates, and to find the full interface expansion, instead of expanding it within the type loader See code marked with #SpecialCorelibInterfaceExpansionAlgorithm - Note that this optimization is only applied for ValueTypes as the presence of inheritance makes the optimization much more complex in many cases, and isn't needed. - This optimization reduces the amount of parsing of the interface implementation table must be done for valuetypes in CoreLib. In particular, with the new interfaces that are added as part of #54650 there is a very deep interface hierarchy that requires a great deal of scanning. As those interfaces are added to all the primitive types, the impact on startup performance is significant and measurable. 2. For interface map expansion that follows the curiously recurring generic pattern, place the open instantiation of the type in the interface map instead of the the exact instantiation, and update all places in the runtime which consider the interface map to deal with that change (Mostly by adding special purpose logic to work with the special marker type in the interface map, but there is also logic to simply force the exact interface type to be loaded, when working with the partially loaded type is not quite good enough, or excessively complex) - This optimization reduces the set of interface types that need to be loaded if they are unused. Of particular benefit are the numerous interfaces associated with the primitive types that are added as part of #54650. Performance of launching an extremely simple .NET process (process with empty main method). Results acquired using local testing on my developer machine, using a simple script that launches the process 500 times in a row. | Before #54650 | After #54650 | After #54650 and #54588 (this pr) | | :-: | :-: | :-: | | 14.1ms | 16.5ms |14.3ms | --- .../RuntimeHelpers.CoreCLR.cs | 3 +- src/coreclr/vm/amd64/asmconstants.h | 4 - src/coreclr/vm/class.cpp | 3 +- src/coreclr/vm/classcompat.cpp | 6 +- src/coreclr/vm/clsload.cpp | 7 +- src/coreclr/vm/clsload.hpp | 3 +- src/coreclr/vm/comcallablewrapper.cpp | 8 +- src/coreclr/vm/cominterfacemarshaler.cpp | 2 +- src/coreclr/vm/compile.cpp | 4 +- src/coreclr/vm/generics.cpp | 6 +- src/coreclr/vm/jitinterface.cpp | 7 +- src/coreclr/vm/marshalnative.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 103 +++++++--- src/coreclr/vm/methodtable.h | 79 +++++++- src/coreclr/vm/methodtable.inl | 29 +++ src/coreclr/vm/methodtablebuilder.cpp | 176 +++++++++++++----- src/coreclr/vm/methodtablebuilder.h | 9 +- src/coreclr/vm/runtimecallablewrapper.cpp | 6 +- src/coreclr/vm/runtimehandles.cpp | 2 +- src/coreclr/vm/siginfo.cpp | 38 ++-- src/coreclr/vm/siginfo.hpp | 3 +- src/coreclr/vm/typehandle.cpp | 2 +- src/coreclr/vm/typehandle.h | 10 + 23 files changed, 395 insertions(+), 117 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 6e3d8b57f50..5cd77f67d6f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -408,7 +408,8 @@ namespace System.Runtime.CompilerServices private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array | 0x40000000 // enum_flag_ComObject | 0x00400000 // enum_flag_ICastable; - | 0x00200000;// enum_flag_IDynamicInterfaceCastable; + | 0x00200000 // enum_flag_IDynamicInterfaceCastable; + | 0x00040000; // enum_flag_Category_ValueType private const int DebugClassNamePtr = // adjust for debug_m_szClassName #if DEBUG diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 50a14d10c47..4cc6ab1de2a 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -203,10 +203,6 @@ ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS #define METHODTABLE_EQUIVALENCE_FLAGS 0x0 #endif -#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS (0x00080000 + 0x40000000 + 0x00400000 + 0x00200000) -ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS - == MethodTable::enum_flag_NonTrivialInterfaceCast); - #define MethodTable__enum_flag_ContainsPointers 0x01000000 ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers == MethodTable::enum_flag_ContainsPointers); diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 7fdf25133c1..02feec829a7 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -1903,7 +1903,8 @@ TypeHandle MethodTable::GetDefItfForComClassItf() InterfaceMapIterator it = IterateInterfaceMap(); if (it.Next()) { - return TypeHandle(it.GetInterface()); + // Can use GetInterfaceApprox, as there are no generic default interfaces + return TypeHandle(it.GetInterfaceApprox()); } else { diff --git a/src/coreclr/vm/classcompat.cpp b/src/coreclr/vm/classcompat.cpp index 1870e6f3dd0..ffd5b0fa983 100644 --- a/src/coreclr/vm/classcompat.cpp +++ b/src/coreclr/vm/classcompat.cpp @@ -1241,7 +1241,11 @@ VOID MethodTableBuilder::BuildInteropVTable_ExpandInterface(InterfaceInfo_t *pIn if (pNewInterface->GetNumInterfaces() != 0) { MethodTable::InterfaceMapIterator it = pNewInterface->IterateInterfaceMap(); while (it.Next()) { - BuildInteropVTable_ExpandInterface(pInterfaceMap, it.GetInterface(), + MethodTable *pItf = it.GetInterfaceApprox(); + if (pItf->HasInstantiation() || pItf->IsSpecialMarkerTypeForGenericCasting()) + continue; + + BuildInteropVTable_ExpandInterface(pInterfaceMap, pItf, pwInterfaceListSize, pdwMaxInterfaceMethods, FALSE); } } diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 62eccc0995f..77a1a7c215c 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -2281,7 +2281,8 @@ TypeHandle ClassLoader::LoadTypeDefOrRefOrSpecThrowing(Module *pModule, LoadTypesFlag fLoadTypes/*=LoadTypes*/ , ClassLoadLevel level /* = CLASS_LOADED */, BOOL dropGenericArgumentLevel /* = FALSE */, - const Substitution *pSubst) + const Substitution *pSubst, + MethodTable *pMTInterfaceMapOwner) { CONTRACT(TypeHandle) { @@ -2315,7 +2316,7 @@ TypeHandle ClassLoader::LoadTypeDefOrRefOrSpecThrowing(Module *pModule, } SigPointer sigptr(pSig, cSig); TypeHandle typeHnd = sigptr.GetTypeHandleThrowing(pModule, pTypeContext, fLoadTypes, - level, dropGenericArgumentLevel, pSubst); + level, dropGenericArgumentLevel, pSubst, (const ZapSig::Context *)0, pMTInterfaceMapOwner); #ifndef DACCESS_COMPILE if ((fNotFoundAction == ThrowIfNotFound) && typeHnd.IsNull()) pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDefOrRefOrSpec, @@ -5294,7 +5295,7 @@ BOOL ClassLoader::CanAccessFamily( while (it.Next()) { // We only loosely check if they are of the same generic type - if (it.GetInterface()->HasSameTypeDefAs(pTargetClass)) + if (it.HasSameTypeDefAs(pTargetClass)) return TRUE; } } diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index b947d4a65bf..195fb4f62cd 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -680,7 +680,8 @@ public: LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED, BOOL dropGenericArgumentLevel = FALSE, - const Substitution *pSubst = NULL /* substitution to apply if the token is a type spec with generic variables */ ); + const Substitution *pSubst = NULL /* substitution to apply if the token is a type spec with generic variables */, + MethodTable *pMTInterfaceMapOwner = NULL); // Load constructed types by providing their constituents static TypeHandle LoadPointerOrByrefTypeThrowing(CorElementType typ, diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index dfee278b891..8b95dac8cdd 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -2520,12 +2520,14 @@ static IUnknown * GetComIPFromCCW_HandleExtendsCOMObject( MethodTable::InterfaceMapIterator intIt = pMT->IterateInterfaceMapFrom(intfIndex); // If the number of slots is 0, then no need to proceed - if (intIt.GetInterface()->GetNumVirtuals() != 0) + MethodTable* pItf = intIt.GetInterfaceApprox(); + if (pItf->GetNumVirtuals() != 0) { MethodDesc *pClsMD = NULL; + _ASSERTE(!pItf->HasInstantiation()); // Find the implementation for the first slot of the interface - DispatchSlot impl(pMT->FindDispatchSlot(intIt.GetInterface()->GetTypeID(), 0, FALSE /* throwOnConflict */)); + DispatchSlot impl(pMT->FindDispatchSlot(pItf->GetTypeID(), 0, FALSE /* throwOnConflict */)); CONSISTENCY_CHECK(!impl.IsNull()); // Get the MethodDesc for this slot in the class @@ -3991,7 +3993,7 @@ ComCallWrapperTemplate::CCWInterfaceMapIterator::CCWInterfaceMapIterator(TypeHan MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap(); while (it.Next()) { - MethodTable *pItfMT = it.GetInterface(); + MethodTable *pItfMT = it.GetInterface(pMT); AppendInterface(pItfMT); } diff --git a/src/coreclr/vm/cominterfacemarshaler.cpp b/src/coreclr/vm/cominterfacemarshaler.cpp index 13b29066d8c..1bee6fa1f18 100644 --- a/src/coreclr/vm/cominterfacemarshaler.cpp +++ b/src/coreclr/vm/cominterfacemarshaler.cpp @@ -425,7 +425,7 @@ VOID COMInterfaceMarshaler::EnsureCOMInterfacesSupported(OBJECTREF oref, MethodT while (it.Next()) { - MethodTable *pItfMT = it.GetInterface(); + MethodTable *pItfMT = it.GetInterfaceApprox(); if (!pItfMT) COMPlusThrow(kInvalidCastException, IDS_EE_CANNOT_COERCE_COMOBJECT); diff --git a/src/coreclr/vm/compile.cpp b/src/coreclr/vm/compile.cpp index 486844c7f79..b36840a3f43 100644 --- a/src/coreclr/vm/compile.cpp +++ b/src/coreclr/vm/compile.cpp @@ -5235,7 +5235,7 @@ void CEEPreloader::ExpandTypeDependencies(TypeHandle th) MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap(); while (intIterator.Next()) { - TriageTypeForZap(intIterator.GetInterface(), TRUE); + TriageTypeForZap(intIterator.GetInterfaceApprox(), TRUE); } // Make sure approx types for all fields are saved @@ -5423,7 +5423,7 @@ void CEEPreloader::TriageTypeFromSoftBoundModule(TypeHandle th, Module * pSoftBo MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap(); while (intIterator.Next()) { - TriageTypeFromSoftBoundModule(intIterator.GetInterface(), pSoftBoundModule); + TriageTypeFromSoftBoundModule(intIterator.GetInterfaceApprox(), pSoftBoundModule); } // It does not seem worth it to reject the remaining items diff --git a/src/coreclr/vm/generics.cpp b/src/coreclr/vm/generics.cpp index b616b84099c..35f5ebd66d7 100644 --- a/src/coreclr/vm/generics.cpp +++ b/src/coreclr/vm/generics.cpp @@ -705,7 +705,11 @@ BOOL RecursionGraph::CheckForIllegalRecursion() MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap(); while (it.Next()) { - AddDependency(it.GetInterface()); + MethodTable *pItfApprox = it.GetInterfaceApprox(); + if (!pItfApprox->IsTypicalTypeDefinition()) + { + AddDependency(pItfApprox); + } } // Check all owned nodes for expanding cycles. The edges recorded above must all diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8483c1fb1dc..a1e4d93d881 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8973,7 +8973,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) int canonicallyMatchingInterfacesFound = 0; while (it.Next()) { - if (it.GetInterface()->GetCanonicalMethodTable() == pOwnerMT) + if (it.GetInterface(pObjMT)->GetCanonicalMethodTable() == pOwnerMT) { canonicallyMatchingInterfacesFound++; if (canonicallyMatchingInterfacesFound > 1) @@ -14301,9 +14301,10 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, MethodTable::InterfaceMapIterator it = thImpl.GetMethodTable()->IterateInterfaceMap(); while (it.Next()) { - if (pInterfaceTypeCanonical == it.GetInterface()->GetCanonicalMethodTable()) + MethodTable *pItfInMap = it.GetInterface(thImpl.GetMethodTable()); + if (pInterfaceTypeCanonical == pItfInMap->GetCanonicalMethodTable()) { - pDeclMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pDeclMethod, it.GetInterface(), FALSE, pDeclMethod->GetMethodInstantiation(), FALSE, TRUE); + pDeclMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pDeclMethod, pItfInMap, FALSE, pDeclMethod->GetMethodInstantiation(), FALSE, TRUE); break; } } diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index b28f34aa266..59106d70f1f 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -1064,7 +1064,7 @@ FCIMPL2(Object*, MarshalNative::InternalCreateWrapperOfType, Object* objUNSAFE, MethodTable::InterfaceMapIterator it = pNewWrapMT->IterateInterfaceMap(); while (it.Next()) { - MethodTable *pItfMT = it.GetInterface(); + MethodTable *pItfMT = it.GetInterfaceApprox(); // ComImport interfaces cannot be generic if (pItfMT->IsComImport()) { if (!Object::SupportsInterface(gc.obj, pItfMT)) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a81a0117e6f..3539acaf79f 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -1544,7 +1544,7 @@ BOOL MethodTable::CanCastToInterface(MethodTable *pTargetMT, TypeHandlePairList InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - if (it.GetInterface()->CanCastByVarianceToInterfaceOrDelegate(pTargetMT, pVisited)) + if (it.GetInterfaceApprox()->CanCastByVarianceToInterfaceOrDelegate(pTargetMT, pVisited, this)) return TRUE; } } @@ -1552,7 +1552,7 @@ BOOL MethodTable::CanCastToInterface(MethodTable *pTargetMT, TypeHandlePairList } //========================================================================================== -BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, TypeHandlePairList *pVisited) +BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, TypeHandlePairList *pVisited, MethodTable* pMTInterfaceMapOwner /*= NULL*/) { CONTRACTL { @@ -1573,6 +1573,16 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, return TRUE; } + // Shortcut for generic approx type scenario + if (pMTInterfaceMapOwner != NULL && + IsSpecialMarkerTypeForGenericCasting() && + GetTypeDefRid() == pTargetMT->GetTypeDefRid() && + GetModule() == pTargetMT->GetModule() && + pTargetMT->GetInstantiation().ContainsAllOneType(pMTInterfaceMapOwner)) + { + return TRUE; + } + if (GetTypeDefRid() != pTargetMT->GetTypeDefRid() || GetModule() != pTargetMT->GetModule() || TypeHandlePairList::Exists(pVisited, this, pTargetMT)) { @@ -1591,6 +1601,11 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, for (DWORD i = 0; i < inst.GetNumArgs(); i++) { TypeHandle thArg = inst[i]; + if (IsSpecialMarkerTypeForGenericCasting() && pMTInterfaceMapOwner) + { + thArg = pMTInterfaceMapOwner; + } + TypeHandle thTargetArg = targetInst[i]; // If argument types are not equivalent, test them for compatibility @@ -1998,7 +2013,7 @@ MethodTable::Debug_DumpInterfaceMap( InterfaceMapIterator it(this); while (it.Next()) { - MethodTable *pInterfaceMT = it.GetInterface(); + MethodTable *pInterfaceMT = it.GetInterfaceApprox(); //LF_ALWAYS allowed here because this is controlled by special env var code:EEConfig::ShouldDumpOnClassLoad LOG((LF_ALWAYS, LL_ALWAYS, @@ -4337,7 +4352,7 @@ BOOL MethodTable::ComputeNeedsRestoreWorker(DataImage *image, TypeHandleList *pV InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - if (!image->CanPrerestoreEagerBindToMethodTable(it.GetInterface(), pVisited)) + if (!image->CanPrerestoreEagerBindToMethodTable(it.GetInterface(this), pVisited)) { UPDATE_RESTORE_REASON(InterfaceIsGeneric); return TRUE; @@ -5389,7 +5404,8 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const MethodTable::InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - it.GetInterface()->DoFullyLoad(&locals.newVisited, level, pPending, &locals.fBailed, pInstContext); + MethodTable* pItf = it.GetInterfaceApprox(); + pItf->DoFullyLoad(&locals.newVisited, level, pPending, &locals.fBailed, pInstContext); if (fNeedAccessChecks) { @@ -5399,7 +5415,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const // A transparent type should not be allowed to implement a critical interface. // However since this has never been enforced before we have many classes that // violate this rule. Enforcing it now will be a breaking change. - DoAccessibilityCheck(this, it.GetInterface(), IDS_CLASSLOAD_INTERFACE_NO_ACCESS); + DoAccessibilityCheck(this, pItf, IDS_CLASSLOAD_INTERFACE_NO_ACCESS); } } } @@ -6225,8 +6241,7 @@ MethodTable::FindEncodedMapDispatchEntry( DispatchMapEntry * pCurEntry = it.Entry(); if (pCurEntry->GetSlotNumber() == slotNumber) { - MethodTable * pCurEntryType = LookupDispatchMapType(pCurEntry->GetTypeID()); - if (pCurEntryType == dispatchTokenType) + if (DispatchMapTypeMatchesMethodTable(pCurEntry->GetTypeID(), dispatchTokenType)) { *pEntry = *pCurEntry; return TRUE; @@ -6751,8 +6766,6 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( } CONTRACT_END; #ifdef FEATURE_DEFAULT_INTERFACES - InterfaceMapIterator it = this->IterateInterfaceMap(); - CQuickArray candidates; unsigned candidatesCount = 0; @@ -6795,7 +6808,8 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMapFrom(dwParentInterfaces); while (!it.Finished()) { - MethodTable *pCurMT = it.GetInterface(); + MethodTable *pCurMT = it.GetInterface(pMT); + MethodDesc *pCurMD = NULL; if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD)) { @@ -7033,7 +7047,7 @@ BOOL MethodTable::ImplementsInterfaceWithSameSlotsAsParent(MethodTable *pItfMT, for (; it.IsValid(); it.Next()) { DispatchMapEntry *pCurEntry = it.Entry(); - if (LookupDispatchMapType(pCurEntry->GetTypeID()) == pItfMT) + if (DispatchMapTypeMatchesMethodTable(pCurEntry->GetTypeID(), pItfMT)) { // this class and its parents up to pParentMT must have no mappings for the interface return FALSE; @@ -7083,7 +7097,7 @@ BOOL MethodTable::HasSameInterfaceImplementationAsParent(MethodTable *pItfMT, Me for (; it.IsValid(); it.Next()) { DispatchMapEntry *pCurEntry = it.Entry(); - if (LookupDispatchMapType(pCurEntry->GetTypeID()) == pItfMT) + if (DispatchMapTypeMatchesMethodTable(pCurEntry->GetTypeID(), pItfMT)) { UINT32 ifaceSlot = pCurEntry->GetSlotNumber(); if (!bitMask.TestBit(ifaceSlot)) @@ -7115,6 +7129,7 @@ BOOL MethodTable::HasSameInterfaceImplementationAsParent(MethodTable *pItfMT, Me #endif // !DACCESS_COMPILE //========================================================================================== +#ifndef DACCESS_COMPILE MethodTable * MethodTable::LookupDispatchMapType(DispatchMapTypeID typeID) { CONTRACTL { @@ -7125,7 +7140,21 @@ MethodTable * MethodTable::LookupDispatchMapType(DispatchMapTypeID typeID) _ASSERTE(!typeID.IsThisClass()); InterfaceMapIterator intIt = IterateInterfaceMapFrom(typeID.GetInterfaceNum()); - return intIt.GetInterface(); + return intIt.GetInterface(this); +} +#endif // DACCESS_COMPILE + +//========================================================================================== +bool MethodTable::DispatchMapTypeMatchesMethodTable(DispatchMapTypeID typeID, MethodTable* pMT) +{ + CONTRACTL { + WRAPPER(THROWS); + GC_TRIGGERS; + } CONTRACTL_END; + + _ASSERTE(!typeID.IsThisClass()); + InterfaceMapIterator intIt = IterateInterfaceMapFrom(typeID.GetInterfaceNum()); + return intIt.CurrentInterfaceMatches(this, pMT); } //========================================================================================== @@ -8311,7 +8340,7 @@ MethodTable::MethodDataInterfaceImpl::PopulateNextLevel() MethodTable::InterfaceMapIterator it = m_pImpl->GetImplMethodTable()->IterateInterfaceMap(); while (it.Next()) { - if (pDeclMT == it.GetInterface()) + if (it.CurrentInterfaceMatches(m_pImpl->GetImplMethodTable(), pDeclMT)) { // We found the interface INDEBUG(dbg_fInterfaceFound = TRUE); DispatchMapTypeID declTypeID = DispatchMapTypeID::InterfaceClassID(it.GetIndex()); @@ -9212,7 +9241,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - if (pInterfaceTypeCanonical == it.GetInterface()) + if (it.CurrentInterfaceMatches(this, pInterfaceTypeCanonical)) { canonicalEquivalentFound = true; break; @@ -9238,13 +9267,13 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* MethodTable::InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - if (it.GetInterface() == pInterfaceType) + if (it.CurrentInterfaceMatches(this, pInterfaceType)) { // This is the variant interface check logic, skip the continue; } - if (!it.GetInterface()->HasSameTypeDefAs(pInterfaceType)) + if (!it.HasSameTypeDefAs(pInterfaceType)) { // Variance matches require a typedef match // Equivalence isn't sufficient, and is uninteresting as equivalent interfaces cannot have static virtuals. @@ -9253,22 +9282,24 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* BOOL equivalentOrVariantCompatible; + MethodTable *pItfInMap = it.GetInterface(this, CLASS_LOAD_EXACTPARENTS); + if (allowVariantMatches) { - equivalentOrVariantCompatible = it.GetInterface()->CanCastTo(pInterfaceType, NULL); + equivalentOrVariantCompatible = pItfInMap->CanCastTo(pInterfaceType, NULL); } else { // When performing override checking to ensure that a concrete type is valid, require the implementation // actually implement the exact or equivalent interface. - equivalentOrVariantCompatible = it.GetInterface()->IsEquivalentTo(pInterfaceType); + equivalentOrVariantCompatible = pItfInMap->IsEquivalentTo(pInterfaceType); } if (equivalentOrVariantCompatible) { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, verifyImplemented); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -9443,9 +9474,10 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() InterfaceMapIterator it = IterateInterfaceMap(); while (it.Next()) { - MethodTable *pInterfaceMT = it.GetInterface(); + MethodTable *pInterfaceMT = it.GetInterfaceApprox(); if (pInterfaceMT->HasVirtualStaticMethods()) { + pInterfaceMT = it.GetInterface(this, CLASS_LOAD_EXACTPARENTS); for (MethodIterator it(pInterfaceMT); it.IsValid(); it.Next()) { MethodDesc *pMD = it.GetMethodDesc(); @@ -9522,7 +9554,7 @@ MethodTable::TryResolveConstraintMethodApprox( DWORD cPotentialMatchingInterfaces = 0; while (it.Next()) { - TypeHandle thPotentialInterfaceType(it.GetInterface()); + TypeHandle thPotentialInterfaceType(it.GetInterface(pCanonMT)); if (thPotentialInterfaceType.AsMethodTable()->GetCanonicalMethodTable() == thInterfaceType.AsMethodTable()->GetCanonicalMethodTable()) { @@ -9773,6 +9805,31 @@ EEClassNativeLayoutInfo const* MethodTable::EnsureNativeLayoutInfoInitialized() #endif } +#ifndef DACCESS_COMPILE +PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMTOwner, ClassLoadLevel loadLevel /*= CLASS_LOADED*/) +{ + CONTRACT(PTR_MethodTable) + { + GC_TRIGGERS; + THROWS; + PRECONDITION(m_i != (DWORD) -1 && m_i < m_count); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + MethodTable *pResult = m_pMap->GetMethodTable(); + if (pResult->IsSpecialMarkerTypeForGenericCasting()) + { + TypeHandle ownerAsInst(pMTOwner); + Instantiation inst(&ownerAsInst, 1); + pResult = ClassLoader::LoadGenericInstantiationThrowing(pResult->GetModule(), pResult->GetCl(), inst, ClassLoader::LoadTypes, loadLevel).AsMethodTable(); + if (pResult->IsFullyLoaded()) + SetInterface(pResult); + } + RETURN (pResult); +} +#endif // DACCESS_COMPILE + #ifdef FEATURE_READYTORUN_COMPILER static BOOL ComputeIsLayoutFixedInCurrentVersionBubble(MethodTable * pMT) diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 13ff58836fe..3fbc6826ed2 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -1240,6 +1240,20 @@ public: BOOL ContainsGenericMethodVariables(); + // When creating an interface map, under some circumstances the + // runtime will place the special marker type in the interface map instead + // of the fully loaded type. This is to reduce the amount of type loading + // performed at process startup. + // + // The current rule is that these interfaces can only appear + // on valuetypes that are not shared generic, and that the special + // marker type is the open generic type. + // + inline bool IsSpecialMarkerTypeForGenericCasting() + { + return IsGenericTypeDefinition(); + } + static BOOL ComputeContainsGenericVariables(Instantiation inst); inline void SetContainsGenericVariables() @@ -1913,7 +1927,7 @@ public: BOOL ArraySupportsBizarreInterface(MethodTable* pInterfaceMT, TypeHandlePairList* pVisited); BOOL ArrayIsInstanceOf(MethodTable* pTargetMT, TypeHandlePairList* pVisited); - BOOL CanCastByVarianceToInterfaceOrDelegate(MethodTable* pTargetMT, TypeHandlePairList* pVisited); + BOOL CanCastByVarianceToInterfaceOrDelegate(MethodTable* pTargetMT, TypeHandlePairList* pVisited, MethodTable* pMTInterfaceMapOwner = NULL); // The inline part of equivalence check. #ifndef DACCESS_COMPILE @@ -2171,8 +2185,16 @@ public: return (m_i == m_count); } - // Get the interface at the current position - inline PTR_MethodTable GetInterface() +#ifndef DACCESS_COMPILE + // Get the interface at the current position. This GetInterfaceMethod + // will ensure that the exact correct instantiation of the interface + // is found, even if the MethodTable in the interface map is the generic + // approximation + PTR_MethodTable GetInterface(MethodTable* pMTOwner, ClassLoadLevel loadLevel = CLASS_LOADED); +#endif + + // Get the interface at the current position, with whatever its normal load level is + inline PTR_MethodTable GetInterfaceApprox() { CONTRACT(PTR_MethodTable) { @@ -2187,6 +2209,53 @@ public: RETURN (m_pMap->GetMethodTable()); } + inline bool CurrentInterfaceMatches(MethodTable* pMTOwner, MethodTable* pMT) + { + CONTRACT(bool) + { + GC_NOTRIGGER; + NOTHROW; + SUPPORTS_DAC; + PRECONDITION(m_i != (DWORD) -1 && m_i < m_count); + } + CONTRACT_END; + + bool exactMatch = m_pMap->GetMethodTable() == pMT; + if (!exactMatch) + { + if (m_pMap->GetMethodTable()->HasSameTypeDefAs(pMT) && + pMT->HasInstantiation() && + m_pMap->GetMethodTable()->IsSpecialMarkerTypeForGenericCasting() && + pMT->GetInstantiation().ContainsAllOneType(pMTOwner)) + { + exactMatch = true; +#ifndef DACCESS_COMPILE + // We match exactly, and have an actual pMT loaded. Insert + // the searched for interface if it is fully loaded, so that + // future checks are more efficient + if (pMT->IsFullyLoaded()) + SetInterface(pMT); +#endif + } + } + + RETURN (exactMatch); + } + + inline bool HasSameTypeDefAs(MethodTable* pMT) + { + CONTRACT(bool) + { + GC_NOTRIGGER; + NOTHROW; + SUPPORTS_DAC; + PRECONDITION(m_i != (DWORD) -1 && m_i < m_count); + } + CONTRACT_END; + + RETURN (m_pMap->GetMethodTable()->HasSameTypeDefAs(pMT)); + } + #ifndef DACCESS_COMPILE void SetInterface(MethodTable *pMT) { @@ -2390,7 +2459,10 @@ public: UINT32 GetTypeID(); + // Will return either the dispatch map type. May trigger type loader in order to get + // exact result. MethodTable *LookupDispatchMapType(DispatchMapTypeID typeID); + bool DispatchMapTypeMatchesMethodTable(DispatchMapTypeID typeID, MethodTable* pMT); MethodDesc *GetIntroducingMethodDesc(DWORD slotNumber); @@ -3663,6 +3735,7 @@ private: | enum_flag_ComObject | enum_flag_ICastable | enum_flag_IDynamicInterfaceCastable + | enum_flag_Category_ValueType }; // enum WFLAGS_HIGH_ENUM diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 88ebe105bda..688fc2630bf 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1570,6 +1570,35 @@ FORCEINLINE BOOL MethodTable::ImplementsInterfaceInline(MethodTable *pInterface) } while (--numInterfaces); + // Second scan, looking for the curiously recurring generic scenario + if (pInterface->HasInstantiation() && pInterface->GetInstantiation().ContainsAllOneType(this)) + { + numInterfaces = GetNumInterfaces(); + pInfo = GetInterfaceMap(); + + do + { + if (pInfo->GetMethodTable()->HasSameTypeDefAs(pInterface) && pInfo->GetMethodTable()->IsSpecialMarkerTypeForGenericCasting()) + { + // Extensible RCW's need to be handled specially because they can have interfaces + // in their map that are added at runtime. These interfaces will have a start offset + // of -1 to indicate this. We cannot take for granted that every instance of this + // COM object has this interface so FindInterface on these interfaces is made to fail. + // + // However, we are only considering the statically available slots here + // (m_wNumInterface doesn't contain the dynamic slots), so we can safely + // ignore this detail. +#ifndef DACCESS_COMPILE + if (pInterface->IsFullyLoaded()) + pInfo->SetMethodTable(pInterface); +#endif + return TRUE; + } + pInfo++; + } + while (--numInterfaces); + } + return FALSE; } diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 9ccb3d78f86..0d372a4659b 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -412,15 +412,22 @@ MethodTableBuilder::ExpandApproxInterface( bmtInterface->dwInterfaceMapSize++; - // Make sure to pass in the substitution from the new itf type created above as - // these methods assume that substitutions are allocated in the stacking heap, - // not the stack. - InterfaceDeclarationScope declaredItfScope(declScope.fIsInterfaceDeclaredOnParent, false); - ExpandApproxDeclaredInterfaces( - bmtInterface, - bmtTypeHandle(pNewItfType), - declaredItfScope - COMMA_INDEBUG(dbg_pClassMT)); + // Checking for further expanded interfaces isn't necessary for the system module, as we can rely on the C# compiler + // to have found all of the interfaces that the type implements, and to place them in the interface list itself. Also + // we can assume no ambiguous interfaces + // Code related to this is marked with #SpecialCorelibInterfaceExpansionAlgorithm + if (!(GetModule()->IsSystem() && IsValueClass())) + { + // Make sure to pass in the substitution from the new itf type created above as + // these methods assume that substitutions are allocated in the stacking heap, + // not the stack. + InterfaceDeclarationScope declaredItfScope(declScope.fIsInterfaceDeclaredOnParent, false); + ExpandApproxDeclaredInterfaces( + bmtInterface, + bmtTypeHandle(pNewItfType), + declaredItfScope + COMMA_INDEBUG(dbg_pClassMT)); + } } // MethodTableBuilder::ExpandApproxInterface //******************************************************************************* @@ -9022,6 +9029,18 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp } } // MethodTableBuilder::CopyExactParentSlots +bool InstantiationIsAllTypeVariables(const Instantiation &inst) +{ + for (auto i = inst.GetNumArgs(); i > 0;) + { + TypeHandle th = inst[--i]; + if (!th.IsGenericVariable()) + return false; + } + + return true; +} + //******************************************************************************* /* static */ void @@ -9038,7 +9057,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap(); while (it.Next()) { - if (it.GetInterface()->HasInstantiation()) + if (it.GetInterfaceApprox()->HasInstantiation()) { hasInstantiatedInterfaces = TRUE; break; @@ -9080,41 +9099,89 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) // (e) If duplicates found then use the slow metadata-based technique code:#LoadExactInterfaceMap_Algorithm2 DWORD nInterfacesCount = pMT->GetNumInterfaces(); MethodTable **pExactMTs = (MethodTable**) _alloca(sizeof(MethodTable *) * nInterfacesCount); + BOOL duplicates; + bool retry = false; + bool retryWithExactInterfaces = !pMT->IsValueType() || pMT->IsSharedByGenericInstantiations(); // Always use exact loading behavior with classes or shared generics, as they have to deal with inheritance, and the + // inexact matching logic for classes would be more complex to write. + DWORD nAssigned = 0; - BOOL duplicates = false; - if (pParentMT != NULL) + do { - MethodTable::InterfaceMapIterator parentIt = pParentMT->IterateInterfaceMap(); - while (parentIt.Next()) + nAssigned = 0; + retry = false; + duplicates = false; + if (pParentMT != NULL) { - duplicates |= InsertMethodTable(parentIt.GetInterface(), pExactMTs, nInterfacesCount, &nAssigned); + MethodTable::InterfaceMapIterator parentIt = pParentMT->IterateInterfaceMap(); + while (parentIt.Next()) + { + duplicates |= InsertMethodTable(parentIt.GetInterface(pParentMT, CLASS_LOAD_EXACTPARENTS), pExactMTs, nInterfacesCount, &nAssigned); + } } - } - InterfaceImplEnum ie(pMT->GetModule(), pMT->GetCl(), NULL); - while ((hr = ie.Next()) == S_OK) - { - MethodTable *pNewIntfMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(pMT->GetModule(), - ie.CurrentToken(), - &typeContext, - ClassLoader::ThrowIfNotFound, - ClassLoader::FailIfUninstDefOrRef, - ClassLoader::LoadTypes, - CLASS_LOAD_EXACTPARENTS, - TRUE).GetMethodTable(); + InterfaceImplEnum ie(pMT->GetModule(), pMT->GetCl(), NULL); + while ((hr = ie.Next()) == S_OK) + { + MethodTable *pNewIntfMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(pMT->GetModule(), + ie.CurrentToken(), + &typeContext, + ClassLoader::ThrowIfNotFound, + ClassLoader::FailIfUninstDefOrRef, + ClassLoader::LoadTypes, + CLASS_LOAD_EXACTPARENTS, + TRUE, + (const Substitution*)0, + retryWithExactInterfaces ? NULL : pMT).GetMethodTable(); - duplicates |= InsertMethodTable(pNewIntfMT, pExactMTs, nInterfacesCount, &nAssigned); - MethodTable::InterfaceMapIterator intIt = pNewIntfMT->IterateInterfaceMap(); - while (intIt.Next()) - { - duplicates |= InsertMethodTable(intIt.GetInterface(), pExactMTs, nInterfacesCount, &nAssigned); + bool uninstGenericCase = pNewIntfMT->IsSpecialMarkerTypeForGenericCasting(); + + duplicates |= InsertMethodTable(pNewIntfMT, pExactMTs, nInterfacesCount, &nAssigned); + + // We have a special algorithm for interface maps in CoreLib, which doesn't expand interfaces, and assumes no ambiguous + // duplicates. Code related to this is marked with #SpecialCorelibInterfaceExpansionAlgorithm + if (!(pMT->GetModule()->IsSystem() && pMT->IsValueType())) + { + MethodTable::InterfaceMapIterator intIt = pNewIntfMT->IterateInterfaceMap(); + while (intIt.Next()) + { + MethodTable *pItfPossiblyApprox = intIt.GetInterfaceApprox(); + if (uninstGenericCase && pItfPossiblyApprox->HasInstantiation() && pItfPossiblyApprox->ContainsGenericVariables()) + { + // We allow a limited set of interface generic shapes with type variables. In particular, we require the + // instantiations to be exactly simple type variables + if (InstantiationIsAllTypeVariables(pItfPossiblyApprox->GetInstantiation())) + { + pItfPossiblyApprox = ClassLoader::LoadTypeDefThrowing(pItfPossiblyApprox->GetModule(), pItfPossiblyApprox->GetCl(), ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_EXACTPARENTS).AsMethodTable(); + } + else + { + retry = true; + break; + } + } + duplicates |= InsertMethodTable(intIt.GetInterface(pNewIntfMT, CLASS_LOAD_EXACTPARENTS), pExactMTs, nInterfacesCount, &nAssigned); + } + } + + if (retry) + break; } - } + + if (retry) + { + retryWithExactInterfaces = true; + } + } while (retry); + if (FAILED(hr)) { pMT->GetAssembly()->ThrowTypeLoadException(pMT->GetMDImport(), pMT->GetCl(), IDS_CLASSLOAD_BADFORMAT); } #ifdef _DEBUG - duplicates |= CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout); + if (!pMT->GetModule()->IsSystem()) + { + + duplicates |= CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout); + } //#InjectInterfaceDuplicates_LoadExactInterfaceMap // If we are injecting duplicates also for non-generic interfaces in check builds, we have to use @@ -9122,6 +9189,10 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) // Has to be in sync with code:#InjectInterfaceDuplicates_Main. duplicates |= pMT->Debug_HasInjectedInterfaceDuplicates(); #endif + // We have a special algorithm for interface maps in CoreLib, which doesn't expand interfaces, and assumes no ambiguous + // duplicates. Code related to this is marked with #SpecialCorelibInterfaceExpansionAlgorithm + _ASSERTE(!duplicates || !(pMT->GetModule()->IsSystem() && pMT->IsValueType())); + CONSISTENCY_CHECK(duplicates || (nAssigned == pMT->GetNumInterfaces())); if (duplicates) { @@ -9181,7 +9252,8 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) pParentMT, pParentSubstForTypeLoad, pParentSubstForComparing, - pStackingAllocator); + pStackingAllocator, + retryWithExactInterfaces ? NULL : pMT); } #ifdef _DEBUG //#ExactInterfaceMap_SupersetOfParent @@ -9204,7 +9276,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) } else { // It is not canonical instantiation, we can compare MethodTables - _ASSERTE(parentInterfacesIterator.GetInterface() == bmtExactInterface.pExactMTs[nInterfaceIndex]); + _ASSERTE(parentInterfacesIterator.GetInterfaceApprox() == bmtExactInterface.pExactMTs[nInterfaceIndex]); } nInterfaceIndex++; } @@ -9237,7 +9309,8 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) pMT->GetCl(), NULL, NULL, - pStackingAllocator + pStackingAllocator, + retryWithExactInterfaces ? NULL : pMT COMMA_INDEBUG(pMT)); CONSISTENCY_CHECK(bmtExactInterface.nAssigned == pMT->GetNumInterfaces()); @@ -9405,9 +9478,8 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) while (thisIt.Next()) { #ifdef _DEBUG - MethodTable*pOldMT = thisIt.GetInterface(); MethodTable *pNewMT = pExactMTs[i]; - CONSISTENCY_CHECK(pOldMT->HasSameTypeDefAs(pNewMT)); + CONSISTENCY_CHECK(thisIt.HasSameTypeDefAs(pNewMT)); #endif // _DEBUG thisIt.SetInterface(pExactMTs[i]); i++; @@ -9422,7 +9494,8 @@ MethodTableBuilder::ExpandExactInheritedInterfaces( MethodTable * pMT, const Substitution * pSubstForTypeLoad, Substitution * pSubstForComparing, - StackingAllocator * pStackingAllocator) + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner) { STANDARD_VM_CONTRACT; @@ -9449,7 +9522,8 @@ MethodTableBuilder::ExpandExactInheritedInterfaces( pParentMT, pParentSubstForTypeLoad, pParentSubstForComparing, - pStackingAllocator); + pStackingAllocator, + pMTInterfaceMapOwner); } ExpandExactDeclaredInterfaces( bmtInfo, @@ -9457,7 +9531,8 @@ MethodTableBuilder::ExpandExactInheritedInterfaces( pMT->GetCl(), pSubstForTypeLoad, pSubstForComparing, - pStackingAllocator + pStackingAllocator, + pMTInterfaceMapOwner COMMA_INDEBUG(pMT)); // Restore type's subsitution chain for comparing interfaces @@ -9473,7 +9548,8 @@ MethodTableBuilder::ExpandExactDeclaredInterfaces( mdToken typeDef, const Substitution * pSubstForTypeLoad, Substitution * pSubstForComparing, - StackingAllocator * pStackingAllocator + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner COMMA_INDEBUG(MethodTable * dbg_pClassMT)) { STANDARD_VM_CONTRACT; @@ -9491,7 +9567,8 @@ MethodTableBuilder::ExpandExactDeclaredInterfaces( ClassLoader::LoadTypes, CLASS_LOAD_EXACTPARENTS, TRUE, - pSubstForTypeLoad).GetMethodTable(); + pSubstForTypeLoad, + pMTInterfaceMapOwner).GetMethodTable(); Substitution ifaceSubstForTypeLoad(ie.CurrentToken(), pModule, pSubstForTypeLoad); Substitution ifaceSubstForComparing(ie.CurrentToken(), pModule, pSubstForComparing); @@ -9500,7 +9577,8 @@ MethodTableBuilder::ExpandExactDeclaredInterfaces( pInterface, &ifaceSubstForTypeLoad, &ifaceSubstForComparing, - pStackingAllocator + pStackingAllocator, + pMTInterfaceMapOwner COMMA_INDEBUG(dbg_pClassMT)); } if (FAILED(hr)) @@ -9516,7 +9594,8 @@ MethodTableBuilder::ExpandExactInterface( MethodTable * pIntf, const Substitution * pSubstForTypeLoad_OnStack, // Allocated on stack! const Substitution * pSubstForComparing_OnStack, // Allocated on stack! - StackingAllocator * pStackingAllocator + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner COMMA_INDEBUG(MethodTable * dbg_pClassMT)) { STANDARD_VM_CONTRACT; @@ -9563,7 +9642,8 @@ MethodTableBuilder::ExpandExactInterface( pIntf->GetCl(), pSubstForTypeLoad, &bmtInfo->pInterfaceSubstitution[n], - pStackingAllocator + pStackingAllocator, + pMTInterfaceMapOwner COMMA_INDEBUG(dbg_pClassMT)); } // MethodTableBuilder::ExpandExactInterface @@ -10716,7 +10796,7 @@ MethodTableBuilder::SetupMethodTable2( MethodTable::InterfaceMapIterator intIt = pMT->IterateInterfaceMap(); while (intIt.Next()) { - MethodTable* pIntfMT = intIt.GetInterface(); + MethodTable* pIntfMT = intIt.GetInterface(pMT, pMT->GetLoadLevel()); if (pIntfMT->GetNumVirtuals() != 0) { BOOL hasComImportMethod = FALSE; diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 63cc9c856c1..e3b861293a3 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -2454,7 +2454,8 @@ private: MethodTable * pIntf, const Substitution * pSubstForTypeLoad_OnStack, // Allocated on stack! const Substitution * pSubstForComparing_OnStack, // Allocated on stack! - StackingAllocator * pStackingAllocator + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner COMMA_INDEBUG(MethodTable * dbg_pClassMT)); public: @@ -2465,7 +2466,8 @@ public: mdToken typeDef, const Substitution * pSubstForTypeLoad, Substitution * pSubstForComparing, - StackingAllocator * pStackingAllocator + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner COMMA_INDEBUG(MethodTable * dbg_pClassMT)); static void @@ -2474,7 +2476,8 @@ public: MethodTable * pParentMT, const Substitution * pSubstForTypeLoad, Substitution * pSubstForComparing, - StackingAllocator * pStackingAllocator); + StackingAllocator * pStackingAllocator, + MethodTable * pMTInterfaceMapOwner); public: // -------------------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index 17f58b0f80b..5f961840743 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -2548,7 +2548,11 @@ BOOL ComObject::SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable) MethodTable::InterfaceMapIterator it = pIntfTable->IterateInterfaceMap(); while (it.Next()) { - bSupportsItf = Object::SupportsInterface(oref, it.GetInterface()); + MethodTable *pItf = it.GetInterfaceApprox(); + if (pItf->HasInstantiation() || pItf->IsGenericTypeDefinition()) + continue; + + bSupportsItf = Object::SupportsInterface(oref, pItf); if (!bSupportsItf) break; } diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 736f36758ee..62d27295861 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -906,7 +906,7 @@ FCIMPL1(PtrArray*, RuntimeTypeHandle::GetInterfaces, ReflectClassBaseObject *pTy MethodTable::InterfaceMapIterator it = typeHandle.GetMethodTable()->IterateInterfaceMap(); while (it.Next()) { - OBJECTREF refInterface = it.GetInterface()->GetManagedClassObject(); + OBJECTREF refInterface = it.GetInterface(typeHandle.GetMethodTable())->GetManagedClassObject(); refRetVal->SetAt(i, refInterface); _ASSERTE(refRetVal->GetAt(i) != NULL); i++; diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index ff958846b8b..2d0889c5324 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -989,7 +989,8 @@ TypeHandle SigPointer::GetTypeHandleThrowing( BOOL dropGenericArgumentLevel/*=FALSE*/, const Substitution * pSubst/*=NULL*/, // ZapSigContext is only set when decoding zapsigs - const ZapSig::Context * pZapSigContext) const + const ZapSig::Context * pZapSigContext, + MethodTable * pMTInterfaceMapOwner) const { CONTRACT(TypeHandle) { @@ -1414,20 +1415,29 @@ TypeHandle SigPointer::GetTypeHandleThrowing( break; } - // Group together the current signature type context and substitution chain, which - // we may later use to instantiate constraints of type arguments that turn out to be - // typespecs, i.e. generic types. - InstantiationContext instContext(pTypeContext, pSubst); + Instantiation genericLoadInst(thisinst, ntypars); - // Now make the instantiated type - // The class loader will check the arity - // When we know it was correctly computed at NGen time, we ask the class loader to skip that check. - thRet = (ClassLoader::LoadGenericInstantiationThrowing(pGenericTypeModule, - tkGenericType, - Instantiation(thisinst, ntypars), - fLoadTypes, level, - &instContext, - pZapSigContext && pZapSigContext->externalTokens == ZapSig::NormalTokens)); + if (pMTInterfaceMapOwner != NULL && genericLoadInst.ContainsAllOneType(pMTInterfaceMapOwner)) + { + thRet = ClassLoader::LoadTypeDefThrowing(pGenericTypeModule, tkGenericType, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, level); + } + else + { + // Group together the current signature type context and substitution chain, which + // we may later use to instantiate constraints of type arguments that turn out to be + // typespecs, i.e. generic types. + InstantiationContext instContext(pTypeContext, pSubst); + + // Now make the instantiated type + // The class loader will check the arity + // When we know it was correctly computed at NGen time, we ask the class loader to skip that check. + thRet = (ClassLoader::LoadGenericInstantiationThrowing(pGenericTypeModule, + tkGenericType, + genericLoadInst, + fLoadTypes, level, + &instContext, + pZapSigContext && pZapSigContext->externalTokens == ZapSig::NormalTokens)); + } break; } diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index 86809e4d27f..7489cd349f0 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -253,7 +253,8 @@ public: ClassLoadLevel level = CLASS_LOADED, BOOL dropGenericArgumentLevel = FALSE, const Substitution *pSubst = NULL, - const ZapSig::Context *pZapSigContext = NULL) const; + const ZapSig::Context *pZapSigContext = NULL, + MethodTable *pMTInterfaceMapOwner = NULL) const; public: //------------------------------------------------------------------------ diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index b198c87467c..81550fd3d3b 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -740,7 +740,7 @@ TypeHandle TypeHandle::MergeClassWithInterface(TypeHandle tClass, TypeHandle tIn MethodTable::InterfaceMapIterator intIt = pMTInterface->IterateInterfaceMap(); while (intIt.Next()) { - MethodTable *pMT = intIt.GetInterface(); + MethodTable *pMT = intIt.GetInterface(pMTInterface); if (pMTClass->ImplementsEquivalentInterface(pMT)) { // Found a common interface. If there are multiple common interfaces, then diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index e623d4a8cec..339d5f397b1 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -756,6 +756,16 @@ public: return m_pArgs; } + bool ContainsAllOneType(TypeHandle th) + { + for (auto i = GetNumArgs(); i > 0;) + { + if ((*this)[--i] != th) + return false; + } + return true; + } + private: // Note that for DAC builds, m_pArgs may be host allocated buffer, not a copy of an object marshalled by DAC. FixupPointer * m_pArgs; From 6cb345d51135e582104a3cd2900e495f3cc84319 Mon Sep 17 00:00:00 2001 From: Aaron Kunkle Date: Mon, 28 Jun 2021 14:24:57 -0700 Subject: [PATCH 166/926] Add perf_slow yaml (#54853) --- eng/pipelines/coreclr/perf_slow.yml | 199 ++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 eng/pipelines/coreclr/perf_slow.yml diff --git a/eng/pipelines/coreclr/perf_slow.yml b/eng/pipelines/coreclr/perf_slow.yml new file mode 100644 index 00000000000..1ad80465846 --- /dev/null +++ b/eng/pipelines/coreclr/perf_slow.yml @@ -0,0 +1,199 @@ +trigger: + batch: true + branches: + include: + - main + - release/* + paths: + include: + - '*' + - src/libraries/System.Private.CoreLib/* + exclude: + - .github/* + - docs/* + - CODE-OF-CONDUCT.md + - CONTRIBUTING.md + - LICENSE.TXT + - PATENTS.TXT + - README.md + - SECURITY.md + - THIRD-PARTY-NOTICES.TXT + +variables: + - template: /eng/pipelines/common/variables.yml + +schedules: +- cron: "30 2 * * *" + displayName: Every night at 2:30AM + branches: + include: + - main + always: true + +jobs: + +- ${{ if and(ne(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'Schedule')) }}: + + # build mono + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/mono/templates/build-job.yml + runtimeFlavor: mono + buildConfig: release + platforms: + - Linux_arm64 + + # build coreclr and libraries + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml + buildConfig: release + platforms: + - Linux_arm64 + jobParameters: + testGroup: perf + + # run arm64 interpreter jobs for mono + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Linux_arm64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + runtimeType: mono + codeGenType: 'Interpreter' + projectFile: microbenchmarks.proj + runKind: micro_mono + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perfa64' + + # build mono on wasm + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Browser_wasm + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: wasm + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: Browser Wasm Artifacts + artifactName: BrowserWasm + archiveType: zip + archiveExtension: .zip + +- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'Schedule')) }}: + + # build coreclr and libraries + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml + buildConfig: release + platforms: + - Linux_arm64 + - windows_arm64 + jobParameters: + testGroup: perf + + # build mono on wasm + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Browser_wasm + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: wasm + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: Browser Wasm Artifacts + artifactName: BrowserWasm + archiveType: zip + archiveExtension: .zip + + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Linux_arm64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: AOT + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: AOT Mono Artifacts + artifactName: LinuxMonoAOTarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + + # run mono aot microbenchmarks perf job + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml # NOTE: should we move this file out of coreclr tempelates because it contains mono jobs? + buildConfig: release + runtimeFlavor: aot + platforms: + - Linux_arm64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + runtimeType: mono + codeGenType: 'AOT' + projectFile: microbenchmarks.proj + runKind: micro_mono + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perftiger' + +# run coreclr Linux arm64 microbenchmarks perf job + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: coreclr + platforms: + - Linux_arm64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + projectFile: microbenchmarks.proj + runKind: micro + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perfa64' + +# run coreclr Windows arm64 microbenchmarks perf job + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: coreclr + platforms: + - windows_arm64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + projectFile: microbenchmarks.proj + runKind: micro + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perfsurf' + From 1c383a27ce2c83d41b135cc149e422d15cfeb4e5 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Mon, 28 Jun 2021 14:33:51 -0700 Subject: [PATCH 167/926] Fix setting breakpoints on AVX 256 instructions and other 32 byte immediate instructions (#54786) --- src/coreclr/debug/ee/controller.cpp | 15 +++++++---- src/coreclr/debug/ee/controller.h | 4 ++- src/coreclr/debug/ee/debugger.cpp | 41 +++++++++++++++++------------ src/coreclr/debug/ee/debugger.h | 38 +++++++++++++++++--------- 4 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 831dc2dd5b4..b17ae8f1150 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -1393,7 +1393,7 @@ bool DebuggerController::ApplyPatch(DebuggerControllerPatch *patch) _ASSERTE(!"VirtualProtect of code page failed"); return false; } -#endif // !defined(HOST_OSX) || !defined(HOST_ARM64) +#endif // !defined(HOST_OSX) || !defined(HOST_ARM64) } // TODO: : determine if this is needed for AMD64 #if defined(TARGET_X86) //REVISIT_TODO what is this?! @@ -1496,7 +1496,7 @@ bool DebuggerController::UnapplyPatch(DebuggerControllerPatch *patch) _ASSERTE(!"VirtualProtect of code page failed"); return false; } -#endif // !defined(HOST_OSX) || !defined(HOST_ARM64) +#endif // !defined(HOST_OSX) || !defined(HOST_ARM64) } else { @@ -4352,6 +4352,7 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, m_pSharedPatchBypassBuffer = patch->GetOrCreateSharedPatchBypassBuffer(); BYTE* patchBypass = m_pSharedPatchBypassBuffer->PatchBypass; + LOG((LF_CORDB, LL_INFO10000, "DPS::DPS: Patch skip for opcode 0x%.4x at address %p buffer allocated at 0x%.8x\n", patch->opcode, patch->address, m_pSharedPatchBypassBuffer)); // Copy the instruction block over to the patch skip // WARNING: there used to be an issue here because CopyInstructionBlock copied the breakpoint from the @@ -4412,8 +4413,9 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, } else { + _ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass); // Copy the data into our buffer. - memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, SharedPatchBypassBuffer::cbBufferBypass); + memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); if (m_instrAttrib.m_fIsWrite) { @@ -4901,11 +4903,14 @@ bool DebuggerPatchSkip::TriggerSingleStep(Thread *thread, const BYTE *ip) break; case 16: - memcpy(reinterpret_cast(targetFixup), bufferBypass, 16); + case 32: + memcpy(reinterpret_cast(targetFixup), bufferBypass, fixupSize); break; default: - _ASSERTE(!"bad operand size"); + _ASSERTE(!"bad operand size. If you hit this and it was because we need to process instructions with larger \ + relative immediates, make sure to update the SharedPatchBypassBuffer size, the DebuggerHeapExecutableMemoryAllocator, \ + and structures depending on DBG_MAX_EXECUTABLE_ALLOC_SIZE."); } } #endif diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h index 9bcfc8682f7..12b1106f7a4 100644 --- a/src/coreclr/debug/ee/controller.h +++ b/src/coreclr/debug/ee/controller.h @@ -288,7 +288,9 @@ public: // "PatchBypass" must be the first field of this class for alignment to be correct. BYTE PatchBypass[MAX_INSTRUCTION_LENGTH]; #if defined(TARGET_AMD64) - const static int cbBufferBypass = 0x10; + // If you update this value, make sure that it fits in the data payload of a + // DebuggerHeapExecutableMemoryChunk. This will need to be bumped to 0x40 for AVX 512 support. + const static int cbBufferBypass = 0x20; BYTE BypassBuffer[cbBufferBypass]; UINT_PTR RipTargetFixup; diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 4706790dd3d..53ee5555ace 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -365,10 +365,10 @@ void Debugger::DoNotCallDirectlyPrivateLock(void) // Thread * pThread; bool fIsCooperative; - + pThread = g_pEEInterface->GetThread(); fIsCooperative = (pThread != NULL) && (pThread->PreemptiveGCDisabled()); - + if (m_fShutdownMode && !fIsCooperative) { // The big fear is that some other random thread will take the debugger-lock and then block on something else, @@ -16299,7 +16299,8 @@ void* DebuggerHeapExecutableMemoryAllocator::Allocate(DWORD numberOfBytes) } } - return ChangePageUsage(pageToAllocateOn, chunkToUse, ChangePageUsageAction::ALLOCATE); + ASSERT(chunkToUse >= 1 && (uint)chunkToUse < CHUNKS_PER_DEBUGGERHEAP); + return GetPointerToChunkWithUsageUpdate(pageToAllocateOn, chunkToUse, ChangePageUsageAction::ALLOCATE); } void DebuggerHeapExecutableMemoryAllocator::Free(void* addr) @@ -16314,9 +16315,9 @@ void DebuggerHeapExecutableMemoryAllocator::Free(void* addr) int chunkNum = static_cast(addr)->data.chunkNumber; // Sanity check: assert that the address really represents the start of a chunk. - ASSERT(((uint64_t)addr - (uint64_t)pageToFreeIn) % 64 == 0); + ASSERT(((uint64_t)addr - (uint64_t)pageToFreeIn) % EXPECTED_CHUNKSIZE == 0); - ChangePageUsage(pageToFreeIn, chunkNum, ChangePageUsageAction::FREE); + GetPointerToChunkWithUsageUpdate(pageToFreeIn, chunkNum, ChangePageUsageAction::FREE); } DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewPage() @@ -16324,6 +16325,7 @@ DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewP void* newPageAddr = VirtualAlloc(NULL, sizeof(DebuggerHeapExecutableMemoryPage), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); DebuggerHeapExecutableMemoryPage *newPage = new (newPageAddr) DebuggerHeapExecutableMemoryPage; + CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex); newPage->SetNextPage(m_pages); // Add the new page to the linked list of pages @@ -16333,8 +16335,9 @@ DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewP bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHeapExecutableMemoryPage* page, /* _Out_ */ int* chunkToUse) { + CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex); uint64_t occupancy = page->GetPageOccupancy(); - bool available = occupancy != UINT64_MAX; + bool available = occupancy != MAX_CHUNK_MASK; if (!available) { @@ -16348,13 +16351,13 @@ bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHea if (chunkToUse) { - // Start i at 62 because first chunk is reserved - for (int i = 62; i >= 0; i--) + // skip the first bit, as that's used by the booking chunk. + for (int i = CHUNKS_PER_DEBUGGERHEAP - 2; i >= 0; i--) { - uint64_t mask = ((uint64_t)1 << i); + uint64_t mask = (1ull << i); if ((mask & occupancy) == 0) { - *chunkToUse = 64 - i - 1; + *chunkToUse = CHUNKS_PER_DEBUGGERHEAP - i - 1; break; } } @@ -16363,12 +16366,12 @@ bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHea return true; } -void* DebuggerHeapExecutableMemoryAllocator::ChangePageUsage(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action) +void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action) { ASSERT(action == ChangePageUsageAction::ALLOCATE || action == ChangePageUsageAction::FREE); + uint64_t mask = 1ull << (CHUNKS_PER_DEBUGGERHEAP - chunkNumber - 1); - uint64_t mask = (uint64_t)0x1 << (64 - chunkNumber - 1); - + CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex); uint64_t prevOccupancy = page->GetPageOccupancy(); uint64_t newOccupancy = (action == ChangePageUsageAction::ALLOCATE) ? (prevOccupancy | mask) : (prevOccupancy ^ mask); page->SetPageOccupancy(newOccupancy); @@ -16459,11 +16462,15 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable) #endif #ifndef HOST_WINDOWS - m_execMemAllocator = new (nothrow) DebuggerHeapExecutableMemoryAllocator(); - ASSERT(m_execMemAllocator != NULL); - if (m_execMemAllocator == NULL) + m_execMemAllocator = NULL; + if (m_fExecutable) { - return E_OUTOFMEMORY; + m_execMemAllocator = new (nothrow) DebuggerHeapExecutableMemoryAllocator(); + ASSERT(m_execMemAllocator != NULL); + if (m_execMemAllocator == NULL) + { + return E_OUTOFMEMORY; + } } #endif diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 9d80fb3eec7..f16f8cd6d9d 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -1047,7 +1047,12 @@ protected: // different part of the address space (not on the heap). // ------------------------------------------------------------------------ */ -#define DBG_MAX_EXECUTABLE_ALLOC_SIZE 48 +constexpr uint64_t DBG_MAX_EXECUTABLE_ALLOC_SIZE=112; +constexpr uint64_t EXPECTED_CHUNKSIZE=128; +constexpr uint64_t DEBUGGERHEAP_PAGESIZE=4096; +constexpr uint64_t CHUNKS_PER_DEBUGGERHEAP=(DEBUGGERHEAP_PAGESIZE / EXPECTED_CHUNKSIZE); +constexpr uint64_t MAX_CHUNK_MASK=((1ull << CHUNKS_PER_DEBUGGERHEAP) - 1); +constexpr uint64_t BOOKKEEPING_CHUNK_MASK (1ull << (CHUNKS_PER_DEBUGGERHEAP - 1)); // Forward declaration struct DebuggerHeapExecutableMemoryPage; @@ -1060,7 +1065,7 @@ struct DebuggerHeapExecutableMemoryPage; // for the page, and the remaining ones are DataChunks and are handed out // by the allocator when it allocates memory. // ------------------------------------------------------------------------ */ -union DECLSPEC_ALIGN(64) DebuggerHeapExecutableMemoryChunk { +union DECLSPEC_ALIGN(EXPECTED_CHUNKSIZE) DebuggerHeapExecutableMemoryChunk { struct DataChunk { @@ -1078,13 +1083,14 @@ union DECLSPEC_ALIGN(64) DebuggerHeapExecutableMemoryChunk { DebuggerHeapExecutableMemoryPage *nextPage; uint64_t pageOccupancy; + static_assert(CHUNKS_PER_DEBUGGERHEAP <= sizeof(pageOccupancy) * 8, + "Our interfaces assume the chunks in a page can be masken on this field"); } bookkeeping; - char _alignpad[64]; + char _alignpad[EXPECTED_CHUNKSIZE]; }; - -static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == 64, "DebuggerHeapExecutableMemoryChunk is expect to be 64 bytes."); +static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == EXPECTED_CHUNKSIZE, "DebuggerHeapExecutableMemoryChunk is expect to be EXPECTED_CHUNKSIZE bytes."); // ------------------------------------------------------------------------ */ // DebuggerHeapExecutableMemoryPage @@ -1095,7 +1101,7 @@ static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == 64, "DebuggerHeapExec // about which of the other chunks are used/free as well as a pointer to // the next page. // ------------------------------------------------------------------------ */ -struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage +struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage { inline DebuggerHeapExecutableMemoryPage* GetNextPage() { @@ -1115,15 +1121,16 @@ struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage inline void SetPageOccupancy(uint64_t newOccupancy) { - // Can't unset first bit of occupancy! - ASSERT((newOccupancy & 0x8000000000000000) != 0); - + // Can't unset the bookmark chunk! + ASSERT((newOccupancy & BOOKKEEPING_CHUNK_MASK) != 0); + ASSERT(newOccupancy <= MAX_CHUNK_MASK); ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.pageOccupancy = newOccupancy; } inline void* GetPointerToChunk(int chunkNum) const { + ASSERT(chunkNum >= 0 && (uint)chunkNum < CHUNKS_PER_DEBUGGERHEAP); return (char*)this + chunkNum * sizeof(DebuggerHeapExecutableMemoryChunk); } @@ -1131,8 +1138,8 @@ struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage { ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - SetPageOccupancy(0x8000000000000000); // only the first bit is set. - for (uint8_t i = 1; i < sizeof(chunks)/sizeof(chunks[0]); i++) + SetPageOccupancy(BOOKKEEPING_CHUNK_MASK); // only the first bit is set. + for (uint8_t i = 1; i < CHUNKS_PER_DEBUGGERHEAP; i++) { ASSERT(i != 0); debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.startOfPage = this; @@ -1141,8 +1148,13 @@ struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage } private: - DebuggerHeapExecutableMemoryChunk chunks[64]; + DebuggerHeapExecutableMemoryChunk chunks[CHUNKS_PER_DEBUGGERHEAP]; + static_assert(sizeof(chunks) == DEBUGGERHEAP_PAGESIZE, + "Expected DebuggerHeapExecutableMemoryPage to have DEBUGGERHEAP_PAGESIZE bytes worth of chunks."); + }; +static_assert(sizeof(DebuggerHeapExecutableMemoryPage) == DEBUGGERHEAP_PAGESIZE, + "DebuggerHeapExecutableMemoryPage exceeded the expected size."); // ------------------------------------------------------------------------ */ // DebuggerHeapExecutableMemoryAllocator class @@ -1170,7 +1182,7 @@ private: DebuggerHeapExecutableMemoryPage* AddNewPage(); bool CheckPageForAvailability(DebuggerHeapExecutableMemoryPage* page, /* _Out_ */ int* chunkToUse); - void* ChangePageUsage(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action); + void* GetPointerToChunkWithUsageUpdate(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action); private: // Linked list of pages that have been allocated From 94f3355d90d8b5db1848957351a6b3585dee13a4 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Mon, 28 Jun 2021 15:33:32 -0700 Subject: [PATCH 168/926] Fix lowering usage of an unset LSRA field. (#54731) * Add repro. * fix the issue. * delete a dead condition * add a todo. * Fix the failures. --- src/coreclr/jit/codegen.h | 2 + src/coreclr/jit/gentree.cpp | 1 + src/coreclr/jit/lclvars.cpp | 1 + src/coreclr/jit/lower.cpp | 68 +++++++++---------- src/coreclr/jit/lowerarmarch.cpp | 4 ++ .../JitBlue/Runtime_54466/Runtime_54466.cs | 32 +++++++++ .../Runtime_54466/Runtime_54466.csproj | 10 +++ 7 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 02971fbde2b..ae862974158 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -863,8 +863,10 @@ protected: // Generate code for a GT_BITCAST that is not contained. void genCodeForBitCast(GenTreeOp* treeNode); +#if defined(TARGET_XARCH) // Generate the instruction to move a value between register files void genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg); +#endif // TARGET_XARCH struct GenIntCastDesc { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a10afeb758b..955a7bba1e9 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7396,6 +7396,7 @@ GenTree* Compiler::gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg) { assert(arg != nullptr); + assert(type != TYP_STRUCT); GenTree* node = nullptr; #if defined(TARGET_ARM) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 916cb93e187..f7c67808bdd 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3779,6 +3779,7 @@ var_types LclVarDsc::GetRegisterType(const GenTreeLclVarCommon* tree) const { if (lclVarType == TYP_STRUCT) { + assert(!tree->OperIsLocalField() && "do not expect struct local fields."); lclVarType = GetLayout()->GetRegisterType(); } targetType = lclVarType; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 389c29f7643..8cbef1709fa 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3073,10 +3073,11 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) DISPTREERANGE(BlockRange(), lclStore); JITDUMP("\n"); - GenTree* src = lclStore->gtGetOp1(); - LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); - bool srcIsMultiReg = src->IsMultiRegNode(); - bool dstIsMultiReg = lclStore->IsMultiRegLclVar(); + GenTree* src = lclStore->gtGetOp1(); + LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); + + const bool srcIsMultiReg = src->IsMultiRegNode(); + const bool dstIsMultiReg = lclStore->IsMultiRegLclVar(); if (!dstIsMultiReg && varTypeIsStruct(varDsc)) { @@ -3098,25 +3099,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } } - if ((varTypeUsesFloatReg(lclStore) != varTypeUsesFloatReg(src)) && !lclStore->IsPhiDefn() && - (src->TypeGet() != TYP_STRUCT)) - { - if (m_lsra->isRegCandidate(varDsc)) - { - GenTree* bitcast = comp->gtNewBitCastNode(lclStore->TypeGet(), src); - lclStore->gtOp1 = bitcast; - src = lclStore->gtGetOp1(); - BlockRange().InsertBefore(lclStore, bitcast); - ContainCheckBitCast(bitcast); - } - else - { - // This is an actual store, we'll just retype it. - lclStore->gtType = src->TypeGet(); - } - } - - if (srcIsMultiReg || lclStore->IsMultiRegLclVar()) + if (srcIsMultiReg || dstIsMultiReg) { const ReturnTypeDesc* retTypeDesc = nullptr; if (src->OperIs(GT_CALL)) @@ -3125,14 +3108,16 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } CheckMultiRegLclVar(lclStore->AsLclVar(), retTypeDesc); } + + const var_types lclRegType = varDsc->GetRegisterType(lclStore); + if ((lclStore->TypeGet() == TYP_STRUCT) && !srcIsMultiReg) { bool convertToStoreObj; if (src->OperGet() == GT_CALL) { - GenTreeCall* call = src->AsCall(); - const ClassLayout* layout = varDsc->GetLayout(); - const var_types regType = layout->GetRegisterType(); + GenTreeCall* call = src->AsCall(); + const ClassLayout* layout = varDsc->GetLayout(); #ifdef DEBUG const unsigned slotCount = layout->GetSlotCount(); @@ -3140,7 +3125,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // Windows x64 doesn't have multireg returns, // x86 uses it only for long return type, not for structs. assert(slotCount == 1); - assert(regType != TYP_UNDEF); + assert(lclRegType != TYP_UNDEF); #else // !TARGET_XARCH || UNIX_AMD64_ABI if (!varDsc->lvIsHfa()) { @@ -3153,7 +3138,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) unsigned size = layout->GetSize(); assert((size <= 8) || (size == 16)); bool isPowerOf2 = (((size - 1) & size) == 0); - bool isTypeDefined = (regType != TYP_UNDEF); + bool isTypeDefined = (lclRegType != TYP_UNDEF); assert(isPowerOf2 == isTypeDefined); } } @@ -3161,7 +3146,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) #endif // DEBUG #if !defined(WINDOWS_AMD64_ABI) - if (!call->HasMultiRegRetVal() && (regType == TYP_UNDEF)) + if (!call->HasMultiRegRetVal() && (lclRegType == TYP_UNDEF)) { // If we have a single return register, // but we can't retype it as a primitive type, we must spill it. @@ -3182,9 +3167,8 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) else if (src->OperIs(GT_CNS_INT)) { assert(src->IsIntegralConst(0) && "expected an INIT_VAL for non-zero init."); - var_types regType = varDsc->GetRegisterType(); #ifdef FEATURE_SIMD - if (varTypeIsSIMD(regType)) + if (varTypeIsSIMD(lclRegType)) { CorInfoType simdBaseJitType = comp->getBaseJitTypeOfSIMDLocal(lclStore); if (simdBaseJitType == CORINFO_TYPE_UNDEF) @@ -3193,7 +3177,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) simdBaseJitType = CORINFO_TYPE_FLOAT; } GenTreeSIMD* simdTree = - comp->gtNewSIMDNode(regType, src, SIMDIntrinsicInit, simdBaseJitType, varDsc->lvExactSize); + comp->gtNewSIMDNode(lclRegType, src, SIMDIntrinsicInit, simdBaseJitType, varDsc->lvExactSize); BlockRange().InsertAfter(src, simdTree); LowerSIMD(simdTree); src = simdTree; @@ -3245,6 +3229,20 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } } + // src and dst can be in registers, check if we need a bitcast. + if (!src->TypeIs(TYP_STRUCT) && (varTypeUsesFloatReg(lclRegType) != varTypeUsesFloatReg(src))) + { + assert(!srcIsMultiReg && !dstIsMultiReg); + assert(lclStore->OperIsLocalStore()); + assert(lclRegType != TYP_UNDEF); + + GenTree* bitcast = comp->gtNewBitCastNode(lclRegType, src); + lclStore->gtOp1 = bitcast; + src = lclStore->gtGetOp1(); + BlockRange().InsertBefore(lclStore, bitcast); + ContainCheckBitCast(bitcast); + } + LowerStoreLoc(lclStore); JITDUMP("lowering store lcl var/field (after):\n"); DISPTREERANGE(BlockRange(), lclStore); @@ -6571,8 +6569,10 @@ void Lowering::ContainCheckBitCast(GenTree* node) { op1->SetContained(); } - LclVarDsc* varDsc = &comp->lvaTable[op1->AsLclVar()->GetLclNum()]; - if (!m_lsra->isRegCandidate(varDsc)) + const LclVarDsc* varDsc = comp->lvaGetDesc(op1->AsLclVar()); + // TODO-Cleanup: we want to check if the local is already known not + // to be on reg, for example, because local enreg is disabled. + if (varDsc->lvDoNotEnregister) { op1->SetContained(); } diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6774a4ef176..7edf8c7103f 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1549,6 +1549,9 @@ void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const assert(storeLoc->OperIsLocalStore()); GenTree* op1 = storeLoc->gtGetOp1(); +#if 0 + // TODO-ARMARCH-CQ: support contained bitcast under STORE_LCL_VAR/FLD, + // currently codegen does not expect it. if (op1->OperIs(GT_BITCAST)) { // If we know that the source of the bitcast will be in a register, then we can make @@ -1561,6 +1564,7 @@ void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const return; } } +#endif const LclVarDsc* varDsc = comp->lvaGetDesc(storeLoc); diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs new file mode 100644 index 00000000000..f51dbd03a4b --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Runtime_54466 +{ + public class Test + { + static int Main() + { + return t(1, 1, 1, 1, Vector2.One, Vector2.One, Vector2.One, Vector2.One); + } + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static int t(int a1, int a2, int a3, int a4, Vector2 x1, Vector2 x2, Vector2 x3, Vector2 x4) + { + if (x1 != Vector2.One) + { + Console.WriteLine("FAIL"); + return 101; + } + return 100; + } + } + +} + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj new file mode 100644 index 00000000000..1100f420532 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54466/Runtime_54466.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 851eafba82a2bb1d5888e7f10359170a7afbca8e Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Mon, 28 Jun 2021 16:37:06 -0700 Subject: [PATCH 169/926] Fix unreached during dump. (#54861) --- src/coreclr/jit/ee_il_dll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index eeaeb093fd0..0f993113be1 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -810,11 +810,11 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) } break; -#ifndef TARGET_AMD64 case CodeGenInterface::VLT_REG_REG: printf("%s-%s", getRegName(var->loc.vlRegReg.vlrrReg1), getRegName(var->loc.vlRegReg.vlrrReg2)); break; +#ifndef TARGET_AMD64 case CodeGenInterface::VLT_REG_STK: if ((int)var->loc.vlRegStk.vlrsStk.vlrssBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP) { From d00b0181d16748fac891f6c25ba4311bbf69db86 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 29 Jun 2021 08:50:32 +0200 Subject: [PATCH 170/926] add System.Net.MsQuic.Transport as dependency for Windows build (#54851) --- eng/Version.Details.xml | 4 ++++ eng/Versions.props | 2 ++ src/libraries/System.Net.Quic/src/System.Net.Quic.csproj | 3 +++ 3 files changed, 9 insertions(+) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f47e2fe1e06..b8b08594ccc 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,6 +4,10 @@ https://github.com/dotnet/icu 59588c1257a842089d0b7df3bad1cdd69ac720e1 + + https://github.com/dotnet/msquic + d7db669b70f4dd67ec001c192f9809c218cab88b + diff --git a/eng/Versions.props b/eng/Versions.props index da6691e1fb6..60393072c37 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -166,6 +166,8 @@ $(MicrosoftNETILLinkTasksVersion) 6.0.0-preview.7.21315.3 + + 6.0.0-preview.7.21328.2 11.1.0-alpha.1.21314.1 11.1.0-alpha.1.21314.1 diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index eb0cac9ddec..60201faf945 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -56,6 +56,9 @@ + + + From 709fcd80586aaca02cf41069d951612c0318a95b Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Tue, 29 Jun 2021 09:05:28 +0200 Subject: [PATCH 171/926] StressLogAnalyzer didn't print the number of messages correctly if it exceeded the int range (2 billion). (#54832) Fix is to just use 64 bit ints instead. --- .../StressLogPlugin/StressLogPlugin.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp index 42e778312dc..54a54bb72cf 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp +++ b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp @@ -544,8 +544,8 @@ static LONG s_wrappedWriteThreadCount; static const LONG MAX_MESSAGE_COUNT = 64 * 1024 * 1024; static StressThreadAndMsg* s_threadMsgBuf; -static volatile LONG s_msgCount = 0; -static volatile LONG s_totalMsgCount = 0; +static volatile LONGLONG s_msgCount = 0; +static volatile LONGLONG s_totalMsgCount = 0; static double s_timeFilterStart = 0; static double s_timeFilterEnd = 0; static wchar_t* s_outputFileName = nullptr; @@ -988,7 +988,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) static void IncludeMessage(uint64_t threadId, StressMsg* msg) { - LONG msgCount = _InterlockedIncrement(&s_msgCount) - 1; + LONGLONG msgCount = _InterlockedIncrement64(&s_msgCount) - 1; if (msgCount < MAX_MESSAGE_COUNT) { s_threadMsgBuf[msgCount].threadId = threadId; @@ -1133,7 +1133,7 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) s_threadStressLogDesc[threadStressLogIndex].workFinished = 1; } - InterlockedAdd(&s_totalMsgCount, totalMsgCount); + InterlockedAdd64(&s_totalMsgCount, totalMsgCount); InterlockedAdd(&s_wrappedWriteThreadCount, wrappedWriteThreadCount); return 0; @@ -1151,7 +1151,7 @@ static double FindLatestTime(StressLog::StressLogHeader* hdr) return latestTime; } -static void PrintFriendlyNumber(int n) +static void PrintFriendlyNumber(LONGLONG n) { if (n < 1000) printf("%d", n); From 969840ecd1964656d5686fa861110d905cd0d6c9 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Tue, 29 Jun 2021 09:15:59 +0200 Subject: [PATCH 172/926] Found a race condition where the LOH flag on a segment is set too late. This gives another thread the chance to allocate in a fresh LOH region that doesn't have the LOH flag set just yet and trip over an assert in Object::ValidateInner. (#54839) The fix is simply to set the flag in get_new_region before the region is put on the list for the LOH generation. --- src/coreclr/gc/gc.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 9cb439e6504..ea9b6fc5c0e 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -5674,9 +5674,17 @@ heap_segment* gc_heap::get_segment_for_uoh (int gen_number, size_t size #ifdef MULTIPLE_HEAPS heap_segment_heap (res) = hp; #endif //MULTIPLE_HEAPS - res->flags |= (gen_number == poh_generation) ? - heap_segment_flags_poh : - heap_segment_flags_loh; + + size_t flags = (gen_number == poh_generation) ? + heap_segment_flags_poh : + heap_segment_flags_loh; + +#ifdef USE_REGIONS + // in the regions case, flags are set by get_new_region + assert ((res->flags & (heap_segment_flags_loh | heap_segment_flags_poh)) == flags); +#else //USE_REGIONS + res->flags |= flags; +#endif //USE_REGIONS FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), @@ -28284,6 +28292,21 @@ heap_segment* gc_heap::get_new_region (int gen_number, size_t size) if (new_region) { + switch (gen_number) + { + default: + assert ((new_region->flags & (heap_segment_flags_loh | heap_segment_flags_poh)) == 0); + break; + + case loh_generation: + new_region->flags |= heap_segment_flags_loh; + break; + + case poh_generation: + new_region->flags |= heap_segment_flags_poh; + break; + } + generation* gen = generation_of (gen_number); heap_segment_next (generation_tail_region (gen)) = new_region; generation_tail_region (gen) = new_region; From 72047c5f3f86d0409443aa22fb6dbde50a4b8caa Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Tue, 29 Jun 2021 13:51:20 +0200 Subject: [PATCH 173/926] Enable Http2_MultipleConnectionsEnabled_InfiniteRequestsCompletelyBlockOneConnection_RemaningRequestsAreHandledByNewConnection test (#54683) Fixes #45204 --- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 7d006162ef0..6b840eee8f5 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2093,7 +2093,6 @@ namespace System.Net.Http.Functional.Tests } [ConditionalFact(nameof(SupportsAlpn))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/45204")] public async Task Http2_MultipleConnectionsEnabled_InfiniteRequestsCompletelyBlockOneConnection_RemaningRequestsAreHandledByNewConnection() { const int MaxConcurrentStreams = 2; From 39e8a6e0b0a53e621cd997b53122663a4ae114f0 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 29 Jun 2021 08:05:01 -0400 Subject: [PATCH 174/926] Avoid allocating unused prefix/localName in XmlCharCheckingReader.ValidateQName (#54836) --- .../src/System/Xml/Core/XmlCharCheckingReader.cs | 3 +-- .../src/System/Xml/ValidateNames.cs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs index 38d606efedb..f1f7bdd4e94 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs @@ -624,8 +624,7 @@ namespace System.Xml private void ValidateQName(string name) { - string prefix, localName; - ValidateNames.ParseQNameThrow(name, out prefix, out localName); + ValidateNames.ParseQNameThrow(name); } private void ValidateQName(string prefix, string localName) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/ValidateNames.cs b/src/libraries/System.Private.Xml/src/System/Xml/ValidateNames.cs index 4aadc807bfb..cce4cf01d5a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/ValidateNames.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/ValidateNames.cs @@ -261,9 +261,9 @@ namespace System.Xml /// /// Calls parseQName and throws exception if the resulting name is not a valid QName. - /// Returns the prefix and local name parts. + /// Returns the colon offset in the name. /// - internal static void ParseQNameThrow(string s, out string prefix, out string localName) + internal static int ParseQNameThrow(string s) { int colonOffset; int len = ParseQName(s, 0, out colonOffset); @@ -274,6 +274,16 @@ namespace System.Xml ThrowInvalidName(s, 0, len); } + return colonOffset; + } + + /// + /// Calls parseQName and throws exception if the resulting name is not a valid QName. + /// Returns the prefix and local name parts. + /// + internal static void ParseQNameThrow(string s, out string prefix, out string localName) + { + int colonOffset = ParseQNameThrow(s); if (colonOffset != 0) { prefix = s.Substring(0, colonOffset); From 1b6106596609a90e0f2db136e6186b39f34b3739 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 29 Jun 2021 15:03:09 +0200 Subject: [PATCH 175/926] Fix Connect_DualMode_DnsConnect_RetrievedEndPoints_Success on Linux (#54681) --- .../System.Net.Sockets/tests/FunctionalTests/Connect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index 5f9730e8ab0..a491fada918 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -129,7 +129,7 @@ namespace System.Net.Sockets.Tests var localEndPoint = client.LocalEndPoint as IPEndPoint; Assert.NotNull(localEndPoint); - Assert.Equal(IPAddress.Loopback.MapToIPv6(), localEndPoint.Address); + Assert.True(localEndPoint.Address.Equals(IPAddress.IPv6Loopback) || localEndPoint.Address.Equals(IPAddress.Loopback.MapToIPv6())); var remoteEndPoint = client.RemoteEndPoint as IPEndPoint; Assert.NotNull(remoteEndPoint); From 5ab1ea9a93b57baad168640db4eed97bfe11d54f Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 29 Jun 2021 15:43:24 +0200 Subject: [PATCH 176/926] InternalCanonicalizeRealPath: don't call strstr with uninitialized lpBuffer. (#54824) --- src/coreclr/pal/src/file/file.cpp | 52 +------------------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/coreclr/pal/src/file/file.cpp b/src/coreclr/pal/src/file/file.cpp index 8d0cfd4f4f8..59cfd07fbdb 100644 --- a/src/coreclr/pal/src/file/file.cpp +++ b/src/coreclr/pal/src/file/file.cpp @@ -325,34 +325,10 @@ CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, PathCharString& lpBuffe goto LExit; } - if (! RealPathHelper(pszCwdBuffer, lpBuffer)) + if (!RealPathHelper(pszCwdBuffer, lpBuffer)) { WARN("realpath() failed with error %d\n", errno); palError = FILEGetLastErrorFromErrno(); -#if defined(HOST_AMD64) - // If we are here, then we tried to invoke realpath - // against a directory. - // - // On Mac64, realpath implementation differs from Mac32 - // by *not* supporting invalid filenames in the path (while - // Mac32 implementation does). - // - // Thus, if we are here, and the error code we see is - // ERROR_FILE_NOT_FOUND, then we should map it to - // ERROR_PATH_NOT_FOUND since it was a directory that - // was not found (and not a file). - if (palError == ERROR_FILE_NOT_FOUND) - { - // Since lpBuffer can be modified by the realpath call, - // and can result in a truncated subset of the original buffer, - // we use strstr as a level of safety. - if (strstr(pszCwdBuffer, lpBuffer) != 0) - { - palError = ERROR_PATH_NOT_FOUND; - } - } -#endif // defined(HOST_AMD64) - goto LExit; } lpFilename = lpExistingPath; @@ -390,32 +366,6 @@ CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, PathCharString& lpBuffe { WARN("realpath() failed with error %d\n", errno); palError = FILEGetLastErrorFromErrno(); - -#if defined(HOST_AMD64) - // If we are here, then we tried to invoke realpath - // against a directory after stripping out the filename - // from the original path. - // - // On Mac64, realpath implementation differs from Mac32 - // by *not* supporting invalid filenames in the path (while - // Mac32 implementation does). - // - // Thus, if we are here, and the error code we see is - // ERROR_FILE_NOT_FOUND, then we should map it to - // ERROR_PATH_NOT_FOUND since it was a directory that - // was not found (and not a file). - if (palError == ERROR_FILE_NOT_FOUND) - { - // Since lpBuffer can be modified by the realpath call, - // and can result in a truncated subset of the original buffer, - // we use strstr as a level of safety. - if (strstr(lpExistingPath, lpBuffer) != 0) - { - palError = ERROR_PATH_NOT_FOUND; - } - } -#endif // defined(HOST_AMD64) - goto LExit; } From 3e115fffb8cbfd3ade4ebe7a7c55c55d94413c7c Mon Sep 17 00:00:00 2001 From: imhameed Date: Tue, 29 Jun 2021 07:00:16 -0700 Subject: [PATCH 177/926] [mono] Enable more runtime tests (#54852) --- src/tests/issues.targets | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index bac25dd4863..964d0d7b17d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1065,12 +1065,6 @@ Doesn't pass after LLVM AOT compilation. - - Doesn't pass after LLVM AOT compilation. - - - Doesn't pass after LLVM AOT compilation. - Doesn't pass after LLVM AOT compilation. @@ -1300,20 +1294,8 @@ needs triage - - needs triage - - - needs triage - - needs triage - - - needs triage - - - needs triage + https://github.com/dotnet/runtime/issues/54867 https://github.com/dotnet/runtime/issues/34376 @@ -1672,9 +1654,15 @@ - + https://github.com/dotnet/runtime/issues/46622 + + https://github.com/dotnet/runtime/issues/54867 + + + https://github.com/dotnet/runtime/issues/54867 + https://github.com/dotnet/runtime/issues/46622 @@ -2404,6 +2392,13 @@ + + https://github.com/dotnet/runtime/issues/54867 + + + https://github.com/dotnet/runtime/issues/54867 + + https://github.com/dotnet/runtime/issues/54122 From 086f6ec5cf3e32c607619eec5a6447c62b91cef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Tue, 29 Jun 2021 10:28:03 -0400 Subject: [PATCH 178/926] update area-owners.md (#54900) --- docs/area-owners.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/area-owners.md b/docs/area-owners.md index 2d588967f4a..4188ea7d982 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -9,7 +9,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | Area | Lead | Owners (area experts to tag in PR's and issues) | Notes | |------------------------------------------------|---------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | area-AssemblyLoader-coreclr | @agocke | @agocke @vitek-karas @vsadov | | -| area-AssemblyLoader-mono | @SamMonoRT | @CoffeeFlux | | +| area-AssemblyLoader-mono | @SamMonoRT | @lambdageek | | | area-Build-mono | @steveisok | @akoeplinger | | | area-Codegen-AOT-mono | @SamMonoRT | @vargaz | | | area-CodeGen-coreclr | @JulieLeeMSFT | @BruceForstall @dotnet/jit-contrib | | @@ -105,7 +105,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-System.Numerics | @jeffhandley | @pgovind @tannergooding | | | area-System.Numerics.Tensors | @jeffhandley | @pgovind @tannergooding | | | area-System.Reflection | @jeffhandley | @buyaa-n @joperezr @krwq @steveharter | Consultants: @GrabYourPitchforks | -| area-System.Reflection-mono | @SamMonoRT | @lambdageek @CoffeeFlux | MonoVM-specific reflection and reflection-emit issues | +| area-System.Reflection-mono | @SamMonoRT | @lambdageek | MonoVM-specific reflection and reflection-emit issues | | area-System.Reflection.Emit | @jeffhandley | @buyaa-n @joperezr @krwq @steveharter | Consultants: @GrabYourPitchforks | | area-System.Reflection.Metadata | @jeffhandley | @buyaa-n @joperezr @krwq @steveharter | Consultants: @GrabYourPitchforks @tmat | | area-System.Resources | @jeffhandley | @buyaa-n @joperezr @krwq | | @@ -136,7 +136,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-TypeSystem-coreclr | @mangod9 | @davidwrighton @MichalStrehovsky @janvorli @mangod9 | | | area-UWP | @tommcdon | @jashook | UWP-specific issues including Microsoft.NETCore.UniversalWindowsPlatform and Microsoft.Net.UWPCoreRuntimeSdk | | area-VM-coreclr | @mangod9 | @mangod9 | | -| area-VM-meta-mono | @SamMonoRT | @lambdageek @CoffeeFlux | | +| area-VM-meta-mono | @SamMonoRT | @lambdageek | | ## Operating Systems From f3bad4f82787f3f2f0e949c750cd3c6f78705468 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 29 Jun 2021 10:29:15 -0400 Subject: [PATCH 179/926] [mono] Avoid printing out LLVM failed messages when verbosity level is 0. (#54885) --- src/mono/mono/mini/mini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index d03efcac914..ae87e446d39 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3327,7 +3327,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts if (COMPILE_LLVM (cfg)) { mono_llvm_check_method_supported (cfg); if (cfg->disable_llvm) { - if (cfg->verbose_level >= (cfg->llvm_only ? 0 : 1)) { + if (cfg->verbose_level > 0) { //nm = mono_method_full_name (cfg->method, TRUE); printf ("LLVM failed for '%s.%s': %s\n", m_class_get_name (method->klass), method->name, cfg->exception_message); //g_free (nm); From 8103a7e3f21f67bf85763ae5703d53c0aaf591ab Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:02:32 +0000 Subject: [PATCH 180/926] [main] Update dependencies from 11 repositories (#54741) [main] Update dependencies from 11 repositories - Address new IL3002 and IL3003 warnings for the latest linker to unblock the build. - Disable the trim analyzer for tests with EnableAggressiveTrimming - Merge branch 'main' into darc-main-997daf91-1bd1-4e82-9eac-1347a9231dc5 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 212 +++++++++--------- eng/Versions.props | 96 ++++---- eng/common/tools.ps1 | 11 +- eng/testing/tests.mobile.targets | 2 + global.json | 10 +- .../System/Reflection/Emit/AssemblyBuilder.cs | 1 + .../src/System/Reflection/RuntimeAssembly.cs | 1 + .../System/Reflection/Emit/AssemblyBuilder.cs | 1 + .../Context/Delegation/DelegatingAssembly.cs | 4 + .../TypeLoading/Assemblies/RoAssembly.cs | 3 + .../src/System/Reflection/RuntimeAssembly.cs | 3 + 12 files changed, 185 insertions(+), 161 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 14ccd5e0cfd..7cdc2d3bf2f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21324.2", + "version": "1.0.0-prerelease.21328.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b8b08594ccc..72b57d3755a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 59588c1257a842089d0b7df3bad1cdd69ac720e1 + 9045735745b259d81d6754122992ad1183fa4b83 https://github.com/dotnet/msquic @@ -10,221 +10,221 @@ - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/dotnet/runtime - f891033db5b8ebf651176a3dcc3bec74a217f85e + 91599fff26619cb8bccb7b39c39590e7cd9a4379 - + https://github.com/mono/linker - c739a81ba553b00df1cb2f5b9974deae996b757a + 1c0138c71850bdab14d740e9c4c8808ed034dc51 - + https://github.com/dotnet/xharness - 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 + 0bc5e50dff0583fda68f52cfcd39c253e0dacbbe - + https://github.com/dotnet/xharness - 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 + 0bc5e50dff0583fda68f52cfcd39c253e0dacbbe - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + a68ec1edf328e737b31a09cb49e1929c28e91d0c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da - + https://github.com/dotnet/emsdk - 617928847d1e11458527b8bbafb5577982291847 + d2873833db748a95140f507d639cbdfdf78d4fd7 - + https://github.com/dotnet/hotreload-utils - 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d + 0451564eb2f0ec1e1b614a21ee3836d24baf2572 - + https://github.com/dotnet/runtime-assets - 8d7b898b96cbdb868cac343e938173105287ed9e + 6e96a8810e85650ec9d5618d5ede5ce14c964d2a - + https://github.com/dotnet/roslyn-analyzers - fcddb771f42866f9521f23f093b1f30e129018bb + c801eb6e3532e2b6f36d9559c9174d1c7cf14c09 diff --git a/eng/Versions.props b/eng/Versions.props index 60393072c37..52372a8cdde 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -48,30 +48,30 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-rc1.21320.2 + 6.0.0-rc1.21324.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 2.5.1-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 2.5.1-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 + 6.0.0-beta.21324.3 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21328.2 3.1.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21328.2 5.0.0 4.3.0 @@ -105,27 +105,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21328.2 4.5.4 4.5.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21328.2 - 6.0.0-beta.21314.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 + 6.0.0-beta.21323.1 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21328.3 + 1.0.0-prerelease.21328.3 + 1.0.0-prerelease.21328.3 + 1.0.0-prerelease.21328.3 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -149,9 +149,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21324.2 - 1.0.0-prerelease.21324.2 - 1.0.1-alpha.0.21314.1 + 1.0.0-prerelease.21328.2 + 1.0.0-prerelease.21328.2 + 1.0.1-alpha.0.21321.1 2.4.1 2.4.2 1.3.0 @@ -162,23 +162,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21317.4 + 6.0.100-preview.6.21327.1 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21315.3 + 6.0.0-preview.7.21321.1 6.0.0-preview.7.21328.2 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21321.1 - 6.0.0-preview.7.21323.1 + 6.0.0-preview.7.21328.1 $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 5619c7aaee1..7942ffaf4cb 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -378,7 +378,16 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } - return $global:_MSBuildExe = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin\msbuild.exe" + + $local:BinFolder = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin" + $local:Prefer64bit = if ($vsRequirements.Prefer64bit) { $vsRequirements.Prefer64bit } else { $false } + if ($local:Prefer64bit -and (Test-Path(Join-Path $local:BinFolder "amd64"))) { + $global:_MSBuildExe = Join-Path $local:BinFolder "amd64\msbuild.exe" + } else { + $global:_MSBuildExe = Join-Path $local:BinFolder "msbuild.exe" + } + + return $global:_MSBuildExe } function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) { diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 83d95eae008..517ebf71985 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -13,6 +13,8 @@ true true + + false false diff --git a/global.json b/global.json index 433797e8116..b7a4121984d 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21324.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21324.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21324.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21324.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21328.2" } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 845c5391eb2..a0faa6ad6e0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -104,6 +104,7 @@ namespace System.Reflection.Emit public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); [RequiresUnreferencedCode("Types might be removed")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 89fb20181a8..dc866cf6ba3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -83,6 +83,7 @@ namespace System.Reflection return null; } + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 70133628f19..5dbdeeb3a67 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -8,6 +8,7 @@ namespace System.Reflection.Emit { public sealed partial class AssemblyBuilder : Assembly { + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override MethodInfo? EntryPoint => null; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 64c98012957..2c7f9d872ed 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -22,11 +22,13 @@ namespace System.Reflection.Context.Delegation UnderlyingAssembly = assembly; } +#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } } +#pragma warning restore IL3003 public override Module ManifestModule { @@ -100,6 +102,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetExportedTypes(); } +#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream GetFile(string name) { @@ -117,6 +120,7 @@ namespace System.Reflection.Context.Delegation { return UnderlyingAssembly.GetFiles(getResourceModules); } +#pragma warning restore IL3003 public override Module[] GetLoadedModules(bool getResourceModules) { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs index 22b3a009390..949994d04b3 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.Serialization; @@ -43,10 +44,12 @@ namespace System.Reflection.TypeLoading public abstract override string Location { get; } #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string CodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string EscapedCodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 602cdc66876..204f43e0e98 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -74,6 +74,7 @@ namespace System.Reflection public override bool ReflectionOnly => false; + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get @@ -252,7 +253,9 @@ namespace System.Reflection public override AssemblyName GetName(bool copiedName) { +#pragma warning disable IL3002 // Suppressing for now. See https://github.com/dotnet/runtime/issues/54835 return AssemblyName.Create(_mono_assembly, CodeBase); +#pragma warning restore IL3002 } [RequiresUnreferencedCode("Types might be removed")] From 6f7f30e118d6652a89dd7261e31d33ecd75f66fe Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 29 Jun 2021 18:18:36 +0200 Subject: [PATCH 181/926] Revert "[main] Update dependencies from 11 repositories (#54741)" (#54909) This reverts commit 8103a7e3f21f67bf85763ae5703d53c0aaf591ab. --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 212 +++++++++--------- eng/Versions.props | 96 ++++---- eng/common/tools.ps1 | 11 +- eng/testing/tests.mobile.targets | 2 - global.json | 10 +- .../System/Reflection/Emit/AssemblyBuilder.cs | 1 - .../src/System/Reflection/RuntimeAssembly.cs | 1 - .../System/Reflection/Emit/AssemblyBuilder.cs | 1 - .../Context/Delegation/DelegatingAssembly.cs | 4 - .../TypeLoading/Assemblies/RoAssembly.cs | 3 - .../src/System/Reflection/RuntimeAssembly.cs | 3 - 12 files changed, 161 insertions(+), 185 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7cdc2d3bf2f..14ccd5e0cfd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21328.2", + "version": "1.0.0-prerelease.21324.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 72b57d3755a..b8b08594ccc 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 9045735745b259d81d6754122992ad1183fa4b83 + 59588c1257a842089d0b7df3bad1cdd69ac720e1 https://github.com/dotnet/msquic @@ -10,221 +10,221 @@ - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 - + https://github.com/dotnet/llvm-project - 26f8bf7b3d57b59dd3b3bd7269b3303e87b298df + 4f0293e0a254a2f014643ecbe973b81f26c87fd4 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/dotnet/runtime - 91599fff26619cb8bccb7b39c39590e7cd9a4379 + f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/mono/linker - 1c0138c71850bdab14d740e9c4c8808ed034dc51 + c739a81ba553b00df1cb2f5b9974deae996b757a - + https://github.com/dotnet/xharness - 0bc5e50dff0583fda68f52cfcd39c253e0dacbbe + 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 - + https://github.com/dotnet/xharness - 0bc5e50dff0583fda68f52cfcd39c253e0dacbbe + 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 - + https://github.com/dotnet/arcade - a68ec1edf328e737b31a09cb49e1929c28e91d0c + 36b148348ee8312f6369c0c56b0d0fe07deec603 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da + f291c7f87a563f29ff2a9af7378495769d97389c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - acab64cb4dfe9fe1a7c1b77b45eda2d91327e9da + f291c7f87a563f29ff2a9af7378495769d97389c - + https://github.com/dotnet/emsdk - d2873833db748a95140f507d639cbdfdf78d4fd7 + 617928847d1e11458527b8bbafb5577982291847 - + https://github.com/dotnet/hotreload-utils - 0451564eb2f0ec1e1b614a21ee3836d24baf2572 + 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d - + https://github.com/dotnet/runtime-assets - 6e96a8810e85650ec9d5618d5ede5ce14c964d2a + 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/roslyn-analyzers - c801eb6e3532e2b6f36d9559c9174d1c7cf14c09 + fcddb771f42866f9521f23f093b1f30e129018bb diff --git a/eng/Versions.props b/eng/Versions.props index 52372a8cdde..60393072c37 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -48,30 +48,30 @@ 3.10.0-2.final 3.10.0-2.final - 6.0.0-rc1.21324.1 + 6.0.0-rc1.21320.2 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 2.5.1-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 - 6.0.0-beta.21324.3 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 2.5.1-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 + 6.0.0-beta.21321.1 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21328.2 - 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 3.1.0 - 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21321.2 5.0.0 4.3.0 @@ -105,27 +105,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21328.2 - 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21321.2 4.5.4 4.5.0 - 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21321.2 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 - 6.0.0-beta.21323.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21307.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 + 6.0.0-beta.21314.1 - 1.0.0-prerelease.21328.3 - 1.0.0-prerelease.21328.3 - 1.0.0-prerelease.21328.3 - 1.0.0-prerelease.21328.3 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21320.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -149,9 +149,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21328.2 - 1.0.0-prerelease.21328.2 - 1.0.1-alpha.0.21321.1 + 1.0.0-prerelease.21324.2 + 1.0.0-prerelease.21324.2 + 1.0.1-alpha.0.21314.1 2.4.1 2.4.2 1.3.0 @@ -162,23 +162,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21327.1 + 6.0.100-preview.6.21317.4 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21321.1 + 6.0.0-preview.7.21315.3 6.0.0-preview.7.21328.2 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 - 11.1.0-alpha.1.21321.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21314.1 - 6.0.0-preview.7.21328.1 + 6.0.0-preview.7.21323.1 $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 7942ffaf4cb..5619c7aaee1 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -378,16 +378,7 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } - - $local:BinFolder = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin" - $local:Prefer64bit = if ($vsRequirements.Prefer64bit) { $vsRequirements.Prefer64bit } else { $false } - if ($local:Prefer64bit -and (Test-Path(Join-Path $local:BinFolder "amd64"))) { - $global:_MSBuildExe = Join-Path $local:BinFolder "amd64\msbuild.exe" - } else { - $global:_MSBuildExe = Join-Path $local:BinFolder "msbuild.exe" - } - - return $global:_MSBuildExe + return $global:_MSBuildExe = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin\msbuild.exe" } function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) { diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 517ebf71985..83d95eae008 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -13,8 +13,6 @@ true true - - false false diff --git a/global.json b/global.json index b7a4121984d..433797e8116 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21324.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21324.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21324.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21324.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21328.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index a0faa6ad6e0..845c5391eb2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -104,7 +104,6 @@ namespace System.Reflection.Emit public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); [RequiresUnreferencedCode("Types might be removed")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index dc866cf6ba3..89fb20181a8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -83,7 +83,6 @@ namespace System.Reflection return null; } - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 5dbdeeb3a67..70133628f19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -8,7 +8,6 @@ namespace System.Reflection.Emit { public sealed partial class AssemblyBuilder : Assembly { - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override MethodInfo? EntryPoint => null; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 2c7f9d872ed..64c98012957 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -22,13 +22,11 @@ namespace System.Reflection.Context.Delegation UnderlyingAssembly = assembly; } -#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } } -#pragma warning restore IL3003 public override Module ManifestModule { @@ -102,7 +100,6 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetExportedTypes(); } -#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream GetFile(string name) { @@ -120,7 +117,6 @@ namespace System.Reflection.Context.Delegation { return UnderlyingAssembly.GetFiles(getResourceModules); } -#pragma warning restore IL3003 public override Module[] GetLoadedModules(bool getResourceModules) { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs index 949994d04b3..22b3a009390 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.Serialization; @@ -44,12 +43,10 @@ namespace System.Reflection.TypeLoading public abstract override string Location { get; } #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string CodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string EscapedCodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 204f43e0e98..602cdc66876 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -74,7 +74,6 @@ namespace System.Reflection public override bool ReflectionOnly => false; - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get @@ -253,9 +252,7 @@ namespace System.Reflection public override AssemblyName GetName(bool copiedName) { -#pragma warning disable IL3002 // Suppressing for now. See https://github.com/dotnet/runtime/issues/54835 return AssemblyName.Create(_mono_assembly, CodeBase); -#pragma warning restore IL3002 } [RequiresUnreferencedCode("Types might be removed")] From 9fb6e818cda2e4364e085fe0b8ecebed97db9596 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 29 Jun 2021 09:43:48 -0700 Subject: [PATCH 182/926] Handle marker types with more than 1 generic argument correctly when they must be materialized into full types (#54875) --- src/coreclr/vm/methodtable.cpp | 8 ++++++-- src/coreclr/vm/methodtable.h | 2 ++ src/coreclr/vm/methodtablebuilder.cpp | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 3539acaf79f..a7f41cce5cd 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -9820,8 +9820,12 @@ PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMT MethodTable *pResult = m_pMap->GetMethodTable(); if (pResult->IsSpecialMarkerTypeForGenericCasting()) { - TypeHandle ownerAsInst(pMTOwner); - Instantiation inst(&ownerAsInst, 1); + TypeHandle ownerAsInst[MaxGenericParametersForSpecialMarkerType]; + for (DWORD i = 0; i < MaxGenericParametersForSpecialMarkerType; i++) + ownerAsInst[i] = pMTOwner; + + _ASSERTE(pResult->GetInstantiation().GetNumArgs() <= MaxGenericParametersForSpecialMarkerType); + Instantiation inst(ownerAsInst, pResult->GetInstantiation().GetNumArgs()); pResult = ClassLoader::LoadGenericInstantiationThrowing(pResult->GetModule(), pResult->GetCl(), inst, ClassLoader::LoadTypes, loadLevel).AsMethodTable(); if (pResult->IsFullyLoaded()) SetInterface(pResult); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 3fbc6826ed2..476a1cd3faa 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -1254,6 +1254,8 @@ public: return IsGenericTypeDefinition(); } + static const DWORD MaxGenericParametersForSpecialMarkerType = 8; + static BOOL ComputeContainsGenericVariables(Instantiation inst); inline void SetContainsGenericVariables() diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 0d372a4659b..89926ffdaae 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -9147,8 +9147,9 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) if (uninstGenericCase && pItfPossiblyApprox->HasInstantiation() && pItfPossiblyApprox->ContainsGenericVariables()) { // We allow a limited set of interface generic shapes with type variables. In particular, we require the - // instantiations to be exactly simple type variables - if (InstantiationIsAllTypeVariables(pItfPossiblyApprox->GetInstantiation())) + // instantiations to be exactly simple type variables, and to have a relatively small number of generic arguments + // so that the fallback instantiating logic works efficiently + if (InstantiationIsAllTypeVariables(pItfPossiblyApprox->GetInstantiation()) && pItfPossiblyApprox->GetInstantiation().GetNumArgs() <= MethodTable::MaxGenericParametersForSpecialMarkerType) { pItfPossiblyApprox = ClassLoader::LoadTypeDefThrowing(pItfPossiblyApprox->GetModule(), pItfPossiblyApprox->GetCl(), ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_EXACTPARENTS).AsMethodTable(); } From 3ad32a5827616084cf2168f481ee57f11fb510ce Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 29 Jun 2021 20:06:39 +0300 Subject: [PATCH 183/926] Remove some unneeded code from division morphing (#53464) * Remove GTF_UNSIGNED check from the condition It is not necessary: GTF_UNSIGNED does not have anything to do with the operands being unsigned. Some positive diffs in runtime tests for win-x86 and one regression in System.Net.WebSockets.ManagedWebSocket.ApplyMask. The regressions is because we generate two "div"s for a long UMOD on x86 with a constant divisor, always, even for powers of two. Something to improve for sure. Naturally, no diffs for win-x64, linux-x64 or linux-arm. * Don't fold casts from constants in UMOD morphing It used to be that "ldc.i4.1 conv.i8" sequences survived importation, and since UMOD morphing is sensitive to constant divisors, morph tried to fold them. This is no longer the case, so stop doing that. Of course, morph can be called from anywhere at any point, but if some code is creating casts from constants, the proper place to fix is that code. No diffs for win-x86 or win-x64 or linux-arm. * Some code modernization Use modern helpers and move comments around. --- src/coreclr/jit/morph.cpp | 43 +++++++++------------------------------ 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 7cc40e6144e..64e852397a8 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11252,11 +11252,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) } #endif #endif // !TARGET_64BIT - - if (op2->gtOper == GT_CAST && op2->AsOp()->gtOp1->IsCnsIntOrI()) - { - op2 = gtFoldExprConst(op2); - } break; case GT_UDIV: @@ -11324,43 +11319,30 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) // Note for TARGET_ARMARCH we don't have a remainder instruction, so we don't do this optimization // #else // TARGET_XARCH - /* If this is an unsigned long mod with op2 which is a cast to long from a - constant int, then don't morph to a call to the helper. This can be done - faster inline using idiv. - */ + // If this is an unsigned long mod with a constant divisor, + // then don't morph to a helper call - it can be done faster inline using idiv. noway_assert(op2); - if ((typ == TYP_LONG) && opts.OptEnabled(CLFLG_CONSTANTFOLD) && - ((tree->gtFlags & GTF_UNSIGNED) == (op1->gtFlags & GTF_UNSIGNED)) && - ((tree->gtFlags & GTF_UNSIGNED) == (op2->gtFlags & GTF_UNSIGNED))) + if ((typ == TYP_LONG) && opts.OptEnabled(CLFLG_CONSTANTFOLD)) { - if (op2->gtOper == GT_CAST && op2->AsCast()->CastOp()->gtOper == GT_CNS_INT && - op2->AsCast()->CastOp()->AsIntCon()->gtIconVal >= 2 && - op2->AsCast()->CastOp()->AsIntCon()->gtIconVal <= 0x3fffffff && - (tree->gtFlags & GTF_UNSIGNED) == (op2->AsCast()->CastOp()->gtFlags & GTF_UNSIGNED)) - { - tree->AsOp()->gtOp2 = op2 = fgMorphCast(op2); - noway_assert(op2->gtOper == GT_CNS_NATIVELONG); - } - - if (op2->gtOper == GT_CNS_NATIVELONG && op2->AsIntConCommon()->LngValue() >= 2 && + if (op2->OperIs(GT_CNS_NATIVELONG) && op2->AsIntConCommon()->LngValue() >= 2 && op2->AsIntConCommon()->LngValue() <= 0x3fffffff) { tree->AsOp()->gtOp1 = op1 = fgMorphTree(op1); - noway_assert(op1->TypeGet() == TYP_LONG); + noway_assert(op1->TypeIs(TYP_LONG)); - // Update flags for op1 morph + // Update flags for op1 morph. tree->gtFlags &= ~GTF_ALL_EFFECT; - tree->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT); // Only update with op1 as op2 is a constant + // Only update with op1 as op2 is a constant. + tree->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT); - // If op1 is a constant, then do constant folding of the division operator - if (op1->gtOper == GT_CNS_NATIVELONG) + // If op1 is a constant, then do constant folding of the division operator. + if (op1->OperIs(GT_CNS_NATIVELONG)) { tree = gtFoldExpr(tree); } - // We may fail to fold if (!tree->OperIsConst()) { tree->AsOp()->CheckDivideByConstOptimized(this); @@ -11414,11 +11396,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #endif #endif // !TARGET_64BIT - if (op2->gtOper == GT_CAST && op2->AsOp()->gtOp1->IsCnsIntOrI()) - { - op2 = gtFoldExprConst(op2); - } - #ifdef TARGET_ARM64 // For ARM64 we don't have a remainder instruction, // The architecture manual suggests the following transformation to From 5f8a17717f1847aa5051397ee14bd55a7f6c7910 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Tue, 29 Jun 2021 10:40:16 -0700 Subject: [PATCH 184/926] Disable some recently enabled HTTP3 tests (#54843) * disable ConnectTimeout_TimesOutSSLAuth_Throws for HTTP3 * disable HTTP3 cookie tests Co-authored-by: Geoffrey Kizer --- .../FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs | 5 +++++ .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index bc4213b39a1..7319e42e684 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -29,6 +29,11 @@ namespace System.Net.Http.Functional.Tests [Fact] public async Task ConnectTimeout_TimesOutSSLAuth_Throws() { + if (UseVersion == HttpVersion.Version30) + { + return; + } + var releaseServer = new TaskCompletionSource(); await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 6b840eee8f5..9902e490ce5 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3097,6 +3097,7 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.Mock; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMsQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3_MsQuic : HttpClientHandlerTest_Cookies { @@ -3105,6 +3106,7 @@ namespace System.Net.Http.Functional.Tests protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.MsQuic; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/53093")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMockQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3_Mock : HttpClientHandlerTest_Cookies { From 5d29560a92ba4ef737581068087476685517c87c Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Tue, 29 Jun 2021 19:59:58 +0200 Subject: [PATCH 185/926] Avoid sending EndStream after RST_STREAM with dedicated lock (#54552) A race condition between sending RST_STREAM and checking conditions for sending EndStream was discovered during stress testing. It happens to be possible that in time after Http2Stream [checked the _responseCompletionState](https://github.com/dotnet/runtime/blob/183c4d100f68fb6c177a1fe71809d581aa25e47b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L275) and [actually send EndStream](https://github.com/dotnet/runtime/blob/183c4d100f68fb6c177a1fe71809d581aa25e47b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L288), a concurrent call to [Cancel method sends a RST_STREAM frame](https://github.com/dotnet/runtime/blob/183c4d100f68fb6c177a1fe71809d581aa25e47b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L389). Such reordering is disallowed by HTTP/2 protocol. Note: The issue and fix were verified manually under the debugger because currently it's not clear how to reliably simulate that situation. Fixes #42200 --- .../Http/SocketsHttpHandler/Http2Stream.cs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index d7d7a889703..4297c50fc0b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -245,9 +245,10 @@ namespace System.Net.Http _requestCompletionState = StreamCompletionState.Failed; Complete(); + + SendReset(); } - SendReset(); if (signalWaiter) { _waitSource.SetResult(true); @@ -275,17 +276,17 @@ namespace System.Net.Http sendReset = _responseCompletionState == StreamCompletionState.Failed; Complete(); } - } - if (sendReset) - { - SendReset(); - } - else - { - // Send EndStream asynchronously and without cancellation. - // If this fails, it means that the connection is aborting and we will be reset. - _connection.LogExceptions(_connection.SendEndStreamAsync(StreamId)); + if (sendReset) + { + SendReset(); + } + else + { + // Send EndStream asynchronously and without cancellation. + // If this fails, it means that the connection is aborting and we will be reset. + _connection.LogExceptions(_connection.SendEndStreamAsync(StreamId)); + } } } } @@ -325,7 +326,7 @@ namespace System.Net.Http private void SendReset() { - Debug.Assert(!Monitor.IsEntered(SyncObject)); + Debug.Assert(Monitor.IsEntered(SyncObject)); Debug.Assert(_requestCompletionState != StreamCompletionState.InProgress); Debug.Assert(_responseCompletionState != StreamCompletionState.InProgress); Debug.Assert(_requestCompletionState == StreamCompletionState.Failed || _responseCompletionState == StreamCompletionState.Failed, @@ -336,6 +337,8 @@ namespace System.Net.Http // Don't send a RST_STREAM if we've already received one from the server. if (_resetException == null) { + // If execution reached this line, it's guaranteed that + // _requestCompletionState == StreamCompletionState.Failed or _responseCompletionState == StreamCompletionState.Failed _connection.LogExceptions(_connection.SendRstStreamAsync(StreamId, Http2ProtocolErrorCode.Cancel)); } } @@ -384,9 +387,12 @@ namespace System.Net.Http // When cancellation propagates, SendRequestBodyAsync will set _requestCompletionState to Failed requestBodyCancellationSource?.Cancel(); - if (sendReset) + lock (SyncObject) { - SendReset(); + if (sendReset) + { + SendReset(); + } } if (signalWaiter) From 53e6b4744109d574f80b2cdf94878f5c3c166a17 Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Tue, 29 Jun 2021 22:07:52 +0300 Subject: [PATCH 186/926] Sgen: Update progress when importing types (#46415) * Sgen: Update progress when importing types * Null check * Remove trailing whitespace * Updated Strings.resx * Sgen: Use string resource --- .../src/Sgen.cs | 17 +++++++++++------ .../src/Resources/Strings.resx | 3 +++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs b/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs index 47771d892ab..00cff157363 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs @@ -36,7 +36,7 @@ namespace Microsoft.XmlSerializer.Generator bool noLogo = false; bool parsableErrors = false; bool silent = false; - bool warnings = false; + bool verbose = false; AppDomain.CurrentDomain.AssemblyResolve += SgenAssemblyResolver; @@ -120,7 +120,7 @@ namespace Microsoft.XmlSerializer.Generator } else if (ArgumentMatch(arg, "verbose")) { - warnings = true; + verbose = true; } else if (ArgumentMatch(arg, "reference")) { @@ -189,7 +189,7 @@ namespace Microsoft.XmlSerializer.Generator ParseReferences(); } - GenerateFile(types, assembly, proxyOnly, silent, warnings, force, codePath, parsableErrors); + GenerateFile(types, assembly, proxyOnly, silent, verbose, force, codePath, parsableErrors); } catch (Exception e) { @@ -205,7 +205,7 @@ namespace Microsoft.XmlSerializer.Generator return 0; } - private void GenerateFile(List typeNames, string assemblyName, bool proxyOnly, bool silent, bool warnings, bool force, string outputDirectory, bool parsableerrors) + private void GenerateFile(List typeNames, string assemblyName, bool proxyOnly, bool silent, bool verbose, bool force, string outputDirectory, bool parsableerrors) { Assembly assembly = LoadAssembly(assemblyName, true); Type[] types; @@ -258,6 +258,11 @@ namespace Microsoft.XmlSerializer.Generator { if (type != null) { + if (verbose) + { + Console.WriteLine(SR.Format(SR.ImportInfo, type.Name, i + 1, types.Length)); + } + bool isObsolete = false; object[] obsoleteAttributes = type.GetCustomAttributes(typeof(ObsoleteAttribute), false); foreach (object attribute in obsoleteAttributes) @@ -278,7 +283,7 @@ namespace Microsoft.XmlSerializer.Generator //Ignore the FileNotFoundException when call GetCustomAttributes e.g. if the type uses the attributes defined in a different assembly catch (FileNotFoundException e) { - if (warnings) + if (verbose) { Console.Out.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoIgnoreType, type.FullName))); WriteWarning(e, parsableerrors); @@ -289,7 +294,7 @@ namespace Microsoft.XmlSerializer.Generator if (!proxyOnly) { - ImportType(type, mappings, importedTypes, warnings, importer, parsableerrors); + ImportType(type, mappings, importedTypes, verbose, importer, parsableerrors); } } diff --git a/src/libraries/System.Private.Xml/src/Resources/Strings.resx b/src/libraries/System.Private.Xml/src/Resources/Strings.resx index d8fe00461bf..d19dc6ec4eb 100644 --- a/src/libraries/System.Private.Xml/src/Resources/Strings.resx +++ b/src/libraries/System.Private.Xml/src/Resources/Strings.resx @@ -3453,4 +3453,7 @@ Usage: dotnet {0} [--assembly <assembly file path>] [--type <type name& Compiling JScript/CSharp scripts is not supported + + Importing {0} ({1}/{2}) + From b056b7b6a1eca5171e42f697a0b3d3c60d1fc048 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 29 Jun 2021 22:15:47 +0200 Subject: [PATCH 187/926] Add NetCoreAppCurrent configuration to libs (Phase 1) (#54544) * Use property to indicate NetCoreApp min version * Add NetCoreAppCurrent to S.Numerics.Tensors * Add NetCoreAppCurrent to S.T.AccessControl * Add NetCoreAppCurrent to System.Memory.Data * Add NetCoreAppCurrent to S.Composition.* projects * Add NetCoreAppCurrent to System.IO.Packaging * Add NetCoreAppCurrent config to System.IO.Ports Adding a NetCoreAppCurrent configuration to System.IO.Ports and reducing package size, build times and platform specific assets by using runtime checks on Unix derivates for the slightly diverging SerialPort implementation. Contributes to https://github.com/dotnet/runtime/issues/54012 * Add NetCoreAppCurrent to System.Data.OleDb * Add NetCoreAppCurrent to M.W32.Registry.AccessControl * Add NetCoreAppCurrent to S.Reflection.Context * Add NetCoreAppCurrent to S.C.Composition.Registration * Add NetCoreAppCurrent to S.Resources.Extensions * Add NetCoreAppCurrent to S.N.H.WinHttpHandler --- Directory.Build.props | 1 + ...icrosoft.Extensions.Logging.Console.csproj | 2 +- ...soft.Extensions.Logging.EventSource.csproj | 2 +- .../Microsoft.Extensions.Primitives.csproj | 2 +- ...osoft.Win32.Registry.AccessControl.pkgproj | 1 + ...rosoft.Win32.Registry.AccessControl.csproj | 11 +++- ...rosoft.Win32.Registry.AccessControl.csproj | 13 +++- ....Win32.Registry.AccessControl.Tests.csproj | 3 - .../pkg/Microsoft.Win32.SystemEvents.pkgproj | 2 +- ...nentModel.Composition.Registration.pkgproj | 1 + ...onentModel.Composition.Registration.csproj | 8 ++- ...onentModel.Composition.Registration.csproj | 12 +++- ...odel.Composition.Registration.Tests.csproj | 1 + .../System.ComponentModel.Composition.pkgproj | 2 +- ...System.Composition.AttributedModel.pkgproj | 1 + .../System.Composition.AttributedModel.csproj | 7 ++- .../pkg/System.Composition.Convention.pkgproj | 1 + .../src/System.Composition.Convention.csproj | 12 +++- .../pkg/System.Composition.Hosting.pkgproj | 1 + .../src/System.Composition.Hosting.csproj | 16 ++++- .../pkg/System.Composition.Runtime.pkgproj | 1 + .../src/System.Composition.Runtime.csproj | 9 ++- .../pkg/System.Composition.TypedParts.pkgproj | 1 + .../src/System.Composition.TypedParts.csproj | 11 +++- .../pkg/System.Data.Odbc.pkgproj | 2 +- .../pkg/System.Data.OleDb.pkgproj | 1 + .../ref/System.Data.OleDb.cs | 4 +- .../ref/System.Data.OleDb.csproj | 11 +++- .../System.Data.OleDb/src/OleDbDataAdapter.cs | 2 +- .../System.Data.OleDb/src/OleDbDataReader.cs | 8 +-- .../src/OleDbMetaDataFactory.cs | 4 +- .../System.Data.OleDb/src/OleDbTransaction.cs | 2 +- .../src/System.Data.OleDb.csproj | 52 ++++++++++++--- .../Data/ProviderBase/DbMetaDataFactory.cs | 12 ++-- .../pkg/System.Diagnostics.EventLog.pkgproj | 2 +- .../src/System.Diagnostics.EventLog.csproj | 1 + ...tem.Diagnostics.PerformanceCounter.pkgproj | 2 +- ...irectoryServices.AccountManagement.pkgproj | 2 +- ...System.DirectoryServices.Protocols.pkgproj | 2 +- .../pkg/System.DirectoryServices.pkgproj | 2 +- .../pkg/System.Drawing.Common.pkgproj | 2 +- .../pkg/System.IO.Packaging.pkgproj | 1 + .../ref/System.IO.Packaging.csproj | 10 ++- .../src/System.IO.Packaging.csproj | 14 ++++- .../System/IO/Packaging/PackagingUtilities.cs | 2 +- .../Packaging/PartBasedPackageProperties.cs | 2 +- .../IO/Packaging/XmlCompatibilityReader.cs | 16 ++--- .../System/IO/Packaging/XmlWrappingReader.cs | 18 +++--- .../src/System/IO/Packaging/ZipPackage.cs | 20 +++--- .../pkg/System.IO.Pipelines.pkgproj | 2 +- .../pkg/System.IO.Ports.pkgproj | 1 + .../ref/System.IO.Ports.csproj | 7 ++- .../src/System.IO.Ports.csproj | 40 ++++++++---- .../src/System/IO/Ports/SerialPort.FreeBSD.cs | 36 ----------- .../src/System/IO/Ports/SerialPort.OSX.cs | 38 ----------- ...SerialPort.Linux.cs => SerialPort.Unix.cs} | 63 +++++++++++++++++++ .../src/System/IO/Ports/SerialPort.Win32.cs | 4 -- .../src/System/IO/Ports/SerialStream.cs | 5 ++ .../pkg/System.Management.pkgproj | 2 +- .../pkg/System.Memory.Data.pkgproj | 1 + .../ref/System.Memory.Data.cs | 2 +- .../ref/System.Memory.Data.csproj | 7 ++- .../src/System.Memory.Data.csproj | 21 +++++-- .../src/System/BinaryData.cs | 4 +- .../System.Net.Http.WinHttpHandler.pkgproj | 1 + .../ref/System.Net.Http.WinHttpHandler.csproj | 11 +++- .../src/System.Net.Http.WinHttpHandler.csproj | 37 ++++++++--- .../System/Net/Http/WinHttpResponseStream.cs | 2 +- .../System/Net/Http/WinHttpTrailersHelper.cs | 4 +- .../pkg/System.Numerics.Tensors.pkgproj | 1 + .../ref/System.Numerics.Tensors.cs | 16 ++--- .../ref/System.Numerics.Tensors.csproj | 10 ++- .../src/System.Numerics.Tensors.csproj | 13 +++- .../System/Numerics/Tensors/SparseTensor.cs | 2 +- .../src/System/Numerics/Tensors/Tensor.cs | 6 +- .../src/System/IO/PinnedBufferMemoryStream.cs | 2 +- .../pkg/System.Reflection.Context.pkgproj | 1 + .../ref/System.Reflection.Context.csproj | 7 ++- .../src/System.Reflection.Context.csproj | 18 ++++-- .../Context/Delegation/DelegatingAssembly.cs | 11 ++++ ...tem.Reflection.MetadataLoadContext.pkgproj | 2 +- .../pkg/System.Resources.Extensions.pkgproj | 1 + .../ref/System.Resources.Extensions.csproj | 7 ++- .../src/System.Resources.Extensions.csproj | 18 +++++- .../Extensions/DeserializingResourceReader.cs | 5 +- ...em.Runtime.CompilerServices.Unsafe.pkgproj | 2 +- .../System.Security.Cryptography.Pkcs.pkgproj | 2 +- .../pkg/System.Security.Permissions.pkgproj | 2 +- .../System.ServiceModel.Syndication.pkgproj | 2 +- .../System.Speech/pkg/System.Speech.pkgproj | 2 +- .../System.Text.Encoding.CodePages.pkgproj | 2 +- .../pkg/System.Text.Json.pkgproj | 2 +- .../System.Threading.AccessControl.pkgproj | 1 + .../ref/System.Threading.AccessControl.csproj | 7 ++- .../src/System.Threading.AccessControl.csproj | 9 ++- .../pkg/System.Threading.Channels.pkgproj | 2 +- 96 files changed, 516 insertions(+), 240 deletions(-) delete mode 100644 src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.FreeBSD.cs delete mode 100644 src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.OSX.cs rename src/libraries/System.IO.Ports/src/System/IO/Ports/{SerialPort.Linux.cs => SerialPort.Unix.cs} (60%) diff --git a/Directory.Build.props b/Directory.Build.props index e2c0de947f3..53be3bd5c9b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -44,6 +44,7 @@ .NETCoreApp $(NetCoreAppCurrentIdentifier),Version=v$(NetCoreAppCurrentVersion) net$(NetCoreAppCurrentVersion) + netcoreapp3.1 net$(AspNetCoreAppCurrentVersion) net$(NetCoreAppToolCurrentVersion) $(NetCoreAppCurrentIdentifier),Version=v$(NetCoreAppToolCurrentVersion) diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index 271f495a8dc..0530b082350 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -57,7 +57,7 @@ - + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj index 3823f1cfac8..61ab1f52eea 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj index 25f981a3803..7fbed60e9ee 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj +++ b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj @@ -31,6 +31,6 @@ Microsoft.Extensions.Primitives.StringSegment - + diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/pkg/Microsoft.Win32.Registry.AccessControl.pkgproj b/src/libraries/Microsoft.Win32.Registry.AccessControl/pkg/Microsoft.Win32.Registry.AccessControl.pkgproj index 95a5015b3ce..6a5c8b77be0 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/pkg/Microsoft.Win32.Registry.AccessControl.pkgproj +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/pkg/Microsoft.Win32.Registry.AccessControl.pkgproj @@ -7,6 +7,7 @@ + \ No newline at end of file diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.csproj b/src/libraries/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.csproj index c6e8d3fbf20..5f89098d521 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.csproj +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.csproj @@ -1,14 +1,23 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/Microsoft.Win32.Registry.AccessControl.csproj b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/Microsoft.Win32.Registry.AccessControl.csproj index f66a53a0840..82538c4efba 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/Microsoft.Win32.Registry.AccessControl.csproj +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/Microsoft.Win32.Registry.AccessControl.csproj @@ -1,17 +1,26 @@ true - netstandard2.0-windows;netstandard2.0;net461-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netstandard2.0-windows;netstandard2.0;net461-windows enable + SR.PlatformNotSupported_RegistryAccessControl - true + true + + + + + + + + diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/tests/Microsoft.Win32.Registry.AccessControl.Tests.csproj b/src/libraries/Microsoft.Win32.Registry.AccessControl/tests/Microsoft.Win32.Registry.AccessControl.Tests.csproj index 1752e89bc52..fb6e2d19699 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/tests/Microsoft.Win32.Registry.AccessControl.Tests.csproj +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/tests/Microsoft.Win32.Registry.AccessControl.Tests.csproj @@ -8,7 +8,4 @@ - - - \ No newline at end of file diff --git a/src/libraries/Microsoft.Win32.SystemEvents/pkg/Microsoft.Win32.SystemEvents.pkgproj b/src/libraries/Microsoft.Win32.SystemEvents/pkg/Microsoft.Win32.SystemEvents.pkgproj index 5ef768cc078..6d9e4dc673c 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/pkg/Microsoft.Win32.SystemEvents.pkgproj +++ b/src/libraries/Microsoft.Win32.SystemEvents/pkg/Microsoft.Win32.SystemEvents.pkgproj @@ -5,7 +5,7 @@ uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - + \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.Composition.Registration/pkg/System.ComponentModel.Composition.Registration.pkgproj b/src/libraries/System.ComponentModel.Composition.Registration/pkg/System.ComponentModel.Composition.Registration.pkgproj index 2ef483eed72..4c1d0b70715 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/pkg/System.ComponentModel.Composition.Registration.pkgproj +++ b/src/libraries/System.ComponentModel.Composition.Registration/pkg/System.ComponentModel.Composition.Registration.pkgproj @@ -9,6 +9,7 @@ true + diff --git a/src/libraries/System.ComponentModel.Composition.Registration/ref/System.ComponentModel.Composition.Registration.csproj b/src/libraries/System.ComponentModel.Composition.Registration/ref/System.ComponentModel.Composition.Registration.csproj index d99d4ac486f..c92f8b23489 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/ref/System.ComponentModel.Composition.Registration.csproj +++ b/src/libraries/System.ComponentModel.Composition.Registration/ref/System.ComponentModel.Composition.Registration.csproj @@ -1,12 +1,14 @@ - netstandard2.1 + $(NetCoreAppCurrent);netstandard2.1 + + - - + + \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/System.ComponentModel.Composition.Registration.csproj b/src/libraries/System.ComponentModel.Composition.Registration/src/System.ComponentModel.Composition.Registration.csproj index cabd1dda2f2..1e12a946d79 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/src/System.ComponentModel.Composition.Registration.csproj +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/System.ComponentModel.Composition.Registration.csproj @@ -1,8 +1,9 @@ - netstandard2.1 + $(NetCoreAppCurrent);netstandard2.1 false + @@ -24,8 +25,17 @@ + + + + + + + + + diff --git a/src/libraries/System.ComponentModel.Composition.Registration/tests/System.ComponentModel.Composition.Registration.Tests.csproj b/src/libraries/System.ComponentModel.Composition.Registration/tests/System.ComponentModel.Composition.Registration.Tests.csproj index d079de0a2b6..df43866bc98 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/tests/System.ComponentModel.Composition.Registration.Tests.csproj +++ b/src/libraries/System.ComponentModel.Composition.Registration/tests/System.ComponentModel.Composition.Registration.Tests.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent) + disable diff --git a/src/libraries/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj b/src/libraries/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj index edf42f9de2e..49ae7cf0595 100644 --- a/src/libraries/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj +++ b/src/libraries/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj @@ -10,7 +10,7 @@ - + diff --git a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj b/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj index 340da4ff117..4c2c1ff8a36 100644 --- a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj +++ b/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj index 999ec09ad8f..7d49a3802f9 100644 --- a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj +++ b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj @@ -1,7 +1,8 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 + @@ -17,4 +18,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj b/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj index 8239d4a17e6..957c0d82866 100644 --- a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj +++ b/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj index 948a3a7934d..500ab5bb2a2 100644 --- a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj +++ b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj @@ -1,8 +1,9 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 false + @@ -21,7 +22,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj b/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj index dcc53d4d65c..d319cbd0c8e 100644 --- a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj +++ b/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj index 66c1f0e8fe1..437ad767826 100644 --- a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj +++ b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj @@ -1,8 +1,9 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 false + @@ -31,10 +32,21 @@ + - + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj b/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj index c72e14fe909..acdef61a398 100644 --- a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj +++ b/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj index cdd6654321f..b5ecf044a58 100644 --- a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj +++ b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj @@ -1,8 +1,9 @@ System.Composition - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 + @@ -12,4 +13,10 @@ + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj b/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj index e884c4bc186..e7ea55ed613 100644 --- a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj +++ b/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj index d22e4354ffe..c8828d012e6 100644 --- a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj +++ b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj @@ -1,9 +1,10 @@ System.Composition - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 false + @@ -29,9 +30,17 @@ + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Data.Odbc/pkg/System.Data.Odbc.pkgproj b/src/libraries/System.Data.Odbc/pkg/System.Data.Odbc.pkgproj index 3d67703c71c..1bf618240ba 100644 --- a/src/libraries/System.Data.Odbc/pkg/System.Data.Odbc.pkgproj +++ b/src/libraries/System.Data.Odbc/pkg/System.Data.Odbc.pkgproj @@ -5,7 +5,7 @@ uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - + diff --git a/src/libraries/System.Data.OleDb/pkg/System.Data.OleDb.pkgproj b/src/libraries/System.Data.OleDb/pkg/System.Data.OleDb.pkgproj index 363d30827ca..1f5f6789ca9 100644 --- a/src/libraries/System.Data.OleDb/pkg/System.Data.OleDb.pkgproj +++ b/src/libraries/System.Data.OleDb/pkg/System.Data.OleDb.pkgproj @@ -5,6 +5,7 @@ net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) + \ No newline at end of file diff --git a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.cs b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.cs index 9011a9f247d..a29a00d7a6a 100644 --- a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.cs +++ b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.cs @@ -179,7 +179,7 @@ namespace System.Data.OleDb public int Fill(System.Data.DataTable dataTable, object ADODBRecordSet) { throw null; } protected override void OnRowUpdated(System.Data.Common.RowUpdatedEventArgs value) { } protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs value) { } - object? System.ICloneable.Clone() { throw null; } + object System.ICloneable.Clone() { throw null; } } public sealed partial class OleDbDataReader : System.Data.Common.DbDataReader { @@ -363,7 +363,7 @@ namespace System.Data.OleDb public override object? Value { get { throw null; } set { } } public override void ResetDbType() { } public void ResetOleDbType() { } - object? System.ICloneable.Clone() { throw null; } + object System.ICloneable.Clone() { throw null; } public override string ToString() { throw null; } } [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBParametersEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] diff --git a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.csproj b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.csproj index b7a09b249da..a08c83b5cc1 100644 --- a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.csproj +++ b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.csproj @@ -1,20 +1,24 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 $(NoWarn);0618 enable + + - - + + + + @@ -22,5 +26,6 @@ + \ No newline at end of file diff --git a/src/libraries/System.Data.OleDb/src/OleDbDataAdapter.cs b/src/libraries/System.Data.OleDb/src/OleDbDataAdapter.cs index a9aa7d1d56b..9c6fb06c901 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbDataAdapter.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbDataAdapter.cs @@ -117,7 +117,7 @@ namespace System.Data.OleDb // prevent someone from registering two different command builders on the adapter by // silently removing the old one - if ((null != handler) && (value.Target is DbCommandBuilder)) + if ((null != handler) && (value!.Target is DbCommandBuilder)) { OleDbRowUpdatingEventHandler? d = (OleDbRowUpdatingEventHandler?)ADP.FindBuilder(handler); if (null != d) diff --git a/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs b/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs index f3b6a0e0117..e50ba5a23b1 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs @@ -968,7 +968,7 @@ namespace System.Data.OleDb OleDbDataReader? reader = null; - if (null != result) + if (result != null) { // only when the first datareader is closed will the connection close ChapterHandle chapterHandle = ChapterHandle.CreateChapterHandle(result, rowbinding, valueOffset); @@ -977,7 +977,7 @@ namespace System.Data.OleDb reader.BuildMetaInfo(); reader.HasRowsRead(); - if (null != _connection) + if (_connection != null) { // connection tracks all readers to prevent cmd from executing // until all readers (including nested) are closed @@ -985,7 +985,7 @@ namespace System.Data.OleDb } } - return reader; + return reader!; } public override string GetDataTypeName(int index) @@ -1730,7 +1730,6 @@ namespace System.Data.OleDb RowHandleBuffer rowHandleBuffer = _rowHandleNativeBuffer!; bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -1881,7 +1880,6 @@ namespace System.Data.OleDb bool mustReleaseBinding = false; bool[] mustRelease = new bool[columnBindings.Length]; StringMemHandle?[] sptr = new StringMemHandle[columnBindings.Length]; - RuntimeHelpers.PrepareConstrainedRegions(); try { diff --git a/src/libraries/System.Data.OleDb/src/OleDbMetaDataFactory.cs b/src/libraries/System.Data.OleDb/src/OleDbMetaDataFactory.cs index e978419c2f8..d2620c38b1c 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbMetaDataFactory.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbMetaDataFactory.cs @@ -214,13 +214,13 @@ namespace System.Data.OleDb StringBuilder compositeSeparatorPattern = new StringBuilder(); StringBuilder patternEscaped = new StringBuilder(); ADP.EscapeSpecialCharacters(catalogSeparatorPattern, patternEscaped); - compositeSeparatorPattern.Append(patternEscaped.ToString()); + compositeSeparatorPattern.Append(patternEscaped); if ((schemaSeparatorPattern != null) && (schemaSeparatorPattern != catalogSeparatorPattern)) { compositeSeparatorPattern.Append('|'); patternEscaped.Length = 0; ADP.EscapeSpecialCharacters(schemaSeparatorPattern, patternEscaped); - compositeSeparatorPattern.Append(patternEscaped.ToString()); + compositeSeparatorPattern.Append(patternEscaped); } dataSourceInformation[DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern] = compositeSeparatorPattern.ToString(); } diff --git a/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs b/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs index 26a20357af1..2181a6cbe75 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs @@ -181,7 +181,7 @@ namespace System.Data.OleDb } else if ((null != _nestedTransaction) && _nestedTransaction.IsAlive) { - throw ADP.ParallelTransactionsNotSupported(Connection); + throw ADP.ParallelTransactionsNotSupported(Connection!); } // either the connection will be open or this will be a zombie diff --git a/src/libraries/System.Data.OleDb/src/System.Data.OleDb.csproj b/src/libraries/System.Data.OleDb/src/System.Data.OleDb.csproj index 4140b529697..27ffeb6ecd3 100644 --- a/src/libraries/System.Data.OleDb/src/System.Data.OleDb.csproj +++ b/src/libraries/System.Data.OleDb/src/System.Data.OleDb.csproj @@ -1,16 +1,24 @@ true - netstandard2.0-windows;netstandard2.0;net461-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netstandard2.0-windows;netstandard2.0;net461-windows true enable + + $(NoWarn);CA2249 + + $(NoWarn);SYSLIB0004 + + $(NoWarn);CA1845 + true SR.PlatformNotSupported_OleDb - $(NoWarn);CS0618 + $(NoWarn);CS0618 + @@ -91,11 +99,43 @@ + - - System.Data.OleDb.OleDbMetaData.xml - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -103,7 +143,5 @@ - - diff --git a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbMetaDataFactory.cs b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbMetaDataFactory.cs index 68892d670c3..f9a4d529da0 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbMetaDataFactory.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbMetaDataFactory.cs @@ -130,10 +130,6 @@ namespace System.Data.ProviderBase DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[_collectionName]!; //DataColumn restrictionNameColumn = metaDataCollectionsTable.Columns[_restrictionName]; - DataTable? resultTable = null; - DbCommand? command = null; - DataTable? schemaTable = null; - Debug.Assert(requestedCollectionRow != null); string sqlCommand = (requestedCollectionRow[populationStringColumn, DataRowVersion.Current] as string)!; int numberOfRestrictions = (int)requestedCollectionRow[numberOfRestrictionsColumn, DataRowVersion.Current]; @@ -144,10 +140,11 @@ namespace System.Data.ProviderBase throw ADP.TooManyRestrictions(collectionName); } - command = connection.CreateCommand(); + DbCommand? command = connection.CreateCommand(); command.CommandText = sqlCommand; command.CommandTimeout = System.Math.Max(command.CommandTimeout, 180); + DataTable? resultTable = null; for (int i = 0; i < numberOfRestrictions; i++) { DbParameter restrictionParameter = command.CreateParameter(); @@ -190,8 +187,8 @@ namespace System.Data.ProviderBase resultTable = new DataTable(collectionName); resultTable.Locale = CultureInfo.InvariantCulture; - schemaTable = reader.GetSchemaTable(); - foreach (DataRow row in schemaTable.Rows) + DataTable? schemaTable = reader.GetSchemaTable(); + foreach (DataRow row in schemaTable!.Rows) { resultTable.Columns.Add(row["ColumnName"] as string, (Type)row["DataType"]); } @@ -210,6 +207,7 @@ namespace System.Data.ProviderBase reader = null; } } + return resultTable; } diff --git a/src/libraries/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj b/src/libraries/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj index 9e45e669acb..5c01f83bb66 100644 --- a/src/libraries/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj +++ b/src/libraries/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj @@ -5,7 +5,7 @@ uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj index a093eb35ac7..bfbb9347303 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj +++ b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj @@ -126,6 +126,7 @@ + diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj b/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj index 9b942dbe58e..2fef4179be2 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj @@ -6,7 +6,7 @@ - + diff --git a/src/libraries/System.DirectoryServices.AccountManagement/pkg/System.DirectoryServices.AccountManagement.pkgproj b/src/libraries/System.DirectoryServices.AccountManagement/pkg/System.DirectoryServices.AccountManagement.pkgproj index 86e4fd6273b..928e56d05b1 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/pkg/System.DirectoryServices.AccountManagement.pkgproj +++ b/src/libraries/System.DirectoryServices.AccountManagement/pkg/System.DirectoryServices.AccountManagement.pkgproj @@ -11,7 +11,7 @@ runtimes/win/lib/net461 - + \ No newline at end of file diff --git a/src/libraries/System.DirectoryServices.Protocols/pkg/System.DirectoryServices.Protocols.pkgproj b/src/libraries/System.DirectoryServices.Protocols/pkg/System.DirectoryServices.Protocols.pkgproj index 274c781251d..470c1346aba 100644 --- a/src/libraries/System.DirectoryServices.Protocols/pkg/System.DirectoryServices.Protocols.pkgproj +++ b/src/libraries/System.DirectoryServices.Protocols/pkg/System.DirectoryServices.Protocols.pkgproj @@ -12,7 +12,7 @@ runtimes/win/lib/net461 - + \ No newline at end of file diff --git a/src/libraries/System.DirectoryServices/pkg/System.DirectoryServices.pkgproj b/src/libraries/System.DirectoryServices/pkg/System.DirectoryServices.pkgproj index 6b392134854..c115ab6fa08 100644 --- a/src/libraries/System.DirectoryServices/pkg/System.DirectoryServices.pkgproj +++ b/src/libraries/System.DirectoryServices/pkg/System.DirectoryServices.pkgproj @@ -13,7 +13,7 @@ runtimes/win/lib/net461 - + \ No newline at end of file diff --git a/src/libraries/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj b/src/libraries/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj index d478641dc5e..16e0cc61ee4 100644 --- a/src/libraries/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj +++ b/src/libraries/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj @@ -6,7 +6,7 @@ - + diff --git a/src/libraries/System.IO.Packaging/pkg/System.IO.Packaging.pkgproj b/src/libraries/System.IO.Packaging/pkg/System.IO.Packaging.pkgproj index 6335a71313f..14c8ca9ff74 100644 --- a/src/libraries/System.IO.Packaging/pkg/System.IO.Packaging.pkgproj +++ b/src/libraries/System.IO.Packaging/pkg/System.IO.Packaging.pkgproj @@ -8,6 +8,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.IO.Packaging/ref/System.IO.Packaging.csproj b/src/libraries/System.IO.Packaging/ref/System.IO.Packaging.csproj index 3d0cb8b6ff7..c2ea960539a 100644 --- a/src/libraries/System.IO.Packaging/ref/System.IO.Packaging.csproj +++ b/src/libraries/System.IO.Packaging/ref/System.IO.Packaging.csproj @@ -1,13 +1,19 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable - + + + + + + + diff --git a/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj b/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj index 1f52d164d2d..abb18580621 100644 --- a/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj +++ b/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj @@ -1,13 +1,17 @@ true - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + + $(NoWarn);CA1847 + true + @@ -38,6 +42,14 @@ + + + + + + + + diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagingUtilities.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagingUtilities.cs index 32f97397141..f87baba3e95 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagingUtilities.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackagingUtilities.cs @@ -33,7 +33,7 @@ namespace System.IO.Packaging //If the first node is XmlDeclaration we check to see if the encoding attribute is present if (reader.Read() && reader.NodeType == XmlNodeType.XmlDeclaration && reader.Depth == 0) { - string encoding = reader.GetAttribute(EncodingAttribute); + string? encoding = reader.GetAttribute(EncodingAttribute); if (!string.IsNullOrEmpty(encoding)) { diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs index f4ed524180e..20885d57997 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs @@ -600,7 +600,7 @@ namespace System.IO.Packaging internal static void ValidateXsiType(XmlReader reader, object ns, string name) { // Get the value of xsi;type - string typeValue = reader.GetAttribute(PackageXmlStringTable.GetXmlString(PackageXmlEnum.Type), + string? typeValue = reader.GetAttribute(PackageXmlStringTable.GetXmlString(PackageXmlEnum.Type), PackageXmlStringTable.GetXmlString(PackageXmlEnum.XmlSchemaInstanceNamespace)); // Missing xsi:type diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs index d24b354558b..df65e29fdf1 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs @@ -405,11 +405,11 @@ namespace System.IO.Packaging /// /// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// - public override string? GetAttribute(string localName, string namespaceURI) + public override string? GetAttribute(string localName, string? namespaceURI) { string? result = null; - if (_ignoredAttributeCount == 0 || !ShouldIgnoreNamespace(namespaceURI)) + if (_ignoredAttributeCount == 0 || !ShouldIgnoreNamespace(namespaceURI!)) { // if the current element does not have any attributes that should be ignored or // the namespace provided is not ignorable, call Reader method @@ -503,7 +503,7 @@ namespace System.IO.Packaging /// /// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// - public override bool MoveToAttribute(string localName, string namespaceURI) + public override bool MoveToAttribute(string localName, string? namespaceURI) { bool result; @@ -518,7 +518,7 @@ namespace System.IO.Packaging result = Reader.MoveToAttribute(localName, namespaceURI); - if (result && ShouldIgnoreNamespace(namespaceURI)) + if (result && ShouldIgnoreNamespace(namespaceURI!)) { result = false; RestoreReaderPosition(); @@ -594,7 +594,7 @@ namespace System.IO.Packaging /// public override string? LookupNamespace(string prefix) { - string namespaceName = Reader.LookupNamespace(prefix); + string? namespaceName = Reader.LookupNamespace(prefix); if (namespaceName != null) { @@ -613,6 +613,7 @@ namespace System.IO.Packaging /// for the xmlns attribute reflects all the /// compatibility (subsuming) rules. /// +#pragma warning disable CS8764 // Nullability of return type doesn't match overridden member public override string? Value { get @@ -631,6 +632,7 @@ namespace System.IO.Packaging return Reader.Value; } } +#pragma warning restore CS8764 /// /// Gets the namespace URI (as defined in the W3C Namespace specification) of the node @@ -693,7 +695,7 @@ namespace System.IO.Packaging // Restore reader state from SaveReaderPosition if (_inAttribute) { - Reader.MoveToAttribute(_currentName); + Reader.MoveToAttribute(_currentName!); } else { @@ -1141,7 +1143,7 @@ namespace System.IO.Packaging Error(SR.XCRChoiceAfterFallback); } - string requiresValue = Reader.GetAttribute(Requires); + string? requiresValue = Reader.GetAttribute(Requires); if (requiresValue == null) { diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlWrappingReader.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlWrappingReader.cs index f94c528ebc4..370b566b5b0 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlWrappingReader.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlWrappingReader.cs @@ -22,14 +22,14 @@ namespace System.IO.Packaging Reader = baseReader; } - public override XmlReaderSettings Settings { get { return _reader.Settings; } } + public override XmlReaderSettings? Settings { get { return _reader.Settings; } } public override XmlNodeType NodeType { get { return _reader.NodeType; } } public override string Name { get { return _reader.Name; } } public override string LocalName { get { return _reader.LocalName; } } public override string NamespaceURI { get { return _reader.NamespaceURI; } } public override string Prefix { get { return _reader.Prefix; } } public override bool HasValue { get { return _reader.HasValue; } } - public override string? Value { get { return _reader.Value; } } + public override string Value { get { return _reader.Value; } } public override int Depth { get { return _reader.Depth; } } public override string BaseURI { get { return _reader.BaseURI; } } public override bool IsEmptyElement { get { return _reader.IsEmptyElement; } } @@ -39,8 +39,8 @@ namespace System.IO.Packaging public override System.Type ValueType { get { return _reader.ValueType; } } public override int AttributeCount { get { return _reader.AttributeCount; } } public override string this[int i] { get { return _reader[i]; } } - public override string this[string name] { get { return _reader[name]; } } - public override string this[string name, string namespaceURI] { get { return _reader[name, namespaceURI]; } } + public override string? this[string name] { get { return _reader[name]; } } + public override string? this[string name, string? namespaceURI] { get { return _reader[name, namespaceURI]; } } public override bool CanResolveEntity { get { return _reader.CanResolveEntity; } } public override bool EOF { get { return _reader.EOF; } } public override ReadState ReadState { get { return _reader.ReadState; } } @@ -52,7 +52,7 @@ namespace System.IO.Packaging return _reader.GetAttribute(name); } - public override string? GetAttribute(string name, string namespaceURI) + public override string? GetAttribute(string name, string? namespaceURI) { return _reader.GetAttribute(name, namespaceURI); } @@ -67,7 +67,7 @@ namespace System.IO.Packaging return _reader.MoveToAttribute(name); } - public override bool MoveToAttribute(string name, string ns) + public override bool MoveToAttribute(string name, string? ns) { return _reader.MoveToAttribute(name, ns); } @@ -109,12 +109,12 @@ namespace System.IO.Packaging string? IXmlNamespaceResolver.LookupPrefix(string namespaceName) { - return (_readerAsResolver == null) ? null : _readerAsResolver.LookupPrefix(namespaceName); + return _readerAsResolver?.LookupPrefix(namespaceName); } - IDictionary? IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope) + IDictionary IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope) { - return (_readerAsResolver == null) ? null : _readerAsResolver.GetNamespacesInScope(scope); + return _readerAsResolver?.GetNamespacesInScope(scope)!; } public override void ResolveEntity() diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs index fb4f41723de..8f4fd15ddfc 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs @@ -851,17 +851,17 @@ namespace System.IO.Packaging // get the required Extension and ContentType attributes - string extensionAttributeValue = reader.GetAttribute(ExtensionAttributeName); + string? extensionAttributeValue = reader.GetAttribute(ExtensionAttributeName); ValidateXmlAttribute(ExtensionAttributeName, extensionAttributeValue, DefaultTagName, reader); - string contentTypeAttributeValue = reader.GetAttribute(ContentTypeAttributeName); + string? contentTypeAttributeValue = reader.GetAttribute(ContentTypeAttributeName); ThrowIfXmlAttributeMissing(ContentTypeAttributeName, contentTypeAttributeValue, DefaultTagName, reader); // The extensions are stored in the Default Dictionary in their original form , but they are compared // in a normalized manner using the ExtensionComparer. PackUriHelper.ValidatedPartUri temporaryUri = PackUriHelper.ValidatePartUri( new Uri(TemporaryPartNameWithoutExtension + extensionAttributeValue, UriKind.Relative)); - _defaultDictionary.Add(temporaryUri.PartUriExtension, new ContentType(contentTypeAttributeValue)); + _defaultDictionary.Add(temporaryUri.PartUriExtension, new ContentType(contentTypeAttributeValue!)); //Skip the EndElement for Default Tag if (!reader.IsEmptyElement) @@ -878,20 +878,20 @@ namespace System.IO.Packaging // get the required Extension and ContentType attributes - string partNameAttributeValue = reader.GetAttribute(PartNameAttributeName); + string? partNameAttributeValue = reader.GetAttribute(PartNameAttributeName); ValidateXmlAttribute(PartNameAttributeName, partNameAttributeValue, OverrideTagName, reader); - string contentTypeAttributeValue = reader.GetAttribute(ContentTypeAttributeName); + string? contentTypeAttributeValue = reader.GetAttribute(ContentTypeAttributeName); ThrowIfXmlAttributeMissing(ContentTypeAttributeName, contentTypeAttributeValue, OverrideTagName, reader); - PackUriHelper.ValidatedPartUri partUri = PackUriHelper.ValidatePartUri(new Uri(partNameAttributeValue, UriKind.Relative)); + PackUriHelper.ValidatedPartUri partUri = PackUriHelper.ValidatePartUri(new Uri(partNameAttributeValue!, UriKind.Relative)); //Lazy initializing - ensure that the override dictionary has been initialized EnsureOverrideDictionary(); // The part Uris are stored in the Override Dictionary in their original form , but they are compared // in a normalized manner using PartUriComparer. - _overrideDictionary.Add(partUri, new ContentType(contentTypeAttributeValue)); + _overrideDictionary.Add(partUri, new ContentType(contentTypeAttributeValue!)); //Skip the EndElement for Override Tag if (!reader.IsEmptyElement) @@ -957,19 +957,19 @@ namespace System.IO.Packaging } //Validate if the required XML attribute is present and not an empty string - private void ValidateXmlAttribute(string attributeName, string attributeValue, string tagName, XmlReader reader) + private void ValidateXmlAttribute(string attributeName, string? attributeValue, string tagName, XmlReader reader) { ThrowIfXmlAttributeMissing(attributeName, attributeValue, tagName, reader); //Checking for empty attribute - if (attributeValue.Length == 0) + if (attributeValue!.Length == 0) throw new XmlException(SR.Format(SR.RequiredAttributeEmpty, tagName, attributeName), null, ((IXmlLineInfo)reader).LineNumber, ((IXmlLineInfo)reader).LinePosition); } //Validate if the required Content type XML attribute is present //Content type of a part can be empty - private void ThrowIfXmlAttributeMissing(string attributeName, string attributeValue, string tagName, XmlReader reader) + private void ThrowIfXmlAttributeMissing(string attributeName, string? attributeValue, string tagName, XmlReader reader) { if (attributeValue == null) throw new XmlException(SR.Format(SR.RequiredAttributeMissing, tagName, attributeName), null, ((IXmlLineInfo)reader).LineNumber, ((IXmlLineInfo)reader).LinePosition); diff --git a/src/libraries/System.IO.Pipelines/pkg/System.IO.Pipelines.pkgproj b/src/libraries/System.IO.Pipelines/pkg/System.IO.Pipelines.pkgproj index ef519b99b1a..a3dc8c78139 100644 --- a/src/libraries/System.IO.Pipelines/pkg/System.IO.Pipelines.pkgproj +++ b/src/libraries/System.IO.Pipelines/pkg/System.IO.Pipelines.pkgproj @@ -19,7 +19,7 @@ - + diff --git a/src/libraries/System.IO.Ports/pkg/System.IO.Ports.pkgproj b/src/libraries/System.IO.Ports/pkg/System.IO.Ports.pkgproj index 1344260b327..d100db7d9a7 100644 --- a/src/libraries/System.IO.Ports/pkg/System.IO.Ports.pkgproj +++ b/src/libraries/System.IO.Ports/pkg/System.IO.Ports.pkgproj @@ -5,6 +5,7 @@ net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) + diff --git a/src/libraries/System.IO.Ports/ref/System.IO.Ports.csproj b/src/libraries/System.IO.Ports/ref/System.IO.Ports.csproj index 85b352191c9..c6278c54043 100644 --- a/src/libraries/System.IO.Ports/ref/System.IO.Ports.csproj +++ b/src/libraries/System.IO.Ports/ref/System.IO.Ports.csproj @@ -1,9 +1,14 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 + + + + + \ No newline at end of file diff --git a/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj b/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj index 878c5e2a3f9..c3691c1b933 100644 --- a/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj +++ b/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj @@ -1,17 +1,18 @@ true - $(DefineConstants);NOSPAN;SERIAL_PORTS + $(DefineConstants);SERIAL_PORTS true annotations - netstandard2.0-windows;netstandard2.0-Linux;netstandard2.0-OSX;netstandard2.0-FreeBSD;netstandard2.0;net461-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent);netstandard2.0-windows;netstandard2.0-Unix;netstandard2.0;net461-windows + true SR.PlatformNotSupported_IOPorts - true + @@ -29,6 +30,7 @@ + - - - - - - - - - - + + + @@ -133,6 +128,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.FreeBSD.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.FreeBSD.cs deleted file mode 100644 index 10ddfa521d5..00000000000 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.FreeBSD.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.ComponentModel; -using System.Collections.Generic; -using System.IO; - -namespace System.IO.Ports -{ - public partial class SerialPort : Component - { - public static string[] GetPortNames() - { - List ports = new List(); - - foreach (string name in Directory.GetFiles("/dev", "ttyd*")) - { - if (!name.EndsWith(".init", StringComparison.Ordinal) && !name.EndsWith(".lock", StringComparison.Ordinal)) - { - ports.Add(name); - } - } - - foreach (string name in Directory.GetFiles("/dev", "cuau*")) - { - if (!name.EndsWith(".init", StringComparison.Ordinal) && !name.EndsWith(".lock", StringComparison.Ordinal)) - { - ports.Add(name); - } - } - - return ports.ToArray(); - } - } -} diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.OSX.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.OSX.cs deleted file mode 100644 index c3077c339bf..00000000000 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.OSX.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.ComponentModel; -using System.Collections.Generic; -using System.IO; - -namespace System.IO.Ports -{ - public partial class SerialPort : Component - { - public static string[] GetPortNames() - { - List ports = new List(); - - foreach (string name in Directory.GetFiles("/dev", "tty.*")) - { - // GetFiles can return unexpected results because of 8.3 matching. - // Like /dev/tty - if (name.StartsWith("/dev/tty.", StringComparison.Ordinal)) - { - ports.Add(name); - } - } - - foreach (string name in Directory.GetFiles("/dev", "cu.*")) - { - if (name.StartsWith("/dev/cu.", StringComparison.Ordinal)) - { - ports.Add(name); - } - } - - return ports.ToArray(); - } - } -} diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Linux.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Unix.cs similarity index 60% rename from src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Linux.cs rename to src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Unix.cs index f922ad091b1..79cc4a791f7 100644 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Linux.cs +++ b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Unix.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; namespace System.IO.Ports { @@ -12,6 +13,20 @@ namespace System.IO.Ports { public static string[] GetPortNames() { +#if NETCOREAPP + return OperatingSystem.IsLinux() ? GetPortNames_Linux() + : OperatingSystem.IsMacOS() ? GetPortNames_OSX() + : OperatingSystem.IsFreeBSD() ? GetPortNames_FreeBSD() +#else + return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? GetPortNames_Linux() + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? GetPortNames_OSX() + : RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")) ? GetPortNames_FreeBSD() +#endif + : throw new PlatformNotSupportedException(SR.PlatformNotSupported_SerialPort_GetPortNames); + } + + private static string[] GetPortNames_Linux() + { const string sysTtyDir = "/sys/class/tty"; const string sysUsbDir = "/sys/bus/usb-serial/devices/"; const string devDir = "/dev/"; @@ -70,5 +85,53 @@ namespace System.IO.Ports return ports.ToArray(); } } + + private static string[] GetPortNames_OSX() + { + List ports = new List(); + + foreach (string name in Directory.GetFiles("/dev", "tty.*")) + { + // GetFiles can return unexpected results because of 8.3 matching. + // Like /dev/tty + if (name.StartsWith("/dev/tty.", StringComparison.Ordinal)) + { + ports.Add(name); + } + } + + foreach (string name in Directory.GetFiles("/dev", "cu.*")) + { + if (name.StartsWith("/dev/cu.", StringComparison.Ordinal)) + { + ports.Add(name); + } + } + + return ports.ToArray(); + } + + private static string[] GetPortNames_FreeBSD() + { + List ports = new List(); + + foreach (string name in Directory.GetFiles("/dev", "ttyd*")) + { + if (!name.EndsWith(".init", StringComparison.Ordinal) && !name.EndsWith(".lock", StringComparison.Ordinal)) + { + ports.Add(name); + } + } + + foreach (string name in Directory.GetFiles("/dev", "cuau*")) + { + if (!name.EndsWith(".init", StringComparison.Ordinal) && !name.EndsWith(".lock", StringComparison.Ordinal)) + { + ports.Add(name); + } + } + + return ports.ToArray(); + } } } diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Win32.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Win32.cs index ac930c78a1a..22d6a92cd4b 100644 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Win32.cs +++ b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialPort.Win32.cs @@ -19,10 +19,6 @@ namespace System.IO.Ports // // QueryDosDevice involves finding any ports that map to \Device\Serialx (call with null to get all, then iterate to get the actual device name) -#if NETSTANDARD - Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); -#endif - using (RegistryKey serialKey = Registry.LocalMachine.OpenSubKey(@"HARDWARE\DEVICEMAP\SERIALCOMM")) { if (serialKey != null) diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.cs index cec491596ef..6a8505c7ba1 100644 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.cs +++ b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.cs @@ -10,7 +10,12 @@ using System.Net.Sockets; namespace System.IO.Ports { +// Issue https://github.com/dotnet/runtime/issues/54916 +// 'SerialStream' overrides array-based 'ReadAsync' but does not override memory-based 'ReadAsync'. Consider overriding memory-based 'ReadAsync' to improve performance. +// 'SerialStream' overrides array-based 'WriteAsync' but does not override memory-based 'WriteAsync'. Consider overriding memory-based 'WriteAsync' to improve performance. +#pragma warning disable CA1844 internal sealed partial class SerialStream : Stream +#pragma warning restore CA1844 { private const int MaxDataBits = 8; private const int MinDataBits = 5; diff --git a/src/libraries/System.Management/pkg/System.Management.pkgproj b/src/libraries/System.Management/pkg/System.Management.pkgproj index fa36b7b68fb..55cde1bab5c 100644 --- a/src/libraries/System.Management/pkg/System.Management.pkgproj +++ b/src/libraries/System.Management/pkg/System.Management.pkgproj @@ -11,7 +11,7 @@ runtimes/win/lib/net461 - + \ No newline at end of file diff --git a/src/libraries/System.Memory.Data/pkg/System.Memory.Data.pkgproj b/src/libraries/System.Memory.Data/pkg/System.Memory.Data.pkgproj index f7170bd4219..4427a3e822a 100644 --- a/src/libraries/System.Memory.Data/pkg/System.Memory.Data.pkgproj +++ b/src/libraries/System.Memory.Data/pkg/System.Memory.Data.pkgproj @@ -4,6 +4,7 @@ net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) + \ No newline at end of file diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs index d2bb7a7622d..302a4da145f 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs @@ -27,7 +27,7 @@ namespace System public static implicit operator System.ReadOnlySpan (System.BinaryData? data) { throw null; } public byte[] ToArray() { throw null; } public System.ReadOnlyMemory ToMemory() { throw null; } - public T ToObjectFromJson(System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public T? ToObjectFromJson(System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public System.IO.Stream ToStream() { throw null; } public override string ToString() { throw null; } } diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj index c5537ac47ec..30f156a04fc 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj @@ -1,13 +1,16 @@ - net461;netstandard2.0 + $(NetCoreAppCurrent);netstandard2.0;net461 enable - + + + + diff --git a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj index 892ebb90f85..b7b49395879 100644 --- a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj @@ -1,16 +1,29 @@  - net461;netstandard2.0 + $(NetCoreAppCurrent);netstandard2.0;net461 true enable + - - + + + - + + + + + + + + + + diff --git a/src/libraries/System.Memory.Data/src/System/BinaryData.cs b/src/libraries/System.Memory.Data/src/System/BinaryData.cs index 35c1fcf9ae1..b0e56d1e7ba 100644 --- a/src/libraries/System.Memory.Data/src/System/BinaryData.cs +++ b/src/libraries/System.Memory.Data/src/System/BinaryData.cs @@ -230,9 +230,9 @@ namespace System /// converted to. /// The to use when serializing to JSON. /// The data converted to the specified type. - public T ToObjectFromJson(JsonSerializerOptions? options = default) + public T? ToObjectFromJson(JsonSerializerOptions? options = default) { - return (T)JsonSerializer.Deserialize(_bytes.Span, typeof(T), options); + return JsonSerializer.Deserialize(_bytes.Span, options); } /// diff --git a/src/libraries/System.Net.Http.WinHttpHandler/pkg/System.Net.Http.WinHttpHandler.pkgproj b/src/libraries/System.Net.Http.WinHttpHandler/pkg/System.Net.Http.WinHttpHandler.pkgproj index e7c9f897745..ef0dd9c8911 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/pkg/System.Net.Http.WinHttpHandler.pkgproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/pkg/System.Net.Http.WinHttpHandler.pkgproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Net.Http.WinHttpHandler/ref/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/ref/System.Net.Http.WinHttpHandler.csproj index 806d5504734..abb8c9bcb21 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/ref/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/ref/System.Net.Http.WinHttpHandler.csproj @@ -1,12 +1,19 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + - + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj index e5a57c97107..525d50608ff 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj @@ -1,17 +1,19 @@ true - netstandard2.0-windows;netstandard2.0;netstandard2.1-windows;netstandard2.1;net461-windows + $(NetCoreAppCurrent)-windows;$(NetcoreAppCurrent);netstandard2.1-windows;netstandard2.1;netstandard2.0-windows;netstandard2.0;net461-windows true enable + - SR.PlatformNotSupported_WinHttpHandler + SR.PlatformNotSupported_WinHttpHandler + + annotations - - - + @@ -99,13 +101,34 @@ Link="Common\System\IO\StreamHelpers.CopyValidation.cs" /> + - + + + + + + + + + + + + + + + + + + + - + + diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs index 16d0235d553..d7432541b94 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs @@ -154,7 +154,7 @@ namespace System.Net.Http Debug.Assert(bytesRead > 0); // Write that data out to the output stream -#if NETSTANDARD2_1 +#if NETSTANDARD2_1 || NETCOREAPP await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false); #else await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs index 1254ae8220b..9f88437e0c4 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs @@ -11,7 +11,7 @@ namespace System.Net.Http internal static class WinHttpTrailersHelper { // UNITTEST is true when building against WinHttpHandler.Unit.Tests, which includes the source file. -#if !NETSTANDARD2_1 && !UNITTEST +#if !NETSTANDARD2_1 && !NETCOREAPP && !UNITTEST // Trailer property name was chosen to be descriptive and be unlikely to collide with a user set property. // Apps and libraries will use this key so it shouldn't change. private const string RequestMessagePropertyName = "__ResponseTrailers"; @@ -24,7 +24,7 @@ namespace System.Net.Http public static HttpHeaders GetResponseTrailers(HttpResponseMessage response) { -#if NETSTANDARD2_1 || UNITTEST +#if NETSTANDARD2_1 || NETCOREAPP || UNITTEST return response.TrailingHeaders; #else HttpResponseTrailers responseTrailers = new HttpResponseTrailers(); diff --git a/src/libraries/System.Numerics.Tensors/pkg/System.Numerics.Tensors.pkgproj b/src/libraries/System.Numerics.Tensors/pkg/System.Numerics.Tensors.pkgproj index 75855f8b61e..372a4d92c7e 100644 --- a/src/libraries/System.Numerics.Tensors/pkg/System.Numerics.Tensors.pkgproj +++ b/src/libraries/System.Numerics.Tensors/pkg/System.Numerics.Tensors.pkgproj @@ -11,6 +11,7 @@ + diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs index b54d42ebf76..3161a4c7e78 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs @@ -97,7 +97,7 @@ namespace System.Numerics.Tensors int System.Collections.ICollection.Count { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } - object System.Collections.IList.this[int index] { get { throw null; } set { } } + object? System.Collections.IList.this[int index] { get { throw null; } set { } } public abstract System.Numerics.Tensors.Tensor Clone(); public virtual System.Numerics.Tensors.Tensor CloneEmpty() { throw null; } public virtual System.Numerics.Tensors.Tensor CloneEmpty(System.ReadOnlySpan dimensions) { throw null; } @@ -139,15 +139,15 @@ namespace System.Numerics.Tensors void System.Collections.Generic.IList.RemoveAt(int index) { } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - int System.Collections.IList.Add(object value) { throw null; } + int System.Collections.IList.Add(object? value) { throw null; } void System.Collections.IList.Clear() { } - bool System.Collections.IList.Contains(object value) { throw null; } - int System.Collections.IList.IndexOf(object value) { throw null; } - void System.Collections.IList.Insert(int index, object value) { } - void System.Collections.IList.Remove(object value) { } + bool System.Collections.IList.Contains(object? value) { throw null; } + int System.Collections.IList.IndexOf(object? value) { throw null; } + void System.Collections.IList.Insert(int index, object? value) { } + void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } - int System.Collections.IStructuralComparable.CompareTo(object other, System.Collections.IComparer comparer) { throw null; } - bool System.Collections.IStructuralEquatable.Equals(object other, System.Collections.IEqualityComparer comparer) { throw null; } + int System.Collections.IStructuralComparable.CompareTo(object? other, System.Collections.IComparer comparer) { throw null; } + bool System.Collections.IStructuralEquatable.Equals(object? other, System.Collections.IEqualityComparer comparer) { throw null; } int System.Collections.IStructuralEquatable.GetHashCode(System.Collections.IEqualityComparer comparer) { throw null; } public virtual System.Numerics.Tensors.CompressedSparseTensor ToCompressedSparseTensor() { throw null; } public virtual System.Numerics.Tensors.DenseTensor ToDenseTensor() { throw null; } diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.csproj index ae9c55011f8..ed6005dc030 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.csproj @@ -1,12 +1,18 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + - + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj index b87c193207b..8dfed2f6283 100644 --- a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj @@ -1,9 +1,10 @@ true - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + @@ -13,7 +14,15 @@ - + + + + + + + + + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/SparseTensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/SparseTensor.cs index 9c40bb49a89..7196a528e9d 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/SparseTensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/SparseTensor.cs @@ -71,7 +71,7 @@ namespace System.Numerics.Tensors public override T GetValue(int index) { - if (!values.TryGetValue(index, out T value)) + if (!values.TryGetValue(index, out T? value)) { value = Zero; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs index 270da201c53..83c6947d76a 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs @@ -705,7 +705,7 @@ namespace System.Numerics.Tensors _tensor = tensor; _index = 0; - Current = default; + Current = default!; } public T Current { get; private set; } @@ -722,7 +722,7 @@ namespace System.Numerics.Tensors } else { - Current = default; + Current = default!; return false; } } @@ -733,7 +733,7 @@ namespace System.Numerics.Tensors public void Reset() { _index = 0; - Current = default; + Current = default!; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PinnedBufferMemoryStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PinnedBufferMemoryStream.cs index 6adab80d3ef..6a980d9fe11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PinnedBufferMemoryStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PinnedBufferMemoryStream.cs @@ -36,7 +36,7 @@ namespace System.IO Initialize(ptr, len, len, FileAccess.Read); } -#if (!NETSTANDARD2_0 && !NETFRAMEWORK) +#if !RESOURCES_EXTENSIONS public override int Read(Span buffer) => ReadCore(buffer); public override void Write(ReadOnlySpan buffer) => WriteCore(buffer); diff --git a/src/libraries/System.Reflection.Context/pkg/System.Reflection.Context.pkgproj b/src/libraries/System.Reflection.Context/pkg/System.Reflection.Context.pkgproj index 29115a97cc5..a3234c92069 100644 --- a/src/libraries/System.Reflection.Context/pkg/System.Reflection.Context.pkgproj +++ b/src/libraries/System.Reflection.Context/pkg/System.Reflection.Context.pkgproj @@ -19,6 +19,7 @@ + diff --git a/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.csproj b/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.csproj index 077aa393f18..39d6843b59f 100644 --- a/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.csproj +++ b/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.csproj @@ -4,10 +4,15 @@ plan on shipping a new desktop version out of band. Instead add API to a different assembly. --> 4.0.0.0 - netstandard2.0 + $(NetCoreAppCurrent);netstandard2.0 enable + + + + + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj index dcbd6df8556..d4270565e93 100644 --- a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj +++ b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj @@ -1,13 +1,17 @@ - netstandard2.1;netstandard2.0 + $(NetCoreAppCurrent);netstandard2.1;netstandard2.0 enable + - SR.PlatformNotSupported_ReflectionContext + + annotations + SR.PlatformNotSupported_ReflectionContext - + + @@ -63,6 +67,12 @@ - + + + + + + diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 64c98012957..cbbeda045ab 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -70,6 +70,10 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingAssembly.SecurityRuleSet; } } +#if NETCOREAPP + [Obsolete] + [RequiresAssemblyFiles] +#endif public override string CodeBase { get { return UnderlyingAssembly.CodeBase; } @@ -85,6 +89,10 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingAssembly.EntryPoint; } } +#if NETCOREAPP + [Obsolete] + [RequiresAssemblyFiles] +#endif public override string EscapedCodeBase { get { return UnderlyingAssembly.EscapedCodeBase; } @@ -188,6 +196,9 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetTypes(); } +#if NETCOREAPP + [Obsolete] +#endif public override bool GlobalAssemblyCache { get { return UnderlyingAssembly.GlobalAssemblyCache; } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/pkg/System.Reflection.MetadataLoadContext.pkgproj b/src/libraries/System.Reflection.MetadataLoadContext/pkg/System.Reflection.MetadataLoadContext.pkgproj index 2f88ba57ac3..e95f40804d9 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/pkg/System.Reflection.MetadataLoadContext.pkgproj +++ b/src/libraries/System.Reflection.MetadataLoadContext/pkg/System.Reflection.MetadataLoadContext.pkgproj @@ -4,7 +4,7 @@ net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - + diff --git a/src/libraries/System.Resources.Extensions/pkg/System.Resources.Extensions.pkgproj b/src/libraries/System.Resources.Extensions/pkg/System.Resources.Extensions.pkgproj index 3ac0955a841..68f6238d743 100644 --- a/src/libraries/System.Resources.Extensions/pkg/System.Resources.Extensions.pkgproj +++ b/src/libraries/System.Resources.Extensions/pkg/System.Resources.Extensions.pkgproj @@ -4,6 +4,7 @@ uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) + \ No newline at end of file diff --git a/src/libraries/System.Resources.Extensions/ref/System.Resources.Extensions.csproj b/src/libraries/System.Resources.Extensions/ref/System.Resources.Extensions.csproj index 184133af2fa..99037ae459c 100644 --- a/src/libraries/System.Resources.Extensions/ref/System.Resources.Extensions.csproj +++ b/src/libraries/System.Resources.Extensions/ref/System.Resources.Extensions.csproj @@ -1,9 +1,14 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable + + + + + \ No newline at end of file diff --git a/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj b/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj index 2787ad7bb95..adacc9b977e 100644 --- a/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj +++ b/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj @@ -1,10 +1,11 @@ true - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 $(DefineConstants);RESOURCES_EXTENSIONS enable + @@ -26,13 +27,26 @@ + + + - + + + + + + + + + + + diff --git a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs index ea9fbb44103..ef40926e32a 100644 --- a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs +++ b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs @@ -33,7 +33,8 @@ namespace System.Resources.Extensions return false; } - // Issue https://github.com/dotnet/runtime/issues/39292 tracks finding an alternative to BinaryFormatter +// Issue https://github.com/dotnet/runtime/issues/39292 tracks finding an alternative to BinaryFormatter +#pragma warning disable SYSLIB0011 private object ReadBinaryFormattedObject() { if (_formatter == null) @@ -46,7 +47,7 @@ namespace System.Resources.Extensions return _formatter.Deserialize(_store.BaseStream); } - +#pragma warning restore SYSLIB0011 internal sealed class UndoTruncatedTypeNameSerializationBinder : SerializationBinder { diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj b/src/libraries/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj index 46ea9e67b0a..90d23508d81 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj @@ -18,7 +18,7 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj b/src/libraries/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj index 0fd0f8c5b4f..0eeb304e2fb 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj @@ -5,7 +5,7 @@ net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - + diff --git a/src/libraries/System.Security.Permissions/pkg/System.Security.Permissions.pkgproj b/src/libraries/System.Security.Permissions/pkg/System.Security.Permissions.pkgproj index cb976d3857d..c4591c3ad06 100644 --- a/src/libraries/System.Security.Permissions/pkg/System.Security.Permissions.pkgproj +++ b/src/libraries/System.Security.Permissions/pkg/System.Security.Permissions.pkgproj @@ -5,7 +5,7 @@ netcoreapp2.0;net461;uap10.0.16299;$(AllXamarinFrameworks) - + \ No newline at end of file diff --git a/src/libraries/System.ServiceModel.Syndication/pkg/System.ServiceModel.Syndication.pkgproj b/src/libraries/System.ServiceModel.Syndication/pkg/System.ServiceModel.Syndication.pkgproj index 9e1434e9208..aa80ef9e3dc 100644 --- a/src/libraries/System.ServiceModel.Syndication/pkg/System.ServiceModel.Syndication.pkgproj +++ b/src/libraries/System.ServiceModel.Syndication/pkg/System.ServiceModel.Syndication.pkgproj @@ -5,7 +5,7 @@ uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - + \ No newline at end of file diff --git a/src/libraries/System.Speech/pkg/System.Speech.pkgproj b/src/libraries/System.Speech/pkg/System.Speech.pkgproj index b34710daef7..fd8103dc52b 100644 --- a/src/libraries/System.Speech/pkg/System.Speech.pkgproj +++ b/src/libraries/System.Speech/pkg/System.Speech.pkgproj @@ -8,7 +8,7 @@ true - + diff --git a/src/libraries/System.Text.Encoding.CodePages/pkg/System.Text.Encoding.CodePages.pkgproj b/src/libraries/System.Text.Encoding.CodePages/pkg/System.Text.Encoding.CodePages.pkgproj index 7150d80c8aa..42c9d699409 100644 --- a/src/libraries/System.Text.Encoding.CodePages/pkg/System.Text.Encoding.CodePages.pkgproj +++ b/src/libraries/System.Text.Encoding.CodePages/pkg/System.Text.Encoding.CodePages.pkgproj @@ -9,7 +9,7 @@ - + diff --git a/src/libraries/System.Text.Json/pkg/System.Text.Json.pkgproj b/src/libraries/System.Text.Json/pkg/System.Text.Json.pkgproj index ac2d21ef89d..2b335bb4c14 100644 --- a/src/libraries/System.Text.Json/pkg/System.Text.Json.pkgproj +++ b/src/libraries/System.Text.Json/pkg/System.Text.Json.pkgproj @@ -15,7 +15,7 @@ .NETCoreApp;UAP - + \ No newline at end of file diff --git a/src/libraries/System.Threading.AccessControl/pkg/System.Threading.AccessControl.pkgproj b/src/libraries/System.Threading.AccessControl/pkg/System.Threading.AccessControl.pkgproj index 7c7145ce3e4..02cd7bd85f1 100644 --- a/src/libraries/System.Threading.AccessControl/pkg/System.Threading.AccessControl.pkgproj +++ b/src/libraries/System.Threading.AccessControl/pkg/System.Threading.AccessControl.pkgproj @@ -7,6 +7,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Threading.AccessControl/ref/System.Threading.AccessControl.csproj b/src/libraries/System.Threading.AccessControl/ref/System.Threading.AccessControl.csproj index 396bb7caf52..3f57e01ad4d 100644 --- a/src/libraries/System.Threading.AccessControl/ref/System.Threading.AccessControl.csproj +++ b/src/libraries/System.Threading.AccessControl/ref/System.Threading.AccessControl.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 enable @@ -8,6 +8,11 @@ + + + + + diff --git a/src/libraries/System.Threading.AccessControl/src/System.Threading.AccessControl.csproj b/src/libraries/System.Threading.AccessControl/src/System.Threading.AccessControl.csproj index 4e2e14f344d..d381ca2177a 100644 --- a/src/libraries/System.Threading.AccessControl/src/System.Threading.AccessControl.csproj +++ b/src/libraries/System.Threading.AccessControl/src/System.Threading.AccessControl.csproj @@ -1,6 +1,6 @@ - netstandard2.0-windows;netstandard2.0;net461-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netstandard2.0-windows;netstandard2.0;net461-windows true enable @@ -50,6 +50,13 @@ + + + + + + + diff --git a/src/libraries/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj b/src/libraries/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj index 28fa02b0c60..787314714ce 100644 --- a/src/libraries/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj +++ b/src/libraries/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj @@ -12,7 +12,7 @@ - + From 36e0432547d7d734ef23fce40e8c278ef80c34c8 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 29 Jun 2021 23:02:03 +0200 Subject: [PATCH 188/926] Big-endian fix for DoubleTests.cs:ParsePatterns (#54818) The recently added DoubleTests.cs:ParsePatterns test case incorrectly swaps characters of the hexadecimal representation of the floating-point numbers under test on big-endian platforms. --- .../System.Runtime/tests/System/DoubleTests.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs index a80eb05598c..79bbac49432 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using Xunit; #pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0, NaN from -NaN @@ -349,15 +350,12 @@ namespace System.Tests internal static string SplitPairs(string input) { - string[] splitPairs = input.Split('-'); - string ret = ""; - foreach (var pair in splitPairs) + if (!BitConverter.IsLittleEndian) { - string reversedPair = Reverse(pair); - ret += reversedPair; + return input.Replace("-", ""); } - return ret; + return string.Concat(input.Split('-').Select(pair => Reverse(pair))); } internal static string Reverse(string s) From 685eda620e0f058b157b6d0a66da6f21c2d165fa Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 29 Jun 2021 19:10:56 -0700 Subject: [PATCH 189/926] Print spillweight of RefPosition (#54933) --- src/coreclr/jit/lsra.cpp | 8 +++++--- src/coreclr/jit/lsra.h | 3 ++- src/coreclr/jit/lsrabuild.cpp | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index e137b7f678a..86d037e12ac 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -2467,7 +2467,7 @@ void LinearScan::dumpVarRefPositions(const char* title) printf(" (Interval %d)\n", interval->intervalIndex); for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition) { - ref->dump(); + ref->dump(this); } } else @@ -4346,7 +4346,7 @@ void LinearScan::dumpRefPositions(const char* str) printf("------------\n"); for (RefPosition& refPos : refPositions) { - refPos.dump(); + refPos.dump(this); } } #endif // DEBUG @@ -8895,7 +8895,7 @@ const char* LinearScan::getScoreName(RegisterScore score) } } -void RefPosition::dump() +void RefPosition::dump(LinearScan* linearScan) { printf("getWeight(this)); printf(">\n"); } diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index eda5d014c65..10ff1f471b4 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1,3 +1,4 @@ + // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. /*****************************************************************************/ @@ -2444,7 +2445,7 @@ public: return *this; } - void dump(); + void dump(LinearScan* linearScan); #endif // DEBUG }; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index dcea8813119..f4ea6fb3579 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -525,7 +525,7 @@ RefPosition* LinearScan::newRefPosition( (regRecord->lastRefPosition->refType != theRefType)); associateRefPosWithInterval(newRP); - DBEXEC(VERBOSE, newRP->dump()); + DBEXEC(VERBOSE, newRP->dump(this)); return newRP; } @@ -620,7 +620,7 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval, associateRefPosWithInterval(newRP); - DBEXEC(VERBOSE, newRP->dump()); + DBEXEC(VERBOSE, newRP->dump(this)); return newRP; } @@ -2624,7 +2624,7 @@ void LinearScan::validateIntervals() printf("-----------------\n"); for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition) { - ref->dump(); + ref->dump(this); RefType refType = ref->refType; if (!defined && RefTypeIsUse(refType)) { From 99f7bae2a236b1bc5f3a6c35fd29df49f13bd539 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Wed, 30 Jun 2021 05:05:04 +0100 Subject: [PATCH 190/926] Use `Span.Fill` implementation for `Array.Fill` (#52590) --- .../src/System/Collections/BitArray.cs | 2 +- .../src/System/Array.cs | 27 +++++-- .../src/System/Xml/Core/XmlTextWriter.cs | 2 +- .../System.Runtime/tests/System/ArrayTests.cs | 70 +++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Collections/src/System/Collections/BitArray.cs b/src/libraries/System.Collections/src/System/Collections/BitArray.cs index 1fddd453b92..6921b13347b 100644 --- a/src/libraries/System.Collections/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Collections/src/System/Collections/BitArray.cs @@ -51,7 +51,7 @@ namespace System.Collections if (defaultValue) { - m_array.AsSpan().Fill(-1); + Array.Fill(m_array, -1); // clear high bit values in the last int Div32Rem(length, out int extraBits); diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index c1893b7dbb7..d305987cc03 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -785,9 +785,16 @@ namespace System ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } - for (int i = 0; i < array.Length; i++) + if (!typeof(T).IsValueType && array.GetType() != typeof(T[])) { - array[i] = value; + for (int i = 0; i < array.Length; i++) + { + array[i] = value; + } + } + else + { + new Span(array).Fill(value); } } @@ -798,19 +805,27 @@ namespace System ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } - if (startIndex < 0 || startIndex > array.Length) + if ((uint)startIndex > (uint)array.Length) { ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); } - if (count < 0 || startIndex > array.Length - count) + if ((uint)count > (uint)(array.Length - startIndex)) { ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } - for (int i = startIndex; i < startIndex + count; i++) + if (!typeof(T).IsValueType && array.GetType() != typeof(T[])) { - array[i] = value; + for (int i = startIndex; i < startIndex + count; i++) + { + array[i] = value; + } + } + else + { + ref T first = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)startIndex); + new Span(ref first, count).Fill(value); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs index a594d83bbb3..ffb19d21d52 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs @@ -144,7 +144,7 @@ namespace System.Xml private static char[] CreateDefaultIndentChars() { var result = new char[IndentArrayLength]; - result.AsSpan().Fill(DefaultIndentChar); + Array.Fill(result, DefaultIndentChar); return result; } diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 04bb40e2aeb..a8930703a02 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -4275,6 +4275,76 @@ namespace System.Tests AssertExtensions.Throws("count", () => Array.Fill(new string[arrayLength], "", startIndex, count)); } + public class Bar : IEquatable + { + public string Value { get; set; } + + public bool Equals(Bar other) => string.Equals(Value, other.Value); + } + + public class Foo + { + } + + private static Bar[] CreateBarArray() + { + return new Bar[] + { + new Bar() { Value = "0" }, + new Bar() { Value = "1" }, + new Bar() { Value = "2" }, + new Bar() { Value = "3" }, + }; + } + + [Fact] + public static void Fill_Downcast() + { + Bar[] barArray = CreateBarArray(); + Array.Fill(barArray, new Bar() { Value = "x" }); + Assert.Equal(new string[] { "x", "x", "x", "x" }, barArray.Select(e => e.Value)); + } + + [Fact] + public static void FillWithStartIndexAndCount_Downcast() + { + Bar[] barArray = CreateBarArray(); + Array.Fill(barArray, new Bar() { Value = "x" }, 1, 2); + Assert.Equal(new string[] { "0", "x", "x", "3" }, barArray.Select(e => e.Value)); + } + + [Fact] + public static void Fill_Sidecast() + { + uint[] uintArray = (uint[])(object)new int[] { 0, 1, 2, 3 }; + Array.Fill(uintArray, 42); + Assert.Equal(new int[] { 42, 42, 42, 42 }, (int[])(object)uintArray); + } + + [Fact] + public static void FillWithStartIndexAndCount_Sidecast() + { + uint[] uintArray = (uint[])(object)new int[] { 0, 1, 2, 3 }; + Array.Fill(uintArray, 42, 1, 2); + Assert.Equal(new int[] { 0, 42, 42, 3 }, (int[])(object)uintArray); + } + + [Fact] + public static void Fill_ThrowsArrayTypeMismatchException() + { + Bar[] barArray = CreateBarArray(); + Assert.Throws(() => Array.Fill(barArray, new Foo())); + Assert.Equal(CreateBarArray(), barArray); + } + + [Fact] + public static void FillWithStartIndexAndCount_ThrowsArrayTypeMismatchException() + { + Bar[] barArray = CreateBarArray(); + Assert.Throws(() => Array.Fill(barArray, new Foo(), 1, 2)); + Assert.Equal(CreateBarArray(), barArray); + } + public static IEnumerable Reverse_Generic_Int_TestData() { // TODO: use (or merge this data into) Reverse_TestData if/when xunit/xunit#965 is merged From c3b63076cd92707ae6f75a3930a509aacfd61915 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Wed, 30 Jun 2021 07:39:20 +0200 Subject: [PATCH 191/926] [wasm] browser http response stream could be seekable (#54749) - browser http response stream could be seekable - test WebAssemblyEnableStreamingResponse --- .../System/Net/Http/HttpClientHandlerTest.cs | 170 ++++++++++++------ 1 file changed, 113 insertions(+), 57 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index d9958e9c520..98c23bdcde0 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -934,11 +934,12 @@ namespace System.Net.Http.Functional.Tests } [Theory] - [InlineData(true)] - [InlineData(false)] - [InlineData(null)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] - public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked) + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + [InlineData(null, false)] + public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked, bool enableWasmStreaming) { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) { @@ -960,6 +961,16 @@ namespace System.Net.Http.Functional.Tests await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { var request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; + if (PlatformDetection.IsBrowser) + { + if (enableWasmStreaming) + { +#if !NETFRAMEWORK + request.Options.Set(new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"), true); +#endif + } + } + using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) using (HttpResponseMessage response = await client.SendAsync(TestAsync, request, CancellationToken.None)) { @@ -970,21 +981,24 @@ namespace System.Net.Http.Functional.Tests // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.False(responseStream.CanSeek); + Assert.Equal(PlatformDetection.IsBrowser && !enableWasmStreaming, responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK Assert.Throws(() => responseStream.Write(new Span(new byte[1]))); - Assert.Throws(() => { responseStream.WriteAsync(new Memory(new byte[1])); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new Memory(new byte[1]))); #endif - Assert.Throws(() => { responseStream.WriteAsync(new byte[1], 0, 1); }); + await Assert.ThrowsAsync(async () => await responseStream.WriteAsync(new byte[1], 0, 1)); Assert.Throws(() => responseStream.WriteByte(1)); // Invalid arguments @@ -998,11 +1012,14 @@ namespace System.Net.Http.Functional.Tests Assert.Throws(() => { responseStream.CopyToAsync(Stream.Null, -1, default); }); Assert.Throws(() => { responseStream.CopyToAsync(nonWritableStream, 100, default); }); Assert.Throws(() => { responseStream.CopyToAsync(disposedStream, 100, default); }); - Assert.Throws(() => responseStream.Read(null, 0, 100)); - Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); - Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + if (PlatformDetection.IsNotBrowser) + { + Assert.Throws(() => responseStream.Read(null, 0, 100)); + Assert.Throws(() => responseStream.Read(new byte[1], -1, 1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 2, 1)); + Assert.Throws(() => responseStream.Read(new byte[1], 0, -1)); + Assert.ThrowsAny(() => responseStream.Read(new byte[1], 0, 2)); + } Assert.Throws(() => responseStream.BeginRead(null, 0, 100, null, null)); Assert.Throws(() => responseStream.BeginRead(new byte[1], -1, 1, null, null)); Assert.ThrowsAny(() => responseStream.BeginRead(new byte[1], 2, 1, null, null)); @@ -1018,62 +1035,99 @@ namespace System.Net.Http.Functional.Tests // Various forms of reading var buffer = new byte[1]; - Assert.Equal('h', responseStream.ReadByte()); + if (PlatformDetection.IsBrowser) + { +#if !NETFRAMEWORK + Assert.Equal('h', await responseStream.ReadByteAsync()); + Assert.Equal('e', await responseStream.ReadByteAsync()); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer)); + Assert.Equal((byte)'o', buffer[0]); + + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); + + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); +#endif + } + else + { + Assert.Equal('h', responseStream.ReadByte()); + Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal((byte)'e', buffer[0]); - Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); - Assert.Equal((byte)'e', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal((byte)'l', buffer[0]); - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); - Assert.Equal((byte)'l', buffer[0]); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal((byte)'l', buffer[0]); #if !NETFRAMEWORK - Assert.Equal(1, responseStream.Read(new Span(buffer))); + Assert.Equal(1, responseStream.Read(new Span(buffer))); #else - Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1)); #endif - Assert.Equal((byte)'o', buffer[0]); + Assert.Equal((byte)'o', buffer[0]); - Assert.Equal(1, responseStream.Read(buffer, 0, 1)); - Assert.Equal((byte)' ', buffer[0]); + Assert.Equal(1, responseStream.Read(buffer, 0, 1)); + Assert.Equal((byte)' ', buffer[0]); - // Doing any of these 0-byte reads causes the connection to fail. - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); + // Doing any of these 0-byte reads causes the connection to fail. + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty(), 0, 0, null)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); + Assert.Equal(0, await responseStream.ReadAsync(Memory.Empty)); #endif - Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); + Assert.Equal(0, await responseStream.ReadAsync(Array.Empty(), 0, 0)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(Span.Empty)); + Assert.Equal(0, responseStream.Read(Span.Empty)); #endif - Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); + Assert.Equal(0, responseStream.Read(Array.Empty(), 0, 0)); - // And copying - var ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); + // And copying + var ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + Assert.Equal("world", Encoding.ASCII.GetString(ms.ToArray())); - // Read and copy again once we've exhausted all data - ms = new MemoryStream(); - await responseStream.CopyToAsync(ms); - responseStream.CopyTo(ms); - Assert.Equal(0, ms.Length); - Assert.Equal(-1, responseStream.ReadByte()); - Assert.Equal(0, responseStream.Read(buffer, 0, 1)); + // Read and copy again once we've exhausted all data + ms = new MemoryStream(); + await responseStream.CopyToAsync(ms); + responseStream.CopyTo(ms); + Assert.Equal(0, ms.Length); + Assert.Equal(-1, responseStream.ReadByte()); + Assert.Equal(0, responseStream.Read(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, responseStream.Read(new Span(buffer))); + Assert.Equal(0, responseStream.Read(new Span(buffer))); #endif - Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); + Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1)); #if !NETFRAMEWORK - Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); + Assert.Equal(0, await responseStream.ReadAsync(new Memory(buffer))); #endif - Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null)); + } } } }, async server => @@ -1103,7 +1157,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54159", TestPlatforms.Browser)] public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -1123,14 +1176,17 @@ namespace System.Net.Http.Functional.Tests // Boolean properties returning correct values Assert.True(responseStream.CanRead); Assert.False(responseStream.CanWrite); - Assert.False(responseStream.CanSeek); + Assert.Equal(PlatformDetection.IsBrowser, responseStream.CanSeek); // Not supported operations Assert.Throws(() => responseStream.BeginWrite(new byte[1], 0, 1, null, null)); - Assert.Throws(() => responseStream.Length); - Assert.Throws(() => responseStream.Position); - Assert.Throws(() => responseStream.Position = 0); - Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + if (!responseStream.CanSeek) + { + Assert.Throws(() => responseStream.Length); + Assert.Throws(() => responseStream.Position); + Assert.Throws(() => responseStream.Position = 0); + Assert.Throws(() => responseStream.Seek(0, SeekOrigin.Begin)); + } Assert.Throws(() => responseStream.SetLength(0)); Assert.Throws(() => responseStream.Write(new byte[1], 0, 1)); #if !NETFRAMEWORK From f399076cb971fcfdab397d8b3786f321acb4b4d5 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 29 Jun 2021 22:55:22 -0700 Subject: [PATCH 192/926] Remove some redundant / unused binplace directories (#54890) * Remove some redundant / unused binplace directories * Fix runtime pack path * Use OuputRid instead of PackageRID On linux-musl, OutputRID differs from PackageRID. * Add RuntimeOS to installer leg for musl So that we have consistent values of $(PackageRID) for installer and libraries * Move LibrariesConfiguration definition earlier in src/tests This property was being defined after a number of places where it was consumed resulting in quite a few inconsistent derived properties. --- Directory.Build.props | 2 +- eng/liveBuilds.targets | 4 +- eng/pipelines/installer/jobs/base-job.yml | 3 ++ .../libraries/prepare-for-bin-publish.yml | 50 ++++++------------- src/libraries/Directory.Build.props | 4 -- src/libraries/Directory.Build.targets | 7 --- .../test_dependencies.csproj | 1 - .../test_dependencies.fsproj | 1 - src/tests/Directory.Build.props | 4 ++ src/tests/Directory.Build.targets | 1 - 10 files changed, 24 insertions(+), 53 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 53be3bd5c9b..d957a798303 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -209,7 +209,7 @@ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)')) $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data')) - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(Configuration)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(PackageRID)', '$(LibrariesConfiguration)')) $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(PackageRID)')) $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)')) $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 11ea3bf8f3a..01a0c6aac67 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -35,9 +35,9 @@ $([MSBuild]::NormalizeDirectory('$(LibrariesAllConfigPackagesDir)', 'Shipping')) $([MSBuild]::NormalizeDirectory('$(LibrariesAllConfigPackagesDir)', 'NonShipping')) - $([MSBuild]::NormalizeDirectory('$(LibrariesArtifactsPath)', 'bin', 'ref', 'microsoft.netcore.app', '$(LibrariesConfiguration)')) + $(MicrosoftNetCoreAppRefPackRefDir) $([MSBuild]::NormalizeDirectory('$(LibrariesArtifactsPath)', 'bin', 'ref', '$(NetCoreAppCurrent)')) - $([MSBuild]::NormalizeDirectory('$(LibrariesArtifactsPath)', 'bin', 'pkg', '$(NetCoreAppCurrent)', 'runtime'))$(LibrariesTargetOSConfigurationArchitecture)\ + $(MicrosoftNetCoreAppRuntimePackRidLibTfmDir) $([MSBuild]::NormalizeDirectory('$(LibrariesArtifactsPath)', 'bin', 'runtime'))$(NetCoreAppCurrent)-$(LibrariesTargetOSConfigurationArchitecture)\ $([MSBuild]::NormalizeDirectory('$(LibrariesArtifactsPath)', 'bin', 'native'))$(NetCoreAppCurrent)-$(LibrariesTargetOSConfigurationArchitecture)\ diff --git a/eng/pipelines/installer/jobs/base-job.yml b/eng/pipelines/installer/jobs/base-job.yml index 35c3728c6f9..22c9b265c7d 100644 --- a/eng/pipelines/installer/jobs/base-job.yml +++ b/eng/pipelines/installer/jobs/base-job.yml @@ -217,6 +217,8 @@ jobs: # lowercase for RID format. (Detection normally converts, but we're preventing it.) - name: OutputRidArg value: /p:OutputRid=linux-musl-${{ parameters.archType }} + - name: RuntimeOSArg + value: /p:RuntimeOS=linux-musl - name: _PortableBuild value: true @@ -231,6 +233,7 @@ jobs: $(LiveOverridePathArgs) $(CommonMSBuildArgs) $(OutputRidArg) + $(RuntimeOSArg) - name: PublishArguments value: >- diff --git a/eng/pipelines/libraries/prepare-for-bin-publish.yml b/eng/pipelines/libraries/prepare-for-bin-publish.yml index fa71beaae65..fbdb90f1871 100644 --- a/eng/pipelines/libraries/prepare-for-bin-publish.yml +++ b/eng/pipelines/libraries/prepare-for-bin-publish.yml @@ -6,46 +6,24 @@ parameters: steps: - ${{ if ne(parameters.isOfficialBuild, true) }}: - task: CopyFiles@2 - displayName: Prepare testhost folder to publish + displayName: Prepare bin folders to publish (unofficial build) inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/testhost - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/testhost - - - task: CopyFiles@2 - displayName: Prepare runtime folder to publish - inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/runtime - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/runtime - - - task: CopyFiles@2 - displayName: Prepare ref folder to publish - inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/ref - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/ref + sourceFolder: $(Build.SourcesDirectory)/artifacts/bin + targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin + contents: | + ref/** + runtime/** + testhost/** - task: CopyFiles@2 - displayName: Prepare shared framework ref assemblies to publish + displayName: Prepare bin folders to publish inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/ref/microsoft.netcore.app - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/ref/microsoft.netcore.app - - - task: CopyFiles@2 - displayName: Prepare docs folder to publish - inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/docs - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/docs - - - task: CopyFiles@2 - displayName: Prepare shared framework runtime folder to publish - inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/pkg - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/pkg - - - task: CopyFiles@2 - displayName: Prepare native folder to publish - inputs: - sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/native - targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin/native + sourceFolder: $(Build.SourcesDirectory)/artifacts/bin + targetFolder: $(Build.ArtifactStagingDirectory)/artifacts/bin + contents: | + docs/** + microsoft.netcore.app.*/** + native/** - task: CopyFiles@2 displayName: Prepare artifacts packages folder to publish diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 0078c1331d3..3981181ee92 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -128,10 +128,6 @@ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(TestArchiveRoot)runtime/ - - $(ArtifactsBinDir)pkg\$(NetCoreAppCurrent)\ref - $(ArtifactsBinDir)pkg\$(NetCoreAppCurrent)\lib - $(ArtifactsBinDir)pkg\aspnetcoreapp\ref $(ArtifactsBinDir)pkg\aspnetcoreapp\lib diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 051d90878db..d4b5f1ac040 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -85,15 +85,8 @@ $(NetCoreAppCurrentRuntimePath) - - $(NETCoreAppPackageRuntimePath) - $(NETCoreAppPackageRefPath) - $(NETCoreAppPackageRuntimePath) - $(NETCoreAppPackageRuntimePath)\..\runtime\$(TargetOS)-$(Configuration)-$(TargetArchitecture) - $(RefRootPath)microsoft.netcore.app\$(Configuration) - $(NETCoreAppPackageRuntimePath)\..\runtime\$(TargetOS)-$(Configuration)-$(TargetArchitecture) diff --git a/src/tests/Common/test_dependencies_fs/test_dependencies.fsproj b/src/tests/Common/test_dependencies_fs/test_dependencies.fsproj index 6a544c39f98..c23ba6368a4 100644 --- a/src/tests/Common/test_dependencies_fs/test_dependencies.fsproj +++ b/src/tests/Common/test_dependencies_fs/test_dependencies.fsproj @@ -7,7 +7,6 @@ true win-arm;win-arm64;win-x64;win-x86;$(PackageRID) true - Release diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props index b714b27db2e..f00de4d102e 100644 --- a/src/tests/Directory.Build.props +++ b/src/tests/Directory.Build.props @@ -1,5 +1,9 @@ + + Release + + diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index cf36a8cdf61..71494ce1d6a 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -265,7 +265,6 @@ true - Release From ff88d1d3a022021a54fbe05949a27db29c690f0c Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Wed, 30 Jun 2021 10:44:10 +0200 Subject: [PATCH 193/926] Saw the ASSERT_HOLDING_SPIN_LOCK fail in the region allocator. (#54896) I think this is due to a race between a thread leaving the lock and a thread entering the lock. The thread entering the lock sets the holding_thread to itself, and the thread leaving the lock sets it to -1. The thread leaving the lock is already outside of the lock, and so its write may occur after the write by the thread entering the lock. The end result is that the lock appears taken, but with a holding_thread of -1, which causes the assert. Swapping the order of the two writes will fix the issue, because now the write to holding_thread is under the lock, so two threads cannot do conflicting writes. --- src/coreclr/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index ea9b6fc5c0e..1d58d35ce86 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -3645,10 +3645,10 @@ void region_allocator::enter_spin_lock() void region_allocator::leave_spin_lock() { - region_allocator_lock.lock = -1; #ifdef _DEBUG region_allocator_lock.holding_thread = (Thread*)-1; #endif //_DEBUG + region_allocator_lock.lock = -1; } uint8_t* region_allocator::allocate (uint32_t num_units, allocate_direction direction) From dabcbf270dc2a20a34626c7624b78113b9868e5d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 30 Jun 2021 12:09:53 +0200 Subject: [PATCH 194/926] Fix handle leak in SocketProtocolSupportPal.Unix.cs (#54898) --- .../Common/src/System/Net/SocketProtocolSupportPal.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs index 916b5cb07cd..d06ce8117fc 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs @@ -23,7 +23,7 @@ namespace System.Net } finally { - if (socket == invalid) + if (socket != invalid) { Interop.Sys.Close(socket); } From f39e721a9779a1bb00078161b57169b4587e4219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:25:18 +0200 Subject: [PATCH 195/926] Create handler via test helpers. (#54825) --- .../FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index 7319e42e684..47716f3c2c9 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -37,10 +37,11 @@ namespace System.Net.Http.Functional.Tests var releaseServer = new TaskCompletionSource(); await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { - using (var handler = new SocketsHttpHandler()) + using (var handler = CreateHttpClientHandler()) using (var invoker = new HttpMessageInvoker(handler)) { - handler.ConnectTimeout = TimeSpan.FromSeconds(1); + var socketsHandler = GetUnderlyingSocketsHttpHandler(handler); + socketsHandler.ConnectTimeout = TimeSpan.FromSeconds(1); await ValidateConnectTimeout(invoker, new UriBuilder(uri) { Scheme = "https" }.Uri, 500, 85_000); From ff57075ea035f989293a5805df52a3dc5593d313 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 30 Jun 2021 07:32:57 -0400 Subject: [PATCH 196/926] [mono] Fix debug logging in apps created using AppleAppBuilder (#54901) --- src/tasks/AppleAppBuilder/Templates/runtime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/AppleAppBuilder/Templates/runtime.m b/src/tasks/AppleAppBuilder/Templates/runtime.m index e0eb0a7e81d..fa906e8070e 100644 --- a/src/tasks/AppleAppBuilder/Templates/runtime.m +++ b/src/tasks/AppleAppBuilder/Templates/runtime.m @@ -194,7 +194,7 @@ unhandled_exception_handler (MonoObject *exc, void *user_data) void log_callback (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { - os_log_info (OS_LOG_DEFAULT, "(%s %s) %s", log_domain, log_level, message); + os_log_info (OS_LOG_DEFAULT, "(%{public}s %{public}s) %{public}s", log_domain, log_level, message); if (fatal) { os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", 1); exit (1); From e823eb65de31ddec41f6452f701ca97d8d8eff35 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 30 Jun 2021 07:34:01 -0400 Subject: [PATCH 197/926] [mono][wasm] Emit more logging from the EmccCompile task. (#54937) --- src/tasks/WasmAppBuilder/EmccCompile.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index b175c62c2ee..78a6a4b1faa 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -112,6 +113,12 @@ namespace Microsoft.WebAssembly.Build.Tasks try { string command = $"emcc {Arguments} -c -o {objFile} {srcFile}"; + + // Log the command in a compact format which can be copy pasted + StringBuilder envStr = new StringBuilder(string.Empty); + foreach (var key in envVarsDict.Keys) + envStr.Append($"{key}={envVarsDict[key]} "); + Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}{command}"); (int exitCode, string output) = Utils.RunShellCommand(command, envVarsDict, workingDir: Environment.CurrentDirectory); if (exitCode != 0) From de8d4e8b7dc56395d19fb7579b56f79f5d12c167 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Wed, 30 Jun 2021 13:40:35 +0200 Subject: [PATCH 198/926] Fix EventPipe native test build errors. (#54893) --- .../eventpipe/test/ep-buffer-manager-tests.c | 3 ++- .../mono/eventpipe/test/ep-buffer-tests.c | 3 ++- .../mono/eventpipe/test/ep-session-tests.c | 9 ++++--- src/mono/mono/eventpipe/test/ep-tests.c | 26 ++++++++++++------- .../mono/eventpipe/test/ep-thread-tests.c | 3 ++- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c index 3a13b05911e..642b58e5297 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-manager-tests.c @@ -102,7 +102,8 @@ buffer_manager_init ( 1, current_provider_config, 1, - false); + NULL, + NULL); EP_LOCK_EXIT (section1) ep_raise_error_if_nok (*session != NULL); diff --git a/src/mono/mono/eventpipe/test/ep-buffer-tests.c b/src/mono/mono/eventpipe/test/ep-buffer-tests.c index b36bdc9dd80..4ff600864c4 100644 --- a/src/mono/mono/eventpipe/test/ep-buffer-tests.c +++ b/src/mono/mono/eventpipe/test/ep-buffer-tests.c @@ -90,7 +90,8 @@ load_buffer_with_events_init ( 1, current_provider_config, 1, - false); + NULL, + NULL); EP_LOCK_EXIT (section1) ep_raise_error_if_nok (*session != NULL); diff --git a/src/mono/mono/eventpipe/test/ep-session-tests.c b/src/mono/mono/eventpipe/test/ep-session-tests.c index f40b0504730..5c09181c1d0 100644 --- a/src/mono/mono/eventpipe/test/ep-session-tests.c +++ b/src/mono/mono/eventpipe/test/ep-session-tests.c @@ -50,7 +50,8 @@ test_create_delete_session (void) 1, current_provider_config, 1, - false); + NULL, + NULL); EP_LOCK_EXIT (section1) ep_raise_error_if_nok (test_session != NULL); @@ -91,7 +92,8 @@ test_add_session_providers (void) 1, current_provider_config, 1, - false); + NULL, + NULL); ep_raise_error_if_nok_holding_lock (test_session != NULL, section1); @@ -173,7 +175,8 @@ test_session_special_get_set (void) 1, current_provider_config, 1, - false); + NULL, + NULL); EP_LOCK_EXIT (section1) ep_raise_error_if_nok (test_session != NULL); diff --git a/src/mono/mono/eventpipe/test/ep-tests.c b/src/mono/mono/eventpipe/test/ep-tests.c index ce8ae4805d4..5ff65d1497a 100644 --- a/src/mono/mono/eventpipe/test/ep-tests.c +++ b/src/mono/mono/eventpipe/test/ep-tests.c @@ -227,6 +227,7 @@ test_enable_disable (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -349,6 +350,7 @@ test_enable_disable_default_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -397,6 +399,7 @@ test_enable_disable_multiple_default_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id_1) { @@ -428,6 +431,7 @@ test_enable_disable_multiple_default_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id_2) { @@ -477,6 +481,7 @@ test_enable_disable_provider_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -557,6 +562,7 @@ test_enable_disable_provider_parse_default_config (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -628,6 +634,7 @@ test_create_delete_provider_with_callback (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -729,6 +736,7 @@ test_session_start_streaming (void) EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, + NULL, NULL); if (!session_id) { @@ -778,7 +786,7 @@ test_session_write_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -831,7 +839,7 @@ test_session_write_event_seq_point (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -888,7 +896,7 @@ test_session_write_wait_get_next_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, NULL); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -953,7 +961,7 @@ test_session_write_get_next_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -1030,7 +1038,7 @@ test_session_write_suspend_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -1090,7 +1098,7 @@ test_write_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -1141,7 +1149,7 @@ test_write_get_next_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; @@ -1201,7 +1209,7 @@ test_write_wait_get_next_event (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); session = ep_get_session (session_id); @@ -1281,7 +1289,7 @@ test_write_event_perf (void) test_location = 3; - session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4,false, NULL, false); + session_id = ep_enable (TEST_FILE, 1, current_provider_config, 1, EP_SESSION_TYPE_FILE, EP_SERIALIZATION_FORMAT_NETTRACE_V4, false, NULL, NULL, NULL); ep_raise_error_if_nok (session_id != 0); test_location = 4; diff --git a/src/mono/mono/eventpipe/test/ep-thread-tests.c b/src/mono/mono/eventpipe/test/ep-thread-tests.c index 2eb85ff976c..a08568c1bfc 100644 --- a/src/mono/mono/eventpipe/test/ep-thread-tests.c +++ b/src/mono/mono/eventpipe/test/ep-thread-tests.c @@ -398,7 +398,8 @@ test_thread_session_state (void) 1, provider_config, 1, - false); + NULL, + NULL); EP_LOCK_EXIT (section1) if (!session) { From 1ceda71444850106c043d07457bcb0d9219e405c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 30 Jun 2021 07:56:26 -0400 Subject: [PATCH 199/926] Remove some allocation from NotifyCollectionChangedEventArgs (#54899) * Remove some allocation from NotifyCollectionChangedEventArgs When initialized with a single object rather than a list of objects (single object is very common), the args is allocating an object[] and then wrapping that object[] in a ReadOnlyList. We can instead just allocate a simple read-only IList wrapper for the object directly; that also makes accessing it faster. Additionally, when constructing an instance using `public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems, int index, int oldIndex)`, even though the same collection instance is used for both the new and old items, it wraps it twice. We can wrap it once. Along the way I also simplified the code, enabling the fields to become readonly, removing duplicate assignments of fields, etc. * Address PR feedback --- .../src/System.ObjectModel.csproj | 1 + .../System/Collections/CollectionHelpers.cs | 73 ++++ .../ObjectModel/ReadOnlyDictionary.cs | 88 +---- .../NotifyCollectionChangedEventArgs.cs | 332 ++++++++---------- 4 files changed, 220 insertions(+), 274 deletions(-) create mode 100644 src/libraries/System.ObjectModel/src/System/Collections/CollectionHelpers.cs diff --git a/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj b/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj index 89776eac649..66c53d0dfb0 100644 --- a/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj +++ b/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj @@ -5,6 +5,7 @@ enable + diff --git a/src/libraries/System.ObjectModel/src/System/Collections/CollectionHelpers.cs b/src/libraries/System.ObjectModel/src/System/Collections/CollectionHelpers.cs new file mode 100644 index 00000000000..86058208873 --- /dev/null +++ b/src/libraries/System.ObjectModel/src/System/Collections/CollectionHelpers.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Collections +{ + internal static class CollectionHelpers + { + internal static void ValidateCopyToArguments(int sourceCount, Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (array.Rank != 1) + { + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (array.Length - index < sourceCount) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + } + + internal static void CopyTo(ICollection collection, Array array, int index) + { + ValidateCopyToArguments(collection.Count, array, index); + + if (collection is ICollection nonGenericCollection) + { + // Easy out if the ICollection implements the non-generic ICollection + nonGenericCollection.CopyTo(array, index); + } + else if (array is T[] items) + { + collection.CopyTo(items, index); + } + else + { + // We can't cast array of value type to object[], so we don't support widening of primitive types here. + if (array is not object?[] objects) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + + try + { + foreach (T item in collection) + { + objects[index++] = item; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + } + } + } +} diff --git a/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs b/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs index 28b4c06533f..428b0663d26 100644 --- a/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs +++ b/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs @@ -175,26 +175,7 @@ namespace System.Collections.ObjectModel void ICollection.CopyTo(Array array, int index) { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - if (array.Rank != 1) - { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - } - if (array.GetLowerBound(0) != 0) - { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); - } - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (array.Length - index < Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } + CollectionHelpers.ValidateCopyToArguments(Count, array, index); if (array is KeyValuePair[] pairs) { @@ -313,7 +294,7 @@ namespace System.Collections.ObjectModel void ICollection.CopyTo(Array array, int index) { - ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper(_collection, array, index); + CollectionHelpers.CopyTo(_collection, array, index); } bool ICollection.IsSynchronized => false; @@ -363,7 +344,7 @@ namespace System.Collections.ObjectModel void ICollection.CopyTo(Array array, int index) { - ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper(_collection, array, index); + CollectionHelpers.CopyTo(_collection, array, index); } bool ICollection.IsSynchronized => false; @@ -371,67 +352,4 @@ namespace System.Collections.ObjectModel object ICollection.SyncRoot => (_collection is ICollection coll) ? coll.SyncRoot : this; } } - - // To share code when possible, use a non-generic class to get rid of irrelevant type parameters. - internal static class ReadOnlyDictionaryHelpers - { - // Abstracted away to avoid redundant implementations. - internal static void CopyToNonGenericICollectionHelper(ICollection collection, Array array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - if (array.Rank != 1) - { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - } - if (array.GetLowerBound(0) != 0) - { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); - } - if (index < 0) - { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (array.Length - index < collection.Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - // Easy out if the ICollection implements the non-generic ICollection - if (collection is ICollection nonGenericCollection) - { - nonGenericCollection.CopyTo(array, index); - return; - } - - if (array is T[] items) - { - collection.CopyTo(items, index); - } - else - { - // We can't cast array of value type to object[], so we don't support - // widening of primitive types here. - object?[]? objects = array as object?[]; - if (objects == null) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - - try - { - foreach (var item in collection) - { - objects[index++] = item; - } - } - catch (ArrayTypeMismatchException) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - } - } - } } diff --git a/src/libraries/System.ObjectModel/src/System/Collections/Specialized/NotifyCollectionChangedEventArgs.cs b/src/libraries/System.ObjectModel/src/System/Collections/Specialized/NotifyCollectionChangedEventArgs.cs index 3638daf718e..60b9898affd 100644 --- a/src/libraries/System.ObjectModel/src/System/Collections/Specialized/NotifyCollectionChangedEventArgs.cs +++ b/src/libraries/System.ObjectModel/src/System/Collections/Specialized/NotifyCollectionChangedEventArgs.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; namespace System.Collections.Specialized { @@ -14,11 +13,11 @@ namespace System.Collections.Specialized /// public class NotifyCollectionChangedEventArgs : EventArgs { - private NotifyCollectionChangedAction _action; - private IList? _newItems; - private IList? _oldItems; - private int _newStartingIndex = -1; - private int _oldStartingIndex = -1; + private readonly NotifyCollectionChangedAction _action; + private readonly IList? _newItems; + private readonly IList? _oldItems; + private readonly int _newStartingIndex = -1; + private readonly int _oldStartingIndex = -1; /// /// Construct a NotifyCollectionChangedEventArgs that describes a reset change. @@ -31,7 +30,7 @@ namespace System.Collections.Specialized throw new ArgumentException(SR.Format(SR.WrongActionForCtor, NotifyCollectionChangedAction.Reset), nameof(action)); } - InitializeAdd(action, null, -1); + _action = action; } /// @@ -39,27 +38,9 @@ namespace System.Collections.Specialized /// /// The action that caused the event; can only be Reset, Add or Remove action. /// The item affected by the change. - public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? changedItem) + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? changedItem) : + this(action, changedItem, -1) { - if ((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove) - && (action != NotifyCollectionChangedAction.Reset)) - { - throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); - } - - if (action == NotifyCollectionChangedAction.Reset) - { - if (changedItem != null) - { - throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); - } - - InitializeAdd(action, null, -1); - } - else - { - InitializeAddOrRemove(action, new object?[] { changedItem }, -1); - } } /// @@ -70,29 +51,34 @@ namespace System.Collections.Specialized /// The index where the change occurred. public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? changedItem, int index) { - if ((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove) - && (action != NotifyCollectionChangedAction.Reset)) + switch (action) { - throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); + case NotifyCollectionChangedAction.Reset: + if (changedItem != null) + { + throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); + } + if (index != -1) + { + throw new ArgumentException(SR.ResetActionRequiresIndexMinus1, nameof(action)); + } + break; + + case NotifyCollectionChangedAction.Add: + _newItems = new SingleItemReadOnlyList(changedItem); + _newStartingIndex = index; + break; + + case NotifyCollectionChangedAction.Remove: + _oldItems = new SingleItemReadOnlyList(changedItem); + _oldStartingIndex = index; + break; + + default: + throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); } - if (action == NotifyCollectionChangedAction.Reset) - { - if (changedItem != null) - { - throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); - } - if (index != -1) - { - throw new ArgumentException(SR.ResetActionRequiresIndexMinus1, nameof(action)); - } - - InitializeAdd(action, null, -1); - } - else - { - InitializeAddOrRemove(action, new object?[] { changedItem }, index); - } + _action = action; } /// @@ -100,32 +86,9 @@ namespace System.Collections.Specialized /// /// The action that caused the event. /// The items affected by the change. - public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems) + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems) : + this(action, changedItems, -1) { - if ((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove) - && (action != NotifyCollectionChangedAction.Reset)) - { - throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); - } - - if (action == NotifyCollectionChangedAction.Reset) - { - if (changedItems != null) - { - throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); - } - - InitializeAdd(action, null, -1); - } - else - { - if (changedItems == null) - { - throw new ArgumentNullException(nameof(changedItems)); - } - - InitializeAddOrRemove(action, changedItems, -1); - } } /// @@ -136,38 +99,47 @@ namespace System.Collections.Specialized /// The index where the change occurred. public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems, int startingIndex) { - if ((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove) - && (action != NotifyCollectionChangedAction.Reset)) + switch (action) { - throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); + case NotifyCollectionChangedAction.Reset: + if (changedItems != null) + { + throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); + } + if (startingIndex != -1) + { + throw new ArgumentException(SR.ResetActionRequiresIndexMinus1, nameof(action)); + } + break; + + case NotifyCollectionChangedAction.Add: + case NotifyCollectionChangedAction.Remove: + if (changedItems == null) + { + throw new ArgumentNullException(nameof(changedItems)); + } + if (startingIndex < -1) + { + throw new ArgumentException(SR.IndexCannotBeNegative, nameof(startingIndex)); + } + + if (action == NotifyCollectionChangedAction.Add) + { + _newItems = new ReadOnlyList(changedItems); + _newStartingIndex = startingIndex; + } + else + { + _oldItems = new ReadOnlyList(changedItems); + _oldStartingIndex = startingIndex; + } + break; + + default: + throw new ArgumentException(SR.MustBeResetAddOrRemoveActionForCtor, nameof(action)); } - if (action == NotifyCollectionChangedAction.Reset) - { - if (changedItems != null) - { - throw new ArgumentException(SR.ResetActionRequiresNullItem, nameof(action)); - } - if (startingIndex != -1) - { - throw new ArgumentException(SR.ResetActionRequiresIndexMinus1, nameof(action)); - } - - InitializeAdd(action, null, -1); - } - else - { - if (changedItems == null) - { - throw new ArgumentNullException(nameof(changedItems)); - } - if (startingIndex < -1) - { - throw new ArgumentException(SR.IndexCannotBeNegative, nameof(startingIndex)); - } - - InitializeAddOrRemove(action, changedItems, startingIndex); - } + _action = action; } /// @@ -176,14 +148,9 @@ namespace System.Collections.Specialized /// Can only be a Replace action. /// The new item replacing the original item. /// The original item that is replaced. - public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? newItem, object? oldItem) + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? newItem, object? oldItem) : + this(action, newItem, oldItem, -1) { - if (action != NotifyCollectionChangedAction.Replace) - { - throw new ArgumentException(SR.Format(SR.WrongActionForCtor, NotifyCollectionChangedAction.Replace), nameof(action)); - } - - InitializeMoveOrReplace(action, new object?[] { newItem }, new object?[] { oldItem }, -1, -1); } /// @@ -200,7 +167,10 @@ namespace System.Collections.Specialized throw new ArgumentException(SR.Format(SR.WrongActionForCtor, NotifyCollectionChangedAction.Replace), nameof(action)); } - InitializeMoveOrReplace(action, new object?[] { newItem }, new object?[] { oldItem }, index, index); + _action = action; + _newItems = new SingleItemReadOnlyList(newItem); + _oldItems = new SingleItemReadOnlyList(oldItem); + _newStartingIndex = _oldStartingIndex = index; } /// @@ -209,22 +179,9 @@ namespace System.Collections.Specialized /// Can only be a Replace action. /// The new items replacing the original items. /// The original items that are replaced. - public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems) + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems) : + this(action, newItems, oldItems, -1) { - if (action != NotifyCollectionChangedAction.Replace) - { - throw new ArgumentException(SR.Format(SR.WrongActionForCtor, NotifyCollectionChangedAction.Replace), nameof(action)); - } - if (newItems == null) - { - throw new ArgumentNullException(nameof(newItems)); - } - if (oldItems == null) - { - throw new ArgumentNullException(nameof(oldItems)); - } - - InitializeMoveOrReplace(action, newItems, oldItems, -1, -1); } /// @@ -249,7 +206,10 @@ namespace System.Collections.Specialized throw new ArgumentNullException(nameof(oldItems)); } - InitializeMoveOrReplace(action, newItems, oldItems, startingIndex, startingIndex); + _action = action; + _newItems = new ReadOnlyList(newItems); + _oldItems = new ReadOnlyList(oldItems); + _newStartingIndex = _oldStartingIndex = startingIndex; } /// @@ -270,8 +230,10 @@ namespace System.Collections.Specialized throw new ArgumentException(SR.IndexCannotBeNegative, nameof(index)); } - object?[] changedItems = new object?[] { changedItem }; - InitializeMoveOrReplace(action, changedItems, changedItems, index, oldIndex); + _action = action; + _newItems = _oldItems = new SingleItemReadOnlyList(changedItem); + _newStartingIndex = index; + _oldStartingIndex = oldIndex; } /// @@ -292,54 +254,12 @@ namespace System.Collections.Specialized throw new ArgumentException(SR.IndexCannotBeNegative, nameof(index)); } - InitializeMoveOrReplace(action, changedItems, changedItems, index, oldIndex); - } - - /// - /// Construct a NotifyCollectionChangedEventArgs with given fields (no validation). Used by WinRT marshaling. - /// - internal NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? newItems, IList? oldItems, int newIndex, int oldIndex) - { _action = action; - _newItems = (newItems == null) ? null : new ReadOnlyList(newItems); - _oldItems = (oldItems == null) ? null : new ReadOnlyList(oldItems); - _newStartingIndex = newIndex; + _newItems = _oldItems = changedItems is not null ? new ReadOnlyList(changedItems) : null; + _newStartingIndex = index; _oldStartingIndex = oldIndex; } - private void InitializeAddOrRemove(NotifyCollectionChangedAction action, IList? changedItems, int startingIndex) - { - if (action == NotifyCollectionChangedAction.Add) - { - InitializeAdd(action, changedItems, startingIndex); - } - else - { - Debug.Assert(action == NotifyCollectionChangedAction.Remove, $"Unsupported action: {action}"); - InitializeRemove(action, changedItems, startingIndex); - } - } - - private void InitializeAdd(NotifyCollectionChangedAction action, IList? newItems, int newStartingIndex) - { - _action = action; - _newItems = (newItems == null) ? null : new ReadOnlyList(newItems); - _newStartingIndex = newStartingIndex; - } - - private void InitializeRemove(NotifyCollectionChangedAction action, IList? oldItems, int oldStartingIndex) - { - _action = action; - _oldItems = (oldItems == null) ? null : new ReadOnlyList(oldItems); - _oldStartingIndex = oldStartingIndex; - } - - private void InitializeMoveOrReplace(NotifyCollectionChangedAction action, IList? newItems, IList? oldItems, int startingIndex, int oldStartingIndex) - { - InitializeAdd(action, newItems, startingIndex); - InitializeRemove(action, oldItems, oldStartingIndex); - } - /// /// The action that caused the event. /// @@ -397,40 +317,74 @@ namespace System.Collections.Specialized public object SyncRoot => _list.SyncRoot; - public int Add(object? value) - { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); - } + public int Add(object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); - public void Clear() - { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); - } + public void Clear() => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); public bool Contains(object? value) => _list.Contains(value); - public void CopyTo(Array array, int index) - { - _list.CopyTo(array, index); - } + public void CopyTo(Array array, int index) => _list.CopyTo(array, index); public IEnumerator GetEnumerator() => _list.GetEnumerator(); public int IndexOf(object? value) => _list.IndexOf(value); - public void Insert(int index, object? value) + public void Insert(int index, object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + + public void Remove(object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + + public void RemoveAt(int index) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + } + + internal sealed class SingleItemReadOnlyList : IList + { + private readonly object? _item; + + public SingleItemReadOnlyList(object? item) => _item = item; + + public object? this[int index] { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + get + { + if (index != 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return _item; + } + set => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public void Remove(object? value) + public bool IsFixedSize => true; + + public bool IsReadOnly => true; + + public int Count => 1; + + public bool IsSynchronized => false; + + public object SyncRoot => this; + + public IEnumerator GetEnumerator() { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + yield return _item; } - public void RemoveAt(int index) + public bool Contains(object? value) => _item is null ? value is null : _item.Equals(value); + + public int IndexOf(object? value) => Contains(value) ? 0 : -1; + + public void CopyTo(Array array, int index) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + CollectionHelpers.ValidateCopyToArguments(1, array, index); + array.SetValue(_item, index); } + + public int Add(object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + public void Clear() => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + public void Insert(int index, object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + public void Remove(object? value) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + public void RemoveAt(int index) => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } } From 2ebd621c81d0c1ef1c6950d2b08a54487b6f0125 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 30 Jun 2021 13:41:27 +0100 Subject: [PATCH 200/926] Unseal StringEnumConverter. Fix #30486 (#54917) --- .../System.Text.Json/ref/System.Text.Json.cs | 6 +++--- .../Json/Serialization/JsonStringEnumConverter.cs | 6 +++--- .../Serialization/EnumConverterTests.cs | 12 +++++------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index f22feb4a517..1e57f75ff97 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -847,12 +847,12 @@ namespace System.Text.Json.Serialization Metadata = 1, Serialization = 2, } - public sealed partial class JsonStringEnumConverter : System.Text.Json.Serialization.JsonConverterFactory + public partial class JsonStringEnumConverter : System.Text.Json.Serialization.JsonConverterFactory { public JsonStringEnumConverter() { } public JsonStringEnumConverter(System.Text.Json.JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true) { } - public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override System.Text.Json.Serialization.JsonConverter CreateConverter(System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public sealed override bool CanConvert(System.Type typeToConvert) { throw null; } + public sealed override System.Text.Json.Serialization.JsonConverter CreateConverter(System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } } public enum JsonUnknownTypeHandling { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonStringEnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonStringEnumConverter.cs index ac6fe18b9db..7d8d161acd0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonStringEnumConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonStringEnumConverter.cs @@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization /// /// Reading is case insensitive, writing can be customized via a . /// - public sealed class JsonStringEnumConverter : JsonConverterFactory + public class JsonStringEnumConverter : JsonConverterFactory { private readonly JsonNamingPolicy? _namingPolicy; private readonly EnumConverterOptions _converterOptions; @@ -45,13 +45,13 @@ namespace System.Text.Json.Serialization } /// - public override bool CanConvert(Type typeToConvert) + public sealed override bool CanConvert(Type typeToConvert) { return typeToConvert.IsEnum; } /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + public sealed override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => EnumConverterFactory.Create(typeToConvert, _converterOptions, _namingPolicy, options); } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs index 2365a920170..46cf684ce28 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs @@ -135,17 +135,15 @@ namespace System.Text.Json.Serialization.Tests [JsonConverter(typeof(JsonStringEnumConverter))] public DayOfWeek WorkStart { get; set; } public DayOfWeek WorkEnd { get; set; } - [LowerCaseEnum] + [JsonConverter(typeof(LowerCaseEnumConverter))] public DayOfWeek WeekEnd { get; set; } } - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false)] - private class LowerCaseEnumAttribute : JsonConverterAttribute + private class LowerCaseEnumConverter : JsonStringEnumConverter { - public LowerCaseEnumAttribute() { } - - public override JsonConverter CreateConverter(Type typeToConvert) - => new JsonStringEnumConverter(new ToLowerNamingPolicy()); + public LowerCaseEnumConverter() : base(new ToLowerNamingPolicy()) + { + } } [Fact] From 5b585012aab2a8bbd90b32fdfd9c6d48884f8074 Mon Sep 17 00:00:00 2001 From: Jan Jahoda Date: Wed, 30 Jun 2021 15:41:52 +0200 Subject: [PATCH 201/926] Add more renegotiate tests (#54609) * Add more renegotiate tests * Remove client certificates * Disable test unless #54692 is merged --- .../SslStreamNetworkStreamTest.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 00fafd6652b..6ff3afb23f3 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -315,6 +315,84 @@ namespace System.Net.Security.Tests } } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] + [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue("https://github.com/dotnet/runtime/pull/54692")] + public async Task SslStream_NegotiateClientCertificateAsync_ClientWriteData() + { + using CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(TestConfiguration.PassingTestTimeout); + + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); + using (client) + using (server) + { + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions() + { + TargetHost = Guid.NewGuid().ToString("N"), + EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, + }; + clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { ServerCertificate = serverCertificate }; + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + client.AuthenticateAsClientAsync(clientOptions, cts.Token), + server.AuthenticateAsServerAsync(serverOptions, cts.Token)); + + Assert.Null(server.RemoteCertificate); + + + var t = server.NegotiateClientCertificateAsync(cts.Token); + + // Send application data instead of Client hello. + await client.WriteAsync(new byte[500], cts.Token); + // Fail as it is not allowed to receive non hnadshake frames during handshake. + await Assert.ThrowsAsync(()=> t); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task SslStream_NegotiateClientCertificateAsync_ServerDontDrainClientData() + { + using CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(TestConfiguration.PassingTestTimeout); + + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); + using (client) + using (server) + { + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions() + { + TargetHost = Guid.NewGuid().ToString("N"), + EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, + }; + clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { ServerCertificate = serverCertificate }; + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + client.AuthenticateAsClientAsync(clientOptions, cts.Token), + server.AuthenticateAsServerAsync(serverOptions, cts.Token)); + + Assert.Null(server.RemoteCertificate); + + // Send application data instead of Client hello. + await client.WriteAsync(new byte[500], cts.Token); + // Server don't drain the client data + await server.ReadAsync(new byte[1]); + // Fail as it is not allowed to receive non hnadshake frames during handshake. + await Assert.ThrowsAsync(()=> + server.NegotiateClientCertificateAsync(cts.Token) + ); + } + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.SupportsTls13))] [InlineData(true)] [InlineData(false)] From 38df267589c9a3359d27f47e83230d8aba93c598 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 30 Jun 2021 16:57:51 +0200 Subject: [PATCH 202/926] add test for opening and reading from device interface (#54673) --- .../FileStreamConformanceTests.Windows.cs | 146 +++++++++++++++++- ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 5 +- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs index 1cfbd351824..b6b2be09ad5 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs @@ -4,13 +4,13 @@ using Microsoft.Win32.SafeHandles; using System.ComponentModel; using System.IO.Pipes; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.ServiceProcess; using System.Threading.Tasks; using Xunit; +using System.Threading; namespace System.IO.Tests { @@ -176,4 +176,148 @@ namespace System.IO.Tests [DllImport(Interop.Libraries.Netapi32)] public static extern int NetShareDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int reserved); } + + [PlatformSpecific(TestPlatforms.Windows)] // the test setup is Windows-specifc + [OuterLoop("Has a very complex setup logic that in theory might have some side-effects")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class DeviceInterfaceTests + { + [Fact] + public async Task DeviceInterfaceCanBeOpenedForAsyncIO() + { + FileStream? fileStream = OpenFirstAvailableDeviceInterface(); + + if (fileStream is null) + { + // it's OK to not have any such devices available + // this test is just best effort + return; + } + + using (fileStream) + { + Assert.True(fileStream.CanRead); + Assert.False(fileStream.CanWrite); + Assert.False(fileStream.CanSeek); // #54143 + + try + { + CancellationTokenSource cts = new(TimeSpan.FromMilliseconds(250)); + + await fileStream.ReadAsync(new byte[4096], cts.Token); + } + catch (OperationCanceledException) + { + // most likely there is no data available and the task is going to get cancelled + // which is fine, we just want to make sure that reading from devices is supported (#54143) + } + } + } + + private static FileStream? OpenFirstAvailableDeviceInterface() + { + const int DIGCF_PRESENT = 0x2; + const int DIGCF_DEVICEINTERFACE = 0x10; + const int ERROR_NO_MORE_ITEMS = 259; + + HidD_GetHidGuid(out Guid HidGuid); + IntPtr deviceInfoSet = SetupDiGetClassDevs(in HidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + try + { + SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA(); + deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData); + + uint deviceIndex = 0; + while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex++, ref deviceInfoData)) + { + if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS) + { + break; + } + + SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); + deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); + + if (!SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, in HidGuid, deviceIndex, ref deviceInterfaceData)) + { + continue; + } + + SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA(); + deviceInterfaceDetailData.cbSize = IntPtr.Size == 8 ? 8 : 6; + + uint size = (uint)Marshal.SizeOf(deviceInterfaceDetailData); + + if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, size, ref size, IntPtr.Zero)) + { + continue; + } + + string devicePath = deviceInterfaceDetailData.DevicePath; + Assert.StartsWith(@"\\?\hid", devicePath); + + try + { + return new FileStream(devicePath, FileMode.Open, FileAccess.Read, FileShare.Read, 0, FileOptions.Asynchronous); + } + catch (IOException) + { + continue; // device has been locked by another process + } + } + } + finally + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + + return null; + } + + [StructLayout(LayoutKind.Sequential)] + struct SP_DEVICE_INTERFACE_DATA + { + public int cbSize; + public Guid interfaceClassGuid; + public int flags; + private nuint reserved; + } + + [StructLayout(LayoutKind.Sequential)] + struct SP_DEVINFO_DATA + { + public uint cbSize; + public Guid ClassGuid; + public uint DevInst; + public nint Reserved; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct SP_DEVICE_INTERFACE_DETAIL_DATA + { + public int cbSize; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] // 256 should be always enough for device interface path + public string DevicePath; + } + + [DllImport("hid.dll", SetLastError = true)] + static extern void HidD_GetHidGuid(out Guid HidGuid); + + [DllImport("setupapi.dll", SetLastError = true)] + static extern IntPtr SetupDiGetClassDevs(in Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, int Flags); + + [DllImport("setupapi.dll", SetLastError = true)] + static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true)] + static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, in Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] + static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, ref uint RequiredSize, IntPtr DeviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true)] + static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); + } } diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 04d57626857..e9ab0a7365e 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -1,4 +1,4 @@ - + true true @@ -19,13 +19,14 @@ + + - From a18b2cbd06c3b53c8d42d38b3c859079003576b6 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 30 Jun 2021 08:23:22 -0700 Subject: [PATCH 203/926] Add `-metrics` argument to superpmi.py asmdiffs (#54930) All the passed metrics are passed through to the `--metrics` argument to jit-analyze. E.g., ``` superpmi.py asmdiffs -metrics PerfScore superpmi.py asmdiffs -metrics CodeSize,PerfScore superpmi.py asmdiffs -metrics CodeSize -metrics PerfScore superpmi.py asmdiffs -metrics CodeSize,PrologSize -metrics PerfScore ``` --- src/coreclr/scripts/superpmi.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index ce4332c3145..c516ec6c3a2 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -311,6 +311,7 @@ asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC i asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baseline JIT. Format is key=value, where key is the option name without leading COMPlus_...") asm_diff_parser.add_argument("-diff_jit_option", action="append", help="Option to pass to the diff JIT. Format is key=value, where key is the option name without leading COMPlus_...") asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed") +asm_diff_parser.add_argument("-metrics", action="append", help="Metrics option to pass to jit-analyze. Can be specified multiple times, or pass comma-separated values.") # subparser for upload upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[core_root_parser, target_parser]) @@ -1970,6 +1971,8 @@ class SuperPMIReplayAsmDiffs: summary_file_info = ( mch_file, md_summary_file ) all_md_summary_files.append(summary_file_info) command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ] + if self.coreclr_args.metrics: + command += [ "--metrics", ",".join(self.coreclr_args.metrics) ] run_and_log(command, logging.INFO) ran_jit_analyze = True @@ -3653,6 +3656,11 @@ def setup_args(args): "Unable to set tag.", modify_arg=lambda arg: make_safe_filename(arg) if arg is not None else arg) + coreclr_args.verify(args, + "metrics", + lambda unused: True, + "Unable to set metrics.") + process_base_jit_path_arg(coreclr_args) jit_in_product_location = False From a1feef9e823ed39196f5d5735cd563e2a479384d Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Wed, 30 Jun 2021 11:56:51 -0400 Subject: [PATCH 204/926] Don't include -llvm.o files in App Resources (fixes iOS samples w/ LLVM AOT) (#54856) Fixes: #54572 --- src/tasks/AppleAppBuilder/Xcode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index ecab7a12404..9c0af486b64 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -102,7 +102,7 @@ internal class Xcode string cmakeLists = Utils.GetEmbeddedResource("CMakeLists.txt.template") .Replace("%ProjectName%", projectName) - .Replace("%AppResources%", string.Join(Environment.NewLine, resources.Select(r => " " + Path.GetRelativePath(binDir, r)))) + .Replace("%AppResources%", string.Join(Environment.NewLine, resources.Where(r => !r.EndsWith("-llvm.o")).Select(r => " " + Path.GetRelativePath(binDir, r)))) .Replace("%MainSource%", nativeMainSource) .Replace("%MonoInclude%", monoInclude) .Replace("%HardenedRuntime%", hardenedRuntime ? "TRUE" : "FALSE"); From 815d32de22ebccb72df25ebf63ee9ea6d186c79c Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Wed, 30 Jun 2021 18:39:27 +0200 Subject: [PATCH 205/926] Remove unused parameter from get_code_base call (#54959) --- .../src/System/Reflection/RuntimeAssembly.cs | 4 ++-- src/mono/mono/metadata/icall-def-netcore.h | 2 +- src/mono/mono/metadata/icall.c | 11 +++-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 602cdc66876..693f6c9e447 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -78,7 +78,7 @@ namespace System.Reflection { get { - return get_code_base(this, false); + return get_code_base(this); } } @@ -467,7 +467,7 @@ namespace System.Reflection private extern string get_location(); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern string get_code_base(Assembly a, bool escaped); + private static extern string? get_code_base(Assembly a); [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern string get_fullname(Assembly a); diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 56b5804d153..e1fec10c366 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -270,7 +270,7 @@ HANDLES(RASSEM_6b, "GetTopLevelForwardedTypes", ves_icall_System_Reflection_Runt HANDLES(RASSEM_7, "InternalGetReferencedAssemblies", ves_icall_System_Reflection_Assembly_InternalGetReferencedAssemblies, GPtrArray_ptr, 1, (MonoReflectionAssembly)) HANDLES(RASSEM_8, "InternalImageRuntimeVersion", ves_icall_System_Reflection_RuntimeAssembly_InternalImageRuntimeVersion, MonoString, 1, (MonoReflectionAssembly)) HANDLES(RASSEM_9, "get_EntryPoint", ves_icall_System_Reflection_RuntimeAssembly_get_EntryPoint, MonoReflectionMethod, 1, (MonoReflectionAssembly)) -HANDLES(RASSEM_10, "get_code_base", ves_icall_System_Reflection_RuntimeAssembly_get_code_base, MonoString, 2, (MonoReflectionAssembly, MonoBoolean)) +HANDLES(RASSEM_10, "get_code_base", ves_icall_System_Reflection_RuntimeAssembly_get_code_base, MonoString, 1, (MonoReflectionAssembly)) HANDLES(RASSEM_11, "get_fullname", ves_icall_System_Reflection_RuntimeAssembly_get_fullname, MonoString, 1, (MonoReflectionAssembly)) HANDLES(RASSEM_12, "get_location", ves_icall_System_Reflection_RuntimeAssembly_get_location, MonoString, 1, (MonoReflectionAssembly)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 51a8fc0a607..84fc4df27c1 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -4459,7 +4459,7 @@ fail: } MonoStringHandle -ves_icall_System_Reflection_RuntimeAssembly_get_code_base (MonoReflectionAssemblyHandle assembly, MonoBoolean escaped, MonoError *error) +ves_icall_System_Reflection_RuntimeAssembly_get_code_base (MonoReflectionAssemblyHandle assembly, MonoError *error) { MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly, assembly); gchar *absolute; @@ -4472,13 +4472,8 @@ ves_icall_System_Reflection_RuntimeAssembly_get_code_base (MonoReflectionAssembl mono_icall_make_platform_path (absolute); - gchar *uri; - if (escaped) { - uri = g_filename_to_uri (absolute, NULL, NULL); - } else { - const gchar *prepend = mono_icall_get_file_path_prefix (absolute); - uri = g_strconcat (prepend, absolute, (const char*)NULL); - } + const gchar *prepend = mono_icall_get_file_path_prefix (absolute); + gchar *uri = g_strconcat (prepend, absolute, (const char*)NULL); g_free (absolute); From bfa08ef3923fe81ae06b114f0f84883e2006e60f Mon Sep 17 00:00:00 2001 From: Aaron Kunkle Date: Wed, 30 Jun 2021 11:15:32 -0700 Subject: [PATCH 206/926] Update perf.yml to Exclude Arm64 Queues (#54973) * split off arm64 perf pipeline * PR comments * PR responses * PR responses * Renaming --- eng/pipelines/coreclr/perf.yml | 76 ---------------------------------- 1 file changed, 76 deletions(-) diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 32ddf224a9d..0db890a8e57 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -41,7 +41,6 @@ jobs: runtimeFlavor: mono buildConfig: release platforms: - - Linux_arm64 - Linux_x64 # build coreclr and libraries @@ -50,29 +49,10 @@ jobs: jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml buildConfig: release platforms: - - Linux_arm64 - Linux_x64 jobParameters: testGroup: perf - # run arm64 interpreter jobs for mono - - template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml - buildConfig: release - runtimeFlavor: mono - platforms: - - Linux_arm64 - jobParameters: - testGroup: perf - liveLibrariesBuildConfig: Release - runtimeType: mono - codeGenType: 'Interpreter' - projectFile: microbenchmarks.proj - runKind: micro_mono - runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml - logicalmachine: 'perfa64' - # build mono on wasm - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -124,9 +104,7 @@ jobs: - Linux_x64 - windows_x64 - windows_x86 - - Linux_arm64 - Linux_musl_x64 - - windows_arm64 jobParameters: testGroup: perf @@ -173,27 +151,6 @@ jobs: archiveType: tar tarCompression: gz - - template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/common/global-build-job.yml - buildConfig: release - runtimeFlavor: mono - platforms: - - Linux_arm64 - jobParameters: - buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) - nameSuffix: AOT - isOfficialBuild: false - extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml - extraStepsParameters: - rootFolder: '$(Build.SourcesDirectory)/artifacts/' - includeRootFolder: true - displayName: AOT Mono Artifacts - artifactName: LinuxMonoAOTarm64 - archiveExtension: '.tar.gz' - archiveType: tar - tarCompression: gz - # build mono Android scenarios - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -342,7 +299,6 @@ jobs: runtimeFlavor: aot platforms: - Linux_x64 - - Linux_arm64 jobParameters: testGroup: perf liveLibrariesBuildConfig: Release @@ -438,38 +394,6 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml logicalmachine: 'perfowl' -# run coreclr Linux arm64 microbenchmarks perf job - - template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml - buildConfig: release - runtimeFlavor: coreclr - platforms: - - Linux_arm64 - jobParameters: - testGroup: perf - liveLibrariesBuildConfig: Release - projectFile: microbenchmarks.proj - runKind: micro - runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml - logicalmachine: 'perfa64' - -# run coreclr Windows arm64 microbenchmarks perf job - - template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml - buildConfig: release - runtimeFlavor: coreclr - platforms: - - windows_arm64 - jobParameters: - testGroup: perf - liveLibrariesBuildConfig: Release - projectFile: microbenchmarks.proj - runKind: micro - runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml - logicalmachine: 'perfsurf' - # run coreclr crossgen perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: From e594edd949114f5fe90ff19724be9a94aaf5111b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 30 Jun 2021 15:20:46 -0400 Subject: [PATCH 207/926] Update analyzer versions (#54888) --- eng/Versions.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 60393072c37..38d89aaab83 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,9 +46,9 @@ - 3.10.0-2.final - 3.10.0-2.final - 6.0.0-rc1.21320.2 + 3.10.0 + 3.10.0 + 6.0.0-rc1.21324.1 6.0.0-beta.21321.1 6.0.0-beta.21321.1 From 8818c1561089dfc4a7b4296982ee2f82a30c3c1d Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Wed, 30 Jun 2021 13:20:36 -0700 Subject: [PATCH 208/926] Optionally capture a dump during generational aware analysis (#54517) --- src/coreclr/inc/clrconfigvalues.h | 2 ++ .../vm/eventing/eventpipe/ds-rt-coreclr.h | 12 +------- src/coreclr/vm/excep.cpp | 20 +++++++++++++ src/coreclr/vm/excep.h | 2 +- src/coreclr/vm/finalizerthread.cpp | 13 +++++---- src/coreclr/vm/gcenv.ee.cpp | 28 +++++++++++++------ src/coreclr/vm/genanalysis.cpp | 17 +++++++++-- src/coreclr/vm/genanalysis.h | 5 +++- 8 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 3f21e41dfa3..0d2a1db98e4 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -722,6 +722,8 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GCGenAnalysisGen, W("GCGenAnalysisGen"), 0, "T RETAIL_CONFIG_DWORD_INFO(INTERNAL_GCGenAnalysisBytes, W("GCGenAnalysisBytes"), 0, "The number of bytes to trigger generational aware analysis") RETAIL_CONFIG_DWORD_INFO(INTERNAL_GCGenAnalysisIndex, W("GCGenAnalysisIndex"), 0, "The gc index to trigger generational aware analysis") RETAIL_CONFIG_STRING_INFO(INTERNAL_GCGenAnalysisCmd, W("GCGenAnalysisCmd"), "An optional filter to match with the command line used to spawn the process") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_GCGenAnalysisTrace, W("GCGenAnalysisTrace"), 1, "Enable/Disable capturing a trace") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_GCGenAnalysisDump, W("GCGenAnalysisDump"), 0, "Enable/Disable capturing a dump") // // Diagnostics Ports diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index 8094d7b22d9..646b8f9bd8f 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -195,20 +195,10 @@ ds_rt_generate_core_dump (DiagnosticsGenerateCoreDumpCommandPayload *payload) ds_ipc_result_t result = DS_IPC_E_FAIL; EX_TRY { -#ifdef HOST_WIN32 - if (GenerateCrashDump (reinterpret_cast(ds_generate_core_dump_command_payload_get_dump_name (payload)), + if (GenerateDump (reinterpret_cast(ds_generate_core_dump_command_payload_get_dump_name (payload)), static_cast(ds_generate_core_dump_command_payload_get_dump_type (payload)), (ds_generate_core_dump_command_payload_get_diagnostics (payload) != 0) ? true : false)) result = DS_IPC_S_OK; -#else - MAKE_UTF8PTR_FROMWIDE_NOTHROW (dump_name, reinterpret_cast(ds_generate_core_dump_command_payload_get_dump_name (payload))); - if (dump_name != nullptr) { - if (PAL_GenerateCoreDump (dump_name, - static_cast(ds_generate_core_dump_command_payload_get_dump_type (payload)), - (ds_generate_core_dump_command_payload_get_diagnostics (payload) != 0) ? true : false)) - result = DS_IPC_S_OK; - } -#endif } EX_CATCH {} EX_END_CATCH(SwallowAllExceptions); diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 1b192e68369..a1fdf255a5c 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4176,6 +4176,26 @@ InitializeCrashDump() #endif // HOST_WINDOWS +bool GenerateDump( + LPCWSTR dumpName, + int dumpType, + bool diag) +{ +#ifdef TARGET_UNIX + MAKE_UTF8PTR_FROMWIDE_NOTHROW (dumpNameUtf8, dumpName); + if (dumpNameUtf8 == nullptr) + { + return false; + } + else + { + return PAL_GenerateCoreDump(dumpNameUtf8, dumpType, diag); + } +#else // TARGET_UNIX + return GenerateCrashDump(dumpName, dumpType, diag); +#endif // TARGET_UNIX +} + //************************************************************************************ // Create crash dump if enabled and terminate process. Generates crash dumps for both // Windows and Linux if enabled. For Linux, it happens in TerminateProcess in the PAL. diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 365c2d148ba..f338dd76f4c 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -196,9 +196,9 @@ enum UnhandledExceptionLocation #ifdef HOST_WINDOWS void InitializeCrashDump(); -bool GenerateCrashDump(LPCWSTR dumpName, int dumpType, bool diag); void CreateCrashDumpIfEnabled(bool stackoverflow = false); #endif +bool GenerateDump(LPCWSTR dumpName, int dumpType, bool diag); // Generates crash dumps if enabled for both Windows and Linux void CrashDumpAndTerminateProcess(UINT exitCode); diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index a303400cbd7..1e4dbf913c8 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -274,14 +274,15 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args) if (gcGenAnalysisState == GcGenAnalysisState::Done) { gcGenAnalysisState = GcGenAnalysisState::Disabled; - EventPipeAdapter::Disable(gcGenAnalysisEventPipeSessionId); + if (gcGenAnalysisTrace) + { + EventPipeAdapter::Disable(gcGenAnalysisEventPipeSessionId); +#ifdef GEN_ANALYSIS_STRESS + GenAnalysis::EnableGenerationalAwareSession(); +#endif + } // Writing an empty file to indicate completion fclose(fopen(GENAWARE_COMPLETION_FILE_NAME,"w+")); -#ifdef GEN_ANALYSIS_STRESS - { - GenAnalysis::EnableGenerationalAwareSession(); - } -#endif } if (!bPriorityBoosted) diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 5be5bc2d9ec..82d4ccbd308 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1644,14 +1644,26 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGene if ((condemnedGeneration == gcGenAnalysisGen) && (promoted_bytes > (uint64_t)gcGenAnalysisBytes) && (gcIndex > (uint64_t)gcGenAnalysisIndex)) #endif { - EventPipeAdapter::ResumeSession(gcGenAnalysisEventPipeSession); - FireEtwGenAwareBegin((int)gcIndex, GetClrInstanceId()); - s_forcedGCInProgress = true; - GCProfileWalkHeap(true); - s_forcedGCInProgress = false; - reportGenerationBounds(); - FireEtwGenAwareEnd((int)gcIndex, GetClrInstanceId()); - EventPipeAdapter::PauseSession(gcGenAnalysisEventPipeSession); + if (gcGenAnalysisTrace) + { + EventPipeAdapter::ResumeSession(gcGenAnalysisEventPipeSession); + FireEtwGenAwareBegin((int)gcIndex, GetClrInstanceId()); + s_forcedGCInProgress = true; + GCProfileWalkHeap(true); + s_forcedGCInProgress = false; + reportGenerationBounds(); + FireEtwGenAwareEnd((int)gcIndex, GetClrInstanceId()); + EventPipeAdapter::PauseSession(gcGenAnalysisEventPipeSession); + } + if (gcGenAnalysisDump) + { + EX_TRY + { + GenerateDump (GENAWARE_DUMP_FILE_NAME, 2, false); + } + EX_CATCH {} + EX_END_CATCH(SwallowAllExceptions); + } gcGenAnalysisState = GcGenAnalysisState::Done; EnableFinalization(true); } diff --git a/src/coreclr/vm/genanalysis.cpp b/src/coreclr/vm/genanalysis.cpp index fa2fcdf0a22..65f37375c33 100644 --- a/src/coreclr/vm/genanalysis.cpp +++ b/src/coreclr/vm/genanalysis.cpp @@ -13,6 +13,8 @@ int64_t gcGenAnalysisGen = -1; int64_t gcGenAnalysisBytes = 0; int64_t gcGenAnalysisIndex = 0; uint32_t gcGenAnalysisBufferMB = 0; +bool gcGenAnalysisTrace = true; +bool gcGenAnalysisDump = false; /* static */ void GenAnalysis::Initialize() { @@ -41,6 +43,8 @@ uint32_t gcGenAnalysisBufferMB = 0; gcGenAnalysisGen = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCGenAnalysisGen); gcGenAnalysisIndex = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCGenAnalysisIndex); gcGenAnalysisBufferMB = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeCircularMB); + gcGenAnalysisTrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCGenAnalysisTrace); + gcGenAnalysisDump = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCGenAnalysisDump); gcGenAnalysisConfigured = GcGenAnalysisState::Enabled; } else @@ -51,14 +55,21 @@ uint32_t gcGenAnalysisBufferMB = 0; if ((gcGenAnalysisConfigured == GcGenAnalysisState::Enabled) && (gcGenAnalysisState == GcGenAnalysisState::Uninitialized)) #endif { - EnableGenerationalAwareSession(); - } + if (gcGenAnalysisTrace) + { + EnableGenerationalAwareSession(); + } + if (gcGenAnalysisDump) + { + gcGenAnalysisState = GcGenAnalysisState::Enabled; + } + } } /* static */ void GenAnalysis::EnableGenerationalAwareSession() { LPCWSTR outputPath = nullptr; - outputPath = GENAWARE_FILE_NAME; + outputPath = GENAWARE_TRACE_FILE_NAME; NewArrayHolder pProviders; int providerCnt = 1; pProviders = new COR_PRF_EVENTPIPE_PROVIDER_CONFIG[providerCnt]; diff --git a/src/coreclr/vm/genanalysis.h b/src/coreclr/vm/genanalysis.h index 84d9c67d13e..8253aa3eab0 100644 --- a/src/coreclr/vm/genanalysis.h +++ b/src/coreclr/vm/genanalysis.h @@ -18,7 +18,8 @@ enum GcGenAnalysisState Done = 3, }; -#define GENAWARE_FILE_NAME W("gcgenaware.nettrace") +#define GENAWARE_TRACE_FILE_NAME W("gcgenaware.nettrace") +#define GENAWARE_DUMP_FILE_NAME W("gcgenaware.dmp") #define GENAWARE_COMPLETION_FILE_NAME "gcgenaware.nettrace.completed" extern bool s_forcedGCInProgress; @@ -29,6 +30,8 @@ extern GcGenAnalysisState gcGenAnalysisConfigured; extern int64_t gcGenAnalysisGen; extern int64_t gcGenAnalysisBytes; extern int64_t gcGenAnalysisIndex; +extern bool gcGenAnalysisTrace; +extern bool gcGenAnalysisDump; class GenAnalysis { From 345eb8c83f77c8abd65f13349c19b79f36c4bf32 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 30 Jun 2021 20:15:52 -0400 Subject: [PATCH 209/926] Avoid object[1] allocation in PropertyInfo.SetValue (#54918) --- .../System/Reflection/Emit/DynamicMethod.cs | 7 ++- .../System/Reflection/MethodBase.CoreCLR.cs | 48 ++++++++---------- .../Reflection/RuntimeConstructorInfo.cs | 14 +++++- .../System/Reflection/RuntimeMethodInfo.cs | 50 ++++++++++++------- .../System/Reflection/RuntimePropertyInfo.cs | 28 +++++------ 5 files changed, 85 insertions(+), 62 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs index 336c1f0c986..8b6a412abed 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs @@ -460,7 +460,12 @@ namespace System.Reflection.Emit bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; StackAllocedArguments stackArgs = default; - Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + } + object? retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions); // copy out. This should be made only if ByRef are present. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs index 532d684605b..7cd1966cb35 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs @@ -65,42 +65,38 @@ namespace System.Reflection return parameterTypes; } - private protected Span CheckArguments(ref StackAllocedArguments stackArgs, object?[]? parameters, Binder? binder, + private protected Span CheckArguments(ref StackAllocedArguments stackArgs, ReadOnlySpan parameters, Binder? binder, BindingFlags invokeAttr, CultureInfo? culture, Signature sig) { Debug.Assert(Unsafe.SizeOf() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf(), "MaxStackAllocArgCount not properly defined."); + Debug.Assert(!parameters.IsEmpty); - Span copyOfParameters = default; + // We need to perform type safety validation against the incoming arguments, but we also need + // to be resilient against the possibility that some other thread (or even the binder itself!) + // may mutate the array after we've validated the arguments but before we've properly invoked + // the method. The solution is to copy the arguments to a different, not-user-visible buffer + // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are + // considered user-visible to threads which may still be holding on to returned instances. - if (parameters is not null) + Span copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount) + ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length) + : new Span(new object?[parameters.Length]); + + ParameterInfo[]? p = null; + for (int i = 0; i < parameters.Length; i++) { - // We need to perform type safety validation against the incoming arguments, but we also need - // to be resilient against the possibility that some other thread (or even the binder itself!) - // may mutate the array after we've validated the arguments but before we've properly invoked - // the method. The solution is to copy the arguments to a different, not-user-visible buffer - // as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are - // considered user-visible to threads which may still be holding on to returned instances. + object? arg = parameters[i]; + RuntimeType argRT = sig.Arguments[i]; - copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount) - ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length) - : new Span(new object?[parameters.Length]); - - ParameterInfo[]? p = null; - for (int i = 0; i < parameters.Length; i++) + if (arg == Type.Missing) { - object? arg = parameters[i]; - RuntimeType argRT = sig.Arguments[i]; - - if (arg == Type.Missing) - { - p ??= GetParametersNoCopy(); - if (p[i].DefaultValue == System.DBNull.Value) - throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); - arg = p[i].DefaultValue!; - } - copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr); + p ??= GetParametersNoCopy(); + if (p[i].DefaultValue == System.DBNull.Value) + throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters)); + arg = p[i].DefaultValue!; } + copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr); } return copyOfParameters; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs index c991b642cfc..21bcf10557f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -340,7 +340,12 @@ namespace System.Reflection bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; StackAllocedArguments stackArgs = default; - Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + } + object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions); // copy out. This should be made only if ByRef are present. @@ -394,7 +399,12 @@ namespace System.Reflection bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; StackAllocedArguments stackArgs = default; - Span arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig); + } + object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions)!; // ctor must return non-null // copy out. This should be made only if ByRef are present. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index 9d1023bda37..f66ab6dedbb 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -410,8 +410,26 @@ namespace System.Reflection [Diagnostics.DebuggerHidden] public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { + // INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type) + // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot + // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects. + if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0) + ThrowNoInvokeException(); + + // check basic method consistency. This call will throw if there are problems in the target/method relationship + CheckConsistency(obj); + + Signature sig = Signature; + int actualCount = (parameters != null) ? parameters.Length : 0; + if (sig.Arguments.Length != actualCount) + throw new TargetParameterCountException(SR.Arg_ParmCnt); + StackAllocedArguments stackArgs = default; // try to avoid intermediate array allocation if possible - Span arguments = InvokeArgumentsCheck(ref stackArgs, obj, invokeAttr, binder, parameters, culture); + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig); + } bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions); @@ -426,34 +444,30 @@ namespace System.Reflection [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] - private Span InvokeArgumentsCheck(ref StackAllocedArguments stackArgs, object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + internal object? InvokeOneParameter(object? obj, BindingFlags invokeAttr, Binder? binder, object? parameter, CultureInfo? culture) { - Signature sig = Signature; - - // get the signature - int formalCount = sig.Arguments.Length; - int actualCount = (parameters != null) ? parameters.Length : 0; - - INVOCATION_FLAGS invocationFlags = InvocationFlags; - // INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type) // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects. - if ((invocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0) + if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0) + { ThrowNoInvokeException(); + } // check basic method consistency. This call will throw if there are problems in the target/method relationship CheckConsistency(obj); - if (formalCount != actualCount) - throw new TargetParameterCountException(SR.Arg_ParmCnt); - - Span retVal = default; - if (actualCount != 0) + Signature sig = Signature; + if (sig.Arguments.Length != 1) { - retVal = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig); + throw new TargetParameterCountException(SR.Arg_ParmCnt); } - return retVal; + + StackAllocedArguments stackArgs = default; + Span arguments = CheckArguments(ref stackArgs, new ReadOnlySpan(ref parameter, 1), binder, invokeAttr, culture, sig); + + bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, constructor: false, wrapExceptions); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index f01ae3933d0..2eb46bf49b9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -239,7 +239,7 @@ namespace System.Reflection public override Type PropertyType => Signature.ReturnType; - public override MethodInfo? GetGetMethod(bool nonPublic) + public override RuntimeMethodInfo? GetGetMethod(bool nonPublic) { if (!Associates.IncludeAccessor(m_getterMethod, nonPublic)) return null; @@ -247,7 +247,7 @@ namespace System.Reflection return m_getterMethod; } - public override MethodInfo? GetSetMethod(bool nonPublic) + public override RuntimeMethodInfo? GetSetMethod(bool nonPublic) { if (!Associates.IncludeAccessor(m_setterMethod, nonPublic)) return null; @@ -282,7 +282,7 @@ namespace System.Reflection ParameterInfo[]? methParams = null; // First try to get the Get method. - MethodInfo? m = GetGetMethod(true); + RuntimeMethodInfo? m = GetGetMethod(true); if (m != null) { // There is a Get method so use it. @@ -337,7 +337,7 @@ namespace System.Reflection [Diagnostics.DebuggerHidden] public override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { - MethodInfo? m = GetGetMethod(true); + RuntimeMethodInfo? m = GetGetMethod(true); if (m == null) throw new ArgumentException(System.SR.Arg_GetMethNotFnd); return m.Invoke(obj, invokeAttr, binder, index, null); @@ -359,28 +359,26 @@ namespace System.Reflection [Diagnostics.DebuggerHidden] public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { - MethodInfo? m = GetSetMethod(true); + RuntimeMethodInfo? m = GetSetMethod(true); if (m == null) throw new ArgumentException(System.SR.Arg_SetMethNotFnd); - object?[] args; - if (index != null) + if (index is null) { - args = new object[index.Length + 1]; + m.InvokeOneParameter(obj, invokeAttr, binder, value, culture); + } + else + { + var args = new object?[index.Length + 1]; for (int i = 0; i < index.Length; i++) args[i] = index[i]; args[index.Length] = value; - } - else - { - args = new object[1]; - args[0] = value; - } - m.Invoke(obj, invokeAttr, binder, args, culture); + m.Invoke(obj, invokeAttr, binder, args, culture); + } } #endregion From 87cd70c4ea9f0452f52db7dc9fd5bb3564a2e9e6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 30 Jun 2021 17:52:57 -0700 Subject: [PATCH 210/926] Fix single method compilation of canonical methods (#54923) --- .../Compiler/SingleMethodCompilationModuleGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs index d091b8f21af..e6bd80b73b5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs @@ -36,7 +36,7 @@ namespace ILCompiler public override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) { - return method == _method; + return (method == _method) || (method == _method.GetCanonMethodTarget(CanonicalFormKind.Specific)); } public override void ApplyProfilerGuidedCompilationRestriction(ProfileDataManager profileGuidedCompileRestriction) From a5e27d0cab52a32b9ddf5f245e47f67569a2082a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 1 Jul 2021 00:37:22 -0500 Subject: [PATCH 211/926] [main] Update dependencies from 8 repositories (#54907) * Update dependencies from https://github.com/dotnet/arcade build 20210628.2 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21324.3 -> To Version 6.0.0-beta.21328.2 * Update dependencies from https://github.com/dotnet/icu build 20210628.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 6.0.0-preview.7.21321.1 -> To Version 6.0.0-preview.7.21328.1 * Update dependencies from https://github.com/dotnet/xharness build 20210628.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21328.2 -> To Version 1.0.0-prerelease.21328.4 * Update dependencies from https://github.com/dotnet/llvm-project build 20210628.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-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 From Version 11.1.0-alpha.1.21321.1 -> To Version 11.1.0-alpha.1.21328.1 * Update dependencies from https://github.com/mono/linker build 20210628.2 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21327.1 -> To Version 6.0.100-preview.6.21328.2 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210628.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21321.1 -> To Version 1.0.1-alpha.0.21328.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210629.1 Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64 From Version 6.0.0-preview.7.21323.1 -> To Version 6.0.0-preview.7.21329.1 * Address new IL3002 and IL3003 warnings for the latest linker to unblock the build. * Disable the trim analyzer for tests with EnableAggressiveTrimming * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210629.4 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21320.4 -> To Version 1.0.0-prerelease.21329.4 * Update dependencies from https://github.com/dotnet/arcade build 20210629.8 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21321.1 -> To Version 6.0.0-beta.21329.8 * Update dependencies from https://github.com/mono/linker build 20210629.2 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21317.4 -> To Version 6.0.100-preview.6.21329.2 * Update dependencies from https://github.com/dotnet/emsdk build 20210630.1 Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64 From Version 6.0.0-preview.7.21323.1 -> To Version 6.0.0-preview.7.21330.1 * Update dependencies from https://github.com/mono/linker build 20210630.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21317.4 -> To Version 6.0.100-preview.6.21330.1 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Ankit Jain Co-authored-by: Eric Erhardt --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 140 +++++++++--------- eng/Versions.props | 62 ++++---- eng/common/tools.ps1 | 11 +- eng/testing/tests.mobile.targets | 2 + global.json | 8 +- .../System/Reflection/Emit/AssemblyBuilder.cs | 1 + .../src/System/Reflection/RuntimeAssembly.cs | 1 + .../System/Reflection/Emit/AssemblyBuilder.cs | 1 + .../Context/Delegation/DelegatingAssembly.cs | 4 + .../TypeLoading/Assemblies/RoAssembly.cs | 3 + .../src/System/Reflection/RuntimeAssembly.cs | 3 + 12 files changed, 131 insertions(+), 107 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 14ccd5e0cfd..a5c11de1747 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21324.2", + "version": "1.0.0-prerelease.21328.4", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b8b08594ccc..cfe0a882b2d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 59588c1257a842089d0b7df3bad1cdd69ac720e1 + e7626ad8c04b150de635f920b5e8dede0aafaf73 https://github.com/dotnet/msquic @@ -10,69 +10,69 @@ - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 https://github.com/microsoft/vstest @@ -114,37 +114,37 @@ https://github.com/dotnet/runtime-assets 8d7b898b96cbdb868cac343e938173105287ed9e - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 - + https://github.com/dotnet/llvm-project - 4f0293e0a254a2f014643ecbe973b81f26c87fd4 + 51c322893cff67a67e503d00e9c328d9d40b6a06 https://github.com/dotnet/runtime @@ -178,45 +178,45 @@ https://github.com/dotnet/runtime f891033db5b8ebf651176a3dcc3bec74a217f85e - + https://github.com/mono/linker - c739a81ba553b00df1cb2f5b9974deae996b757a + f574448d16af45f7ac2c4b89d71dea73dec86726 - + https://github.com/dotnet/xharness - 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 + 2890d740e1dd9fc41a634777c4af59a3986b1f7b - + https://github.com/dotnet/xharness - 49b0eedd8ecb0dea7e00c89097a5cd0f1f584257 + 2890d740e1dd9fc41a634777c4af59a3986b1f7b - + https://github.com/dotnet/arcade - 36b148348ee8312f6369c0c56b0d0fe07deec603 + 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + b89374348ff2344a625677584be9dfc9bea2b971 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + b89374348ff2344a625677584be9dfc9bea2b971 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + b89374348ff2344a625677584be9dfc9bea2b971 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - f291c7f87a563f29ff2a9af7378495769d97389c + b89374348ff2344a625677584be9dfc9bea2b971 - + https://github.com/dotnet/emsdk - 617928847d1e11458527b8bbafb5577982291847 + f5349765b7af1970c5b25cce4ed278544907cbe0 - + https://github.com/dotnet/hotreload-utils - 6adb8ac00a59fe409f232b8b32758aa7d10b4d1d + 2b1536142083d6270dc40c5cba74fbb0a612beab https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index 38d89aaab83..d8104e89eb4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,19 +50,19 @@ 3.10.0 6.0.0-rc1.21324.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 2.5.1-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 - 6.0.0-beta.21321.1 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 2.5.1-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 + 6.0.0-beta.21329.8 5.9.0-preview.2 @@ -122,10 +122,10 @@ 6.0.0-beta.21314.1 6.0.0-beta.21314.1 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 - 1.0.0-prerelease.21320.4 + 1.0.0-prerelease.21329.4 + 1.0.0-prerelease.21329.4 + 1.0.0-prerelease.21329.4 + 1.0.0-prerelease.21329.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -149,9 +149,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21324.2 - 1.0.0-prerelease.21324.2 - 1.0.1-alpha.0.21314.1 + 1.0.0-prerelease.21328.4 + 1.0.0-prerelease.21328.4 + 1.0.1-alpha.0.21328.1 2.4.1 2.4.2 1.3.0 @@ -162,23 +162,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21317.4 + 6.0.100-preview.6.21330.1 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21315.3 + 6.0.0-preview.7.21328.1 6.0.0-preview.7.21328.2 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 - 11.1.0-alpha.1.21314.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21328.1 - 6.0.0-preview.7.21323.1 + 6.0.0-preview.7.21330.1 $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 5619c7aaee1..4b255203249 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -378,7 +378,16 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } - return $global:_MSBuildExe = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin\msbuild.exe" + + $local:BinFolder = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin" + $local:Prefer64bit = if (Get-Member -InputObject $vsRequirements -Name 'Prefer64bit') { $vsRequirements.Prefer64bit } else { $false } + if ($local:Prefer64bit -and (Test-Path(Join-Path $local:BinFolder "amd64"))) { + $global:_MSBuildExe = Join-Path $local:BinFolder "amd64\msbuild.exe" + } else { + $global:_MSBuildExe = Join-Path $local:BinFolder "msbuild.exe" + } + + return $global:_MSBuildExe } function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) { diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 83d95eae008..517ebf71985 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -13,6 +13,8 @@ true true + + false false diff --git a/global.json b/global.json index 433797e8116..77fd48d8d2a 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21329.8", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21321.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21321.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21329.8", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21329.8", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21329.8", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 845c5391eb2..a0faa6ad6e0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -104,6 +104,7 @@ namespace System.Reflection.Emit public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); [RequiresUnreferencedCode("Types might be removed")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 89fb20181a8..dc866cf6ba3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -83,6 +83,7 @@ namespace System.Reflection return null; } + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 70133628f19..5dbdeeb3a67 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -8,6 +8,7 @@ namespace System.Reflection.Emit { public sealed partial class AssemblyBuilder : Assembly { + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override MethodInfo? EntryPoint => null; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index cbbeda045ab..7b86c7f71e9 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -22,11 +22,13 @@ namespace System.Reflection.Context.Delegation UnderlyingAssembly = assembly; } +#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } } +#pragma warning restore IL3003 public override Module ManifestModule { @@ -108,6 +110,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetExportedTypes(); } +#pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream GetFile(string name) { @@ -125,6 +128,7 @@ namespace System.Reflection.Context.Delegation { return UnderlyingAssembly.GetFiles(getResourceModules); } +#pragma warning restore IL3003 public override Module[] GetLoadedModules(bool getResourceModules) { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs index 22b3a009390..949994d04b3 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.Serialization; @@ -43,10 +44,12 @@ namespace System.Reflection.TypeLoading public abstract override string Location { get; } #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string CodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string EscapedCodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 693f6c9e447..5fb9e074174 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -74,6 +74,7 @@ namespace System.Reflection public override bool ReflectionOnly => false; + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get @@ -252,7 +253,9 @@ namespace System.Reflection public override AssemblyName GetName(bool copiedName) { +#pragma warning disable IL3002 // Suppressing for now. See https://github.com/dotnet/runtime/issues/54835 return AssemblyName.Create(_mono_assembly, CodeBase); +#pragma warning restore IL3002 } [RequiresUnreferencedCode("Types might be removed")] From 61109138bc1d9bf98088a7dfe439ef3abac566db Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 1 Jul 2021 09:43:28 +0200 Subject: [PATCH 212/926] Remove GenerateCompiledExpressionsTempFilePathForEditing hack (#54977) This was fix in Feb 2019: https://github.com/dotnet/msbuild/pull/4100. --- src/libraries/Directory.Build.targets | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index d4b5f1ac040..27487851a0d 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -202,14 +202,6 @@ - - - - - Date: Thu, 1 Jul 2021 11:32:46 +0300 Subject: [PATCH 213/926] [JIT] Improve inliner: new heuristics, rely on PGO data (#52708) Co-authored-by: Andy Ayers --- src/coreclr/jit/compiler.hpp | 2 +- src/coreclr/jit/fgbasic.cpp | 655 ++++++++++++++++++--- src/coreclr/jit/importer.cpp | 82 +-- src/coreclr/jit/inline.def | 2 +- src/coreclr/jit/inline.h | 33 +- src/coreclr/jit/inlinepolicy.cpp | 938 ++++++++++++++++++------------ src/coreclr/jit/inlinepolicy.h | 135 +++-- src/coreclr/jit/jitconfigvalues.h | 11 + src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/optcse.cpp | 2 +- src/coreclr/jit/varset.h | 8 +- 11 files changed, 1316 insertions(+), 554 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index e898605c7b6..d05bfc6bbeb 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1516,7 +1516,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX inline bool Compiler::lvaHaveManyLocals() const { - return (lvaCount >= lclMAX_TRACKED); + return (lvaCount >= (unsigned)JitConfig.JitMaxLocalsToTrack()); } /***************************************************************************** diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 0807b69d7ee..62a9467da63 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -746,6 +746,15 @@ public: // Empty } + enum FgSlot + { + SLOT_INVALID = UINT_MAX, + SLOT_UNKNOWN = 0, + SLOT_CONSTANT = 1, + SLOT_ARRAYLEN = 2, + SLOT_ARGUMENT = 3 + }; + void Clear() { depth = 0; @@ -764,31 +773,55 @@ public: } void PushArgument(unsigned arg) { - Push(static_cast(SLOT_ARGUMENT + arg)); + Push((FgSlot)(SLOT_ARGUMENT + arg)); } - unsigned GetSlot0() const + FgSlot GetSlot0() const { - assert(depth >= 1); - return slot0; + return depth >= 1 ? slot0 : FgSlot::SLOT_UNKNOWN; } - unsigned GetSlot1() const + FgSlot GetSlot1() const { - assert(depth >= 2); - return slot1; + return depth >= 2 ? slot1 : FgSlot::SLOT_UNKNOWN; } - static bool IsConstant(unsigned value) + FgSlot Top(const int n = 0) + { + if (n == 0) + { + return depth >= 1 ? slot0 : SLOT_UNKNOWN; + } + if (n == 1) + { + return depth == 2 ? slot1 : SLOT_UNKNOWN; + } + unreached(); + } + static bool IsConstant(FgSlot value) { return value == SLOT_CONSTANT; } - static bool IsArrayLen(unsigned value) + static bool IsConstantOrConstArg(FgSlot value, InlineInfo* info) + { + return IsConstant(value) || IsConstArgument(value, info); + } + static bool IsArrayLen(FgSlot value) { return value == SLOT_ARRAYLEN; } - static bool IsArgument(unsigned value) + static bool IsArgument(FgSlot value) { return value >= SLOT_ARGUMENT; } - static unsigned SlotTypeToArgNum(unsigned value) + static bool IsConstArgument(FgSlot value, InlineInfo* info) + { + if ((info == nullptr) || !IsArgument(value)) + { + return false; + } + const unsigned argNum = value - SLOT_ARGUMENT; + assert(argNum < info->argCnt); + return info->inlArgInfo[argNum].argIsInvariant; + } + static unsigned SlotTypeToArgNum(FgSlot value) { assert(IsArgument(value)); return value - SLOT_ARGUMENT; @@ -805,34 +838,18 @@ public: { return depth >= 1; } - -private: - enum FgSlot + void Push(FgSlot slot) { - SLOT_INVALID = UINT_MAX, - SLOT_UNKNOWN = 0, - SLOT_CONSTANT = 1, - SLOT_ARRAYLEN = 2, - SLOT_ARGUMENT = 3 - }; - - void Push(FgSlot type) - { - switch (depth) + assert(depth <= 2); + slot1 = slot0; + slot0 = slot; + if (depth < 2) { - case 0: - ++depth; - slot0 = type; - break; - case 1: - ++depth; - FALLTHROUGH; - case 2: - slot1 = slot0; - slot0 = type; + depth++; } } +private: FgSlot slot0; FgSlot slot1; unsigned depth; @@ -843,21 +860,19 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed const BYTE* codeBegp = codeAddr; const BYTE* codeEndp = codeAddr + codeSize; unsigned varNum; - bool seenJump = false; - var_types varType = DUMMY_INIT(TYP_UNDEF); // TYP_ type - typeInfo ti; // Verifier type. + var_types varType = DUMMY_INIT(TYP_UNDEF); // TYP_ type + typeInfo ti; // Verifier type. bool typeIsNormed = false; FgStack pushedStack; const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0; const bool makeInlineObservations = (compInlineResult != nullptr); const bool isInlining = compIsForInlining(); - const bool isTier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); const bool isPreJit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); - const bool resolveTokens = makeInlineObservations && (isTier1 || isPreJit); + const bool isTier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); unsigned retBlocks = 0; - unsigned intrinsicCalls = 0; int prefixFlags = 0; - int value = 0; + bool preciseScan = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan(); + const bool resolveTokens = preciseScan && (isPreJit || isTier1); if (makeInlineObservations) { @@ -899,11 +914,22 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed CORINFO_RESOLVED_TOKEN resolvedToken; + OPCODE opcode = CEE_NOP; + OPCODE prevOpcode = CEE_NOP; + bool handled = false; while (codeAddr < codeEndp) { - OPCODE opcode = (OPCODE)getU1LittleEndian(codeAddr); + prevOpcode = opcode; + opcode = (OPCODE)getU1LittleEndian(codeAddr); codeAddr += sizeof(__int8); + if (!handled && preciseScan) + { + // Push something unknown to the stack since we couldn't find anything useful for inlining + pushedStack.PushUnknown(); + } + handled = false; + DECODE_OPCODE: if ((unsigned)opcode >= CEE_COUNT) @@ -918,7 +944,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations && (opcode >= CEE_LDNULL) && (opcode <= CEE_LDC_R8)) { + // LDTOKEN and LDSTR are handled below pushedStack.PushConstant(); + handled = true; } unsigned sz = opcodeSizes[opcode]; @@ -947,6 +975,28 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed BADCODE3("Illegal opcode", ": %02X", (int)opcode); } + case CEE_SIZEOF: + case CEE_LDTOKEN: + case CEE_LDSTR: + { + if (preciseScan) + { + pushedStack.PushConstant(); + handled = true; + } + break; + } + + case CEE_DUP: + { + if (preciseScan) + { + pushedStack.Push(pushedStack.Top()); + handled = true; + } + break; + } + case CEE_THROW: { if (makeInlineObservations) @@ -966,8 +1016,30 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed // toSkip > 0 means we most likely will hit a pattern (e.g. box+isinst+brtrue) that // will be folded into a const - // TODO: uncomment later - // codeAddr += toSkip; + if (preciseScan) + { + codeAddr += toSkip; + } + } + } + break; + } + + case CEE_CASTCLASS: + case CEE_ISINST: + { + if (makeInlineObservations) + { + FgStack::FgSlot slot = pushedStack.Top(); + if (FgStack::IsConstantOrConstArg(slot, impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN); + handled = true; // and keep argument in the pushedStack + } + else if (FgStack::IsArgument(slot)) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CAST); + handled = true; // and keep argument in the pushedStack } } break; @@ -989,7 +1061,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed CORINFO_METHOD_HANDLE methodHnd = nullptr; bool isJitIntrinsic = false; - bool mustExpand = false; NamedIntrinsic ni = NI_Illegal; if (resolveTokens) @@ -1001,34 +1072,317 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (isJitIntrinsic) { - intrinsicCalls++; ni = lookupNamedIntrinsic(methodHnd); - switch (ni) - { - case NI_IsSupported_True: - case NI_IsSupported_False: - { - pushedStack.PushConstant(); - break; - } + bool foldableIntrinsc = false; - default: + if (IsMathIntrinsic(ni)) + { + // Most Math(F) intrinsics have single arguments + foldableIntrinsc = FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo); + } + else + { + switch (ni) { - break; + // These are most likely foldable without arguments + case NI_System_Collections_Generic_Comparer_get_Default: + case NI_System_Collections_Generic_EqualityComparer_get_Default: + case NI_System_Enum_HasFlag: + case NI_System_GC_KeepAlive: + { + pushedStack.PushUnknown(); + foldableIntrinsc = true; + break; + } + + case NI_System_Span_get_Item: + case NI_System_ReadOnlySpan_get_Item: + { + if (FgStack::IsArgument(pushedStack.Top(0)) || FgStack::IsArgument(pushedStack.Top(1))) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK); + } + break; + } + + // These are foldable if the first argument is a constant + case NI_System_Type_get_IsValueType: + case NI_System_Type_GetTypeFromHandle: + case NI_System_String_get_Length: + case NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness: + case NI_System_Numerics_BitOperations_PopCount: +#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS) + case NI_Vector128_Create: + case NI_Vector256_Create: +#elif defined(TARGET_ARM64) && defined(FEATURE_HW_INTRINSICS) + case NI_Vector64_Create: + case NI_Vector128_Create: +#endif + { + // Top() in order to keep it as is in case of foldableIntrinsc + if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo)) + { + foldableIntrinsc = true; + } + break; + } + + // These are foldable if two arguments are constants + case NI_System_Type_op_Equality: + case NI_System_Type_op_Inequality: + case NI_System_String_get_Chars: + case NI_System_Type_IsAssignableTo: + case NI_System_Type_IsAssignableFrom: + { + if (FgStack::IsConstantOrConstArg(pushedStack.Top(0), impInlineInfo) && + FgStack::IsConstantOrConstArg(pushedStack.Top(1), impInlineInfo)) + { + foldableIntrinsc = true; + pushedStack.PushConstant(); + } + break; + } + + case NI_IsSupported_True: + case NI_IsSupported_False: + { + foldableIntrinsc = true; + pushedStack.PushConstant(); + break; + } +#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS) + case NI_Vector128_get_Count: + case NI_Vector256_get_Count: + foldableIntrinsc = true; + pushedStack.PushConstant(); + // TODO: check if it's a loop condition - we unroll such loops. + break; + case NI_Vector256_get_Zero: + case NI_Vector256_get_AllBitsSet: + foldableIntrinsc = true; + pushedStack.PushUnknown(); + break; +#elif defined(TARGET_ARM64) && defined(FEATURE_HW_INTRINSICS) + case NI_Vector64_get_Count: + case NI_Vector128_get_Count: + foldableIntrinsc = true; + pushedStack.PushConstant(); + break; + case NI_Vector128_get_Zero: + case NI_Vector128_get_AllBitsSet: + foldableIntrinsc = true; + pushedStack.PushUnknown(); + break; +#endif + + default: + { + break; + } + } + } + + if (foldableIntrinsc) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_INTRINSIC); + handled = true; + } + else if (ni != NI_Illegal) + { + // Otherwise note "intrinsic" (most likely will be lowered as single instructions) + // except Math where only a few intrinsics won't end up as normal calls + if (!IsMathIntrinsic(ni) || IsTargetIntrinsic(ni)) + { + compInlineResult->Note(InlineObservation::CALLEE_INTRINSIC); } } } - if ((OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET) + if ((codeAddr < codeEndp - sz) && (OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET) { // If the method has a call followed by a ret, assume that // it is a wrapper method. compInlineResult->Note(InlineObservation::CALLEE_LOOKS_LIKE_WRAPPER); } + + if (!isJitIntrinsic && !handled && FgStack::IsArgument(pushedStack.Top())) + { + // Optimistically assume that "call(arg)" returns something arg-dependent. + // However, we don't know how many args it expects and its return type. + handled = true; + } } break; + case CEE_LDIND_I1: + case CEE_LDIND_U1: + case CEE_LDIND_I2: + case CEE_LDIND_U2: + case CEE_LDIND_I4: + case CEE_LDIND_U4: + case CEE_LDIND_I8: + case CEE_LDIND_I: + case CEE_LDIND_R4: + case CEE_LDIND_R8: + case CEE_LDIND_REF: + { + if (FgStack::IsArgument(pushedStack.Top())) + { + handled = true; + } + break; + } + + // Unary operators: + case CEE_CONV_I: + case CEE_CONV_U: + case CEE_CONV_I1: + case CEE_CONV_I2: + case CEE_CONV_I4: + case CEE_CONV_I8: + case CEE_CONV_R4: + case CEE_CONV_R8: + case CEE_CONV_U4: + case CEE_CONV_U8: + case CEE_CONV_U2: + case CEE_CONV_U1: + case CEE_CONV_R_UN: + case CEE_CONV_OVF_I: + case CEE_CONV_OVF_U: + case CEE_CONV_OVF_I1: + case CEE_CONV_OVF_U1: + case CEE_CONV_OVF_I2: + case CEE_CONV_OVF_U2: + case CEE_CONV_OVF_I4: + case CEE_CONV_OVF_U4: + case CEE_CONV_OVF_I8: + case CEE_CONV_OVF_U8: + case CEE_CONV_OVF_I_UN: + case CEE_CONV_OVF_U_UN: + case CEE_CONV_OVF_I1_UN: + case CEE_CONV_OVF_I2_UN: + case CEE_CONV_OVF_I4_UN: + case CEE_CONV_OVF_I8_UN: + case CEE_CONV_OVF_U1_UN: + case CEE_CONV_OVF_U2_UN: + case CEE_CONV_OVF_U4_UN: + case CEE_CONV_OVF_U8_UN: + case CEE_NOT: + case CEE_NEG: + { + if (makeInlineObservations) + { + FgStack::FgSlot arg = pushedStack.Top(); + if (FgStack::IsConstArgument(arg, impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN); + handled = true; + } + else if (FgStack::IsArgument(arg) || FgStack::IsConstant(arg)) + { + handled = true; + } + } + break; + } + + // Binary operators: + case CEE_ADD: + case CEE_SUB: + case CEE_MUL: + case CEE_DIV: + case CEE_DIV_UN: + case CEE_REM: + case CEE_REM_UN: + case CEE_AND: + case CEE_OR: + case CEE_XOR: + case CEE_SHL: + case CEE_SHR: + case CEE_SHR_UN: + case CEE_ADD_OVF: + case CEE_ADD_OVF_UN: + case CEE_MUL_OVF: + case CEE_MUL_OVF_UN: + case CEE_SUB_OVF: + case CEE_SUB_OVF_UN: + case CEE_CEQ: + case CEE_CGT: + case CEE_CGT_UN: + case CEE_CLT: + case CEE_CLT_UN: + { + if (!makeInlineObservations) + { + break; + } + + if (!preciseScan) + { + switch (opcode) + { + case CEE_CEQ: + case CEE_CGT: + case CEE_CGT_UN: + case CEE_CLT: + case CEE_CLT_UN: + fgObserveInlineConstants(opcode, pushedStack, isInlining); + break; + default: + break; + } + } + else + { + FgStack::FgSlot arg0 = pushedStack.Top(1); + FgStack::FgSlot arg1 = pushedStack.Top(0); + + if ((FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo)) || + (FgStack::IsConstant(arg1) && FgStack::IsConstArgument(arg0, impInlineInfo)) || + (FgStack::IsConstArgument(arg0, impInlineInfo) && + FgStack::IsConstArgument(arg1, impInlineInfo))) + { + // keep stack unchanged + handled = true; + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR); + } + if ((FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1)) || + (FgStack::IsConstant(arg1) && FgStack::IsConstant(arg0))) + { + // both are constants, but we're mostly interested in cases where a const arg leads to + // a foldable expression. + handled = true; + } + else if (FgStack::IsArgument(arg0) && FgStack::IsConstantOrConstArg(arg1, impInlineInfo)) + { + // "Arg op CNS" --> keep arg0 in the stack for the next ops + handled = true; + compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS); + } + else if (FgStack::IsArgument(arg1) && FgStack::IsConstantOrConstArg(arg0, impInlineInfo)) + { + // "CNS op ARG" --> keep arg1 in the stack for the next ops + pushedStack.Push(arg1); + handled = true; + compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS); + } + + if (FgStack::IsConstArgument(arg1, impInlineInfo)) + { + // Special case: "X / ConstArg" or "X % ConstArg" + if ((opcode == CEE_DIV) || (opcode == CEE_DIV_UN) || (opcode == CEE_REM) || + (opcode == CEE_REM_UN)) + { + compInlineResult->Note(InlineObservation::CALLSITE_DIV_BY_CNS); + } + handled = true; + } + } + break; + } + + // Jumps case CEE_LEAVE: case CEE_LEAVE_S: case CEE_BR: @@ -1058,8 +1412,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed case CEE_BNE_UN: case CEE_BNE_UN_S: { - seenJump = true; - if (codeAddr > codeEndp - sz) { goto TOO_FAR; @@ -1082,21 +1434,152 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed BADCODE3("code jumps to outer space", " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp)); } + if (makeInlineObservations && (jmpDist < 0)) + { + compInlineResult->Note(InlineObservation::CALLEE_BACKWARD_JUMP); + } + // Mark the jump target jumpTarget->bitVectSet(jmpAddr); // See if jump might be sensitive to inlining - if (makeInlineObservations && (opcode != CEE_BR_S) && (opcode != CEE_BR)) + if (!preciseScan && makeInlineObservations && (opcode != CEE_BR_S) && (opcode != CEE_BR)) { fgObserveInlineConstants(opcode, pushedStack, isInlining); } + else if (preciseScan && makeInlineObservations) + { + switch (opcode) + { + // Binary + case CEE_BEQ: + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BNE_UN: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + case CEE_BEQ_S: + case CEE_BGE_S: + case CEE_BGT_S: + case CEE_BLE_S: + case CEE_BLT_S: + case CEE_BNE_UN_S: + case CEE_BGE_UN_S: + case CEE_BGT_UN_S: + case CEE_BLE_UN_S: + case CEE_BLT_UN_S: + { + FgStack::FgSlot op1 = pushedStack.Top(1); + FgStack::FgSlot op2 = pushedStack.Top(0); + + if (FgStack::IsConstantOrConstArg(op1, impInlineInfo) && + FgStack::IsConstantOrConstArg(op2, impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_BRANCH); + } + if (FgStack::IsConstArgument(op1, impInlineInfo) || + FgStack::IsConstArgument(op2, impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST); + } + + if ((FgStack::IsArgument(op1) && FgStack::IsArrayLen(op2)) || + (FgStack::IsArgument(op2) && FgStack::IsArrayLen(op1))) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK); + } + else if ((FgStack::IsArgument(op1) && FgStack::IsConstantOrConstArg(op2, impInlineInfo)) || + (FgStack::IsArgument(op2) && FgStack::IsConstantOrConstArg(op1, impInlineInfo))) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST); + } + else if (FgStack::IsArgument(op1) || FgStack::IsArgument(op2)) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_TEST); + } + else if (FgStack::IsConstant(op1) || FgStack::IsConstant(op2)) + { + compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS); + } + break; + } + + // Unary + case CEE_BRFALSE_S: + case CEE_BRTRUE_S: + case CEE_BRFALSE: + case CEE_BRTRUE: + { + if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_BRANCH); + } + else if (FgStack::IsArgument(pushedStack.Top())) + { + // E.g. brtrue is basically "if (X == 0)" + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST); + } + break; + } + + default: + break; + } + } } break; + case CEE_LDFLDA: + case CEE_LDFLD: + case CEE_STFLD: + { + if (FgStack::IsArgument(pushedStack.Top())) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_STRUCT_FIELD_ACCESS); + handled = true; // keep argument on top of the stack + } + break; + } + + case CEE_LDELEM_I1: + case CEE_LDELEM_U1: + case CEE_LDELEM_I2: + case CEE_LDELEM_U2: + case CEE_LDELEM_I4: + case CEE_LDELEM_U4: + case CEE_LDELEM_I8: + case CEE_LDELEM_I: + case CEE_LDELEM_R4: + case CEE_LDELEM_R8: + case CEE_LDELEM_REF: + case CEE_STELEM_I: + case CEE_STELEM_I1: + case CEE_STELEM_I2: + case CEE_STELEM_I4: + case CEE_STELEM_I8: + case CEE_STELEM_R4: + case CEE_STELEM_R8: + case CEE_STELEM_REF: + case CEE_LDELEM: + case CEE_STELEM: + { + if (!preciseScan) + { + break; + } + if (FgStack::IsArgument(pushedStack.Top()) || FgStack::IsArgument(pushedStack.Top(1))) + { + compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK); + } + break; + } + case CEE_SWITCH: { - seenJump = true; - if (makeInlineObservations) { compInlineResult->Note(InlineObservation::CALLEE_HAS_SWITCH); @@ -1160,10 +1643,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed noway_assert(sz == sizeof(__int8)); prefixFlags |= PREFIX_UNALIGNED; - value = getU1LittleEndian(codeAddr); codeAddr += sizeof(__int8); impValidateMemoryAccessOpcode(codeAddr, codeEndp, false); + handled = true; goto OBSERVE_OPCODE; } @@ -1182,6 +1665,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed BADCODE("constrained. has to be followed by callvirt, call or ldftn"); } } + handled = true; goto OBSERVE_OPCODE; } @@ -1198,6 +1682,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed BADCODE("readonly. has to be followed by ldelema or call"); } } + handled = true; goto OBSERVE_OPCODE; } @@ -1207,6 +1692,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed prefixFlags |= PREFIX_VOLATILE; impValidateMemoryAccessOpcode(codeAddr, codeEndp, true); + handled = true; goto OBSERVE_OPCODE; } @@ -1223,6 +1709,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed BADCODE("tailcall. has to be followed by call, callvirt or calli"); } } + handled = true; goto OBSERVE_OPCODE; } @@ -1316,6 +1803,19 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } break; + case CEE_LDLOC_0: + case CEE_LDLOC_1: + case CEE_LDLOC_2: + case CEE_LDLOC_3: + // + if (preciseScan && makeInlineObservations && (prevOpcode == (CEE_STLOC_3 - (CEE_LDLOC_3 - opcode)))) + { + // Fold stloc+ldloc + pushedStack.Push(pushedStack.Top(1)); // throw away SLOT_UNKNOWN inserted by STLOC + handled = true; + } + break; + case CEE_LDARGA: case CEE_LDARGA_S: case CEE_LDLOCA: @@ -1350,6 +1850,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed impInlineInfo->inlArgInfo[varNum].argHasLdargaOp = true; pushedStack.PushArgument(varNum); + handled = true; } } else @@ -1479,6 +1980,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { pushedStack.PushArgument(opcode - CEE_LDARG_0); + handled = true; } break; @@ -1495,6 +1997,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { pushedStack.PushArgument(varNum); + handled = true; } } break; @@ -1503,17 +2006,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { pushedStack.PushArrayLen(); - } - break; - - case CEE_CEQ: - case CEE_CGT: - case CEE_CGT_UN: - case CEE_CLT: - case CEE_CLT_UN: - if (makeInlineObservations) - { - fgObserveInlineConstants(opcode, pushedStack, isInlining); + handled = true; } break; @@ -1646,10 +2139,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } } -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - //------------------------------------------------------------------------ // fgAdjustForAddressExposedOrWrittenThis: update var table for cases // where the this pointer value can change. @@ -1723,7 +2212,7 @@ void Compiler::fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, boo { if (opcode == CEE_BRFALSE || opcode == CEE_BRFALSE_S || opcode == CEE_BRTRUE || opcode == CEE_BRTRUE_S) { - unsigned slot0 = stack.GetSlot0(); + FgStack::FgSlot slot0 = stack.GetSlot0(); if (FgStack::IsArgument(slot0)) { compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST); @@ -1750,8 +2239,8 @@ void Compiler::fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, boo return; } - unsigned slot0 = stack.GetSlot0(); - unsigned slot1 = stack.GetSlot1(); + FgStack::FgSlot slot0 = stack.GetSlot0(); + FgStack::FgSlot slot1 = stack.GetSlot1(); // Arg feeds constant test if ((FgStack::IsConstant(slot0) && FgStack::IsArgument(slot1)) || @@ -1794,6 +2283,10 @@ void Compiler::fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, boo } } +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + //------------------------------------------------------------------------ // fgMarkBackwardJump: mark blocks indicating there is a jump backwards in // IL, from a higher to lower IL offset. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index fc8544f9138..66998450dbd 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -18921,36 +18921,34 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I inlineResult->Note(InlineObservation::CALLSITE_NONGENERIC_CALLS_GENERIC); } - if (pInlineInfo != nullptr) + // Inspect callee's arguments (and the actual values at the callsite for them) + CORINFO_SIG_INFO sig = info.compMethodInfo->args; + CORINFO_ARG_LIST_HANDLE sigArg = sig.args; + + GenTreeCall::Use* argUse = pInlineInfo == nullptr ? nullptr : pInlineInfo->iciCall->AsCall()->gtCallArgs; + + for (unsigned i = 0; i < info.compMethodInfo->args.numArgs; i++) { - // Inspect callee's arguments (and the actual values at the callsite for them) - CORINFO_SIG_INFO sig = info.compMethodInfo->args; - CORINFO_ARG_LIST_HANDLE sigArg = sig.args; + CORINFO_CLASS_HANDLE sigClass; + CorInfoType corType = strip(info.compCompHnd->getArgType(&sig, sigArg, &sigClass)); + GenTree* argNode = argUse == nullptr ? nullptr : argUse->GetNode()->gtSkipPutArgType(); - GenTreeCall::Use* argUse = pInlineInfo->iciCall->AsCall()->gtCallArgs; - - for (unsigned i = 0; i < info.compMethodInfo->args.numArgs; i++) + if (corType == CORINFO_TYPE_CLASS) { - assert(argUse != nullptr); - - CORINFO_CLASS_HANDLE sigClass; - CorInfoType corType = strip(info.compCompHnd->getArgType(&sig, sigArg, &sigClass)); - GenTree* argNode = argUse->GetNode()->gtSkipPutArgType(); - - if (corType == CORINFO_TYPE_CLASS) - { - sigClass = info.compCompHnd->getArgClass(&sig, sigArg); - } - else if (corType == CORINFO_TYPE_VALUECLASS) - { - inlineResult->Note(InlineObservation::CALLEE_ARG_STRUCT); - } - else if (corType == CORINFO_TYPE_BYREF) - { - sigClass = info.compCompHnd->getArgClass(&sig, sigArg); - corType = info.compCompHnd->getChildType(sigClass, &sigClass); - } + sigClass = info.compCompHnd->getArgClass(&sig, sigArg); + } + else if (corType == CORINFO_TYPE_VALUECLASS) + { + inlineResult->Note(InlineObservation::CALLEE_ARG_STRUCT); + } + else if (corType == CORINFO_TYPE_BYREF) + { + sigClass = info.compCompHnd->getArgClass(&sig, sigArg); + corType = info.compCompHnd->getChildType(sigClass, &sigClass); + } + if (argNode != nullptr) + { bool isExact = false; bool isNonNull = false; CORINFO_CLASS_HANDLE argCls = gtGetClassHandle(argNode, &isExact, &isNonNull); @@ -18978,10 +18976,9 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I { inlineResult->Note(InlineObservation::CALLSITE_ARG_CONST); } - - sigArg = info.compCompHnd->getArgNext(sigArg); argUse = argUse->GetNext(); } + sigArg = info.compCompHnd->getArgNext(sigArg); } // Note if the callee's return type is a value type @@ -18998,6 +18995,7 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I { inlineResult->Note(InlineObservation::CALLEE_CLASS_PROMOTABLE); } + inlineResult->Note(InlineObservation::CALLEE_CLASS_VALUETYPE); } #ifdef FEATURE_SIMD @@ -19071,13 +19069,9 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I assert(callSiteWeight >= 0); assert(entryWeight >= 0); - if (entryWeight != 0) - { - inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true); - - double frequency = callSiteWeight / entryWeight; - inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, frequency); - } + inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true); + double frequency = entryWeight == 0.0 ? 0.0 : callSiteWeight / entryWeight; + inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, frequency); } } @@ -20626,14 +20620,7 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) return compOpportunisticallyDependsOn(InstructionSet_SSE41); case NI_System_Math_FusedMultiplyAdd: - { - // AMD64/x86 has FMA3 instructions to directly compute fma. However, in - // the scenario where it is supported we should have generated GT_HWINTRINSIC - // nodes in place of the GT_INTRINSIC node. - - assert(!compIsaSupportedDebugOnly(InstructionSet_FMA)); - return false; - } + return compOpportunisticallyDependsOn(InstructionSet_FMA); default: return false; @@ -20649,14 +20636,7 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) return true; case NI_System_Math_FusedMultiplyAdd: - { - // ARM64 has AdvSimd instructions to directly compute fma. However, in - // the scenario where it is supported we should have generated GT_HWINTRINSIC - // nodes in place of the GT_INTRINSIC node. - - assert(!compIsaSupportedDebugOnly(InstructionSet_AdvSimd)); - return false; - } + return compOpportunisticallyDependsOn(InstructionSet_AdvSimd); default: return false; diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def index 001cb415491..f21e4f929ea 100644 --- a/src/coreclr/jit/inline.def +++ b/src/coreclr/jit/inline.def @@ -172,7 +172,7 @@ INLINE_OBSERVATION(RARE_GC_STRUCT, bool, "rarely called, has gc str // ------ Call Site Information ------- -INLINE_OBSERVATION(NONGENERIC_CALLS_GENERIC, bool, "callee is generic and caller is not", INFORMATION, CALLSITE) +INLINE_OBSERVATION(NONGENERIC_CALLS_GENERIC, bool, "callee is generic and caller is not", INFORMATION, CALLSITE) INLINE_OBSERVATION(ARG_EXACT_CLS, int, "arg is of an exact class", INFORMATION, CALLSITE) INLINE_OBSERVATION(ARG_EXACT_CLS_SIG_IS_NOT, int, "arg is more concrete than in sig.", INFORMATION, CALLSITE) INLINE_OBSERVATION(ARG_CONST, int, "arg is a constant", INFORMATION, CALLSITE) diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index f74f9e53109..bc08dd8110d 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -248,6 +248,12 @@ public: // Policy estimates virtual int CodeSizeEstimate() = 0; + // Does Policy require a more precise IL scan? + virtual bool RequiresPreciseScan() + { + return false; + } + #if defined(DEBUG) || defined(INLINE_DATA) // Record observation for prior failure @@ -263,10 +269,35 @@ public: virtual void DumpSchema(FILE* file) const { } + +#define XATTR_I4(x) \ + if ((INT32)x != 0) \ + { \ + fprintf(file, " " #x "=\"%d\"", (INT32)x); \ + } +#define XATTR_R8(x) \ + if (fabs(x) > 0.01) \ + { \ + fprintf(file, " " #x "=\"%.2lf\"", x); \ + } +#define XATTR_B(x) \ + if (x) \ + { \ + fprintf(file, " " #x "=\"True\""); \ + } + // Detailed data value dump as XML - virtual void DumpXml(FILE* file, unsigned indent = 0) const + void DumpXml(FILE* file, unsigned indent = 0) + { + fprintf(file, "%*s<%s", indent, "", GetName()); + OnDumpXml(file); + fprintf(file, " />\n"); + } + + virtual void OnDumpXml(FILE* file, unsigned indent = 0) const { } + // True if this is the inline targeted by data collection bool IsDataCollectionTarget() { diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index 25cd23cf522..c328233cdf5 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -94,7 +94,17 @@ InlinePolicy* InlinePolicy::GetPolicy(Compiler* compiler, bool isPrejitRoot) return new (compiler, CMK_Inlining) ProfilePolicy(compiler, isPrejitRoot); } - // Use the default policy by default + const bool isPrejit = compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); + const bool isSpeedOpt = compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_SPEED_OPT); + + if ((JitConfig.JitExtDefaultPolicy() != 0)) + { + if (isPrejitRoot || !isPrejit || (isPrejit && isSpeedOpt)) + { + return new (compiler, CMK_Inlining) ExtendedDefaultPolicy(compiler, isPrejitRoot); + } + } + return new (compiler, CMK_Inlining) DefaultPolicy(compiler, isPrejitRoot); } @@ -288,94 +298,10 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value) m_IsInstanceCtor = value; break; - case InlineObservation::CALLEE_RETURNS_STRUCT: - m_ReturnsStructByValue = value; - break; - - case InlineObservation::CALLEE_CLASS_VALUETYPE: - m_IsFromValueClass = value; - break; - - case InlineObservation::CALLSITE_NONGENERIC_CALLS_GENERIC: - m_NonGenericCallsGeneric = value; - break; - case InlineObservation::CALLEE_CLASS_PROMOTABLE: m_IsFromPromotableValueClass = value; break; - case InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS: - m_BinaryExprWithCns++; - break; - - case InlineObservation::CALLEE_ARG_STRUCT: - m_ArgIsStructByValue++; - break; - - case InlineObservation::CALLEE_ARG_STRUCT_FIELD_ACCESS: - m_FldAccessOverArgStruct++; - break; - - case InlineObservation::CALLEE_ARG_FEEDS_CAST: - m_ArgCasted++; - break; - - case InlineObservation::CALLEE_FOLDABLE_BOX: - m_FoldableBox++; - break; - - case InlineObservation::CALLEE_INTRINSIC: - m_Intrinsic++; - break; - - case InlineObservation::CALLEE_BACKWARD_JUMP: - m_BackwardJump++; - break; - - case InlineObservation::CALLEE_THROW_BLOCK: - m_ThrowBlock++; - break; - - case InlineObservation::CALLSITE_ARG_EXACT_CLS: - m_ArgIsExactCls++; - break; - - case InlineObservation::CALLSITE_ARG_BOXED: - m_ArgIsBoxedAtCallsite++; - break; - - case InlineObservation::CALLSITE_ARG_CONST: - m_ArgIsConst++; - break; - - case InlineObservation::CALLSITE_ARG_EXACT_CLS_SIG_IS_NOT: - m_ArgIsExactClsSigIsNot++; - break; - - case InlineObservation::CALLSITE_FOLDABLE_INTRINSIC: - m_FoldableIntrinsic++; - break; - - case InlineObservation::CALLSITE_FOLDABLE_EXPR: - m_FoldableExpr++; - break; - - case InlineObservation::CALLSITE_FOLDABLE_EXPR_UN: - m_FoldableExprUn++; - break; - - case InlineObservation::CALLSITE_FOLDABLE_BRANCH: - m_FoldableBranch++; - break; - - case InlineObservation::CALLSITE_DIV_BY_CNS: - m_DivByCns++; - break; - - case InlineObservation::CALLSITE_HAS_PROFILE: - m_HasProfile = value; - break; - case InlineObservation::CALLSITE_IN_TRY_REGION: m_CallsiteIsInTryRegion = value; break; @@ -477,10 +403,6 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value) break; } - case InlineObservation::CALLSITE_IN_NORETURN_REGION: - m_IsCallsiteInNoReturnRegion = value; - break; - case InlineObservation::CALLSITE_IN_LOOP: m_CallsiteIsInLoop = true; break; @@ -537,85 +459,6 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value) } } -#if defined(DEBUG) || defined(INLINE_DATA) -//------------------------------------------------------------------------ -// DumpXml: Dump DefaultPolicy data as XML -// -// Arguments: -// file - stream to output to -// indent - indent level - -void DefaultPolicy::DumpXml(FILE* file, unsigned indent) const -{ - fprintf(file, "%*s 0.01) \ - { \ - fprintf(file, " " #x "=\"%.2lf\"", x); \ - } -#define XATTR_B(x) \ - if (x) \ - { \ - fprintf(file, " " #x "=\"True\""); \ - } - - XATTR_R8(m_Multiplier); - XATTR_R8(m_ProfileFrequency); - XATTR_I4(m_CodeSize); - XATTR_I4(m_CallsiteFrequency); - XATTR_I4(m_CallsiteDepth); - XATTR_I4(m_InstructionCount); - XATTR_I4(m_LoadStoreCount); - XATTR_I4(m_ArgFeedsTest); - XATTR_I4(m_ArgFeedsConstantTest); - XATTR_I4(m_ArgFeedsRangeCheck); - XATTR_I4(m_ConstantArgFeedsConstantTest); - XATTR_I4(m_BinaryExprWithCns); - XATTR_I4(m_ArgCasted); - XATTR_I4(m_ArgIsStructByValue); - XATTR_I4(m_FldAccessOverArgStruct); - XATTR_I4(m_FoldableBox); - XATTR_I4(m_Intrinsic); - XATTR_I4(m_BackwardJump); - XATTR_I4(m_ThrowBlock); - XATTR_I4(m_ArgIsExactCls); - XATTR_I4(m_ArgIsExactClsSigIsNot); - XATTR_I4(m_ArgIsConst); - XATTR_I4(m_ArgIsBoxedAtCallsite); - XATTR_I4(m_FoldableIntrinsic); - XATTR_I4(m_FoldableExpr); - XATTR_I4(m_FoldableExprUn); - XATTR_I4(m_FoldableBranch); - XATTR_I4(m_DivByCns); - XATTR_I4(m_CalleeNativeSizeEstimate); - XATTR_I4(m_CallsiteNativeSizeEstimate); - XATTR_B(m_IsForceInline); - XATTR_B(m_IsForceInlineKnown); - XATTR_B(m_IsInstanceCtor); - XATTR_B(m_IsFromPromotableValueClass); - XATTR_B(m_HasSimd); - XATTR_B(m_LooksLikeWrapperMethod); - XATTR_B(m_MethodIsMostlyLoadStore); - XATTR_B(m_CallsiteIsInTryRegion); - XATTR_B(m_CallsiteIsInLoop); - XATTR_B(m_IsNoReturn); - XATTR_B(m_IsNoReturnKnown); - XATTR_B(m_ReturnsStructByValue); - XATTR_B(m_IsFromValueClass); - XATTR_B(m_NonGenericCallsGeneric); - XATTR_B(m_IsCallsiteInNoReturnRegion); - XATTR_B(m_HasProfile); - fprintf(file, " />\n"); -} -#endif - //------------------------------------------------------------------------ // BudgetCheck: see if this inline would exceed the current budget // @@ -822,7 +665,6 @@ void DefaultPolicy::NoteInt(InlineObservation obs, int value) void DefaultPolicy::NoteDouble(InlineObservation obs, double value) { assert(obs == InlineObservation::CALLSITE_PROFILE_FREQUENCY); - m_ProfileFrequency = value; } //------------------------------------------------------------------------ @@ -927,174 +769,6 @@ double DefaultPolicy::DetermineMultiplier() break; } - if (m_ReturnsStructByValue) - { - // For structs-passed-by-value we might avoid expensive copy operations if we inline - JITDUMP("\nInline candidate returns a struct by value."); - } - - if (m_ArgIsStructByValue > 0) - { - // Same here - JITDUMP("\n%d arguments are structs passed by value.", m_ArgIsStructByValue); - } - - if (m_NonGenericCallsGeneric) - { - // Especially, if such a callee has many foldable branches like 'typeof(T) == typeof(T2)' - JITDUMP("\nInline candidate is generic and caller is not."); - } - - if (m_IsCallsiteInNoReturnRegion) - { - // E.g. - // - // throw new ArgumentException(SR.GetMessage()); - // - // ^ Here we have two calls inside a BBJ_THROW block - // Unfortunately, we're not able to detect ThrowHelpers calls yet. - JITDUMP("\nCallsite is in a no-return region."); - } - - if (m_FoldableBranch > 0) - { - // Examples: - // - // if (typeof(T) == typeof(int)) { - // if (Avx2.IsSupported) { - // if (arg0 / 10 > 100) { // where arg0 is a constant at the callsite - // if (Math.Abs(arg0) > 10) { // same here - // etc. - // - JITDUMP("\nInline candidate has %d foldable branches.", m_FoldableBranch); - } - - if (m_ArgCasted > 0) - { - JITDUMP("\nArgument feeds ISINST/CASTCLASS %d times.", m_ArgCasted); - } - - if (m_FldAccessOverArgStruct > 0) - { - // Such ldfld/stfld are cheap for promotable structs - JITDUMP("\n%d ldfld or stfld over arguments which are structs", m_ArgIsStructByValue); - } - - if (m_FoldableBox > 0) - { - // We met some BOX+ISINST+BR or BOX+UNBOX patterns (see impBoxPatternMatch). - // Especially useful with m_IsGenericFromNonGeneric - JITDUMP("\nInline has %d foldable BOX ops.", m_FoldableBox); - } - - if (m_Intrinsic > 0) - { - // In most cases such intrinsics are lowered as single CPU instructions - JITDUMP("\nInline has %d intrinsics.", m_Intrinsic); - } - - if (m_BinaryExprWithCns > 0) - { - // In some cases we're not able to detect potentially foldable expressions, e.g.: - // - // ldc.i4.0 - // call int SomeFoldableNonIntrinsicCall - // ceq - // - // so at least we can note potential constant tests - JITDUMP("\nInline candidate has %d binary expressions with constants.", m_BinaryExprWithCns); - } - - if (m_ThrowBlock > 0) - { - // 'throw' opcode and its friends (Exception's ctor, its exception message, etc) significantly increase - // NativeSizeEstimate. However, such basic-blocks won't hurt us since they are always moved to - // the end of the functions and don't impact Register Allocations. - // NOTE: Unfortunately, we're not able to recognize ThrowHelper calls here yet. - JITDUMP("\nInline has %d throw blocks.", m_ThrowBlock); - } - - if (m_ArgIsBoxedAtCallsite > 0) - { - // Callsite is going to box n arguments. We might avoid boxing after inlining. - // Example: - // - // void DoNothing(object o) {} // o is unused, so the boxing is redundant - // - // void Caller() => DoNothing(42); // 42 is going to be boxed at the call site. - // - JITDUMP("\nCallsite is going to box %d arguments.", m_ArgIsBoxedAtCallsite); - } - - if (m_ArgIsExactClsSigIsNot > 0) - { - // If we inline such a callee - we'll be able to devirtualize all the calls for such arguments - // Example: - // - // int Callee(object o) => o.GetHashCode(); // virtual call - // - // int Caller(string s) => Callee(s); // String is 'exact' (sealed) - // - JITDUMP("\nCallsite passes %d arguments of exact classes while callee accepts non-exact ones.", - m_ArgIsExactClsSigIsNot); - } - - if (m_ArgIsExactCls > 0) - { - JITDUMP("\nCallsite passes %d arguments of exact classes.", m_ArgIsExactCls); - } - - if (m_ArgIsConst > 0) - { - // Normally, we try to note all the places where constant arguments lead to folding/feed tests - // but just in case: - JITDUMP("\n%d arguments are constants at the callsite.", m_ArgIsConst); - } - - if (m_FoldableIntrinsic > 0) - { - // Examples: - // - // typeof(T1) == typeof(T2) - // Math.Abs(constArg) - // BitOperation.PopCount(10) - JITDUMP("\nInline has %d foldable intrinsics.", m_FoldableIntrinsic); - } - - if (m_FoldableExpr > 0) - { - // E.g. add/mul/ceq, etc. over constant/constant arguments - JITDUMP("\nInline has %d foldable binary expressions.", m_FoldableExpr); - } - - if (m_FoldableExprUn > 0) - { - // E.g. casts, negations, etc. over constants/constant arguments - JITDUMP("\nInline has %d foldable unary expressions.", m_FoldableExprUn); - } - - if (m_DivByCns > 0) - { - // E.g. callee has "x / arg0" where arg0 is a const at the call site - - // we'll avoid a very expensive DIV instruction after inlining. - JITDUMP("\nInline has %d Div-by-constArg expressions.", m_DivByCns); - } - - if (m_BackwardJump) - { - const bool callSiteIsInLoop = m_CallsiteFrequency == InlineCallsiteFrequency::LOOP; - JITDUMP("\nInline has %d backward jumps (loops?).", m_BackwardJump); - if (callSiteIsInLoop) - { - JITDUMP(" And is inlined into a loop.") - } - } - - if (m_HasProfile) - { - JITDUMP("\nCallsite has profile data: %g.", m_ProfileFrequency); - } - #ifdef DEBUG int additionalMultiplier = JitConfig.JitInlineAdditionalMultiplier(); @@ -1306,6 +980,42 @@ int DefaultPolicy::CodeSizeEstimate() } } +#if defined(DEBUG) || defined(INLINE_DATA) +//------------------------------------------------------------------------ +// OnDumpXml: Dump DefaultPolicy data as XML +// +// Arguments: +// file - stream to output to +// indent - indent level + +void DefaultPolicy::OnDumpXml(FILE* file, unsigned indent) const +{ + XATTR_R8(m_Multiplier); + XATTR_I4(m_CodeSize); + XATTR_I4(m_CallsiteFrequency); + XATTR_I4(m_CallsiteDepth); + XATTR_I4(m_InstructionCount); + XATTR_I4(m_LoadStoreCount); + XATTR_I4(m_ArgFeedsTest); + XATTR_I4(m_ArgFeedsConstantTest); + XATTR_I4(m_ArgFeedsRangeCheck); + XATTR_I4(m_ConstantArgFeedsConstantTest); + XATTR_I4(m_CalleeNativeSizeEstimate); + XATTR_I4(m_CallsiteNativeSizeEstimate); + XATTR_B(m_IsForceInline); + XATTR_B(m_IsForceInlineKnown); + XATTR_B(m_IsInstanceCtor); + XATTR_B(m_IsFromPromotableValueClass); + XATTR_B(m_HasSimd); + XATTR_B(m_LooksLikeWrapperMethod); + XATTR_B(m_MethodIsMostlyLoadStore); + XATTR_B(m_CallsiteIsInTryRegion); + XATTR_B(m_CallsiteIsInLoop); + XATTR_B(m_IsNoReturn); + XATTR_B(m_IsNoReturnKnown); +} +#endif + //------------------------------------------------------------------------ // PropagateNeverToRuntime: determine if a never result should cause the // method to be marked as un-inlinable. @@ -1497,6 +1207,538 @@ void RandomPolicy::DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) #pragma warning(disable : 4351) #endif +//------------------------------------------------------------------------ +// NoteInt: handle an observed boolean value +// +// Arguments: +// obs - the current obsevation +// value - the value being observed + +void ExtendedDefaultPolicy::NoteBool(InlineObservation obs, bool value) +{ + switch (obs) + { + case InlineObservation::CALLEE_RETURNS_STRUCT: + m_ReturnsStructByValue = value; + break; + + case InlineObservation::CALLEE_CLASS_VALUETYPE: + m_IsFromValueClass = value; + break; + + case InlineObservation::CALLSITE_NONGENERIC_CALLS_GENERIC: + m_NonGenericCallsGeneric = value; + break; + + case InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS: + m_BinaryExprWithCns++; + break; + + case InlineObservation::CALLEE_ARG_STRUCT: + m_ArgIsStructByValue++; + break; + + case InlineObservation::CALLEE_ARG_STRUCT_FIELD_ACCESS: + m_FldAccessOverArgStruct++; + break; + + case InlineObservation::CALLEE_ARG_FEEDS_CAST: + m_ArgCasted++; + break; + + case InlineObservation::CALLEE_FOLDABLE_BOX: + m_FoldableBox++; + break; + + case InlineObservation::CALLEE_INTRINSIC: + m_Intrinsic++; + break; + + case InlineObservation::CALLEE_BACKWARD_JUMP: + m_BackwardJump++; + break; + + case InlineObservation::CALLEE_THROW_BLOCK: + m_ThrowBlock++; + break; + + case InlineObservation::CALLSITE_ARG_EXACT_CLS: + m_ArgIsExactCls++; + break; + + case InlineObservation::CALLSITE_ARG_BOXED: + m_ArgIsBoxedAtCallsite++; + break; + + case InlineObservation::CALLSITE_ARG_CONST: + m_ArgIsConst++; + break; + + case InlineObservation::CALLSITE_ARG_EXACT_CLS_SIG_IS_NOT: + m_ArgIsExactClsSigIsNot++; + break; + + case InlineObservation::CALLSITE_FOLDABLE_INTRINSIC: + m_FoldableIntrinsic++; + break; + + case InlineObservation::CALLSITE_FOLDABLE_EXPR: + m_FoldableExpr++; + break; + + case InlineObservation::CALLSITE_FOLDABLE_EXPR_UN: + m_FoldableExprUn++; + break; + + case InlineObservation::CALLSITE_FOLDABLE_BRANCH: + m_FoldableBranch++; + break; + + case InlineObservation::CALLSITE_DIV_BY_CNS: + m_DivByCns++; + break; + + case InlineObservation::CALLSITE_HAS_PROFILE: + m_HasProfile = value; + break; + + case InlineObservation::CALLSITE_IN_NORETURN_REGION: + m_IsCallsiteInNoReturnRegion = value; + break; + + default: + DefaultPolicy::NoteBool(obs, value); + break; + } +} + +//------------------------------------------------------------------------ +// NoteInt: handle an observed integer value +// +// Arguments: +// obs - the current obsevation +// value - the value being observed + +void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value) +{ + switch (obs) + { + case InlineObservation::CALLEE_IL_CODE_SIZE: + { + assert(m_IsForceInlineKnown); + assert(value != 0); + m_CodeSize = static_cast(value); + + if (m_IsForceInline) + { + // Candidate based on force inline + SetCandidate(InlineObservation::CALLEE_IS_FORCE_INLINE); + } + else if (m_CodeSize <= InlineStrategy::ALWAYS_INLINE_SIZE) + { + // Candidate based on small size + SetCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE); + } + else if (m_CodeSize <= (unsigned)JitConfig.JitExtDefaultPolicyMaxIL()) + { + // Candidate, pending profitability evaluation + SetCandidate(InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE); + } + else + { + // Callee too big, not a candidate + SetNever(InlineObservation::CALLEE_TOO_MUCH_IL); + } + break; + } + case InlineObservation::CALLEE_NUMBER_OF_BASIC_BLOCKS: + { + if (!m_IsForceInline && m_IsNoReturn && (value == 1)) + { + SetNever(InlineObservation::CALLEE_DOES_NOT_RETURN); + } + else if (!m_IsForceInline) + { + unsigned bbLimit = (unsigned)JitConfig.JitExtDefaultPolicyMaxBB(); + if (m_IsPrejitRoot) + { + // We're not able to recognize arg-specific foldable branches + // in prejit-root mode. + bbLimit += 3; + } + bbLimit += m_FoldableBranch; + if ((unsigned)value > bbLimit) + { + SetNever(InlineObservation::CALLEE_TOO_MANY_BASIC_BLOCKS); + } + } + break; + } + default: + DefaultPolicy::NoteInt(obs, value); + break; + } +} + +//------------------------------------------------------------------------ +// NoteInt: handle an observed double value +// +// Arguments: +// obs - the current obsevation +// value - the value being observed + +void ExtendedDefaultPolicy::NoteDouble(InlineObservation obs, double value) +{ + // So far, CALLSITE_PROFILE_FREQUENCY is the only "double" property. + assert(obs == InlineObservation::CALLSITE_PROFILE_FREQUENCY); + m_ProfileFrequency = value; +} + +//------------------------------------------------------------------------ +// DetermineMultiplier: determine benefit multiplier for this inline +// +// Notes: uses the accumulated set of observations to compute a +// profitability boost for the inline candidate. + +double ExtendedDefaultPolicy::DetermineMultiplier() +{ + double multiplier = 0.0; + + if (m_IsInstanceCtor) + { + multiplier += 1.5; + JITDUMP("\nmultiplier in instance constructors increased to %g.", multiplier); + } + + if (m_IsFromValueClass) + { + multiplier += 3.0; + JITDUMP("\nmultiplier in methods of struct increased to %g.", multiplier); + } + + if (m_ReturnsStructByValue) + { + // For structs-passed-by-value we might avoid expensive copy operations if we inline. + multiplier += 1.5; + JITDUMP("\nInline candidate returns a struct by value. Multiplier increased to %g.", multiplier); + } + else if (m_ArgIsStructByValue > 0) + { + // Same here + multiplier += 1.5; + JITDUMP("\n%d arguments are structs passed by value. Multiplier increased to %g.", m_ArgIsStructByValue, + multiplier); + } + else if (m_FldAccessOverArgStruct > 0) + { + multiplier += 1.0; + // Such ldfld/stfld are cheap for promotable structs + JITDUMP("\n%d ldfld or stfld over arguments which are structs. Multiplier increased to %g.", + m_FldAccessOverArgStruct, multiplier); + } + + if (m_LooksLikeWrapperMethod) + { + multiplier += 1.0; + JITDUMP("\nInline candidate looks like a wrapper method. Multiplier increased to %g.", multiplier); + } + + if (m_MethodIsMostlyLoadStore) + { + multiplier += 3.0; + JITDUMP("\nInline candidate is mostly loads and stores. Multiplier increased to %g.", multiplier); + } + + if (m_ArgFeedsRangeCheck > 0) + { + multiplier += 0.5; + JITDUMP("\nInline candidate has arg that feeds range check. Multiplier increased to %g.", multiplier); + } + + if (m_NonGenericCallsGeneric) + { + multiplier += 1.5; + JITDUMP("\nInline candidate is generic and caller is not. Multiplier increased to %g.", multiplier); + } + + if (m_FoldableBranch > 0) + { + // Examples: + // + // if (typeof(T) == typeof(int)) { + // if (Avx2.IsSupported) { + // if (arg0 / 10 > 100) { // where arg0 is a constant at the callsite + // if (Math.Abs(arg0) > 10) { // same here + // etc. + // + multiplier += 3.0 + m_FoldableBranch; + JITDUMP("\nInline candidate has %d foldable branches. Multiplier increased to %g.", m_FoldableBranch, + multiplier); + } + else if (m_ConstantArgFeedsConstantTest > 0) + { + multiplier += 3.0; + JITDUMP("\nInline candidate has const arg that feeds a conditional. Multiplier increased to %g.", multiplier); + } + else if ((m_ArgIsConst > 0) && (m_FoldableExpr < 1)) + { + // TODO: handle 'if (SomeMethod(constArg))' patterns in fgFindJumpTargets + // The previous version of inliner optimistically assumed this is "has const arg that feeds a conditional" + multiplier += 3.0; + JITDUMP("\nCallsite passes a consant. Multiplier increased to %g.", multiplier); + } + + if ((m_FoldableBox > 0) && m_NonGenericCallsGeneric) + { + // We met some BOX+ISINST+BR or BOX+UNBOX patterns (see impBoxPatternMatch). + multiplier += 3.0; + JITDUMP("\nInline has %d foldable BOX ops. Multiplier increased to %g.", m_FoldableBox, multiplier); + } + +#ifdef FEATURE_SIMD + if (m_HasSimd) + { + multiplier += JitConfig.JitInlineSIMDMultiplier(); + JITDUMP("\nInline candidate has SIMD type args, locals or return value. Multiplier increased to %g.", + multiplier); + } +#endif + + if (m_Intrinsic > 0) + { + // In most cases such intrinsics are lowered as single CPU instructions + multiplier += 1.5; + JITDUMP("\nInline has %d intrinsics. Multiplier increased to %g.", m_Intrinsic, multiplier); + } + + if (m_ArgIsBoxedAtCallsite > 0) + { + // Callsite is going to box n arguments. We might avoid boxing after inlining. + // Example: + // + // void DoNothing(object o) {} // o is unused, so the boxing is redundant + // + // void Caller() => DoNothing(42); // 42 is going to be boxed at the call site. + // + multiplier += 0.5; + JITDUMP("\nCallsite is going to box %d arguments. Multiplier increased to %g.", m_ArgIsBoxedAtCallsite, + multiplier); + } + + if (m_ArgIsExactClsSigIsNot > 0) + { + // If we inline such a callee - we'll be able to devirtualize all the calls for such arguments + // Example: + // + // int Callee(object o) => o.GetHashCode(); // virtual call + // + // int Caller(string s) => Callee(s); // String is 'exact' (sealed) + // + multiplier += 2.5; + JITDUMP("\nCallsite passes %d arguments of exact classes while callee accepts non-exact ones. Multiplier " + "increased to %g.", + m_ArgIsExactClsSigIsNot, multiplier); + } + + if (m_FoldableIntrinsic > 0) + { + // Examples: + // + // typeof(T1) == typeof(T2) + // Math.Abs(constArg) + // BitOperation.PopCount(10) + multiplier += 1.0 + m_FoldableIntrinsic; + JITDUMP("\nInline has %d foldable intrinsics. Multiplier increased to %g.", m_FoldableIntrinsic, multiplier); + } + + if (m_FoldableExpr > 0) + { + // E.g. add/mul/ceq, etc. over constant/constant arguments + multiplier += 1.0 + m_FoldableExpr; + JITDUMP("\nInline has %d foldable binary expressions. Multiplier increased to %g.", m_FoldableExpr, + multiplier); + } + + if (m_FoldableExprUn > 0) + { + // E.g. casts, negations, etc. over constants/constant arguments + multiplier += m_FoldableExprUn; + JITDUMP("\nInline has %d foldable unary expressions. Multiplier increased to %g.", m_FoldableExprUn, + multiplier); + } + + if (m_DivByCns > 0) + { + // E.g. callee has "x / arg0" where arg0 is a const at the call site - + // we'll avoid a very expensive DIV instruction after inlining. + multiplier += 3.0; + JITDUMP("\nInline has %d Div-by-constArg expressions. Multiplier increased to %g.", m_DivByCns, multiplier); + } + + if (m_BinaryExprWithCns > 0) + { + // In some cases we're not able to detect potentially foldable expressions, e.g.: + // + // ldc.i4.0 + // call int SomeFoldableNonIntrinsicCall + // ceq + // + // so at least we can note potential constant tests + multiplier += m_BinaryExprWithCns * 0.5; + JITDUMP("\nInline candidate has %d binary expressions with constants. Multiplier increased to %g.", + m_BinaryExprWithCns, multiplier); + + // For prejit roots we do not see the call sites. To be suitably optimistic + // assume that call sites may pass constants and make these m_BinaryExprWithCns + // foldable. + if (m_IsPrejitRoot) + { + multiplier += m_BinaryExprWithCns; + } + } + + if (m_ArgFeedsConstantTest > 0) + { + multiplier += m_IsPrejitRoot ? 3.0 : 1.0; + JITDUMP("\nInline candidate has an arg that feeds a constant test. Multiplier increased to %g.", multiplier); + } + else if (m_IsPrejitRoot && (m_ArgFeedsTest > 0)) + { + multiplier += 3.0; + JITDUMP("\nPrejit root candidate has arg that feeds a conditional. Multiplier increased to %g.", multiplier); + } + + switch (m_CallsiteFrequency) + { + case InlineCallsiteFrequency::RARE: + // Note this one is not additive, it uses '=' instead of '+=' + multiplier = 1.3; + JITDUMP("\nInline candidate callsite is rare. Multiplier limited to %g.", multiplier); + break; + case InlineCallsiteFrequency::BORING: + multiplier += 1.3; + JITDUMP("\nInline candidate callsite is boring. Multiplier increased to %g.", multiplier); + break; + case InlineCallsiteFrequency::WARM: + multiplier += 2.0; + JITDUMP("\nInline candidate callsite is warm. Multiplier increased to %g.", multiplier); + break; + case InlineCallsiteFrequency::LOOP: + multiplier += 3.0; + JITDUMP("\nInline candidate callsite is in a loop. Multiplier increased to %g.", multiplier); + break; + case InlineCallsiteFrequency::HOT: + multiplier += 3.0; + JITDUMP("\nInline candidate callsite is hot. Multiplier increased to %g.", multiplier); + break; + default: + assert(!"Unexpected callsite frequency"); + break; + } + + if (m_HasProfile) + { + // There are cases when Profile Data can be misleading or polluted: + // 1) We don't support context-sensitive instrumentation + // 2) The static profile that we ship can be slightly irrelevant for the current app + // 3) We don't support deoptimizations so we can't re-collect profiles if something changes + // 4) Sometimes, it still makes sense to inline methods in cold blocks to improve type/esacape analysis + // for the whole caller. + // + const double profileTrustCoef = (double)JitConfig.JitExtDefaultPolicyProfTrust() / 10.0; + const double profileScale = (double)JitConfig.JitExtDefaultPolicyProfScale() / 10.0; + + multiplier *= (1.0 - profileTrustCoef) + min(m_ProfileFrequency, 1.0) * profileScale; + JITDUMP("\nCallsite has profile data: %g.", m_ProfileFrequency); + } + + if (m_RootCompiler->lvaTableCnt > ((unsigned)(JitConfig.JitMaxLocalsToTrack() / 4))) + { + // Slow down inlining if we already have to many locals in the rootCompiler. + multiplier /= ((double)m_RootCompiler->lvaTableCnt / ((double)JitConfig.JitMaxLocalsToTrack() / 4.0)); + JITDUMP("\nCaller %d locals. Multiplier decreased to %g.", m_RootCompiler->lvaTableCnt, multiplier); + } + + if (m_BackwardJump) + { + // TODO: investigate in which cases we should [never] inline callees with loops. + // For now let's add some friction. + multiplier *= 0.7; + JITDUMP("\nInline has %d backward jumps (loops?). Multiplier decreased to %g.", m_BackwardJump, multiplier); + } + + if (m_IsCallsiteInNoReturnRegion) + { + // E.g. + // + // throw new ArgumentException(SR.GetMessage()); + // + // ^ Here we have two calls inside a BBJ_THROW block + // Unfortunately, we're not able to detect ThrowHelpers calls yet. + + // Try to avoid inlining such methods for now. + multiplier = 1.0; + JITDUMP("\nCallsite is in a no-return region. Multiplier limited to %g.", multiplier); + } + +#ifdef DEBUG + + int additionalMultiplier = JitConfig.JitInlineAdditionalMultiplier(); + + if (additionalMultiplier != 0) + { + multiplier += additionalMultiplier; + JITDUMP("\nmultiplier increased via JitInlineAdditionalMultiplier=%d to %g.", additionalMultiplier, multiplier); + } + + if (m_RootCompiler->compInlineStress()) + { + multiplier += 10; + JITDUMP("\nmultiplier increased via inline stress to %g.", multiplier); + } + +#endif // DEBUG + + return multiplier; +} + +#if defined(DEBUG) || defined(INLINE_DATA) +//------------------------------------------------------------------------ +// DumpXml: Dump ExtendedDefaultPolicy data as XML +// +// Arguments: +// file - stream to output to +// indent - indent level + +void ExtendedDefaultPolicy::OnDumpXml(FILE* file, unsigned indent) const +{ + DefaultPolicy::OnDumpXml(file, indent); + XATTR_R8(m_ProfileFrequency) + XATTR_I4(m_BinaryExprWithCns) + XATTR_I4(m_ArgCasted) + XATTR_I4(m_ArgIsStructByValue) + XATTR_I4(m_FldAccessOverArgStruct) + XATTR_I4(m_FoldableBox) + XATTR_I4(m_Intrinsic) + XATTR_I4(m_BackwardJump) + XATTR_I4(m_ThrowBlock) + XATTR_I4(m_ArgIsExactCls) + XATTR_I4(m_ArgIsExactClsSigIsNot) + XATTR_I4(m_ArgIsConst) + XATTR_I4(m_ArgIsBoxedAtCallsite) + XATTR_I4(m_FoldableIntrinsic) + XATTR_I4(m_FoldableExpr) + XATTR_I4(m_FoldableExprUn) + XATTR_I4(m_FoldableBranch) + XATTR_I4(m_DivByCns) + XATTR_B(m_ReturnsStructByValue) + XATTR_B(m_IsFromValueClass) + XATTR_B(m_NonGenericCallsGeneric) + XATTR_B(m_IsCallsiteInNoReturnRegion) + XATTR_B(m_HasProfile) +} +#endif + //------------------------------------------------------------------------ // DiscretionaryPolicy: construct a new DiscretionaryPolicy // @@ -2362,28 +2604,6 @@ void DiscretionaryPolicy::DumpData(FILE* file) const fprintf(file, ",%u", m_IsNoReturn ? 1 : 0); fprintf(file, ",%u", m_CalleeHasGCStruct ? 1 : 0); fprintf(file, ",%u", m_CallsiteDepth); - fprintf(file, ",%u", m_BinaryExprWithCns); - fprintf(file, ",%u", m_ArgCasted); - fprintf(file, ",%u", m_ArgIsStructByValue); - fprintf(file, ",%u", m_FldAccessOverArgStruct); - fprintf(file, ",%u", m_FoldableBox); - fprintf(file, ",%u", m_Intrinsic); - fprintf(file, ",%u", m_BackwardJump); - fprintf(file, ",%u", m_ThrowBlock); - fprintf(file, ",%u", m_ArgIsExactCls); - fprintf(file, ",%u", m_ArgIsExactClsSigIsNot); - fprintf(file, ",%u", m_ArgIsConst); - fprintf(file, ",%u", m_ArgIsBoxedAtCallsite); - fprintf(file, ",%u", m_FoldableIntrinsic); - fprintf(file, ",%u", m_FoldableExpr); - fprintf(file, ",%u", m_FoldableExprUn); - fprintf(file, ",%u", m_FoldableBranch); - fprintf(file, ",%u", m_DivByCns); - fprintf(file, ",%u", m_ReturnsStructByValue ? 1 : 0); - fprintf(file, ",%u", m_IsFromValueClass ? 1 : 0); - fprintf(file, ",%u", m_NonGenericCallsGeneric ? 1 : 0); - fprintf(file, ",%u", m_IsCallsiteInNoReturnRegion ? 1 : 0); - fprintf(file, ",%u", m_HasProfile ? 1 : 0); } #endif // defined(DEBUG) || defined(INLINE_DATA) diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h index 9c107f480c2..7d0d83bd736 100644 --- a/src/coreclr/jit/inlinepolicy.h +++ b/src/coreclr/jit/inlinepolicy.h @@ -10,6 +10,7 @@ // // LegalPolicy - partial class providing common legality checks // DefaultPolicy - default inliner policy +// ExtendedDefaltPolicy - a more aggressive and profile-driven variation of DefaultPolicy // DiscretionaryPolicy - default variant with uniform size policy // ModelPolicy - policy based on statistical modelling // ProfilePolicy - policy based on statistical modelling and profile feedback @@ -87,7 +88,6 @@ public: , m_RootCompiler(compiler) , m_StateMachine(nullptr) , m_Multiplier(0.0) - , m_ProfileFrequency(0.0) , m_CodeSize(0) , m_CallsiteFrequency(InlineCallsiteFrequency::UNUSED) , m_CallsiteDepth(0) @@ -97,23 +97,6 @@ public: , m_ArgFeedsConstantTest(0) , m_ArgFeedsRangeCheck(0) , m_ConstantArgFeedsConstantTest(0) - , m_BinaryExprWithCns(0) - , m_ArgCasted(0) - , m_ArgIsStructByValue(0) - , m_FldAccessOverArgStruct(0) - , m_FoldableBox(0) - , m_Intrinsic(0) - , m_BackwardJump(0) - , m_ThrowBlock(0) - , m_ArgIsExactCls(0) - , m_ArgIsExactClsSigIsNot(0) - , m_ArgIsConst(0) - , m_ArgIsBoxedAtCallsite(0) - , m_FoldableIntrinsic(0) - , m_FoldableExpr(0) - , m_FoldableExprUn(0) - , m_FoldableBranch(0) - , m_DivByCns(0) , m_CalleeNativeSizeEstimate(0) , m_CallsiteNativeSizeEstimate(0) , m_IsForceInline(false) @@ -127,11 +110,6 @@ public: , m_CallsiteIsInLoop(false) , m_IsNoReturn(false) , m_IsNoReturnKnown(false) - , m_ReturnsStructByValue(false) - , m_IsFromValueClass(false) - , m_NonGenericCallsGeneric(false) - , m_IsCallsiteInNoReturnRegion(false) - , m_HasProfile(false) { // empty } @@ -153,14 +131,12 @@ public: int CodeSizeEstimate() override; #if defined(DEBUG) || defined(INLINE_DATA) + void OnDumpXml(FILE* file, unsigned indent = 0) const override; const char* GetName() const override { return "DefaultPolicy"; } - - void DumpXml(FILE* file, unsigned indent = 0) const override; - #endif // (DEBUG) || defined(INLINE_DATA) protected: @@ -172,15 +148,14 @@ protected: }; // Helper methods - double DetermineMultiplier(); - int DetermineNativeSizeEstimate(); + virtual double DetermineMultiplier(); + int DetermineNativeSizeEstimate(); int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo); // Data members Compiler* m_RootCompiler; // root compiler instance CodeSeqSM* m_StateMachine; double m_Multiplier; - double m_ProfileFrequency; unsigned m_CodeSize; InlineCallsiteFrequency m_CallsiteFrequency; unsigned m_CallsiteDepth; @@ -190,23 +165,6 @@ protected: unsigned m_ArgFeedsConstantTest; unsigned m_ArgFeedsRangeCheck; unsigned m_ConstantArgFeedsConstantTest; - unsigned m_BinaryExprWithCns; - unsigned m_ArgCasted; - unsigned m_ArgIsStructByValue; - unsigned m_FldAccessOverArgStruct; - unsigned m_FoldableBox; - unsigned m_Intrinsic; - unsigned m_BackwardJump; - unsigned m_ThrowBlock; - unsigned m_ArgIsExactCls; - unsigned m_ArgIsExactClsSigIsNot; - unsigned m_ArgIsConst; - unsigned m_ArgIsBoxedAtCallsite; - unsigned m_FoldableIntrinsic; - unsigned m_FoldableExpr; - unsigned m_FoldableExprUn; - unsigned m_FoldableBranch; - unsigned m_DivByCns; int m_CalleeNativeSizeEstimate; int m_CallsiteNativeSizeEstimate; bool m_IsForceInline : 1; @@ -220,11 +178,86 @@ protected: bool m_CallsiteIsInLoop : 1; bool m_IsNoReturn : 1; bool m_IsNoReturnKnown : 1; - bool m_ReturnsStructByValue : 1; - bool m_IsFromValueClass : 1; - bool m_NonGenericCallsGeneric : 1; - bool m_IsCallsiteInNoReturnRegion : 1; - bool m_HasProfile : 1; +}; + +// ExtendedDefaultPolicy is a slightly more aggressive variant of +// DefaultPolicy with an extended list of observations including profile data. +class ExtendedDefaultPolicy : public DefaultPolicy +{ +public: + ExtendedDefaultPolicy::ExtendedDefaultPolicy(Compiler* compiler, bool isPrejitRoot) + : DefaultPolicy(compiler, isPrejitRoot) + , m_ProfileFrequency(0.0) + , m_BinaryExprWithCns(0) + , m_ArgCasted(0) + , m_ArgIsStructByValue(0) + , m_FldAccessOverArgStruct(0) + , m_FoldableBox(0) + , m_Intrinsic(0) + , m_BackwardJump(0) + , m_ThrowBlock(0) + , m_ArgIsExactCls(0) + , m_ArgIsExactClsSigIsNot(0) + , m_ArgIsConst(0) + , m_ArgIsBoxedAtCallsite(0) + , m_FoldableIntrinsic(0) + , m_FoldableExpr(0) + , m_FoldableExprUn(0) + , m_FoldableBranch(0) + , m_DivByCns(0) + , m_ReturnsStructByValue(false) + , m_IsFromValueClass(false) + , m_NonGenericCallsGeneric(false) + , m_IsCallsiteInNoReturnRegion(false) + , m_HasProfile(false) + { + // Empty + } + + void NoteBool(InlineObservation obs, bool value) override; + void NoteInt(InlineObservation obs, int value) override; + void NoteDouble(InlineObservation obs, double value) override; + + double DetermineMultiplier() override; + + bool RequiresPreciseScan() override + { + return true; + } + +#if defined(DEBUG) || defined(INLINE_DATA) + void OnDumpXml(FILE* file, unsigned indent = 0) const override; + + const char* GetName() const override + { + return "ExtendedDefaultPolicy"; + } +#endif // defined(DEBUG) || defined(INLINE_DATA) + +protected: + double m_ProfileFrequency; + unsigned m_BinaryExprWithCns; + unsigned m_ArgCasted; + unsigned m_ArgIsStructByValue; + unsigned m_FldAccessOverArgStruct; + unsigned m_FoldableBox; + unsigned m_Intrinsic; + unsigned m_BackwardJump; + unsigned m_ThrowBlock; + unsigned m_ArgIsExactCls; + unsigned m_ArgIsExactClsSigIsNot; + unsigned m_ArgIsConst; + unsigned m_ArgIsBoxedAtCallsite; + unsigned m_FoldableIntrinsic; + unsigned m_FoldableExpr; + unsigned m_FoldableExprUn; + unsigned m_FoldableBranch; + unsigned m_DivByCns; + bool m_ReturnsStructByValue : 1; + bool m_IsFromValueClass : 1; + bool m_NonGenericCallsGeneric : 1; + bool m_IsCallsiteInNoReturnRegion : 1; + bool m_HasProfile : 1; }; // DiscretionaryPolicy is a variant of the default policy. It diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index b1c9e522883..d55fe9be97f 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -383,6 +383,9 @@ CONFIG_INTEGER(JitAggressiveInlining, W("JitAggressiveInlining"), 0) // Aggressi CONFIG_INTEGER(JitELTHookEnabled, W("JitELTHookEnabled"), 0) // If 1, emit Enter/Leave/TailCall callbacks CONFIG_INTEGER(JitInlineSIMDMultiplier, W("JitInlineSIMDMultiplier"), 3) +// Ex lclMAX_TRACKED constant. +CONFIG_INTEGER(JitMaxLocalsToTrack, W("JitMaxLocalsToTrack"), 0x400) + #if defined(FEATURE_ENABLE_NO_RANGE_CHECKS) CONFIG_INTEGER(JitNoRngChks, W("JitNoRngChks"), 0) // If 1, don't generate range checks #endif // defined(FEATURE_ENABLE_NO_RANGE_CHECKS) @@ -454,6 +457,14 @@ CONFIG_STRING(JitNoInlineRange, W("JitNoInlineRange")) CONFIG_STRING(JitInlineReplayFile, W("JitInlineReplayFile")) #endif // defined(DEBUG) || defined(INLINE_DATA) +// Extended version of DefaultPolicy that includes a more precise IL scan, +// relies on PGO if it exists and generally is more aggressive. +CONFIG_INTEGER(JitExtDefaultPolicy, W("JitExtDefaultPolicy"), 1) +CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x64) +CONFIG_INTEGER(JitExtDefaultPolicyMaxBB, W("JitExtDefaultPolicyMaxBB"), 7) +CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfScale"), 0x22) +CONFIG_INTEGER(JitExtDefaultPolicyProfTrust, W("JitExtDefaultPolicyProfTrust"), 0x7) + CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index f7c67808bdd..eec7450e656 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3566,7 +3566,7 @@ void Compiler::lvaSortByRefCount() jitstd::sort(tracked, tracked + trackedCount, LclVarDsc_BlendedCode_Less(lvaTable DEBUGARG(lvaCount))); } - lvaTrackedCount = min(lclMAX_TRACKED, trackedCount); + lvaTrackedCount = min((unsigned)JitConfig.JitMaxLocalsToTrack(), trackedCount); JITDUMP("Tracked variable (%u out of %u) table:\n", lvaTrackedCount, lvaCount); diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 0a202303efa..b4137ec52eb 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -2575,7 +2575,7 @@ public: } // If we have maxed out lvaTrackedCount then this CSE may end up as an untracked variable - if (m_pCompiler->lvaTrackedCount == lclMAX_TRACKED) + if (m_pCompiler->lvaTrackedCount == (unsigned)JitConfig.JitMaxLocalsToTrack()) { cse_def_cost += 1; cse_use_cost += 1; diff --git a/src/coreclr/jit/varset.h b/src/coreclr/jit/varset.h index c1940616671..88f86394376 100644 --- a/src/coreclr/jit/varset.h +++ b/src/coreclr/jit/varset.h @@ -68,12 +68,6 @@ typedef BitSetOps VarSetOps; #else -typedef VarSetOpsRaw VarSetOps; +typedef VarSetOpsRaw VarSetOps; #endif #define ALLVARSET_REP BSUInt64 From 784ddffced6984719f7b76e3f2750990b1087eaa Mon Sep 17 00:00:00 2001 From: iinuwa Date: Thu, 1 Jul 2021 04:59:59 -0500 Subject: [PATCH 214/926] Enable more LDAP TLS tests on linux (#54377) * Remove SupportedOsPlatform attribute for LDAP TLS * Throw ObjectDisposedException when setting LDAP TLS settings on Linux. --- .../Protocols/ldap/LdapSessionOptions.Linux.cs | 16 +++++++++++++++- .../Protocols/ldap/LdapSessionOptions.Windows.cs | 1 - .../tests/LdapSessionOptionsTests.cs | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs index b7bcca29c0a..dbedfdbc78c 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Linux.cs @@ -9,7 +9,21 @@ namespace System.DirectoryServices.Protocols { private static void PALCertFreeCRLContext(IntPtr certPtr) { /* No op */ } - public bool SecureSocketLayer { get; set; } + private bool _secureSocketLayer; + + public bool SecureSocketLayer + { + get + { + if (_connection._disposed) throw new ObjectDisposedException(GetType().Name); + return _secureSocketLayer; + } + set + { + if (_connection._disposed) throw new ObjectDisposedException(GetType().Name); + _secureSocketLayer = value; + } + } public int ProtocolVersion { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs index c587f4218a4..14b8878a1c9 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.Windows.cs @@ -9,7 +9,6 @@ namespace System.DirectoryServices.Protocols { private static void PALCertFreeCRLContext(IntPtr certPtr) => Interop.Ldap.CertFreeCRLContext(certPtr); - [SupportedOSPlatform("windows")] public bool SecureSocketLayer { get diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs index ef1436b7502..77cdf83c496 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/LdapSessionOptionsTests.cs @@ -64,7 +64,6 @@ namespace System.DirectoryServices.Protocols.Tests } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] public void SecureSocketLayer_GetSetWhenDisposed_ThrowsObjectDisposedException() { var connection = new LdapConnection("server"); From 43ff743cddff4b4cf9dfa546d72564b68634186f Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 1 Jul 2021 13:22:31 +0300 Subject: [PATCH 215/926] Re-enable tests on mono interpreter (#54811) --- .../tests/DI.External.Tests/AssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/AssemblyInfo.cs index 35ac0338117..2696156cfbb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/AssemblyInfo.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/48929", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/43411", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))] From 6dd08fb8121d6db1ecab0b4df96783bbf64fc129 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 1 Jul 2021 06:40:51 -0400 Subject: [PATCH 216/926] Avoid stackalloc[1] in a few places (#54746) --- .../IO/Strategies/WindowsFileStreamStrategy.cs | 18 +++++------------- .../src/System/Text/TranscodingStream.cs | 11 +++++------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs index dee1c5a9548..259b438883a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs @@ -140,22 +140,14 @@ namespace System.IO.Strategies } } - // ReadByte and WriteByte methods are used only when the user has disabled buffering on purpose - // their performance is going to be horrible - // TODO: should we consider adding a new event provider and log an event so it can be detected? - public override int ReadByte() + public override unsafe int ReadByte() { - Span buffer = stackalloc byte[1]; - int bytesRead = Read(buffer); - return bytesRead == 1 ? buffer[0] : -1; + byte b; + return Read(new Span(&b, 1)) != 0 ? b : -1; } - public override void WriteByte(byte value) - { - Span buffer = stackalloc byte[1]; - buffer[0] = value; - Write(buffer); - } + public override unsafe void WriteByte(byte value) => + Write(new ReadOnlySpan(&value, 1)); // this method just disposes everything (no buffer, no need to flush) public override ValueTask DisposeAsync() diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs index 6a459c5f774..4dbc443535b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs @@ -437,11 +437,10 @@ namespace System.Text } } - public override int ReadByte() + public override unsafe int ReadByte() { - Span buffer = stackalloc byte[1]; - int bytesRead = Read(buffer); - return (bytesRead == 0) ? -1 /* EOF */ : buffer[0]; + byte b; + return Read(new Span(&b, 1)) != 0 ? b : -1; } public override long Seek(long offset, SeekOrigin origin) @@ -607,7 +606,7 @@ namespace System.Text } } - public override void WriteByte(byte value) - => Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1)); + public override unsafe void WriteByte(byte value) + => Write(new ReadOnlySpan(&value, 1)); } } From 03cafd76bc1b048b4bb93e90f40cbae8ee30a815 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 1 Jul 2021 06:43:44 -0400 Subject: [PATCH 217/926] Reduce overheads of EventSource.WriteEvent(int, object[]) with ETW (#54925) * Reduce overheads of EventSource.WriteEvent(int, object[]) with ETW Cuts allocation by ~3-4x on basic use cases by avoiding a `List(8)` and `List(8)` allocation along with the underlying arrays. * Fix standalone build * Address PR feedback --- .../Diagnostics/Tracing/EventProvider.cs | 87 ++++++++++++++----- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs index d66b9f44eac..d43e49f92ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs @@ -91,10 +91,10 @@ namespace System.Diagnostics.Tracing [ThreadStatic] private static WriteEventErrorCode s_returnCode; // The last return code - private const int s_basicTypeAllocationBufferSize = 16; - private const int s_etwMaxNumberArguments = 128; - private const int s_etwAPIMaxRefObjCount = 8; - private const int s_traceEventMaximumSize = 65482; + private const int BasicTypeAllocationBufferSize = 16; + private const int EtwMaxNumberArguments = 128; + private const int EtwAPIMaxRefObjCount = 8; + private const int TraceEventMaximumSize = 65482; public enum WriteEventErrorCode : int { @@ -776,7 +776,7 @@ namespace System.Diagnostics.Tracing // then the array parameters dataDescriptor++; - dataBuffer += s_basicTypeAllocationBufferSize; + dataBuffer += BasicTypeAllocationBufferSize; dataDescriptor->Size = (uint)blobRet.Length; } else if (data is IntPtr) @@ -935,7 +935,7 @@ namespace System.Diagnostics.Tracing // advance buffers dataDescriptor++; - dataBuffer += s_basicTypeAllocationBufferSize; + dataBuffer += BasicTypeAllocationBufferSize; return (object?)sRet ?? (object?)blobRet; } @@ -975,7 +975,7 @@ namespace System.Diagnostics.Tracing // // // - internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, params object?[] eventPayload) + internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, object?[] eventPayload) { WriteEventErrorCode status = WriteEventErrorCode.NoError; @@ -983,7 +983,7 @@ namespace System.Diagnostics.Tracing { int argCount = eventPayload.Length; - if (argCount > s_etwMaxNumberArguments) + if (argCount > EtwMaxNumberArguments) { s_returnCode = WriteEventErrorCode.TooManyArgs; return false; @@ -992,13 +992,23 @@ namespace System.Diagnostics.Tracing uint totalEventSize = 0; int index; int refObjIndex = 0; - List refObjPosition = new List(s_etwAPIMaxRefObjCount); - List dataRefObj = new List(s_etwAPIMaxRefObjCount); + +#if ES_BUILD_STANDALONE + int[] refObjPosition = new int[EtwAPIMaxRefObjCount]; + object?[] dataRefObj = new object?[EtwAPIMaxRefObjCount]; +#else + Debug.Assert(EtwAPIMaxRefObjCount == 8, $"{nameof(EtwAPIMaxRefObjCount)} must equal the number of fields in {nameof(EightObjects)}"); + EightObjects eightObjectStack = default; + Span refObjPosition = stackalloc int[EtwAPIMaxRefObjCount]; + Span dataRefObj = new Span(ref eightObjectStack._arg0, EtwAPIMaxRefObjCount); +#endif + EventData* userData = stackalloc EventData[2 * argCount]; for (int i = 0; i < 2 * argCount; i++) userData[i] = default; - EventData* userDataPtr = (EventData*)userData; - byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument + + EventData* userDataPtr = userData; + byte* dataBuffer = stackalloc byte[BasicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument byte* currentBuffer = dataBuffer; // @@ -1020,15 +1030,32 @@ namespace System.Diagnostics.Tracing int idx = (int)(userDataPtr - userData - 1); if (!(supportedRefObj is string)) { - if (eventPayload.Length + idx + 1 - index > s_etwMaxNumberArguments) + if (eventPayload.Length + idx + 1 - index > EtwMaxNumberArguments) { s_returnCode = WriteEventErrorCode.TooManyArgs; return false; } hasNonStringRefArgs = true; } - dataRefObj.Add(supportedRefObj); - refObjPosition.Add(idx); + + if (refObjIndex >= dataRefObj.Length) + { +#if ES_BUILD_STANDALONE + Array.Resize(ref dataRefObj, dataRefObj.Length * 2); + Array.Resize(ref refObjPosition, refObjPosition.Length * 2); +#else + Span newDataRefObj = new object?[dataRefObj.Length * 2]; + dataRefObj.CopyTo(newDataRefObj); + dataRefObj = newDataRefObj; + + Span newRefObjPosition = new int[refObjPosition.Length * 2]; + refObjPosition.CopyTo(newRefObjPosition); + refObjPosition = newRefObjPosition; +#endif + } + + dataRefObj[refObjIndex] = supportedRefObj; + refObjPosition[refObjIndex] = idx; refObjIndex++; } } @@ -1042,22 +1069,23 @@ namespace System.Diagnostics.Tracing // update argCount based on actual number of arguments written to 'userData' argCount = (int)(userDataPtr - userData); - if (totalEventSize > s_traceEventMaximumSize) + if (totalEventSize > TraceEventMaximumSize) { s_returnCode = WriteEventErrorCode.EventTooBig; return false; } // the optimized path (using "fixed" instead of allocating pinned GCHandles - if (!hasNonStringRefArgs && (refObjIndex < s_etwAPIMaxRefObjCount)) + if (!hasNonStringRefArgs && (refObjIndex <= EtwAPIMaxRefObjCount)) { // Fast path: at most 8 string arguments // ensure we have at least s_etwAPIMaxStringCount in dataString, so that // the "fixed" statement below works - while (refObjIndex < s_etwAPIMaxRefObjCount) + while (refObjIndex < EtwAPIMaxRefObjCount) { - dataRefObj.Add(null); + dataRefObj[refObjIndex] = null; + refObjPosition[refObjIndex] = -1; ++refObjIndex; } @@ -1067,7 +1095,7 @@ namespace System.Diagnostics.Tracing fixed (char* v0 = (string?)dataRefObj[0], v1 = (string?)dataRefObj[1], v2 = (string?)dataRefObj[2], v3 = (string?)dataRefObj[3], v4 = (string?)dataRefObj[4], v5 = (string?)dataRefObj[5], v6 = (string?)dataRefObj[6], v7 = (string?)dataRefObj[7]) { - userDataPtr = (EventData*)userData; + userDataPtr = userData; if (dataRefObj[0] != null) { userDataPtr[refObjPosition[0]].Ptr = (ulong)v0; @@ -1107,7 +1135,7 @@ namespace System.Diagnostics.Tracing else { // Slow path: use pinned handles - userDataPtr = (EventData*)userData; + userDataPtr = userData; GCHandle[] rgGCHandle = new GCHandle[refObjIndex]; for (int i = 0; i < refObjIndex; ++i) @@ -1145,6 +1173,23 @@ namespace System.Diagnostics.Tracing return true; } +#if !ES_BUILD_STANDALONE + /// Workaround for inability to stackalloc object[EtwAPIMaxRefObjCount == 8]. + private struct EightObjects + { + internal object? _arg0; +#pragma warning disable CA1823, CS0169, IDE0051 + private object? _arg1; + private object? _arg2; + private object? _arg3; + private object? _arg4; + private object? _arg5; + private object? _arg6; + private object? _arg7; +#pragma warning restore CA1823, CS0169, IDE0051 + } +#endif + /// /// WriteEvent, method to be used by generated code on a derived class /// From 6a70a501101eff32a5cb12413ca572f39e46de7e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 1 Jul 2021 06:44:11 -0400 Subject: [PATCH 218/926] Ensure pwszLanguage passed to GetFileMUIPath is empty string (#54978) We're currently treating it only as output, but according to the docs it's also used as input and so needs to be well-formed. Using an empty string ensures it doesn't take precedence over the flags, per the docs for the function. --- .../System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index db16e80b678..93c9070d539 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -782,6 +782,7 @@ namespace System while (true) { // Search all installed languages. The enumerator is re-used between loop iterations. + language[0] = '\0'; bool succeeded = Interop.Kernel32.GetFileMUIPath( Interop.Kernel32.MUI_USE_INSTALLED_LANGUAGES, filePath, language, ref languageLength, @@ -798,6 +799,7 @@ namespace System // Final fallback, using the preferred installed UI language. enumerator = 0; + language[0] = '\0'; succeeded = Interop.Kernel32.GetFileMUIPath( Interop.Kernel32.MUI_USER_PREFERRED_UI_LANGUAGES, filePath, language, ref languageLength, From e363f9acfa1811904eaf9ce017120b8385a58443 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 1 Jul 2021 13:30:56 +0200 Subject: [PATCH 219/926] Remove unnecessary Prev field in portable tailcall helpers (#54894) GC only ever needs to look at the top dispatcher frame as other dispatchers cannot have live arg buffers, so it is not necessary to keep these linked together. We can save a bit of stack this way. Also update design document that was a bit outdated. --- .../design/features/tailcalls-with-helpers.md | 95 +++++++++++-------- .../RuntimeHelpers.CoreCLR.cs | 2 - src/coreclr/vm/corelib.h | 2 - src/coreclr/vm/threads.cpp | 2 +- src/coreclr/vm/threads.h | 1 - 5 files changed, 57 insertions(+), 45 deletions(-) diff --git a/docs/design/features/tailcalls-with-helpers.md b/docs/design/features/tailcalls-with-helpers.md index 316a2a3a008..f471e498233 100644 --- a/docs/design/features/tailcalls-with-helpers.md +++ b/docs/design/features/tailcalls-with-helpers.md @@ -99,19 +99,23 @@ certain tailcalls to generic methods. The second IL stub extracts the arguments and calls the target function. For the above case a function like the following will be generated: ```csharp -void IL_STUB_CallTailCallTarget(IntPtr argBuffer, IntPtr result, IntPtr* retAddr) +void IL_STUB_CallTailCallTarget(IntPtr argBuffer, IntPtr result, PortableTailCallFrame* pFrame) { - int arg1 = *(int*)argBuffer; - RuntimeHelpers.FreeTailCallArgBuffer(); - *retAddr = StubHelpers.NextCallReturnAddress(); + pFrame->NextCall = null; + pFrame->TailCallAwareReturnAddress = StubHelpers.NextCallReturnAddress(); + int arg1 = *(int*)(argBuffer + 4); + *argBuffer = TAILCALLARGBUFFER_ABANDONED; *(bool*)result = IsOdd(arg1); } ``` -It matches the function above but also includes a call to -`StubHelpers.NextCallReturnAddress`. This is a JIT intrinsic that represents the -address of where the next call will return to. This is part of how the mechanism -detects that there is a previous dispatcher that should be used, which will be -described in the next section. +It matches the function above by loading the argument that was written, and +then writing a sentinel value that communicates to GC that the arg buffer does +not need to be scanned anymore, to avoid extending the lifetime of (by-)refs +unnecessarily. In addition, it also includes a call to +`StubHelpers.NextCallReturnAddress`. This is a JIT intrinsic that represents +the address of where the next call will return to. This is part of how the +mechanism detects that there is a previous dispatcher that should be used, +which will be described in the next section. As described above there are cases when the runtime needs to be passed the target function pointer. In those cases this stub will instead load the function @@ -128,16 +132,14 @@ to be set up again since returning would not return directly back to the previous dispatcher. The mechanism uses some data structures to describe the dispatchers that are -currently live on the stack and to facilitate detection of previous dispatchers. -The dispatchers themselves are described by a linked list of -`PortableTailCallFrame` entries. These entries are in a one-to-one -correspondence with each live instance of the dispatcher in the current stack. -This structure looks like the following: +currently live on the stack and to facilitate detection of previous +dispatchers. The dispatchers themselves are described by a series of +`PortableTailCallFrame` entries. These entries are stored on the stack in each +live instance of the dispatcher. This structure looks like the following: ```csharp struct PortableTailCallFrame { - public PortableTailCallFrame* Prev; public IntPtr TailCallAwareReturnAddress; public IntPtr NextCall; } @@ -147,8 +149,8 @@ Here the `TailCallAwareReturnAddress` is an address that can be used to detect whether a return would go to that particular dispatcher. `NextCall` is what the dispatcher uses to perform the next tailcall of a sequence. -The head of this linked list is stored in TLS, along with information about the -currently allocated argument buffer that can be used by GC: +The current frame is stored in TLS along with information about the currently +allocated argument buffer that can be used by GC: ```csharp struct TailCallTls @@ -162,35 +164,44 @@ struct TailCallTls Finally, the dispatcher follows: ```csharp -void DispatchTailCalls(IntPtr callTarget, IntPtr result, IntPtr callersRetAddrSlot) +private static unsafe void DispatchTailCalls( + IntPtr callersRetAddrSlot, + delegate* callTarget, + IntPtr retVal) { IntPtr callersRetAddr; - TailCallTls* tls = - RuntimeHelpers.GetTailCallInfo(callersRetAddrSlot, &callersRetAddr); - TailCallFrame* prevDispatcher = tls->Frame; - if (callersRetAddr == prevDispatcher->TailCallAwareReturnAddress) + TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr); + PortableTailCallFrame* prevFrame = tls->Frame; + if (callersRetAddr == prevFrame->TailCallAwareReturnAddress) { - prevDispatcher->NextCall = callTarget; + prevFrame->NextCall = callTarget; return; } - PortableTailCallFrame frame; - frame.Prev = prevDispatcher; + PortableTailCallFrame newFrame; + // GC uses NextCall to keep LoaderAllocator alive after we link it below, + // so we must null it out before that. + newFrame.NextCall = null; + try { - tls->Frame = &frame; + tls->Frame = &newFrame; do { - frame.NextCall = IntPtr.Zero; - var fptr = (func* void(IntPtr, IntPtr, IntPtr*))callTarget; - fptr(tls->ArgBuffer, result, &frame.TailCallAwareReturnAddress); - callTarget = frame.NextCall; - } while (frame.NextCall != IntPtr.Zero); + callTarget(tls->ArgBuffer, retVal, &newFrame); + callTarget = newFrame.NextCall; + } while (callTarget != null); } finally { - tls->Frame = prevDispatcher; + tls->Frame = prevFrame; + + // If the arg buffer is reporting inst argument, it is safe to abandon it now + if (tls->ArgBuffer != IntPtr.Zero && *(int*)tls->ArgBuffer == 1 /* TAILCALLARGBUFFER_INSTARG_ONLY */) + { + *(int*)tls->ArgBuffer = 2 /* TAILCALLARGBUFFER_ABANDONED */; + } } } ``` @@ -198,16 +209,16 @@ void DispatchTailCalls(IntPtr callTarget, IntPtr result, IntPtr callersRetAddrSl It is first responsible for detecting whether we can return and let a previous dispatcher perform the tailcall. To do this it needs to obtain the caller's return address (i.e. an address in the caller's caller). Furthermode, it needs -to obtain information about the linked list of dispatcher frames. Due to return -address hijacking in the VM it is not enough to simply read the return address -directly from the stack -- instead, assistance from the VM is required in the -form of a helper. This helper both returns the TLS information and the correct -return address. +to obtain information about the current, existing dispatcher frame. Due to +return address hijacking in the VM it is not enough to simply read the return +address directly from the stack -- instead, assistance from the VM is required +in the form of a helper. This helper both returns the TLS information and the +correct return address. In the case a return would go back to a dispatcher we simply record the next call by saving the `callTarget` parameter, a function pointer to a -`CallTailCallTarget` stub. Otherwise a new entry in the linked list is set up -and a loop is entered that starts dispatching tailcalls. +`CallTailCallTarget` stub. Otherwise the new dispatcher is recorded and a loop +is entered that starts dispatching tailcalls. This loop calls into the `CallTailCallTarget` stubs so it is from these stubs that we need to store the return address for comparisons in the future. These @@ -217,6 +228,12 @@ whether we can use a previous dispatcher. This will be the case when we return directly to a `CallTailCallTarget` stub which will then return to the dispatcher. +Note that we take care to zero out PortableTailCallFrame.NextCall from the +CallTailCallTarget stub instead of doing it in the dispatcher before calling +the stub. This is because GC will use NextCall to keep collectible assemblies +alive in the event that there is a GC inside the dispatcher. Once control has +been transfered to CallTailCallTarget we can safely reset the field. + ## The JIT's transformation Based on these functions the JIT needs to do a relatively simple transformation when it sees a tailcall that it cannot dispatch as a fast tailcall. This diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 5cd77f67d6f..3d243e2021c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -324,7 +324,6 @@ namespace System.Runtime.CompilerServices } PortableTailCallFrame newFrame; - newFrame.Prev = prevFrame; // GC uses NextCall to keep LoaderAllocator alive after we link it below, // so we must null it out before that. newFrame.NextCall = null; @@ -497,7 +496,6 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Sequential)] internal unsafe struct PortableTailCallFrame { - public PortableTailCallFrame* Prev; public IntPtr TailCallAwareReturnAddress; public delegate* NextCall; } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 8f6afcc3f73..3b5ac383218 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -779,7 +779,6 @@ DEFINE_FIELD(RAW_ARRAY_DATA, PADDING, Padding) DEFINE_FIELD(RAW_ARRAY_DATA, DATA, Data) DEFINE_CLASS(PORTABLE_TAIL_CALL_FRAME, CompilerServices, PortableTailCallFrame) -DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, PREV, Prev) DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, TAILCALL_AWARE_RETURN_ADDRESS, TailCallAwareReturnAddress) DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, NEXT_CALL, NextCall) @@ -788,7 +787,6 @@ DEFINE_FIELD(TAIL_CALL_TLS, FRAME, Frame) DEFINE_FIELD(TAIL_CALL_TLS, ARG_BUFFER, ArgBuffer) DEFINE_CLASS_U(CompilerServices, PortableTailCallFrame, PortableTailCallFrame) -DEFINE_FIELD_U(Prev, PortableTailCallFrame, Prev) DEFINE_FIELD_U(TailCallAwareReturnAddress, PortableTailCallFrame, TailCallAwareReturnAddress) DEFINE_FIELD_U(NextCall, PortableTailCallFrame, NextCall) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 4dfa4a22b3f..fa93110399d 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -50,7 +50,7 @@ #include "roapi.h" #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT -static const PortableTailCallFrame g_sentinelTailCallFrame = { NULL, NULL, NULL }; +static const PortableTailCallFrame g_sentinelTailCallFrame = { NULL, NULL }; TailCallTls::TailCallTls() // A new frame will always be allocated before the frame is modified, diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 0aadbf40260..d18b21d58f9 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -969,7 +969,6 @@ class BaseStackGuard; struct PortableTailCallFrame { - PortableTailCallFrame* Prev; void* TailCallAwareReturnAddress; void* NextCall; }; From 3ed780db3472f793091c326793394b86d793cae3 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Thu, 1 Jul 2021 13:48:15 +0200 Subject: [PATCH 220/926] Remove RequiresAssemblyFilesAttribute in the trimmed app (#54962) --- .../src/ILLink/ILLink.LinkAttributes.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.xml index 71d27482d47..f61b2459da2 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.xml @@ -18,9 +18,6 @@ - - - @@ -121,9 +118,15 @@ + + + + + + From fc73461cda5e8023c33543e5c293ecc99ed63fc4 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 1 Jul 2021 18:07:21 +0600 Subject: [PATCH 221/926] Fix typo in variable name (#54989) --- .../src/System/Drawing/DrawingComWrappers.cs | 8 ++++---- src/tests/Interop/COM/ComWrappers/API/Program.cs | 12 ++++++------ .../COM/ComWrappers/GlobalInstance/GlobalInstance.cs | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs index 7895381853c..236ae1be2aa 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs @@ -27,9 +27,9 @@ namespace System.Drawing private static ComInterfaceEntry* InitializeComInterfaceEntry() { - GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); + GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease); - IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInteface, fpAddRef, fpRelease); + IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInterface, fpAddRef, fpRelease); ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); wrapperEntry->IID = IID_IStream; @@ -68,10 +68,10 @@ namespace System.Drawing internal static class IStreamVtbl { - public static IntPtr Create(IntPtr fpQueryInteface, IntPtr fpAddRef, IntPtr fpRelease) + public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease) { IntPtr* vtblRaw = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), IntPtr.Size * 14); - vtblRaw[0] = fpQueryInteface; + vtblRaw[0] = fpQueryInterface; vtblRaw[1] = fpAddRef; vtblRaw[2] = fpRelease; vtblRaw[3] = (IntPtr)(delegate* unmanaged)&Read; diff --git a/src/tests/Interop/COM/ComWrappers/API/Program.cs b/src/tests/Interop/COM/ComWrappers/API/Program.cs index 1a779a7da61..52da8f8d060 100644 --- a/src/tests/Interop/COM/ComWrappers/API/Program.cs +++ b/src/tests/Interop/COM/ComWrappers/API/Program.cs @@ -18,10 +18,10 @@ namespace ComWrappersTests { protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { - IntPtr fpQueryInteface = default; + IntPtr fpQueryInterface = default; IntPtr fpAddRef = default; IntPtr fpRelease = default; - ComWrappers.GetIUnknownImpl(out fpQueryInteface, out fpAddRef, out fpRelease); + ComWrappers.GetIUnknownImpl(out fpQueryInterface, out fpAddRef, out fpRelease); ComInterfaceEntry* entryRaw = null; count = 0; @@ -31,7 +31,7 @@ namespace ComWrappersTests { IUnknownImpl = new IUnknownVtbl() { - QueryInterface = fpQueryInteface, + QueryInterface = fpQueryInterface, AddRef = fpAddRef, Release = fpRelease }, @@ -70,9 +70,9 @@ namespace ComWrappersTests { Console.WriteLine($"Running {nameof(ValidateIUnknownImpls)}..."); - ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease); + ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease); - Assert.AreNotEqual(fpQueryInteface, IntPtr.Zero); + Assert.AreNotEqual(fpQueryInterface, IntPtr.Zero); Assert.AreNotEqual(fpAddRef, IntPtr.Zero); Assert.AreNotEqual(fpRelease, IntPtr.Zero); } @@ -198,7 +198,7 @@ namespace ComWrappersTests static void ValidateWrappersInstanceIsolation() { - Console.WriteLine($"Running {nameof(ValidateWrappersInstanceIsolation)}..."); + Console.WriteLine($"Running {nameof(ValidateWrappersInstanceIsolation)}..."); var cw1 = new TestComWrappers(); var cw2 = new TestComWrappers(); diff --git a/src/tests/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs b/src/tests/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs index 899c4f9c74a..7a358a972f2 100644 --- a/src/tests/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs +++ b/src/tests/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs @@ -116,14 +116,14 @@ namespace ComWrappersTests.GlobalInstance } else if (string.Equals(ManagedServerTypeName, obj.GetType().Name)) { - IntPtr fpQueryInteface = default; + IntPtr fpQueryInterface = default; IntPtr fpAddRef = default; IntPtr fpRelease = default; - ComWrappers.GetIUnknownImpl(out fpQueryInteface, out fpAddRef, out fpRelease); + ComWrappers.GetIUnknownImpl(out fpQueryInterface, out fpAddRef, out fpRelease); var vtbl = new IUnknownVtbl() { - QueryInterface = fpQueryInteface, + QueryInterface = fpQueryInterface, AddRef = fpAddRef, Release = fpRelease }; @@ -176,14 +176,14 @@ namespace ComWrappersTests.GlobalInstance private unsafe ComInterfaceEntry* ComputeVtablesForTestObject(Test obj, out int count) { - IntPtr fpQueryInteface = default; + IntPtr fpQueryInterface = default; IntPtr fpAddRef = default; IntPtr fpRelease = default; - ComWrappers.GetIUnknownImpl(out fpQueryInteface, out fpAddRef, out fpRelease); + ComWrappers.GetIUnknownImpl(out fpQueryInterface, out fpAddRef, out fpRelease); var iUnknownVtbl = new IUnknownVtbl() { - QueryInterface = fpQueryInteface, + QueryInterface = fpQueryInterface, AddRef = fpAddRef, Release = fpRelease }; From ffa4923a6e5332410f44444c890d685d8cbaf434 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 1 Jul 2021 05:49:52 -0700 Subject: [PATCH 222/926] Add analyzers to ref-pack / ASP.NET transport package (#54950) * Add analyzers to ref-pack / ASP.NET transport package * Updating shared framework SDK * Respond to feedback * Update arcade SDKs and remove property --- docs/coding-guidelines/libraries-packaging.md | 4 ++++ eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- .../Microsoft.NETCore.App.Ref.sfxproj | 2 ++ src/libraries/Directory.Build.props | 5 +++-- src/libraries/Directory.Build.targets | 5 +++++ .../gen/Microsoft.Extensions.Logging.Generators.csproj | 1 + .../gen/System.Text.Json.SourceGeneration.csproj | 2 ++ 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/docs/coding-guidelines/libraries-packaging.md b/docs/coding-guidelines/libraries-packaging.md index 51c5424583e..95b1183a5a4 100644 --- a/docs/coding-guidelines/libraries-packaging.md +++ b/docs/coding-guidelines/libraries-packaging.md @@ -14,6 +14,8 @@ In some occasions we may want to include a library in the shared framework, but Libraries included in the shared framework should ensure all direct and transitive assembly references are also included in the shared framework. This will be validated as part of the build and errors raised if any dependencies are unsatisfied. +Source generators and analyzers can be included in the shared framework by specifying `IsNetCoreAppAnalyzer`. These projects should specify `AnalyzerLanguage` as mentioned [below](#analyzers--source-generators). + Removing a library from the shared framework is a breaking change and should be avoided. ## Transport package @@ -26,6 +28,8 @@ This package represents the set of libraries which are produced in dotnet/runtim To add a library to the ASP.NETCore shared framework, that library should set the `IsAspNetCoreApp` property for its `ref` and `src` project. This is typically done in the library's `Directory.Build.props`, for example https://github.com/dotnet/runtime/blob/98ac23212e6017c615e7e855e676fc43c8e44cb8/src/libraries/Microsoft.Extensions.Logging.Abstractions/Directory.Build.props#L4. +Source generators and analyzers can be included in the ASP.NETCore shared framework by specifying `IsAspNetCoreAppAnalyzer`. These projects should specify `AnalyzerLanguage` as mentioned [below](#analyzers--source-generators). + Libraries included in this transport package should ensure all direct and transitive assembly references are also included in either the ASP.NETCore shared framework or the .NETCore shared framework. This is not validated in dotnet/runtime at the moment: https://github.com/dotnet/runtime/issues/52562 Removing a library from this transport package is a breaking change and should be avoided. diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index cfe0a882b2d..1a6424850bb 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -54,9 +54,9 @@ https://github.com/dotnet/arcade 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + ff3e7d23139c30feefe36d3d4e8d41a06160f254 https://github.com/dotnet/arcade @@ -70,9 +70,9 @@ https://github.com/dotnet/arcade 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + ff3e7d23139c30feefe36d3d4e8d41a06160f254 https://github.com/microsoft/vstest diff --git a/global.json b/global.json index 77fd48d8d2a..f2f7fc31029 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21329.8", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21330.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21329.8", "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21329.8", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21329.8", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Ref.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Ref.sfxproj index bbdfecaeebc..f1e52a78e99 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Ref.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Ref.sfxproj @@ -15,6 +15,8 @@ + + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 3981181ee92..4d39fad397e 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -128,8 +128,9 @@ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(TestArchiveRoot)runtime/ - $(ArtifactsBinDir)pkg\aspnetcoreapp\ref - $(ArtifactsBinDir)pkg\aspnetcoreapp\lib + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'pkg', 'aspnetcoreapp')) + $([MSBuild]::NormalizeDirectory('$(ASPNETCoreAppPackageRootPath)', 'ref')) + $([MSBuild]::NormalizeDirectory('$(ASPNETCoreAppPackageRootPath)', 'lib')) $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common')) $([MSBuild]::NormalizeDirectory('$(CommonPathRoot)', 'src')) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 27487851a0d..b10705260f4 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -42,6 +42,7 @@ true + + true false false + true cs diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj index ec46f798ab3..176138ce41f 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj @@ -7,6 +7,8 @@ CS1574 false true + true + cs From ccc11af79b645e87b9d93c8c5ff4d02313f7b028 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 1 Jul 2021 06:23:34 -0700 Subject: [PATCH 223/926] Revert "Add ActivitySource support to DiagnosticsHandler (#54437)" (#55006) This reverts commit c88da2905317c0b199c5814594f1ea5d079e0760. --- .../src/ILLink/ILLink.Substitutions.xml | 2 +- .../src/System.Net.Http.csproj | 3 +- .../src/System/Net/Http/DiagnosticsHandler.cs | 292 ++++++++++-------- .../Http/DiagnosticsHandlerLoggingStrings.cs | 25 ++ .../src/System/Net/Http/HttpClientHandler.cs | 26 +- .../tests/FunctionalTests/DiagnosticsTests.cs | 158 +++------- 6 files changed, 260 insertions(+), 246 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml index d6aea3dbf37..314469c96d7 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml @@ -1,7 +1,7 @@ - + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 1423025f7c1..0fee14684e2 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -1,4 +1,4 @@ - + win true @@ -494,6 +494,7 @@ + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index 14c1464e77a..4e4d730c4c1 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -15,121 +15,103 @@ namespace System.Net.Http /// internal sealed class DiagnosticsHandler : DelegatingHandler { - private const string Namespace = "System.Net.Http"; - private const string RequestWriteNameDeprecated = Namespace + ".Request"; - private const string ResponseWriteNameDeprecated = Namespace + ".Response"; - private const string ExceptionEventName = Namespace + ".Exception"; - private const string ActivityName = Namespace + ".HttpRequestOut"; - private const string ActivityStartName = ActivityName + ".Start"; - private const string ActivityStopName = ActivityName + ".Stop"; - - private static readonly DiagnosticListener s_diagnosticListener = new("HttpHandlerDiagnosticListener"); - private static readonly ActivitySource s_activitySource = new(Namespace); - - public static bool IsGloballyEnabled { get; } = GetEnableActivityPropagationValue(); - - private static bool GetEnableActivityPropagationValue() - { - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(Namespace + ".EnableActivityPropagation", out bool enableActivityPropagation)) - { - return enableActivityPropagation; - } - - // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. - string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Suppress Activity propagation. - return false; - } - - // Defaults to enabling Activity propagation. - return true; - } - + /// + /// DiagnosticHandler constructor + /// + /// Inner handler: Windows or Unix implementation of HttpMessageHandler. + /// Note that DiagnosticHandler is the latest in the pipeline public DiagnosticsHandler(HttpMessageHandler innerHandler) : base(innerHandler) { - Debug.Assert(IsGloballyEnabled); } - private static bool ShouldLogDiagnostics(HttpRequestMessage request, out Activity? activity) + internal static bool IsEnabled() { - if (request is null) + // check if there is a parent Activity (and propagation is not suppressed) + // or if someone listens to HttpHandlerDiagnosticListener + return IsGloballyEnabled() && (Activity.Current != null || Settings.s_diagnosticListener.IsEnabled()); + } + + internal static bool IsGloballyEnabled() + { + return Settings.s_activityPropagationEnabled; + } + + // SendAsyncCore returns already completed ValueTask for when async: false is passed. + // Internally, it calls the synchronous Send method of the base class. + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => + SendAsyncCore(request, async: false, cancellationToken).AsTask().GetAwaiter().GetResult(); + + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + SendAsyncCore(request, async: true, cancellationToken).AsTask(); + + private async ValueTask SendAsyncCore(HttpRequestMessage request, bool async, + CancellationToken cancellationToken) + { + // HttpClientHandler is responsible to call static DiagnosticsHandler.IsEnabled() before forwarding request here. + // It will check if propagation is on (because parent Activity exists or there is a listener) or off (forcibly disabled) + // This code won't be called unless consumer unsubscribes from DiagnosticListener right after the check. + // So some requests happening right after subscription starts might not be instrumented. Similarly, + // when consumer unsubscribes, extra requests might be instrumented + + if (request == null) { throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } - activity = null; + Activity? activity = null; + DiagnosticListener diagnosticListener = Settings.s_diagnosticListener; - if (s_activitySource.HasListeners()) + // if there is no listener, but propagation is enabled (with previous IsEnabled() check) + // do not write any events just start/stop Activity and propagate Ids + if (!diagnosticListener.IsEnabled()) { - activity = s_activitySource.CreateActivity(ActivityName, ActivityKind.Client); + activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); + activity.Start(); + InjectHeaders(activity, request); + + try + { + return async ? + await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : + base.Send(request, cancellationToken); + } + finally + { + activity.Stop(); + } } - if (activity is null) - { - bool diagnosticListenerEnabled = s_diagnosticListener.IsEnabled(); + Guid loggingRequestId = Guid.Empty; - if (Activity.Current is not null || (diagnosticListenerEnabled && s_diagnosticListener.IsEnabled(ActivityName, request))) + // There is a listener. Check if listener wants to be notified about HttpClient Activities + if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityName, request)) + { + activity = new Activity(DiagnosticsHandlerLoggingStrings.ActivityName); + + // Only send start event to users who subscribed for it, but start activity anyway + if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityStartName)) { - // If a diagnostics listener is enabled for the Activity, always create one - activity = new Activity(ActivityName); + StartActivity(diagnosticListener, activity, new ActivityStartData(request)); } else { - // There is no Activity, but we may still want to use the instrumented SendAsyncCore if diagnostic listeners are interested in other events - return diagnosticListenerEnabled; + activity.Start(); } } - - activity.Start(); - - if (s_diagnosticListener.IsEnabled(ActivityStartName)) - { - Write(ActivityStartName, new ActivityStartData(request)); - } - - InjectHeaders(activity, request); - - return true; - } - - protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (ShouldLogDiagnostics(request, out Activity? activity)) - { - ValueTask sendTask = SendAsyncCore(request, activity, async: false, cancellationToken); - return sendTask.IsCompleted ? - sendTask.Result : - sendTask.AsTask().GetAwaiter().GetResult(); - } - else - { - return base.Send(request, cancellationToken); - } - } - - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (ShouldLogDiagnostics(request, out Activity? activity)) - { - return SendAsyncCore(request, activity, async: true, cancellationToken).AsTask(); - } - else - { - return base.SendAsync(request, cancellationToken); - } - } - - private async ValueTask SendAsyncCore(HttpRequestMessage request, Activity? activity, bool async, CancellationToken cancellationToken) - { - Guid loggingRequestId = default; - - if (s_diagnosticListener.IsEnabled(RequestWriteNameDeprecated)) + // try to write System.Net.Http.Request event (deprecated) + if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated)) { + long timestamp = Stopwatch.GetTimestamp(); loggingRequestId = Guid.NewGuid(); - Write(RequestWriteNameDeprecated, new RequestData(request, loggingRequestId, Stopwatch.GetTimestamp())); + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, + new RequestData(request, loggingRequestId, timestamp)); + } + + // If we are on at all, we propagate current activity information + Activity? currentActivity = Activity.Current; + if (currentActivity != null) + { + InjectHeaders(currentActivity, request); } HttpResponseMessage? response = null; @@ -144,39 +126,52 @@ namespace System.Net.Http catch (OperationCanceledException) { taskStatus = TaskStatus.Canceled; + + // we'll report task status in HttpRequestOut.Stop throw; } catch (Exception ex) { - if (s_diagnosticListener.IsEnabled(ExceptionEventName)) - { - Write(ExceptionEventName, new ExceptionData(ex, request)); - } - taskStatus = TaskStatus.Faulted; + + if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ExceptionEventName)) + { + // If request was initially instrumented, Activity.Current has all necessary context for logging + // Request is passed to provide some context if instrumentation was disabled and to avoid + // extensive Activity.Tags usage to tunnel request properties + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); + } throw; } finally { - if (activity is not null) + // always stop activity if it was started + if (activity != null) { - activity.SetEndTime(DateTime.UtcNow); - - if (s_diagnosticListener.IsEnabled(ActivityStopName)) - { - Write(ActivityStopName, new ActivityStopData(response, request, taskStatus)); - } - - activity.Stop(); + StopActivity(diagnosticListener, activity, new ActivityStopData( + response, + // If request is failed or cancelled, there is no response, therefore no information about request; + // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests + // and not retain all requests in Start + request, + taskStatus)); } - - if (s_diagnosticListener.IsEnabled(ResponseWriteNameDeprecated)) + // Try to write System.Net.Http.Response event (deprecated) + if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated)) { - Write(ResponseWriteNameDeprecated, new ResponseData(response, loggingRequestId, Stopwatch.GetTimestamp(), taskStatus)); + long timestamp = Stopwatch.GetTimestamp(); + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, + new ResponseData( + response, + loggingRequestId, + timestamp, + taskStatus)); } } } + #region private + private sealed class ActivityStartData { // matches the properties selected in https://github.com/dotnet/diagnostics/blob/ffd0254da3bcc47847b1183fa5453c0877020abd/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/HttpRequestSourceConfiguration.cs#L36-L40 @@ -274,29 +269,55 @@ namespace System.Net.Http public override string ToString() => $"{{ {nameof(Response)} = {Response}, {nameof(LoggingRequestId)} = {LoggingRequestId}, {nameof(Timestamp)} = {Timestamp}, {nameof(RequestTaskStatus)} = {RequestTaskStatus} }}"; } + private static class Settings + { + private const string EnableActivityPropagationEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"; + private const string EnableActivityPropagationAppCtxSettingName = "System.Net.Http.EnableActivityPropagation"; + + public static readonly bool s_activityPropagationEnabled = GetEnableActivityPropagationValue(); + + private static bool GetEnableActivityPropagationValue() + { + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(EnableActivityPropagationAppCtxSettingName, out bool enableActivityPropagation)) + { + return enableActivityPropagation; + } + + // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. + string? envVar = Environment.GetEnvironmentVariable(EnableActivityPropagationEnvironmentVariableSettingName); + if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) + { + // Suppress Activity propagation. + return false; + } + + // Defaults to enabling Activity propagation. + return true; + } + + public static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); + } + private static void InjectHeaders(Activity currentActivity, HttpRequestMessage request) { - const string TraceParentHeaderName = "traceparent"; - const string TraceStateHeaderName = "tracestate"; - const string RequestIdHeaderName = "Request-Id"; - const string CorrelationContextHeaderName = "Correlation-Context"; - if (currentActivity.IdFormat == ActivityIdFormat.W3C) { - if (!request.Headers.Contains(TraceParentHeaderName)) + if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName)) { - request.Headers.TryAddWithoutValidation(TraceParentHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, currentActivity.Id); if (currentActivity.TraceStateString != null) { - request.Headers.TryAddWithoutValidation(TraceStateHeaderName, currentActivity.TraceStateString); + request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceStateHeaderName, currentActivity.TraceStateString); } } } else { - if (!request.Headers.Contains(RequestIdHeaderName)) + if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName)) { - request.Headers.TryAddWithoutValidation(RequestIdHeaderName, currentActivity.Id); + request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName, currentActivity.Id); } } @@ -312,16 +333,41 @@ namespace System.Net.Http baggage.Add(new NameValueHeaderValue(WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)).ToString()); } while (e.MoveNext()); - request.Headers.TryAddWithoutValidation(CorrelationContextHeaderName, baggage); + request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.CorrelationContextHeaderName, baggage); } } } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] - private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string name, T value) + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + string name, + T value) { - s_diagnosticListener.Write(name, value); + diagnosticSource.Write(name, value); } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency.")] + private static Activity StartActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + return diagnosticSource.StartActivity(activity, args); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency.")] + private static void StopActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + diagnosticSource.StopActivity(activity, args); + } + + #endregion } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs new file mode 100644 index 00000000000..0fa57394c1c --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Net.Http +{ + /// + /// Defines names of DiagnosticListener and Write events for DiagnosticHandler + /// + internal static class DiagnosticsHandlerLoggingStrings + { + public const string DiagnosticListenerName = "HttpHandlerDiagnosticListener"; + public const string RequestWriteNameDeprecated = "System.Net.Http.Request"; + public const string ResponseWriteNameDeprecated = "System.Net.Http.Response"; + + public const string ExceptionEventName = "System.Net.Http.Exception"; + public const string ActivityName = "System.Net.Http.HttpRequestOut"; + public const string ActivityStartName = "System.Net.Http.HttpRequestOut.Start"; + + public const string RequestIdHeaderName = "Request-Id"; + public const string CorrelationContextHeaderName = "Correlation-Context"; + + public const string TraceParentHeaderName = "traceparent"; + public const string TraceStateHeaderName = "tracestate"; + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index bfb8f6cae78..2e3289643cf 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -20,17 +20,17 @@ namespace System.Net.Http public partial class HttpClientHandler : HttpMessageHandler { private readonly HttpHandlerType _underlyingHandler; - private readonly HttpMessageHandler _handler; + private readonly DiagnosticsHandler? _diagnosticsHandler; private ClientCertificateOption _clientCertificateOptions; private volatile bool _disposed; public HttpClientHandler() { - _handler = _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled) + _underlyingHandler = new HttpHandlerType(); + if (DiagnosticsHandler.IsGloballyEnabled()) { - _handler = new DiagnosticsHandler(_handler); + _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); } ClientCertificateOptions = ClientCertificateOption.Manual; } @@ -288,11 +288,21 @@ namespace System.Net.Http public IDictionary Properties => _underlyingHandler.Properties; [UnsupportedOSPlatform("browser")] - protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => - _handler.Send(request, cancellationToken); + protected internal override HttpResponseMessage Send(HttpRequestMessage request, + CancellationToken cancellationToken) + { + return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? + _diagnosticsHandler.Send(request, cancellationToken) : + _underlyingHandler.Send(request, cancellationToken); + } - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => - _handler.SendAsync(request, cancellationToken); + protected internal override Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? + _diagnosticsHandler.SendAsync(request, cancellationToken) : + _underlyingHandler.SendAsync(request, cancellationToken); + } // lazy-load the validator func so it can be trimmed by the ILLinker if it isn't used. private static Func? s_dangerousAcceptAnyServerCertificateValidator; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 66b60e800b8..1fb6fd925fd 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Tracing; -using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Net.Test.Common; @@ -257,7 +256,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus status = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Canceled, status); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } }); @@ -345,7 +344,7 @@ namespace System.Net.Http.Functional.Tests activityStopResponseLogged = GetProperty(kvp.Value, "Response"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } }); @@ -410,7 +409,7 @@ namespace System.Net.Http.Functional.Tests Assert.Contains("goodkey=bad%2Fvalue", correlationContext); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -468,7 +467,7 @@ namespace System.Net.Http.Functional.Tests Assert.False(request.Headers.TryGetValues("traceparent", out var _)); Assert.False(request.Headers.TryGetValues("tracestate", out var _)); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } }); @@ -520,7 +519,7 @@ namespace System.Net.Http.Functional.Tests } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Stop")) { - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } }); @@ -609,7 +608,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -648,7 +647,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult(); + activityStopTcs.SetResult();; } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -797,6 +796,44 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void SendAsync_ExpectedActivityPropagationWithoutListener() + { + RemoteExecutor.Invoke(async (useVersion, testAsync) => + { + Activity parent = new Activity("parent").Start(); + + await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(useVersion, testAsync, uri); + }, + async server => + { + HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); + AssertHeadersAreInjected(requestData, parent); + }); + }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void SendAsync_ExpectedActivityPropagationWithoutListenerOrParentActivity() + { + RemoteExecutor.Invoke(async (useVersion, testAsync) => + { + await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(useVersion, testAsync, uri); + }, + async server => + { + HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); + AssertNoHeadersAreInjected(requestData); + }); + }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + } + [ConditionalTheory(nameof(EnableActivityPropagationEnvironmentVariableIsNotSetAndRemoteExecutorSupported))] [InlineData("true")] [InlineData("1")] @@ -862,111 +899,6 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).Dispose(); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [InlineData(true, true, true)] // Activity was set and ActivitySource created an Activity - [InlineData(true, false, true)] // Activity was set and ActivitySource created an Activity - [InlineData(true, null, true)] // Activity was set and ActivitySource created an Activity - [InlineData(true, true, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created - [InlineData(true, false, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created - [InlineData(true, null, false)] // Activity was set, ActivitySource chose not to create an Activity, so one was manually created - [InlineData(true, true, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created - [InlineData(true, false, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created - [InlineData(true, null, null)] // Activity was set, ActivitySource had no listeners, so an Activity was manually created - [InlineData(false, true, true)] // DiagnosticListener requested an Activity and ActivitySource created an Activity - [InlineData(false, true, false)] // DiagnosticListener requested an Activity, ActivitySource chose not to create an Activity, so one was manually created - [InlineData(false, true, null)] // DiagnosticListener requested an Activity, ActivitySource had no listeners, so an Activity was manually created - [InlineData(false, false, true)] // No Activity is set, DiagnosticListener does not want one, but ActivitySource created an Activity - [InlineData(false, false, false)] // No Activity is set, DiagnosticListener does not want one and ActivitySource chose not to create an Activity - [InlineData(false, false, null)] // No Activity is set, DiagnosticListener does not want one and ActivitySource has no listeners - [InlineData(false, null, true)] // No Activity is set, there is no DiagnosticListener, but ActivitySource created an Activity - [InlineData(false, null, false)] // No Activity is set, there is no DiagnosticListener and ActivitySource chose not to create an Activity - [InlineData(false, null, null)] // No Activity is set, there is no DiagnosticListener and ActivitySource has no listeners - public void SendAsync_ActivityIsCreatedIfRequested(bool currentActivitySet, bool? diagnosticListenerActivityEnabled, bool? activitySourceCreatesActivity) - { - string parameters = $"{currentActivitySet},{diagnosticListenerActivityEnabled},{activitySourceCreatesActivity}"; - - RemoteExecutor.Invoke(async (useVersion, testAsync, parametersString) => - { - bool?[] parameters = parametersString.Split(',').Select(p => p.Length == 0 ? (bool?)null : bool.Parse(p)).ToArray(); - bool currentActivitySet = parameters[0].Value; - bool? diagnosticListenerActivityEnabled = parameters[1]; - bool? activitySourceCreatesActivity = parameters[2]; - - bool madeASamplingDecision = false; - if (activitySourceCreatesActivity.HasValue) - { - ActivitySource.AddActivityListener(new ActivityListener - { - ShouldListenTo = _ => true, - Sample = (ref ActivityCreationOptions _) => - { - madeASamplingDecision = true; - return activitySourceCreatesActivity.Value ? ActivitySamplingResult.AllData : ActivitySamplingResult.None; - } - }); - } - - bool listenerCallbackWasCalled = false; - IDisposable listenerSubscription = new MemoryStream(); // Dummy disposable - if (diagnosticListenerActivityEnabled.HasValue) - { - var diagnosticListenerObserver = new FakeDiagnosticListenerObserver(_ => listenerCallbackWasCalled = true); - - diagnosticListenerObserver.Enable(name => !name.Contains("HttpRequestOut") || diagnosticListenerActivityEnabled.Value); - - listenerSubscription = DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver); - } - - Activity activity = currentActivitySet ? new Activity("parent").Start() : null; - - if (!currentActivitySet) - { - // Listen to new activity creations if an Activity was created without a parent - // (when a DiagnosticListener forced one to be created) - ActivitySource.AddActivityListener(new ActivityListener - { - ShouldListenTo = _ => true, - ActivityStarted = created => - { - Assert.Null(activity); - activity = created; - } - }); - } - - using (listenerSubscription) - { - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - - if (currentActivitySet || diagnosticListenerActivityEnabled == true || activitySourceCreatesActivity == true) - { - Assert.NotNull(activity); - AssertHeadersAreInjected(requestData, activity); - } - else - { - AssertNoHeadersAreInjected(requestData); - - if (!currentActivitySet) - { - Assert.Null(activity); - } - } - }); - } - - Assert.Equal(activitySourceCreatesActivity.HasValue, madeASamplingDecision); - Assert.Equal(diagnosticListenerActivityEnabled.HasValue, listenerCallbackWasCalled); - }, UseVersion.ToString(), TestAsync.ToString(), parameters).Dispose(); - } - private static T GetProperty(object obj, string propertyName) { Type t = obj.GetType(); From f6cc321fbd49b097364951137d3fd50c3b4872d2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 1 Jul 2021 15:26:58 +0200 Subject: [PATCH 224/926] Remove reference to non-existent System.Net.NameResolution.Unit.Tests.csproj (#55003) --- .../System.Net.NameResolution.sln | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/System.Net.NameResolution.sln b/src/libraries/System.Net.NameResolution/System.Net.NameResolution.sln index 83e8aa0d133..eb9d01b1c49 100644 --- a/src/libraries/System.Net.NameResolution/System.Net.NameResolution.sln +++ b/src/libraries/System.Net.NameResolution/System.Net.NameResolution.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.NameResolution.F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.NameResolution.Pal.Tests", "tests\PalTests\System.Net.NameResolution.Pal.Tests.csproj", "{72B2CC3A-D66E-4F7E-BFA4-38EE373B5043}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.NameResolution.Unit.Tests", "tests\UnitTests\System.Net.NameResolution.Unit.Tests.csproj", "{82007016-ED8D-408E-BA83-DFB3F74B104F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj", "{A3FC70CE-3746-4E03-A92F-0102716FA97E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.CompilerServices.Unsafe", "..\System.Runtime.CompilerServices.Unsafe\src\System.Runtime.CompilerServices.Unsafe.ilproj", "{D62E59F2-BDF3-4060-85E0-20D1BB41B95E}" @@ -34,7 +32,6 @@ Global {00BF602B-F709-4108-9A39-C57A493D3EF8} = {41E915D1-3857-4F01-8576-B24104359E0B} {BE2A4103-7CA0-4DAD-A4AD-F623CBFAD379} = {41E915D1-3857-4F01-8576-B24104359E0B} {72B2CC3A-D66E-4F7E-BFA4-38EE373B5043} = {41E915D1-3857-4F01-8576-B24104359E0B} - {82007016-ED8D-408E-BA83-DFB3F74B104F} = {41E915D1-3857-4F01-8576-B24104359E0B} {77720BB9-1319-4BF7-A2DB-B1FC3F20FD1F} = {B9D5A47D-9CA5-4FE1-AFE2-5E85A940B43D} {15B66DCE-70BF-43D6-854D-42A6C840B22C} = {B9D5A47D-9CA5-4FE1-AFE2-5E85A940B43D} {A3FC70CE-3746-4E03-A92F-0102716FA97E} = {B9D5A47D-9CA5-4FE1-AFE2-5E85A940B43D} @@ -73,10 +70,6 @@ Global {72B2CC3A-D66E-4F7E-BFA4-38EE373B5043}.Debug|Any CPU.Build.0 = Debug|Any CPU {72B2CC3A-D66E-4F7E-BFA4-38EE373B5043}.Release|Any CPU.ActiveCfg = Release|Any CPU {72B2CC3A-D66E-4F7E-BFA4-38EE373B5043}.Release|Any CPU.Build.0 = Release|Any CPU - {82007016-ED8D-408E-BA83-DFB3F74B104F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82007016-ED8D-408E-BA83-DFB3F74B104F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82007016-ED8D-408E-BA83-DFB3F74B104F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82007016-ED8D-408E-BA83-DFB3F74B104F}.Release|Any CPU.Build.0 = Release|Any CPU {A3FC70CE-3746-4E03-A92F-0102716FA97E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A3FC70CE-3746-4E03-A92F-0102716FA97E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3FC70CE-3746-4E03-A92F-0102716FA97E}.Release|Any CPU.ActiveCfg = Release|Any CPU From e97d67e187beb01471ab587637006101bbf4be11 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 1 Jul 2021 11:23:10 -0400 Subject: [PATCH 225/926] [mono][aot] Implement fetching from the rgctx in llvmonly mode using inline code instead of (#54840) calling mono_fill_class_rgctx (). Also bump the initial size of the rgctx to 16 to allow loading more entries without doing a nested lookup. --- src/mono/mono/mini/jit-icalls.c | 4 - src/mono/mono/mini/method-to-ir.c | 140 +++++++++------------- src/mono/mono/mini/mini-generic-sharing.c | 2 +- src/mono/mono/mini/mini.c | 2 + src/mono/mono/mini/mini.h | 1 + 5 files changed, 58 insertions(+), 91 deletions(-) diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 0bc127c9787..189eae79016 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -1512,10 +1512,6 @@ mono_fill_class_rgctx (MonoVTable *vtable, int index) ERROR_DECL (error); gpointer res; - /* - * This is perf critical. - * fill_runtime_generic_context () contains a fallpath. - */ res = mono_class_fill_runtime_generic_context (vtable, index, error); if (!is_ok (error)) { mono_error_set_pending_exception (error); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index d011f3b6da0..9f1df4ec820 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2583,106 +2583,74 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt { MonoInst *call; - // FIXME: No fastpath since the slot is not a compile time constant - MonoInst *args [2] = { rgctx }; - EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry); - if (entry->in_mrgctx) - call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); - else - call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); - return call; -#if 0 - /* - * FIXME: This can be called during decompose, which is a problem since it creates - * new bblocks. - * Also, the fastpath doesn't work since the slot number is dynamically allocated. - */ - int i, slot, depth, index, rgctx_reg, val_reg, res_reg; - gboolean mrgctx; - MonoBasicBlock *is_null_bb, *end_bb; - MonoInst *res, *ins, *call; - MonoInst *args[16]; + MonoInst *slot_ins; + EMIT_NEW_AOTCONST (cfg, slot_ins, MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry); - slot = mini_get_rgctx_entry_slot (entry); - - mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot); - index = MONO_RGCTX_SLOT_INDEX (slot); - if (mrgctx) - index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / TARGET_SIZEOF_VOID_P; - for (depth = 0; ; ++depth) { - int size = mono_class_rgctx_get_array_size (depth, mrgctx); - - if (index < size - 1) - break; - index -= size - 1; - } - - NEW_BBLOCK (cfg, end_bb); - NEW_BBLOCK (cfg, is_null_bb); - - if (mrgctx) { - rgctx_reg = rgctx->dreg; - } else { - rgctx_reg = alloc_preg (cfg); - - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)); - // FIXME: Avoid this check by allocating the table when the vtable is created etc. - NEW_BBLOCK (cfg, is_null_bb); - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); - } - - for (i = 0; i < depth; ++i) { - int array_reg = alloc_preg (cfg); - - /* load ptr to next array */ - if (mrgctx && i == 0) - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT); + // Can't add basic blocks during decompose/interp entry mode etc. + // FIXME: Add a fastpath for in_mrgctx + if (cfg->after_method_to_ir || cfg->gsharedvt || cfg->interp_entry_only || entry->in_mrgctx) { + MonoInst *args [2] = { rgctx, slot_ins }; + if (entry->in_mrgctx) + call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); else - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, 0); - rgctx_reg = array_reg; - /* is the ptr null? */ - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0); - /* if yes, jump to actual trampoline */ - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); + call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); + return call; } - /* fetch slot */ - val_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, rgctx_reg, (index + 1) * TARGET_SIZEOF_VOID_P); - /* is the slot null? */ - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0); - /* if yes, jump to actual trampoline */ - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); + MonoBasicBlock *slowpath_bb, *end_bb; + MonoInst *ins, *res; + int rgctx_reg, res_reg; + + /* + * rgctx = vtable->runtime_generic_context; + * if (rgctx) { + * val = rgctx [slot + 1]; + * if (val) + * return val; + * } + * + */ + NEW_BBLOCK (cfg, end_bb); + NEW_BBLOCK (cfg, slowpath_bb); + + rgctx_reg = alloc_preg (cfg); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)); + // FIXME: Avoid this check by allocating the table when the vtable is created etc. + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb); + + int table_size = mono_class_rgctx_get_array_size (0, FALSE); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_ins->dreg, table_size - 1); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBGE, slowpath_bb); + + int shifted_slot_reg = alloc_ireg (cfg); + EMIT_NEW_BIALU_IMM (cfg, ins, OP_ISHL_IMM, shifted_slot_reg, slot_ins->dreg, TARGET_SIZEOF_VOID_P == 8 ? 3 : 2); + + int addr_reg = alloc_preg (cfg); + EMIT_NEW_UNALU (cfg, ins, OP_MOVE, addr_reg, rgctx_reg); + EMIT_NEW_BIALU (cfg, ins, OP_PADD, addr_reg, addr_reg, shifted_slot_reg); + int val_reg = alloc_preg (cfg); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, addr_reg, TARGET_SIZEOF_VOID_P); + + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb); - /* Fastpath */ res_reg = alloc_preg (cfg); - MONO_INST_NEW (cfg, ins, OP_MOVE); - ins->dreg = res_reg; - ins->sreg1 = val_reg; - MONO_ADD_INS (cfg->cbb, ins); + EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, val_reg); res = ins; MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - /* Slowpath */ - MONO_START_BB (cfg, is_null_bb); - args [0] = rgctx; - EMIT_NEW_ICONST (cfg, args [1], index); - if (mrgctx) - call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); - else - call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); - MONO_INST_NEW (cfg, ins, OP_MOVE); - ins->dreg = res_reg; - ins->sreg1 = call->dreg; - MONO_ADD_INS (cfg->cbb, ins); + MONO_START_BB (cfg, slowpath_bb); + slowpath_bb->out_of_line = TRUE; + + MonoInst *args[2] = { rgctx, slot_ins }; + call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); + EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, call->dreg); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); MONO_START_BB (cfg, end_bb); return res; -#endif } /* diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 7cf73897551..a596cf3e6cd 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -2877,7 +2877,7 @@ lookup_or_register_info (MonoMemoryManager *mem_manager, MonoClass *klass, MonoM static inline int class_rgctx_array_size (int n) { - return 4 << n; + return 16 << n; } static inline int diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index ae87e446d39..72a5bf4179d 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3543,6 +3543,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts cfg->opt &= ~MONO_OPT_BRANCH; } + cfg->after_method_to_ir = TRUE; + /* todo: remove code when we have verified that the liveness for try/catch blocks * works perfectly */ diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index c865bf5b293..2afc62d7942 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1479,6 +1479,7 @@ typedef struct { guint self_init : 1; guint code_exec_only : 1; guint interp_entry_only : 1; + guint after_method_to_ir : 1; guint8 uses_simd_intrinsics; int r4_stack_type; gpointer debug_info; From 779b79401648908279d0903b5a91f593c745efd0 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 1 Jul 2021 11:27:10 -0400 Subject: [PATCH 226/926] [wasm][aot] Enable System.Runtime.Tests (#54960) .. this hasn't crashed in last 14 days on the tracking PR. - And disable the one failing test with ActiveIssue --- src/libraries/System.Runtime/tests/System/Type/TypeTests.cs | 1 + src/libraries/tests.proj | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs index 8f2d1415825..ca984b43318 100644 --- a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs +++ b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs @@ -528,6 +528,7 @@ namespace System.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/52393", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public void GetTypeByName_InvokeViaReflection_Success() { MethodInfo method = typeof(Type).GetMethod("GetType", new[] { typeof(string) }); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index c4054686952..34588fb46c2 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -280,9 +280,6 @@ - - - From 27a1f0b9ea57642cb9f92e075c4873afb71a9400 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 1 Jul 2021 12:09:06 -0400 Subject: [PATCH 227/926] [mono] Avoid putting static virtual methods into the IMT table. (#54981) --- src/mono/mono/metadata/object.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index a8d2c1c70a1..5b5434682c5 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -1525,6 +1525,11 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_ continue; } + if (m_method_is_static (method)) { + vt_slot ++; + continue; + } + if (method->flags & METHOD_ATTRIBUTE_VIRTUAL) { add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, vt_slot, slot_num); vt_slot ++; From f50777b4a027fff14ebb85776ad5fc3fe54af10b Mon Sep 17 00:00:00 2001 From: imhameed Date: Thu, 1 Jul 2021 09:30:27 -0700 Subject: [PATCH 228/926] [mono] Implement the rest of Vector{64,128}{,} (#54924) --- src/mono/mono/mini/mini-llvm.c | 63 +++++-- src/mono/mono/mini/mini-ops.h | 8 +- src/mono/mono/mini/simd-intrinsics.c | 268 ++++++++++++++++++++------- src/mono/mono/mini/simd-methods.h | 20 +- 4 files changed, 277 insertions(+), 82 deletions(-) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index fd88f9cf163..088d843cd06 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -259,6 +259,11 @@ enum { ARM64_MAX_VECTOR_ELEMS = 16, }; +const int mask_0_incr_1 [] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +}; + static LLVMIntPredicate cond_to_llvm_cond [] = { LLVMIntEQ, LLVMIntNE, @@ -7625,7 +7630,6 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) break; case OP_XCAST: { LLVMTypeRef t = simd_class_to_llvm_type (ctx, ins->klass); - values [ins->dreg] = LLVMBuildBitCast (builder, lhs, t, ""); break; } @@ -7633,6 +7637,45 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) values [ins->dreg] = concatenate_vectors (ctx, lhs, rhs); break; } + case OP_XINSERT_LOWER: + case OP_XINSERT_UPPER: { + const char *oname = ins->opcode == OP_XINSERT_LOWER ? "xinsert_lower" : "xinsert_upper"; + int ix = ins->opcode == OP_XINSERT_LOWER ? 0 : 1; + LLVMTypeRef src_t = LLVMTypeOf (lhs); + unsigned int width = mono_llvm_get_prim_size_bits (src_t); + LLVMTypeRef int_t = LLVMIntType (width / 2); + LLVMTypeRef intvec_t = LLVMVectorType (int_t, 2); + LLVMValueRef insval = LLVMBuildBitCast (builder, rhs, int_t, oname); + LLVMValueRef val = LLVMBuildBitCast (builder, lhs, intvec_t, oname); + val = LLVMBuildInsertElement (builder, val, insval, const_int32 (ix), oname); + val = LLVMBuildBitCast (builder, val, src_t, oname); + values [ins->dreg] = val; + break; + } + case OP_XLOWER: + case OP_XUPPER: { + const char *oname = ins->opcode == OP_XLOWER ? "xlower" : "xupper"; + LLVMTypeRef src_t = LLVMTypeOf (lhs); + unsigned int elems = LLVMGetVectorSize (src_t); + g_assert (elems >= 2 && elems <= MAX_VECTOR_ELEMS); + unsigned int ret_elems = elems / 2; + int startix = ins->opcode == OP_XLOWER ? 0 : ret_elems; + LLVMValueRef val = LLVMBuildShuffleVector (builder, lhs, LLVMGetUndef (src_t), create_const_vector_i32 (&mask_0_incr_1 [startix], ret_elems), oname); + values [ins->dreg] = val; + break; + } + case OP_XWIDEN: + case OP_XWIDEN_UNSAFE: { + const char *oname = ins->opcode == OP_XWIDEN ? "xwiden" : "xwiden_unsafe"; + LLVMTypeRef src_t = LLVMTypeOf (lhs); + unsigned int elems = LLVMGetVectorSize (src_t); + g_assert (elems <= MAX_VECTOR_ELEMS / 2); + unsigned int ret_elems = elems * 2; + LLVMValueRef upper = ins->opcode == OP_XWIDEN ? LLVMConstNull (src_t) : LLVMGetUndef (src_t); + LLVMValueRef val = LLVMBuildShuffleVector (builder, lhs, upper, create_const_vector_i32 (mask_0_incr_1, ret_elems), oname); + values [ins->dreg] = val; + break; + } #endif // defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_WASM) #if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_WASM) @@ -9101,18 +9144,17 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) value = LLVMBuildBitCast (ctx->builder, lhs, vec_type, ""); } - const int mask_values [] = { 0, 1, 2, 3, 4, 5, 6, 7 }; LLVMValueRef mask_vec; LLVMTypeRef dst_type; if (ins->inst_c0 == MONO_TYPE_I2) { - mask_vec = create_const_vector_i32 (mask_values, 8); + mask_vec = create_const_vector_i32 (mask_0_incr_1, 8); dst_type = sse_i2_t; } else if (ins->inst_c0 == MONO_TYPE_I4) { - mask_vec = create_const_vector_i32 (mask_values, 4); + mask_vec = create_const_vector_i32 (mask_0_incr_1, 4); dst_type = sse_i4_t; } else { g_assert (ins->inst_c0 == MONO_TYPE_I8); - mask_vec = create_const_vector_i32 (mask_values, 2); + mask_vec = create_const_vector_i32 (mask_0_incr_1, 2); dst_type = sse_i8_t; } @@ -9501,18 +9543,13 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) case OP_ARM64_EXT: { LLVMTypeRef ret_t = LLVMTypeOf (lhs); unsigned int elems = LLVMGetVectorSize (ret_t); - const int unrolled_mask [MAX_VECTOR_ELEMS] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - }; - enum { ARM64_EXT_MAX_INDEX = MAX_VECTOR_ELEMS / 2 }; - int max_index = elems; + g_assert (elems <= ARM64_MAX_VECTOR_ELEMS); LLVMValueRef index = arg3; LLVMValueRef default_value = lhs; - ImmediateUnrollCtx ictx = immediate_unroll_begin (ctx, bb, max_index, index, ret_t, "arm64_ext"); + ImmediateUnrollCtx ictx = immediate_unroll_begin (ctx, bb, elems, index, ret_t, "arm64_ext"); int i = 0; while (immediate_unroll_next (&ictx, &i)) { - LLVMValueRef mask = create_const_vector_i32 (&unrolled_mask [i], elems); + LLVMValueRef mask = create_const_vector_i32 (&mask_0_incr_1 [i], elems); LLVMValueRef result = LLVMBuildShuffleVector (builder, lhs, rhs, mask, "arm64_ext"); immediate_unroll_commit (&ictx, i, result); } diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 25dd6d8fd6e..ed2d11d563a 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1562,8 +1562,13 @@ MINI_OP3(OP_XOP_OVR_SCALAR_X_X_X_X, "xop_ovr_scalar_x_x_x_x", XREG, XREG, XREG, MINI_OP(OP_XOP_OVR_BYSCALAR_X_X_X, "xop_ovr_byscalar_x_x_x", XREG, XREG, XREG) MINI_OP(OP_XCONCAT, "xconcat", XREG, XREG, XREG) - MINI_OP(OP_XCAST, "xcast", XREG, XREG, NONE) +MINI_OP(OP_XLOWER, "xlower", XREG, XREG, NONE) +MINI_OP(OP_XUPPER, "xupper", XREG, XREG, NONE) +MINI_OP(OP_XWIDEN, "xwiden", XREG, XREG, NONE) +MINI_OP(OP_XWIDEN_UNSAFE, "xwiden_unsafe", XREG, XREG, NONE) +MINI_OP(OP_XINSERT_LOWER, "xinsert_lower", XREG, XREG, XREG) +MINI_OP(OP_XINSERT_UPPER, "xinsert_upper", XREG, XREG, XREG) /* Extract an element from a vector with a variable lane index. * The index is assumed to be in range. @@ -1589,6 +1594,7 @@ MINI_OP3(OP_XINSERT_I8, "xinsert_i8", XREG, XREG, LREG, IREG) MINI_OP3(OP_XINSERT_R4, "xinsert_r4", XREG, XREG, FREG, IREG) MINI_OP3(OP_XINSERT_R8, "xinsert_r8", XREG, XREG, FREG, IREG) + MINI_OP(OP_LZCNT32, "lzcnt32", IREG, IREG, NONE) MINI_OP(OP_LZCNT64, "lzcnt64", LREG, LREG, NONE) MINI_OP(OP_POPCNT32, "popcnt32", IREG, IREG, NONE) diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index 9b4fce55bc0..7408241745a 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -275,9 +275,9 @@ get_vector_t_elem_type (MonoType *vector_type) g_assert (vector_type->type == MONO_TYPE_GENERICINST); klass = mono_class_from_mono_type_internal (vector_type); g_assert ( - !strcmp (m_class_get_name (klass), "Vector`1") || + !strcmp (m_class_get_name (klass), "Vector`1") || !strcmp (m_class_get_name (klass), "Vector64`1") || - !strcmp (m_class_get_name (klass), "Vector128`1") || + !strcmp (m_class_get_name (klass), "Vector128`1") || !strcmp (m_class_get_name (klass), "Vector256`1")); etype = mono_class_get_context (klass)->class_inst->type_argv [0]; return etype; @@ -539,7 +539,28 @@ type_to_xextract_op (MonoTypeEnum type) } } +static int +type_to_extract_op (MonoTypeEnum type) +{ + switch (type) { + case MONO_TYPE_I1: case MONO_TYPE_U1: return OP_EXTRACT_I1; + case MONO_TYPE_I2: case MONO_TYPE_U2: return OP_EXTRACT_I2; + case MONO_TYPE_I4: case MONO_TYPE_U4: return OP_EXTRACT_I4; + case MONO_TYPE_I8: case MONO_TYPE_U8: return OP_EXTRACT_I8; + case MONO_TYPE_R4: return OP_EXTRACT_R4; + case MONO_TYPE_R8: return OP_EXTRACT_R8; + case MONO_TYPE_I: case MONO_TYPE_U: +#if TARGET_SIZEOF_VOID_P == 8 + return OP_EXTRACT_I8; +#else + return OP_EXTRACT_I4; +#endif + default: g_assert_not_reached (); + } +} + static guint16 sri_vector_methods [] = { + SN_As, SN_AsByte, SN_AsDouble, SN_AsInt16, @@ -550,17 +571,37 @@ static guint16 sri_vector_methods [] = { SN_AsUInt16, SN_AsUInt32, SN_AsUInt64, + SN_AsVector128, + SN_AsVector2, + SN_AsVector256, + SN_AsVector3, + SN_AsVector4, SN_Create, + SN_CreateScalar, SN_CreateScalarUnsafe, + SN_GetElement, + SN_GetLower, + SN_GetUpper, + SN_ToScalar, + SN_ToVector128, + SN_ToVector128Unsafe, + SN_ToVector256, + SN_ToVector256Unsafe, + SN_WithElement, }; +/* nint and nuint haven't been enabled yet for System.Runtime.Intrinsics. + * Remove this once support has been added. + */ +#define MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE(t) ((MONO_TYPE_IS_VECTOR_PRIMITIVE(t)) && ((t)->type != MONO_TYPE_I) && ((t)->type != MONO_TYPE_U)) + static gboolean is_elementwise_create_overload (MonoMethodSignature *fsig, MonoType *ret_type) { uint16_t param_count = fsig->param_count; if (param_count < 1) return FALSE; MonoType *type = fsig->params [0]; - if (!MONO_TYPE_IS_VECTOR_PRIMITIVE (type)) return FALSE; + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (type)) return FALSE; if (!mono_metadata_type_equal (ret_type, type)) return FALSE; for (uint16_t i = 1; i < param_count; ++i) if (!mono_metadata_type_equal (type, fsig->params [i])) return FALSE; @@ -592,6 +633,7 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi MonoTypeEnum arg0_type = fsig->param_count > 0 ? get_underlying_type (fsig->params [0]) : MONO_TYPE_VOID; switch (id) { + case SN_As: case SN_AsByte: case SN_AsDouble: case SN_AsInt16: @@ -602,8 +644,9 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi case SN_AsUInt16: case SN_AsUInt32: case SN_AsUInt64: { - MonoType *etype = get_vector_t_elem_type (fsig->params [0]); - if (!MONO_TYPE_IS_VECTOR_PRIMITIVE (etype)) + MonoType *ret_type = get_vector_t_elem_type (fsig->ret); + MonoType *arg_type = get_vector_t_elem_type (fsig->params [0]); + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (ret_type) || !MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (arg_type)) return NULL; return emit_simd_ins (cfg, klass, OP_XCAST, args [0]->dreg, -1); } @@ -617,8 +660,145 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi return emit_vector_create_elementwise (cfg, fsig, fsig->ret, etype, args); break; } + case SN_CreateScalar: + return emit_simd_ins_for_sig (cfg, klass, OP_CREATE_SCALAR, -1, arg0_type, fsig, args); case SN_CreateScalarUnsafe: return emit_simd_ins_for_sig (cfg, klass, OP_CREATE_SCALAR_UNSAFE, -1, arg0_type, fsig, args); + case SN_GetElement: { + MonoClass *arg_class = mono_class_from_mono_type_internal (fsig->params [0]); + MonoType *etype = mono_class_get_context (arg_class)->class_inst->type_argv [0]; + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (etype)) + return NULL; + int size = mono_class_value_size (arg_class, NULL); + int esize = mono_class_value_size (mono_class_from_mono_type_internal (etype), NULL); + int elems = size / esize; + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, args [1]->dreg, elems); + MONO_EMIT_NEW_COND_EXC (cfg, GE_UN, "ArgumentOutOfRangeException"); + int extract_op = type_to_xextract_op (arg0_type); + return emit_simd_ins_for_sig (cfg, klass, extract_op, -1, arg0_type, fsig, args); + } + case SN_GetLower: + case SN_GetUpper: { + MonoType *arg_type = get_vector_t_elem_type (fsig->params [0]); + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (arg_type)) + return NULL; + int op = id == SN_GetLower ? OP_XLOWER : OP_XUPPER; + return emit_simd_ins_for_sig (cfg, klass, op, 0, arg0_type, fsig, args); + } + case SN_ToScalar: { + MonoType *arg_type = get_vector_t_elem_type (fsig->params [0]); + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (arg_type)) + return NULL; + int extract_op = type_to_extract_op (arg0_type); + return emit_simd_ins_for_sig (cfg, klass, extract_op, 0, arg0_type, fsig, args); + } + case SN_ToVector128: + case SN_ToVector128Unsafe: { + MonoType *arg_type = get_vector_t_elem_type (fsig->params [0]); + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (arg_type)) + return NULL; + int op = id == SN_ToVector128 ? OP_XWIDEN : OP_XWIDEN_UNSAFE; + return emit_simd_ins_for_sig (cfg, klass, op, 0, arg0_type, fsig, args); + } + case SN_WithElement: { + MonoClass *arg_class = mono_class_from_mono_type_internal (fsig->params [0]); + MonoType *etype = mono_class_get_context (arg_class)->class_inst->type_argv [0]; + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (etype)) + return NULL; + int size = mono_class_value_size (arg_class, NULL); + int esize = mono_class_value_size (mono_class_from_mono_type_internal (etype), NULL); + int elems = size / esize; + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, args [1]->dreg, elems); + MONO_EMIT_NEW_COND_EXC (cfg, GE_UN, "ArgumentOutOfRangeException"); + int insert_op = type_to_xinsert_op (arg0_type); + MonoInst *ins = emit_simd_ins (cfg, klass, insert_op, args [0]->dreg, args [2]->dreg); + ins->sreg3 = args [1]->dreg; + ins->inst_c1 = arg0_type; + return ins; + } + case SN_WithLower: + case SN_WithUpper: { + MonoType *arg_type = get_vector_t_elem_type (fsig->params [0]); + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (arg_type)) + return NULL; + int op = id == SN_GetLower ? OP_XINSERT_LOWER : OP_XINSERT_UPPER; + return emit_simd_ins_for_sig (cfg, klass, op, 0, arg0_type, fsig, args); + } + default: + break; + } + + return NULL; +} + +static guint16 vector64_vector128_t_methods [] = { + SN_Equals, + SN_get_AllBitsSet, + SN_get_Count, + SN_get_IsSupported, + SN_get_Zero, +}; + +static MonoInst* +emit_vector64_vector128_t (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + int id = lookup_intrins (vector64_vector128_t_methods, sizeof (vector64_vector128_t_methods), cmethod); + if (id == -1) + return NULL; + + MonoClass *klass = cmethod->klass; + MonoType *type = m_class_get_byval_arg (klass); + MonoType *etype = mono_class_get_context (klass)->class_inst->type_argv [0]; + int size = mono_class_value_size (klass, NULL); + int esize = mono_class_value_size (mono_class_from_mono_type_internal (etype), NULL); + g_assert (size > 0); + g_assert (esize > 0); + int len = size / esize; + + if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (etype)) + return NULL; + + if (cfg->verbose_level > 1) { + char *name = mono_method_full_name (cmethod, TRUE); + printf (" SIMD intrinsic %s\n", name); + g_free (name); + } + + switch (id) { + case SN_get_IsSupported: { + MonoInst *ins = NULL; + EMIT_NEW_ICONST (cfg, ins, 1); + return ins; + } + default: + break; + } + + if (!COMPILE_LLVM (cfg)) + return NULL; + + switch (id) { + case SN_get_Count: { + MonoInst *ins = NULL; + if (!(fsig->param_count == 0 && fsig->ret->type == MONO_TYPE_I4)) + break; + EMIT_NEW_ICONST (cfg, ins, len); + return ins; + } + case SN_get_Zero: { + return emit_simd_ins (cfg, klass, OP_XZERO, -1, -1); + } + case SN_get_AllBitsSet: { + MonoInst *ins = emit_simd_ins (cfg, klass, OP_XZERO, -1, -1); + return emit_xcompare (cfg, klass, etype->type, ins, ins); + } + case SN_Equals: { + if (fsig->param_count == 1 && fsig->ret->type == MONO_TYPE_BOOLEAN && mono_metadata_type_equal (fsig->params [0], type)) { + int sreg1 = load_simd_vreg (cfg, cmethod, args [0], NULL); + return emit_simd_ins (cfg, klass, OP_XEQUAL, sreg1, args [1]->dreg); + } + break; + } default: break; } @@ -2186,7 +2366,7 @@ emit_x86_intrinsics ( return emit_simd_ins (cfg, klass, op, args [0]->dreg, args[0]->dreg); else if (fsig->param_count == 2) return emit_simd_ins (cfg, klass, op, args [0]->dreg, args[1]->dreg); - else + else g_assert_not_reached (); break; } @@ -2342,15 +2522,15 @@ emit_x86_intrinsics ( IntrinsicId op = (IntrinsicId)0; switch (arg0_type) { case MONO_TYPE_I2: - case MONO_TYPE_U2: + case MONO_TYPE_U2: op = is_imm ? INTRINS_SSE_PSRLI_W : INTRINS_SSE_PSRL_W; break; case MONO_TYPE_I4: - case MONO_TYPE_U4: + case MONO_TYPE_U4: op = is_imm ? INTRINS_SSE_PSRLI_D : INTRINS_SSE_PSRL_D; break; case MONO_TYPE_I8: - case MONO_TYPE_U8: + case MONO_TYPE_U8: op = is_imm ? INTRINS_SSE_PSRLI_Q : INTRINS_SSE_PSRL_Q; break; default: g_assert_not_reached (); break; @@ -2362,11 +2542,11 @@ emit_x86_intrinsics ( IntrinsicId op = (IntrinsicId)0; switch (arg0_type) { case MONO_TYPE_I2: - case MONO_TYPE_U2: + case MONO_TYPE_U2: op = is_imm ? INTRINS_SSE_PSRAI_W : INTRINS_SSE_PSRA_W; break; case MONO_TYPE_I4: - case MONO_TYPE_U4: + case MONO_TYPE_U4: op = is_imm ? INTRINS_SSE_PSRAI_D : INTRINS_SSE_PSRA_D; break; default: g_assert_not_reached (); break; @@ -2378,15 +2558,15 @@ emit_x86_intrinsics ( IntrinsicId op = (IntrinsicId)0; switch (arg0_type) { case MONO_TYPE_I2: - case MONO_TYPE_U2: + case MONO_TYPE_U2: op = is_imm ? INTRINS_SSE_PSLLI_W : INTRINS_SSE_PSLL_W; break; case MONO_TYPE_I4: - case MONO_TYPE_U4: + case MONO_TYPE_U4: op = is_imm ? INTRINS_SSE_PSLLI_D : INTRINS_SSE_PSLL_D; break; case MONO_TYPE_I8: - case MONO_TYPE_U8: + case MONO_TYPE_U8: op = is_imm ? INTRINS_SSE_PSLLI_Q : INTRINS_SSE_PSLL_Q; break; default: g_assert_not_reached (); break; @@ -2564,8 +2744,8 @@ emit_x86_intrinsics ( switch (id) { case SN_Crc32: { MonoTypeEnum arg1_type = get_underlying_type (fsig->params [1]); - return emit_simd_ins_for_sig (cfg, klass, - arg1_type == MONO_TYPE_U8 ? OP_SSE42_CRC64 : OP_SSE42_CRC32, + return emit_simd_ins_for_sig (cfg, klass, + arg1_type == MONO_TYPE_U8 ? OP_SSE42_CRC64 : OP_SSE42_CRC32, arg1_type, arg0_type, fsig, args); } default: @@ -2755,55 +2935,6 @@ emit_x86_intrinsics ( return NULL; } -static guint16 vector_128_t_methods [] = { - SN_get_Count, - SN_get_Zero, -}; - -static MonoInst* -emit_vector128_t (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) -{ - MonoInst *ins; - MonoType *type, *etype; - MonoClass *klass; - int size, len, id; - - id = lookup_intrins (vector_128_t_methods, sizeof (vector_128_t_methods), cmethod); - if (id == -1) - return NULL; - - klass = cmethod->klass; - type = m_class_get_byval_arg (klass); - etype = mono_class_get_context (klass)->class_inst->type_argv [0]; - size = mono_class_value_size (mono_class_from_mono_type_internal (etype), NULL); - g_assert (size); - len = 16 / size; - - if (!MONO_TYPE_IS_PRIMITIVE (etype) || etype->type == MONO_TYPE_CHAR || etype->type == MONO_TYPE_BOOLEAN || etype->type == MONO_TYPE_I || etype->type == MONO_TYPE_U) - return NULL; - - if (cfg->verbose_level > 1) { - char *name = mono_method_full_name (cmethod, TRUE); - printf (" SIMD intrinsic %s\n", name); - g_free (name); - } - - switch (id) { - case SN_get_Count: - if (!(fsig->param_count == 0 && fsig->ret->type == MONO_TYPE_I4)) - break; - EMIT_NEW_ICONST (cfg, ins, len); - return ins; - case SN_get_Zero: { - return emit_simd_ins (cfg, klass, OP_XZERO, -1, -1); - } - default: - break; - } - - return NULL; -} - static guint16 vector_256_t_methods [] = { SN_get_Count, }; @@ -2860,8 +2991,6 @@ emit_amd64_intrinsics (const char *class_ns, const char *class_name, MonoCompile } if (!strcmp (class_ns, "System.Runtime.Intrinsics")) { - if (!strcmp (class_name, "Vector128`1")) - return emit_vector128_t (cfg, cmethod, fsig, args); if (!strcmp (class_name, "Vector256`1")) return emit_vector256_t (cfg, cmethod, fsig, args); } @@ -2933,6 +3062,11 @@ mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (!strcmp (class_name, "Vector128") || !strcmp (class_name, "Vector64")) return emit_sri_vector (cfg, cmethod, fsig, args); } + + if (!strcmp (class_ns, "System.Runtime.Intrinsics")) { + if (!strcmp (class_name, "Vector64`1") || !strcmp (class_name, "Vector128`1")) + return emit_vector64_vector128_t (cfg, cmethod, fsig, args); + } #endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) return emit_simd_intrinsics (class_ns, class_name, cfg, cmethod, fsig, args); diff --git a/src/mono/mono/mini/simd-methods.h b/src/mono/mono/mini/simd-methods.h index 4abd0ebb7e6..d223b2ba5ae 100644 --- a/src/mono/mono/mini/simd-methods.h +++ b/src/mono/mono/mini/simd-methods.h @@ -39,7 +39,8 @@ METHOD(ConvertToSingle) METHOD(ConvertToDouble) METHOD(Narrow) METHOD(Widen) -// Vector128 +// Vector64, Vector128, Vector256 +METHOD(As) METHOD(AsByte) METHOD(AsDouble) METHOD(AsInt16) @@ -50,8 +51,25 @@ METHOD(AsSingle) METHOD(AsUInt16) METHOD(AsUInt32) METHOD(AsUInt64) +METHOD(AsVector128) +METHOD(AsVector2) +METHOD(AsVector256) +METHOD(AsVector3) +METHOD(AsVector4) METHOD(Create) +METHOD(CreateScalar) METHOD(CreateScalarUnsafe) +METHOD(GetElement) +METHOD(GetLower) +METHOD(GetUpper) +METHOD(ToScalar) +METHOD(ToVector128) +METHOD(ToVector128Unsafe) +METHOD(ToVector256) +METHOD(ToVector256Unsafe) +METHOD(WithElement) +METHOD(WithLower) +METHOD(WithUpper) // Bmi1 METHOD(AndNot) METHOD(BitFieldExtract) From a291fabdadee05b44aea09cc4bb11bef5a5dc305 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Thu, 1 Jul 2021 13:11:09 -0400 Subject: [PATCH 229/926] Preserve necessary type and method (#54932) * Preserve necessary type and method * Change the way of getting the type * Inline the constants * Enable NonValidated_ValidAndInvalidValues_DictionaryMembersWork * Enable TransformStrStrResolver3 --- eng/testing/ILLink.Descriptor.xunit.xml | 5 +++++ .../tests/UnitTests/Headers/HttpHeadersTest.cs | 1 - .../Xslt/XslCompiledTransformApi/XslCompiledTransform.cs | 1 - .../InteropServices/IDispatchImplAttributeTests.cs | 8 ++------ .../CollectionTests/CollectionTests.Generic.Write.cs | 1 - 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/eng/testing/ILLink.Descriptor.xunit.xml b/eng/testing/ILLink.Descriptor.xunit.xml index 8e6986d8a51..9866068fd7a 100644 --- a/eng/testing/ILLink.Descriptor.xunit.xml +++ b/eng/testing/ILLink.Descriptor.xunit.xml @@ -15,5 +15,10 @@ + + + + + diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs index 2a25ea71961..2486e8bc45b 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs @@ -1501,7 +1501,6 @@ namespace System.Net.Http.Tests } } - [ActiveIssue("https://github.com/dotnet/runtime/issues/53647", TestPlatforms.Browser)] [Fact] public void NonValidated_ValidAndInvalidValues_DictionaryMembersWork() { diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs index 878eb87aad9..54d1fd9a7f8 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs @@ -3002,7 +3002,6 @@ namespace System.Xml.Tests } //[Variation("Pass XmlUrlResolver, load style sheet with document function, should resolve during transform", Param = "xmlResolver_document_function.txt")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51911", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] [InlineData("xmlResolver_document_function.txt", XslInputType.Reader, ReaderType.XmlValidatingReader)] [InlineData("xmlResolver_document_function.txt", XslInputType.URI, ReaderType.XmlValidatingReader)] [InlineData("xmlResolver_document_function.txt", XslInputType.Navigator, ReaderType.XmlValidatingReader)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/IDispatchImplAttributeTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/IDispatchImplAttributeTests.cs index 59274bd8a29..ac192126f64 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/IDispatchImplAttributeTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/IDispatchImplAttributeTests.cs @@ -8,18 +8,14 @@ namespace System.Runtime.InteropServices.Tests { public class IDispatchImplAttributeTests { - private const string TypeName = "System.Runtime.InteropServices.IDispatchImplAttribute"; - private const string ValueName = "Value"; - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50717", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] [InlineData(-1)] [InlineData(0)] [InlineData(2)] public void Ctor_ImplTypeShort(short implType) { - Type type = typeof(HandleCollector).Assembly.GetType(TypeName); - PropertyInfo valueProperty = type.GetProperty(ValueName); + Type type = Type.GetType("System.Runtime.InteropServices.IDispatchImplAttribute, System.Runtime.InteropServices"); + PropertyInfo valueProperty = type.GetProperty("Value"); Assert.NotNull(type); Assert.NotNull(valueProperty); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs index 33fc759ae82..e6963f2ca07 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs @@ -633,7 +633,6 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50721", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] public static void WriteHashSetTOfHashSetT() { HashSet> input = new HashSet>(new List> From 7298a534a00d21ab275576b89f2f29126bbc83b1 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 1 Jul 2021 13:16:30 -0400 Subject: [PATCH 230/926] Fix differences from S.S.C.Primitives autogenerated ref file. --- .../ref/System.Security.Cryptography.Primitives.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 49510d6484c..143f099f485 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -76,7 +76,7 @@ namespace System.Security.Cryptography public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } public void Clear() { } - public override void CopyTo(System.IO.Stream destination, int bufferSize) { throw null; } + public override void CopyTo(System.IO.Stream destination, int bufferSize) { } public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override void Dispose(bool disposing) { } public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } @@ -88,13 +88,13 @@ namespace System.Security.Cryptography public System.Threading.Tasks.ValueTask FlushFinalBlockAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override int Read(byte[] buffer, int offset, int count) { throw null; } public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } - public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override int ReadByte() { throw null; } public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } - public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override void WriteByte(byte value) { } } public enum CryptoStreamMode From eeadfdb2f57e607241b9b09ada1ef08717e618f6 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 1 Jul 2021 10:31:58 -0700 Subject: [PATCH 231/926] Mark vars as do not enreg earlier in minopts. (#54998) * Improve morphblock logic. * change lclVars. * Extract compEnregLocals * same for args/locals --- src/coreclr/jit/compiler.cpp | 4 +- src/coreclr/jit/compiler.h | 5 ++ src/coreclr/jit/compiler.hpp | 5 ++ src/coreclr/jit/decomposelongs.cpp | 2 +- src/coreclr/jit/lclvars.cpp | 20 +++--- src/coreclr/jit/lsra.cpp | 4 +- src/coreclr/jit/morph.cpp | 1 + src/coreclr/jit/morphblock.cpp | 104 ++++++++++++++++------------- 8 files changed, 83 insertions(+), 62 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 23aed4e317d..6e691f73f77 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2855,8 +2855,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags) setUsesSIMDTypes(false); #endif // FEATURE_SIMD - lvaEnregEHVars = (((opts.compFlags & CLFLG_REGVAR) != 0) && JitConfig.EnableEHWriteThru()); - lvaEnregMultiRegVars = (((opts.compFlags & CLFLG_REGVAR) != 0) && JitConfig.EnableMultiRegLocals()); + lvaEnregEHVars = (compEnregLocals() && JitConfig.EnableEHWriteThru()); + lvaEnregMultiRegVars = (compEnregLocals() && JitConfig.EnableMultiRegLocals()); if (compIsForImportOnly()) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index fbafd6e9a84..9086613da28 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9778,6 +9778,11 @@ public: #endif // FEATURE_MULTIREG_RET } + bool compEnregLocals() + { + return ((opts.compFlags & CLFLG_REGVAR) != 0); + } + bool compEnregStructLocals() { return (JitConfig.JitEnregStructLocals() != 0); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index d05bfc6bbeb..0d6336d089b 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1585,6 +1585,11 @@ inline unsigned Compiler::lvaGrabTemp(bool shortLifetime DEBUGARG(const char* re lvaTable[tempNum].lvIsTemp = shortLifetime; lvaTable[tempNum].lvOnFrame = true; + if (!compEnregLocals()) + { + lvaSetVarDoNotEnregister(tempNum DEBUGARG(Compiler::DNER_NoRegVars)); + } + // If we've started normal ref counting, bump the ref count of this // local, as we no longer do any incremental counting, and we presume // this new local will be referenced. diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index 39a4d01acc4..1ee7d142858 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -2076,7 +2076,7 @@ genTreeOps DecomposeLongs::GetLoOper(genTreeOps oper) // void DecomposeLongs::PromoteLongVars() { - if ((m_compiler->opts.compFlags & CLFLG_REGVAR) == 0) + if (!m_compiler->compEnregLocals()) { return; } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index eec7450e656..852c2586caa 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1431,6 +1431,11 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, } #endif + if (!compEnregLocals()) + { + lvaSetVarDoNotEnregister(varNum DEBUGARG(Compiler::DNER_NoRegVars)); + } + #ifdef DEBUG varDsc->SetStackOffset(BAD_STK_OFFS); #endif @@ -2622,6 +2627,7 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister case DNER_NoRegVars: JITDUMP("opts.compFlags & CLFLG_REGVAR is not set\n"); assert((opts.compFlags & CLFLG_REGVAR) == 0); + assert(!compEnregLocals()); break; case DNER_MinOptsGC: JITDUMP("It is a GC Ref and we are compiling MinOpts\n"); @@ -3467,7 +3473,7 @@ void Compiler::lvaSortByRefCount() assert(varDsc->lvType != TYP_STRUCT || varDsc->lvDoNotEnregister); // For structs, should have set this when we set lvAddrExposed. } - else if (varTypeIsStruct(varDsc)) + if (varTypeIsStruct(varDsc)) { // Promoted structs will never be considered for enregistration anyway, // and the DoNotEnregister flag was used to indicate whether promotion was @@ -3481,28 +3487,24 @@ void Compiler::lvaSortByRefCount() lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); } } - else if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT)) + if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT)) { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_DepField)); } - else if (varDsc->lvPinned) + if (varDsc->lvPinned) { varDsc->lvTracked = 0; #ifdef JIT32_GCENCODER lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef)); #endif } - else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet())) + if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet())) { varDsc->lvTracked = 0; lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_MinOptsGC)); } - else if ((opts.compFlags & CLFLG_REGVAR) == 0) - { - lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_NoRegVars)); - } #if defined(JIT32_GCENCODER) && defined(FEATURE_EH_FUNCLETS) - else if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0) + if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0) { // For x86/Linux, we need to track "this". // However we cannot have it in tracked variables, so we set "this" pointer always untracked diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 86d037e12ac..a2bb138ad0c 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -700,7 +700,7 @@ LinearScan::LinearScan(Compiler* theCompiler) // after the first liveness analysis - either by optimizations or by Lowering, and the tracked // set won't be recomputed until after Lowering (and this constructor is called prior to Lowering), // so we don't want to check that yet. - enregisterLocalVars = ((compiler->opts.compFlags & CLFLG_REGVAR) != 0); + enregisterLocalVars = compiler->compEnregLocals(); #ifdef TARGET_ARM64 availableIntRegs = (RBM_ALLINT & ~(RBM_PR | RBM_FP | RBM_LR) & ~compiler->codeGen->regSet.rsMaskResvd); #else @@ -1448,7 +1448,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc) { return false; } - assert((compiler->opts.compFlags & CLFLG_REGVAR) != 0); + assert(compiler->compEnregLocals()); if (!varDsc->lvTracked) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 64e852397a8..4ec1328f878 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2064,6 +2064,7 @@ GenTree* Compiler::fgMakeTmpArgNode(fgArgTabEntry* curArgTabEntry) { arg->ChangeOper(GT_LCL_FLD); arg->gtType = type; + lvaSetVarDoNotEnregister(tmpVarNum DEBUGARG(Compiler::DNER_LocalField)); } else { diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index 5e8d47a671b..515ce6fa4ab 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -137,7 +137,11 @@ GenTree* MorphInitBlockHelper::Morph() if (m_transformationDecision == BlockTransformation::Undefined) { - GenTree* oneAsgTree = m_comp->fgMorphOneAsgBlockOp(m_asg); + GenTree* oneAsgTree = nullptr; + if (m_dst != m_dstLclNode) + { + oneAsgTree = m_comp->fgMorphOneAsgBlockOp(m_asg); + } if (oneAsgTree != nullptr) { assert((m_asg == oneAsgTree) && "fgMorphOneAsgBlock must return the incoming tree."); @@ -362,22 +366,6 @@ void MorphInitBlockHelper::MorphStructCases() m_result = newTree; } } - - if (m_transformationDecision != BlockTransformation::FieldByField) - { - if (m_dst != m_dstLclNode) - { - // If we access the dst as a whole but not directly, for example, with OBJ(ADDR(LCL_VAR)) - // then set doNotEnreg. - // TODO-1stClassStructs: remove it when we can represent narowing struct cast - // without taking address of the lcl. - m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); - } - else if (m_dstVarDsc->lvPromoted) - { - m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); - } - } } if (m_transformationDecision == BlockTransformation::Undefined) @@ -403,6 +391,22 @@ void MorphInitBlockHelper::MorphStructCases() m_result->AsOp()->gtOp2 = m_src; } #endif // FEATURE_SIMD + + if (m_dstVarDsc != nullptr) + { + if (m_dst != m_dstLclNode) + { + // If we access the dst as a whole but not directly, for example, with OBJ(ADDR(LCL_VAR)) + // then set doNotEnreg. + // TODO-1stClassStructs: remove it when we can represent narowing struct cast + // without taking address of the lcl. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_dstVarDsc->lvPromoted) + { + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + } } } @@ -981,35 +985,6 @@ void MorphCopyBlockHelper::MorphStructCases() JITDUMP(requiresCopyBlock ? " this requires a CopyBlock.\n" : " using field by field assignments.\n"); - // Mark the dest/src structs as DoNotEnreg when they are not being fully referenced as the same type. - // - if (!m_dstDoFldAsg && (m_dstVarDsc != nullptr) && !m_dstSingleLclVarAsg) - { - if (!m_dstVarDsc->lvRegStruct || (m_dstVarDsc->lvType != m_dst->TypeGet())) - { - if (!m_dst->IsMultiRegLclVar() || (m_blockSize != m_dstVarDsc->lvExactSize) || - (m_dstVarDsc->lvCustomLayout && m_dstVarDsc->lvContainsHoles)) - { - // Mark it as DoNotEnregister. - m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); - } - else if (m_dst->IsMultiRegLclVar()) - { - // Handle this as lvIsMultiRegRet; this signals to SSA that it can't consider these fields - // SSA candidates (we don't have a way to represent multiple SSANums on MultiRegLclVar nodes). - m_dstVarDsc->lvIsMultiRegRet = true; - } - } - } - - if (!m_srcDoFldAsg && (m_srcVarDsc != nullptr) && !m_srcSingleLclVarAsg) - { - if (!m_srcVarDsc->lvRegStruct || (m_srcVarDsc->lvType != m_dst->TypeGet())) - { - m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); - } - } - if (requiresCopyBlock) { const var_types asgType = m_dst->TypeGet(); @@ -1031,6 +1006,40 @@ void MorphCopyBlockHelper::MorphStructCases() m_result = CopyFieldByField(); m_transformationDecision = BlockTransformation::FieldByField; } + + // Mark the dest/src structs as DoNotEnreg when they are not being fully referenced as the same type. + // + if (!m_dstDoFldAsg && (m_dstVarDsc != nullptr) && !m_dstSingleLclVarAsg) + { + if (m_dst != m_dstLclNode) + { + // Mark it as DoNotEnregister. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_dstVarDsc->lvPromoted) + { + // Mark it as DoNotEnregister. + m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_dst->IsMultiRegLclVar()) + { + // Handle this as lvIsMultiRegRet; this signals to SSA that it can't consider these fields + // SSA candidates (we don't have a way to represent multiple SSANums on MultiRegLclVar nodes). + m_dstVarDsc->lvIsMultiRegRet = true; + } + } + + if (!m_srcDoFldAsg && (m_srcVarDsc != nullptr) && !m_srcSingleLclVarAsg) + { + if (m_src != m_srcLclNode) + { + m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + else if (m_srcVarDsc->lvPromoted) + { + m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } + } } //------------------------------------------------------------------------ @@ -1156,8 +1165,6 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() // We will *not* consider this to define the local, but rather have each individual field assign // be a definition. addrSpillOp->gtFlags &= ~(GTF_LIVENESS_MASK); - assert(m_comp->lvaGetPromotionType(addrSpillOp->AsLclVarCommon()->GetLclNum()) != - Compiler::PROMOTION_TYPE_INDEPENDENT); addrSpillIsStackDest = true; // addrSpill represents the address of LclVar[varNum] in our // local stack frame } @@ -1423,6 +1430,7 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() m_srcLclNode->ChangeOper(GT_LCL_FLD); m_srcLclNode->gtType = destType; m_srcLclNode->AsLclFld()->SetFieldSeq(curFieldSeq); + m_comp->lvaSetVarDoNotEnregister(m_srcLclNum DEBUGARG(Compiler::DNER_LocalField)); srcFld = m_srcLclNode; done = true; } From be80f675c7aa1fc895c053f80229d2591c69e5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 1 Jul 2021 14:34:47 -0400 Subject: [PATCH 232/926] [samples] Include parent Directory.Build.targets in wasm/samples (#54972) Fixes building the samples against the intree ref assemblies --- src/mono/sample/wasm/Directory.Build.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets index e5c54692567..492c61cf048 100644 --- a/src/mono/sample/wasm/Directory.Build.targets +++ b/src/mono/sample/wasm/Directory.Build.targets @@ -1,4 +1,5 @@ + From 04dac7b0fede29d44f896c5fd793754f83974175 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 1 Jul 2021 11:55:05 -0700 Subject: [PATCH 233/926] Allow restricting cultures creation with any arbitrary names in Globalization Invariant Mode (#54247) Co-authored-by: Eric Erhardt --- docs/workflow/trimming/feature-switches.md | 1 + .../tests/CultureInfo/GetCultureInfo.cs | 76 +++++++++++++++++++ .../tests/Invariant/InvariantMode.cs | 66 ++++++++++------ .../Invariant/runtimeconfig.template.json | 3 +- .../ILLink/ILLink.Substitutions.Shared.xml | 1 + .../src/Resources/Strings.resx | 3 + .../src/System/AppContextConfigHelper.cs | 7 +- .../src/System/Globalization/CultureData.cs | 5 +- .../src/System/Globalization/CultureInfo.cs | 10 ++- .../System/Globalization/GlobalizationMode.cs | 4 +- .../ManifestBasedResourceGroveler.cs | 2 +- .../src/System/TimeZoneInfo.Win32.cs | 6 +- .../InvariantGlobalizationTrue.cs | 27 ++++--- .../System.Runtime.TrimmingTests.proj | 2 +- .../InvariantGlobalizationTests.cs | 12 ++- 15 files changed, 168 insertions(+), 57 deletions(-) diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md index 9836a8375db..ac2ca01a243 100644 --- a/docs/workflow/trimming/feature-switches.md +++ b/docs/workflow/trimming/feature-switches.md @@ -13,6 +13,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif | EnableUnsafeBinaryFormatterSerialization | System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization | BinaryFormatter serialization support is trimmed when set to false | | EventSourceSupport | System.Diagnostics.Tracing.EventSource.IsSupported | Any EventSource related code or logic is trimmed when set to false | | InvariantGlobalization | System.Globalization.Invariant | All globalization specific code and data is trimmed when set to true | +| PredefinedCulturesOnly | System.Globalization.PredefinedCulturesOnly | Don't allow creating a culture for which the platform does not have data | | UseSystemResourceKeys | System.Resources.UseSystemResourceKeys | Any localizable resources for system assemblies is trimmed when set to true | | HttpActivityPropagationSupport | System.Net.Http.EnableActivityPropagation | Any dependency related to diagnostics support for System.Net.Http is trimmed when set to false | | UseNativeHttpHandler | System.Net.Http.UseNativeHttpHandler | HttpClient uses by default platform native implementation of HttpMessageHandler if set to true. | diff --git a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs index 0b4bdd57089..e6399bb5cc6 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.DotNet.RemoteExecutor; using System.Diagnostics; +using System.Collections.Concurrent; using Xunit; namespace System.Globalization.Tests @@ -139,5 +140,80 @@ namespace System.Globalization.Tests } }, cultureName, predefinedCulturesOnlyEnvVar, new RemoteInvokeOptions { StartInfo = psi }).Dispose(); } + + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(true, true, false)] + [InlineData(true, true, true)] + [InlineData(true, false, true)] + [InlineData(false, true, true)] + [InlineData(false, true, false)] + [InlineData(false, false, true)] + public void TestAllowInvariantCultureOnly(bool enableInvariant, bool predefinedCulturesOnly, bool declarePredefinedCulturesOnly) + { + var psi = new ProcessStartInfo(); + psi.Environment.Clear(); + + if (enableInvariant) + { + psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "true"); + } + + if (declarePredefinedCulturesOnly) + { + psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", predefinedCulturesOnly ? "true" : "false"); + } + + bool restricted = enableInvariant && (declarePredefinedCulturesOnly ? predefinedCulturesOnly : true); + + RemoteExecutor.Invoke((invariantEnabled, isRestricted) => + { + bool restrictedMode = bool.Parse(isRestricted); + + // First ensure we can create the current cultures regardless of the mode we are in + Assert.NotNull(CultureInfo.CurrentCulture); + Assert.NotNull(CultureInfo.CurrentUICulture); + + // Invariant culture should be valid all the time + Assert.Equal("", new CultureInfo("").Name); + Assert.Equal("", CultureInfo.InvariantCulture.Name); + + if (restrictedMode) + { + Assert.Equal("", CultureInfo.CurrentCulture.Name); + Assert.Equal("", CultureInfo.CurrentUICulture.Name); + + // Throwing exception is testing accessing the resources in this restricted mode. + // We should retrieve the resources from the neutral resources in the main assemblies. + AssertExtensions.Throws(() => new CultureInfo("en-US")); + AssertExtensions.Throws(() => new CultureInfo("en")); + + AssertExtensions.Throws(() => new CultureInfo("ja-JP")); + AssertExtensions.Throws(() => new CultureInfo("es")); + + // Test throwing exceptions from non-core assemblies. + Exception exception = Record.Exception(() => new ConcurrentBag(null)); + Assert.NotNull(exception); + Assert.IsType(exception); + Assert.Equal("collection", (exception as ArgumentNullException).ParamName); + Assert.Equal("The collection argument is null. (Parameter 'collection')", exception.Message); + } + else + { + Assert.Equal("en-US", new CultureInfo("en-US").Name); + Assert.Equal("ja-JP", new CultureInfo("ja-JP").Name); + Assert.Equal("en", new CultureInfo("en").Name); + Assert.Equal("es", new CultureInfo("es").Name); + } + + // Ensure the Invariant Mode functionality still work + if (bool.Parse(invariantEnabled)) + { + Assert.True(CultureInfo.CurrentCulture.Calendar is GregorianCalendar); + Assert.True("abcd".Equals("ABCD", StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal("Invariant Language (Invariant Country)", CultureInfo.CurrentCulture.NativeName); + } + + }, enableInvariant.ToString(), restricted.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + } } } diff --git a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs index 94ec2003e31..a2cd0baf70f 100644 --- a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs +++ b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Reflection; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; @@ -13,6 +14,23 @@ namespace System.Globalization.Tests { public class InvariantModeTests { + private static bool PredefinedCulturesOnlyIsDisabled { get; } = !PredefinedCulturesOnly(); + private static bool PredefinedCulturesOnly() + { + bool ret; + + try + { + ret = (bool) typeof(object).Assembly.GetType("System.Globalization.GlobalizationMode").GetProperty("PredefinedCulturesOnly", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); + } + catch + { + ret = false; + } + + return ret; + } + public static IEnumerable Cultures_TestData() { yield return new object[] { "en-US" }; @@ -490,13 +508,13 @@ namespace System.Globalization.Tests yield return new object[] { "xn--de-jg4avhby1noc0d", 0, 21, "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" }; } - [Fact] + [ConditionalFact(nameof(PredefinedCulturesOnlyIsDisabled))] public static void IcuShouldNotBeLoaded() { Assert.False(PlatformDetection.IsIcuGlobalization); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(Cultures_TestData))] public void TestCultureData(string cultureName) { @@ -636,7 +654,7 @@ namespace System.Globalization.Tests Assert.True(cultureName.Equals(ci.CompareInfo.Name, StringComparison.OrdinalIgnoreCase)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(Cultures_TestData))] public void SetCultureData(string cultureName) { @@ -652,13 +670,13 @@ namespace System.Globalization.Tests Assert.Throws(() => ci.DateTimeFormat.Calendar = new TaiwanCalendar()); } - [Fact] + [ConditionalFact(nameof(PredefinedCulturesOnlyIsDisabled))] public void TestEnum() { Assert.Equal(new CultureInfo[1] { CultureInfo.InvariantCulture }, CultureInfo.GetCultures(CultureTypes.AllCultures)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(Cultures_TestData))] public void TestSortVersion(string cultureName) { @@ -670,7 +688,7 @@ namespace System.Globalization.Tests Assert.Equal(version, new CultureInfo(cultureName).CompareInfo.Version); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData(0, 0)] [InlineData(1, 2)] [InlineData(100_000, 200_000)] @@ -683,7 +701,7 @@ namespace System.Globalization.Tests Assert.Equal(expectedSortKeyLength, CultureInfo.InvariantCulture.CompareInfo.GetSortKeyLength(dummySpan)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData(0x4000_0000)] [InlineData(int.MaxValue)] public unsafe void TestGetSortKeyLength_OverlongArgument(int inputLength) @@ -698,7 +716,7 @@ namespace System.Globalization.Tests }); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData("Hello", CompareOptions.None, "Hello")] [InlineData("Hello", CompareOptions.IgnoreWidth, "Hello")] [InlineData("Hello", CompareOptions.IgnoreCase, "HELLO")] @@ -741,7 +759,7 @@ namespace System.Globalization.Tests } } - [Fact] + [ConditionalFact(nameof(PredefinedCulturesOnlyIsDisabled))] public void TestSortKey_ZeroWeightCodePoints() { // In the invariant globalization mode, there's no such thing as a zero-weight code point, @@ -753,7 +771,7 @@ namespace System.Globalization.Tests Assert.NotEqual(0, SortKey.Compare(sortKeyForEmptyString, sortKeyForZeroWidthJoiner)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData("", "", 0)] [InlineData("", "not-empty", -1)] [InlineData("not-empty", "", 1)] @@ -794,7 +812,7 @@ namespace System.Globalization.Tests return sc; } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(IndexOf_TestData))] public void TestIndexOf(string source, string value, int startIndex, int count, CompareOptions options, int result) { @@ -841,7 +859,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(LastIndexOf_TestData))] public void TestLastIndexOf(string source, string value, int startIndex, int count, CompareOptions options, int result) { @@ -901,7 +919,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(IsPrefix_TestData))] public void TestIsPrefix(string source, string value, CompareOptions options, bool result) { @@ -936,7 +954,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(IsSuffix_TestData))] public void TestIsSuffix(string source, string value, CompareOptions options, bool result) { @@ -971,7 +989,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData("", false)] [InlineData('x', true)] [InlineData('\ud800', true)] // standalone high surrogate @@ -988,7 +1006,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(Compare_TestData))] public void TestCompare(string source, string value, CompareOptions options, int result) { @@ -1019,7 +1037,7 @@ namespace System.Globalization.Tests } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(ToLower_TestData))] public void TestToLower(string upper, string lower, bool result) { @@ -1030,7 +1048,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(ToUpper_TestData))] public void TestToUpper(string lower, string upper, bool result) { @@ -1041,7 +1059,7 @@ namespace System.Globalization.Tests } } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData("", NormalizationForm.FormC)] [InlineData("\uFB01", NormalizationForm.FormC)] [InlineData("\uFB01", NormalizationForm.FormD)] @@ -1063,7 +1081,7 @@ namespace System.Globalization.Tests Assert.Equal(s, s.Normalize(form)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(GetAscii_TestData))] public void GetAscii(string unicode, int index, int count, string expected) { @@ -1078,7 +1096,7 @@ namespace System.Globalization.Tests Assert.Equal(expected, new IdnMapping().GetAscii(unicode, index, count)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [MemberData(nameof(GetUnicode_TestData))] public void GetUnicode(string ascii, int index, int count, string expected) { @@ -1093,7 +1111,7 @@ namespace System.Globalization.Tests Assert.Equal(expected, new IdnMapping().GetUnicode(ascii, index, count)); } - [Fact] + [ConditionalFact(nameof(PredefinedCulturesOnlyIsDisabled))] public void TestHashing() { StringComparer cultureComparer = StringComparer.Create(CultureInfo.GetCultureInfo("tr-TR"), true); @@ -1102,7 +1120,7 @@ namespace System.Globalization.Tests Assert.Equal(ordinalComparer.GetHashCode(turkishString), cultureComparer.GetHashCode(turkishString)); } - [Theory] + [ConditionalTheory(nameof(PredefinedCulturesOnlyIsDisabled))] [InlineData('a', 'A', 'a')] [InlineData('A', 'A', 'a')] [InlineData('i', 'I', 'i')] // to verify that we don't special-case the Turkish I in the invariant globalization mode @@ -1121,7 +1139,7 @@ namespace System.Globalization.Tests Assert.Equal(expectedToLower, Rune.ToLower(originalRune, CultureInfo.GetCultureInfo("tr-TR")).Value); } - [Fact] + [ConditionalFact(nameof(PredefinedCulturesOnlyIsDisabled))] public void TestGetCultureInfo_PredefinedOnly_ReturnsSame() { Assert.Equal(CultureInfo.GetCultureInfo("en-US"), CultureInfo.GetCultureInfo("en-US", predefinedOnly: true)); diff --git a/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json b/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json index dd404e151b8..076d8498455 100644 --- a/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json +++ b/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json @@ -1,5 +1,6 @@ { "configProperties": { - "System.Globalization.Invariant": true + "System.Globalization.Invariant": true, + "System.Globalization.PredefinedCulturesOnly": false } } \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index ba2db281981..f7ca754bb6a 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -10,6 +10,7 @@ to be trimmed when Invariant=true, and allows for the Settings static cctor (on Unix) to be preserved when Invariant=false. --> + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 7c80c23f632..1669ffb9bbb 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -957,6 +957,9 @@ Culture is not supported. + + Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. + Resolved assembly's simple name should be the same as of the requested assembly. diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs b/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs index 331ef281bb3..712f8973ca6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs @@ -10,15 +10,12 @@ namespace System internal static bool GetBooleanConfig(string configName, bool defaultValue) => AppContext.TryGetSwitch(configName, out bool value) ? value : defaultValue; - internal static bool GetBooleanConfig(string switchName, string envVariable) + internal static bool GetBooleanConfig(string switchName, string envVariable, bool defaultValue = false) { if (!AppContext.TryGetSwitch(switchName, out bool ret)) { string? switchValue = Environment.GetEnvironmentVariable(envVariable); - if (switchValue != null) - { - ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1"); - } + ret = switchValue != null ? (bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1")) : defaultValue; } return ret; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index 55e12c11483..c952e4cb0cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -671,8 +671,7 @@ namespace System.Globalization if (GlobalizationMode.PredefinedCulturesOnly) { - Debug.Assert(!GlobalizationMode.Invariant); - if (GlobalizationMode.UseNls ? !NlsIsEnsurePredefinedLocaleName(cultureName): !IcuIsEnsurePredefinedLocaleName(cultureName)) + if (GlobalizationMode.Invariant || (GlobalizationMode.UseNls ? !NlsIsEnsurePredefinedLocaleName(cultureName): !IcuIsEnsurePredefinedLocaleName(cultureName))) return null; } @@ -865,7 +864,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { // LCID is not supported in the InvariantMode - throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); + throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupportedInInvariantMode); } // Convert the lcid to a name, then use that diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs index 729119c8c16..e862e03293b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs @@ -158,6 +158,8 @@ namespace System.Globalization return s_userDefaultUICulture!; } + private static string GetCultureNotSupportedExceptionMessage() => GlobalizationMode.Invariant ? SR.Argument_CultureNotSupportedInInvariantMode : SR.Argument_CultureNotSupported; + public CultureInfo(string name) : this(name, true) { } @@ -174,7 +176,7 @@ namespace System.Globalization if (cultureData == null) { - throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported); + throw new CultureNotFoundException(nameof(name), name, GetCultureNotSupportedExceptionMessage()); } _cultureData = cultureData; @@ -249,7 +251,7 @@ namespace System.Globalization } CultureData? cultureData = CultureData.GetCultureData(cultureName, false) ?? - throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported); + throw new CultureNotFoundException(nameof(cultureName), cultureName, GetCultureNotSupportedExceptionMessage()); _cultureData = cultureData; @@ -1060,7 +1062,7 @@ namespace System.Globalization } catch (ArgumentException) { - throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); + throw new CultureNotFoundException(nameof(culture), culture, GetCultureNotSupportedExceptionMessage()); } lock (lcidTable) @@ -1096,7 +1098,7 @@ namespace System.Globalization } result = CreateCultureInfoNoThrow(name, useUserOverride: false) ?? - throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported); + throw new CultureNotFoundException(nameof(name), name, GetCultureNotSupportedExceptionMessage()); result._isReadOnly = true; // Remember our name as constructed. Do NOT use alternate sort name versions because diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index 5afa2a4e2b9..3536ed7d534 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -11,8 +11,8 @@ namespace System.Globalization // split from GlobalizationMode so the whole class can be trimmed when Invariant=true. private static partial class Settings { - internal static readonly bool PredefinedCulturesOnly = AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY"); internal static bool Invariant { get; } = GetInvariantSwitchValue(); + internal static readonly bool PredefinedCulturesOnly = AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", Invariant); } // Note: Invariant=true and Invariant=false are substituted at different levels in the ILLink.Substitutions file. @@ -20,7 +20,7 @@ namespace System.Globalization // static cctor (on Unix) to be preserved when Invariant=false. internal static bool Invariant => Settings.Invariant; - internal static bool PredefinedCulturesOnly => !Invariant && Settings.PredefinedCulturesOnly; + internal static bool PredefinedCulturesOnly => Settings.PredefinedCulturesOnly; private static bool GetInvariantSwitchValue() => AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs index 18b714a42ce..c940600ee8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs @@ -139,7 +139,7 @@ namespace System.Resources Debug.Assert(a != null, "assembly != null"); NeutralResourcesLanguageAttribute? attr = a.GetCustomAttribute(); - if (attr == null) + if (attr == null || (GlobalizationMode.Invariant && GlobalizationMode.PredefinedCulturesOnly)) { fallbackLocation = UltimateResourceFallbackLocation.MainAssembly; return CultureInfo.InvariantCulture; diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index 93c9070d539..30862efd02b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -848,15 +848,15 @@ namespace System /// If a localized resource file exists, we LoadString resource ID "123" and /// return it to our caller. /// - private static string GetLocalizedNameByMuiNativeResource(string resource, CultureInfo? cultureInfo = null) + private static string GetLocalizedNameByMuiNativeResource(string resource) { - if (string.IsNullOrEmpty(resource)) + if (string.IsNullOrEmpty(resource) || (GlobalizationMode.Invariant && GlobalizationMode.PredefinedCulturesOnly)) { return string.Empty; } // Use the current UI culture when culture not specified - cultureInfo ??= CultureInfo.CurrentUICulture; + CultureInfo cultureInfo = CultureInfo.CurrentUICulture; // parse "@tzres.dll, -100" // diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/InvariantGlobalizationTrue.cs b/src/libraries/System.Runtime/tests/TrimmingTests/InvariantGlobalizationTrue.cs index 7daa557239f..6b16a662c50 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/InvariantGlobalizationTrue.cs +++ b/src/libraries/System.Runtime/tests/TrimmingTests/InvariantGlobalizationTrue.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; using System.Reflection; -using System.Threading; /// /// Ensures setting InvariantGlobalization = true still works in a trimmed app. @@ -13,18 +12,24 @@ class Program { static int Main(string[] args) { - // since we are using Invariant GlobalizationMode = true, setting the culture doesn't matter. - // The app will always use Invariant mode, so even in the Turkish culture, 'i' ToUpper will be "I" - Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR"); - if ("i".ToUpper() != "I") - { - // 'i' ToUpper was not "I", so fail - return -1; - } - // Ensure the internal GlobalizationMode class is trimmed correctly Type globalizationMode = GetCoreLibType("System.Globalization.GlobalizationMode"); const BindingFlags allStatics = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; + + try + { + CultureInfo.CurrentCulture = new CultureInfo("tr-TR"); + return -1; // we expect new CultureInfo("tr-TR") to throw. + } + catch (CultureNotFoundException) + { + } + + if ("i".ToUpper() != "I") + { + return -3; + } + foreach (MemberInfo member in globalizationMode.GetMembers(allStatics)) { // properties and their backing getter methods are OK @@ -44,7 +49,7 @@ class Program // Some unexpected member was left on GlobalizationMode, fail Console.WriteLine($"Member '{member.Name}' was not trimmed from GlobalizationMode, but should have been."); - return -2; + return -4; } return 100; diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index 02b4882782e..2f2da7a4e12 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -15,7 +15,7 @@ --feature System.Globalization.Invariant false - --feature System.Globalization.Invariant true + --feature System.Globalization.Invariant true --feature System.Globalization.PredefinedCulturesOnly true - - - From 09785ef78f8c758350ec1ded604484bbbed0ce99 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Thu, 1 Jul 2021 12:08:30 -0700 Subject: [PATCH 235/926] Recategorize emsdk dependency (#55028) --- eng/Version.Details.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1a6424850bb..90a6d501016 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,6 +8,10 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b + + https://github.com/dotnet/emsdk + f5349765b7af1970c5b25cce4ed278544907cbe0 + @@ -210,10 +214,6 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization b89374348ff2344a625677584be9dfc9bea2b971 - - https://github.com/dotnet/emsdk - f5349765b7af1970c5b25cce4ed278544907cbe0 - https://github.com/dotnet/hotreload-utils 2b1536142083d6270dc40c5cba74fbb0a612beab From b8beed27f441acbad86fca4a37f7e1287fec071f Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 1 Jul 2021 21:39:04 +0200 Subject: [PATCH 236/926] Poison address-exposed user variables in debug (#54685) * Poison address exposed user variables in debug code Fix #13072 * Run jit-format * Use named scratch register and kill it in LSRA * Enable it unconditionally for testing purposes * Remove unnecessary modified reg on ARM * Fix OSR and get rid of test code * Remove a declaration * Undo modified comment and use modulo instead of and * Add a test * Rephrase comment Co-authored-by: Kunal Pathak * Disable poisoning test on mono * Remove outdated line Co-authored-by: Kunal Pathak --- src/coreclr/jit/codegen.h | 2 + src/coreclr/jit/codegencommon.cpp | 62 +++++++++++++++++++ src/coreclr/jit/codegenlinear.cpp | 7 +++ src/coreclr/jit/compiler.h | 10 +++ src/coreclr/jit/flowgraph.cpp | 4 +- src/coreclr/jit/lsrabuild.cpp | 9 +++ .../src/System/Decimal.DecCalc.cs | 15 ----- src/tests/JIT/Directed/debugging/poison.cs | 56 +++++++++++++++++ .../JIT/Directed/debugging/poison.csproj | 11 ++++ src/tests/issues.targets | 3 + 10 files changed, 162 insertions(+), 17 deletions(-) create mode 100644 src/tests/JIT/Directed/debugging/poison.cs create mode 100644 src/tests/JIT/Directed/debugging/poison.csproj diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index ae862974158..132a763c06b 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -348,6 +348,8 @@ protected: void genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pInitRegZeroed, regMaskTP maskArgRegsLiveIn); + void genPoisonFrame(regMaskTP bbRegLiveIn); + #if defined(TARGET_ARM) bool genInstrWithConstant( diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index e99f047c061..cfbdf1e9033 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -12528,3 +12528,65 @@ void CodeGenInterface::VariableLiveKeeper::dumpLvaVariableLiveRanges() const } #endif // DEBUG #endif // USING_VARIABLE_LIVE_RANGE + +//----------------------------------------------------------------------------- +// genPoisonFrame: Generate code that places a recognizable value into address exposed variables. +// +// Remarks: +// This function emits code to poison address exposed non-zero-inited local variables. We expect this function +// to be called when emitting code for the scratch BB that comes right after the prolog. +// The variables are poisoned using 0xcdcdcdcd. +void CodeGen::genPoisonFrame(regMaskTP regLiveIn) +{ + assert(compiler->compShouldPoisonFrame()); + assert((regLiveIn & genRegMask(REG_SCRATCH)) == 0); + + // The first time we need to poison something we will initialize a register to the largest immediate cccccccc that + // we can fit. + bool hasPoisonImm = false; + for (unsigned varNum = 0; varNum < compiler->info.compLocalsCount; varNum++) + { + LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); + if (varDsc->lvIsParam || varDsc->lvMustInit || !varDsc->lvAddrExposed) + { + continue; + } + + assert(varDsc->lvOnFrame); + + if (!hasPoisonImm) + { +#ifdef TARGET_64BIT + genSetRegToIcon(REG_SCRATCH, (ssize_t)0xcdcdcdcdcdcdcdcd, TYP_LONG); +#else + genSetRegToIcon(REG_SCRATCH, (ssize_t)0xcdcdcdcd, TYP_INT); +#endif + hasPoisonImm = true; + } + +// For 64-bit we check if the local is 8-byte aligned. For 32-bit, we assume everything is always 4-byte aligned. +#ifdef TARGET_64BIT + bool fpBased; + int addr = compiler->lvaFrameAddress((int)varNum, &fpBased); +#else + int addr = 0; +#endif + int size = (int)compiler->lvaLclSize(varNum); + int end = addr + size; + for (int offs = addr; offs < end;) + { +#ifdef TARGET_64BIT + if ((offs % 8) == 0 && end - offs >= 8) + { + GetEmitter()->emitIns_S_R(ins_Store(TYP_LONG), EA_8BYTE, REG_SCRATCH, (int)varNum, offs - addr); + offs += 8; + continue; + } +#endif + + assert((offs % 4) == 0 && end - offs >= 4); + GetEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, REG_SCRATCH, (int)varNum, offs - addr); + offs += 4; + } + } +} diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 796c128b98e..8bf1f852ffd 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -395,6 +395,13 @@ void CodeGen::genCodeForBBlist() compiler->compCurStmt = nullptr; compiler->compCurLifeTree = nullptr; + // Emit poisoning into scratch BB that comes right after prolog. + // We cannot emit this code in the prolog as it might make the prolog too large. + if (compiler->compShouldPoisonFrame() && compiler->fgBBisScratch(block)) + { + genPoisonFrame(newLiveRegSet); + } + // Traverse the block in linear order, generating code for each node as we // as we encounter it. CLANG_FORMAT_COMMENT_ANCHOR; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9086613da28..ca8daaf7a96 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9839,6 +9839,16 @@ public: return (info.compUnmanagedCallCountWithGCTransition > 0); } + // Returns true if address-exposed user variables should be poisoned with a recognizable value + bool compShouldPoisonFrame() + { +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (opts.IsOSR()) + return false; +#endif + return !info.compInitMem && opts.compDbgCode; + } + #if defined(DEBUG) void compDispLocalVars(); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index e7a281cdeca..04faed33f50 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -2603,8 +2603,8 @@ void Compiler::fgAddInternal() noway_assert(!compIsForInlining()); // The backend requires a scratch BB into which it can safely insert a P/Invoke method prolog if one is - // required. Create it here. - if (compMethodRequiresPInvokeFrame()) + // required. Similarly, we need a scratch BB for poisoning. Create it here. + if (compMethodRequiresPInvokeFrame() || compShouldPoisonFrame()) { fgEnsureFirstBBisScratch(); fgFirstBB->bbFlags |= BBF_DONT_REMOVE; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index f4ea6fb3579..cb04ddddf51 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -2333,6 +2333,15 @@ void LinearScan::buildIntervals() // assert(block->isRunRarely()); } + // For frame poisoning we generate code into scratch BB right after prolog since + // otherwise the prolog might become too large. In this case we will put the poison immediate + // into the scratch register, so it will be killed here. + if (compiler->compShouldPoisonFrame() && compiler->fgFirstBBisScratch() && block == compiler->fgFirstBB) + { + addRefsForPhysRegMask(genRegMask(REG_SCRATCH), currentLoc + 1, RefTypeKill, true); + currentLoc += 2; + } + LIR::Range& blockRange = LIR::AsRange(block); for (GenTree* node : blockRange) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs index d56ce8ebb7f..a966b35b83c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs @@ -153,13 +153,6 @@ namespace System 1e80 }; - // Used to fill uninitialized stack variables with non-zero pattern in debug builds - [Conditional("DEBUG")] - private static unsafe void DebugPoison(ref T s) where T : unmanaged - { - MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref s, 1)).Fill(0xCD); - } - #region Decimal Math Helpers private static unsafe uint GetExponent(float f) @@ -990,7 +983,6 @@ ThrowOverflow: // Have to scale by a bunch. Move the number to a buffer where it has room to grow as it's scaled. // Unsafe.SkipInit(out Buf24 bufNum); - DebugPoison(ref bufNum); bufNum.Low64 = low64; bufNum.Mid64 = tmp64; @@ -1339,7 +1331,6 @@ ThrowOverflow: ulong tmp; uint hiProd; Unsafe.SkipInit(out Buf24 bufProd); - DebugPoison(ref bufProd); if ((d1.High | d1.Mid) == 0) { @@ -1920,7 +1911,6 @@ ReturnZero: internal static unsafe void VarDecDiv(ref DecCalc d1, ref DecCalc d2) { Unsafe.SkipInit(out Buf12 bufQuo); - DebugPoison(ref bufQuo); uint power; int curScale; @@ -2021,7 +2011,6 @@ ReturnZero: // Shift both dividend and divisor left by curScale. // Unsafe.SkipInit(out Buf16 bufRem); - DebugPoison(ref bufRem); bufRem.Low64 = d1.Low64 << curScale; bufRem.High64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - curScale); @@ -2090,7 +2079,6 @@ ReturnZero: // Start by finishing the shift left by curScale. // Unsafe.SkipInit(out Buf12 bufDivisor); - DebugPoison(ref bufDivisor); bufDivisor.Low64 = divisor; bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - curScale)); @@ -2243,7 +2231,6 @@ ThrowOverflow: d1.uflags = d2.uflags; // Try to scale up dividend to match divisor. Unsafe.SkipInit(out Buf12 bufQuo); - DebugPoison(ref bufQuo); bufQuo.Low64 = d1.Low64; bufQuo.U2 = d1.High; @@ -2303,7 +2290,6 @@ ThrowOverflow: int shift = BitOperations.LeadingZeroCount(tmp); Unsafe.SkipInit(out Buf28 b); - DebugPoison(ref b); b.Buf24.Low64 = d1.Low64 << shift; b.Buf24.Mid64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - shift); @@ -2358,7 +2344,6 @@ ThrowOverflow: else { Unsafe.SkipInit(out Buf12 bufDivisor); - DebugPoison(ref bufDivisor); bufDivisor.Low64 = d2.Low64 << shift; bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - shift)); diff --git a/src/tests/JIT/Directed/debugging/poison.cs b/src/tests/JIT/Directed/debugging/poison.cs new file mode 100644 index 00000000000..463894a4e6a --- /dev/null +++ b/src/tests/JIT/Directed/debugging/poison.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.CompilerServices; + +public class Program +{ + [SkipLocalsInit] + public static unsafe int Main() + { + bool result = true; + + int poisoned; + Unsafe.SkipInit(out poisoned); + result &= VerifyPoison(&poisoned, sizeof(int)); + + GCRef zeroed; + Unsafe.SkipInit(out zeroed); + result &= VerifyZero(Unsafe.AsPointer(ref zeroed), Unsafe.SizeOf()); + + WithoutGCRef poisoned2; + Unsafe.SkipInit(out poisoned2); + result &= VerifyPoison(&poisoned2, sizeof(WithoutGCRef)); + + return result ? 100 : 101; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe bool VerifyPoison(void* val, int size) + => AllEq(new Span(val, size), 0xCD); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe bool VerifyZero(void* val, int size) + => AllEq(new Span(val, size), 0); + + private static unsafe bool AllEq(Span span, byte byteVal) + { + foreach (byte b in span) + { + if (b != byteVal) + return false; + } + + return true; + } + + private struct GCRef + { + public object ARef; + } + + private struct WithoutGCRef + { + public double ADouble; + public int ANumber; + public float AFloat; + } +} diff --git a/src/tests/JIT/Directed/debugging/poison.csproj b/src/tests/JIT/Directed/debugging/poison.csproj new file mode 100644 index 00000000000..b2691ab4cab --- /dev/null +++ b/src/tests/JIT/Directed/debugging/poison.csproj @@ -0,0 +1,11 @@ + + + Exe + PdbOnly + False + True + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 964d0d7b17d..2f98f5c69ab 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1650,6 +1650,9 @@ needs triage + + Tests coreclr JIT's debug poisoning of address taken variables + From ca00588105feae898bc73b6d24bf9600d41b39db Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 1 Jul 2021 16:00:55 -0400 Subject: [PATCH 237/926] Delete stale ActiveIssue from HttpHeadersTest (#55027) This is reported to have been fixed by https://github.com/dotnet/runtime/pull/54932. --- .../System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs index 2486e8bc45b..f2a4367c704 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs @@ -1538,7 +1538,6 @@ namespace System.Net.Http.Tests Assert.Equal(new HashSet { "Location", "Date" }, nonValidated.Keys.ToHashSet()); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/53647", TestPlatforms.Browser)] [Fact] public void NonValidated_ValidInvalidAndRaw_AllReturned() { From 8dad36481b7436712604235b93ed4f367fc95057 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Fri, 2 Jul 2021 00:37:24 +0200 Subject: [PATCH 238/926] [Mono] Include loaded interpreter methods as EventPipe session rundown method events. (#54953) * Include interpreter methods in EventPipe session rundown events. * Fix build error. --- src/mono/mono/eventpipe/ep-rt-mono.c | 40 ++++++++++++++++------- src/mono/mono/metadata/object-internals.h | 4 +++ src/mono/mono/mini/ee.h | 1 + src/mono/mono/mini/interp-stubs.c | 5 +++ src/mono/mono/mini/interp/interp.c | 33 ++++++++++++++++--- src/mono/mono/mini/mini-runtime.c | 9 ++++- src/mono/mono/utils/mono-internal-hash.c | 4 +-- src/mono/mono/utils/mono-internal-hash.h | 4 +-- 8 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index f8b85538db0..5ff2183936f 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -115,6 +115,7 @@ typedef struct _EventPipeSampleProfileData { uintptr_t thread_ip; uint32_t payload_data; bool async_frame; + bool safe_point_frame; } EventPipeSampleProfileData; // Rundown flags. @@ -898,13 +899,15 @@ eventpipe_execute_rundown ( if (root_domain) { uint64_t domain_id = (uint64_t)root_domain; - // Iterate all functions in use (both JIT and AOT). + // Iterate all functions in use (JIT, AOT and Interpreter). EventPipeFireMethodEventsData events_data; events_data.domain = root_domain; events_data.buffer_size = 1024 * sizeof(uint32_t); events_data.buffer = g_new (uint8_t, events_data.buffer_size); events_data.method_events_func = method_events_func; mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); + if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) + mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); g_free (events_data.buffer); // Iterate all assemblies in domain. @@ -997,21 +1000,32 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( EP_ASSERT (frame != NULL); EP_ASSERT (data != NULL); - gboolean result = false; EventPipeSampleProfileData *sample_data = (EventPipeSampleProfileData *)data; if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { - if (frame->type == FRAME_TYPE_MANAGED_TO_NATIVE) - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - else + switch (frame->type) { + case FRAME_TYPE_MANAGED: sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + break; + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP: + if (frame->managed) + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + else + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP_TO_MANAGED: + case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + break; + default: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } } - bool safe_point_frame = false; - result = eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_contents, &sample_data->async_frame, &safe_point_frame); - if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && safe_point_frame) - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - return result; + return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_contents, &sample_data->async_frame, &sample_data->safe_point_frame); } static @@ -1515,8 +1529,12 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; data->async_frame = FALSE; + data->safe_point_frame = FALSE; ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && data->safe_point_frame) + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + sampled_thread_count++; } } diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 0af9ed94068..9dfb86c1d9d 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -610,6 +610,9 @@ typedef struct { /* Safely access System.Delegate from native code */ TYPED_HANDLE_DECL (MonoDelegate); + +typedef void (*InterpJitInfoFunc) (MonoJitInfo *ji, gpointer user_data); + /* * Callbacks supplied by the runtime and called by the modules in metadata/ * This interface is easier to extend than adding a new function type + @@ -643,6 +646,7 @@ typedef struct { void (*get_exception_stats)(guint32 *exception_count); // Same as compile_method, but returns a MonoFtnDesc in llvmonly mode gpointer (*get_ftnptr)(MonoMethod *method, MonoError *error); + void (*interp_jit_info_foreach)(InterpJitInfoFunc func, gpointer user_data); } MonoRuntimeCallbacks; typedef gboolean (*MonoInternalStackWalk) (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data); diff --git a/src/mono/mono/mini/ee.h b/src/mono/mono/mini/ee.h index ba2796207f0..6f171c85bd7 100644 --- a/src/mono/mono/mini/ee.h +++ b/src/mono/mono/mini/ee.h @@ -59,6 +59,7 @@ typedef gpointer MonoInterpFrameHandle; MONO_EE_CALLBACK (void, invalidate_transformed, (void)) \ MONO_EE_CALLBACK (void, cleanup, (void)) \ MONO_EE_CALLBACK (void, mark_stack, (gpointer thread_info, GcScanFunc func, gpointer gc_data, gboolean precise)) \ + MONO_EE_CALLBACK (void, jit_info_foreach, (InterpJitInfoFunc func, gpointer user_data)) \ typedef struct _MonoEECallbacks { diff --git a/src/mono/mono/mini/interp-stubs.c b/src/mono/mono/mini/interp-stubs.c index 8aa96e0183c..81e71c09827 100644 --- a/src/mono/mono/mini/interp-stubs.c +++ b/src/mono/mono/mini/interp-stubs.c @@ -215,6 +215,11 @@ stub_mark_stack (gpointer thread_data, GcScanFunc func, gpointer gc_data, gboole { } +static void +stub_jit_info_foreach (InterpJitInfoFunc func, gpointer user_data) +{ +} + #undef MONO_EE_CALLBACK #define MONO_EE_CALLBACK(ret, name, sig) stub_ ## name, diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 1c7468ba3ca..cc305593c7f 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -7325,7 +7325,7 @@ static int num_methods; const int opcount_threshold = 100000; static void -interp_add_imethod (gpointer method) +interp_add_imethod (gpointer method, gpointer user_data) { InterpMethod *imethod = (InterpMethod*) method; if (imethod->opcounts > opcount_threshold) @@ -7351,7 +7351,7 @@ interp_print_method_counts (void) jit_mm_lock (jit_mm); imethods = (InterpMethod**) malloc (jit_mm->interp_code_hash.num_entries * sizeof (InterpMethod*)); - mono_internal_hash_table_apply (&jit_mm->interp_code_hash, interp_add_imethod); + mono_internal_hash_table_apply (&jit_mm->interp_code_hash, interp_add_imethod, NULL); jit_mm_unlock (jit_mm); qsort (imethods, num_methods, sizeof (InterpMethod*), imethod_opcount_comparer); @@ -7372,7 +7372,7 @@ interp_set_optimizations (guint32 opts) } static void -invalidate_transform (gpointer imethod_) +invalidate_transform (gpointer imethod_, gpointer user_data) { InterpMethod *imethod = (InterpMethod *) imethod_; imethod->transformed = FALSE; @@ -7451,13 +7451,38 @@ interp_invalidate_transformed (void) MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); - mono_internal_hash_table_apply (&jit_mm->interp_code_hash, invalidate_transform); + mono_internal_hash_table_apply (&jit_mm->interp_code_hash, invalidate_transform, NULL); jit_mm_unlock (jit_mm); if (need_stw_restart) mono_restart_world (MONO_THREAD_INFO_FLAGS_NO_GC); } +typedef struct { + InterpJitInfoFunc func; + gpointer user_data; +} InterpJitInfoFuncUserData; + +static void +interp_call_jit_info_func (gpointer imethod, gpointer user_data) +{ + InterpJitInfoFuncUserData *data = (InterpJitInfoFuncUserData *)user_data; + data->func (((InterpMethod *)imethod)->jinfo, data->user_data); +} + +static void +interp_jit_info_foreach (InterpJitInfoFunc func, gpointer user_data) +{ + InterpJitInfoFuncUserData data = {func, user_data}; + + // FIXME: Enumerate all memory managers + MonoJitMemoryManager *jit_mm = get_default_jit_mm (); + + jit_mm_lock (jit_mm); + mono_internal_hash_table_apply (&jit_mm->interp_code_hash, interp_call_jit_info_func, &data); + jit_mm_unlock (jit_mm); +} + static void interp_cleanup (void) { diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index ce8930ff6c8..c500e946968 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -160,6 +160,7 @@ GSList *mono_interp_only_classes; static void register_icalls (void); static void runtime_cleanup (MonoDomain *domain, gpointer user_data); static void mini_invalidate_transformed_interp_methods (MonoAssemblyLoadContext *alc, uint32_t generation); +static void mini_interp_jit_info_foreach(InterpJitInfoFunc func, gpointer user_data); gboolean mono_running_on_valgrind (void) @@ -4357,6 +4358,7 @@ mini_init (const char *filename, const char *runtime_version) callbacks.install_state_summarizer = mini_register_sigterm_handler; #endif callbacks.metadata_update_published = mini_invalidate_transformed_interp_methods; + callbacks.interp_jit_info_foreach = mini_interp_jit_info_foreach; callbacks.init_mem_manager = init_jit_mem_manager; callbacks.free_mem_manager = free_jit_mem_manager; @@ -5139,12 +5141,17 @@ mono_runtime_install_custom_handlers_usage (void) } #endif /* HOST_WIN32 */ -void +static void mini_invalidate_transformed_interp_methods (MonoAssemblyLoadContext *alc G_GNUC_UNUSED, uint32_t generation G_GNUC_UNUSED) { mini_get_interp_callbacks ()->invalidate_transformed (); } +static void +mini_interp_jit_info_foreach(InterpJitInfoFunc func, gpointer user_data) +{ + mini_get_interp_callbacks ()->jit_info_foreach (func, user_data); +} /* * mini_get_default_mem_manager: diff --git a/src/mono/mono/utils/mono-internal-hash.c b/src/mono/mono/utils/mono-internal-hash.c index 92b68d17861..f19622afe59 100644 --- a/src/mono/mono/utils/mono-internal-hash.c +++ b/src/mono/mono/utils/mono-internal-hash.c @@ -107,12 +107,12 @@ mono_internal_hash_table_insert (MonoInternalHashTable *table, } void -mono_internal_hash_table_apply (MonoInternalHashTable *table, MonoInternalHashApplyFunc func) +mono_internal_hash_table_apply (MonoInternalHashTable *table, MonoInternalHashApplyFunc func, gpointer user_data) { for (gint i = 0; i < table->size; i++) { gpointer head = table->table [i]; while (head) { - func (head); + func (head, user_data); head = *(table->next_value (head)); } } diff --git a/src/mono/mono/utils/mono-internal-hash.h b/src/mono/mono/utils/mono-internal-hash.h index 8a85f7d10aa..31acdbf0d2a 100644 --- a/src/mono/mono/utils/mono-internal-hash.h +++ b/src/mono/mono/utils/mono-internal-hash.h @@ -37,7 +37,7 @@ typedef struct _MonoInternalHashTable MonoInternalHashTable; typedef gpointer (*MonoInternalHashKeyExtractFunc) (gpointer value); typedef gpointer* (*MonoInternalHashNextValueFunc) (gpointer value); -typedef void (*MonoInternalHashApplyFunc) (gpointer value); +typedef void (*MonoInternalHashApplyFunc) (gpointer value, gpointer user_data); struct _MonoInternalHashTable { @@ -73,7 +73,7 @@ mono_internal_hash_table_insert (MonoInternalHashTable *table, gpointer key, gpointer value); void -mono_internal_hash_table_apply (MonoInternalHashTable *table, MonoInternalHashApplyFunc func); +mono_internal_hash_table_apply (MonoInternalHashTable *table, MonoInternalHashApplyFunc func, gpointer user_data); gboolean mono_internal_hash_table_remove (MonoInternalHashTable *table, gpointer key); From 08a7b2382799082eedb94d70fca6c66eb75f2872 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 1 Jul 2021 20:08:40 -0300 Subject: [PATCH 239/926] [debugger]Componentize debugger (#54887) * First version of componentize debugger * Trying to save history of debugger-agent.h * adding debugger-agent.h as a new file * Fixing compilation * Fixing wasm compilation * Fixing wasm compilation. * Running wasm app. * Unrelated change. * Fixing debugging console app * Fixing compilation on linux * Fixing wasm debugger. * Remove unused callbacks * Moving mini-wasm-debugger to component folder. * Fixing wasm debugger. * Fix non wasm compilation * Fixing compilation on other platforms. * Removing more code from component callback. * Transforming more functions into static. * Fix compilation. * Moving more files to component folder. * moving files. * Fix android compilation. * Fix wasm compilation. * Try to fix windows compilation. * Changing what @lambdageek suggested. * Changing what @lamdbageek suggested. * Not used anymore. * Using the same function to initialize wasm and non-wasm debugger. * Rollback exported function name. * Changing where components are initialized. Saving debugger transport information outside the component. * set sdb options before component startup. * Fixing console debug. * Fix ios compilation. * Fix calling parse_options without parameters. * Last @lambdageek suggestions. --- .../Directory.Build.props | 7 + src/mono/dlls/mscordbi/CMakeLists.txt | 4 +- src/mono/dlls/mscordbi/cordb.h | 2 +- src/mono/mono/component/CMakeLists.txt | 22 ++ .../mono/{mini => component}/debugger-agent.c | 244 ++++++++++-------- src/mono/mono/component/debugger-agent.h | 56 ++++ .../{mini => component}/debugger-engine.c | 76 ++---- .../{mini => component}/debugger-engine.h | 169 +----------- .../debugger-mono-compat.h | 0 .../{mini => component}/debugger-protocol.c | 0 .../{mini => component}/debugger-protocol.h | 0 .../debugger-state-machine.c | 5 +- .../debugger-state-machine.h | 0 src/mono/mono/component/debugger-stub.c | 203 +++++++++++++++ src/mono/mono/component/debugger.c | 35 +++ src/mono/mono/component/debugger.h | 224 ++++++++++++++++ .../{mini => component}/mini-wasm-debugger.c | 96 +++---- src/mono/mono/metadata/assembly-internals.h | 6 +- src/mono/mono/metadata/class-init.h | 8 +- src/mono/mono/metadata/class-internals.h | 52 ++-- src/mono/mono/metadata/components.c | 8 + src/mono/mono/metadata/components.h | 9 + .../mono/metadata/custom-attrs-internals.h | 4 +- src/mono/mono/metadata/debug-internals.h | 8 +- src/mono/mono/metadata/debug-mono-ppdb.h | 2 +- src/mono/mono/metadata/domain-internals.h | 4 +- src/mono/mono/metadata/gc-internals.h | 8 +- src/mono/mono/metadata/handle.c | 2 +- src/mono/mono/metadata/handle.h | 6 +- src/mono/mono/metadata/loader-internals.h | 4 +- src/mono/mono/metadata/marshal.h | 2 +- src/mono/mono/metadata/metadata-internals.h | 10 +- src/mono/mono/metadata/metadata.c | 2 - src/mono/mono/metadata/mono-debug.h | 2 +- src/mono/mono/metadata/mono-hash-internals.h | 5 +- src/mono/mono/metadata/object-internals.h | 40 +-- src/mono/mono/metadata/reflection-internals.h | 18 +- src/mono/mono/metadata/runtime.h | 4 +- src/mono/mono/metadata/seq-points-data.h | 7 +- src/mono/mono/metadata/threads-types.h | 10 +- src/mono/mono/metadata/verify-internals.h | 2 +- src/mono/mono/mini/CMakeLists.txt | 13 +- src/mono/mono/mini/aot-runtime.c | 7 +- src/mono/mono/mini/debugger-agent-external.c | 62 +++++ src/mono/mono/mini/debugger-agent-external.h | 29 +++ src/mono/mono/mini/debugger-agent-stubs.c | 122 --------- src/mono/mono/mini/debugger-agent.h | 115 --------- src/mono/mono/mini/driver.c | 14 +- src/mono/mono/mini/interp/interp.c | 5 +- src/mono/mono/mini/intrinsics.c | 1 - src/mono/mono/mini/method-to-ir.c | 1 - src/mono/mono/mini/mini-amd64-gsharedvt.c | 1 - src/mono/mono/mini/mini-amd64.c | 1 - src/mono/mono/mini/mini-arm.c | 1 - src/mono/mono/mini/mini-exceptions.c | 17 +- src/mono/mono/mini/mini-posix.c | 7 +- src/mono/mono/mini/mini-runtime.c | 55 ++-- src/mono/mono/mini/mini-runtime.h | 16 +- src/mono/mono/mini/mini-wasm.c | 14 + src/mono/mono/mini/mini-wasm.h | 7 - src/mono/mono/mini/mini.c | 1 - src/mono/mono/mini/mini.h | 41 +-- src/mono/mono/mini/monovm.c | 2 + src/mono/mono/mini/seq-points.h | 8 +- src/mono/mono/mini/tramp-amd64-gsharedvt.c | 1 - src/mono/mono/mini/tramp-amd64.c | 6 +- src/mono/mono/mini/tramp-arm.c | 7 +- src/mono/mono/mini/tramp-arm64.c | 7 +- src/mono/mono/mini/tramp-s390x.c | 7 +- src/mono/mono/mini/tramp-wasm.c | 5 +- src/mono/mono/mini/tramp-x86.c | 9 +- src/mono/mono/sgen/gc-internal-agnostic.h | 2 +- src/mono/mono/utils/json.h | 26 +- src/mono/mono/utils/memfuncs.h | 3 +- src/mono/mono/utils/mono-context.h | 4 +- src/mono/mono/utils/mono-error-internals.h | 3 +- src/mono/mono/utils/mono-flight-recorder.h | 14 +- src/mono/mono/utils/mono-threads-api.h | 2 +- src/mono/mono/utils/mono-threads.h | 4 +- src/mono/mono/utils/mono-time.h | 2 +- src/mono/mono/utils/networking.h | 8 +- src/mono/wasm/Makefile | 1 + src/mono/wasm/wasm.proj | 1 + 83 files changed, 1145 insertions(+), 873 deletions(-) rename src/mono/mono/{mini => component}/debugger-agent.c (97%) create mode 100644 src/mono/mono/component/debugger-agent.h rename src/mono/mono/{mini => component}/debugger-engine.c (95%) rename src/mono/mono/{mini => component}/debugger-engine.h (78%) rename src/mono/mono/{mini => component}/debugger-mono-compat.h (100%) rename src/mono/mono/{mini => component}/debugger-protocol.c (100%) rename src/mono/mono/{mini => component}/debugger-protocol.h (100%) rename src/mono/mono/{mini => component}/debugger-state-machine.c (99%) rename src/mono/mono/{mini => component}/debugger-state-machine.h (100%) create mode 100644 src/mono/mono/component/debugger-stub.c create mode 100644 src/mono/mono/component/debugger.c create mode 100644 src/mono/mono/component/debugger.h rename src/mono/mono/{mini => component}/mini-wasm-debugger.c (86%) create mode 100644 src/mono/mono/mini/debugger-agent-external.c create mode 100644 src/mono/mono/mini/debugger-agent-external.h delete mode 100644 src/mono/mono/mini/debugger-agent-stubs.c delete mode 100644 src/mono/mono/mini/debugger-agent.h diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 3efbe093fb7..171be1106ee 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -193,6 +193,13 @@ + + + + + + + diff --git a/src/mono/dlls/mscordbi/CMakeLists.txt b/src/mono/dlls/mscordbi/CMakeLists.txt index d060638b40d..32e61b129f3 100644 --- a/src/mono/dlls/mscordbi/CMakeLists.txt +++ b/src/mono/dlls/mscordbi/CMakeLists.txt @@ -130,11 +130,11 @@ if (CLR_CMAKE_HOST_UNIX) append("-Wno-strict-prototypes -Wno-deprecated -Wno-pointer-arith" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif (CLR_CMAKE_HOST_UNIX) -add_library(mscordbi SHARED "${mscorbi_sources};${PROJECT_SOURCE_DIR}/../../mono/mini/debugger-protocol.c;${PROJECT_SOURCE_DIR}/../../../coreclr/pal/prebuilt/idl/xcordebug_i.cpp;${PROJECT_SOURCE_DIR}/../../../coreclr/pal/prebuilt/idl/cordebug_i.cpp") +add_library(mscordbi SHARED "${mscorbi_sources};${PROJECT_SOURCE_DIR}/../../mono/component/debugger-protocol.c;${PROJECT_SOURCE_DIR}/../../../coreclr/pal/prebuilt/idl/xcordebug_i.cpp;${PROJECT_SOURCE_DIR}/../../../coreclr/pal/prebuilt/idl/cordebug_i.cpp") #SET(CMAKE_C_COMPILER ${CMAKE_CXX_COMPILER}) -set_source_files_properties(${PROJECT_SOURCE_DIR}/../../mono/mini/debugger-protocol.c PROPERTIES LANGUAGE CXX) +set_source_files_properties(${PROJECT_SOURCE_DIR}/../../mono/component/debugger-protocol.c PROPERTIES LANGUAGE CXX) set(COREDBI_LIBRARIES utilcodestaticnohost diff --git a/src/mono/dlls/mscordbi/cordb.h b/src/mono/dlls/mscordbi/cordb.h index f96ef8fc03d..d81915dc85a 100644 --- a/src/mono/dlls/mscordbi/cordb.h +++ b/src/mono/dlls/mscordbi/cordb.h @@ -12,7 +12,7 @@ #include "corhdr.h" #include "xcordebug.h" -#include +#include #include #include "arraylist.h" diff --git a/src/mono/mono/component/CMakeLists.txt b/src/mono/mono/component/CMakeLists.txt index f523d14e568..62ab5b34528 100644 --- a/src/mono/mono/component/CMakeLists.txt +++ b/src/mono/mono/component/CMakeLists.txt @@ -6,6 +6,7 @@ set(MONO_EVENTPIPE_GEN_INCLUDE_PATH "${CMAKE_CURRENT_BINARY_DIR}/eventpipe") set(MONO_HOT_RELOAD_COMPONENT_NAME "hot_reload") set(MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME "diagnostics_tracing") +set(MONO_DEBUGGER_COMPONENT_NAME "debugger") # a list of every component. set(components "") @@ -14,6 +15,27 @@ set(components "") # component_name-sources list for each component, and a # component_name-stub-sources list for the component stub. +# debugger +list(APPEND components + ${MONO_DEBUGGER_COMPONENT_NAME} +) +set(${MONO_DEBUGGER_COMPONENT_NAME}-sources + ${MONO_COMPONENT_PATH}/debugger.c + ${MONO_COMPONENT_PATH}/debugger.h + ${MONO_COMPONENT_PATH}/debugger-agent.c + ${MONO_COMPONENT_PATH}/debugger-agent.h + ${MONO_COMPONENT_PATH}/debugger-engine.c + ${MONO_COMPONENT_PATH}/debugger-engine.h + ${MONO_COMPONENT_PATH}/debugger-state-machine.h + ${MONO_COMPONENT_PATH}/debugger-state-machine.c + ${MONO_COMPONENT_PATH}/mini-wasm-debugger.c + ${MONO_COMPONENT_PATH}/debugger-protocol.h + ${MONO_COMPONENT_PATH}/debugger-protocol.c + ) +set(${MONO_DEBUGGER_COMPONENT_NAME}-stub-sources + ${MONO_COMPONENT_PATH}/debugger-stub.c + ) + # hot_reload list(APPEND components ${MONO_HOT_RELOAD_COMPONENT_NAME} diff --git a/src/mono/mono/mini/debugger-agent.c b/src/mono/mono/component/debugger-agent.c similarity index 97% rename from src/mono/mono/mini/debugger-agent.c rename to src/mono/mono/component/debugger-agent.c index f61ffce5d22..f72c95392e9 100644 --- a/src/mono/mono/mini/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -78,16 +78,19 @@ #include #include #include -#include "debugger-state-machine.h" + +#include #include "debugger-agent.h" -#include "mini.h" -#include "seq-points.h" -#include "aot-runtime.h" -#include "mini-runtime.h" -#include "interp/interp.h" +#include +#include +#include +#include +#include #include "debugger-engine.h" -#include "mono/metadata/debug-mono-ppdb.h" -#include "mono/metadata/custom-attrs-internals.h" +#include +#include +#include +#include #ifdef HAVE_UCONTEXT_H #include @@ -125,6 +128,47 @@ #pragma warning(disable:4312) // FIXME pointer cast to different size #endif +#define GENERATE_TRY_GET_CLASS_WITH_CACHE_DBG(shortname,name_space,name) \ +MonoClass* \ +mono_class_try_get_##shortname##_class (void) \ +{ \ + static volatile MonoClass *tmp_class; \ + static volatile gboolean inited; \ + MonoClass *klass = (MonoClass *)tmp_class; \ + mono_memory_barrier (); \ + if (!inited) { \ + klass = mono_class_try_load_from_name (mdbg_mono_defaults->corlib, name_space, name); \ + tmp_class = klass; \ + mono_memory_barrier (); \ + inited = TRUE; \ + } \ + return klass; \ +} + +#ifndef MONO_HANDLE_TRACK_OWNER + +#define MONO_HANDLE_NEW_DBG(type, object) \ + (MONO_HANDLE_CAST_FOR (type) (mono_handle_new (MONO_HANDLE_TYPECHECK_FOR (type) (object), mono_thread_info_current ()))) + +#else + +#define MONO_HANDLE_NEW_DBG(type, object) \ + (MONO_HANDLE_CAST_FOR (type) (mono_handle_new (MONO_HANDLE_TYPECHECK_FOR (type) (object), mono_thread_info_current (), HANDLE_OWNER))) + +#endif + +static inline MonoType* +mono_get_object_type_dbg (void) +{ + return m_class_get_byval_arg (mdbg_mono_defaults->object_class); +} + +static inline MonoType* +mono_get_void_type_dbg (void) +{ + return m_class_get_byval_arg (mdbg_mono_defaults->void_class); +} + typedef struct { gboolean enabled; char *transport; @@ -367,6 +411,7 @@ static gint32 suspend_count; /* Whenever to buffer reply messages and send them together */ static gboolean buffer_replies; +MonoDefaults *mdbg_mono_defaults; #ifndef TARGET_WASM #define GET_TLS_DATA_FROM_THREAD(thread) \ @@ -395,7 +440,6 @@ static gboolean buffer_replies; static void transport_init (void); static void transport_connect (const char *address); static gboolean transport_handshake (void); -static void register_transport (DebuggerTransport *trans); static gsize WINAPI debugger_thread (void *arg); @@ -475,9 +519,12 @@ static gboolean ensure_jit (DbgEngineStackFrame* the_frame); static int ensure_runtime_is_suspended (void); static int handle_multiple_ss_requests (void); -static GENERATE_TRY_GET_CLASS_WITH_CACHE (fixed_buffer, "System.Runtime.CompilerServices", "FixedBufferAttribute") +/* Callbacks used by wasm debugger */ +static void mono_dbg_debugger_agent_user_break (void); +static GENERATE_TRY_GET_CLASS_WITH_CACHE_DBG (fixed_buffer, "System.Runtime.CompilerServices", "FixedBufferAttribute") + #ifndef DISABLE_SOCKET_TRANSPORT static void register_socket_transport (void); @@ -550,6 +597,8 @@ parse_flag (const char *option, char *flag) static void debugger_agent_parse_options (char *options) { + if (!options) + return; char **args, **ptr; char *host; int port; @@ -686,11 +735,13 @@ mono_debugger_is_disconnected (void) } static void -debugger_agent_init (void) +debugger_agent_init (MonoDefaults *mono_defaults) { if (!agent_config.enabled) return; + mdbg_mono_defaults = mono_defaults; + DebuggerEngineCallbacks cbs; memset (&cbs, 0, sizeof (cbs)); cbs.tls_get_restore_state = tls_get_restore_state; @@ -701,13 +752,6 @@ debugger_agent_init (void) cbs.ss_calculate_framecount = mono_ss_calculate_framecount; cbs.ensure_jit = ensure_jit; cbs.ensure_runtime_is_suspended = ensure_runtime_is_suspended; - cbs.get_this_async_id = mono_get_this_async_id; - cbs.set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag; - cbs.get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method; - cbs.create_breakpoint_events = mono_dbg_create_breakpoint_events; - cbs.process_breakpoint_events = mono_dbg_process_breakpoint_events; - cbs.ss_create_init_args = mono_ss_create_init_args; - cbs.ss_args_destroy = mono_ss_args_destroy; cbs.handle_multiple_ss_requests = handle_multiple_ss_requests; mono_de_init (&cbs); @@ -735,7 +779,7 @@ debugger_agent_init (void) mono_profiler_set_jit_failed_callback (prof, jit_failed); mono_profiler_set_gc_finalizing_callback (prof, gc_finalizing); mono_profiler_set_gc_finalized_callback (prof, gc_finalized); - + mono_native_tls_alloc (&debugger_tls_id, NULL); /* Needed by the hash_table_new_type () call below */ @@ -1217,7 +1261,7 @@ register_socket_transport (void) trans.send = socket_transport_send; trans.recv = socket_transport_recv; - register_transport (&trans); + mono_debugger_agent_register_transport (&trans); } /* @@ -1258,7 +1302,7 @@ register_socket_fd_transport (void) trans.send = socket_transport_send; trans.recv = socket_transport_recv; - register_transport (&trans); + mono_debugger_agent_register_transport (&trans); } #endif /* DISABLE_SOCKET_TRANSPORT */ @@ -1267,28 +1311,10 @@ register_socket_fd_transport (void) * TRANSPORT CODE */ -#define MAX_TRANSPORTS 16 + static DebuggerTransport *transport; -static DebuggerTransport transports [MAX_TRANSPORTS]; -static int ntransports; - -void -mono_debugger_agent_register_transport (DebuggerTransport *trans) -{ - register_transport (trans); -} - -static void -register_transport (DebuggerTransport *trans) -{ - g_assert (ntransports < MAX_TRANSPORTS); - - memcpy (&transports [ntransports], trans, sizeof (DebuggerTransport)); - ntransports ++; -} - static void transport_init (void) { @@ -1298,6 +1324,8 @@ transport_init (void) register_socket_transport (); register_socket_fd_transport (); #endif + int ntransports = 0; + DebuggerTransport *transports = mono_debugger_agent_get_transports (&ntransports); for (i = 0; i < ntransports; ++i) { if (!strcmp (agent_config.transport, transports [i].name)) @@ -1357,8 +1385,8 @@ transport_recv (void *buf, int len) return result; } -gboolean -mono_debugger_agent_transport_handshake (void) +static gboolean +debugger_agent_transport_handshake (void) { gboolean result; MONO_ENTER_GC_UNSAFE; @@ -1575,16 +1603,20 @@ static GHashTable *obj_to_objref; static MonoGHashTable *suspended_objs; #ifdef TARGET_WASM -void mono_init_debugger_agent_for_wasm (int log_level_parm) +void +mono_init_debugger_agent_for_wasm (int log_level_parm) { if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1) return; + int ntransports = 0; + DebuggerTransport *transports = mono_debugger_agent_get_transports (&ntransports); + ids_init(); objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); obj_to_objref = g_hash_table_new (NULL, NULL); - log_level = log_level; + log_level = log_level_parm; event_requests = g_ptr_array_new (); vm_start_event_sent = TRUE; transport = &transports [0]; @@ -2148,11 +2180,11 @@ get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX); frame = (MonoInterpFrameHandle*)ext->interp_exit_data; - ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame); + ji = mini_get_interp_callbacks_api ()->frame_get_jit_info (frame); if (domain) *domain = mono_domain_get (); if (out_ip) - *out_ip = mini_get_interp_callbacks ()->frame_get_ip (frame); + *out_ip = mini_get_interp_callbacks_api ()->frame_get_ip (frame); } return ji; } @@ -2454,7 +2486,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) g_assert (ji); /* Can't suspend in these methods */ method = jinfo_get_method (ji); - if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) + if (method->klass == mdbg_mono_defaults->string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) return; save_thread_context (ctx); @@ -2911,7 +2943,7 @@ static gint32 isFixedSizeArray (MonoClassField *f) CattrNamedArg *arginfo; int num_named_args; - mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, + mono_reflection_create_custom_attr_data_args_noalloc (mdbg_mono_defaults->corlib, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &num_named_args, &arginfo, error); if (!is_ok (error)) { ret = 0; @@ -3072,7 +3104,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls, gboolean f gboolean has_interp_resume_state = FALSE; MonoInterpFrameHandle interp_resume_frame = NULL; gpointer interp_resume_ip = 0; - mini_get_interp_callbacks ()->get_resume_state (jit_data, &has_interp_resume_state, &interp_resume_frame, &interp_resume_ip); + mini_get_interp_callbacks_api ()->get_resume_state (jit_data, &has_interp_resume_state, &interp_resume_frame, &interp_resume_ip); if (has_interp_resume_state && tls->frame_count > 0) { StackFrame *top_frame = tls->frames [0]; if (interp_resume_frame == top_frame->interp_frame) { @@ -3206,9 +3238,9 @@ dbg_path_get_basename (const char *filename) return g_strdup (&r[1]); } -static GENERATE_TRY_GET_CLASS_WITH_CACHE(hidden_klass, "System.Diagnostics", "DebuggerHiddenAttribute") -static GENERATE_TRY_GET_CLASS_WITH_CACHE(step_through_klass, "System.Diagnostics", "DebuggerStepThroughAttribute") -static GENERATE_TRY_GET_CLASS_WITH_CACHE(non_user_klass, "System.Diagnostics", "DebuggerNonUserCodeAttribute") +static GENERATE_TRY_GET_CLASS_WITH_CACHE_DBG (hidden_klass, "System.Diagnostics", "DebuggerHiddenAttribute") +static GENERATE_TRY_GET_CLASS_WITH_CACHE_DBG (step_through_klass, "System.Diagnostics", "DebuggerStepThroughAttribute") +static GENERATE_TRY_GET_CLASS_WITH_CACHE_DBG (non_user_klass, "System.Diagnostics", "DebuggerNonUserCodeAttribute") static void init_jit_info_dbg_attrs (MonoJitInfo *ji) @@ -3721,7 +3753,7 @@ runtime_initialized (MonoProfiler *prof) { process_profiler_event (EVENT_KIND_VM_START, mono_thread_current ()); if (CHECK_PROTOCOL_VERSION (2, 59)) - process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, (mono_defaults.corlib->assembly)); + process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, (mdbg_mono_defaults->corlib->assembly)); if (agent_config.defer) { ERROR_DECL (error); start_debugger_thread (error); @@ -4130,7 +4162,7 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) //This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not //since thread probably changed... int -mono_get_this_async_id (DbgEngineStackFrame *frame) +mono_de_frame_async_id (DbgEngineStackFrame *frame) { MonoClassField *builder_field; gpointer builder; @@ -4326,7 +4358,7 @@ user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data) /* * Called by System.Diagnostics.Debugger:Break (). */ -void +static void mono_dbg_debugger_agent_user_break (void) { if (agent_config.enabled) { @@ -4348,7 +4380,7 @@ mono_dbg_debugger_agent_user_break (void) mono_loader_unlock (); process_event (EVENT_KIND_USER_BREAK, NULL, 0, &ctx, events, suspend_policy); - } else if (mini_debug_options.native_debugger_break) { + } else if (get_mini_debug_options ()->native_debugger_break) { G_BREAKPOINT (); } } @@ -4759,11 +4791,11 @@ debugger_agent_unhandled_exception (MonoException *exc) process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy); } -void -mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, +static void +debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) { - if (catch_ctx == NULL && catch_frame == NULL && mini_debug_options.suspend_on_unhandled && mono_object_class (exc) != mono_defaults.threadabortexception_class) { + if (catch_ctx == NULL && catch_frame == NULL && get_mini_debug_options ()->suspend_on_unhandled && mono_object_class (exc) != mdbg_mono_defaults->threadabortexception_class) { mono_runtime_printf_err ("Unhandled exception, suspending..."); while (1) ; @@ -6142,7 +6174,7 @@ mono_do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, gu PRINT_DEBUG_MSG (1, "[%p] Invoke result: %p, exc: %s, time: %ld ms.\n", (gpointer) (gsize) mono_native_thread_id_get (), res, exc ? m_class_get_name (exc->vtable->klass) : NULL, (long)mono_stopwatch_elapsed_ms (&watch)); if (exc) { buffer_add_byte (buf, 0); - buffer_add_value (buf, mono_get_object_type (), &exc, domain); + buffer_add_value (buf, mono_get_object_type_dbg (), &exc, domain); } else { gboolean out_this = FALSE; gboolean out_args = FALSE; @@ -6157,11 +6189,11 @@ mono_do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, gu } else if (sig->ret->type == MONO_TYPE_VOID && !m->string_ctor) { if (!strcmp (m->name, ".ctor")) { if (!m_class_is_valuetype (m->klass)) - buffer_add_value (buf, mono_get_object_type (), &this_arg, domain); + buffer_add_value (buf, mono_get_object_type_dbg (), &this_arg, domain); else buffer_add_value (buf, m_class_get_byval_arg (m->klass), this_buf, domain); } else { - buffer_add_value (buf, mono_get_void_type (), NULL, domain); + buffer_add_value (buf, mono_get_void_type_dbg (), NULL, domain); } } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) { if (sig->ret->byref) { @@ -6503,7 +6535,7 @@ module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoAr static void buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val) { - if (val && val->vtable->klass == mono_defaults.runtimetype_class) { + if (val && val->vtable->klass == mdbg_mono_defaults->runtimetype_class) { /* Special case these so the client doesn't have to handle Type objects */ buffer_add_byte (buf, VALUE_TYPE_ID_TYPE); @@ -6544,9 +6576,9 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass ERROR_DECL (error); SETUP_ICALL_FRAME; - typed_args_h = MONO_HANDLE_NEW (MonoArray, NULL); - named_args_h = MONO_HANDLE_NEW (MonoArray, NULL); - val_h = MONO_HANDLE_NEW (MonoObject, NULL); + typed_args_h = MONO_HANDLE_NEW_DBG (MonoArray, NULL); + named_args_h = MONO_HANDLE_NEW_DBG (MonoArray, NULL); + val_h = MONO_HANDLE_NEW_DBG (MonoObject, NULL); mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, typed_args_h, named_args_h, &arginfo, error); if (!is_ok (error)) { @@ -6708,7 +6740,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) wait_for_suspend (); #ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT - env_class = mono_class_try_load_from_name (mono_defaults.corlib, "System", "Environment"); + env_class = mono_class_try_load_from_name (mdbg_mono_defaults->corlib, "System", "Environment"); if (env_class) { ERROR_DECL (error); exit_method = mono_class_get_method_from_name_checked (env_class, "Exit", 1, 0, error); @@ -7089,7 +7121,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (exc_class) { req->modifiers [i].data.exc_class = exc_class; - if (!mono_class_is_assignable_from_internal (mono_defaults.exception_class, exc_class)) { + if (!mono_class_is_assignable_from_internal (mdbg_mono_defaults->exception_class, exc_class)) { g_free (req); return ERR_INVALID_ARGUMENT; } @@ -7750,7 +7782,7 @@ collect_interfaces (MonoClass *klass, GHashTable *ifaces, MonoError *error) static int get_static_field_value(MonoClassField* f, MonoClass* klass, MonoDomain* domain, MonoInternalThread* thread, Buffer* buf) { - MonoStringHandle string_handle = MONO_HANDLE_NEW(MonoString, NULL); // FIXME? Not always needed. + MonoStringHandle string_handle = MONO_HANDLE_NEW_DBG (MonoString, NULL); // FIXME? Not always needed. ERROR_DECL(error); guint8* val; MonoVTable* vtable; @@ -8692,19 +8724,19 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g g_error ("Could not load token due to %s", mono_error_get_message (error)); } - if (handle_class == mono_defaults.typehandle_class) { + if (handle_class == mdbg_mono_defaults->typehandle_class) { buffer_add_byte (buf, TOKEN_TYPE_TYPE); if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) buffer_add_typeid (buf, domain, (MonoClass *) val); else buffer_add_typeid (buf, domain, mono_class_from_mono_type_internal ((MonoType*)val)); - } else if (handle_class == mono_defaults.fieldhandle_class) { + } else if (handle_class == mdbg_mono_defaults->fieldhandle_class) { buffer_add_byte (buf, TOKEN_TYPE_FIELD); buffer_add_fieldid (buf, domain, (MonoClassField *)val); - } else if (handle_class == mono_defaults.methodhandle_class) { + } else if (handle_class == mdbg_mono_defaults->methodhandle_class) { buffer_add_byte (buf, TOKEN_TYPE_METHOD); buffer_add_methodid (buf, domain, (MonoMethod *)val); - } else if (handle_class == mono_defaults.string_class) { + } else if (handle_class == mdbg_mono_defaults->string_class) { char *s; s = mono_string_to_utf8_checked_internal ((MonoString *)val, error); @@ -9003,7 +9035,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (tls->frames [0]->de.ji->is_interp) { MonoJitTlsData *jit_data = thread->thread_info->jit_data; - mini_get_interp_callbacks ()->set_resume_state (jit_data, NULL, NULL, tls->frames [0]->interp_frame, (guint8*)tls->frames [0]->de.ji->code_start + sp.native_offset); + mini_get_interp_callbacks_api ()->set_resume_state (jit_data, NULL, NULL, tls->frames [0]->interp_frame, (guint8*)tls->frames [0]->de.ji->code_start + sp.native_offset); } else { MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->de.ji->code_start + sp.native_offset); } @@ -9045,12 +9077,12 @@ cmd_stack_frame_get_this (StackFrame *frame, MonoMethodSignature *sig, Buffer *b if (m_class_is_valuetype (frame->api_method->klass)) { if (!sig->hasthis) { MonoObject *p = NULL; - buffer_add_value (buf, mono_get_object_type (), &p, frame->de.domain); + buffer_add_value (buf, mono_get_object_type_dbg (), &p, frame->de.domain); } else { if (frame->de.ji->is_interp) { guint8 *addr; - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_this (frame->interp_frame); buffer_add_value_full (buf, m_class_get_this_arg (frame->actual_method->klass), addr, frame->de.domain, FALSE, NULL, 1); } else { @@ -9065,7 +9097,7 @@ cmd_stack_frame_get_this (StackFrame *frame, MonoMethodSignature *sig, Buffer *b if (frame->de.ji->is_interp) { guint8 *addr; - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_this (frame->interp_frame); buffer_add_value_full (buf, m_class_get_byval_arg (frame->api_method->klass), addr, frame->de.domain, FALSE, NULL, 1); } else { @@ -9082,7 +9114,7 @@ cmd_stack_frame_get_parameter (StackFrame *frame, MonoMethodSignature *sig, int if (frame->de.ji->is_interp) { guint8 *addr; - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_arg (frame->interp_frame, pos); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_arg (frame->interp_frame, pos); buffer_add_value_full (buf, sig->params [pos], addr, frame->de.domain, FALSE, NULL, 1); } else { @@ -9205,7 +9237,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (frame->de.ji->is_interp) { guint8 *addr; - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_local (frame->interp_frame, pos); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_local (frame->interp_frame, pos); buffer_add_value_full (buf, header->locals [pos], addr, frame->de.domain, FALSE, NULL, 1); } else { @@ -9271,9 +9303,9 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) guint8 *addr; if (is_arg) - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_arg (frame->interp_frame, pos); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_arg (frame->interp_frame, pos); else - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_local (frame->interp_frame, pos); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_local (frame->interp_frame, pos); err = mono_de_set_interp_var (t, addr, val_buf); if (err != ERR_NONE) return err; @@ -9306,7 +9338,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (frame->de.ji->is_interp) { guint8 *addr; - addr = (guint8*)mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame); + addr = (guint8*)mini_get_interp_callbacks_api ()->frame_get_this (frame->interp_frame); err = mono_de_set_interp_var (m_class_get_this_arg (frame->actual_method->klass), addr, val_buf); if (err != ERR_NONE) return err; @@ -9548,7 +9580,7 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoClassField *f = NULL; MonoClass *k; gboolean found; - MonoStringHandle string_handle = MONO_HANDLE_NEW (MonoString, NULL); // FIXME? Not always needed. + MonoStringHandle string_handle = MONO_HANDLE_NEW_DBG (MonoString, NULL); // FIXME? Not always needed. if (command == CMD_OBJECT_REF_IS_COLLECTED) { objid = decode_objid (p, &p, end); @@ -10226,36 +10258,28 @@ debugger_thread (void *arg) return 0; } -void -mono_debugger_agent_init (void) -{ - MonoDebuggerCallbacks cbs; - - memset (&cbs, 0, sizeof (cbs)); - cbs.version = MONO_DBG_CALLBACKS_VERSION; - cbs.parse_options = debugger_agent_parse_options; - cbs.init = debugger_agent_init; - cbs.breakpoint_hit = debugger_agent_breakpoint_hit; - cbs.single_step_event = debugger_agent_single_step_event; - cbs.single_step_from_context = debugger_agent_single_step_from_context; - cbs.breakpoint_from_context = debugger_agent_breakpoint_from_context; - cbs.free_mem_manager = debugger_agent_free_mem_manager; - cbs.unhandled_exception = debugger_agent_unhandled_exception; - cbs.handle_exception = mono_debugger_agent_handle_exception; - cbs.begin_exception_filter = debugger_agent_begin_exception_filter; - cbs.end_exception_filter = debugger_agent_end_exception_filter; - cbs.user_break = mono_dbg_debugger_agent_user_break; - cbs.debug_log = debugger_agent_debug_log; - cbs.debug_log_is_enabled = debugger_agent_debug_log_is_enabled; - cbs.send_crash = mono_debugger_agent_send_crash; - - mini_install_dbg_callbacks (&cbs); -} void -mono_debugger_agent_parse_options (char *options) +debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table) { - sdb_options = options; + fn_table->parse_options = debugger_agent_parse_options; + fn_table->init = debugger_agent_init; + fn_table->breakpoint_hit = debugger_agent_breakpoint_hit; + fn_table->single_step_event = debugger_agent_single_step_event; + fn_table->single_step_from_context = debugger_agent_single_step_from_context; + fn_table->breakpoint_from_context = debugger_agent_breakpoint_from_context; + fn_table->free_mem_manager = debugger_agent_free_mem_manager; + fn_table->unhandled_exception = debugger_agent_unhandled_exception; + fn_table->handle_exception = debugger_agent_handle_exception; + fn_table->begin_exception_filter = debugger_agent_begin_exception_filter; + fn_table->end_exception_filter = debugger_agent_end_exception_filter; + fn_table->user_break = mono_dbg_debugger_agent_user_break; + fn_table->debug_log = debugger_agent_debug_log; + fn_table->debug_log_is_enabled = debugger_agent_debug_log_is_enabled; + fn_table->send_crash = mono_debugger_agent_send_crash; + fn_table->transport_handshake = debugger_agent_transport_handshake; } + + #endif /* DISABLE_SDB */ diff --git a/src/mono/mono/component/debugger-agent.h b/src/mono/mono/component/debugger-agent.h new file mode 100644 index 00000000000..60f42448202 --- /dev/null +++ b/src/mono/mono/component/debugger-agent.h @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef __MONO_DEBUGGER_AGENT_COMPONENT_H__ +#define __MONO_DEBUGGER_AGENT_COMPONENT_H__ + +#include +#include "debugger.h" +#include + +void +debugger_agent_add_function_pointers (MonoComponentDebugger* fn_table); + +void +mono_ss_calculate_framecount (void *the_tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); + +void +mono_ss_discard_frame_context (void *the_tls); + +#ifdef TARGET_WASM +DebuggerTlsData* +mono_wasm_get_tls (void); + +void +mono_init_debugger_agent_for_wasm (int log_level); + +void +mono_wasm_save_thread_context (void); + +#endif + +void +mini_wasm_debugger_add_function_pointers (MonoComponentDebugger* fn_table); + +MdbgProtErrorCode +mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp); + +MdbgProtErrorCode +mono_process_dbg_packet (int id, MdbgProtCommandSet command_set, int command, gboolean *no_reply, guint8 *p, guint8 *end, MdbgProtBuffer *buf); + +void +mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); + +void* +mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, MdbgProtEventKind kind); + +int +mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); + +void +mono_ss_args_destroy (SingleStepArgs *ss_args); + +int +mono_de_frame_async_id (DbgEngineStackFrame *frame); +#endif diff --git a/src/mono/mono/mini/debugger-engine.c b/src/mono/mono/component/debugger-engine.c similarity index 95% rename from src/mono/mono/mini/debugger-engine.c rename to src/mono/mono/component/debugger-engine.c index 8c85227e842..73102833571 100644 --- a/src/mono/mono/mini/debugger-engine.c +++ b/src/mono/mono/component/debugger-engine.c @@ -10,13 +10,13 @@ */ #include -#include "mini-runtime.h" +#include #if !defined (DISABLE_SDB) || defined(TARGET_WASM) #include -#include "seq-points.h" -#include "aot-runtime.h" +#include +#include #include "debugger-engine.h" #include "debugger-state-machine.h" #include @@ -24,6 +24,8 @@ static void mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args); static gboolean mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, MonoContext *ctx, MonoMethod* method); +static gpointer get_this_addr(DbgEngineStackFrame* the_frame); +static MonoMethod* get_set_notification_method(MonoClass* async_builder_class); static DebuggerEngineCallbacks rt_callbacks; @@ -89,17 +91,6 @@ mono_de_foreach_domain (GHFunc func, gpointer user_data) g_hash_table_foreach (domains, func, user_data); } -/* - * LOCKING: Takes the loader lock - */ -void -mono_de_domain_remove (MonoDomain *domain) -{ - mono_loader_lock (); - g_hash_table_remove (domains, domain); - mono_loader_unlock (); -} - /* * LOCKING: Takes the loader lock */ @@ -210,7 +201,7 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo PRINT_DEBUG_MSG (1, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset); } else if (count == 0) { if (ji->is_interp) { - mini_get_interp_callbacks ()->set_breakpoint (ji, inst->ip); + mini_get_interp_callbacks_api ()->set_breakpoint (ji, inst->ip); } else { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_set_breakpoint (ji, inst->ip); @@ -239,7 +230,7 @@ remove_breakpoint (BreakpointInstance *inst) if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) { if (ji->is_interp) { - mini_get_interp_callbacks ()->clear_breakpoint (ji, ip); + mini_get_interp_callbacks_api ()->clear_breakpoint (ji, ip); } else { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_clear_breakpoint (ji, ip); @@ -464,17 +455,6 @@ mono_de_set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, M return bp; } -MonoBreakpoint * -mono_de_get_breakpoint_by_id (int id) -{ - for (int i = 0; i < breakpoints->len; ++i) { - MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i); - if (bp->req->id == id) - return bp; - } - return NULL; -} - void mono_de_clear_breakpoint (MonoBreakpoint *bp) { @@ -617,7 +597,7 @@ mono_de_start_single_stepping (void) #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_start_single_stepping (); #endif - mini_get_interp_callbacks ()->start_single_stepping (); + mini_get_interp_callbacks_api ()->start_single_stepping (); } } @@ -630,7 +610,7 @@ mono_de_stop_single_stepping (void) #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_stop_single_stepping (); #endif - mini_get_interp_callbacks ()->stop_single_stepping (); + mini_get_interp_callbacks_api ()->stop_single_stepping (); } } @@ -656,11 +636,11 @@ get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX); frame = (MonoInterpFrameHandle*)ext->interp_exit_data; - ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame); + ji = mini_get_interp_callbacks_api ()->frame_get_jit_info (frame); if (domain) *domain = mono_domain_get (); if (out_ip) - *out_ip = mini_get_interp_callbacks ()->frame_get_ip (frame); + *out_ip = mini_get_interp_callbacks_api ()->frame_get_ip (frame); } return ji; } @@ -775,7 +755,7 @@ mono_de_cancel_ss (SingleStepReq *req) } void -mono_de_cancel_all_ss () +mono_de_cancel_all_ss (void) { int i; for (i = 0; i < the_ss_reqs->len; ++i) { @@ -832,7 +812,7 @@ mono_de_process_single_step (void *tls, gboolean from_signal) * Stopping in memset makes half-initialized vtypes visible. * Stopping in memcpy makes half-copied vtypes visible. */ - if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) + if (method->klass == mdbg_mono_defaults->string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) goto exit; /* @@ -892,13 +872,13 @@ mono_de_process_single_step (void *tls, gboolean from_signal) g_ptr_array_add (reqs, ss_req->req); void *bp_events; - bp_events = rt_callbacks.create_breakpoint_events (reqs, NULL, ji, EVENT_KIND_BREAKPOINT); + bp_events = mono_dbg_create_breakpoint_events (reqs, NULL, ji, EVENT_KIND_BREAKPOINT); g_ptr_array_free (reqs, TRUE); mono_loader_unlock (); - rt_callbacks.process_breakpoint_events (bp_events, method, ctx, il_offset); + mono_dbg_process_breakpoint_events (bp_events, method, ctx, il_offset); exit: mono_de_ss_req_release (ss_req); @@ -1123,7 +1103,7 @@ mono_de_process_breakpoint (void *void_tls, gboolean from_signal) mono_debug_free_method_async_debug_info (asyncMethod); //breakpoint was hit in parallelly executing async method, ignore it - if (ss_req->async_id != rt_callbacks.get_this_async_id (frames [0])) + if (ss_req->async_id != mono_de_frame_async_id (frames [0])) continue; } @@ -1154,14 +1134,14 @@ mono_de_process_breakpoint (void *void_tls, gboolean from_signal) mono_de_ss_start (ss_req, &args); } - void *bp_events = rt_callbacks.create_breakpoint_events (ss_reqs, bp_reqs, ji, kind); + void *bp_events = mono_dbg_create_breakpoint_events (ss_reqs, bp_reqs, ji, kind); mono_loader_unlock (); g_ptr_array_free (bp_reqs, TRUE); g_ptr_array_free (ss_reqs, TRUE); - rt_callbacks.process_breakpoint_events (bp_events, method, ctx, sp.il_offset); + mono_dbg_process_breakpoint_events (bp_events, method, ctx, sp.il_offset); } /* @@ -1352,7 +1332,7 @@ mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args) // of this await call and sets async_id so we can distinguish it from parallel executions for (i = 0; i < asyncMethod->num_awaits; i++) { if (sp->il_offset == asyncMethod->yield_offsets [i]) { - ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]); + ss_req->async_id = mono_de_frame_async_id (frames [0]); ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]); g_hash_table_destroy (ss_req_bp_cache); mono_debug_free_method_async_debug_info (asyncMethod); @@ -1368,9 +1348,9 @@ mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args) } if (ss_req->depth == STEP_DEPTH_OUT) { //If we are inside `async void` method, do normal step-out - if (rt_callbacks.set_set_notification_for_wait_completion_flag (frames [0])) { - ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]); - ss_req->async_stepout_method = rt_callbacks.get_notify_debugger_of_wait_completion_method (); + if (set_set_notification_for_wait_completion_flag (frames [0])) { + ss_req->async_id = mono_de_frame_async_id (frames [0]); + ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method (); ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0); g_hash_table_destroy (ss_req_bp_cache); mono_debug_free_method_async_debug_info (asyncMethod); @@ -1499,7 +1479,7 @@ mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args) mono_loader_unlock (); cleanup: - rt_callbacks.ss_args_destroy (ss_args); + mono_ss_args_destroy (ss_args); } @@ -1542,7 +1522,7 @@ mono_de_ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, S } SingleStepArgs args; - err = rt_callbacks.ss_create_init_args (ss_req, &args); + err = mono_ss_create_init_args (ss_req, &args); if (err) return err; g_ptr_array_add (the_ss_reqs, ss_req); @@ -1671,12 +1651,12 @@ get_object_id_for_debugger_method (MonoClass* async_builder_class) return method; } -gpointer +static gpointer get_this_addr (DbgEngineStackFrame *the_frame) { StackFrame *frame = (StackFrame *)the_frame; if (frame->de.ji->is_interp) - return mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame); + return mini_get_interp_callbacks_api ()->frame_get_this (frame->interp_frame); MonoDebugVarInfo *var = frame->jit->this_var; if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) @@ -1716,7 +1696,7 @@ get_async_method_builder (DbgEngineStackFrame *frame) return builder; } -MonoMethod* +static MonoMethod* get_set_notification_method (MonoClass* async_builder_class) { ERROR_DECL (error); @@ -1739,7 +1719,7 @@ get_notify_debugger_of_wait_completion_method (void) if (notify_debugger_of_wait_completion_method_cache != NULL) return notify_debugger_of_wait_completion_method_cache; ERROR_DECL (error); - MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task"); + MonoClass* task_class = mono_class_load_from_name (mdbg_mono_defaults->corlib, "System.Threading.Tasks", "Task"); GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, 1, FALSE, error); mono_error_assert_ok (error); g_assert (array->len == 1); diff --git a/src/mono/mono/mini/debugger-engine.h b/src/mono/mono/component/debugger-engine.h similarity index 78% rename from src/mono/mono/mini/debugger-engine.h rename to src/mono/mono/component/debugger-engine.h index 20fe88fedb8..a3c30d6d995 100644 --- a/src/mono/mono/mini/debugger-engine.h +++ b/src/mono/mono/component/debugger-engine.h @@ -2,15 +2,15 @@ * \file */ -#ifndef __MONO_DEBUGGER_ENGINE_H__ -#define __MONO_DEBUGGER_ENGINE_H__ +#ifndef __MONO_DEBUGGER_ENGINE_COMPONENT_H__ +#define __MONO_DEBUGGER_ENGINE_COMPONENT_H__ -#include "mini.h" +#include #include -#include +#include "debugger-state-machine.h" #include #include -#include +#include "debugger-protocol.h" #define ModifierKind MdbgProtModifierKind #define StepDepth MdbgProtStepDepth @@ -263,59 +263,6 @@ #define FRAME_FLAG_DEBUGGER_INVOKE MDBGPROT_FRAME_FLAG_DEBUGGER_INVOKE #define FRAME_FLAG_NATIVE_TRANSITION MDBGPROT_FRAME_FLAG_NATIVE_TRANSITION -typedef struct { - ModifierKind kind; - union { - int count; /* For kind == MOD_KIND_COUNT */ - MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */ - MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */ - MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */ - GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */ - GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ - StepFilter filter; /* For kind == MOD_KIND_STEP */ - } data; - gboolean caught, uncaught, subclasses, not_filtered_feature, everything_else; /* For kind == MOD_KIND_EXCEPTION_ONLY */ -} Modifier; - -typedef struct{ - int id; - int event_kind; - int suspend_policy; - int nmodifiers; - gpointer info; - Modifier modifiers [MONO_ZERO_LEN_ARRAY]; -} EventRequest; - -/* - * Describes a single step request. - */ -typedef struct { - EventRequest *req; - MonoInternalThread *thread; - StepDepth depth; - StepSize size; - StepFilter filter; - gpointer last_sp; - gpointer start_sp; - MonoMethod *start_method; - MonoMethod *last_method; - int last_line; - /* Whenever single stepping is performed using start/stop_single_stepping () */ - gboolean global; - /* The list of breakpoints used to implement step-over */ - GSList *bps; - /* The number of frames at the start of a step-over */ - int nframes; - /* If set, don't stop in methods that are not part of user assemblies */ - MonoAssembly** user_assemblies; - /* Used to distinguish stepping breakpoint hits in parallel tasks executions */ - int async_id; - /* Used to know if we are in process of async step-out and distishing from exception breakpoints */ - MonoMethod* async_stepout_method; - int refcount; -} SingleStepReq; - - /* * Contains information about an inserted breakpoint. */ @@ -326,64 +273,6 @@ typedef struct { MonoDomain *domain; } BreakpointInstance; -/* - * Contains generic information about a breakpoint. - */ -typedef struct { - /* - * The method where the breakpoint is placed. Can be NULL in which case it - * is inserted into every method. This is used to implement method entry/ - * exit events. Can be a generic method definition, in which case the - * breakpoint is inserted into every instance. - */ - MonoMethod *method; - long il_offset; - EventRequest *req; - /* - * A list of BreakpointInstance structures describing where the breakpoint - * was inserted. There could be more than one because of - * generics/appdomains/method entry/exit. - */ - GPtrArray *children; -} MonoBreakpoint; - -typedef struct { - MonoJitInfo *ji; - MonoDomain *domain; - MonoMethod *method; - guint32 native_offset; -} DbgEngineStackFrame; - -typedef struct { - /* - * Method where to start single stepping - */ - MonoMethod *method; - - /* - * If ctx is set, tls must belong to the same thread. - */ - MonoContext *ctx; - void *tls; - - /* - * Stopped at a throw site - */ - gboolean step_to_catch; - - /* - * Sequence point to start from. - */ - SeqPoint sp; - MonoSeqPointInfo *info; - - /* - * Frame data, will be freed at the end of ss_start if provided - */ - DbgEngineStackFrame **frames; - int nframes; -} SingleStepArgs; - /* * OBJECT IDS */ @@ -430,9 +319,6 @@ typedef struct gboolean has_ctx; } StackFrame; -void mono_debugger_free_objref (gpointer value); - -typedef int DbgEngineErrorCode; #define DE_ERR_NONE 0 // WARNING WARNING WARNING // Error codes MUST match those of sdb for now @@ -453,32 +339,6 @@ mono_debugger_set_thread_state (DebuggerTlsData *ref, MonoDebuggerThreadState ex MonoDebuggerThreadState mono_debugger_get_thread_state (DebuggerTlsData *ref); -typedef struct { - MonoContext *(*tls_get_restore_state) (void *tls); - gboolean (*try_process_suspend) (void *tls, MonoContext *ctx, gboolean from_breakpoint); - gboolean (*begin_breakpoint_processing) (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal); - void (*begin_single_step_processing) (MonoContext *ctx, gboolean from_signal); - - void (*ss_discard_frame_context) (void *tls); - void (*ss_calculate_framecount) (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); - gboolean (*ensure_jit) (DbgEngineStackFrame *frame); - int (*ensure_runtime_is_suspended) (void); - - int (*get_this_async_id) (DbgEngineStackFrame *frame); - - void* (*create_breakpoint_events) (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind); - void (*process_breakpoint_events) (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); - - gboolean (*set_set_notification_for_wait_completion_flag) (DbgEngineStackFrame *f); - MonoMethod* (*get_notify_debugger_of_wait_completion_method)(void); - - int (*ss_create_init_args) (SingleStepReq *ss_req, SingleStepArgs *args); - void (*ss_args_destroy) (SingleStepArgs *ss_args); - int (*handle_multiple_ss_requests)(void); -} DebuggerEngineCallbacks; - - -void mono_de_init (DebuggerEngineCallbacks *cbs); void mono_de_cleanup (void); void mono_de_set_log_level (int level, FILE *file); @@ -489,15 +349,13 @@ void mono_de_unlock (void); // domain handling void mono_de_foreach_domain (GHFunc func, gpointer user_data); void mono_de_domain_add (MonoDomain *domain); -void mono_de_domain_remove (MonoDomain *domain); //breakpoints void mono_de_clear_breakpoint (MonoBreakpoint *bp); MonoBreakpoint* mono_de_set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError *error); void mono_de_collect_breakpoints_by_sp (SeqPoint *sp, MonoJitInfo *ji, GPtrArray *ss_reqs, GPtrArray *bp_reqs); void mono_de_clear_breakpoints_for_domain (MonoDomain *domain); -void mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji); -MonoBreakpoint * mono_de_get_breakpoint_by_id (int id); +void mono_de_add_pending_breakpoints(MonoMethod* method, MonoJitInfo* ji); //single stepping void mono_de_start_single_stepping (void); @@ -513,12 +371,12 @@ DbgEngineErrorCode mono_de_set_interp_var (MonoType *t, gpointer addr, guint8 *v gboolean set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame); MonoClass * get_class_to_get_builder_field(DbgEngineStackFrame *frame); -gpointer get_this_addr (DbgEngineStackFrame *the_frame); gpointer get_async_method_builder (DbgEngineStackFrame *frame); -MonoMethod* get_set_notification_method (MonoClass* async_builder_class); MonoMethod* get_notify_debugger_of_wait_completion_method (void); MonoMethod* get_object_id_for_debugger_method (MonoClass* async_builder_class); +void mono_debugger_free_objref(gpointer value); + #ifdef HOST_ANDROID #define PRINT_DEBUG_MSG(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; } } while (0) @@ -545,14 +403,5 @@ void win32_debugger_log(FILE *stream, const gchar *format, ...); #define PRINT_MSG(...) g_print (__VA_ARGS__) #endif -int -mono_ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args); - -void -mono_ss_args_destroy (SingleStepArgs *ss_args); - -int -mono_get_this_async_id (DbgEngineStackFrame *frame); - void -mono_ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); +mono_de_init(DebuggerEngineCallbacks* cbs); diff --git a/src/mono/mono/mini/debugger-mono-compat.h b/src/mono/mono/component/debugger-mono-compat.h similarity index 100% rename from src/mono/mono/mini/debugger-mono-compat.h rename to src/mono/mono/component/debugger-mono-compat.h diff --git a/src/mono/mono/mini/debugger-protocol.c b/src/mono/mono/component/debugger-protocol.c similarity index 100% rename from src/mono/mono/mini/debugger-protocol.c rename to src/mono/mono/component/debugger-protocol.c diff --git a/src/mono/mono/mini/debugger-protocol.h b/src/mono/mono/component/debugger-protocol.h similarity index 100% rename from src/mono/mono/mini/debugger-protocol.h rename to src/mono/mono/component/debugger-protocol.h diff --git a/src/mono/mono/mini/debugger-state-machine.c b/src/mono/mono/component/debugger-state-machine.c similarity index 99% rename from src/mono/mono/mini/debugger-state-machine.c rename to src/mono/mono/component/debugger-state-machine.c index 1aff76cbade..c5b96b7aada 100644 --- a/src/mono/mono/mini/debugger-state-machine.c +++ b/src/mono/mono/component/debugger-state-machine.c @@ -12,11 +12,10 @@ #include #include -#include -#include +#include "debugger-state-machine.h" #include #include -#include +#include "debugger-engine.h" #include #include diff --git a/src/mono/mono/mini/debugger-state-machine.h b/src/mono/mono/component/debugger-state-machine.h similarity index 100% rename from src/mono/mono/mini/debugger-state-machine.h rename to src/mono/mono/component/debugger-state-machine.h diff --git a/src/mono/mono/component/debugger-stub.c b/src/mono/mono/component/debugger-stub.c new file mode 100644 index 00000000000..1c5467adadd --- /dev/null +++ b/src/mono/mono/component/debugger-stub.c @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include + +#include "mono/mini/mini-runtime.h" +#include "debugger-agent.h" + +#include + +static bool +debugger_avaliable (void); + +static void +stub_debugger_parse_options (char *options); + +static void +stub_debugger_init (MonoDefaults *mono_defaults); + +static void +stub_debugger_breakpoint_hit (void *sigctx); + +static void +stub_debugger_single_step_event (void *sigctx); + +static void +stub_debugger_free_mem_manager (gpointer mem_manager); + +static void +stub_debugger_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); + +static void +stub_debugger_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); + +static void +stub_debugger_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); + +static void +stub_debugger_user_break (void); + +static void +stub_debugger_debug_log (int level, MonoString *category, MonoString *message); + +static gboolean +stub_debugger_debug_log_is_enabled (void); + +static void +stub_debugger_unhandled_exception (MonoException *exc); + +static void +stub_debugger_single_step_from_context (MonoContext *ctx); + +static void +stub_debugger_breakpoint_from_context (MonoContext *ctx); + +static void +stub_debugger_send_crash (char *json_dump, MonoStackHash *hashes, int pause); + +static gboolean +stub_debugger_transport_handshake (void); + +static void +stub_mono_wasm_breakpoint_hit (void); + +static void +stub_mono_wasm_single_step_hit (void); + +static MonoComponentDebugger fn_table = { + { MONO_COMPONENT_ITF_VERSION, &debugger_avaliable }, + &stub_debugger_init, + &stub_debugger_user_break, + &stub_debugger_parse_options, + &stub_debugger_breakpoint_hit, + &stub_debugger_single_step_event, + &stub_debugger_single_step_from_context, + &stub_debugger_breakpoint_from_context, + &stub_debugger_free_mem_manager, + &stub_debugger_unhandled_exception, + &stub_debugger_handle_exception, + &stub_debugger_begin_exception_filter, + &stub_debugger_end_exception_filter, + &stub_debugger_debug_log, + &stub_debugger_debug_log_is_enabled, + &stub_debugger_send_crash, + &stub_debugger_transport_handshake, + + //wasm + &stub_mono_wasm_breakpoint_hit, + &stub_mono_wasm_single_step_hit +}; + +static bool +debugger_avaliable (void) +{ + return false; +} + +MonoComponentDebugger * +mono_component_debugger_init (void) +{ + return &fn_table; +} + +static void +stub_debugger_parse_options (char *options) +{ + if (!options) + return; + g_error ("This runtime is configured with the debugger agent disabled."); +} + +static void +stub_debugger_init (MonoDefaults *mono_defaults) +{ +} + +static void +stub_debugger_breakpoint_hit (void *sigctx) +{ +} + +static void +stub_debugger_single_step_event (void *sigctx) +{ +} + +static void +stub_debugger_free_mem_manager (gpointer mem_manager) +{ +} + +static void +stub_debugger_handle_exception (MonoException *exc, MonoContext *throw_ctx, + MonoContext *catch_ctx, StackFrameInfo *catch_frame) +{ +} + +static void +stub_debugger_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx) +{ +} + +static void +stub_debugger_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx) +{ +} + +static void +stub_debugger_user_break (void) +{ + G_BREAKPOINT (); +} + +static void +stub_debugger_debug_log (int level, MonoString *category, MonoString *message) +{ +} + +static gboolean +stub_debugger_debug_log_is_enabled (void) +{ + return FALSE; +} + +static void +stub_debugger_unhandled_exception (MonoException *exc) +{ + g_assert_not_reached (); +} + +static void +stub_debugger_single_step_from_context (MonoContext *ctx) +{ + g_assert_not_reached (); +} + +static void +stub_debugger_breakpoint_from_context (MonoContext *ctx) +{ + g_assert_not_reached (); +} + +static void +stub_debugger_send_crash (char *json_dump, MonoStackHash *hashes, int pause) +{ +} + +static gboolean +stub_debugger_transport_handshake (void) +{ + g_assert_not_reached(); +} + +static void +stub_mono_wasm_breakpoint_hit (void) +{ +} + +static void +stub_mono_wasm_single_step_hit (void) +{ +} diff --git a/src/mono/mono/component/debugger.c b/src/mono/mono/component/debugger.c new file mode 100644 index 00000000000..2ebb4f46aae --- /dev/null +++ b/src/mono/mono/component/debugger.c @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include + +#include "mono/mini/mini-runtime.h" + +#include +#include "debugger-agent.h" +#include "debugger-engine.h" + +static bool +debugger_avaliable (void); + +static MonoComponentDebugger fn_table = { + { MONO_COMPONENT_ITF_VERSION, &debugger_avaliable } +}; + +static bool +debugger_avaliable (void) +{ + return true; +} + + +MonoComponentDebugger * +mono_component_debugger_init (void) +{ + debugger_agent_add_function_pointers (&fn_table); +#ifdef TARGET_WASM + mini_wasm_debugger_add_function_pointers (&fn_table); +#endif + return &fn_table; +} diff --git a/src/mono/mono/component/debugger.h b/src/mono/mono/component/debugger.h new file mode 100644 index 00000000000..de50c564113 --- /dev/null +++ b/src/mono/mono/component/debugger.h @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef _MONO_COMPONENT_DEBUGGER_H +#define _MONO_COMPONENT_DEBUGGER_H + +#include +#include "mono/metadata/threads-types.h" +#include "mono/utils/mono-error.h" +#include "mono/utils/mono-context.h" +#include "mono/utils/mono-stack-unwinding.h" +#include "mono/component/component.h" +#include "debugger-protocol.h" +#include "mono/metadata/seq-points-data.h" + +typedef struct { + MdbgProtModifierKind kind; + union { + int count; /* For kind == MOD_KIND_COUNT */ + MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */ + MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */ + MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */ + GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */ + GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ + MdbgProtStepFilter filter; /* For kind == MOD_KIND_STEP */ + } data; + gboolean caught, uncaught, subclasses, not_filtered_feature, everything_else; /* For kind == MOD_KIND_EXCEPTION_ONLY */ +} Modifier; + +typedef struct{ + int id; + int event_kind; + int suspend_policy; + int nmodifiers; + gpointer info; + Modifier modifiers [MONO_ZERO_LEN_ARRAY]; +} EventRequest; + +typedef struct { + MonoJitInfo *ji; + MonoDomain *domain; + MonoMethod *method; + guint32 native_offset; +} DbgEngineStackFrame; + + +typedef struct { + /* + * Method where to start single stepping + */ + MonoMethod *method; + + /* + * If ctx is set, tls must belong to the same thread. + */ + MonoContext *ctx; + void *tls; + + /* + * Stopped at a throw site + */ + gboolean step_to_catch; + + /* + * Sequence point to start from. + */ + SeqPoint sp; + MonoSeqPointInfo *info; + + /* + * Frame data, will be freed at the end of ss_start if provided + */ + DbgEngineStackFrame **frames; + int nframes; +} SingleStepArgs; + +typedef struct { + const char* name; + void (*connect) (const char* address); + void (*close1) (void); + void (*close2) (void); + gboolean(*send) (void* buf, int len); + int (*recv) (void* buf, int len); +} DebuggerTransport; + +typedef struct { + EventRequest *req; + MonoInternalThread *thread; + MdbgProtStepDepth depth; + MdbgProtStepSize size; + MdbgProtStepFilter filter; + gpointer last_sp; + gpointer start_sp; + MonoMethod *start_method; + MonoMethod *last_method; + int last_line; + /* Whenever single stepping is performed using start/stop_single_stepping () */ + gboolean global; + /* The list of breakpoints used to implement step-over */ + GSList *bps; + /* The number of frames at the start of a step-over */ + int nframes; + /* If set, don't stop in methods that are not part of user assemblies */ + MonoAssembly** user_assemblies; + /* Used to distinguish stepping breakpoint hits in parallel tasks executions */ + int async_id; + /* Used to know if we are in process of async step-out and distishing from exception breakpoints */ + MonoMethod* async_stepout_method; + int refcount; +} SingleStepReq; + +typedef struct { + MonoContext *(*tls_get_restore_state) (void *tls); + gboolean (*try_process_suspend) (void *tls, MonoContext *ctx, gboolean from_breakpoint); + gboolean (*begin_breakpoint_processing) (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal); + void (*begin_single_step_processing) (MonoContext *ctx, gboolean from_signal); + + void (*ss_discard_frame_context) (void *tls); + void (*ss_calculate_framecount) (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***frames, int *nframes); + gboolean (*ensure_jit) (DbgEngineStackFrame *frame); + int (*ensure_runtime_is_suspended) (void); + int (*handle_multiple_ss_requests)(void); +} DebuggerEngineCallbacks; + +/* + * Contains generic information about a breakpoint. + */ +typedef struct { + /* + * The method where the breakpoint is placed. Can be NULL in which case it + * is inserted into every method. This is used to implement method entry/ + * exit events. Can be a generic method definition, in which case the + * breakpoint is inserted into every instance. + */ + MonoMethod* method; + long il_offset; + EventRequest* req; + /* + * A list of BreakpointInstance structures describing where the breakpoint + * was inserted. There could be more than one because of + * generics/appdomains/method entry/exit. + */ + GPtrArray* children; +} MonoBreakpoint; + +typedef int DbgEngineErrorCode; + +typedef struct _InvokeData InvokeData; + +struct _InvokeData +{ + int id; + int flags; + guint8 *p; + guint8 *endp; + /* This is the context which needs to be restored after the invoke */ + MonoContext ctx; + gboolean has_ctx; + /* + * If this is set, invoke this method with the arguments given by ARGS. + */ + MonoMethod *method; + gpointer *args; + guint32 suspend_count; + int nmethods; + + InvokeData *last_invoke; +}; + +typedef struct _DebuggerTlsData DebuggerTlsData; + +typedef struct MonoComponentDebugger { + MonoComponent component; + void (*init) (MonoDefaults *mono_defaults); + void (*user_break) (void); + void (*parse_options) (char *options); + void (*breakpoint_hit) (void *sigctx); + void (*single_step_event) (void *sigctx); + void (*single_step_from_context) (MonoContext *ctx); + void (*breakpoint_from_context) (MonoContext *ctx); + void (*free_mem_manager) (gpointer mem_manager); + void (*unhandled_exception) (MonoException *exc); + void (*handle_exception) (MonoException *exc, MonoContext *throw_ctx, + MonoContext *catch_ctx, MonoStackFrameInfo *catch_frame); + void (*begin_exception_filter) (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); + void (*end_exception_filter) (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); + void (*debug_log) (int level, MonoString *category, MonoString *message); + gboolean (*debug_log_is_enabled) (void); + void (*send_crash) (char *json_dump, MonoStackHash *hashes, int pause); + gboolean (*transport_handshake) (void); + + //wasm + void (*mono_wasm_breakpoint_hit) (void); + void (*mono_wasm_single_step_hit) (void); + +} MonoComponentDebugger; + + +extern MonoDefaults *mdbg_mono_defaults; + +#define DE_ERR_NONE 0 +// WARNING WARNING WARNING +// Error codes MUST match those of sdb for now +#define DE_ERR_NOT_IMPLEMENTED 100 + +#if defined(HOST_WIN32) && !HAVE_API_SUPPORT_WIN32_CONSOLE +void win32_debugger_log(FILE *stream, const gchar *format, ...); +#define PRINT_ERROR_MSG(...) win32_debugger_log (log_file, __VA_ARGS__) +#define PRINT_MSG(...) win32_debugger_log (log_file, __VA_ARGS__) +#else +#define PRINT_ERROR_MSG(...) g_printerr (__VA_ARGS__) +#define PRINT_MSG(...) g_print (__VA_ARGS__) +#endif + + +MONO_COMPONENT_EXPORT_ENTRYPOINT +MonoComponentDebugger * +mono_component_debugger_init (void); + +#define MONO_DBG_CALLBACKS_VERSION (4) + + +#endif/*_MONO_COMPONENT_DEBUGGER_H*/ diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/component/mini-wasm-debugger.c similarity index 86% rename from src/mono/mono/mini/mini-wasm-debugger.c rename to src/mono/mono/component/mini-wasm-debugger.c index 8c5951cf104..4df2172841e 100644 --- a/src/mono/mono/mini/mini-wasm-debugger.c +++ b/src/mono/mono/component/mini-wasm-debugger.c @@ -1,6 +1,6 @@ #include -#include "mini.h" -#include "mini-runtime.h" +#include +#include #include #include #include @@ -10,9 +10,10 @@ #include #include #include -#include -#include -#include +#include +#include "debugger-protocol.h" +#include "debugger-agent.h" +#include //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle #include @@ -24,6 +25,8 @@ #include "mono/metadata/assembly-internals.h" #include "mono/metadata/debug-mono-ppdb.h" +#include + static int log_level = 1; //functions exported to be used by JS @@ -40,7 +43,6 @@ extern void mono_wasm_asm_loaded (const char *asm_name, const char *assembly_dat G_END_DECLS -static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); static gboolean receive_debugger_agent_message (void *data, int len); static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly); @@ -114,14 +116,14 @@ begin_single_step_processing (MonoContext *ctx, gboolean from_signal) static void ss_discard_frame_context (void *the_tls) { - mono_ss_discard_frame_context (mono_wasm_get_tls()); + mono_ss_discard_frame_context (mono_wasm_get_tls ()); } static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes) { - mono_wasm_save_thread_context(); - mono_ss_calculate_framecount(mono_wasm_get_tls(), NULL, force_use_ctx, out_frames, nframes); + mono_wasm_save_thread_context (); + mono_ss_calculate_framecount (mono_wasm_get_tls (), NULL, force_use_ctx, out_frames, nframes); } static gboolean @@ -144,12 +146,25 @@ handle_multiple_ss_requests (void) { return 1; } -void -mono_wasm_debugger_init (void) +static void +mono_wasm_enable_debugging_internal (int debug_level) { + PRINT_DEBUG_MSG (1, "DEBUGGING ENABLED\n"); + debugger_enabled = TRUE; + log_level = debug_level; +} + +static void +mono_wasm_debugger_init (MonoDefaults *mono_defaults) +{ + int debug_level = mono_wasm_get_debug_level(); + mono_wasm_enable_debugging_internal (debug_level); + if (!debugger_enabled) return; + mdbg_mono_defaults = mono_defaults; + DebuggerEngineCallbacks cbs = { .tls_get_restore_state = tls_get_restore_state, .try_process_suspend = try_process_suspend, @@ -159,24 +174,16 @@ mono_wasm_debugger_init (void) .ss_calculate_framecount = ss_calculate_framecount, .ensure_jit = ensure_jit, .ensure_runtime_is_suspended = ensure_runtime_is_suspended, - .get_this_async_id = mono_get_this_async_id, - .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag, - .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method, - .create_breakpoint_events = mono_dbg_create_breakpoint_events, - .process_breakpoint_events = mono_dbg_process_breakpoint_events, - .ss_create_init_args = mono_ss_create_init_args, - .ss_args_destroy = mono_ss_args_destroy, .handle_multiple_ss_requests = handle_multiple_ss_requests, }; - mono_debug_init (MONO_DEBUG_FORMAT_MONO); mono_de_init (&cbs); mono_de_set_log_level (log_level, stdout); - mini_debug_options.gen_sdb_seq_points = TRUE; - mini_debug_options.mdb_optimizations = TRUE; + get_mini_debug_options ()->gen_sdb_seq_points = TRUE; + get_mini_debug_options ()->mdb_optimizations = TRUE; mono_disable_optimizations (MONO_OPT_LINEARS); - mini_debug_options.load_aot_jit_info_eagerly = TRUE; + get_mini_debug_options ()->load_aot_jit_info_eagerly = TRUE; MonoProfilerHandle prof = mono_profiler_create (NULL); mono_profiler_set_jit_done_callback (prof, jit_done); @@ -184,9 +191,6 @@ mono_wasm_debugger_init (void) mono_profiler_set_domain_loaded_callback (prof, appdomain_load); mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded); - mini_get_dbg_callbacks ()->handle_exception = mono_debugger_agent_handle_exception; - mini_get_dbg_callbacks ()->user_break = mono_dbg_debugger_agent_user_break; - //debugger-agent initialization DebuggerTransport trans; trans.name = "buffer-wasm-communication"; @@ -196,14 +200,6 @@ mono_wasm_debugger_init (void) mono_init_debugger_agent_for_wasm (log_level); } -MONO_API void -mono_wasm_enable_debugging (int debug_level) -{ - PRINT_DEBUG_MSG (1, "DEBUGGING ENABLED\n"); - debugger_enabled = TRUE; - log_level = debug_level; -} - static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) { @@ -233,17 +229,16 @@ assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly) } } - -void +static void mono_wasm_single_step_hit (void) { - mono_de_process_single_step (mono_wasm_get_tls(), FALSE); + mono_de_process_single_step (mono_wasm_get_tls (), FALSE); } -void +static void mono_wasm_breakpoint_hit (void) { - mono_de_process_breakpoint (mono_wasm_get_tls(), FALSE); + mono_de_process_breakpoint (mono_wasm_get_tls (), FALSE); } static gboolean @@ -404,14 +399,14 @@ mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command, MdbgProtErrorCode error = 0; if (command_set == MDBGPROT_CMD_SET_VM && command == MDBGPROT_CMD_VM_INVOKE_METHOD ) { - DebuggerTlsData* tls = mono_wasm_get_tls(); + DebuggerTlsData* tls = mono_wasm_get_tls (); InvokeData invoke_data; - memset(&invoke_data, 0, sizeof(InvokeData)); + memset (&invoke_data, 0, sizeof (InvokeData)); invoke_data.endp = data + size; - error = mono_do_invoke_method(tls, &buf, &invoke_data, data, &data); + error = mono_do_invoke_method (tls, &buf, &invoke_data, data, &data); } else - error = mono_process_dbg_packet(id, command_set, command, &no_reply, data, data + size, &buf); + error = mono_process_dbg_packet (id, command_set, command, &no_reply, data, data + size, &buf); EM_ASM ({ MONO.mono_wasm_add_dbg_command_received ($0, $1, $2, $3); }, error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf); @@ -433,19 +428,28 @@ receive_debugger_agent_message (void *data, int len) #else // HOST_WASM -void +static void mono_wasm_single_step_hit (void) { } -void +static void mono_wasm_breakpoint_hit (void) { } -void -mono_wasm_debugger_init (void) +static void +mono_wasm_debugger_init (MonoDefaults *mono_defaults) { } + #endif // HOST_WASM + +void +mini_wasm_debugger_add_function_pointers (MonoComponentDebugger* fn_table) +{ + fn_table->init = mono_wasm_debugger_init; + fn_table->mono_wasm_breakpoint_hit = mono_wasm_breakpoint_hit; + fn_table->mono_wasm_single_step_hit = mono_wasm_single_step_hit; +} diff --git a/src/mono/mono/metadata/assembly-internals.h b/src/mono/mono/metadata/assembly-internals.h index 4548b7fd864..28f3fdc502f 100644 --- a/src/mono/mono/metadata/assembly-internals.h +++ b/src/mono/mono/metadata/assembly-internals.h @@ -31,7 +31,7 @@ typedef enum { G_ENUM_FUNCTIONS (MonoAssemblyNameEqFlags) -void +MONO_COMPONENT_API void mono_assembly_name_free_internal (MonoAssemblyName *aname); gboolean @@ -108,7 +108,7 @@ void mono_assembly_request_prepare_open (MonoAssemblyOpenReque MonoAssemblyContextKind asmctx, MonoAssemblyLoadContext *alc); -void mono_assembly_request_prepare_byname (MonoAssemblyByNameRequest *req, +MONO_COMPONENT_API void mono_assembly_request_prepare_byname (MonoAssemblyByNameRequest *req, MonoAssemblyContextKind asmctx, MonoAssemblyLoadContext *alc); @@ -120,7 +120,7 @@ MonoAssembly* mono_assembly_request_load_from (MonoImage *image, const const MonoAssemblyLoadRequest *req, MonoImageOpenStatus *status); -MonoAssembly* mono_assembly_request_byname (MonoAssemblyName *aname, +MONO_COMPONENT_API MonoAssembly* mono_assembly_request_byname (MonoAssemblyName *aname, const MonoAssemblyByNameRequest *req, MonoImageOpenStatus *status); diff --git a/src/mono/mono/metadata/class-init.h b/src/mono/mono/metadata/class-init.h index 5dbf66b6d1b..1ec64459376 100644 --- a/src/mono/mono/metadata/class-init.h +++ b/src/mono/mono/metadata/class-init.h @@ -25,10 +25,10 @@ mono_class_create_generic_inst (MonoGenericClass *gclass); MonoClass * mono_class_create_bounded_array (MonoClass *element_class, uint32_t rank, mono_bool bounded); -MonoClass * +MONO_COMPONENT_API MonoClass * mono_class_create_array (MonoClass *element_class, uint32_t rank); -MonoClass * +MONO_COMPONENT_API MonoClass * mono_class_create_generic_parameter (MonoGenericParam *param); MonoClass * @@ -49,7 +49,7 @@ mono_class_setup_basic_field_info (MonoClass *klass); void mono_class_setup_fields (MonoClass *klass); -void +MONO_COMPONENT_API void mono_class_setup_methods (MonoClass *klass); void @@ -64,7 +64,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ void mono_class_setup_interface_offsets (MonoClass *klass); -void +MONO_COMPONENT_API void mono_class_setup_vtable (MonoClass *klass); void diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 37ce4ce6a14..3759bb4de27 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -317,7 +317,7 @@ int mono_class_interface_match (const uint8_t *bitmap, int id); MONO_API int mono_class_interface_offset (MonoClass *klass, MonoClass *itf); -int mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match); +MONO_COMPONENT_API int mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match); typedef gpointer MonoRuntimeGenericContext; @@ -578,7 +578,7 @@ typedef struct { MonoMethod *wrapper_method; } MonoJitICallInfo; -void +MONO_COMPONENT_API void mono_class_setup_supertypes (MonoClass *klass); /* WARNING @@ -773,7 +773,7 @@ mono_class_get_implemented_interfaces (MonoClass *klass, MonoError *error); int mono_class_get_vtable_size (MonoClass *klass); -gboolean +MONO_COMPONENT_API gboolean mono_class_is_open_constructed_type (MonoType *t); void @@ -788,10 +788,10 @@ mono_class_get_finalizer (MonoClass *klass); gboolean mono_class_needs_cctor_run (MonoClass *klass, MonoMethod *caller); -gboolean +MONO_COMPONENT_API gboolean mono_class_field_is_special_static (MonoClassField *field); -guint32 +MONO_COMPONENT_API guint32 mono_class_field_get_special_static_type (MonoClassField *field); gboolean @@ -800,7 +800,7 @@ mono_class_has_special_static_fields (MonoClass *klass); const char* mono_class_get_field_default_value (MonoClassField *field, MonoTypeEnum *def_type); -MonoProperty* +MONO_COMPONENT_API MonoProperty* mono_class_get_property_from_name_internal (MonoClass *klass, const char *name); const char* @@ -894,10 +894,10 @@ mono_method_get_is_covariant_override_impl (MonoMethod *method); void mono_method_set_is_covariant_override_impl (MonoMethod *methoddef); -MonoMethod* +MONO_COMPONENT_API MonoMethod* mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context, MonoError *error); -MonoMethod * +MONO_COMPONENT_API MonoMethod * mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContext *context, MonoError *error); MonoMemoryManager * @@ -915,7 +915,7 @@ mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericConte MonoType* mono_class_inflate_generic_type_with_mempool (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error); -MonoType* +MONO_COMPONENT_API MonoType* mono_class_inflate_generic_type_checked (MonoType *type, MonoGenericContext *context, MonoError *error); MONO_API void @@ -1062,16 +1062,16 @@ mono_loader_init (void); void mono_loader_cleanup (void); -void +MONO_COMPONENT_API void mono_loader_lock (void); -void +MONO_COMPONENT_API void mono_loader_unlock (void); -void +MONO_COMPONENT_API void mono_loader_lock_track_ownership (gboolean track); -gboolean +MONO_COMPONENT_API gboolean mono_loader_lock_is_owned_by_self (void); void @@ -1086,7 +1086,7 @@ mono_reflection_init (void); void mono_icall_init (void); -gpointer +MONO_COMPONENT_API gpointer mono_method_get_wrapper_data (MonoMethod *method, guint32 id); gboolean @@ -1209,7 +1209,7 @@ mono_method_search_in_array_class (MonoClass *klass, const char *name, MonoMetho void mono_class_setup_interface_id (MonoClass *klass); -MonoGenericContainer* +MONO_COMPONENT_API MonoGenericContainer* mono_class_get_generic_container (MonoClass *klass); gpointer @@ -1220,13 +1220,13 @@ mono_class_alloc0 (MonoClass *klass, int size); #define mono_class_alloc0(klass, size) (g_cast (mono_class_alloc0 ((klass), (size)))) -void +MONO_COMPONENT_API void mono_class_setup_interfaces (MonoClass *klass, MonoError *error); -MonoClassField* +MONO_COMPONENT_API MonoClassField* mono_class_get_field_from_name_full (MonoClass *klass, const char *name, MonoType *type); -MonoVTable* +MONO_COMPONENT_API MonoVTable* mono_class_vtable_checked (MonoClass *klass, MonoError *error); void @@ -1247,7 +1247,7 @@ mono_class_is_variant_compatible (MonoClass *klass, MonoClass *oklass, gboolean gboolean mono_class_is_subclass_of_internal (MonoClass *klass, MonoClass *klassc, gboolean check_interfaces); -mono_bool +MONO_COMPONENT_API mono_bool mono_class_is_assignable_from_internal (MonoClass *klass, MonoClass *oklass); gboolean @@ -1261,7 +1261,7 @@ mono_field_get_type_checked (MonoClassField *field, MonoError *error); MonoType* mono_field_get_type_internal (MonoClassField *field); -MonoClassField* +MONO_COMPONENT_API MonoClassField* mono_class_get_fields_internal (MonoClass* klass, gpointer *iter); MonoClassField* @@ -1276,7 +1276,7 @@ mono_class_has_finalizer (MonoClass *klass); void mono_unload_interface_id (MonoClass *klass); -GPtrArray* +MONO_COMPONENT_API GPtrArray* mono_class_get_methods_by_name (MonoClass *klass, const char *name, guint32 bflags, guint32 mlisttype, gboolean allow_ctors, MonoError *error); char* @@ -1300,10 +1300,10 @@ mono_class_from_name_case_checked (MonoImage *image, const char* name_space, con MONO_PROFILER_API MonoClass * mono_class_from_mono_type_internal (MonoType *type); -MonoClassField* +MONO_COMPONENT_API MonoClassField* mono_field_from_token_checked (MonoImage *image, uint32_t token, MonoClass **retklass, MonoGenericContext *context, MonoError *error); -gpointer +MONO_COMPONENT_API gpointer mono_ldtoken_checked (MonoImage *image, guint32 token, MonoClass **handle_class, MonoGenericContext *context, MonoError *error); MonoImage * @@ -1312,10 +1312,10 @@ mono_get_image_for_generic_param (MonoGenericParam *param); char * mono_make_generic_name_string (MonoImage *image, int num); -MonoClass * +MONO_COMPONENT_API MonoClass * mono_class_load_from_name (MonoImage *image, const char* name_space, const char *name); -MonoClass* +MONO_COMPONENT_API MonoClass* mono_class_try_load_from_name (MonoImage *image, const char* name_space, const char *name); void @@ -1325,7 +1325,7 @@ gboolean mono_class_has_failure (const MonoClass *klass); /* Kind specific accessors */ -MonoGenericClass* +MONO_COMPONENT_API MonoGenericClass* mono_class_get_generic_class (MonoClass *klass); MonoGenericClass* diff --git a/src/mono/mono/metadata/components.c b/src/mono/mono/metadata/components.c index 765bc726a2e..d0a2c1257f2 100644 --- a/src/mono/mono/metadata/components.c +++ b/src/mono/mono/metadata/components.c @@ -6,6 +6,7 @@ #include #include #include "mono/component/component.h" +#include "mono/component/debugger.h" #include "mono/component/hot_reload.h" #include "mono/component/event_pipe.h" #include "mono/component/diagnostics_server.h" @@ -32,8 +33,14 @@ typedef struct _MonoComponentEntry { #define HOT_RELOAD_LIBRARY_NAME "hot_reload" #define HOT_RELOAD_COMPONENT_NAME HOT_RELOAD_LIBRARY_NAME + +#define DEBUGGER_LIBRARY_NAME "debugger" +#define DEBUGGER_COMPONENT_NAME DEBUGGER_LIBRARY_NAME + MonoComponentHotReload *hot_reload = NULL; +MonoComponentDebugger *debugger = NULL; + MonoComponentEventPipe *event_pipe = NULL; MonoComponentDiagnosticsServer *diagnostics_server = NULL; @@ -44,6 +51,7 @@ MonoComponentDiagnosticsServer *diagnostics_server = NULL; /* One per component */ MonoComponentEntry components[] = { + { DEBUGGER_LIBRARY_NAME, DEBUGGER_COMPONENT_NAME, COMPONENT_INIT_FUNC (debugger), (MonoComponent**)&debugger, NULL }, { HOT_RELOAD_LIBRARY_NAME, HOT_RELOAD_COMPONENT_NAME, COMPONENT_INIT_FUNC (hot_reload), (MonoComponent**)&hot_reload, NULL }, { DIAGNOSTICS_TRACING_LIBRARY_NAME, EVENT_PIPE_COMPONENT_NAME, COMPONENT_INIT_FUNC (event_pipe), (MonoComponent**)&event_pipe, NULL }, { DIAGNOSTICS_TRACING_LIBRARY_NAME, DIAGNOSTICS_SERVER_COMPONENT_NAME, COMPONENT_INIT_FUNC (diagnostics_server), (MonoComponent**)&diagnostics_server, NULL }, diff --git a/src/mono/mono/metadata/components.h b/src/mono/mono/metadata/components.h index 0a022e349a2..2aceed820c9 100644 --- a/src/mono/mono/metadata/components.h +++ b/src/mono/mono/metadata/components.h @@ -9,6 +9,7 @@ #include #include #include +#include void mono_component_event_pipe_100ns_ticks_start (void); @@ -44,4 +45,12 @@ mono_component_diagnostics_server (void) return diagnostics_server; } +static inline +MonoComponentDebugger * +mono_component_debugger (void) +{ + extern MonoComponentDebugger *debugger; + return debugger; +} + #endif/*_MONO_METADATA_COMPONENTS_H*/ diff --git a/src/mono/mono/metadata/custom-attrs-internals.h b/src/mono/mono/metadata/custom-attrs-internals.h index 407268a22fa..16a232a80db 100644 --- a/src/mono/mono/metadata/custom-attrs-internals.h +++ b/src/mono/mono/metadata/custom-attrs-internals.h @@ -29,10 +29,10 @@ mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx); void mono_assembly_init_weak_fields (MonoImage *image); -void +MONO_COMPONENT_API void mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoArrayHandleOut typed_args_out, MonoArrayHandleOut named_args_out, CattrNamedArg **named_arg_info, MonoError *error); -void +MONO_COMPONENT_API void mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, gpointer **typed_args_out, gpointer **named_args_out, int *num_named_args, CattrNamedArg **named_arg_info, MonoError *error); diff --git a/src/mono/mono/metadata/debug-internals.h b/src/mono/mono/metadata/debug-internals.h index 4fb12e39eca..e08b767b4c3 100644 --- a/src/mono/mono/metadata/debug-internals.h +++ b/src/mono/mono/metadata/debug-internals.h @@ -92,7 +92,7 @@ typedef struct { void mono_debugger_lock (void); void mono_debugger_unlock (void); -void +MONO_COMPONENT_API void mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); @@ -100,16 +100,16 @@ mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, MONO_API void mono_debug_free_locals (MonoDebugLocalsInfo *info); -void +MONO_COMPONENT_API void mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info); -gboolean +MONO_COMPONENT_API gboolean mono_debug_image_has_debug_info (MonoImage *image); MonoDebugSourceLocation * mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain); -char* +MONO_COMPONENT_API char* mono_debug_image_get_sourcelink (MonoImage *image); #endif /* __DEBUG_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/debug-mono-ppdb.h b/src/mono/mono/metadata/debug-mono-ppdb.h index f7148be9f06..c27a97069c9 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.h +++ b/src/mono/mono/metadata/debug-mono-ppdb.h @@ -38,7 +38,7 @@ mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo); MonoDebugMethodAsyncInfo* mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo); -MonoImage * +MONO_COMPONENT_API MonoImage * mono_ppdb_get_image (MonoPPDBFile *ppdb); char * diff --git a/src/mono/mono/metadata/domain-internals.h b/src/mono/mono/metadata/domain-internals.h index 176b70d772f..398898c6e60 100644 --- a/src/mono/mono/metadata/domain-internals.h +++ b/src/mono/mono/metadata/domain-internals.h @@ -80,7 +80,7 @@ mono_install_runtime_load (MonoLoadFunc func); MonoDomain* mono_runtime_load (const char *filename, const char *runtime_version); -void +MONO_COMPONENT_API void mono_runtime_quit_internal (void); void @@ -136,7 +136,7 @@ mono_runtime_register_runtimeconfig_json_properties (MonovmRuntimeConfigArgument void mono_runtime_install_appctx_properties (void); -void +MONO_COMPONENT_API void mono_domain_set_fast (MonoDomain *domain); G_END_DECLS diff --git a/src/mono/mono/metadata/gc-internals.h b/src/mono/mono/metadata/gc-internals.h index d1bd5590b06..60e2d79d0ff 100644 --- a/src/mono/mono/metadata/gc-internals.h +++ b/src/mono/mono/metadata/gc-internals.h @@ -68,7 +68,7 @@ void mono_object_register_finalizer_handle (MonoObjectHandle obj); extern void mono_gc_init (void); -extern void mono_gc_base_init (void); +MONO_COMPONENT_API extern void mono_gc_base_init (void); extern void mono_gc_base_cleanup (void); extern void mono_gc_init_icalls (void); @@ -78,7 +78,7 @@ extern void mono_gc_init_icalls (void); */ extern gboolean mono_gc_is_gc_thread (void); -extern gboolean mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread); +MONO_COMPONENT_API extern gboolean mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread); extern void mono_gc_set_stack_end (void *stack_end); @@ -174,7 +174,7 @@ typedef void (*MonoFinalizationProc)(gpointer, gpointer); // same as SGenFinaliz void mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data); void mono_gc_add_memory_pressure (gint64 value); MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); -void mono_gc_deregister_root (char* addr); +MONO_COMPONENT_API void mono_gc_deregister_root (char* addr); void mono_gc_finalize_domain (MonoDomain *domain); void mono_gc_run_finalize (void *obj, void *data); void mono_gc_clear_domain (MonoDomain * domain); @@ -310,7 +310,7 @@ void mono_gc_set_desktop_mode (void); /* * Return whenever this GC can move objects */ -gboolean mono_gc_is_moving (void); +MONO_COMPONENT_API gboolean mono_gc_is_moving (void); typedef void* (*MonoGCLockedCallbackFunc) (void *data); diff --git a/src/mono/mono/metadata/handle.c b/src/mono/mono/metadata/handle.c index 50fb6f5612a..d6af32f73b2 100644 --- a/src/mono/mono/metadata/handle.c +++ b/src/mono/mono/metadata/handle.c @@ -149,7 +149,7 @@ mono_handle_chunk_leak_check (HandleStack *handles) { // There are deliberately locals and a constant NULL global with this same name. #ifdef __cplusplus -extern MonoThreadInfo * const mono_thread_info_current_var = NULL; +MonoThreadInfo * const mono_thread_info_current_var = NULL; #else MonoThreadInfo * const mono_thread_info_current_var = NULL; #endif diff --git a/src/mono/mono/metadata/handle.h b/src/mono/mono/metadata/handle.h index 62bf1288194..0e43247971a 100644 --- a/src/mono/mono/metadata/handle.h +++ b/src/mono/mono/metadata/handle.h @@ -124,9 +124,9 @@ typedef void (*GcScanFunc) (gpointer*, gpointer); #endif #ifndef MONO_HANDLE_TRACK_OWNER -MonoRawHandle mono_handle_new (MonoObject *object, MonoThreadInfo *info); +MONO_COMPONENT_API MonoRawHandle mono_handle_new (MonoObject *object, MonoThreadInfo *info); #else -MonoRawHandle mono_handle_new (MonoObject *object, MonoThreadInfo *info, const char* owner); +MONO_COMPONENT_API MonoRawHandle mono_handle_new (MonoObject *object, MonoThreadInfo *info, const char* owner); #endif void mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise, gboolean check); @@ -134,7 +134,7 @@ gboolean mono_handle_stack_is_empty (HandleStack *stack); HandleStack* mono_handle_stack_alloc (void); void mono_handle_stack_free (HandleStack *handlestack); MonoRawHandle mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value); -MonoThreadInfo* mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name); +MONO_COMPONENT_API MonoThreadInfo* mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name); void mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain); #ifdef MONO_HANDLE_TRACK_SP diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index a317db69785..0e1934455d1 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -296,10 +296,10 @@ mono_mem_manager_free (MonoMemoryManager *memory_manager, gboolean debug_unload) void mono_mem_manager_free_objects (MonoMemoryManager *memory_manager); -void +MONO_COMPONENT_API void mono_mem_manager_lock (MonoMemoryManager *memory_manager); -void +MONO_COMPONENT_API void mono_mem_manager_unlock (MonoMemoryManager *memory_manager); MONO_COMPONENT_API diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index 6ce138c1170..08300a001ef 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -403,7 +403,7 @@ mono_type_to_stind (MonoType *type); /* functions to create various architecture independent helper functions */ -MonoMethod * +MONO_COMPONENT_API MonoMethod * mono_marshal_method_from_wrapper (MonoMethod *wrapper); WrapperInfo* diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index a3f56cf1f84..545693ef12a 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -720,7 +720,7 @@ table_info_get_rows (const MonoTableInfo *table) } /* for use with allocated memory blocks (assumes alignment is to 8 bytes) */ -guint mono_aligned_addr_hash (gconstpointer ptr); +MONO_COMPONENT_API guint mono_aligned_addr_hash (gconstpointer ptr); void mono_image_check_for_module_cctor (MonoImage *image); @@ -818,7 +818,7 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) int mono_image_relative_delta_index (MonoImage *image_dmeta, int token); -void +MONO_COMPONENT_API void mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); gboolean @@ -916,7 +916,7 @@ mono_metadata_parse_generic_inst (MonoImage *image, const char **rptr, MonoError *error); -MonoGenericInst * +MONO_COMPONENT_API MonoGenericInst * mono_metadata_get_generic_inst (int type_argc, MonoType **type_argv); @@ -1052,7 +1052,7 @@ mono_type_create_from_typespec_checked (MonoImage *image, guint32 type_spec, Mon MonoMethodSignature* mono_method_get_signature_checked (MonoMethod *method, MonoImage *image, guint32 token, MonoGenericContext *context, MonoError *error); -MonoMethod * +MONO_COMPONENT_API MonoMethod * mono_get_method_checked (MonoImage *image, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error); guint32 @@ -1085,7 +1085,7 @@ mono_loader_set_strict_assembly_name_check (gboolean enabled); gboolean mono_loader_get_strict_assembly_name_check (void); -gboolean +MONO_COMPONENT_API gboolean mono_type_in_image (MonoType *type, MonoImage *image); gboolean diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index ca8cced208a..1e3cd55ed2d 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -1967,8 +1967,6 @@ mono_metadata_init (void) for (i = 0; i < NBUILTIN_TYPES (); ++i) g_hash_table_insert (type_cache, (gpointer) &builtin_types [i], (gpointer) &builtin_types [i]); - mono_components_init (); - mono_metadata_update_init (); } diff --git a/src/mono/mono/metadata/mono-debug.h b/src/mono/mono/metadata/mono-debug.h index 4af24ca3d1a..2ddbce09947 100644 --- a/src/mono/mono/metadata/mono-debug.h +++ b/src/mono/mono/metadata/mono-debug.h @@ -191,7 +191,7 @@ mono_debug_add_delegate_trampoline (void* code, int size); MONO_API MonoDebugLocalsInfo* mono_debug_lookup_locals (MonoMethod *method, mono_bool ignore_pdb); -MonoDebugMethodAsyncInfo* +MONO_API MonoDebugMethodAsyncInfo* mono_debug_lookup_method_async_debug_info (MonoMethod *method); MONO_API diff --git a/src/mono/mono/metadata/mono-hash-internals.h b/src/mono/mono/metadata/mono-hash-internals.h index 1ba508b2030..84b8894f9e0 100644 --- a/src/mono/mono/metadata/mono-hash-internals.h +++ b/src/mono/mono/metadata/mono-hash-internals.h @@ -7,11 +7,12 @@ #include "mono/metadata/mono-hash.h" #include "mono/metadata/mono-gc.h" +#include "mono/utils/mono-compiler.h" -MonoGHashTable * +MONO_COMPONENT_API MonoGHashTable * mono_g_hash_table_new_type_internal (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type, MonoGCRootSource source, void *key, const char *msg); -void +MONO_COMPONENT_API void mono_g_hash_table_insert_internal (MonoGHashTable *h, gpointer k, gpointer v); #endif /* __MONO_G_HASH_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 9dfb86c1d9d..19f00b0630f 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -84,7 +84,7 @@ } \ } while (0) -MonoClass * +MONO_COMPONENT_API MonoClass * mono_class_create_array (MonoClass *element_class, uint32_t rank); MonoArrayHandle @@ -1490,7 +1490,7 @@ mono_array_calc_byte_len (MonoClass *klass, uintptr_t len, uintptr_t *res); MonoArray* mono_array_new_checked (MonoClass *eclass, uintptr_t n, MonoError *error); -MonoArray* +MONO_COMPONENT_API MonoArray* mono_array_new_full_checked (MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error); ICALL_EXPORT @@ -1503,7 +1503,7 @@ mono_create_ftnptr (gpointer addr); gpointer mono_get_addr_from_ftnptr (gpointer descr); -void +MONO_COMPONENT_API void mono_nullable_init (guint8 *buf, MonoObject *value, MonoClass *klass); void @@ -1512,13 +1512,13 @@ mono_nullable_init_from_handle (guint8 *buf, MonoObjectHandle value, MonoClass * void mono_nullable_init_unboxed (guint8 *buf, gpointer value, MonoClass *klass); -MonoObject * +MONO_COMPONENT_API MonoObject * mono_value_box_checked (MonoClass *klass, void* val, MonoError *error); MonoObjectHandle mono_value_box_handle (MonoClass *klass, gpointer val, MonoError *error); -MonoObject* +MONO_COMPONENT_API MonoObject* mono_nullable_box (gpointer buf, MonoClass *klass, MonoError *error); MonoObjectHandle @@ -1646,10 +1646,10 @@ mono_object_new_alloc_specific_checked (MonoVTable *vtable, MonoError *error); void mono_field_get_value_internal (MonoObject *obj, MonoClassField *field, void *value); -void +MONO_COMPONENT_API void mono_field_static_get_value_checked (MonoVTable *vt, MonoClassField *field, void *value, MonoStringHandleOut string_handle, MonoError *error); -void +MONO_COMPONENT_API void mono_field_static_get_value_for_thread (MonoInternalThread *thread, MonoVTable *vt, MonoClassField *field, void *value, MonoStringHandleOut string_handle, MonoError *error); guint8* @@ -1668,7 +1668,7 @@ mono_field_get_value_object_checked (MonoClassField *field, MonoObject *obj, Mon MonoObjectHandle mono_static_field_get_value_handle (MonoClassField *field, MonoError *error); -gpointer +MONO_COMPONENT_API gpointer mono_special_static_field_get_offset (MonoClassField *field, MonoError *error); gboolean @@ -1732,7 +1732,7 @@ mono_error_set_pending_exception (MonoError *error) MonoArray * mono_glist_to_array (GList *list, MonoClass *eclass, MonoError *error); -MonoObject * +MONO_COMPONENT_API MonoObject * mono_object_new_checked (MonoClass *klass, MonoError *error); MonoObjectHandle @@ -1776,7 +1776,7 @@ mono_string_new_len_checked (const char *text, guint length, MonoError *error); MonoString * mono_string_new_size_checked (gint32 len, MonoError *error); -MonoString* +MONO_COMPONENT_API MonoString* mono_ldstr_checked (MonoImage *image, uint32_t str_index, MonoError *error); MonoStringHandle @@ -1826,7 +1826,7 @@ mono_runtime_try_invoke (MonoMethod *method, void *obj, void **params, MonoObjec MONO_COMPONENT_API MonoObjectHandle mono_runtime_try_invoke_handle (MonoMethod *method, MonoObjectHandle obj, void **params, MonoError* error); -MonoObject* +MONO_COMPONENT_API MonoObject* mono_runtime_invoke_checked (MonoMethod *method, void *obj, void **params, MonoError *error); MonoObjectHandle @@ -1900,7 +1900,7 @@ mono_object_get_data (MonoObject *o) #define mono_handle_get_data_unsafe(handle) ((gpointer)((guint8*)MONO_HANDLE_RAW (handle) + MONO_ABI_SIZEOF (MonoObject))) -gpointer +MONO_COMPONENT_API gpointer mono_vtype_get_field_addr (gpointer vtype, MonoClassField *field); #define MONO_OBJECT_SETREF_INTERNAL(obj,fieldname,value) do { \ @@ -1933,7 +1933,7 @@ mono_string_empty_internal (MonoDomain *domain); char * mono_string_to_utf8len (MonoStringHandle s, gsize *utf8len, MonoError *error); -char* +MONO_COMPONENT_API char* mono_string_to_utf8_checked_internal (MonoString *string_obj, MonoError *error); mono_bool @@ -1942,7 +1942,7 @@ mono_string_equal_internal (MonoString *s1, MonoString *s2); unsigned mono_string_hash_internal (MonoString *s); -int +MONO_COMPONENT_API int mono_object_hash_internal (MonoObject* obj); ICALL_EXTERN_C @@ -1975,7 +1975,7 @@ MONO_PROFILER_API MonoDomain* mono_vtable_domain_internal (MonoVTable *vtable); MONO_PROFILER_API MonoClass* mono_vtable_class_internal (MonoVTable *vtable); -MonoMethod* +MONO_COMPONENT_API MonoMethod* mono_object_get_virtual_method_internal (MonoObject *obj, MonoMethod *method); MonoMethod* @@ -1999,7 +1999,7 @@ mono_raise_exception_internal (MonoException *ex); void mono_field_set_value_internal (MonoObject *obj, MonoClassField *field, void *value); -void +MONO_COMPONENT_API void mono_field_static_set_value_internal (MonoVTable *vt, MonoClassField *field, void *value); void @@ -2027,14 +2027,14 @@ mono_runtime_get_aotid_arr (void); MonoGCHandle mono_gchandle_new_internal (MonoObject *obj, mono_bool pinned); -MonoGCHandle +MONO_COMPONENT_API MonoGCHandle mono_gchandle_new_weakref_internal (MonoObject *obj, mono_bool track_resurrection); -ICALL_EXTERN_C +MONO_COMPONENT_API ICALL_EXTERN_C MonoObject* mono_gchandle_get_target_internal (MonoGCHandle gchandle); -void mono_gchandle_free_internal (MonoGCHandle gchandle); +MONO_COMPONENT_API void mono_gchandle_free_internal (MonoGCHandle gchandle); /* Reference queue support * @@ -2066,7 +2066,7 @@ mono_gc_wbarrier_set_arrayref_internal (MonoArray *arr, void* slot_ptr, MonoObj void mono_gc_wbarrier_arrayref_copy_internal (void* dest_ptr, const void* src_ptr, int count); -void +MONO_COMPONENT_API void mono_gc_wbarrier_generic_store_internal (void volatile* ptr, MonoObject* value); void diff --git a/src/mono/mono/metadata/reflection-internals.h b/src/mono/mono/metadata/reflection-internals.h index 9ea2c1cdc98..b1504e4cda9 100644 --- a/src/mono/mono/metadata/reflection-internals.h +++ b/src/mono/mono/metadata/reflection-internals.h @@ -25,7 +25,7 @@ mono_domain_try_type_resolve_name (MonoAssembly *assembly, MonoStringHandle name MonoReflectionTypeBuilderHandle mono_class_get_ref_info (MonoClass *klass); -gboolean +MONO_COMPONENT_API gboolean mono_reflection_parse_type_checked (char *name, MonoTypeNameParse *info, MonoError *error); gboolean @@ -37,7 +37,7 @@ mono_reflection_type_resolve_user_types (MonoReflectionType *type, MonoError *er MonoType * mono_reflection_type_handle_mono_type (MonoReflectionTypeHandle ref_type, MonoError *error); -MonoType* +MONO_COMPONENT_API MonoType* mono_reflection_get_type_checked (MonoAssemblyLoadContext *alc, MonoImage *rootimage, MonoImage* image, MonoTypeNameParse *info, gboolean ignorecase, gboolean search_mscorlib, gboolean *type_resolve, MonoError *error); MonoType* @@ -63,17 +63,17 @@ mono_reflection_get_custom_attrs_blob_checked (MonoReflectionAssembly *assembly, MonoCustomAttrInfo* mono_custom_attrs_from_index_checked (MonoImage *image, uint32_t idx, gboolean ignore_missing, MonoError *error); -MonoCustomAttrInfo* +MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error); -MonoCustomAttrInfo* +MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_class_checked (MonoClass *klass, MonoError *error); -MonoCustomAttrInfo* +MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_assembly_checked (MonoAssembly *assembly, gboolean ignore_missing, MonoError *error); -MonoCustomAttrInfo* +MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_property_checked (MonoClass *klass, MonoProperty *property, MonoError *error); MonoCustomAttrInfo* mono_custom_attrs_from_event_checked (MonoClass *klass, MonoEvent *event, MonoError *error); -MonoCustomAttrInfo* +MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_field_checked (MonoClass *klass, MonoClassField *field, MonoError *error); MonoCustomAttrInfo* mono_custom_attrs_from_param_checked (MonoMethod *method, uint32_t param, MonoError *error); @@ -85,10 +85,10 @@ mono_identifier_unescape_type_name_chars (char* identifier); MonoImage * mono_find_dynamic_image_owner (void *ptr); -MonoReflectionAssemblyHandle +MONO_COMPONENT_API MonoReflectionAssemblyHandle mono_assembly_get_object_handle (MonoAssembly *assembly, MonoError *error); -MonoReflectionType* +MONO_COMPONENT_API MonoReflectionType* mono_type_get_object_checked (MonoType *type, MonoError *error); MonoReflectionTypeHandle diff --git a/src/mono/mono/metadata/runtime.h b/src/mono/mono/metadata/runtime.h index 99a6b4d3641..9ddcf7616e5 100644 --- a/src/mono/mono/metadata/runtime.h +++ b/src/mono/mono/metadata/runtime.h @@ -16,13 +16,13 @@ #include #include -gboolean mono_runtime_try_shutdown (void); +MONO_COMPONENT_API gboolean mono_runtime_try_shutdown (void); void mono_runtime_init_tls (void); MONO_PROFILER_API char* mono_runtime_get_aotid (void); -MonoAssembly* mono_runtime_get_entry_assembly (void); +MONO_COMPONENT_API MonoAssembly* mono_runtime_get_entry_assembly (void); void mono_runtime_ensure_entry_assembly (MonoAssembly *assembly); diff --git a/src/mono/mono/metadata/seq-points-data.h b/src/mono/mono/metadata/seq-points-data.h index 8d07f442203..4b9c1d0d479 100644 --- a/src/mono/mono/metadata/seq-points-data.h +++ b/src/mono/mono/metadata/seq-points-data.h @@ -8,6 +8,7 @@ #define __MONO_SEQ_POINTS_DATA_H__ #include +#include "mono/utils/mono-compiler.h" #define MONO_SEQ_POINT_FLAG_NONEMPTY_STACK 1 #define MONO_SEQ_POINT_FLAG_EXIT_IL 2 @@ -45,13 +46,13 @@ typedef struct { void mono_seq_point_info_free (gpointer info); -gboolean +MONO_COMPONENT_API gboolean mono_seq_point_iterator_next (SeqPointIterator* it); -void +MONO_COMPONENT_API void mono_seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info); -void +MONO_COMPONENT_API void mono_seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next); int diff --git a/src/mono/mono/metadata/threads-types.h b/src/mono/mono/metadata/threads-types.h index 394a526c552..b9fe7d3a14f 100644 --- a/src/mono/mono/metadata/threads-types.h +++ b/src/mono/mono/metadata/threads-types.h @@ -80,7 +80,7 @@ typedef enum { MONO_THREAD_CREATE_FLAGS_SMALL_STACK = 0x08, } MonoThreadCreateFlags; -MonoInternalThread* +MONO_COMPONENT_API MonoInternalThread* mono_thread_create_internal (MonoThreadStart func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error); MonoInternalThreadHandle @@ -197,11 +197,11 @@ MONO_PROFILER_API MonoInternalThread *mono_thread_internal_current (void); MonoInternalThreadHandle mono_thread_internal_current_handle (void); -gboolean +MONO_COMPONENT_API gboolean mono_thread_internal_abort (MonoInternalThread *thread); void mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread); -void mono_thread_internal_reset_abort (MonoInternalThread *thread); +MONO_COMPONENT_API void mono_thread_internal_reset_abort (MonoInternalThread *thread); void mono_thread_internal_unhandled_exception (MonoObject* exc); @@ -299,7 +299,7 @@ uint32_t mono_alloc_special_static_data (uint32_t static_type, uint32_t size, ui ICALL_EXTERN_C void* mono_get_special_static_data (uint32_t offset); -gpointer mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset); +MONO_COMPONENT_API gpointer mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset); void mono_thread_resume_interruption (gboolean exec); @@ -344,7 +344,7 @@ mono_thread_internal_current_is_attached (void); void mono_thread_internal_describe (MonoInternalThread *internal, GString *str); -gboolean +MONO_COMPONENT_API gboolean mono_thread_internal_is_current (MonoInternalThread *internal); gboolean diff --git a/src/mono/mono/metadata/verify-internals.h b/src/mono/mono/metadata/verify-internals.h index 4609a05bc2d..ac4beff1450 100644 --- a/src/mono/mono/metadata/verify-internals.h +++ b/src/mono/mono/metadata/verify-internals.h @@ -11,7 +11,7 @@ #include gboolean mono_verifier_class_is_valid_generic_instantiation (MonoClass *klass); -gboolean mono_verifier_is_method_valid_generic_instantiation (MonoMethod *method); +MONO_COMPONENT_API gboolean mono_verifier_is_method_valid_generic_instantiation (MonoMethod *method); /*Token validation macros and functions */ #define IS_MEMBER_REF(token) (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF) diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index ba786bcf9d8..c66362a55f3 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -168,15 +168,9 @@ set(mini_common_sources monovm.c) set(debugger_sources - debugger-engine.h - debugger-engine.c - debugger-agent.h - debugger-agent.c - debugger-protocol.h - debugger-protocol.c - debugger-agent-stubs.c - debugger-state-machine.h - debugger-state-machine.c) + debugger-agent-external.h + debugger-agent-external.c + ) set(amd64_sources mini-amd64.c @@ -228,7 +222,6 @@ set(wasm_sources tramp-wasm.c exceptions-wasm.c aot-runtime-wasm.c - mini-wasm-debugger.c wasm_m2n_invoke.g.h cpu-wasm.h) diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 6a181644fee..5efa6184fb6 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -65,7 +65,6 @@ #include "mini.h" #include "seq-points.h" -#include "debugger-agent.h" #include "aot-compiler.h" #include "aot-runtime.h" #include "jit-icalls.h" @@ -73,6 +72,8 @@ #include "mono-private-unstable.h" #include "llvmonly-runtime.h" +#include + #ifndef DISABLE_AOT #ifdef MONO_ARCH_CODE_EXEC_ONLY @@ -5321,10 +5322,10 @@ load_function_full (MonoAotModule *amodule, const char *name, MonoTrampInfo **ou MONO_AOT_ICALL (mono_exception_from_token) case MONO_JIT_ICALL_mono_debugger_agent_single_step_from_context: - target = (gpointer)mini_get_dbg_callbacks ()->single_step_from_context; + target = (gpointer)mono_component_debugger ()->single_step_from_context; break; case MONO_JIT_ICALL_mono_debugger_agent_breakpoint_from_context: - target = (gpointer)mini_get_dbg_callbacks ()->breakpoint_from_context; + target = (gpointer)mono_component_debugger ()->breakpoint_from_context; break; case MONO_JIT_ICALL_mono_throw_exception: target = mono_get_throw_exception_addr (); diff --git a/src/mono/mono/mini/debugger-agent-external.c b/src/mono/mono/mini/debugger-agent-external.c new file mode 100644 index 00000000000..af2d1057359 --- /dev/null +++ b/src/mono/mono/mini/debugger-agent-external.c @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include "debugger-agent-external.h" + +#ifndef DISABLE_SDB + +static char *sdb_options = NULL; + +#define MAX_TRANSPORTS 16 +static DebuggerTransport transports [MAX_TRANSPORTS]; +static int ntransports = 0; + +static void +register_transport (DebuggerTransport *trans) +{ + g_assert (ntransports < MAX_TRANSPORTS); + + memcpy (&transports [ntransports], trans, sizeof (DebuggerTransport)); + ntransports ++; +} + +void +mono_debugger_agent_register_transport (DebuggerTransport *trans) +{ + register_transport (trans); +} + +gboolean +mono_debugger_agent_transport_handshake (void) +{ + return mono_component_debugger ()->transport_handshake (); +} + +void +mono_debugger_agent_init (void) +{ + //not need to do anything anymore +} + +void +mono_debugger_agent_parse_options (char *options) +{ + sdb_options = options; +} + +DebuggerTransport * +mono_debugger_agent_get_transports (int *ntrans) +{ + *ntrans = ntransports; + return transports; +} + +char * +mono_debugger_agent_get_sdb_options (void) +{ + return sdb_options; +} +#endif /* DISABLE_SDB */ diff --git a/src/mono/mono/mini/debugger-agent-external.h b/src/mono/mono/mini/debugger-agent-external.h new file mode 100644 index 00000000000..b37cd6b5e04 --- /dev/null +++ b/src/mono/mono/mini/debugger-agent-external.h @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef __MONO_DEBUGGER_AGENT_H__ +#define __MONO_DEBUGGER_AGENT_H__ + +#include "mini.h" +#include "mono/component/debugger.h" + +MONO_API void +mono_debugger_agent_init (void); + +MONO_API void +mono_debugger_agent_parse_options (char *options); + +MONO_API MONO_RT_EXTERNAL_ONLY gboolean +mono_debugger_agent_transport_handshake (void); + +MONO_API void +mono_debugger_agent_register_transport (DebuggerTransport *trans); + +MONO_COMPONENT_API DebuggerTransport * +mono_debugger_agent_get_transports (int *ntrans); + +MONO_COMPONENT_API char * +mono_debugger_agent_get_sdb_options (void); + +#endif diff --git a/src/mono/mono/mini/debugger-agent-stubs.c b/src/mono/mono/mini/debugger-agent-stubs.c deleted file mode 100644 index 7746e3ae8c8..00000000000 --- a/src/mono/mono/mini/debugger-agent-stubs.c +++ /dev/null @@ -1,122 +0,0 @@ -/* \file - * Soft Debugger stubs - * - * Author: - * Zoltan Varga (vargaz@gmail.com) - * - * Copyright 2009-2010 Novell, Inc. - * Copyright 2011 Xamarin Inc. - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include - -#include "mini-runtime.h" -#include "debugger-agent.h" - -static void -stub_debugger_agent_parse_options (char *options) -{ - g_error ("This runtime is configured with the debugger agent disabled."); -} - -static void -stub_debugger_agent_init (void) -{ -} - -static void -stub_debugger_agent_breakpoint_hit (void *sigctx) -{ -} - -static void -stub_debugger_agent_single_step_event (void *sigctx) -{ -} - -static void -stub_debugger_agent_free_mem_manager (gpointer mem_manager) -{ -} - -static void -stub_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, - MonoContext *catch_ctx, StackFrameInfo *catch_frame) -{ -} - -static void -stub_debugger_agent_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx) -{ -} - -static void -stub_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx) -{ -} - -static void -stub_debugger_agent_user_break (void) -{ - G_BREAKPOINT (); -} - -static void -stub_debugger_agent_debug_log (int level, MonoString *category, MonoString *message) -{ -} - -static gboolean -stub_debugger_agent_debug_log_is_enabled (void) -{ - return FALSE; -} - -static void -stub_debugger_agent_unhandled_exception (MonoException *exc) -{ - g_assert_not_reached (); -} - -static void -stub_debugger_agent_single_step_from_context (MonoContext *ctx) -{ - g_assert_not_reached (); -} - -static void -stub_debugger_agent_breakpoint_from_context (MonoContext *ctx) -{ - g_assert_not_reached (); -} - -static void -stub_debugger_agent_send_crash (char *json_dump, MonoStackHash *hashes, int pause) -{ -} - -void -mono_debugger_agent_stub_init (void) -{ - MonoDebuggerCallbacks cbs; - - memset (&cbs, 0, sizeof (MonoDebuggerCallbacks)); - cbs.version = MONO_DBG_CALLBACKS_VERSION; - cbs.parse_options = stub_debugger_agent_parse_options; - cbs.init = stub_debugger_agent_init; - cbs.breakpoint_hit = stub_debugger_agent_breakpoint_hit; - cbs.single_step_event = stub_debugger_agent_single_step_event; - cbs.single_step_from_context = stub_debugger_agent_single_step_from_context; - cbs.breakpoint_from_context = stub_debugger_agent_breakpoint_from_context; - cbs.unhandled_exception = stub_debugger_agent_unhandled_exception; - cbs.handle_exception = stub_debugger_agent_handle_exception; - cbs.begin_exception_filter = stub_debugger_agent_begin_exception_filter; - cbs.end_exception_filter = stub_debugger_agent_end_exception_filter; - cbs.user_break = stub_debugger_agent_user_break; - cbs.debug_log = stub_debugger_agent_debug_log; - cbs.debug_log_is_enabled = stub_debugger_agent_debug_log_is_enabled; - cbs.send_crash = stub_debugger_agent_send_crash; - - mini_install_dbg_callbacks (&cbs); -} diff --git a/src/mono/mono/mini/debugger-agent.h b/src/mono/mono/mini/debugger-agent.h deleted file mode 100644 index bf0a06e2056..00000000000 --- a/src/mono/mono/mini/debugger-agent.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * \file - */ - -#ifndef __MONO_DEBUGGER_AGENT_H__ -#define __MONO_DEBUGGER_AGENT_H__ - -#include "mini.h" -#include "debugger-protocol.h" - -#include - -#define MONO_DBG_CALLBACKS_VERSION (4) -// 2. debug_log parameters changed from MonoString* to MonoStringHandle -// 3. debug_log parameters changed from MonoStringHandle back to MonoString* - -typedef struct _InvokeData InvokeData; - -struct _InvokeData -{ - int id; - int flags; - guint8 *p; - guint8 *endp; - /* This is the context which needs to be restored after the invoke */ - MonoContext ctx; - gboolean has_ctx; - /* - * If this is set, invoke this method with the arguments given by ARGS. - */ - MonoMethod *method; - gpointer *args; - guint32 suspend_count; - int nmethods; - - InvokeData *last_invoke; -}; - -typedef struct { - const char *name; - void (*connect) (const char *address); - void (*close1) (void); - void (*close2) (void); - gboolean (*send) (void *buf, int len); - int (*recv) (void *buf, int len); -} DebuggerTransport; - -struct _MonoDebuggerCallbacks { - int version; - void (*parse_options) (char *options); - void (*init) (void); - void (*breakpoint_hit) (void *sigctx); - void (*single_step_event) (void *sigctx); - void (*single_step_from_context) (MonoContext *ctx); - void (*breakpoint_from_context) (MonoContext *ctx); - void (*free_mem_manager) (gpointer mem_manager); - void (*unhandled_exception) (MonoException *exc); - void (*handle_exception) (MonoException *exc, MonoContext *throw_ctx, - MonoContext *catch_ctx, StackFrameInfo *catch_frame); - void (*begin_exception_filter) (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); - void (*end_exception_filter) (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx); - void (*user_break) (void); - void (*debug_log) (int level, MonoString *category, MonoString *message); - gboolean (*debug_log_is_enabled) (void); - void (*send_crash) (char *json_dump, MonoStackHash *hashes, int pause); -}; - -typedef struct _DebuggerTlsData DebuggerTlsData; - -MONO_API void -mono_debugger_agent_init (void); - -MONO_API void -mono_debugger_agent_parse_options (char *options); - -void -mono_debugger_agent_stub_init (void); - -MONO_API MONO_RT_EXTERNAL_ONLY gboolean -mono_debugger_agent_transport_handshake (void); - -MONO_API void -mono_debugger_agent_register_transport (DebuggerTransport *trans); - -MdbgProtErrorCode -mono_process_dbg_packet (int id, MdbgProtCommandSet command_set, int command, gboolean *no_reply, guint8 *p, guint8 *end, MdbgProtBuffer *buf); - -void -mono_init_debugger_agent_for_wasm (int log_level); - -void* -mono_dbg_create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, MdbgProtEventKind kind); - -void -mono_dbg_process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset); - -void -mono_dbg_debugger_agent_user_break (void); - -void -mono_wasm_save_thread_context (void); - -DebuggerTlsData* -mono_wasm_get_tls (void); - -MdbgProtErrorCode -mono_do_invoke_method (DebuggerTlsData *tls, MdbgProtBuffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp); - -void -mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); - -void -mono_ss_discard_frame_context (void *the_tls); - -#endif diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c index 4c694d5d0c5..f2792e4e6a4 100644 --- a/src/mono/mono/mini/driver.c +++ b/src/mono/mono/mini/driver.c @@ -56,6 +56,9 @@ #include "mono/metadata/custom-attrs-internals.h" #include +#include +#include + #include "mini.h" #include "jit.h" #include "aot-compiler.h" @@ -66,7 +69,6 @@ #include #include #include -#include "debugger-agent.h" #if TARGET_OSX # include #endif @@ -1807,7 +1809,7 @@ mono_jit_parse_options (int argc, char * argv[]) if (strncmp (argv [i], "--debugger-agent=", 17) == 0) { MonoDebugOptions *opt = mini_get_debug_options (); - sdb_options = g_strdup (argv [i] + 17); + mono_debugger_agent_parse_options (g_strdup (argv [i] + 17)); opt->mdb_optimizations = TRUE; enable_debugging = TRUE; } else if (!strcmp (argv [i], "--soft-breakpoints")) { @@ -2382,7 +2384,7 @@ mono_main (int argc, char* argv[]) } else if (strncmp (argv [i], "--debugger-agent=", 17) == 0) { MonoDebugOptions *opt = mini_get_debug_options (); - sdb_options = g_strdup (argv [i] + 17); + mono_debugger_agent_parse_options (g_strdup (argv [i] + 17)); opt->mdb_optimizations = TRUE; enable_debugging = TRUE; } else if (strcmp (argv [i], "--security") == 0) { @@ -3260,3 +3262,9 @@ mono_parse_env_options (int *ref_argc, char **ref_argv []) fprintf (stderr, "%s", ret); exit (1); } + +MonoDebugOptions * +get_mini_debug_options (void) +{ + return &mini_debug_options; +} diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index cc305593c7f..2038284d4f9 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -72,10 +72,11 @@ #include #include #include -#include #include #include +#include + #ifdef TARGET_ARM #include #endif @@ -3286,7 +3287,7 @@ main_loop: MINT_IN_BREAK; MINT_IN_CASE(MINT_BREAK) ++ip; - do_debugger_tramp (mini_get_dbg_callbacks ()->user_break, frame); + do_debugger_tramp (mono_component_debugger ()->user_break, frame); MINT_IN_BREAK; MINT_IN_CASE(MINT_BREAKPOINT) ++ip; diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 20d6f8b5a28..90c45fd6700 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -14,7 +14,6 @@ #include "mini-runtime.h" #include "ir-emit.h" #include "jit-icalls.h" -#include "debugger-agent.h" #include #include diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 9f1df4ec820..dc0ad9462c5 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -80,7 +80,6 @@ #include "jit-icalls.h" #include "jit.h" -#include "debugger-agent.h" #include "seq-points.h" #include "aot-compiler.h" #include "mini-llvm.h" diff --git a/src/mono/mono/mini/mini-amd64-gsharedvt.c b/src/mono/mono/mini/mini-amd64-gsharedvt.c index c18131d883c..3a7d769e589 100644 --- a/src/mono/mono/mini/mini-amd64-gsharedvt.c +++ b/src/mono/mono/mini/mini-amd64-gsharedvt.c @@ -27,7 +27,6 @@ #include "mini.h" #include "mini-amd64.h" #include "mini-amd64-gsharedvt.h" -#include "debugger-agent.h" #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED) diff --git a/src/mono/mono/mini/mini-amd64.c b/src/mono/mono/mini/mini-amd64.c index 150791b62c3..87c0b175ee6 100644 --- a/src/mono/mono/mini/mini-amd64.c +++ b/src/mono/mono/mini/mini-amd64.c @@ -44,7 +44,6 @@ #include "ir-emit.h" #include "mini-amd64.h" #include "cpu-amd64.h" -#include "debugger-agent.h" #include "mini-gc.h" #include "mini-runtime.h" #include "aot-runtime.h" diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index 4fc43ee3071..58c0cef0484 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -29,7 +29,6 @@ #include "mini-arm.h" #include "cpu-arm.h" #include "ir-emit.h" -#include "debugger-agent.h" #include "mini-gc.h" #include "mini-runtime.h" #include "aot-runtime.h" diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index 9ffc8410766..d6df5b15012 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -70,11 +70,10 @@ #include #include #include +#include #include "mini.h" #include "trace.h" -#include "debugger-agent.h" -#include "debugger-engine.h" #include "seq-points.h" #include "llvm-runtime.h" #include "mini-llvm.h" @@ -2392,7 +2391,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt #endif } - mini_get_dbg_callbacks ()->begin_exception_filter (mono_ex, ctx, &initial_ctx); + mono_component_debugger ()->begin_exception_filter (mono_ex, ctx, &initial_ctx); if (G_UNLIKELY (mono_profiler_clauses_enabled ())) { jit_tls->orig_ex_ctx_set = TRUE; @@ -2416,7 +2415,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt if (enable_trace) g_print ("[%p:] EXCEPTION filter result: %d\n", (void*)(gsize)mono_native_thread_id_get (), filtered); - mini_get_dbg_callbacks ()->end_exception_filter (mono_ex, ctx, &initial_ctx); + mono_component_debugger ()->end_exception_filter (mono_ex, ctx, &initial_ctx); if (filtered && out_filter_idx) *out_filter_idx = filter_idx; if (out_ji) @@ -2657,7 +2656,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu if (res == MONO_FIRST_PASS_UNHANDLED) { if (mini_debug_options.break_on_exc) G_BREAKPOINT (); - mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); + mono_component_debugger ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); // FIXME: This runs managed code so it might cause another stack overflow when // we are handling a stack overflow @@ -2667,21 +2666,21 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu gboolean unhandled = FALSE; if (unhandled) - mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); + mono_component_debugger ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); else if (!ji || (jinfo_get_method (ji)->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)) { if (last_mono_wrapper_runtime_invoke && !mono_thread_internal_current ()->threadpool_thread) { - mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); + mono_component_debugger ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL); if (mini_get_debug_options ()->top_runtime_invoke_unhandled) { mini_set_abort_threshold (&catch_frame); mono_unhandled_exception_internal (obj); } } else { - mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); + mono_component_debugger ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); } } else if (res != MONO_FIRST_PASS_CALLBACK_TO_NATIVE) if (!is_caught_unmanaged) - mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); + mono_component_debugger ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); } } diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c index b273a96a5fa..346b4bd43f2 100644 --- a/src/mono/mono/mini/mini-posix.c +++ b/src/mono/mono/mini/mini-posix.c @@ -69,13 +69,14 @@ #include #include #include -#include +#include +#include #include "mini.h" #include #include #include "trace.h" -#include "debugger-agent.h" +#include #include "mini-runtime.h" #include "jit-icalls.h" @@ -999,7 +1000,7 @@ dump_native_stacktrace (const char *signal, MonoContext *mctx) // see if we can notify any attached debugger instances. // // At this point we are accepting that the below step might end in a crash - mini_get_dbg_callbacks ()->send_crash (output, &hashes, 0 /* wait # seconds */); + mono_component_debugger ()->send_crash (output, &hashes, 0 /* wait # seconds */); } output = NULL; mono_state_free_mem (&merp_mem); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index c500e946968..87f2070d389 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -72,6 +72,7 @@ #include #include #include +#include #include "mini.h" #include "seq-points.h" @@ -86,7 +87,6 @@ #include "mini-gc.h" #include "mini-llvm.h" -#include "debugger-agent.h" #include "lldb.h" #include "mini-runtime.h" #include "interp/interp.h" @@ -145,7 +145,6 @@ static mono_mutex_t jit_mutex; static MonoCodeManager *global_codeman; MonoDebugOptions mini_debug_options; -char *sdb_options; #ifdef VALGRIND_JIT_REGISTER_MAP int valgrind_register; @@ -3514,10 +3513,10 @@ MONO_SIG_HANDLER_FUNC (, mono_sigsegv_signal_handler) #if defined(MONO_ARCH_SOFT_DEBUG_SUPPORTED) && defined(HAVE_SIG_INFO) if (mono_arch_is_single_step_event (info, ctx)) { - mini_get_dbg_callbacks ()->single_step_event (ctx); + mono_component_debugger ()->single_step_event (ctx); return; } else if (mono_arch_is_breakpoint_event (info, ctx)) { - mini_get_dbg_callbacks ()->breakpoint_hit (ctx); + mono_component_debugger ()->breakpoint_hit (ctx); return; } #endif @@ -4148,7 +4147,7 @@ free_jit_mem_manager (MonoMemoryManager *mem_manager) g_hash_table_destroy (info->seq_points); g_hash_table_destroy (info->arch_seq_points); if (info->agent_info) - mini_get_dbg_callbacks ()->free_mem_manager (info); + mono_component_debugger ()->free_mem_manager (info); g_hash_table_destroy (info->gsharedvt_arg_tramp_hash); if (info->llvm_jit_callees) { g_hash_table_foreach (info->llvm_jit_callees, free_jit_callee_list, NULL); @@ -4215,21 +4214,6 @@ mini_install_interp_callbacks (const MonoEECallbacks *cbs) mono_interp_callbacks_pointer = cbs; } -static MonoDebuggerCallbacks dbg_cbs; - -void -mini_install_dbg_callbacks (MonoDebuggerCallbacks *cbs) -{ - g_assert (cbs->version == MONO_DBG_CALLBACKS_VERSION); - memcpy (&dbg_cbs, cbs, sizeof (MonoDebuggerCallbacks)); -} - -MonoDebuggerCallbacks* -mini_get_dbg_callbacks (void) -{ - return &dbg_cbs; -} - int mono_ee_api_version (void) { @@ -4287,14 +4271,9 @@ mini_init (const char *filename, const char *runtime_version) if (mono_use_interpreter) mono_ee_interp_init (mono_interp_opts_string); #endif - - mono_debugger_agent_stub_init (); -#ifndef DISABLE_SDB - mono_debugger_agent_init (); -#endif - - if (sdb_options) - mini_get_dbg_callbacks ()->parse_options (sdb_options); + mono_components_init (); + + mono_component_debugger ()->parse_options (mono_debugger_agent_get_sdb_options ()); mono_os_mutex_init_recursive (&jit_mutex); @@ -4333,8 +4312,8 @@ mini_init (const char *filename, const char *runtime_version) callbacks.get_runtime_build_info = mono_get_runtime_build_info; callbacks.get_runtime_build_version = mono_get_runtime_build_version; callbacks.set_cast_details = mono_set_cast_details; - callbacks.debug_log = mini_get_dbg_callbacks ()->debug_log; - callbacks.debug_log_is_enabled = mini_get_dbg_callbacks ()->debug_log_is_enabled; + callbacks.debug_log = mono_component_debugger ()->debug_log; + callbacks.debug_log_is_enabled = mono_component_debugger ()->debug_log_is_enabled; callbacks.get_vtable_trampoline = mini_get_vtable_trampoline; callbacks.get_imt_trampoline = mini_get_imt_trampoline; callbacks.imt_entry_inited = mini_imt_entry_inited; @@ -4430,11 +4409,7 @@ mini_init (const char *filename, const char *runtime_version) if (default_opt & MONO_OPT_AOT) mono_aot_init (); - mini_get_dbg_callbacks ()->init (); - -#ifdef TARGET_WASM - mono_wasm_debugger_init (); -#endif + mono_component_debugger ()->init (&mono_defaults); #ifdef MONO_ARCH_GSHARED_SUPPORTED mono_set_generic_sharing_supported (TRUE); @@ -4587,7 +4562,7 @@ register_icalls (void) #if defined(HOST_ANDROID) || defined(TARGET_ANDROID) mono_add_internal_call_internal ("System.Diagnostics.Debugger::Mono_UnhandledException_internal", - mini_get_dbg_callbacks ()->unhandled_exception); + mono_component_debugger ()->unhandled_exception); #endif /* @@ -4811,7 +4786,7 @@ register_icalls (void) register_icall (mono_fill_class_rgctx, mono_icall_sig_ptr_ptr_int, FALSE); register_icall (mono_fill_method_rgctx, mono_icall_sig_ptr_ptr_int, FALSE); - register_dyn_icall (mini_get_dbg_callbacks ()->user_break, mono_debugger_agent_user_break, mono_icall_sig_void, FALSE); + register_dyn_icall (mono_component_debugger ()->user_break, mono_debugger_agent_user_break, mono_icall_sig_void, FALSE); register_icall (mini_llvm_init_method, mono_icall_sig_void_ptr_ptr_ptr_ptr, TRUE); register_icall_no_wrapper (mini_llvmonly_resolve_iface_call_gsharedvt, mono_icall_sig_ptr_object_int_ptr_ptr); @@ -5187,3 +5162,9 @@ mini_get_stack_overflow_ex (void) { return mono_get_root_domain ()->stack_overflow_ex; } + +const MonoEECallbacks* +mini_get_interp_callbacks_api (void) +{ + return mono_interp_callbacks_pointer; +} diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index 67d441495bc..57fb1b29824 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -452,9 +452,10 @@ extern GHashTable *mono_single_method_hash; extern GList* mono_aot_paths; extern MonoDebugOptions mini_debug_options; extern GSList *mono_interp_only_classes; -extern char *sdb_options; extern MonoMethodDesc *mono_stats_method_desc; +MONO_COMPONENT_API MonoDebugOptions *get_mini_debug_options (void); + /* This struct describes what execution engine feature to use. This subsume, and will eventually sunset, mono_aot_only / mono_llvm_only and friends. @@ -510,10 +511,7 @@ MonoEECallbacks* mono_interp_callbacks_pointer; #define mini_get_interp_callbacks() (mono_interp_callbacks_pointer) -typedef struct _MonoDebuggerCallbacks MonoDebuggerCallbacks; - -void mini_install_dbg_callbacks (MonoDebuggerCallbacks *cbs); -MonoDebuggerCallbacks *mini_get_dbg_callbacks (void); +MONO_COMPONENT_API const MonoEECallbacks* mini_get_interp_callbacks_api (void); MonoDomain* mini_init (const char *filename, const char *runtime_version); void mini_cleanup (MonoDomain *domain); @@ -530,10 +528,10 @@ void mono_precompile_assemblies (void); MONO_API int mono_parse_default_optimizations (const char* p); gboolean mono_running_on_valgrind (void); -MonoLMF * mono_get_lmf (void); +MONO_COMPONENT_API MonoLMF * mono_get_lmf (void); void mono_set_lmf (MonoLMF *lmf); -void mono_push_lmf (MonoLMFExt *ext); -void mono_pop_lmf (MonoLMF *lmf); +MONO_COMPONENT_API void mono_push_lmf (MonoLMFExt *ext); +MONO_COMPONENT_API void mono_pop_lmf (MonoLMF *lmf); MONO_API MONO_RT_EXTERNAL_ONLY void mono_jit_set_domain (MonoDomain *domain); @@ -555,7 +553,7 @@ gpointer mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, Mon void mini_register_jump_site (MonoMethod *method, gpointer ip); void mini_patch_jump_sites (MonoMethod *method, gpointer addr); void mini_patch_llvm_jit_callees (MonoMethod *method, gpointer addr); -gpointer mono_jit_search_all_backends_for_jit_info (MonoMethod *method, MonoJitInfo **ji); +MONO_COMPONENT_API gpointer mono_jit_search_all_backends_for_jit_info (MonoMethod *method, MonoJitInfo **ji); gpointer mono_jit_find_compiled_method_with_jit_info (MonoMethod *method, MonoJitInfo **ji); gpointer mono_jit_find_compiled_method (MonoMethod *method); gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error); diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index 673e575781d..56d58e1c6a0 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -9,10 +9,12 @@ #include #include #include +#include //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle #include +static int mono_wasm_debug_level = 0; #ifndef DISABLE_JIT #include "ir-emit.h" @@ -760,3 +762,15 @@ mono_arch_load_function (MonoJitICallId jit_icall_id) { return NULL; } + +MONO_API void +mono_wasm_enable_debugging (int log_level) +{ + mono_wasm_debug_level = log_level; +} + +int +mono_wasm_get_debug_level (void) +{ + return mono_wasm_debug_level; +} diff --git a/src/mono/mono/mini/mini-wasm.h b/src/mono/mono/mini/mini-wasm.h index 1c78fb83528..df0a852b1a5 100644 --- a/src/mono/mono/mini/mini-wasm.h +++ b/src/mono/mono/mini/mini-wasm.h @@ -97,18 +97,11 @@ typedef struct { #define MONO_ARCH_LLVM_TARGET_LAYOUT "e-m:e-p:32:32-i64:64-n32:64-S128" #define MONO_ARCH_LLVM_TARGET_TRIPLE "wasm32-unknown-emscripten" -void mono_wasm_debugger_init (void); - // sdks/wasm/driver.c is C and uses this G_EXTERN_C void mono_wasm_enable_debugging (int log_level); -void mono_wasm_breakpoint_hit (void); void mono_wasm_set_timeout (int timeout, int id); -void mono_wasm_single_step_hit (void); -void mono_wasm_breakpoint_hit (void); -void mono_wasm_user_break (void); - int mono_wasm_assembly_already_added (const char *assembly_name); void mono_wasm_print_stack_trace (void); diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 72a5bf4179d..11141bc79d4 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -71,7 +71,6 @@ #include "jit-icalls.h" #include "mini-gc.h" -#include "debugger-agent.h" #include "llvm-runtime.h" #include "mini-llvm.h" #include "lldb.h" diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 2afc62d7942..aefa9a270c8 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2432,7 +2432,7 @@ gboolean mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode); gboolean mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig, gboolean virtual_); int mono_arch_translate_tls_offset (int offset); gboolean mono_arch_opcode_supported (int opcode); -void mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func); +MONO_COMPONENT_API void mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func); gboolean mono_arch_have_fast_tls (void); #ifdef MONO_ARCH_HAS_REGISTER_ICALL @@ -2451,14 +2451,14 @@ mono_arch_is_soft_float (void) /* Soft Debug support */ #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED -void mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip); -void mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip); -void mono_arch_start_single_stepping (void); -void mono_arch_stop_single_stepping (void); +MONO_COMPONENT_API void mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip); +MONO_COMPONENT_API void mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip); +MONO_COMPONENT_API void mono_arch_start_single_stepping (void); +MONO_COMPONENT_API void mono_arch_stop_single_stepping (void); gboolean mono_arch_is_single_step_event (void *info, void *sigctx); gboolean mono_arch_is_breakpoint_event (void *info, void *sigctx); -void mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji); -void mono_arch_skip_single_step (MonoContext *ctx); +MONO_COMPONENT_API void mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji); +MONO_COMPONENT_API void mono_arch_skip_single_step (MonoContext *ctx); SeqPointInfo *mono_arch_get_seq_point_info (guint8 *code); #endif @@ -2483,9 +2483,9 @@ void mono_handle_hard_stack_ovf (MonoJitTlsData *jit_tls, MonoJi void mono_arch_undo_ip_adjustment (MonoContext *ctx); void mono_arch_do_ip_adjustment (MonoContext *ctx); gpointer mono_arch_ip_from_context (void *sigctx); -host_mgreg_t mono_arch_context_get_int_reg (MonoContext *ctx, int reg); -host_mgreg_t*mono_arch_context_get_int_reg_address (MonoContext *ctx, int reg); -void mono_arch_context_set_int_reg (MonoContext *ctx, int reg, host_mgreg_t val); +MONO_COMPONENT_API host_mgreg_t mono_arch_context_get_int_reg (MonoContext *ctx, int reg); +MONO_COMPONENT_API host_mgreg_t*mono_arch_context_get_int_reg_address (MonoContext *ctx, int reg); +MONO_COMPONENT_API void mono_arch_context_set_int_reg (MonoContext *ctx, int reg, host_mgreg_t val); void mono_arch_flush_register_windows (void); gboolean mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm); gboolean mono_arch_is_int_overflow (void *sigctx, void *info); @@ -2547,18 +2547,18 @@ gboolean mono_handle_exception (MonoContext *ctx, gpointer obj) void mono_handle_native_crash (const char *signal, MonoContext *mctx, MONO_SIG_HANDLER_INFO_TYPE *siginfo); MONO_API void mono_print_thread_dump (void *sigctx); MONO_API void mono_print_thread_dump_from_ctx (MonoContext *ctx); -void mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data); -void mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions unwind_options, void *user_data); +MONO_COMPONENT_API void mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data); +MONO_COMPONENT_API void mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions unwind_options, void *user_data); void mono_walk_stack (MonoJitStackWalk func, MonoUnwindOptions options, void *user_data); gboolean mono_thread_state_init_from_sigctx (MonoThreadUnwindState *ctx, void *sigctx); void mono_thread_state_init (MonoThreadUnwindState *ctx); -gboolean mono_thread_state_init_from_current (MonoThreadUnwindState *ctx); -gboolean mono_thread_state_init_from_monoctx (MonoThreadUnwindState *ctx, MonoContext *mctx); +MONO_COMPONENT_API gboolean mono_thread_state_init_from_current (MonoThreadUnwindState *ctx); +MONO_COMPONENT_API gboolean mono_thread_state_init_from_monoctx (MonoThreadUnwindState *ctx, MonoContext *mctx); void mono_setup_altstack (MonoJitTlsData *tls); void mono_free_altstack (MonoJitTlsData *tls); gpointer mono_altstack_restore_prot (host_mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp); -MonoJitInfo* mini_jit_info_table_find (gpointer addr); +MONO_COMPONENT_API MonoJitInfo* mini_jit_info_table_find (gpointer addr); MonoJitInfo* mini_jit_info_table_find_ext (gpointer addr, gboolean allow_trampolines); G_EXTERN_C void mono_resume_unwind (MonoContext *ctx); @@ -2566,7 +2566,7 @@ MonoJitInfo * mono_find_jit_info (MonoJitTlsData *jit_tls, MonoJi typedef gboolean (*MonoExceptionFrameWalk) (MonoMethod *method, gpointer ip, size_t native_offset, gboolean managed, gpointer user_data); MONO_API gboolean mono_exception_walk_trace (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data); -void mono_restore_context (MonoContext *ctx); +MONO_COMPONENT_API void mono_restore_context (MonoContext *ctx); guint8* mono_jinfo_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len); int mono_jinfo_get_epilog_size (MonoJitInfo *ji); G_EXTERN_C void mono_llvm_rethrow_exception (MonoObject *ex); @@ -2703,7 +2703,7 @@ mono_class_rgctx_get_array_size (int n, gboolean mrgctx); MonoGenericContext mono_method_construct_object_context (MonoMethod *method); -MonoMethod* +MONO_COMPONENT_API MonoMethod* mono_method_get_declaring_generic_method (MonoMethod *method); int @@ -2949,12 +2949,15 @@ mini_safepoints_enabled (void) gpointer mono_arch_load_function (MonoJitICallId jit_icall_id); -MonoGenericContext +MONO_COMPONENT_API MonoGenericContext mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info); -gpointer +MONO_COMPONENT_API gpointer mono_get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx); MonoMemoryManager* mini_get_default_mem_manager (void); +MONO_COMPONENT_API int +mono_wasm_get_debug_level (void); + #endif /* __MONO_MINI_H__ */ diff --git a/src/mono/mono/mini/monovm.c b/src/mono/mono/mini/monovm.c index a7a5f8ad653..d3f1cdb93c6 100644 --- a/src/mono/mono/mini/monovm.c +++ b/src/mono/mono/mini/monovm.c @@ -11,6 +11,8 @@ #include #include +#include + static MonoCoreTrustedPlatformAssemblies *trusted_platform_assemblies; static MonoCoreLookupPaths *native_lib_paths; static MonoCoreLookupPaths *app_paths; diff --git a/src/mono/mono/mini/seq-points.h b/src/mono/mono/mini/seq-points.h index 17cba827789..a35584971c6 100644 --- a/src/mono/mono/mini/seq-points.h +++ b/src/mono/mono/mini/seq-points.h @@ -12,16 +12,16 @@ void mono_save_seq_point_info (MonoCompile *cfg, MonoJitInfo *jinfo); -MonoSeqPointInfo* +MONO_COMPONENT_API MonoSeqPointInfo* mono_get_seq_points (MonoMethod *method); -gboolean +MONO_COMPONENT_API gboolean mono_find_next_seq_point_for_native_offset (MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point); -gboolean +MONO_COMPONENT_API gboolean mono_find_prev_seq_point_for_native_offset (MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point); -gboolean +MONO_COMPONENT_API gboolean mono_find_seq_point (MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point); void diff --git a/src/mono/mono/mini/tramp-amd64-gsharedvt.c b/src/mono/mono/mini/tramp-amd64-gsharedvt.c index 63fec1dffea..0d564fbeefe 100644 --- a/src/mono/mono/mini/tramp-amd64-gsharedvt.c +++ b/src/mono/mono/mini/tramp-amd64-gsharedvt.c @@ -26,7 +26,6 @@ #include "mini.h" #include "mini-amd64.h" #include "mini-amd64-gsharedvt.h" -#include "debugger-agent.h" #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED) diff --git a/src/mono/mono/mini/tramp-amd64.c b/src/mono/mono/mini/tramp-amd64.c index 086796f51da..ad749405c15 100644 --- a/src/mono/mono/mini/tramp-amd64.c +++ b/src/mono/mono/mini/tramp-amd64.c @@ -28,12 +28,12 @@ #include "mini.h" #include "mini-amd64.h" #include "mini-runtime.h" -#include "debugger-agent.h" #ifndef DISABLE_INTERPRETER #include "interp/interp.h" #endif #include "mono/utils/mono-tls-inline.h" +#include #ifdef MONO_ARCH_CODE_EXEC_ONLY #include "aot-runtime.h" @@ -963,9 +963,9 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_debugger_agent_breakpoint_from_context)); } else { if (single_step) - amd64_mov_reg_imm (code, AMD64_R11, mini_get_dbg_callbacks ()->single_step_from_context); + amd64_mov_reg_imm (code, AMD64_R11, mono_component_debugger ()->single_step_from_context); else - amd64_mov_reg_imm (code, AMD64_R11, mini_get_dbg_callbacks ()->breakpoint_from_context); + amd64_mov_reg_imm (code, AMD64_R11, mono_component_debugger ()->breakpoint_from_context); } amd64_call_reg (code, AMD64_R11); diff --git a/src/mono/mono/mini/tramp-arm.c b/src/mono/mono/mini/tramp-arm.c index 3a9f87e02ae..0834f106505 100644 --- a/src/mono/mono/mini/tramp-arm.c +++ b/src/mono/mono/mini/tramp-arm.c @@ -24,7 +24,6 @@ #include "mini.h" #include "mini-arm.h" #include "mini-runtime.h" -#include "debugger-agent.h" #include "jit-icalls.h" #ifndef DISABLE_INTERPRETER @@ -32,6 +31,8 @@ #endif #include "mono/utils/mono-tls-inline.h" +#include + void mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr) { @@ -810,9 +811,9 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); ARM_B (code, 0); if (single_step) - *(gpointer*)code = (gpointer)mini_get_dbg_callbacks ()->single_step_from_context; + *(gpointer*)code = (gpointer)mono_component_debugger ()->single_step_from_context; else - *(gpointer*)code = (gpointer)mini_get_dbg_callbacks ()->breakpoint_from_context; + *(gpointer*)code = (gpointer)mono_component_debugger ()->breakpoint_from_context; code += 4; ARM_BLX_REG (code, ARMREG_IP); } diff --git a/src/mono/mono/mini/tramp-arm64.c b/src/mono/mono/mini/tramp-arm64.c index bffb46341a2..e0ea4f11e78 100644 --- a/src/mono/mono/mini/tramp-arm64.c +++ b/src/mono/mono/mini/tramp-arm64.c @@ -17,7 +17,6 @@ #include "mini.h" #include "mini-runtime.h" -#include "debugger-agent.h" #include #include @@ -27,6 +26,8 @@ #endif #include "mono/utils/mono-tls-inline.h" +#include + void mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr) { @@ -536,7 +537,7 @@ mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboo * mono_arch_create_sdb_trampoline: * * Return a trampoline which captures the current context, passes it to - * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (), + * mono_component_debugger ()->single_step_from_context ()/mono_component_debugger ()->breakpoint_from_context (), * then restores the (potentially changed) context. */ guint8* @@ -604,7 +605,7 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo else code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_debugger_agent_breakpoint_from_context)); } else { - void (*addr) (MonoContext *ctx) = single_step ? mini_get_dbg_callbacks ()->single_step_from_context : mini_get_dbg_callbacks ()->breakpoint_from_context; + void (*addr) (MonoContext *ctx) = single_step ? mono_component_debugger ()->single_step_from_context : mono_component_debugger ()->breakpoint_from_context; code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr); } diff --git a/src/mono/mono/mini/tramp-s390x.c b/src/mono/mono/mini/tramp-s390x.c index d035295e872..d301a3777ea 100644 --- a/src/mono/mono/mini/tramp-s390x.c +++ b/src/mono/mono/mini/tramp-s390x.c @@ -51,9 +51,10 @@ #include "mini-s390x.h" #include "mini-runtime.h" #include "jit-icalls.h" -#include "debugger-agent.h" #include "mono/utils/mono-tls-inline.h" +#include + /*========================= End of Includes ========================*/ /*------------------------------------------------------------------*/ @@ -195,9 +196,9 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo s390_la (code, s390_r2, 0, STK_BASE, ctx_offset); if (single_step) - ep = (mini_get_dbg_callbacks())->single_step_from_context; + ep = (mono_component_debugger ())->single_step_from_context; else - ep = (mini_get_dbg_callbacks())->breakpoint_from_context; + ep = (mono_component_debugger ())->breakpoint_from_context; S390_SET (code, s390_r1, ep); s390_basr (code, s390_r14, s390_r1); diff --git a/src/mono/mono/mini/tramp-wasm.c b/src/mono/mono/mini/tramp-wasm.c index 4156cfccb23..8ba5f54b978 100644 --- a/src/mono/mono/mini/tramp-wasm.c +++ b/src/mono/mono/mini/tramp-wasm.c @@ -1,5 +1,6 @@ #include "mini.h" #include "interp/interp.h" +#include void mono_sdb_single_step_trampoline (void); @@ -76,10 +77,10 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo guint8* code; if (single_step) { name = "sdb_single_step_trampoline"; - code = (guint8*)mono_wasm_single_step_hit; + code = (guint8*)mono_component_debugger ()->mono_wasm_single_step_hit; } else { name = "sdb_breakpoint_trampoline"; - code = (guint8*)mono_wasm_breakpoint_hit; + code = (guint8*)mono_component_debugger ()->mono_wasm_breakpoint_hit; } if (info) diff --git a/src/mono/mono/mini/tramp-x86.c b/src/mono/mono/mini/tramp-x86.c index 218e961dc16..582628105b1 100644 --- a/src/mono/mono/mini/tramp-x86.c +++ b/src/mono/mono/mini/tramp-x86.c @@ -24,10 +24,11 @@ #include "mini.h" #include "mini-x86.h" #include "mini-runtime.h" -#include "debugger-agent.h" #include "jit-icalls.h" #include "mono/utils/mono-tls-inline.h" +#include + /* * mono_arch_get_unbox_trampoline: * @m: method pointer @@ -618,7 +619,7 @@ mono_arch_get_gsharedvt_arg_trampoline (gpointer arg, gpointer addr) * mono_arch_create_sdb_trampoline: * * Return a trampoline which captures the current context, passes it to - * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (), + * mono_component_debugger ()->single_step_from_context ()/mono_component_debugger ()->breakpoint_from_context (), * then restores the (potentially changed) context. */ guint8* @@ -682,9 +683,9 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo x86_breakpoint (code); } else { if (single_step) - x86_call_code (code, mini_get_dbg_callbacks ()->single_step_from_context); + x86_call_code (code, mono_component_debugger ()->single_step_from_context); else - x86_call_code (code, mini_get_dbg_callbacks ()->breakpoint_from_context); + x86_call_code (code, mono_component_debugger ()->breakpoint_from_context); } /* Restore registers from ctx */ diff --git a/src/mono/mono/sgen/gc-internal-agnostic.h b/src/mono/mono/sgen/gc-internal-agnostic.h index b14713fb62c..73d431d54d8 100644 --- a/src/mono/mono/sgen/gc-internal-agnostic.h +++ b/src/mono/mono/sgen/gc-internal-agnostic.h @@ -98,7 +98,7 @@ MonoGCDescriptor mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits) MonoGCDescriptor mono_gc_make_vector_descr (void); /* Return a root descriptor for a root with all refs */ -MonoGCDescriptor mono_gc_make_root_descr_all_refs (int numbits) +MONO_COMPONENT_API MonoGCDescriptor mono_gc_make_root_descr_all_refs (int numbits) MONO_PERMIT (need (sgen_lock_gc)); /* Return the bitmap encoded by a descriptor */ diff --git a/src/mono/mono/utils/json.h b/src/mono/mono/utils/json.h index 03e6503fc98..7b7d8e82b46 100644 --- a/src/mono/mono/utils/json.h +++ b/src/mono/mono/utils/json.h @@ -12,7 +12,9 @@ #ifndef __MONO_UTILS_JSON_H__ #define __MONO_UTILS_JSON_H__ - #include +#include +#include "mono-compiler.h" + #define JSON_INDENT_VALUE 2 @@ -21,17 +23,17 @@ typedef struct JsonWriter { int indent; } JsonWriter; -void mono_json_writer_init (JsonWriter* writer); -void mono_json_writer_destroy (JsonWriter* writer); -void mono_json_writer_indent(JsonWriter* writer); -void mono_json_writer_indent_push(JsonWriter* writer); -void mono_json_writer_indent_pop(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_init (JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_destroy (JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_indent(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_indent_push(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_indent_pop(JsonWriter* writer); void mono_json_writer_vprintf(JsonWriter* writer, const gchar *format, va_list args); -void mono_json_writer_printf(JsonWriter* writer, const gchar *format, ...); -void mono_json_writer_array_begin(JsonWriter* writer); -void mono_json_writer_array_end(JsonWriter* writer); -void mono_json_writer_object_begin(JsonWriter* writer); -void mono_json_writer_object_end(JsonWriter* writer); -void mono_json_writer_object_key(JsonWriter* writer, const gchar* format, ...); +MONO_COMPONENT_API void mono_json_writer_printf(JsonWriter* writer, const gchar *format, ...); +MONO_COMPONENT_API void mono_json_writer_array_begin(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_array_end(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_object_begin(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_object_end(JsonWriter* writer); +MONO_COMPONENT_API void mono_json_writer_object_key(JsonWriter* writer, const gchar* format, ...); #endif diff --git a/src/mono/mono/utils/memfuncs.h b/src/mono/mono/utils/memfuncs.h index bc8c578bcb2..904b28c9656 100644 --- a/src/mono/mono/utils/memfuncs.h +++ b/src/mono/mono/utils/memfuncs.h @@ -12,6 +12,7 @@ #include #include +#include "mono-compiler.h" /* These functions must be used when it's possible that either destination is not @@ -19,7 +20,7 @@ word aligned or size is not a multiple of word size. */ void mono_gc_bzero_atomic (void *dest, size_t size); void mono_gc_bzero_aligned (void *dest, size_t size); -void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); +MONO_COMPONENT_API void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); void mono_gc_memmove_aligned (void *dest, const void *src, size_t size); guint64 mono_determine_physical_ram_size (void); guint64 mono_determine_physical_ram_available_size (void); diff --git a/src/mono/mono/utils/mono-context.h b/src/mono/mono/utils/mono-context.h index b305b9ba705..bd1a3cd0104 100644 --- a/src/mono/mono/utils/mono-context.h +++ b/src/mono/mono/utils/mono-context.h @@ -1076,7 +1076,7 @@ typedef struct { * The naming is misleading, the SIGCTX argument should be the platform's context * structure (ucontext_c on posix, CONTEXT on windows). */ -void mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx); +MONO_COMPONENT_API void mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx); /* * This will not completely initialize SIGCTX since MonoContext contains less @@ -1084,6 +1084,6 @@ void mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx); * the system, and use this function to override the parts of it which are * also in MonoContext. */ -void mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx); +MONO_COMPONENT_API void mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx); #endif /* __MONO_MONO_CONTEXT_H__ */ diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h index 76b3e33ee9c..2256c6b5adb 100644 --- a/src/mono/mono/utils/mono-error-internals.h +++ b/src/mono/mono/utils/mono-error-internals.h @@ -164,6 +164,7 @@ void mono_error_dup_strings (MonoError *error, gboolean dup_strings); /* This function is not very useful as you can't provide any details beyond the message.*/ +MONO_COMPONENT_API void mono_error_set_error (MonoError *error, int error_code, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4); @@ -283,7 +284,7 @@ mono_error_set_platform_not_supported (MonoError *error, const char *message) MonoException* mono_error_prepare_exception (MonoError *error, MonoError *error_out); -MonoException* +MONO_COMPONENT_API MonoException* mono_error_convert_to_exception (MonoError *error); void diff --git a/src/mono/mono/utils/mono-flight-recorder.h b/src/mono/mono/utils/mono-flight-recorder.h index 092c8be2221..6a56f02e424 100644 --- a/src/mono/mono/utils/mono-flight-recorder.h +++ b/src/mono/mono/utils/mono-flight-recorder.h @@ -31,16 +31,16 @@ typedef struct { MonoFlightRecorderItem *items [MONO_ZERO_LEN_ARRAY]; // The data of the history } MonoFlightRecorder; -MonoCoopMutex * +MONO_COMPONENT_API MonoCoopMutex * mono_flight_recorder_mutex (MonoFlightRecorder *recorder); -MonoFlightRecorder * +MONO_COMPONENT_API MonoFlightRecorder * mono_flight_recorder_init (size_t max_size, size_t payload_size); -void +MONO_COMPONENT_API void mono_flight_recorder_free (MonoFlightRecorder *recorder); -void +MONO_COMPONENT_API void mono_flight_recorder_append (MonoFlightRecorder *recorder, gpointer payload); // Used to traverse the ring buffer in order of oldest to newest message @@ -52,15 +52,15 @@ typedef struct { } MonoFlightRecorderIter; // Mutex has to be held when called -void +MONO_COMPONENT_API void mono_flight_recorder_iter_init (MonoFlightRecorder *recorder, MonoFlightRecorderIter *iter); // Mutex has to be held when called -void +MONO_COMPONENT_API void mono_flight_recorder_iter_destroy (MonoFlightRecorderIter *iter); // Mutex has to be held when called -gboolean +MONO_COMPONENT_API gboolean mono_flight_recorder_iter_next (MonoFlightRecorderIter *iter, MonoFlightRecorderHeader *header, gpointer *payload); #endif diff --git a/src/mono/mono/utils/mono-threads-api.h b/src/mono/mono/utils/mono-threads-api.h index a2c84d29e72..0dee0c176f0 100644 --- a/src/mono/mono/utils/mono-threads-api.h +++ b/src/mono/mono/utils/mono-threads-api.h @@ -48,7 +48,7 @@ mono_stackdata_get_stackpointer (const MonoStackData *stackdata) MONO_API MONO_RT_EXTERNAL_ONLY gpointer mono_threads_enter_gc_unsafe_region (gpointer* stackdata); -gpointer +MONO_COMPONENT_API gpointer mono_threads_enter_gc_unsafe_region_internal (MonoStackData *stackdata); MONO_API MONO_RT_EXTERNAL_ONLY void diff --git a/src/mono/mono/utils/mono-threads.h b/src/mono/mono/utils/mono-threads.h index adb65611f4c..4a29ab561e7 100644 --- a/src/mono/mono/utils/mono-threads.h +++ b/src/mono/mono/utils/mono-threads.h @@ -479,7 +479,7 @@ mono_thread_info_lookup (MonoNativeThreadId id); gboolean mono_thread_info_resume (MonoNativeThreadId tid); -void +MONO_COMPONENT_API void mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data); void @@ -552,7 +552,7 @@ mono_thread_info_is_live (THREAD_INFO_TYPE *info); int mono_thread_info_get_system_max_stack_size (void); -MonoThreadHandle* +MONO_COMPONENT_API MonoThreadHandle* mono_threads_open_thread_handle (MonoThreadHandle *handle); void diff --git a/src/mono/mono/utils/mono-time.h b/src/mono/mono/utils/mono-time.h index 874a4677c69..685f2bc76e3 100644 --- a/src/mono/mono/utils/mono-time.h +++ b/src/mono/mono/utils/mono-time.h @@ -17,7 +17,7 @@ gint64 mono_msec_boottime (void); /* Returns the number of milliseconds ticks from unspecified time: this should be monotonic */ -gint64 mono_msec_ticks (void); +MONO_COMPONENT_API gint64 mono_msec_ticks (void); /* Returns the number of 100ns ticks from unspecified time: this should be monotonic */ MONO_COMPONENT_API gint64 mono_100ns_ticks (void); diff --git a/src/mono/mono/utils/networking.h b/src/mono/mono/utils/networking.h index 4f6a0f5d733..67a96391c6a 100644 --- a/src/mono/mono/utils/networking.h +++ b/src/mono/mono/utils/networking.h @@ -87,11 +87,11 @@ typedef struct { } MonoAddress; /* This only supports IPV4 / IPV6 and tcp */ -int mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInfo **res); +MONO_COMPONENT_API int mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInfo **res); -void mono_free_address_info (MonoAddressInfo *ai); +MONO_COMPONENT_API void mono_free_address_info (MonoAddressInfo *ai); -void mono_socket_address_init (MonoSocketAddress *sa, socklen_t *len, int family, const void *address, int port); +MONO_COMPONENT_API void mono_socket_address_init (MonoSocketAddress *sa, socklen_t *len, int family, const void *address, int port); void *mono_get_local_interfaces (int family, int *interface_count); @@ -107,7 +107,7 @@ int mono_networking_get_tcp_protocol (void); int mono_networking_get_ip_protocol (void); int mono_networking_get_ipv6_protocol (void); -void mono_networking_init (void); +MONO_COMPONENT_API void mono_networking_init (void); void mono_networking_shutdown (void); diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 86288cd674e..64d7b9d658f 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -51,6 +51,7 @@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION) # FIXME: When https://github.com/dotnet/runtime/issues/54565 is fixed, and the WasmApp targets are updated to use mono runtime components, remove this MONO_COMPONENT_LIBS= \ $(MONO_BIN_DIR)/libmono-component-hot_reload-static.a \ + $(MONO_BIN_DIR)/libmono-component-debugger-static.a \ $(MONO_BIN_DIR)/libmono-component-diagnostics_tracing-stub-static.a MONO_OBJ_DIR=$(OBJDIR)/mono/Browser.wasm.$(CONFIG) diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index ffc2fb92825..c8186ff30d3 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -190,6 +190,7 @@ + + diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs index 4696bc9e2d1..dd494b9fdcf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs @@ -4,11 +4,27 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Security.Cryptography; + +using Internal.TypeSystem; namespace ILCompiler.Diagnostics { public class PerfMapWriter { + public const int LegacyCrossgen1FormatVersion = 0; + + public const int CurrentFormatVersion = 1; + + public enum PseudoRVA : uint + { + OutputGuid = 0xFFFFFFFF, + TargetOS = 0xFFFFFFFE, + TargetArchitecture = 0xFFFFFFFD, + FormatVersion = 0xFFFFFFFC, + } + private TextWriter _writer; private PerfMapWriter(TextWriter writer) @@ -16,11 +32,32 @@ namespace ILCompiler.Diagnostics _writer = writer; } - public static void Write(string perfMapFileName, IEnumerable methods) + public static void Write(string perfMapFileName, int perfMapFormatVersion, IEnumerable methods, IEnumerable inputAssemblies, TargetOS targetOS, TargetArchitecture targetArch) { + if (perfMapFormatVersion > CurrentFormatVersion) + { + throw new NotSupportedException(perfMapFormatVersion.ToString()); + } + using (TextWriter writer = new StreamWriter(perfMapFileName)) { + IEnumerable orderedInputs = inputAssemblies.OrderBy(asm => asm.Name, StringComparer.OrdinalIgnoreCase); + PerfMapWriter perfMapWriter = new PerfMapWriter(writer); + + List inputHash = new List(); + foreach (AssemblyInfo inputAssembly in orderedInputs) + { + inputHash.AddRange(inputAssembly.Mvid.ToByteArray()); + } + inputHash.Add((byte)targetOS); + inputHash.Add((byte)targetArch); + Guid outputGuid = new Guid(MD5.HashData(inputHash.ToArray())); + perfMapWriter.WriteLine(outputGuid.ToString(), (uint)PseudoRVA.OutputGuid, 0); + perfMapWriter.WriteLine(targetOS.ToString(), (uint)PseudoRVA.TargetOS, 0); + perfMapWriter.WriteLine(targetArch.ToString(), (uint)PseudoRVA.TargetArchitecture, 0); + perfMapWriter.WriteLine(CurrentFormatVersion.ToString(), (uint)PseudoRVA.FormatVersion, 0); + foreach (MethodInfo methodInfo in methods) { if (methodInfo.HotRVA != 0 && methodInfo.HotLength != 0) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index 74da51246b6..6404f77fbac 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -45,6 +45,12 @@ namespace ILCompiler.DependencyAnalysis /// private readonly EcmaModule _componentModule; + /// + /// Compilation input files. Input files are emitted as perfmap entries and used + /// to calculate the output GUID of the ReadyToRun executable for symbol indexation. + /// + private readonly IEnumerable _inputFiles; + /// /// Nodes to emit into the output executable as collected by the dependency analysis. /// @@ -101,9 +107,9 @@ namespace ILCompiler.DependencyAnalysis private string _perfMapPath; /// - /// MVID of the input managed module to embed in the perfmap file name. + /// Requested version of the perfmap file format /// - private Guid? _perfMapMvid; + private int _perfMapFormatVersion; /// /// If non-zero, the PE file will be laid out such that it can naturally be mapped with a higher alignment than 4KB. @@ -132,6 +138,7 @@ namespace ILCompiler.DependencyAnalysis public ReadyToRunObjectWriter( string objectFilePath, EcmaModule componentModule, + IEnumerable inputFiles, IEnumerable nodes, NodeFactory factory, bool generateMapFile, @@ -140,13 +147,14 @@ namespace ILCompiler.DependencyAnalysis string pdbPath, bool generatePerfMapFile, string perfMapPath, - Guid? perfMapMvid, + int perfMapFormatVersion, bool generateProfileFile, CallChainProfile callChainProfile, int customPESectionAlignment) { _objectFilePath = objectFilePath; _componentModule = componentModule; + _inputFiles = inputFiles; _nodes = nodes; _nodeFactory = factory; _customPESectionAlignment = customPESectionAlignment; @@ -156,7 +164,7 @@ namespace ILCompiler.DependencyAnalysis _pdbPath = pdbPath; _generatePerfMapFile = generatePerfMapFile; _perfMapPath = perfMapPath; - _perfMapMvid = perfMapMvid; + _perfMapFormatVersion = perfMapFormatVersion; bool generateMap = (generateMapFile || generateMapCsvFile); bool generateSymbols = (generatePdbFile || generatePerfMapFile); @@ -329,6 +337,11 @@ namespace ILCompiler.DependencyAnalysis if (_outputInfoBuilder != null) { + foreach (string inputFile in _inputFiles) + { + _outputInfoBuilder.AddInputModule(_nodeFactory.TypeSystemContext.GetModuleFromPath(inputFile)); + } + r2rPeBuilder.AddSections(_outputInfoBuilder); if (_generateMapFile) @@ -361,7 +374,7 @@ namespace ILCompiler.DependencyAnalysis { path = Path.GetDirectoryName(_objectFilePath); } - _symbolFileBuilder.SavePerfMap(path, _objectFilePath, _perfMapMvid); + _symbolFileBuilder.SavePerfMap(path, _perfMapFormatVersion, _objectFilePath, _nodeFactory.Target.OperatingSystem, _nodeFactory.Target.Architecture); } if (_profileFileBuilder != null) @@ -430,6 +443,7 @@ namespace ILCompiler.DependencyAnalysis public static void EmitObject( string objectFilePath, EcmaModule componentModule, + IEnumerable inputFiles, IEnumerable nodes, NodeFactory factory, bool generateMapFile, @@ -438,7 +452,7 @@ namespace ILCompiler.DependencyAnalysis string pdbPath, bool generatePerfMapFile, string perfMapPath, - Guid? perfMapMvid, + int perfMapFormatVersion, bool generateProfileFile, CallChainProfile callChainProfile, int customPESectionAlignment) @@ -447,6 +461,7 @@ namespace ILCompiler.DependencyAnalysis ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter( objectFilePath, componentModule, + inputFiles, nodes, factory, generateMapFile: generateMapFile, @@ -455,7 +470,7 @@ namespace ILCompiler.DependencyAnalysis pdbPath: pdbPath, generatePerfMapFile: generatePerfMapFile, perfMapPath: perfMapPath, - perfMapMvid: perfMapMvid, + perfMapFormatVersion: perfMapFormatVersion, generateProfileFile: generateProfileFile, callChainProfile, customPESectionAlignment); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 1dce9f8f67f..ac7058bf90d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -247,7 +247,7 @@ namespace ILCompiler private readonly string _pdbPath; private readonly bool _generatePerfMapFile; private readonly string _perfMapPath; - private readonly Guid? _perfMapMvid; + private readonly int _perfMapFormatVersion; private readonly bool _generateProfileFile; private readonly Func _printReproInstructions; @@ -283,7 +283,7 @@ namespace ILCompiler string pdbPath, bool generatePerfMapFile, string perfMapPath, - Guid? perfMapMvid, + int perfMapFormatVersion, bool generateProfileFile, int parallelism, ProfileDataManager profileData, @@ -309,7 +309,7 @@ namespace ILCompiler _pdbPath = pdbPath; _generatePerfMapFile = generatePerfMapFile; _perfMapPath = perfMapPath; - _perfMapMvid = perfMapMvid; + _perfMapFormatVersion = perfMapFormatVersion; _generateProfileFile = generateProfileFile; _customPESectionAlignment = customPESectionAlignment; SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout); @@ -347,6 +347,7 @@ namespace ILCompiler ReadyToRunObjectWriter.EmitObject( outputFile, componentModule: null, + inputFiles: _inputFiles, nodes, NodeFactory, generateMapFile: _generateMapFile, @@ -355,7 +356,7 @@ namespace ILCompiler pdbPath: _pdbPath, generatePerfMapFile: _generatePerfMapFile, perfMapPath: _perfMapPath, - perfMapMvid: _perfMapMvid, + perfMapFormatVersion: _perfMapFormatVersion, generateProfileFile: _generateProfileFile, callChainProfile: _profileData.CallChainProfile, _customPESectionAlignment); @@ -427,6 +428,7 @@ namespace ILCompiler ReadyToRunObjectWriter.EmitObject( outputFile, componentModule: inputModule, + inputFiles: new string[] { inputFile }, componentGraph.MarkedNodeList, componentFactory, generateMapFile: false, @@ -435,7 +437,7 @@ namespace ILCompiler pdbPath: null, generatePerfMapFile: false, perfMapPath: null, - perfMapMvid: null, + perfMapFormatVersion: _perfMapFormatVersion, generateProfileFile: false, _profileData.CallChainProfile, customPESectionAlignment: 0); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index fa4d3182e9a..c1207a4b5b2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -31,7 +31,7 @@ namespace ILCompiler private string _pdbPath; private bool _generatePerfMapFile; private string _perfMapPath; - private Guid? _perfMapMvid; + private int _perfMapFormatVersion; private bool _generateProfileFile; private int _parallelism; Func _printReproInstructions; @@ -156,11 +156,11 @@ namespace ILCompiler return this; } - public ReadyToRunCodegenCompilationBuilder UsePerfMapFile(bool generatePerfMapFile, string perfMapPath, Guid? inputModuleMvid) + public ReadyToRunCodegenCompilationBuilder UsePerfMapFile(bool generatePerfMapFile, string perfMapPath, int perfMapFormatVersion) { _generatePerfMapFile = generatePerfMapFile; _perfMapPath = perfMapPath; - _perfMapMvid = inputModuleMvid; + _perfMapFormatVersion = perfMapFormatVersion; return this; } @@ -312,7 +312,7 @@ namespace ILCompiler pdbPath: _pdbPath, generatePerfMapFile: _generatePerfMapFile, perfMapPath: _perfMapPath, - perfMapMvid: _perfMapMvid, + perfMapFormatVersion: _perfMapFormatVersion, generateProfileFile: _generateProfileFile, _parallelism, _profileData, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs index 82145e37999..ca6efc9edb1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs @@ -106,6 +106,7 @@ namespace ILCompiler.PEWriter /// public class OutputInfoBuilder { + private readonly List _inputModules; private readonly List _nodes; private readonly List _symbols; private readonly List
_sections; @@ -117,6 +118,7 @@ namespace ILCompiler.PEWriter public OutputInfoBuilder() { + _inputModules = new List(); _nodes = new List(); _symbols = new List(); _sections = new List
(); @@ -127,6 +129,11 @@ namespace ILCompiler.PEWriter _relocCounts = new Dictionary(); } + public void AddInputModule(EcmaModule module) + { + _inputModules.Add(module); + } + public void AddNode(OutputNode node, ISymbolDefinitionNode symbol) { _nodes.Add(node); @@ -197,6 +204,16 @@ namespace ILCompiler.PEWriter } } + public IEnumerable EnumerateInputAssemblies() + { + foreach (EcmaModule inputModule in _inputModules) + { + yield return new AssemblyInfo( + inputModule.Assembly.GetName().Name, + inputModule.MetadataReader.GetGuid(inputModule.MetadataReader.GetModuleDefinition().Mvid)); + } + } + private string FormatMethodName(MethodDesc method, TypeNameFormatter typeNameFormatter) { StringBuilder output = new StringBuilder(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs index 7ec93572f6f..8cd0ad481cc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Internal.TypeSystem; using ILCompiler.Diagnostics; namespace ILCompiler.PEWriter @@ -28,12 +29,34 @@ namespace ILCompiler.PEWriter new PdbWriter(pdbPath, PDBExtraData.None).WritePDBData(dllFileName, _outputInfoBuilder.EnumerateMethods()); } - public void SavePerfMap(string perfMapPath, string dllFileName, Guid? perfMapMvid) + public void SavePerfMap(string perfMapPath, int perfMapFormatVersion, string dllFileName, TargetOS targetOS, TargetArchitecture targetArch) { - string mvidComponent = (perfMapMvid.HasValue ? perfMapMvid.Value.ToString() : "composite"); - string perfMapFileName = Path.Combine(perfMapPath, Path.GetFileNameWithoutExtension(dllFileName) + ".ni.{" + mvidComponent + "}.map"); + string perfMapExtension; + if (perfMapFormatVersion == PerfMapWriter.LegacyCrossgen1FormatVersion) + { + string mvidComponent = null; + foreach (AssemblyInfo inputAssembly in _outputInfoBuilder.EnumerateInputAssemblies()) + { + if (mvidComponent == null) + { + mvidComponent = inputAssembly.Mvid.ToString(); + } + else + { + mvidComponent = "composite"; + break; + } + } + perfMapExtension = ".ni.{" + mvidComponent + "}.map"; + } + else + { + perfMapExtension = ".ni.r2rmap"; + } + + string perfMapFileName = Path.Combine(perfMapPath, Path.GetFileNameWithoutExtension(dllFileName) + perfMapExtension); Console.WriteLine("Emitting PerfMap file: {0}", perfMapFileName); - PerfMapWriter.Write(perfMapFileName, _outputInfoBuilder.EnumerateMethods()); + PerfMapWriter.Write(perfMapFileName, perfMapFormatVersion, _outputInfoBuilder.EnumerateMethods(), _outputInfoBuilder.EnumerateInputAssemblies(), targetOS, targetArch); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index 1424375efc4..0d332d59f91 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -7,7 +7,7 @@ AnyCPU Open true - netstandard2.0 + $(NetCoreAppToolCurrent) false 8002,NU1701 win-x64;win-x86 @@ -27,6 +27,9 @@ - + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index f727ed872e0..821a38f545e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; @@ -15,9 +16,9 @@ using System.Text; using Internal.CorConstants; using Internal.Runtime; using Internal.ReadyToRunConstants; +using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; -using System.Linq; namespace ILCompiler.Reflection.ReadyToRun { @@ -79,6 +80,8 @@ namespace ILCompiler.Reflection.ReadyToRun public sealed class ReadyToRunReader { + public const int GuidByteSize = 16; + private const string SystemModuleName = "System.Private.CoreLib"; /// @@ -176,6 +179,33 @@ namespace ILCompiler.Reflection.ReadyToRun } } + /// + /// Conversion of the PE machine ID to TargetArchitecture used by TargetDetails. + /// + public TargetArchitecture TargetArchitecture + { + get + { + switch (Machine) + { + case Machine.I386: + return TargetArchitecture.X86; + + case Machine.Amd64: + return TargetArchitecture.X64; + + case Machine.ArmThumb2: + return TargetArchitecture.ARM; + + case Machine.Arm64: + return TargetArchitecture.ARM64; + + default: + throw new NotImplementedException(_machine.ToString()); + } + } + } + /// /// Targeting operating system for the R2R executable /// @@ -188,6 +218,36 @@ namespace ILCompiler.Reflection.ReadyToRun } } + /// + /// Targeting operating system converted to the enumeration used by TargetDetails. + /// + public TargetOS TargetOperatingSystem + { + get + { + switch (OperatingSystem) + { + case OperatingSystem.Windows: + return TargetOS.Windows; + + case OperatingSystem.Linux: + return TargetOS.Linux; + + case OperatingSystem.Apple: + return TargetOS.OSX; + + case OperatingSystem.FreeBSD: + return TargetOS.FreeBSD; + + case OperatingSystem.NetBSD: + return TargetOS.FreeBSD; + + default: + throw new NotImplementedException(OperatingSystem.ToString()); + } + } + } + /// /// Targeting processor architecture of the R2R executable /// @@ -537,6 +597,12 @@ namespace ILCompiler.Reflection.ReadyToRun return (_composite ? null : _assemblyCache[0]); } + public string GetGlobalAssemblyName() + { + MetadataReader mdReader = GetGlobalMetadata().MetadataReader; + return mdReader.GetString(mdReader.GetAssemblyDefinition().Name); + } + private unsafe void EnsureHeader() { if (_readyToRunHeader != null) @@ -1087,6 +1153,26 @@ namespace ILCompiler.Reflection.ReadyToRun } } + public Guid GetAssemblyMvid(int assemblyIndex) + { + EnsureHeader(); + if (_composite) + { + if (!ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection mvidSection)) + { + return Guid.Empty; + } + int mvidOffset = GetOffset(mvidSection.RelativeVirtualAddress) + GuidByteSize * assemblyIndex; + return new Guid(new ReadOnlySpan(Image, mvidOffset, ReadyToRunReader.GuidByteSize)); + } + else + { + Debug.Assert(assemblyIndex == 0); + MetadataReader mdReader = GetGlobalMetadata().MetadataReader; + return mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid); + } + } + /// /// Iterates through a native hashtable to get all RIDs /// diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs index 573b1eb5cb4..c91e615eb73 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -12,6 +12,8 @@ namespace ILCompiler { internal class CommandLineOptions { + public const int DefaultPerfMapFormatVersion = 0; + public bool Help; public string HelpText; @@ -56,6 +58,7 @@ namespace ILCompiler public string PdbPath; public bool PerfMap; public string PerfMapPath; + public int PerfMapFormatVersion; public int Parallelism; public int CustomPESectionAlignment; public string MethodLayout; @@ -81,6 +84,7 @@ namespace ILCompiler MibcFilePaths = Array.Empty(); CodegenOptions = Array.Empty(); + PerfMapFormatVersion = DefaultPerfMapFormatVersion; Parallelism = Environment.ProcessorCount; SingleMethodGenericArg = null; @@ -139,6 +143,7 @@ namespace ILCompiler syntax.DefineOption("pdb-path", ref PdbPath, SR.PdbFilePathOption); syntax.DefineOption("perfmap", ref PerfMap, SR.PerfMapFileOption); syntax.DefineOption("perfmap-path", ref PerfMapPath, SR.PerfMapFilePathOption); + syntax.DefineOption("perfmap-format-version", ref PerfMapFormatVersion, SR.PerfMapFormatVersionOption); syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 2f84f6fb5a7..aeeb9f57bb3 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -517,7 +517,6 @@ namespace ILCompiler List inputModules = new List(); List rootingModules = new List(); - Guid? inputModuleMvid = null; foreach (var inputFile in inFilePaths) { @@ -526,10 +525,6 @@ namespace ILCompiler rootingModules.Add(module); versionBubbleModulesHash.Add(module); - if (!_commandLineOptions.Composite && !inputModuleMvid.HasValue) - { - inputModuleMvid = module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid); - } if (!_commandLineOptions.CompositeOrInputBubble) { @@ -687,7 +682,7 @@ namespace ILCompiler .UseMapFile(_commandLineOptions.Map) .UseMapCsvFile(_commandLineOptions.MapCsv) .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath) - .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, inputModuleMvid) + .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.PerfMapFormatVersion) .UseProfileFile(jsonProfile != null) .UseParallelism(_commandLineOptions.Parallelism) .UseProfileData(profileDataManager) diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 084138ebc7c..c031f303e44 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -345,4 +345,7 @@ Explicit specification of the PerfMap file path - + + Explicitly request a particular PerfMap format version + + \ No newline at end of file diff --git a/src/coreclr/tools/r2rdump/CommandLineOptions.cs b/src/coreclr/tools/r2rdump/CommandLineOptions.cs index 8857cdbae87..9a1296bdbd6 100644 --- a/src/coreclr/tools/r2rdump/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rdump/CommandLineOptions.cs @@ -40,6 +40,7 @@ namespace R2RDump command.AddOption(new Option(new[] { "--pdb-path" }, "PDB output path for --create-pdb")); command.AddOption(new Option(new[] { "--create-perfmap" }, "Create PerfMap")); command.AddOption(new Option(new[] { "--perfmap-path" }, "PerfMap output path for --create-perfmap")); + command.AddOption(new Option(new[] { "--perfmap-format-version" }, "PerfMap format version for --create-perfmap")); return command; } } diff --git a/src/coreclr/tools/r2rdump/R2RDump.cs b/src/coreclr/tools/r2rdump/R2RDump.cs index 8a24273302b..94d71835966 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/tools/r2rdump/R2RDump.cs @@ -20,6 +20,7 @@ using ILCompiler.Diagnostics; using ILCompiler.Reflection.ReadyToRun; using Internal.Runtime; +using Internal.TypeSystem; namespace R2RDump { @@ -55,6 +56,7 @@ namespace R2RDump public bool CreatePerfmap { get; set; } public string PerfmapPath { get; set; } + public int PerfmapFormatVersion { get; set; } public FileInfo[] Reference { get; set; } @@ -65,6 +67,11 @@ namespace R2RDump private SignatureFormattingOptions signatureFormattingOptions; + public DumpOptions() + { + PerfmapFormatVersion = PerfMapWriter.CurrentFormatVersion; + } + /// /// Probing extensions to use when looking up assemblies under reference paths. /// @@ -425,9 +432,9 @@ namespace R2RDump string perfmapPath = _options.PerfmapPath; if (string.IsNullOrEmpty(perfmapPath)) { - perfmapPath = Path.ChangeExtension(r2r.Filename, ".map"); - PerfMapWriter.Write(perfmapPath, ProduceDebugInfoMethods(r2r)); + perfmapPath = Path.ChangeExtension(r2r.Filename, ".r2rmap"); } + PerfMapWriter.Write(perfmapPath, _options.PerfmapFormatVersion, ProduceDebugInfoMethods(r2r), ProduceDebugInfoAssemblies(r2r), r2r.TargetOperatingSystem, r2r.TargetArchitecture); } if (standardDump) @@ -457,6 +464,21 @@ namespace R2RDump } } + IEnumerable ProduceDebugInfoAssemblies(ReadyToRunReader r2r) + { + if (r2r.Composite) + { + foreach (KeyValuePair kvpRefAssembly in r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Key, StringComparer.OrdinalIgnoreCase)) + { + yield return new AssemblyInfo(kvpRefAssembly.Key, r2r.GetAssemblyMvid(kvpRefAssembly.Value)); + } + } + else + { + yield return new AssemblyInfo(r2r.GetGlobalAssemblyName(), r2r.GetAssemblyMvid(0)); + } + } + /// /// Returns true if the name, signature or id of method matches query /// diff --git a/src/coreclr/tools/r2rdump/R2RDump.sln b/src/coreclr/tools/r2rdump/R2RDump.sln index 6596a597f10..e8f71beb5a0 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.sln +++ b/src/coreclr/tools/r2rdump/R2RDump.sln @@ -8,38 +8,89 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Reflection.Ready EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Diagnostics", "..\aot\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj", "{4E9512BA-F963-472A-B689-37D4D32456F3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.ReadyToRun", "..\aot\ILCompiler.TypeSystem.ReadyToRun\ILCompiler.TypeSystem.ReadyToRun.csproj", "{F9CC5645-9E5D-41EE-ACD3-120F661DDA51}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|Any CPU.ActiveCfg = Release|Any CPU + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|Any CPU.Build.0 = Release|Any CPU + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|x64.ActiveCfg = Release|x64 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|x64.Build.0 = Release|x64 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|x86.ActiveCfg = Debug|x86 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Checked|x86.Build.0 = Debug|x86 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.ActiveCfg = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.Build.0 = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x64.ActiveCfg = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x64.Build.0 = Debug|x64 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x86.ActiveCfg = Debug|x86 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x86.Build.0 = Debug|x86 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|Any CPU.ActiveCfg = Release|Any CPU {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|Any CPU.Build.0 = Release|Any CPU {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|x64.ActiveCfg = Release|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|x64.Build.0 = Release|x64 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|x86.ActiveCfg = Release|x86 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|x86.Build.0 = Release|x86 + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|Any CPU.ActiveCfg = Release|Any CPU + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|Any CPU.Build.0 = Release|Any CPU + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|x64.ActiveCfg = Release|x64 + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|x64.Build.0 = Release|x64 + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|x86.ActiveCfg = Release|Any CPU + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Checked|x86.Build.0 = Release|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|x64.ActiveCfg = Debug|x64 {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|x64.Build.0 = Debug|x64 + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Debug|x86.Build.0 = Debug|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|Any CPU.Build.0 = Release|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x64.ActiveCfg = Release|x64 {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x64.Build.0 = Release|x64 + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x86.ActiveCfg = Release|Any CPU + {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x86.Build.0 = Release|Any CPU + {4E9512BA-F963-472A-B689-37D4D32456F3}.Checked|Any CPU.ActiveCfg = Checked|x86 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Checked|x64.ActiveCfg = Checked|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Checked|x64.Build.0 = Checked|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Checked|x86.ActiveCfg = Checked|x86 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Checked|x86.Build.0 = Checked|x86 {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|Any CPU.ActiveCfg = Debug|x64 {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|Any CPU.Build.0 = Debug|x64 {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x64.ActiveCfg = Debug|x64 {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x64.Build.0 = Debug|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x86.ActiveCfg = Debug|x86 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x86.Build.0 = Debug|x86 {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|Any CPU.Build.0 = Release|Any CPU {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x64.ActiveCfg = Release|Any CPU {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x64.Build.0 = Release|Any CPU + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x86.ActiveCfg = Release|x86 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x86.Build.0 = Release|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Checked|Any CPU.ActiveCfg = Checked|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Checked|x64.ActiveCfg = Checked|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Checked|x64.Build.0 = Checked|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Checked|x86.ActiveCfg = Checked|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Checked|x86.Build.0 = Checked|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Debug|Any CPU.ActiveCfg = Debug|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Debug|x64.ActiveCfg = Debug|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Debug|x64.Build.0 = Debug|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Debug|x86.ActiveCfg = Debug|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Debug|x86.Build.0 = Debug|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Release|Any CPU.ActiveCfg = Release|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Release|x64.ActiveCfg = Release|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Release|x64.Build.0 = Release|x64 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Release|x86.ActiveCfg = Release|x86 + {F9CC5645-9E5D-41EE-ACD3-120F661DDA51}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/coreclr/tools/r2rdump/TextDumper.cs b/src/coreclr/tools/r2rdump/TextDumper.cs index 0e062b88508..7ca2b58c77d 100644 --- a/src/coreclr/tools/r2rdump/TextDumper.cs +++ b/src/coreclr/tools/r2rdump/TextDumper.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; using System.Reflection.PortableExecutable; using System.Text; @@ -18,8 +19,6 @@ namespace R2RDump { class TextDumper : Dumper { - private const int GuidByteSize = 16; - public TextDumper(ReadyToRunReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) : base(r2r, writer, disassembler, options) { @@ -90,14 +89,8 @@ namespace R2RDump int assemblyIndex = 0; foreach (string assemblyName in _r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key)) { - string dividerName = $@"Component Assembly [{assemblyIndex}]: {assemblyName}"; - if (_r2r.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection mvidSection)) - { - int mvidOffset = _r2r.GetOffset(mvidSection.RelativeVirtualAddress) + GuidByteSize * assemblyIndex; - Guid mvid = new Guid(new ReadOnlySpan(_r2r.Image, mvidOffset, GuidByteSize)); - dividerName += $@" - MVID {mvid:b}"; - } - WriteDivider(dividerName); + Guid mvid = _r2r.GetAssemblyMvid(assemblyIndex); + WriteDivider($@"Component Assembly [{assemblyIndex}]: {assemblyName} - MVID {mvid:b}"); ReadyToRunCoreHeader assemblyHeader = _r2r.ReadyToRunAssemblyHeaders[assemblyIndex]; foreach (ReadyToRunSection section in NormalizedSections(assemblyHeader)) { @@ -513,12 +506,10 @@ namespace R2RDump _writer.WriteLine("Composite executable: {0}", ownerCompositeExecutable.ToEscapedString()); break; case ReadyToRunSectionType.ManifestAssemblyMvids: - int mvidOffset = _r2r.GetOffset(section.RelativeVirtualAddress); - int mvidCount = section.Size / GuidByteSize; + int mvidCount = section.Size / ReadyToRunReader.GuidByteSize; for (int mvidIndex = 0; mvidIndex < mvidCount; mvidIndex++) { - Guid mvid = new Guid(new Span(_r2r.Image, mvidOffset + GuidByteSize * mvidIndex, GuidByteSize)); - _writer.WriteLine("MVID[{0}] = {1:b}", mvidIndex, mvid); + _writer.WriteLine("MVID[{0}] = {1:b}", mvidIndex, _r2r.GetAssemblyMvid(mvidIndex)); } break; default: From 9c060fe922ee139200d91a8491e60446f5a23cb5 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 1 Jul 2021 16:32:18 -0700 Subject: [PATCH 241/926] Make GlobalizationMode code consistent (#55039) Co-authored-by: Stephen Toub --- .../src/System/Globalization/GlobalizationMode.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index 3536ed7d534..d611ea2b5e1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -11,23 +11,17 @@ namespace System.Globalization // split from GlobalizationMode so the whole class can be trimmed when Invariant=true. private static partial class Settings { - internal static bool Invariant { get; } = GetInvariantSwitchValue(); - internal static readonly bool PredefinedCulturesOnly = AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", Invariant); + internal static bool Invariant { get; } = AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); + internal static bool PredefinedCulturesOnly { get; } = AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", Invariant); } // Note: Invariant=true and Invariant=false are substituted at different levels in the ILLink.Substitutions file. // This allows for the whole Settings nested class to be trimmed when Invariant=true, and allows for the Settings // static cctor (on Unix) to be preserved when Invariant=false. internal static bool Invariant => Settings.Invariant; - internal static bool PredefinedCulturesOnly => Settings.PredefinedCulturesOnly; - - private static bool GetInvariantSwitchValue() => - AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); - private static bool TryGetAppLocalIcuSwitchValue([NotNullWhen(true)] out string? value) => TryGetStringValue("System.Globalization.AppLocalIcu", "DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", out value); - private static bool TryGetStringValue(string switchName, string envVariable, [NotNullWhen(true)] out string? value) { value = AppContext.GetData(switchName) as string; From d0a09199f8bf30cb3e3f46654117aeae1f72cd99 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Thu, 1 Jul 2021 16:50:55 -0700 Subject: [PATCH 242/926] package native quic library (#54992) * package native quic library * Update System.Net.Quic.csproj * Fix binplacing * exclude msquic.dll from trimming * Update src/libraries/System.Net.Quic/src/System.Net.Quic.csproj Co-authored-by: Viktor Hofer * remove local windows override Co-authored-by: Viktor Hofer --- .../src/System.Net.Quic.csproj | 26 ++++++++++++------- src/libraries/illink-oob.targets | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 60201faf945..6d8de7f5d6c 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -56,9 +56,14 @@ - - + + + + @@ -76,16 +81,17 @@ + + + + + + + + - - PreserveNewest - PreserveNewest - - - PreserveNewest - PreserveNewest - PreserveNewest PreserveNewest diff --git a/src/libraries/illink-oob.targets b/src/libraries/illink-oob.targets index b985b9edf5f..72cecaf3b8b 100644 --- a/src/libraries/illink-oob.targets +++ b/src/libraries/illink-oob.targets @@ -24,7 +24,7 @@ <_OOBsToIgnore Include="System.Configuration.ConfigurationManager" /> <_OOBsToIgnore Include="System.Speech" /> - <_NetCoreAppRuntimeAssemblies Include="$(NetCoreAppCurrentRuntimePath)*.dll" Exclude="$(NetCoreAppCurrentRuntimePath)*.Generator.dll;$(NetCoreAppCurrentRuntimePath)*.Native.dll" /> + <_NetCoreAppRuntimeAssemblies Include="$(NetCoreAppCurrentRuntimePath)*.dll" Exclude="$(NetCoreAppCurrentRuntimePath)*.Generator.dll;$(NetCoreAppCurrentRuntimePath)*.Native.dll;$(NetCoreAppCurrentRuntimePath)*msquic.dll" /> <_RuntimePackTrimmedAssemblies Include="$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)*.dll" /> From 66cf2ab08bb6e1f4e1a3a6fa91f696a9bfa69c39 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 1 Jul 2021 22:06:30 -0400 Subject: [PATCH 243/926] [Mono] Condition Workload AOT import to be osx only (#55040) * Condition Workload AOT import to be osx only If we don't, then the aot packs will be imported on Windows when running an iOS offline build Fixes https://github.com/dotnet/runtime/issues/54944 * Update src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets Co-authored-by: Larry Ewing * Update WorkloadManifest.targets Co-authored-by: Steve Pfister Co-authored-by: Larry Ewing --- .../WorkloadManifest.targets | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 2102eabc36c..ab039e9a716 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -20,6 +20,8 @@ + + @@ -27,11 +29,15 @@ + + + + From e263adcf514b15926dde500cce5fb6072d1b395f Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 1 Jul 2021 19:32:14 -0700 Subject: [PATCH 244/926] Delete `compQuirkForPPP`. (#55050) --- src/coreclr/jit/codegencommon.cpp | 3 -- src/coreclr/jit/compiler.cpp | 88 ------------------------------- src/coreclr/jit/compiler.h | 4 -- src/coreclr/jit/layout.cpp | 32 ----------- src/coreclr/jit/layout.h | 17 ------ 5 files changed, 144 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index cfbdf1e9033..78926e64fda 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -122,9 +122,6 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler) #ifdef TARGET_AMD64 // This will be set before final frame layout. compiler->compVSQuirkStackPaddingNeeded = 0; - - // Set to true if we perform the Quirk that fixes the PPP issue - compiler->compQuirkForPPPflag = false; #endif // TARGET_AMD64 // Initialize the IP-mapping logic. diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 6e691f73f77..7019d8afad6 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5099,11 +5099,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl } } -#ifdef TARGET_AMD64 - // Check if we need to add the Quirk for the PPP backward compat issue - compQuirkForPPPflag = compQuirkForPPP(); -#endif - // Insert GC Polls DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls); @@ -5397,89 +5392,6 @@ void Compiler::ProcessShutdownWork(ICorStaticInfo* statInfo) { } -#ifdef TARGET_AMD64 -// Check if we need to add the Quirk for the PPP backward compat issue. -// This Quirk addresses a compatibility issue between the new RyuJit and the previous JIT64. -// A backward compatibity issue called 'PPP' exists where a PInvoke call passes a 32-byte struct -// into a native API which basically writes 48 bytes of data into the struct. -// With the stack frame layout used by the RyuJIT the extra 16 bytes written corrupts a -// caller saved register and this leads to an A/V in the calling method. -// The older JIT64 jit compiler just happened to have a different stack layout and/or -// caller saved register set so that it didn't hit the A/V in the caller. -// By increasing the amount of stack allocted for the struct by 32 bytes we can fix this. -// -// Return true if we actually perform the Quirk, otherwise return false -// -bool Compiler::compQuirkForPPP() -{ - if (lvaCount != 2) - { // We require that there are exactly two locals - return false; - } - - if (compTailCallUsed) - { // Don't try this quirk if a tail call was used - return false; - } - - bool hasOutArgs = false; - LclVarDsc* varDscExposedStruct = nullptr; - - unsigned lclNum; - LclVarDsc* varDsc; - - /* Look for struct locals that are address taken */ - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - if (varDsc->lvIsParam) // It can't be a parameter - { - continue; - } - - // We require that the OutgoingArg space lclVar exists - if (lclNum == lvaOutgoingArgSpaceVar) - { - hasOutArgs = true; // Record that we saw it - continue; - } - - // Look for a 32-byte address exposed Struct and record its varDsc - if ((varDsc->TypeGet() == TYP_STRUCT) && varDsc->lvAddrExposed && (varDsc->lvExactSize == 32)) - { - varDscExposedStruct = varDsc; - } - } - - // We only perform the Quirk when there are two locals - // one of them is a address exposed struct of size 32 - // and the other is the outgoing arg space local - // - if (hasOutArgs && (varDscExposedStruct != nullptr)) - { -#ifdef DEBUG - if (verbose) - { - printf("\nAdding a backwards compatibility quirk for the 'PPP' issue\n"); - } -#endif // DEBUG - - // Increase the exact size of this struct by 32 bytes - // This fixes the PPP backward compat issue - varDscExposedStruct->lvExactSize += 32; - - // The struct is now 64 bytes. - // We're on x64 so this should be 8 pointer slots. - assert((varDscExposedStruct->lvExactSize / TARGET_POINTER_SIZE) == 8); - - varDscExposedStruct->SetLayout( - varDscExposedStruct->GetLayout()->GetPPPQuirkLayout(getAllocator(CMK_ClassLayout))); - - return true; - } - return false; -} -#endif // TARGET_AMD64 - /*****************************************************************************/ #ifdef DEBUG diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ca8daaf7a96..36acfe6adec 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9935,7 +9935,6 @@ public: // Bytes of padding between save-reg area and locals. #define VSQUIRK_STACK_PAD (2 * REGSIZE_BYTES) unsigned compVSQuirkStackPaddingNeeded; - bool compQuirkForPPPflag; #endif unsigned compArgSize; // total size of arguments in bytes (including register args (lvIsRegArg)) @@ -10155,9 +10154,6 @@ protected: bool compProfilerMethHndIndirected; // Whether compProfilerHandle is pointer to the handle or is an actual handle #endif -#ifdef TARGET_AMD64 - bool compQuirkForPPP(); // Check if this method should be Quirked for the PPP issue -#endif public: // Assumes called as part of process shutdown; does any compiler-specific work associated with that. static void ProcessShutdownWork(ICorStaticInfo* statInfo); diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 3b07a5bada3..cbcb631185d 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -382,38 +382,6 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler) INDEBUG(m_gcPtrsInitialized = true;) } -#ifdef TARGET_AMD64 -ClassLayout* ClassLayout::GetPPPQuirkLayout(CompAllocator alloc) -{ - assert(m_gcPtrsInitialized); - assert(m_classHandle != NO_CLASS_HANDLE); - assert(m_isValueClass); - assert(m_size == 32); - - if (m_pppQuirkLayout == nullptr) - { - m_pppQuirkLayout = new (alloc) ClassLayout(m_classHandle, m_isValueClass, 64 DEBUGARG(m_className)); - m_pppQuirkLayout->m_gcPtrCount = m_gcPtrCount; - - static_assert_no_msg(_countof(m_gcPtrsArray) == 8); - - for (int i = 0; i < 4; i++) - { - m_pppQuirkLayout->m_gcPtrsArray[i] = m_gcPtrsArray[i]; - } - - for (int i = 4; i < 8; i++) - { - m_pppQuirkLayout->m_gcPtrsArray[i] = TYPE_GC_NONE; - } - - INDEBUG(m_pppQuirkLayout->m_gcPtrsInitialized = true;) - } - - return m_pppQuirkLayout; -} -#endif // TARGET_AMD64 - //------------------------------------------------------------------------ // AreCompatible: check if 2 layouts are the same for copying. // diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index cef2fe6ded3..dcc443cdd44 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -35,12 +35,6 @@ class ClassLayout BYTE m_gcPtrsArray[sizeof(BYTE*)]; }; -#ifdef TARGET_AMD64 - // A layout that has its size artificially inflated to avoid stack corruption due to - // bugs in user code - see Compiler::compQuirkForPPP for details. - ClassLayout* m_pppQuirkLayout; -#endif - // Class name as reported by ICorJitInfo::getClassName INDEBUG(const char* m_className;) @@ -56,9 +50,6 @@ class ClassLayout #endif , m_gcPtrCount(0) , m_gcPtrs(nullptr) -#ifdef TARGET_AMD64 - , m_pppQuirkLayout(nullptr) -#endif #ifdef DEBUG , m_className("block") #endif @@ -76,9 +67,6 @@ class ClassLayout #endif , m_gcPtrCount(0) , m_gcPtrs(nullptr) -#ifdef TARGET_AMD64 - , m_pppQuirkLayout(nullptr) -#endif #ifdef DEBUG , m_className(className) #endif @@ -89,11 +77,6 @@ class ClassLayout void InitializeGCPtrs(Compiler* compiler); public: -#ifdef TARGET_AMD64 - // Get the layout for the PPP quirk - see Compiler::compQuirkForPPP for details. - ClassLayout* GetPPPQuirkLayout(CompAllocator alloc); -#endif - CORINFO_CLASS_HANDLE GetClassHandle() const { return m_classHandle; From 348fefa67103dc5447c94b8c9af899e8f1fe9d89 Mon Sep 17 00:00:00 2001 From: imhameed Date: Thu, 1 Jul 2021 19:43:19 -0700 Subject: [PATCH 245/926] [mono] Enable many HardwareIntrinsic tests on wasm --- src/tests/issues.targets | 216 --------------------------------------- 1 file changed, 216 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 2f98f5c69ab..a44ef402262 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -2541,222 +2541,6 @@ needs triage - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - - - https://github.com/dotnet/runtime/issues/41758 - https://github.com/dotnet/runtime/issues/41472 From 9c43a63fdc35e0a05401d8421b4a670b81725fff Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 1 Jul 2021 20:20:23 -0700 Subject: [PATCH 246/926] Add binplaced analyzers to ASP.NET transport package (#55042) --- .../pkg/Microsoft.AspNetCore.Internal.Transport.pkgproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Microsoft.AspNetCore.Internal.Transport/pkg/Microsoft.AspNetCore.Internal.Transport.pkgproj b/src/libraries/Microsoft.AspNetCore.Internal.Transport/pkg/Microsoft.AspNetCore.Internal.Transport.pkgproj index cd0a159269b..23046099e10 100644 --- a/src/libraries/Microsoft.AspNetCore.Internal.Transport/pkg/Microsoft.AspNetCore.Internal.Transport.pkgproj +++ b/src/libraries/Microsoft.AspNetCore.Internal.Transport/pkg/Microsoft.AspNetCore.Internal.Transport.pkgproj @@ -8,6 +8,8 @@ Internal transport package to provide aspnetcore with the assemblies that make up the Microsoft.ASPNetCore.App shared framework. + <_analyzers Include="$(ASPNETCoreAppPackageRootPath)\analyzers\**\*.*" /> + <_libDocs Include="$(ASPNETCoreAppPackageRuntimePath)\*.xml" /> From 40bd417ad8c62bc762483f8ad9cf9bf71b5ba2d2 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 1 Jul 2021 23:31:27 -0400 Subject: [PATCH 247/926] Delete stale references to System.IO.FileSystem.Primitives (#55041) --- .../src/System.IO.Compression.ZipFile.csproj | 1 - .../System.IO.FileSystem/ref/System.IO.FileSystem.csproj | 1 - src/libraries/System.Net.Sockets/ref/System.Net.Sockets.csproj | 1 - 3 files changed, 3 deletions(-) diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj b/src/libraries/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj index 366de34f8b9..93eb3e71933 100644 --- a/src/libraries/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj +++ b/src/libraries/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj @@ -17,7 +17,6 @@ - diff --git a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.csproj b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.csproj index cff7c81e031..0ceabbf133b 100644 --- a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.csproj +++ b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.csproj @@ -9,7 +9,6 @@ - diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.csproj index 7229303b940..9387aa99419 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.csproj @@ -7,7 +7,6 @@ - From 24668758896c413897ee8e956d55c1ac50f7245e Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Thu, 1 Jul 2021 20:38:07 -0700 Subject: [PATCH 248/926] Fix fix_allocation_context for regions (#54931) --- src/coreclr/gc/gc.cpp | 50 ++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 1d58d35ce86..268fffcd4d7 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -7117,6 +7117,8 @@ void gc_heap::fix_youngest_allocation_area() assert (generation_allocation_pointer (youngest_generation) == nullptr); assert (generation_allocation_limit (youngest_generation) == nullptr); heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated; + assert (heap_segment_mem (ephemeral_heap_segment) <= heap_segment_allocated (ephemeral_heap_segment)); + assert (heap_segment_allocated (ephemeral_heap_segment) <= heap_segment_reserved (ephemeral_heap_segment)); } //for_gc_p indicates that the work is being done for GC, @@ -7128,35 +7130,41 @@ void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p, (size_t)acontext, (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit)); + if (acontext->alloc_ptr == 0) + { + return; + } int align_const = get_alignment_constant (TRUE); - - if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) || +#ifdef USE_REGIONS + bool is_ephemeral_heap_segment = in_range_for_segment (acontext->alloc_limit, ephemeral_heap_segment); +#else // USE_REGIONS + bool is_ephemeral_heap_segment = true; +#endif // USE_REGIONS + if ((!is_ephemeral_heap_segment) || ((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) || !for_gc_p) { uint8_t* point = acontext->alloc_ptr; - if (point != 0) + size_t size = (acontext->alloc_limit - acontext->alloc_ptr); + // the allocation area was from the free list + // it was shortened by Align (min_obj_size) to make room for + // at least the shortest unused object + size += Align (min_obj_size, align_const); + assert ((size >= Align (min_obj_size))); + + dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, + (size_t)point + size )); + make_unused_array (point, size); + + if (for_gc_p) { - size_t size = (acontext->alloc_limit - acontext->alloc_ptr); - // the allocation area was from the free list - // it was shortened by Align (min_obj_size) to make room for - // at least the shortest unused object - size += Align (min_obj_size, align_const); - assert ((size >= Align (min_obj_size))); - - dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, - (size_t)point + size )); - make_unused_array (point, size); - - if (for_gc_p) - { - generation_free_obj_space (generation_of (0)) += size; - if (record_ac_p) - alloc_contexts_used ++; - } + generation_free_obj_space (generation_of (0)) += size; + if (record_ac_p) + alloc_contexts_used ++; } } else if (for_gc_p) { + assert (is_ephemeral_heap_segment); alloc_allocated = acontext->alloc_ptr; assert (heap_segment_allocated (ephemeral_heap_segment) <= heap_segment_committed (ephemeral_heap_segment)); @@ -14191,6 +14199,8 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, if (heap_segment_used (seg) < (alloc_allocated - plug_skew)) { heap_segment_used (seg) = alloc_allocated - plug_skew; + assert (heap_segment_mem (seg) <= heap_segment_used (seg)); + assert (heap_segment_used (seg) <= heap_segment_reserved (seg)); } } #ifdef BACKGROUND_GC From 020e6c128fa522d09db7ea5a68eae3133462294b Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 2 Jul 2021 00:11:15 -0400 Subject: [PATCH 249/926] [mono][wasm] Disable some tests which crash on AOT. (#55054) --- .../Conformance.dynamic.context.method.regmethod.regclass.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs index 9daae9ce25e..73a71285d9a 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs @@ -744,6 +744,7 @@ namespace System.Dynamic.Runtime.Tests // ManagedTests.DynamicCSharp.Conformance.dynamic.context.method.regmethod.regclass.regclass012.regclass012 [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55051", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void CalledFrom_ForExpressionBody() { dynamic mc = new MemberClass(); @@ -757,6 +758,7 @@ namespace System.Dynamic.Runtime.Tests // ManagedTests.DynamicCSharp.Conformance.dynamic.context.method.regmethod.regclass.regclass012a.regclass012a [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55051", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void CalledFrom_ForExpressionConditions() { dynamic mc = new MemberClass(); From 705c01b41dc5551c6c32c8e7d7e5ccad744143b2 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 1 Jul 2021 22:09:02 -0700 Subject: [PATCH 250/926] disable a failing test. (#55063) --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index a44ef402262..d51525edca1 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -11,6 +11,9 @@ https://github.com/dotnet/runtime/issues/49881 + + https://github.com/dotnet/runtime/issues/55062 + From 97898a4c9292d91a8e4f4b6add67f66f66f661b3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 00:35:34 -0500 Subject: [PATCH 251/926] [main] Update dependencies from dnceng/internal/dotnet-optimization dotnet/arcade dotnet/xharness dotnet/hotreload-utils (#55007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210630.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21329.4 -> To Version 1.0.0-prerelease.21330.5 * Update dependencies from https://github.com/dotnet/arcade build 20210630.2 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21329.8 -> To Version 6.0.0-beta.21330.2 * Update dependencies from https://github.com/dotnet/xharness build 20210630.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21328.4 -> To Version 1.0.0-prerelease.21330.2 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210630.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21328.1 -> To Version 1.0.1-alpha.0.21330.1 * Disable nullability for EnC test * Also disable nullability in browser EnC functional test Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing Co-authored-by: Aleksey Kliger (λgeek) --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 88 +++++++++---------- eng/Versions.props | 40 ++++----- global.json | 4 +- ...tadata.ApplyUpdate.Test.MethodBody1.csproj | 1 + .../ApplyUpdateReferencedAssembly.csproj | 1 + 6 files changed, 69 insertions(+), 67 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a5c11de1747..9c120588ffd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21328.4", + "version": "1.0.0-prerelease.21330.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 90a6d501016..00d781b1461 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,65 +14,65 @@ - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 https://github.com/dotnet/arcade ff3e7d23139c30feefe36d3d4e8d41a06160f254 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 https://github.com/dotnet/arcade @@ -186,37 +186,37 @@ https://github.com/mono/linker f574448d16af45f7ac2c4b89d71dea73dec86726 - + https://github.com/dotnet/xharness - 2890d740e1dd9fc41a634777c4af59a3986b1f7b + 6d17e5ba4709de02f2e5c62a308f8518253cb002 - + https://github.com/dotnet/xharness - 2890d740e1dd9fc41a634777c4af59a3986b1f7b + 6d17e5ba4709de02f2e5c62a308f8518253cb002 - + https://github.com/dotnet/arcade - 6b9d24236d8d1906284e6cb6c28e3fe93a69b7d2 + 26345756f99087811b1fe4d02ff213eb172ec506 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - b89374348ff2344a625677584be9dfc9bea2b971 + 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - b89374348ff2344a625677584be9dfc9bea2b971 + 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - b89374348ff2344a625677584be9dfc9bea2b971 + 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - b89374348ff2344a625677584be9dfc9bea2b971 + 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f - + https://github.com/dotnet/hotreload-utils - 2b1536142083d6270dc40c5cba74fbb0a612beab + a8e0dc88077495e43d7820f631815fa95ce92f8a https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index d8104e89eb4..765b461a291 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,19 +50,19 @@ 3.10.0 6.0.0-rc1.21324.1 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 2.5.1-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 - 6.0.0-beta.21329.8 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 2.5.1-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 + 6.0.0-beta.21330.2 5.9.0-preview.2 @@ -122,10 +122,10 @@ 6.0.0-beta.21314.1 6.0.0-beta.21314.1 - 1.0.0-prerelease.21329.4 - 1.0.0-prerelease.21329.4 - 1.0.0-prerelease.21329.4 - 1.0.0-prerelease.21329.4 + 1.0.0-prerelease.21330.5 + 1.0.0-prerelease.21330.5 + 1.0.0-prerelease.21330.5 + 1.0.0-prerelease.21330.5 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -149,9 +149,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21328.4 - 1.0.0-prerelease.21328.4 - 1.0.1-alpha.0.21328.1 + 1.0.0-prerelease.21330.2 + 1.0.0-prerelease.21330.2 + 1.0.1-alpha.0.21330.1 2.4.1 2.4.2 1.3.0 diff --git a/global.json b/global.json index f2f7fc31029..9650ac9e9ad 100644 --- a/global.json +++ b/global.json @@ -14,8 +14,8 @@ "msbuild-sdks": { "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21330.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21329.8", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21329.8", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21330.2", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21330.2", "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj index 57ba4f3ec52..87a4a28d000 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj @@ -5,6 +5,7 @@ true deltascript.json true + disable diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj index 46e260f24e1..06190d7b86f 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -11,6 +11,7 @@ false false + disable From 4c92aef2b08f9c4374c520e7e664a44f1ad8ce56 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Fri, 2 Jul 2021 11:00:21 +0200 Subject: [PATCH 252/926] Http2Stream throws a wrapped Http2ConnectionException on GO_AWAY (#54625) If server sends `GO_AWAY` to client, `Http2Connection` handles it and sets a correct `Http2ConnectionException` to `_resetException` field followed by resetting all active Http2Streams. Each of these streams is expected to rethrow that `_resetException` to communicate the original protocol error to the application code. However, the method `Http2Stream.SendDataAsync` currently doesn't take into account that field, thus when it gets cancelled as part of a stream reset it just throws `OperationCanceledException` which doesn't contain any details. This PR fixes that and makes `Http2Stream.SendDataAsync` throw the original `Http2ConnectionException` wrapped by `IOException`. Fixes #42472 --- .../Http/SocketsHttpHandler/Http2Stream.cs | 17 +++++++++ .../HttpClientHandlerTest.Http2.cs | 37 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 4297c50fc0b..87ebe079038 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -1281,6 +1281,23 @@ namespace System.Net.Http await _connection.SendStreamDataAsync(StreamId, current, flush, _requestBodyCancellationSource.Token).ConfigureAwait(false); } } + catch (OperationCanceledException e) when (e.CancellationToken == _requestBodyCancellationSource.Token) + { + lock (SyncObject) + { + if (_resetException is Exception resetException) + { + if (_canRetry) + { + ThrowRetry(SR.net_http_request_aborted, resetException); + } + + ThrowRequestAborted(resetException); + } + } + + throw; + } finally { linkedRegistration.Dispose(); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index b24c7037c2d..7f28c57cf75 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -903,6 +903,39 @@ namespace System.Net.Http.Functional.Tests } } + [ConditionalFact(nameof(SupportsAlpn))] + public async Task GoAwayFrame_RequestWithBody_ServerDisconnect_AbortStreamsAndThrowIOException() + { + using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) + using (HttpClient client = CreateHttpClient()) + { + var request = new HttpRequestMessage(HttpMethod.Post, server.Address); + request.Version = new Version(2, 0); + var content = new string('*', 300); + var stream = new CustomContent.SlowTestStream(Encoding.UTF8.GetBytes(content), null, count: 60); + request.Content = new CustomContent(stream); + + Task sendTask = client.SendAsync(request); + + Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); + (int streamId, _) = await connection.ReadAndParseRequestHeaderAsync(readBody: false); + await connection.SendDefaultResponseHeadersAsync(streamId); + + await connection.SendGoAway(0, errorCode: ProtocolErrors.PROTOCOL_ERROR); + + // Expect client to detect that server has disconnected and throw an exception + var exception = await Assert.ThrowsAnyAsync(() => + new Task[] + { + sendTask + }.WhenAllOrAnyFailed(TestHelper.PassingTestTimeoutMilliseconds)); + + Assert.IsType(exception.InnerException); + Assert.NotNull(exception.InnerException.InnerException); + Assert.Contains("PROTOCOL_ERROR", exception.InnerException.InnerException.Message); + } + } + [ConditionalFact(nameof(SupportsAlpn))] public async Task GoAwayFrame_UnprocessedStreamFirstRequestFinishedFirst_RequestRestarted() { @@ -2790,8 +2823,8 @@ namespace System.Net.Http.Functional.Tests // Trying to read on the response stream should fail now, and client should ignore any data received await AssertProtocolErrorForIOExceptionAsync(SendAndReceiveResponseDataAsync(contentBytes, responseStream, connection, streamId), ProtocolErrors.ENHANCE_YOUR_CALM); - // Attempting to write on the request body should now fail with OperationCanceledException. - Exception e = await Assert.ThrowsAnyAsync(async () => { await SendAndReceiveRequestDataAsync(contentBytes, requestStream, connection, streamId); }); + // Attempting to write on the request body should now fail with IOException. + Exception e = await Assert.ThrowsAnyAsync(async () => { await SendAndReceiveRequestDataAsync(contentBytes, requestStream, connection, streamId); }); // Propagate the exception to the request stream serialization task. // This allows the request processing to complete. From 41e6601c5f9458fa8c5dea25ee78cbf121aa322f Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 2 Jul 2021 09:41:38 -0400 Subject: [PATCH 253/926] [mono][llvm] Only emit 'LLVM failed' messages on verbosity > 0. (#55060) --- src/mono/mono/mini/mini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 11141bc79d4..7d24fe6374b 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3880,7 +3880,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts if (!cfg->disable_llvm) mono_llvm_emit_method (cfg); if (cfg->disable_llvm) { - if (cfg->verbose_level >= (cfg->llvm_only ? 0 : 1)) { + if (cfg->verbose_level > 0) { //nm = mono_method_full_name (cfg->method, TRUE); printf ("LLVM failed for '%s.%s': %s\n", m_class_get_name (method->klass), method->name, cfg->exception_message); //g_free (nm); From f4e88f41ccce5c37c5d1c33eba09b569a9a6aecd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 2 Jul 2021 16:09:49 +0200 Subject: [PATCH 254/926] all RandomAccess methods should work for both sync and async file handles (#54266) Co-authored-by: campersau Co-authored-by: Stephen Toub --- .../TestUtilities/System/PlatformDetection.cs | 2 + .../FileStreamConformanceTests.Windows.cs | 3 +- .../tests/FileStream/FileStreamOptions.cs | 22 +- .../tests/RandomAccess/Base.cs | 51 +-- .../tests/RandomAccess/GetLength.cs | 14 +- .../tests/RandomAccess/Mixed.Windows.cs | 130 +++++++ .../tests/RandomAccess/NoBuffering.Windows.cs | 73 ++-- .../tests/RandomAccess/Read.cs | 39 +- .../tests/RandomAccess/ReadAsync.cs | 31 +- .../tests/RandomAccess/ReadScatter.cs | 38 +- .../tests/RandomAccess/ReadScatterAsync.cs | 46 +-- .../tests/RandomAccess/Write.cs | 39 +- .../tests/RandomAccess/WriteAsync.cs | 31 +- .../tests/RandomAccess/WriteGather.cs | 38 +- .../tests/RandomAccess/WriteGatherAsync.cs | 46 +-- .../tests/System.IO.FileSystem.Tests.csproj | 1 + .../SafeHandles/SafeFileHandle.Windows.cs | 68 ++-- .../src/Resources/Strings.resx | 3 + .../System.Private.CoreLib.Shared.projitems | 3 + .../src/System/IO/RandomAccess.Unix.cs | 38 +- .../src/System/IO/RandomAccess.Windows.cs | 345 +++++++++++++----- .../src/System/IO/RandomAccess.cs | 74 ++-- .../AsyncWindowsFileStreamStrategy.cs | 11 - .../Strategies/FileStreamHelpers.Windows.cs | 27 +- .../Net5CompatFileStreamStrategy.Windows.cs | 26 +- .../SyncWindowsFileStreamStrategy.cs | 50 --- .../Strategies/WindowsFileStreamStrategy.cs | 47 ++- 27 files changed, 839 insertions(+), 457 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index aa44cfe53f1..fc829692c03 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -91,6 +91,8 @@ namespace System } } + public static bool IsAsyncFileIOSupported => !IsBrowser && !(IsWindows && IsMonoRuntime); // https://github.com/dotnet/runtime/issues/34582 + public static bool IsLineNumbersSupported => true; public static bool IsInContainer => GetIsInContainer(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs index b6b2be09ad5..a372213550f 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.Windows.cs @@ -42,6 +42,7 @@ namespace System.IO.Tests } [PlatformSpecific(TestPlatforms.Windows)] // DOS device paths (\\.\ and \\?\) are a Windows concept + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public class SeekableDeviceFileStreamStandaloneConformanceTests : UnbufferedAsyncFileStreamStandaloneConformanceTests { protected override string GetTestFilePath(int? index = null, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) @@ -90,7 +91,7 @@ namespace System.IO.Tests return false; } - // the "Server Service" allows for file sharing. It can be disabled on some of our CI machines. + // the "Server Service" allows for file sharing. It can be disabled on some of our CI machines. using (ServiceController sharingService = new ServiceController("Server")) { return sharingService.Status == ServiceControllerStatus.Running; diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs index 9be492d21bd..8dd7f87b843 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamOptions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Linq; using System.Text; using Xunit; @@ -135,13 +136,22 @@ namespace System.IO.Tests Assert.Throws(() => new FileStreamOptions { BufferSize = -1 }); } + public static IEnumerable GetSettingsArePropagatedArguments() + { + yield return new object[] { FileMode.Create, FileAccess.Write, FileOptions.None }; + yield return new object[] { FileMode.Open, FileAccess.Read, FileOptions.None }; + yield return new object[] { FileMode.Create, FileAccess.ReadWrite, FileOptions.None }; + + if (PlatformDetection.IsAsyncFileIOSupported) + { + yield return new object[] { FileMode.Create, FileAccess.Write, FileOptions.Asynchronous }; + yield return new object[] { FileMode.Open, FileAccess.Read, FileOptions.Asynchronous }; + yield return new object[] { FileMode.Create, FileAccess.ReadWrite, FileOptions.Asynchronous }; + } + } + [Theory] - [InlineData(FileMode.Create, FileAccess.Write, FileOptions.None)] - [InlineData(FileMode.Create, FileAccess.Write, FileOptions.Asynchronous)] - [InlineData(FileMode.Open, FileAccess.Read, FileOptions.None)] - [InlineData(FileMode.Open, FileAccess.Read, FileOptions.Asynchronous)] - [InlineData(FileMode.Create, FileAccess.ReadWrite, FileOptions.None)] - [InlineData(FileMode.Create, FileAccess.ReadWrite, FileOptions.Asynchronous)] + [MemberData(nameof(GetSettingsArePropagatedArguments))] public void SettingsArePropagated(FileMode mode, FileAccess access, FileOptions fileOptions) { string filePath = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs index 45d0547a391..6f8726500d3 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Base.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.IO.Pipes; using System.Threading; using Microsoft.Win32.SafeHandles; @@ -12,12 +13,18 @@ namespace System.IO.Tests { protected abstract T MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset); - protected virtual bool ShouldThrowForSyncHandle => false; - - protected virtual bool ShouldThrowForAsyncHandle => false; - protected virtual bool UsesOffsets => true; + public static IEnumerable GetSyncAsyncOptions() + { + yield return new object[] { FileOptions.None }; + + if (PlatformDetection.IsAsyncFileIOSupported) + { + yield return new object[] { FileOptions.Asynchronous }; + } + } + [Fact] public void ThrowsArgumentNullExceptionForNullHandle() { @@ -52,12 +59,12 @@ namespace System.IO.Tests } } - [Fact] - public void ThrowsArgumentOutOfRangeExceptionForNegativeFileOffset() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsArgumentOutOfRangeExceptionForNegativeFileOffset(FileOptions options) { if (UsesOffsets) { - FileOptions options = ShouldThrowForAsyncHandle ? FileOptions.None : FileOptions.Asynchronous; using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: options)) { AssertExtensions.Throws("fileOffset", () => MethodUnderTest(handle, Array.Empty(), -1)); @@ -65,32 +72,6 @@ namespace System.IO.Tests } } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [SkipOnPlatform(TestPlatforms.Browser, "async file IO is not supported on browser")] - public void ThrowsArgumentExceptionForAsyncFileHandle() - { - if (ShouldThrowForAsyncHandle) - { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: FileOptions.Asynchronous)) - { - AssertExtensions.Throws("handle", () => MethodUnderTest(handle, new byte[100], 0)); - } - } - } - - [Fact] - public void ThrowsArgumentExceptionForSyncFileHandle() - { - if (ShouldThrowForSyncHandle) - { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: FileOptions.None)) - { - AssertExtensions.Throws("handle", () => MethodUnderTest(handle, new byte[100], 0)); - } - } - } - protected static CancellationTokenSource GetCancelledTokenSource() { CancellationTokenSource source = new CancellationTokenSource(); @@ -98,12 +79,10 @@ namespace System.IO.Tests return source; } - protected SafeFileHandle GetHandleToExistingFile(FileAccess access) + protected SafeFileHandle GetHandleToExistingFile(FileAccess access, FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[1]); - - FileOptions options = ShouldThrowForAsyncHandle ? FileOptions.None : FileOptions.Asynchronous; return File.OpenHandle(filePath, FileMode.Open, access, FileShare.None, options); } } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs index 46e2914aed1..1c21748f019 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/GetLength.cs @@ -13,23 +13,25 @@ namespace System.IO.Tests protected override bool UsesOffsets => false; - [Fact] - public void ReturnsZeroForEmptyFile() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReturnsZeroForEmptyFile(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: options)) { Assert.Equal(0, RandomAccess.GetLength(handle)); } } - [Fact] - public void ReturnsExactSizeForNonEmptyFiles() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReturnsExactSizeForNonEmptyFiles(FileOptions options) { const int fileSize = 123; string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[fileSize]); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { Assert.Equal(fileSize, RandomAccess.GetLength(handle)); } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs new file mode 100644 index 00000000000..dcc7d47924f --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [PlatformSpecific(TestPlatforms.Windows)] + public class RandomAccess_Mixed : FileSystemTest + { + [DllImport(Interop.Libraries.Kernel32, EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + private static extern unsafe SafeFileHandle CreateFileW( + string lpFileName, + FileAccess dwDesiredAccess, + FileShare dwShareMode, + IntPtr lpSecurityAttributes, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile); + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task UsingSingleBuffer(bool async) + { + string filePath = GetTestFilePath(); + FileOptions options = async ? FileOptions.Asynchronous : FileOptions.None; + + // we want to test all combinations: starting with sync|async write, then sync|async read etc + foreach (bool syncWrite in new bool[] { true, false }) + { + foreach (bool syncRead in new bool[] { true, false }) + { + // File.OpenHandle initializes ThreadPoolBinding for async file handles on Windows + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.ReadWrite, options: options)) + { + await Validate(handle, options, new bool[] { syncWrite, !syncWrite }, new bool[] { syncRead, !syncRead }); + } + + // tests code path where ThreadPoolBinding is not initialized + using (SafeFileHandle tpBindingNotInitialized = CreateFileW(filePath, FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Create, (int)options, IntPtr.Zero)) + { + await Validate(tpBindingNotInitialized, options, new bool[] { syncWrite, !syncWrite }, new bool[] { syncRead, !syncRead }); + } + } + } + + static async Task Validate(SafeFileHandle handle, FileOptions options, bool[] syncWrites, bool[] syncReads) + { + byte[] writeBuffer = new byte[1]; + byte[] readBuffer = new byte[2]; + long fileOffset = 0; + + foreach (bool syncWrite in syncWrites) + { + foreach (bool syncRead in syncReads) + { + writeBuffer[0] = (byte)fileOffset; + + Assert.Equal(writeBuffer.Length, syncWrite ? RandomAccess.Write(handle, writeBuffer, fileOffset) : await RandomAccess.WriteAsync(handle, writeBuffer, fileOffset)); + Assert.Equal(writeBuffer.Length, syncRead ? RandomAccess.Read(handle, readBuffer, fileOffset) : await RandomAccess.ReadAsync(handle, readBuffer, fileOffset)); + Assert.Equal(writeBuffer[0], readBuffer[0]); + + fileOffset += 1; + } + } + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task UsingMultipleBuffers(bool async) + { + string filePath = GetTestFilePath(); + FileOptions options = async ? FileOptions.Asynchronous : FileOptions.None; + + foreach (bool syncWrite in new bool[] { true, false }) + { + foreach (bool syncRead in new bool[] { true, false }) + { + // File.OpenHandle initializes ThreadPoolBinding for async file handles on Windows + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.ReadWrite, options: options)) + { + await Validate(handle, options, new bool[] { syncWrite, !syncWrite }, new bool[] { syncRead, !syncRead }); + } + + // tests code path where ThreadPoolBinding is not initialized + using (SafeFileHandle tpBindingNotInitialized = CreateFileW(filePath, FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Create, (int)options, IntPtr.Zero)) + { + await Validate(tpBindingNotInitialized, options, new bool[] { syncWrite, !syncWrite }, new bool[] { syncRead, !syncRead }); + } + } + } + + static async Task Validate(SafeFileHandle handle, FileOptions options, bool[] syncWrites, bool[] syncReads) + { + byte[] writeBuffer_1 = new byte[1]; + byte[] writeBuffer_2 = new byte[1]; + byte[] readBuffer_1 = new byte[1]; + byte[] readBuffer_2 = new byte[1]; + long fileOffset = 0; + + IReadOnlyList> readBuffers = new Memory[] { readBuffer_1, readBuffer_2 }; + IReadOnlyList> writeBuffers = new ReadOnlyMemory[] { writeBuffer_1, writeBuffer_2 }; + + foreach (bool syncWrite in syncWrites) + { + foreach (bool syncRead in syncReads) + { + writeBuffer_1[0] = (byte)fileOffset; + writeBuffer_2[0] = (byte)(fileOffset+1); + + Assert.Equal(writeBuffer_1.Length + writeBuffer_2.Length, syncWrite ? RandomAccess.Write(handle, writeBuffers, fileOffset) : await RandomAccess.WriteAsync(handle, writeBuffers, fileOffset)); + Assert.Equal(writeBuffer_1.Length + writeBuffer_2.Length, syncRead ? RandomAccess.Read(handle, readBuffers, fileOffset) : await RandomAccess.ReadAsync(handle, readBuffers, fileOffset)); + Assert.Equal(writeBuffer_1[0], readBuffer_1[0]); + Assert.Equal(writeBuffer_2[0], readBuffer_2[0]); + + fileOffset += 2; + } + } + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs index ea998260094..585335c4cdc 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Security.Cryptography; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -14,15 +15,18 @@ namespace System.IO.Tests { private const FileOptions NoBuffering = (FileOptions)0x20000000; - [Fact] - public async Task ReadAsyncUsingSingleBuffer() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ReadUsingSingleBuffer(bool async) { const int fileSize = 1_000_000; // 1 MB string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous | NoBuffering)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, + options: FileOptions.Asynchronous | NoBuffering)) // to use Scatter&Gather APIs on Windows the handle MUST be opened for async IO using (SectorAlignedMemory buffer = SectorAlignedMemory.Allocate(Environment.SystemPageSize)) { int current = 0; @@ -39,7 +43,9 @@ namespace System.IO.Tests // It's possible to get 0 if we are lucky and file size is a multiple of physical sector size. do { - current = await RandomAccess.ReadAsync(handle, buffer.Memory, fileOffset: total); + current = async + ? await RandomAccess.ReadAsync(handle, buffer.Memory, fileOffset: total) + : RandomAccess.Read(handle, buffer.GetSpan(), fileOffset: total); Assert.True(expected.AsSpan(total, current).SequenceEqual(buffer.GetSpan().Slice(0, current))); @@ -51,8 +57,10 @@ namespace System.IO.Tests } } - [Fact] - public async Task ReadAsyncUsingMultipleBuffers() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ReadAsyncUsingMultipleBuffers(bool async) { const int fileSize = 1_000_000; // 1 MB string filePath = GetTestFilePath(); @@ -66,16 +74,17 @@ namespace System.IO.Tests long current = 0; long total = 0; + IReadOnlyList> buffers = new Memory[] + { + buffer_1.Memory, + buffer_2.Memory, + }; + do { - current = await RandomAccess.ReadAsync( - handle, - new Memory[] - { - buffer_1.Memory, - buffer_2.Memory, - }, - fileOffset: total); + current = async + ? await RandomAccess.ReadAsync(handle, buffers, fileOffset: total) + : RandomAccess.Read(handle, buffers, fileOffset: total); int takeFromFirst = Math.Min(buffer_1.Memory.Length, (int)current); Assert.True(expected.AsSpan((int)total, takeFromFirst).SequenceEqual(buffer_1.GetSpan().Slice(0, takeFromFirst))); @@ -89,8 +98,10 @@ namespace System.IO.Tests } } - [Fact] - public async Task WriteAsyncUsingSingleBuffer() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task WriteUsingSingleBuffer(bool async) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; @@ -107,18 +118,19 @@ namespace System.IO.Tests int take = Math.Min(content.Length - total, bufferSize); content.AsSpan(total, take).CopyTo(buffer.GetSpan()); - total += await RandomAccess.WriteAsync( - handle, - buffer.Memory, - fileOffset: total); + total += async + ? await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset: total) + : RandomAccess.Write(handle, buffer.GetSpan(), fileOffset: total); } } Assert.Equal(content, File.ReadAllBytes(filePath)); } - [Fact] - public async Task WriteAsyncUsingMultipleBuffers() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task WriteAsyncUsingMultipleBuffers(bool async) { string filePath = GetTestFilePath(); int bufferSize = Environment.SystemPageSize; @@ -131,19 +143,20 @@ namespace System.IO.Tests { long total = 0; + IReadOnlyList> buffers = new ReadOnlyMemory[] + { + buffer_1.Memory, + buffer_2.Memory, + }; + while (total != fileSize) { content.AsSpan((int)total, bufferSize).CopyTo(buffer_1.GetSpan()); content.AsSpan((int)total + bufferSize, bufferSize).CopyTo(buffer_2.GetSpan()); - total += await RandomAccess.WriteAsync( - handle, - new ReadOnlyMemory[] - { - buffer_1.Memory, - buffer_2.Memory, - }, - fileOffset: total); + total += async + ? await RandomAccess.WriteAsync(handle, buffers, fileOffset: total) + : RandomAccess.Write(handle, buffers, fileOffset: total); } } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs index 81021d04aee..9559e7cebc1 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs @@ -13,39 +13,54 @@ namespace System.IO.Tests protected override int MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.Read(handle, bytes, fileOffset); - protected override bool ShouldThrowForAsyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform sync IO using async handle - - [Fact] - public void ThrowsOnWriteAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsOnWriteAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write, options)) { Assert.Throws(() => RandomAccess.Read(handle, new byte[1], 0)); } } - [Fact] - public void ReadToAnEmptyBufferReturnsZero() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadToAnEmptyBufferReturnsZero(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[1]); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { Assert.Equal(0, RandomAccess.Read(handle, Array.Empty(), fileOffset: 0)); } } - [Fact] - public void ReadsBytesFromGivenFileAtGivenOffset() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void CanUseStackAllocatedMemory(FileOptions options) + { + string filePath = GetTestFilePath(); + File.WriteAllBytes(filePath, new byte[1] { 3 }); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) + { + Span stackAllocated = stackalloc byte[2]; + Assert.Equal(1, RandomAccess.Read(handle, stackAllocated, fileOffset: 0)); + Assert.Equal(3, stackAllocated[0]); + } + } + + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadsBytesFromGivenFileAtGivenOffset(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] actual = new byte[fileSize + 1]; int current = 0; diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs index c18da9ecd50..f23cd8f92f6 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs @@ -17,13 +17,11 @@ namespace System.IO.Tests protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.ReadAsync(handle, bytes, fileOffset); - protected override bool ShouldThrowForSyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform async IO using sync handle - - [Fact] - public async Task TaskAlreadyCanceledAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task TaskAlreadyCanceledAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: options)) { CancellationTokenSource cts = GetCancelledTokenSource(); CancellationToken token = cts.Token; @@ -35,36 +33,39 @@ namespace System.IO.Tests } } - [Fact] - public async Task ThrowsOnWriteAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ThrowsOnWriteAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write, options)) { await Assert.ThrowsAsync(async () => await RandomAccess.ReadAsync(handle, new byte[1], 0)); } } - [Fact] - public async Task ReadToAnEmptyBufferReturnsZeroAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadToAnEmptyBufferReturnsZeroAsync(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[1]); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { Assert.Equal(0, await RandomAccess.ReadAsync(handle, Array.Empty(), fileOffset: 0)); } } - [Fact] - public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task HappyPath(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] actual = new byte[fileSize + 1]; int current = 0; diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs index 68058b6242f..7f51ea6e847 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs @@ -13,48 +13,49 @@ namespace System.IO.Tests protected override long MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.Read(handle, new Memory[] { bytes }, fileOffset); - protected override bool ShouldThrowForAsyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform sync IO using async handle - - [Fact] - public void ThrowsArgumentNullExceptionForNullBuffers() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { AssertExtensions.Throws("buffers", () => RandomAccess.Read(handle, buffers: null, 0)); } } - [Fact] - public void ThrowsOnWriteAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsOnWriteAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write, options)) { Assert.Throws(() => RandomAccess.Read(handle, new Memory[] { new byte[1] }, 0)); } } - [Fact] - public void ReadToAnEmptyBufferReturnsZero() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadToAnEmptyBufferReturnsZero(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[1]); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { Assert.Equal(0, RandomAccess.Read(handle, new Memory[] { Array.Empty() }, fileOffset: 0)); } } - [Fact] - public void ReadsBytesFromGivenFileAtGivenOffset() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadsBytesFromGivenFileAtGivenOffset(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] actual = new byte[fileSize + 1]; long current = 0; @@ -86,13 +87,14 @@ namespace System.IO.Tests } } - [Fact] - public void ReadToTheSameBufferOverwritesContent() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadToTheSameBufferOverwritesContent(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[3] { 1, 2, 3 }); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] buffer = new byte[1]; Assert.Equal(buffer.Length + buffer.Length, RandomAccess.Read(handle, Enumerable.Repeat(buffer.AsMemory(), 2).ToList(), fileOffset: 0)); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs index 68d631a6dc3..250d9c31677 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs @@ -17,22 +17,21 @@ namespace System.IO.Tests protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.ReadAsync(handle, new Memory[] { bytes }, fileOffset); - protected override bool ShouldThrowForSyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform async IO using sync handle - - [Fact] - public void ThrowsArgumentNullExceptionForNullBuffers() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: options)) { AssertExtensions.Throws("buffers", () => RandomAccess.ReadAsync(handle, buffers: null, 0)); } } - [Fact] - public async Task TaskAlreadyCanceledAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task TaskAlreadyCanceledAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: options)) { CancellationTokenSource cts = GetCancelledTokenSource(); CancellationToken token = cts.Token; @@ -44,36 +43,39 @@ namespace System.IO.Tests } } - [Fact] - public async Task ThrowsOnWriteAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ThrowsOnWriteAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Write, options)) { await Assert.ThrowsAsync(async () => await RandomAccess.ReadAsync(handle, new Memory[] { new byte[1] }, 0)); } } - [Fact] - public async Task ReadToAnEmptyBufferReturnsZeroAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadToAnEmptyBufferReturnsZeroAsync(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[1]); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { Assert.Equal(0, await RandomAccess.ReadAsync(handle, new Memory[] { Array.Empty() }, fileOffset: 0)); } } - [Fact] - public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] expected = RandomNumberGenerator.GetBytes(fileSize); File.WriteAllBytes(filePath, expected); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] actual = new byte[fileSize + 1]; long current = 0; @@ -90,6 +92,7 @@ namespace System.IO.Tests new Memory[] { buffer_1, + Array.Empty(), buffer_2 }, fileOffset: total); @@ -104,13 +107,14 @@ namespace System.IO.Tests } } - [Fact] - public async Task ReadToTheSameBufferOverwritesContent() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadToTheSameBufferOverwritesContent(FileOptions options) { string filePath = GetTestFilePath(); File.WriteAllBytes(filePath, new byte[3] { 1, 2, 3 }); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) { byte[] buffer = new byte[1]; Assert.Equal(buffer.Length + buffer.Length, await RandomAccess.ReadAsync(handle, Enumerable.Repeat(buffer.AsMemory(), 2).ToList(), fileOffset: 0)); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs index abcac008dc6..3cac633f53f 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs @@ -12,35 +12,50 @@ namespace System.IO.Tests protected override int MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.Write(handle, bytes, fileOffset); - protected override bool ShouldThrowForAsyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform sync IO using async handle - - [Fact] - public void ThrowsOnReadAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsOnReadAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { Assert.Throws(() => RandomAccess.Write(handle, new byte[1], 0)); } } - [Fact] - public void WriteUsingEmptyBufferReturnsZero() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WriteUsingEmptyBufferReturnsZero(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(0, RandomAccess.Write(handle, Array.Empty(), fileOffset: 0)); } } - [Fact] - public void WritesBytesFromGivenBufferToGivenFileAtGivenOffset() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void CanUseStackAllocatedMemory(FileOptions options) + { + string filePath = GetTestFilePath(); + Span stackAllocated = stackalloc byte[2] { 1, 2 }; + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) + { + Assert.Equal(stackAllocated.Length, RandomAccess.Write(handle, stackAllocated, fileOffset: 0)); + } + + Assert.Equal(stackAllocated.ToArray(), File.ReadAllBytes(filePath)); + } + + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WritesBytesFromGivenBufferToGivenFileAtGivenOffset(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { int total = 0; int current = 0; diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs index 074f5baac59..b5c2399f964 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs @@ -16,13 +16,11 @@ namespace System.IO.Tests protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, bytes, fileOffset); - protected override bool ShouldThrowForSyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform async IO using sync handle - - [Fact] - public async Task TaskAlreadyCanceledAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task TaskAlreadyCanceledAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: options)) { CancellationTokenSource cts = GetCancelledTokenSource(); CancellationToken token = cts.Token; @@ -34,32 +32,35 @@ namespace System.IO.Tests } } - [Fact] - public async Task ThrowsOnReadAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ThrowsOnReadAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { await Assert.ThrowsAsync(async () => await RandomAccess.WriteAsync(handle, new byte[1], 0)); } } - [Fact] - public async Task WriteUsingEmptyBufferReturnsZeroAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WriteUsingEmptyBufferReturnsZeroAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(0, await RandomAccess.WriteAsync(handle, Array.Empty(), fileOffset: 0)); } } - [Fact] - public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { int total = 0; int current = 0; diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs index a9483d6c0ea..5af52f0c61a 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs @@ -14,44 +14,45 @@ namespace System.IO.Tests protected override long MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.Write(handle, new ReadOnlyMemory[] { bytes }, fileOffset); - protected override bool ShouldThrowForAsyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform sync IO using async handle - - [Fact] - public void ThrowsArgumentNullExceptionForNullBuffers() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, options: options)) { AssertExtensions.Throws("buffers", () => RandomAccess.Write(handle, buffers: null, 0)); } } - [Fact] - public void ThrowsOnReadAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsOnReadAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { Assert.Throws(() => RandomAccess.Write(handle, new ReadOnlyMemory[] { new byte[1] }, 0)); } } - [Fact] - public void WriteUsingEmptyBufferReturnsZero() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WriteUsingEmptyBufferReturnsZero(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(0, RandomAccess.Write(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0)); } } - [Fact] - public void WritesBytesFromGivenBuffersToGivenFileAtGivenOffset() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WritesBytesFromGivenBuffersToGivenFileAtGivenOffset(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { long total = 0; long current = 0; @@ -81,8 +82,9 @@ namespace System.IO.Tests Assert.Equal(content, File.ReadAllBytes(filePath)); } - [Fact] - public void DuplicatedBufferDuplicatesContent() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void DuplicatedBufferDuplicatesContent(FileOptions options) { const byte value = 1; const int repeatCount = 2; @@ -90,7 +92,7 @@ namespace System.IO.Tests ReadOnlyMemory buffer = new byte[1] { value }; List> buffers = Enumerable.Repeat(buffer, repeatCount).ToList(); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(repeatCount, RandomAccess.Write(handle, buffers, fileOffset: 0)); } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs index f8369eb13c7..d6bd235efb7 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs @@ -18,22 +18,21 @@ namespace System.IO.Tests protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { bytes }, fileOffset); - protected override bool ShouldThrowForSyncHandle - => OperatingSystem.IsWindows(); // on Windows we can NOT perform async IO using sync handle - - [Fact] - public void ThrowsArgumentNullExceptionForNullBuffers() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ThrowsArgumentNullExceptionForNullBuffers(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { AssertExtensions.Throws("buffers", () => RandomAccess.WriteAsync(handle, buffers: null, 0)); } } - [Fact] - public async Task TaskAlreadyCanceledAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task TaskAlreadyCanceledAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.CreateNew, FileAccess.ReadWrite, options: options)) { CancellationTokenSource cts = GetCancelledTokenSource(); CancellationToken token = cts.Token; @@ -45,32 +44,35 @@ namespace System.IO.Tests } } - [Fact] - public async Task ThrowsOnReadAccess() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ThrowsOnReadAccess(FileOptions options) { - using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read)) + using (SafeFileHandle handle = GetHandleToExistingFile(FileAccess.Read, options)) { await Assert.ThrowsAsync(async () => await RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { new byte[1] }, 0)); } } - [Fact] - public async Task WriteUsingEmptyBufferReturnsZeroAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WriteUsingEmptyBufferReturnsZeroAsync(FileOptions options) { - using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(0, await RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0)); } } - [Fact] - public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync(FileOptions options) { const int fileSize = 4_001; string filePath = GetTestFilePath(); byte[] content = RandomNumberGenerator.GetBytes(fileSize); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { long total = 0; long current = 0; @@ -86,6 +88,7 @@ namespace System.IO.Tests new ReadOnlyMemory[] { buffer_1, + Array.Empty(), buffer_2 }, fileOffset: total); @@ -99,8 +102,9 @@ namespace System.IO.Tests Assert.Equal(content, File.ReadAllBytes(filePath)); } - [Fact] - public async Task DuplicatedBufferDuplicatesContentAsync() + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task DuplicatedBufferDuplicatesContentAsync(FileOptions options) { const byte value = 1; const int repeatCount = 2; @@ -108,7 +112,7 @@ namespace System.IO.Tests ReadOnlyMemory buffer = new byte[1] { value }; List> buffers = Enumerable.Repeat(buffer, repeatCount).ToList(); - using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: FileOptions.Asynchronous)) + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { Assert.Equal(repeatCount, await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0)); } diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index bf845ce77b5..91135c4439c 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -73,6 +73,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index 5aa7a2c7f4d..7eb66725421 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -46,7 +46,11 @@ namespace Microsoft.Win32.SafeHandles Preallocate(fullPath, preallocationSize, fileHandle); } - fileHandle.InitThreadPoolBindingIfNeeded(); + if ((options & FileOptions.Asynchronous) != 0) + { + // the handle has not been exposed yet, so we don't need to aquire a lock + fileHandle.InitThreadPoolBinding(); + } return fileHandle; } @@ -140,35 +144,53 @@ namespace Microsoft.Win32.SafeHandles } } - internal void InitThreadPoolBindingIfNeeded() + internal void EnsureThreadPoolBindingInitialized() { - if (IsAsync == true && ThreadPoolBinding == null) + if (IsAsync && ThreadPoolBinding == null) { - // This is necessary for async IO using IO Completion ports via our - // managed Threadpool API's. This (theoretically) calls the OS's - // BindIoCompletionCallback method, and passes in a stub for the - // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped - // struct for this request and gets a delegate to a managed callback - // from there, which it then calls on a threadpool thread. (We allocate - // our native OVERLAPPED structs 2 pointers too large and store EE state - // & GC handles there, one to an IAsyncResult, the other to a delegate.) - try - { - ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(this); - } - catch (ArgumentException ex) - { - if (OwnsHandle) - { - // We should close the handle so that the handle is not open until SafeFileHandle GC - Dispose(); - } + Init(); + } - throw new IOException(SR.IO_BindHandleFailed, ex); + void Init() // moved to a separate method so EnsureThreadPoolBindingInitialized can be inlined + { + lock (this) + { + if (ThreadPoolBinding == null) + { + InitThreadPoolBinding(); + } } } } + private void InitThreadPoolBinding() + { + Debug.Assert(IsAsync); + + // This is necessary for async IO using IO Completion ports via our + // managed Threadpool API's. This (theoretically) calls the OS's + // BindIoCompletionCallback method, and passes in a stub for the + // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped + // struct for this request and gets a delegate to a managed callback + // from there, which it then calls on a threadpool thread. (We allocate + // our native OVERLAPPED structs 2 pointers too large and store EE state + // & GC handles there, one to an IAsyncResult, the other to a delegate.) + try + { + ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(this); + } + catch (ArgumentException ex) + { + if (OwnsHandle) + { + // We should close the handle so that the handle is not open until SafeFileHandle GC + Dispose(); + } + + throw new IOException(SR.IO_BindHandleFailed, ex); + } + } + internal unsafe FileOptions GetFileOptions() { FileOptions fileOptions = _fileOptions; diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 1669ffb9bbb..05cc719ed05 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2668,6 +2668,9 @@ The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. + + IO operation will not work. Most likely the file will become too long. + IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index de5bc44f1ba..1ceea5e5c0c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1516,6 +1516,9 @@ Common\Interop\Windows\Kernel32\Interop.GetModuleFileName.cs + + + Common\Interop\Windows\Kernel32\Interop.GetOverlappedResult.cs Common\Interop\Windows\Kernel32\Interop.GetProcessMemoryInfo.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index 22a3934fe40..a47a601438c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -67,25 +67,12 @@ namespace System.IO return FileStreamHelpers.CheckFileCall(result, path: null); } - private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, - CancellationToken cancellationToken) - { - return new ValueTask(Task.Factory.StartNew(static state => - { - var args = ((SafeFileHandle handle, Memory buffer, long fileOffset))state!; - return ReadAtOffset(args.handle, args.buffer.Span, args.fileOffset); - }, (handle, buffer, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - } + private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + => ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); private static ValueTask ReadScatterAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) - { - return new ValueTask(Task.Factory.StartNew(static state => - { - var args = ((SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset))state!; - return ReadScatterAtOffset(args.handle, args.buffers, args.fileOffset); - }, (handle, buffers, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - } + => ScheduleSyncReadScatterAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); private static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { @@ -130,24 +117,11 @@ namespace System.IO return FileStreamHelpers.CheckFileCall(result, path: null); } - private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, - CancellationToken cancellationToken) - { - return new ValueTask(Task.Factory.StartNew(static state => - { - var args = ((SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset))state!; - return WriteAtOffset(args.handle, args.buffer.Span, args.fileOffset); - }, (handle, buffer, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - } + private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + => ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) - { - return new ValueTask(Task.Factory.StartNew(static state => - { - var args = ((SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset))state!; - return WriteGatherAtOffset(args.handle, args.buffers, args.fileOffset); - }, (handle, buffers, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - } + => ScheduleSyncWriteGatherAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 5d198ccb078..610af86a8d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -14,6 +14,8 @@ namespace System.IO { public static partial class RandomAccess { + private static readonly IOCompletionCallback s_callback = AllocateCallback(); + internal static unsafe long GetFileLength(SafeFileHandle handle, string? path) { Interop.Kernel32.FILE_STANDARD_INFO info; @@ -28,122 +30,188 @@ namespace System.IO internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer, long fileOffset, string? path = null) { - NativeOverlapped nativeOverlapped = GetNativeOverlapped(fileOffset); - int r = ReadFileNative(handle, buffer, syncUsingOverlapped: true, &nativeOverlapped, out int errorCode); - - if (r == -1) + if (handle.IsAsync) { - // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. - if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE) - { - r = 0; - } - else - { - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - { - ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle)); - } - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); - } + return ReadSyncUsingAsyncHandle(handle, buffer, fileOffset, path); } - return r; + NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); + fixed (byte* pinned = &MemoryMarshal.GetReference(buffer)) + { + if (Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, out int numBytesRead, &overlapped) != 0) + { + return numBytesRead; + } + + int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + switch (errorCode) + { + case Interop.Errors.ERROR_HANDLE_EOF: + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position : + // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, + // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" + return numBytesRead; + case Interop.Errors.ERROR_BROKEN_PIPE: + // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. + return 0; + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + } + } + + private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span buffer, long fileOffset, string? path) + { + handle.EnsureThreadPoolBindingInitialized(); + + CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding!); + NativeOverlapped* overlapped = null; + + try + { + overlapped = GetNativeOverlappedForAsyncHandle(handle.ThreadPoolBinding!, fileOffset, resetEvent); + + fixed (byte* pinned = &MemoryMarshal.GetReference(buffer)) + { + Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped); + + int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + if (errorCode == Interop.Errors.ERROR_IO_PENDING) + { + resetEvent.WaitOne(); + errorCode = Interop.Errors.ERROR_SUCCESS; + } + + if (errorCode == Interop.Errors.ERROR_SUCCESS) + { + int result = 0; + if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) + { + Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); + return result; + } + + errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + } + + switch (errorCode) + { + case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) + case Interop.Errors.ERROR_BROKEN_PIPE: + // EOF on a pipe. Callback will not be called. + // We clear the overlapped status bit for this special case (failure + // to do so looks like we are freeing a pending overlapped later). + overlapped->InternalLow = IntPtr.Zero; + return 0; + + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + } + } + finally + { + if (overlapped != null) + { + resetEvent.FreeNativeOverlapped(overlapped); + } + + resetEvent.Dispose(); + } } internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset, string? path = null) { - NativeOverlapped nativeOverlapped = GetNativeOverlapped(fileOffset); - int r = WriteFileNative(handle, buffer, true, &nativeOverlapped, out int errorCode); - - if (r == -1) + if (handle.IsAsync) { - // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. - if (errorCode == Interop.Errors.ERROR_NO_DATA) + return WriteSyncUsingAsyncHandle(handle, buffer, fileOffset, path); + } + + NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); + fixed (byte* pinned = &MemoryMarshal.GetReference(buffer)) + { + if (Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, out int numBytesWritten, &overlapped) != 0) { - r = 0; + return numBytesWritten; } - else + + int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + switch (errorCode) { - // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large or for synchronous writes - // to a handle opened asynchronously. - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + case Interop.Errors.ERROR_NO_DATA: // EOF on a pipe + return 0; + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + } + } + + private static unsafe int WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset, string? path) + { + handle.EnsureThreadPoolBindingInitialized(); + + CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding!); + NativeOverlapped* overlapped = null; + + try + { + overlapped = GetNativeOverlappedForAsyncHandle(handle.ThreadPoolBinding!, fileOffset, resetEvent); + + fixed (byte* pinned = &MemoryMarshal.GetReference(buffer)) + { + Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped); + + int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + if (errorCode == Interop.Errors.ERROR_IO_PENDING) { - throw new IOException(SR.IO_FileTooLongOrHandleNotSync); + resetEvent.WaitOne(); + errorCode = Interop.Errors.ERROR_SUCCESS; } - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + if (errorCode == Interop.Errors.ERROR_SUCCESS) + { + int result = 0; + if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) + { + Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); + return result; + } + + errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + } + + switch (errorCode) + { + case Interop.Errors.ERROR_NO_DATA: + // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. + return 0; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + // ERROR_INVALID_PARAMETER may be returned for writes + // where the position is too large or for synchronous writes + // to a handle opened asynchronously. + throw new IOException(SR.IO_FileTooLong); + + default: + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } } } - - return r; - } - - internal static unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, bool syncUsingOverlapped, NativeOverlapped* overlapped, out int errorCode) - { - Debug.Assert(handle != null, "handle != null"); - - int r; - int numBytesRead = 0; - - fixed (byte* p = &MemoryMarshal.GetReference(bytes)) + finally { - r = overlapped == null || syncUsingOverlapped ? - Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, overlapped) : - Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped); - } - - if (r == 0) - { - errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); - - if (syncUsingOverlapped && errorCode == Interop.Errors.ERROR_HANDLE_EOF) + if (overlapped != null) { - // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position : - // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, - // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" - return numBytesRead; + resetEvent.FreeNativeOverlapped(overlapped); } - return -1; - } - else - { - errorCode = 0; - return numBytesRead; - } - } - - internal static unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, bool syncUsingOverlapped, NativeOverlapped* overlapped, out int errorCode) - { - Debug.Assert(handle != null, "handle != null"); - - int numBytesWritten = 0; - int r; - - fixed (byte* p = &MemoryMarshal.GetReference(buffer)) - { - r = overlapped == null || syncUsingOverlapped ? - Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, overlapped) : - Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped); - } - - if (r == 0) - { - errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); - return -1; - } - else - { - errorCode = 0; - return numBytesWritten; + resetEvent.Dispose(); } } private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) - => Map(QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken)); + => handle.IsAsync + ? Map(QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken)) + : ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); private static ValueTask Map((SafeFileHandle.ValueTaskSource? vts, int errorCode) tuple) => tuple.vts != null @@ -153,6 +221,8 @@ namespace System.IO internal static unsafe (SafeFileHandle.ValueTaskSource? vts, int errorCode) QueueAsyncReadFile( SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) { + handle.EnsureThreadPoolBindingInitialized(); + SafeFileHandle.ValueTaskSource vts = handle.GetValueTaskSource(); try { @@ -200,11 +270,15 @@ namespace System.IO } private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) - => Map(QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken)); + => handle.IsAsync + ? Map(QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken)) + : ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); internal static unsafe (SafeFileHandle.ValueTaskSource? vts, int errorCode) QueueAsyncWriteFile( SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) { + handle.EnsureThreadPoolBindingInitialized(); + SafeFileHandle.ValueTaskSource vts = handle.GetValueTaskSource(); try { @@ -291,6 +365,11 @@ namespace System.IO private static ValueTask ReadScatterAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { + if (!handle.IsAsync) + { + return ScheduleSyncReadScatterAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); + } + if (CanUseScatterGatherWindowsAPIs(handle)) { long totalBytes = 0; @@ -358,6 +437,8 @@ namespace System.IO private static unsafe ValueTask ReadFileScatterAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToRead, long fileOffset, CancellationToken cancellationToken) { + handle.EnsureThreadPoolBindingInitialized(); + SafeFileHandle.ValueTaskSource vts = handle.GetValueTaskSource(); try { @@ -426,6 +507,11 @@ namespace System.IO private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { + if (!handle.IsAsync) + { + return ScheduleSyncWriteGatherAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); + } + if (CanUseScatterGatherWindowsAPIs(handle)) { long totalBytes = 0; @@ -507,6 +593,8 @@ namespace System.IO private static unsafe ValueTask WriteFileGatherAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToWrite, long fileOffset, CancellationToken cancellationToken) { + handle.EnsureThreadPoolBindingInitialized(); + SafeFileHandle.ValueTaskSource vts = handle.GetValueTaskSource(); try { @@ -545,14 +633,73 @@ namespace System.IO return new ValueTask(vts, vts.Version); } - private static NativeOverlapped GetNativeOverlapped(long fileOffset) + private static unsafe NativeOverlapped* GetNativeOverlappedForAsyncHandle(ThreadPoolBoundHandle threadPoolBinding, long fileOffset, CallbackResetEvent resetEvent) { - NativeOverlapped nativeOverlapped = default; - // For pipes the offsets are ignored by the OS - nativeOverlapped.OffsetLow = unchecked((int)fileOffset); - nativeOverlapped.OffsetHigh = (int)(fileOffset >> 32); + // After SafeFileHandle is bound to ThreadPool, we need to use ThreadPoolBinding + // to allocate a native overlapped and provide a valid callback. + NativeOverlapped* result = threadPoolBinding.AllocateNativeOverlapped(s_callback, resetEvent, null); - return nativeOverlapped; + // For pipes the offsets are ignored by the OS + result->OffsetLow = unchecked((int)fileOffset); + result->OffsetHigh = (int)(fileOffset >> 32); + + // From https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult: + // "If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed. + // Use of file, named pipe, or communications-device handles for this purpose is discouraged. + // It is safer to use an event object because of the confusion that can occur when multiple simultaneous overlapped operations + // are performed on the same file, named pipe, or communications device. + // In this situation, there is no way to know which operation caused the object's state to be signaled." + // Since we want RandomAccess APIs to be thread-safe, we provide a dedicated wait handle. + result->EventHandle = resetEvent.SafeWaitHandle.DangerousGetHandle(); + + return result; + } + + private static NativeOverlapped GetNativeOverlappedForSyncHandle(SafeFileHandle handle, long fileOffset) + { + Debug.Assert(!handle.IsAsync); + + NativeOverlapped result = default; + result.OffsetLow = unchecked((int)fileOffset); + result.OffsetHigh = (int)(fileOffset >> 32); + return result; + } + + private static unsafe IOCompletionCallback AllocateCallback() + { + return new IOCompletionCallback(Callback); + + static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) + { + CallbackResetEvent state = (CallbackResetEvent)ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped)!; + state.FreeNativeOverlapped(pOverlapped); + } + } + + // We need to store the reference count (see the comment in FreeNativeOverlappedIfItIsSafe) and an EventHandle to signal the completion. + // We could keep these two things separate, but since ManualResetEvent is sealed and we want to avoid any extra allocations, this type has been created. + // It's basically ManualResetEvent with reference count. + private sealed class CallbackResetEvent : EventWaitHandle + { + private readonly ThreadPoolBoundHandle _threadPoolBoundHandle; + private int _freeWhenZero = 2; // one for the callback and another for the method that calls GetOverlappedResult + + internal CallbackResetEvent(ThreadPoolBoundHandle threadPoolBoundHandle) : base(initialState: false, EventResetMode.ManualReset) + { + _threadPoolBoundHandle = threadPoolBoundHandle; + } + + internal unsafe void FreeNativeOverlapped(NativeOverlapped* pOverlapped) + { + // Each SafeFileHandle opened for async IO is bound to ThreadPool. + // It requires us to provide a callback even if we want to use EventHandle and use GetOverlappedResult to obtain the result. + // There can be a race condition between the call to GetOverlappedResult and the callback invocation, + // so we need to track the number of references, and when it drops to zero, then free the native overlapped. + if (Interlocked.Decrement(ref _freeWhenZero) == 0) + { + _threadPoolBoundHandle.FreeNativeOverlapped(pOverlapped); + } + } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs index 199d0d4ea91..6da5aa183b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs @@ -37,14 +37,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was opened for async IO. /// is negative. /// was not opened for reading. /// An I/O error occurred. /// Position of the file is not advanced. public static int Read(SafeFileHandle handle, Span buffer, long fileOffset) { - ValidateInput(handle, fileOffset, mustBeSync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); return ReadAtOffset(handle, buffer, fileOffset); } @@ -60,14 +59,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was opened for async IO. /// is negative. /// was not opened for reading. /// An I/O error occurred. /// Position of the file is not advanced. public static long Read(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) { - ValidateInput(handle, fileOffset, mustBeSync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); ValidateBuffers(buffers); return ReadScatterAtOffset(handle, buffers, fileOffset); @@ -85,14 +83,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was not opened for async IO. /// is negative. /// was not opened for reading. /// An I/O error occurred. /// Position of the file is not advanced. public static ValueTask ReadAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken = default) { - ValidateInput(handle, fileOffset, mustBeAsync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); if (cancellationToken.IsCancellationRequested) { @@ -114,14 +111,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was not opened for async IO. /// is negative. /// was not opened for reading. /// An I/O error occurred. /// Position of the file is not advanced. public static ValueTask ReadAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken = default) { - ValidateInput(handle, fileOffset, mustBeAsync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); ValidateBuffers(buffers); if (cancellationToken.IsCancellationRequested) @@ -143,14 +139,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was opened for async IO. /// is negative. /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. public static int Write(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { - ValidateInput(handle, fileOffset, mustBeSync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); return WriteAtOffset(handle, buffer, fileOffset); } @@ -166,14 +161,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was opened for async IO. /// is negative. /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. public static long Write(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) { - ValidateInput(handle, fileOffset, mustBeSync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); ValidateBuffers(buffers); return WriteGatherAtOffset(handle, buffers, fileOffset); @@ -191,14 +185,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was not opened for async IO. /// is negative. /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. public static ValueTask WriteAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken = default) { - ValidateInput(handle, fileOffset, mustBeAsync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); if (cancellationToken.IsCancellationRequested) { @@ -220,14 +213,13 @@ namespace System.IO /// is invalid. /// The file is closed. /// The file does not support seeking (pipe or socket). - /// was not opened for async IO. /// is negative. /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. public static ValueTask WriteAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken = default) { - ValidateInput(handle, fileOffset, mustBeAsync: OperatingSystem.IsWindows()); + ValidateInput(handle, fileOffset); ValidateBuffers(buffers); if (cancellationToken.IsCancellationRequested) @@ -238,7 +230,7 @@ namespace System.IO return WriteGatherAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); } - private static void ValidateInput(SafeFileHandle handle, long fileOffset, bool mustBeSync = false, bool mustBeAsync = false) + private static void ValidateInput(SafeFileHandle handle, long fileOffset) { if (handle is null) { @@ -258,17 +250,9 @@ namespace System.IO ThrowHelper.ThrowNotSupportedException_UnseekableStream(); } - else if (mustBeSync && handle.IsAsync) - { - ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle)); - } - else if (mustBeAsync && !handle.IsAsync) - { - ThrowHelper.ThrowArgumentException_HandleNotAsync(nameof(handle)); - } else if (fileOffset < 0) { - ThrowHelper.ThrowArgumentOutOfRangeException_NeedPosNum(nameof(fileOffset)); + ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(nameof(fileOffset)); } } @@ -279,5 +263,43 @@ namespace System.IO ThrowHelper.ThrowArgumentNullException(ExceptionArgument.buffers); } } + + private static ValueTask ScheduleSyncReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + { + return new ValueTask(Task.Factory.StartNew(static state => + { + var args = ((SafeFileHandle handle, Memory buffer, long fileOffset))state!; + return ReadAtOffset(args.handle, args.buffer.Span, args.fileOffset); + }, (handle, buffer, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + } + + private static ValueTask ScheduleSyncReadScatterAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, + long fileOffset, CancellationToken cancellationToken) + { + return new ValueTask(Task.Factory.StartNew(static state => + { + var args = ((SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset))state!; + return ReadScatterAtOffset(args.handle, args.buffers, args.fileOffset); + }, (handle, buffers, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + } + + private static ValueTask ScheduleSyncWriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + { + return new ValueTask(Task.Factory.StartNew(static state => + { + var args = ((SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset))state!; + return WriteAtOffset(args.handle, args.buffer.Span, args.fileOffset); + }, (handle, buffer, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + } + + private static ValueTask ScheduleSyncWriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, + long fileOffset, CancellationToken cancellationToken) + { + return new ValueTask(Task.Factory.StartNew(static state => + { + var args = ((SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset))state!; + return WriteGatherAtOffset(args.handle, args.buffers, args.fileOffset); + }, (handle, buffers, fileOffset), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index a194c4802d1..f077d94e36e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -22,14 +22,6 @@ namespace System.IO.Strategies internal override bool IsAsync => true; - public override int Read(byte[] buffer, int offset, int count) - { - ValueTask vt = ReadAsyncInternal(new Memory(buffer, offset, count), CancellationToken.None); - return vt.IsCompleted ? - vt.Result : - vt.AsTask().GetAwaiter().GetResult(); - } - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken).AsTask(); @@ -66,9 +58,6 @@ namespace System.IO.Strategies : (errorCode == 0) ? ValueTask.FromResult(0) : ValueTask.FromException(HandleIOError(positionBefore, errorCode)); } - public override void Write(byte[] buffer, int offset, int count) - => WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 39275721010..087c51f6f2d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -189,6 +189,31 @@ namespace System.IO.Strategies } + internal static unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) + { + Debug.Assert(handle != null, "handle != null"); + + int r; + int numBytesRead = 0; + + fixed (byte* p = &MemoryMarshal.GetReference(bytes)) + { + r = overlapped == null + ? Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, overlapped) + : Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped); + } + + if (r == 0) + { + errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + return -1; + } + else + { + errorCode = 0; + return numBytesRead; + } + } internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? path, bool canSeek, long filePosition, Stream destination, int bufferSize, CancellationToken cancellationToken) { @@ -265,7 +290,7 @@ namespace System.IO.Strategies } // Kick off the read. - synchronousSuccess = RandomAccess.ReadFileNative(handle, copyBuffer, false, readAwaitable._nativeOverlapped, out errorCode) >= 0; + synchronousSuccess = ReadFileNative(handle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0; } // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs index ec74ccf1ac4..addfebcadac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -82,7 +83,7 @@ namespace System.IO.Strategies private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) { - handle.InitThreadPoolBindingIfNeeded(); + handle.EnsureThreadPoolBindingInitialized(); if (handle.CanSeek) SeekCore(handle, 0, SeekOrigin.Current); @@ -1005,14 +1006,33 @@ namespace System.IO.Strategies { Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); - return RandomAccess.ReadFileNative(handle, bytes, false, overlapped, out errorCode); + return FileStreamHelpers.ReadFileNative(handle, bytes, overlapped, out errorCode); } private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) { Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); - return RandomAccess.WriteFileNative(handle, buffer, false, overlapped, out errorCode); + int numBytesWritten = 0; + int r; + + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) + { + r = overlapped == null + ? Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, overlapped) + : Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped); + } + + if (r == 0) + { + errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + return -1; + } + else + { + errorCode = 0; + return numBytesWritten; + } } public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs index e3f111e2c71..4091b2db65c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -22,10 +21,6 @@ namespace System.IO.Strategies internal override bool IsAsync => false; - public override int Read(byte[] buffer, int offset, int count) => ReadSpan(new Span(buffer, offset, count)); - - public override int Read(Span buffer) => ReadSpan(buffer); - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that @@ -46,19 +41,6 @@ namespace System.IO.Strategies base.ReadAsync(buffer, cancellationToken); } - public override void Write(byte[] buffer, int offset, int count) - => WriteSpan(new ReadOnlySpan(buffer, offset, count)); - - public override void Write(ReadOnlySpan buffer) - { - if (_fileHandle.IsClosed) - { - ThrowHelper.ThrowObjectDisposedException_FileClosed(); - } - - WriteSpan(buffer); - } - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // If we weren't opened for asynchronous I/O, we still call to the base implementation so that @@ -80,37 +62,5 @@ namespace System.IO.Strategies } public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush - - private unsafe int ReadSpan(Span destination) - { - if (!CanRead) - { - ThrowHelper.ThrowNotSupportedException_UnreadableStream(); - } - - Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); - - int r = RandomAccess.ReadAtOffset(_fileHandle, destination, _filePosition, _path); - Debug.Assert(r >= 0, $"RandomAccess.ReadAtOffset returned {r}."); - _filePosition += r; - - return r; - } - - private unsafe void WriteSpan(ReadOnlySpan source) - { - if (!CanWrite) - { - ThrowHelper.ThrowNotSupportedException_UnwritableStream(); - } - - Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); - - int r = RandomAccess.WriteAtOffset(_fileHandle, source, _filePosition, _path); - Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); - _filePosition += r; - - UpdateLengthOnChangePosition(); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs index 259b438883a..dda1c621119 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs @@ -26,7 +26,7 @@ namespace System.IO.Strategies _share = share; _exposedHandle = true; - handle.InitThreadPoolBindingIfNeeded(); + handle.EnsureThreadPoolBindingInitialized(); if (handle.CanSeek) { @@ -257,5 +257,50 @@ namespace System.IO.Strategies _filePosition = value; } } + + public override int Read(byte[] buffer, int offset, int count) => ReadSpan(new Span(buffer, offset, count)); + + public override int Read(Span buffer) => ReadSpan(buffer); + + private unsafe int ReadSpan(Span destination) + { + if (_fileHandle.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if ((_access & FileAccess.Read) == 0) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + int r = RandomAccess.ReadAtOffset(_fileHandle, destination, _filePosition, _path); + Debug.Assert(r >= 0, $"RandomAccess.ReadAtOffset returned {r}."); + _filePosition += r; + + return r; + } + + public override void Write(byte[] buffer, int offset, int count) + => WriteSpan(new ReadOnlySpan(buffer, offset, count)); + + public override void Write(ReadOnlySpan buffer) => WriteSpan(buffer); + + private unsafe void WriteSpan(ReadOnlySpan source) + { + if (_fileHandle.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if ((_access & FileAccess.Write) == 0) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + + int r = RandomAccess.WriteAtOffset(_fileHandle, source, _filePosition, _path); + Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); + _filePosition += r; + + UpdateLengthOnChangePosition(); + } } } From c1e5fcc33e65080c027ca6f7fceebcbd473b4654 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 2 Jul 2021 08:34:47 -0600 Subject: [PATCH 255/926] Resolve ILLink warnings in Microsoft.Extensions.Hosting (#55048) --- .../src/ILLink/ILLink.Suppressions.xml | 17 -------------- .../src/OptionsBuilderExtensions.cs | 23 +++++++------------ 2 files changed, 8 insertions(+), 32 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/ILLink/ILLink.Suppressions.xml diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/ILLink/ILLink.Suppressions.xml b/src/libraries/Microsoft.Extensions.Hosting/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 7e836fa7664..00000000000 --- a/src/libraries/Microsoft.Extensions.Hosting/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ILLink - IL2091 - member - F:Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.<>c__DisplayClass1_0`1.options - - - ILLink - IL2091 - member - M:Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.<>c__DisplayClass0_0`1.<ValidateOnStart>b__0(Microsoft.Extensions.DependencyInjection.ValidatorOptions,Microsoft.Extensions.Options.IOptionsMonitor{`0}) - - - diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs index 99d20caec83..94c8115d1a7 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs @@ -10,6 +10,8 @@ namespace Microsoft.Extensions.DependencyInjection /// /// Extension methods for adding configuration related options services to the DI container via . /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", + Justification = "Workaround for https://github.com/mono/linker/issues/1416. Outer method has been annotated with DynamicallyAccessedMembers.")] public static class OptionsBuilderExtensions { /// @@ -28,23 +30,14 @@ namespace Microsoft.Extensions.DependencyInjection optionsBuilder.Services.AddHostedService(); optionsBuilder.Services.AddOptions() - .Configure>((vo, options) => ValidateOnStartHelper(vo, options, optionsBuilder)); + .Configure>((vo, options) => + { + // This adds an action that resolves the options value to force evaluation + // We don't care about the result as duplicates are not important + vo.Validators[typeof(TOptions)] = () => options.Get(optionsBuilder.Name); + }); return optionsBuilder; } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", - Justification = "Workaround for https://github.com/mono/linker/issues/1416. Outer method has been annotated with DynamicallyAccessedMembers.")] - private static void ValidateOnStartHelper(ValidatorOptions vo, IOptionsMonitor options, OptionsBuilder optionsBuilder) - where TOptions : class - { - // This adds an action that resolves the options value to force evaluation - // We don't care about the result as duplicates are not important - vo.Validators[typeof(TOptions)] = () => GetOptionsMethod(options, optionsBuilder); - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", - Justification = "Workaround for https://github.com/mono/linker/issues/1416. Outer method has been annotated with DynamicallyAccessedMembers.")] - private static void GetOptionsMethod(IOptionsMonitor options, OptionsBuilder optionsBuilder) where TOptions : class => options.Get(optionsBuilder.Name); } } From bbb7d90f4242de6fc3259a95ac30214d5180bdff Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 2 Jul 2021 11:27:40 -0400 Subject: [PATCH 256/926] [mono][jit] Allow inlining of methods which make calls to ThrowHelper methods. (#55061) Calls to ThrowHelper are very common in perf sensitive code like indexers/iterators. --- src/mono/mono/mini/method-to-ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index dc0ad9462c5..c0052d7a9c7 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -7895,7 +7895,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } /* Common call */ - if (!(cfg->opt & MONO_OPT_AGGRESSIVE_INLINING) && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) && !(cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING)) + if (!(cfg->opt & MONO_OPT_AGGRESSIVE_INLINING) && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) && !(cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) && !method_does_not_return (cmethod)) INLINE_FAILURE ("call"); common_call = TRUE; From b26d811385f54084ce1c64591ba70721df96a7b9 Mon Sep 17 00:00:00 2001 From: Anirudh Agnihotry Date: Fri, 2 Jul 2021 08:43:52 -0700 Subject: [PATCH 257/926] Add Csproj pack infra for packages with ref and runtime assemblies and symbols. (#54250) * add infra for adding ref and runtime assemblies delete the project add a comment move the warning disable to cenral location moving props/targets to packaging.props and packaging.targets use intellisense package for ref and runtime xml files add comments, flag for ref assemblies, remove xml from runtimes, use outputItem to get the reference assembly addressing some more feedback remove ref assemblies add suppression file and update package validatio * move PackageValidationBaselineVersion to packaging.targets * update the package validation version --- NuGet.config | 2 + eng/Versions.props | 2 +- eng/packaging.props | 10 +++-- eng/packaging.targets | 37 +++++++++++++++++++ global.json | 2 +- src/libraries/Directory.Build.targets | 19 ---------- .../src/CompatibilitySuppressions.xml | 16 ++++++++ ...icrosoft.Extensions.DependencyModel.csproj | 2 - ...ensions.HostFactoryResolver.Sources.csproj | 2 +- .../src/System.IO.Hashing.csproj | 2 +- .../pkg/System.Windows.Extensions.pkgproj | 12 ------ .../src/CompatibilitySuppressions.xml | 12 ++++++ .../src/System.Windows.Extensions.csproj | 1 + 13 files changed, 78 insertions(+), 41 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Windows.Extensions/pkg/System.Windows.Extensions.pkgproj create mode 100644 src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml diff --git a/NuGet.config b/NuGet.config index 7812fc325d5..f39fab9df8e 100644 --- a/NuGet.config +++ b/NuGet.config @@ -17,6 +17,8 @@ + + diff --git a/eng/Versions.props b/eng/Versions.props index 765b461a291..1c8ae0b1d33 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -64,7 +64,7 @@ 6.0.0-beta.21330.2 6.0.0-beta.21330.2 - 5.9.0-preview.2 + 6.0.0-preview.1.102 6.0.0-alpha.1.20612.4 6.0.0-preview.7.21321.2 diff --git a/eng/packaging.props b/eng/packaging.props index 534d3ddcf32..9e992bbf7a3 100644 --- a/eng/packaging.props +++ b/eng/packaging.props @@ -13,6 +13,7 @@ 6.0.0 true true + .dll;.exe;.winmd;.json;.pri; @@ -29,10 +30,6 @@ - - $(MSBuildThisFileDirectory)useSharedDesignerContext.txt - - @(NETCoreApp30RIDs) + + + + + diff --git a/eng/packaging.targets b/eng/packaging.targets index 1f19f57c98a..d6344a0b666 100644 --- a/eng/packaging.targets +++ b/eng/packaging.targets @@ -1,4 +1,15 @@ + + + $(TargetsForTfmSpecificContentInPackage);AddRuntimeSpecificAssemblies;LibIntellisenseDocs + $(TargetsForTfmSpecificDebugSymbolsInPackage);AddRuntimeDebugSymbolsWithTfm + false + + true + $(MSBuildThisFileDirectory)useSharedDesignerContext.txt + $([MSBuild]::Subtract($(MajorVersion), 1)).0.0 + + + + + $([System.IO.Path]::GetDirectoryName($(TargetPath)))\$(TargetName).pdb + + + + + /runtimes/$(_runtimeOS)/lib/$(TargetFrameworkWithoutSuffix)/$(TargetName).pdb + $(TargetFrameworkWithoutSuffix) + + + diff --git a/global.json b/global.json index 9650ac9e9ad..ca21d08dc3b 100644 --- a/global.json +++ b/global.json @@ -13,7 +13,7 @@ }, "msbuild-sdks": { "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21330.3", - "Microsoft.DotNet.PackageValidation": "1.0.0-preview.6.21274.7", + "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21330.2", "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21330.2", "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3", diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index b10705260f4..b330933c280 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -17,7 +17,6 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'testhost', '$(NetCoreAppCurrentBuildSettings)')) $([MSBuild]::NormalizeDirectory('$(NetCoreAppCurrentTestHostPath)', 'shared', '$(MicrosoftNetCoreAppFrameworkName)', '$(ProductVersion)')) - $(TargetsForTfmSpecificContentInPackage);LibIntellisenseDocs $(PackageOutputPath) $(NoWarn);nullable @@ -145,11 +144,6 @@ - - $([MSBuild]::Subtract($(MajorVersion), 1)).0.0 - true - - @@ -235,11 +229,6 @@ - - false - true - - - - - .dll;.exe;.winmd;.json;.pri; - .pdb;.mdb;$(AllowedOutputExtensionsInPackageBuildOutputFolder) - - - IncludeAnalyzersInPackage;$(BeforePack) diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..d088c3cafe5 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml @@ -0,0 +1,16 @@ + + + + + PKV006 + .NETFramework,Version=v4.5.1 + + + PKV006 + .NETStandard,Version=v1.3 + + + PKV006 + .NETStandard,Version=v1.6 + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj index b7ccc5b0db1..81b60b71352 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj @@ -2,8 +2,6 @@ netstandard2.0;net461 true - - $(NoWarn);PKV006 Abstractions for reading `.deps` files. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj index 43edfb9491a..dff90383f96 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj @@ -10,7 +10,7 @@ true - false + true Internal package for sharing Microsoft.Extensions.Hosting.HostFactoryResolver type. diff --git a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj index 3ef2ef309ec..acfa86bb798 100644 --- a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj +++ b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj @@ -5,7 +5,7 @@ $(NetCoreAppCurrent);netstandard2.0;net461 true - false + true Provides non-cryptographic hash algorithms, such as CRC-32. Commonly Used Types: diff --git a/src/libraries/System.Windows.Extensions/pkg/System.Windows.Extensions.pkgproj b/src/libraries/System.Windows.Extensions/pkg/System.Windows.Extensions.pkgproj deleted file mode 100644 index 240cfdfab1b..00000000000 --- a/src/libraries/System.Windows.Extensions/pkg/System.Windows.Extensions.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - netcoreapp3.1 - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml b/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..c6379c3e4ac --- /dev/null +++ b/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETCoreapp,Version=v3.0 + + + PKV007 + .NETCoreapp,Version=v3.0-win + + \ No newline at end of file diff --git a/src/libraries/System.Windows.Extensions/src/System.Windows.Extensions.csproj b/src/libraries/System.Windows.Extensions/src/System.Windows.Extensions.csproj index e995c093bb7..276e9865a70 100644 --- a/src/libraries/System.Windows.Extensions/src/System.Windows.Extensions.csproj +++ b/src/libraries/System.Windows.Extensions/src/System.Windows.Extensions.csproj @@ -2,6 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1 + true From 5847f6d156ea92f33979c2765ec4c1c05214ca68 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 2 Jul 2021 12:24:46 -0400 Subject: [PATCH 258/926] Annotate System.Reflection.Context for nullability (#54938) The impact here is almost entirely internal; the ref only gains two question marks. --- .../ref/System.Reflection.Context.cs | 4 +- .../src/System.Reflection.Context.csproj | 2 - .../Reflection/Context/CollectionServices.cs | 4 +- .../Context/Custom/AttributeUtils.cs | 4 +- .../Reflection/Context/Custom/CustomType.cs | 29 ++++----- .../CustomReflectionContext.Projector.cs | 57 +++++++++++------ .../Context/CustomReflectionContext.cs | 10 +-- .../Context/Delegation/DelegatingAssembly.cs | 24 +++---- .../Delegation/DelegatingConstructorInfo.cs | 12 ++-- .../Context/Delegation/DelegatingEventInfo.cs | 18 +++--- .../DelegatingExceptionHandlingClause.cs | 2 +- .../Context/Delegation/DelegatingFieldInfo.cs | 14 ++--- .../Delegation/DelegatingMethodBody.cs | 4 +- .../Delegation/DelegatingMethodInfo.cs | 12 ++-- .../Context/Delegation/DelegatingModule.cs | 16 ++--- .../Delegation/DelegatingParameterInfo.cs | 6 +- .../Delegation/DelegatingPropertyInfo.cs | 22 +++---- .../Context/Delegation/DelegatingType.cs | 45 ++++++------- .../Context/Projection/ProjectingAssembly.cs | 19 +++--- .../Projection/ProjectingConstructorInfo.cs | 12 ++-- .../Context/Projection/ProjectingEventInfo.cs | 19 +++--- .../ProjectingExceptionHandlingClause.cs | 2 +- .../Context/Projection/ProjectingFieldInfo.cs | 11 ++-- .../ProjectingManifestResourceInfo.cs | 2 +- .../Projection/ProjectingMethodInfo.cs | 15 +++-- .../Context/Projection/ProjectingModule.cs | 21 +++---- .../Projection/ProjectingParameterInfo.cs | 3 +- .../Projection/ProjectingPropertyInfo.cs | 11 ++-- .../Context/Projection/ProjectingType.cs | 41 ++++++------ .../Context/Projection/Projector.cs | 63 ++++++++++++------- .../Context/Virtual/InheritedMethodInfo.cs | 11 ++-- .../Context/Virtual/InheritedPropertyInfo.cs | 15 ++--- .../Context/Virtual/VirtualMethodBase.cs | 12 ++-- .../Context/Virtual/VirtualParameter.cs | 5 +- ...alPropertyBase.FuncPropertyAccessorBase.cs | 2 +- .../VirtualPropertyBase.PropertySetterBase.cs | 2 +- .../Context/Virtual/VirtualPropertyBase.cs | 36 +++++------ .../VirtualPropertyInfo.PropertyGetter.cs | 8 +-- .../VirtualPropertyInfo.PropertySetter.cs | 10 +-- .../Context/Virtual/VirtualPropertyInfo.cs | 20 +++--- .../Context/Virtual/VirtualReturnParameter.cs | 2 +- 41 files changed, 331 insertions(+), 296 deletions(-) diff --git a/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.cs b/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.cs index f3b42d67b82..c639433b61c 100644 --- a/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.cs +++ b/src/libraries/System.Reflection.Context/ref/System.Reflection.Context.cs @@ -11,8 +11,8 @@ namespace System.Reflection.Context protected CustomReflectionContext() { } protected CustomReflectionContext(System.Reflection.ReflectionContext source) { } protected virtual System.Collections.Generic.IEnumerable AddProperties(System.Type type) { throw null; } - protected System.Reflection.PropertyInfo CreateProperty(System.Type propertyType, string name, System.Func? getter, System.Action? setter) { throw null; } - protected System.Reflection.PropertyInfo CreateProperty(System.Type propertyType, string name, System.Func? getter, System.Action? setter, System.Collections.Generic.IEnumerable? propertyCustomAttributes, System.Collections.Generic.IEnumerable? getterCustomAttributes, System.Collections.Generic.IEnumerable? setterCustomAttributes) { throw null; } + protected System.Reflection.PropertyInfo CreateProperty(System.Type propertyType, string name, System.Func? getter, System.Action? setter) { throw null; } + protected System.Reflection.PropertyInfo CreateProperty(System.Type propertyType, string name, System.Func? getter, System.Action? setter, System.Collections.Generic.IEnumerable? propertyCustomAttributes, System.Collections.Generic.IEnumerable? getterCustomAttributes, System.Collections.Generic.IEnumerable? setterCustomAttributes) { throw null; } protected virtual System.Collections.Generic.IEnumerable GetCustomAttributes(System.Reflection.MemberInfo member, System.Collections.Generic.IEnumerable declaredAttributes) { throw null; } protected virtual System.Collections.Generic.IEnumerable GetCustomAttributes(System.Reflection.ParameterInfo parameter, System.Collections.Generic.IEnumerable declaredAttributes) { throw null; } public override System.Reflection.Assembly MapAssembly(System.Reflection.Assembly assembly) { throw null; } diff --git a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj index d4270565e93..4d30b81b51d 100644 --- a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj +++ b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj @@ -6,8 +6,6 @@ - - annotations SR.PlatformNotSupported_ReflectionContext diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CollectionServices.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CollectionServices.cs index eac5ff07ff9..a950033ccf1 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CollectionServices.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CollectionServices.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Context return Array.Empty(); } - public static bool CompareArrays(T[] left, T[] right) + public static bool CompareArrays(T[] left, T[] right) where T : notnull { if (left.Length != right.Length) return false; @@ -26,7 +26,7 @@ namespace System.Reflection.Context return true; } - public static int GetArrayHashCode(T[] array) + public static int GetArrayHashCode(T[] array) where T : notnull { int hashcode = 0; foreach (T t in array) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs index 1b7e20a4c56..44be895c7f0 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs @@ -15,7 +15,7 @@ namespace System.Reflection.Context.Custom if (!inherit) return CollectionServices.IEnumerableToArray(attributes, attributeFilterType); - CustomType baseMember = type.BaseType as CustomType; + CustomType? baseMember = type.BaseType as CustomType; if (baseMember == null) return CollectionServices.IEnumerableToArray(attributes, attributeFilterType); @@ -55,7 +55,7 @@ namespace System.Reflection.Context.Custom if (!inherit) return CollectionServices.IEnumerableToArray(attributes, attributeFilterType); - CustomMethodInfo baseMember = method.GetBaseDefinition() as CustomMethodInfo; + CustomMethodInfo? baseMember = method.GetBaseDefinition() as CustomMethodInfo; if (baseMember == null || baseMember.Equals(method)) return CollectionServices.IEnumerableToArray(attributes, attributeFilterType); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs index ea2d3ae2ee5..5a048fcf91c 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Projection; using System.Reflection.Context.Virtual; @@ -10,7 +11,7 @@ namespace System.Reflection.Context.Custom { internal sealed class CustomType : ProjectingType { - private IEnumerable _newProperties; + private IEnumerable? _newProperties; public CustomType(Type template, CustomReflectionContext context) : base(template, context.Projector) @@ -37,9 +38,9 @@ namespace System.Reflection.Context.Custom return AttributeUtils.IsDefined(this, attributeType, inherit); } - public override bool IsInstanceOfType(object o) + public override bool IsInstanceOfType([NotNullWhen(true)] object? o) { - Type objectType = ReflectionContext.GetTypeForObject(o); + Type objectType = ReflectionContext.GetTypeForObject(o!); return IsAssignableFrom(objectType); } @@ -64,7 +65,7 @@ namespace System.Reflection.Context.Custom // adding new properties declared on base types if (!getDeclaredOnly) { - CustomType baseType = BaseType as CustomType; + CustomType? baseType = BaseType as CustomType; while (baseType != null) { IEnumerable newProperties = baseType.NewProperties; @@ -81,9 +82,9 @@ namespace System.Reflection.Context.Custom return results.ToArray(); } - protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) { - PropertyInfo property = base.GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers); + PropertyInfo? property = base.GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers); bool getIgnoreCase = (bindingAttr & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase; bool getDeclaredOnly = (bindingAttr & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly; @@ -111,7 +112,7 @@ namespace System.Reflection.Context.Custom StringComparison comparison = getIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - CustomType type = this; + CustomType? type = this; foreach (PropertyInfo newDeclaredProperty in type.NewProperties) { if (string.Equals(newDeclaredProperty.Name, name, comparison)) @@ -163,7 +164,7 @@ namespace System.Reflection.Context.Custom // adding new methods declared on base types if (!getDeclaredOnly) { - CustomType baseType = BaseType as CustomType; + CustomType? baseType = BaseType as CustomType; while (baseType != null) { // We shouldn't add a base type method directly on a subtype. @@ -181,9 +182,9 @@ namespace System.Reflection.Context.Custom return results.ToArray(); } - protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) { - MethodInfo method = base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers); + MethodInfo? method = base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers); bool getIgnoreCase = (bindingAttr & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase; bool getDeclaredOnly = (bindingAttr & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly; @@ -226,7 +227,7 @@ namespace System.Reflection.Context.Custom { if (string.Equals(newDeclaredProperty.Name, targetPropertyName, comparison)) { - MethodInfo accessor = getPropertyGetter ? newDeclaredProperty.GetGetMethod() : newDeclaredProperty.GetSetMethod(); + MethodInfo? accessor = getPropertyGetter ? newDeclaredProperty.GetGetMethod() : newDeclaredProperty.GetSetMethod(); if (accessor != null) matchingMethods.Add(accessor); } @@ -235,7 +236,7 @@ namespace System.Reflection.Context.Custom // adding new methods declared on base types if (!getDeclaredOnly) { - CustomType baseType = BaseType as CustomType; + CustomType? baseType = BaseType as CustomType; while (baseType != null) { @@ -247,7 +248,7 @@ namespace System.Reflection.Context.Custom { PropertyInfo inheritedProperty = new InheritedPropertyInfo(newBaseProperty, this); - MethodInfo accessor = getPropertyGetter ? inheritedProperty.GetGetMethod() : inheritedProperty.GetSetMethod(); + MethodInfo? accessor = getPropertyGetter ? inheritedProperty.GetGetMethod() : inheritedProperty.GetSetMethod(); if (accessor != null) matchingMethods.Add(accessor); } @@ -278,7 +279,7 @@ namespace System.Reflection.Context.Custom if (binder == null) binder = Type.DefaultBinder; - return (MethodInfo)binder.SelectMethod(bindingAttr, matchingMethods.ToArray(), types, modifiers); + return (MethodInfo?)binder.SelectMethod(bindingAttr, matchingMethods.ToArray(), types, modifiers); } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs index 95e33d7173f..322a25c75c9 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Custom; using System.Reflection.Context.Projection; @@ -45,7 +46,8 @@ namespace System.Reflection.Context return value; } - public override TypeInfo ProjectType(Type value) + [return: NotNullIfNotNull("value")] + public override TypeInfo? ProjectType(Type? value) { if (value == null) return null; @@ -55,7 +57,8 @@ namespace System.Reflection.Context return new CustomType(value, ReflectionContext); } - public override Assembly ProjectAssembly(Assembly value) + [return: NotNullIfNotNull("value")] + public override Assembly? ProjectAssembly(Assembly? value) { if (value == null) return null; @@ -65,7 +68,8 @@ namespace System.Reflection.Context return new CustomAssembly(value, ReflectionContext); } - public override Module ProjectModule(Module value) + [return: NotNullIfNotNull("value")] + public override Module? ProjectModule(Module? value) { if (value == null) return null; @@ -75,7 +79,8 @@ namespace System.Reflection.Context return new CustomModule(value, ReflectionContext); } - public override FieldInfo ProjectField(FieldInfo value) + [return: NotNullIfNotNull("value")] + public override FieldInfo? ProjectField(FieldInfo? value) { if (value == null) return null; @@ -85,7 +90,8 @@ namespace System.Reflection.Context return new CustomFieldInfo(value, ReflectionContext); } - public override EventInfo ProjectEvent(EventInfo value) + [return: NotNullIfNotNull("value")] + public override EventInfo? ProjectEvent(EventInfo? value) { if (value == null) return null; @@ -95,7 +101,8 @@ namespace System.Reflection.Context return new CustomEventInfo(value, ReflectionContext); } - public override ConstructorInfo ProjectConstructor(ConstructorInfo value) + [return: NotNullIfNotNull("value")] + public override ConstructorInfo? ProjectConstructor(ConstructorInfo? value) { if (value == null) return null; @@ -105,7 +112,8 @@ namespace System.Reflection.Context return new CustomConstructorInfo(value, ReflectionContext); } - public override MethodInfo ProjectMethod(MethodInfo value) + [return: NotNullIfNotNull("value")] + public override MethodInfo? ProjectMethod(MethodInfo? value) { if (value == null) return null; @@ -115,23 +123,25 @@ namespace System.Reflection.Context return new CustomMethodInfo(value, ReflectionContext); } - public override MethodBase ProjectMethodBase(MethodBase value) + [return: NotNullIfNotNull("value")] + public override MethodBase? ProjectMethodBase(MethodBase? value) { if (value == null) return null; - MethodInfo method = value as MethodInfo; + MethodInfo? method = value as MethodInfo; if (method != null) return ProjectMethod(method); - ConstructorInfo constructor = value as ConstructorInfo; + ConstructorInfo? constructor = value as ConstructorInfo; if (constructor != null) return ProjectConstructor(constructor); throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidMethodType, value.GetType())); } - public override PropertyInfo ProjectProperty(PropertyInfo value) + [return: NotNullIfNotNull("value")] + public override PropertyInfo? ProjectProperty(PropertyInfo? value) { if (value == null) return null; @@ -141,7 +151,8 @@ namespace System.Reflection.Context return new CustomPropertyInfo(value, ReflectionContext); } - public override ParameterInfo ProjectParameter(ParameterInfo value) + [return: NotNullIfNotNull("value")] + public override ParameterInfo? ProjectParameter(ParameterInfo? value) { if (value == null) return null; @@ -151,7 +162,8 @@ namespace System.Reflection.Context return new CustomParameterInfo(value, ReflectionContext); } - public override MethodBody ProjectMethodBody(MethodBody value) + [return: NotNullIfNotNull("value")] + public override MethodBody? ProjectMethodBody(MethodBody? value) { if (value == null) return null; @@ -161,7 +173,8 @@ namespace System.Reflection.Context return new ProjectingMethodBody(value, this); } - public override LocalVariableInfo ProjectLocalVariable(LocalVariableInfo value) + [return: NotNullIfNotNull("value")] + public override LocalVariableInfo? ProjectLocalVariable(LocalVariableInfo? value) { if (value == null) return null; @@ -171,7 +184,8 @@ namespace System.Reflection.Context return new ProjectingLocalVariableInfo(value, this); } - public override ExceptionHandlingClause ProjectExceptionHandlingClause(ExceptionHandlingClause value) + [return: NotNullIfNotNull("value")] + public override ExceptionHandlingClause? ProjectExceptionHandlingClause(ExceptionHandlingClause? value) { if (value == null) return null; @@ -181,7 +195,8 @@ namespace System.Reflection.Context return new ProjectingExceptionHandlingClause(value, this); } - public override CustomAttributeData ProjectCustomAttributeData(CustomAttributeData value) + [return: NotNullIfNotNull("value")] + public override CustomAttributeData? ProjectCustomAttributeData(CustomAttributeData? value) { if (value == null) return null; @@ -191,7 +206,8 @@ namespace System.Reflection.Context return new ProjectingCustomAttributeData(value, this); } - public override ManifestResourceInfo ProjectManifestResource(ManifestResourceInfo value) + [return: NotNullIfNotNull("value")] + public override ManifestResourceInfo? ProjectManifestResource(ManifestResourceInfo? value) { if (value == null) return null; @@ -201,12 +217,13 @@ namespace System.Reflection.Context return new ProjectingManifestResourceInfo(value, this); } - public override MemberInfo ProjectMember(MemberInfo value) + [return: NotNullIfNotNull("value")] + public override MemberInfo? ProjectMember(MemberInfo? value) { if (value == null) return null; - MemberInfo output = null; + MemberInfo? output; switch (value.MemberType) { case MemberTypes.TypeInfo: @@ -243,7 +260,7 @@ namespace System.Reflection.Context public override CustomAttributeTypedArgument ProjectTypedArgument(CustomAttributeTypedArgument value) { - Type argumentType = ProjectType(value.ArgumentType); + Type? argumentType = ProjectType(value.ArgumentType); return new CustomAttributeTypedArgument(argumentType, value.Value); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs index 4ff6bef37c0..e4ed81281a4 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs @@ -66,8 +66,8 @@ namespace System.Reflection.Context protected PropertyInfo CreateProperty( Type propertyType, string name, - Func? getter, - Action? setter) + Func? getter, + Action? setter) { return new VirtualPropertyInfo( name, @@ -83,8 +83,8 @@ namespace System.Reflection.Context protected PropertyInfo CreateProperty( Type propertyType, string name, - Func? getter, - Action? setter, + Func? getter, + Action? setter, IEnumerable? propertyCustomAttributes, IEnumerable? getterCustomAttributes, IEnumerable? setterCustomAttributes) @@ -115,7 +115,7 @@ namespace System.Reflection.Context if (prop == null) throw new InvalidOperationException(SR.InvalidOperation_AddNullProperty); - VirtualPropertyBase vp = prop as VirtualPropertyBase; + VirtualPropertyBase? vp = prop as VirtualPropertyBase; if (vp == null || vp.ReflectionContext != this) throw new InvalidOperationException(SR.InvalidOperation_AddPropertyDifferentContext); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 7b86c7f71e9..990302be8c5 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -76,17 +76,17 @@ namespace System.Reflection.Context.Delegation [Obsolete] [RequiresAssemblyFiles] #endif - public override string CodeBase + public override string? CodeBase { get { return UnderlyingAssembly.CodeBase; } } - public override object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) + public override object? CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object[]? args, CultureInfo? culture, object[]? activationAttributes) { return UnderlyingAssembly.CreateInstance(typeName, ignoreCase, bindingAttr, binder, args, culture, activationAttributes); } - public override MethodInfo EntryPoint + public override MethodInfo? EntryPoint { get { return UnderlyingAssembly.EntryPoint; } } @@ -100,7 +100,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingAssembly.EscapedCodeBase; } } - public override string FullName + public override string? FullName { get { return UnderlyingAssembly.FullName; } } @@ -112,7 +112,7 @@ namespace System.Reflection.Context.Delegation #pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] - public override FileStream GetFile(string name) + public override FileStream? GetFile(string name) { return UnderlyingAssembly.GetFile(name); } @@ -135,7 +135,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetLoadedModules(getResourceModules); } - public override ManifestResourceInfo GetManifestResourceInfo(string resourceName) + public override ManifestResourceInfo? GetManifestResourceInfo(string resourceName) { return UnderlyingAssembly.GetManifestResourceInfo(resourceName); } @@ -145,17 +145,17 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetManifestResourceNames(); } - public override Stream GetManifestResourceStream(string name) + public override Stream? GetManifestResourceStream(string name) { return UnderlyingAssembly.GetManifestResourceStream(name); } - public override Stream GetManifestResourceStream(Type type, string name) + public override Stream? GetManifestResourceStream(Type type, string name) { return UnderlyingAssembly.GetManifestResourceStream(type, name); } - public override Module GetModule(string name) + public override Module? GetModule(string name) { return UnderlyingAssembly.GetModule(name); } @@ -185,12 +185,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingAssembly.GetSatelliteAssembly(culture); } - public override Assembly GetSatelliteAssembly(CultureInfo culture, Version version) + public override Assembly GetSatelliteAssembly(CultureInfo culture, Version? version) { return UnderlyingAssembly.GetSatelliteAssembly(culture, version); } - public override Type GetType(string name, bool throwOnError, bool ignoreCase) + public override Type? GetType(string name, bool throwOnError, bool ignoreCase) { return UnderlyingAssembly.GetType(name, throwOnError, ignoreCase); } @@ -223,7 +223,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingAssembly.IsDynamic; } } - public override Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) + public override Module LoadModule(string moduleName, byte[]? rawModule, byte[]? rawSymbolStore) { return UnderlyingAssembly.LoadModule(moduleName, rawModule, rawSymbolStore); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingConstructorInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingConstructorInfo.cs index 516aefaec69..c53b0a69572 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingConstructorInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingConstructorInfo.cs @@ -31,7 +31,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingConstructor.ContainsGenericParameters; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return UnderlyingConstructor.DeclaringType; } } @@ -81,7 +81,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingConstructor.Name; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return UnderlyingConstructor.ReflectedType; } } @@ -108,7 +108,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingConstructor.GetGenericArguments(); } - public override MethodBody GetMethodBody() + public override MethodBody? GetMethodBody() { return UnderlyingConstructor.GetMethodBody(); } @@ -123,12 +123,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingConstructor.GetParameters(); } - public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { return UnderlyingConstructor.Invoke(invokeAttr, binder, parameters, culture); } - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { return UnderlyingConstructor.Invoke(obj, invokeAttr, binder, parameters, culture); } @@ -138,7 +138,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingConstructor.IsDefined(attributeType, inherit); } - public override string ToString() + public override string? ToString() { return UnderlyingConstructor.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingEventInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingEventInfo.cs index 7fa78f42c16..32a665481a7 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingEventInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingEventInfo.cs @@ -20,12 +20,12 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingEvent.Attributes; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return UnderlyingEvent.DeclaringType; } } - public override Type EventHandlerType + public override Type? EventHandlerType { get { return UnderlyingEvent.EventHandlerType; } } @@ -50,19 +50,19 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingEvent.Name; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return UnderlyingEvent.ReflectedType; } } public EventInfo UnderlyingEvent { get; } - public override void AddEventHandler(object target, Delegate handler) + public override void AddEventHandler(object? target, Delegate? handler) { UnderlyingEvent.AddEventHandler(target, handler); } - public override MethodInfo GetAddMethod(bool nonPublic) + public override MethodInfo? GetAddMethod(bool nonPublic) { return UnderlyingEvent.GetAddMethod(nonPublic); } @@ -72,12 +72,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingEvent.GetOtherMethods(nonPublic); } - public override MethodInfo GetRaiseMethod(bool nonPublic) + public override MethodInfo? GetRaiseMethod(bool nonPublic) { return UnderlyingEvent.GetRaiseMethod(nonPublic); } - public override MethodInfo GetRemoveMethod(bool nonPublic) + public override MethodInfo? GetRemoveMethod(bool nonPublic) { return UnderlyingEvent.GetRemoveMethod(nonPublic); } @@ -102,12 +102,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingEvent.IsDefined(attributeType, inherit); } - public override void RemoveEventHandler(object target, Delegate handler) + public override void RemoveEventHandler(object? target, Delegate? handler) { UnderlyingEvent.RemoveEventHandler(target, handler); } - public override string ToString() + public override string? ToString() { return UnderlyingEvent.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingExceptionHandlingClause.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingExceptionHandlingClause.cs index 2d0585a5f91..123855af46a 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingExceptionHandlingClause.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingExceptionHandlingClause.cs @@ -16,7 +16,7 @@ namespace System.Reflection.Context.Delegation _clause = clause; } - public override Type CatchType + public override Type? CatchType { get { return _clause.CatchType; } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingFieldInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingFieldInfo.cs index 042e8058b57..6136dd55a38 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingFieldInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingFieldInfo.cs @@ -21,7 +21,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingField.Attributes; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return UnderlyingField.DeclaringType; } } @@ -66,7 +66,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingField.Name; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return UnderlyingField.ReflectedType; } } @@ -93,7 +93,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingField.GetOptionalCustomModifiers(); } - public override object GetRawConstantValue() + public override object? GetRawConstantValue() { return UnderlyingField.GetRawConstantValue(); } @@ -103,12 +103,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingField.GetRequiredCustomModifiers(); } - public override object GetValue(object obj) + public override object? GetValue(object? obj) { return UnderlyingField.GetValue(obj); } - public override object GetValueDirect(TypedReference obj) + public override object? GetValueDirect(TypedReference obj) { return UnderlyingField.GetValueDirect(obj); } @@ -118,7 +118,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingField.IsDefined(attributeType, inherit); } - public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) + public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) { UnderlyingField.SetValue(obj, value, invokeAttr, binder, culture); } @@ -128,7 +128,7 @@ namespace System.Reflection.Context.Delegation UnderlyingField.SetValueDirect(obj, value); } - public override string ToString() + public override string? ToString() { return UnderlyingField.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodBody.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodBody.cs index d1810b1e6d6..18e9573d2c5 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodBody.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodBody.cs @@ -42,12 +42,12 @@ namespace System.Reflection.Context.Delegation get { return _body.MaxStackSize; } } - public override byte[] GetILAsByteArray() + public override byte[]? GetILAsByteArray() { return _body.GetILAsByteArray(); } - public override string ToString() + public override string? ToString() { return _body.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodInfo.cs index 2b7fbee4f5e..1f4fd838c35 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingMethodInfo.cs @@ -32,7 +32,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingMethod.ContainsGenericParameters; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return UnderlyingMethod.DeclaringType; } } @@ -82,7 +82,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingMethod.Name; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return UnderlyingMethod.ReflectedType; } } @@ -134,7 +134,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingMethod.GetGenericMethodDefinition(); } - public override MethodBody GetMethodBody() + public override MethodBody? GetMethodBody() { return UnderlyingMethod.GetMethodBody(); } @@ -149,7 +149,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingMethod.GetParameters(); } - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { return UnderlyingMethod.Invoke(obj, invokeAttr, binder, parameters, culture); } @@ -169,12 +169,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingMethod.CreateDelegate(delegateType); } - public override Delegate CreateDelegate(Type delegateType, object target) + public override Delegate CreateDelegate(Type delegateType, object? target) { return UnderlyingMethod.CreateDelegate(delegateType, target); } - public override string ToString() + public override string? ToString() { return UnderlyingMethod.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingModule.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingModule.cs index aa98340a318..c1f11fcc4ba 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingModule.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingModule.cs @@ -52,7 +52,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingModule.ScopeName; } } - public override Type[] FindTypes(TypeFilter filter, object filterCriteria) + public override Type[] FindTypes(TypeFilter? filter, object? filterCriteria) { return UnderlyingModule.FindTypes(filter, filterCriteria); } @@ -72,7 +72,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingModule.GetCustomAttributesData(); } - public override FieldInfo GetField(string name, BindingFlags bindingAttr) + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return UnderlyingModule.GetField(name, bindingAttr); } @@ -82,7 +82,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingModule.GetFields(bindingFlags); } - protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) { if (types == null) { @@ -107,7 +107,7 @@ namespace System.Reflection.Context.Delegation // return UnderlyingModule.GetSignerCertificate(); //} - public override Type GetType(string className, bool throwOnError, bool ignoreCase) + public override Type? GetType(string className, bool throwOnError, bool ignoreCase) { return UnderlyingModule.GetType(className, throwOnError, ignoreCase); } @@ -127,17 +127,17 @@ namespace System.Reflection.Context.Delegation return UnderlyingModule.IsResource(); } - public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override FieldInfo? ResolveField(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { return UnderlyingModule.ResolveField(metadataToken, genericTypeArguments, genericMethodArguments); } - public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override MemberInfo? ResolveMember(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { return UnderlyingModule.ResolveMember(metadataToken, genericTypeArguments, genericMethodArguments); } - public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override MethodBase? ResolveMethod(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { return UnderlyingModule.ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments); } @@ -152,7 +152,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingModule.ResolveString(metadataToken); } - public override Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override Type ResolveType(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { return UnderlyingModule.ResolveType(metadataToken, genericTypeArguments, genericMethodArguments); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingParameterInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingParameterInfo.cs index 8f2bf3317f4..cd9c27b0e86 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingParameterInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingParameterInfo.cs @@ -20,7 +20,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingParameter.Attributes; } } - public override object DefaultValue + public override object? DefaultValue { get { return UnderlyingParameter.DefaultValue; } } @@ -35,7 +35,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingParameter.MetadataToken; } } - public override string Name + public override string? Name { get { return UnderlyingParameter.Name; } } @@ -50,7 +50,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingParameter.Position; } } - public override object RawDefaultValue + public override object? RawDefaultValue { get { return UnderlyingParameter.RawDefaultValue; } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingPropertyInfo.cs index 3aaab34993a..d2816b98186 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingPropertyInfo.cs @@ -31,7 +31,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingProperty.CanWrite; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return UnderlyingProperty.DeclaringType; } } @@ -56,7 +56,7 @@ namespace System.Reflection.Context.Delegation get { return UnderlyingProperty.PropertyType; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return UnderlyingProperty.ReflectedType; } } @@ -68,7 +68,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingProperty.GetAccessors(nonPublic); } - public override MethodInfo GetGetMethod(bool nonPublic) + public override MethodInfo? GetGetMethod(bool nonPublic) { return UnderlyingProperty.GetGetMethod(nonPublic); } @@ -78,27 +78,27 @@ namespace System.Reflection.Context.Delegation return UnderlyingProperty.GetIndexParameters(); } - public override MethodInfo GetSetMethod(bool nonPublic) + public override MethodInfo? GetSetMethod(bool nonPublic) { return UnderlyingProperty.GetSetMethod(nonPublic); } - public override object GetValue(object obj, object[] index) + public override object? GetValue(object? obj, object?[]? index) { return UnderlyingProperty.GetValue(obj, index); } - public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + public override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { return UnderlyingProperty.GetValue(obj, invokeAttr, binder, index, culture); } - public override void SetValue(object obj, object value, object[] index) + public override void SetValue(object? obj, object? value, object?[]? index) { UnderlyingProperty.SetValue(obj, value, index); } - public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { UnderlyingProperty.SetValue(obj, value, invokeAttr, binder, index, culture); } @@ -118,12 +118,12 @@ namespace System.Reflection.Context.Delegation return UnderlyingProperty.GetCustomAttributesData(); } - public override object GetConstantValue() + public override object? GetConstantValue() { return UnderlyingProperty.GetConstantValue(); } - public override object GetRawConstantValue() + public override object? GetRawConstantValue() { return UnderlyingProperty.GetRawConstantValue(); } @@ -143,7 +143,7 @@ namespace System.Reflection.Context.Delegation return UnderlyingProperty.IsDefined(attributeType, inherit); } - public override string ToString() + public override string? ToString() { return UnderlyingProperty.ToString(); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs index e107493ff0a..5b23ec03714 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; @@ -28,12 +29,12 @@ namespace System.Reflection.Context.Delegation get { return _typeInfo.Assembly; } } - public override string AssemblyQualifiedName + public override string? AssemblyQualifiedName { get { return _typeInfo.AssemblyQualifiedName; } } - public override Type BaseType + public override Type? BaseType { get { return _typeInfo.BaseType; } } @@ -48,17 +49,17 @@ namespace System.Reflection.Context.Delegation get { return _typeInfo.GenericParameterPosition; } } - public override MethodBase DeclaringMethod + public override MethodBase? DeclaringMethod { get { return _typeInfo.DeclaringMethod; } } - public override Type DeclaringType + public override Type? DeclaringType { get { return _typeInfo.DeclaringType; } } - public override string FullName + public override string? FullName { get { return _typeInfo.FullName; } } @@ -128,17 +129,17 @@ namespace System.Reflection.Context.Delegation get { return _typeInfo.Name; } } - public override string Namespace + public override string? Namespace { get { return _typeInfo.Namespace; } } - public override Type ReflectedType + public override Type? ReflectedType { get { return _typeInfo.ReflectedType; } } - public override StructLayoutAttribute StructLayoutAttribute + public override StructLayoutAttribute? StructLayoutAttribute { get { return _typeInfo.StructLayoutAttribute; } } @@ -173,7 +174,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetDefaultMembers(); } - public override string GetEnumName(object value) + public override string? GetEnumName(object value) { return _typeInfo.GetEnumName(value); } @@ -243,7 +244,7 @@ namespace System.Reflection.Context.Delegation return Type.GetTypeCode(_typeInfo); } - public override bool IsAssignableFrom(Type c) + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) { return _typeInfo.IsAssignableFrom(c); } @@ -263,12 +264,12 @@ namespace System.Reflection.Context.Delegation return _typeInfo.IsEnumDefined(value); } - public override bool IsEquivalentTo(Type other) + public override bool IsEquivalentTo([NotNullWhen(true)] Type? other) { return _typeInfo.IsEquivalentTo(other); } - public override bool IsInstanceOfType(object o) + public override bool IsInstanceOfType([NotNullWhen(true)] object? o) { return _typeInfo.IsInstanceOfType(o); } @@ -296,7 +297,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.Attributes; } - protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) { return _typeInfo.GetConstructor(bindingAttr, binder, callConvention, types, modifiers); } @@ -306,12 +307,12 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetConstructors(bindingAttr); } - public override Type GetElementType() + public override Type? GetElementType() { return _typeInfo.GetElementType(); } - public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) { return _typeInfo.GetEvent(name, bindingAttr); } @@ -321,7 +322,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetEvents(bindingAttr); } - public override FieldInfo GetField(string name, BindingFlags bindingAttr) + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _typeInfo.GetField(name, bindingAttr); } @@ -331,7 +332,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetFields(bindingAttr); } - public override Type GetInterface(string name, bool ignoreCase) + public override Type? GetInterface(string name, bool ignoreCase) { return _typeInfo.GetInterface(name, ignoreCase); } @@ -346,7 +347,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetMembers(bindingAttr); } - protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) { // Unfortunately we cannot directly call the protected GetMethodImpl on _typeInfo. return (types == null) ? @@ -359,7 +360,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetMethods(bindingAttr); } - public override Type GetNestedType(string name, BindingFlags bindingAttr) + public override Type? GetNestedType(string name, BindingFlags bindingAttr) { return _typeInfo.GetNestedType(name, bindingAttr); } @@ -374,10 +375,10 @@ namespace System.Reflection.Context.Delegation return _typeInfo.GetProperties(bindingAttr); } - protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) { // Unfortunately we cannot directly call the protected GetPropertyImpl on _typeInfo. - PropertyInfo property; + PropertyInfo? property; if (types == null) { @@ -409,7 +410,7 @@ namespace System.Reflection.Context.Delegation return _typeInfo.HasElementType; } - public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) { return _typeInfo.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingAssembly.cs index ba18ce41cd4..451bf328ba6 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingAssembly.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection.Context.Delegation; @@ -45,7 +46,7 @@ namespace System.Reflection.Context.Projection return base.IsDefined(attributeType, inherit); } - public override MethodInfo EntryPoint + public override MethodInfo? EntryPoint { get { return Projector.ProjectMethod(base.EntryPoint); } } @@ -60,12 +61,12 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetLoadedModules(getResourceModules), Projector.ProjectModule); } - public override ManifestResourceInfo GetManifestResourceInfo(string resourceName) + public override ManifestResourceInfo? GetManifestResourceInfo(string resourceName) { return Projector.ProjectManifestResource(base.GetManifestResourceInfo(resourceName)); } - public override Module GetModule(string name) + public override Module? GetModule(string name) { return Projector.ProjectModule(base.GetModule(name)); } @@ -80,12 +81,12 @@ namespace System.Reflection.Context.Projection return Projector.ProjectAssembly(base.GetSatelliteAssembly(culture)); } - public override Assembly GetSatelliteAssembly(CultureInfo culture, Version version) + public override Assembly GetSatelliteAssembly(CultureInfo culture, Version? version) { return Projector.ProjectAssembly(base.GetSatelliteAssembly(culture, version)); } - public override Type GetType(string name, bool throwOnError, bool ignoreCase) + public override Type? GetType(string name, bool throwOnError, bool ignoreCase) { return Projector.ProjectType(base.GetType(name, throwOnError, ignoreCase)); } @@ -95,16 +96,14 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetTypes(), Projector.ProjectType); } - public override Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) + public override Module LoadModule(string moduleName, byte[]? rawModule, byte[]? rawSymbolStore) { return Projector.ProjectModule(base.LoadModule(moduleName, rawModule, rawSymbolStore)); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as ProjectingAssembly; - - return other != null && + return o is ProjectingAssembly other && Projector == other.Projector && UnderlyingAssembly == other.UnderlyingAssembly; } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingConstructorInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingConstructorInfo.cs index f420d4bfd76..091de112cff 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingConstructorInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingConstructorInfo.cs @@ -20,7 +20,7 @@ namespace System.Reflection.Context.Projection public Projector Projector { get; } - public override Type DeclaringType + public override Type? DeclaringType { get { return Projector.ProjectType(base.DeclaringType); } } @@ -30,7 +30,7 @@ namespace System.Reflection.Context.Projection get { return Projector.ProjectModule(base.Module); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return Projector.ProjectType(base.ReflectedType); } } @@ -59,7 +59,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetGenericArguments(), Projector.ProjectType); } - public override MethodBody GetMethodBody() + public override MethodBody? GetMethodBody() { return Projector.ProjectMethodBody(base.GetMethodBody()); } @@ -69,11 +69,9 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetParameters(), Projector.ProjectParameter); } - public override bool Equals(object o) + public override bool Equals(object? o) { - ProjectingConstructorInfo other = o as ProjectingConstructorInfo; - - return other != null && + return o is ProjectingConstructorInfo other && Projector == other.Projector && UnderlyingConstructor.Equals(other.UnderlyingConstructor); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingEventInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingEventInfo.cs index 628fc159c25..c50c54fac6c 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingEventInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingEventInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -20,12 +21,12 @@ namespace System.Reflection.Context.Projection public Projector Projector { get; } - public override Type DeclaringType + public override Type? DeclaringType { get { return Projector.ProjectType(base.DeclaringType); } } - public override Type EventHandlerType + public override Type? EventHandlerType { get { return Projector.ProjectType(base.EventHandlerType); } } @@ -34,12 +35,12 @@ namespace System.Reflection.Context.Projection { get { return Projector.ProjectModule(base.Module); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return Projector.ProjectType(base.ReflectedType); } } - public override MethodInfo GetAddMethod(bool nonPublic) + public override MethodInfo? GetAddMethod(bool nonPublic) { return Projector.ProjectMethod(base.GetAddMethod(nonPublic)); } @@ -49,12 +50,12 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetOtherMethods(nonPublic), Projector.ProjectMethod); } - public override MethodInfo GetRaiseMethod(bool nonPublic) + public override MethodInfo? GetRaiseMethod(bool nonPublic) { return Projector.ProjectMethod(base.GetRaiseMethod(nonPublic)); } - public override MethodInfo GetRemoveMethod(bool nonPublic) + public override MethodInfo? GetRemoveMethod(bool nonPublic) { return Projector.ProjectMethod(base.GetRemoveMethod(nonPublic)); } @@ -78,11 +79,9 @@ namespace System.Reflection.Context.Projection return base.IsDefined(attributeType, inherit); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as ProjectingEventInfo; - - return other != null && + return o is ProjectingEventInfo other && Projector == other.Projector && UnderlyingEvent.Equals(other.UnderlyingEvent); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs index bcc74828a26..0f27f4d73e2 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs @@ -19,7 +19,7 @@ namespace System.Reflection.Context.Projection _projector = projector; } - public override Type CatchType + public override Type? CatchType { get { return _projector.ProjectType(base.CatchType); } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingFieldInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingFieldInfo.cs index 1fa50872ca2..579cebc2c00 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingFieldInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingFieldInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -20,7 +21,7 @@ namespace System.Reflection.Context.Projection public Projector Projector { get; } - public override Type DeclaringType + public override Type? DeclaringType { get { return Projector.ProjectType(base.DeclaringType); } } @@ -35,7 +36,7 @@ namespace System.Reflection.Context.Projection get { return Projector.ProjectModule(base.Module); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return Projector.ProjectType(base.ReflectedType); } } @@ -69,11 +70,9 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetRequiredCustomModifiers(), Projector.ProjectType); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as ProjectingFieldInfo; - - return other != null && + return o is ProjectingFieldInfo other && Projector == other.Projector && UnderlyingField.Equals(other.UnderlyingField); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs index 32b9b1831df..4001f0774ff 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs @@ -19,7 +19,7 @@ namespace System.Reflection.Context.Projection _projector = projector; } - public override Assembly ReferencedAssembly + public override Assembly? ReferencedAssembly { get { return _projector.ProjectAssembly(base.ReferencedAssembly); } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodInfo.cs index ad6aa9b5096..3d214a5eb5c 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -20,7 +21,7 @@ namespace System.Reflection.Context.Projection public Projector Projector { get; } - public override Type DeclaringType + public override Type? DeclaringType { get { return Projector.ProjectType(base.DeclaringType); } } @@ -30,7 +31,7 @@ namespace System.Reflection.Context.Projection get { return Projector.ProjectModule(base.Module); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return Projector.ProjectType(base.ReflectedType); } } @@ -93,7 +94,7 @@ namespace System.Reflection.Context.Projection return Projector.ProjectMethod(base.GetGenericMethodDefinition()); } - public override MethodBody GetMethodBody() + public override MethodBody? GetMethodBody() { return Projector.ProjectMethodBody(base.GetMethodBody()); } @@ -113,16 +114,14 @@ namespace System.Reflection.Context.Projection return base.CreateDelegate(Projector.Unproject(delegateType)); } - public override Delegate CreateDelegate(Type delegateType, object target) + public override Delegate CreateDelegate(Type delegateType, object? target) { return base.CreateDelegate(Projector.Unproject(delegateType), target); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as ProjectingMethodInfo; - - return other != null && + return o is ProjectingMethodInfo other && Projector == other.Projector && UnderlyingMethod.Equals(other.UnderlyingMethod); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingModule.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingModule.cs index 9867a1338b3..50d37e38f65 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingModule.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingModule.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -44,7 +45,7 @@ namespace System.Reflection.Context.Projection return base.IsDefined(attributeType, inherit); } - public override FieldInfo GetField(string name, BindingFlags bindingAttr) + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return Projector.ProjectField(base.GetField(name, bindingAttr)); } @@ -54,7 +55,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetFields(bindingFlags), Projector.ProjectField); } - protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) { types = Projector.Unproject(types); return Projector.ProjectMethod(base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers)); @@ -65,7 +66,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetMethods(bindingFlags), Projector.ProjectMethod); } - public override Type GetType(string className, bool throwOnError, bool ignoreCase) + public override Type? GetType(string className, bool throwOnError, bool ignoreCase) { return Projector.ProjectType(base.GetType(className, throwOnError, ignoreCase)); } @@ -75,7 +76,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetTypes(), Projector.ProjectType); } - public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override FieldInfo? ResolveField(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { genericTypeArguments = Projector.Unproject(genericTypeArguments); genericMethodArguments = Projector.Unproject(genericMethodArguments); @@ -83,7 +84,7 @@ namespace System.Reflection.Context.Projection return Projector.ProjectField(base.ResolveField(metadataToken, genericTypeArguments, genericMethodArguments)); } - public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override MemberInfo? ResolveMember(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { genericTypeArguments = Projector.Unproject(genericTypeArguments); genericMethodArguments = Projector.Unproject(genericMethodArguments); @@ -91,7 +92,7 @@ namespace System.Reflection.Context.Projection return Projector.ProjectMember(base.ResolveMember(metadataToken, genericTypeArguments, genericMethodArguments)); } - public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override MethodBase? ResolveMethod(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { genericTypeArguments = Projector.Unproject(genericTypeArguments); genericMethodArguments = Projector.Unproject(genericMethodArguments); @@ -99,7 +100,7 @@ namespace System.Reflection.Context.Projection return Projector.ProjectMethodBase(base.ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments)); } - public override Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + public override Type ResolveType(int metadataToken, Type[]? genericTypeArguments, Type[]? genericMethodArguments) { genericTypeArguments = Projector.Unproject(genericTypeArguments); genericMethodArguments = Projector.Unproject(genericMethodArguments); @@ -107,11 +108,9 @@ namespace System.Reflection.Context.Projection return Projector.ProjectType(base.ResolveType(metadataToken, genericTypeArguments, genericMethodArguments)); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as ProjectingModule; - - return other != null && + return o is ProjectingModule other && Projector == other.Projector && UnderlyingModule.Equals(other.UnderlyingModule); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingParameterInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingParameterInfo.cs index 72b25b775f8..ec0718b33bd 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingParameterInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingParameterInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -59,7 +60,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetRequiredCustomModifiers(), Projector.ProjectType); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { return o is ProjectingParameterInfo other && Projector == other.Projector && diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingPropertyInfo.cs index 3b68e6e0eff..a05f123157a 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingPropertyInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -20,7 +21,7 @@ namespace System.Reflection.Context.Projection public Projector Projector { get; } - public override Type DeclaringType + public override Type? DeclaringType { get { return Projector.ProjectType(base.DeclaringType); } } @@ -35,7 +36,7 @@ namespace System.Reflection.Context.Projection get { return Projector.ProjectType(base.PropertyType); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return Projector.ProjectType(base.ReflectedType); } } @@ -45,7 +46,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetAccessors(nonPublic), Projector.ProjectMethod); } - public override MethodInfo GetGetMethod(bool nonPublic) + public override MethodInfo? GetGetMethod(bool nonPublic) { return Projector.ProjectMethod(base.GetGetMethod(nonPublic)); } @@ -55,7 +56,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetIndexParameters(), Projector.ProjectParameter); } - public override MethodInfo GetSetMethod(bool nonPublic) + public override MethodInfo? GetSetMethod(bool nonPublic) { return Projector.ProjectMethod(base.GetSetMethod(nonPublic)); } @@ -89,7 +90,7 @@ namespace System.Reflection.Context.Projection return Projector.Project(base.GetRequiredCustomModifiers(), Projector.ProjectType); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { return o is ProjectingPropertyInfo other && Projector == other.Projector && diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs index b80a12f3393..019893177d4 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Projection @@ -30,17 +31,17 @@ namespace System.Reflection.Context.Projection get { return _projector.ProjectAssembly(base.Assembly); } } - public override Type BaseType + public override Type? BaseType { get { return _projector.ProjectType(base.BaseType); } } - public override MethodBase DeclaringMethod + public override MethodBase? DeclaringMethod { get { return _projector.ProjectMethodBase(base.DeclaringMethod); } } - public override Type DeclaringType + public override Type? DeclaringType { get { return _projector.ProjectType(base.DeclaringType); } } @@ -50,7 +51,7 @@ namespace System.Reflection.Context.Projection get { return _projector.ProjectModule(base.Module); } } - public override Type ReflectedType + public override Type? ReflectedType { get { return _projector.ProjectType(base.ReflectedType); } } @@ -134,9 +135,9 @@ namespace System.Reflection.Context.Projection return matchingMembers.ToArray(); } - public override bool IsAssignableFrom(Type c) + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) { - ProjectingType otherType = c as ProjectingType; + ProjectingType? otherType = c as ProjectingType; if (otherType == null || Projector != otherType.Projector) return false; @@ -150,18 +151,18 @@ namespace System.Reflection.Context.Projection return base.IsDefined(attributeType, inherit); } - public override bool IsEquivalentTo(Type other) + public override bool IsEquivalentTo([NotNullWhen(true)] Type? other) { - ProjectingType otherType = other as ProjectingType; + ProjectingType? otherType = other as ProjectingType; if (otherType == null || Projector != otherType.Projector) return false; return UnderlyingType.IsEquivalentTo(otherType.UnderlyingType); } - public override bool IsInstanceOfType(object o) + public override bool IsInstanceOfType([NotNullWhen(true)] object? o) { - Type objectType = _projector.ProjectType(o.GetType()); + Type? objectType = _projector.ProjectType(o?.GetType()); return IsAssignableFrom(objectType); } @@ -171,14 +172,14 @@ namespace System.Reflection.Context.Projection // and interfaces->objec. public override bool IsSubclassOf(Type c) { - ProjectingType otherType = c as ProjectingType; + ProjectingType? otherType = c as ProjectingType; if (otherType == null || Projector != otherType.Projector) return false; return UnderlyingType.IsSubclassOf(otherType.UnderlyingType); } - protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) { types = _projector.Unproject(types); @@ -190,12 +191,12 @@ namespace System.Reflection.Context.Projection return _projector.Project(base.GetConstructors(bindingAttr), _projector.ProjectConstructor); } - public override Type GetElementType() + public override Type? GetElementType() { return _projector.ProjectType(base.GetElementType()); } - public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) { return _projector.ProjectEvent(base.GetEvent(name, bindingAttr)); } @@ -205,7 +206,7 @@ namespace System.Reflection.Context.Projection return _projector.Project(base.GetEvents(bindingAttr), _projector.ProjectEvent); } - public override FieldInfo GetField(string name, BindingFlags bindingAttr) + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _projector.ProjectField(base.GetField(name, bindingAttr)); } @@ -215,7 +216,7 @@ namespace System.Reflection.Context.Projection return _projector.Project(base.GetFields(bindingAttr), _projector.ProjectField); } - public override Type GetInterface(string name, bool ignoreCase) + public override Type? GetInterface(string name, bool ignoreCase) { return _projector.ProjectType(base.GetInterface(name, ignoreCase)); } @@ -256,7 +257,7 @@ namespace System.Reflection.Context.Projection return members; } - protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) { types = _projector.Unproject(types); @@ -268,7 +269,7 @@ namespace System.Reflection.Context.Projection return _projector.Project(base.GetMethods(bindingAttr), _projector.ProjectMethod); } - public override Type GetNestedType(string name, BindingFlags bindingAttr) + public override Type? GetNestedType(string name, BindingFlags bindingAttr) { return _projector.ProjectType(base.GetNestedType(name, bindingAttr)); } @@ -283,7 +284,7 @@ namespace System.Reflection.Context.Projection return _projector.Project(base.GetProperties(bindingAttr), _projector.ProjectProperty); } - protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) { returnType = _projector.Unproject(returnType); types = _projector.Unproject(types); @@ -318,7 +319,7 @@ namespace System.Reflection.Context.Projection return _projector.ProjectType(base.MakeByRefType()); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { return o is ProjectingType other && Projector == other.Projector && diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/Projector.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/Projector.cs index 19b9a82d28f..8e4772e1906 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/Projector.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/Projector.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Reflection.Context.Projection { internal abstract class Projector { - public IList Project(IList values, Func project) + [return: NotNullIfNotNull("values")] + public IList? Project(IList? values, Func project) { if (values == null || values.Count == 0) return values; @@ -18,7 +20,8 @@ namespace System.Reflection.Context.Projection return Array.AsReadOnly(projected); } - public T[] Project(T[] values, Func project) + [return: NotNullIfNotNull("values")] + public T[]? Project(T[]? values, Func project) { if (values == null || values.Length == 0) return values; @@ -39,27 +42,44 @@ namespace System.Reflection.Context.Projection return value; } - public abstract TypeInfo ProjectType(Type value); - public abstract Assembly ProjectAssembly(Assembly value); - public abstract Module ProjectModule(Module value); - public abstract FieldInfo ProjectField(FieldInfo value); - public abstract EventInfo ProjectEvent(EventInfo value); - public abstract ConstructorInfo ProjectConstructor(ConstructorInfo value); - public abstract MethodInfo ProjectMethod(MethodInfo value); - public abstract MethodBase ProjectMethodBase(MethodBase value); - public abstract PropertyInfo ProjectProperty(PropertyInfo value); - public abstract ParameterInfo ProjectParameter(ParameterInfo value); - public abstract MethodBody ProjectMethodBody(MethodBody value); - public abstract LocalVariableInfo ProjectLocalVariable(LocalVariableInfo value); - public abstract ExceptionHandlingClause ProjectExceptionHandlingClause(ExceptionHandlingClause value); - public abstract CustomAttributeData ProjectCustomAttributeData(CustomAttributeData value); - public abstract ManifestResourceInfo ProjectManifestResource(ManifestResourceInfo value); + [return: NotNullIfNotNull("value")] + public abstract TypeInfo? ProjectType(Type? value); + [return: NotNullIfNotNull("value")] + public abstract Assembly? ProjectAssembly(Assembly? value); + [return: NotNullIfNotNull("value")] + public abstract Module? ProjectModule(Module? value); + [return: NotNullIfNotNull("value")] + public abstract FieldInfo? ProjectField(FieldInfo? value); + [return: NotNullIfNotNull("value")] + public abstract EventInfo? ProjectEvent(EventInfo? value); + [return: NotNullIfNotNull("value")] + public abstract ConstructorInfo? ProjectConstructor(ConstructorInfo? value); + [return: NotNullIfNotNull("value")] + public abstract MethodInfo? ProjectMethod(MethodInfo? value); + [return: NotNullIfNotNull("value")] + public abstract MethodBase? ProjectMethodBase(MethodBase? value); + [return: NotNullIfNotNull("value")] + public abstract PropertyInfo? ProjectProperty(PropertyInfo? value); + [return: NotNullIfNotNull("value")] + public abstract ParameterInfo? ProjectParameter(ParameterInfo? value); + [return: NotNullIfNotNull("value")] + public abstract MethodBody? ProjectMethodBody(MethodBody? value); + [return: NotNullIfNotNull("value")] + public abstract LocalVariableInfo? ProjectLocalVariable(LocalVariableInfo? value); + [return: NotNullIfNotNull("value")] + public abstract ExceptionHandlingClause? ProjectExceptionHandlingClause(ExceptionHandlingClause? value); + [return: NotNullIfNotNull("value")] + public abstract CustomAttributeData? ProjectCustomAttributeData(CustomAttributeData? value); + [return: NotNullIfNotNull("value")] + public abstract ManifestResourceInfo? ProjectManifestResource(ManifestResourceInfo? value); public abstract CustomAttributeTypedArgument ProjectTypedArgument(CustomAttributeTypedArgument value); public abstract CustomAttributeNamedArgument ProjectNamedArgument(CustomAttributeNamedArgument value); public abstract InterfaceMapping ProjectInterfaceMapping(InterfaceMapping value); - public abstract MemberInfo ProjectMember(MemberInfo value); + [return: NotNullIfNotNull("value")] + public abstract MemberInfo? ProjectMember(MemberInfo? value); - public Type[] Unproject(Type[] values) + [return: NotNullIfNotNull("values")] + public Type[]? Unproject(Type[]? values) { if (values == null) return null; @@ -73,7 +93,8 @@ namespace System.Reflection.Context.Projection return newTypes; } - public Type Unproject(Type value) + [return: NotNullIfNotNull("value")] + public Type? Unproject(Type? value) { if (value is ProjectingType projectingType) return projectingType.UnderlyingType; @@ -81,7 +102,7 @@ namespace System.Reflection.Context.Projection return value; } - public bool NeedsProjection(object value) + public bool NeedsProjection([NotNullWhen(true)] object? value) { Debug.Assert(value != null); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs index f366517170a..a88c1ccffee 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Virtual @@ -15,11 +16,11 @@ namespace System.Reflection.Context.Virtual : base(baseMethod) { Debug.Assert(reflectedType != null); - Debug.Assert(reflectedType.IsSubclassOf(baseMethod.DeclaringType)); + Debug.Assert(reflectedType.IsSubclassOf(baseMethod.DeclaringType!)); Debug.Assert(baseMethod is VirtualMethodBase); // Should we require that baseMethod is a declared method? - Debug.Assert(baseMethod.ReflectedType.Equals(baseMethod.DeclaringType)); + Debug.Assert(baseMethod.ReflectedType!.Equals(baseMethod.DeclaringType)); _reflectedType = reflectedType; } @@ -32,11 +33,9 @@ namespace System.Reflection.Context.Virtual } } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { - var other = o as InheritedMethodInfo; - - return other != null && + return o is InheritedMethodInfo other && UnderlyingMethod.Equals(other.UnderlyingMethod) && ReflectedType.Equals(other.ReflectedType); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs index a4a142399f7..117b0020e65 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection.Context.Delegation; namespace System.Reflection.Context.Virtual @@ -15,11 +16,11 @@ namespace System.Reflection.Context.Virtual : base(baseProperty) { Debug.Assert(reflectedType != null); - Debug.Assert(reflectedType.IsSubclassOf(baseProperty.DeclaringType)); + Debug.Assert(reflectedType.IsSubclassOf(baseProperty.DeclaringType!)); Debug.Assert(baseProperty is VirtualPropertyBase); // Should we require that baseProperty is a declared property? - Debug.Assert(baseProperty.ReflectedType.Equals(baseProperty.DeclaringType)); + Debug.Assert(baseProperty.ReflectedType!.Equals(baseProperty.DeclaringType)); _reflectedType = reflectedType; } @@ -32,25 +33,25 @@ namespace System.Reflection.Context.Virtual } } - public override MethodInfo GetGetMethod(bool nonPublic) + public override MethodInfo? GetGetMethod(bool nonPublic) { - MethodInfo underlyingGetter = UnderlyingProperty.GetGetMethod(nonPublic); + MethodInfo? underlyingGetter = UnderlyingProperty.GetGetMethod(nonPublic); if (underlyingGetter == null) return null; else return new InheritedMethodInfo(underlyingGetter, _reflectedType); } - public override MethodInfo GetSetMethod(bool nonPublic) + public override MethodInfo? GetSetMethod(bool nonPublic) { - MethodInfo underlyingSetter = UnderlyingProperty.GetSetMethod(nonPublic); + MethodInfo? underlyingSetter = UnderlyingProperty.GetSetMethod(nonPublic); if (underlyingSetter == null) return null; else return new InheritedMethodInfo(underlyingSetter, _reflectedType); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { return o is InheritedPropertyInfo other && UnderlyingProperty.Equals(other.UnderlyingProperty) && diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualMethodBase.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualMethodBase.cs index 22e7a4e3f42..c61e099ff6b 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualMethodBase.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualMethodBase.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context.Virtual { internal abstract class VirtualMethodBase : MethodInfo { - private ParameterInfo _returnParameter; + private ParameterInfo? _returnParameter; protected abstract Type[] GetParameterTypes(); @@ -44,10 +44,10 @@ namespace System.Reflection.Context.Virtual public override sealed Module Module { - get { return DeclaringType.Module; } + get { return DeclaringType!.Module; } } - public override sealed Type ReflectedType + public override sealed Type? ReflectedType { get { return DeclaringType; } } @@ -112,20 +112,20 @@ namespace System.Reflection.Context.Virtual return false; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { // We don't need to compare the invokees // But do we need to compare the contexts and return types? return obj is VirtualMethodBase other && Name == other.Name && - DeclaringType.Equals(other.DeclaringType) && + DeclaringType!.Equals(other.DeclaringType) && CollectionServices.CompareArrays(GetParameterTypes(), other.GetParameterTypes()); } public override int GetHashCode() { return Name.GetHashCode() ^ - DeclaringType.GetHashCode() ^ + DeclaringType!.GetHashCode() ^ CollectionServices.GetArrayHashCode(GetParameterTypes()); } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualParameter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualParameter.cs index 4cf95be206c..aac19da96e3 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualParameter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualParameter.cs @@ -2,12 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Reflection.Context.Virtual { internal class VirtualParameter : ParameterInfo { - public VirtualParameter(MemberInfo member, Type parameterType, string name, int position) + public VirtualParameter(MemberInfo member, Type parameterType, string? name, int position) { Debug.Assert(position >= -1); @@ -36,7 +37,7 @@ namespace System.Reflection.Context.Virtual return clonedParameters; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { // Do we need to compare Name and ParameterType? return obj is VirtualParameter other && diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.FuncPropertyAccessorBase.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.FuncPropertyAccessorBase.cs index dd0f2b5410d..f6dab499a17 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.FuncPropertyAccessorBase.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.FuncPropertyAccessorBase.cs @@ -26,7 +26,7 @@ namespace System.Reflection.Context.Virtual get { return base.Attributes | MethodAttributes.SpecialName; } } - public override sealed Type DeclaringType + public override sealed Type? DeclaringType { get { return DeclaringProperty.DeclaringType; } } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.PropertySetterBase.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.PropertySetterBase.cs index 8385dbb2318..245b618bed6 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.PropertySetterBase.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.PropertySetterBase.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Virtual { protected abstract class PropertySetterBase : FuncPropertyAccessorBase { - private Type[] _parameterTypes; + private Type[]? _parameterTypes; protected PropertySetterBase(VirtualPropertyBase property) : base(property) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.cs index fcd5e161a0c..7f2526bbf20 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyBase.cs @@ -12,8 +12,8 @@ namespace System.Reflection.Context.Virtual { private readonly string _name; private readonly Type _propertyType; - private Type _declaringType; - private ParameterInfo[] _indexedParameters; + private Type? _declaringType; + private ParameterInfo[]? _indexedParameters; protected VirtualPropertyBase(Type propertyType, string name, CustomReflectionContext context) { @@ -40,7 +40,7 @@ namespace System.Reflection.Context.Virtual get { return PropertyAttributes.None; } } - public override sealed Type DeclaringType + public override sealed Type? DeclaringType { get { return _declaringType; } } @@ -72,24 +72,24 @@ namespace System.Reflection.Context.Virtual public override sealed Module Module { - get { return DeclaringType.Module; } + get { return DeclaringType!.Module; } } - public override sealed Type ReflectedType + public override sealed Type? ReflectedType { get { return DeclaringType; } } public override sealed MethodInfo[] GetAccessors(bool nonPublic) { - MethodInfo getMethod = GetGetMethod(nonPublic); - MethodInfo setMethod = GetSetMethod(nonPublic); + MethodInfo? getMethod = GetGetMethod(nonPublic); + MethodInfo? setMethod = GetSetMethod(nonPublic); Debug.Assert(getMethod != null || setMethod != null); if (getMethod == null || setMethod == null) { - return new MethodInfo[] { getMethod ?? setMethod }; + return new MethodInfo[] { getMethod ?? setMethod! }; } return new MethodInfo[] { getMethod, setMethod }; @@ -100,22 +100,22 @@ namespace System.Reflection.Context.Virtual return (ParameterInfo[])GetIndexParametersNoCopy().Clone(); } - public override sealed object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + public override sealed object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { - MethodInfo getMethod = GetGetMethod(true); + MethodInfo? getMethod = GetGetMethod(true); if (getMethod == null) throw new ArgumentException(SR.Argument_GetMethNotFnd); return getMethod.Invoke(obj, invokeAttr, binder, index, culture); } - public override sealed void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + public override sealed void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) { - MethodInfo setMethod = GetSetMethod(true); + MethodInfo? setMethod = GetSetMethod(true); if (setMethod == null) throw new ArgumentException(SR.Argument_GetMethNotFnd); - object[] args = null; + object?[] args; if (index != null) { @@ -174,13 +174,13 @@ namespace System.Reflection.Context.Virtual return false; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { // We don't need to compare the getters and setters. // But do we need to compare the contexts and return types? return obj is VirtualPropertyBase other && _name == other._name && - _declaringType.Equals(other._declaringType) && + _declaringType!.Equals(other._declaringType) && _propertyType == other._propertyType && CollectionServices.CompareArrays(GetIndexParametersNoCopy(), other.GetIndexParametersNoCopy()); } @@ -188,14 +188,14 @@ namespace System.Reflection.Context.Virtual public override int GetHashCode() { return _name.GetHashCode() ^ - _declaringType.GetHashCode() ^ + _declaringType!.GetHashCode() ^ _propertyType.GetHashCode() ^ CollectionServices.GetArrayHashCode(GetIndexParametersNoCopy()); } public override string ToString() { - return base.ToString(); + return base.ToString()!; } internal void SetDeclaringType(Type declaringType) @@ -207,7 +207,7 @@ namespace System.Reflection.Context.Virtual { if (_indexedParameters == null) { - MethodInfo method = GetGetMethod(true); + MethodInfo? method = GetGetMethod(true); if (method != null) { _indexedParameters = VirtualParameter.CloneParameters(this, method.GetParameters(), skipLastParameter: false); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs index 86caee374ed..4175f59cf85 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs @@ -12,10 +12,10 @@ namespace System.Reflection.Context.Virtual { private sealed class PropertyGetter : PropertyGetterBase { - private readonly Func _getter; + private readonly Func _getter; private readonly IEnumerable _attributes; - public PropertyGetter(VirtualPropertyBase property, Func getter, IEnumerable getterAttributes) + public PropertyGetter(VirtualPropertyBase property, Func getter, IEnumerable? getterAttributes) : base(property) { Debug.Assert(null != getter); @@ -24,14 +24,14 @@ namespace System.Reflection.Context.Virtual _attributes = getterAttributes ?? CollectionServices.Empty(); } - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { // invokeAttr, binder, and culture are ignored, similar to what runtime reflection does with the default binder. if (parameters != null && parameters.Length > 0) throw new TargetParameterCountException(); - if (!ReflectedType.IsInstanceOfType(obj)) + if (!ReflectedType!.IsInstanceOfType(obj)) throw new ArgumentException(); return _getter(obj); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs index f7b3878d8f4..448a58fe151 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs @@ -12,11 +12,11 @@ namespace System.Reflection.Context.Virtual { private sealed class PropertySetter : PropertySetterBase { - private readonly Action _setter; + private readonly Action _setter; private readonly ParameterInfo _valueParameter; private readonly IEnumerable _attributes; - public PropertySetter(VirtualPropertyBase property, Action setter, IEnumerable setterAttributes) + public PropertySetter(VirtualPropertyBase property, Action setter, IEnumerable? setterAttributes) : base(property) { Debug.Assert(null != setter); @@ -31,19 +31,19 @@ namespace System.Reflection.Context.Virtual return new ParameterInfo[] { _valueParameter }; } - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) { // invokeAttr, binder, and culture are ignored, similar to what runtime reflection does with the default binder. if (parameters == null || parameters.Length != 1) throw new TargetParameterCountException(); - object value = parameters[0]; + object? value = parameters[0]; if (obj == null) throw new TargetException(SR.Target_InstanceMethodRequiresTarget); - if (!ReflectedType.IsInstanceOfType(obj)) + if (!ReflectedType!.IsInstanceOfType(obj)) throw new TargetException(SR.Target_ObjectTargetMismatch); if (ReturnType.IsInstanceOfType(value)) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs index e56f1837c87..1d984656f16 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs @@ -10,25 +10,25 @@ namespace System.Reflection.Context.Virtual // Represents a func-based 'PropertyInfo' internal sealed partial class VirtualPropertyInfo : VirtualPropertyBase { - private readonly PropertyGetter _getter; - private readonly PropertySetter _setter; + private readonly PropertyGetter? _getter; + private readonly PropertySetter? _setter; private readonly IEnumerable _attributes; public VirtualPropertyInfo( string name, Type propertyType, - Func getter, - Action setter, - IEnumerable propertyAttributes, - IEnumerable getterAttributes, - IEnumerable setterAttributes, + Func? getter, + Action? setter, + IEnumerable? propertyAttributes, + IEnumerable? getterAttributes, + IEnumerable? setterAttributes, CustomReflectionContext context) : base(propertyType, name, context) { if (getter == null && setter == null) throw new ArgumentException(SR.ArgumentNull_GetterOrSetterMustBeSpecified); - CustomType rcType = propertyType as CustomType; + CustomType? rcType = propertyType as CustomType; if (rcType == null || rcType.ReflectionContext != context) throw new ArgumentException(SR.Argument_PropertyTypeFromDifferentContext); @@ -41,14 +41,14 @@ namespace System.Reflection.Context.Virtual _attributes = propertyAttributes ?? CollectionServices.Empty(); } - public override MethodInfo GetGetMethod(bool nonPublic) + public override MethodInfo? GetGetMethod(bool nonPublic) { // Current we don't support adding nonpulbic getters Debug.Assert(_getter == null || _getter.IsPublic); return _getter; } - public override MethodInfo GetSetMethod(bool nonPublic) + public override MethodInfo? GetSetMethod(bool nonPublic) { // Current we don't support adding nonpulbic setters Debug.Assert(_setter == null || _setter.IsPublic); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs index ac2e91834a4..4a8fa0367e0 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Virtual internal sealed class VirtualReturnParameter : VirtualParameter { public VirtualReturnParameter(MethodInfo method) - : base(method, method.ReturnType, name:null, position: -1) + : base(method, method.ReturnType, name: null, position: -1) { } } From 81232430ca6b6ee88321f6482c4df5eee606e6e1 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Fri, 2 Jul 2021 12:28:11 -0400 Subject: [PATCH 259/926] [Mono] Update tracking issue number (#54516) * Run test to see failures on Android * Disable more irrelevant CI lanes * Update tracking issue * Enable all CI lanes. * Revert unintentional changes * One more * Another one * Disable two more tests --- src/tests/issues.targets | 89 +++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index d51525edca1..f94900a7421 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -2748,12 +2748,6 @@ needs triage - - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 - needs triage @@ -2763,12 +2757,6 @@ needs triage - - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 - needs triage @@ -2808,68 +2796,62 @@ https://github.com/dotnet/runtime/issues/50440 - - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 - - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - - needs triage + + https://github.com/dotnet/runtime/issues/54913 + + + https://github.com/dotnet/runtime/issues/54906 + + + https://github.com/dotnet/runtime/issues/54905 + + + https://github.com/dotnet/runtime/issues/54905 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54911 - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 - - - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54908 - https://github.com/dotnet/runtime/issues/44648 + https://github.com/dotnet/runtime/issues/54906 needs triage @@ -2895,27 +2877,30 @@ https://github.com/dotnet/runtime/issues/44643 + + https://github.com/dotnet/runtime/issues/54906 + - https://github.com/dotnet/runtime/issues/51513 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/51513 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/51513 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/51513 + https://github.com/dotnet/runtime/issues/54906 - https://github.com/dotnet/runtime/issues/51513 - - - https://github.com/dotnet/runtime/issues/51513 + https://github.com/dotnet/runtime/issues/54906 https://github.com/dotnet/runtime/issues/52763 + + https://github.com/dotnet/runtime/issues/54974 + https://github.com/dotnet/runtime/issues/53077 From 96ef64d2ed65fb46d394f52d1e50d7a94c6bca71 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 2 Jul 2021 09:29:23 -0700 Subject: [PATCH 260/926] Add documentation for SafeBuffer.ReadSpan/WriteSpan (#54942) --- .../Runtime/InteropServices/SafeBuffer.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs index 8f7eee8c448..3e7f204573c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs @@ -4,7 +4,7 @@ /*============================================================ ** ** Purpose: Unsafe code that uses pointers should use -** SafePointer to fix subtle lifetime problems with the +** SafeBuffer to fix subtle lifetime problems with the ** underlying resource. ** ===========================================================*/ @@ -203,6 +203,13 @@ namespace System.Runtime.InteropServices return value; } + /// + /// Reads the specified number of value types from memory starting at the offset, and writes them into an array starting at the index. + /// The value type to read. + /// The location from which to start reading. + /// The output array to write to. + /// The location in the output array to begin writing to. + /// The number of value types to read from the input array and to write to the output array. [CLSCompliant(false)] public void ReadArray(ulong byteOffset, T[] array, int index, int count) where T : struct @@ -219,6 +226,11 @@ namespace System.Runtime.InteropServices ReadSpan(byteOffset, new Span(array, index, count)); } + /// + /// Reads value types from memory starting at the offset, and writes them into a span. The number of value types that will be read is determined by the length of the span. + /// The value type to read. + /// The location from which to start reading. + /// The output span to write to. [CLSCompliant(false)] public void ReadSpan(ulong byteOffset, Span buffer) where T : struct @@ -279,6 +291,14 @@ namespace System.Runtime.InteropServices } } + /// + /// Writes the specified number of value types to a memory location by reading bytes starting from the specified location in the input array. + /// + /// The value type to write. + /// The location in memory to write to. + /// The input array. + /// The offset in the array to start reading from. + /// The number of value types to write. [CLSCompliant(false)] public void WriteArray(ulong byteOffset, T[] array, int index, int count) where T : struct @@ -295,6 +315,12 @@ namespace System.Runtime.InteropServices WriteSpan(byteOffset, new ReadOnlySpan(array, index, count)); } + /// + /// Writes the value types from a read-only span to a memory location. + /// + /// The value type to write. + /// The location in memory to write to. + /// The input span. [CLSCompliant(false)] public void WriteSpan(ulong byteOffset, ReadOnlySpan data) where T : struct From e8572d3e5d23e624dac4f3744c745885a6d3addb Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 2 Jul 2021 12:32:15 -0700 Subject: [PATCH 261/926] Fix spinwaiting in LockFreeReaderHashtable (#55082) Task.Delay(1).Wait() is variant of sync-over-async anti-patern. It does not work well with threadpool used to run parallel AOT compilation. It can lead to too many threadpool threads getting created and progress slowing down to crawl, at least temporarily. --- .../Common/Utilities/LockFreeReaderHashtable.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs index 1c56f36a33e..eee17ccf0cc 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs @@ -245,20 +245,14 @@ namespace Internal.TypeSystem if (sentinel == null) return null; - TValue value = Volatile.Read(ref hashtable[tableIndex]); + var sw = new SpinWait(); while (true) { - for (int i = 0; (i < 10000) && value == sentinel; i++) - { - value = Volatile.Read(ref hashtable[tableIndex]); - } + TValue value = Volatile.Read(ref hashtable[tableIndex]); if (value != sentinel) - break; - - Task.Delay(1).Wait(); + return value; + sw.SpinOnce(); } - - return value; } /// From b862155e1bb5f0718410b0ccc1601cddffe9077a Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 2 Jul 2021 13:47:06 -0700 Subject: [PATCH 262/926] Add support to DAC for reading tagged memory on tracked types (#54994) * Add support to DAC for reading tagged memory on tracked types * Build error on macOS. * DACize the relevant SyncBlock APIs. Create helper function for tagged memory API querying. --- src/coreclr/debug/daccess/daccess.cpp | 4 + src/coreclr/debug/daccess/dacimpl.h | 13 ++- src/coreclr/debug/daccess/request.cpp | 106 +++++++++++++++++++++ src/coreclr/inc/sospriv.idl | 11 +++ src/coreclr/pal/prebuilt/idl/sospriv_i.cpp | 2 + src/coreclr/pal/prebuilt/inc/sospriv.h | 96 +++++++++++++++++++ src/coreclr/vm/methodtable.cpp | 4 +- src/coreclr/vm/syncblk.h | 16 +++- 8 files changed, 245 insertions(+), 7 deletions(-) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 9d3e9e587fc..470ce99e395 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -3291,6 +3291,10 @@ ClrDataAccess::QueryInterface(THIS_ { ifaceRet = static_cast(this); } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface11))) + { + ifaceRet = static_cast(this); + } else { *iface = NULL; diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 165dcb47fb1..744ebaf7293 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -839,7 +839,8 @@ class ClrDataAccess public ISOSDacInterface7, public ISOSDacInterface8, public ISOSDacInterface9, - public ISOSDacInterface10 + public ISOSDacInterface10, + public ISOSDacInterface11 { public: ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0); @@ -1213,6 +1214,16 @@ public: virtual HRESULT STDMETHODCALLTYPE GetComWrappersCCWData(CLRDATA_ADDRESS ccw, CLRDATA_ADDRESS *managedObject, int *refCount); virtual HRESULT STDMETHODCALLTYPE IsComWrappersRCW(CLRDATA_ADDRESS rcw, BOOL *isComWrappersRCW); virtual HRESULT STDMETHODCALLTYPE GetComWrappersRCWData(CLRDATA_ADDRESS rcw, CLRDATA_ADDRESS *identity); + + // ISOSDacInterface11 + virtual HRESULT STDMETHODCALLTYPE IsTrackedType( + CLRDATA_ADDRESS objAddr, + BOOL *isTrackedType, + BOOL *hasTaggedMemory); + virtual HRESULT STDMETHODCALLTYPE GetTaggedMemory( + CLRDATA_ADDRESS objAddr, + CLRDATA_ADDRESS *taggedMemory, + size_t *taggedMemorySizeInBytes); // // ClrDataAccess. // diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 31638507e94..968664489d8 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -5040,3 +5040,109 @@ HRESULT ClrDataAccess::GetComWrappersRCWData(CLRDATA_ADDRESS rcw, CLRDATA_ADDRES return E_NOTIMPL; #endif // FEATURE_COMWRAPPERS } + +namespace +{ + BOOL TryReadTaggedMemoryState( + CLRDATA_ADDRESS objAddr, + ICorDebugDataTarget* target, + CLRDATA_ADDRESS *taggedMemory = NULL, + size_t *taggedMemorySizeInBytes = NULL) + { + BOOL hasTaggedMemory = FALSE; + +#ifdef FEATURE_OBJCMARSHAL + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + PTR_SyncBlock pSyncBlk = DACGetSyncBlockFromObjectPointer(CLRDATA_ADDRESS_TO_TADDR(objAddr), target); + if (pSyncBlk != NULL) + { + PTR_InteropSyncBlockInfo pInfo = pSyncBlk->GetInteropInfoNoCreate(); + if (pInfo != NULL) + { + CLRDATA_ADDRESS taggedMemoryLocal = PTR_CDADDR(pInfo->GetTaggedMemory()); + if (taggedMemoryLocal != NULL) + { + hasTaggedMemory = TRUE; + if (taggedMemory) + *taggedMemory = taggedMemoryLocal; + + if (taggedMemorySizeInBytes) + *taggedMemorySizeInBytes = pInfo->GetTaggedMemorySizeInBytes(); + } + } + } + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; +#endif // FEATURE_OBJCMARSHAL + + return hasTaggedMemory; + } +} + +HRESULT ClrDataAccess::IsTrackedType( + CLRDATA_ADDRESS objAddr, + BOOL *isTrackedType, + BOOL *hasTaggedMemory) +{ + if (objAddr == 0 + || isTrackedType == NULL + || hasTaggedMemory == NULL) + { + return E_INVALIDARG; + } + + *isTrackedType = FALSE; + *hasTaggedMemory = FALSE; + + SOSDacEnter(); + + TADDR mtTADDR = DACGetMethodTableFromObjectPointer(CLRDATA_ADDRESS_TO_TADDR(objAddr), m_pTarget); + if (mtTADDR==NULL) + hr = E_INVALIDARG; + + BOOL bFree = FALSE; + MethodTable *mt = NULL; + if (SUCCEEDED(hr)) + { + mt = PTR_MethodTable(mtTADDR); + if (!DacValidateMethodTable(mt, bFree)) + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + *isTrackedType = mt->IsTrackedReferenceWithFinalizer(); + hr = *isTrackedType ? S_OK : S_FALSE; + *hasTaggedMemory = TryReadTaggedMemoryState(objAddr, m_pTarget); + } + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetTaggedMemory( + CLRDATA_ADDRESS objAddr, + CLRDATA_ADDRESS *taggedMemory, + size_t *taggedMemorySizeInBytes) +{ + if (objAddr == 0 + || taggedMemory == NULL + || taggedMemorySizeInBytes == NULL) + { + return E_INVALIDARG; + } + + *taggedMemory = NULL; + *taggedMemorySizeInBytes = 0; + + SOSDacEnter(); + + if (FALSE == TryReadTaggedMemoryState(objAddr, m_pTarget, taggedMemory, taggedMemorySizeInBytes)) + { + hr = S_FALSE; + } + + SOSDacLeave(); + return hr; +} diff --git a/src/coreclr/inc/sospriv.idl b/src/coreclr/inc/sospriv.idl index 332ec79c50a..102801f7e5d 100644 --- a/src/coreclr/inc/sospriv.idl +++ b/src/coreclr/inc/sospriv.idl @@ -438,3 +438,14 @@ interface ISOSDacInterface10 : IUnknown HRESULT IsComWrappersRCW(CLRDATA_ADDRESS rcw, BOOL *isComWrappersRCW); HRESULT GetComWrappersRCWData(CLRDATA_ADDRESS rcw, CLRDATA_ADDRESS *identity); } + +[ + object, + local, + uuid(96BA1DB9-14CD-4492-8065-1CAAECF6E5CF) +] +interface ISOSDacInterface11 : IUnknown +{ + HRESULT IsTrackedType(CLRDATA_ADDRESS objAddr, BOOL* isTrackedType, BOOL* hasTaggedMemory); + HRESULT GetTaggedMemory(CLRDATA_ADDRESS objAddr, CLRDATA_ADDRESS* taggedMemory, size_t* taggedMemorySizeInBytes); +} diff --git a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp index c00c70a183b..2f9afdc48e3 100644 --- a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp @@ -109,6 +109,8 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface9,0x4eca42d8,0x7e7b,0x4c8a,0xa1,0x16,0 MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface10,0x90B8FCC3,0x7251,0x4B0A,0xAE,0x3D,0x5C,0x13,0xA6,0x7E,0xC9,0xAA); +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface11,0x96BA1DB9,0x14CD,0x4492,0x80,0x65,0x1C,0xAA,0xEC,0xF6,0xE5,0xCF); + #undef MIDL_DEFINE_GUID #ifdef __cplusplus diff --git a/src/coreclr/pal/prebuilt/inc/sospriv.h b/src/coreclr/pal/prebuilt/inc/sospriv.h index 367ca229043..5c8f17a7fff 100644 --- a/src/coreclr/pal/prebuilt/inc/sospriv.h +++ b/src/coreclr/pal/prebuilt/inc/sospriv.h @@ -2896,6 +2896,102 @@ EXTERN_C const IID IID_ISOSDacInterface10; #endif /* __ISOSDacInterface10_INTERFACE_DEFINED__ */ +#ifndef __ISOSDacInterface11_INTERFACE_DEFINED__ +#define __ISOSDacInterface11_INTERFACE_DEFINED__ + +/* interface ISOSDacInterface11 */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSDacInterface11; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("96BA1DB9-14CD-4492-8065-1CAAECF6E5CF") + ISOSDacInterface11 : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE IsTrackedType( + CLRDATA_ADDRESS objAddr, + BOOL *isTrackedType, + BOOL *hasTaggedMemory) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetTaggedMemory( + CLRDATA_ADDRESS objAddr, + CLRDATA_ADDRESS *taggedMemory, + size_t *taggedMemorySizeInBytes) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSDacInterface11Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSDacInterface11 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSDacInterface11 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSDacInterface11 * This); + + HRESULT ( STDMETHODCALLTYPE *IsTrackedType )( + ISOSDacInterface11 * This, + CLRDATA_ADDRESS objAddr, + BOOL *isTrackedType, + BOOL *hasTaggedMemory); + + HRESULT ( STDMETHODCALLTYPE *GetTaggedMemory )( + ISOSDacInterface11 * This, + CLRDATA_ADDRESS objAddr, + CLRDATA_ADDRESS *taggedMemory, + size_t *taggedMemorySizeInBytes); + + END_INTERFACE + } ISOSDacInterface11Vtbl; + + interface ISOSDacInterface11 + { + CONST_VTBL struct ISOSDacInterface11Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSDacInterface11_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSDacInterface11_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSDacInterface11_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSDacInterface11_IsTrackedType(This,objAddr,isTrackedType,hasTaggedMemory) \ + ( (This)->lpVtbl -> IsTrackedType(This,objAddr,isTrackedType,hasTaggedMemory) ) + +#define ISOSDacInterface11_GetTaggedMemory(This,objAddr,taggedMemory,taggedMemorySizeInBytes) \ + ( (This)->lpVtbl -> GetTaggedMemory(This,objAddr,taggedMemory,taggedMemorySizeInBytes) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSDacInterface11_INTERFACE_DEFINED__ */ /* Additional Prototypes for ALL interfaces */ diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a7f41cce5cd..2fc25cf8133 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -623,14 +623,14 @@ void MethodTable::SetIsTrackedReferenceWithFinalizer() SetFlag(enum_flag_IsTrackedReferenceWithFinalizer); } +#endif // !DACCESS_COMPILE + BOOL MethodTable::IsTrackedReferenceWithFinalizer() { LIMITED_METHOD_DAC_CONTRACT; return GetFlag(enum_flag_IsTrackedReferenceWithFinalizer); } -#endif // !DACCESS_COMPILE - //========================================================================================== WORD MethodTable::GetNumMethods() { diff --git a/src/coreclr/vm/syncblk.h b/src/coreclr/vm/syncblk.h index 8d001786cfc..6fb565c1aa4 100644 --- a/src/coreclr/vm/syncblk.h +++ b/src/coreclr/vm/syncblk.h @@ -889,12 +889,13 @@ private: #ifdef FEATURE_OBJCMARSHAL public: - void* AllocTaggedMemory(_Out_ size_t* memoryInSizeT) +#ifndef DACCESS_COMPILE + PTR_VOID AllocTaggedMemory(_Out_ size_t* memoryInSizeT) { LIMITED_METHOD_CONTRACT; _ASSERTE(memoryInSizeT != NULL); - *memoryInSizeT = _countof(m_taggedAlloc) / sizeof(SIZE_T); + *memoryInSizeT = GetTaggedMemorySizeInBytes() / sizeof(SIZE_T); // The allocation is meant to indicate that memory // has been made available by the system. Calling the 'get' @@ -903,15 +904,22 @@ public: m_taggedMemory = m_taggedAlloc; return m_taggedMemory; } +#endif // !DACCESS_COMPILE - void* GetTaggedMemory() + PTR_VOID GetTaggedMemory() { LIMITED_METHOD_CONTRACT; return m_taggedMemory; } + size_t GetTaggedMemorySizeInBytes() + { + LIMITED_METHOD_CONTRACT; + return _countof(m_taggedAlloc); + } + private: - void* m_taggedMemory; + PTR_VOID m_taggedMemory; // Two pointers worth of bytes of the requirement for // the current consuming implementation so that is what From 5b337cbad72dbdfd0ef673560fd6c3f84621a7d5 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Fri, 2 Jul 2021 23:08:34 +0200 Subject: [PATCH 263/926] [mono] Fix type lookup failure in mono_class_is_subclass_of_internal (#54817) * Only call mono_class_init_internal if check_interfaces is true. * Fixes https://github.com/dotnet/runtime/issues/54816 --- src/mono/mono/metadata/class.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 1bf5438e9f4..957ea8f8450 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -3523,8 +3523,10 @@ mono_class_is_subclass_of_internal (MonoClass *klass, MonoClass *klassc, { MONO_REQ_GC_UNSAFE_MODE; /* FIXME test for interfaces with variant generic arguments */ - mono_class_init_internal (klass); - mono_class_init_internal (klassc); + if (check_interfaces) { + mono_class_init_internal (klass); + mono_class_init_internal (klassc); + } if (check_interfaces && MONO_CLASS_IS_INTERFACE_INTERNAL (klassc) && !MONO_CLASS_IS_INTERFACE_INTERNAL (klass)) { if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, m_class_get_interface_id (klassc))) From fe9a54df8f4b5e6a2e4cafc0e4234a64e8c9cf8e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 2 Jul 2021 15:19:12 -0700 Subject: [PATCH 264/926] Expose and implement generic math interfaces on core types (#54650) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pin MicrosoftNetCompilersToolsetVersion to a version that supports Static Abstracts in Interfaces * Fixed issues related to enabling generic math in a general sense (#4) - Disable constraint checking during EEInit - Disable il linker running on CoreLib - Fixup generic math tests to actually build * Adding interfaces to support generic math * Implement generic math interfaces on core types * Updating the System.Runtime ref assembly to support generic math * Add a basic xunit test for generic-math * Removing unnecessary nullable annotations * Ensure all preview interface members are explicitly implemented * Don't use var for various methods in Double/Half/Single * Ensure FeatureGenericMath is defined for Mono * Skip generic math tests on Mono WASM due to https://github.com/dotnet/runtime/issues/54910 * Apply suggestions from code review Co-authored-by: Aleksey Kliger (λgeek) Co-authored-by: David Wrighton Co-authored-by: Aleksey Kliger (λgeek) --- eng/Versions.props | 2 + src/coreclr/clr.featuredefines.props | 4 +- src/coreclr/vm/typedesc.cpp | 5 + .../System.Private.CoreLib.Shared.projitems | 23 + .../System.Private.CoreLib/src/System/Byte.cs | 732 ++++- .../System.Private.CoreLib/src/System/Char.cs | 746 +++++ .../src/System/DateOnly.cs | 78 +- .../src/System/DateTime.cs | 120 + .../src/System/DateTimeOffset.cs | 119 + .../src/System/Decimal.cs | 537 ++++ .../src/System/Double.cs | 837 ++++- .../System.Private.CoreLib/src/System/Guid.cs | 292 ++ .../System.Private.CoreLib/src/System/Half.cs | 895 ++++++ .../src/System/IAdditionOperators.cs | 33 + .../src/System/IAdditiveIdentity.cs | 22 + .../src/System/IBitwiseOperators.cs | 43 + .../src/System/IComparisonOperators.cs | 46 + .../src/System/IDecrementOperators.cs | 29 + .../src/System/IDivisionOperators.cs | 33 + .../src/System/IEqualityOperators.cs | 31 + .../src/System/IFloatingPoint.cs | 342 ++ .../src/System/IIncrementOperators.cs | 29 + .../src/System/IInteger.cs | 47 + .../src/System/IMinMaxValue.cs | 24 + .../src/System/IModulusOperators.cs | 34 + .../src/System/IMultiplicativeIdentity.cs | 22 + .../src/System/IMultiplyOperators.cs | 33 + .../src/System/INumber.cs | 192 ++ .../src/System/IParseable.cs | 35 + .../src/System/IShiftOperators.cs | 39 + .../src/System/ISpanParseable.cs | 33 + .../src/System/ISubtractionOperators.cs | 33 + .../src/System/IUnaryNegationOperators.cs | 30 + .../src/System/IUnaryPlusOperators.cs | 30 + .../src/System/Int16.cs | 721 +++++ .../src/System/Int32.cs | 698 +++++ .../src/System/Int64.cs | 664 ++++ .../src/System/IntPtr.cs | 739 ++++- .../src/System/SByte.cs | 740 +++++ .../src/System/Single.cs | 833 ++++- .../src/System/TimeOnly.cs | 81 +- .../src/System/TimeSpan.cs | 179 ++ .../src/System/UInt16.cs | 708 +++++ .../src/System/UInt32.cs | 698 +++++ .../src/System/UInt64.cs | 678 ++++ .../src/System/UIntPtr.cs | 754 ++++- .../System.Runtime/ref/System.Runtime.cs | 2771 ++++++++++++++++- .../System.Runtime/ref/System.Runtime.csproj | 6 +- .../tests/System.Runtime.Tests.csproj | 2 + .../tests/System/GenericMathTests.cs | 73 + .../tests/System/Int32GenericMathTests.cs | 35 + .../System.Private.CoreLib.csproj | 2 + 52 files changed, 15913 insertions(+), 19 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IAdditionOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IAdditiveIdentity.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IBitwiseOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IComparisonOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IDecrementOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IDivisionOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IEqualityOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IFloatingPoint.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IIncrementOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IInteger.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IMinMaxValue.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IModulusOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IMultiplicativeIdentity.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IMultiplyOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/INumber.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IParseable.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IShiftOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/ISpanParseable.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/ISubtractionOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IUnaryNegationOperators.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IUnaryPlusOperators.cs create mode 100644 src/libraries/System.Runtime/tests/System/GenericMathTests.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs diff --git a/eng/Versions.props b/eng/Versions.props index 1c8ae0b1d33..cdb313d0a06 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -17,6 +17,8 @@ release true + + 4.0.0-2.21323.11 true false false diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 67a5eab17c8..08fca8de6dd 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -9,6 +9,7 @@ true true true + true true @@ -58,7 +59,8 @@ $(DefineConstants);FEATURE_BASICFREEZE $(DefineConstants);FEATURE_PORTABLE_SHUFFLE_THUNKS $(DefineConstants);FEATURE_ICASTABLE - + $(DefineConstants);FEATURE_GENERIC_MATH + $(DefineConstants);PROFILING_SUPPORTED $(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index fbc6ff0170e..f8567e0a9aa 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -1730,6 +1730,11 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra } CONTRACTL_END; + // During EEStartup, we cannot safely validate constraints, but we can also be confident that the code doesn't violate them + // Just skip validation and declare that the constraints are satisfied. + if (g_fEEInit) + return TRUE; + IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); mdGenericParamConstraint tkConstraint; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 1ceea5e5c0c..57d2b9b8586 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2257,4 +2257,27 @@ Interop\Windows\Kernel32\Interop.Threading.cs + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 8f127b6cbdd..39bfeae2a7e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -13,6 +14,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Byte : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly byte m_value; // Do not rename (binary serialization) @@ -22,7 +30,6 @@ namespace System // The minimum value that a Byte may represent: 0. public const byte MinValue = 0; - // Compares this object to another object, returning an integer that // indicates the relationship. // Returns a value less than zero if this object @@ -190,14 +197,14 @@ namespace System } // - // IConvertible implementation + // IConvertible // + public TypeCode GetTypeCode() { return TypeCode.Byte; } - bool IConvertible.ToBoolean(IFormatProvider? provider) { return Convert.ToBoolean(m_value); @@ -272,5 +279,724 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static byte IAdditionOperators.operator +(byte left, byte right) + => (byte)(left + right); + + // [RequiresPreviewFeatures] + // static checked byte IAdditionOperators.operator +(byte left, byte right) + // => checked((byte)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static byte IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static byte IBinaryInteger.LeadingZeroCount(byte value) + => (byte)(BitOperations.LeadingZeroCount(value) - 24); + + [RequiresPreviewFeatures] + static byte IBinaryInteger.PopCount(byte value) + => (byte)BitOperations.PopCount(value); + + [RequiresPreviewFeatures] + static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) + => (byte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); + + [RequiresPreviewFeatures] + static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) + => (byte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); + + [RequiresPreviewFeatures] + static byte IBinaryInteger.TrailingZeroCount(byte value) + => (byte)(BitOperations.TrailingZeroCount(value << 24) - 24); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(byte value) + => BitOperations.IsPow2((uint)value); + + [RequiresPreviewFeatures] + static byte IBinaryNumber.Log2(byte value) + => (byte)BitOperations.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static byte IBitwiseOperators.operator &(byte left, byte right) + => (byte)(left & right); + + [RequiresPreviewFeatures] + static byte IBitwiseOperators.operator |(byte left, byte right) + => (byte)(left | right); + + [RequiresPreviewFeatures] + static byte IBitwiseOperators.operator ^(byte left, byte right) + => (byte)(left ^ right); + + [RequiresPreviewFeatures] + static byte IBitwiseOperators.operator ~(byte value) + => (byte)(~value); + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(byte left, byte right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(byte left, byte right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(byte left, byte right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(byte left, byte right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static byte IDecrementOperators.operator --(byte value) + => value--; + + // [RequiresPreviewFeatures] + // static checked byte IDecrementOperators.operator --(byte value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static byte IDivisionOperators.operator /(byte left, byte right) + => (byte)(left / right); + + // [RequiresPreviewFeatures] + // static checked byte IDivisionOperators.operator /(byte left, byte right) + // => checked((byte)(left / right)); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(byte left, byte right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(byte left, byte right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static byte IIncrementOperators.operator ++(byte value) + => value++; + + // [RequiresPreviewFeatures] + // static checked byte IIncrementOperators.operator ++(byte value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static byte IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static byte IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static byte IModulusOperators.operator %(byte left, byte right) + => (byte)(left % right); + + // [RequiresPreviewFeatures] + // static checked byte IModulusOperators.operator %(byte left, byte right) + // => checked((byte)(left % right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static byte IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static byte IMultiplyOperators.operator *(byte left, byte right) + => (byte)(left * right); + + // [RequiresPreviewFeatures] + // static checked byte IMultiplyOperators.operator *(byte left, byte right) + // => checked((byte)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static byte INumber.One => 1; + + [RequiresPreviewFeatures] + static byte INumber.Zero => 0; + + [RequiresPreviewFeatures] + static byte INumber.Abs(byte value) + => value; + + [RequiresPreviewFeatures] + static byte INumber.Clamp(byte value, byte min, byte max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static byte INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return checked((byte)(char)(object)value); + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((byte)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((byte)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((byte)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((byte)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((byte)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((byte)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((byte)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((byte)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return checked((byte)(ushort)(object)value); + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((byte)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((byte)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((byte)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static byte INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + return (actualValue > MaxValue) ? MaxValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + return (actualValue > MaxValue) ? MaxValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (byte)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (byte)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static byte INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (byte)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (byte)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (byte)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (byte)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (byte)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (byte)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (byte)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (byte)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (byte)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (byte)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (byte)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (byte)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (byte)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (byte Quotient, byte Remainder) INumber.DivRem(byte left, byte right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static byte INumber.Max(byte x, byte y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static byte INumber.Min(byte x, byte y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static byte INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static byte INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static byte INumber.Sign(byte value) + => (byte)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out byte result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (byte)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static byte IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out byte result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static byte IShiftOperators.operator <<(byte value, int shiftAmount) + => (byte)(value << shiftAmount); + + [RequiresPreviewFeatures] + static byte IShiftOperators.operator >>(byte value, int shiftAmount) + => (byte)(value >> shiftAmount); + + // [RequiresPreviewFeatures] + // static byte IShiftOperators.operator >>>(byte value, int shiftAmount) + // => (byte)(value >> shiftAmount); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static byte ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out byte result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static byte ISubtractionOperators.operator -(byte left, byte right) + => (byte)(left - right); + + // [RequiresPreviewFeatures] + // static checked byte ISubtractionOperators.operator -(byte left, byte right) + // => checked((byte)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static byte IUnaryNegationOperators.operator -(byte value) + => (byte)(-value); + + // [RequiresPreviewFeatures] + // static checked byte IUnaryNegationOperators.operator -(byte value) + // => checked((byte)(-value)); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static byte IUnaryPlusOperators.operator +(byte value) + => (byte)(+value); + + // [RequiresPreviewFeatures] + // static checked byte IUnaryPlusOperators.operator +(byte value) + // => checked((byte)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 613d0909429..2f58b54b746 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -14,7 +14,10 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; namespace System @@ -23,6 +26,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Char : IComparable, IComparable, IEquatable, IConvertible, ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // // Member Variables @@ -1049,5 +1059,741 @@ namespace System // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters. return (int)s[index]; } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static char IAdditionOperators.operator +(char left, char right) + => (char)(left + right); + + // [RequiresPreviewFeatures] + // static checked char IAdditionOperators.operator +(char left, char right) + // => checked((char)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static char IAdditiveIdentity.AdditiveIdentity => (char)0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static char IBinaryInteger.LeadingZeroCount(char value) + => (char)(BitOperations.LeadingZeroCount(value) - 16); + + [RequiresPreviewFeatures] + static char IBinaryInteger.PopCount(char value) + => (char)BitOperations.PopCount(value); + + [RequiresPreviewFeatures] + static char IBinaryInteger.RotateLeft(char value, char rotateAmount) + => (char)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static char IBinaryInteger.RotateRight(char value, char rotateAmount) + => (char)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static char IBinaryInteger.TrailingZeroCount(char value) + => (char)(BitOperations.TrailingZeroCount(value << 16) - 16); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(char value) + => BitOperations.IsPow2((uint)value); + + [RequiresPreviewFeatures] + static char IBinaryNumber.Log2(char value) + => (char)BitOperations.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static char IBitwiseOperators.operator &(char left, char right) + => (char)(left & right); + + [RequiresPreviewFeatures] + static char IBitwiseOperators.operator |(char left, char right) + => (char)(left | right); + + [RequiresPreviewFeatures] + static char IBitwiseOperators.operator ^(char left, char right) + => (char)(left ^ right); + + [RequiresPreviewFeatures] + static char IBitwiseOperators.operator ~(char value) + => (char)(~value); + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(char left, char right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(char left, char right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(char left, char right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(char left, char right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static char IDecrementOperators.operator --(char value) + => value--; + + // [RequiresPreviewFeatures] + // static checked char IDecrementOperators.operator --(char value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static char IDivisionOperators.operator /(char left, char right) + => (char)(left / right); + + // [RequiresPreviewFeatures] + // static checked char IDivisionOperators.operator /(char left, char right) + // => checked((char)(left / right)); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(char left, char right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(char left, char right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static char IIncrementOperators.operator ++(char value) + => value++; + + // [RequiresPreviewFeatures] + // static checked char IIncrementOperators.operator ++(char value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static char IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static char IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static char IModulusOperators.operator %(char left, char right) + => (char)(left % right); + + // [RequiresPreviewFeatures] + // static checked char IModulusOperators.operator %(char left, char right) + // => checked((char)(left % right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static char IMultiplicativeIdentity.MultiplicativeIdentity => (char)1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static char IMultiplyOperators.operator *(char left, char right) + => (char)(left * right); + + // [RequiresPreviewFeatures] + // static checked char IMultiplyOperators.operator *(char left, char right) + // => checked((char)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static char INumber.One => (char)1; + + [RequiresPreviewFeatures] + static char INumber.Zero => (char)0; + + [RequiresPreviewFeatures] + static char INumber.Abs(char value) + => value; + + [RequiresPreviewFeatures] + static char INumber.Clamp(char value, char min, char max) + => (char)Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static char INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (char)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((char)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((char)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((char)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((char)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((char)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((char)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((char)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((char)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (char)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((char)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((char)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((char)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static char INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (char)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (char)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (char)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (char)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static char INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (char)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (char)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (char)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (char)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (char)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (char)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (char)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (char)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (char)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (char)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (char)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (char)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (char)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (char Quotient, char Remainder) INumber.DivRem(char left, char right) + => ((char, char))Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static char INumber.Max(char x, char y) + => (char)Math.Max(x, y); + + [RequiresPreviewFeatures] + static char INumber.Min(char x, char y) + => (char)Math.Min(x, y); + + [RequiresPreviewFeatures] + static char INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s); + + [RequiresPreviewFeatures] + static char INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + { + if (s.Length != 1) + { + throw new FormatException(SR.Format_NeedSingleChar); + } + return s[0]; + } + + [RequiresPreviewFeatures] + static char INumber.Sign(char value) + => (char)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out char result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (char)(byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (char)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out char result) + => TryParse(s, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) + { + if (s.Length != 1) + { + result = default; + return false; + } + result = s[0]; + return true; + } + + // + // IParseable + // + + [RequiresPreviewFeatures] + static char IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out char result) + => TryParse(s, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static char IShiftOperators.operator <<(char value, int shiftAmount) + => (char)(value << shiftAmount); + + [RequiresPreviewFeatures] + static char IShiftOperators.operator >>(char value, int shiftAmount) + => (char)(value >> shiftAmount); + + // [RequiresPreviewFeatures] + // static char IShiftOperators.operator >>>(char value, int shiftAmount) + // => (char)(value >> shiftAmount); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static char ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + { + if (s.Length != 1) + { + throw new FormatException(SR.Format_NeedSingleChar); + } + return s[0]; + } + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out char result) + { + if (s.Length != 1) + { + result = default; + return false; + } + result = s[0]; + return true; + } + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static char ISubtractionOperators.operator -(char left, char right) + => (char)(left - right); + + // [RequiresPreviewFeatures] + // static checked char ISubtractionOperators.operator -(char left, char right) + // => checked((char)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static char IUnaryNegationOperators.operator -(char value) + => (char)(-value); + + // [RequiresPreviewFeatures] + // static checked char IUnaryNegationOperators.operator -(char value) + // => checked((char)(-value)); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static char IUnaryPlusOperators.operator +(char value) + => (char)(+value); + + // [RequiresPreviewFeatures] + // static checked char IUnaryPlusOperators.operator +(char value) + // => checked((char)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs index 1ca9b084ee0..7e64638fa51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Versioning; namespace System { @@ -11,6 +12,13 @@ namespace System /// Represents dates with values ranging from January 1, 0001 Anno Domini (Common Era) through December 31, 9999 A.D. (C.E.) in the Gregorian calendar. /// public readonly struct DateOnly : IComparable, IComparable, IEquatable, ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IComparisonOperators, + IMinMaxValue, + ISpanParseable +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dayNumber; @@ -822,5 +830,73 @@ namespace System return DateTimeFormat.TryFormat(GetEquivalentDateTime(), destination, out charsWritten, format, provider); } + +#if FEATURE_GENERIC_MATH + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(DateOnly left, DateOnly right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(DateOnly left, DateOnly right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(DateOnly left, DateOnly right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(DateOnly left, DateOnly right) + => left >= right; + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(DateOnly left, DateOnly right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(DateOnly left, DateOnly right) + => left != right; + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static DateOnly IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static DateOnly IMinMaxValue.MaxValue => MaxValue; + + // + // IParseable + // + + [RequiresPreviewFeatures] + static DateOnly IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out DateOnly result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static DateOnly ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out DateOnly result) + => TryParse(s, provider, DateTimeStyles.None, out result); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs index dc2ebdf1380..1d0235268a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Runtime.Versioning; namespace System { @@ -44,6 +45,17 @@ namespace System [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly partial struct DateTime : IComparable, ISpanFormattable, IConvertible, IComparable, IEquatable, ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IAdditionOperators, + IAdditiveIdentity, + IComparisonOperators, + IMinMaxValue, + ISpanParseable, + ISubtractionOperators, + ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // Number of 100ns ticks per time unit private const long TicksPerMillisecond = 10000; @@ -1503,5 +1515,113 @@ namespace System result = new DateTime(ticks); return true; } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static DateTime IAdditionOperators.operator +(DateTime left, TimeSpan right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked DateTime IAdditionOperators.operator +(DateTime left, TimeSpan right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static TimeSpan IAdditiveIdentity.AdditiveIdentity + => default; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(DateTime left, DateTime right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(DateTime left, DateTime right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(DateTime left, DateTime right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(DateTime left, DateTime right) + => left >= right; + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(DateTime left, DateTime right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(DateTime left, DateTime right) + => left != right; + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static DateTime IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static DateTime IMinMaxValue.MaxValue => MaxValue; + + // + // IParseable + // + + [RequiresPreviewFeatures] + static DateTime IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out DateTime result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static DateTime ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out DateTime result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static DateTime ISubtractionOperators.operator -(DateTime left, TimeSpan right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked DateTime ISubtractionOperators.operator -(DateTime left, TimeSpan right) + // => checked(left - right); + + [RequiresPreviewFeatures] + static TimeSpan ISubtractionOperators.operator -(DateTime left, DateTime right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan ISubtractionOperators.operator -(DateTime left, DateTime right) + // => checked(left - right); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs index 8b91fd6a2a8..f9fcc33b9a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Runtime.Versioning; namespace System { @@ -32,6 +33,17 @@ namespace System [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct DateTimeOffset : IComparable, ISpanFormattable, IComparable, IEquatable, ISerializable, IDeserializationCallback +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IAdditionOperators, + IAdditiveIdentity, + IComparisonOperators, + IMinMaxValue, + ISpanParseable, + ISubtractionOperators, + ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // Constants internal const long MaxOffset = TimeSpan.TicksPerHour * 14; @@ -852,5 +864,112 @@ namespace System public static bool operator >=(DateTimeOffset left, DateTimeOffset right) => left.UtcDateTime >= right.UtcDateTime; + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static DateTimeOffset IAdditionOperators.operator +(DateTimeOffset left, TimeSpan right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked DateTimeOffset IAdditionOperators.operator +(DateTimeOffset left, TimeSpan right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static TimeSpan IAdditiveIdentity.AdditiveIdentity => default; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(DateTimeOffset left, DateTimeOffset right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(DateTimeOffset left, DateTimeOffset right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(DateTimeOffset left, DateTimeOffset right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(DateTimeOffset left, DateTimeOffset right) + => left >= right; + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(DateTimeOffset left, DateTimeOffset right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(DateTimeOffset left, DateTimeOffset right) + => left != right; + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static DateTimeOffset IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static DateTimeOffset IMinMaxValue.MaxValue => MaxValue; + + // + // IParseable + // + + [RequiresPreviewFeatures] + static DateTimeOffset IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out DateTimeOffset result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static DateTimeOffset ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out DateTimeOffset result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static DateTimeOffset ISubtractionOperators.operator -(DateTimeOffset left, TimeSpan right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked DateTimeOffset ISubtractionOperators.operator -(DateTimeOffset left, TimeSpan right) + // => checked(left - right); + + [RequiresPreviewFeatures] + static TimeSpan ISubtractionOperators.operator -(DateTimeOffset left, DateTimeOffset right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan ISubtractionOperators.operator -(DateTimeOffset left, DateTimeOffset right) + // => checked(left - right); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index 429d4c3cdf6..e5f26f2ff01 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Runtime.Versioning; namespace System { @@ -58,6 +59,12 @@ namespace System [System.Runtime.Versioning.NonVersionable] // This only applies to field layout [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly partial struct Decimal : ISpanFormattable, IComparable, IConvertible, IComparable, IEquatable, ISerializable, IDeserializationCallback +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // Sign mask for the flags field. A value of zero in this bit indicates a // positive Decimal value, and a value of one in this bit indicates a @@ -1072,5 +1079,535 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static decimal IAdditionOperators.operator +(decimal left, decimal right) + => checked(left + right); + + // [RequiresPreviewFeatures] + // static checked decimal IAdditionOperators.operator +(decimal left, decimal right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static decimal IAdditiveIdentity.AdditiveIdentity => 0.0m; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(decimal left, decimal right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(decimal left, decimal right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(decimal left, decimal right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(decimal left, decimal right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static decimal IDecrementOperators.operator --(decimal value) + => value--; + + // [RequiresPreviewFeatures] + // static checked decimal IDecrementOperators.operator --(decimal value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static decimal IDivisionOperators.operator /(decimal left, decimal right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked decimal IDivisionOperators.operator /(decimal left, decimal right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(decimal left, decimal right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(decimal left, decimal right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static decimal IIncrementOperators.operator ++(decimal value) + => value++; + + // [RequiresPreviewFeatures] + // static checked decimal IIncrementOperators.operator ++(decimal value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static decimal IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static decimal IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static decimal IModulusOperators.operator %(decimal left, decimal right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked decimal IModulusOperators.operator %(decimal left, decimal right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static decimal IMultiplicativeIdentity.MultiplicativeIdentity => 1.0m; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static decimal IMultiplyOperators.operator *(decimal left, decimal right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked decimal IMultiplyOperators.operator *(decimal left, decimal right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static decimal INumber.One => 1.0m; + + [RequiresPreviewFeatures] + static decimal INumber.Zero => 0.0m; + + [RequiresPreviewFeatures] + static decimal INumber.Abs(decimal value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static decimal INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (decimal)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (decimal)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static decimal INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (decimal)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (decimal)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static decimal INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (decimal)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (decimal)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static decimal INumber.Clamp(decimal value, decimal min, decimal max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + static (decimal Quotient, decimal Remainder) INumber.DivRem(decimal left, decimal right) + => (left / right, left % right); + + [RequiresPreviewFeatures] + static decimal INumber.Max(decimal x, decimal y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static decimal INumber.Min(decimal x, decimal y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static decimal INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static decimal INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static decimal INumber.Sign(decimal value) + => Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out decimal result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + result = (decimal)(object)value; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + result = (decimal)(double)(object)value; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + result = (long)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + result = (decimal)(float)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + result = (ulong)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out decimal result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out decimal result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static decimal IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out decimal result) + => TryParse(s, NumberStyles.Number, provider, out result); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static decimal ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static decimal ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Number, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out decimal result) + => TryParse(s, NumberStyles.Number, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static decimal ISubtractionOperators.operator -(decimal left, decimal right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked decimal ISubtractionOperators.operator -(decimal left, decimal right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static decimal IUnaryNegationOperators.operator -(decimal value) + => -value; + + // [RequiresPreviewFeatures] + // static checked decimal IUnaryNegationOperators.operator -(decimal value) + // => checked(-value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static decimal IUnaryPlusOperators.operator +(decimal value) + => +value; + + // [RequiresPreviewFeatures] + // static checked decimal IUnaryPlusOperators.operator +(decimal value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index d3c97412027..85d5f8c36f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -25,6 +25,12 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Double : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryFloatingPoint, + IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly double m_value; // Do not rename (binary serialization) @@ -50,11 +56,11 @@ namespace System internal const ulong SignMask = 0x8000_0000_0000_0000; internal const int SignShift = 63; - internal const int ShiftedSignMask = (int)(SignMask >> SignShift); + internal const uint ShiftedSignMask = (uint)(SignMask >> SignShift); internal const ulong ExponentMask = 0x7FF0_0000_0000_0000; internal const int ExponentShift = 52; - internal const int ShiftedExponentMask = (int)(ExponentMask >> ExponentShift); + internal const uint ShiftedExponentMask = (uint)(ExponentMask >> ExponentShift); internal const ulong SignificandMask = 0x000F_FFFF_FFFF_FFFF; @@ -144,7 +150,7 @@ namespace System internal static int ExtractExponentFromBits(ulong bits) { - return (int)(bits >> ExponentShift) & ShiftedExponentMask; + return (int)(bits >> ExponentShift) & (int)ShiftedExponentMask; } internal static ulong ExtractSignificandFromBits(ulong bits) @@ -444,5 +450,830 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static double IAdditionOperators.operator +(double left, double right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked double IAdditionOperators.operator +(double left, double right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static double IAdditiveIdentity.AdditiveIdentity => 0.0; + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(double value) + { + ulong bits = BitConverter.DoubleToUInt64Bits(value); + + uint exponent = (uint)(bits >> ExponentShift) & ShiftedExponentMask; + ulong significand = bits & SignificandMask; + + return (value > 0) + && (exponent != MinExponent) && (exponent != MaxExponent) + && (significand == MinSignificand); + } + + [RequiresPreviewFeatures] + static double IBinaryNumber.Log2(double value) + => Math.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static double IBitwiseOperators.operator &(double left, double right) + { + ulong bits = BitConverter.DoubleToUInt64Bits(left) & BitConverter.DoubleToUInt64Bits(right); + return BitConverter.UInt64BitsToDouble(bits); + } + + [RequiresPreviewFeatures] + static double IBitwiseOperators.operator |(double left, double right) + { + ulong bits = BitConverter.DoubleToUInt64Bits(left) | BitConverter.DoubleToUInt64Bits(right); + return BitConverter.UInt64BitsToDouble(bits); + } + + [RequiresPreviewFeatures] + static double IBitwiseOperators.operator ^(double left, double right) + { + ulong bits = BitConverter.DoubleToUInt64Bits(left) ^ BitConverter.DoubleToUInt64Bits(right); + return BitConverter.UInt64BitsToDouble(bits); + } + + [RequiresPreviewFeatures] + static double IBitwiseOperators.operator ~(double value) + { + ulong bits = ~BitConverter.DoubleToUInt64Bits(value); + return BitConverter.UInt64BitsToDouble(bits); + } + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(double left, double right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(double left, double right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(double left, double right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(double left, double right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static double IDecrementOperators.operator --(double value) + => value--; + + // [RequiresPreviewFeatures] + // static checked double IDecrementOperators.operator --(double value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static double IDivisionOperators.operator /(double left, double right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked double IDivisionOperators.operator /(double left, double right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(double left, double right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(double left, double right) + => left != right; + + // + // IFloatingPoint + // + + [RequiresPreviewFeatures] + static double IFloatingPoint.E => Math.E; + + [RequiresPreviewFeatures] + static double IFloatingPoint.Epsilon => Epsilon; + + [RequiresPreviewFeatures] + static double IFloatingPoint.NaN => NaN; + + [RequiresPreviewFeatures] + static double IFloatingPoint.NegativeInfinity => NegativeInfinity; + + [RequiresPreviewFeatures] + static double IFloatingPoint.NegativeZero => -0.0; + + [RequiresPreviewFeatures] + static double IFloatingPoint.Pi => Math.PI; + + [RequiresPreviewFeatures] + static double IFloatingPoint.PositiveInfinity => PositiveInfinity; + + [RequiresPreviewFeatures] + static double IFloatingPoint.Tau => Math.Tau; + + [RequiresPreviewFeatures] + static double IFloatingPoint.Acos(double x) + => Math.Acos(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Acosh(double x) + => Math.Acosh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Asin(double x) + => Math.Asin(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Asinh(double x) + => Math.Asinh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Atan(double x) + => Math.Atan(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Atan2(double y, double x) + => Math.Atan2(y, x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Atanh(double x) + => Math.Atanh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.BitIncrement(double x) + => Math.BitIncrement(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.BitDecrement(double x) + => Math.BitDecrement(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Cbrt(double x) + => Math.Cbrt(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Ceiling(double x) + => Math.Ceiling(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.CopySign(double x, double y) + => Math.CopySign(x, y); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Cos(double x) + => Math.Cos(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Cosh(double x) + => Math.Cosh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Exp(double x) + => Math.Exp(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Floor(double x) + => Math.Floor(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.FusedMultiplyAdd(double left, double right, double addend) + => Math.FusedMultiplyAdd(left, right, addend); + + [RequiresPreviewFeatures] + static double IFloatingPoint.IEEERemainder(double left, double right) + => Math.IEEERemainder(left, right); + + [RequiresPreviewFeatures] + static TInteger IFloatingPoint.ILogB(double x) + => TInteger.Create(Math.ILogB(x)); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Log(double x) + => Math.Log(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Log(double x, double newBase) + => Math.Log(x, newBase); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Log2(double x) + => Math.Log2(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Log10(double x) + => Math.Log10(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.MaxMagnitude(double x, double y) + => Math.MaxMagnitude(x, y); + + [RequiresPreviewFeatures] + static double IFloatingPoint.MinMagnitude(double x, double y) + => Math.MinMagnitude(x, y); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Pow(double x, double y) + => Math.Pow(x, y); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Round(double x) + => Math.Round(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Round(double x, TInteger digits) + => Math.Round(x, int.Create(digits)); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Round(double x, MidpointRounding mode) + => Math.Round(x, mode); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Round(double x, TInteger digits, MidpointRounding mode) + => Math.Round(x, int.Create(digits), mode); + + [RequiresPreviewFeatures] + static double IFloatingPoint.ScaleB(double x, TInteger n) + => Math.ScaleB(x, int.Create(n)); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Sin(double x) + => Math.Sin(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Sinh(double x) + => Math.Sinh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Sqrt(double x) + => Math.Sqrt(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Tan(double x) + => Math.Tan(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Tanh(double x) + => Math.Tanh(x); + + [RequiresPreviewFeatures] + static double IFloatingPoint.Truncate(double x) + => Math.Truncate(x); + + // static double IFloatingPoint.AcosPi(double x) + // => Math.AcosPi(x); + // + // static double IFloatingPoint.AsinPi(double x) + // => Math.AsinPi(x); + // + // static double IFloatingPoint.AtanPi(double x) + // => Math.AtanPi(x); + // + // static double IFloatingPoint.Atan2Pi(double y, double x) + // => Math.Atan2Pi(y, x); + // + // static double IFloatingPoint.Compound(double x, double n) + // => Math.Compound(x, n); + // + // static double IFloatingPoint.CosPi(double x) + // => Math.CosPi(x); + // + // static double IFloatingPoint.ExpM1(double x) + // => Math.ExpM1(x); + // + // static double IFloatingPoint.Exp2(double x) + // => Math.Exp2(x); + // + // static double IFloatingPoint.Exp2M1(double x) + // => Math.Exp2M1(x); + // + // static double IFloatingPoint.Exp10(double x) + // => Math.Exp10(x); + // + // static double IFloatingPoint.Exp10M1(double x) + // => Math.Exp10M1(x); + // + // static double IFloatingPoint.Hypot(double x, double y) + // => Math.Hypot(x, y); + // + // static double IFloatingPoint.LogP1(double x) + // => Math.LogP1(x); + // + // static double IFloatingPoint.Log2P1(double x) + // => Math.Log2P1(x); + // + // static double IFloatingPoint.Log10P1(double x) + // => Math.Log10P1(x); + // + // static double IFloatingPoint.MaxMagnitudeNumber(double x, double y) + // => Math.MaxMagnitudeNumber(x, y); + // + // static double IFloatingPoint.MaxNumber(double x, double y) + // => Math.MaxNumber(x, y); + // + // static double IFloatingPoint.MinMagnitudeNumber(double x, double y) + // => Math.MinMagnitudeNumber(x, y); + // + // static double IFloatingPoint.MinNumber(double x, double y) + // => Math.MinNumber(x, y); + // + // static double IFloatingPoint.Root(double x, double n) + // => Math.Root(x, n); + // + // static double IFloatingPoint.SinPi(double x) + // => Math.SinPi(x, y); + // + // static double TanPi(double x) + // => Math.TanPi(x, y); + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static double IIncrementOperators.operator ++(double value) + => value++; + + // [RequiresPreviewFeatures] + // static checked double IIncrementOperators.operator ++(double value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static double IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static double IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static double IModulusOperators.operator %(double left, double right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked double IModulusOperators.operator %(double left, double right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static double IMultiplicativeIdentity.MultiplicativeIdentity => 1.0; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static double IMultiplyOperators.operator *(double left, double right) + => (double)(left * right); + + // [RequiresPreviewFeatures] + // static checked double IMultiplyOperators.operator *(double left, double right) + // => checked((double)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static double INumber.One => 1.0; + + [RequiresPreviewFeatures] + static double INumber.Zero => 0.0; + + [RequiresPreviewFeatures] + static double INumber.Abs(double value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static double INumber.Clamp(double value, double min, double max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static double INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (double)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static double INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (double)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static double INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (double)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (double Quotient, double Remainder) INumber.DivRem(double left, double right) + => (left / right, left % right); + + [RequiresPreviewFeatures] + static double INumber.Max(double x, double y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static double INumber.Min(double x, double y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static double INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static double INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static double INumber.Sign(double value) + => Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out double result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + result = (double)(decimal)(object)value; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + result = (double)(object)value; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + result = (long)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + result = (float)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + result = (ulong)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out double result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out double result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static double IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out double result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static double ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static double ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out double result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static double ISubtractionOperators.operator -(double left, double right) + => (double)(left - right); + + // [RequiresPreviewFeatures] + // static checked double ISubtractionOperators.operator -(double left, double right) + // => checked((double)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static double IUnaryNegationOperators.operator -(double value) + => (double)(-value); + + // [RequiresPreviewFeatures] + // static checked double IUnaryNegationOperators.operator -(double value) + // => checked((double)(-value)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static double IUnaryPlusOperators.operator +(double value) + => (double)(+value); + + // [RequiresPreviewFeatures] + // static checked double IUnaryPlusOperators.operator +(double value) + // => checked((double)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 0a261754279..fa7d6acf9d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -19,6 +19,12 @@ namespace System [NonVersionable] // This only applies to field layout [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly partial struct Guid : ISpanFormattable, IComparable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IComparisonOperators, + ISpanParseable +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { public static readonly Guid Empty; @@ -1161,5 +1167,291 @@ namespace System // Like with the IFormattable implementation, provider is ignored. return TryFormat(destination, out charsWritten, format); } + +#if FEATURE_GENERIC_MATH + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a < (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b < (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c < (uint)right._c; + } + + if (left._d != right._d) + { + return left._d < right._d; + } + + if (left._e != right._e) + { + return left._e < right._e; + } + + if (left._f != right._f) + { + return left._f < right._f; + } + + if (left._g != right._g) + { + return left._g < right._g; + } + + if (left._h != right._h) + { + return left._h < right._h; + } + + if (left._i != right._i) + { + return left._i < right._i; + } + + if (left._j != right._j) + { + return left._j < right._j; + } + + if (left._k != right._k) + { + return left._k < right._k; + } + + return false; + } + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a < (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b < (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c < (uint)right._c; + } + + if (left._d != right._d) + { + return left._d < right._d; + } + + if (left._e != right._e) + { + return left._e < right._e; + } + + if (left._f != right._f) + { + return left._f < right._f; + } + + if (left._g != right._g) + { + return left._g < right._g; + } + + if (left._h != right._h) + { + return left._h < right._h; + } + + if (left._i != right._i) + { + return left._i < right._i; + } + + if (left._j != right._j) + { + return left._j < right._j; + } + + if (left._k != right._k) + { + return left._k < right._k; + } + + return true; + } + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a > (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b > (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c > (uint)right._c; + } + + if (left._d != right._d) + { + return left._d > right._d; + } + + if (left._e != right._e) + { + return left._e > right._e; + } + + if (left._f != right._f) + { + return left._f > right._f; + } + + if (left._g != right._g) + { + return left._g > right._g; + } + + if (left._h != right._h) + { + return left._h > right._h; + } + + if (left._i != right._i) + { + return left._i > right._i; + } + + if (left._j != right._j) + { + return left._j > right._j; + } + + if (left._k != right._k) + { + return left._k > right._k; + } + + return false; + } + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a > (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b > (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c > (uint)right._c; + } + + if (left._d != right._d) + { + return left._d > right._d; + } + + if (left._e != right._e) + { + return left._e > right._e; + } + + if (left._f != right._f) + { + return left._f > right._f; + } + + if (left._g != right._g) + { + return left._g > right._g; + } + + if (left._h != right._h) + { + return left._h > right._h; + } + + if (left._i != right._i) + { + return left._i > right._i; + } + + if (left._j != right._j) + { + return left._j > right._j; + } + + if (left._k != right._k) + { + return left._k > right._k; + } + + return true; + } + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(Guid left, Guid right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(Guid left, Guid right) + => left != right; + + // + // IParseable + // + + [RequiresPreviewFeatures] + static Guid IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Guid result) + => TryParse(s, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static Guid ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out Guid result) + => TryParse(s, out result); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index c5c5b353581..ea64c10f835 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -5,7 +5,9 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; namespace System { @@ -16,6 +18,12 @@ namespace System /// [StructLayout(LayoutKind.Sequential)] public readonly struct Half : IComparable, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryFloatingPoint, + IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private const NumberStyles DefaultParseStyle = NumberStyles.Float | NumberStyles.AllowThousands; @@ -23,9 +31,11 @@ namespace System private const ushort SignMask = 0x8000; private const ushort SignShift = 15; + private const ushort ShiftedSignMask = SignMask >> SignShift; private const ushort ExponentMask = 0x7C00; private const ushort ExponentShift = 10; + private const ushort ShiftedExponentMask = ExponentMask >> ExponentShift; private const ushort SignificandMask = 0x03FF; private const ushort SignificandShift = 0; @@ -690,5 +700,890 @@ namespace System => BitConverter.UInt64BitsToDouble(((sign ? 1UL : 0UL) << double.SignShift) + ((ulong)exp << double.ExponentShift) + sig); #endregion + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static Half IAdditionOperators.operator +(Half left, Half right) + => (Half)((float)left + (float)right); + + // [RequiresPreviewFeatures] + // static checked Half IAdditionOperators.operator +(Half left, Half right) + // => checked((Half)((float)left + (float)right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static Half IAdditiveIdentity.AdditiveIdentity => PositiveZero; + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(Half value) + { + uint bits = BitConverter.HalfToUInt16Bits(value); + + uint exponent = (bits >> ExponentShift) & ShiftedExponentMask; + uint significand = bits & SignificandMask; + + return (value > PositiveZero) + && (exponent != MinExponent) && (exponent != MaxExponent) + && (significand == MinSignificand); + } + + [RequiresPreviewFeatures] + static Half IBinaryNumber.Log2(Half value) + => (Half)MathF.Log2((float)value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static Half IBitwiseOperators.operator &(Half left, Half right) + { + ushort bits = (ushort)(BitConverter.HalfToUInt16Bits(left) & BitConverter.HalfToUInt16Bits(right)); + return BitConverter.UInt16BitsToHalf(bits); + } + + [RequiresPreviewFeatures] + static Half IBitwiseOperators.operator |(Half left, Half right) + { + ushort bits = (ushort)(BitConverter.HalfToUInt16Bits(left) | BitConverter.HalfToUInt16Bits(right)); + return BitConverter.UInt16BitsToHalf(bits); + } + + [RequiresPreviewFeatures] + static Half IBitwiseOperators.operator ^(Half left, Half right) + { + ushort bits = (ushort)(BitConverter.HalfToUInt16Bits(left) ^ BitConverter.HalfToUInt16Bits(right)); + return BitConverter.UInt16BitsToHalf(bits); + } + + [RequiresPreviewFeatures] + static Half IBitwiseOperators.operator ~(Half value) + { + ushort bits = (ushort)(~BitConverter.HalfToUInt16Bits(value)); + return BitConverter.UInt16BitsToHalf(bits); + } + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(Half left, Half right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(Half left, Half right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(Half left, Half right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(Half left, Half right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static Half IDecrementOperators.operator --(Half value) + { + var tmp = (float)value; + tmp--; + return (Half)tmp; + } + + // [RequiresPreviewFeatures] + // static checked Half IDecrementOperators.operator --(Half value) + // { + // var tmp = (float)value; + // tmp--; + // return (Half)tmp; + // } + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(Half left, Half right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(Half left, Half right) + => left != right; + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static Half IDivisionOperators.operator /(Half left, Half right) + => (Half)((float)left / (float)right); + + // [RequiresPreviewFeatures] + // static checked Half IDivisionOperators.operator /(Half left, Half right) + // => checked((Half)((float)left / (float)right)); + + // + // IFloatingPoint + // + + [RequiresPreviewFeatures] + static Half IFloatingPoint.E => (Half)MathF.E; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Epsilon => Epsilon; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.NaN => NaN; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.NegativeInfinity => NegativeInfinity; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.NegativeZero => NegativeZero; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Pi => (Half)MathF.PI; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.PositiveInfinity => PositiveInfinity; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Tau => (Half)MathF.Tau; + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Acos(Half x) + => (Half)MathF.Acos((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Acosh(Half x) + => (Half)MathF.Acosh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Asin(Half x) + => (Half)MathF.Asin((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Asinh(Half x) + => (Half)MathF.Asinh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Atan(Half x) + => (Half)MathF.Atan((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Atan2(Half y, Half x) + => (Half)MathF.Atan2((float)y, (float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Atanh(Half x) + => (Half)MathF.Atanh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.BitIncrement(Half x) + { + ushort bits = BitConverter.HalfToUInt16Bits(x); + + if ((bits & ExponentMask) >= ExponentMask) + { + // NaN returns NaN + // -Infinity returns float.MinValue + // +Infinity returns +Infinity + return (bits == (ExponentMask | SignMask)) ? MinValue : x; + } + + if (bits == NegativeZeroBits) + { + // -0.0 returns float.Epsilon + return Epsilon; + } + + // Negative values need to be decremented + // Positive values need to be incremented + + bits += unchecked((ushort)((bits < 0) ? -1 : +1)); + return BitConverter.UInt16BitsToHalf(bits); + } + + [RequiresPreviewFeatures] + static Half IFloatingPoint.BitDecrement(Half x) + { + ushort bits = BitConverter.HalfToUInt16Bits(x); + + if ((bits & ExponentMask) >= ExponentMask) + { + // NaN returns NaN + // -Infinity returns -Infinity + // +Infinity returns float.MaxValue + return (bits == ExponentMask) ? MaxValue : x; + } + + if (bits == PositiveZeroBits) + { + // +0.0 returns -float.Epsilon + return new Half(EpsilonBits | SignMask); + } + + // Negative values need to be incremented + // Positive values need to be decremented + + bits += (ushort)((bits < 0) ? +1 : -1); + return BitConverter.UInt16BitsToHalf(bits); + } + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Cbrt(Half x) + => (Half)MathF.Cbrt((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Ceiling(Half x) + => (Half)MathF.Ceiling((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.CopySign(Half x, Half y) + => (Half)MathF.CopySign((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Cos(Half x) + => (Half)MathF.Cos((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Cosh(Half x) + => (Half)MathF.Cosh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Exp(Half x) + => (Half)MathF.Exp((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Floor(Half x) + => (Half)MathF.Floor((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.FusedMultiplyAdd(Half left, Half right, Half addend) + => (Half)MathF.FusedMultiplyAdd((float)left, (float)right, (float)addend); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.IEEERemainder(Half left, Half right) + => (Half)MathF.IEEERemainder((float)left, (float)right); + + [RequiresPreviewFeatures] + static TInteger IFloatingPoint.ILogB(Half x) + => TInteger.Create(MathF.ILogB((float)x)); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Log(Half x) + => (Half)MathF.Log((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Log(Half x, Half newBase) + => (Half)MathF.Log((float)x, (float)newBase); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Log2(Half x) + => (Half)MathF.Log2((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Log10(Half x) + => (Half)MathF.Log10((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.MaxMagnitude(Half x, Half y) + => (Half)MathF.MaxMagnitude((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.MinMagnitude(Half x, Half y) + => (Half)MathF.MinMagnitude((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Pow(Half x, Half y) + => (Half)MathF.Pow((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Round(Half x) + => (Half)MathF.Round((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Round(Half x, TInteger digits) + => (Half)MathF.Round((float)x, int.Create(digits)); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Round(Half x, MidpointRounding mode) + => (Half)MathF.Round((float)x, mode); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Round(Half x, TInteger digits, MidpointRounding mode) + => (Half)MathF.Round((float)x, int.Create(digits), mode); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.ScaleB(Half x, TInteger n) + => (Half)MathF.ScaleB((float)x, int.Create(n)); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Sin(Half x) + => (Half)MathF.Sin((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Sinh(Half x) + => (Half)MathF.Sinh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Sqrt(Half x) + => (Half)MathF.Sqrt((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Tan(Half x) + => (Half)MathF.Tan((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Tanh(Half x) + => (Half)MathF.Tanh((float)x); + + [RequiresPreviewFeatures] + static Half IFloatingPoint.Truncate(Half x) + => (Half)MathF.Truncate((float)x); + + // static Half IFloatingPoint.AcosPi(Half x) + // => (Half)MathF.AcosPi((float)x); + // + // static Half IFloatingPoint.AsinPi(Half x) + // => (Half)MathF.AsinPi((float)x); + // + // static Half IFloatingPoint.AtanPi(Half x) + // => (Half)MathF.AtanPi((float)x); + // + // static Half IFloatingPoint.Atan2Pi(Half y, Half x) + // => (Half)MathF.Atan2Pi((float)y, (float)x); + // + // static Half IFloatingPoint.Compound(Half x, Half n) + // => (Half)MathF.Compound((float)x, (float)n); + // + // static Half IFloatingPoint.CosPi(Half x) + // => (Half)MathF.CosPi((float)x); + // + // static Half IFloatingPoint.ExpM1(Half x) + // => (Half)MathF.ExpM1((float)x); + // + // static Half IFloatingPoint.Exp2(Half x) + // => (Half)MathF.Exp2((float)x); + // + // static Half IFloatingPoint.Exp2M1(Half x) + // => (Half)MathF.Exp2M1((float)x); + // + // static Half IFloatingPoint.Exp10(Half x) + // => (Half)MathF.Exp10((float)x); + // + // static Half IFloatingPoint.Exp10M1(Half x) + // => (Half)MathF.Exp10M1((float)x); + // + // static Half IFloatingPoint.Hypot(Half x, Half y) + // => (Half)MathF.Hypot((float)x, (float)y); + // + // static Half IFloatingPoint.LogP1(Half x) + // => (Half)MathF.LogP1((float)x); + // + // static Half IFloatingPoint.Log2P1(Half x) + // => (Half)MathF.Log2P1((float)x); + // + // static Half IFloatingPoint.Log10P1(Half x) + // => (Half)MathF.Log10P1((float)x); + // + // static Half IFloatingPoint.MaxMagnitudeNumber(Half x, Half y) + // => (Half)MathF.MaxMagnitudeNumber((float)x, (float)y); + // + // static Half IFloatingPoint.MaxNumber(Half x, Half y) + // => (Half)MathF.MaxNumber((float)x, (float)y); + // + // static Half IFloatingPoint.MinMagnitudeNumber(Half x, Half y) + // => (Half)MathF.MinMagnitudeNumber((float)x, (float)y); + // + // static Half IFloatingPoint.MinNumber(Half x, Half y) + // => (Half)MathF.MinNumber((float)x, (float)y); + // + // static Half IFloatingPoint.Root(Half x, Half n) + // => (Half)MathF.Root((float)x, (float)n); + // + // static Half IFloatingPoint.SinPi(Half x) + // => (Half)MathF.SinPi((float)x, (float)y); + // + // static Half TanPi(Half x) + // => (Half)MathF.TanPi((float)x, (float)y); + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static Half IIncrementOperators.operator ++(Half value) + { + var tmp = (float)value; + tmp++; + return (Half)tmp; + } + + // [RequiresPreviewFeatures] + // static checked Half IIncrementOperators.operator ++(Half value) + // { + // var tmp = (float)value; + // tmp++; + // return (Half)tmp; + // } + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static Half IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static Half IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static Half IModulusOperators.operator %(Half left, Half right) + => (Half)((float)left % (float)right); + + // [RequiresPreviewFeatures] + // static checked Half IModulusOperators.operator %(Half left, Half right) + // => checked((Half)((float)left % (float)right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static Half IMultiplicativeIdentity.MultiplicativeIdentity => (Half)1.0f; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static Half IMultiplyOperators.operator *(Half left, Half right) + => (Half)((float)left * (float)right); + + // [RequiresPreviewFeatures] + // static checked Half IMultiplyOperators.operator *(Half left, Half right) + // => checked((Half)((float)left * (float)right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static Half INumber.One => (Half)1.0f; + + [RequiresPreviewFeatures] + static Half INumber.Zero => PositiveZero; + + [RequiresPreviewFeatures] + static Half INumber.Abs(Half value) + => (Half)MathF.Abs((float)value); + + [RequiresPreviewFeatures] + static Half INumber.Clamp(Half value, Half min, Half max) + => (Half)Math.Clamp((float)value, (float)min, (float)max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Half INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (Half)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (Half)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (Half)(float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (Half)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (Half)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (Half)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (Half)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (Half)(long)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (Half)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (Half)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (Half)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (Half)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (Half)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (Half)(ulong)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Half INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (Half)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (Half)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (Half)(float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (Half)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (Half)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (Half)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (Half)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (Half)(long)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (Half)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (Half)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (Half)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (Half)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (Half)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (Half)(ulong)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Half INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (Half)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (Half)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (Half)(float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (Half)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (Half)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (Half)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (Half)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (Half)(long)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (Half)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (Half)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (Half)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (Half)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (Half)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (Half)(ulong)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (Half Quotient, Half Remainder) INumber.DivRem(Half left, Half right) + => ((Half, Half))((float)left / (float)right, (float)left % (float)right); + + [RequiresPreviewFeatures] + static Half INumber.Max(Half x, Half y) + => (Half)MathF.Max((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half INumber.Min(Half x, Half y) + => (Half)MathF.Min((float)x, (float)y); + + [RequiresPreviewFeatures] + static Half INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static Half INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static Half INumber.Sign(Half value) + => (Half)MathF.Sign((float)value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out Half result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (Half)(byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (Half)(char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + result = (Half)(float)(decimal)(object)value; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + result = (Half)(double)(object)value; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (Half)(short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (Half)(int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + result = (Half)(long)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (Half)(long)(nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (Half)(sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + result = (Half)(float)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (Half)(ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (Half)(uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + result = (Half)(ulong)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (Half)(ulong)(nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Half result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Half result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static Half IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Half result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static Half ISignedNumber.NegativeOne => (Half)(-1.0f); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static Half ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out Half result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static Half ISubtractionOperators.operator -(Half left, Half right) + => (Half)((float)left - (float)right); + + // [RequiresPreviewFeatures] + // static checked Half ISubtractionOperators.operator -(Half left, Half right) + // => checked((Half)((float)left - (float)right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static Half IUnaryNegationOperators.operator -(Half value) + => (Half)(-(float)value); + + // [RequiresPreviewFeatures] + // static checked Half IUnaryNegationOperators.operator -(Half value) + // => checked((Half)(-(float)value)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static Half IUnaryPlusOperators.operator +(Half value) + => value; + + // [RequiresPreviewFeatures] + // static checked Half IUnaryPlusOperators.operator +(Half value) + // => checked(value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IAdditionOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IAdditionOperators.cs new file mode 100644 index 00000000000..fb09cfac334 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IAdditionOperators.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the sum of two values. + /// The type that implements this interface. + /// The type that will be added to . + /// The type that contains the sum of and . + [RequiresPreviewFeatures] + public interface IAdditionOperators + where TSelf : IAdditionOperators + { + /// Adds two values together to compute their sum. + /// The value to which is added. + /// The value which is added to . + /// The sum of and . + static abstract TResult operator +(TSelf left, TOther right); + + // /// Adds two values together to compute their sum. + // /// The value to which is added. + // /// The value which is added to . + // /// The sum of and . + // /// The sum of and is not representable by . + // static abstract checked TResult operator +(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IAdditiveIdentity.cs b/src/libraries/System.Private.CoreLib/src/System/IAdditiveIdentity.cs new file mode 100644 index 00000000000..5b07145c328 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IAdditiveIdentity.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for getting the additive identity of a given type. + /// The type that implements this interface. + /// The type that contains the additive identify of . + [RequiresPreviewFeatures] + public interface IAdditiveIdentity + where TSelf : IAdditiveIdentity + { + /// Gets the additive identity of the current type. + static abstract TResult AdditiveIdentity { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IBitwiseOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IBitwiseOperators.cs new file mode 100644 index 00000000000..d4d1a35d0e6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IBitwiseOperators.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for performing bitwise operations over two values. + /// The type that implements this interface. + /// The type that will is used in the operation with . + /// The type that contains the result of op . + [RequiresPreviewFeatures] + public interface IBitwiseOperators + where TSelf : IBitwiseOperators + { + /// Computes the bitwise-and of two values. + /// The value to and with . + /// The value to and with . + /// The bitwise-and of and . + static abstract TResult operator &(TSelf left, TOther right); + + /// Computes the bitwise-or of two values. + /// The value to or with . + /// The value to or with . + /// The bitwise-or of and . + static abstract TResult operator |(TSelf left, TOther right); + + /// Computes the exclusive-or of two values. + /// The value to xor with . + /// The value to xorwith . + /// The exclusive-or of and . + static abstract TResult operator ^(TSelf left, TOther right); + + /// Computes the ones-complement representation of a given value. + /// The value for which to compute its ones-complement. + /// The ones-complement of . + static abstract TResult operator ~(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IComparisonOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IComparisonOperators.cs new file mode 100644 index 00000000000..5bd4a22e28c --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IComparisonOperators.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for comparing two values to determine relative order. + /// The type that implements this interface. + /// The type that will be compared with . + [RequiresPreviewFeatures] + public interface IComparisonOperators + : IComparable, + IComparable, + IEqualityOperators + where TSelf : IComparisonOperators + { + /// Compares two values to determine which is less. + /// The value to compare with . + /// The value to compare with . + /// true if is less than ; otherwise, false. + static abstract bool operator <(TSelf left, TOther right); + + /// Compares two values to determine which is less or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is less than or equal to ; otherwise, false. + static abstract bool operator <=(TSelf left, TOther right); + + /// Compares two values to determine which is greater. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than ; otherwise, false. + static abstract bool operator >(TSelf left, TOther right); + + /// Compares two values to determine which is greater or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than or equal to ; otherwise, false. + static abstract bool operator >=(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IDecrementOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IDecrementOperators.cs new file mode 100644 index 00000000000..9a240b14a96 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IDecrementOperators.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for decrementing a given value. + /// The type that implements this interface. + [RequiresPreviewFeatures] + public interface IDecrementOperators + where TSelf : IDecrementOperators + { + /// Decrements a value. + /// The value to decrement. + /// The result of decrementing . + static abstract TSelf operator --(TSelf value); + + // /// Decrements a value. + // /// The value to decrement. + // /// The result of decrementing . + // /// The result of decrementing is not representable by . + // static abstract checked TSelf operator --(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IDivisionOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IDivisionOperators.cs new file mode 100644 index 00000000000..4beba8cddbd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IDivisionOperators.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the quotient of two values. + /// The type that implements this interface. + /// The type that will divide . + /// The type that contains the quotient of and . + [RequiresPreviewFeatures] + public interface IDivisionOperators + where TSelf : IDivisionOperators + { + /// Divides two values together to compute their quotient. + /// The value which divides. + /// The value which divides . + /// The quotient of divided-by . + static abstract TResult operator /(TSelf left, TOther right); + + // /// Divides two values together to compute their quotient. + // /// The value which divides. + // /// The value which divides . + // /// The quotient of divided-by . + // /// The quotient of divided-by is not representable by . + // static abstract checked TResult operator /(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IEqualityOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IEqualityOperators.cs new file mode 100644 index 00000000000..f6f8fa5050a --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IEqualityOperators.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for comparing two values to determine equality. + /// The type that implements this interface. + /// The type that will be compared with . + [RequiresPreviewFeatures] + public interface IEqualityOperators : IEquatable + where TSelf : IEqualityOperators + { + /// Compares two values to determine equality. + /// The value to compare with . + /// The value to compare with . + /// true if is equal to ; otherwise, false. + static abstract bool operator ==(TSelf left, TOther right); + + /// Compares two values to determine inequality. + /// The value to compare with . + /// The value to compare with . + /// true if is not equal to ; otherwise, false. + static abstract bool operator !=(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IFloatingPoint.cs b/src/libraries/System.Private.CoreLib/src/System/IFloatingPoint.cs new file mode 100644 index 00000000000..cb69e03fd77 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IFloatingPoint.cs @@ -0,0 +1,342 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a floating-point type. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface IFloatingPoint + : ISignedNumber + where TSelf : IFloatingPoint + { + /// Gets the mathematical constant e. + static abstract TSelf E { get; } + + /// Gets the smallest value such that can be added to 0 that does not result in 0. + static abstract TSelf Epsilon { get; } + + /// Gets a value that represents NaN. + static abstract TSelf NaN { get; } + + /// Gets a value that represents negative infinity. + static abstract TSelf NegativeInfinity { get; } + + /// Gets a value that represents negative zero. + static abstract TSelf NegativeZero { get; } + + /// Gets the mathematical constant pi. + static abstract TSelf Pi { get; } + + /// Gets a value that represents positive infinity. + static abstract TSelf PositiveInfinity { get; } + + /// Gets the mathematical constant tau. + static abstract TSelf Tau { get; } + + /// Computes the arc-cosine of a value. + /// The value, in radians, whose arc-cosine is to be computed. + /// The arc-cosine of . + static abstract TSelf Acos(TSelf x); + + /// Computes the hyperbolic arc-cosine of a value. + /// The value, in radians, whose hyperbolic arc-cosine is to be computed. + /// The hyperbolic arc-cosine of . + static abstract TSelf Acosh(TSelf x); + + /// Computes the arc-sine of a value. + /// The value, in radians, whose arc-sine is to be computed. + /// The arc-sine of . + static abstract TSelf Asin(TSelf x); + + /// Computes the hyperbolic arc-sine of a value. + /// The value, in radians, whose hyperbolic arc-sine is to be computed. + /// The hyperbolic arc-sine of . + static abstract TSelf Asinh(TSelf x); + + /// Computes the arc-tangent of a value. + /// The value, in radians, whose arc-tangent is to be computed. + /// The arc-tangent of . + static abstract TSelf Atan(TSelf x); + + /// Computes the arc-tangent of the quotient of two values. + /// The y-coordinate of a point. + /// The x-coordinate of a point. + /// The arc-tangent of divided-by . + static abstract TSelf Atan2(TSelf y, TSelf x); + + /// Computes the hyperbolic arc-tangent of a value. + /// The value, in radians, whose hyperbolic arc-tangent is to be computed. + /// The hyperbolic arc-tangent of . + static abstract TSelf Atanh(TSelf x); + + /// Decrements a value to the smallest value that compares less than a given value. + /// The value to be bitwise decremented. + /// The smallest value that compares less than . + static abstract TSelf BitDecrement(TSelf x); + + /// Increments a value to the smallest value that compares greater than a given value. + /// The value to be bitwise incremented. + /// The smallest value that compares greater than . + static abstract TSelf BitIncrement(TSelf x); + + /// Computes the cube-root of a value. + /// The value whose cube-root is to be computed. + /// The cube-root of . + static abstract TSelf Cbrt(TSelf x); + + /// Computes the ceiling of a value. + /// The value whose ceiling is to be computed. + /// The ceiling of . + static abstract TSelf Ceiling(TSelf x); + + /// Copies the sign of a value to the sign of another value.. + /// The value whose magnitude is used in the result. + /// The value whose sign is used in the result. + /// A value with the magnitude of and the sign of . + static abstract TSelf CopySign(TSelf x, TSelf y); + + /// Computes the cosine of a value. + /// The value, in radians, whose cosine is to be computed. + /// The cosine of . + static abstract TSelf Cos(TSelf x); + + /// Computes the hyperbolic cosine of a value. + /// The value, in radians, whose hyperbolic cosine is to be computed. + /// The hyperbolic cosine of . + static abstract TSelf Cosh(TSelf x); + + /// Computes raised to a given power. + /// The power to which is raised. + /// raised to the power of . + static abstract TSelf Exp(TSelf x); + + /// Computes the floor of a value. + /// The value whose floor is to be computed. + /// The floor of . + static abstract TSelf Floor(TSelf x); + + /// Computes the fused multiply-add of three values. + /// The value which multiplies. + /// The value which multiplies . + /// The value that is added to the product of and . + /// The result of times plus computed as one ternary operation. + static abstract TSelf FusedMultiplyAdd(TSelf left, TSelf right, TSelf addend); + + /// Computes the remainder of two values as specified by IEEE 754. + /// The value which divides. + /// The value which divides . + /// The remainder of divided-by as specified by IEEE 754. + static abstract TSelf IEEERemainder(TSelf left, TSelf right); + + /// Computes the integer logarithm of a value. + /// The value whose integer logarithm is to be computed. + /// The integer logarithm of . + static abstract TInteger ILogB(TSelf x) + where TInteger : IBinaryInteger; + + /// Determines if a value is finite. + /// The value to be checked. + /// true if is finite; otherwise, false. + static abstract bool IsFinite(TSelf value); + + /// Determines if a value is infinite. + /// The value to be checked. + /// true if is infinite; otherwise, false. + static abstract bool IsInfinity(TSelf value); + + /// Determines if a value is NaN. + /// The value to be checked. + /// true if is NaN; otherwise, false. + static abstract bool IsNaN(TSelf value); + + /// Determines if a value is negative. + /// The value to be checked. + /// true if is negative; otherwise, false. + static abstract bool IsNegative(TSelf value); + + /// Determines if a value is negative infinity. + /// The value to be checked. + /// true if is negative infinity; otherwise, false. + static abstract bool IsNegativeInfinity(TSelf value); + + /// Determines if a value is normal. + /// The value to be checked. + /// true if is normal; otherwise, false. + static abstract bool IsNormal(TSelf value); + + /// Determines if a value is positive infinity. + /// The value to be checked. + /// true if is positive infinity; otherwise, false. + static abstract bool IsPositiveInfinity(TSelf value); + + /// Determines if a value is subnormal. + /// The value to be checked. + /// true if is subnormal; otherwise, false. + static abstract bool IsSubnormal(TSelf value); + + /// Computes the natural (base- logarithm of a value. + /// The value whose natural logarithm is to be computed. + /// The natural logarithm of . + static abstract TSelf Log(TSelf x); + + /// Computes the logarithm of a value in the specified base. + /// The value whose logarithm is to be computed. + /// The base in which the logarithm is to be computed. + /// The base- logarithm of . + static abstract TSelf Log(TSelf x, TSelf newBase); + + /// Computes the base-2 logarithm of a value. + /// The value whose base-2 logarithm is to be computed. + /// The base-2 logarithm of . + static abstract TSelf Log2(TSelf x); + + /// Computes the base-10 logarithm of a value. + /// The value whose base-10 logarithm is to be computed. + /// The base-10 logarithm of . + static abstract TSelf Log10(TSelf x); + + /// Compares two values to compute which is greater. + /// The value to compare with . + /// The value to compare with . + /// if it is greater than ; otherwise, . + /// For this method matches the IEEE 754:2019 maximumMagnitude function. This requires NaN inputs to not be propagated back to the caller and for -0.0 to be treated as less than +0.0. + static abstract TSelf MaxMagnitude(TSelf x, TSelf y); + + /// Compares two values to compute which is lesser. + /// The value to compare with . + /// The value to compare with . + /// if it is less than ; otherwise, . + /// For this method matches the IEEE 754:2019 minimumMagnitude function. This requires NaN inputs to not be propagated back to the caller and for -0.0 to be treated as less than +0.0. + static abstract TSelf MinMagnitude(TSelf x, TSelf y); + + /// Computes a value raised to a given power. + /// The value which is raised to the power of . + /// The power to which is raised. + /// raised to the power of . + static abstract TSelf Pow(TSelf x, TSelf y); + + /// Rounds a value to the nearest integer using the default rounding mode (). + /// The value to round. + /// The result of rounding to the nearest integer using the default rounding mode. + static abstract TSelf Round(TSelf x); + + /// Rounds a value to a specified number of fractional-digits using the default rounding mode (). + /// The value to round. + /// The number of fractional digits to which should be rounded. + /// The result of rounding to fractional-digits using the default rounding mode. + static abstract TSelf Round(TSelf x, TInteger digits) + where TInteger : IBinaryInteger; + + /// Rounds a value to the nearest integer using the specified rounding mode. + /// The value to round. + /// The mode under which should be rounded. + /// The result of rounding to the nearest integer using . + static abstract TSelf Round(TSelf x, MidpointRounding mode); + + /// Rounds a value to a specified number of fractional-digits using the default rounding mode (). + /// The value to round. + /// The number of fractional digits to which should be rounded. + /// The mode under which should be rounded. + /// The result of rounding to fractional-digits using . + static abstract TSelf Round(TSelf x, TInteger digits, MidpointRounding mode) + where TInteger : IBinaryInteger; + + /// Computes the product of a value and its base-radix raised to the specified power. + /// The value which base-radix raised to the power of multiplies. + /// The value to which base-radix is raised before multipliying . + /// The product of and base-radix raised to the power of . + static abstract TSelf ScaleB(TSelf x, TInteger n) + where TInteger : IBinaryInteger; + + /// Computes the sine of a value. + /// The value, in radians, whose sine is to be computed. + /// The sine of . + static abstract TSelf Sin(TSelf x); + + /// Computes the hyperbolic sine of a value. + /// The value, in radians, whose hyperbolic sine is to be computed. + /// The hyperbolic sine of . + static abstract TSelf Sinh(TSelf x); + + /// Computes the square-root of a value. + /// The value whose square-root is to be computed. + /// The square-root of . + static abstract TSelf Sqrt(TSelf x); + + /// Computes the tangent of a value. + /// The value, in radians, whose tangent is to be computed. + /// The tangent of . + static abstract TSelf Tan(TSelf x); + + /// Computes the hyperbolic tangent of a value. + /// The value, in radians, whose hyperbolic tangent is to be computed. + /// The hyperbolic tangent of . + static abstract TSelf Tanh(TSelf x); + + /// Truncates a value. + /// The value to truncate. + /// The truncation of . + static abstract TSelf Truncate(TSelf x); + + // static abstract TSelf AcosPi(TSelf x); + // + // static abstract TSelf AsinPi(TSelf x); + // + // static abstract TSelf AtanPi(TSelf x); + // + // static abstract TSelf Atan2Pi(TSelf y, TSelf x); + // + // static abstract TSelf Compound(TSelf x, TSelf n); + // + // static abstract TSelf CosPi(TSelf x); + // + // static abstract TSelf ExpM1(TSelf x); + // + // static abstract TSelf Exp2(TSelf x); + // + // static abstract TSelf Exp2M1(TSelf x); + // + // static abstract TSelf Exp10(TSelf x); + // + // static abstract TSelf Exp10M1(TSelf x); + // + // static abstract TSelf Hypot(TSelf x, TSelf y); + // + // static abstract TSelf LogP1(TSelf x); + // + // static abstract TSelf Log2P1(TSelf x); + // + // static abstract TSelf Log10P1(TSelf x); + // + // static abstract TSelf MaxMagnitudeNumber(TSelf x, TSelf y); + // + // static abstract TSelf MaxNumber(TSelf x, TSelf y); + // + // static abstract TSelf MinMagnitudeNumber(TSelf x, TSelf y); + // + // static abstract TSelf MinNumber(TSelf x, TSelf y); + // + // static abstract TSelf Root(TSelf x, TSelf n); + // + // static abstract TSelf SinPi(TSelf x); + // + // static abstract TSelf TanPi(TSelf x); + } + + /// Defines a floating-point type that is represented in a base-2 format. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface IBinaryFloatingPoint + : IBinaryNumber, + IFloatingPoint + where TSelf : IBinaryFloatingPoint + { + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IIncrementOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IIncrementOperators.cs new file mode 100644 index 00000000000..5b4986a5fc2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IIncrementOperators.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for incrementing a given value. + /// The type that implements this interface. + [RequiresPreviewFeatures] + public interface IIncrementOperators + where TSelf : IIncrementOperators + { + /// Increments a value. + /// The value to increment. + /// The result of incrementing . + static abstract TSelf operator ++(TSelf value); + + // /// Increments a value. + // /// The value to increment. + // /// The result of incrementing . + // /// The result of incrementing is not representable by . + // static abstract checked TSelf operator ++(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IInteger.cs b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs new file mode 100644 index 00000000000..a93b439625e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines an integer type that is represented in a base-2 format. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface IBinaryInteger + : IBinaryNumber, + IShiftOperators + where TSelf : IBinaryInteger + { + /// Computes the number of leading zeros in a value. + /// The value whose leading zeroes are to be counted. + /// The number of leading zeros in . + static abstract TSelf LeadingZeroCount(TSelf value); + + /// Computes the number of bits that are set in a value. + /// The value whose set bits are to be counted. + /// The number of set bits in . + static abstract TSelf PopCount(TSelf value); + + /// Rotates a value left by a given amount. + /// The value which is rotated left by . + /// The amount by which is rotated left. + /// The result of rotating left by . + static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); + + /// Rotates a value right by a given amount. + /// The value which is rotated right by . + /// The amount by which is rotated right. + /// The result of rotating right by . + static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + + /// Computes the number of trailing zeros in a value. + /// The value whose trailing zeroes are to be counted. + /// The number of trailing zeros in . + static abstract TSelf TrailingZeroCount(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IMinMaxValue.cs b/src/libraries/System.Private.CoreLib/src/System/IMinMaxValue.cs new file mode 100644 index 00000000000..cc1eae37873 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IMinMaxValue.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for getting the minimum and maximum value of a type. + /// The type that implements this interface. + [RequiresPreviewFeatures] + public interface IMinMaxValue + where TSelf : IMinMaxValue + { + /// Gets the minimum value of the current type. + static abstract TSelf MinValue { get; } + + /// Gets the maximum value of the current type. + static abstract TSelf MaxValue { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IModulusOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IModulusOperators.cs new file mode 100644 index 00000000000..24b5beae107 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IModulusOperators.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the modulus or remainder of two values. + /// The type that implements this interface. + /// The type that will divide . + /// The type that contains the modulus or remainder of and . + /// This type represents the % in C# which is often used to compute the remainder and may differ from an actual modulo operation depending on the type that implements the interface. + [RequiresPreviewFeatures] + public interface IModulusOperators + where TSelf : IModulusOperators + { + /// Divides two values together to compute their modulus or remainder. + /// The value which divides. + /// The value which divides . + /// The modulus or remainder of divided-by . + static abstract TResult operator %(TSelf left, TOther right); + + // /// Divides two values together to compute their modulus or remainder. + // /// The value which divides. + // /// The value which divides . + // /// The modulus or remainder of divided-by . + // /// The modulus or remainder of divided-by is not representable by . + // static abstract checked TResult operator %(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IMultiplicativeIdentity.cs b/src/libraries/System.Private.CoreLib/src/System/IMultiplicativeIdentity.cs new file mode 100644 index 00000000000..9560071ae51 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IMultiplicativeIdentity.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for getting the multiplicative identity of a given type. + /// The type that implements this interface. + /// The type that contains the multiplicative identify of . + [RequiresPreviewFeatures] + public interface IMultiplicativeIdentity + where TSelf : IMultiplicativeIdentity + { + /// Gets the multiplicative identity of the current type. + static abstract TResult MultiplicativeIdentity { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IMultiplyOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IMultiplyOperators.cs new file mode 100644 index 00000000000..e9fc0fc1c19 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IMultiplyOperators.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the product of two values. + /// The type that implements this interface. + /// The type that will multiply . + /// The type that contains the product of and . + [RequiresPreviewFeatures] + public interface IMultiplyOperators + where TSelf : IMultiplyOperators + { + /// Multiplies two values together to compute their product. + /// The value which multiplies. + /// The value which multiplies . + /// The product of divided-by . + static abstract TResult operator *(TSelf left, TOther right); + + // /// Multiplies two values together to compute their product. + // /// The value which multiplies. + // /// The value which multiplies . + // /// The product of divided-by . + // /// The product of multiplied-by is not representable by . + // static abstract checked TResult operator *(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/INumber.cs b/src/libraries/System.Private.CoreLib/src/System/INumber.cs new file mode 100644 index 00000000000..c1a4f8b109c --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/INumber.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a number type. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface INumber + : IAdditionOperators, + IAdditiveIdentity, + IComparisonOperators, // implies IEquatableOperators + IDecrementOperators, + IDivisionOperators, + IIncrementOperators, + IModulusOperators, + IMultiplicativeIdentity, + IMultiplyOperators, + ISpanFormattable, // implies IFormattable + ISpanParseable, // implies IParseable + ISubtractionOperators, + IUnaryNegationOperators, + IUnaryPlusOperators + where TSelf : INumber + { + /// Gets the value 1 for the type. + static abstract TSelf One { get; } + + /// Gets the value 0 for the type. + static abstract TSelf Zero { get; } + + /// Computes the absolute of a value. + /// The value for which to get its absolute. + /// The absolute of . + /// The absolute of is not representable by . + static abstract TSelf Abs(TSelf value); + + /// Clamps a value to an inclusive minimum and maximum value. + /// The value to clamp. + /// The inclusive minimum to which should clamp. + /// The inclusive maximum to which should clamp. + /// The result of clamping to the inclusive range of and . + /// is greater than . + static abstract TSelf Clamp(TSelf value, TSelf min, TSelf max); + + /// Creates an instance of the current type from a value. + /// The type of . + /// The value which is used to create the instance of . + /// An instance of created from . + /// is not supported. + /// is not representable by . + static abstract TSelf Create(TOther value) + where TOther : INumber; + + /// Creates an instance of the current type from a value, saturating any values that fall outside the representable range of the current type. + /// The type of . + /// The value which is used to create the instance of . + /// An instance of created from , saturating if falls outside the representable range of . + /// is not supported. + static abstract TSelf CreateSaturating(TOther value) + where TOther : INumber; + + /// Creates an instance of the current type from a value, truncating any values that fall outside the representable range of the current type. + /// The type of . + /// The value which is used to create the instance of . + /// An instance of created from , truncating if falls outside the representable range of . + /// is not supported. + static abstract TSelf CreateTruncating(TOther value) + where TOther : INumber; + + /// Computes the quotient and remainder of two values. + /// The value which divides. + /// The value which divides . + /// The quotient and remainder of divided-by . + static abstract (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right); + + /// Compares two values to compute which is greater. + /// The value to compare with . + /// The value to compare with . + /// if it is greater than ; otherwise, . + /// For this method matches the IEEE 754:2019 maximum function. This requires NaN inputs to be propagated back to the caller and for -0.0 to be treated as less than +0.0. + static abstract TSelf Max(TSelf x, TSelf y); + + /// Compares two values to compute which is lesser. + /// The value to compare with . + /// The value to compare with . + /// if it is less than ; otherwise, . + /// For this method matches the IEEE 754:2019 minimum function. This requires NaN inputs to be propagated back to the caller and for -0.0 to be treated as less than +0.0. + static abstract TSelf Min(TSelf x, TSelf y); + + /// Parses a string into a value. + /// The string to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// The result of parsing . + /// is not a supported value. + /// is null. + /// is not in the correct format. + /// is not representable by . + static abstract TSelf Parse(string s, NumberStyles style, IFormatProvider? provider); + + /// Parses a span of characters into a value. + /// The span of characters to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// The result of parsing . + /// is not a supported value. + /// is not in the correct format. + /// is not representable by . + static abstract TSelf Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider); + + /// Computes the sign of a value. + /// The value whose sign is to be computed. + /// A positive value if is positive, if is zero, and a negative value if is negative. + /// It is recommended that a function return 1, 0, and -1, respectively. + static abstract TSelf Sign(TSelf value); + + /// Tries to create an instance of the current type from a value. + /// The type of . + /// The value which is used to create the instance of . + /// On return, contains the result of succesfully creating an instance of from or an undefined value on failure. + /// true if an instance of the current type was succesfully created from ; otherwise, false. + /// is not supported. + static abstract bool TryCreate(TOther value, out TSelf result) + where TOther : INumber; + + /// Tries to parses a string into a value. + /// The string to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of succesfully parsing or an undefined value on failure. + /// true if was successfully parsed; otherwise, false. + /// is not a supported value. + static abstract bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out TSelf result); + + /// Tries to parses a span of characters into a value. + /// The span of characters to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of succesfully parsing or an undefined value on failure. + /// true if was successfully parsed; otherwise, false. + /// is not a supported value. + static abstract bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out TSelf result); + } + + /// Defines a number that is represented in a base-2 format. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface IBinaryNumber + : IBitwiseOperators, + INumber + where TSelf : IBinaryNumber + { + /// Determines if a value is a power of two. + /// The value to be checked. + /// true if is a power of two; otherwise, false. + static abstract bool IsPow2(TSelf value); + + /// Computes the log2 of a value. + /// The value whose log2 is to be computed. + /// The log2 of . + static abstract TSelf Log2(TSelf value); + } + + /// Defines a number type which can represent both positive and negative values. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface ISignedNumber + : INumber + where TSelf : ISignedNumber + { + /// Gets the value -1 for the type. + static abstract TSelf NegativeOne { get; } + } + + /// Defines a number type which can only represent positive values, that is it cannot represent negative values. + /// The type that implements the interface. + [RequiresPreviewFeatures] + public interface IUnsignedNumber + : INumber + where TSelf : IUnsignedNumber + { + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IParseable.cs b/src/libraries/System.Private.CoreLib/src/System/IParseable.cs new file mode 100644 index 00000000000..77064639e36 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IParseable.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for parsing a string to a value. + /// The type that implements this interface. + [RequiresPreviewFeatures] + public interface IParseable + where TSelf : IParseable + { + /// Parses a string into a value. + /// The string to parse. + /// An object that provides culture-specific formatting information about . + /// The result of parsing . + /// is null. + /// is not in the correct format. + /// is not representable by . + static abstract TSelf Parse(string s, IFormatProvider? provider); + + /// Tries to parses a string into a value. + /// The string to parse. + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of succesfully parsing or an undefined value on failure. + /// true if was successfully parsed; otherwise, false. + static abstract bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out TSelf result); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IShiftOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IShiftOperators.cs new file mode 100644 index 00000000000..8ba4d810233 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IShiftOperators.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for shifting a value by another value. + /// The type that implements this interface. + /// The type that contains the result of shifting by . + [RequiresPreviewFeatures] + public interface IShiftOperators + where TSelf : IShiftOperators + { + /// Shifts a value left by a given amount. + /// The value which is shifted left by . + /// The amount by which is shifted left. + /// The result of shifting left by . + static abstract TResult operator <<(TSelf value, int shiftAmount); // TODO_GENERIC_MATH: shiftAmount should be TOther + + /// Shifts a value right by a given amount. + /// The value which is shifted right by . + /// The amount by which is shifted right. + /// The result of shifting right by . + /// This operation is meant to perform a signed (otherwise known as an arithmetic) right shift on signed types. + static abstract TResult operator >>(TSelf value, int shiftAmount); // TODO_GENERIC_MATH: shiftAmount should be TOther + + // /// Shifts a value right by a given amount. + // /// The value which is shifted right by . + // /// The amount by which is shifted right. + // /// The result of shifting right by . + // /// This operation is meant to perform n unsigned (otherwise known as a logical) right shift on all types. + // static abstract TResult operator >>>(TSelf value, int shiftAmount); // TODO_GENERIC_MATH: shiftAmount should be TOther + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/ISpanParseable.cs b/src/libraries/System.Private.CoreLib/src/System/ISpanParseable.cs new file mode 100644 index 00000000000..6264fe15802 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/ISpanParseable.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for parsing a span of characters to a value. + /// The type that implements this interface. + [RequiresPreviewFeatures] + public interface ISpanParseable : IParseable + where TSelf : ISpanParseable + { + /// Parses a span of characters into a value. + /// The span of characters to parse. + /// An object that provides culture-specific formatting information about . + /// The result of parsing . + /// is not in the correct format. + /// is not representable by . + static abstract TSelf Parse(ReadOnlySpan s, IFormatProvider? provider); + + /// Tries to parses a span of characters into a value. + /// The span of characters to parse. + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of succesfully parsing or an undefined value on failure. + /// true if was successfully parsed; otherwise, false. + static abstract bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out TSelf result); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/ISubtractionOperators.cs b/src/libraries/System.Private.CoreLib/src/System/ISubtractionOperators.cs new file mode 100644 index 00000000000..f11da9d2f98 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/ISubtractionOperators.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the difference of two values. + /// The type that implements this interface. + /// The type that will be subtracted from . + /// The type that contains the difference of subtracted from . + [RequiresPreviewFeatures] + public interface ISubtractionOperators + where TSelf : ISubtractionOperators + { + /// Subtracts two values to compute their difference. + /// The value from which is subtracted. + /// The value which is subtracted from . + /// The difference of subtracted from . + static abstract TResult operator -(TSelf left, TOther right); + + // /// Subtracts two values to compute their difference. + // /// The value from which is subtracted. + // /// The value which is subtracted from . + // /// The difference of subtracted from . + // /// The difference of subtracted from is not representable by . + // static abstract checked TResult operator -(TSelf left, TOther right); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IUnaryNegationOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IUnaryNegationOperators.cs new file mode 100644 index 00000000000..686887ce9c8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IUnaryNegationOperators.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the unary negation of a value. + /// The type that implements this interface. + /// The type that contains the result of negating . + [RequiresPreviewFeatures] + public interface IUnaryNegationOperators + where TSelf : IUnaryNegationOperators + { + /// Computes the unary negation of a value. + /// The value for which to compute its unary negation. + /// The unary negation of . + static abstract TResult operator -(TSelf value); + + // /// Computes the unary negation of a value. + // /// The value for which to compute its unary negation. + // /// The unary negation of . + // /// The unary negation of is not representable by . + // static abstract checked TResult operator -(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IUnaryPlusOperators.cs b/src/libraries/System.Private.CoreLib/src/System/IUnaryPlusOperators.cs new file mode 100644 index 00000000000..a719ff5d0b1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IUnaryPlusOperators.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +#if !FEATURE_GENERIC_MATH +#error FEATURE_GENERIC_MATH is not defined +#endif + +namespace System +{ + /// Defines a mechanism for computing the unary plus of a value. + /// The type that implements this interface. + /// The type that contains the result of negating . + [RequiresPreviewFeatures] + public interface IUnaryPlusOperators + where TSelf : IUnaryPlusOperators + { + /// Computes the unary plus of a value. + /// The value for which to compute its unary plus. + /// The unary plus of . + static abstract TResult operator +(TSelf value); + + // /// Computes the unary plus of a value. + // /// The value for which to compute its unary plus. + // /// The unary plus of . + // /// The unary plus of is not representable by . + // static abstract checked TResult operator +(TSelf value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 8f33d790699..f42c84de2bd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -13,6 +14,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Int16 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly short m_value; // Do not rename (binary serialization) @@ -274,5 +282,718 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static short IAdditionOperators.operator +(short left, short right) + => (short)(left + right); + + // [RequiresPreviewFeatures] + // static checked short IAdditionOperators.operator +(short left, short right) + // => checked((short)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static short IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static short IBinaryInteger.LeadingZeroCount(short value) + => (short)(BitOperations.LeadingZeroCount((ushort)value) - 16); + + [RequiresPreviewFeatures] + static short IBinaryInteger.PopCount(short value) + => (short)BitOperations.PopCount((ushort)value); + + [RequiresPreviewFeatures] + static short IBinaryInteger.RotateLeft(short value, short rotateAmount) + => (short)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static short IBinaryInteger.RotateRight(short value, short rotateAmount) + => (short)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static short IBinaryInteger.TrailingZeroCount(short value) + => (byte)(BitOperations.TrailingZeroCount(value << 16) - 16); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(short value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static short IBinaryNumber.Log2(short value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (short)BitOperations.Log2((ushort)value); + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static short IBitwiseOperators.operator &(short left, short right) + => (short)(left & right); + + [RequiresPreviewFeatures] + static short IBitwiseOperators.operator |(short left, short right) + => (short)(left | right); + + [RequiresPreviewFeatures] + static short IBitwiseOperators.operator ^(short left, short right) + => (short)(left ^ right); + + [RequiresPreviewFeatures] + static short IBitwiseOperators.operator ~(short value) + => (short)(~value); + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(short left, short right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(short left, short right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(short left, short right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(short left, short right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static short IDecrementOperators.operator --(short value) + => value--; + + // [RequiresPreviewFeatures] + // static checked short IDecrementOperators.operator --(short value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static short IDivisionOperators.operator /(short left, short right) + => (short)(left / right); + + // [RequiresPreviewFeatures] + // static checked short IDivisionOperators.operator /(short left, short right) + // => checked((short)(left / right)); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(short left, short right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(short left, short right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static short IIncrementOperators.operator ++(short value) + => value++; + + // [RequiresPreviewFeatures] + // static checked short IIncrementOperators.operator ++(short value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static short IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static short IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static short IModulusOperators.operator %(short left, short right) + => (short)(left % right); + + // [RequiresPreviewFeatures] + // static checked short IModulusOperators.operator %(short left, short right) + // => checked((short)(left % right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static short IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static short IMultiplyOperators.operator *(short left, short right) + => (short)(left * right); + + // [RequiresPreviewFeatures] + // static checked short IMultiplyOperators.operator *(short left, short right) + // => checked((short)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static short INumber.One => 1; + + [RequiresPreviewFeatures] + static short INumber.Zero => 0; + + [RequiresPreviewFeatures] + static short INumber.Abs(short value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static short INumber.Clamp(short value, short min, short max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static short INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return checked((short)(char)(object)value); + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((short)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((short)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return checked((short)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((short)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((short)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return checked((short)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return checked((short)(ushort)(object)value); + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((short)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((short)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((short)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static short INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + return (actualValue > MaxValue) ? MaxValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + return (actualValue > MaxValue) ? MaxValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > (uint)MaxValue) ? MaxValue : (short)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > (uint)MaxValue) ? MaxValue : (short)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static short INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (short)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (short)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (short)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (short)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (short)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (short)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (short)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (short)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (short)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (short)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (short)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (short Quotient, short Remainder) INumber.DivRem(short left, short right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static short INumber.Max(short x, short y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static short INumber.Min(short x, short y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static short INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static short INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static short INumber.Sign(short value) + => (short)Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out short result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > (uint)MaxValue) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > (uint)MaxValue) + { + result = default; + return false; + } + + result = (short)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static short IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out short result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static short IShiftOperators.operator <<(short value, int shiftAmount) + => (short)(value << shiftAmount); + + [RequiresPreviewFeatures] + static short IShiftOperators.operator >>(short value, int shiftAmount) + => (short)(value >> shiftAmount); + + // [RequiresPreviewFeatures] + // static short IShiftOperators.operator >>>(short value, int shiftAmount) + // => (short)((ushort)value >> shiftAmount); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static short ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static short ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out short result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static short ISubtractionOperators.operator -(short left, short right) + => (short)(left - right); + + // [RequiresPreviewFeatures] + // static checked short ISubtractionOperators.operator -(short left, short right) + // => checked((short)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static short IUnaryNegationOperators.operator -(short value) + => (short)(-value); + + // [RequiresPreviewFeatures] + // static checked short IUnaryNegationOperators.operator -(short value) + // => checked((short)(-value)); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static short IUnaryPlusOperators.operator +(short value) + => (short)(+value); + + // [RequiresPreviewFeatures] + // static checked short IUnaryPlusOperators.operator +(short value) + // => checked((short)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 3c46f513dc5..d8514bc89f7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -13,6 +14,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Int32 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int m_value; // Do not rename (binary serialization) @@ -266,5 +274,695 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static int IAdditionOperators.operator +(int left, int right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked int IAdditionOperators.operator +(int left, int right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static int IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static int IBinaryInteger.LeadingZeroCount(int value) + => BitOperations.LeadingZeroCount((uint)value); + + [RequiresPreviewFeatures] + static int IBinaryInteger.PopCount(int value) + => BitOperations.PopCount((uint)value); + + [RequiresPreviewFeatures] + static int IBinaryInteger.RotateLeft(int value, int rotateAmount) + => (int)BitOperations.RotateLeft((uint)value, rotateAmount); + + [RequiresPreviewFeatures] + static int IBinaryInteger.RotateRight(int value, int rotateAmount) + => (int)BitOperations.RotateRight((uint)value, rotateAmount); + + [RequiresPreviewFeatures] + static int IBinaryInteger.TrailingZeroCount(int value) + => BitOperations.TrailingZeroCount(value); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(int value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static int IBinaryNumber.Log2(int value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return BitOperations.Log2((uint)value); + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static int IBitwiseOperators.operator &(int left, int right) + => left & right; + + [RequiresPreviewFeatures] + static int IBitwiseOperators.operator |(int left, int right) + => left | right; + + [RequiresPreviewFeatures] + static int IBitwiseOperators.operator ^(int left, int right) + => left ^ right; + + [RequiresPreviewFeatures] + static int IBitwiseOperators.operator ~(int value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(int left, int right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(int left, int right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(int left, int right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(int left, int right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static int IDecrementOperators.operator --(int value) + => value--; + + // [RequiresPreviewFeatures] + // static checked int IDecrementOperators.operator --(int value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static int IDivisionOperators.operator /(int left, int right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked int IDivisionOperators.operator /(int left, int right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(int left, int right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(int left, int right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static int IIncrementOperators.operator ++(int value) + => value++; + + // [RequiresPreviewFeatures] + // static checked int IIncrementOperators.operator ++(int value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static int IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static int IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static int IModulusOperators.operator %(int left, int right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked int IModulusOperators.operator %(int left, int right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static int IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static int IMultiplyOperators.operator *(int left, int right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked int IMultiplyOperators.operator *(int left, int right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static int INumber.One => 1; + + [RequiresPreviewFeatures] + static int INumber.Zero => 0; + + [RequiresPreviewFeatures] + static int INumber.Abs(int value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static int INumber.Clamp(int value, int min, int max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + internal static int Create(TOther value) + where TOther : INumber + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((int)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((int)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return checked((int)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((int)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return checked((int)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return checked((int)(ushort)(object)value); + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((int)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((int)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((int)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int INumber.Create(TOther value) + => Create(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (int)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (int)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (int)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (int)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (int)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (int)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (int)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (int)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (int)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (int)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (int Quotient, int Remainder) INumber.DivRem(int left, int right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static int INumber.Max(int x, int y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static int INumber.Min(int x, int y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static int INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static int INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static int INumber.Sign(int value) + => Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out int result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (int)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static int IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out int result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static int IShiftOperators.operator <<(int value, int shiftAmount) + => value << shiftAmount; + + [RequiresPreviewFeatures] + static int IShiftOperators.operator >>(int value, int shiftAmount) + => value >> shiftAmount; + + // [RequiresPreviewFeatures] + // static int IShiftOperators.operator >>>(int value, int shiftAmount) + // => (int)((uint)value >> shiftAmount); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static int ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static int ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out int result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static int ISubtractionOperators.operator -(int left, int right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked int ISubtractionOperators.operator -(int left, int right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static int IUnaryNegationOperators.operator -(int value) + => -value; + + // [RequiresPreviewFeatures] + // static checked int IUnaryNegationOperators.operator -(int value) + // => checked(-value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static int IUnaryPlusOperators.operator +(int value) + => +value; + + // [RequiresPreviewFeatures] + // static checked int IUnaryPlusOperators.operator +(int value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 2c53290de59..c0420f5052e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -13,6 +14,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Int64 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly long m_value; // Do not rename (binary serialization) @@ -253,5 +261,661 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static long IAdditionOperators.operator +(long left, long right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked long IAdditionOperators.operator +(long left, long right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static long IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static long IBinaryInteger.LeadingZeroCount(long value) + => BitOperations.LeadingZeroCount((ulong)value); + + [RequiresPreviewFeatures] + static long IBinaryInteger.PopCount(long value) + => BitOperations.PopCount((ulong)value); + + [RequiresPreviewFeatures] + static long IBinaryInteger.RotateLeft(long value, long rotateAmount) + => (long)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static long IBinaryInteger.RotateRight(long value, long rotateAmount) + => (long)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static long IBinaryInteger.TrailingZeroCount(long value) + => BitOperations.TrailingZeroCount(value); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(long value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static long IBinaryNumber.Log2(long value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return BitOperations.Log2((ulong)value); + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static long IBitwiseOperators.operator &(long left, long right) + => left & right; + + [RequiresPreviewFeatures] + static long IBitwiseOperators.operator |(long left, long right) + => left | right; + + [RequiresPreviewFeatures] + static long IBitwiseOperators.operator ^(long left, long right) + => left ^ right; + + [RequiresPreviewFeatures] + static long IBitwiseOperators.operator ~(long value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(long left, long right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(long left, long right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(long left, long right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(long left, long right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static long IDecrementOperators.operator --(long value) + => value--; + + // [RequiresPreviewFeatures] + // static checked long IDecrementOperators.operator --(long value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static long IDivisionOperators.operator /(long left, long right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked long IDivisionOperators.operator /(long left, long right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(long left, long right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(long left, long right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static long IIncrementOperators.operator ++(long value) + => value++; + + // [RequiresPreviewFeatures] + // static checked long IIncrementOperators.operator ++(long value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static long IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static long IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static long IModulusOperators.operator %(long left, long right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked long IModulusOperators.operator %(long left, long right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static long IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static long IMultiplyOperators.operator *(long left, long right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked long IMultiplyOperators.operator *(long left, long right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static long INumber.One => 1; + + [RequiresPreviewFeatures] + static long INumber.Zero => 0; + + [RequiresPreviewFeatures] + static long INumber.Abs(long value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static long INumber.Clamp(long value, long min, long max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static long INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((long)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((long)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return checked((long)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((long)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((long)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static long INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (long)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (long)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (long)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (long)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (long)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static long INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (long)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (long)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (long)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (long)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (long)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (long Quotient, long Remainder) INumber.DivRem(long left, long right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static long INumber.Max(long x, long y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static long INumber.Min(long x, long y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static long INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static long INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static long INumber.Sign(long value) + => Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out long result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (long)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (long)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + result = (long)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (long)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (long)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (long)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static long IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out long result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static long IShiftOperators.operator <<(long value, int shiftAmount) + => value << (int)shiftAmount; + + [RequiresPreviewFeatures] + static long IShiftOperators.operator >>(long value, int shiftAmount) + => value >> (int)shiftAmount; + + // [RequiresPreviewFeatures] + // static long IShiftOperators.operator >>>(long value, int shiftAmount) + // => (long)((ulong)value >> (int)shiftAmount); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static long ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static long ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out long result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static long ISubtractionOperators.operator -(long left, long right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked long ISubtractionOperators.operator -(long left, long right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static long IUnaryNegationOperators.operator -(long value) + => -value; + + // [RequiresPreviewFeatures] + // static checked long IUnaryNegationOperators.operator -(long value) + // => checked(-value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static long IUnaryPlusOperators.operator +(long value) + => +value; + + // [RequiresPreviewFeatures] + // static checked long IUnaryPlusOperators.operator +(long value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 16735ffc421..f64effb167a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -21,7 +22,14 @@ namespace System [Serializable] [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public readonly struct IntPtr : IEquatable, IComparable, IComparable, ISpanFormattable, ISerializable + public readonly struct IntPtr : IEquatable, IComparable, IComparable, ISpanFormattable, ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // WARNING: We allow diagnostic tools to directly inspect this member (_value). // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. @@ -240,5 +248,734 @@ namespace System Unsafe.SkipInit(out result); return nint_t.TryParse(s, style, provider, out Unsafe.As(ref result)); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static nint IAdditionOperators.operator +(nint left, nint right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked nint IAdditionOperators.operator +(nint left, nint right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static nint IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static nint IBinaryInteger.LeadingZeroCount(nint value) + { + if (Environment.Is64BitProcess) + { + return BitOperations.LeadingZeroCount((uint)value); + } + else + { + return BitOperations.LeadingZeroCount((ulong)value); + } + } + + [RequiresPreviewFeatures] + static nint IBinaryInteger.PopCount(nint value) + { + if (Environment.Is64BitProcess) + { + return BitOperations.PopCount((uint)value); + } + else + { + return BitOperations.PopCount((ulong)value); + } + } + + [RequiresPreviewFeatures] + static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) + { + if (Environment.Is64BitProcess) + { + return (nint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); + } + else + { + return (nint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) + + { + if (Environment.Is64BitProcess) + { + return (nint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + } + else + { + return (nint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nint IBinaryInteger.TrailingZeroCount(nint value) + { + if (Environment.Is64BitProcess) + { + return BitOperations.TrailingZeroCount((uint)value); + } + else + { + return BitOperations.TrailingZeroCount((ulong)value); + } + } + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(nint value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static nint IBinaryNumber.Log2(nint value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + if (Environment.Is64BitProcess) + { + return BitOperations.Log2((uint)value); + } + else + { + return BitOperations.Log2((ulong)value); + } + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static nint IBitwiseOperators.operator &(nint left, nint right) + => left & right; + + [RequiresPreviewFeatures] + static nint IBitwiseOperators.operator |(nint left, nint right) + => left | right; + + [RequiresPreviewFeatures] + static nint IBitwiseOperators.operator ^(nint left, nint right) + => left ^ right; + + [RequiresPreviewFeatures] + static nint IBitwiseOperators.operator ~(nint value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(nint left, nint right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(nint left, nint right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(nint left, nint right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(nint left, nint right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static nint IDecrementOperators.operator --(nint value) + => value--; + + // [RequiresPreviewFeatures] + // static checked nint IDecrementOperators.operator --(nint value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static nint IDivisionOperators.operator /(nint left, nint right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked nint IDivisionOperators.operator /(nint left, nint right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(nint left, nint right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(nint left, nint right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static nint IIncrementOperators.operator ++(nint value) + => value++; + + // [RequiresPreviewFeatures] + // static checked nint IIncrementOperators.operator ++(nint value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static nint IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static nint IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static nint IModulusOperators.operator %(nint left, nint right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked nint IModulusOperators.operator %(nint left, nint right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static nint IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static nint IMultiplyOperators.operator *(nint left, nint right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked nint IMultiplyOperators.operator *(nint left, nint right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static nint INumber.One => 1; + + [RequiresPreviewFeatures] + static nint INumber.Zero => 0; + + [RequiresPreviewFeatures] + static nint INumber.Abs(nint value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static nint INumber.Clamp(nint value, nint min, nint max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nint INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((nint)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((nint)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return checked((nint)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return checked((nint)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((nint)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((nint)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((nint)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nint INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > nint.MaxValue) ? MaxValue : + (actualValue < nint.MinValue) ? MinValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > nint.MaxValue) ? MaxValue : + (actualValue < nint.MinValue) ? MinValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > nint.MaxValue) ? MaxValue : + (actualValue < nint.MinValue) ? MinValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > nint.MaxValue) ? MaxValue : + (actualValue < nint.MinValue) ? MinValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > nint.MaxValue) ? MaxValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > (nuint)nint.MaxValue) ? MaxValue : (nint)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > (nuint)nint.MaxValue) ? MaxValue : (nint)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nint INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (nint)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (nint)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (nint)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (nint)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (nint)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (nint)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nint)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (nint Quotient, nint Remainder) INumber.DivRem(nint left, nint right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static nint INumber.Max(nint x, nint y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static nint INumber.Min(nint x, nint y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static nint INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static nint INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static nint INumber.Sign(nint value) + => Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out nint result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < nint.MinValue) || (actualValue > nint.MaxValue)) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < nint.MinValue) || (actualValue > nint.MaxValue)) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < nint.MinValue) || (actualValue > nint.MaxValue)) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < nint.MinValue) || (actualValue > nint.MaxValue)) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > nint.MaxValue) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > (nuint)nint.MaxValue) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > (nuint)nint.MaxValue) + { + result = default; + return false; + } + + result = (nint)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out nint result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out nint result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static nint IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out nint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static nint IShiftOperators.operator <<(nint value, int shiftAmount) + => value << (int)shiftAmount; + + [RequiresPreviewFeatures] + static nint IShiftOperators.operator >>(nint value, int shiftAmount) + => value >> (int)shiftAmount; + + // [RequiresPreviewFeatures] + // static nint IShiftOperators.operator >>>(nint value, int shiftAmount) + // => (nint)((nuint)value >> (int)shiftAmount); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static nint ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static nint ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out nint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static nint ISubtractionOperators.operator -(nint left, nint right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked nint ISubtractionOperators.operator -(nint left, nint right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static nint IUnaryNegationOperators.operator -(nint value) + => -value; + + // [RequiresPreviewFeatures] + // static checked nint IUnaryNegationOperators.operator -(nint value) + // => checked(-value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static nint IUnaryPlusOperators.operator +(nint value) + => +value; + + // [RequiresPreviewFeatures] + // static checked nint IUnaryPlusOperators.operator +(nint value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 880492a4597..cec0716214f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -14,6 +15,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct SByte : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly sbyte m_value; // Do not rename (binary serialization) @@ -283,5 +291,737 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static sbyte IAdditionOperators.operator +(sbyte left, sbyte right) + => (sbyte)(left + right); + + // [RequiresPreviewFeatures] + // static checked sbyte IAdditionOperators.operator +(sbyte left, sbyte right) + // => checked((sbyte)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static sbyte IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static sbyte IBinaryInteger.LeadingZeroCount(sbyte value) + => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 16); + + [RequiresPreviewFeatures] + static sbyte IBinaryInteger.PopCount(sbyte value) + => (sbyte)BitOperations.PopCount((byte)value); + + [RequiresPreviewFeatures] + static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) + => (sbyte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); + + [RequiresPreviewFeatures] + static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) + => (sbyte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); + + [RequiresPreviewFeatures] + static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) + => (sbyte)(BitOperations.TrailingZeroCount(value << 24) - 24); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(sbyte value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static sbyte IBinaryNumber.Log2(sbyte value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + return (sbyte)BitOperations.Log2((byte)value); + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static sbyte IBitwiseOperators.operator &(sbyte left, sbyte right) + => (sbyte)(left & right); + + [RequiresPreviewFeatures] + static sbyte IBitwiseOperators.operator |(sbyte left, sbyte right) + => (sbyte)(left | right); + + [RequiresPreviewFeatures] + static sbyte IBitwiseOperators.operator ^(sbyte left, sbyte right) + => (sbyte)(left ^ right); + + [RequiresPreviewFeatures] + static sbyte IBitwiseOperators.operator ~(sbyte value) + => (sbyte)(~value); + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(sbyte left, sbyte right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(sbyte left, sbyte right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(sbyte left, sbyte right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(sbyte left, sbyte right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static sbyte IDecrementOperators.operator --(sbyte value) + => value--; + + // [RequiresPreviewFeatures] + // static checked sbyte IDecrementOperators.operator --(sbyte value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static sbyte IDivisionOperators.operator /(sbyte left, sbyte right) + => (sbyte)(left / right); + + // [RequiresPreviewFeatures] + // static checked sbyte IDivisionOperators.operator /(sbyte left, sbyte right) + // => checked((sbyte)(left / right)); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(sbyte left, sbyte right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(sbyte left, sbyte right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static sbyte IIncrementOperators.operator ++(sbyte value) + => value++; + + // [RequiresPreviewFeatures] + // static checked sbyte IIncrementOperators.operator ++(sbyte value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static sbyte IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static sbyte IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static sbyte IModulusOperators.operator %(sbyte left, sbyte right) + => (sbyte)(left % right); + + // [RequiresPreviewFeatures] + // static checked sbyte IModulusOperators.operator %(sbyte left, sbyte right) + // => checked((sbyte)(left % right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static sbyte IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static sbyte IMultiplyOperators.operator *(sbyte left, sbyte right) + => (sbyte)(left * right); + + // [RequiresPreviewFeatures] + // static checked sbyte IMultiplyOperators.operator *(sbyte left, sbyte right) + // => checked((sbyte)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static sbyte INumber.One => 1; + + [RequiresPreviewFeatures] + static sbyte INumber.Zero => 0; + + [RequiresPreviewFeatures] + static sbyte INumber.Abs(sbyte value) + => Math.Abs(value); + + [RequiresPreviewFeatures] + static sbyte INumber.Clamp(sbyte value, sbyte min, sbyte max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static sbyte INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return checked((sbyte)(byte)(object)value); + } + else if (typeof(TOther) == typeof(char)) + { + return checked((sbyte)(char)(object)value); + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((sbyte)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((sbyte)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((sbyte)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((sbyte)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((sbyte)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((sbyte)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return checked((sbyte)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return checked((sbyte)(ushort)(object)value); + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((sbyte)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((sbyte)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((sbyte)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static sbyte INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + var actualValue = (byte)(object)value; + return (actualValue > MaxValue) ? MaxValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + return (actualValue > MaxValue) ? MaxValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < MinValue) ? MinValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + return (actualValue > MaxValue) ? MaxValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > (uint)MaxValue) ? MaxValue : (sbyte)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > (uint)MaxValue) ? MaxValue : (sbyte)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static sbyte INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (sbyte)(byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (sbyte)(char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (sbyte)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (sbyte)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (sbyte)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (sbyte)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (sbyte)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (sbyte)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (sbyte)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (sbyte)(ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (sbyte)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (sbyte)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (sbyte)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (sbyte Quotient, sbyte Remainder) INumber.DivRem(sbyte left, sbyte right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static sbyte INumber.Max(sbyte x, sbyte y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static sbyte INumber.Min(sbyte x, sbyte y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static sbyte INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static sbyte INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static sbyte INumber.Sign(sbyte value) + => (sbyte)Math.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out sbyte result) + { + if (typeof(TOther) == typeof(byte)) + { + var actualValue = (byte)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + var actualValue = (char)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + var actualValue = (ushort)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > (uint)MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > (uint)MaxValue) + { + result = default; + return false; + } + + result = (sbyte)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static sbyte IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out sbyte result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static sbyte IShiftOperators.operator <<(sbyte value, int shiftAmount) + => (sbyte)(value << shiftAmount); + + [RequiresPreviewFeatures] + static sbyte IShiftOperators.operator >>(sbyte value, int shiftAmount) + => (sbyte)(value >> shiftAmount); + + // [RequiresPreviewFeatures] + // static sbyte IShiftOperators.operator >>>(sbyte value, int shiftAmount) + // => (sbyte)((byte)value >> shiftAmount); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static sbyte ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static sbyte ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out sbyte result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static sbyte ISubtractionOperators.operator -(sbyte left, sbyte right) + => (sbyte)(left - right); + + // [RequiresPreviewFeatures] + // static checked sbyte ISubtractionOperators.operator -(sbyte left, sbyte right) + // => checked((sbyte)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static sbyte IUnaryNegationOperators.operator -(sbyte value) + => (sbyte)(-value); + + // [RequiresPreviewFeatures] + // static checked sbyte IUnaryNegationOperators.operator -(sbyte value) + // => checked((sbyte)(-value)); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static sbyte IUnaryPlusOperators.operator +(sbyte value) + => (sbyte)(+value); + + // [RequiresPreviewFeatures] + // static checked sbyte IUnaryPlusOperators.operator +(sbyte value) + // => checked((sbyte)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 8ba2e6e1496..92dc3a9b6df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -24,6 +24,12 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Single : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryFloatingPoint, + IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly float m_value; // Do not rename (binary serialization) @@ -46,11 +52,11 @@ namespace System internal const uint SignMask = 0x8000_0000; internal const int SignShift = 31; - internal const int ShiftedSignMask = (int)(SignMask >> SignShift); + internal const uint ShiftedSignMask = SignMask >> SignShift; internal const uint ExponentMask = 0x7F80_0000; internal const int ExponentShift = 23; - internal const int ShiftedExponentMask = (int)(ExponentMask >> ExponentShift); + internal const uint ShiftedExponentMask = ExponentMask >> ExponentShift; internal const uint SignificandMask = 0x007F_FFFF; @@ -140,7 +146,7 @@ namespace System internal static int ExtractExponentFromBits(uint bits) { - return (int)(bits >> ExponentShift) & ShiftedExponentMask; + return (int)(bits >> ExponentShift) & (int)ShiftedExponentMask; } internal static uint ExtractSignificandFromBits(uint bits) @@ -436,5 +442,826 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static float IAdditionOperators.operator +(float left, float right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked float IAdditionOperators.operator +(float left, float right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static float IAdditiveIdentity.AdditiveIdentity => 0.0f; + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(float value) + { + uint bits = BitConverter.SingleToUInt32Bits(value); + + uint exponent = (bits >> ExponentShift) & ShiftedExponentMask; + uint significand = bits & SignificandMask; + + return (value > 0) + && (exponent != MinExponent) && (exponent != MaxExponent) + && (significand == MinSignificand); + } + + [RequiresPreviewFeatures] + static float IBinaryNumber.Log2(float value) + => MathF.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static float IBitwiseOperators.operator &(float left, float right) + { + uint bits = BitConverter.SingleToUInt32Bits(left) & BitConverter.SingleToUInt32Bits(right); + return BitConverter.UInt32BitsToSingle(bits); + } + + [RequiresPreviewFeatures] + static float IBitwiseOperators.operator |(float left, float right) + { + uint bits = BitConverter.SingleToUInt32Bits(left) | BitConverter.SingleToUInt32Bits(right); + return BitConverter.UInt32BitsToSingle(bits); + } + + [RequiresPreviewFeatures] + static float IBitwiseOperators.operator ^(float left, float right) + { + uint bits = BitConverter.SingleToUInt32Bits(left) ^ BitConverter.SingleToUInt32Bits(right); + return BitConverter.UInt32BitsToSingle(bits); + } + + [RequiresPreviewFeatures] + static float IBitwiseOperators.operator ~(float value) + { + uint bits = ~BitConverter.SingleToUInt32Bits(value); + return BitConverter.UInt32BitsToSingle(bits); + } + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(float left, float right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(float left, float right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(float left, float right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(float left, float right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static float IDecrementOperators.operator --(float value) + => value--; + + // [RequiresPreviewFeatures] + // static checked float IDecrementOperators.operator --(float value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static float IDivisionOperators.operator /(float left, float right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked float IDivisionOperators.operator /(float left, float right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(float left, float right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(float left, float right) + => left != right; + + // + // IFloatingPoint + // + + [RequiresPreviewFeatures] + static float IFloatingPoint.E => MathF.E; + + [RequiresPreviewFeatures] + static float IFloatingPoint.Epsilon => Epsilon; + + [RequiresPreviewFeatures] + static float IFloatingPoint.NaN => NaN; + + [RequiresPreviewFeatures] + static float IFloatingPoint.NegativeInfinity => NegativeInfinity; + + [RequiresPreviewFeatures] + static float IFloatingPoint.NegativeZero => -0.0f; + + [RequiresPreviewFeatures] + static float IFloatingPoint.Pi => MathF.PI; + + [RequiresPreviewFeatures] + static float IFloatingPoint.PositiveInfinity => PositiveInfinity; + + [RequiresPreviewFeatures] + static float IFloatingPoint.Tau => MathF.Tau; + + [RequiresPreviewFeatures] + static float IFloatingPoint.Acos(float x) + => MathF.Acos(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Acosh(float x) + => MathF.Acosh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Asin(float x) + => MathF.Asin(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Asinh(float x) + => MathF.Asinh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Atan(float x) + => MathF.Atan(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Atan2(float y, float x) + => MathF.Atan2(y, x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Atanh(float x) + => MathF.Atanh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.BitIncrement(float x) + => MathF.BitIncrement(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.BitDecrement(float x) + => MathF.BitDecrement(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Cbrt(float x) + => MathF.Cbrt(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Ceiling(float x) + => MathF.Ceiling(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.CopySign(float x, float y) + => MathF.CopySign(x, y); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Cos(float x) + => MathF.Cos(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Cosh(float x) + => MathF.Cosh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Exp(float x) + => MathF.Exp(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Floor(float x) + => MathF.Floor(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.FusedMultiplyAdd(float left, float right, float addend) + => MathF.FusedMultiplyAdd(left, right, addend); + + [RequiresPreviewFeatures] + static float IFloatingPoint.IEEERemainder(float left, float right) + => MathF.IEEERemainder(left, right); + + [RequiresPreviewFeatures] + static TInteger IFloatingPoint.ILogB(float x) + => TInteger.Create(MathF.ILogB(x)); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Log(float x) + => MathF.Log(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Log(float x, float newBase) + => MathF.Log(x, newBase); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Log2(float x) + => MathF.Log2(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Log10(float x) + => MathF.Log10(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.MaxMagnitude(float x, float y) + => MathF.MaxMagnitude(x, y); + + [RequiresPreviewFeatures] + static float IFloatingPoint.MinMagnitude(float x, float y) + => MathF.MinMagnitude(x, y); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Pow(float x, float y) + => MathF.Pow(x, y); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Round(float x) + => MathF.Round(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Round(float x, TInteger digits) + => MathF.Round(x, int.Create(digits)); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Round(float x, MidpointRounding mode) + => MathF.Round(x, mode); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Round(float x, TInteger digits, MidpointRounding mode) + => MathF.Round(x, int.Create(digits), mode); + + [RequiresPreviewFeatures] + static float IFloatingPoint.ScaleB(float x, TInteger n) + => MathF.ScaleB(x, int.Create(n)); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Sin(float x) + => MathF.Sin(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Sinh(float x) + => MathF.Sinh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Sqrt(float x) + => MathF.Sqrt(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Tan(float x) + => MathF.Tan(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Tanh(float x) + => MathF.Tanh(x); + + [RequiresPreviewFeatures] + static float IFloatingPoint.Truncate(float x) + => MathF.Truncate(x); + + // static float IFloatingPoint.AcosPi(float x) + // => MathF.AcosPi(x); + // + // static float IFloatingPoint.AsinPi(float x) + // => MathF.AsinPi(x); + // + // static float IFloatingPoint.AtanPi(float x) + // => MathF.AtanPi(x); + // + // static float IFloatingPoint.Atan2Pi(float y, float x) + // => MathF.Atan2Pi(y, x); + // + // static float IFloatingPoint.Compound(float x, float n) + // => MathF.Compound(x, n); + // + // static float IFloatingPoint.CosPi(float x) + // => MathF.CosPi(x); + // + // static float IFloatingPoint.ExpM1(float x) + // => MathF.ExpM1(x); + // + // static float IFloatingPoint.Exp2(float x) + // => MathF.Exp2(x); + // + // static float IFloatingPoint.Exp2M1(float x) + // => MathF.Exp2M1(x); + // + // static float IFloatingPoint.Exp10(float x) + // => MathF.Exp10(x); + // + // static float IFloatingPoint.Exp10M1(float x) + // => MathF.Exp10M1(x); + // + // static float IFloatingPoint.Hypot(float x, float y) + // => MathF.Hypot(x, y); + // + // static float IFloatingPoint.LogP1(float x) + // => MathF.LogP1(x); + // + // static float IFloatingPoint.Log2P1(float x) + // => MathF.Log2P1(x); + // + // static float IFloatingPoint.Log10P1(float x) + // => MathF.Log10P1(x); + // + // static float IFloatingPoint.MaxMagnitudeNumber(float x, float y) + // => MathF.MaxMagnitudeNumber(x, y); + // + // static float IFloatingPoint.MaxNumber(float x, float y) + // => MathF.MaxNumber(x, y); + // + // static float IFloatingPoint.MinMagnitudeNumber(float x, float y) + // => MathF.MinMagnitudeNumber(x, y); + // + // static float IFloatingPoint.MinNumber(float x, float y) + // => MathF.MinNumber(x, y); + // + // static float IFloatingPoint.Root(float x, float n) + // => MathF.Root(x, n); + // + // static float IFloatingPoint.SinPi(float x) + // => MathF.SinPi(x, y); + // + // static float TanPi(float x) + // => MathF.TanPi(x, y); + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static float IIncrementOperators.operator ++(float value) + => value++; + + // [RequiresPreviewFeatures] + // static checked float IIncrementOperators.operator ++(float value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static float IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static float IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static float IModulusOperators.operator %(float left, float right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked float IModulusOperators.operator %(float left, float right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static float IMultiplicativeIdentity.MultiplicativeIdentity => 1.0f; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static float IMultiplyOperators.operator *(float left, float right) + => (float)(left * right); + + // [RequiresPreviewFeatures] + // static checked float IMultiplyOperators.operator *(float left, float right) + // => checked((float)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static float INumber.One => 1.0f; + + [RequiresPreviewFeatures] + static float INumber.Zero => 0.0f; + + [RequiresPreviewFeatures] + static float INumber.Abs(float value) + => MathF.Abs(value); + + [RequiresPreviewFeatures] + static float INumber.Clamp(float value, float min, float max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static float INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (float)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static float INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (float)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static float INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (float)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (float)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (float Quotient, float Remainder) INumber.DivRem(float left, float right) + => (left / right, left % right); + + [RequiresPreviewFeatures] + static float INumber.Max(float x, float y) + => MathF.Max(x, y); + + [RequiresPreviewFeatures] + static float INumber.Min(float x, float y) + => MathF.Min(x, y); + + [RequiresPreviewFeatures] + static float INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static float INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static float INumber.Sign(float value) + => MathF.Sign(value); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out float result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + result = (float)(decimal)(object)value; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + result = (float)(double)(object)value; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + result = (long)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + result = (nint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + result = (float)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + result = (ulong)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out float result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out float result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static float IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out float result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISignedNumber + // + + [RequiresPreviewFeatures] + static float ISignedNumber.NegativeOne => -1; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static float ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out float result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static float ISubtractionOperators.operator -(float left, float right) + => (float)(left - right); + + // [RequiresPreviewFeatures] + // static checked float ISubtractionOperators.operator -(float left, float right) + // => checked((float)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static float IUnaryNegationOperators.operator -(float value) => (float)(-value); + + // [RequiresPreviewFeatures] + // static checked float IUnaryNegationOperators.operator -(float value) => checked((float)(-value)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static float IUnaryPlusOperators.operator +(float value) => (float)(+value); + + // [RequiresPreviewFeatures] + // static checked float IUnaryPlusOperators.operator +(float value) => checked((float)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs index 22426e0aa71..554cc3a41cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Versioning; namespace System { @@ -11,6 +12,14 @@ namespace System /// Represents a time of day, as would be read from a clock, within the range 00:00:00 to 23:59:59.9999999. /// public readonly struct TimeOnly : IComparable, IComparable, IEquatable, ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IComparisonOperators, + IMinMaxValue, + ISpanParseable, + ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { // represent the number of ticks map to the time of the day. 1 ticks = 100-nanosecond in time measurements. private readonly long _ticks; @@ -896,5 +905,75 @@ namespace System return DateTimeFormat.TryFormat(ToDateTime(), destination, out charsWritten, format, provider); } + +#if FEATURE_GENERIC_MATH + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(TimeOnly left, TimeOnly right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(TimeOnly left, TimeOnly right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(TimeOnly left, TimeOnly right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(TimeOnly left, TimeOnly right) + => left >= right; + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(TimeOnly left, TimeOnly right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(TimeOnly left, TimeOnly right) + => left != right; + + // + // IParseable + // + + [RequiresPreviewFeatures] + static TimeOnly IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out TimeOnly result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static TimeOnly ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, provider, DateTimeStyles.None); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out TimeOnly result) + => TryParse(s, provider, DateTimeStyles.None, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static TimeSpan ISubtractionOperators.operator -(TimeOnly left, TimeOnly right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan ISubtractionOperators.operator -(TimeOnly left, TimeOnly right) + // => checked(left - right); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs index b0634677650..6bc5341c8cc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.Versioning; namespace System { @@ -26,6 +27,22 @@ namespace System // [Serializable] public readonly struct TimeSpan : IComparable, IComparable, IEquatable, ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IAdditionOperators, + IAdditiveIdentity, + IComparisonOperators, + IDivisionOperators, + IDivisionOperators, + IMinMaxValue, + IMultiplyOperators, + IMultiplicativeIdentity, + ISpanParseable, + ISubtractionOperators, + IUnaryNegationOperators, + IUnaryPlusOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { public const long TicksPerMillisecond = 10000; @@ -487,5 +504,167 @@ namespace System public static bool operator >(TimeSpan t1, TimeSpan t2) => t1._ticks > t2._ticks; public static bool operator >=(TimeSpan t1, TimeSpan t2) => t1._ticks >= t2._ticks; + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static TimeSpan IAdditionOperators.operator +(TimeSpan left, TimeSpan right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan IAdditionOperators.operator +(TimeSpan left, TimeSpan right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static TimeSpan IAdditiveIdentity.AdditiveIdentity => default; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(TimeSpan left, TimeSpan right) + => left.operator <=(TimeSpan left, TimeSpan right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(TimeSpan left, TimeSpan right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(TimeSpan left, TimeSpan right) + => left >= right; + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static TimeSpan IDivisionOperators.operator /(TimeSpan left, double right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan IDivisionOperators.operator /(TimeSpan left, double right) + // => checked(left / right); + + [RequiresPreviewFeatures] + static double IDivisionOperators.operator /(TimeSpan left, TimeSpan right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked double IDivisionOperators.operator /(TimeSpan left, TimeSpan right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(TimeSpan left, TimeSpan right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(TimeSpan left, TimeSpan right) + => left != right; + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static TimeSpan IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static TimeSpan IMinMaxValue.MaxValue => MaxValue; + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static double IMultiplicativeIdentity.MultiplicativeIdentity => 1.0; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static TimeSpan IMultiplyOperators.operator *(TimeSpan left, double right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan IMultiplyOperators.operator *(TimeSpan left, double right) + // => checked(left * right); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static TimeSpan IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out TimeSpan result) + => TryParse(s, provider, out result); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static TimeSpan ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out TimeSpan result) + => TryParse(s, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static TimeSpan ISubtractionOperators.operator -(TimeSpan left, TimeSpan right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked TimeSpan ISubtractionOperators.operator -(TimeSpan left, TimeSpan right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static TimeSpan IUnaryNegationOperators.operator -(TimeSpan value) + => -value; + + // [RequiresPreviewFeatures] + // static checked TimeSpan IUnaryNegationOperators.operator -(TimeSpan value) + // => checked(-value); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static TimeSpan IUnaryPlusOperators.operator +(TimeSpan value) + => +value; + + // [RequiresPreviewFeatures] + // static checked TimeSpan IUnaryPlusOperators.operator +(TimeSpan value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 35dff0a957a..218c092ed19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -14,6 +15,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct UInt16 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly ushort m_value; // Do not rename (binary serialization) @@ -265,5 +273,705 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static ushort IAdditionOperators.operator +(ushort left, ushort right) + => (ushort)(left + right); + + // [RequiresPreviewFeatures] + // static checked ushort IAdditionOperators.operator +(ushort left, ushort right) + // => checked((ushort)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static ushort IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static ushort IBinaryInteger.LeadingZeroCount(ushort value) + => (ushort)(BitOperations.LeadingZeroCount(value) - 16); + + [RequiresPreviewFeatures] + static ushort IBinaryInteger.PopCount(ushort value) + => (ushort)BitOperations.PopCount(value); + + [RequiresPreviewFeatures] + static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) + => (ushort)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) + => (ushort)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); + + [RequiresPreviewFeatures] + static ushort IBinaryInteger.TrailingZeroCount(ushort value) + => (ushort)(BitOperations.TrailingZeroCount(value << 16) - 16); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(ushort value) + => BitOperations.IsPow2((uint)value); + + [RequiresPreviewFeatures] + static ushort IBinaryNumber.Log2(ushort value) + => (ushort)BitOperations.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static ushort IBitwiseOperators.operator &(ushort left, ushort right) + => (ushort)(left & right); + + [RequiresPreviewFeatures] + static ushort IBitwiseOperators.operator |(ushort left, ushort right) + => (ushort)(left | right); + + [RequiresPreviewFeatures] + static ushort IBitwiseOperators.operator ^(ushort left, ushort right) + => (ushort)(left ^ right); + + [RequiresPreviewFeatures] + static ushort IBitwiseOperators.operator ~(ushort value) + => (ushort)(~value); + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(ushort left, ushort right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(ushort left, ushort right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(ushort left, ushort right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(ushort left, ushort right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static ushort IDecrementOperators.operator --(ushort value) + => value--; + + // [RequiresPreviewFeatures] + // static checked ushort IDecrementOperators.operator --(ushort value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static ushort IDivisionOperators.operator /(ushort left, ushort right) + => (ushort)(left / right); + + // [RequiresPreviewFeatures] + // static checked ushort IDivisionOperators.operator /(ushort left, ushort right) + // => checked((ushort)(left / right)); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(ushort left, ushort right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(ushort left, ushort right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static ushort IIncrementOperators.operator ++(ushort value) + => value++; + + // [RequiresPreviewFeatures] + // static checked ushort IIncrementOperators.operator ++(ushort value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static ushort IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static ushort IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static ushort IModulusOperators.operator %(ushort left, ushort right) + => (ushort)(left % right); + + // [RequiresPreviewFeatures] + // static checked ushort IModulusOperators.operator %(ushort left, ushort right) + // => checked((ushort)(left % right)); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static ushort IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static ushort IMultiplyOperators.operator *(ushort left, ushort right) + => (ushort)(left * right); + + // [RequiresPreviewFeatures] + // static checked ushort IMultiplyOperators.operator *(ushort left, ushort right) + // => checked((ushort)(left * right)); + + // + // INumber + // + + [RequiresPreviewFeatures] + static ushort INumber.One => 1; + + [RequiresPreviewFeatures] + static ushort INumber.Zero => 0; + + [RequiresPreviewFeatures] + static ushort INumber.Abs(ushort value) + => value; + + [RequiresPreviewFeatures] + static ushort INumber.Clamp(ushort value, ushort min, ushort max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((ushort)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((ushort)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((ushort)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((ushort)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((ushort)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((ushort)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((ushort)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((ushort)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return checked((ushort)(uint)(object)value); + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((ushort)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((ushort)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (ushort)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (ushort)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (ushort)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (ushort)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (ushort)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (ushort)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (ushort)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (ushort)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (ushort)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (ushort)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (ushort)(uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ushort)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (ushort)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (ushort Quotient, ushort Remainder) INumber.DivRem(ushort left, ushort right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static ushort INumber.Max(ushort x, ushort y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static ushort INumber.Min(ushort x, ushort y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static ushort INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static ushort INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static ushort INumber.Sign(ushort value) + => (ushort)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out ushort result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + var actualValue = (uint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (ushort)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static ushort IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out ushort result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static ushort IShiftOperators.operator <<(ushort value, int shiftAmount) + => (ushort)(value << shiftAmount); + + [RequiresPreviewFeatures] + static ushort IShiftOperators.operator >>(ushort value, int shiftAmount) + => (ushort)(value >> shiftAmount); + + // [RequiresPreviewFeatures] + // static ushort IShiftOperators.operator >>>(ushort value, int shiftAmount) + // => (ushort)(value >> shiftAmount); + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static ushort ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out ushort result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static ushort ISubtractionOperators.operator -(ushort left, ushort right) + => (ushort)(left - right); + + // [RequiresPreviewFeatures] + // static checked ushort ISubtractionOperators.operator -(ushort left, ushort right) + // => checked((ushort)(left - right)); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static ushort IUnaryNegationOperators.operator -(ushort value) + => (ushort)(-value); + + // [RequiresPreviewFeatures] + // static checked ushort IUnaryNegationOperators.operator -(ushort value) + // => checked((ushort)(-value)); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static ushort IUnaryPlusOperators.operator +(ushort value) + => (ushort)(+value); + + // [RequiresPreviewFeatures] + // static checked ushort IUnaryPlusOperators.operator +(ushort value) + // => checked((ushort)(+value)); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index fd1f741345f..14d06e9b0ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -14,6 +15,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct UInt32 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly uint m_value; // Do not rename (binary serialization) @@ -251,5 +259,695 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static uint IAdditionOperators.operator +(uint left, uint right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked uint IAdditionOperators.operator +(uint left, uint right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static uint IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static uint IBinaryInteger.LeadingZeroCount(uint value) + => (uint)BitOperations.LeadingZeroCount(value); + + [RequiresPreviewFeatures] + static uint IBinaryInteger.PopCount(uint value) + => (uint)BitOperations.PopCount(value); + + [RequiresPreviewFeatures] + static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) + => BitOperations.RotateLeft(value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) + => BitOperations.RotateRight(value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static uint IBinaryInteger.TrailingZeroCount(uint value) + => (uint)BitOperations.TrailingZeroCount(value); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(uint value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static uint IBinaryNumber.Log2(uint value) + => (uint)BitOperations.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static uint IBitwiseOperators.operator &(uint left, uint right) + => left & right; + + [RequiresPreviewFeatures] + static uint IBitwiseOperators.operator |(uint left, uint right) + => left | right; + + [RequiresPreviewFeatures] + static uint IBitwiseOperators.operator ^(uint left, uint right) + => left ^ right; + + [RequiresPreviewFeatures] + static uint IBitwiseOperators.operator ~(uint value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(uint left, uint right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(uint left, uint right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(uint left, uint right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(uint left, uint right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static uint IDecrementOperators.operator --(uint value) + => value--; + + // [RequiresPreviewFeatures] + // static checked uint IDecrementOperators.operator --(uint value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static uint IDivisionOperators.operator /(uint left, uint right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked uint IDivisionOperators.operator /(uint left, uint right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(uint left, uint right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(uint left, uint right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static uint IIncrementOperators.operator ++(uint value) + => value++; + + // [RequiresPreviewFeatures] + // static checked uint IIncrementOperators.operator ++(uint value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static uint IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static uint IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static uint IModulusOperators.operator %(uint left, uint right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked uint IModulusOperators.operator %(uint left, uint right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static uint IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static uint IMultiplyOperators.operator *(uint left, uint right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked uint IMultiplyOperators.operator *(uint left, uint right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static uint INumber.One => 1; + + [RequiresPreviewFeatures] + static uint INumber.Zero => 0; + + [RequiresPreviewFeatures] + static uint INumber.Abs(uint value) + => value; + + [RequiresPreviewFeatures] + static uint INumber.Clamp(uint value, uint min, uint max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((uint)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((uint)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((uint)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((uint)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((uint)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((uint)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((uint)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((uint)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((uint)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return checked((uint)(nuint)(object)value); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > MaxValue) ? MaxValue : (uint)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + return (actualValue > MaxValue) ? MaxValue : (uint)actualValue; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (uint)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (uint)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (uint)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (uint)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (uint)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (uint)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (uint)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (uint)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (uint)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (uint)(nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (uint Quotient, uint Remainder) INumber.DivRem(uint left, uint right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static uint INumber.Max(uint x, uint y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static uint INumber.Min(uint x, uint y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static uint INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static uint INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static uint INumber.Sign(uint value) + => (uint)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out uint result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < 0) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + var actualValue = (nuint)(object)value; + + if (actualValue > MaxValue) + { + result = default; + return false; + } + + result = (uint)actualValue; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static uint IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out uint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static uint IShiftOperators.operator <<(uint value, int shiftAmount) + => value << (int)shiftAmount; + + [RequiresPreviewFeatures] + static uint IShiftOperators.operator >>(uint value, int shiftAmount) + => value >> (int)shiftAmount; + + // [RequiresPreviewFeatures] + // static uint IShiftOperators.operator >>>(uint value, int shiftAmount) + // => value >> (int)shiftAmount; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static uint ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static uint ISubtractionOperators.operator -(uint left, uint right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked uint ISubtractionOperators.operator -(uint left, uint right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static uint IUnaryNegationOperators.operator -(uint value) + => 0u - value; + + // [RequiresPreviewFeatures] + // static checked uint IUnaryNegationOperators.operator -(uint value) + // => checked(0u - value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static uint IUnaryPlusOperators.operator +(uint value) + => +value; + + // [RequiresPreviewFeatures] + // static checked uint IUnaryPlusOperators.operator +(uint value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index da6773eaa8a..e341b0bee93 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -14,6 +15,13 @@ namespace System [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct UInt64 : IComparable, IConvertible, ISpanFormattable, IComparable, IEquatable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly ulong m_value; // Do not rename (binary serialization) @@ -250,5 +258,675 @@ namespace System { return Convert.DefaultToType((IConvertible)this, type, provider); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static ulong IAdditionOperators.operator +(ulong left, ulong right) + => left + right; + + // [RequiresPreviewFeatures] + // static checked ulong IAdditionOperators.operator +(ulong left, ulong right) + // => checked(left + right); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static ulong IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static ulong IBinaryInteger.LeadingZeroCount(ulong value) + => (ulong)BitOperations.LeadingZeroCount(value); + + [RequiresPreviewFeatures] + static ulong IBinaryInteger.PopCount(ulong value) + => (ulong)BitOperations.PopCount(value); + + [RequiresPreviewFeatures] + static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) + => BitOperations.RotateLeft(value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) + => BitOperations.RotateRight(value, (int)rotateAmount); + + [RequiresPreviewFeatures] + static ulong IBinaryInteger.TrailingZeroCount(ulong value) + => (ulong)BitOperations.TrailingZeroCount(value); + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(ulong value) + => BitOperations.IsPow2(value); + + [RequiresPreviewFeatures] + static ulong IBinaryNumber.Log2(ulong value) + => (ulong)BitOperations.Log2(value); + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static ulong IBitwiseOperators.operator &(ulong left, ulong right) + => left & right; + + [RequiresPreviewFeatures] + static ulong IBitwiseOperators.operator |(ulong left, ulong right) + => left | right; + + [RequiresPreviewFeatures] + static ulong IBitwiseOperators.operator ^(ulong left, ulong right) + => left ^ right; + + [RequiresPreviewFeatures] + static ulong IBitwiseOperators.operator ~(ulong value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(ulong left, ulong right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(ulong left, ulong right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(ulong left, ulong right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(ulong left, ulong right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static ulong IDecrementOperators.operator --(ulong value) + => value--; + + // [RequiresPreviewFeatures] + // static checked ulong IDecrementOperators.operator --(ulong value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static ulong IDivisionOperators.operator /(ulong left, ulong right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked ulong IDivisionOperators.operator /(ulong left, ulong right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(ulong left, ulong right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(ulong left, ulong right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static ulong IIncrementOperators.operator ++(ulong value) + => value++; + + // [RequiresPreviewFeatures] + // static checked ulong IIncrementOperators.operator ++(ulong value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static ulong IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static ulong IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static ulong IModulusOperators.operator %(ulong left, ulong right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked ulong IModulusOperators.operator %(ulong left, ulong right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static ulong IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static ulong IMultiplyOperators.operator *(ulong left, ulong right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked ulong IMultiplyOperators.operator *(ulong left, ulong right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static ulong INumber.One => 1; + + [RequiresPreviewFeatures] + static ulong INumber.Zero => 0; + + [RequiresPreviewFeatures] + static ulong INumber.Abs(ulong value) + => value; + + [RequiresPreviewFeatures] + static ulong INumber.Clamp(ulong value, ulong min, ulong max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ulong INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((ulong)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((ulong)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((ulong)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((ulong)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((ulong)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((ulong)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((ulong)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((ulong)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ulong INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + return (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (ulong)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ulong INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (ulong)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (ulong)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (ulong)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (ulong)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (ulong)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (ulong)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (ulong)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (ulong)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (ulong Quotient, ulong Remainder) INumber.DivRem(ulong left, ulong right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static ulong INumber.Max(ulong x, ulong y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static ulong INumber.Min(ulong x, ulong y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static ulong INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static ulong INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static ulong INumber.Sign(ulong value) + => (ulong)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out ulong result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < MinValue) || (actualValue > MaxValue)) + { + result = default; + return false; + } + + result = (ulong)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + result = (ulong)(object)value; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static ulong IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out ulong result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static ulong IShiftOperators.operator <<(ulong value, int shiftAmount) + => value << (int)shiftAmount; + + [RequiresPreviewFeatures] + static ulong IShiftOperators.operator >>(ulong value, int shiftAmount) + => value >> (int)shiftAmount; + + // [RequiresPreviewFeatures] + // static ulong IShiftOperators.operator >>>(ulong value, int shiftAmount) + // => value >> (int)shiftAmount; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static ulong ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out ulong result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static ulong ISubtractionOperators.operator -(ulong left, ulong right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked ulong ISubtractionOperators.operator -(ulong left, ulong right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static ulong IUnaryNegationOperators.operator -(ulong value) + => 0UL - value; + + // [RequiresPreviewFeatures] + // static checked ulong IUnaryNegationOperators.operator -(ulong value) + // => checked(0UL - value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static ulong IUnaryPlusOperators.operator +(ulong value) + => +value; + + // [RequiresPreviewFeatures] + // static checked ulong IUnaryPlusOperators.operator +(ulong value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index dcb2ce79c9c..43c549fe94e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -22,7 +23,14 @@ namespace System [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public readonly struct UIntPtr : IEquatable, IComparable, IComparable, ISpanFormattable, ISerializable + public readonly struct UIntPtr : IEquatable, IComparable, IComparable, ISpanFormattable, ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , IBinaryInteger, + IMinMaxValue, + IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly unsafe void* _value; // Do not rename (binary serialization) @@ -232,5 +240,749 @@ namespace System Unsafe.SkipInit(out result); return nuint_t.TryParse(s, style, provider, out Unsafe.As(ref result)); } + +#if FEATURE_GENERIC_MATH + // + // IAdditionOperators + // + + [RequiresPreviewFeatures] + static nuint IAdditionOperators.operator +(nuint left, nuint right) + => (nuint)(left + right); + + // [RequiresPreviewFeatures] + // static checked nuint IAdditionOperators.operator +(nuint left, nuint right) + // => checked((nuint)(left + right)); + + // + // IAdditiveIdentity + // + + [RequiresPreviewFeatures] + static nuint IAdditiveIdentity.AdditiveIdentity => 0; + + // + // IBinaryInteger + // + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.LeadingZeroCount(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.LeadingZeroCount((uint)value); + } + else + { + return (nuint)BitOperations.LeadingZeroCount((ulong)value); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.PopCount(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.PopCount((uint)value); + } + else + { + return (nuint)BitOperations.PopCount((ulong)value); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); + } + else + { + return (nuint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + } + else + { + return (nuint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.TrailingZeroCount(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.TrailingZeroCount((uint)value); + } + else + { + return (nuint)BitOperations.TrailingZeroCount((ulong)value); + } + } + + // + // IBinaryNumber + // + + [RequiresPreviewFeatures] + static bool IBinaryNumber.IsPow2(nuint value) + { + if (Environment.Is64BitProcess) + { + return BitOperations.IsPow2((uint)value); + } + else + { + return BitOperations.IsPow2((ulong)value); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryNumber.Log2(nuint value) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.Log2((uint)value); + } + else + { + return (nuint)BitOperations.Log2((ulong)value); + } + } + + // + // IBitwiseOperators + // + + [RequiresPreviewFeatures] + static nuint IBitwiseOperators.operator &(nuint left, nuint right) + => left & right; + + [RequiresPreviewFeatures] + static nuint IBitwiseOperators.operator |(nuint left, nuint right) + => left | right; + + [RequiresPreviewFeatures] + static nuint IBitwiseOperators.operator ^(nuint left, nuint right) + => left ^ right; + + [RequiresPreviewFeatures] + static nuint IBitwiseOperators.operator ~(nuint value) + => ~value; + + // + // IComparisonOperators + // + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <(nuint left, nuint right) + => left < right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator <=(nuint left, nuint right) + => left <= right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >(nuint left, nuint right) + => left > right; + + [RequiresPreviewFeatures] + static bool IComparisonOperators.operator >=(nuint left, nuint right) + => left >= right; + + // + // IDecrementOperators + // + + [RequiresPreviewFeatures] + static nuint IDecrementOperators.operator --(nuint value) + => value--; + + // [RequiresPreviewFeatures] + // static checked nuint IDecrementOperators.operator --(nuint value) + // => checked(value--); + + // + // IDivisionOperators + // + + [RequiresPreviewFeatures] + static nuint IDivisionOperators.operator /(nuint left, nuint right) + => left / right; + + // [RequiresPreviewFeatures] + // static checked nuint IDivisionOperators.operator /(nuint left, nuint right) + // => checked(left / right); + + // + // IEqualityOperators + // + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator ==(nuint left, nuint right) + => left == right; + + [RequiresPreviewFeatures] + static bool IEqualityOperators.operator !=(nuint left, nuint right) + => left != right; + + // + // IIncrementOperators + // + + [RequiresPreviewFeatures] + static nuint IIncrementOperators.operator ++(nuint value) + => value++; + + // [RequiresPreviewFeatures] + // static checked nuint IIncrementOperators.operator ++(nuint value) + // => checked(value++); + + // + // IMinMaxValue + // + + [RequiresPreviewFeatures] + static nuint IMinMaxValue.MinValue => MinValue; + + [RequiresPreviewFeatures] + static nuint IMinMaxValue.MaxValue => MaxValue; + + // + // IModulusOperators + // + + [RequiresPreviewFeatures] + static nuint IModulusOperators.operator %(nuint left, nuint right) + => left % right; + + // [RequiresPreviewFeatures] + // static checked nuint IModulusOperators.operator %(nuint left, nuint right) + // => checked(left % right); + + // + // IMultiplicativeIdentity + // + + [RequiresPreviewFeatures] + static nuint IMultiplicativeIdentity.MultiplicativeIdentity => 1; + + // + // IMultiplyOperators + // + + [RequiresPreviewFeatures] + static nuint IMultiplyOperators.operator *(nuint left, nuint right) + => left * right; + + // [RequiresPreviewFeatures] + // static checked nuint IMultiplyOperators.operator *(nuint left, nuint right) + // => checked(left * right); + + // + // INumber + // + + [RequiresPreviewFeatures] + static nuint INumber.One => 1; + + [RequiresPreviewFeatures] + static nuint INumber.Zero => 0; + + [RequiresPreviewFeatures] + static nuint INumber.Abs(nuint value) + => value; + + [RequiresPreviewFeatures] + static nuint INumber.Clamp(nuint value, nuint min, nuint max) + => Math.Clamp(value, min, max); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nuint INumber.Create(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return checked((nuint)(decimal)(object)value); + } + else if (typeof(TOther) == typeof(double)) + { + return checked((nuint)(double)(object)value); + } + else if (typeof(TOther) == typeof(short)) + { + return checked((nuint)(short)(object)value); + } + else if (typeof(TOther) == typeof(int)) + { + return checked((nuint)(int)(object)value); + } + else if (typeof(TOther) == typeof(long)) + { + return checked((nuint)(long)(object)value); + } + else if (typeof(TOther) == typeof(nint)) + { + return checked((nuint)(nint)(object)value); + } + else if (typeof(TOther) == typeof(sbyte)) + { + return checked((nuint)(sbyte)(object)value); + } + else if (typeof(TOther) == typeof(float)) + { + return checked((nuint)(float)(object)value); + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return checked((nuint)(ulong)(object)value); + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nuint INumber.CreateSaturating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (byte)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + return (actualValue > nuint.MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + return (actualValue > nuint.MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + return (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + return (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + return ((Size == 4) && (actualValue > uint.MaxValue)) ? MaxValue : + (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + return (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + return (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + return (actualValue > nuint.MaxValue) ? MaxValue : + (actualValue < 0) ? MinValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + return (actualValue > nuint.MaxValue) ? MaxValue : (nuint)actualValue; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static nuint INumber.CreateTruncating(TOther value) + { + if (typeof(TOther) == typeof(byte)) + { + return (nuint)(object)value; + } + else if (typeof(TOther) == typeof(char)) + { + return (char)(object)value; + } + else if (typeof(TOther) == typeof(decimal)) + { + return (nuint)(decimal)(object)value; + } + else if (typeof(TOther) == typeof(double)) + { + return (nuint)(double)(object)value; + } + else if (typeof(TOther) == typeof(short)) + { + return (nuint)(short)(object)value; + } + else if (typeof(TOther) == typeof(int)) + { + return (nuint)(int)(object)value; + } + else if (typeof(TOther) == typeof(long)) + { + return (nuint)(long)(object)value; + } + else if (typeof(TOther) == typeof(nint)) + { + return (nuint)(nint)(object)value; + } + else if (typeof(TOther) == typeof(sbyte)) + { + return (nuint)(sbyte)(object)value; + } + else if (typeof(TOther) == typeof(float)) + { + return (nuint)(float)(object)value; + } + else if (typeof(TOther) == typeof(ushort)) + { + return (ushort)(object)value; + } + else if (typeof(TOther) == typeof(uint)) + { + return (uint)(object)value; + } + else if (typeof(TOther) == typeof(ulong)) + { + return (nuint)(ulong)(object)value; + } + else if (typeof(TOther) == typeof(nuint)) + { + return (nuint)(object)value; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + return default; + } + } + + [RequiresPreviewFeatures] + static (nuint Quotient, nuint Remainder) INumber.DivRem(nuint left, nuint right) + => Math.DivRem(left, right); + + [RequiresPreviewFeatures] + static nuint INumber.Max(nuint x, nuint y) + => Math.Max(x, y); + + [RequiresPreviewFeatures] + static nuint INumber.Min(nuint x, nuint y) + => Math.Min(x, y); + + [RequiresPreviewFeatures] + static nuint INumber.Parse(string s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static nuint INumber.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) + => Parse(s, style, provider); + + [RequiresPreviewFeatures] + static nuint INumber.Sign(nuint value) + => (nuint)((value == 0) ? 0 : 1); + + [RequiresPreviewFeatures] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumber.TryCreate(TOther value, out nuint result) + { + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + result = (char)(object)value; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + var actualValue = (decimal)(object)value; + + if ((actualValue < 0) || (actualValue > nuint.MaxValue)) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(double)) + { + var actualValue = (double)(object)value; + + if ((actualValue < 0) || (actualValue > nuint.MaxValue)) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + var actualValue = (short)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + var actualValue = (int)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + var actualValue = (long)(object)value; + + if ((actualValue < 0) || ((Size == 4) && (actualValue > uint.MaxValue))) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + var actualValue = (nint)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(sbyte)) + { + var actualValue = (sbyte)(object)value; + + if (actualValue < 0) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(float)) + { + var actualValue = (float)(object)value; + + if ((actualValue < 0) || (actualValue > nuint.MaxValue)) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + var actualValue = (ulong)(object)value; + + if (actualValue > nuint.MaxValue) + { + result = default; + return false; + } + + result = (nuint)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + result = (nuint)(object)value; + return true; + } + else + { + ThrowHelper.ThrowNotSupportedException(); + result = default; + return false; + } + } + + [RequiresPreviewFeatures] + static bool INumber.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out nuint result) + => TryParse(s, style, provider, out result); + + [RequiresPreviewFeatures] + static bool INumber.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out nuint result) + => TryParse(s, style, provider, out result); + + // + // IParseable + // + + [RequiresPreviewFeatures] + static nuint IParseable.Parse(string s, IFormatProvider? provider) + => Parse(s, provider); + + [RequiresPreviewFeatures] + static bool IParseable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out nuint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // IShiftOperators + // + + [RequiresPreviewFeatures] + static nuint IShiftOperators.operator <<(nuint value, int shiftAmount) + => value << (int)shiftAmount; + + [RequiresPreviewFeatures] + static nuint IShiftOperators.operator >>(nuint value, int shiftAmount) + => value >> (int)shiftAmount; + + // [RequiresPreviewFeatures] + // static nuint IShiftOperators.operator >>>(nuint value, int shiftAmount) + // => value >> (int)shiftAmount; + + // + // ISpanParseable + // + + [RequiresPreviewFeatures] + static nuint ISpanParseable.Parse(ReadOnlySpan s, IFormatProvider? provider) + => Parse(s, NumberStyles.Integer, provider); + + [RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out nuint result) + => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + [RequiresPreviewFeatures] + static nuint ISubtractionOperators.operator -(nuint left, nuint right) + => left - right; + + // [RequiresPreviewFeatures] + // static checked nuint ISubtractionOperators.operator -(nuint left, nuint right) + // => checked(left - right); + + // + // IUnaryNegationOperators + // + + [RequiresPreviewFeatures] + static nuint IUnaryNegationOperators.operator -(nuint value) + => (nuint)0 - value; + + // [RequiresPreviewFeatures] + // static checked nuint IUnaryNegationOperators.operator -(nuint value) + // => checked((nuint)0 - value); + + // + // IUnaryPlusOperators + // + + [RequiresPreviewFeatures] + static nuint IUnaryPlusOperators.operator +(nuint value) + => +value; + + // [RequiresPreviewFeatures] + // static checked nuint IUnaryPlusOperators.operator +(nuint value) + // => checked(+value); +#endif // FEATURE_GENERIC_MATH } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 47e45170b40..67adfd0c6d3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -705,6 +705,13 @@ namespace System public static void SetByte(System.Array array, int index, byte value) { } } public readonly partial struct Byte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly byte _dummyPrimitive; public const byte MaxValue = (byte)255; @@ -744,6 +751,134 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Byte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Byte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Byte result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IAdditionOperators.operator +(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryInteger.LeadingZeroCount(byte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryInteger.PopCount(byte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryInteger.TrailingZeroCount(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(byte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBinaryNumber.Log2(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBitwiseOperators.operator &(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBitwiseOperators.operator |(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBitwiseOperators.operator ^(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IBitwiseOperators.operator ~(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IDecrementOperators.operator --(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IDivisionOperators.operator /(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IIncrementOperators.operator ++(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IModulusOperators.operator %(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IMultiplyOperators.operator *(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Abs(byte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Clamp(byte value, byte min, byte max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (byte Quotient, byte Remainder) INumber.DivRem(byte left, byte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Max(byte x, byte y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Min(byte x, byte y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte INumber.Sign(byte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out byte result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out byte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static byte IShiftOperators.operator <<(byte value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static byte IShiftOperators.operator >>(byte value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out byte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte ISubtractionOperators.operator -(byte left, byte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IUnaryNegationOperators.operator -(byte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static byte IUnaryPlusOperators.operator +(byte value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial class CannotUnloadAppDomainException : System.SystemException { @@ -753,6 +888,13 @@ namespace System public CannotUnloadAppDomainException(string? message, System.Exception? innerException) { } } public readonly partial struct Char : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly char _dummyPrimitive; public const char MaxValue = '\uFFFF'; @@ -829,6 +971,134 @@ namespace System public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Char result) { throw null; } bool System.ISpanFormattable.TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } string System.IFormattable.ToString(string? format, IFormatProvider? formatProvider) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IAdditionOperators.operator +(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryInteger.LeadingZeroCount(char value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryInteger.PopCount(char value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryInteger.RotateLeft(char value, char rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryInteger.RotateRight(char value, char rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryInteger.TrailingZeroCount(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(char value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBinaryNumber.Log2(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBitwiseOperators.operator &(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBitwiseOperators.operator |(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBitwiseOperators.operator ^(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IBitwiseOperators.operator ~(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IDecrementOperators.operator --(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IDivisionOperators.operator /(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IIncrementOperators.operator ++(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IModulusOperators.operator %(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IMultiplyOperators.operator *(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Abs(char value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Clamp(char value, char min, char max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (char Quotient, char Remainder) INumber.DivRem(char left, char right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Max(char x, char y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Min(char x, char y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char INumber.Sign(char value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out char result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out char result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static char IShiftOperators.operator <<(char value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures, System.Runtime.CompilerServices.SpecialNameAttribute] + static char IShiftOperators.operator >>(char value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out char result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char ISubtractionOperators.operator -(char left, char right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IUnaryNegationOperators.operator -(char value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static char IUnaryPlusOperators.operator +(char value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public sealed partial class CharEnumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.ICloneable, System.IDisposable { @@ -1317,7 +1587,14 @@ namespace System public static bool TryToBase64Chars(System.ReadOnlySpan bytes, System.Span chars, out int charsWritten, System.Base64FormattingOptions options = System.Base64FormattingOptions.None) { throw null; } } public delegate TOutput Converter(TInput input); - public readonly struct DateOnly : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable + public readonly partial struct DateOnly : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IComparisonOperators, + System.IMinMaxValue, + System.ISpanParseable +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { public static DateOnly MinValue { get { throw null; } } public static DateOnly MaxValue { get { throw null; } } @@ -1376,8 +1653,50 @@ namespace System public string ToString(System.IFormatProvider? provider) { throw null; } public string ToString(string? format, System.IFormatProvider? provider) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateOnly IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateOnly IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.DateOnly left, System.DateOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.DateOnly left, System.DateOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.DateOnly left, System.DateOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.DateOnly left, System.DateOnly right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.DateOnly left, System.DateOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.DateOnly left, System.DateOnly right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateOnly IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.DateOnly result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateOnly ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.DateOnly result) { throw null; } +#endif // FEATURE_GENERIC_MATH } public readonly partial struct DateTime : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IAdditionOperators, + System.IAdditiveIdentity, + System.IComparisonOperators, + System.IMinMaxValue, + System.ISpanParseable, + System.ISubtractionOperators, + System.ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.DateTime MaxValue; @@ -1498,6 +1817,48 @@ namespace System public static bool TryParseExact(System.ReadOnlySpan s, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? provider, System.Globalization.DateTimeStyles style, out System.DateTime result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? format, System.IFormatProvider? provider, System.Globalization.DateTimeStyles style, out System.DateTime result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? provider, System.Globalization.DateTimeStyles style, out System.DateTime result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTime IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTime IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.DateTime IAdditionOperators.operator +(System.DateTime left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.DateTime left, System.DateTime right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.DateTime left, System.DateTime right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.DateTime left, System.DateTime right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.DateTime left, System.DateTime right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.DateTime left, System.DateTime right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.DateTime left, System.DateTime right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTime IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.DateTime result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTime ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.DateTime result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTime ISubtractionOperators.operator -(System.DateTime left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan ISubtractionOperators.operator -(System.DateTime left, System.DateTime right) { throw null; } +#endif // FEATURE_GENERIC_MATH } public enum DateTimeKind { @@ -1506,6 +1867,16 @@ namespace System Local = 2, } public readonly partial struct DateTimeOffset : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IAdditionOperators, + System.IAdditiveIdentity, + System.IComparisonOperators, + System.IMinMaxValue, System.ISpanParseable, + System.ISubtractionOperators, + System.ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.DateTimeOffset MaxValue; @@ -1598,6 +1969,48 @@ namespace System public static bool TryParseExact(System.ReadOnlySpan input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? formatProvider, System.Globalization.DateTimeStyles styles, out System.DateTimeOffset result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? format, System.IFormatProvider? formatProvider, System.Globalization.DateTimeStyles styles, out System.DateTimeOffset result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? formatProvider, System.Globalization.DateTimeStyles styles, out System.DateTimeOffset result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTimeOffset IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTimeOffset IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.DateTimeOffset IAdditionOperators.operator +(System.DateTimeOffset left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTimeOffset IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.DateTimeOffset result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.DateTimeOffset ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.DateTimeOffset result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.DateTimeOffset ISubtractionOperators.operator -(System.DateTimeOffset left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan ISubtractionOperators.operator -(System.DateTimeOffset left, System.DateTimeOffset right) { throw null; } +#endif // FEATURE_GENERIC_MATH } public enum DayOfWeek { @@ -1634,6 +2047,12 @@ namespace System public string ToString(System.IFormatProvider? provider) { throw null; } } public readonly partial struct Decimal : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; [System.Runtime.CompilerServices.DecimalConstantAttribute((byte)0, (byte)0, (uint)4294967295, (uint)4294967295, (uint)4294967295)] @@ -1773,6 +2192,107 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Decimal result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Decimal result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Decimal result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IAdditionOperators.operator +(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(decimal left, decimal right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(decimal left, decimal right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(decimal left, decimal right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IDecrementOperators.operator --(decimal value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IDivisionOperators.operator /(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(decimal left, decimal right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IIncrementOperators.operator ++(decimal value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IModulusOperators.operator %(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IMultiplyOperators.operator *(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Abs(decimal value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Clamp(decimal value, decimal min, decimal max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (decimal Quotient, decimal Remainder) INumber.DivRem(decimal left, decimal right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Max(decimal x, decimal y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Min(decimal x, decimal y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal INumber.Sign(decimal value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out decimal result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out decimal result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static decimal ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out decimal result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal ISubtractionOperators.operator -(decimal left, decimal right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IUnaryNegationOperators.operator -(decimal value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static decimal IUnaryPlusOperators.operator +(decimal value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public abstract partial class Delegate : System.ICloneable, System.Runtime.Serialization.ISerializable { @@ -1821,6 +2341,12 @@ namespace System public DivideByZeroException(string? message, System.Exception? innerException) { } } public readonly partial struct Double : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryFloatingPoint, + System.IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly double _dummyPrimitive; public const double Epsilon = 5E-324; @@ -1878,6 +2404,213 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Double result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Double result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Double result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.E { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Epsilon { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.NaN { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.NegativeInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.NegativeZero { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Pi { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.PositiveInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Tau { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IAdditionOperators.operator +(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(double value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IBinaryNumber.Log2(double value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IBitwiseOperators.operator &(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IBitwiseOperators.operator |(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IBitwiseOperators.operator ^(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IBitwiseOperators.operator ~(double value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IDecrementOperators.operator --(double value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IDivisionOperators.operator /(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Acos(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Acosh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Asin(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Asinh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Atan(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Atan2(double y, double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Atanh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.BitIncrement(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.BitDecrement(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Cbrt(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Ceiling(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.CopySign(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Cos(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Cosh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Exp(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Floor(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.FusedMultiplyAdd(double left, double right, double addend) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.IEEERemainder(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static TInteger IFloatingPoint.ILogB(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Log(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Log(double x, double newBase) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Log2(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Log10(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.MaxMagnitude(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.MinMagnitude(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Pow(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Round(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Round(double x, TInteger digits) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Round(double x, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Round(double x, TInteger digits, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.ScaleB(double x, TInteger n) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Sin(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Sinh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Sqrt(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Tan(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Tanh(double x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IFloatingPoint.Truncate(double x) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IIncrementOperators.operator ++(double value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IModulusOperators.operator %(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IMultiplyOperators.operator *(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Abs(double value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Clamp(double value, double min, double max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (double Quotient, double Remainder) INumber.DivRem(double left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Max(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Min(double x, double y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double INumber.Sign(double value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out double result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out double result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out double result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double ISubtractionOperators.operator -(double left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IUnaryNegationOperators.operator -(double value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IUnaryPlusOperators.operator +(double value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial class DuplicateWaitObjectException : System.ArgumentException { @@ -2270,6 +3003,12 @@ namespace System public GopherStyleUriParser() { } } public readonly partial struct Guid : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IComparisonOperators, + System.ISpanParseable +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.Guid Empty; @@ -2303,8 +3042,40 @@ namespace System public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? format, out System.Guid result) { throw null; } public bool TryWriteBytes(System.Span destination) { throw null; } bool System.ISpanFormattable.TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.Guid left, System.Guid right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.Guid left, System.Guid right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.Guid left, System.Guid right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.Guid left, System.Guid right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.Guid left, System.Guid right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.Guid left, System.Guid right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Guid IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Guid result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Guid ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Guid result) { throw null; } +#endif // FEATURE_GENERIC_MATH } public readonly partial struct Half : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryFloatingPoint, + System.IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static System.Half Epsilon { get { throw null; } } @@ -2350,6 +3121,213 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.Half result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Half result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.E { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Epsilon { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.NaN { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.NegativeInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.NegativeZero { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Pi { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.PositiveInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Tau { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IAdditionOperators.operator +(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(System.Half value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IBinaryNumber.Log2(System.Half value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IBitwiseOperators.operator &(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IBitwiseOperators.operator |(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IBitwiseOperators.operator ^(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IBitwiseOperators.operator ~(System.Half value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IDecrementOperators.operator --(System.Half value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IDivisionOperators.operator /(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Acos(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Acosh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Asin(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Asinh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Atan(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Atan2(System.Half y, System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Atanh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.BitIncrement(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.BitDecrement(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Cbrt(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Ceiling(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.CopySign(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Cos(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Cosh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Exp(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Floor(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.FusedMultiplyAdd(System.Half left, System.Half right, System.Half addend) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.IEEERemainder(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static TInteger IFloatingPoint.ILogB(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Log(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Log(System.Half x, System.Half newBase) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Log2(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Log10(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.MaxMagnitude(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.MinMagnitude(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Pow(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Round(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Round(System.Half x, TInteger digits) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Round(System.Half x, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Round(System.Half x, TInteger digits, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.ScaleB(System.Half x, TInteger n) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Sin(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Sinh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Sqrt(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Tan(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Tanh(System.Half x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IFloatingPoint.Truncate(System.Half x) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IIncrementOperators.operator ++(System.Half value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IModulusOperators.operator %(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IMultiplyOperators.operator *(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Abs(System.Half value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Clamp(System.Half value, System.Half min, System.Half max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (System.Half Quotient, System.Half Remainder) INumber.DivRem(System.Half left, System.Half right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Max(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Min(System.Half x, System.Half y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half INumber.Sign(System.Half value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out System.Half result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Half result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Half result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half ISubtractionOperators.operator -(System.Half left, System.Half right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IUnaryNegationOperators.operator -(System.Half value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.Half IUnaryPlusOperators.operator +(System.Half value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial struct HashCode { @@ -2377,6 +3355,239 @@ namespace System { public HttpStyleUriParser() { } } +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IAdditionOperators + where TSelf : System.IAdditionOperators + { + static abstract TResult operator +(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IAdditiveIdentity + where TSelf : System.IAdditiveIdentity + { + static abstract TResult AdditiveIdentity { get; } + } +[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IBinaryFloatingPoint : System.IBinaryNumber, System.IFloatingPoint + where TSelf : IBinaryFloatingPoint + { + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IBinaryInteger : System.IBinaryNumber, System.IShiftOperators + where TSelf : IBinaryInteger + { + static abstract TSelf LeadingZeroCount(TSelf value); + static abstract TSelf PopCount(TSelf value); + static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + static abstract TSelf TrailingZeroCount(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IBinaryNumber : System.IBitwiseOperators, System.INumber + where TSelf : IBinaryNumber + { + static abstract bool IsPow2(TSelf value); + static abstract TSelf Log2(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IBitwiseOperators + where TSelf : System.IBitwiseOperators + { + static abstract TResult operator &(TSelf left, TOther right); + static abstract TResult operator |(TSelf left, TOther right); + static abstract TResult operator ^(TSelf left, TOther right); + static abstract TResult operator ~(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IComparisonOperators : System.IComparable, System.IComparable, System.IEqualityOperators + where TSelf : IComparisonOperators + { + static abstract bool operator <(TSelf left, TOther right); + static abstract bool operator <=(TSelf left, TOther right); + static abstract bool operator >(TSelf left, TOther right); + static abstract bool operator >=(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IDecrementOperators + where TSelf : System.IDecrementOperators + { + static abstract TSelf operator --(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IDivisionOperators + where TSelf : System.IDivisionOperators + { + static abstract TResult operator /(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IEqualityOperators : IEquatable + where TSelf : System.IEqualityOperators + { + static abstract bool operator ==(TSelf left, TOther right); + static abstract bool operator !=(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IFloatingPoint : System.ISignedNumber + where TSelf : System.IFloatingPoint + { + static abstract TSelf E { get; } + static abstract TSelf Epsilon { get; } + static abstract TSelf NaN { get; } + static abstract TSelf NegativeInfinity { get; } + static abstract TSelf NegativeZero { get; } + static abstract TSelf Pi { get; } + static abstract TSelf PositiveInfinity { get; } + static abstract TSelf Tau { get; } + static abstract TSelf Acos(TSelf x); + static abstract TSelf Acosh(TSelf x); + static abstract TSelf Asin(TSelf x); + static abstract TSelf Asinh(TSelf x); + static abstract TSelf Atan(TSelf x); + static abstract TSelf Atan2(TSelf y, TSelf x); + static abstract TSelf Atanh(TSelf x); + static abstract TSelf BitIncrement(TSelf x); + static abstract TSelf BitDecrement(TSelf x); + static abstract TSelf Cbrt(TSelf x); + static abstract TSelf Ceiling(TSelf x); + static abstract TSelf CopySign(TSelf x, TSelf y); + static abstract TSelf Cos(TSelf x); + static abstract TSelf Cosh(TSelf x); + static abstract TSelf Exp(TSelf x); + static abstract TSelf Floor(TSelf x); + static abstract TSelf FusedMultiplyAdd(TSelf left, TSelf right, TSelf addend); + static abstract TSelf IEEERemainder(TSelf left, TSelf right); + static abstract TInteger ILogB(TSelf x) where TInteger : IBinaryInteger; + static abstract bool IsFinite(TSelf value); + static abstract bool IsInfinity(TSelf value); + static abstract bool IsNaN(TSelf value); + static abstract bool IsNegative(TSelf value); + static abstract bool IsNegativeInfinity(TSelf value); + static abstract bool IsNormal(TSelf value); + static abstract bool IsPositiveInfinity(TSelf value); + static abstract bool IsSubnormal(TSelf value); + static abstract TSelf Log(TSelf x); + static abstract TSelf Log(TSelf x, TSelf newBase); + static abstract TSelf Log2(TSelf x); + static abstract TSelf Log10(TSelf x); + static abstract TSelf MaxMagnitude(TSelf x, TSelf y); + static abstract TSelf MinMagnitude(TSelf x, TSelf y); + static abstract TSelf Pow(TSelf x, TSelf y); + static abstract TSelf Round(TSelf x); + static abstract TSelf Round(TSelf x, TInteger digits) where TInteger : IBinaryInteger; + static abstract TSelf Round(TSelf x, MidpointRounding mode); + static abstract TSelf Round(TSelf x, TInteger digits, MidpointRounding mode) where TInteger : IBinaryInteger; + static abstract TSelf ScaleB(TSelf x, TInteger n) where TInteger : IBinaryInteger; + static abstract TSelf Sin(TSelf x); + static abstract TSelf Sinh(TSelf x); + static abstract TSelf Sqrt(TSelf x); + static abstract TSelf Tan(TSelf x); + static abstract TSelf Tanh(TSelf x); + static abstract TSelf Truncate(TSelf x); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IIncrementOperators + where TSelf : System.IIncrementOperators + { + static abstract TSelf operator ++(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IMinMaxValue + where TSelf : System.IMinMaxValue + { + static abstract TSelf MinValue { get; } + static abstract TSelf MaxValue { get; } + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IModulusOperators + where TSelf : System.IModulusOperators + { + static abstract TResult operator %(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IMultiplicativeIdentity + where TSelf : System.IMultiplicativeIdentity + { + static abstract TResult MultiplicativeIdentity { get; } + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IMultiplyOperators + where TSelf : System.IMultiplyOperators + { + static abstract TResult operator *(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface INumber : System.IAdditionOperators, System.IAdditiveIdentity, System.IComparable, System.IComparable, System.IComparisonOperators, System.IDecrementOperators, System.IDivisionOperators, System.IEquatable, System.IEqualityOperators, System.IFormattable, System.IIncrementOperators, System.IModulusOperators, System.IMultiplicativeIdentity, System.IMultiplyOperators, System.IParseable, System.ISpanFormattable, System.ISpanParseable, System.ISubtractionOperators, System.IUnaryNegationOperators, System.IUnaryPlusOperators + where TSelf : System.INumber + { + static abstract TSelf One { get; } + static abstract TSelf Zero { get; } + static abstract TSelf Abs(TSelf value); + static abstract TSelf Clamp(TSelf value, TSelf min, TSelf max); + static abstract TSelf Create(TOther value) where TOther : INumber; + static abstract TSelf CreateSaturating(TOther value) where TOther : INumber; + static abstract TSelf CreateTruncating(TOther value) where TOther : INumber; + static abstract (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right); + static abstract TSelf Max(TSelf x, TSelf y); + static abstract TSelf Min(TSelf x, TSelf y); + static abstract TSelf Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider); + static abstract TSelf Parse(ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider); + static abstract TSelf Sign(TSelf value); + static abstract bool TryCreate(TOther value, out TSelf result) where TOther : INumber; + static abstract bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out TSelf result); + static abstract bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, out TSelf result); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IParseable + where TSelf : System.IParseable + { + static abstract TSelf Parse(string s, System.IFormatProvider? provider); + static abstract bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out TSelf result); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IShiftOperators + where TSelf : System.IShiftOperators + { + static abstract TResult operator <<(TSelf value, int shiftAmount); + static abstract TResult operator >>(TSelf value, int shiftAmount); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface ISignedNumber : System.INumber, System.IUnaryNegationOperators + where TSelf : System.ISignedNumber + { + static abstract TSelf NegativeOne { get; } + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface ISpanParseable : System.IParseable + where TSelf : System.ISpanParseable + { + static abstract TSelf Parse(System.ReadOnlySpan s, System.IFormatProvider? provider); + static abstract bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out TSelf result); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface ISubtractionOperators + where TSelf : System.ISubtractionOperators + { + static abstract TResult operator -(TSelf left, TOther right); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IUnaryNegationOperators + where TSelf : System.IUnaryNegationOperators + { + static abstract TResult operator -(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IUnaryPlusOperators + where TSelf : System.IUnaryPlusOperators + { + static abstract TResult operator +(TSelf value); + } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + public partial interface IUnsignedNumber : System.INumber + where TSelf : IUnsignedNumber + { + } +#endif // FEATURE_GENERIC_MATH public partial interface IAsyncDisposable { System.Threading.Tasks.ValueTask DisposeAsync(); @@ -2477,6 +3688,13 @@ namespace System public InsufficientMemoryException(string? message, System.Exception? innerException) { } } public readonly partial struct Int16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly short _dummyPrimitive; public const short MaxValue = (short)32767; @@ -2516,8 +3734,146 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.Int16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int16 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IAdditionOperators.operator +(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryInteger.LeadingZeroCount(short value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryInteger.PopCount(short value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryInteger.RotateLeft(short value, short rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryInteger.RotateRight(short value, short rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryInteger.TrailingZeroCount(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(short value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBinaryNumber.Log2(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBitwiseOperators.operator &(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBitwiseOperators.operator |(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBitwiseOperators.operator ^(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IBitwiseOperators.operator ~(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IDecrementOperators.operator --(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IDivisionOperators.operator /(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IIncrementOperators.operator ++(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IModulusOperators.operator %(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IMultiplyOperators.operator *(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Abs(short value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Clamp(short value, short min, short max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (short Quotient, short Remainder) INumber.DivRem(short left, short right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Max(short x, short y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Min(short x, short y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short INumber.Sign(short value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out short result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out short result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static short IShiftOperators.operator <<(short value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static short IShiftOperators.operator >>(short value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out short result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short ISubtractionOperators.operator -(short left, short right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IUnaryNegationOperators.operator -(short value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static short IUnaryPlusOperators.operator +(short value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public readonly partial struct Int32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public const int MaxValue = 2147483647; @@ -2557,8 +3913,146 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.Int32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int32 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IAdditionOperators.operator +(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryInteger.LeadingZeroCount(int value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryInteger.PopCount(int value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryInteger.RotateLeft(int value, int rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryInteger.RotateRight(int value, int rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryInteger.TrailingZeroCount(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(int value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBinaryNumber.Log2(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBitwiseOperators.operator &(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBitwiseOperators.operator |(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBitwiseOperators.operator ^(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IBitwiseOperators.operator ~(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IDecrementOperators.operator --(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IDivisionOperators.operator /(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IIncrementOperators.operator ++(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IModulusOperators.operator %(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IMultiplyOperators.operator *(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Abs(int value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Clamp(int value, int min, int max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (int Quotient, int Remainder) INumber.DivRem(int left, int right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Max(int x, int y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Min(int x, int y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int INumber.Sign(int value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out int result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out int result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static int IShiftOperators.operator <<(int value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static int IShiftOperators.operator >>(int value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out int result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int ISubtractionOperators.operator -(int left, int right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IUnaryNegationOperators.operator -(int value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static int IUnaryPlusOperators.operator +(int value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public readonly partial struct Int64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly long _dummyPrimitive; public const long MaxValue = (long)9223372036854775807; @@ -2598,8 +4092,146 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.Int64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int64 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IAdditionOperators.operator +(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryInteger.LeadingZeroCount(long value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryInteger.PopCount(long value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryInteger.RotateLeft(long value, long rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryInteger.RotateRight(long value, long rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryInteger.TrailingZeroCount(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(long value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBinaryNumber.Log2(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBitwiseOperators.operator &(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBitwiseOperators.operator |(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBitwiseOperators.operator ^(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IBitwiseOperators.operator ~(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IDecrementOperators.operator --(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IDivisionOperators.operator /(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IIncrementOperators.operator ++(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IModulusOperators.operator %(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IMultiplyOperators.operator *(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Abs(long value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Clamp(long value, long min, long max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (long Quotient, long Remainder) INumber.DivRem(long left, long right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Max(long x, long y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Min(long x, long y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long INumber.Sign(long value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out long result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out long result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static long IShiftOperators.operator <<(long value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static long IShiftOperators.operator >>(long value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out long result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long ISubtractionOperators.operator -(long left, long right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IUnaryNegationOperators.operator -(long value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static long IUnaryPlusOperators.operator +(long value) { throw null; } +#endif // FEATURE_GENERIC_MATH } - public readonly partial struct IntPtr : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable + public readonly partial struct IntPtr : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.IntPtr Zero; @@ -2648,6 +4280,137 @@ namespace System public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.IntPtr result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.IntPtr result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.IntPtr result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IAdditionOperators.operator +(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryInteger.LeadingZeroCount(nint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryInteger.PopCount(nint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryInteger.TrailingZeroCount(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(nint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBinaryNumber.Log2(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBitwiseOperators.operator &(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBitwiseOperators.operator |(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBitwiseOperators.operator ^(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IBitwiseOperators.operator ~(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IDecrementOperators.operator --(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IDivisionOperators.operator /(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IIncrementOperators.operator ++(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IModulusOperators.operator %(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IMultiplyOperators.operator *(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Abs(nint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Clamp(nint value, nint min, nint max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (nint Quotient, nint Remainder) INumber.DivRem(nint left, nint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Max(nint x, nint y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Min(nint x, nint y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint INumber.Sign(nint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out nint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out nint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static nint IShiftOperators.operator <<(nint value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static nint IShiftOperators.operator >>(nint value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out nint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint ISubtractionOperators.operator -(nint left, nint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IUnaryNegationOperators.operator -(nint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nint IUnaryPlusOperators.operator +(nint value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial class InvalidCastException : System.SystemException { @@ -3434,6 +5197,13 @@ namespace System } [System.CLSCompliantAttribute(false)] public readonly partial struct SByte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.ISignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly sbyte _dummyPrimitive; public const sbyte MaxValue = (sbyte)127; @@ -3473,6 +5243,137 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.SByte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.SByte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.SByte result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IAdditionOperators.operator +(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryInteger.LeadingZeroCount(sbyte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryInteger.PopCount(sbyte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(sbyte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBinaryNumber.Log2(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBitwiseOperators.operator &(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBitwiseOperators.operator |(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBitwiseOperators.operator ^(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IBitwiseOperators.operator ~(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IDecrementOperators.operator --(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IDivisionOperators.operator /(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IIncrementOperators.operator ++(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IModulusOperators.operator %(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IMultiplyOperators.operator *(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Abs(sbyte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Clamp(sbyte value, sbyte min, sbyte max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (sbyte Quotient, sbyte Remainder) INumber.DivRem(sbyte left, sbyte right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Max(sbyte x, sbyte y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Min(sbyte x, sbyte y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte INumber.Sign(sbyte value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out sbyte result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out sbyte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static sbyte IShiftOperators.operator <<(sbyte value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static sbyte IShiftOperators.operator >>(sbyte value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out sbyte result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte ISubtractionOperators.operator -(sbyte left, sbyte right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IUnaryNegationOperators.operator -(sbyte value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static sbyte IUnaryPlusOperators.operator +(sbyte value) { throw null; } +#endif // FEATURE_GENERIC_MATH } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Struct, Inherited=false)] public sealed partial class SerializableAttribute : System.Attribute @@ -3480,6 +5381,12 @@ namespace System public SerializableAttribute() { } } public readonly partial struct Single : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryFloatingPoint, + System.IMinMaxValue +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly float _dummyPrimitive; public const float Epsilon = 1E-45f; @@ -3537,6 +5444,213 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.Single result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Single result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Single result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.E { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Epsilon { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.NaN { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.NegativeInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.NegativeZero { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Pi { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.PositiveInfinity { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Tau { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IAdditionOperators.operator +(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(float value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IBinaryNumber.Log2(float value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IBitwiseOperators.operator &(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IBitwiseOperators.operator |(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IBitwiseOperators.operator ^(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IBitwiseOperators.operator ~(float value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IDecrementOperators.operator --(float value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IDivisionOperators.operator /(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Acos(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Acosh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Asin(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Asinh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Atan(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Atan2(float y, float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Atanh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.BitIncrement(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.BitDecrement(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Cbrt(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Ceiling(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.CopySign(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Cos(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Cosh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Exp(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Floor(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.FusedMultiplyAdd(float left, float right, float addend) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.IEEERemainder(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static TInteger IFloatingPoint.ILogB(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Log(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Log(float x, float newBase) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Log2(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Log10(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.MaxMagnitude(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.MinMagnitude(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Pow(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Round(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Round(float x, TInteger digits) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Round(float x, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Round(float x, TInteger digits, System.MidpointRounding mode) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.ScaleB(float x, TInteger n) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Sin(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Sinh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Sqrt(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Tan(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Tanh(float x) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IFloatingPoint.Truncate(float x) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IIncrementOperators.operator ++(float value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IModulusOperators.operator %(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IMultiplyOperators.operator *(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Abs(float value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Clamp(float value, float min, float max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (float Quotient, float Remainder) INumber.DivRem(float left, float right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Max(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Min(float x, float y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float INumber.Sign(float value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out float result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out float result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float ISignedNumber.NegativeOne { get; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out float result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float ISubtractionOperators.operator -(float left, float right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IUnaryNegationOperators.operator -(float value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static float IUnaryPlusOperators.operator +(float value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public readonly ref partial struct Span { @@ -3843,7 +5957,15 @@ namespace System { public ThreadStaticAttribute() { } } - public readonly struct TimeOnly : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable + public readonly partial struct TimeOnly : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IComparisonOperators, + System.IMinMaxValue, + System.ISpanParseable, + System.ISubtractionOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { public static System.TimeOnly MinValue { get { throw null; } } public static System.TimeOnly MaxValue { get { throw null; } } @@ -3907,6 +6029,35 @@ namespace System public string ToString(System.IFormatProvider? provider) { throw null; } public string ToString(string? format, System.IFormatProvider? provider) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.TimeOnly left, System.TimeOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.TimeOnly left, System.TimeOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.TimeOnly left, System.TimeOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.TimeOnly left, System.TimeOnly right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.TimeOnly left, System.TimeOnly right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.TimeOnly left, System.TimeOnly right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeOnly IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.TimeOnly result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeOnly ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.TimeOnly result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan ISubtractionOperators.operator -(System.TimeOnly left, System.TimeOnly right) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial class TimeoutException : System.SystemException { @@ -3916,6 +6067,22 @@ namespace System public TimeoutException(string? message, System.Exception? innerException) { } } public readonly partial struct TimeSpan : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IAdditionOperators, + System.IAdditiveIdentity, + System.IComparisonOperators, + System.IDivisionOperators, + System.IDivisionOperators, + System.IMinMaxValue, + System.IMultiplyOperators, + System.IMultiplicativeIdentity, + System.ISpanParseable, + System.ISubtractionOperators, + System.IUnaryNegationOperators, + System.IUnaryPlusOperators +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.TimeSpan MaxValue; @@ -4000,6 +6167,63 @@ namespace System public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? format, System.IFormatProvider? formatProvider, out System.TimeSpan result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? formatProvider, System.Globalization.TimeSpanStyles styles, out System.TimeSpan result) { throw null; } public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string?[]? formats, System.IFormatProvider? formatProvider, out System.TimeSpan result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static System.TimeSpan IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static double IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IAdditionOperators.operator +(System.TimeSpan left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(System.TimeSpan left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(System.TimeSpan left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(System.TimeSpan left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(System.TimeSpan left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IDivisionOperators.operator /(System.TimeSpan left, double right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static double IDivisionOperators.operator /(System.TimeSpan left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(System.TimeSpan left, System.TimeSpan right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(System.TimeSpan left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IMultiplyOperators.operator *(System.TimeSpan left, double right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.TimeSpan result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan ISpanParseable.Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.TimeSpan result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan ISubtractionOperators.operator -(System.TimeSpan left, System.TimeSpan right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IUnaryNegationOperators.operator -(System.TimeSpan value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static System.TimeSpan IUnaryPlusOperators.operator +(System.TimeSpan value) { throw null; } +#endif // FEATURE_GENERIC_MATH } [System.ObsoleteAttribute("System.TimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo instead.")] public abstract partial class TimeZone @@ -4683,6 +6907,13 @@ namespace System } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly ushort _dummyPrimitive; public const ushort MaxValue = (ushort)65535; @@ -4722,9 +6953,144 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.UInt16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt16 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IAdditionOperators.operator +(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryInteger.LeadingZeroCount(ushort value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryInteger.PopCount(ushort value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryInteger.TrailingZeroCount(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(ushort value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBinaryNumber.Log2(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBitwiseOperators.operator &(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBitwiseOperators.operator |(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBitwiseOperators.operator ^(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IBitwiseOperators.operator ~(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IDecrementOperators.operator --(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IDivisionOperators.operator /(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IIncrementOperators.operator ++(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IModulusOperators.operator %(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IMultiplyOperators.operator *(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Abs(ushort value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Clamp(ushort value, ushort min, ushort max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (ushort Quotient, ushort Remainder) INumber.DivRem(ushort left, ushort right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Max(ushort x, ushort y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Min(ushort x, ushort y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort INumber.Sign(ushort value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out ushort result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out ushort result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static ushort IShiftOperators.operator <<(ushort value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static ushort IShiftOperators.operator >>(ushort value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out ushort result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort ISubtractionOperators.operator -(ushort left, ushort right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IUnaryNegationOperators.operator -(ushort value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ushort IUnaryPlusOperators.operator +(ushort value) { throw null; } +#endif // FEATURE_GENERIC_MATH } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly uint _dummyPrimitive; public const uint MaxValue = (uint)4294967295; @@ -4764,9 +7130,144 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.UInt32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt32 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IAdditionOperators.operator +(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryInteger.LeadingZeroCount(uint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryInteger.PopCount(uint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryInteger.TrailingZeroCount(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(uint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBinaryNumber.Log2(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBitwiseOperators.operator &(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBitwiseOperators.operator |(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBitwiseOperators.operator ^(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IBitwiseOperators.operator ~(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IDecrementOperators.operator --(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IDivisionOperators.operator /(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IIncrementOperators.operator ++(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IModulusOperators.operator %(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IMultiplyOperators.operator *(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Abs(uint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Clamp(uint value, uint min, uint max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (uint Quotient, uint Remainder) INumber.DivRem(uint left, uint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Max(uint x, uint y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Min(uint x, uint y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint INumber.Sign(uint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out uint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out uint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static uint IShiftOperators.operator <<(uint value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static uint IShiftOperators.operator >>(uint value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out uint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint ISubtractionOperators.operator -(uint left, uint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IUnaryNegationOperators.operator -(uint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static uint IUnaryPlusOperators.operator +(uint value) { throw null; } +#endif // FEATURE_GENERIC_MATH } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.ISpanFormattable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly ulong _dummyPrimitive; public const ulong MaxValue = (ulong)18446744073709551615; @@ -4806,9 +7307,144 @@ namespace System public static bool TryParse(System.ReadOnlySpan s, out System.UInt64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt64 result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IAdditionOperators.operator +(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryInteger.LeadingZeroCount(ulong value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryInteger.PopCount(ulong value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryInteger.TrailingZeroCount(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(ulong value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBinaryNumber.Log2(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBitwiseOperators.operator &(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBitwiseOperators.operator |(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBitwiseOperators.operator ^(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IBitwiseOperators.operator ~(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IDecrementOperators.operator --(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IDivisionOperators.operator /(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IIncrementOperators.operator ++(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IModulusOperators.operator %(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IMultiplyOperators.operator *(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Abs(ulong value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Clamp(ulong value, ulong min, ulong max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (ulong Quotient, ulong Remainder) INumber.DivRem(ulong left, ulong right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Max(ulong x, ulong y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Min(ulong x, ulong y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong INumber.Sign(ulong value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out ulong result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out ulong result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static ulong IShiftOperators.operator <<(ulong value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static ulong IShiftOperators.operator >>(ulong value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out ulong result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong ISubtractionOperators.operator -(ulong left, ulong right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IUnaryNegationOperators.operator -(ulong value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static ulong IUnaryPlusOperators.operator +(ulong value) { throw null; } +#endif // FEATURE_GENERIC_MATH } [System.CLSCompliantAttribute(false)] - public readonly partial struct UIntPtr : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable + public readonly partial struct UIntPtr : System.IComparable, System.IComparable, System.IEquatable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable +#if FEATURE_GENERIC_MATH +#pragma warning disable SA1001 + , System.IBinaryInteger, + System.IMinMaxValue, + System.IUnsignedNumber +#pragma warning restore SA1001 +#endif // FEATURE_GENERIC_MATH { private readonly int _dummyPrimitive; public static readonly System.UIntPtr Zero; @@ -4853,6 +7489,133 @@ namespace System public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UIntPtr result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.UIntPtr result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UIntPtr result) { throw null; } + +#if FEATURE_GENERIC_MATH + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IMinMaxValue.MinValue { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IMinMaxValue.MaxValue { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IMultiplicativeIdentity.MultiplicativeIdentity { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.One { get { throw null; } } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Zero { get { throw null; } } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IAdditionOperators.operator +(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryInteger.LeadingZeroCount(nuint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryInteger.PopCount(nuint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryInteger.TrailingZeroCount(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IBinaryNumber.IsPow2(nuint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBinaryNumber.Log2(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBitwiseOperators.operator &(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBitwiseOperators.operator |(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBitwiseOperators.operator ^(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IBitwiseOperators.operator ~(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator <=(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IComparisonOperators.operator >=(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IDecrementOperators.operator --(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IDivisionOperators.operator /(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator ==(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IEqualityOperators.operator !=(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IIncrementOperators.operator ++(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IModulusOperators.operator %(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IMultiplyOperators.operator *(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Abs(nuint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Clamp(nuint value, nuint min, nuint max) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Create(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.CreateSaturating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.CreateTruncating(TOther value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static (nuint Quotient, nuint Remainder) INumber.DivRem(nuint left, nuint right) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Max(nuint x, nuint y) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Min(nuint x, nuint y) { throw null; }[System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint INumber.Sign(nuint value) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryCreate(TOther value, out nuint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool INumber.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IParseable.Parse(string s, System.IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool IParseable.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out nuint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeatures] + static nuint IShiftOperators.operator <<(nuint value, int shiftAmount) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeatures] + static nuint IShiftOperators.operator >>(nuint value, int shiftAmount) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint ISpanParseable.Parse(System.ReadOnlySpan s, IFormatProvider? provider) { throw null; } + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static bool ISpanParseable.TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out nuint result) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint ISubtractionOperators.operator -(nuint left, nuint right) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IUnaryNegationOperators.operator -(nuint value) { throw null; } + + [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] + static nuint IUnaryPlusOperators.operator +(nuint value) { throw null; } +#endif // FEATURE_GENERIC_MATH } public partial class UnauthorizedAccessException : System.SystemException { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.csproj b/src/libraries/System.Runtime/ref/System.Runtime.csproj index 9a05ef6d403..c978be13789 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.csproj +++ b/src/libraries/System.Runtime/ref/System.Runtime.csproj @@ -1,6 +1,7 @@ true + true v4.0.30319 <_runtimeOS Condition="'$(TargetsMobile)' == 'true'">$(TargetOS.ToLowerInvariant()) - <_runtimeOSVersionIndex>$(_runtimeOS.IndexOfAny(".-0123456789")) - <_runtimeOSFamily Condition="'$(_runtimeOSVersionIndex)' != '-1'">$(_runtimeOS.SubString(0, $(_runtimeOSVersionIndex))) - <_portableOS>linux <_portableOS Condition="'$(_runtimeOS)' == 'linux-musl'">linux-musl - <_portableOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx - <_portableOS Condition="'$(_runtimeOSFamily)' == 'win' or '$(_runtimeOS)' == 'win' or '$(TargetOS)' == 'windows'">win - <_portableOS Condition="'$(_runtimeOSFamily)' == 'FreeBSD'">freebsd - <_portableOS Condition="'$(_runtimeOSFamily)' == 'illumos'">illumos - <_portableOS Condition="'$(_runtimeOSFamily)' == 'Solaris'">solaris + <_portableOS Condition="'$(_hostOS)' == 'OSX'">osx + <_portableOS Condition="'$(_runtimeOS)' == 'win' or '$(TargetOS)' == 'windows'">win + <_portableOS Condition="'$(_runtimeOS)' == 'FreeBSD' or '$(TargetOS)' == 'FreeBSD'">freebsd + <_portableOS Condition="'$(_runtimeOS)' == 'illumos' or '$(TargetOS)' == 'illumos'">illumos + <_portableOS Condition="'$(_runtimeOS)' == 'Solaris' or '$(TargetOS)' == 'Solaris'">solaris <_portableOS Condition="'$(_runtimeOS)' == 'Browser'">browser <_portableOS Condition="'$(_runtimeOS)' == 'maccatalyst'">maccatalyst <_portableOS Condition="'$(_runtimeOS)' == 'ios'">ios @@ -135,16 +134,12 @@ <_runtimeOS Condition="$(_runtimeOS.StartsWith('tizen'))">linux <_runtimeOS Condition="'$(PortableBuild)' == 'true'">$(_portableOS) - - - <_portableOS Condition="'$(TargetOS)' == 'Unix' and '$(_runtimeOSFamily)' != 'osx' and '$(_runtimeOSFamily)' != 'FreeBSD' and '$(_runtimeOS)' != 'linux-musl' and '$(_runtimeOSFamily)' != 'illumos' and '$(_runtimeOSFamily)' != 'Solaris'">linux + <_toolRuntimeRID Condition="'$(CrossBuild)' == 'true'">$(_hostOS.ToLowerInvariant)-$(_hostArch) <_toolRuntimeRID Condition="'$(BuildingInsideVisualStudio)' == 'true'">$(_runtimeOS)-x64 <_toolRuntimeRID Condition="'$(_toolRuntimeRID)' == ''">$(_runtimeOS)-$(_hostArch) - - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'linux-musl' and $(TargetArchitecture.StartsWith('arm')) and !$(_hostArch.StartsWith('arm'))">linux-x64 <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser'">linux-x64 @@ -164,26 +159,12 @@ $(_toolRuntimeRID) <_packageRID Condition="'$(PortableBuild)' == 'true'">$(_portableOS)-$(TargetArchitecture) + <_packageRID Condition="'$(CrossBuild)' == 'true'">$(_hostOS.ToLowerInvariant)-$(TargetArchitecture) $(_packageRID) $(_runtimeOS)-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'windows'">win-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'OSX'">osx-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Linux'">linux-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'FreeBSD'">freebsd-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'NetBSD'">netbsd-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'illumos'">illumos-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Solaris'">solaris-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'MacCatalyst'">maccatalyst-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'iOS'">ios-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'iOSSimulator'">iossimulator-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'tvOS'">tvos-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'tvOSSimulator'">tvossimulator-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Android'">android-$(TargetArchitecture) - <_outputRID Condition="'$(TargetOS)' == 'Browser'">browser-$(TargetArchitecture) - $(PackageRID) - $(_outputRID) + $(_portableOS)-$(TargetArchitecture) diff --git a/src/coreclr/.nuget/Directory.Build.props b/src/coreclr/.nuget/Directory.Build.props index c8c468ebc60..cff00f49e0e 100644 --- a/src/coreclr/.nuget/Directory.Build.props +++ b/src/coreclr/.nuget/Directory.Build.props @@ -119,8 +119,8 @@ - - + amd64 $(TargetArchitecture) diff --git a/src/coreclr/.nuget/builds.targets b/src/coreclr/.nuget/builds.targets index afab9019e11..4dd4d825bae 100644 --- a/src/coreclr/.nuget/builds.targets +++ b/src/coreclr/.nuget/builds.targets @@ -7,11 +7,11 @@ - + - <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(PackageRID)'" /> + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(OutputRid)'" /> @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj index be3bd23767f..4b1fc1af499 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj @@ -7,7 +7,7 @@ $(TargetArchitecture) arm - $(_runtimeOS)-$(TargetArchitectureAppHost) + $(PackageRID) diff --git a/src/libraries/pkg/Directory.Build.props b/src/libraries/pkg/Directory.Build.props index f05c02795a6..d31c32ee647 100644 --- a/src/libraries/pkg/Directory.Build.props +++ b/src/libraries/pkg/Directory.Build.props @@ -16,8 +16,8 @@ - - + + amd64 $(TargetArchitecture) diff --git a/src/libraries/pkg/dir.traversal.targets b/src/libraries/pkg/dir.traversal.targets index 0cb3942d47a..a3dd57e19a8 100644 --- a/src/libraries/pkg/dir.traversal.targets +++ b/src/libraries/pkg/dir.traversal.targets @@ -47,18 +47,18 @@ - FilterProjectsPackageRID; + FilterProjectsOutputRid; $(TraversalBuildDependsOn); - - + + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '' and '$(SkipBuildIdentityPackage)' != 'true'" /> - <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(PackageRID)' and '$(SkipBuildRuntimePackage)' != 'true'" /> + <_projectsToBuild Include="@(Project)" Condition="'%(Project.PackageTargetRuntime)' == '$(OutputRid)' and '$(SkipBuildRuntimePackage)' != 'true'" /> diff --git a/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj b/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj index 8fedc510cad..b7b43e933df 100644 --- a/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj +++ b/src/libraries/pkg/runtime.native.System.IO.Ports/runtime.native.System.IO.Ports.pkgproj @@ -6,7 +6,7 @@ - runtimes/$(PackageRID)/native + runtimes/$(OutputRid)/native diff --git a/src/tests/build.proj b/src/tests/build.proj index d1ce4ab95ba..e06e245e965 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -43,7 +43,7 @@ - <_ConfigurationProperties>/p:TargetOS=$(TargetOS) /p:TargetArchitecture=$(TargetArchitecture) /p:Configuration=$(Configuration) + <_ConfigurationProperties>/p:TargetOS=$(TargetOS) /p:TargetArchitecture=$(TargetArchitecture) /p:Configuration=$(Configuration) /p:CrossBuild=$(CrossBuild) "$(DotNetTool)" restore $(RestoreProj) $(PackageVersionArg) /p:SetTFMForRestore=true $(_ConfigurationProperties) "$(DotNetTool)" restore -r $(__DistroRid) $(RestoreProj) $(PackageVersionArg) /p:SetTFMForRestore=true $(_ConfigurationProperties) diff --git a/src/tests/build.sh b/src/tests/build.sh index da6fa03e1cf..e5575f69f98 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -573,6 +573,10 @@ if [[ "${__BuildArch}" != "${__HostArch}" ]]; then __CrossBuild=1 fi +if [[ "$__CrossBuild" == 1 ]]; then + __UnprocessedBuildArgs+=("/p:CrossBuild=true") +fi + # Set dependent variables __LogsDir="$__RootBinDir/log" __MsbuildDebugLogsDir="$__LogsDir/MsbuildDebugLogs" From 8e3d6cd3fecf2576608680faefa5cf3aefa9480d Mon Sep 17 00:00:00 2001 From: Anirudh Agnihotry Date: Tue, 6 Jul 2021 06:08:40 -0700 Subject: [PATCH 280/926] port system.diagnostics.performanceCounter (#55134) * port system.diagnostics.performanceCounter * remvoing custom and private targets hook and remove unused package rferences --- Directory.Build.targets | 5 +- ...tem.Diagnostics.PerformanceCounter.pkgproj | 12 -- .../src/CompatibilitySuppressions.xml | 124 ++++++++++++++++++ ...stem.Diagnostics.PerformanceCounter.csproj | 7 +- 4 files changed, 130 insertions(+), 18 deletions(-) delete mode 100644 src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj create mode 100644 src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml diff --git a/Directory.Build.targets b/Directory.Build.targets index 0460647e6de..e390cc3dda3 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -54,7 +54,9 @@ $(RuntimePackageDisclaimer) $(PackageDescription) + $(BeforePack);AddNETStandardCompatErrorFileForPackaging + AddNETStandardCompatErrorFileForPackaging;$(GenerateNuspecDependsOn) @@ -76,8 +78,7 @@ + Outputs="unused"> <_NETStandardCompatErrorFilePath>$(BaseIntermediateOutputPath)netstandardcompaterrors\%(NETStandardCompatError.Identity)\$(PackageId).targets <_NETStandardCompatErrorFileTarget>NETStandardCompatError_$(PackageId.Replace('.', '_'))_$([System.String]::new('%(NETStandardCompatError.Supported)').Replace('.', '_')) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj b/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj deleted file mode 100644 index 2fef4179be2..00000000000 --- a/src/libraries/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - uap10.0.16299;net461;netcoreapp2.0;$(AllXamarinFrameworks) - - - - - - - diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..fe46e5dfbd6 --- /dev/null +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml @@ -0,0 +1,124 @@ + + + + + CP0001 + T:System.Diagnostics.CounterCreationData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterCreationDataCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterSample + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.CounterSampleCalculator + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.ICollectData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceDataCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.InstanceDataCollectionCollection + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounter + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterCategory + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterCategoryType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterInstanceLifetime + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterManager + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceCounterType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterData + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSet + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstance + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstanceCounterDataSet + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterSetInstanceType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + + CP0001 + T:System.Diagnostics.PerformanceData.CounterType + lib/netstandard2.0/System.Diagnostics.PerformanceCounter.dll + lib/net461/System.Diagnostics.PerformanceCounter.dll + + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index 600f6fc58b9..8f68e23f04d 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -3,6 +3,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 $(NoWarn);CA1847 + true @@ -149,9 +150,7 @@ - - - - + + From 1c57282e6885dac6be2c4793fa7317ed25c05283 Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Tue, 6 Jul 2021 07:45:47 -0700 Subject: [PATCH 281/926] Add test global install path is used when DOTNET_ROOT does not exist (#54883) * Add test * PR feedback Add test covering case where DOTNET_ROOT exists but does not have a dotnet installation on it * Update tests * Use product behavior for test where DOTNET_ROOT exists but has no host * Fix test * Get platform specific extension for hostfxr Use HaveUsedNetRootInstallLocation --- .../MultiArchInstallLocation.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs index c1b00cafd4e..3206cd74d15 100644 --- a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs +++ b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs @@ -71,6 +71,56 @@ namespace HostActivation.Tests .And.NotHaveStdErrContaining("Using environment variable DOTNET_ROOT="); } + [Fact] + public void EnvironmentVariable_DotnetRootPathDoesNotExist() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + using (TestOnlyProductBehavior.Enable(appExe)) + { + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot("non_existent_path") + .MultilevelLookup(false) + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, + sharedTestState.InstallLocation) + .Execute() + .Should().Pass() + .And.HaveStdErrContaining("Did not find [DOTNET_ROOT] directory [non_existent_path]") + // If DOTNET_ROOT points to a folder that does not exist, we fall back to the global install path. + .And.HaveUsedGlobalInstallLocation(sharedTestState.InstallLocation) + .And.HaveStdOutContaining("Hello World"); + } + } + + [Fact] + public void EnvironmentVariable_DotnetRootPathExistsButHasNoHost() + { + var fixture = sharedTestState.PortableAppFixture + .Copy(); + + var appExe = fixture.TestProject.AppExe; + var projDir = fixture.TestProject.ProjectDirectory; + using (TestOnlyProductBehavior.Enable(appExe)) + { + Command.Create(appExe) + .EnableTracingAndCaptureOutputs() + .DotNetRoot(projDir) + .MultilevelLookup(false) + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath, + sharedTestState.InstallLocation) + .Execute() + .Should().Fail() + .And.HaveUsedDotNetRootInstallLocation(projDir, fixture.CurrentRid) + // If DOTNET_ROOT points to a folder that exists we assume that there's a dotnet installation in it + .And.HaveStdErrContaining($"A fatal error occurred. The required library {RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform ("hostfxr")} could not be found."); + } + } + [Fact] [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] public void InstallLocationFile_ArchSpecificLocationIsPickedFirst() @@ -173,6 +223,7 @@ namespace HostActivation.Tests PortableAppFixture = fixture; BaseDirectory = Path.GetDirectoryName(PortableAppFixture.SdkDotnet.GreatestVersionHostFxrFilePath); + InstallLocation = fixture.BuiltDotnet.BinPath; } public void Dispose() From dd631901d0f17e11aa0542eac056cc0199020949 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 6 Jul 2021 11:08:20 -0400 Subject: [PATCH 282/926] Fix a couple missing SupportedOSPlatform attributes on ThreadPool (#55148) --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 2 ++ .../ref/System.Threading.ThreadPool.cs | 2 ++ .../src/System/Threading/ThreadPool.Mono.cs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 86dd19a5a75..47b1bd454d8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -450,6 +450,7 @@ namespace System.Threading private static extern unsafe bool PostQueuedCompletionStatus(NativeOverlapped* overlapped); [CLSCompliant(false)] + [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => PostQueuedCompletionStatus(overlapped); @@ -547,6 +548,7 @@ namespace System.Threading ); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [SupportedOSPlatform("windows")] public static bool BindHandle(IntPtr osHandle) { return BindIOCompletionCallbackNative(osHandle); diff --git a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs index 5d4212129c1..0c31290c67a 100644 --- a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs +++ b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs @@ -22,6 +22,7 @@ namespace System.Threading public static long PendingWorkItemCount { get { throw null; } } public static int ThreadCount { get { throw null; } } [System.ObsoleteAttribute("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static bool BindHandle(System.IntPtr osHandle) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static bool BindHandle(System.Runtime.InteropServices.SafeHandle osHandle) { throw null; } @@ -43,6 +44,7 @@ namespace System.Threading public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { throw null; } public static bool SetMinThreads(int workerThreads, int completionPortThreads) { throw null; } [System.CLSCompliantAttribute(false)] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public unsafe static bool UnsafeQueueNativeOverlapped(System.Threading.NativeOverlapped* overlapped) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Threading.IThreadPoolWorkItem callBack, bool preferLocal) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Threading.WaitCallback callBack, object? state) { throw null; } diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs index 11b23df3888..786f2220a3d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs @@ -19,6 +19,7 @@ namespace System.Threading } [CLSCompliant(false)] + [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) { // OS doesn't signal handle, so do it here (CoreCLR does this assignment in ThreadPoolNative::CorPostQueuedCompletionStatus) @@ -28,6 +29,7 @@ namespace System.Threading } [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [SupportedOSPlatform("windows")] public static bool BindHandle(IntPtr osHandle) { throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle From c865a54d1db736ea1f8e5c489273a7690ddb6053 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 6 Jul 2021 17:32:13 +0200 Subject: [PATCH 283/926] [Mono] Optimize mono_dl_build_path for component and Android module loading. (#54971) * Optimize mono_dl_build_path for component and Android module loading. mono_dl_build_path probes a lot of variations of paths and in some situations where modules uses platform specific naming, it could try at least two paths before getting to the correct one. This have been a problem for Android a long time since IO is slow on devices, so always failing two probes per module load adds to the startup time on Android. For dynamic component loading the existing schema is particular bad since the scenario of disabling a component means that the component won't exist, and using the existing probing it could do 3 or more load attempts before realize that the component is not present and use the stub implmenentation. Commit splits current mono_dl_build_path probing logic so it can be customized. For components a new function has been added that will always use platform prefix and platform suffixes, and if not found it will stop looking. For most platform that means only one attempt to load a component instead of at least 3, optimize both the scenario when the component is awailable as well as when it should be disabled. Commit also change the default behavior of mono_dl_build_path on Android reversing current schema making it more likely to find modules using platform specific naming, like libMyLibrary.so, in first attempt. It will still fallback using no prefix and default suffix and then the verbatim name to make sure dllimports will still work as before, but favorizing modules using platform specific naming. On all other platforms mono_dl_build_path will continue to keep its current behaviour. --- src/mono/mono/metadata/components.c | 85 ++++++++---- src/mono/mono/metadata/native-library.c | 8 +- src/mono/mono/utils/mono-dl.c | 176 ++++++++++++++++++------ src/mono/mono/utils/mono-dl.h | 1 + 4 files changed, 192 insertions(+), 78 deletions(-) diff --git a/src/mono/mono/metadata/components.c b/src/mono/mono/metadata/components.c index d0a2c1257f2..242580434fd 100644 --- a/src/mono/mono/metadata/components.c +++ b/src/mono/mono/metadata/components.c @@ -16,17 +16,23 @@ #include "mono/utils/mono-logger-internals.h" #include "mono/utils/mono-path.h" #include "mono/utils/mono-time.h" +#include "mono/utils/refcount.h" static gint64 event_pipe_100ns_ticks; typedef MonoComponent * (*MonoComponentInitFn) (void); +typedef struct _MonoComponentLibrary { + MonoRefCount ref; + MonoDl *lib; +} MonoComponentLibrary; + typedef struct _MonoComponentEntry { const char *lib_name; const char *name; MonoComponentInitFn init; MonoComponent **component; - MonoDl *lib; + MonoComponentLibrary *lib; } MonoComponentEntry; #define COMPONENT_INIT_FUNC(name) (MonoComponentInitFn) mono_component_ ## name ## _init @@ -58,8 +64,10 @@ MonoComponentEntry components[] = { }; #ifndef STATIC_COMPONENTS +static GHashTable *component_library_load_history = NULL; + static MonoComponent* -get_component (const MonoComponentEntry *component, MonoDl **component_lib); +get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib); #endif void @@ -82,12 +90,14 @@ mono_components_init (void) for (int i = 0; i < G_N_ELEMENTS (components); ++i) *components [i].component = components [i].init (); #else + component_library_load_history = g_hash_table_new (g_str_hash, g_str_equal); + /* call get_component for each component and init it or its stubs and add it to loaded_components */ - MonoDl *lib = NULL; + MonoComponentLibrary *component_lib = NULL; for (int i = 0; i < G_N_ELEMENTS (components); ++i) { - *components [i].component = get_component (&components [i], &lib); - components [i].lib = lib; + *components [i].component = get_component (&components [i], &component_lib); + components [i].lib = component_lib; if (!*components [i].component) *components [i].component = components [i].init (); } @@ -106,11 +116,11 @@ component_init_name (const MonoComponentEntry *component) } static gpointer -load_component_entrypoint (MonoDl *lib, const MonoComponentEntry *component) +load_component_entrypoint (MonoComponentLibrary *component_lib, const MonoComponentEntry *component) { char *component_init = component_init_name (component); gpointer sym = NULL; - char *error_msg = mono_dl_symbol (lib, component_init, &sym); + char *error_msg = mono_dl_symbol (component_lib->lib, component_init, &sym); if (error_msg) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s library does not have symbol %s: %s", component->name, component_init, error_msg); g_free (error_msg); @@ -150,59 +160,76 @@ try_load (const char* dir, const MonoComponentEntry *component, const char* comp char *path = NULL; void *iter = NULL; - while ((path = mono_dl_build_path (dir, component_base_lib, &iter))) { + while (lib == NULL && (path = mono_dl_build_platform_path (dir, component_base_lib, &iter))) { char *error_msg = NULL; lib = mono_dl_open (path, MONO_DL_EAGER | MONO_DL_LOCAL, &error_msg); - if (lib) - break; if (!lib) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found: %s", component->name, error_msg); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s not found at %s: %s", component_base_lib, path, error_msg); + g_free (error_msg); + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s found at %s", component_base_lib, path); } - g_free (error_msg); g_free (path); } - if (lib) - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found at %s", component->name, path); - g_free (path); + return lib; } + + static MonoComponentInitFn -load_component (const MonoComponentEntry *component, MonoDl **lib_out) +load_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out) { // If init method has been static linked not using stub library, use that instead of dynamic component. if (component->init() && component->init()->available ()) { - *lib_out = NULL; + *component_lib_out = NULL; return component->init; } char *component_base_lib = component_library_base_name (component); MonoComponentInitFn result = NULL; + MonoComponentLibrary *component_lib = NULL; - /* FIXME: just copy what mono_profiler_load does, assuming it works */ + // Check history of component library loads to reduce try_load attempts (optimization for libraries hosting multiple components). + if (!g_hash_table_lookup_extended (component_library_load_history, component_base_lib, NULL, (gpointer *)&component_lib)) { + MonoDl *lib = NULL; +#if !defined(HOST_IOS) && !defined(HOST_TVOS) && !defined(HOST_WATCHOS) && !defined(HOST_MACCAT) && !defined(HOST_ANDROID) + lib = try_load (components_dir (), component, component_base_lib); +#endif + if (!lib) + lib = try_load (NULL, component, component_base_lib); - /* FIXME: do I need to provide a path? */ - MonoDl *lib = NULL; - lib = try_load (components_dir (), component, component_base_lib); - if (!lib) - lib = try_load (NULL, component, component_base_lib); + component_lib = g_new0 (MonoComponentLibrary, 1); + if (component_lib) { + mono_refcount_init (component_lib, NULL); + component_lib->lib = lib; + } - g_free (component_base_lib); - if (!lib) + g_hash_table_insert (component_library_load_history, g_strdup (component_base_lib), (gpointer)component_lib); + } + + if (!component_lib || !component_lib->lib) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found", component->name); goto done; + } - gpointer sym = load_component_entrypoint (lib, component); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found in %s", component->name, component_base_lib); + + gpointer sym = load_component_entrypoint (component_lib, component); result = (MonoComponentInitFn)sym; - *lib_out = lib; + + mono_refcount_inc (component_lib); + *component_lib_out = component_lib; done: + g_free (component_base_lib); return result; } MonoComponent* -get_component (const MonoComponentEntry *component, MonoDl **lib_out) +get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out) { - MonoComponentInitFn initfn = load_component (component, lib_out); + MonoComponentInitFn initfn = load_component (component, component_lib_out); if (!initfn) return NULL; return initfn(); diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 10808d2144b..6867a07379c 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -493,12 +493,11 @@ static MonoDl * netcore_probe_for_module_variations (const char *mdirname, const char *file_name, int raw_flags) { void *iter = NULL; - char *full_name; + char *full_name = NULL; MonoDl *module = NULL; - // FIXME: this appears to search *.dylib twice for some reason - while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) { - char *error_msg; + while (module == NULL && (full_name = mono_dl_build_path (mdirname, file_name, &iter))) { + char *error_msg = NULL; module = mono_dl_open_full (full_name, MONO_DL_LAZY, raw_flags, &error_msg); if (!module) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg); @@ -506,7 +505,6 @@ netcore_probe_for_module_variations (const char *mdirname, const char *file_name } g_free (full_name); } - g_free (full_name); return module; } diff --git a/src/mono/mono/utils/mono-dl.c b/src/mono/mono/utils/mono-dl.c index eec013399de..166cd5d602a 100644 --- a/src/mono/mono/utils/mono-dl.c +++ b/src/mono/mono/utils/mono-dl.c @@ -390,24 +390,78 @@ mono_dl_close (MonoDl *module) g_free (module); } -/** - * mono_dl_build_path: - * \param directory optional directory - * \param name base name of the library - * \param iter iterator token - * Given a directory name and the base name of a library, iterate - * over the possible file names of the library, taking into account - * the possible different suffixes and prefixes on the host platform. - * - * The returned file name must be freed by the caller. - * \p iter must point to a NULL pointer the first time the function is called - * and then passed unchanged to the following calls. - * \returns the filename or NULL at the end of the iteration - */ -char* -mono_dl_build_path (const char *directory, const char *name, void **iter) +typedef gboolean (*dl_library_name_formatting_func)(int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix); + +static +gboolean +dl_default_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ + /* + The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our + "bootstrap" phase in which we check the passed name verbatim and only if we fail to find + the dll thus named, we start appending suffixes, each time increasing idx twice (since now + the 0 value became special and we need to offset idx to a 0-based array index). This is + done to handle situations when mapped dll name is specified as libsomething.so.1 or + libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill + here. + */ + if (idx == 0) { + /* Name */ + *need_prefix = FALSE; + *need_suffix = FALSE; + *suffix = ""; + } else if (idx == 1) { + /* netcore system libs have a suffix but no prefix */ + *need_prefix = FALSE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [0]; + } else { + /* Prefix.Name.suffix */ + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [idx - 2]; + if ((*suffix) [0] == '\0') + return FALSE; + } + + return TRUE; +} + +static +gboolean +dl_reverse_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ + const char ** suffixes = mono_dl_get_so_suffixes (); + int suffix_count = 0; + + while (suffixes [suffix_count][0] != '\0') + suffix_count++; + + if (idx < suffix_count) { + /* Prefix.Name.suffix */ + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = suffixes [idx]; + } else if (idx == suffix_count) { + /* netcore system libs have a suffix but no prefix */ + *need_prefix = FALSE; + *need_suffix = TRUE; + *suffix = suffixes [0]; + } else if (idx == suffix_count + 1) { + /* Name */ + *need_prefix = FALSE; + *need_suffix = FALSE; + *suffix = ""; + } else { + return FALSE; + } + + return TRUE; +} + +static char* +dl_build_path (const char *directory, const char *name, void **iter, dl_library_name_formatting_func func) { - int idx; const char *prefix; const char *suffix; gboolean need_prefix = TRUE, need_suffix = TRUE; @@ -419,34 +473,10 @@ mono_dl_build_path (const char *directory, const char *name, void **iter) if (!iter) return NULL; - /* - The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our - "bootstrap" phase in which we check the passed name verbatim and only if we fail to find - the dll thus named, we start appending suffixes, each time increasing idx twice (since now - the 0 value became special and we need to offset idx to a 0-based array index). This is - done to handle situations when mapped dll name is specified as libsomething.so.1 or - libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill - here. - */ + iteration = GPOINTER_TO_UINT (*iter); - idx = iteration; - if (idx == 0) { - /* Name */ - need_prefix = FALSE; - need_suffix = FALSE; - suffix = ""; - } else if (idx == 1) { - /* netcore system libs have a suffix but no prefix */ - need_prefix = FALSE; - need_suffix = TRUE; - suffix = mono_dl_get_so_suffixes () [0]; - suffixlen = strlen (suffix); - } else { - /* Prefix.Name.suffix */ - suffix = mono_dl_get_so_suffixes () [idx - 2]; - if (suffix [0] == '\0') - return NULL; - } + if (!func (iteration, &need_prefix, &need_suffix, &suffix)) + return NULL; if (need_prefix) { prlen = strlen (mono_dl_get_so_prefix ()); @@ -471,6 +501,64 @@ mono_dl_build_path (const char *directory, const char *name, void **iter) return res; } +/** + * mono_dl_build_path: + * \param directory optional directory + * \param name base name of the library + * \param iter iterator token + * Given a directory name and the base name of a library, iterate + * over the possible file names of the library, taking into account + * the possible different suffixes and prefixes on the host platform. + * + * The returned file name must be freed by the caller. + * \p iter must point to a NULL pointer the first time the function is called + * and then passed unchanged to the following calls. + * \returns the filename or NULL at the end of the iteration + */ +char* +mono_dl_build_path (const char *directory, const char *name, void **iter) +{ +#ifdef HOST_ANDROID + return dl_build_path (directory, name, iter, dl_reverse_library_name_formatting); +#else + return dl_build_path (directory, name, iter, dl_default_library_name_formatting); +#endif +} + +static +gboolean +dl_prefix_suffix_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix) +{ + /* Prefix.Name.suffix */ + *need_prefix = TRUE; + *need_suffix = TRUE; + *suffix = mono_dl_get_so_suffixes () [idx]; + if ((*suffix) [0] == '\0') + return FALSE; + + return TRUE; +} + +/** + * mono_dl_build_platform_path: + * \param directory optional directory + * \param name base name of the library + * \param iter iterator token + * Given a directory name and the base name of a library, iterate + * over platform prefix and suffixes generating a library name + * suitable for the platform. Function won't try additional fallbacks. + * + * The returned file name must be freed by the caller. + * \p iter must point to a NULL pointer the first time the function is called + * and then passed unchanged to the following calls. + * \returns the filename or NULL at the end of the iteration + */ +char* +mono_dl_build_platform_path (const char *directory, const char *name, void **iter) +{ + return dl_build_path (directory, name, iter, dl_prefix_suffix_library_name_formatting); +} + MonoDlFallbackHandler * mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data) { diff --git a/src/mono/mono/utils/mono-dl.h b/src/mono/mono/utils/mono-dl.h index 44e7e9e34e1..5a810f8c295 100644 --- a/src/mono/mono/utils/mono-dl.h +++ b/src/mono/mono/utils/mono-dl.h @@ -36,6 +36,7 @@ MONO_EXTERN_C void mono_dl_close (MonoDl *module); char* mono_dl_build_path (const char *directory, const char *name, void **iter); +char* mono_dl_build_platform_path (const char *directory, const char *name, void **iter); MonoDl* mono_dl_open_runtime_lib (const char *lib_name, int flags, char **error_msg); From 86b7f92190e8a622f65ea61ad83c2aae45bd8879 Mon Sep 17 00:00:00 2001 From: Sung Yoon Whang Date: Tue, 6 Jul 2021 09:38:51 -0700 Subject: [PATCH 284/926] Add missing startup flags in runtime/start event (#55151) --- src/coreclr/vm/eventtrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 14bd22538d2..ac7be2a9439 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -5045,7 +5045,7 @@ VOID ETW::InfoLog::RuntimeInformation(INT32 type) { PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""); UINT8 startupMode = 0; - UINT startupFlags = 0; + UINT startupFlags = CorHost2::GetStartupFlags(); PathString dllPath; UINT8 Sku = ETW::InfoLog::InfoStructs::CoreCLR; From 515f7c104975fface92b7e155c92bb3b96f67b3b Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 6 Jul 2021 18:40:55 +0200 Subject: [PATCH 285/926] Replace Composition* pkgprojs with NuGet Pack task (#55208) System.Composition is a meta-package only so that required a bit try and error but that worked out quite well. The added dependency groups in that meta-package are intentional. Contributes to https://github.com/dotnet/runtime/issues/53202 --- .../Directory.Build.props | 11 -------- ...System.Composition.AttributedModel.pkgproj | 12 -------- .../src/CompatibilitySuppressions.xml | 12 ++++++++ .../System.Composition.AttributedModel.csproj | 12 ++++++++ .../Directory.Build.props | 13 --------- .../pkg/System.Composition.Convention.pkgproj | 12 -------- .../src/CompatibilitySuppressions.xml | 12 ++++++++ .../src/System.Composition.Convention.csproj | 14 ++++++++++ .../Directory.Build.props | 9 ------ .../pkg/System.Composition.Hosting.pkgproj | 12 -------- .../src/CompatibilitySuppressions.xml | 12 ++++++++ .../src/System.Composition.Hosting.csproj | 10 +++++++ .../Directory.Build.props | 9 ------ .../pkg/System.Composition.Runtime.pkgproj | 12 -------- .../src/CompatibilitySuppressions.xml | 12 ++++++++ .../src/System.Composition.Runtime.csproj | 10 +++++++ .../Directory.Build.props | 10 ------- .../pkg/System.Composition.TypedParts.pkgproj | 12 -------- .../src/CompatibilitySuppressions.xml | 12 ++++++++ .../src/System.Composition.TypedParts.csproj | 11 ++++++++ .../System.Composition/Directory.Build.props | 15 ---------- .../pkg/System.Composition.pkgproj | 11 -------- .../src/System.Composition.csproj | 28 +++++++++++++++++++ .../src/System.Composition.proj | 6 ---- .../Directory.Build.props | 4 --- ...stem.Diagnostics.PerformanceCounter.csproj | 4 +++ 26 files changed, 149 insertions(+), 148 deletions(-) delete mode 100644 src/libraries/System.Composition.AttributedModel/Directory.Build.props delete mode 100644 src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj create mode 100644 src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Composition.Convention/Directory.Build.props delete mode 100644 src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj create mode 100644 src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Composition.Hosting/Directory.Build.props delete mode 100644 src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj create mode 100644 src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Composition.Runtime/Directory.Build.props delete mode 100644 src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj create mode 100644 src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Composition.TypedParts/Directory.Build.props delete mode 100644 src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj create mode 100644 src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml delete mode 100644 src/libraries/System.Composition/Directory.Build.props delete mode 100644 src/libraries/System.Composition/pkg/System.Composition.pkgproj create mode 100644 src/libraries/System.Composition/src/System.Composition.csproj delete mode 100644 src/libraries/System.Composition/src/System.Composition.proj diff --git a/src/libraries/System.Composition.AttributedModel/Directory.Build.props b/src/libraries/System.Composition.AttributedModel/Directory.Build.props deleted file mode 100644 index 67ee2eb4760..00000000000 --- a/src/libraries/System.Composition.AttributedModel/Directory.Build.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - Provides common attributes used by System.Composition types. - -Commonly Used Types: -System.Composition.ExportAttribute -System.Composition.ImportAttribute -System.Composition.Convention.AttributedModelProvider - - \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj b/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj deleted file mode 100644 index 4c2c1ff8a36..00000000000 --- a/src/libraries/System.Composition.AttributedModel/pkg/System.Composition.AttributedModel.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..e80b49dae2d --- /dev/null +++ b/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj index 7d49a3802f9..70f72d925e7 100644 --- a/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj +++ b/src/libraries/System.Composition.AttributedModel/src/System.Composition.AttributedModel.csproj @@ -1,6 +1,14 @@ $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + Provides common attributes used by System.Composition types. + +Commonly Used Types: +System.Composition.ExportAttribute +System.Composition.ImportAttribute +System.Composition.Convention.AttributedModelProvider @@ -22,4 +30,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/Directory.Build.props b/src/libraries/System.Composition.Convention/Directory.Build.props deleted file mode 100644 index 8ea89c58349..00000000000 --- a/src/libraries/System.Composition.Convention/Directory.Build.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - Provides types that support using Managed Extensibility Framework with a convention-based configuration model. - -Commonly Used Types: -System.Composition.Convention.ConventionBuilder -System.Composition.Convention.ExportConventionBuilder -System.Composition.Convention.ImportConventionBuilder -System.Composition.Convention.PartConventionBuilder -System.Composition.Convention.ParameterImportConventionBuilder - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj b/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj deleted file mode 100644 index 957c0d82866..00000000000 --- a/src/libraries/System.Composition.Convention/pkg/System.Composition.Convention.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..e80b49dae2d --- /dev/null +++ b/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj index 500ab5bb2a2..eb348cc059b 100644 --- a/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj +++ b/src/libraries/System.Composition.Convention/src/System.Composition.Convention.csproj @@ -2,6 +2,16 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides types that support using Managed Extensibility Framework with a convention-based configuration model. + +Commonly Used Types: +System.Composition.Convention.ConventionBuilder +System.Composition.Convention.ExportConventionBuilder +System.Composition.Convention.ImportConventionBuilder +System.Composition.Convention.PartConventionBuilder +System.Composition.Convention.ParameterImportConventionBuilder @@ -34,4 +44,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/Directory.Build.props b/src/libraries/System.Composition.Hosting/Directory.Build.props deleted file mode 100644 index 80edbe7be3b..00000000000 --- a/src/libraries/System.Composition.Hosting/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - Provides Managed Extensibility Framework types that are useful to developers of extensible applications, or hosts. - -Commonly Used Types: -System.Composition.Hosting.CompositionHost - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj b/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj deleted file mode 100644 index d319cbd0c8e..00000000000 --- a/src/libraries/System.Composition.Hosting/pkg/System.Composition.Hosting.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..e80b49dae2d --- /dev/null +++ b/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj index 437ad767826..b15d5cb0895 100644 --- a/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj +++ b/src/libraries/System.Composition.Hosting/src/System.Composition.Hosting.csproj @@ -2,6 +2,12 @@ $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides Managed Extensibility Framework types that are useful to developers of extensible applications, or hosts. + +Commonly Used Types: +System.Composition.Hosting.CompositionHost @@ -49,4 +55,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/Directory.Build.props b/src/libraries/System.Composition.Runtime/Directory.Build.props deleted file mode 100644 index 8464a7c1394..00000000000 --- a/src/libraries/System.Composition.Runtime/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - Contains runtime components of the Managed Extensibility Framework. - -Commonly Used Types: -System.Composition.CompositionContext - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj b/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj deleted file mode 100644 index acdef61a398..00000000000 --- a/src/libraries/System.Composition.Runtime/pkg/System.Composition.Runtime.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..e80b49dae2d --- /dev/null +++ b/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj index b5ecf044a58..2dcd969991e 100644 --- a/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj +++ b/src/libraries/System.Composition.Runtime/src/System.Composition.Runtime.csproj @@ -2,6 +2,12 @@ System.Composition $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + Contains runtime components of the Managed Extensibility Framework. + +Commonly Used Types: +System.Composition.CompositionContext @@ -19,4 +25,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/Directory.Build.props b/src/libraries/System.Composition.TypedParts/Directory.Build.props deleted file mode 100644 index 4169d3592bf..00000000000 --- a/src/libraries/System.Composition.TypedParts/Directory.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - Provides some extension methods for the Managed Extensibility Framework. - -Commonly Used Types: -System.Composition.CompositionContextExtensions -System.Composition.Hosting.ContainerConfiguration - - \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj b/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj deleted file mode 100644 index e7ea55ed613..00000000000 --- a/src/libraries/System.Composition.TypedParts/pkg/System.Composition.TypedParts.pkgproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..e80b49dae2d --- /dev/null +++ b/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml @@ -0,0 +1,12 @@ + + + + + PKV006 + .NETStandard,Version=v1.0 + + + PKV006 + .NETPortable,Version=v0.0,Profile=Profile259 + + \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj index c8828d012e6..3843a7ecc59 100644 --- a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj +++ b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj @@ -3,6 +3,13 @@ System.Composition $(NetCoreAppCurrent);netstandard2.0;net461 false + true + Microsoft + Provides some extension methods for the Managed Extensibility Framework. + +Commonly Used Types: +System.Composition.CompositionContextExtensions +System.Composition.Hosting.ContainerConfiguration @@ -43,4 +50,8 @@ + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition/Directory.Build.props b/src/libraries/System.Composition/Directory.Build.props deleted file mode 100644 index be90cca2f6e..00000000000 --- a/src/libraries/System.Composition/Directory.Build.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - Microsoft - This packages provides a version of the Managed Extensibility Framework (MEF) that is lightweight and specifically optimized for high throughput scenarios, such as the web. - -Commonly Used Types: -System.Composition.ExportAttribute -System.Composition.ImportAttribute -System.Composition.Convention.ConventionBuilder -System.Composition.Hosting.CompositionHost -System.Composition.CompositionContext -System.Composition.CompositionContextExtensions - - diff --git a/src/libraries/System.Composition/pkg/System.Composition.pkgproj b/src/libraries/System.Composition/pkg/System.Composition.pkgproj deleted file mode 100644 index 3998ca2d922..00000000000 --- a/src/libraries/System.Composition/pkg/System.Composition.pkgproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Composition/src/System.Composition.csproj b/src/libraries/System.Composition/src/System.Composition.csproj new file mode 100644 index 00000000000..9314413cfd5 --- /dev/null +++ b/src/libraries/System.Composition/src/System.Composition.csproj @@ -0,0 +1,28 @@ + + + $(NetCoreAppCurrent);netstandard2.0;net461 + true + Microsoft + + $(NoWarn);NU5128 + + false + This packages provides a version of the Managed Extensibility Framework (MEF) that is lightweight and specifically optimized for high throughput scenarios, such as the web. + +Commonly Used Types: +System.Composition.ExportAttribute +System.Composition.ImportAttribute +System.Composition.Convention.ConventionBuilder +System.Composition.Hosting.CompositionHost +System.Composition.CompositionContext +System.Composition.CompositionContextExtensions + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Composition/src/System.Composition.proj b/src/libraries/System.Composition/src/System.Composition.proj deleted file mode 100644 index 8d90792dd6f..00000000000 --- a/src/libraries/System.Composition/src/System.Composition.proj +++ /dev/null @@ -1,6 +0,0 @@ - - - - netstandard2.0 - - \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props b/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props index a3b1dbe5348..e7e8cb9ac08 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props +++ b/src/libraries/System.Diagnostics.PerformanceCounter/Directory.Build.props @@ -3,9 +3,5 @@ Open windows - Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. - -Commonly Used Types: -System.Diagnostics.PerformanceCounter \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index 8f68e23f04d..51468f0d57d 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -4,6 +4,10 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461 $(NoWarn);CA1847 true + Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. + +Commonly Used Types: +System.Diagnostics.PerformanceCounter From 0265e79f314b2e298364c7b301d8f85479432b6f Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 6 Jul 2021 19:09:59 +0200 Subject: [PATCH 286/926] Increase EventPipe profiler sampling thread priority. (#55149) --- src/mono/mono/eventpipe/ep-rt-mono.c | 38 ++++++++++++++++++++++++++++ src/mono/mono/eventpipe/ep-rt-mono.h | 11 +++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 5ff2183936f..8694b23c438 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -440,6 +440,9 @@ ep_rt_mono_thread_get_or_create (void); void * ep_rt_mono_thread_attach (bool background_thread); +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); + void ep_rt_mono_thread_detach (void); @@ -1126,6 +1129,41 @@ ep_rt_mono_thread_attach (bool background_thread) return thread; } +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) +{ + void *result = ep_rt_mono_thread_attach (background_thread); + if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { + // Increase sampling thread priority, accepting failures. +#ifdef HOST_WIN32 + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); +#elif _POSIX_PRIORITY_SCHEDULING + int policy; + int priority; + struct sched_param param; + int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); + if (schedparam_result == 0) { + // Attempt to switch the thread to real time scheduling. This will not + // necessarily work on all OSs; for example, most Linux systems will give + // us EPERM here unless configured to allow this. + priority = param.sched_priority; + param.sched_priority = sched_get_priority_max (SCHED_RR); + if (param.sched_priority != -1) { + schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); + if (schedparam_result != 0) { + // Fallback, attempt to increase to max priority using current policy. + param.sched_priority = sched_get_priority_max (policy); + if (param.sched_priority != -1 && param.sched_priority != priority) + pthread_setschedparam (pthread_self (), policy, ¶m); + } + } + } +#endif + } + + return result; +} + void ep_rt_mono_thread_detach (void) { diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 22bdccdcd7d..2c3a5f7e36d 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -527,6 +527,15 @@ ep_rt_mono_thread_setup (bool background_thread) ep_rt_mono_thread_attach (background_thread); } +static +inline +void +ep_rt_mono_thread_setup_2 (bool background_thread, EventPipeThreadType thread_type) +{ + extern void * ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); + ep_rt_mono_thread_attach_2 (background_thread, thread_type); +} + static inline void @@ -1233,7 +1242,7 @@ EP_RT_DEFINE_THREAD_FUNC (ep_rt_thread_mono_start_func) { rt_mono_thread_params_internal_t *thread_params = (rt_mono_thread_params_internal_t *)data; - ep_rt_mono_thread_setup (thread_params->background_thread); + ep_rt_mono_thread_setup_2 (thread_params->background_thread, thread_params->thread_params.thread_type); thread_params->thread_params.thread = ep_rt_thread_get_handle (); mono_thread_start_return_t result = thread_params->thread_params.thread_func (thread_params); From a2665c73fa479fff09a29afb58186bedee7545d8 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 6 Jul 2021 19:12:11 +0200 Subject: [PATCH 287/926] Fix inconsistent linkage error in dynamic component build on Windows. (#55142) https://github.com/dotnet/runtime/pull/54887 added MONO_COMPONENT_API to mono_gc_memmove_atomic.h, but that function is declared in two different headers, memfuncs.h and gc-internal-agnostic.h and the second one didn't use MONO_COMPONENT_API causing inconsistent linking errors on Windows when building dynamic components. --- src/mono/mono/sgen/gc-internal-agnostic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/sgen/gc-internal-agnostic.h b/src/mono/mono/sgen/gc-internal-agnostic.h index 73d431d54d8..581cdb4896e 100644 --- a/src/mono/mono/sgen/gc-internal-agnostic.h +++ b/src/mono/mono/sgen/gc-internal-agnostic.h @@ -110,7 +110,7 @@ word aligned or size is not a multiple of word size. */ void mono_gc_bzero_atomic (void *dest, size_t size); void mono_gc_bzero_aligned (void *dest, size_t size); -void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); +MONO_COMPONENT_API void mono_gc_memmove_atomic (void *dest, const void *src, size_t size); void mono_gc_memmove_aligned (void *dest, const void *src, size_t size); FILE *mono_gc_get_logfile (void); From ca972ed72e3371115639730f4e89628617271922 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Tue, 6 Jul 2021 19:46:27 +0200 Subject: [PATCH 288/926] [Mono] Detail EventPipe sample profiler frames in unmanaged code. (#55139) * Detail sample profiler frames in unmanaged code. * Include CompileMehtod frame in callstack when JIT:ing code to identify frames marked as being in unmanaged code when JIT:ing. * Add Monitor.Enter into callstack when hitting monitor enter icall to identify frames marked as being in unmanaged code due to Monitor contention. * Include pinvoke frame in (JIT/AOT) to identify frames marked as being in unmanaged code while running a pinvoke. * Identify runtime invoke icall frame on top of callstack and classify as being in managed code. * Include pinvoke wrapper in rundown events. --- src/mono/mono/eventpipe/ep-rt-mono.c | 354 +++++++++++++++++---- src/mono/mono/metadata/loader.c | 1 + src/mono/mono/metadata/method-builder.h | 7 +- src/mono/mono/mini/mini-exceptions.c | 5 + src/mono/mono/mini/mini-runtime.c | 59 +++- src/mono/mono/mini/mini-runtime.h | 1 + src/mono/mono/utils/mono-stack-unwinding.h | 4 +- 7 files changed, 354 insertions(+), 77 deletions(-) diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 8694b23c438..9a83d80061b 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -52,6 +52,17 @@ static uint32_t _ep_rt_mono_max_sampled_thread_count = 32; // Mono profiler. static MonoProfilerHandle _ep_rt_mono_profiler = NULL; +// Phantom JIT compile method. +MonoMethod *_ep_rt_mono_runtime_helper_compile_method = NULL; +MonoJitInfo *_ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; + +// Monitor.Enter methods. +MonoMethod *_ep_rt_mono_monitor_enter_method = NULL; +MonoJitInfo *_ep_rt_mono_monitor_enter_method_jitinfo = NULL; + +MonoMethod *_ep_rt_mono_monitor_enter_v4_method = NULL; +MonoJitInfo *_ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; + // Rundown types. typedef bool @@ -109,14 +120,21 @@ typedef struct _EventPipeFireMethodEventsData{ ep_rt_mono_fire_method_rundown_events_func method_events_func; } EventPipeFireMethodEventsData; -typedef struct _EventPipeSampleProfileData { +typedef struct _EventPipeStackWalkData { + EventPipeStackContents *stack_contents; + bool top_frame; + bool async_frame; + bool safe_point_frame; + bool runtime_invoke_frame; +} EventPipeStackWalkData; + +typedef struct _EventPipeSampleProfileStackWalkData { + EventPipeStackWalkData stack_walk_data; EventPipeStackContents stack_contents; uint64_t thread_id; uintptr_t thread_ip; uint32_t payload_data; - bool async_frame; - bool safe_point_frame; -} EventPipeSampleProfileData; +} EventPipeSampleProfileStackWalkData; // Rundown flags. #define RUNTIME_SKU_CORECLR 0x2 @@ -258,6 +276,13 @@ eventpipe_execute_rundown ( ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func, ep_rt_mono_fire_method_rundown_events_func methods_events_func); +static +gboolean +eventpipe_walk_managed_stack_for_thread ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + EventPipeStackWalkData *stack_walk_data); + static gboolean eventpipe_walk_managed_stack_for_thread_func ( @@ -746,20 +771,22 @@ eventpipe_fire_method_events ( MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, events_data->domain) : NULL; if (debug_info) { offset_entries = debug_info->num_line_numbers; - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (!events_data->buffer || needed_size > events_data->buffer_size) { - g_free (events_data->buffer); - events_data->buffer_size = (size_t)(needed_size * 1.5); - events_data->buffer = g_new (uint8_t, events_data->buffer_size); - } + if (offset_entries != 0) { + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (!events_data->buffer || needed_size > events_data->buffer_size) { + g_free (events_data->buffer); + events_data->buffer_size = (size_t)(needed_size * 1.5); + events_data->buffer = g_new (uint8_t, events_data->buffer_size); + } - if (events_data->buffer) { - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; + if (events_data->buffer) { + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } } } @@ -797,6 +824,21 @@ eventpipe_fire_method_events ( g_free (method_signature); } +static +inline +bool +include_method (MonoMethod *method) +{ + if (!method) { + return false; + } else if (!m_method_is_wrapper (method)) { + return true; + } else { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; + } +} + static void eventpipe_fire_method_events_func ( @@ -808,7 +850,7 @@ eventpipe_fire_method_events_func ( if (ji && !ji->is_trampoline && !ji->async) { MonoMethod *method = jinfo_get_method (ji); - if (method && !m_method_is_wrapper (method)) + if (include_method (method)) eventpipe_fire_method_events (ji, method, events_data); } } @@ -902,15 +944,28 @@ eventpipe_execute_rundown ( if (root_domain) { uint64_t domain_id = (uint64_t)root_domain; - // Iterate all functions in use (JIT, AOT and Interpreter). + // Emit all functions in use (JIT, AOT and Interpreter). EventPipeFireMethodEventsData events_data; events_data.domain = root_domain; events_data.buffer_size = 1024 * sizeof(uint32_t); events_data.buffer = g_new (uint8_t, events_data.buffer_size); events_data.method_events_func = method_events_func; + + // All called JIT/AOT methods should be included in jit info table. mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); + + // All called interpreted methods should be included in interpreter jit info table. if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); + + // Phantom methods injected in callstacks representing runtime functions. + if (_ep_rt_mono_runtime_helper_compile_method_jitinfo && _ep_rt_mono_runtime_helper_compile_method) + eventpipe_fire_method_events (_ep_rt_mono_runtime_helper_compile_method_jitinfo, _ep_rt_mono_runtime_helper_compile_method, &events_data); + if (_ep_rt_mono_monitor_enter_method_jitinfo && _ep_rt_mono_monitor_enter_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_method_jitinfo, _ep_rt_mono_monitor_enter_method, &events_data); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo && _ep_rt_mono_monitor_enter_v4_method) + eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_v4_method_jitinfo, _ep_rt_mono_monitor_enter_v4_method, &events_data); + g_free (events_data.buffer); // Iterate all assemblies in domain. @@ -939,17 +994,78 @@ eventpipe_execute_rundown ( return TRUE; } +inline +static +bool +in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) + return true; + + return false; +} + +inline +static +bool +in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && + (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) + return true; + + return false; +} + +inline +static +bool +in_monitor_enter_v4_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) + return true; + + return false; +} + static gboolean eventpipe_walk_managed_stack_for_thread ( MonoStackFrameInfo *frame, MonoContext *ctx, - void *data, - bool *async_frame, - bool *safe_point_frame) + EventPipeStackWalkData *stack_walk_data) { EP_ASSERT (frame != NULL); - EP_ASSERT (data != NULL); + EP_ASSERT (stack_walk_data != NULL); switch (frame->type) { case FRAME_TYPE_DEBUGGER_INVOKE: @@ -958,23 +1074,41 @@ eventpipe_walk_managed_stack_for_thread ( case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_JIT_ENTRY: + // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. + // Makes it possible to detect stacks waiting on JIT compiler. + if (_ep_rt_mono_runtime_helper_compile_method && stack_walk_data->top_frame) + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_runtime_helper_compile_method), _ep_rt_mono_runtime_helper_compile_method); + stack_walk_data->top_frame = false; return FALSE; case FRAME_TYPE_MANAGED: case FRAME_TYPE_INTERP: - if (!frame->ji) - return FALSE; - *async_frame |= frame->ji->async; - MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; - if (method && m_method_is_wrapper (method)) { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info(method); - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll) - *safe_point_frame = true; - } else if (method && !m_method_is_wrapper (method)) { - ep_stack_contents_append ((EventPipeStackContents *)data, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { - ep_stack_contents_append ((EventPipeStackContents *)data, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + if (frame->ji) { + stack_walk_data->async_frame |= frame->ji->async; + MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; + if (method && m_method_is_wrapper (method)) { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->safe_point_frame = true; + }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->runtime_invoke_frame = true; + } else if (_ep_rt_mono_monitor_enter_method && in_monitor_enter_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_method), _ep_rt_mono_monitor_enter_method); + } else if (_ep_rt_mono_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_v4_method), _ep_rt_mono_monitor_enter_v4_method); + } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } + } else if (method && !m_method_is_wrapper (method)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + } } - return ep_stack_contents_get_length ((EventPipeStackContents *)data) >= EP_MAX_STACK_DEPTH; + stack_walk_data->top_frame = false; + return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; default: EP_UNREACHABLE ("eventpipe_walk_managed_stack_for_thread"); return FALSE; @@ -988,9 +1122,7 @@ eventpipe_walk_managed_stack_for_thread_func ( MonoContext *ctx, void *data) { - bool async_frame = false; - bool safe_point_frame = false; - return eventpipe_walk_managed_stack_for_thread (frame, ctx, data, &async_frame, &safe_point_frame); + return eventpipe_walk_managed_stack_for_thread (frame, ctx, (EventPipeStackWalkData *)data); } static @@ -1003,7 +1135,7 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( EP_ASSERT (frame != NULL); EP_ASSERT (data != NULL); - EventPipeSampleProfileData *sample_data = (EventPipeSampleProfileData *)data; + EventPipeSampleProfileStackWalkData *sample_data = (EventPipeSampleProfileStackWalkData *)data; if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { switch (frame->type) { @@ -1014,11 +1146,11 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( case FRAME_TYPE_TRAMPOLINE: sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; break; + case FRAME_TYPE_JIT_ENTRY: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; case FRAME_TYPE_INTERP: - if (frame->managed) - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - else - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; break; case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: @@ -1028,7 +1160,7 @@ eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( } } - return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_contents, &sample_data->async_frame, &sample_data->safe_point_frame); + return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); } static @@ -1054,6 +1186,65 @@ ep_rt_mono_init (void) _ep_rt_mono_profiler = mono_profiler_create (NULL); mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_eventpipe_thread_exited); + + MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); + if (method_signature) { + method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); + method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); + + ERROR_DECL (error); + MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); + if (is_ok (error) && runtime_helpers) { + MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); + if (method_builder) { + _ep_rt_mono_runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); + mono_mb_free (method_builder); + } + } + mono_error_cleanup (error); + mono_metadata_free_method_signature (method_signature); + + _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_runtime_helper_compile_method) { + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; + _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + } + } + + { + ERROR_DECL (error); + MonoMethodDesc *desc = NULL; + MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); + if (is_ok (error) && monitor) { + desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; + } + } + + desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); + if (desc) { + _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_method_jitinfo) { + _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); + _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; + } + } + } + mono_error_cleanup (error); + } } void @@ -1085,6 +1276,20 @@ ep_rt_mono_fini (void) if (_ep_rt_mono_initialized) mono_rand_close (_ep_rt_mono_rand_provider); + g_free (_ep_rt_mono_runtime_helper_compile_method_jitinfo); + _ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; + + mono_free_method (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_method_jitinfo); + _ep_rt_mono_monitor_enter_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_method = NULL; + + g_free (_ep_rt_mono_monitor_enter_v4_method_jitinfo); + _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; + _ep_rt_mono_monitor_enter_v4_method = NULL; + _ep_rt_mono_sampled_thread_callstacks = NULL; _ep_rt_mono_rand_provider = NULL; _ep_rt_mono_initialized = FALSE; @@ -1484,10 +1689,17 @@ ep_rt_mono_walk_managed_stack_for_thread ( { EP_ASSERT (thread != NULL && stack_contents != NULL); + EventPipeStackWalkData stack_walk_data; + stack_walk_data.stack_contents = stack_contents; + stack_walk_data.top_frame = true; + stack_walk_data.async_frame = false; + stack_walk_data.safe_point_frame = false; + stack_walk_data.runtime_invoke_frame = false; + if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); return true; } @@ -1541,7 +1753,7 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( // Sample profiler only runs on one thread, no need to synchorinize. if (!_ep_rt_mono_sampled_thread_callstacks) - _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileData), _ep_rt_mono_max_sampled_thread_count); + _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileStackWalkData), _ep_rt_mono_max_sampled_thread_count); // Make sure there is room based on previous max number of sampled threads. // NOTE, there is a chance there are more threads than max, if that's the case we will @@ -1562,16 +1774,22 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); if (thread_state->valid) { if (sampled_thread_count < _ep_rt_mono_max_sampled_thread_count) { - EventPipeSampleProfileData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileData, sampled_thread_count); + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, sampled_thread_count); data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; - data->async_frame = FALSE; - data->safe_point_frame = FALSE; + data->stack_walk_data.stack_contents = &data->stack_contents; + data->stack_walk_data.top_frame = true; + data->stack_walk_data.async_frame = false; + data->stack_walk_data.safe_point_frame = false; + data->stack_walk_data.runtime_invoke_frame = false; ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); - if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && data->safe_point_frame) + mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { + // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame + // as second, re-classify current callstack to be executing managed code. data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } sampled_thread_count++; } @@ -1588,12 +1806,12 @@ ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( // adapter instance and only set recorded tid as parameter inside adapter. THREAD_INFO_TYPE adapter = { { 0 } }; for (uint32_t i = 0; i < sampled_thread_count; ++i) { - EventPipeSampleProfileData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileData, i); + EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, i); if (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length(&data->stack_contents) > 0) { // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event // for this specific stackwalk or we could cleanup stack_frames before writing profile event. - if (data->async_frame) { + if (data->stack_walk_data.async_frame) { for (int i = 0; i < data->stack_contents.next_available_frame; ++i) mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [i], TRUE, FALSE); } @@ -1758,19 +1976,21 @@ ep_rt_mono_write_event_method_il_to_native_map ( MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; if (debug_info) { - offset_entries = debug_info->num_line_numbers; - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (needed_size > sizeof (fixed_buffer)) { - buffer = g_new (uint8_t, needed_size); - il_offsets = (uint32_t*)buffer; - } else { - il_offsets = fixed_buffer; - } - if (il_offsets) { - native_offsets = il_offsets + offset_entries; - for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { - il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; - native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + if (offset_entries != 0) { + offset_entries = debug_info->num_line_numbers; + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (needed_size > sizeof (fixed_buffer)) { + buffer = g_new (uint8_t, needed_size); + il_offsets = (uint32_t*)buffer; + } else { + il_offsets = fixed_buffer; + } + if (il_offsets) { + native_offsets = il_offsets + offset_entries; + for (int offset_count = 0; offset_count < offset_entries; ++offset_count) { + il_offsets [offset_count] = debug_info->line_numbers [offset_count].il_offset; + native_offsets [offset_count] = debug_info->line_numbers [offset_count].native_offset; + } } } diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index f16c8c36f81..fc64623fad6 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -1657,6 +1657,7 @@ stack_walk_adapter (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: return FALSE; case FRAME_TYPE_MANAGED: case FRAME_TYPE_INTERP: diff --git a/src/mono/mono/metadata/method-builder.h b/src/mono/mono/metadata/method-builder.h index d746fd3806c..fd3903c8664 100644 --- a/src/mono/mono/metadata/method-builder.h +++ b/src/mono/mono/metadata/method-builder.h @@ -18,6 +18,7 @@ #include #include #include +#include typedef struct _MonoMethodBuilder MonoMethodBuilder; @@ -30,16 +31,16 @@ typedef struct { MonoMethod* (*create_method) (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack); } MonoMethodBuilderCallbacks; -MonoMethodBuilder * +MONO_COMPONENT_API MonoMethodBuilder * mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type); MonoMethodBuilder * mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type); -void +MONO_COMPONENT_API void mono_mb_free (MonoMethodBuilder *mb); -MonoMethod * +MONO_COMPONENT_API MonoMethod * mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack); guint32 diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index d6df5b15012..eea17726ba2 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -450,6 +450,8 @@ arch_unwind_frame (MonoJitTlsData *jit_tls, frame->type = FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX; memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); } + } else if (ext->kind == MONO_LMFEXT_JIT_ENTRY) { + frame->type = FRAME_TYPE_JIT_ENTRY; } else { g_assert_not_reached (); } @@ -1873,6 +1875,7 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: continue; case FRAME_TYPE_INTERP: case FRAME_TYPE_MANAGED: @@ -2278,6 +2281,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt case FRAME_TYPE_TRAMPOLINE: case FRAME_TYPE_INTERP_TO_MANAGED: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + case FRAME_TYPE_JIT_ENTRY: *ctx = new_ctx; continue; case FRAME_TYPE_INTERP_ENTRY: @@ -2722,6 +2726,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu case FRAME_TYPE_TRAMPOLINE: case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: case FRAME_TYPE_INTERP_ENTRY: + case FRAME_TYPE_JIT_ENTRY: *ctx = new_ctx; continue; case FRAME_TYPE_INTERP_TO_MANAGED: diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 87f2070d389..7ba23bd560f 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -87,6 +87,7 @@ #include "mini-gc.h" #include "mini-llvm.h" +#include "llvm-runtime.h" #include "lldb.h" #include "mini-runtime.h" #include "interp/interp.h" @@ -2664,13 +2665,54 @@ lookup_start: return p; } +typedef struct { + MonoMethod *method; + guint32 opt; + gboolean jit_only; + MonoError *error; + gpointer code; +} JitCompileMethodWithOptCallbackData; + +static void +jit_compile_method_with_opt_cb (gpointer arg) +{ + JitCompileMethodWithOptCallbackData *params = (JitCompileMethodWithOptCallbackData *)arg; + params->code = mono_jit_compile_method_with_opt (params->method, params->opt, params->jit_only, params->error); +} + +static gpointer +jit_compile_method_with_opt (JitCompileMethodWithOptCallbackData *params) +{ + MonoLMFExt ext; + + memset (&ext, 0, sizeof (MonoLMFExt)); + ext.kind = MONO_LMFEXT_JIT_ENTRY; + mono_push_lmf (&ext); + + gboolean thrown = FALSE; +#if defined(ENABLE_LLVM_RUNTIME) || defined(ENABLE_LLVM) + mono_llvm_cpp_catch_exception (jit_compile_method_with_opt_cb, params, &thrown); +#else + jit_compile_method_with_opt_cb (params); +#endif + + mono_pop_lmf (&ext.lmf); + + return !thrown ? params->code : NULL; +} + gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error) { - gpointer code; + JitCompileMethodWithOptCallbackData params; - code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), FALSE, error); - return code; + params.method = method; + params.opt = mono_get_optimizations_for_method (method, default_opt); + params.jit_only = FALSE; + params.error = error; + params.code = NULL; + + return jit_compile_method_with_opt (¶ms); } /* @@ -2681,10 +2723,15 @@ mono_jit_compile_method (MonoMethod *method, MonoError *error) gpointer mono_jit_compile_method_jit_only (MonoMethod *method, MonoError *error) { - gpointer code; + JitCompileMethodWithOptCallbackData params; - code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), TRUE, error); - return code; + params.method = method; + params.opt = mono_get_optimizations_for_method (method, default_opt); + params.jit_only = TRUE; + params.error = error; + params.code = NULL; + + return jit_compile_method_with_opt (¶ms); } /* diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index 57fb1b29824..2e349a35ed0 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -197,6 +197,7 @@ struct MonoJitTlsData { #define MONO_LMFEXT_DEBUGGER_INVOKE 1 #define MONO_LMFEXT_INTERP_EXIT 2 #define MONO_LMFEXT_INTERP_EXIT_WITH_CTX 3 +#define MONO_LMFEXT_JIT_ENTRY 4 /* * The MonoLMF structure is arch specific, it includes at least these fields. diff --git a/src/mono/mono/utils/mono-stack-unwinding.h b/src/mono/mono/utils/mono-stack-unwinding.h index 15335fc9619..f27b2cbb00a 100644 --- a/src/mono/mono/utils/mono-stack-unwinding.h +++ b/src/mono/mono/utils/mono-stack-unwinding.h @@ -30,7 +30,9 @@ typedef enum { FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX = 6, /* Frame for transitioning to interpreted code */ FRAME_TYPE_INTERP_ENTRY = 7, - FRAME_TYPE_NUM = 8 + /* Frame marking transition to native JIT compiler */ + FRAME_TYPE_JIT_ENTRY = 8, + FRAME_TYPE_NUM = 9 } MonoStackFrameType; typedef enum { From be5a9507cd1b1a4cad383533d640046618b7e968 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 6 Jul 2021 14:30:01 -0400 Subject: [PATCH 289/926] Update new `LoggerMessage.Define` APIs, to take `LogDefineOptions` (#54581) * Change preview overloads, take LogDefineOptions Related to #50913 * Apply PR feedback * Fix compile issue --- .../gen/LoggerMessageGenerator.Emitter.cs | 2 +- ...crosoft.Extensions.Logging.Abstractions.cs | 19 ++++--- .../src/LogDefineOptions.cs | 18 ++++++ .../src/LoggerMessage.cs | 56 +++++++++---------- .../TestWithNestedClass.generated.txt | 2 +- .../TestWithSkipEnabledCheck.generated.txt | 2 +- .../Baselines/TestWithTwoParams.generated.txt | 2 +- .../tests/Common/LoggerMessageTest.cs | 14 ++--- 8 files changed, 69 insertions(+), 46 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs index 908efaf6afe..16c16f482aa 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs @@ -370,7 +370,7 @@ namespace {lc.Namespace} GenDefineTypes(lm, brackets: true); - _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", true); + _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", new global::Microsoft.Extensions.Logging.LogDefineOptions() {{ SkipEnabledCheck = true }}); "); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index ef9d46564ec..1404880a972 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -48,6 +48,11 @@ namespace Microsoft.Extensions.Logging { void SetScopeProvider(Microsoft.Extensions.Logging.IExternalScopeProvider scopeProvider); } + public partial class LogDefineOptions + { + public LogDefineOptions() { } + public bool SkipEnabledCheck { get { throw null; } set { } } + } public static partial class LoggerExtensions { public static System.IDisposable BeginScope(this Microsoft.Extensions.Logging.ILogger logger, string messageFormat, params object?[] args) { throw null; } @@ -94,7 +99,7 @@ namespace Microsoft.Extensions.Logging public static partial class LoggerMessage { public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } @@ -103,17 +108,17 @@ namespace Microsoft.Extensions.Logging public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } - public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, Microsoft.Extensions.Logging.LogDefineOptions? options) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public sealed partial class LoggerMessageAttribute : System.Attribute diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs new file mode 100644 index 00000000000..48016fcd2b7 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogDefineOptions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Extensions.Logging +{ + /// + /// Options for and its overloads + /// + public class LogDefineOptions + { + /// + /// Gets or sets the flag to skip IsEnabled check for the logging method. + /// + public bool SkipEnabledCheck { get; set; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs index ee40fc116bc..db1ffdfd1d8 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs @@ -128,7 +128,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -136,9 +136,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 0); @@ -147,7 +147,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -170,7 +170,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -179,9 +179,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 1); @@ -190,7 +190,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -214,7 +214,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -224,9 +224,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 2); @@ -235,7 +235,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -260,7 +260,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -271,9 +271,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 3); @@ -282,7 +282,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -308,7 +308,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -320,9 +320,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 4); @@ -331,7 +331,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -358,7 +358,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -371,9 +371,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 5); @@ -382,7 +382,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } @@ -410,7 +410,7 @@ namespace Microsoft.Extensions.Logging /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) - => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + => Define(logLevel, eventId, formatString, options: null); /// /// Creates a delegate which can be invoked for logging a message. @@ -424,9 +424,9 @@ namespace Microsoft.Extensions.Logging /// The /// The event id /// The named format string - /// Skips the check if the logging category is enabled. + /// The /// A delegate which when invoked creates a log message. - public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, LogDefineOptions? options) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 6); @@ -435,7 +435,7 @@ namespace Microsoft.Extensions.Logging logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); } - if (skipEnabledCheck) + if (options != null && options.SkipEnabledCheck) { return Log; } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt index 024e0464c71..e10a678aef2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithNestedClass.generated.txt @@ -17,7 +17,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses.NestedNamesp { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action __M9Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), "M9", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), "M9", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M9(global::Microsoft.Extensions.Logging.ILogger logger) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt index c4b242a0a9e..6576bb4b394 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithSkipEnabledCheck.generated.txt @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "Message: When using SkipEnabledCheck, the generated code skips logger.IsEnabled(logLevel) check before calling log. To be used when consumer has already guarded logger method in an IsEnabled check.", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt index ae5b89b74a4..5daee3ddd43 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithTwoParams.generated.txt @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action, global::System.Exception?> __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {a1} {a2}", true); + global::Microsoft.Extensions.Logging.LoggerMessage.Define>(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {a1} {a2}", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 a1, global::System.Collections.Generic.IEnumerable a2) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs index 6422a7a188b..a47ccc154fc 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs @@ -430,13 +430,13 @@ namespace Microsoft.Extensions.Logging.Test public static IEnumerable LogMessagesDataSkipEnabledCheck => new[] { - new object[] { LoggerMessage.Define(LogLevel.Error, 0, "Log ", skipEnabledCheck: true), 0 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 1, "Log {P0}", skipEnabledCheck: true), 1 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 2, "Log {P0} {P1}", skipEnabledCheck: true), 2 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 3, "Log {P0} {P1} {P2}", skipEnabledCheck: true), 3 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 4, "Log {P0} {P1} {P2} {P3}", skipEnabledCheck: true), 4 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 5, "Log {P0} {P1} {P2} {P3} {P4}", skipEnabledCheck: true), 5 }, - new object[] { LoggerMessage.Define(LogLevel.Error, 6, "Log {P0} {P1} {P2} {P3} {P4} {P5}", skipEnabledCheck: true), 6 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 0, "Log ", options: new LogDefineOptions() { SkipEnabledCheck = true }), 0 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 1, "Log {P0}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 1 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 2, "Log {P0} {P1}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 2 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 3, "Log {P0} {P1} {P2}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 3 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 4, "Log {P0} {P1} {P2} {P3}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 4 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 5, "Log {P0} {P1} {P2} {P3} {P4}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 5 }, + new object[] { LoggerMessage.Define(LogLevel.Error, 6, "Log {P0} {P1} {P2} {P3} {P4} {P5}", options: new LogDefineOptions() { SkipEnabledCheck = true }), 6 }, }; private delegate Delegate Define(LogLevel logLevel, EventId eventId, string formatString); From aeb467ee7ae1472845a39662d99d70d40b1d6f17 Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Tue, 6 Jul 2021 11:32:31 -0700 Subject: [PATCH 290/926] compile composite with avx2 on x64 (#55057) * compile composite with avx2 on x64 * fix xml coding style --- src/coreclr/tools/aot/crossgen2/Properties/Resources.resx | 2 +- .../Microsoft.NETCore.App.Runtime.Composite.sfxproj | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index c031f303e44..56fbb93adb9 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -169,7 +169,7 @@ Instruction set(s) to use for compilation - Instruction set must not be '{0}' for this architecture and operating system + Instruction set '{0}' is not valid for this architecture and operating system Instruction set(s) specified without also specifying input-bubble diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj index 0cd26443fc5..a3f364815d6 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Composite.sfxproj @@ -32,6 +32,9 @@ + + + From 1f547ac5529211e6de21572bc6f5fb930a5456e6 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:36:22 -0400 Subject: [PATCH 291/926] Fix jit dump enable flag (#55209) --- src/mono/CMakeLists.txt | 4 ++++ src/mono/cmake/config.h.in | 3 +++ src/mono/cmake/configure.cmake | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 038be0cbcf7..8f73b2a7dda 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -726,6 +726,10 @@ else() # FIXME: set(NAME_DEV_RANDOM "\"/dev/random\"") endif() + +if(HOST_LINUX AND HAVE_SYS_MMAN_H AND HAVE_ELF_H AND HAVE_SYS_SYSCALL_H) + set(ENABLE_JIT_DUMP 1) +endif() ### End of other checks ###################################### diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 4a76a42e146..d684efa2b2e 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -1073,6 +1073,9 @@ /* Enable static linking of mono runtime components */ #cmakedefine STATIC_COMPONENTS +/* Enable perf jit dump support */ +#cmakedefine ENABLE_JIT_DUMP 1 + #if defined(ENABLE_LLVM) && defined(HOST_WIN32) && defined(TARGET_WIN32) && (!defined(TARGET_AMD64) || !defined(_MSC_VER)) #error LLVM for host=Windows and target=Windows is only supported on x64 MSVC build. #endif diff --git a/src/mono/cmake/configure.cmake b/src/mono/cmake/configure.cmake index 7e259bff586..34fa15f556c 100644 --- a/src/mono/cmake/configure.cmake +++ b/src/mono/cmake/configure.cmake @@ -57,8 +57,8 @@ endfunction() ac_check_headers ( sys/types.h sys/stat.h sys/filio.h sys/sockio.h sys/utime.h sys/un.h sys/syscall.h sys/uio.h sys/param.h sys/sysctl.h sys/prctl.h sys/socket.h sys/utsname.h sys/select.h sys/user.h sys/poll.h sys/wait.h sts/auxv.h sys/resource.h - sys/ioctl.h sys/errno.h sys/sendfile.h sys/statvfs.h sys/statfs.h sys/mman.h sys/mount.h sys/time.h sys/random.h - strings.h stdint.h unistd.h netdb.h utime.h semaphore.h libproc.h alloca.h ucontext.h pwd.h + sys/ioctl.h sys/errno.h sys/sendfile.h sys/statvfs.h sys/statfs.h sys/mman.h sys/mount.h sys/time.h sys/random.h sys/mman.h + strings.h stdint.h unistd.h netdb.h utime.h semaphore.h libproc.h alloca.h ucontext.h pwd.h elf.h gnu/lib-names.h netinet/tcp.h netinet/in.h link.h arpa/inet.h unwind.h poll.h wchar.h linux/magic.h android/legacy_signal_inlines.h android/ndk-version.h execinfo.h pthread.h pthread_np.h net/if.h dirent.h CommonCrypto/CommonDigest.h dlfcn.h getopt.h pwd.h iconv.h alloca.h From 331078a8cc98a24ea8bb6a669cedb2bb1b68f1f7 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 6 Jul 2021 13:55:19 -0500 Subject: [PATCH 292/926] Use the emsdk workload manifest for the emscripten packages (#55110) --- eng/Version.Details.xml | 2 +- eng/Versions.props | 4 +-- .../WorkloadManifest.json.in | 34 +++---------------- .../WorkloadManifest.targets | 3 -- 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 00d781b1461..8301d951525 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,7 +8,7 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk f5349765b7af1970c5b25cce4ed278544907cbe0 diff --git a/eng/Versions.props b/eng/Versions.props index cdb313d0a06..5240e72cbb2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -180,7 +180,7 @@ 11.1.0-alpha.1.21328.1 11.1.0-alpha.1.21328.1 - 6.0.0-preview.7.21330.1 - $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) + 6.0.0-preview.7.21330.1 + $(MicrosoftNETWorkloadEmscriptenManifest60100) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index 4c50a08f007..059efeed70c 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -1,5 +1,8 @@ { "version": "${WorkloadVersion}", + "depends-on": { + "Microsoft.NET.Workload.Emscripten": "${EmscriptenVersion}" + }, "workloads": { "microsoft-net-sdk-blazorwebassembly-aot": { "description": "Browser Runtime native performance tools", @@ -11,7 +14,7 @@ "Microsoft.NET.Runtime.Emscripten.Python", "Microsoft.NET.Runtime.Emscripten.Sdk" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "linux-x64", "osx-x64", "osx-arm64" ] }, "microsoft-net-runtime-android": { @@ -266,34 +269,5 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NET.Runtime.Emscripten.Node" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Python" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Sdk" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64" - } - } } } \ No newline at end of file diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index ab039e9a716..4176041469b 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -44,9 +44,6 @@ - - - From 656775f584f8b4dc291d4050d57fbb8466bc15a4 Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Tue, 6 Jul 2021 12:00:30 -0700 Subject: [PATCH 293/926] Add llvm ios (#55222) * add LLVM scenario * add copy to payload * reverse paths * fix log path * Move log fix * remove extra scenario --- eng/pipelines/coreclr/perf.yml | 16 +++++++++++++ .../templates/build-perf-sample-apps.yml | 23 +++++++++++++++++++ eng/pipelines/coreclr/templates/perf-job.yml | 8 ++++++- .../coreclr/templates/run-performance-job.yml | 3 +-- .../coreclr/templates/run-scenarios-job.yml | 2 +- eng/testing/performance/performance-setup.ps1 | 7 +++++- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 0db890a8e57..6fc0b85de73 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -237,6 +237,22 @@ jobs: logicalmachine: 'perfpixel4a' iosLlvmBuild: False + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Windows_x64 + jobParameters: + testGroup: perf + runtimeType: iOSMono + projectFile: ios_scenarios.proj + runKind: ios_scenarios + runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml + logicalmachine: 'perfpixel4a' + iosLlvmBuild: True + # run mono microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml index 6bfd8637bfa..65b6226093a 100644 --- a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -44,6 +44,29 @@ steps: archiveExtension: '.tar.gz' archiveType: tar tarCompression: gz + - script: rm -r -f $(Build.SourcesDirectory)/src/mono/sample/iOS/bin + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS + displayName: Clean bindir + - script: make build-appbundle TARGET=iOS MONO_ARCH=arm64 MONO_CONFIG=Release AOT=True USE_LLVM=True + env: + DevTeamProvisioning: '-' + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS + displayName: Build HelloiOS AOT sample app LLVM=True + - task: PublishBuildArtifacts@1 + condition: succeededOrFailed() + displayName: 'Publish binlog' + inputs: + pathtoPublish: $(Build.SourcesDirectory)/src/mono/sample/iOS/msbuild.binlog + artifactName: ${{ parameters.artifactName }} + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app + includeRootFolder: true + displayName: iOS Sample App LLVM + artifactName: iOSSampleAppLLVM + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz - template: /eng/pipelines/common/upload-artifact-step.yml parameters: diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 3dee80bb370..59b7843925e 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -165,7 +165,13 @@ jobs: artifactFileName: 'iOSSampleAppNoLLVM.tar.gz' artifactName: 'iOSSampleAppNoLLVM' displayName: 'iOS Sample App NoLLVM' - + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory)/iosHelloWorld/llvm + cleanUnpackFolder: false + artifactFileName: 'iOSSampleAppLLVM.tar.gz' + artifactName: 'iOSSampleAppLLVM' + displayName: 'iOS Sample App LLVM' # Create Core_Root - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(buildConfig) $(archType) generatelayoutonly $(librariesOverrideArg) diff --git a/eng/pipelines/coreclr/templates/run-performance-job.yml b/eng/pipelines/coreclr/templates/run-performance-job.yml index e031d0fa1ce..de523bb2d99 100644 --- a/eng/pipelines/coreclr/templates/run-performance-job.yml +++ b/eng/pipelines/coreclr/templates/run-performance-job.yml @@ -23,7 +23,6 @@ parameters: runKind: '' # required -- test category logicalMachine: '' # required -- Used to specify a which pool of machines the test should run against javascriptEngine: 'NoJS' - iosLlvmBuild: 'False' jobs: - template: xplat-pipeline-job.yml @@ -143,6 +142,6 @@ jobs: displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}_${{ parameters.iosLlvmBuild }}' + artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_${{ parameters.logicalMachine }}_${{ parameters.javascriptEngine }}_${{ parameters.pgoRunType }}' continueOnError: true condition: always() diff --git a/eng/pipelines/coreclr/templates/run-scenarios-job.yml b/eng/pipelines/coreclr/templates/run-scenarios-job.yml index 075807453ec..2fa6822205a 100644 --- a/eng/pipelines/coreclr/templates/run-scenarios-job.yml +++ b/eng/pipelines/coreclr/templates/run-scenarios-job.yml @@ -157,6 +157,6 @@ jobs: displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}' + artifactName: 'Performance_Run_$(osGroup)$(osSubgroup)_$(archType)_$(buildConfig)_${{ parameters.runtimeType }}_${{ parameters.codeGenType }}_${{ parameters.runKind }}_$(iOSLlvmBuild)' continueOnError: true condition: always() diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index 03aff42d25f..078cad080a4 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -152,7 +152,12 @@ if ($iOSMono) { { mkdir $WorkItemDirectory } - Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + if($iOSLlvmBuild) { + Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse + } else { + Copy-Item -path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + } + $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' } From 017e16c7eadf27ed7e6a6f5c77f202d7977ab66c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 6 Jul 2021 13:17:14 -0600 Subject: [PATCH 294/926] Resolve ILLink warnings in System.Memory.Data (#54980) The current APIs require unreferenced code, since they call into JsonSerializer without providing JsonTypeInfo. #54979 is logged to add "trim compatible" APIs here. --- .../ref/System.Memory.Data.cs | 7 +++- .../ref/System.Memory.Data.csproj | 3 ++ .../src/ILLink/ILLink.Suppressions.xml | 41 ------------------- .../src/System.Memory.Data.csproj | 4 ++ .../src/System/BinaryData.cs | 9 ++-- 5 files changed, 17 insertions(+), 47 deletions(-) delete mode 100644 src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs index 302a4da145f..165004afbf2 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs @@ -9,14 +9,16 @@ namespace System public partial class BinaryData { public BinaryData(byte[] data) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public BinaryData(object? jsonSerializable, System.Text.Json.JsonSerializerOptions? options = null, System.Type? type = null) { } public BinaryData(System.ReadOnlyMemory data) { } public BinaryData(string data) { } - public static BinaryData Empty { get; } + public static System.BinaryData Empty { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public static System.BinaryData FromBytes(byte[] data) { throw null; } public static System.BinaryData FromBytes(System.ReadOnlyMemory data) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public static System.BinaryData FromObjectAsJson(T jsonSerializable, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static System.BinaryData FromStream(System.IO.Stream stream) { throw null; } public static System.Threading.Tasks.Task FromStreamAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -27,6 +29,7 @@ namespace System public static implicit operator System.ReadOnlySpan (System.BinaryData? data) { throw null; } public byte[] ToArray() { throw null; } public System.ReadOnlyMemory ToMemory() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed.")] public T? ToObjectFromJson(System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public System.IO.Stream ToStream() { throw null; } public override string ToString() { throw null; } diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj index 30f156a04fc..01e210e939f 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.csproj @@ -6,6 +6,9 @@ + + + diff --git a/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index f3e2744fd58..00000000000 --- a/src/libraries/System.Memory.Data/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.BinaryData.#ctor(System.Object,System.Text.Json.JsonSerializerOptions,System.Type) - - - ILLink - IL2026 - member - M:System.BinaryData.FromObjectAsJson``1(``0,System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2026 - member - M:System.BinaryData.ToObjectFromJson``1(System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2067 - member - M:System.BinaryData.#ctor(System.Object,System.Text.Json.JsonSerializerOptions,System.Type) - - - ILLink - IL2087 - member - M:System.BinaryData.FromObjectAsJson``1(``0,System.Text.Json.JsonSerializerOptions) - - - ILLink - IL2087 - member - M:System.BinaryData.ToObjectFromJson``1(System.Text.Json.JsonSerializerOptions) - - - \ No newline at end of file diff --git a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj index b7b49395879..1505c60f012 100644 --- a/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj +++ b/src/libraries/System.Memory.Data/src/System.Memory.Data.csproj @@ -13,6 +13,10 @@ Link="Common\System\Threading\Tasks\TaskToApm.cs" /> + + + + diff --git a/src/libraries/System.Memory.Data/src/System/BinaryData.cs b/src/libraries/System.Memory.Data/src/System/BinaryData.cs index b0e56d1e7ba..2e5dfbfacfb 100644 --- a/src/libraries/System.Memory.Data/src/System/BinaryData.cs +++ b/src/libraries/System.Memory.Data/src/System/BinaryData.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; @@ -17,6 +16,8 @@ namespace System /// public class BinaryData { + private const string JsonSerializerRequiresUnreferencedCode = "JSON serialization and deserialization might require types that cannot be statically analyzed."; + /// /// The backing store for the instance. /// @@ -41,12 +42,12 @@ namespace System /// Creates a instance by serializing the provided object to JSON /// using . /// - /// /// The object that will be serialized to JSON using /// . /// The options to use when serializing to JSON. /// The type to use when serializing the data. If not specified, will /// be used to determine the type. + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public BinaryData(object? jsonSerializable, JsonSerializerOptions? options = default, Type? type = default) { type ??= jsonSerializable?.GetType() ?? typeof(object); @@ -177,12 +178,11 @@ namespace System /// Creates a instance by serializing the provided object using /// the . /// - /// /// The type to use when serializing the data. /// The data to use. /// The options to use when serializing to JSON. - /// /// A value representing the UTF-8 encoding of the JSON representation of . + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public static BinaryData FromObjectAsJson(T jsonSerializable, JsonSerializerOptions? options = default) { byte[] buffer = JsonSerializer.SerializeToUtf8Bytes(jsonSerializable, typeof(T), options); @@ -230,6 +230,7 @@ namespace System /// converted to. /// The to use when serializing to JSON. /// The data converted to the specified type. + [RequiresUnreferencedCode(JsonSerializerRequiresUnreferencedCode)] public T? ToObjectFromJson(JsonSerializerOptions? options = default) { return JsonSerializer.Deserialize(_bytes.Span, options); From 890fde7c5a57ca3f367131f173773c410e4fc26f Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 6 Jul 2021 13:19:56 -0600 Subject: [PATCH 295/926] Finish migrating System.Drawing.Common to ComWrappers (#54884) * Finish migrating System.Drawing.Common to ComWrappers This resolves all ILLink warnings in System.Drawing.Common, making it work in a trimmed application. --- .../Ole32/Interop.IStream.COMWrappers.cs | 58 +++++++++++++ ...am.cs => Interop.IStream.NoCOMWrappers.cs} | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 59 ------------- .../src/System.Drawing.Common.csproj | 12 ++- .../src/System/Drawing/Bitmap.Windows.cs | 11 ++- ...mWrappers.cs => DrawingCom.COMWrappers.cs} | 45 ++++++---- .../Drawing/DrawingCom.NoCOMWrappers.cs | 16 ++++ .../src/System/Drawing/DrawingCom.cs | 25 ++++++ .../System/Drawing/GdiplusNative.Windows.cs | 20 ++--- .../Drawing/Icon.Windows.COMWrappers.cs | 15 +--- .../src/System/Drawing/Image.Windows.cs | 45 ++++++---- .../Drawing/Imaging/Metafile.Windows.cs | 51 +++++++----- .../Drawing/Internal/GPStream.COMWrappers.cs | 82 +++++++++++++++++++ .../Internal/GPStream.NoCOMWrappers.cs | 56 +++++++++++++ .../src/System/Drawing/Internal/GPStream.cs | 49 +---------- 15 files changed, 353 insertions(+), 193 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs rename src/libraries/Common/src/Interop/Windows/Ole32/{Interop.IStream.cs => Interop.IStream.NoCOMWrappers.cs} (97%) delete mode 100644 src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml rename src/libraries/System.Drawing.Common/src/System/Drawing/{DrawingComWrappers.cs => DrawingCom.COMWrappers.cs} (89%) create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs new file mode 100644 index 00000000000..a3ff4e14fb7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// IStream interface. + /// + /// + /// This interface explicitly doesn't use the built-in COM support, but instead is only used with ComWrappers. + /// + internal interface IStream + { + // pcbRead is optional + unsafe void Read(byte* pv, uint cb, uint* pcbRead); + + // pcbWritten is optional + unsafe void Write(byte* pv, uint cb, uint* pcbWritten); + + // SeekOrgin matches the native values, plibNewPosition is optional + unsafe void Seek(long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition); + + void SetSize(ulong libNewSize); + + // pcbRead and pcbWritten are optional + unsafe HRESULT CopyTo( + IntPtr pstm, + ulong cb, + ulong* pcbRead, + ulong* pcbWritten); + + void Commit(uint grfCommitFlags); + + void Revert(); + + HRESULT LockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + HRESULT UnlockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + unsafe void Stat( + STATSTG* pstatstg, + STATFLAG grfStatFlag); + + unsafe HRESULT Clone(IntPtr* ppstm); + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs similarity index 97% rename from src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs rename to src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs index 6fdf6718f05..b8907e5a33a 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Ole32 { /// - /// COM IStream interface. + /// COM IStream interface. /// /// /// The definition in does not lend diff --git a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index a0b7401fb0a..00000000000 --- a/src/libraries/System.Drawing.Common/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - ILLink - IL2050 - member - M:System.Drawing.Bitmap.#ctor(System.IO.Stream,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.FromStream(System.IO.Stream,System.Boolean,System.Boolean) - - - ILLink - IL2050 - member - M:System.Drawing.Image.InitializeFromStream(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Image.Save(System.IO.Stream,System.Drawing.Imaging.ImageCodecInfo,System.Drawing.Imaging.EncoderParameters) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.Rectangle,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream,System.IntPtr,System.Drawing.RectangleF,System.Drawing.Imaging.MetafileFrameUnit,System.Drawing.Imaging.EmfType,System.String) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.#ctor(System.IO.Stream) - - - ILLink - IL2050 - member - M:System.Drawing.Imaging.Metafile.GetMetafileHeader(System.IO.Stream) - - - diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index b02d55a46c6..ff707877c2d 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -192,6 +192,7 @@ + @@ -294,8 +295,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.GlobalLock.cs" /> - + + - + + + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs index 4fe1b54ae1b..84f959f94c2 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs @@ -9,25 +9,24 @@ namespace System.Drawing { public sealed partial class Bitmap { - public Bitmap(Stream stream, bool useIcm) + public unsafe Bitmap(Stream stream, bool useIcm) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - IntPtr bitmap = IntPtr.Zero; - int status; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr bitmap = IntPtr.Zero; if (useIcm) { - status = Gdip.GdipCreateBitmapFromStreamICM(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStreamICM(streamWrapper.Ptr, &bitmap)); } else { - status = Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out bitmap); + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStream(streamWrapper.Ptr, &bitmap)); } - Gdip.CheckStatus(status); ValidateImage(bitmap); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs similarity index 89% rename from src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs rename to src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs index 236ae1be2aa..5889a4ac704 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingComWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.COMWrappers.cs @@ -14,16 +14,16 @@ namespace System.Drawing /// /// Supports IStream and IPicture COM interfaces. /// - internal unsafe class DrawingComWrappers : ComWrappers + internal unsafe partial class DrawingCom : ComWrappers { private const int S_OK = (int)Interop.HRESULT.S_OK; private static readonly Guid IID_IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry(); - internal static DrawingComWrappers Instance { get; } = new DrawingComWrappers(); + internal static DrawingCom Instance { get; } = new DrawingCom(); - private DrawingComWrappers() { } + private DrawingCom() { } private static ComInterfaceEntry* InitializeComInterfaceEntry() { @@ -31,7 +31,7 @@ namespace System.Drawing IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInterface, fpAddRef, fpRelease); - ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingComWrappers), sizeof(ComInterfaceEntry)); + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingCom), sizeof(ComInterfaceEntry)); wrapperEntry->IID = IID_IStream; wrapperEntry->Vtable = iStreamVtbl; return wrapperEntry; @@ -66,6 +66,27 @@ namespace System.Drawing throw new NotImplementedException(); } + internal static IStreamWrapper GetComWrapper(Interop.Ole32.IStream stream) + { + IntPtr streamWrapperPtr = Instance.GetOrCreateComInterfaceForObject(stream, CreateComInterfaceFlags.None); + + Guid streamIID = IID_IStream; + int result = Marshal.QueryInterface(streamWrapperPtr, ref streamIID, out IntPtr streamPtr); + + Marshal.Release(streamWrapperPtr); + + ThrowExceptionForHR(result); + + return new IStreamWrapper(streamPtr); + } + + internal static void ThrowExceptionForHR(int errorCode) + { + // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only + // throw the Exception corresponding to the specified errorCode. + Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); + } + internal static class IStreamVtbl { public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease) @@ -159,16 +180,13 @@ namespace System.Drawing try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - Interop.Ole32.IStream pstmStream = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)pstm); - inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten); + return (int)inst.CopyTo(pstm, cb, pcbRead, pcbWritten); } catch (Exception e) { return e.HResult; } - - return S_OK; } [UnmanagedCallersOnly] @@ -250,23 +268,16 @@ namespace System.Drawing [UnmanagedCallersOnly] private static int Clone(IntPtr thisPtr, IntPtr* ppstm) { - if (ppstm == null) - { - return (int)Interop.HRESULT.STG_E_INVALIDPOINTER; - } - try { Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); - *ppstm = Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None); + return (int)inst.Clone(ppstm); } catch (Exception e) { return e.HResult; } - - return S_OK; } } @@ -297,7 +308,7 @@ namespace System.Drawing { // Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation Guid streamIID = IID_IStream; - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); + ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); try { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs new file mode 100644 index 00000000000..d3fdd2115bf --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.NoCOMWrappers.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Internal; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal static partial class DrawingCom + { + internal static IStreamWrapper GetComWrapper(GPStream stream) + { + return new IStreamWrapper(Marshal.GetComInterfaceForObject(stream)); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs new file mode 100644 index 00000000000..a5b53e65ff5 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/DrawingCom.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal partial class DrawingCom + { + internal readonly struct IStreamWrapper : IDisposable + { + public readonly IntPtr Ptr; + + public IStreamWrapper(IntPtr ptr) + { + Ptr = ptr; + } + + public void Dispose() + { + Marshal.Release(Ptr); + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index 47aeb4f9397..3edf4a0e7b6 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -210,10 +210,10 @@ namespace System.Drawing internal static extern int GdipDeleteBrush(HandleRef brush); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStream(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStreamICM(Interop.Ole32.IStream stream, out IntPtr image); + internal static extern int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipCloneImage(HandleRef image, out IntPtr cloneimage); @@ -222,7 +222,7 @@ namespace System.Drawing internal static extern int GdipSaveImageToFile(HandleRef image, string filename, ref Guid classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSaveImageToStream(HandleRef image, Interop.Ole32.IStream stream, ref Guid classId, HandleRef encoderParams); + internal static extern int GdipSaveImageToStream(HandleRef image, IntPtr stream, Guid* classId, HandleRef encoderParams); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipSaveAdd(HandleRef image, HandleRef encoderParams); @@ -327,7 +327,7 @@ namespace System.Drawing internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromStream(Interop.Ole32.IStream stream, IntPtr header); + internal static extern int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetMetafileHeaderFromMetafile(HandleRef metafile, IntPtr header); @@ -336,16 +336,16 @@ namespace System.Drawing internal static extern int GdipGetHemfFromMetafile(HandleRef metafile, out IntPtr hEnhMetafile); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromStream(Interop.Ole32.IStream stream, out IntPtr metafile); + internal static extern int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStreamI(Interop.Ole32.IStream stream, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + internal static extern int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipComment(HandleRef graphics, int sizeData, byte[] data); @@ -354,10 +354,10 @@ namespace System.Drawing internal static extern int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStream(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStreamICM(Interop.Ole32.IStream stream, out IntPtr bitmap); + internal static extern int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs index a03ad5ed133..3d9ad1a3566 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -28,7 +28,7 @@ namespace System.Drawing // But, in the interest of simplicity, we just call to // OLE to do it for us. PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); - Guid iid = DrawingComWrappers.IPicture.IID; + Guid iid = DrawingCom.IPicture.IID; IntPtr lpPicture; Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); @@ -36,13 +36,13 @@ namespace System.Drawing try { // Use UniqueInstance here because we never want to cache the wrapper. It only gets used once and then disposed. - using DrawingComWrappers.IPicture picture = (DrawingComWrappers.IPicture)DrawingComWrappers.Instance + using DrawingCom.IPicture picture = (DrawingCom.IPicture)DrawingCom.Instance .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); var gpStream = new GPStream(outputStream, makeSeekable: false); - streamPtr = DrawingComWrappers.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + streamPtr = DrawingCom.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); - CheckSaveAsFileResult(picture.SaveAsFile(streamPtr, -1, null)); + DrawingCom.ThrowExceptionForHR(picture.SaveAsFile(streamPtr, -1, null)); } finally { @@ -61,13 +61,6 @@ namespace System.Drawing } } - private static void CheckSaveAsFileResult(int errorCode) - { - // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only - // throw the Exception corresponding to the specified errorCode. - Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); - } - [DllImport(Interop.Libraries.Oleaut32)] private static unsafe extern int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index a6dd53744a9..ccea5ff42d5 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -23,16 +23,7 @@ namespace System.Drawing if (stream == null) throw new ArgumentNullException(nameof(stream)); - IntPtr image = IntPtr.Zero; - - if (useEmbeddedColorManagement) - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(new GPStream(stream), out image)); - } - else - { - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); - } + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement); if (validateImageData) ValidateImage(image); @@ -45,9 +36,7 @@ namespace System.Drawing // Used for serialization private IntPtr InitializeFromStream(Stream stream) { - IntPtr image = IntPtr.Zero; - - Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(new GPStream(stream), out image)); + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement: false); ValidateImage(image); nativeImage = image; @@ -59,6 +48,22 @@ namespace System.Drawing return image; } + private static unsafe IntPtr LoadGdipImageFromStream(GPStream stream, bool useEmbeddedColorManagement) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(stream); + + IntPtr image = IntPtr.Zero; + if (useEmbeddedColorManagement) + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(streamWrapper.Ptr, &image)); + } + else + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(streamWrapper.Ptr, &image)); + } + return image; + } + internal Image(IntPtr nativeImage) => SetNativeImage(nativeImage); /// @@ -240,11 +245,15 @@ namespace System.Drawing if (!saved) { - Gdip.CheckStatus(Gdip.GdipSaveImageToStream( - new HandleRef(this, nativeImage), - new GPStream(stream, makeSeekable: false), - ref g, - new HandleRef(encoderParams, encoderParamsMemory))); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream, makeSeekable: false)); + unsafe + { + Gdip.CheckStatus(Gdip.GdipSaveImageToStream( + new HandleRef(this, nativeImage), + streamWrapper.Ptr, + &g, + new HandleRef(encoderParams, encoderParamsMemory))); + } } } finally diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs index 447799333fe..4d8dccb01e7 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Windows.cs @@ -1,11 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.InteropServices; -using System.IO; using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; using Gdip = System.Drawing.SafeNativeMethods.Gdip; -using System.Runtime.Serialization; namespace System.Drawing.Imaging { @@ -27,14 +26,18 @@ namespace System.Drawing.Imaging /// /// Initializes a new instance of the class from the specified stream. /// - public Metafile(Stream stream) + public unsafe Metafile(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } - Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(new GPStream(stream), out IntPtr metafile)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(streamWrapper.Ptr, &metafile)); + SetNativeImage(metafile); } @@ -145,16 +148,19 @@ namespace System.Drawing.Imaging /// /// Initializes a new instance of the class from the specified data stream. /// - public Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, MetafileFrameUnit.GdiCompatible, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -162,16 +168,19 @@ namespace System.Drawing.Imaging /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out IntPtr metafile)); + &metafile)); SetNativeImage(metafile); } @@ -179,31 +188,32 @@ namespace System.Drawing.Imaging /// /// Initializes a new instance of the class with the specified filename. /// - public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + public unsafe Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) { - IntPtr metafile = IntPtr.Zero; + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + IntPtr metafile = IntPtr.Zero; if (frameRect.IsEmpty) { Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, IntPtr.Zero, frameUnit, description, - out metafile)); + &metafile)); } else { Gdip.CheckStatus(Gdip.GdipRecordMetafileStreamI( - new GPStream(stream), + streamWrapper.Ptr, referenceHdc, type, - ref frameRect, + &frameRect, frameUnit, description, - out metafile)); + &metafile)); } SetNativeImage(metafile); @@ -292,7 +302,8 @@ namespace System.Drawing.Imaging try { - Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(new GPStream(stream), memory)); + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(streamWrapper.Ptr, memory)); int[] type = new int[] { 0 }; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs new file mode 100644 index 00000000000..65079d5698a --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.COMWrappers.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public unsafe Interop.HRESULT Clone(IntPtr* ppstm) + { + if (ppstm == null) + { + return Interop.HRESULT.STG_E_INVALIDPOINTER; + } + + // The cloned object should have the same current "position" + var clone = new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + + *ppstm = DrawingCom.Instance.GetOrCreateComInterfaceForObject(clone, CreateComInterfaceFlags.None); + + return Interop.HRESULT.S_OK; + } + + public unsafe Interop.HRESULT CopyTo(IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + Interop.HRESULT hr = (Interop.HRESULT)WriteToStream(pstm, b, read, &written); + if (hr != Interop.HRESULT.S_OK) + { + return hr; + } + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + { + *pcbRead = totalRead; + } + + if (pcbWritten != null) + { + *pcbWritten = totalWritten; + } + + return Interop.HRESULT.S_OK; + } + + private static unsafe int WriteToStream(IntPtr pstm, byte* pv, uint cb, uint* pcbWritten) + { + return ((delegate* unmanaged)(*(*(void***)pstm + 4 /* IStream.Write slot */))) + (pstm, pv, cb, pcbWritten); + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs new file mode 100644 index 00000000000..0c4e56ef8d6 --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.NoCOMWrappers.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public Interop.Ole32.IStream Clone() + { + // The cloned object should have the same current "position" + return new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + } + + public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + pstm.Write(b, read, &written); + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + *pcbRead = totalRead; + + if (pcbWritten != null) + *pcbWritten = totalWritten; + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs index dd6a8a16350..524be173b52 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs @@ -1,12 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.IO; namespace System.Drawing.Internal { - internal sealed class GPStream : Interop.Ole32.IStream + internal sealed partial class GPStream : Interop.Ole32.IStream { private readonly Stream _dataStream; @@ -41,15 +40,6 @@ namespace System.Drawing.Internal _virtualPosition = -1; } - public Interop.Ole32.IStream Clone() - { - // The cloned object should have the same current "position" - return new GPStream(_dataStream) - { - _virtualPosition = _virtualPosition - }; - } - public void Commit(uint grfCommitFlags) { _dataStream.Flush(); @@ -58,43 +48,6 @@ namespace System.Drawing.Internal ActualizeVirtualPosition(); } - public unsafe void CopyTo(Interop.Ole32.IStream pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) - { - byte[] buffer = ArrayPool.Shared.Rent(4096); - - ulong remaining = cb; - ulong totalWritten = 0; - ulong totalRead = 0; - - fixed (byte* b = buffer) - { - while (remaining > 0) - { - uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; - Read(b, read, &read); - remaining -= read; - totalRead += read; - - if (read == 0) - { - break; - } - - uint written; - pstm.Write(b, read, &written); - totalWritten += written; - } - } - - ArrayPool.Shared.Return(buffer); - - if (pcbRead != null) - *pcbRead = totalRead; - - if (pcbWritten != null) - *pcbWritten = totalWritten; - } - public unsafe void Read(byte* pv, uint cb, uint* pcbRead) { ActualizeVirtualPosition(); From 971d204a54e3e3b57b0640fe34fad30185701d0d Mon Sep 17 00:00:00 2001 From: naminodarie <32071278+naminodarie@users.noreply.github.com> Date: Wed, 7 Jul 2021 04:58:00 +0900 Subject: [PATCH 296/926] Avoid conflicts between default [semi-]voiced sound mark rules and custom rules (#55116) --- .../pal_collation.c | 37 ++++++-- .../CompareInfo/CompareInfoTests.Compare.cs | 90 +++++++++++++++++++ 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c index 7c02c4ede71..4a8d5bcd721 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c @@ -112,6 +112,17 @@ static const UChar g_HalfFullHigherChars[] = { }; static const int32_t g_HalfFullCharsLength = (sizeof(g_HalfFullHigherChars) / sizeof(UChar)); +// Hiragana without [semi-]voiced sound mark for custom collation rules +// If Hiragana with [semi-]voiced sound mark is added to custom collation rules, there is a conflict +// between the custom rule and some default rule. +static const UChar g_HiraganaWithoutVoicedSoundMarkChars[] = { + 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, + 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, 0x3061, 0x3063, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, + 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, + 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093, 0x3095, 0x3096, 0x309D, +}; +static const int32_t g_HiraganaWithoutVoicedSoundMarkCharsLength = (sizeof(g_HiraganaWithoutVoicedSoundMarkChars) / sizeof(UChar)); + /* ICU collation rules reserve any punctuation and whitespace characters for use in the syntax. Thus, to use these characters in a rule, they need to be escaped. @@ -150,22 +161,36 @@ custom rules in order to support IgnoreKanaType and IgnoreWidth CompareOptions c */ static void FillIgnoreKanaRules(UChar* completeRules, int32_t* fillIndex, int32_t completeRulesLength, int32_t isIgnoreKanaType) { - UChar compareChar = isIgnoreKanaType ? '=' : '<'; - assert((*fillIndex) + (4 * (hiraganaEnd - hiraganaStart + 1)) <= completeRulesLength); if ((*fillIndex) + (4 * (hiraganaEnd - hiraganaStart + 1)) > completeRulesLength) // check the allocated the size { return; } - for (UChar hiraganaChar = hiraganaStart; hiraganaChar <= hiraganaEnd; hiraganaChar++) + if (isIgnoreKanaType) { - // Hiragana is the range 3041 to 3096 & 309D & 309E - if (hiraganaChar <= 0x3096 || hiraganaChar >= 0x309D) // characters between 3096 and 309D are not mapped to katakana + for (UChar hiraganaChar = hiraganaStart; hiraganaChar <= hiraganaEnd; hiraganaChar++) { + // Hiragana is the range 3041 to 3096 & 309D & 309E + if (hiraganaChar <= 0x3096 || hiraganaChar >= 0x309D) // characters between 3096 and 309D are not mapped to katakana + { + completeRules[*fillIndex] = '&'; + completeRules[(*fillIndex) + 1] = hiraganaChar; + completeRules[(*fillIndex) + 2] = '='; + completeRules[(*fillIndex) + 3] = hiraganaChar + hiraganaToKatakanaOffset; + (*fillIndex) += 4; + } + } + } + else + { + // Avoid conflicts between default [semi-]voiced sound mark rules and custom rules + for (int i = 0; i < g_HiraganaWithoutVoicedSoundMarkCharsLength; i++) + { + UChar hiraganaChar = g_HiraganaWithoutVoicedSoundMarkChars[i]; completeRules[*fillIndex] = '&'; completeRules[(*fillIndex) + 1] = hiraganaChar; - completeRules[(*fillIndex) + 2] = compareChar; + completeRules[(*fillIndex) + 2] = '<'; completeRules[(*fillIndex) + 3] = hiraganaChar + hiraganaToKatakanaOffset; (*fillIndex) += 4; } diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs index 26207c1ccf7..ca3800ccfa5 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs @@ -240,6 +240,20 @@ namespace System.Globalization.Tests yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreCase, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "c", "C", CompareOptions.IgnoreKanaType, -1 }; + // Japanese [semi-]voiced sound mark + yield return new object[] { s_invariantCompare, "\u306F", "\u3070", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3071", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u3070", "\u3071", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D0", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D1", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u30D0", "\u30D1", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3070", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u306F", "\u3071", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u3070", "\u3071", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D0", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30CF", "\u30D1", CompareOptions.IgnoreNonSpace, 0 }; + yield return new object[] { s_invariantCompare, "\u30D0", "\u30D1", CompareOptions.IgnoreNonSpace, 0 }; + // Spanish yield return new object[] { new CultureInfo("es-ES").CompareInfo, "llegar", "lugar", CompareOptions.None, -1 }; @@ -485,5 +499,81 @@ namespace System.Globalization.Tests $"Expect '{(int)hiraganaChar:x4}' == {(int)hiraganaChar + hiraganaToKatakanaOffset:x4} with CompareOptions.IgnoreKanaType"); } } + + [Fact] + public void TestHiraganaAndKatakana() + { + const char hiraganaStart = '\u3041'; + const char hiraganaEnd = '\u3096'; + const int hiraganaToKatakanaOffset = 0x30a1 - 0x3041; + List hiraganaList = new List(); + for (char c = hiraganaStart; c <= hiraganaEnd; c++) // Hiragana + { + // TODO: small hiragana/katakana orders + // https://github.com/dotnet/runtime/issues/54987 + switch (c) + { + case '\u3041': + case '\u3043': + case '\u3045': + case '\u3047': + case '\u3049': + case '\u3063': + case '\u3083': + case '\u3085': + case '\u3087': + case '\u308E': + case '\u3095': + case '\u3096': + break; + default: + hiraganaList.Add(c); + break; + } + } + for (char c = '\u309D'; c <= '\u309E'; c++) // Hiragana iteration mark + { + hiraganaList.Add(c); + } + CompareOptions[] options = new[] { + CompareOptions.None, + CompareOptions.IgnoreCase, + CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreSymbols, + CompareOptions.IgnoreKanaType, + CompareOptions.IgnoreWidth, + CompareOptions.Ordinal, + CompareOptions.OrdinalIgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace, + CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, + }; + + foreach (var option in options) + { + for (int i = 0; i < hiraganaList.Count; i++) + { + char hiraganaChar1 = hiraganaList[i]; + char katakanaChar1 = (char)(hiraganaChar1 + hiraganaToKatakanaOffset); + + for (int j = i; j < hiraganaList.Count; j++) + { + char hiraganaChar2 = hiraganaList[j]; + char katakanaChar2 = (char)(hiraganaChar2 + hiraganaToKatakanaOffset); + + int hiraganaResult = s_invariantCompare.Compare(new string(hiraganaChar1, 1), new string(hiraganaChar2, 1), option); + int katakanaResult = s_invariantCompare.Compare(new string(katakanaChar1, 1), new string(katakanaChar2, 1), option); + Assert.True(hiraganaResult == katakanaResult, + $"Expect Compare({(int)hiraganaChar1:x4}, {(int)hiraganaChar2:x4}) == Compare({(int)katakanaChar1:x4}, {(int)katakanaChar2:x4}) with CompareOptions.{option}"); + } + } + } + } } } From ae5ee8f02d6fc99469e1f194be45b5f649c2da1a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 6 Jul 2021 16:42:17 -0400 Subject: [PATCH 297/926] Enable ToBase64Transform.CanTransformMultipleBlocks (#55055) * Enable ToBase64Transform.CanTransformMultipleBlocks * Address PR feedback --- .../Security/Cryptography/Base64Transforms.cs | 60 +++++------- .../tests/Base64TransformsTests.cs | 98 ++++++++++++++----- 2 files changed, 99 insertions(+), 59 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs index 0dc17c595d8..3dda623337f 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs @@ -23,30 +23,36 @@ namespace System.Security.Cryptography // converting to Base64 takes 3 bytes input and generates 4 bytes output public int InputBlockSize => 3; public int OutputBlockSize => 4; - public bool CanTransformMultipleBlocks => false; + public bool CanTransformMultipleBlocks => true; public virtual bool CanReuseTransform => true; public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - // inputCount < InputBlockSize is not allowed - ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount, InputBlockSize); + ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); + + int inputBlocks = Math.DivRem(inputCount, InputBlockSize, out int inputRemainder); + + if (inputBlocks == 0) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); if (outputBuffer == null) ThrowHelper.ThrowArgumentNull(ThrowHelper.ExceptionArgument.outputBuffer); - // For now, only convert 3 bytes to 4 - Span input = inputBuffer.AsSpan(inputOffset, InputBlockSize); - Span output = outputBuffer.AsSpan(outputOffset, OutputBlockSize); + if (inputRemainder != 0) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); + + int requiredOutputLength = checked(inputBlocks * OutputBlockSize); + if (requiredOutputLength > outputBuffer.Length - outputOffset) + ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.outputBuffer); + + Span input = inputBuffer.AsSpan(inputOffset, inputCount); + Span output = outputBuffer.AsSpan(outputOffset, requiredOutputLength); OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: false); - if (written != OutputBlockSize) - { - ThrowHelper.ThrowCryptographicException(); - } - Debug.Assert(status == OperationStatus.Done); - Debug.Assert(consumed == InputBlockSize); + Debug.Assert(consumed == input.Length); + Debug.Assert(written == output.Length); return written; } @@ -56,28 +62,20 @@ namespace System.Security.Cryptography // inputCount <= InputBlockSize is allowed ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - // Convert.ToBase64CharArray already does padding, so all we have to check is that - // the inputCount wasn't 0 + // Convert.ToBase64CharArray already does padding, so all we have to check is that the inputCount wasn't 0 if (inputCount == 0) - { return Array.Empty(); - } - else if (inputCount > InputBlockSize) - { - ThrowHelper.ThrowArgumentOutOfRange(ThrowHelper.ExceptionArgument.inputCount); - } - // Again, for now only a block at a time Span input = inputBuffer.AsSpan(inputOffset, inputCount); - byte[] output = new byte[OutputBlockSize]; + + int inputBlocks = Math.DivRem(inputCount, InputBlockSize, out int inputRemainder); + int outputBlocks = inputBlocks + (inputRemainder != 0 ? 1 : 0); + + byte[] output = new byte[outputBlocks * OutputBlockSize]; OperationStatus status = Base64.EncodeToUtf8(input, output, out int consumed, out int written, isFinalBlock: true); - if (written != OutputBlockSize) - { - ThrowHelper.ThrowCryptographicException(); - } - + Debug.Assert(written == output.Length); Debug.Assert(status == OperationStatus.Done); Debug.Assert(consumed == inputCount); @@ -390,14 +388,6 @@ namespace System.Security.Cryptography ThrowInvalidOffLen(); } - public static void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, int inputBlockSize) - { - ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - - if (inputCount < inputBlockSize) - ThrowArgumentOutOfRange(ExceptionArgument.inputCount); - } - [DoesNotReturn] public static void ThrowArgumentNull(ExceptionArgument argument) => throw new ArgumentNullException(argument.ToString()); [DoesNotReturn] diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs index aab283c32c5..1a6cc5ed182 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/tests/Base64TransformsTests.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics; using System.IO; +using System.Linq; using Xunit; namespace System.Security.Cryptography.Encoding.Tests @@ -56,34 +56,29 @@ namespace System.Security.Cryptography.Encoding.Tests [Fact] public void InvalidInput_ToBase64Transform() { - byte[] data_5bytes = Text.Encoding.ASCII.GetBytes("aaaaa"); + byte[] data_3bytes = Text.Encoding.ASCII.GetBytes("aaa"); + ICryptoTransform transform = new ToBase64Transform(); - using (var transform = new ToBase64Transform()) - { - InvalidInput_Base64Transform(transform); + AssertExtensions.Throws("inputBuffer", () => transform.TransformBlock(null, 0, 0, null, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformBlock(Array.Empty(), -1, 0, null, 0)); + AssertExtensions.Throws("outputBuffer", () => transform.TransformBlock(data_3bytes, 0, 3, null, 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(Array.Empty(), 0, 1, null, 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(data_3bytes, 0, 1, new byte[10], 0)); + AssertExtensions.Throws("inputCount", () => transform.TransformBlock(new byte[4], 0, 4, new byte[10], 0)); + AssertExtensions.Throws("outputBuffer", () => transform.TransformBlock(data_3bytes, 0, 3, new byte[1], 0)); + AssertExtensions.Throws(null, () => transform.TransformBlock(Array.Empty(), 1, 0, null, 0)); - // These exceptions only thrown in ToBase - AssertExtensions.Throws("inputCount", () => transform.TransformFinalBlock(data_5bytes, 0, 5)); - } + AssertExtensions.Throws("inputBuffer", () => transform.TransformFinalBlock(null, 0, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); + AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); + AssertExtensions.Throws(null, () => transform.TransformFinalBlock(Array.Empty(), 1, 0)); } [Fact] public void InvalidInput_FromBase64Transform() { byte[] data_4bytes = Text.Encoding.ASCII.GetBytes("aaaa"); - ICryptoTransform transform = new FromBase64Transform(); - InvalidInput_Base64Transform(transform); - - // These exceptions only thrown in FromBase - transform.Dispose(); - Assert.Throws(() => transform.TransformBlock(data_4bytes, 0, 4, null, 0)); - Assert.Throws(() => transform.TransformFinalBlock(Array.Empty(), 0, 0)); - } - - private void InvalidInput_Base64Transform(ICryptoTransform transform) - { - byte[] data_4bytes = Text.Encoding.ASCII.GetBytes("aaaa"); AssertExtensions.Throws("inputBuffer", () => transform.TransformBlock(null, 0, 0, null, 0)); AssertExtensions.Throws("inputOffset", () => transform.TransformBlock(Array.Empty(), -1, 0, null, 0)); @@ -95,6 +90,28 @@ namespace System.Security.Cryptography.Encoding.Tests AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); AssertExtensions.Throws("inputOffset", () => transform.TransformFinalBlock(Array.Empty(), -1, 0)); AssertExtensions.Throws(null, () => transform.TransformFinalBlock(Array.Empty(), 1, 0)); + + // These exceptions only thrown in FromBase + transform.Dispose(); + Assert.Throws(() => transform.TransformBlock(data_4bytes, 0, 4, null, 0)); + Assert.Throws(() => transform.TransformFinalBlock(Array.Empty(), 0, 0)); + } + + [Fact] + public void ToBase64_TransformFinalBlock_MatchesConvert() + { + for (int i = 0; i < 100; i++) + { + byte[] input = new byte[i]; + Random.Shared.NextBytes(input); + + string expected = Convert.ToBase64String(input); + + using var transform = new ToBase64Transform(); + string actual = string.Concat(transform.TransformFinalBlock(input, 0, input.Length).Select(b => char.ToString((char)b))); + + Assert.Equal(expected, actual); + } } [Theory, MemberData(nameof(TestData_Ascii))] @@ -115,6 +132,38 @@ namespace System.Security.Cryptography.Encoding.Tests } } + [Theory] + [InlineData(100)] + [InlineData(101)] + [InlineData(102)] + [InlineData(103)] + public static void RoundtripCryptoStream(int length) + { + byte[] expected = RandomNumberGenerator.GetBytes(length); + var ms = new MemoryStream(); + + using (var toBase64 = new ToBase64Transform()) + using (var stream = new CryptoStream(ms, toBase64, CryptoStreamMode.Write, leaveOpen: true)) + { + stream.Write(expected); + } + + ms.Position = 0; + + byte[] actual = new byte[expected.Length]; + using (var fromBase64 = new FromBase64Transform()) + using (var stream = new CryptoStream(ms, fromBase64, CryptoStreamMode.Read, leaveOpen: true)) + { + int totalRead = 0, bytesRead; + while ((bytesRead = stream.Read(actual.AsSpan(totalRead))) != 0) + { + totalRead += bytesRead; + } + Assert.Equal(actual.Length, totalRead); + AssertExtensions.SequenceEqual(expected, actual); + } + } + private static void ValidateCryptoStream(string expected, string data, ICryptoTransform transform) { byte[] inputBytes = Text.Encoding.ASCII.GetBytes(data); @@ -148,9 +197,10 @@ namespace System.Security.Cryptography.Encoding.Tests byte[] inputBytes = Text.Encoding.ASCII.GetBytes(data); Assert.True(inputBytes.Length > 4); - // Test passing blocks > 4 characters to TransformFinalBlock (not supported) - _ = expected; - AssertExtensions.Throws("inputCount", () => transform.TransformFinalBlock(inputBytes, 0, inputBytes.Length)); + // Test passing blocks > 4 characters to TransformFinalBlock (supported) + byte[] outputBytes = transform.TransformFinalBlock(inputBytes, 0, inputBytes.Length); + string outputString = Text.Encoding.ASCII.GetString(outputBytes, 0, outputBytes.Length); + Assert.Equal(expected, outputString); } } @@ -280,7 +330,7 @@ namespace System.Security.Cryptography.Encoding.Tests { using (var transform = new ToBase64Transform()) { - Assert.False(transform.CanTransformMultipleBlocks); + Assert.True(transform.CanTransformMultipleBlocks); Assert.True(transform.CanReuseTransform); } } From d4b98b91d042513917b964cf78a65bd937ead3e8 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 6 Jul 2021 19:04:29 -0400 Subject: [PATCH 298/926] [mono][aot] Avoid using direct-icalls on ios simulator. (#55224) Fixes https://github.com/dotnet/runtime/issues/55000. --- src/tasks/AotCompilerTask/MonoAOTCompiler.props | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index 159c01ff3f2..c6b2f2ad28e 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -5,7 +5,6 @@ - @@ -13,6 +12,9 @@ + + + From c45b03a0dd9e608c47f7806889b9ce47cda46bc5 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 6 Jul 2021 19:25:22 -0700 Subject: [PATCH 299/926] Fix BinderTracingTest (non-clean) build on Linux (#55225) --- .../Loader/binding/tracing/BinderTracingTest.targets | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.targets b/src/tests/Loader/binding/tracing/BinderTracingTest.targets index 426a5556093..67b0d2d3a20 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.targets +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.targets @@ -38,15 +38,16 @@ fr-FR + $(OutDir)/DependentAssemblies - + - - - + + + From 61ea22a0fe4c35aa43ec3d5e99914a961b0f35b6 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 6 Jul 2021 22:42:06 -0400 Subject: [PATCH 300/926] Avoid FileStream allocations in Sockets (#55232) Use File.OpenHandle, as all that's needed is the SafeFileHandle, not the FileStream that wraps it. --- .../Net/Sockets/SocketAsyncEventArgs.Unix.cs | 10 ++++---- .../Sockets/SocketAsyncEventArgs.Windows.cs | 23 ++++++++++--------- .../src/System/Net/Sockets/SocketPal.Unix.cs | 20 ++++++++-------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs index 28d30164968..dca98cd90ee 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.IO; using System.Threading; -using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; namespace System.Net.Sockets { @@ -248,7 +248,7 @@ namespace System.Net.Sockets { Debug.Assert(_sendPacketsElements != null); SendPacketsElement[] elements = (SendPacketsElement[])_sendPacketsElements.Clone(); - FileStream[] files = new FileStream[elements.Length]; + SafeFileHandle[] fileHandles = new SafeFileHandle[elements.Length]; // Open all files synchronously ahead of time so that any exceptions are propagated // to the caller, to match Windows behavior. @@ -259,14 +259,14 @@ namespace System.Net.Sockets string? path = elements[i]?.FilePath; if (path != null) { - files[i] = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, useAsync: true); + fileHandles[i] = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.Asynchronous); } } } catch (Exception exc) { // Clean up any files that were already opened. - foreach (FileStream s in files) + foreach (SafeFileHandle s in fileHandles) { s?.Dispose(); } @@ -288,7 +288,7 @@ namespace System.Net.Sockets throw; } - SocketPal.SendPacketsAsync(socket, SendPacketsFlags, elements, files, cancellationToken, (bytesTransferred, error) => + SocketPal.SendPacketsAsync(socket, SendPacketsFlags, elements, fileHandles, cancellationToken, (bytesTransferred, error) => { if (error == SocketError.Success) { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index d8d59e91a2d..b22efcee8d9 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -8,6 +8,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using Microsoft.Win32.SafeHandles; namespace System.Net.Sockets { @@ -71,7 +72,7 @@ namespace System.Net.Sockets private Internals.SocketAddress? _pinnedSocketAddress; // SendPacketsElements property variables. - private FileStream[]? _sendPacketsFileStreams; + private SafeFileHandle[]? _sendPacketsFileHandles; // Overlapped object related variables. private PreAllocatedOverlapped _preAllocatedOverlapped; @@ -702,7 +703,7 @@ namespace System.Net.Sockets { // Loop through the elements attempting to open each files and get its handle. int index = 0; - _sendPacketsFileStreams = new FileStream[sendPacketsElementsFileCount]; + _sendPacketsFileHandles = new SafeFileHandle[sendPacketsElementsFileCount]; try { foreach (SendPacketsElement spe in sendPacketsElementsCopy) @@ -710,8 +711,8 @@ namespace System.Net.Sockets if (spe?.FilePath != null) { // Create a FileStream to open the file. - _sendPacketsFileStreams[index] = - new FileStream(spe.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); + _sendPacketsFileHandles[index] = + File.OpenHandle(spe.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); // Get the file handle from the stream. index++; @@ -722,8 +723,8 @@ namespace System.Net.Sockets { // Got an exception opening a file - close any open streams, then throw. for (int i = index - 1; i >= 0; i--) - _sendPacketsFileStreams[i].Dispose(); - _sendPacketsFileStreams = null; + _sendPacketsFileHandles[i].Dispose(); + _sendPacketsFileHandles = null; throw; } } @@ -1018,7 +1019,7 @@ namespace System.Net.Sockets else if (spe.FilePath != null) { // This element is a file. - sendPacketsDescriptorPinned[descriptorIndex].fileHandle = _sendPacketsFileStreams![fileIndex].SafeFileHandle.DangerousGetHandle(); + sendPacketsDescriptorPinned[descriptorIndex].fileHandle = _sendPacketsFileHandles![fileIndex].DangerousGetHandle(); sendPacketsDescriptorPinned[descriptorIndex].fileOffset = spe.OffsetLong; sendPacketsDescriptorPinned[descriptorIndex].length = (uint)spe.Count; sendPacketsDescriptorPinned[descriptorIndex].flags = @@ -1239,14 +1240,14 @@ namespace System.Net.Sockets private void FinishOperationSendPackets() { // Close the files if open. - if (_sendPacketsFileStreams != null) + if (_sendPacketsFileHandles != null) { - for (int i = 0; i < _sendPacketsFileStreams.Length; i++) + for (int i = 0; i < _sendPacketsFileHandles.Length; i++) { - _sendPacketsFileStreams[i]?.Dispose(); + _sendPacketsFileHandles[i]?.Dispose(); } - _sendPacketsFileStreams = null; + _sendPacketsFileHandles = null; } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index 7006ff63a0a..e7db1ec5851 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -1907,10 +1907,10 @@ namespace System.Net.Sockets return GetSocketErrorForErrorCode(err); } - private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, long offset, long count, CancellationToken cancellationToken, Action callback) + private static SocketError SendFileAsync(SafeSocketHandle handle, SafeFileHandle fileHandle, long offset, long count, CancellationToken cancellationToken, Action callback) { long bytesSent; - SocketError socketError = handle.AsyncContext.SendFileAsync(fileStream.SafeFileHandle, offset, count, out bytesSent, callback, cancellationToken); + SocketError socketError = handle.AsyncContext.SendFileAsync(fileHandle, offset, count, out bytesSent, callback, cancellationToken); if (socketError == SocketError.Success) { callback(bytesSent, SocketError.Success); @@ -1919,13 +1919,13 @@ namespace System.Net.Sockets } public static async void SendPacketsAsync( - Socket socket, TransmitFileOptions options, SendPacketsElement[] elements, FileStream[] files, CancellationToken cancellationToken, Action callback) + Socket socket, TransmitFileOptions options, SendPacketsElement[] elements, SafeFileHandle[] fileHandles, CancellationToken cancellationToken, Action callback) { SocketError error = SocketError.Success; long bytesTransferred = 0; try { - Debug.Assert(elements.Length == files.Length); + Debug.Assert(elements.Length == fileHandles.Length); for (int i = 0; i < elements.Length; i++) { SendPacketsElement e = elements[i]; @@ -1937,15 +1937,17 @@ namespace System.Net.Sockets } else { - FileStream fs = files[i] ?? e.FileStream!; - if (e.Count > fs.Length - e.OffsetLong) + SafeFileHandle fileHandle = fileHandles[i] ?? e.FileStream!.SafeFileHandle; + long fsLength = RandomAccess.GetLength(fileHandle); + + if (e.Count > fsLength - e.OffsetLong) { throw new ArgumentOutOfRangeException(); } var tcs = new TaskCompletionSource(); - error = SendFileAsync(socket.InternalSafeHandle, fs, e.OffsetLong, - e.Count > 0 ? e.Count : fs.Length - e.OffsetLong, + error = SendFileAsync(socket.InternalSafeHandle, fileHandle, e.OffsetLong, + e.Count > 0 ? e.Count : fsLength - e.OffsetLong, cancellationToken, (transferred, se) => { @@ -1975,7 +1977,7 @@ namespace System.Net.Sockets } catch (Exception exc) { - foreach (FileStream fs in files) + foreach (SafeFileHandle fs in fileHandles) { fs?.Dispose(); } From a2853029c2d79b3663fc243b0c43ec43b76ff189 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 6 Jul 2021 22:58:55 -0400 Subject: [PATCH 301/926] Relax chain status for building an invalid chain. (#55231) The chain results can be either PartialChain or NotSignatureValid depending on what we get back from the underlying platform. This relaxes the test so that both are acceptable. --- .../tests/ChainTests.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 77fd99c01a6..8e9db6ed695 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -988,11 +988,17 @@ tHP28fj0LUop/QFojSZPsaPAW6JvoQ0t4hd6WoyX6z7FsA== // Clear UntrustedRoot, if it happened. allFlags &= ~X509ChainStatusFlags.UntrustedRoot; - Assert.Equal( - X509ChainStatusFlags.NotSignatureValid, - allFlags); - - Assert.Equal(3, chain.ChainElements.Count); + // The chain result can either be PartialChain or NotSignatureValid. + // If the flags are PartialChain, then move on. + // If the flags are not PartialChain, we make sure the result is + // NotSignatureValid. + // In the case of PartialChain, we don't care how many certificates + // are in the chain. + if (allFlags != X509ChainStatusFlags.PartialChain) + { + Assert.Equal(X509ChainStatusFlags.NotSignatureValid, allFlags); + Assert.Equal(3, chain.ChainElements.Count); + } Assert.False(valid, $"Chain is valid on execution {iter}"); } From a1bb733e140cf7ce9c7c0919eb698f5d4b80dd0c Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 6 Jul 2021 23:16:23 -0500 Subject: [PATCH 302/926] Pass ASSERTIONS=0 along with -O0 so it doesn't break Blazor (#55226) --- src/mono/wasm/Makefile | 2 +- src/mono/wasm/build/WasmApp.Native.targets | 5 +++-- src/mono/wasm/wasm.proj | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 64d7b9d658f..8455ce69a39 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -71,7 +71,7 @@ MONO_LIBS = \ $(ICU_LIBDIR)/libicuuc.a \ $(ICU_LIBDIR)/libicui18n.a -EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DDEBUG=1 +EMCC_DEBUG_FLAGS =-g -Os -s -DDEBUG=1 EMCC_RELEASE_FLAGS=-Oz ifeq ($(NOSTRIP),) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index e1de26e4a4c..50e491ac060 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -160,13 +160,14 @@ <_WasmRuntimeICallTablePath>$(_WasmIntermediateOutputPath)runtime-icall-table.h <_WasmPInvokeTablePath>$(_WasmIntermediateOutputPath)pinvoke-table.h - <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 + <_EmccAssertionLevelDefault>0 + <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(OS)' != 'Windows_NT' and '$(Configuration)' == 'Debug'">-Os <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' != 'Debug'">-Oz <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz $(_EmccOptimizationFlagDefault) - -O0 + -O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index c8186ff30d3..45640378ac3 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -178,7 +178,7 @@ DependsOnTargets="GenerateEmccPropsAndRspFiles;BuildPInvokeTable;BundleTimezones"> - -g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 + -g -Os -s -DENABLE_NETCORE=1 -DDEBUG=1 -Oz -DENABLE_NETCORE=1 "$(EMSDK_PATH)/upstream/bin/wasm-opt" --strip-dwarf "$(NativeBinDir)dotnet.wasm" -o "$(NativeBinDir)dotnet.wasm" $(ArtifactsObjDir)wasm From 9ce467fe6164e781b67897615ff7dd01820084ff Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 7 Jul 2021 06:36:29 +0200 Subject: [PATCH 303/926] Fix race when signaling waitable objects in managed implementation (#55200) * TrySignalToSatisfyWait may invalidate multiple elements of the WaitedListNode linked list, if the same object occurs multiple times in a single WaitForMultipleObjects call. * Fixes https://github.com/dotnet/runtime/issues/52614 --- .../WaitSubsystem.ThreadWaitInfo.Unix.cs | 15 +++++++++++++++ .../WaitSubsystem.WaitableObject.Unix.cs | 8 ++++---- .../tests/MethodCoverage.cs | 1 - 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs index 58f61e4cd2e..77f452577fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs @@ -641,6 +641,21 @@ namespace System.Threading } } + // Like Next, but skip nodes registered on the same thread. + public WaitedListNode? NextThread + { + get + { + s_lock.VerifyIsLocked(); + WaitedListNode? ret = _next; + while (ret != null && ReferenceEquals(ret._waitInfo, _waitInfo)) + { + ret = ret._next; + } + return ret; + } + } + public void RegisterWait(WaitableObject waitableObject) { s_lock.VerifyIsLocked(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs index f5f37471440..cbce78aaa5c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs @@ -611,7 +611,7 @@ namespace System.Threading waiterNode = nextWaiterNode) { // Signaling a waiter will unregister the waiter node, so keep the next node before trying - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false); } @@ -635,7 +635,7 @@ namespace System.Threading { // Signaling a waiter will unregister the waiter node, but it may only abort the wait without satisfying the // wait, in which case we would try to signal another waiter. So, keep the next node before trying. - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; if (waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false)) { @@ -689,7 +689,7 @@ namespace System.Threading waiterNode = nextWaiterNode) { // Signaling the waiter will unregister the waiter node, so keep the next node before trying - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; if (waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false) && --count == 0) { @@ -753,7 +753,7 @@ namespace System.Threading { // Signaling a waiter will unregister the waiter node, but it may only abort the wait without satisfying the // wait, in which case we would try to signal another waiter. So, keep the next node before trying. - nextWaiterNode = waiterNode.Next; + nextWaiterNode = waiterNode.NextThread; ThreadWaitInfo waitInfo = waiterNode.WaitInfo; if (waitInfo.TrySignalToSatisfyWait(waiterNode, isAbandoned)) diff --git a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs index c005e262351..e696a38cf92 100644 --- a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs +++ b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs @@ -322,7 +322,6 @@ namespace TaskCoverage /// FromAsync testing: Not supported in .NET Native /// [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52614", TestPlatforms.iOS | TestPlatforms.tvOS)] public static void FromAsync() { Task emptyTask = new Task(() => { }); From c6b61fb32627e3eede21ad9d9cf18400084c40a2 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Wed, 7 Jul 2021 10:22:16 +0200 Subject: [PATCH 304/926] SafeFileHandle.Unix: omit stat call when opening file with write access. (#55206) --- .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index c510d4d9b4a..1ce14b82abe 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -64,26 +64,31 @@ namespace Microsoft.Win32.SafeHandles // Make sure it's not a directory; we do this after opening it once we have a file descriptor // to avoid race conditions. - Interop.Sys.FileStatus status; - if (Interop.Sys.FStat(handle, out status) != 0) + // + // We can omit the check when write access is requested. open will have failed with EISDIR. + if ((flags & (Interop.Sys.OpenFlags.O_WRONLY | Interop.Sys.OpenFlags.O_RDWR)) == 0) { - Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); - handle.Dispose(); - throw Interop.GetExceptionForIoErrno(error, path); - } - if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) - { - handle.Dispose(); - throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true); - } + Interop.Sys.FileStatus status; + if (Interop.Sys.FStat(handle, out status) != 0) + { + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + handle.Dispose(); + throw Interop.GetExceptionForIoErrno(error, path); + } + if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) + { + handle.Dispose(); + throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true); + } - if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) - { - // we take advantage of the information provided by the fstat syscall - // and for regular files (most common case) - // avoid one extra sys call for determining whether file can be seeked - handle._canSeek = NullableBool.True; - Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0); + if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) + { + // we take advantage of the information provided by the fstat syscall + // and for regular files (most common case) + // avoid one extra sys call for determining whether file can be seeked + handle._canSeek = NullableBool.True; + Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0); + } } return handle; From 121bcd18e1d85cdc4549dd4fcfc4760a283878e4 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 7 Jul 2021 05:30:29 -0400 Subject: [PATCH 305/926] [mono] Remove mono_corlib_version, it has not been maintained for some time. (#55236) Fixes https://github.com/dotnet/runtime/issues/47767. --- src/mono/CMakeLists.txt | 23 ---------- .../GenerateMonoCoreLibVersionFile.targets | 36 --------------- .../System.Private.CoreLib.csproj | 3 -- .../src/ILLink/ILLink.Descriptors.xml | 2 - src/mono/mono/metadata/appdomain.c | 45 ------------------- 5 files changed, 109 deletions(-) delete mode 100644 src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 8f73b2a7dda..52a82116bc4 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -62,29 +62,6 @@ if(ENABLE_MINIMAL) process_enable_minimal() endif() -# -# This is the version of the corlib-runtime interface. When -# making changes to this interface (by changing the layout -# of classes the runtime knows about, changing icall signature or -# semantics etc), change this variable. -# -# This must be unique relative to corlib interface and semantics. -# -# If you change corlib such that a runtime change is required, or -# vice versa, change this string. Examples include removing icalls, -# adding icalls, changing icall signatures, and changing type layouts -# that both sides know. -# -# It is an arbitrary string and should be parsed as such. -# A guid works and is encouraged. -# -# There is no ordering of corlib versions, no old or new, -# an exact match is required between corlib and runtime. -# -# This line is parsed by other tools, it should remain in the format they expect. -# -set(MONO_CORLIB_VERSION 1A5E0066-58DC-428A-B21C-0AD6CDAE2789) - set(DISABLE_MDB 1) set(DISABLE_COM 1) set(DISABLE_PERFCOUNTERS 1) diff --git a/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets b/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets deleted file mode 100644 index b9bfab7bd6a..00000000000 --- a/src/mono/System.Private.CoreLib/GenerateMonoCoreLibVersionFile.targets +++ /dev/null @@ -1,36 +0,0 @@ - - - - GenerateMonoCoreLibVersionFile;$(CompileDependsOn) - $(IntermediateOutputPath)\Environment.MonoCoreLibVersion.cs - - - - - <_MonoCoreLibVersionNumber>$([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MonoProjectRoot)\CMakeLists.txt')), 'MONO_CORLIB_VERSION ([a-fA-F0-9\-]+)').Groups[1].Value) - <_MonoCoreLibVersionFileLines> -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// This is a generated file. Do not edit. - -#pragma warning disable CA1823 // unused field -namespace System -{ - public partial class Environment - { - private const string mono_corlib_version = "$(_MonoCoreLibVersionNumber)"%3B - } -} - - - - - - - - - - - - diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index fd93f623800..5290a6bc8c5 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,7 +1,4 @@  - - - false true diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml index f332a9d01cb..eac1bad9a0b 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -129,8 +129,6 @@ - - diff --git a/src/mono/mono/metadata/appdomain.c b/src/mono/mono/metadata/appdomain.c index 1e9b3d21383..09d8c4527e3 100644 --- a/src/mono/mono/metadata/appdomain.c +++ b/src/mono/mono/metadata/appdomain.c @@ -309,37 +309,6 @@ exit: HANDLE_FUNCTION_RETURN (); } -static char* -mono_get_corlib_version (void) -{ - ERROR_DECL (error); - - MonoClass *klass; - MonoClassField *field; - - klass = mono_class_load_from_name (mono_defaults.corlib, "System", "Environment"); - mono_class_init_internal (klass); - field = mono_class_get_field_from_name_full (klass, "mono_corlib_version", NULL); - if (!field) - return NULL; - - if (! (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_LITERAL))) - return NULL; - - char *value; - MonoTypeEnum field_type; - const char *data = mono_class_get_field_default_value (field, &field_type); - if (field_type != MONO_TYPE_STRING) - return NULL; - mono_metadata_read_constant_value (data, field_type, &value, error); - mono_error_assert_ok (error); - - char *res = mono_string_from_blob (value, error); - mono_error_assert_ok (error); - - return res; -} - /** * mono_check_corlib_version: * Checks that the corlib that is loaded matches the version of this runtime. @@ -364,18 +333,6 @@ mono_check_corlib_version_internal (void) return NULL; #else char *result = NULL; - char *version = mono_get_corlib_version (); - if (!version) { - result = g_strdup_printf ("expected corlib string (%s) but not found or not string", MONO_CORLIB_VERSION); - goto exit; - } - if (strcmp (version, MONO_CORLIB_VERSION) != 0) { - result = g_strdup_printf ("The runtime did not find the mscorlib.dll it expected. " - "Expected interface version %s but found %s. Check that " - "your runtime and class libraries are matching.", - MONO_CORLIB_VERSION, version); - goto exit; - } /* Check that the managed and unmanaged layout of MonoInternalThread matches */ guint32 native_offset; @@ -384,8 +341,6 @@ mono_check_corlib_version_internal (void) managed_offset = mono_field_get_offset (mono_class_get_field_from_name_full (mono_defaults.internal_thread_class, "last", NULL)); if (native_offset != managed_offset) result = g_strdup_printf ("expected InternalThread.last field offset %u, found %u. See InternalThread.last comment", native_offset, managed_offset); -exit: - g_free (version); return result; #endif } From fcedb50e55344e24d404c8a98b444ef1b4a69868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 7 Jul 2021 08:56:34 -0400 Subject: [PATCH 306/926] [mono] Don't call Assembly.CodeBase directly in RuntimeAssembly.GetName (#54895) * [mono] Don't call Assembly.CodeBase directly in RuntimeAssembly.GetName It's marked as not available in single file apps. Call the underlying get_code_base icall. Fixes https://github.com/dotnet/runtime/issues/54835 * [icall] Use MonoImage:filename for RuntimeAssembly.get_code_base For bundled asssemblies in single file scenarios, RuntimeAssembly.CodeBase will be null, matching CoreCLR. * disable codebase test on wasm --- .../System.Reflection/tests/AssemblyTests.cs | 2 +- .../src/System/Reflection/RuntimeAssembly.cs | 4 +--- src/mono/mono/metadata/icall.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Reflection/tests/AssemblyTests.cs b/src/libraries/System.Reflection/tests/AssemblyTests.cs index 0732615ec9e..6c5c8c2bb32 100644 --- a/src/libraries/System.Reflection/tests/AssemblyTests.cs +++ b/src/libraries/System.Reflection/tests/AssemblyTests.cs @@ -510,7 +510,7 @@ namespace System.Reflection.Tests } #pragma warning disable SYSLIB0012 - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] // single file public void CodeBase() { Assert.NotEmpty(Helpers.ExecutingAssembly.CodeBase); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 5fb9e074174..21ac12509e6 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -253,9 +253,7 @@ namespace System.Reflection public override AssemblyName GetName(bool copiedName) { -#pragma warning disable IL3002 // Suppressing for now. See https://github.com/dotnet/runtime/issues/54835 - return AssemblyName.Create(_mono_assembly, CodeBase); -#pragma warning restore IL3002 + return AssemblyName.Create(_mono_assembly, get_code_base (this)); } [RequiresUnreferencedCode("Types might be removed")] diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 84fc4df27c1..d725b1c3b5f 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -4462,12 +4462,18 @@ MonoStringHandle ves_icall_System_Reflection_RuntimeAssembly_get_code_base (MonoReflectionAssemblyHandle assembly, MonoError *error) { MonoAssembly *mass = MONO_HANDLE_GETVAL (assembly, assembly); - gchar *absolute; - if (g_path_is_absolute (mass->image->name)) { - absolute = g_strdup (mass->image->name); + /* return NULL for bundled assemblies in single-file scenarios */ + const char* filename = m_image_get_filename (mass->image); + + if (!filename) + return NULL_HANDLE_STRING; + + gchar *absolute; + if (g_path_is_absolute (filename)) { + absolute = g_strdup (filename); } else { - absolute = g_build_filename (mass->basedir, mass->image->name, (const char*)NULL); + absolute = g_build_filename (mass->basedir, filename, (const char*)NULL); } mono_icall_make_platform_path (absolute); From 974e1af03b57bd4a366189c1e9e45ab771c9c985 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 7 Jul 2021 17:21:46 +0200 Subject: [PATCH 307/926] Fix incorrect assert in AbandonExisting test case (#55198) * Handle the case where the "notAbandonedWait" event is not signaled --- src/libraries/System.Threading/tests/MutexTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index a893662df15..4af1d538bfa 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -348,7 +348,7 @@ namespace System.Threading.Tests } else { - Assert.True(m2Index < notAbandonedWaitIndex); + Assert.True(!isNotAbandonedWaitObjectSignaled || m2Index < notAbandonedWaitIndex); Assert.Equal(m2Index, ame.MutexIndex); } From 527e5663b55440172b44a933c48c5e9c6be74f62 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Wed, 7 Jul 2021 18:25:57 +0300 Subject: [PATCH 308/926] Disable folding of implementation-defined casts (#53782) * Enhance the FloatOvfToInt2 test to exercise VN paths It is not very effective right now because the "bad" combinations of types are morphed into helpers too early, but it will be helpful when we enable folding outside of the importer for those cases too. * Enhance the FloatOvfToInt2 test to cover importer It now fails on Windows x86, where many of the previous similar failures were observed. * Disable the test on Mono * Re-enable tests disabled against #13651 * Re-enable tests disabled against #51346 * Re-enable tests disabled against #47374 * Disable folding in gtFoldExprConst * Disable folding in VN * Temporarily promote the test to Pri0 * Reword the comment * Move the tests back to Pri1 Where they originally were. --- src/coreclr/jit/gentree.cpp | 48 +- src/coreclr/jit/valuenum.cpp | 42 +- .../tests/Convert/ConvertTests.cs | 13 - .../JIT/Methodical/Overflow/FloatOvfToInt2.cs | 483 ++++++++++++++++++ src/tests/issues.targets | 12 + 5 files changed, 533 insertions(+), 65 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 955a7bba1e9..35b01ad27ec 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14458,47 +14458,27 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) break; case GT_CAST: + f1 = forceCastToFloat(d1); - if (tree->gtOverflow() && - ((op1->TypeIs(TYP_DOUBLE) && CheckedOps::CastFromDoubleOverflows(d1, tree->CastToType())) || - (op1->TypeIs(TYP_FLOAT) && - CheckedOps::CastFromFloatOverflows(forceCastToFloat(d1), tree->CastToType())))) + if ((op1->TypeIs(TYP_DOUBLE) && CheckedOps::CastFromDoubleOverflows(d1, tree->CastToType())) || + (op1->TypeIs(TYP_FLOAT) && CheckedOps::CastFromFloatOverflows(f1, tree->CastToType()))) { + // The conversion overflows. The ECMA spec says, in III 3.27, that + // "...if overflow occurs converting a floating point type to an integer, ..., + // the value returned is unspecified." However, it would at least be + // desirable to have the same value returned for casting an overflowing + // constant to an int as would be obtained by passing that constant as + // a parameter and then casting that parameter to an int type. + + // Don't fold overflowing converions, as the value returned by + // JIT's codegen doesn't always match with the C compiler's cast result. + // We want the behavior to be the same with or without folding. + return tree; } assert(tree->TypeIs(genActualType(tree->CastToType()))); - if ((op1->TypeIs(TYP_FLOAT) && !_finite(forceCastToFloat(d1))) || - (op1->TypeIs(TYP_DOUBLE) && !_finite(d1))) - { - // The floating point constant is not finite. The ECMA spec says, in - // III 3.27, that "...if overflow occurs converting a floating point type - // to an integer, ..., the value returned is unspecified." However, it would - // at least be desirable to have the same value returned for casting an overflowing - // constant to an int as would obtained by passing that constant as a parameter - // then casting that parameter to an int type. We will assume that the C compiler's - // cast logic will yield the desired result (and trust testing to tell otherwise). - // Cross-compilation is an issue here; if that becomes an important scenario, we should - // capture the target-specific values of overflow casts to the various integral types as - // constants in a target-specific function. - CLANG_FORMAT_COMMENT_ANCHOR; - - // Don't fold conversions of +inf/-inf to integral value on all platforms - // as the value returned by JIT helper doesn't match with the C compiler's cast result. - // We want the behavior to be same with or without folding. - return tree; - } - - if (d1 <= -1.0 && varTypeIsUnsigned(tree->CastToType())) - { - // Don't fold conversions of these cases becasue the result is unspecified per ECMA spec - // and the native math doing the fold doesn't match the run-time computation on all - // platforms. - // We want the behavior to be same with or without folding. - return tree; - } - switch (tree->CastToType()) { case TYP_BYTE: diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index e49ba55eca8..110730f15e6 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -3012,7 +3012,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu case TYP_FLOAT: { float arg0Val = GetConstantSingle(arg0VN); - assert(!checkedCast || !CheckedOps::CastFromFloatOverflows(arg0Val, castToType)); + assert(!CheckedOps::CastFromFloatOverflows(arg0Val, castToType)); switch (castToType) { @@ -3054,7 +3054,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu case TYP_DOUBLE: { double arg0Val = GetConstantDouble(arg0VN); - assert(!checkedCast || !CheckedOps::CastFromDoubleOverflows(arg0Val, castToType)); + assert(!CheckedOps::CastFromDoubleOverflows(arg0Val, castToType)); switch (castToType) { @@ -3322,26 +3322,32 @@ bool ValueNumStore::VNEvalShouldFold(var_types typ, VNFunc func, ValueNum arg0VN } } - // Is this a checked cast that will always throw an exception? - if (func == VNF_CastOvf) + // Is this a checked cast that will always throw an exception or one with an implementation-defined result? + if (VNFuncIsNumericCast(func)) { - var_types castToType; - bool fromUnsigned; - GetCastOperFromVN(arg1VN, &castToType, &fromUnsigned); var_types castFromType = TypeOfVN(arg0VN); - switch (castFromType) + // By policy, we do not fold conversions from floating-point types that result in + // overflow, as the value the C++ compiler gives us does not always match our own codegen. + if ((func == VNF_CastOvf) || varTypeIsFloating(castFromType)) { - case TYP_INT: - return !CheckedOps::CastFromIntOverflows(GetConstantInt32(arg0VN), castToType, fromUnsigned); - case TYP_LONG: - return !CheckedOps::CastFromLongOverflows(GetConstantInt64(arg0VN), castToType, fromUnsigned); - case TYP_FLOAT: - return !CheckedOps::CastFromFloatOverflows(GetConstantSingle(arg0VN), castToType); - case TYP_DOUBLE: - return !CheckedOps::CastFromDoubleOverflows(GetConstantDouble(arg0VN), castToType); - default: - return false; + var_types castToType; + bool fromUnsigned; + GetCastOperFromVN(arg1VN, &castToType, &fromUnsigned); + + switch (castFromType) + { + case TYP_INT: + return !CheckedOps::CastFromIntOverflows(GetConstantInt32(arg0VN), castToType, fromUnsigned); + case TYP_LONG: + return !CheckedOps::CastFromLongOverflows(GetConstantInt64(arg0VN), castToType, fromUnsigned); + case TYP_FLOAT: + return !CheckedOps::CastFromFloatOverflows(GetConstantSingle(arg0VN), castToType); + case TYP_DOUBLE: + return !CheckedOps::CastFromDoubleOverflows(GetConstantDouble(arg0VN), castToType); + default: + return false; + } } } diff --git a/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs b/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs index cf2242b4141..eb7d20b6ac7 100644 --- a/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Convert/ConvertTests.cs @@ -1650,7 +1650,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/13651")] public static void ConvertDoubleToUIntTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1660,7 +1659,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/13651")] public static void ConvertDoubleToNullableUIntTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1679,7 +1677,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertDoubleToNullableULongTest(bool useInterpreter) { foreach (double value in new double[] { 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1905,7 +1902,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToUIntTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1915,7 +1911,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToNullableUIntTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -1934,7 +1929,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableDoubleToNullableULongTest(bool useInterpreter) { foreach (double? value in new double?[] { null, 0, 1, -1, double.MinValue, double.MaxValue, double.Epsilon, double.NegativeInfinity, double.PositiveInfinity, double.NaN }) @@ -3096,7 +3090,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/47374", TestRuntimes.CoreCLR)] public static void ConvertFloatToUIntTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3106,7 +3099,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/47374", TestRuntimes.CoreCLR)] public static void ConvertFloatToNullableUIntTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3124,9 +3116,7 @@ namespace System.Linq.Expressions.Tests } } - [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertFloatToNullableULongTest(bool useInterpreter) { foreach (float value in new float[] { 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3352,7 +3342,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToUIntTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3362,7 +3351,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToNullableUIntTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) @@ -3381,7 +3369,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51346", TestRuntimes.CoreCLR)] public static void ConvertNullableFloatToNullableULongTest(bool useInterpreter) { foreach (float? value in new float?[] { null, 0, 1, -1, float.MinValue, float.MaxValue, float.Epsilon, float.NegativeInfinity, float.PositiveInfinity, float.NaN }) diff --git a/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs b/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs index dfd292b755b..acff1809714 100644 --- a/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs +++ b/src/tests/JIT/Methodical/Overflow/FloatOvfToInt2.cs @@ -6,6 +6,9 @@ using System.Runtime.CompilerServices; internal class FloatOvfToInt { + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool BreakUpFlow() => false; + [MethodImpl(MethodImplOptions.NoInlining)] public static long FloatToLong(float f) { @@ -228,6 +231,64 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesFloatLongVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToLong(bigf) != FloatToLongInline(bigf)) return 401; + if (FloatToUlong(bigf) != FloatToUlongInline(bigf)) return 402; + if (FloatToLong(-bigf) != FloatToLongInline(-bigf)) return 403; + if (FloatToUlong(-bigf) != FloatToUlongInline(-bigf)) return 404; + + return 100; + } + + public static int TestValuesFloatLongImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToLong(bigf) != FloatToLongInline(100000000000000000000000000000.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(100000000000000000000000000000.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-100000000000000000000000000000.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-100000000000000000000000000000.0f)) return 504; + + bigf = 987654321001234567899876543210.0f; + if (FloatToLong(bigf) != FloatToLongInline(987654321001234567899876543210.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(987654321001234567899876543210.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-987654321001234567899876543210.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-987654321001234567899876543210.0f)) return 504; + + bigf = 254783961024896571038054632179.0f; + if (FloatToLong(bigf) != FloatToLongInline(254783961024896571038054632179.0f)) return 501; + if (FloatToUlong(bigf) != FloatToUlongInline(254783961024896571038054632179.0f)) return 502; + if (FloatToLong(-bigf) != FloatToLongInline(-254783961024896571038054632179.0f)) return 503; + if (FloatToUlong(-bigf) != FloatToUlongInline(-254783961024896571038054632179.0f)) return 504; + + return 100; + } + public static int TestValuesFloatInt() { float bigf = 100000000000000000000000000000.0f; @@ -251,6 +312,64 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesFloatIntVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToInt(bigf) != FloatToIntInline(bigf)) return 411; + if (FloatToUint(bigf) != FloatToUintInline(bigf)) return 412; + if (FloatToInt(-bigf) != FloatToIntInline(-bigf)) return 413; + if (FloatToUint(-bigf) != FloatToUintInline(-bigf)) return 414; + + return 100; + } + + public static int TestValuesFloatIntImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToInt(bigf) != FloatToIntInline(100000000000000000000000000000.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(100000000000000000000000000000.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-100000000000000000000000000000.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-100000000000000000000000000000.0f)) return 514; + + bigf = 987654321001234567899876543210.0f; + if (FloatToInt(bigf) != FloatToIntInline(987654321001234567899876543210.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(987654321001234567899876543210.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-987654321001234567899876543210.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-987654321001234567899876543210.0f)) return 514; + + bigf = 254783961024896571038054632179.0f; + if (FloatToInt(bigf) != FloatToIntInline(254783961024896571038054632179.0f)) return 511; + if (FloatToUint(bigf) != FloatToUintInline(254783961024896571038054632179.0f)) return 512; + if (FloatToInt(-bigf) != FloatToIntInline(-254783961024896571038054632179.0f)) return 513; + if (FloatToUint(-bigf) != FloatToUintInline(-254783961024896571038054632179.0f)) return 514; + + return 100; + } + public static int TestValuesFloatShort() { float bigf = 100000000000000000000000000000.0f; @@ -274,6 +393,64 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesFloatShortVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToShort(bigf) != FloatToShortInline(bigf)) return 421; + if (FloatToUshort(bigf) != FloatToUshortInline(bigf)) return 422; + if (FloatToShort(-bigf) != FloatToShortInline(-bigf)) return 423; + if (FloatToUshort(-bigf) != FloatToUshortInline(-bigf)) return 424; + + return 100; + } + + public static int TestValuesFloatShortImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToShort(bigf) != FloatToShortInline(100000000000000000000000000000.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(100000000000000000000000000000.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-100000000000000000000000000000.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-100000000000000000000000000000.0f)) return 524; + + bigf = 987654321001234567899876543210.0f; + if (FloatToShort(bigf) != FloatToShortInline(987654321001234567899876543210.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(987654321001234567899876543210.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-987654321001234567899876543210.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-987654321001234567899876543210.0f)) return 524; + + bigf = 254783961024896571038054632179.0f; + if (FloatToShort(bigf) != FloatToShortInline(254783961024896571038054632179.0f)) return 521; + if (FloatToUshort(bigf) != FloatToUshortInline(254783961024896571038054632179.0f)) return 522; + if (FloatToShort(-bigf) != FloatToShortInline(-254783961024896571038054632179.0f)) return 523; + if (FloatToUshort(-bigf) != FloatToUshortInline(-254783961024896571038054632179.0f)) return 524; + + return 100; + } + public static int TestValuesFloatByte() { float bigf = 100000000000000000000000000000.0f; @@ -297,21 +474,91 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesFloatByteVN() + { + float bigf = 100000000000000000000000000000.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + bigf = 987654321001234567899876543210.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + bigf = 254783961024896571038054632179.0f; + + if (BreakUpFlow()) + return 1000; + + if (FloatToSbyte(bigf) != FloatToSbyteInline(bigf)) return 441; + if (FloatToByte(bigf) != FloatToByteInline(bigf)) return 442; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-bigf)) return 443; + if (FloatToByte(-bigf) != FloatToByteInline(-bigf)) return 444; + + return 100; + } + + public static int TestValuesFloatByteImport() + { + float bigf = 100000000000000000000000000000.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(100000000000000000000000000000.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(100000000000000000000000000000.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-100000000000000000000000000000.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-100000000000000000000000000000.0f)) return 544; + + bigf = 987654321001234567899876543210.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(987654321001234567899876543210.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(987654321001234567899876543210.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-987654321001234567899876543210.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-987654321001234567899876543210.0f)) return 544; + + bigf = 254783961024896571038054632179.0f; + if (FloatToSbyte(bigf) != FloatToSbyteInline(254783961024896571038054632179.0f)) return 541; + if (FloatToByte(bigf) != FloatToByteInline(254783961024896571038054632179.0f)) return 542; + if (FloatToSbyte(-bigf) != FloatToSbyteInline(-254783961024896571038054632179.0f)) return 543; + if (FloatToByte(-bigf) != FloatToByteInline(-254783961024896571038054632179.0f)) return 544; + + return 100; + } + public static int TestValuesDoubleLong() { double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 204; bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 204; bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 201; if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 202; if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 203; @@ -320,6 +567,52 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesDoubleLongVN() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + bigd = 987654321001234567899876543210.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + bigd = 254783961024896571038054632179.0; + if (DoubleToLong(bigd) != DoubleToLongInline(bigd)) return 301; + if (DoubleToUlong(bigd) != DoubleToUlongInline(bigd)) return 302; + if (DoubleToLong(-bigd) != DoubleToLongInline(-bigd)) return 303; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-bigd)) return 304; + + return 100; + } + + public static int TestValuesDoubleLongImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToLong(bigd) != DoubleToLongInline(100000000000000000000000000000.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(100000000000000000000000000000.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-100000000000000000000000000000.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-100000000000000000000000000000.0)) return 604; + + bigd = 987654321001234567899876543210.0; + if (DoubleToLong(bigd) != DoubleToLongInline(987654321001234567899876543210.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(987654321001234567899876543210.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-987654321001234567899876543210.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-987654321001234567899876543210.0)) return 604; + + bigd = 254783961024896571038054632179.0; + if (DoubleToLong(bigd) != DoubleToLongInline(254783961024896571038054632179.0)) return 601; + if (DoubleToUlong(bigd) != DoubleToUlongInline(254783961024896571038054632179.0)) return 602; + if (DoubleToLong(-bigd) != DoubleToLongInline(-254783961024896571038054632179.0)) return 603; + if (DoubleToUlong(-bigd) != DoubleToUlongInline(-254783961024896571038054632179.0)) return 604; + + return 100; + } + public static int TestValuesDoubleInt() { double bigd = 100000000000000000000000000000.0; @@ -343,6 +636,64 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesDoubleIntVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToInt(bigd) != DoubleToIntInline(bigd)) return 311; + if (DoubleToUint(bigd) != DoubleToUintInline(bigd)) return 312; + if (DoubleToInt(-bigd) != DoubleToIntInline(-bigd)) return 313; + if (DoubleToUint(-bigd) != DoubleToUintInline(-bigd)) return 314; + + return 100; + } + + public static int TestValuesDoubleIntImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToInt(bigd) != DoubleToIntInline(100000000000000000000000000000.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(100000000000000000000000000000.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-100000000000000000000000000000.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-100000000000000000000000000000.0)) return 614; + + bigd = 987654321001234567899876543210.0; + if (DoubleToInt(bigd) != DoubleToIntInline(987654321001234567899876543210.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(987654321001234567899876543210.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-987654321001234567899876543210.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-987654321001234567899876543210.0)) return 614; + + bigd = 254783961024896571038054632179.0; + if (DoubleToInt(bigd) != DoubleToIntInline(254783961024896571038054632179.0)) return 611; + if (DoubleToUint(bigd) != DoubleToUintInline(254783961024896571038054632179.0)) return 612; + if (DoubleToInt(-bigd) != DoubleToIntInline(-254783961024896571038054632179.0)) return 613; + if (DoubleToUint(-bigd) != DoubleToUintInline(-254783961024896571038054632179.0)) return 614; + + return 100; + } + public static int TestValuesDoubleShort() { double bigd = 100000000000000000000000000000.0; @@ -366,6 +717,64 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesDoubleShortVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToShort(bigd) != DoubleToShortInline(bigd)) return 321; + if (DoubleToUshort(bigd) != DoubleToUshortInline(bigd)) return 322; + if (DoubleToShort(-bigd) != DoubleToShortInline(-bigd)) return 323; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 324; + + return 100; + } + + public static int TestValuesDoubleShortImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToShort(bigd) != DoubleToShortInline(100000000000000000000000000000.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(100000000000000000000000000000.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-100000000000000000000000000000.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-bigd)) return 624; + + bigd = 987654321001234567899876543210.0; + if (DoubleToShort(bigd) != DoubleToShortInline(987654321001234567899876543210.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(987654321001234567899876543210.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-987654321001234567899876543210.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-987654321001234567899876543210.0)) return 624; + + bigd = 254783961024896571038054632179.0; + if (DoubleToShort(bigd) != DoubleToShortInline(254783961024896571038054632179.0)) return 621; + if (DoubleToUshort(bigd) != DoubleToUshortInline(254783961024896571038054632179.0)) return 622; + if (DoubleToShort(-bigd) != DoubleToShortInline(-254783961024896571038054632179.0)) return 623; + if (DoubleToUshort(-bigd) != DoubleToUshortInline(-254783961024896571038054632179.0)) return 624; + + return 100; + } + public static int TestValuesDoubleByte() { double bigd = 100000000000000000000000000000.0; @@ -389,17 +798,91 @@ internal class FloatOvfToInt return 100; } + public static int TestValuesDoubleByteVN() + { + double bigd = 100000000000000000000000000000.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + bigd = 987654321001234567899876543210.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + bigd = 254783961024896571038054632179.0; + + if (BreakUpFlow()) + return 1000; + + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(bigd)) return 341; + if (DoubleToByte(bigd) != DoubleToByteInline(bigd)) return 342; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-bigd)) return 343; + if (DoubleToByte(-bigd) != DoubleToByteInline(-bigd)) return 344; + + return 100; + } + + public static int TestValuesDoubleByteImport() + { + double bigd = 100000000000000000000000000000.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(100000000000000000000000000000.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(100000000000000000000000000000.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-100000000000000000000000000000.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-100000000000000000000000000000.0)) return 644; + + bigd = 987654321001234567899876543210.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(987654321001234567899876543210.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(987654321001234567899876543210.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-987654321001234567899876543210.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-987654321001234567899876543210.0)) return 644; + + bigd = 254783961024896571038054632179.0; + if (DoubleToSbyte(bigd) != DoubleToSbyteInline(987654321001234567899876543210.0)) return 641; + if (DoubleToByte(bigd) != DoubleToByteInline(987654321001234567899876543210.0)) return 642; + if (DoubleToSbyte(-bigd) != DoubleToSbyteInline(-987654321001234567899876543210.0)) return 643; + if (DoubleToByte(-bigd) != DoubleToByteInline(-987654321001234567899876543210.0)) return 644; + + return 100; + } + public static int TestValues() { int res = TestValuesFloatLong(); if (res != 100) return res; + res = TestValuesFloatLongVN(); if (res != 100) return res; + res = TestValuesFloatLongImport(); if (res != 100) return res; res = TestValuesFloatInt(); if (res != 100) return res; + res = TestValuesFloatIntVN(); if (res != 100) return res; + res = TestValuesFloatIntImport(); if (res != 100) return res; res = TestValuesFloatShort(); if (res != 100) return res; + res = TestValuesFloatShortImport(); if (res != 100) return res; + res = TestValuesFloatShortVN(); if (res != 100) return res; res = TestValuesFloatByte(); if (res != 100) return res; + res = TestValuesFloatByteImport(); if (res != 100) return res; + res = TestValuesFloatByteVN(); if (res != 100) return res; res = TestValuesDoubleLong(); if (res != 100) return res; + res = TestValuesDoubleLongVN(); if (res != 100) return res; + res = TestValuesDoubleLongImport(); if (res != 100) return res; res = TestValuesDoubleInt(); if (res != 100) return res; + res = TestValuesDoubleIntVN(); if (res != 100) return res; + res = TestValuesDoubleIntImport(); if (res != 100) return res; res = TestValuesDoubleShort(); if (res != 100) return res; + res = TestValuesDoubleShortVN(); if (res != 100) return res; + res = TestValuesDoubleShortImport(); if (res != 100) return res; res = TestValuesDoubleByte(); if (res != 100) return res; + res = TestValuesDoubleByteVN(); if (res != 100) return res; + res = TestValuesDoubleByteImport(); if (res != 100) return res; return res; } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index f94900a7421..169d2c0b213 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1026,6 +1026,18 @@ Mono does not define out of range fp to int conversions + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + + + https://github.com/dotnet/runtime/issues/51323 + https://github.com/dotnet/runtime/issues/48190 From a789606c37e9c8c7d42d8fa70c7840ae3c441f0e Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 7 Jul 2021 19:34:44 +0300 Subject: [PATCH 309/926] [interp] Re-enable tests on android (#54997) Fixed by https://github.com/dotnet/runtime/pull/54734 --- src/tests/issues.targets | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 169d2c0b213..9aa21e9c19c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -2215,12 +2215,6 @@ https://github.com/dotnet/runtime/issues/54376 - - https://github.com/dotnet/runtime/issues/54374e - - - https://github.com/dotnet/runtime/issues/54374 - From d5f95444d74a6ce2e1d09915ed0fdb3bba727f4c Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 7 Jul 2021 10:01:45 -0700 Subject: [PATCH 310/926] System.Text.Json: Add TimeSpanConverter (#54186) * Added TimeSpanConverter. * Code review. * Test tweak. * Added invalid cases. * Remove the ToArray call in the case of ValueSequence. * Support escaped strings in TimeSpanConverter. * Removed 'g' format fallback. * Fixed 'h:mm:ss' being accepted by TimeSpanConverter. * Code review. * Code review. --- .../System.Text.Json/ref/System.Text.Json.cs | 1 + .../src/Resources/Strings.resx | 3 + .../src/System.Text.Json.csproj | 1 + .../src/System/Text/Json/JsonHelpers.Date.cs | 6 -- .../Text/Json/Reader/Utf8JsonReader.TryGet.cs | 35 +++---- .../Converters/Value/TimeSpanConverter.cs | 99 +++++++++++++++++++ .../JsonSerializerOptions.Converters.cs | 3 +- .../JsonMetadataServices.Converters.cs | 6 ++ .../src/System/Text/Json/ThrowHelper.cs | 4 + .../JsonDateTimeTestData.cs | 2 +- .../Serialization/Value.ReadTests.cs | 71 +++++++++++++ .../Serialization/Value.WriteTests.cs | 19 ++++ .../Utf8JsonReaderTests.TryGet.Date.cs | 51 ++++++++++ 13 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 5a6753eb343..67a09866d74 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -903,6 +903,7 @@ namespace System.Text.Json.Serialization.Metadata public static System.Text.Json.Serialization.JsonConverter SByteConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter SingleConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter StringConverter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter TimeSpanConverter { get { throw null; } } [System.CLSCompliantAttribute(false)] public static System.Text.Json.Serialization.JsonConverter UInt16Converter { get { throw null; } } [System.CLSCompliantAttribute(false)] diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 9f73cb0ec29..c650f6ebad8 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -318,6 +318,9 @@ The JSON value is not in a supported DateTimeOffset format. + + The JSON value is not in a supported TimeSpan format. + The JSON value is not in a supported Guid format. diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 96038fb8d86..190cbd48534 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -165,6 +165,7 @@ + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs index a4dc832223d..7f05dfafc51 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs @@ -117,12 +117,6 @@ namespace System.Text.Json return IsInRangeInclusive(length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidDateTimeOffsetParseLength(long length) - { - return IsInRangeInclusive(length, JsonConstants.MinimumDateTimeParseLength, JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - } - /// /// Parse the given UTF-8 as extended ISO 8601 format. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs index 32947d9f090..0af169f2e1d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs @@ -1073,25 +1073,25 @@ namespace System.Text.Json { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(sequenceLength)) + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(ValueSpan.Length)) + if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; @@ -1141,25 +1141,25 @@ namespace System.Text.Json { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(sequenceLength)) + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedDateTimeOffsetParseLength : JsonConstants.MaximumDateTimeOffsetParseLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (!JsonHelpers.IsValidDateTimeOffsetParseLength(ValueSpan.Length)) + if (!JsonHelpers.IsInRangeInclusive(ValueSpan.Length, JsonConstants.MinimumDateTimeParseLength, maximumLength)) { value = default; return false; @@ -1210,24 +1210,25 @@ namespace System.Text.Json { ReadOnlySpan span = stackalloc byte[0]; + int maximumLength = _stringHasEscaping ? JsonConstants.MaximumEscapedGuidLength : JsonConstants.MaximumFormatGuidLength; + if (HasValueSequence) { long sequenceLength = ValueSequence.Length; - if (sequenceLength > JsonConstants.MaximumEscapedGuidLength) + if (sequenceLength > maximumLength) { value = default; return false; } Debug.Assert(sequenceLength <= JsonConstants.MaximumEscapedGuidLength); - Span stackSpan = stackalloc byte[(int)sequenceLength]; - + Span stackSpan = stackalloc byte[_stringHasEscaping ? JsonConstants.MaximumEscapedGuidLength : JsonConstants.MaximumFormatGuidLength]; ValueSequence.CopyTo(stackSpan); - span = stackSpan; + span = stackSpan.Slice(0, (int)sequenceLength); } else { - if (ValueSpan.Length > JsonConstants.MaximumEscapedGuidLength) + if (ValueSpan.Length > maximumLength) { value = default; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs new file mode 100644 index 00000000000..3767b7ff6d4 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Buffers.Text; +using System.Diagnostics; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class TimeSpanConverter : JsonConverter + { + private const int MinimumTimeSpanFormatLength = 8; // hh:mm:ss + private const int MaximumTimeSpanFormatLength = 26; // -dddddddd.hh:mm:ss.fffffff + private const int MaximumEscapedTimeSpanFormatLength = JsonConstants.MaxExpansionFactorWhileEscaping * MaximumTimeSpanFormatLength; + + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw ThrowHelper.GetInvalidOperationException_ExpectedString(reader.TokenType); + } + + bool isEscaped = reader._stringHasEscaping; + int maximumLength = isEscaped ? MaximumEscapedTimeSpanFormatLength : MaximumTimeSpanFormatLength; + + ReadOnlySpan source = stackalloc byte[0]; + + if (reader.HasValueSequence) + { + ReadOnlySequence valueSequence = reader.ValueSequence; + long sequenceLength = valueSequence.Length; + + if (!JsonHelpers.IsInRangeInclusive(sequenceLength, MinimumTimeSpanFormatLength, maximumLength)) + { + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + Span stackSpan = stackalloc byte[isEscaped ? MaximumEscapedTimeSpanFormatLength : MaximumTimeSpanFormatLength]; + valueSequence.CopyTo(stackSpan); + source = stackSpan.Slice(0, (int)sequenceLength); + } + else + { + source = reader.ValueSpan; + + if (!JsonHelpers.IsInRangeInclusive(source.Length, MinimumTimeSpanFormatLength, maximumLength)) + { + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + } + + if (isEscaped) + { + int backslash = source.IndexOf(JsonConstants.BackSlash); + Debug.Assert(backslash != -1); + + Span sourceUnescaped = stackalloc byte[source.Length]; + + JsonReaderHelper.Unescape(source, sourceUnescaped, backslash, out int written); + Debug.Assert(written > 0); + + source = sourceUnescaped.Slice(0, written); + Debug.Assert(!source.IsEmpty); + } + + byte firstChar = source[0]; + if (!JsonHelpers.IsDigit(firstChar) && firstChar != '-') + { + // Note: Utf8Parser.TryParse allows for leading whitespace so we + // need to exclude that case here. + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + bool result = Utf8Parser.TryParse(source, out TimeSpan tmpValue, out int bytesConsumed, 'c'); + + // Note: Utf8Parser.TryParse will return true for invalid input so + // long as it starts with an integer. Example: "2021-06-18" or + // "1$$$$$$$$$$". We need to check bytesConsumed to know if the + // entire source was actually valid. + + if (result && source.Length == bytesConsumed) + { + return tmpValue; + } + + throw ThrowHelper.GetFormatException(DataType.TimeSpan); + } + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + { + Span output = stackalloc byte[MaximumTimeSpanFormatLength]; + + bool result = Utf8Formatter.TryFormat(value, output, out int bytesWritten, 'c'); + Debug.Assert(result); + + writer.WriteStringValue(output.Slice(0, bytesWritten)); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index 95de3062e66..a5270028def 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -49,7 +49,7 @@ namespace System.Text.Json private static Dictionary GetDefaultSimpleConverters() { - const int NumberOfSimpleConverters = 23; + const int NumberOfSimpleConverters = 24; var converters = new Dictionary(NumberOfSimpleConverters); // Use a dictionary for simple converters. @@ -72,6 +72,7 @@ namespace System.Text.Json Add(JsonMetadataServices.SByteConverter); Add(JsonMetadataServices.SingleConverter); Add(JsonMetadataServices.StringConverter); + Add(JsonMetadataServices.TimeSpanConverter); Add(JsonMetadataServices.UInt16Converter); Add(JsonMetadataServices.UInt32Converter); Add(JsonMetadataServices.UInt64Converter); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs index 0be5273425c..f51bc851a0a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs @@ -110,6 +110,12 @@ namespace System.Text.Json.Serialization.Metadata public static JsonConverter StringConverter => s_stringConverter ??= new StringConverter(); private static JsonConverter? s_stringConverter; + /// + /// Returns a instance that converts values. + /// + public static JsonConverter TimeSpanConverter => s_timeSpanConverter ??= new TimeSpanConverter(); + private static JsonConverter? s_timeSpanConverter; + /// /// Returns a instance that converts values. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index 2b6b85878c9..96296fa7b2f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -636,6 +636,9 @@ namespace System.Text.Json case DataType.DateTimeOffset: message = SR.FormatDateTimeOffset; break; + case DataType.TimeSpan: + message = SR.FormatTimeSpan; + break; case DataType.Base64String: message = SR.CannotDecodeInvalidBase64; break; @@ -723,6 +726,7 @@ namespace System.Text.Json Boolean, DateTime, DateTimeOffset, + TimeSpan, Base64String, Guid, } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs index 799f2abd0b4..8d4efb5e3cb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs @@ -189,7 +189,6 @@ namespace System.Text.Json.Tests yield return new object[] { "\"1997-07-16T19:20:30.4555555+1400\"" }; yield return new object[] { "\"1997-07-16T19:20:30.4555555-1400\"" }; - // Proper format but invalid calendar date, time, or time zone designator fields yield return new object[] { "\"1997-00-16T19:20:30.4555555\"" }; yield return new object[] { "\"1997-07-16T25:20:30.4555555\"" }; @@ -215,6 +214,7 @@ namespace System.Text.Json.Tests yield return new object[] { "\"1997-07-16T19:20:30.45555555550000000\"" }; yield return new object[] { "\"1997-07-16T19:20:30.45555555555555555\"" }; yield return new object[] { "\"1997-07-16T19:20:30.45555555555555555555\"" }; + yield return new object[] { "\"1997-07-16T19:20:30.4555555555555555+01:300\"" }; // Hex strings diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs index b4a8d0db76e..75ad7d1e6ba 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Runtime.CompilerServices; +using Newtonsoft.Json; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -80,6 +81,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); + Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); Assert.Throws(() => JsonSerializer.Deserialize("\"abc\"")); @@ -118,6 +120,7 @@ namespace System.Text.Json.Serialization.Tests [InlineData(typeof(sbyte))] [InlineData(typeof(float))] [InlineData(typeof(string))] + [InlineData(typeof(TimeSpan))] [InlineData(typeof(ushort))] [InlineData(typeof(uint))] [InlineData(typeof(ulong))] @@ -303,6 +306,9 @@ namespace System.Text.Json.Serialization.Tests Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize(unexpectedString)); + Assert.Throws(() => JsonSerializer.Deserialize("1")); Assert.Throws(() => JsonSerializer.Deserialize("1")); @@ -442,5 +448,70 @@ namespace System.Text.Json.Serialization.Tests string str = JsonSerializer.Deserialize(json); Assert.True(json.AsSpan(1, json.Length - 2).SequenceEqual(str.AsSpan())); } + + [Theory] + [InlineData("23:59:59")] + [InlineData("\\u002D23:59:59", "-23:59:59")] + [InlineData("\\u0032\\u0033\\u003A\\u0035\\u0039\\u003A\\u0035\\u0039", "23:59:59")] + [InlineData("23:59:59.9", "23:59:59.9000000")] + [InlineData("23:59:59.9999999")] + [InlineData("9999999.23:59:59.9999999")] + [InlineData("-9999999.23:59:59.9999999")] + [InlineData("10675199.02:48:05.4775807")] // TimeSpan.MaxValue + [InlineData("-10675199.02:48:05.4775808")] // TimeSpan.MinValue + public static void TimeSpan_Read_Success(string json, string? actual = null) + { + TimeSpan value = JsonSerializer.Deserialize($"\"{json}\""); + + Assert.Equal(TimeSpan.Parse(actual ?? json), value); + Assert.Equal(value, JsonConvert.DeserializeObject($"\"{json}\"")); + } + + [Fact] + public static void TimeSpan_Read_KnownDifferences() + { + string value = "24:00:00"; + + // 24:00:00 should be invalid because hours can only be up to 23. + Assert.Throws(() => JsonSerializer.Deserialize($"\"{value}\"")); + + TimeSpan expectedValue = TimeSpan.Parse("24.00:00:00"); + + // TimeSpan.Parse has a quirk where it treats 24:00:00 as 24.00:00:00. + Assert.Equal(expectedValue, TimeSpan.Parse(value)); + + // Newtonsoft uses TimeSpan.Parse so it is subject to the quirk. + Assert.Equal(expectedValue, JsonConvert.DeserializeObject($"\"{value}\"")); + } + + [Theory] + [InlineData("\t23:59:59")] // Otherwise valid but has invalid json character + [InlineData("\\t23:59:59")] // Otherwise valid but has leading whitespace + [InlineData("23:59:59 ")] // Otherwise valid but has trailing whitespace + [InlineData("24:00:00")] + [InlineData("\\u0032\\u0034\\u003A\\u0030\\u0030\\u003A\\u0030\\u0030")] + [InlineData("00:60:00")] + [InlineData("00:00:60")] + [InlineData("00:00:00.00000009")] + [InlineData("900000000.00:00:00")] + [InlineData("1:00:00")] // 'g' Format + [InlineData("1:2:00:00")] // 'g' Format + [InlineData("+00:00:00")] + [InlineData("2021-06-18")] + [InlineData("1$")] + [InlineData("10675199.02:48:05.4775808")] // TimeSpan.MaxValue + 1 + [InlineData("-10675199.02:48:05.4775809")] // TimeSpan.MinValue - 1 + [InlineData("1234", false)] + [InlineData("{}", false)] + [InlineData("[]", false)] + [InlineData("true", false)] + [InlineData("null", false)] + public static void TimeSpan_Read_Failure(string json, bool addQuotes = true) + { + if (addQuotes) + json = $"\"{json}\""; + + Assert.Throws(() => JsonSerializer.Deserialize(json)); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs index 210cc6fbde5..f3e6bd8d3a4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.WriteTests.cs @@ -112,5 +112,24 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(@"""1.2.3.4""", JsonSerializer.Serialize(version)); } } + + [Theory] + [InlineData("1:59:59", "01:59:59")] + [InlineData("23:59:59")] + [InlineData("23:59:59.9", "23:59:59.9000000")] + [InlineData("23:59:59.9999999")] + [InlineData("1.23:59:59")] + [InlineData("9999999.23:59:59.9999999")] + [InlineData("-9999999.23:59:59.9999999")] + [InlineData("10675199.02:48:05.4775807")] // TimeSpan.MaxValue + [InlineData("-10675199.02:48:05.4775808")] // TimeSpan.MinValue + public static void TimeSpan_Write_Success(string value, string? expectedValue = null) + { + TimeSpan ts = TimeSpan.Parse(value); + string json = JsonSerializer.Serialize(ts); + + Assert.Equal($"\"{expectedValue ?? value}\"", json); + Assert.Equal(json, JsonConvert.SerializeObject(ts)); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs index 1df529baf95..bd4cbdb864e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Globalization; using Xunit; @@ -201,5 +202,55 @@ namespace System.Text.Json.Tests // Test upstream serializer. Assert.Equal(DateTime.Parse(expectedString), JsonSerializer.Deserialize(jsonString)); } + + [Theory] + [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] + public static void TryGetDateTime_HasValueSequence_False(string testString) + { + static void test(string testString, bool isFinalBlock) + { + byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); + ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); + var json = new Utf8JsonReader(sequence, isFinalBlock: isFinalBlock, state: default); + + Assert.True(json.Read(), "json.Read()"); + Assert.Equal(JsonTokenType.String, json.TokenType); + Assert.True(json.HasValueSequence, "json.HasValueSequence"); + // If the string is empty, the ValueSequence is empty, because it contains all 0 bytes between the two characters + Assert.Equal(string.IsNullOrEmpty(testString), json.ValueSequence.IsEmpty); + Assert.False(json.TryGetDateTime(out DateTime actual), "json.TryGetDateTime(out DateTime actual)"); + Assert.Equal(DateTime.MinValue, actual); + + JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTime()); + } + + test(testString, isFinalBlock: true); + test(testString, isFinalBlock: false); + } + + [Theory] + [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] + public static void TryGetDateTimeOffset_HasValueSequence_False(string testString) + { + static void test(string testString, bool isFinalBlock) + { + byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); + ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); + var json = new Utf8JsonReader(sequence, isFinalBlock: isFinalBlock, state: default); + + Assert.True(json.Read(), "json.Read()"); + Assert.Equal(JsonTokenType.String, json.TokenType); + Assert.True(json.HasValueSequence, "json.HasValueSequence"); + // If the string is empty, the ValueSequence is empty, because it contains all 0 bytes between the two characters + Assert.Equal(string.IsNullOrEmpty(testString), json.ValueSequence.IsEmpty); + Assert.False(json.TryGetDateTimeOffset(out DateTimeOffset actual), "json.TryGetDateTimeOffset(out DateTimeOffset actual)"); + Assert.Equal(DateTimeOffset.MinValue, actual); + + JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTimeOffset()); + } + + test(testString, isFinalBlock: true); + test(testString, isFinalBlock: false); + } } } From 164ce9db5abd9c2dfc8264823b1192aafdd0e6e2 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 7 Jul 2021 13:20:48 -0400 Subject: [PATCH 311/926] Fix a couple more unnecessary spilled locals (#55263) --- .../System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs | 2 +- .../System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 4b00bb31f65..63427bb96b5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -173,7 +173,7 @@ namespace System.Net.Http // See Http2Connection.SendAsync for a full comment on this logic -- it is identical behavior. if (sendContentTask.IsCompleted || _request.Content?.AllowDuplex != true || - sendContentTask == await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) || + await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == sendContentTask || sendContentTask.IsCompleted) { try diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index e7db1ec5851..31936b78a3d 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -1933,7 +1933,7 @@ namespace System.Net.Sockets { if (e.MemoryBuffer != null) { - bytesTransferred += await socket.SendAsync(e.MemoryBuffer.Value, SocketFlags.None, cancellationToken).ConfigureAwait(false); + bytesTransferred = await socket.SendAsync(e.MemoryBuffer.Value, SocketFlags.None, cancellationToken).ConfigureAwait(false) + bytesTransferred; } else { From 847cc5645dbe1644f76d72009893a6a86a4e0405 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 7 Jul 2021 14:12:12 -0400 Subject: [PATCH 312/926] Implement CBC one shots on SymmetricAlgorithm --- .../AES/AesCipherOneShotTests.cs | 574 ++++++++++++++++ .../AES/AesCipherTests.OneShot.cs | 638 ------------------ .../AES/AesCipherTests.cs | 6 + .../DES/DESCipherOneShotTests.cs | 559 +++++++++++++++ .../DES/DESCipherTests.OneShot.cs | 624 ----------------- .../DES/DESCipherTests.cs | 25 + .../RC2/RC2CipherOneShotTests.cs | 584 ++++++++++++++++ .../RC2/RC2CipherTests.OneShot.cs | 627 ----------------- .../RC2/RC2CipherTests.cs | 21 + .../RC2/RC2Factory.cs | 3 + .../Symmetric/SymmetricOneShotBase.cs | 414 ++++++++++++ .../TripleDES/TripleDESCipherOneShotTests.cs | 563 ++++++++++++++++ .../TripleDES/TripleDESCipherTests.OneShot.cs | 633 ----------------- .../TripleDES/TripleDESCipherTests.cs | 25 + .../Cryptography/AesImplementation.cs | 46 ++ .../Cryptography/DesImplementation.cs | 46 ++ .../Cryptography/RC2Implementation.cs | 56 ++ .../Cryptography/TripleDesImplementation.cs | 46 ++ .../tests/RC2Provider.cs | 2 + ...urity.Cryptography.Algorithms.Tests.csproj | 18 +- .../System/Security/Cryptography/AesCng.cs | 38 ++ .../Security/Cryptography/TripleDESCng.cs | 38 ++ .../tests/RC2Provider.cs | 2 + .../tests/SymmetricCngTestHelpers.cs | 6 + ...tem.Security.Cryptography.Cng.Tests.csproj | 8 +- .../tests/RC2CryptoServiceProviderProvider.cs | 2 + .../tests/ShimHelpers.cs | 2 + .../tests/RC2Provider.cs | 2 + ...System.Security.Cryptography.Primitives.cs | 10 + .../Cryptography/SymmetricAlgorithm.cs | 379 ++++++++++- .../tests/SymmetricAlgorithmTests.cs | 284 +++++++- 31 files changed, 3735 insertions(+), 2546 deletions(-) create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs delete mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs delete mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs delete mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs delete mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs new file mode 100644 index 00000000000..77d5e0cf778 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs @@ -0,0 +1,574 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security.Cryptography; + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Encryption.Aes.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + public class AesCipherOneShotTests : SymmetricOneShotBase + { + protected override byte[] Key => + new byte[] { 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12 }; + + protected override byte[] IV => + new byte[] { 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22 }; + + protected override SymmetricAlgorithm CreateAlgorithm() => AesFactory.Create(); + + [Theory] + [MemberData(nameof(TestCases))] + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + public static IEnumerable TestCases + { + get + { + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2E, 0x35, 0xFF, 0xFB, 0x08, 0x56, 0xE9, 0x21, + 0x83, 0xF8, 0x8A, 0xF1, 0x79, 0xC0, 0x2A, 0xD4, + 0x07, 0xBC, 0x83, 0x1E, 0x5B, 0x12, 0x48, 0x1F, + 0x9E, 0x91, 0xD5, 0x44, 0xA0, 0x85, 0x0B, 0x19, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2E, 0x35, 0xFF, 0xFB, 0x08, 0x56, 0xE9, 0x21, + 0x83, 0xF8, 0x8A, 0xF1, 0x79, 0xC0, 0x2A, 0xD4, + }, + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2E, 0x35, 0xFF, 0xFB, 0x08, 0x56, 0xE9, 0x21, + 0x83, 0xF8, 0x8A, 0xF1, 0x79, 0xC0, 0x2A, 0xD4, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2E, 0x35, 0xFF, 0xFB, 0x08, 0x56, 0xE9, 0x21, + 0x83, 0xF8, 0x8A, 0xF1, 0x79, 0xC0, 0x2A, 0xD4, + 0x63, 0xB1, 0x3D, 0x28, 0xD7, 0xD1, 0x1E, 0x2E, + 0x09, 0x40, 0xA1, 0xF0, 0xFD, 0xE3, 0xF1, 0x03, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2E, 0x35, 0xFF, 0xFB, 0x08, 0x56, 0xE9, 0x21, + 0x83, 0xF8, 0x8A, 0xF1, 0x79, 0xC0, 0x2A, 0xD4, + 0xAC, 0xBB, 0xEC, 0xDF, 0x53, 0x6B, 0x9A, 0x34, + 0x0F, 0x03, 0x58, 0x00, 0x2D, 0x86, 0x13, 0xC9, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1B, 0x98, 0x27, 0x53, 0xC2, 0xCA, 0x0B, 0xF9, + 0x60, 0x32, 0xD8, 0x07, 0x16, 0x28, 0xCB, 0xEB, + 0x12, 0x9A, 0xC3, 0xC8, 0x9C, 0x14, 0xCD, 0x37, + 0xA2, 0x43, 0x65, 0x14, 0xC7, 0xDC, 0x17, 0xFD, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1B, 0x98, 0x27, 0x53, 0xC2, 0xCA, 0x0B, 0xF9, + 0x60, 0x32, 0xD8, 0x07, 0x16, 0x28, 0xCB, 0xEB, + 0x31, 0xD7, 0xED, 0x14, 0x2B, 0x5D, 0x76, 0x3A, + 0x35, 0xFD, 0x3C, 0x56, 0xD0, 0xF1, 0x16, 0xB3, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1B, 0x98, 0x27, 0x53, 0xC2, 0xCA, 0x0B, 0xF9, + 0x60, 0x32, 0xD8, 0x07, 0x16, 0x28, 0xCB, 0xEB, + 0x13, 0xCC, 0xD3, 0x3E, 0xE4, 0xE0, 0x3A, 0x04, + 0x6F, 0xA3, 0xD6, 0xCB, 0x98, 0xBD, 0x47, 0x59, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1B, 0x98, 0x27, 0x53, 0xC2, 0xCA, 0x0B, 0xF9, + 0x60, 0x32, 0xD8, 0x07, 0x16, 0x28, 0xCB, 0xEB, + 0xA8, 0xB6, 0xE2, 0x45, 0x01, 0xF2, 0xA0, 0x3A, + 0xA6, 0x57, 0x69, 0x4F, 0xF9, 0x8B, 0xB9, 0x40, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0xF0, 0x32, 0xE1, 0x6F, 0x32, 0x7C, 0x4C, 0xB4, + 0xC9, 0x99, 0xF2, 0x10, 0x5C, 0xE9, 0xED, 0x70, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + // ECB test cases + // plaintext requires no padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, + 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + }, + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0xC1, 0xCA, 0x44, 0xE8, 0x05, 0xFF, 0xCB, 0x6F, + 0x4D, 0x7F, 0xE9, 0x17, 0x12, 0xFE, 0xBB, 0xAC, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, + 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, + 0xD3, 0xAA, 0x33, 0x5B, 0x93, 0xC2, 0x3D, 0x96, + 0xFD, 0x89, 0xB1, 0x8C, 0x47, 0x75, 0x65, 0xA8, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x82, 0x8D, 0x60, 0xDC, 0x44, 0x26, 0xCF, 0xDE, + 0xC9, 0x54, 0x33, 0x47, 0xE2, 0x9E, 0xF0, 0x8C, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x49, 0x39, 0x1B, 0x69, 0xA1, 0xF3, 0x66, 0xE4, + 0x3E, 0x40, 0x51, 0xB8, 0x05, 0x60, 0xDC, 0xFD, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0xCD, 0x0D, 0xCD, 0xEA, 0xA2, 0x1F, 0xC1, 0xC3, + 0x81, 0xEE, 0x8A, 0x63, 0x94, 0x5F, 0x85, 0x43, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, + 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, + 0x9C, 0xE4, 0x0D, 0x2F, 0xCD, 0x82, 0x25, 0x0E, + 0x13, 0xAB, 0x4B, 0x6B, 0xC0, 0x9A, 0x21, 0x2E, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, + 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs deleted file mode 100644 index fd82160f1a7..00000000000 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.OneShot.cs +++ /dev/null @@ -1,638 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Security.Cryptography; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Encryption.Aes.Tests -{ - using Aes = System.Security.Cryptography.Aes; - - public partial class AesCipherTests - { - private static byte[] s_aes128OneShotKey = - new byte[] { 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12 }; - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - // Even though we have set the instance to use CFB, the Ecb one shots should - // always be done in ECB. - aes.FeedbackSize = 8; - aes.Mode = CipherMode.CFB; - aes.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; - - byte[] encrypted = aes.EncryptEcb(plaintext, padding); - byte[] decrypted = aes.DecryptEcb(encrypted, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted[..plaintext.Length]); - AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - - decrypted = aes.DecryptEcb(ciphertext, padding); - encrypted = aes.EncryptEcb(decrypted, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (plaintext.Length == 0) - { - // Can't have a ciphertext length shorter than zero. - return; - } - - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - Span destinationBuffer = new byte[plaintext.Length - 1]; - - bool result = aes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (ciphertext.Length == 0) - { - // Can't have a too small buffer for zero. - return; - } - - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - Span destinationBuffer = new byte[ciphertext.Length - 1]; - - bool result = aes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - Span destinationBuffer = new byte[expectedPlaintextSize]; - - bool result = aes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - int expectedCiphertextSize = aes.GetCiphertextLengthEcb(plaintext.Length, padding); - Span destinationBuffer = new byte[expectedCiphertextSize]; - - bool result = aes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(expectedCiphertextSize, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - // Padding is random so we can't validate the last block. - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - - Span largeBuffer = new byte[expectedPlaintextSize + 10]; - Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); - largeBuffer.Fill(0xCC); - - bool result = aes.TryDecryptEcb( - ciphertext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - - Span excess = largeBuffer.Slice(destinationBuffer.Length); - AssertFilledWith(0xCC, excess); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - Span largeBuffer = new byte[ciphertext.Length + 10]; - Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); - largeBuffer.Fill(0xCC); - - bool result = aes.TryEncryptEcb( - plaintext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - - AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); - Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - ciphertext.AsSpan().CopyTo(ciphertextBuffer); - - bool result = aes.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - - int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); - plaintext.AsSpan().CopyTo(plaintextBuffer); - - bool result = aes.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - byte[] decrypted = aes.DecryptEcb(ciphertext.AsSpan(), padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - byte[] encrypted = aes.EncryptEcb(plaintext.AsSpan(), padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - byte[] decrypted = aes.DecryptEcb(ciphertext, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (Aes aes = AesFactory.Create()) - { - aes.Key = s_aes128OneShotKey; - byte[] encrypted = aes.EncryptEcb(plaintext, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = aes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - private static void AssertFilledWith(byte value, ReadOnlySpan span) - { - for (int i = 0; i < span.Length; i++) - { - Assert.Equal(value, span[i]); - } - } - - public static IEnumerable EcbTestCases - { - get - { - // plaintext requires no padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, - 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, - 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, - 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, - 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, - }, - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, - 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, - 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, - 0xC1, 0xCA, 0x44, 0xE8, 0x05, 0xFF, 0xCB, 0x6F, - 0x4D, 0x7F, 0xE9, 0x17, 0x12, 0xFE, 0xBB, 0xAC, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xD8, 0xF5, 0x32, 0x53, 0x82, 0x89, 0xEF, 0x7D, - 0x06, 0xB5, 0x06, 0xA4, 0xFD, 0x5B, 0xE9, 0xC9, - 0xD3, 0xAA, 0x33, 0x5B, 0x93, 0xC2, 0x3D, 0x96, - 0xFD, 0x89, 0xB1, 0x8C, 0x47, 0x75, 0x65, 0xA8, - }, - - PaddingMode.ISO10126, - }; - - // plaintext requires padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, - 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, - 0x82, 0x8D, 0x60, 0xDC, 0x44, 0x26, 0xCF, 0xDE, - 0xC9, 0x54, 0x33, 0x47, 0xE2, 0x9E, 0xF0, 0x8C, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, - 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, - 0x49, 0x39, 0x1B, 0x69, 0xA1, 0xF3, 0x66, 0xE4, - 0x3E, 0x40, 0x51, 0xB8, 0x05, 0x60, 0xDC, 0xFD, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, - 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, - 0xCD, 0x0D, 0xCD, 0xEA, 0xA2, 0x1F, 0xC1, 0xC3, - 0x81, 0xEE, 0x8A, 0x63, 0x94, 0x5F, 0x85, 0x43, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xC3, 0x03, 0x87, 0xCD, 0x79, 0x19, 0xB1, 0xC3, - 0x50, 0x2C, 0x9D, 0x7B, 0x1F, 0x8A, 0xBE, 0x0F, - 0x9C, 0xE4, 0x0D, 0x2F, 0xCD, 0x82, 0x25, 0x0E, - 0x13, 0xAB, 0x4B, 0x6B, 0xC0, 0x9A, 0x21, 0x2E, - }, - - PaddingMode.ISO10126, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - new byte[] - { - 0x6D, 0xE5, 0xF6, 0x07, 0xAB, 0x7E, 0xB8, 0x20, - 0x2F, 0x39, 0x57, 0x70, 0x3B, 0x04, 0xE8, 0xB5, - }, - - PaddingMode.PKCS7, - }; - } - } - } -} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs index 2d7e23749d4..465958943f8 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs @@ -1139,6 +1139,12 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests liveOneShotDecryptBytes = aes.DecryptEcb(cipherBytes, paddingMode); liveOneShotEncryptBytes = aes.EncryptEcb(plainBytes, paddingMode); } + else if (cipherMode == CipherMode.CBC) + { + aes.Key = key; + liveOneShotDecryptBytes = aes.DecryptCbc(cipherBytes, iv, paddingMode); + liveOneShotEncryptBytes = aes.EncryptCbc(plainBytes, iv, paddingMode); + } } Assert.Equal(cipherBytes, liveEncryptBytes); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs new file mode 100644 index 00000000000..d775ffb9f12 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs @@ -0,0 +1,559 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security.Cryptography; + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Encryption.Des.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + public class DesCipherOneShotTests : SymmetricOneShotBase + { + protected override byte[] Key => new byte[] + { + 0x74, 0x4B, 0x93, 0x3A, 0x96, 0x33, 0x61, 0xD6 + }; + + protected override byte[] IV => new byte[] + { + 0x01, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + }; + + protected override SymmetricAlgorithm CreateAlgorithm() => DESFactory.Create(); + + [Theory] + [MemberData(nameof(TestCases))] + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + public static IEnumerable TestCases + { + get + { + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x29, 0x30, 0x13, 0x97, 0xCD, 0x5E, 0x30, 0x2C, + 0xED, 0x11, 0x65, 0xA8, 0xF3, 0xA0, 0x11, 0x42, + 0xD0, 0x53, 0x1B, 0xB2, 0x55, 0xC0, 0x65, 0x8D, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x29, 0x30, 0x13, 0x97, 0xCD, 0x5E, 0x30, 0x2C, + 0xED, 0x11, 0x65, 0xA8, 0xF3, 0xA0, 0x11, 0x42, + }, + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x29, 0x30, 0x13, 0x97, 0xCD, 0x5E, 0x30, 0x2C, + 0xED, 0x11, 0x65, 0xA8, 0xF3, 0xA0, 0x11, 0x42, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x29, 0x30, 0x13, 0x97, 0xCD, 0x5E, 0x30, 0x2C, + 0xED, 0x11, 0x65, 0xA8, 0xF3, 0xA0, 0x11, 0x42, + 0x26, 0xA4, 0x0F, 0x7F, 0x69, 0x53, 0xEE, 0xF1, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x29, 0x30, 0x13, 0x97, 0xCD, 0x5E, 0x30, 0x2C, + 0xED, 0x11, 0x65, 0xA8, 0xF3, 0xA0, 0x11, 0x42, + 0x4F, 0xE1, 0xDF, 0x40, 0xE8, 0x30, 0x80, 0xB3, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x57, 0xB9, 0x91, 0xCF, 0x46, 0x03, 0x2B, 0x45, + 0x2A, 0x59, 0x19, 0xB3, 0x97, 0x7A, 0xC7, 0x73, + 0x69, 0xD7, 0xBD, 0x06, 0x3A, 0x3B, 0x40, 0x87, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x57, 0xB9, 0x91, 0xCF, 0x46, 0x03, 0x2B, 0x45, + 0x2A, 0x59, 0x19, 0xB3, 0x97, 0x7A, 0xC7, 0x73, + 0x71, 0xB4, 0x8D, 0x6C, 0xDD, 0x98, 0x83, 0x55, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x57, 0xB9, 0x91, 0xCF, 0x46, 0x03, 0x2B, 0x45, + 0x2A, 0x59, 0x19, 0xB3, 0x97, 0x7A, 0xC7, 0x73, + 0xAE, 0xF8, 0x98, 0x2C, 0xB3, 0x71, 0x9F, 0xDF, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x57, 0xB9, 0x91, 0xCF, 0x46, 0x03, 0x2B, 0x45, + 0x2A, 0x59, 0x19, 0xB3, 0x97, 0x7A, 0xC7, 0x73, + 0x5B, 0xFE, 0xD3, 0x3F, 0xDA, 0xBC, 0x15, 0x29, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x49, 0xD5, 0xE1, 0x5E, 0x17, 0x12, 0x23, 0xCF + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + }, + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0xEC, 0x52, 0xA1, 0x7E, 0x52, 0x54, 0x6E, 0x9E, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, + 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, + 0x44, 0x4C, 0xA5, 0xC2, 0xCC, 0x54, 0xAC, 0xF9, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0x60, 0x8E, 0xC3, 0xB8, 0x09, 0x84, 0xCF, 0x3B, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0xE7, 0xA4, 0x10, 0xF1, 0x7B, 0xFF, 0x32, 0x4A, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0x92, 0x9A, 0x36, 0xFE, 0xA4, 0xB3, 0xEC, 0xA0, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, + 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, + 0xDB, 0x86, 0xA4, 0xAB, 0xDE, 0x05, 0xE4, 0xE7, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs deleted file mode 100644 index 6f5106816bc..00000000000 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.OneShot.cs +++ /dev/null @@ -1,624 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Security.Cryptography; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Encryption.Des.Tests -{ - public partial class DesCipherTests - { - private static byte[] s_desOneShotKey = new byte[] - { - 0x74, 0x4B, 0x93, 0x3A, 0x96, 0x33, 0x61, 0xD6 - }; - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - byte[] encrypted = des.EncryptEcb(plaintext, padding); - byte[] decrypted = des.DecryptEcb(encrypted, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted[..plaintext.Length]); - AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - - decrypted = des.DecryptEcb(ciphertext, padding); - encrypted = des.EncryptEcb(decrypted, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (plaintext.Length == 0) - { - // Can't have a ciphertext length shorter than zero. - return; - } - - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - Span destinationBuffer = new byte[plaintext.Length - 1]; - - bool result = des.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (ciphertext.Length == 0) - { - // Can't have a too small buffer for zero. - return; - } - - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - Span destinationBuffer = new byte[ciphertext.Length - 1]; - - bool result = des.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - Span destinationBuffer = new byte[expectedPlaintextSize]; - - bool result = des.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - int expectedCiphertextSize = des.GetCiphertextLengthEcb(plaintext.Length, padding); - Span destinationBuffer = new byte[expectedCiphertextSize]; - - bool result = des.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(expectedCiphertextSize, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - // Padding is random so we can't validate the last block. - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - - Span largeBuffer = new byte[expectedPlaintextSize + 10]; - Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); - largeBuffer.Fill(0xCC); - - bool result = des.TryDecryptEcb( - ciphertext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - - Span excess = largeBuffer.Slice(destinationBuffer.Length); - AssertFilledWith(0xCC, excess); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - Span largeBuffer = new byte[ciphertext.Length + 10]; - Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); - largeBuffer.Fill(0xCC); - - bool result = des.TryEncryptEcb( - plaintext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - - AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); - Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - ciphertext.AsSpan().CopyTo(ciphertextBuffer); - - bool result = des.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - - int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); - plaintext.AsSpan().CopyTo(plaintextBuffer); - - bool result = des.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - byte[] decrypted = des.DecryptEcb(ciphertext.AsSpan(), padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - byte[] encrypted = des.EncryptEcb(plaintext.AsSpan(), padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - byte[] decrypted = des.DecryptEcb(ciphertext, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (DES des = DESFactory.Create()) - { - des.Key = s_desOneShotKey; - byte[] encrypted = des.EncryptEcb(plaintext, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = des.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - private static void AssertFilledWith(byte value, ReadOnlySpan span) - { - for (int i = 0; i < span.Length; i++) - { - Assert.Equal(value, span[i]); - } - } - - public static IEnumerable EcbTestCases - { - get - { - // plaintext requires no padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, - 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, - 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, - 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, - }, - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, - 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, - 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, - 0xEC, 0x52, 0xA1, 0x7E, 0x52, 0x54, 0x6E, 0x9E, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0xEE, 0x8B, 0xA7, 0xEE, 0x11, 0x84, 0x1D, 0xA2, - 0xC4, 0x16, 0xB4, 0x05, 0x83, 0xA0, 0x60, 0x37, - 0x44, 0x4C, 0xA5, 0xC2, 0xCC, 0x54, 0xAC, 0xF9, - }, - - PaddingMode.ISO10126, - }; - - // plaintext requires padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, - 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, - 0x60, 0x8E, 0xC3, 0xB8, 0x09, 0x84, 0xCF, 0x3B, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, - 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, - 0xE7, 0xA4, 0x10, 0xF1, 0x7B, 0xFF, 0x32, 0x4A, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, - 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, - 0x92, 0x9A, 0x36, 0xFE, 0xA4, 0xB3, 0xEC, 0xA0, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xEA, 0x91, 0x68, 0xFE, 0x02, 0xFE, 0x57, 0x6F, - 0x60, 0x17, 0x05, 0xD5, 0x94, 0xA2, 0xF8, 0xE2, - 0xDB, 0x86, 0xA4, 0xAB, 0xDE, 0x05, 0xE4, 0xE7, - }, - - PaddingMode.ISO10126, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - new byte[] - { - 0xED, 0xD9, 0xE3, 0xFC, 0xC6, 0x55, 0xDC, 0x32, - }, - - PaddingMode.PKCS7, - }; - } - } - } -} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs index dae50482fc0..d23a48a2851 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs @@ -524,6 +524,8 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { byte[] liveEncryptBytes; byte[] liveDecryptBytes; + byte[] liveOneShotDecryptBytes = null; + byte[] liveOneShotEncryptBytes = null; using (DES des = DESFactory.Create()) { @@ -537,10 +539,33 @@ namespace System.Security.Cryptography.Encryption.Des.Tests liveEncryptBytes = DESEncryptDirectKey(des, key, iv, plainBytes); liveDecryptBytes = DESDecryptDirectKey(des, key, iv, cipherBytes); + + if (cipherMode == CipherMode.ECB) + { + des.Key = key; + liveOneShotDecryptBytes = des.DecryptEcb(cipherBytes, paddingMode); + liveOneShotEncryptBytes = des.EncryptEcb(plainBytes, paddingMode); + } + else if (cipherMode == CipherMode.CBC) + { + des.Key = key; + liveOneShotDecryptBytes = des.DecryptCbc(cipherBytes, iv, paddingMode); + liveOneShotEncryptBytes = des.EncryptCbc(plainBytes, iv, paddingMode); + } } Assert.Equal(cipherBytes, liveEncryptBytes); Assert.Equal(plainBytes, liveDecryptBytes); + + if (liveOneShotDecryptBytes is not null) + { + Assert.Equal(plainBytes, liveOneShotDecryptBytes); + } + + if (liveOneShotEncryptBytes is not null) + { + Assert.Equal(cipherBytes, liveOneShotEncryptBytes); + } } private static byte[] DESEncryptDirectKey(DES des, byte[] key, byte[] iv, byte[] plainBytes) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs new file mode 100644 index 00000000000..411aadaf347 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs @@ -0,0 +1,584 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security.Cryptography; + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Encryption.RC2.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + public class RC2CipherOneShotTests : SymmetricOneShotBase + { + protected override byte[] Key => new byte[] + { + 0x83, 0x2F, 0x81, 0x1B, 0x61, 0x02, 0xCC, 0x8F, + 0x2F, 0x78, 0x10, 0x68, 0x06, 0xA6, 0x35, 0x50, + }; + + protected override byte[] IV => new byte[] + { + 0x01, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + }; + + protected override SymmetricAlgorithm CreateAlgorithm() => RC2Factory.Create(); + + [Theory] + [MemberData(nameof(TestCases))] + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + public static IEnumerable TestCases + { + get + { + // plaintext that is block aligned + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x9E, 0x33, 0x06, 0xDE, 0x21, 0xA7, 0xC6, 0x6C, + 0x59, 0x21, 0xEE, 0x34, 0x9F, 0x28, 0x1D, 0x0F, + 0xED, 0xFC, 0x8E, 0xD8, 0x85, 0x94, 0xC9, 0x20, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x9E, 0x33, 0x06, 0xDE, 0x21, 0xA7, 0xC6, 0x6C, + 0x59, 0x21, 0xEE, 0x34, 0x9F, 0x28, 0x1D, 0x0F, + }, + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x9E, 0x33, 0x06, 0xDE, 0x21, 0xA7, 0xC6, 0x6C, + 0x59, 0x21, 0xEE, 0x34, 0x9F, 0x28, 0x1D, 0x0F, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x9E, 0x33, 0x06, 0xDE, 0x21, 0xA7, 0xC6, 0x6C, + 0x59, 0x21, 0xEE, 0x34, 0x9F, 0x28, 0x1D, 0x0F, + 0xF7, 0x6D, 0x66, 0xD5, 0x32, 0xA3, 0x1F, 0xB1, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x9E, 0x33, 0x06, 0xDE, 0x21, 0xA7, 0xC6, 0x6C, + 0x59, 0x21, 0xEE, 0x34, 0x9F, 0x28, 0x1D, 0x0F, + 0x0E, 0x76, 0x86, 0x35, 0x65, 0x52, 0x48, 0x07, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1E, 0xEC, 0x97, 0x05, 0xAC, 0xAB, 0x92, 0x13, + 0x69, 0x53, 0x97, 0x00, 0x00, 0xD0, 0x1E, 0x3D, + 0xA9, 0x9E, 0xA2, 0x1D, 0x16, 0x8E, 0xCF, 0x1A, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1E, 0xEC, 0x97, 0x05, 0xAC, 0xAB, 0x92, 0x13, + 0x69, 0x53, 0x97, 0x00, 0x00, 0xD0, 0x1E, 0x3D, + 0x27, 0xF6, 0x57, 0x70, 0x65, 0x1D, 0x58, 0x87, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1E, 0xEC, 0x97, 0x05, 0xAC, 0xAB, 0x92, 0x13, + 0x69, 0x53, 0x97, 0x00, 0x00, 0xD0, 0x1E, 0x3D, + 0x2F, 0xDD, 0xAC, 0xE4, 0xB0, 0x01, 0x4D, 0x75, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x1E, 0xEC, 0x97, 0x05, 0xAC, 0xAB, 0x92, 0x13, + 0x69, 0x53, 0x97, 0x00, 0x00, 0xD0, 0x1E, 0x3D, + 0x01, 0x52, 0x7A, 0x20, 0x22, 0x94, 0xD4, 0x3D, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x60, 0x4C, 0x99, 0xAF, 0xA4, 0xE1, 0xB0, 0x76, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + // plaintext that is block aligned + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + // plaintext that is block aligned + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + }, + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0x72, 0x8A, 0x57, 0x94, 0x2D, 0x79, 0xBD, 0xAA, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, + 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, + 0xEB, 0x5E, 0x2E, 0xB9, 0x1A, 0x1E, 0x1B, 0xE4, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0xF2, 0x94, 0x11, 0xA3, 0xE8, 0xAD, 0xA7, 0xE6, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0xE3, 0xB2, 0x3D, 0xAA, 0x91, 0x6A, 0xD0, 0x06, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0x17, 0x97, 0x3A, 0x77, 0x69, 0x5E, 0x79, 0xE9, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, + 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, + 0x22, 0xC0, 0x50, 0x52, 0x56, 0x5A, 0x15, 0xFD, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs deleted file mode 100644 index 4a22728224d..00000000000 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.OneShot.cs +++ /dev/null @@ -1,627 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Security.Cryptography; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Encryption.RC2.Tests -{ - using RC2 = System.Security.Cryptography.RC2; - - public partial class RC2CipherTests - { - private static byte[] s_rc2OneShotKey = new byte[] - { - 0x83, 0x2F, 0x81, 0x1B, 0x61, 0x02, 0xCC, 0x8F, - 0x2F, 0x78, 0x10, 0x68, 0x06, 0xA6, 0x35, 0x50, - }; - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - byte[] encrypted = rc2.EncryptEcb(plaintext, padding); - byte[] decrypted = rc2.DecryptEcb(encrypted, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted[..plaintext.Length]); - AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - - decrypted = rc2.DecryptEcb(ciphertext, padding); - encrypted = rc2.EncryptEcb(decrypted, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (plaintext.Length == 0) - { - // Can't have a ciphertext length shorter than zero. - return; - } - - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - Span destinationBuffer = new byte[plaintext.Length - 1]; - - bool result = rc2.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (ciphertext.Length == 0) - { - // Can't have a too small buffer for zero. - return; - } - - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - Span destinationBuffer = new byte[ciphertext.Length - 1]; - - bool result = rc2.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - Span destinationBuffer = new byte[expectedPlaintextSize]; - - bool result = rc2.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - int expectedCiphertextSize = rc2.GetCiphertextLengthEcb(plaintext.Length, padding); - Span destinationBuffer = new byte[expectedCiphertextSize]; - - bool result = rc2.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(expectedCiphertextSize, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - // Padding is random so we can't validate the last block. - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - - Span largeBuffer = new byte[expectedPlaintextSize + 10]; - Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); - largeBuffer.Fill(0xCC); - - bool result = rc2.TryDecryptEcb( - ciphertext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - - Span excess = largeBuffer.Slice(destinationBuffer.Length); - AssertFilledWith(0xCC, excess); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - Span largeBuffer = new byte[ciphertext.Length + 10]; - Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); - largeBuffer.Fill(0xCC); - - bool result = rc2.TryEncryptEcb( - plaintext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - - AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); - Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - ciphertext.AsSpan().CopyTo(ciphertextBuffer); - - bool result = rc2.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - - int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); - plaintext.AsSpan().CopyTo(plaintextBuffer); - - bool result = rc2.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - byte[] decrypted = rc2.DecryptEcb(ciphertext.AsSpan(), padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - byte[] encrypted = rc2.EncryptEcb(plaintext.AsSpan(), padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - byte[] decrypted = rc2.DecryptEcb(ciphertext, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (RC2 rc2 = RC2Factory.Create()) - { - rc2.Key = s_rc2OneShotKey; - byte[] encrypted = rc2.EncryptEcb(plaintext, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = rc2.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - private static void AssertFilledWith(byte value, ReadOnlySpan span) - { - for (int i = 0; i < span.Length; i++) - { - Assert.Equal(value, span[i]); - } - } - - public static IEnumerable EcbTestCases - { - get - { - // plaintext that is block aligned - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, - 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, - 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, - 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, - }, - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, - 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, - 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, - 0x72, 0x8A, 0x57, 0x94, 0x2D, 0x79, 0xBD, 0xAA, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x8A, 0x65, 0xEB, 0x8C, 0x62, 0x14, 0xDD, 0x83, - 0x71, 0x0F, 0x1B, 0x21, 0xAD, 0x5F, 0xCD, 0xC1, - 0xEB, 0x5E, 0x2E, 0xB9, 0x1A, 0x1E, 0x1B, 0xE4, - }, - - PaddingMode.ISO10126, - }; - - // plaintext requires padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, - 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, - 0xF2, 0x94, 0x11, 0xA3, 0xE8, 0xAD, 0xA7, 0xE6, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, - 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, - 0xE3, 0xB2, 0x3D, 0xAA, 0x91, 0x6A, 0xD0, 0x06, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, - 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, - 0x17, 0x97, 0x3A, 0x77, 0x69, 0x5E, 0x79, 0xE9, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0x14, 0x76, 0x59, 0x63, 0xBD, 0x9E, 0xF3, 0x2E, - 0xF6, 0xA1, 0x05, 0x03, 0x44, 0x59, 0xF5, 0x88, - 0x22, 0xC0, 0x50, 0x52, 0x56, 0x5A, 0x15, 0xFD, - }, - - PaddingMode.ISO10126, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - new byte[] - { - 0x9D, 0x70, 0x70, 0x58, 0x47, 0x5A, 0xD0, 0xC8, - }, - - PaddingMode.PKCS7, - }; - } - } - } -} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs index 7b065d22b47..8b953975741 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherTests.cs @@ -161,6 +161,27 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests byte[] decrypted = alg.Decrypt(cipher); Assert.Equal(expectedDecryptedBytes, decrypted); + + if (RC2Factory.OneShotSupported) + { + byte[] oneShotEncrypt = cipherMode switch + { + CipherMode.ECB => alg.EncryptEcb(textHex.HexToByteArray(), paddingMode), + CipherMode.CBC => alg.EncryptCbc(textHex.HexToByteArray(), iv.HexToByteArray(), paddingMode), + _ => throw new NotImplementedException(), + }; + + Assert.Equal(expectedEncryptedBytes, oneShotEncrypt); + + byte[] oneShotDecrypt = cipherMode switch + { + CipherMode.ECB => alg.DecryptEcb(cipher, paddingMode), + CipherMode.CBC => alg.DecryptCbc(cipher, iv.HexToByteArray(), paddingMode), + _ => throw new NotImplementedException(), + }; + + Assert.Equal(expectedDecryptedBytes, oneShotDecrypt); + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Factory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Factory.cs index 760aff5c1fb..73bf13eb318 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Factory.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2Factory.cs @@ -8,6 +8,7 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests public interface IRC2Provider { RC2 Create(); + bool OneShotSupported { get; } } public static partial class RC2Factory @@ -17,6 +18,8 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests return s_provider.Create(); } + public static bool OneShotSupported => s_provider.OneShotSupported; + public static bool IsSupported { get; } = Test.Cryptography.PlatformSupport.IsRC2Supported; } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs new file mode 100644 index 00000000000..89e6b5d285d --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs @@ -0,0 +1,414 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Security.Cryptography; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + public abstract class SymmetricOneShotBase + { + protected abstract byte[] Key { get; } + protected abstract byte[] IV { get; } + protected abstract SymmetricAlgorithm CreateAlgorithm(); + + protected void OneShotRoundtripTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + // Set the instance to use a different mode and padding than what will be used + // in the one-shots to test that the one shot "wins". + alg.FeedbackSize = 8; + alg.Mode = mode == CipherMode.ECB ? CipherMode.CBC : CipherMode.ECB; + alg.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; + + byte[] encrypted = mode switch + { + CipherMode.ECB => alg.EncryptEcb(plaintext, padding), + CipherMode.CBC => alg.EncryptCbc(plaintext, IV, padding), + _ => throw new NotImplementedException(), + }; + byte[] decrypted = mode switch + { + CipherMode.ECB => alg.DecryptEcb(encrypted, padding), + CipherMode.CBC => alg.DecryptCbc(encrypted, IV, padding), + _ => throw new NotImplementedException(), + }; + + AssertPlaintexts(plaintext, decrypted, padding); + AssertCiphertexts(encrypted, ciphertext, padding, alg.BlockSize / 8); + + decrypted = mode switch + { + CipherMode.ECB => alg.DecryptEcb(ciphertext, padding), + CipherMode.CBC => alg.DecryptCbc(ciphertext, IV, padding), + _ => throw new NotImplementedException(), + }; + encrypted = mode switch + { + CipherMode.ECB => alg.EncryptEcb(decrypted, padding), + CipherMode.CBC => alg.EncryptCbc(decrypted, IV, padding), + _ => throw new NotImplementedException(), + }; + + AssertPlaintexts(plaintext, decrypted, padding); + AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + } + } + + protected void TryDecryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + if (plaintext.Length == 0) + { + // Can't have a ciphertext length shorter than zero. + return; + } + + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + Span destinationBuffer = new byte[plaintext.Length - 1]; + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + + Assert.False(result, "TryDecrypt"); + Assert.Equal(0, bytesWritten); + } + } + + protected void TryEncryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + if (ciphertext.Length == 0) + { + // Can't have a too small buffer for zero. + return; + } + + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + Span destinationBuffer = new byte[ciphertext.Length - 1]; + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + Assert.False(result, "TryEncrypt"); + Assert.Equal(0, bytesWritten); + } + } + + protected void TryDecryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + Span destinationBuffer = new byte[expectedPlaintextSize]; + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + Assert.True(result, "TryDecrypt"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + AssertPlaintexts(plaintext, destinationBuffer, padding); + } + } + + protected void TryEncryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + int expectedCiphertextSize = mode switch + { + CipherMode.ECB => alg.GetCiphertextLengthEcb(plaintext.Length, padding), + CipherMode.CBC => alg.GetCiphertextLengthCbc(plaintext.Length, padding), + _ => throw new NotImplementedException(), + }; + Span destinationBuffer = new byte[expectedCiphertextSize]; + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + Assert.True(result, "TryEncrypt"); + Assert.Equal(expectedCiphertextSize, bytesWritten); + + AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + } + } + + protected void TryDecryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + + Span largeBuffer = new byte[expectedPlaintextSize + 10]; + Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); + largeBuffer.Fill(0xCC); + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + + Assert.True(result, "TryDecrypt"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + AssertPlaintexts(plaintext, destinationBuffer, padding); + + Span excess = largeBuffer.Slice(destinationBuffer.Length); + AssertExtensions.FilledWith(0xCC, excess); + } + } + + protected void TryEncryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + Span largeBuffer = new byte[ciphertext.Length + 10]; + Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); + largeBuffer.Fill(0xCC); + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + + Assert.True(result, "TryEncrypt"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + AssertExtensions.FilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); + } + } + + protected void TryDecryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; + int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); + Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + ciphertext.AsSpan().CopyTo(ciphertextBuffer); + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryDecryptCbc(ciphertextBuffer, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + Assert.True(result, "TryDecrypt"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + AssertPlaintexts(plaintext, destinationBuffer, padding); + Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + + protected void TryEncryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + (int plaintextOffset, int ciphertextOffset)[] offsets = + { + (0, 0), (8, 0), (0, 8), (8, 8), + }; + + foreach ((int plaintextOffset, int ciphertextOffset) in offsets) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + + int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); + Span buffer = new byte[destinationSize]; + Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); + Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); + plaintext.AsSpan().CopyTo(plaintextBuffer); + + int bytesWritten; + bool result = mode switch + { + CipherMode.ECB => alg.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryEncryptCbc(plaintextBuffer, IV, destinationBuffer, out bytesWritten, padding), + _ => throw new NotImplementedException(), + }; + Assert.True(result, "TryEncrypt"); + Assert.Equal(destinationBuffer.Length, bytesWritten); + + AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); + } + } + } + + protected void DecryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + byte[] decrypted = mode switch + { + CipherMode.ECB => alg.DecryptEcb(ciphertext.AsSpan(), padding), + CipherMode.CBC => alg.DecryptCbc(ciphertext.AsSpan(), IV.AsSpan(), padding), + _ => throw new NotImplementedException(), + }; + + AssertPlaintexts(plaintext, decrypted, padding); + } + } + + protected void EncryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + byte[] encrypted = mode switch + { + CipherMode.ECB => alg.EncryptEcb(plaintext.AsSpan(), padding), + CipherMode.CBC => alg.EncryptCbc(plaintext.AsSpan(), IV.AsSpan(), padding), + _ => throw new NotImplementedException(), + }; + + AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + } + } + + protected void DecryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + byte[] decrypted = mode switch + { + CipherMode.ECB => alg.DecryptEcb(ciphertext, padding), + CipherMode.CBC => alg.DecryptCbc(ciphertext, IV, padding), + _ => throw new NotImplementedException(), + }; + + AssertPlaintexts(plaintext, decrypted, padding); + } + } + + protected void EncryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + alg.Key = Key; + byte[] encrypted = mode switch + { + CipherMode.ECB => alg.EncryptEcb(plaintext, padding), + CipherMode.CBC => alg.EncryptCbc(plaintext, IV, padding), + _ => throw new NotImplementedException(), + }; + + AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + } + } + + [Fact] + public void DerivedTypesDefineTest() + { + const string TestSuffix = "Test"; + Type implType = GetType(); + Type defType = typeof(SymmetricOneShotBase); + List missingMethods = new List(); + + foreach (MethodInfo info in defType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)) + { + if (info.IsFamily && info.Name.EndsWith(TestSuffix, StringComparison.Ordinal)) + { + string targetMethodName = info.Name[..^TestSuffix.Length]; + + MethodInfo info2 = implType.GetMethod( + targetMethodName, + BindingFlags.Instance | BindingFlags.Public); + + if (info2 is null) + { + missingMethods.Add(targetMethodName); + } + } + } + + Assert.Empty(missingMethods); + } + + private static void AssertPlaintexts(ReadOnlySpan expected, ReadOnlySpan actual, PaddingMode padding) + { + if (padding == PaddingMode.Zeros) + { + AssertExtensions.SequenceEqual(expected, actual.Slice(0, expected.Length)); + AssertExtensions.FilledWith(0, actual.Slice(actual.Length)); + } + else + { + AssertExtensions.SequenceEqual(expected, actual); + } + } + + private static void AssertCiphertexts(ReadOnlySpan expected, ReadOnlySpan actual, PaddingMode padding, int blockSizeBytes) + { + if (padding == PaddingMode.ISO10126) + { + // The padding is random, so we can't check the exact ciphertext. + AssertExtensions.SequenceEqual(expected[..^blockSizeBytes], actual[..^blockSizeBytes]); + } + else + { + AssertExtensions.SequenceEqual(expected, actual); + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs new file mode 100644 index 00000000000..d5706706858 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs @@ -0,0 +1,563 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security.Cryptography; + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Encryption.TripleDes.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + public class TripleDESCipherOneShotTests : SymmetricOneShotBase + { + protected override byte[] Key => new byte[] + { + 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xA0, + }; + + protected override byte[] IV => new byte[] + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }; + + protected override SymmetricAlgorithm CreateAlgorithm() => TripleDESFactory.Create(); + + [Theory] + [MemberData(nameof(TestCases))] + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + [Theory] + [MemberData(nameof(TestCases))] + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + + public static IEnumerable TestCases + { + get + { + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xDD, 0x97, 0xD0, 0xD3, 0x8D, 0xC6, 0xFD, 0x26, + 0x5A, 0x76, 0x3B, 0x0E, 0x0D, 0x91, 0x12, 0x98, + 0x55, 0xDF, 0x36, 0xB0, 0xB3, 0x91, 0x72, 0x30, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xDD, 0x97, 0xD0, 0xD3, 0x8D, 0xC6, 0xFD, 0x26, + 0x5A, 0x76, 0x3B, 0x0E, 0x0D, 0x91, 0x12, 0x98, + }, + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xDD, 0x97, 0xD0, 0xD3, 0x8D, 0xC6, 0xFD, 0x26, + 0x5A, 0x76, 0x3B, 0x0E, 0x0D, 0x91, 0x12, 0x98, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xDD, 0x97, 0xD0, 0xD3, 0x8D, 0xC6, 0xFD, 0x26, + 0x5A, 0x76, 0x3B, 0x0E, 0x0D, 0x91, 0x12, 0x98, + 0x62, 0x8C, 0x88, 0x03, 0xD2, 0xD7, 0xBF, 0xB3, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0xDD, 0x97, 0xD0, 0xD3, 0x8D, 0xC6, 0xFD, 0x26, + 0x5A, 0x76, 0x3B, 0x0E, 0x0D, 0x91, 0x12, 0x98, + 0x64, 0x32, 0xD7, 0xEF, 0x2D, 0xAC, 0x16, 0xF9, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xFC, 0x1C, 0xB2, 0x4C, 0x7E, 0x4A, 0x29, 0x08, + 0xD9, 0x76, 0x41, 0xC4, 0x1A, 0x06, 0xD3, 0x9F, + 0x1F, 0xA3, 0xA9, 0x74, 0x8A, 0x21, 0x15, 0x90, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xFC, 0x1C, 0xB2, 0x4C, 0x7E, 0x4A, 0x29, 0x08, + 0xD9, 0x76, 0x41, 0xC4, 0x1A, 0x06, 0xD3, 0x9F, + 0x38, 0xBC, 0x88, 0x44, 0x52, 0xBD, 0x23, 0xEA, + }, + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xFC, 0x1C, 0xB2, 0x4C, 0x7E, 0x4A, 0x29, 0x08, + 0xD9, 0x76, 0x41, 0xC4, 0x1A, 0x06, 0xD3, 0x9F, + 0xE0, 0x46, 0x72, 0xBA, 0x0B, 0x55, 0xF4, 0x29, + }, + + PaddingMode.ANSIX923, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xFC, 0x1C, 0xB2, 0x4C, 0x7E, 0x4A, 0x29, 0x08, + 0xD9, 0x76, 0x41, 0xC4, 0x1A, 0x06, 0xD3, 0x9F, + 0x94, 0x03, 0x51, 0x1C, 0xA8, 0xAC, 0x1E, 0x68, + }, + + PaddingMode.ISO10126, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CBC, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x79, 0x6B, 0x9D, 0x8B, 0xFD, 0xD4, 0x23, 0xCE, + }, + + PaddingMode.PKCS7, + CipherMode.CBC, + }; + + // plaintext requires no padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + }, + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x34, 0xE6, 0x86, 0x6D, 0x94, 0x2E, 0x98, 0x0F, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, + 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, + 0x5E, 0xEE, 0x73, 0xBB, 0x94, 0xED, 0x29, 0x7A, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + // plaintext requires padding + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0xB1, 0x3D, 0x05, 0x93, 0x98, 0xE6, 0x2C, 0xDF, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0xC9, 0x52, 0x8F, 0xC1, 0x30, 0xC0, 0x7C, 0x63, + }, + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0x6A, 0x97, 0x38, 0x85, 0x3B, 0x48, 0x81, 0x5E, + }, + + PaddingMode.ANSIX923, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, + 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, + 0x33, 0x58, 0x09, 0x2C, 0xD8, 0xB5, 0x36, 0xAD, + }, + + PaddingMode.ISO10126, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.ECB, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, + }, + + PaddingMode.PKCS7, + CipherMode.ECB, + }; + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs deleted file mode 100644 index 3469f4c6fec..00000000000 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.OneShot.cs +++ /dev/null @@ -1,633 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Security.Cryptography; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Encryption.TripleDes.Tests -{ - public partial class TripleDESCipherTests - { - private static byte[] s_tdes192OneShotKey = new byte[] - { - 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12, - 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xA0, - }; - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EcbRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - // Even though we have set the instance to use CFB, the Ecb one shots should - // always be done in ECB. - tdes.FeedbackSize = 8; - tdes.Mode = CipherMode.CFB; - tdes.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; - - byte[] encrypted = tdes.EncryptEcb(plaintext, padding); - byte[] decrypted = tdes.DecryptEcb(encrypted, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted[..plaintext.Length]); - AssertFilledWith(0, plaintext.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - - decrypted = tdes.DecryptEcb(ciphertext, padding); - encrypted = tdes.EncryptEcb(decrypted, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (plaintext.Length == 0) - { - // Can't have a ciphertext length shorter than zero. - return; - } - - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - Span destinationBuffer = new byte[plaintext.Length - 1]; - - bool result = tdes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - if (ciphertext.Length == 0) - { - // Can't have a too small buffer for zero. - return; - } - - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - Span destinationBuffer = new byte[ciphertext.Length - 1]; - - bool result = tdes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.False(result, "TryDecryptEcb"); - Assert.Equal(0, bytesWritten); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - Span destinationBuffer = new byte[expectedPlaintextSize]; - - bool result = tdes.TryDecryptEcb(ciphertext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - int expectedCiphertextSize = tdes.GetCiphertextLengthEcb(plaintext.Length, padding); - Span destinationBuffer = new byte[expectedCiphertextSize]; - - bool result = tdes.TryEncryptEcb(plaintext, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(expectedCiphertextSize, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - // Padding is random so we can't validate the last block. - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - - Span largeBuffer = new byte[expectedPlaintextSize + 10]; - Span destinationBuffer = largeBuffer.Slice(0, expectedPlaintextSize); - largeBuffer.Fill(0xCC); - - bool result = tdes.TryDecryptEcb( - ciphertext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - } - - Span excess = largeBuffer.Slice(destinationBuffer.Length); - AssertFilledWith(0xCC, excess); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - Span largeBuffer = new byte[ciphertext.Length + 10]; - Span destinationBuffer = largeBuffer.Slice(0, ciphertext.Length); - largeBuffer.Fill(0xCC); - - bool result = tdes.TryEncryptEcb( - plaintext, - destinationBuffer, - padding, - out int bytesWritten); - - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - } - - AssertFilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryDecryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - int expectedPlaintextSize = padding == PaddingMode.Zeros ? ciphertext.Length : plaintext.Length; - int destinationSize = Math.Max(expectedPlaintextSize, ciphertext.Length) + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(plaintextOffset, expectedPlaintextSize); - Span ciphertextBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - ciphertext.AsSpan().CopyTo(ciphertextBuffer); - - bool result = tdes.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryDecryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, destinationBuffer.Slice(0, plaintext.Length).ToArray()); - AssertFilledWith(0, destinationBuffer.Slice(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(ciphertextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void TryEncryptEcb_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - (int plaintextOffset, int ciphertextOffset)[] offsets = - { - (0, 0), (8, 0), (0, 8), (8, 8), - }; - - foreach ((int plaintextOffset, int ciphertextOffset) in offsets) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - - int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); - Span buffer = new byte[destinationSize]; - Span destinationBuffer = buffer.Slice(ciphertextOffset, ciphertext.Length); - Span plaintextBuffer = buffer.Slice(plaintextOffset, plaintext.Length); - plaintext.AsSpan().CopyTo(plaintextBuffer); - - bool result = tdes.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out int bytesWritten); - Assert.True(result, "TryEncryptEcb"); - Assert.Equal(destinationBuffer.Length, bytesWritten); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], destinationBuffer[..^blockSizeBytes].ToArray()); - } - else - { - Assert.Equal(ciphertext, destinationBuffer.ToArray()); - Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); - } - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - byte[] decrypted = tdes.DecryptEcb(ciphertext.AsSpan(), padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - byte[] encrypted = tdes.EncryptEcb(plaintext.AsSpan(), padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void DecryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - byte[] decrypted = tdes.DecryptEcb(ciphertext, padding); - - if (padding == PaddingMode.Zeros) - { - Assert.Equal(plaintext, decrypted.AsSpan(0, plaintext.Length).ToArray()); - AssertFilledWith(0, decrypted.AsSpan(plaintext.Length)); - } - else - { - Assert.Equal(plaintext, decrypted); - } - } - } - - [Theory] - [MemberData(nameof(EcbTestCases))] - public static void EncryptEcb_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding) - { - using (TripleDES tdes = TripleDESFactory.Create()) - { - tdes.Key = s_tdes192OneShotKey; - byte[] encrypted = tdes.EncryptEcb(plaintext, padding); - - if (padding == PaddingMode.ISO10126) - { - int blockSizeBytes = tdes.BlockSize / 8; - Assert.Equal(ciphertext[..^blockSizeBytes], encrypted[..^blockSizeBytes]); - } - else - { - Assert.Equal(ciphertext, encrypted); - } - } - } - - private static void AssertFilledWith(byte value, ReadOnlySpan span) - { - for (int i = 0; i < span.Length; i++) - { - Assert.Equal(value, span[i]); - } - } - - public static IEnumerable EcbTestCases - { - get - { - // plaintext requires no padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, - 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, - 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, - }, - - PaddingMode.PKCS7, - }; - - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, - 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, - }, - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, - 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, - 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, - 0x34, 0xE6, 0x86, 0x6D, 0x94, 0x2E, 0x98, 0x0F, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, - 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, - }, - - // ciphertext - new byte[] - { - 0x2C, 0xE7, 0xF1, 0x5C, 0x7B, 0xA8, 0x40, 0x0C, - 0x1A, 0x09, 0xDC, 0x63, 0x43, 0xC9, 0x1A, 0x63, - 0x5E, 0xEE, 0x73, 0xBB, 0x94, 0xED, 0x29, 0x7A, - }, - - PaddingMode.ISO10126, - }; - - // plaintext requires padding - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, - 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, - 0xB1, 0x3D, 0x05, 0x93, 0x98, 0xE6, 0x2C, 0xDF, - }, - - PaddingMode.PKCS7, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, - 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, - 0xC9, 0x52, 0x8F, 0xC1, 0x30, 0xC0, 0x7C, 0x63, - }, - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, - 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, - 0x6A, 0x97, 0x38, 0x85, 0x3B, 0x48, 0x81, 0x5E, - }, - - PaddingMode.ANSIX923, - }; - - yield return new object[] - { - // plaintext - new byte[] - { - 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, - 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, - 0x59, - }, - - // ciphertext - new byte[] - { - 0xD5, 0xD5, 0x80, 0x3F, 0xC3, 0x7E, 0x4A, 0xE4, - 0xF2, 0x93, 0x9B, 0xC3, 0xDC, 0x4F, 0xA0, 0x23, - 0x33, 0x58, 0x09, 0x2C, 0xD8, 0xB5, 0x36, 0xAD, - }, - - PaddingMode.ISO10126, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.Zeros, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - Array.Empty(), - - PaddingMode.None, - }; - - yield return new object[] - { - // plaintext - Array.Empty(), - - // ciphertext - new byte[] - { - 0x65, 0xE4, 0x9C, 0xD3, 0xE6, 0xBE, 0xB8, 0x40, - }, - - PaddingMode.PKCS7, - }; - } - } - } -} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs index 9bc2b80f14c..64b3203b4b7 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs @@ -492,6 +492,8 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { byte[] liveEncryptBytes; byte[] liveDecryptBytes; + byte[] liveOneShotDecryptBytes = null; + byte[] liveOneShotEncryptBytes = null; using (TripleDES tdes = TripleDESFactory.Create()) { @@ -505,6 +507,29 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests liveEncryptBytes = TripleDESEncryptDirectKey(tdes, key, iv, plainBytes); liveDecryptBytes = TripleDESDecryptDirectKey(tdes, key, iv, cipherBytes); + + if (cipherMode == CipherMode.ECB) + { + tdes.Key = key; + liveOneShotDecryptBytes = tdes.DecryptEcb(cipherBytes, paddingMode); + liveOneShotEncryptBytes = tdes.EncryptEcb(plainBytes, paddingMode); + } + else if (cipherMode == CipherMode.CBC) + { + tdes.Key = key; + liveOneShotDecryptBytes = tdes.DecryptCbc(cipherBytes, iv, paddingMode); + liveOneShotEncryptBytes = tdes.EncryptCbc(plainBytes, iv, paddingMode); + } + + if (liveOneShotDecryptBytes is not null) + { + Assert.Equal(plainBytes, liveOneShotDecryptBytes); + } + + if (liveOneShotEncryptBytes is not null) + { + Assert.Equal(cipherBytes, liveOneShotEncryptBytes); + } } Assert.Equal(cipherBytes, liveEncryptBytes); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs index f00ee5582d3..b08732a5aea 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs @@ -87,6 +87,52 @@ namespace Internal.Cryptography } } + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + private ICryptoTransform CreateTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting) { // note: rbgIV is guaranteed to be cloned before this method, so no need to clone it again diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs index 34f0fc6367f..d0b960fc4ae 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs @@ -131,6 +131,52 @@ namespace Internal.Cryptography } } + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // only 8bits feedback is available on all platforms diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs index b4c8029f7a5..33f39984990 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs @@ -134,6 +134,62 @@ namespace Internal.Cryptography } } + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + if (!ValidKeySize(Key.Length, out int keySize)) + throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); + + int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + effectiveKeyLength: effectiveKeySize, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + if (!ValidKeySize(Key.Length, out int keySize)) + throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); + + int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + effectiveKeyLength: effectiveKeySize, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + paddingSize: BlockSize / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // CFB not supported at all diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs index 8c0e2825a73..59328765642 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs @@ -136,6 +136,52 @@ namespace Internal.Cryptography } } + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CBC, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: BlockSize / BitsPerByte, + 0, /*feedback size */ + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // only 8bits/64bits feedback would be valid. diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RC2Provider.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RC2Provider.cs index 1669a0055b2..b29c6a8f1e3 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RC2Provider.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RC2Provider.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { return RC2.Create(); } + + public bool OneShotSupported => true; } public partial class RC2Factory diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 01c484e98d2..f4f089bea4c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -11,12 +11,14 @@ Link="CommonTest\System\IO\PositionValueStream.cs" /> + - + - + - + - + plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: true, + paddingMode, + CipherMode.CBC); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: false, + paddingMode, + CipherMode.CBC); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs index 3687df173b5..b03c756c1ad 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs @@ -129,6 +129,44 @@ namespace System.Security.Cryptography } } + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: true, + paddingMode, + CipherMode.CBC); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: false, + paddingMode, + CipherMode.CBC); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/RC2Provider.cs b/src/libraries/System.Security.Cryptography.Cng/tests/RC2Provider.cs index 1669a0055b2..b29c6a8f1e3 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/RC2Provider.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/RC2Provider.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { return RC2.Create(); } + + public bool OneShotSupported => true; } public partial class RC2Factory diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs index 9f1a604a74a..9810072403f 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs @@ -111,6 +111,12 @@ namespace System.Security.Cryptography.Cng.Tests oneShotEphemeralEncrypted = ephemeral.EncryptEcb(plainBytes, paddingMode); oneShotPersistedDecrypted = persisted.DecryptEcb(oneShotEphemeralEncrypted, paddingMode); } + else if (cipherMode == CipherMode.CBC) + { + oneShotPersistedEncrypted = persisted.EncryptCbc(plainBytes, persisted.IV, paddingMode); + oneShotEphemeralEncrypted = ephemeral.EncryptCbc(plainBytes, ephemeral.IV, paddingMode); + oneShotPersistedDecrypted = persisted.DecryptCbc(oneShotEphemeralEncrypted, persisted.IV, paddingMode); + } if (oneShotPersistedEncrypted is not null) { diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 1761b8d4a2e..8b6195ed23f 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -146,15 +146,15 @@ Link="CommonTest\AlgorithmImplementations\AES\AesContractTests.cs" /> - + - + diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/RC2CryptoServiceProviderProvider.cs b/src/libraries/System.Security.Cryptography.Csp/tests/RC2CryptoServiceProviderProvider.cs index 66ec211646b..6065b54ad43 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/RC2CryptoServiceProviderProvider.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/RC2CryptoServiceProviderProvider.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { return new RC2CryptoServiceProvider(); } + + public bool OneShotSupported => false; } public partial class RC2Factory diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs index 00a71bd1687..b14a21f2daa 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs @@ -80,6 +80,8 @@ namespace System.Security.Cryptography.Csp.Tests // CryptoServiceProviders will not get one-shot APIs as they are being deprecated "TryEncryptEcbCore", "TryDecryptEcbCore", + "TryEncryptCbcCore", + "TryDecryptCbcCore", }; IEnumerable baseMethods = shimType. diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/RC2Provider.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/RC2Provider.cs index 1669a0055b2..b29c6a8f1e3 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/RC2Provider.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/RC2Provider.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests { return RC2.Create(); } + + public bool OneShotSupported => true; } public partial class RC2Factory diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 143f099f485..5eee91bb97a 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -253,11 +253,17 @@ namespace System.Security.Cryptography public abstract System.Security.Cryptography.ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV); public virtual System.Security.Cryptography.ICryptoTransform CreateEncryptor() { throw null; } public abstract System.Security.Cryptography.ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV); + public byte[] DecryptCbc(byte[] ciphertext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public byte[] DecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public int DecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public byte[] DecryptEcb(byte[] ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public byte[] DecryptEcb(System.ReadOnlySpan ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public int DecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public byte[] EncryptCbc(byte[] plaintext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public byte[] EncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public int EncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public byte[] EncryptEcb(byte[] plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public byte[] EncryptEcb(System.ReadOnlySpan plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public int EncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } @@ -266,8 +272,12 @@ namespace System.Security.Cryptography public int GetCiphertextLengthCbc(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public int GetCiphertextLengthCfb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } public int GetCiphertextLengthEcb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } + public bool TryDecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + protected virtual bool TryDecryptCbcCore(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool TryDecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } protected virtual bool TryDecryptEcbCore(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + public bool TryEncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + protected virtual bool TryEncryptCbcCore(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool TryEncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } protected virtual bool TryEncryptEcbCore(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool ValidKeySize(int bitLength) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs index a79a220a707..857edc6d03c 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics.CodeAnalysis; using Internal.Cryptography; @@ -601,7 +602,7 @@ namespace System.Security.Cryptography if (!TryEncryptEcbCore(plaintext, buffer, paddingMode, out int written) || written != ciphertextLength) { - // This means a user-derived imiplementation added more padding than we expected or + // This means a user-derived implementation added more padding than we expected or // did something non-standard (encrypt to a partial block). This can't happen for // multiple padding blocks since the buffer would have been too small in the first // place. It doesn't make sense to try and support partial block encryption, likely @@ -666,6 +667,319 @@ namespace System.Security.Cryptography return TryEncryptEcbCore(plaintext, destination, paddingMode, out bytesWritten); } + /// + /// Decrypts data using CBC mode with the specified padding mode. + /// + /// The data to decrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The decrypted plaintext data. + /// + /// or is . + /// + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptCbc(byte[] ciphertext, byte[] iv, PaddingMode paddingMode = PaddingMode.PKCS7) + { + if (ciphertext is null) + throw new ArgumentNullException(nameof(ciphertext)); + if (iv is null) + throw new ArgumentNullException(nameof(iv)); + + return DecryptCbc(new ReadOnlySpan(ciphertext), new ReadOnlySpan(iv), paddingMode); + } + + /// + /// Decrypts data using CBC mode with the specified padding mode. + /// + /// The data to decrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The decrypted plaintext data. + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptCbc(ReadOnlySpan ciphertext, ReadOnlySpan iv, PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + + // CBC is more typically used with padding so the resulting plaintext is + // unlikely to be the same size as the ciphertext. So we rent since we are + // likely to going to need to resize anyway. + // This will pass the rented buffer to user code (the virtual Core) so + // don't use CryptoPool. + byte[] decryptRent = ArrayPool.Shared.Rent(ciphertext.Length); + Span decryptBuffer = decryptRent.AsSpan(0, ciphertext.Length); + + if (!TryDecryptCbcCore(ciphertext, iv, decryptBuffer, paddingMode, out int written) + || (uint)written > decryptBuffer.Length) + { + // This means decrypting the ciphertext grew in to a larger plaintext or overflowed. + // A user-derived class could do this, but it is not expected in any of the + // implementations that we ship. + throw new CryptographicException(SR.Argument_DestinationTooShort); + } + + byte[] plaintext = decryptBuffer.Slice(0, written).ToArray(); + CryptographicOperations.ZeroMemory(decryptBuffer.Slice(0, written)); + ArrayPool.Shared.Return(decryptRent); + + return plaintext; + } + + /// + /// Decrypts data into the specified buffer, using CBC mode with the specified padding mode. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The total number of bytes written to + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// The buffer in is too small to hold the plaintext data. + /// + /// + /// This method's behavior is defined by . + /// + public int DecryptCbc( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + + if (!TryDecryptCbcCore(ciphertext, iv, destination, paddingMode, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to decrypt data into the specified buffer, using CBC mode with the specified padding mode. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public bool TryDecryptCbc( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + out int bytesWritten, + PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + return TryDecryptCbcCore(ciphertext, iv, destination, paddingMode, out bytesWritten); + } + + /// + /// Encrypts data using CBC mode with the specified padding mode. + /// + /// The data to encrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The encrypted ciphertext data. + /// + /// or is . + /// + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// could not encrypt the plaintext. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptCbc(byte[] plaintext, byte[] iv, PaddingMode paddingMode = PaddingMode.PKCS7) + { + if (plaintext is null) + throw new ArgumentNullException(nameof(plaintext)); + if (iv is null) + throw new ArgumentNullException(nameof(iv)); + + return EncryptCbc(new ReadOnlySpan(plaintext), new ReadOnlySpan(iv), paddingMode); + } + + /// + /// Encrypts data using CBC mode with the specified padding mode. + /// + /// The data to encrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The encrypted ciphertext data. + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// could not encrypt the plaintext. + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptCbc( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + + int ciphertextLength = GetCiphertextLengthCbc(plaintext.Length, paddingMode); + + // We expect most if not all uses to encrypt to exactly the ciphertextLength + byte[] buffer = GC.AllocateUninitializedArray(ciphertextLength); + + if (!TryEncryptCbcCore(plaintext, iv, buffer, paddingMode, out int written) || + written != ciphertextLength) + { + // This means a user-derived implementation added more padding than we expected or + // did something non-standard (encrypt to a partial block). This can't happen for + // multiple padding blocks since the buffer would have been too small in the first + // place. It doesn't make sense to try and support partial block encryption, likely + // something went very wrong. So throw. + throw new CryptographicException(SR.Format(SR.Cryptography_EncryptedIncorrectLength, nameof(TryEncryptCbcCore))); + } + + return buffer; + } + + /// + /// Encrypts data into the specified buffer, using CBC mode with the specified padding mode. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The total number of bytes written to . + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// The buffer in is too small to hold the ciphertext data. + /// + /// + /// This method's behavior is defined by . + /// + public int EncryptCbc( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + + if (!TryEncryptCbcCore(plaintext, iv, destination, paddingMode, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to encrypt data into the specified buffer, using CBC mode with the specified padding mode. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// is not a valid padding mode. + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// This method's behavior is defined by . + /// + public bool TryEncryptCbc( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + out int bytesWritten, + PaddingMode paddingMode = PaddingMode.PKCS7) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + + return TryEncryptCbcCore(plaintext, iv, destination, paddingMode, out bytesWritten); + } + /// /// When overridden in a derived class, attempts to encrypt data into the specified /// buffer, using ECB mode with the specified padding mode. @@ -719,12 +1033,75 @@ namespace System.Security.Cryptography throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + /// + /// When overridden in a derived class, attempts to encrypt data into the specified + /// buffer, using CBC mode with the specified padding mode. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + /// Implementations of this method must write precisely + /// GetCiphertextLengthCbc(plaintext.Length, paddingMode) bytes to + /// and report that via . + /// + /// + protected virtual bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + + /// + /// When overridden in a derived class, attempts to decrypt data + /// into the specified buffer, using CBC mode with the specified padding mode. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + protected virtual bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + private static void CheckPaddingMode(PaddingMode paddingMode) { if (paddingMode < PaddingMode.None || paddingMode > PaddingMode.ISO10126) throw new ArgumentOutOfRangeException(nameof(paddingMode), SR.Cryptography_InvalidPaddingMode); } + private void CheckInitializationVectorSize(ReadOnlySpan iv) + { + if (iv.Length != BlockSize >> 3) + throw new ArgumentException(SR.Cryptography_InvalidIVSize, nameof(iv)); + } + protected CipherMode ModeValue; protected PaddingMode PaddingValue; protected byte[]? KeyValue; diff --git a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs index 65fa6db5699..380cd19b099 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs @@ -154,13 +154,13 @@ namespace System.Security.Cryptography.Primitives.Tests [Fact] public static void EncryptEcb_EncryptProducesIncorrectlyPaddedValue() { - static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + static bool EncryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) { bytesWritten = destination.Length + 1; return true; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryEncryptEcbCoreImpl = EncryptImpl, @@ -179,7 +179,7 @@ namespace System.Security.Cryptography.Primitives.Tests return true; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryDecryptEcbCoreImpl = DecryptImpl, @@ -192,13 +192,13 @@ namespace System.Security.Cryptography.Primitives.Tests [Fact] public static void EncryptEcb_EncryptCoreFails() { - static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + static bool EncryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) { bytesWritten = 0; return false; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryEncryptEcbCoreImpl = EncryptImpl, @@ -211,13 +211,13 @@ namespace System.Security.Cryptography.Primitives.Tests [Fact] public static void EncryptEcb_EncryptCoreOverflowWritten() { - static bool EncryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) + static bool EncryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) { bytesWritten = -1; return true; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryEncryptEcbCoreImpl = EncryptImpl, @@ -230,13 +230,13 @@ namespace System.Security.Cryptography.Primitives.Tests [Fact] public static void DecryptEcb_DecryptCoreFails() { - static bool DecryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) + static bool DecryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) { bytesWritten = 0; return false; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryDecryptEcbCoreImpl = DecryptImpl, @@ -249,13 +249,13 @@ namespace System.Security.Cryptography.Primitives.Tests [Fact] public static void DecryptEcb_DecryptCoreOverflowWritten() { - static bool DecryptImpl(ReadOnlySpan plaintext, Span destination, PaddingMode paddingMode, out int bytesWritten) + static bool DecryptImpl(ReadOnlySpan ciphertext, Span destination, PaddingMode paddingMode, out int bytesWritten) { bytesWritten = -1; return true; } - EcbSymmetricAlgorithm alg = new EcbSymmetricAlgorithm + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm { BlockSize = 128, TryDecryptEcbCoreImpl = DecryptImpl, @@ -265,6 +265,236 @@ namespace System.Security.Cryptography.Primitives.Tests alg.DecryptEcb(Array.Empty(), PaddingMode.None)); } + [Fact] + public static void EncryptCbc_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.EncryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCbc_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.DecryptEcb(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void EncryptCbc_EncryptProducesIncorrectlyPaddedValue() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCbcCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCbc_DecryptBytesWrittenLies() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCbcCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCbc(new byte[128 / 8], new byte[128 / 8], PaddingMode.None)); + } + + [Fact] + public static void EncryptCbc_EncryptCoreFails() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCbcCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void EncryptCbc_EncryptCoreOverflowWritten() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCbcCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCbc_DecryptCoreFails() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCbcCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCbc_DecryptCoreOverflowWritten() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCbcCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCbc(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCbc_BadInitializationVectorLength() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + Assert.True(false, "Initialization vector was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCbcCoreImpl = DecryptImpl, + }; + + byte[] badIv = new byte[alg.BlockSize / 8 + 1]; + byte[] destination = new byte[alg.BlockSize / 8]; + + AssertExtensions.Throws("iv", () => + alg.DecryptCbc(Array.Empty(), badIv, PaddingMode.None)); + + AssertExtensions.Throws("iv", () => + alg.DecryptCbc(Array.Empty(), badIv, destination, PaddingMode.None)); + + AssertExtensions.Throws("iv", () => + alg.TryDecryptCbc(Array.Empty(), badIv, destination, out _, PaddingMode.None)); + } + + [Fact] + public static void EncryptCbc_BadInitializationVectorLength() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) + { + Assert.True(false, "Initialization vector was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCbcCoreImpl = EncryptImpl, + }; + + byte[] badIv = new byte[alg.BlockSize / 8 + 1]; + byte[] destination = new byte[alg.BlockSize / 8]; + + AssertExtensions.Throws("iv", () => + alg.EncryptCbc(Array.Empty(), badIv, PaddingMode.None)); + + AssertExtensions.Throws("iv", () => + alg.EncryptCbc(Array.Empty(), badIv, destination, PaddingMode.None)); + + AssertExtensions.Throws("iv", () => + alg.TryEncryptCbc(Array.Empty(), badIv, destination, out _, PaddingMode.None)); + } + public static IEnumerable CiphertextLengthTheories { get @@ -364,7 +594,7 @@ namespace System.Security.Cryptography.Primitives.Tests public override void GenerateKey() => throw new NotImplementedException(); } - private class EcbSymmetricAlgorithm : AnySizeAlgorithm + private class OneShotSymmetricAlgorithm : AnySizeAlgorithm { public delegate bool TryEncryptEcbCoreFunc( ReadOnlySpan plaintext, @@ -378,8 +608,24 @@ namespace System.Security.Cryptography.Primitives.Tests PaddingMode paddingMode, out int bytesWritten); + public delegate bool TryEncryptCbcCoreFunc( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten); + + public delegate bool TryDecryptCbcCoreFunc( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten); + public TryEncryptEcbCoreFunc TryEncryptEcbCoreImpl { get; set; } public TryDecryptEcbCoreFunc TryDecryptEcbCoreImpl { get; set; } + public TryEncryptCbcCoreFunc TryEncryptCbcCoreImpl { get; set; } + public TryDecryptCbcCoreFunc TryDecryptCbcCoreImpl { get; set; } protected override bool TryEncryptEcbCore( ReadOnlySpan plaintext, @@ -392,6 +638,20 @@ namespace System.Security.Cryptography.Primitives.Tests Span destination, PaddingMode paddingMode, out int bytesWritten) => TryDecryptEcbCoreImpl(ciphertext, destination, paddingMode, out bytesWritten); + + protected override bool TryEncryptCbcCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) => TryEncryptCbcCoreImpl(plaintext, iv, destination, paddingMode, out bytesWritten); + + protected override bool TryDecryptCbcCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + out int bytesWritten) => TryDecryptCbcCoreImpl(ciphertext, iv, destination, paddingMode, out bytesWritten); } } } From fcd12b86629c1ff6ea1ecff54645f39c442e764c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 7 Jul 2021 14:29:49 -0400 Subject: [PATCH 313/926] Fix temporary timer leak in Ping (#55268) --- .../src/System/Net/NetworkInformation/Ping.RawSocket.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs index 86887a9c698..de343859d87 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs @@ -199,9 +199,7 @@ namespace System.Net.NetworkInformation using (Socket socket = GetRawSocket(socketConfig)) { int ipHeaderLength = socketConfig.IsIpv4 ? MinIpHeaderLengthInBytes : 0; - CancellationTokenSource timeoutTokenSource = new CancellationTokenSource(); - - timeoutTokenSource.CancelAfter(timeout); + CancellationTokenSource timeoutTokenSource = new CancellationTokenSource(timeout); try { @@ -244,6 +242,10 @@ namespace System.Net.NetworkInformation catch (OperationCanceledException) { } + finally + { + timeoutTokenSource.Dispose(); + } // We have exceeded our timeout duration, and no reply has been received. return CreateTimedOutPingReply(); From b9020ea1ee41e2459ed44511c9a493e8f25ab16d Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 7 Jul 2021 15:00:02 -0400 Subject: [PATCH 314/926] Obsolete useManagedSha1 and ProduceLegacyHmacValues --- docs/project/list-of-diagnostics.md | 2 ++ src/libraries/Common/src/System/Obsoletions.cs | 6 ++++++ .../ref/System.Security.Cryptography.Algorithms.cs | 3 +++ .../src/System/Security/Cryptography/HMACSHA1.cs | 2 ++ .../src/System/Security/Cryptography/HMACSHA384.cs | 2 ++ .../src/System/Security/Cryptography/HMACSHA512.cs | 2 ++ .../tests/HmacSha1Tests.cs | 6 ++++-- .../tests/HmacSha384Tests.cs | 2 ++ .../tests/HmacSha512Tests.cs | 2 ++ 9 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 9497fc7b692..2d168532317 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -83,6 +83,8 @@ The PR that reveals the implementation of the `(() => h.ProduceLegacyHmacValues = true); +#pragma warning restore SYSLIB0029 } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs index 4da18847fd9..c9f52a982c0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/HmacSha512Tests.cs @@ -48,9 +48,11 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests { using (var h = new HMACSHA512()) { +#pragma warning disable SYSLIB0029 // ProduceLegacyHmacValues is obsolete Assert.False(h.ProduceLegacyHmacValues); h.ProduceLegacyHmacValues = false; // doesn't throw Assert.Throws(() => h.ProduceLegacyHmacValues = true); +#pragma warning restore SYSLIB0029 } } From 1c79e60368455d4bbe123b10d827179bae937662 Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Wed, 7 Jul 2021 12:08:08 -0700 Subject: [PATCH 315/926] Fix LLVM path (#55244) --- eng/testing/performance/performance-setup.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index 078cad080a4..9ed93d17fbd 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -153,9 +153,9 @@ if ($iOSMono) { mkdir $WorkItemDirectory } if($iOSLlvmBuild) { - Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse + Copy-Item -path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\llvm -Recurse } else { - Copy-Item -path "$SourceDirectory\iosHelloWorld\llvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse + Copy-Item -path "$SourceDirectory\iosHelloWorld\nollvm" $PayloadDirectory\iosHelloWorld\nollvm -Recurse } $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' From 0d1e9ca2c64fac343fb4f57c92c261ff757b512a Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Wed, 7 Jul 2021 12:25:53 -0700 Subject: [PATCH 316/926] close quic streams on Dispose (#54798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * close quic streams on Dispose * feedback from review * update ShutdownState handling * feedback from review * fix failing cancelling test Co-authored-by: Marie Píchová --- .../Interop/SafeMsQuicConfigurationHandle.cs | 1 + .../Interop/SafeMsQuicConnectionHandle.cs | 1 + .../Interop/SafeMsQuicListenerHandle.cs | 1 + .../Interop/SafeMsQuicRegistrationHandle.cs | 1 + .../MsQuic/Interop/SafeMsQuicStreamHandle.cs | 1 + .../MsQuic/MsQuicConnection.cs | 22 ++- .../Implementations/MsQuic/MsQuicStream.cs | 166 ++++++++++++++---- .../tests/FunctionalTests/MsQuicTests.cs | 12 +- ...icStreamConnectedStreamConformanceTests.cs | 16 -- .../tests/FunctionalTests/QuicStreamTests.cs | 2 +- 10 files changed, 159 insertions(+), 64 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index a51927e915d..64f236ffd44 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -28,6 +28,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal protected override bool ReleaseHandle() { MsQuicApi.Api.ConfigurationCloseDelegate(handle); + SetHandle(IntPtr.Zero); return true; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs index 38d908d3adf..74f806c9818 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -22,6 +22,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal protected override bool ReleaseHandle() { MsQuicApi.Api.ConnectionCloseDelegate(handle); + SetHandle(IntPtr.Zero); return true; } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs index 8a22ab84875..87e59b69a22 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs @@ -16,6 +16,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal protected override bool ReleaseHandle() { MsQuicApi.Api.ListenerCloseDelegate(handle); + SetHandle(IntPtr.Zero); return true; } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs index 47a0599c43f..d9c6e7c70ad 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs @@ -16,6 +16,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal protected override bool ReleaseHandle() { MsQuicApi.Api.RegistrationCloseDelegate(handle); + SetHandle(IntPtr.Zero); return true; } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs index c9e775b885e..3037f60956a 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs @@ -22,6 +22,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal protected override bool ReleaseHandle() { MsQuicApi.Api.StreamCloseDelegate(handle); + SetHandle(IntPtr.Zero); return true; } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index f31dfc36fd5..9e4005a6b5a 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -20,6 +20,7 @@ namespace System.Net.Quic.Implementations.MsQuic { private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + private const uint DefaultResetValue = 0xffffffff; // Arbitrary value unlikely to conflict with application protocols. // Delegate that wraps the static function that will be called when receiving an event. private static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); @@ -70,7 +71,7 @@ namespace System.Net.Quic.Implementations.MsQuic SingleWriter = true, }); - public void RemoveStream(MsQuicStream stream) + public void RemoveStream(MsQuicStream? stream) { bool releaseHandles; lock (this) @@ -252,7 +253,7 @@ namespace System.Net.Quic.Implementations.MsQuic state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success); // Stop accepting new streams. - state.AcceptQueue.Writer.Complete(); + state.AcceptQueue.Writer.TryComplete(); // Stop notifying about available streams. TaskCompletionSource? unidirectionalTcs = null; @@ -625,7 +626,7 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"Exception occurred during connection callback: {ex.Message}"); + NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex.Message}"); } // TODO: trigger an exception on any outstanding async calls. @@ -648,9 +649,17 @@ namespace System.Net.Quic.Implementations.MsQuic private async Task FlushAcceptQueue() { _state.AcceptQueue.Writer.TryComplete(); - await foreach (MsQuicStream item in _state.AcceptQueue.Reader.ReadAllAsync().ConfigureAwait(false)) + await foreach (MsQuicStream stream in _state.AcceptQueue.Reader.ReadAllAsync().ConfigureAwait(false)) { - item.Dispose(); + if (stream.CanRead) + { + stream.AbortRead(DefaultResetValue); + } + if (stream.CanWrite) + { + stream.AbortWrite(DefaultResetValue); + } + stream.Dispose(); } } @@ -681,7 +690,8 @@ namespace System.Net.Quic.Implementations.MsQuic _configuration?.Dispose(); if (releaseHandles) { - _state!.Handle?.Dispose(); + // We may not be fully initialized if constructor fails. + _state.Handle?.Dispose(); if (_state.StateGCHandle.IsAllocated) _state.StateGCHandle.Free(); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index a0fea559d6c..1d4d43ef72f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -19,7 +19,6 @@ namespace System.Net.Quic.Implementations.MsQuic internal static readonly StreamCallbackDelegate s_streamDelegate = new StreamCallbackDelegate(NativeCallbackHandler); private readonly State _state = new State(); - private GCHandle _stateHandle; // Backing for StreamId private long _streamId = -1; @@ -27,17 +26,12 @@ namespace System.Net.Quic.Implementations.MsQuic // Used to check if StartAsync has been called. private bool _started; - // Used by the class to indicate that the stream is m_Readable. - private readonly bool _canRead; - - // Used by the class to indicate that the stream is writable. - private readonly bool _canWrite; - - private volatile bool _disposed; + private int _disposed; private sealed class State { public SafeMsQuicStreamHandle Handle = null!; // set in ctor. + public GCHandle StateGCHandle; public MsQuicConnection.State ConnectionState = null!; // set in ctor. public ReadState ReadState; @@ -65,36 +59,50 @@ namespace System.Net.Quic.Implementations.MsQuic public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); public ShutdownState ShutdownState; + public int ShutdownDone; // Set once stream have been shutdown. public readonly TaskCompletionSource ShutdownCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public void Cleanup() + { + ShutdownState = ShutdownState.Finished; + CleanupSendState(this); + Handle?.Dispose(); + Marshal.FreeHGlobal(SendQuicBuffers); + SendQuicBuffers = IntPtr.Zero; + if (StateGCHandle.IsAllocated) StateGCHandle.Free(); + ConnectionState?.RemoveStream(null); + } } // inbound. internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { _state.Handle = streamHandle; - _canRead = true; - _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _started = true; + if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + { + _state.SendState = SendState.Closed; + } - _stateHandle = GCHandle.Alloc(_state); + _state.StateGCHandle = GCHandle.Alloc(_state); try { MsQuicApi.Api.SetCallbackHandlerDelegate( _state.Handle, s_streamDelegate, - GCHandle.ToIntPtr(_stateHandle)); + GCHandle.ToIntPtr(_state.StateGCHandle)); } catch { - _stateHandle.Free(); + _state.StateGCHandle.Free(); throw; } if (!connectionState.TryAddStream(this)) { - _stateHandle.Free(); + _state.StateGCHandle.Free(); throw new ObjectDisposedException(nameof(QuicConnection)); } @@ -104,7 +112,7 @@ namespace System.Net.Quic.Implementations.MsQuic { NetEventSource.Info( _state, - $"[Stream#{_state.GetHashCode()}] inbound {(_canWrite ? "bi" : "uni")}directional stream created " + + $"[Stream#{_state.GetHashCode()}] inbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + $"in Connection#{_state.ConnectionState.GetHashCode()}."); } } @@ -114,16 +122,19 @@ namespace System.Net.Quic.Implementations.MsQuic { Debug.Assert(connectionState.Handle != null); - _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); - _canWrite = true; - _stateHandle = GCHandle.Alloc(_state); + _state.StateGCHandle = GCHandle.Alloc(_state); + if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + { + _state.ReadState = ReadState.Closed; + } + try { uint status = MsQuicApi.Api.StreamOpenDelegate( connectionState.Handle, flags, s_streamDelegate, - GCHandle.ToIntPtr(_stateHandle), + GCHandle.ToIntPtr(_state.StateGCHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); @@ -134,14 +145,14 @@ namespace System.Net.Quic.Implementations.MsQuic catch { _state.Handle?.Dispose(); - _stateHandle.Free(); + _state.StateGCHandle.Free(); throw; } if (!connectionState.TryAddStream(this)) { _state.Handle?.Dispose(); - _stateHandle.Free(); + _state.StateGCHandle.Free(); throw new ObjectDisposedException(nameof(QuicConnection)); } @@ -151,14 +162,14 @@ namespace System.Net.Quic.Implementations.MsQuic { NetEventSource.Info( _state, - $"[Stream#{_state.GetHashCode()}] outbound {(_canRead ? "bi" : "uni")}directional stream created " + + $"[Stream#{_state.GetHashCode()}] outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + $"in Connection#{_state.ConnectionState.GetHashCode()}."); } } - internal override bool CanRead => _canRead; + internal override bool CanRead => _disposed == 0 && _state.ReadState < ReadState.Aborted; - internal override bool CanWrite => _canWrite; + internal override bool CanWrite => _disposed == 0 && _state.SendState < SendState.Aborted; internal override long StreamId { @@ -225,10 +236,14 @@ namespace System.Net.Quic.Implementations.MsQuic private async ValueTask HandleWriteStartState(CancellationToken cancellationToken) { - if (!_canWrite) + if (_state.SendState == SendState.Closed) { throw new InvalidOperationException(SR.net_quic_writing_notallowed); } + else if ( _state.SendState == SendState.Aborted) + { + throw new OperationCanceledException(cancellationToken); + } if (cancellationToken.IsCancellationRequested) { @@ -250,11 +265,12 @@ namespace System.Net.Quic.Implementations.MsQuic _started = true; } - // if token was already cancelled, this would execute syncronously + // if token was already cancelled, this would execute synchronously CancellationTokenRegistration registration = cancellationToken.UnsafeRegister(static (s, token) => { var state = (State)s!; bool shouldComplete = false; + lock (state) { if (state.SendState == SendState.None || state.SendState == SendState.Pending) @@ -313,7 +329,7 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); - if (!_canRead) + if (_state.ReadState == ReadState.Closed) { throw new InvalidOperationException(SR.net_quic_reading_notallowed); } @@ -434,6 +450,11 @@ namespace System.Net.Quic.Implementations.MsQuic lock (_state) { + if (_state.SendState < SendState.Aborted) + { + _state.SendState = SendState.Aborted; + } + if (_state.ShutdownWriteState == ShutdownWriteState.None) { _state.ShutdownWriteState = ShutdownWriteState.Canceled; @@ -532,6 +553,11 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); + lock (_state) + { + _state.SendState = SendState.Finished; + } + // it is ok to send shutdown several times, MsQuic will ignore it StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } @@ -596,17 +622,62 @@ namespace System.Net.Quic.Implementations.MsQuic private void Dispose(bool disposing) { - if (_disposed) + int disposed = Interlocked.Exchange(ref _disposed, 1); + if (disposed != 0) { return; } - _disposed = true; - _state.Handle.Dispose(); - Marshal.FreeHGlobal(_state.SendQuicBuffers); - if (_stateHandle.IsAllocated) _stateHandle.Free(); - CleanupSendState(_state); - _state.ConnectionState?.RemoveStream(this); + bool callShutdown = false; + bool abortRead = false; + bool releaseHandles = false; + lock (_state) + { + if (_state.SendState < SendState.Aborted) + { + callShutdown = true; + } + + if (_state.ReadState < ReadState.ReadsCompleted) + { + abortRead = true; + _state.ReadState = ReadState.Aborted; + } + + if (_state.ShutdownState == ShutdownState.None) + { + _state.ShutdownState = ShutdownState.Pending; + } + + // Check if we already got final event. + releaseHandles = Interlocked.Exchange(ref _state.ShutdownDone, 1) == 2; + if (releaseHandles) + { + _state.ShutdownState = ShutdownState.Finished; + } + } + + if (callShutdown) + { + try + { + // Handle race condition when stream can be closed handling SHUTDOWN_COMPLETE. + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); + } catch (ObjectDisposedException) { }; + } + + if (abortRead) + { + try + { + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, 0xffffffff); + } catch (ObjectDisposedException) { }; + } + + if (releaseHandles) + { + _state.Cleanup(); + } if (NetEventSource.Log.IsEnabled()) { @@ -671,8 +742,13 @@ namespace System.Net.Quic.Implementations.MsQuic return MsQuicStatusCodes.Success; } } - catch (Exception) + catch (Exception ex) { + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex.Message}"); + } + return MsQuicStatusCodes.InternalError; } } @@ -824,6 +900,13 @@ namespace System.Net.Quic.Implementations.MsQuic state.ShutdownCompletionSource.SetResult(); } + // Dispose was called before complete event. + bool releaseHandles = Interlocked.Exchange(ref state.ShutdownDone, 2) == 1; + if (releaseHandles) + { + state.Cleanup(); + } + return MsQuicStatusCodes.Success; } @@ -1135,7 +1218,7 @@ namespace System.Net.Quic.Implementations.MsQuic private void ThrowIfDisposed() { - if (_disposed) + if (_disposed == 1) { throw new ObjectDisposedException(nameof(MsQuicStream)); } @@ -1237,7 +1320,12 @@ namespace System.Net.Quic.Implementations.MsQuic /// /// Connection was closed, either by user or by the peer. /// - ConnectionClosed + ConnectionClosed, + + /// + /// Stream is closed for reading. + /// + Closed } private enum ShutdownWriteState @@ -1252,6 +1340,7 @@ namespace System.Net.Quic.Implementations.MsQuic { None, Canceled, + Pending, Finished, ConnectionClosed } @@ -1262,7 +1351,8 @@ namespace System.Net.Quic.Implementations.MsQuic Pending, Aborted, Finished, - ConnectionClosed + ConnectionClosed, + Closed } } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 277265a17a5..e8ac81b3e3b 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -124,6 +124,7 @@ namespace System.Net.Quic.Tests ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; + listener.Dispose(); // No stream opened yet, should return immediately. Assert.True(clientConnection.WaitForAvailableUnidirectionalStreamsAsync().IsCompletedSuccessfully); @@ -133,9 +134,11 @@ namespace System.Net.Quic.Tests ValueTask waitTask = clientConnection.WaitForAvailableUnidirectionalStreamsAsync(); Assert.False(waitTask.IsCompleted); Assert.Throws(() => clientConnection.OpenUnidirectionalStream()); - - // Close the stream, the waitTask should finish as a result. + // Close the streams, the waitTask should finish as a result. stream.Dispose(); + QuicStream newStream = await serverConnection.AcceptStreamAsync(); + newStream.Dispose(); + await waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(10)); } @@ -158,8 +161,10 @@ namespace System.Net.Quic.Tests Assert.False(waitTask.IsCompleted); Assert.Throws(() => clientConnection.OpenBidirectionalStream()); - // Close the stream, the waitTask should finish as a result. + // Close the streams, the waitTask should finish as a result. stream.Dispose(); + QuicStream newStream = await serverConnection.AcceptStreamAsync(); + newStream.Dispose(); await waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(10)); } @@ -190,6 +195,7 @@ namespace System.Net.Quic.Tests [Theory] [MemberData(nameof(WriteData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public async Task WriteTests(int[][] writes, WriteType writeType) { await RunClientServer( diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 908fdb3a3d3..f100657cb46 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -24,24 +24,8 @@ namespace System.Net.Quic.Tests protected override bool UsableAfterCanceledReads => false; protected override bool BlocksOnZeroByteReads => true; - // TODO: These are all hanging, likely due to Stream close behavior. - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task Read_Eof_Returns0(ReadWriteMode mode, bool dataAvailableFirst) => base.Read_Eof_Returns0(mode, dataAvailableFirst); - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task CopyToAsync_AllDataCopied(int byteCount, bool useAsync) => base.CopyToAsync_AllDataCopied(byteCount, useAsync); - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task CopyToAsync_AllDataCopied_Large(bool useAsync) => base.CopyToAsync_AllDataCopied_Large(useAsync); - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task Dispose_ClosesStream(int disposeMode) => base.Dispose_ClosesStream(disposeMode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task Write_DataReadFromDesiredOffset(ReadWriteMode mode) => base.Write_DataReadFromDesiredOffset(mode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] - public override Task Parallel_ReadWriteMultipleStreamsConcurrently() => base.Parallel_ReadWriteMultipleStreamsConcurrently(); - // TODO: new additions, find out the actual reason for hanging [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 4eee9b459d9..9c8574e9827 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -561,7 +561,7 @@ namespace System.Net.Quic.Tests { await ReadUntilAborted().WaitAsync(TimeSpan.FromSeconds(3)); } - catch (QuicStreamAbortedException) { } + catch { } await stream.ShutdownCompleted(); } From ab2ae56a8c596456f284fdf709bd804cef23df84 Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Wed, 7 Jul 2021 12:49:09 -0700 Subject: [PATCH 317/926] Add MetricsEventSource (#54333) * Add MetricsEventSource Our out-of-process tools like dotnet-counters and dotnet-monitor need access to the metrics produced by the new Meter APIs without requiring the app to take any dependency on a separate OpenTelemetry library. System.Diagnostics.Metrics EventSource is a new source designed to let those tools access this data. The EventSource includes high performance in-proc pre-aggregation capable of observing millions of instrument invocations/sec/thread with low CPU overhead. This change does not create any new BCL API surface, the aggregated data is solely exposed by subscribing to the EventSource such as using ETW, EventPipe, Lttng, or EventListener. For anyone wanting in-process APIs to consume the data they could either use MeterListener for unaggregated data or a library such as OpenTelemetry for pre-aggregated data. --- ...System.Diagnostics.DiagnosticSource.csproj | 20 +- .../src/System/Diagnostics/ActivitySource.cs | 2 +- .../System/Diagnostics/DiagnosticListener.cs | 2 +- .../DiagnosticSourceEventSource.cs | 10 +- .../Diagnostics/Metrics/AggregationManager.cs | 365 ++++++ .../System/Diagnostics/Metrics/Aggregator.cs | 52 + .../Diagnostics/Metrics/AggregatorStore.cs | 516 ++++++++ .../Metrics/ExponentialHistogramAggregator.cs | 225 ++++ .../Diagnostics/Metrics/InstrumentState.cs | 42 + .../Metrics/LastValueAggregator.cs | 35 + .../src/System/Diagnostics/Metrics/Meter.cs | 3 + .../Diagnostics/Metrics/MetricsEventSource.cs | 449 +++++++ .../Diagnostics/Metrics/ObjectSequence.cs | 122 ++ .../Metrics/ObjectSequence.netcore.cs | 60 + .../Metrics/ObjectSequence.netfx.cs | 153 +++ .../Diagnostics/Metrics/RateAggregator.cs | 67 ++ .../Diagnostics/Metrics/StringSequence.cs | 116 ++ .../Metrics/StringSequence.netcore.cs | 55 + .../Metrics/StringSequence.netfx.cs | 155 +++ .../tests/AggregationManagerTests.cs | 753 ++++++++++++ .../tests/ExponentialHistogramTests.cs | 323 +++++ .../tests/MetricEventSourceTests.cs | 1036 +++++++++++++++++ ....Diagnostics.DiagnosticSource.Tests.csproj | 22 +- 23 files changed, 4572 insertions(+), 11 deletions(-) create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Aggregator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregatorStore.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ExponentialHistogramAggregator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/InstrumentState.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/LastValueAggregator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netcore.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netfx.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RateAggregator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netcore.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netfx.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/tests/AggregationManagerTests.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/tests/ExponentialHistogramTests.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index 6c8fbb0203c..e1dca02a19d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -1,4 +1,4 @@ - + true false @@ -9,7 +9,8 @@ $(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER - $(DefineConstants);W3C_DEFAULT_ID_FORMAT + $(DefineConstants);W3C_DEFAULT_ID_FORMAT;MEMORYMARSHAL_SUPPORT;OS_ISBROWSER_SUPPORT + true @@ -39,18 +40,32 @@ + + + + + + + + + + + + + + @@ -95,6 +110,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index 283028fc8d4..2f1915fcbe0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -46,7 +46,7 @@ namespace System.Diagnostics }, this); } - GC.KeepAlive(DiagnosticSourceEventSource.Logger); + GC.KeepAlive(DiagnosticSourceEventSource.Log); } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs index e06a92777d1..81e37f7c1ec 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs @@ -144,7 +144,7 @@ namespace System.Diagnostics // Touch DiagnosticSourceEventSource.Logger so we ensure that the // DiagnosticSourceEventSource has been constructed (and thus is responsive to ETW requests to be enabled). - GC.KeepAlive(DiagnosticSourceEventSource.Logger); + GC.KeepAlive(DiagnosticSourceEventSource.Log); } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index e4b5b8c4951..72f7572313f 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -162,7 +162,7 @@ namespace System.Diagnostics [EventSource(Name = "Microsoft-Diagnostics-DiagnosticSource")] internal sealed class DiagnosticSourceEventSource : EventSource { - public static DiagnosticSourceEventSource Logger = new DiagnosticSourceEventSource(); + public static DiagnosticSourceEventSource Log = new DiagnosticSourceEventSource(); public static class Keywords { @@ -1252,7 +1252,7 @@ namespace System.Diagnostics } object? ret = null; // Avoid the exception which can be thrown during accessing the object properties. - try { ret = fetch!.Fetch(obj); } catch (Exception e) { Logger.Message($"Property {objType}.{_propertyName} threw the exception {e}"); } + try { ret = fetch!.Fetch(obj); } catch (Exception e) { Log.Message($"Property {objType}.{_propertyName} threw the exception {e}"); } return ret; } @@ -1314,7 +1314,7 @@ namespace System.Diagnostics } // no implementation of IEnumerable found, return a null fetcher - Logger.Message($"*Enumerate applied to non-enumerable type {type}"); + Log.Message($"*Enumerate applied to non-enumerable type {type}"); return new PropertyFetch(type); } else @@ -1322,13 +1322,13 @@ namespace System.Diagnostics PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName); if (propertyInfo == null) { - Logger.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away."); + Log.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away."); return new PropertyFetch(type); } // Delegate creation below is incompatible with static properties. else if (propertyInfo.GetMethod?.IsStatic == true || propertyInfo.SetMethod?.IsStatic == true) { - Logger.Message($"Property {propertyName} is static."); + Log.Message($"Property {propertyName} is static."); return new PropertyFetch(type); } Type typedPropertyFetcher = typeInfo.IsValueType ? diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs new file mode 100644 index 00000000000..41bb991cda7 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs @@ -0,0 +1,365 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.Versioning; +using System.Security; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Diagnostics.Metrics +{ + [UnsupportedOSPlatform("browser")] + [SecuritySafeCritical] + internal sealed class AggregationManager + { + public const double MinCollectionTimeSecs = 0.1; + private static readonly QuantileAggregation s_defaultHistogramConfig = new QuantileAggregation(new double[] { 0.50, 0.95, 0.99 }); + + // these fields are modified after construction and accessed on multiple threads, use lock(this) to ensure the data + // is synchronized + private readonly List> _instrumentConfigFuncs = new(); + private TimeSpan _collectionPeriod; + + private readonly ConcurrentDictionary _instrumentStates = new(); + private readonly CancellationTokenSource _cts = new(); + private Thread? _collectThread; + private readonly MeterListener _listener; + private int _currentTimeSeries; + private int _currentHistograms; + + private readonly int _maxTimeSeries; + private readonly int _maxHistograms; + private readonly Action _collectMeasurement; + private readonly Action _beginCollection; + private readonly Action _endCollection; + private readonly Action _beginInstrumentMeasurements; + private readonly Action _endInstrumentMeasurements; + private readonly Action _instrumentPublished; + private readonly Action _initialInstrumentEnumerationComplete; + private readonly Action _collectionError; + private readonly Action _timeSeriesLimitReached; + private readonly Action _histogramLimitReached; + + public AggregationManager( + int maxTimeSeries, + int maxHistograms, + Action collectMeasurement, + Action beginCollection, + Action endCollection, + Action beginInstrumentMeasurements, + Action endInstrumentMeasurements, + Action instrumentPublished, + Action initialInstrumentEnumerationComplete, + Action collectionError, + Action timeSeriesLimitReached, + Action histogramLimitReached) + { + _maxTimeSeries = maxTimeSeries; + _maxHistograms = maxHistograms; + _collectMeasurement = collectMeasurement; + _beginCollection = beginCollection; + _endCollection = endCollection; + _beginInstrumentMeasurements = beginInstrumentMeasurements; + _endInstrumentMeasurements = endInstrumentMeasurements; + _instrumentPublished = instrumentPublished; + _initialInstrumentEnumerationComplete = initialInstrumentEnumerationComplete; + _collectionError = collectionError; + _timeSeriesLimitReached = timeSeriesLimitReached; + _histogramLimitReached = histogramLimitReached; + + _listener = new MeterListener() + { + InstrumentPublished = (instrument, listener) => + { + _instrumentPublished(instrument); + InstrumentState? state = GetInstrumentState(instrument); + if (state != null) + { + _beginInstrumentMeasurements(instrument); + listener.EnableMeasurementEvents(instrument, state); + } + }, + MeasurementsCompleted = (instrument, cookie) => + { + _endInstrumentMeasurements(instrument); + RemoveInstrumentState(instrument, (InstrumentState)cookie!); + } + }; + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + _listener.SetMeasurementEventCallback((i, m, l, c) => ((InstrumentState)c!).Update((double)m, l)); + } + + public void Include(string meterName) + { + Include(i => i.Meter.Name == meterName); + } + + public void Include(string meterName, string instrumentName) + { + Include(i => i.Meter.Name == meterName && i.Name == instrumentName); + } + + private void Include(Predicate instrumentFilter) + { + lock (this) + { + _instrumentConfigFuncs.Add(instrumentFilter); + } + } + + public AggregationManager SetCollectionPeriod(TimeSpan collectionPeriod) + { + // The caller, MetricsEventSource, is responsible for enforcing this + Debug.Assert(collectionPeriod.TotalSeconds >= MinCollectionTimeSecs); + lock (this) + { + _collectionPeriod = collectionPeriod; + } + return this; + } + + public void Start() + { + // if already started or already stopped we can't be started again + Debug.Assert(_collectThread == null && !_cts.IsCancellationRequested); + Debug.Assert(_collectionPeriod.TotalSeconds >= MinCollectionTimeSecs); + + // This explicitly uses a Thread and not a Task so that metrics still work + // even when an app is experiencing thread-pool starvation. Although we + // can't make in-proc metrics robust to everything, this is a common enough + // problem in .NET apps that it feels worthwhile to take the precaution. + _collectThread = new Thread(() => CollectWorker(_cts.Token)); + _collectThread.IsBackground = true; + _collectThread.Name = "MetricsEventSource CollectWorker"; + _collectThread.Start(); + + _listener.Start(); + _initialInstrumentEnumerationComplete(); + } + + private void CollectWorker(CancellationToken cancelToken) + { + try + { + double collectionIntervalSecs = -1; + lock (this) + { + collectionIntervalSecs = _collectionPeriod.TotalSeconds; + } + Debug.Assert(collectionIntervalSecs >= MinCollectionTimeSecs); + + DateTime startTime = DateTime.UtcNow; + DateTime intervalStartTime = startTime; + while (!cancelToken.IsCancellationRequested) + { + // intervals end at startTime + X*collectionIntervalSecs. Under normal + // circumstance X increases by 1 each interval, but if the time it + // takes to do collection is very large then we might need to skip + // ahead multiple intervals to catch back up. + // + DateTime now = DateTime.UtcNow; + double secsSinceStart = (now - startTime).TotalSeconds; + double alignUpSecsSinceStart = Math.Ceiling(secsSinceStart / collectionIntervalSecs) * + collectionIntervalSecs; + DateTime nextIntervalStartTime = startTime.AddSeconds(alignUpSecsSinceStart); + + // The delay timer precision isn't exact. We might have a situation + // where in the previous loop iterations intervalStartTime=20.00, + // nextIntervalStartTime=21.00, the timer was supposed to delay for 1s but + // it exited early so we looped around and DateTime.Now=20.99. + // Aligning up from DateTime.Now would give us 21.00 again so we also need to skip + // forward one time interval + DateTime minNextInterval = intervalStartTime.AddSeconds(collectionIntervalSecs); + if (nextIntervalStartTime <= minNextInterval) + { + nextIntervalStartTime = minNextInterval; + } + + // pause until the interval is complete + TimeSpan delayTime = nextIntervalStartTime - now; + if (cancelToken.WaitHandle.WaitOne(delayTime)) + { + // don't do collection if timer may not have run to completion + break; + } + + // collect statistics for the completed interval + _beginCollection(intervalStartTime, nextIntervalStartTime); + Collect(); + _endCollection(intervalStartTime, nextIntervalStartTime); + intervalStartTime = nextIntervalStartTime; + } + } + catch (Exception e) + { + _collectionError(e); + } + } + + public void Dispose() + { + _cts.Cancel(); + if (_collectThread != null) + { + _collectThread.Join(); + _collectThread = null; + } + _listener.Dispose(); + } + + private void RemoveInstrumentState(Instrument instrument, InstrumentState state) + { + _instrumentStates.TryRemove(instrument, out _); + } + + private InstrumentState? GetInstrumentState(Instrument instrument) + { + if (!_instrumentStates.TryGetValue(instrument, out InstrumentState? instrumentState)) + { + lock (this) // protect _instrumentConfigFuncs list + { + foreach (Predicate filter in _instrumentConfigFuncs) + { + if (filter(instrument)) + { + instrumentState = BuildInstrumentState(instrument); + if (instrumentState != null) + { + _instrumentStates.TryAdd(instrument, instrumentState); + // I don't think it is possible for the instrument to be removed immediately + // and instrumentState = _instrumentStates[instrument] should work, but writing + // this defensively. + _instrumentStates.TryGetValue(instrument, out instrumentState); + } + break; + } + } + } + } + return instrumentState; + } + + internal InstrumentState? BuildInstrumentState(Instrument instrument) + { + Func? createAggregatorFunc = GetAggregatorFactory(instrument); + if (createAggregatorFunc == null) + { + return null; + } + Type aggregatorType = createAggregatorFunc.GetType().GenericTypeArguments[0]; + Type instrumentStateType = typeof(InstrumentState<>).MakeGenericType(aggregatorType); + return (InstrumentState)Activator.CreateInstance(instrumentStateType, createAggregatorFunc)!; + } + + private Func? GetAggregatorFactory(Instrument instrument) + { + Type type = instrument.GetType(); + Type? genericDefType = null; + genericDefType = type.IsGenericType ? type.GetGenericTypeDefinition() : null; + if (genericDefType == typeof(Counter<>)) + { + return () => + { + lock (this) + { + return CheckTimeSeriesAllowed() ? new RateSumAggregator() : null; + } + }; + } + else if (genericDefType == typeof(ObservableCounter<>)) + { + return () => + { + lock (this) + { + return CheckTimeSeriesAllowed() ? new RateAggregator() : null; + } + }; + } + else if (genericDefType == typeof(ObservableGauge<>)) + { + return () => + { + lock (this) + { + return CheckTimeSeriesAllowed() ? new LastValue() : null; + } + }; + } + else if (genericDefType == typeof(Histogram<>)) + { + return () => + { + lock (this) + { + return (!CheckTimeSeriesAllowed() || !CheckHistogramAllowed()) ? + null : + new ExponentialHistogramAggregator(s_defaultHistogramConfig); + } + }; + } + else + { + return null; + } + } + + private bool CheckTimeSeriesAllowed() + { + if (_currentTimeSeries < _maxTimeSeries) + { + _currentTimeSeries++; + return true; + } + else if (_currentTimeSeries == _maxTimeSeries) + { + _currentTimeSeries++; + _timeSeriesLimitReached(); + return false; + } + else + { + return false; + } + } + + private bool CheckHistogramAllowed() + { + if (_currentHistograms < _maxHistograms) + { + _currentHistograms++; + return true; + } + else if (_currentHistograms == _maxHistograms) + { + _currentHistograms++; + _histogramLimitReached(); + return false; + } + else + { + return false; + } + } + + internal void Collect() + { + _listener.RecordObservableInstruments(); + + foreach (KeyValuePair kv in _instrumentStates) + { + kv.Value.Collect(kv.Key, (LabeledAggregationStatistics labeledAggStats) => + { + _collectMeasurement(kv.Key, labeledAggStats); + }); + } + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Aggregator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Aggregator.cs new file mode 100644 index 00000000000..37807616807 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Aggregator.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.Tracing; + +namespace System.Diagnostics.Metrics +{ + internal abstract class Aggregator + { + // This can be called concurrently with Collect() + public abstract void Update(double measurement); + + // This can be called concurrently with Update() + public abstract IAggregationStatistics Collect(); + } + + internal interface IAggregationStatistics { } + + internal readonly struct QuantileValue + { + public QuantileValue(double quantile, double value) + { + Quantile = quantile; + Value = value; + } + public double Quantile { get; } + public double Value { get; } + } + + internal sealed class HistogramStatistics : IAggregationStatistics + { + internal HistogramStatistics(QuantileValue[] quantiles) + { + Quantiles = quantiles; + } + + public QuantileValue[] Quantiles { get; } + } + + internal sealed class LabeledAggregationStatistics + { + public LabeledAggregationStatistics(IAggregationStatistics stats, params KeyValuePair[] labels) + { + AggregationStatistics = stats; + Labels = labels; + } + + public KeyValuePair[] Labels { get; } + public IAggregationStatistics AggregationStatistics { get; } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregatorStore.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregatorStore.cs new file mode 100644 index 00000000000..cca119c8050 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregatorStore.cs @@ -0,0 +1,516 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Diagnostics.Metrics +{ + /// + /// AggregatorStore is a high performance map from an unordered list of labels (KeyValuePairs) to an instance of TAggregator + /// + /// The type of Aggregator returned by the store + // + // This is implemented as a two level Dictionary lookup with a number of optimizations applied. The conceptual lookup is: + // 1. Sort ReadOnlySpan> by the key names + // 2. Split ReadOnlySpan> into ReadOnlySpan and ReadOnlySpan + // 3. LabelNameDictionary.Lookup(ReadOnlySpan) -> ConcurrentDictionary + // 4. ConcurrentDictionary.Lookup(ReadOnlySpan) -> TAggregator + // + // There are several things we are optimizing for: + // - CPU instructions per lookup: In the common case the key portion of the KeyValuePairs is unchanged between requests + // and they are given in the same order. This means we can cache the 2nd level concurrent dictionary and the permutation that + // will sort the labels as long as we determine the keys are unchanged from the previous request. The first time a new set of + // keys is observed we call into LabelInstructionCompiler.Create which will determine the canonical sort order, do the 1st level + // lookup, and then return a new _cachedLookupFunc. Invoking _cachedLookupFunc confirms the keys match what was previously + // observed, re-orders the values with the cached permutation and performs the 2nd level lookup against the cached 2nd level + // Dictionary. If we wanted to get really fancy we could have that compiler generate IL that would be JIT compiled, but right now + // LabelInstructionCompiler simply creates a managed data structure (LabelInstructionInterpretter) that encodes the permutation + // in an array of LabelInstructions and the 2nd level dictionary in another field. LabelInstructionInterpretter.GetAggregator + // re-orders the values with a for loop and then does the lookup. Depending on ratio between fast-path and slow-path invocations + // it may also not be a win to further pessimize the slow-path (JIT compilation is expensive) to squeeze yet more cycles out of + // the fast path. + // - Allocations per lookup: Any lookup of 3 or fewer labels on the above fast path is allocation free. We have separate + // dictionaries dependending on the number of labels in the list and the dictionary keys are structures representing fixed size + // lists of strings or objects. For example with two labels the lookup is done in a + // FixedSizeLabelNameDictionary> + // Above 3 labels we have StringSequenceMany and ObjectSequenceMany which wraps an underlying string[] or object?[] respectively. + // Doing a lookup with those types will need to do allocations for those arrays. + // - Total memory footprint per-store: We have a store for every instrument we are tracking and an entry in the 2nd level + // dictionary for every label set. This can add up to a lot of entries. Splitting the label sets into keys and values means we + // only need to store each unique key list once (as the key of the 1st level dictionary). It is common for all labelsets on an + // instrument to have the same keys so this can be a sizable savings. We also use a union to store the 1st level dictionaries + // for different label set sizes because most instruments always specify labelsets with the same number of labels (most likely + // zero). + [System.Security.SecuritySafeCritical] // using SecurityCritical type ReadOnlySpan + internal struct AggregatorStore where TAggregator : Aggregator + { + // this union can be: + // null + // TAggregator + // FixedSizeLabelNameDictionary> + // FixedSizeLabelNameDictionary> + // FixedSizeLabelNameDictionary> + // FixedSizeLabelNameDictionary> + // MultiSizeLabelNameDictionary - this is used when we need to store more than one of the above union items + private volatile object? _stateUnion; + private volatile AggregatorLookupFunc? _cachedLookupFunc; + private readonly Func _createAggregatorFunc; + + public AggregatorStore(Func createAggregator) + { + _stateUnion = null; + _cachedLookupFunc = null; + _createAggregatorFunc = createAggregator; + } + + public TAggregator? GetAggregator(ReadOnlySpan> labels) + { + AggregatorLookupFunc? lookupFunc = _cachedLookupFunc; + if (lookupFunc != null) + { + if (lookupFunc(labels, out TAggregator? aggregator)) return aggregator; + } + + // slow path, label names have changed from what the lookupFunc cached so we need to + // rebuild it + return GetAggregatorSlow(labels); + } + + private TAggregator? GetAggregatorSlow(ReadOnlySpan> labels) + { + AggregatorLookupFunc lookupFunc = LabelInstructionCompiler.Create(ref this, _createAggregatorFunc, labels); + _cachedLookupFunc = lookupFunc; + bool match = lookupFunc(labels, out TAggregator? aggregator); + Debug.Assert(match); + return aggregator; + } + + public void Collect(Action visitFunc) + { + object? stateUnion = _stateUnion; + switch (_stateUnion) + { + case TAggregator agg: + IAggregationStatistics stats = agg.Collect(); + visitFunc(new LabeledAggregationStatistics(stats)); + break; + + case FixedSizeLabelNameDictionary aggs1: + aggs1.Collect(visitFunc); + break; + + case FixedSizeLabelNameDictionary aggs2: + aggs2.Collect(visitFunc); + break; + + case FixedSizeLabelNameDictionary aggs3: + aggs3.Collect(visitFunc); + break; + + case FixedSizeLabelNameDictionary aggsMany: + aggsMany.Collect(visitFunc); + break; + + case MultiSizeLabelNameDictionary aggsMultiSize: + aggsMultiSize.Collect(visitFunc); + break; + } + } + + + public TAggregator? GetAggregator() + { + while (true) + { + object? state = _stateUnion; + if (state == null) + { + // running this delegate will increment the counter for the number of time series + // even though in the rare race condition we don't store it. If we wanted to be perfectly + // accurate we need to decrement the counter again, but I don't think mitigating that + // error is worth the complexity + TAggregator? newState = _createAggregatorFunc(); + if (newState == null) + { + return newState; + } + if (Interlocked.CompareExchange(ref _stateUnion, newState, null) is null) + { + return newState; + } + continue; + } + else if (state is TAggregator aggState) + { + return aggState; + } + else if (state is MultiSizeLabelNameDictionary multiSizeState) + { + return multiSizeState.GetNoLabelAggregator(_createAggregatorFunc); + } + else + { + MultiSizeLabelNameDictionary newState = new(state); + if (Interlocked.CompareExchange(ref _stateUnion, newState, state) == state) + { + return newState.GetNoLabelAggregator(_createAggregatorFunc); + } + continue; + } + } + } + + public ConcurrentDictionary GetLabelValuesDictionary(in TStringSequence names) + where TStringSequence : IStringSequence, IEquatable + where TObjectSequence : IObjectSequence, IEquatable + { + while (true) + { + object? state = _stateUnion; + if (state == null) + { + FixedSizeLabelNameDictionary newState = new(); + if (Interlocked.CompareExchange(ref _stateUnion, newState, null) is null) + { + return newState.GetValuesDictionary(names); + } + continue; + } + else if (state is FixedSizeLabelNameDictionary fixedState) + { + return fixedState.GetValuesDictionary(names); + } + else if (state is MultiSizeLabelNameDictionary multiSizeState) + { + return multiSizeState.GetFixedSizeLabelNameDictionary().GetValuesDictionary(names); + } + else + { + MultiSizeLabelNameDictionary newState = new(state); + if (Interlocked.CompareExchange(ref _stateUnion, newState, state) == state) + { + return newState.GetFixedSizeLabelNameDictionary().GetValuesDictionary(names); + } + continue; + } + } + } + } + + internal class MultiSizeLabelNameDictionary where TAggregator : Aggregator + { + private TAggregator? NoLabelAggregator; + private FixedSizeLabelNameDictionary? Label1; + private FixedSizeLabelNameDictionary? Label2; + private FixedSizeLabelNameDictionary? Label3; + private FixedSizeLabelNameDictionary? LabelMany; + + public MultiSizeLabelNameDictionary(object initialLabelNameDict) + { + NoLabelAggregator = null; + Label1 = null; + Label2 = null; + Label3 = null; + LabelMany = null; + switch (initialLabelNameDict) + { + case TAggregator val0: + NoLabelAggregator = val0; + break; + + case FixedSizeLabelNameDictionary val1: + Label1 = val1; + break; + + case FixedSizeLabelNameDictionary val2: + Label2 = val2; + break; + + case FixedSizeLabelNameDictionary val3: + Label3 = val3; + break; + + case FixedSizeLabelNameDictionary valMany: + LabelMany = valMany; + break; + } + } + + public TAggregator? GetNoLabelAggregator(Func createFunc) + { + if (NoLabelAggregator == null) + { + TAggregator? aggregator = createFunc(); + if (aggregator != null) + { + Interlocked.CompareExchange(ref NoLabelAggregator, aggregator, null); + } + } + return NoLabelAggregator; + } + + public FixedSizeLabelNameDictionary GetFixedSizeLabelNameDictionary() + where TStringSequence : IStringSequence, IEquatable + where TObjectSequence : IObjectSequence, IEquatable + { + TStringSequence? seq = default; + switch (seq) + { + case StringSequence1: + if (Label1 == null) + { + Interlocked.CompareExchange(ref Label1, new FixedSizeLabelNameDictionary(), null); + } + return (FixedSizeLabelNameDictionary)(object)Label1; + + case StringSequence2: + if (Label2 == null) + { + Interlocked.CompareExchange(ref Label2, new FixedSizeLabelNameDictionary(), null); + } + return (FixedSizeLabelNameDictionary)(object)Label2; + + case StringSequence3: + if (Label3 == null) + { + Interlocked.CompareExchange(ref Label3, new FixedSizeLabelNameDictionary(), null); + } + return (FixedSizeLabelNameDictionary)(object)Label3; + + case StringSequenceMany: + if (LabelMany == null) + { + Interlocked.CompareExchange(ref LabelMany, new FixedSizeLabelNameDictionary(), null); + } + return (FixedSizeLabelNameDictionary)(object)LabelMany; + + default: + // we should never get here unless this library has a bug + Debug.Fail("Unexpected sequence type"); + return null; + } + } + + public void Collect(Action visitFunc) + { + if (NoLabelAggregator != null) + { + IAggregationStatistics stats = NoLabelAggregator.Collect(); + visitFunc(new LabeledAggregationStatistics(stats)); + } + Label1?.Collect(visitFunc); + Label2?.Collect(visitFunc); + Label3?.Collect(visitFunc); + LabelMany?.Collect(visitFunc); + } + } + + internal struct LabelInstruction + { + public LabelInstruction(int sourceIndex, string labelName) + { + SourceIndex = sourceIndex; + LabelName = labelName; + } + public readonly int SourceIndex { get; } + public readonly string LabelName { get; } + } + + internal delegate bool AggregatorLookupFunc(ReadOnlySpan> labels, out TAggregator? aggregator); + + [System.Security.SecurityCritical] // using SecurityCritical type ReadOnlySpan + internal static class LabelInstructionCompiler + { + public static AggregatorLookupFunc Create( + ref AggregatorStore aggregatorStore, + Func createAggregatorFunc, + ReadOnlySpan> labels) + where TAggregator : Aggregator + { + LabelInstruction[] instructions = Compile(labels); + Array.Sort(instructions, (LabelInstruction a, LabelInstruction b) => string.CompareOrdinal(a.LabelName, b.LabelName)); + int expectedLabels = labels.Length; + switch (instructions.Length) + { + case 0: + TAggregator? defaultAggregator = aggregatorStore.GetAggregator(); + return (ReadOnlySpan> l, out TAggregator? aggregator) => + { + if (l.Length != expectedLabels) + { + aggregator = null; + return false; + } + aggregator = defaultAggregator; + return true; + }; + + case 1: + StringSequence1 names1 = new StringSequence1(instructions[0].LabelName); + ConcurrentDictionary valuesDict1 = + aggregatorStore.GetLabelValuesDictionary(names1); + LabelInstructionInterpretter interpretter1 = + new LabelInstructionInterpretter( + expectedLabels, instructions, valuesDict1, createAggregatorFunc); + return interpretter1.GetAggregator; + + case 2: + StringSequence2 names2 = new StringSequence2(instructions[0].LabelName, instructions[1].LabelName); + ConcurrentDictionary valuesDict2 = + aggregatorStore.GetLabelValuesDictionary(names2); + LabelInstructionInterpretter interpretter2 = + new LabelInstructionInterpretter( + expectedLabels, instructions, valuesDict2, createAggregatorFunc); + return interpretter2.GetAggregator; + + case 3: + StringSequence3 names3 = new StringSequence3(instructions[0].LabelName, instructions[1].LabelName, + instructions[2].LabelName); + ConcurrentDictionary valuesDict3 = + aggregatorStore.GetLabelValuesDictionary(names3); + LabelInstructionInterpretter interpretter3 = + new LabelInstructionInterpretter( + expectedLabels, instructions, valuesDict3, createAggregatorFunc); + return interpretter3.GetAggregator; + + default: + string[] labelNames = new string[instructions.Length]; + for (int i = 0; i < instructions.Length; i++) + { + labelNames[i] = instructions[i].LabelName; + } + StringSequenceMany namesMany = new StringSequenceMany(labelNames); + ConcurrentDictionary valuesDictMany = + aggregatorStore.GetLabelValuesDictionary(namesMany); + LabelInstructionInterpretter interpretter4 = + new LabelInstructionInterpretter( + expectedLabels, instructions, valuesDictMany, createAggregatorFunc); + return interpretter4.GetAggregator; + } + } + + private static LabelInstruction[] Compile(ReadOnlySpan> labels) + { + LabelInstruction[] valueFetches = new LabelInstruction[labels.Length]; + for (int i = 0; i < labels.Length; i++) + { + valueFetches[i] = new LabelInstruction(i, labels[i].Key); + } + + return valueFetches; + } + } + + [System.Security.SecurityCritical] // using SecurityCritical type ReadOnlySpan + internal class LabelInstructionInterpretter + where TObjectSequence : struct, IObjectSequence, IEquatable + where TAggregator : Aggregator + { + private int _expectedLabelCount; + private LabelInstruction[] _instructions; + private ConcurrentDictionary _valuesDict; + private Func _createAggregator; + + public LabelInstructionInterpretter( + int expectedLabelCount, + LabelInstruction[] instructions, + ConcurrentDictionary valuesDict, + Func createAggregator) + { + _expectedLabelCount = expectedLabelCount; + _instructions = instructions; + _valuesDict = valuesDict; + _createAggregator = _ => createAggregator(); + } + + // Returns true if label keys matched what was expected + // aggregator may be null even when true is returned if + // we have hit the storage limits + public bool GetAggregator( + ReadOnlySpan> labels, + out TAggregator? aggregator) + { + aggregator = null; + if (labels.Length != _expectedLabelCount) + { + return false; + } + + TObjectSequence values = default; + if (values is ObjectSequenceMany) + { + values = (TObjectSequence)(object)new ObjectSequenceMany(new object[_expectedLabelCount]); + } +#if MEMORYMARSHAL_SUPPORT + Span indexedValues = values.AsSpan(); +#else + ref TObjectSequence indexedValues = ref values; +#endif + for (int i = 0; i < _instructions.Length; i++) + { + LabelInstruction instr = _instructions[i]; + if (instr.LabelName != labels[instr.SourceIndex].Key) + { + return false; + } + indexedValues[i] = labels[instr.SourceIndex].Value; + } + + if (!_valuesDict.TryGetValue(values, out aggregator)) + { + // running this delegate will increment the counter for the number of time series + // even though in the rare race condition we don't store it. If we wanted to be perfectly + // accurate we need to decrement the counter again, but I don't think mitigating that + // error is worth the complexity + aggregator = _createAggregator(values); + if (aggregator is null) + { + return true; + } + aggregator = _valuesDict.GetOrAdd(values, aggregator); + } + return true; + } + } + + internal class FixedSizeLabelNameDictionary : + ConcurrentDictionary> + where TAggregator : Aggregator + where TStringSequence : IStringSequence, IEquatable + where TObjectSequence : IObjectSequence, IEquatable + { + public void Collect(Action visitFunc) + { + foreach (KeyValuePair> kvName in this) + { +#if MEMORYMARSHAL_SUPPORT + Span indexedNames = kvName.Key.AsSpan(); +#else + TStringSequence indexedNames = kvName.Key; +#endif + foreach (KeyValuePair kvValue in kvName.Value) + { +#if MEMORYMARSHAL_SUPPORT + Span indexedValues = kvValue.Key.AsSpan(); +#else + TObjectSequence indexedValues = kvValue.Key; +#endif + KeyValuePair[] labels = new KeyValuePair[indexedNames.Length]; + for (int i = 0; i < labels.Length; i++) + { + labels[i] = new KeyValuePair(indexedNames[i], indexedValues[i]?.ToString() ?? ""); + } + IAggregationStatistics stats = kvValue.Value.Collect(); + visitFunc(new LabeledAggregationStatistics(stats, labels)); + } + } + } + + public ConcurrentDictionary GetValuesDictionary(in TStringSequence names) => + GetOrAdd(names, _ => new ConcurrentDictionary()); + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ExponentialHistogramAggregator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ExponentialHistogramAggregator.cs new file mode 100644 index 00000000000..b12603b85f0 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ExponentialHistogramAggregator.cs @@ -0,0 +1,225 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Diagnostics.Metrics +{ + internal sealed class QuantileAggregation + { + public QuantileAggregation(params double[] quantiles) + { + Quantiles = quantiles; + Array.Sort(Quantiles); + } + + public double[] Quantiles { get; set; } + public double MaxRelativeError { get; set; } = 0.001; + } + + + + // This histogram ensures that the quantiles reported from the histogram are within some bounded % error of the correct + // value. More mathematically, if we have a set of measurements where quantile X = Y, the histogram should always report a + // value Y` where Y*(1-E) <= Y` <= Y*(1+E). E is our allowable error, so if E = 0.01 then the reported value Y` is + // between 0.99*Y and 1.01*Y. We achieve this by ensuring that if a bucket holds measurements from M_min to M_max + // then M_max - M_min <= M_min*E. We can determine which bucket must hold quantile X and we know that all values assigned to + // the bucket are within the error bound if we approximate the result as M_min. + // Note: we should be able to refine this to return the bucket midpoint rather than bucket lower bound, halving the number of + // buckets to achieve the same error bound. + // + // Implementation: The histogram buckets are implemented as an array of arrays (a tree). The top level has a fixed 4096 entries + // corresponding to every possible sign+exponent in the encoding of a double (IEEE 754 spec). The 2nd level has variable size + // depending on how many buckets are needed to achieve the error bounds. For ease of insertion we round the 2nd level size up to + // the nearest power of 2. This lets us mask off the first k bits in the mantissa to map a measurement to one of 2^k 2nd level + // buckets. The top level array is pre-allocated but the 2nd level arrays are created on demand. + // + // PERF Note: This histogram has a fast Update() but the _counters array has a sizable memory footprint (32KB+ on 64 bit) + // It is probably well suited for tracking 10s or maybe 100s of histograms but if we wanted to go higher + // we probably want to trade a little more CPU cost in Update() + code complexity to avoid eagerly allocating 4096 + // top level entries. + internal sealed class ExponentialHistogramAggregator : Aggregator + { + private const int ExponentArraySize = 4096; + private const int ExponentShift = 52; + private const double MinRelativeError = 0.0001; + + private readonly QuantileAggregation _config; + private int[]?[] _counters; + private int _count; + private readonly int _mantissaMax; + private readonly int _mantissaMask; + private readonly int _mantissaShift; + + private struct Bucket + { + public Bucket(double value, int count) + { + Value = value; + Count = count; + } + public double Value; + public int Count; + } + + public ExponentialHistogramAggregator(QuantileAggregation config) + { + _config = config; + _counters = new int[ExponentArraySize][]; + if (_config.MaxRelativeError < MinRelativeError) + { + // Ensure that we don't create enormous histograms trying to get overly high precision + throw new ArgumentException(); + } + int mantissaBits = (int)Math.Ceiling(Math.Log(1 / _config.MaxRelativeError, 2)) - 1; + _mantissaShift = 52 - mantissaBits; + _mantissaMax = 1 << mantissaBits; + _mantissaMask = _mantissaMax - 1; + } + + public override IAggregationStatistics Collect() + { + int[]?[] counters; + int count; + lock (this) + { + counters = _counters; + count = _count; + _counters = new int[ExponentArraySize][]; + _count = 0; + } + + QuantileValue[] quantiles = new QuantileValue[_config.Quantiles.Length]; + int nextQuantileIndex = 0; + if (nextQuantileIndex == _config.Quantiles.Length) + { + return new HistogramStatistics(quantiles); + } + + // Reduce the count if there are any NaN or +/-Infinity values that were logged + count -= GetInvalidCount(counters); + + // Consider each bucket to have N entries in it, and each entry has value GetBucketCanonicalValue(). + // If all these entries were inserted in a sorted array, we are trying to find the value of the entry with + // index=target. + int target = QuantileToRank(_config.Quantiles[nextQuantileIndex], count); + + // the total number of entries in all buckets iterated so far + int cur = 0; + foreach (Bucket b in IterateBuckets(counters)) + { + cur += b.Count; + while (cur > target) + { + quantiles[nextQuantileIndex] = new QuantileValue( + _config.Quantiles[nextQuantileIndex], b.Value); + nextQuantileIndex++; + if (nextQuantileIndex == _config.Quantiles.Length) + { + return new HistogramStatistics(quantiles); + } + target = QuantileToRank(_config.Quantiles[nextQuantileIndex], count); + } + } + + Debug.Assert(count == 0); + return new HistogramStatistics(Array.Empty()); + } + + private int GetInvalidCount(int[]?[] counters) + { + int[]? positiveInfAndNan = counters[ExponentArraySize / 2 - 1]; + int[]? negativeInfAndNan = counters[ExponentArraySize - 1]; + int count = 0; + if (positiveInfAndNan != null) + { + foreach (int bucketCount in positiveInfAndNan) + { + count += bucketCount; + } + } + if (negativeInfAndNan != null) + { + foreach (int bucketCount in negativeInfAndNan) + { + count += bucketCount; + } + } + return count; + } + + private IEnumerable IterateBuckets(int[]?[] counters) + { + // iterate over the negative exponent buckets + const int LowestNegativeOffset = ExponentArraySize / 2; + // exponent = ExponentArraySize-1 encodes infinity and NaN, which we want to ignore + for (int exponent = ExponentArraySize-2; exponent >= LowestNegativeOffset; exponent--) + { + int[]? mantissaCounts = counters[exponent]; + if (mantissaCounts == null) + { + continue; + } + for (int mantissa = _mantissaMax-1; mantissa >= 0; mantissa--) + { + int count = mantissaCounts[mantissa]; + if (count > 0) + { + yield return new Bucket(GetBucketCanonicalValue(exponent, mantissa), count); + } + } + } + + // iterate over the positive exponent buckets + // exponent = lowestNegativeOffset-1 encodes infinity and NaN, which we want to ignore + for (int exponent = 0; exponent < LowestNegativeOffset-1; exponent++) + { + int[]? mantissaCounts = counters[exponent]; + if (mantissaCounts == null) + { + continue; + } + for (int mantissa = 0; mantissa < _mantissaMax; mantissa++) + { + int count = mantissaCounts[mantissa]; + if (count > 0) + { + yield return new Bucket(GetBucketCanonicalValue(exponent, mantissa), count); + } + } + } + } + + public override void Update(double measurement) + { + lock (this) + { + // This is relying on the bit representation of IEEE 754 to decompose + // the double. The sign bit + exponent bits land in exponent, the + // remainder lands in mantissa. + // the bucketing precision comes entirely from how many significant + // bits of the mantissa are preserved. + ulong bits = (ulong)BitConverter.DoubleToInt64Bits(measurement); + int exponent = (int)(bits >> ExponentShift); + int mantissa = (int)(bits >> _mantissaShift) & _mantissaMask; + ref int[]? mantissaCounts = ref _counters[exponent]; + mantissaCounts ??= new int[_mantissaMax]; + mantissaCounts[mantissa]++; + _count++; + } + } + + private int QuantileToRank(double quantile, int count) + { + return Math.Min(Math.Max(0, (int)(quantile * count)), count - 1); + } + + // This is the upper bound for negative valued buckets and the + // lower bound for positive valued buckets + private double GetBucketCanonicalValue(int exponent, int mantissa) + { + long bits = ((long)exponent << ExponentShift) | ((long)mantissa << _mantissaShift); + return BitConverter.Int64BitsToDouble(bits); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/InstrumentState.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/InstrumentState.cs new file mode 100644 index 00000000000..fa1b2612aad --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/InstrumentState.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Security; + +namespace System.Diagnostics.Metrics +{ + internal abstract class InstrumentState + { + // This can be called concurrently with Collect() + [SecuritySafeCritical] + public abstract void Update(double measurement, ReadOnlySpan> labels); + + // This can be called concurrently with Update() + public abstract void Collect(Instrument instrument, Action aggregationVisitFunc); + } + + + internal sealed class InstrumentState : InstrumentState + where TAggregator : Aggregator + { + private AggregatorStore _aggregatorStore; + + public InstrumentState(Func createAggregatorFunc) + { + _aggregatorStore = new AggregatorStore(createAggregatorFunc); + } + + public override void Collect(Instrument instrument, Action aggregationVisitFunc) + { + _aggregatorStore.Collect(aggregationVisitFunc); + } + + [SecuritySafeCritical] + public override void Update(double measurement, ReadOnlySpan> labels) + { + TAggregator? aggregator = _aggregatorStore.GetAggregator(labels); + aggregator?.Update(measurement); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/LastValueAggregator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/LastValueAggregator.cs new file mode 100644 index 00000000000..a16026222b7 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/LastValueAggregator.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.Metrics +{ + internal sealed class LastValue : Aggregator + { + private double? _lastValue; + + public override void Update(double value) + { + _lastValue = value; + } + + public override IAggregationStatistics Collect() + { + lock (this) + { + LastValueStatistics stats = new LastValueStatistics(_lastValue); + _lastValue = null; + return stats; + } + } + } + + internal sealed class LastValueStatistics : IAggregationStatistics + { + internal LastValueStatistics(double? lastValue) + { + LastValue = lastValue; + } + + public double? LastValue { get; } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs index db23a765f8f..7aa624c68be 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs @@ -44,6 +44,9 @@ namespace System.Diagnostics.Metrics { s_allMeters.Add(this); } + + // Ensure the metrics EventSource has been created in case we need to log this meter + GC.KeepAlive(MetricsEventSource.Log); } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs new file mode 100644 index 00000000000..669039c0c80 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -0,0 +1,449 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Tracing; +using System.Globalization; +using System.Runtime.Versioning; +using System.Text; + +namespace System.Diagnostics.Metrics +{ + /// + /// This EventSource is intended to let out-of-process tools (such as dotnet-counters) do + /// ad-hoc monitoring for the new Instrument APIs. This source only supports one listener + /// at a time. Each new listener will overwrite the configuration about which metrics + /// are being collected and the time interval for the collection. In the future it would + /// be nice to have support for multiple concurrent out-of-proc tools but EventSource's + /// handling of filter arguments doesn't make that easy right now. + /// + /// Configuration - The EventSource accepts the following filter arguments: + /// - SessionId - An arbitrary opaque string that will be sent back to the listener in + /// many event payloads. If listener B reconfigures the EventSource while listener A + /// is still running it is possible that each of them will observe some of the events + /// that were generated using the other's requested configuration. Filtering on sessionId + /// allows each listener to ignore those events. + /// - RefreshInterval - The frequency in seconds for sending the metric time series data. + /// The format is anything parsable using double.TryParse(). Any + /// value less than AggregationManager.MinCollectionTimeSecs (currently 0.1 sec) is rounded + /// up to the minimum. If not specified the default interval is 1 second. + /// - Metrics - A semicolon separated list. Each item in the list is either the name of a + /// Meter or 'meter_name\instrument_name'. For example "Foo;System.Runtime\gc-gen0-size" + /// would include all instruments in the 'Foo' meter and the single 'gc-gen0-size' instrument + /// in the 'System.Runtime' meter. + /// - MaxTimeSeries - An integer that sets an upper bound on the number of time series + /// this event source will track. Because instruments can have unbounded sets of tags + /// even specifying a single metric could create unbounded load without this limit. + /// - MaxHistograms - An integer that sets an upper bound on the number of histograms + /// this event source will track. This allows setting a tighter bound on histograms + /// than time series in general given that histograms use considerably more memory. + /// + [EventSource(Name = "System.Diagnostics.Metrics")] + internal sealed class MetricsEventSource : EventSource + { + public static readonly MetricsEventSource Log = new(); + + public static class Keywords + { + /// + /// Indicates diagnostics messages from MetricsEventSource should be included. + /// + public const EventKeywords Messages = (EventKeywords)0x1; + /// + /// Indicates that all the time series data points should be included + /// + public const EventKeywords TimeSeriesValues = (EventKeywords)0x2; + /// + /// Indicates that instrument published notifications should be included + /// + public const EventKeywords InstrumentPublishing = (EventKeywords)0x4; + } + + private CommandHandler _handler; + + private MetricsEventSource() + { + _handler = new CommandHandler(); + } + + /// + /// Used to send ad-hoc diagnostics to humans. + /// + [Event(1, Keywords = Keywords.Messages)] + public void Message(string? Message) + { + WriteEvent(1, Message); + } + + [Event(2, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void CollectionStart(string sessionId, DateTime intervalStartTime, DateTime intervalEndTime) + { + WriteEvent(2, sessionId, intervalStartTime, intervalEndTime); + } + + [Event(3, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void CollectionStop(string sessionId, DateTime intervalStartTime, DateTime intervalEndTime) + { + WriteEvent(3, sessionId, intervalStartTime, intervalEndTime); + } + + [Event(4, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void CounterRateValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string rate) + { + WriteEvent(4, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, rate); + } + + [Event(5, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void GaugeValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string lastValue) + { + WriteEvent(5, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, lastValue); + } + + [Event(6, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void HistogramValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string quantiles) + { + WriteEvent(6, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, quantiles); + } + + // Sent when we begin to monitor the value of a intrument, either because new session filter arguments changed subscriptions + // or because an instrument matching the pre-existing filter has just been created. This event precedes all *MetricPublished events + // for the same named instrument. + [Event(7, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void BeginInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) + { + WriteEvent(7, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); + } + + // Sent when we stop monitoring the value of a intrument, either because new session filter arguments changed subscriptions + // or because the Meter has been disposed. + [Event(8, Keywords = Keywords.TimeSeriesValues)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void EndInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) + { + WriteEvent(8, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); + } + + [Event(9, Keywords = Keywords.TimeSeriesValues | Keywords.Messages | Keywords.InstrumentPublishing)] + public void Error(string sessionId, string errorMessage, string errorStack) + { + WriteEvent(9, sessionId, errorMessage, errorStack); + } + + [Event(10, Keywords = Keywords.TimeSeriesValues | Keywords.InstrumentPublishing)] + public void InitialInstrumentEnumerationComplete(string sessionId) + { + WriteEvent(10, sessionId); + } + + [Event(11, Keywords = Keywords.InstrumentPublishing)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")] + public void InstrumentPublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description) + { + WriteEvent(11, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? ""); + } + + [Event(12, Keywords = Keywords.TimeSeriesValues)] + public void TimeSeriesLimitReached(string sessionId) + { + WriteEvent(12, sessionId); + } + + [Event(13, Keywords = Keywords.TimeSeriesValues)] + public void HistogramLimitReached(string sessionId) + { + WriteEvent(13, sessionId); + } + + /// + /// Called when the EventSource gets a command from a EventListener or ETW. + /// + [NonEvent] + protected override void OnEventCommand(EventCommandEventArgs command) + { + lock (this) + { + _handler.OnEventCommand(command); + } + } + + // EventSource assumes that every method defined on it represents an event. + // Methods that are declared explicitly can use the [NonEvent] attribute to opt-out but + // lambdas can't. Putting all the command handling logic in this nested class + // is a simpler way to opt everything out in bulk. + private sealed class CommandHandler + { + private AggregationManager? _aggregationManager; + private string _sessionId = ""; + + public void OnEventCommand(EventCommandEventArgs command) + { + try + { +#if OS_ISBROWSER_SUPPORT + if (OperatingSystem.IsBrowser()) + { + // AggregationManager uses a dedicated thread to avoid losing data for apps experiencing threadpool starvation + // and browser doesn't support Thread.Start() + // + // This limitation shouldn't really matter because browser also doesn't support out-of-proc EventSource communication + // which is the intended scenario for this EventSource. If it matters in the future AggregationManager can be + // modified to have some other fallback path that works for browser. + Log.Error("", "System.Diagnostics.Metrics EventSource not supported on browser", ""); + return; + } +#endif + if (command.Command == EventCommand.Update || command.Command == EventCommand.Disable || + command.Command == EventCommand.Enable) + { + if (_aggregationManager != null) + { + _aggregationManager.Dispose(); + _aggregationManager = null; + Log.Message($"Previous session with id {_sessionId} is stopped"); + } + _sessionId = ""; + } + if ((command.Command == EventCommand.Update || command.Command == EventCommand.Enable) && + command.Arguments != null) + { + if (command.Arguments!.TryGetValue("SessionId", out string? id)) + { + _sessionId = id!; + Log.Message($"SessionId argument received: {_sessionId}"); + } + else + { + _sessionId = System.Guid.NewGuid().ToString(); + Log.Message($"New session started. SessionId auto-generated: {_sessionId}"); + } + + + double defaultIntervalSecs = 1; + Debug.Assert(AggregationManager.MinCollectionTimeSecs <= defaultIntervalSecs); + double refreshIntervalSecs = defaultIntervalSecs; + if (command.Arguments!.TryGetValue("RefreshInterval", out string? refreshInterval)) + { + Log.Message($"RefreshInterval argument received: {refreshInterval}"); + if (!double.TryParse(refreshInterval, out refreshIntervalSecs)) + { + Log.Message($"Failed to parse RefreshInterval. Using default {defaultIntervalSecs}s."); + refreshIntervalSecs = defaultIntervalSecs; + } + else if (refreshIntervalSecs < AggregationManager.MinCollectionTimeSecs) + { + Log.Message($"RefreshInterval too small. Using minimum interval {AggregationManager.MinCollectionTimeSecs} seconds."); + refreshIntervalSecs = AggregationManager.MinCollectionTimeSecs; + } + } + else + { + Log.Message($"No RefreshInterval argument received. Using default {defaultIntervalSecs}s."); + refreshIntervalSecs = defaultIntervalSecs; + } + + int defaultMaxTimeSeries = 1000; + int maxTimeSeries; + if (command.Arguments!.TryGetValue("MaxTimeSeries", out string? maxTimeSeriesString)) + { + Log.Message($"MaxTimeSeries argument received: {maxTimeSeriesString}"); + if (!int.TryParse(maxTimeSeriesString, out maxTimeSeries)) + { + Log.Message($"Failed to parse MaxTimeSeries. Using default {defaultMaxTimeSeries}"); + maxTimeSeries = defaultMaxTimeSeries; + } + } + else + { + Log.Message($"No MaxTimeSeries argument received. Using default {defaultMaxTimeSeries}"); + maxTimeSeries = defaultMaxTimeSeries; + } + + int defaultMaxHistograms = 20; + int maxHistograms; + if (command.Arguments!.TryGetValue("MaxHistograms", out string? maxHistogramsString)) + { + Log.Message($"MaxHistograms argument received: {maxHistogramsString}"); + if (!int.TryParse(maxHistogramsString, out maxHistograms)) + { + Log.Message($"Failed to parse MaxHistograms. Using default {defaultMaxHistograms}"); + maxHistograms = defaultMaxHistograms; + } + } + else + { + Log.Message($"No MaxHistogram argument received. Using default {defaultMaxHistograms}"); + maxHistograms = defaultMaxHistograms; + } + + string sessionId = _sessionId; + _aggregationManager = new AggregationManager( + maxTimeSeries, + maxHistograms, + (i, s) => TransmitMetricValue(i, s, sessionId), + (startIntervalTime, endIntervalTime) => Log.CollectionStart(sessionId, startIntervalTime, endIntervalTime), + (startIntervalTime, endIntervalTime) => Log.CollectionStop(sessionId, startIntervalTime, endIntervalTime), + i => Log.BeginInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description), + i => Log.EndInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description), + i => Log.InstrumentPublished(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description), + () => Log.InitialInstrumentEnumerationComplete(sessionId), + e => Log.Error(sessionId, e.Message, e.StackTrace?.ToString() ?? ""), + () => Log.TimeSeriesLimitReached(sessionId), + () => Log.HistogramLimitReached(sessionId)); + + _aggregationManager.SetCollectionPeriod(TimeSpan.FromSeconds(refreshIntervalSecs)); + + if (command.Arguments!.TryGetValue("Metrics", out string? metricsSpecs)) + { + Log.Message($"Metrics argument received: {metricsSpecs}"); + ParseSpecs(metricsSpecs); + } + else + { + Log.Message("No Metrics argument received"); + } + + _aggregationManager.Start(); + } + } + catch (Exception e) when (LogError(e)) + { + // this will never run + } + } + + private bool LogError(Exception e) + { + Log.Error(_sessionId, e.Message, e.StackTrace?.ToString() ?? ""); + // this code runs as an exception filter + // returning false ensures the catch handler isn't run + return false; + } + + private static readonly char[] s_instrumentSeperators = new char[] { '\r', '\n', ',', ';' }; + + [UnsupportedOSPlatform("browser")] + private void ParseSpecs(string? metricsSpecs) + { + if (metricsSpecs == null) + { + return; + } + string[] specStrings = metricsSpecs.Split(s_instrumentSeperators, StringSplitOptions.RemoveEmptyEntries); + foreach (string specString in specStrings) + { + if (!MetricSpec.TryParse(specString, out MetricSpec spec)) + { + Log.Message("Failed to parse metric spec: {specString}"); + } + else + { + Log.Message("Parsed metric: {spec}"); + if (spec.InstrumentName != null) + { + _aggregationManager!.Include(spec.MeterName, spec.InstrumentName); + } + else + { + _aggregationManager!.Include(spec.MeterName); + } + } + } + } + + private void TransmitMetricValue(Instrument instrument, LabeledAggregationStatistics stats, string sessionId) + { + if (stats.AggregationStatistics is RateStatistics rateStats) + { + Log.CounterRateValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels), + rateStats.Delta.HasValue ? rateStats.Delta.Value.ToString(CultureInfo.InvariantCulture) : ""); + } + else if (stats.AggregationStatistics is LastValueStatistics lastValueStats) + { + Log.GaugeValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels), + lastValueStats.LastValue.HasValue ? lastValueStats.LastValue.Value.ToString(CultureInfo.InvariantCulture) : ""); + } + else if (stats.AggregationStatistics is HistogramStatistics histogramStats) + { + Log.HistogramValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels), FormatQuantiles(histogramStats.Quantiles)); + } + } + + private string FormatTags(KeyValuePair[] labels) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < labels.Length; i++) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}={1}", labels[i].Key, labels[i].Value); + if (i != labels.Length - 1) + { + sb.Append(','); + } + } + return sb.ToString(); + } + + private string FormatQuantiles(QuantileValue[] quantiles) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < quantiles.Length; i++) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}={1}", quantiles[i].Quantile, quantiles[i].Value); + if (i != quantiles.Length - 1) + { + sb.Append(';'); + } + } + return sb.ToString(); + } + } + + private class MetricSpec + { + private const char MeterInstrumentSeparator = '\\'; + public string MeterName { get; private set; } + public string? InstrumentName { get; private set; } + + public MetricSpec(string meterName, string? instrumentName) + { + MeterName = meterName; + InstrumentName = instrumentName; + } + + public static bool TryParse(string text, out MetricSpec spec) + { + int slashIdx = text.IndexOf(MeterInstrumentSeparator); + if (slashIdx == -1) + { + spec = new MetricSpec(text.Trim(), null); + return true; + } + else + { + string meterName = text.Substring(0, slashIdx).Trim(); + string? instrumentName = text.Substring(slashIdx + 1).Trim(); + spec = new MetricSpec(meterName, instrumentName); + return true; + } + } + + public override string ToString() => InstrumentName != null ? + MeterName + MeterInstrumentSeparator + InstrumentName : + MeterName; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.cs new file mode 100644 index 00000000000..56420d9c95d --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Diagnostics.Metrics +{ + internal partial struct ObjectSequence1 : IEquatable, IObjectSequence + { + public object? Value1; + + public ObjectSequence1(object? value1) + { + Value1 = value1; + } + + public override int GetHashCode() => Value1?.GetHashCode() ?? 0; + + public bool Equals(ObjectSequence1 other) + { + return Value1 is null ? other.Value1 is null : Value1.Equals(other.Value1); + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is ObjectSequence1 os1 && Equals(os1); + } + } + + internal partial struct ObjectSequence2 : IEquatable, IObjectSequence + { + public object? Value1; + public object? Value2; + + public ObjectSequence2(object? value1, object? value2) + { + Value1 = value1; + Value2 = value2; + } + + public bool Equals(ObjectSequence2 other) + { + return (Value1 is null ? other.Value1 is null : Value1.Equals(other.Value1)) && + (Value2 is null ? other.Value2 is null : Value2.Equals(other.Value2)); + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is ObjectSequence2 os2 && Equals(os2); + } + } + + internal partial struct ObjectSequence3 : IEquatable, IObjectSequence + { + public object? Value1; + public object? Value2; + public object? Value3; + + public ObjectSequence3(object? value1, object? value2, object? value3) + { + Value1 = value1; + Value2 = value2; + Value3 = value3; + } + + public bool Equals(ObjectSequence3 other) + { + return (Value1 is null ? other.Value1 is null : Value1.Equals(other.Value1)) && + (Value2 is null ? other.Value2 is null : Value2.Equals(other.Value2)) && + (Value3 is null ? other.Value3 is null : Value3.Equals(other.Value3)); + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is ObjectSequence3 os3 && Equals(os3); + } + } + + internal partial struct ObjectSequenceMany : IEquatable, IObjectSequence + { + private readonly object?[] _values; + + public ObjectSequenceMany(object[] values) + { + _values = values; + } + + public bool Equals(ObjectSequenceMany other) + { + if (_values.Length != other._values.Length) + { + return false; + } + for (int i = 0; i < _values.Length; i++) + { + object? value = _values[i], otherValue = other._values[i]; + if (value is null) + { + if (otherValue is not null) + { + return false; + } + } + else if (!value.Equals(otherValue)) + { + return false; + } + } + return true; + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is ObjectSequenceMany osm && Equals(osm); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netcore.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netcore.cs new file mode 100644 index 00000000000..d71cb0dcc68 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netcore.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Diagnostics.Metrics +{ + internal interface IObjectSequence + { + Span AsSpan(); + } + + internal partial struct ObjectSequence1 : IEquatable, IObjectSequence + { + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 1); + } + } + + internal partial struct ObjectSequence2 : IEquatable, IObjectSequence + { + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 2); + } + + public override int GetHashCode() => HashCode.Combine(Value1, Value2); + } + + internal partial struct ObjectSequence3 : IEquatable, IObjectSequence + { + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 3); + } + + public override int GetHashCode() => HashCode.Combine(Value1, Value2, Value3); + } + + internal partial struct ObjectSequenceMany : IEquatable, IObjectSequence + { + + public Span AsSpan() + { + return _values.AsSpan(); + } + + public override int GetHashCode() + { + HashCode h = default; + for (int i = 0; i < _values.Length; i++) + { + h.Add(_values[i]); + } + return h.ToHashCode(); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netfx.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netfx.cs new file mode 100644 index 00000000000..f7de3bdda89 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/ObjectSequence.netfx.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Diagnostics.Metrics +{ + internal interface IObjectSequence + { + object? this[int i] { get; set; } + } + + internal partial struct ObjectSequence1 : IEquatable, IObjectSequence + { + public object? this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + } + + internal partial struct ObjectSequence2 : IEquatable, IObjectSequence + { + public object? this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + else if (i == 1) + { + return Value2; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else if (i == 1) + { + Value2 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + + // this isn't exactly identical to the netcore algorithm, but good enough + public override int GetHashCode() => (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0 << 3); + } + + internal partial struct ObjectSequence3 : IEquatable, IObjectSequence + { + public object? this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + else if (i == 1) + { + return Value2; + } + else if (i == 2) + { + return Value3; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else if (i == 1) + { + Value2 = value; + } + else if (i == 2) + { + Value3 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + + // this isn't exactly identical to the netcore algorithm, but good enough + public override int GetHashCode() => (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0 << 3) ^ (Value3?.GetHashCode() ?? 0 << 6); + } + + internal partial struct ObjectSequenceMany : IEquatable, IObjectSequence + { + public object? this[int i] + { + get + { + return _values[i]; + } + set + { + _values[i] = value; + } + } + + public override int GetHashCode() + { + int hash = 0; + for (int i = 0; i < _values.Length; i++) + { + // this isn't exactly identical to the netcore algorithm, but good enough + hash <<= 3; + object? value = _values[i]; + if (value != null) + { + hash ^= value.GetHashCode(); + } + } + return hash; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RateAggregator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RateAggregator.cs new file mode 100644 index 00000000000..dc2728821e9 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RateAggregator.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.Metrics +{ + internal sealed class RateSumAggregator : Aggregator + { + private double _sum; + + public override void Update(double value) + { + lock (this) + { + _sum += value; + } + } + + public override IAggregationStatistics Collect() + { + lock (this) + { + RateStatistics? stats = new RateStatistics(_sum); + _sum = 0; + return stats; + } + } + } + + internal sealed class RateAggregator : Aggregator + { + private double? _prevValue; + private double _value; + + public override void Update(double value) + { + lock (this) + { + _value = value; + } + } + + public override IAggregationStatistics Collect() + { + lock (this) + { + double? delta = null; + if (_prevValue.HasValue) + { + delta = _value - _prevValue.Value; + } + RateStatistics stats = new RateStatistics(delta); + _prevValue = _value; + return stats; + } + } + } + + internal sealed class RateStatistics : IAggregationStatistics + { + public RateStatistics(double? delta) + { + Delta = delta; + } + + public double? Delta { get; } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.cs new file mode 100644 index 00000000000..7cbf2996613 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Diagnostics.Metrics +{ + internal partial struct StringSequence1 : IEquatable, IStringSequence + { + public string Value1; + + public StringSequence1(string value1) + { + Value1 = value1; + } + + public override int GetHashCode() => Value1.GetHashCode(); + + public bool Equals(StringSequence1 other) + { + return Value1 == other.Value1; + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is StringSequence1 ss1 && Equals(ss1); + } + } + + internal partial struct StringSequence2 : IEquatable, IStringSequence + { + public string Value1; + public string Value2; + + public StringSequence2(string value1, string value2) + { + Value1 = value1; + Value2 = value2; + } + + public bool Equals(StringSequence2 other) + { + return Value1 == other.Value1 && Value2 == other.Value2; + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is StringSequence2 ss2 && Equals(ss2); + } + } + + internal partial struct StringSequence3 : IEquatable, IStringSequence + { + public string Value1; + public string Value2; + public string Value3; + + public StringSequence3(string value1, string value2, string value3) + { + Value1 = value1; + Value2 = value2; + Value3 = value3; + } + + public bool Equals(StringSequence3 other) + { + return Value1 == other.Value1 && Value2 == other.Value2 && Value3 == other.Value3; + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is StringSequence3 ss3 && Equals(ss3); + } + } + + internal partial struct StringSequenceMany : IEquatable, IStringSequence + { + private readonly string[] _values; + + public StringSequenceMany(string[] values) + { + _values = values; + } + + public Span AsSpan() + { + return _values.AsSpan(); + } + + public bool Equals(StringSequenceMany other) + { + if (_values.Length != other._values.Length) + { + return false; + } + for (int i = 0; i < _values.Length; i++) + { + if (_values[i] != other._values[i]) + { + return false; + } + } + return true; + } + + //GetHashCode() is in the platform specific files + public override bool Equals(object? obj) + { + return obj is StringSequenceMany ssm && Equals(ssm); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netcore.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netcore.cs new file mode 100644 index 00000000000..8d8ceedf50a --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netcore.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Diagnostics.Metrics +{ + internal interface IStringSequence + { + Span AsSpan(); + } + + internal partial struct StringSequence1 : IEquatable, IStringSequence + { + + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 1); + } + } + + internal partial struct StringSequence2 : IEquatable, IStringSequence + { + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 2); + } + + public override int GetHashCode() => HashCode.Combine(Value1, Value2); + } + + internal partial struct StringSequence3 : IEquatable, IStringSequence + { + public Span AsSpan() + { + return MemoryMarshal.CreateSpan(ref Value1, 3); + } + + public override int GetHashCode() => HashCode.Combine(Value1, Value2, Value3); + } + + internal partial struct StringSequenceMany : IEquatable, IStringSequence + { + public override int GetHashCode() + { + HashCode h = default; + for (int i = 0; i < _values.Length; i++) + { + h.Add(_values[i]); + } + return h.ToHashCode(); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netfx.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netfx.cs new file mode 100644 index 00000000000..0d7e9b5d32d --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/StringSequence.netfx.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.Metrics +{ + internal interface IStringSequence + { + string this[int i] { get; set; } + int Length { get; } + } + + internal partial struct StringSequence1 : IEquatable, IStringSequence + { + public string this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + + public int Length => 1; + } + + internal partial struct StringSequence2 : IEquatable, IStringSequence + { + + public string this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + else if (i == 1) + { + return Value2; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else if (i == 1) + { + Value2 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + + public int Length => 2; + + // this isn't exactly identical to the netcore algorithm, but good enough + public override int GetHashCode() => (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0 << 3); + } + + internal partial struct StringSequence3 : IEquatable, IStringSequence + { + public string this[int i] + { + get + { + if (i == 0) + { + return Value1; + } + else if (i == 1) + { + return Value2; + } + else if (i == 2) + { + return Value3; + } + throw new IndexOutOfRangeException(); + } + set + { + if (i == 0) + { + Value1 = value; + } + else if (i == 1) + { + Value2 = value; + } + else if (i == 2) + { + Value3 = value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + } + + public int Length => 3; + + // this isn't exactly identical to the netcore algorithm, but good enough + public override int GetHashCode() => (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0 << 3) ^ (Value3?.GetHashCode() ?? 0 << 6); + } + + internal partial struct StringSequenceMany : IEquatable, IStringSequence + { + public string this[int i] + { + get + { + return _values[i]; + } + set + { + _values[i] = value; + } + } + + public int Length => _values.Length; + + public override int GetHashCode() + { + int hash = 0; + for (int i = 0; i < _values.Length; i++) + { + // this isn't exactly identical to the netcore algorithm, but good enough + hash <<= 3; + hash ^= _values[i].GetHashCode(); + } + return hash; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/AggregationManagerTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/AggregationManagerTests.cs new file mode 100644 index 00000000000..cd26bc55e8c --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/AggregationManagerTests.cs @@ -0,0 +1,753 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Diagnostics.Metrics.Tests +{ + public class AggregationStoreTests + { + [Fact] + public void GetDefaultAggregator() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + LastValue val = store.GetAggregator(); + + Assert.NotNull(val); + Assert.Equal(val, store.GetAggregator()); + } + + [Fact] + public void GetNoLabels() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + var span = new ReadOnlySpan>(); + LastValue val = store.GetAggregator(span); + + Assert.NotNull(val); + Assert.Equal(val, store.GetAggregator(span)); + Assert.Equal(val, store.GetAggregator()); + } + + [Fact] + public void GetOneLabel() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + KeyValuePair[] labels2 = new KeyValuePair[] { new KeyValuePair("color", "blue") }; + KeyValuePair[] labels3 = new KeyValuePair[] { new KeyValuePair("size", 1) }; + KeyValuePair[] labels4 = new KeyValuePair[] { new KeyValuePair("size", "eight") }; + var span1 = new ReadOnlySpan>(labels1, 0, 1); + var span2 = new ReadOnlySpan>(labels2, 0, 1); + var span3 = new ReadOnlySpan>(labels3, 0, 1); + var span4 = new ReadOnlySpan>(labels4, 0, 1); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + } + + [Fact] + public void GetTwoLabel() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { new KeyValuePair("color", "red"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels2 = new KeyValuePair[] + { new KeyValuePair("color", "blue"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels3 = new KeyValuePair[] + { new KeyValuePair("size", 1), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels4 = new KeyValuePair[] + { new KeyValuePair("size", "eight"), new KeyValuePair("name", "ned") }; + var span1 = new ReadOnlySpan>(labels1, 0, 2); + var span2 = new ReadOnlySpan>(labels2, 0, 2); + var span3 = new ReadOnlySpan>(labels3, 0, 2); + var span4 = new ReadOnlySpan>(labels4, 0, 2); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + } + + [Fact] + public void GetTwoLabelUnordered() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { new KeyValuePair("color", "red"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels2 = new KeyValuePair[] + { new KeyValuePair("name", "ned"), new KeyValuePair("color", "red") }; + var span1 = new ReadOnlySpan>(labels1, 0, 2); + var span2 = new ReadOnlySpan>(labels2, 0, 2); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, val2); + } + + [Fact] + public void GetThreeLabel() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels2 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "blue"), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels3 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("size", 1), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels4 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("size", "eight"), + new KeyValuePair("name", "ned") + }; + var span1 = new ReadOnlySpan>(labels1, 0, 3); + var span2 = new ReadOnlySpan>(labels2, 0, 3); + var span3 = new ReadOnlySpan>(labels3, 0, 3); + var span4 = new ReadOnlySpan>(labels4, 0, 3); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + } + + [Fact] + public void GetThreeLabelUnordered() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels2 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("name", "ned"), + new KeyValuePair("color", "red") + }; + KeyValuePair[] labels3 = new KeyValuePair[] + { + new KeyValuePair("color", "red"), + new KeyValuePair("alpha", 15), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels4 = new KeyValuePair[] + { + new KeyValuePair("name", "ned"), + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red") + }; + KeyValuePair[] labels5 = new KeyValuePair[] + { + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned"), + new KeyValuePair("alpha", 15) + }; + KeyValuePair[] labels6 = new KeyValuePair[] + { + new KeyValuePair("name", "ned"), + new KeyValuePair("color", "red"), + new KeyValuePair("alpha", 15) + }; + var span1 = new ReadOnlySpan>(labels1, 0, 3); + var span2 = new ReadOnlySpan>(labels2, 0, 3); + var span3 = new ReadOnlySpan>(labels3, 0, 3); + var span4 = new ReadOnlySpan>(labels4, 0, 3); + var span5 = new ReadOnlySpan>(labels5, 0, 3); + var span6 = new ReadOnlySpan>(labels6, 0, 3); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + LastValue val2 = store.GetAggregator(span2); + Assert.Equal(val1, val2); + LastValue val3 = store.GetAggregator(span3); + Assert.Equal(val1, val3); + LastValue val4 = store.GetAggregator(span4); + Assert.Equal(val1, val4); + LastValue val5 = store.GetAggregator(span5); + Assert.Equal(val1, val5); + LastValue val6 = store.GetAggregator(span6); + Assert.Equal(val1, val6); + } + + [Fact] + public void GetFourLabel() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + KeyValuePair[] labels2 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "blue"), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + KeyValuePair[] labels3 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("size", 1), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + KeyValuePair[] labels4 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("size", "eight"), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + var span1 = new ReadOnlySpan>(labels1, 0, 4); + var span2 = new ReadOnlySpan>(labels2, 0, 4); + var span3 = new ReadOnlySpan>(labels3, 0, 4); + var span4 = new ReadOnlySpan>(labels4, 0, 4); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + } + + + [Fact] + public void GetFourLabelUnordered() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + KeyValuePair[] labels2 = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44), + new KeyValuePair("color", "red") + }; + KeyValuePair[] labels3 = new KeyValuePair[] + { + new KeyValuePair("four", 44), + new KeyValuePair("color", "red"), + new KeyValuePair("alpha", 15), + new KeyValuePair("name", "ned") + }; + KeyValuePair[] labels4 = new KeyValuePair[] + { + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44), + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red") + }; + KeyValuePair[] labels5 = new KeyValuePair[] + { + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned"), + new KeyValuePair("alpha", 15), + new KeyValuePair("four", 44) + }; + KeyValuePair[] labels6 = new KeyValuePair[] + { + new KeyValuePair("four", 44), + new KeyValuePair("name", "ned"), + new KeyValuePair("color", "red"), + new KeyValuePair("alpha", 15) + }; + var span1 = new ReadOnlySpan>(labels1, 0, 4); + var span2 = new ReadOnlySpan>(labels2, 0, 4); + var span3 = new ReadOnlySpan>(labels3, 0, 4); + var span4 = new ReadOnlySpan>(labels4, 0, 4); + var span5 = new ReadOnlySpan>(labels5, 0, 4); + var span6 = new ReadOnlySpan>(labels6, 0, 4); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + LastValue val2 = store.GetAggregator(span2); + Assert.Equal(val1, val2); + LastValue val3 = store.GetAggregator(span3); + Assert.Equal(val1, val3); + LastValue val4 = store.GetAggregator(span4); + Assert.Equal(val1, val4); + LastValue val5 = store.GetAggregator(span5); + Assert.Equal(val1, val5); + LastValue val6 = store.GetAggregator(span6); + Assert.Equal(val1, val6); + } + + [Fact] + public void GetMultiRank0Start() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { new KeyValuePair("color", "red"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels2 = new KeyValuePair[] + { new KeyValuePair("color", "blue"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels3 = new KeyValuePair[] + { new KeyValuePair("size", 1), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels4 = new KeyValuePair[] + { new KeyValuePair("size", "eight"), new KeyValuePair("name", "ned") }; + var span1 = new ReadOnlySpan>(labels1, 0, 2); + var span2 = new ReadOnlySpan>(labels2, 0, 2); + var span3 = new ReadOnlySpan>(labels3, 0, 2); + var span4 = new ReadOnlySpan>(labels4, 0, 2); + + var span = new ReadOnlySpan>(); + LastValue val = store.GetAggregator(span); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + + Assert.Equal(val, store.GetAggregator(span)); + Assert.NotEqual(val, val1); + Assert.NotEqual(val, val2); + Assert.NotEqual(val, val3); + Assert.NotEqual(val, val4); + } + + [Fact] + public void GetMultiRank1Start() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] + { new KeyValuePair("color", "red"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels2 = new KeyValuePair[] + { new KeyValuePair("color", "blue"), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels3 = new KeyValuePair[] + { new KeyValuePair("size", 1), new KeyValuePair("name", "ned") }; + KeyValuePair[] labels4 = new KeyValuePair[] + { new KeyValuePair("size", "eight"), new KeyValuePair("name", "ned") }; + var span1 = new ReadOnlySpan>(labels1, 0, 2); + var span2 = new ReadOnlySpan>(labels2, 0, 2); + var span3 = new ReadOnlySpan>(labels3, 0, 2); + var span4 = new ReadOnlySpan>(labels4, 0, 2); + + KeyValuePair[] labels = new KeyValuePair[] + { new KeyValuePair("color", "red") }; + var span = new ReadOnlySpan>(labels, 0, 1); + LastValue val = store.GetAggregator(span); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + + Assert.Equal(val, store.GetAggregator(span)); + Assert.NotEqual(val, val1); + Assert.NotEqual(val, val2); + Assert.NotEqual(val, val3); + Assert.NotEqual(val, val4); + } + + [Fact] + public void GetMultiRank2Start() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + KeyValuePair[] labels2 = new KeyValuePair[] { new KeyValuePair("color", "blue") }; + KeyValuePair[] labels3 = new KeyValuePair[] { new KeyValuePair("size", 1) }; + KeyValuePair[] labels4 = new KeyValuePair[] { new KeyValuePair("size", "eight") }; + var span1 = new ReadOnlySpan>(labels1, 0, 1); + var span2 = new ReadOnlySpan>(labels2, 0, 1); + var span3 = new ReadOnlySpan>(labels3, 0, 1); + var span4 = new ReadOnlySpan>(labels4, 0, 1); + + KeyValuePair[] labels = new KeyValuePair[] + { new KeyValuePair("color", "red"), new KeyValuePair("name", "ned") }; + var span = new ReadOnlySpan>(labels, 0, 2); + LastValue val = store.GetAggregator(span); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + + Assert.Equal(val, store.GetAggregator(span)); + Assert.NotEqual(val, val1); + Assert.NotEqual(val, val2); + Assert.NotEqual(val, val3); + Assert.NotEqual(val, val4); + } + + [Fact] + public void GetMultiRank3Start() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + KeyValuePair[] labels2 = new KeyValuePair[] { new KeyValuePair("color", "blue") }; + KeyValuePair[] labels3 = new KeyValuePair[] { new KeyValuePair("size", 1) }; + KeyValuePair[] labels4 = new KeyValuePair[] { new KeyValuePair("size", "eight") }; + var span1 = new ReadOnlySpan>(labels1, 0, 1); + var span2 = new ReadOnlySpan>(labels2, 0, 1); + var span3 = new ReadOnlySpan>(labels3, 0, 1); + var span4 = new ReadOnlySpan>(labels4, 0, 1); + + KeyValuePair[] labels = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned") + }; + var span = new ReadOnlySpan>(labels, 0, 3); + LastValue val = store.GetAggregator(span); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + + Assert.Equal(val, store.GetAggregator(span)); + Assert.NotEqual(val, val1); + Assert.NotEqual(val, val2); + Assert.NotEqual(val, val3); + Assert.NotEqual(val, val4); + } + + [Fact] + public void GetMultiRank4Start() + { + AggregatorStore store = new AggregatorStore(() => new LastValue()); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + KeyValuePair[] labels2 = new KeyValuePair[] { new KeyValuePair("color", "blue") }; + KeyValuePair[] labels3 = new KeyValuePair[] { new KeyValuePair("size", 1) }; + KeyValuePair[] labels4 = new KeyValuePair[] { new KeyValuePair("size", "eight") }; + var span1 = new ReadOnlySpan>(labels1, 0, 1); + var span2 = new ReadOnlySpan>(labels2, 0, 1); + var span3 = new ReadOnlySpan>(labels3, 0, 1); + var span4 = new ReadOnlySpan>(labels4, 0, 1); + + KeyValuePair[] labels = new KeyValuePair[] + { + new KeyValuePair("alpha", 15), + new KeyValuePair("color", "red"), + new KeyValuePair("name", "ned"), + new KeyValuePair("four", 44) + }; + var span = new ReadOnlySpan>(labels, 0, 4); + LastValue val = store.GetAggregator(span); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val2 = store.GetAggregator(span2); + Assert.NotNull(val2); + Assert.NotEqual(val1, val2); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val3 = store.GetAggregator(span3); + Assert.NotNull(val3); + Assert.NotEqual(val1, val3); + Assert.NotEqual(val2, val3); + Assert.Equal(val3, store.GetAggregator(span3)); + LastValue val4 = store.GetAggregator(span4); + Assert.NotNull(val4); + Assert.NotEqual(val1, val4); + Assert.NotEqual(val2, val4); + Assert.NotEqual(val3, val4); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val1, store.GetAggregator(span1)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val4, store.GetAggregator(span4)); + Assert.Equal(val2, store.GetAggregator(span2)); + Assert.Equal(val3, store.GetAggregator(span3)); + Assert.Equal(val3, store.GetAggregator(span3)); + + Assert.Equal(val, store.GetAggregator(span)); + Assert.NotEqual(val, val1); + Assert.NotEqual(val, val2); + Assert.NotEqual(val, val3); + Assert.NotEqual(val, val4); + } + + [Fact] + public void AggregatorLimitReached_NoLabel() + { + AggregatorStore store = new AggregatorStore(() => null); + var span = new ReadOnlySpan>(); + LastValue val = store.GetAggregator(span); + + Assert.Null(val); + Assert.Equal(val, store.GetAggregator(span)); + Assert.Equal(val, store.GetAggregator()); + } + + [Fact] + public void AggregatorLimitReached_WithLabels() + { + AggregatorStore store = new AggregatorStore(() => null); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + var span1 = new ReadOnlySpan>(labels1, 0, 1); + LastValue val = store.GetAggregator(span1); + + Assert.Null(val); + Assert.Equal(val, store.GetAggregator(span1)); + } + + [Fact] + public void AggregatorLimitReached_Multisize_NoLabel() + { + int count = 1; + AggregatorStore store = new AggregatorStore(() => + { + if (count > 0) + { + count--; + return new LastValue(); + } + return null; + }); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + var span = new ReadOnlySpan>(); + var span1 = new ReadOnlySpan>(labels1, 0, 1); + + LastValue val1 = store.GetAggregator(span1); + Assert.NotNull(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + LastValue val0 = store.GetAggregator(span); + Assert.Null(val0); + Assert.Equal(val0, store.GetAggregator(span)); + } + + [Fact] + public void AggregatorLimitReached_Multisize_TwoLabel() + { + int count = 1; + AggregatorStore store = new AggregatorStore(() => + { + if (count > 0) + { + count--; + return new LastValue(); + } + return null; + }); + KeyValuePair[] labels1 = new KeyValuePair[] { new KeyValuePair("color", "red") }; + var span = new ReadOnlySpan>(); + var span1 = new ReadOnlySpan>(labels1, 0, 1); + + LastValue val0 = store.GetAggregator(span); + Assert.NotNull(val0); + Assert.Equal(val0, store.GetAggregator(span)); + LastValue val1 = store.GetAggregator(span1); + Assert.Null(val1); + Assert.Equal(val1, store.GetAggregator(span1)); + + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ExponentialHistogramTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ExponentialHistogramTests.cs new file mode 100644 index 00000000000..75ceeb1ac92 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ExponentialHistogramTests.cs @@ -0,0 +1,323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Diagnostics.Metrics.Tests +{ + public class ExponentialHistogramTests + { + [Fact] + public void HappyPath() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5, 0.95); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(3, stats.Quantiles[0].Value); + Assert.Equal(0.95, stats.Quantiles[1].Quantile); + Assert.Equal(5, stats.Quantiles[1].Value); + } + + [Fact] + public void MinMax() + { + QuantileAggregation quantiles = new QuantileAggregation(0.0, 1.0); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(0.0, stats.Quantiles[0].Quantile); + Assert.Equal(1, stats.Quantiles[0].Value); + Assert.Equal(1.0, stats.Quantiles[1].Quantile); + Assert.Equal(5, stats.Quantiles[1].Value); + } + + [Fact] + public void NoQuantiles() + { + QuantileAggregation quantiles = new QuantileAggregation(); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + Assert.Equal(0, stats.Quantiles.Length); + } + + [Fact] + public void OutOfBoundsQuantiles() + { + QuantileAggregation quantiles = new QuantileAggregation(-3, 100); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(-3, stats.Quantiles[0].Quantile); + Assert.Equal(1, stats.Quantiles[0].Value); + Assert.Equal(100, stats.Quantiles[1].Quantile); + Assert.Equal(5, stats.Quantiles[1].Value); + } + + [Fact] + public void UnorderedQuantiles() + { + QuantileAggregation quantiles = new QuantileAggregation(0.9, 0.1); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(0.1, stats.Quantiles[0].Quantile); + Assert.Equal(1, stats.Quantiles[0].Value); + Assert.Equal(0.9, stats.Quantiles[1].Quantile); + Assert.Equal(5, stats.Quantiles[1].Value); + } + + [Fact] + public void DifferencesLessThanErrorBound() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(90.01); + aggregator.Update(90.01); + aggregator.Update(90.02); + aggregator.Update(90.03); + aggregator.Update(90.04); + aggregator.Update(100.01); + aggregator.Update(100.01); + aggregator.Update(100.02); + aggregator.Update(100.03); + aggregator.Update(100.04); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(100, stats.Quantiles[0].Value); + Assert.True(Math.Abs(100.01 - stats.Quantiles[0].Value) <= 100.01 * quantiles.MaxRelativeError); + } + + [Fact] + public void DifferencesGreaterThanErrorBound() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + quantiles.MaxRelativeError = 0.0001; + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(90.01); + aggregator.Update(90.01); + aggregator.Update(90.02); + aggregator.Update(90.03); + aggregator.Update(90.04); + aggregator.Update(100.01); + aggregator.Update(100.01); + aggregator.Update(100.02); + aggregator.Update(100.03); + aggregator.Update(100.04); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + + //At default error of 0.001 result of 100 would be acceptable, but with higher precision it is not + Assert.True(100 < stats.Quantiles[0].Value); + Assert.True(Math.Abs(100.01 - stats.Quantiles[0].Value) <= 100.01 * quantiles.MaxRelativeError); + } + + [Fact] + public void NoUpdates() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(0, stats.Quantiles.Length); + } + + [Fact] + public void OneUpdate() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(99); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(99, stats.Quantiles[0].Value); + } + + [Fact] + public void NoUpdatesInSomeIntervals() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(0, stats.Quantiles.Length); + } + + [Fact] + public void UpdatesAfterNoUpdates() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + var stats = (HistogramStatistics)aggregator.Collect(); + Assert.NotNull(stats); + Assert.Equal(0, stats.Quantiles.Length); + + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(3, stats.Quantiles[0].Value); + } + + [Fact] + public void IterateCollect() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + + aggregator.Update(1); + aggregator.Update(2); + aggregator.Update(3); + aggregator.Update(4); + aggregator.Update(5); + var stats = (HistogramStatistics)aggregator.Collect(); + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(3, stats.Quantiles[0].Value); + + aggregator.Update(9); + aggregator.Update(8); + aggregator.Update(7); + aggregator.Update(6); + aggregator.Update(5); + stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(7, stats.Quantiles[0].Value); + } + + [Fact] + public void NegativeValues() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + + aggregator.Update(-1); + aggregator.Update(-2); + aggregator.Update(-3); + aggregator.Update(-4); + aggregator.Update(-5); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(-3, stats.Quantiles[0].Value); + } + + [Fact] + public void ZeroValues() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + + aggregator.Update(0); + aggregator.Update(0); + aggregator.Update(0); + aggregator.Update(0); + aggregator.Update(0); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(0, stats.Quantiles[0].Value); + } + + [Fact] + public void MixedValues() + { + QuantileAggregation quantiles = new QuantileAggregation(0.5); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + + aggregator.Update(19); + aggregator.Update(-4); + aggregator.Update(100_000); + aggregator.Update(-0.5); + aggregator.Update(-189.3231); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(1, stats.Quantiles.Length); + Assert.Equal(0.5, stats.Quantiles[0].Quantile); + Assert.Equal(-0.5, stats.Quantiles[0].Value); + } + + [Fact] + public void FilterNaNAndInfinities() + { + QuantileAggregation quantiles = new QuantileAggregation(0,1); + ExponentialHistogramAggregator aggregator = new ExponentialHistogramAggregator(quantiles); + + aggregator.Update(double.NaN); + aggregator.Update(-double.NaN); + aggregator.Update(double.PositiveInfinity); + aggregator.Update(double.NegativeInfinity); + aggregator.Update(100); + var stats = (HistogramStatistics)aggregator.Collect(); + + Assert.NotNull(stats); + Assert.Equal(2, stats.Quantiles.Length); + Assert.Equal(0, stats.Quantiles[0].Quantile); + Assert.Equal(100, stats.Quantiles[0].Value); + Assert.Equal(1, stats.Quantiles[1].Quantile); + Assert.Equal(100, stats.Quantiles[1].Value); + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs new file mode 100644 index 00000000000..f0b77bf2af4 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -0,0 +1,1036 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace System.Diagnostics.Metrics.Tests +{ + public class MetricEventSourceTests + { + ITestOutputHelper _output; + + public MetricEventSourceTests(ITestOutputHelper output) + { + _output = output; + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesTimeSeriesWithEmptyMetadata() + { + using Meter meter = new Meter("TestMeter1"); + Counter c = meter.CreateCounter("counter1"); + int counterState = 3; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }); + int gaugeState = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }); + Histogram h = meter.CreateHistogram("histogram1"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter1")) + { + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesTimeSeriesWithMetadata() + { + using Meter meter = new Meter("TestMeter2"); + Counter c = meter.CreateCounter("counter1", "hat", "Fooz!!"); + int counterState = 3; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; } , "MB", "Size of universe"); + int gaugeState = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!"); + Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter2")) + { + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesTimeSeriesForLateMeter() + { + // this ensures the MetricsEventSource exists when the listener tries to query + using Meter dummy = new Meter("dummy"); + Meter meter = null; + try + { + Counter c; + ObservableCounter oc; + ObservableGauge og; + Histogram h; + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter3")) + { + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + // the Meter is created after the EventSource was already monitoring + meter = new Meter("TestMeter3"); + c = meter.CreateCounter("counter1"); + int counterState = 3; + oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }); + int gaugeState = 0; + og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }); + h = meter.CreateHistogram("histogram1"); + + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + } + finally + { + meter?.Dispose(); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesTimeSeriesForLateInstruments() + { + // this ensures the MetricsEventSource exists when the listener tries to query + using Meter meter = new Meter("TestMeter4"); + Counter c; + ObservableCounter oc; + ObservableGauge og; + Histogram h; + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter4")) + { + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + // Instruments are created after the EventSource was already monitoring + c = meter.CreateCounter("counter1"); + int counterState = 3; + oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }); + int gaugeState = 0; + og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }); + h = meter.CreateHistogram("histogram1"); + + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesTimeSeriesWithTags() + { + using Meter meter = new Meter("TestMeter5"); + Counter c = meter.CreateCounter("counter1"); + int counterState = 3; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => + { + counterState += 7; + return new Measurement[] + { + new Measurement(counterState, new KeyValuePair("Color", "red"), new KeyValuePair("Size", 19) ), + new Measurement(2*counterState, new KeyValuePair("Color", "blue"), new KeyValuePair("Size", 4 ) ) + }; + }); + int gaugeState = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => + { + gaugeState += 9; + return new Measurement[] + { + new Measurement(gaugeState, new KeyValuePair("Color", "red"), new KeyValuePair("Size", 19) ), + new Measurement(2*gaugeState, new KeyValuePair("Color", "blue"), new KeyValuePair("Size", 4 ) ) + }; + }); + Histogram h = meter.CreateHistogram("histogram1"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter5")) + { + c.Add(5, new KeyValuePair("Color", "red")); + c.Add(6, new KeyValuePair("Color", "blue")); + h.Record(19, new KeyValuePair("Size", 123)); + h.Record(20, new KeyValuePair("Size", 124)); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + c.Add(12, new KeyValuePair("Color", "red")); + c.Add(13, new KeyValuePair("Color", "blue")); + h.Record(26, new KeyValuePair("Size", 123)); + h.Record(27, new KeyValuePair("Size", 124)); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=red", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=blue", "", "6", "13"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "Color=red,Size=19", "", "", "7"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "Color=blue,Size=4", "", "", "14"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "Color=red,Size=19", "", "9", "18"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "Color=blue,Size=4", "", "18", "36"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=123", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=124", "", "0.5=20;0.95=20;0.99=20", "0.5=27;0.95=27;0.99=27"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourceFiltersInstruments() + { + using Meter meterA = new Meter("TestMeterA"); + using Meter meterB = new Meter("TestMeterB"); + using Meter meterC = new Meter("TestMeterC"); + Counter c1a = meterA.CreateCounter("counter1"); + Counter c2a = meterA.CreateCounter("counter2"); + Counter c3a = meterA.CreateCounter("counter3"); + Counter c1b = meterB.CreateCounter("counter1"); + Counter c2b = meterB.CreateCounter("counter2"); + Counter c3b = meterB.CreateCounter("counter3"); + Counter c1c = meterC.CreateCounter("counter1"); + Counter c2c = meterC.CreateCounter("counter2"); + Counter c3c = meterC.CreateCounter("counter3"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, + "TestMeterA\\counter3;TestMeterB\\counter1;TestMeterC\\counter2;TestMeterB;TestMeterC\\counter3")) + { + c1a.Add(1); + c2a.Add(1); + c3a.Add(1); + c1b.Add(1); + c2b.Add(1); + c3b.Add(1); + c1c.Add(1); + c2c.Add(1); + c3c.Add(1); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + c1a.Add(2); + c2a.Add(2); + c3a.Add(2); + c1b.Add(2); + c2b.Add(2); + c3b.Add(2); + c1c.Add(2); + c2c.Add(2); + c3c.Add(2); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c3a, c1b, c2b, c3b, c2c, c3c); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meterA.Name, c3a.Name, "", "", "1", "2"); + AssertCounterEventsPresent(events, meterB.Name, c1b.Name, "", "", "1", "2"); + AssertCounterEventsPresent(events, meterB.Name, c2b.Name, "", "", "1", "2"); + AssertCounterEventsPresent(events, meterB.Name, c3b.Name, "", "", "1", "2"); + AssertCounterEventsPresent(events, meterC.Name, c3c.Name, "", "", "1", "2"); + AssertCounterEventsPresent(events, meterC.Name, c3c.Name, "", "", "1", "2"); + AssertCounterEventsNotPresent(events, meterA.Name, c1a.Name, ""); + AssertCounterEventsNotPresent(events, meterA.Name, c2a.Name, ""); + AssertCounterEventsNotPresent(events, meterC.Name, c1c.Name, ""); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesMissingDataPoints() + { + using Meter meter = new Meter("TestMeter6"); + Counter c = meter.CreateCounter("counter1"); + int counterState = 3; + int counterCollectInterval = 0; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => + { + counterState += 7; + counterCollectInterval++; + if ((counterCollectInterval % 2) == 1) + { + return new Measurement[] { new Measurement(counterState) }; + } + else + { + return new Measurement[0]; + } + }); + + int gaugeState = 0; + int gaugeCollectInterval = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => + { + gaugeState += 9; + gaugeCollectInterval++; + if ((gaugeCollectInterval % 2) == 1) + { + return new Measurement[] { new Measurement(gaugeState) }; + } + else + { + return new Measurement[0]; + } + }); + + Histogram h = meter.CreateHistogram("histogram1"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter6")) + { + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + // no measurements in interval 2 + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + // no measurements in interval 4 + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 4); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "0", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "0", "14", "0"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "", "27", ""); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "", "0.5=26;0.95=26;0.99=26", ""); + AssertCollectStartStopEventsPresent(events, intervalSecs, 4); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesEndEventsOnNewListener() + { + using Meter meter = new Meter("TestMeter7"); + Counter c = meter.CreateCounter("counter1", "hat", "Fooz!!"); + int counterState = 3; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }, "MB", "Size of universe"); + int gaugeState = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!"); + Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter7")) + { + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + + // some alternate listener starts listening + using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "ADifferentMeter"); + listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 4); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertEndInstrumentReportingEventsPresent(events, c, oc, og, h); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesEndEventsOnMeterDispose() + { + using Meter meterA = new Meter("TestMeter8"); + using Meter meterB = new Meter("TestMeter9"); + Counter c = meterA.CreateCounter("counter1", "hat", "Fooz!!"); + int counterState = 3; + ObservableCounter oc = meterA.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }, "MB", "Size of universe"); + int gaugeState = 0; + ObservableGauge og = meterA.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!"); + Histogram h = meterB.CreateHistogram("histogram1", "a unit", "the description"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter8;TestMeter9")) + { + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + + meterA.Dispose(); + listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 3); + + h.Record(21); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meterA.Name, c.Name, "", c.Unit, "5", "12"); + AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7"); + AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18"); + AssertHistogramEventsPresent(events, meterB.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26", "0.5=21;0.95=21;0.99=21"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertEndInstrumentReportingEventsPresent(events, c, oc, og); + } + + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesInstruments() + { + using Meter meterA = new Meter("TestMeter10"); + using Meter meterB = new Meter("TestMeter11"); + Counter c = meterA.CreateCounter("counter1", "hat", "Fooz!!"); + int counterState = 3; + ObservableCounter oc = meterA.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }, "MB", "Size of universe"); + int gaugeState = 0; + ObservableGauge og = meterA.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!"); + Histogram h = meterB.CreateHistogram("histogram1", "a unit", "the description"); + + EventWrittenEventArgs[] events; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.InstrumentPublishing, null, "")) + { + listener.WaitForEnumerationComplete(TimeSpan.FromSeconds(5)); + events = listener.Events.ToArray(); + } + + AssertInstrumentPublishingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourcePublishesAllDataTypes() + { + using Meter meter = new Meter("TestMeter12"); + Counter i = meter.CreateCounter("counterInt"); + Counter s = meter.CreateCounter("counterShort"); + Counter b = meter.CreateCounter("counterByte"); + Counter l = meter.CreateCounter("counterLong"); + Counter dec = meter.CreateCounter("counterDecimal"); + Counter f = meter.CreateCounter("counterFloat"); + Counter d = meter.CreateCounter("counterDouble"); + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter12")) + { + i.Add(1_234_567); + s.Add(21_432); + b.Add(1); + l.Add(123_456_789_012); + dec.Add(123_456_789_012_345); + f.Add(123_456.789F); + d.Add(87_654_321_987_654.4); + + i.Add(1); + s.Add(1); + b.Add(1); + l.Add(1); + dec.Add(1); + f.Add(1); + d.Add(1); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + i.Add(1_234_567); + s.Add(21_432); + b.Add(1); + l.Add(123_456_789_012); + dec.Add(123_456_789_012_345); + f.Add(123_456.789F); + d.Add(87_654_321_987_654.4); + + i.Add(1); + s.Add(1); + b.Add(1); + l.Add(1); + dec.Add(1); + f.Add(1); + d.Add(1); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, i, s, b, l, dec, f, d); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, i.Name, "", "", "1234568", "1234568"); + AssertCounterEventsPresent(events, meter.Name, s.Name, "", "", "21433", "21433"); + AssertCounterEventsPresent(events, meter.Name, b.Name, "", "", "2", "2"); + AssertCounterEventsPresent(events, meter.Name, l.Name, "", "", "123456789013", "123456789013"); + AssertCounterEventsPresent(events, meter.Name, dec.Name, "", "", "123456789012346", "123456789012346"); + AssertCounterEventsPresent(events, meter.Name, f.Name, "", "", "123457.7890625", "123457.7890625"); + AssertCounterEventsPresent(events, meter.Name, d.Name, "", "", "87654321987655.4", "87654321987655.4"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourceEnforcesTimeSeriesLimit() + { + using Meter meter = new Meter("TestMeter13"); + Counter c = meter.CreateCounter("counter1"); + + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 2, 50, "TestMeter13")) + { + c.Add(5, new KeyValuePair("Color", "red")); + c.Add(6, new KeyValuePair("Color", "blue")); + c.Add(7, new KeyValuePair("Color", "green")); + c.Add(8, new KeyValuePair("Color", "yellow")); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + c.Add(12, new KeyValuePair("Color", "red")); + c.Add(13, new KeyValuePair("Color", "blue")); + c.Add(14, new KeyValuePair("Color", "green")); + c.Add(15, new KeyValuePair("Color", "yellow")); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=red", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=blue", "", "6", "13"); + AssertTimeSeriesLimitPresent(events); + AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=green"); + AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=yellow"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void EventSourceEnforcesHistogramLimit() + { + using Meter meter = new Meter("TestMeter14"); + Histogram h = meter.CreateHistogram("histogram1"); + + + EventWrittenEventArgs[] events; + double intervalSecs = 0.3; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 50, 2, "TestMeter14")) + { + h.Record(5, new KeyValuePair("Color", "red")); + h.Record(6, new KeyValuePair("Color", "blue")); + h.Record(7, new KeyValuePair("Color", "green")); + h.Record(8, new KeyValuePair("Color", "yellow")); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + + h.Record(12, new KeyValuePair("Color", "red")); + h.Record(13, new KeyValuePair("Color", "blue")); + h.Record(14, new KeyValuePair("Color", "green")); + h.Record(15, new KeyValuePair("Color", "yellow")); + listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "Color=red", "", "0.5=5;0.95=5;0.99=5", "0.5=12;0.95=12;0.99=12"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "Color=blue", "", "0.5=6;0.95=6;0.99=6", "0.5=13;0.95=13;0.99=13"); + AssertHistogramLimitPresent(events); + AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=green"); + AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=yellow"); + AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + } + + private void AssertBeginInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) + { + var beginReportEvents = events.Where(e => e.EventName == "BeginInstrumentReporting").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + InstrumentType = e.Payload[4].ToString(), + Unit = e.Payload[5].ToString(), + Description = e.Payload[6].ToString() + }).ToArray(); + + foreach(Instrument i in expectedInstruments) + { + var e = beginReportEvents.Where(ev => ev.InstrumentName == i.Name && ev.MeterName == i.Meter.Name).FirstOrDefault(); + Assert.True(e != null, "Expected to find a BeginInstrumentReporting event for " + i.Meter.Name + "\\" + i.Name); + Assert.Equal(i.Meter.Version ?? "", e.MeterVersion); + Assert.Equal(i.GetType().Name, e.InstrumentType); + Assert.Equal(i.Unit ?? "", e.Unit); + Assert.Equal(i.Description ?? "", e.Description); + } + + Assert.Equal(expectedInstruments.Length, beginReportEvents.Length); + } + + private void AssertEndInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) + { + var beginReportEvents = events.Where(e => e.EventName == "EndInstrumentReporting").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + InstrumentType = e.Payload[4].ToString(), + Unit = e.Payload[5].ToString(), + Description = e.Payload[6].ToString() + }).ToArray(); + + foreach (Instrument i in expectedInstruments) + { + var e = beginReportEvents.Where(ev => ev.InstrumentName == i.Name && ev.MeterName == i.Meter.Name).FirstOrDefault(); + Assert.True(e != null, "Expected to find a EndInstrumentReporting event for " + i.Meter.Name + "\\" + i.Name); + Assert.Equal(i.Meter.Version ?? "", e.MeterVersion); + Assert.Equal(i.GetType().Name, e.InstrumentType); + Assert.Equal(i.Unit ?? "", e.Unit); + Assert.Equal(i.Description ?? "", e.Description); + } + + Assert.Equal(expectedInstruments.Length, beginReportEvents.Length); + } + + private void AssertInitialEnumerationCompleteEventPresent(EventWrittenEventArgs[] events) + { + Assert.Equal(1, events.Where(e => e.EventName == "InitialInstrumentEnumerationComplete").Count()); + } + + private void AssertTimeSeriesLimitPresent(EventWrittenEventArgs[] events) + { + Assert.Equal(1, events.Where(e => e.EventName == "TimeSeriesLimitReached").Count()); + } + + private void AssertHistogramLimitPresent(EventWrittenEventArgs[] events) + { + Assert.Equal(1, events.Where(e => e.EventName == "HistogramLimitReached").Count()); + } + + private void AssertInstrumentPublishingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) + { + var publishEvents = events.Where(e => e.EventName == "InstrumentPublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + InstrumentType = e.Payload[4].ToString(), + Unit = e.Payload[5].ToString(), + Description = e.Payload[6].ToString() + }).ToArray(); + + foreach (Instrument i in expectedInstruments) + { + var e = publishEvents.Where(ev => ev.InstrumentName == i.Name && ev.MeterName == i.Meter.Name).FirstOrDefault(); + Assert.True(e != null, "Expected to find a InstrumentPublished event for " + i.Meter.Name + "\\" + i.Name); + Assert.Equal(i.Meter.Version ?? "", e.MeterVersion); + Assert.Equal(i.GetType().Name, e.InstrumentType); + Assert.Equal(i.Unit ?? "", e.Unit); + Assert.Equal(i.Description ?? "", e.Description); + } + + Assert.Equal(expectedInstruments.Length, publishEvents.Length); + } + + private void AssertCounterEventsPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags, + string expectedUnit, params string[] expectedRates) + { + var counterEvents = events.Where(e => e.EventName == "CounterRateValuePublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + Unit = e.Payload[4].ToString(), + Tags = e.Payload[5].ToString(), + Rate = e.Payload[6].ToString() + }).ToArray(); + var filteredEvents = counterEvents.Where(e => e.MeterName == meterName && e.InstrumentName == instrumentName && e.Tags == tags).ToArray(); + Assert.True(filteredEvents.Length >= expectedRates.Length); + for (int i = 0; i < expectedRates.Length; i++) + { + Assert.Equal(expectedUnit, filteredEvents[i].Unit); + Assert.Equal(expectedRates[i], filteredEvents[i].Rate); + } + } + + private void AssertCounterEventsNotPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags) + { + var counterEvents = events.Where(e => e.EventName == "CounterRateValuePublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + Tags = e.Payload[5].ToString() + }).ToArray(); + var filteredEvents = counterEvents.Where(e => e.MeterName == meterName && e.InstrumentName == instrumentName && e.Tags == tags).ToArray(); + Assert.Equal(0, filteredEvents.Length); + } + + private void AssertGaugeEventsPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags, + string expectedUnit, params string[] expectedValues) + { + var counterEvents = events.Where(e => e.EventName == "GaugeValuePublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + Unit = e.Payload[4].ToString(), + Tags = e.Payload[5].ToString(), + Value = e.Payload[6].ToString(), + }).ToArray(); + var filteredEvents = counterEvents.Where(e => e.MeterName == meterName && e.InstrumentName == instrumentName && e.Tags == tags).ToArray(); + Assert.True(filteredEvents.Length >= expectedValues.Length); + for (int i = 0; i < expectedValues.Length; i++) + { + Assert.Equal(filteredEvents[i].Unit, expectedUnit); + Assert.Equal(filteredEvents[i].Value, expectedValues[i]); + } + } + + private void AssertHistogramEventsPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags, + string expectedUnit, params string[] expectedQuantiles) + { + var counterEvents = events.Where(e => e.EventName == "HistogramValuePublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + Unit = e.Payload[4].ToString(), + Tags = e.Payload[5].ToString(), + Quantiles = (string)e.Payload[6] + }).ToArray(); + var filteredEvents = counterEvents.Where(e => e.MeterName == meterName && e.InstrumentName == instrumentName && e.Tags == tags).ToArray(); + Assert.True(filteredEvents.Length >= expectedQuantiles.Length); + for (int i = 0; i < expectedQuantiles.Length; i++) + { + Assert.Equal(filteredEvents[i].Unit, expectedUnit); + Assert.Equal(expectedQuantiles[i], filteredEvents[i].Quantiles); + } + } + + private void AssertHistogramEventsNotPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags) + { + var counterEvents = events.Where(e => e.EventName == "HistogramValuePublished").Select(e => + new + { + MeterName = e.Payload[1].ToString(), + MeterVersion = e.Payload[2].ToString(), + InstrumentName = e.Payload[3].ToString(), + Tags = e.Payload[5].ToString() + }).ToArray(); + var filteredEvents = counterEvents.Where(e => e.MeterName == meterName && e.InstrumentName == instrumentName && e.Tags == tags).ToArray(); + Assert.Equal(0, filteredEvents.Length); + } + private void AssertCollectStartStopEventsPresent(EventWrittenEventArgs[] events, double expectedIntervalSecs, int expectedPairs) + { + int startEventsSeen = 0; + int stopEventsSeen = 0; + for(int i = 0; i < events.Length; i++) + { + EventWrittenEventArgs e = events[i]; + if(e.EventName == "CollectionStart") + { + Assert.True(startEventsSeen == stopEventsSeen, "Unbalanced CollectionStart event"); + startEventsSeen++; + } + else if(e.EventName == "CollectionStop") + { + Assert.True(startEventsSeen == stopEventsSeen + 1, "Unbalanced CollectionStop event"); + stopEventsSeen++; + } + else if (e.EventName == "CounterRateValuePublished" || + e.EventName == "GaugeValuePublished" || + e.EventName == "HistogramValuePublished") + { + Assert.True(startEventsSeen == stopEventsSeen + 1, "Instrument value published outside collection interval"); + } + } + + Assert.Equal(expectedPairs, startEventsSeen); + Assert.Equal(expectedPairs, stopEventsSeen); + } + } + + class MetricsEventListener : EventListener + { + + public const EventKeywords MessagesKeyword = (EventKeywords)0x1; + public const EventKeywords TimeSeriesValues = (EventKeywords)0x2; + public const EventKeywords InstrumentPublishing = (EventKeywords)0x4; + + + public MetricsEventListener(ITestOutputHelper output, EventKeywords keywords, double? refreshInterval, params string[]? instruments) : + this(output, keywords, Guid.NewGuid().ToString(), refreshInterval, 50, 50, instruments) + { + } + + public MetricsEventListener(ITestOutputHelper output, EventKeywords keywords, double? refreshInterval, + int timeSeriesLimit, int histogramLimit, params string[]? instruments) : + this(output, keywords, Guid.NewGuid().ToString(), refreshInterval, timeSeriesLimit, histogramLimit, instruments) + { + } + + public MetricsEventListener(ITestOutputHelper output, EventKeywords keywords, string sessionId, double? refreshInterval, + int timeSeriesLimit, int histogramLimit, params string[]? instruments) : + this(output, keywords, sessionId, + FormatArgDictionary(refreshInterval,timeSeriesLimit, histogramLimit, instruments, sessionId)) + { + } + + private static Dictionary FormatArgDictionary(double? refreshInterval, int? timeSeriesLimit, int? histogramLimit, string?[]? instruments, string? sessionId) + { + Dictionary d = new Dictionary(); + if(instruments != null) + { + d.Add("Metrics", string.Join(",", instruments)); + } + if(refreshInterval.HasValue) + { + d.Add("RefreshInterval", refreshInterval.ToString()); + } + if(sessionId != null) + { + d.Add("SessionId", sessionId); + } + if(timeSeriesLimit != null) + { + d.Add("MaxTimeSeries", timeSeriesLimit.ToString()); + } + if (histogramLimit != null) + { + d.Add("MaxHistograms", histogramLimit.ToString()); + } + return d; + } + + public MetricsEventListener(ITestOutputHelper output, EventKeywords keywords, string sessionId, Dictionary arguments) + { + _output = output; + _keywords = keywords; + _sessionId = sessionId; + _arguments = arguments; + if (_source != null) + { + _output.WriteLine($"[{DateTime.Now:hh:mm:ss:fffff}] Enabling EventSource"); + EnableEvents(_source, EventLevel.Informational, _keywords, _arguments); + } + } + + ITestOutputHelper _output; + EventKeywords _keywords; + string _sessionId; + Dictionary _arguments; + EventSource _source; + AutoResetEvent _autoResetEvent = new AutoResetEvent(false); + public List Events { get; } = new List(); + + public string SessionId => _sessionId; + + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource.Name == "System.Diagnostics.Metrics") + { + _source = eventSource; + if(_keywords != 0) + { + _output.WriteLine($"[{DateTime.Now:hh:mm:ss:fffff}] Enabling EventSource"); + EnableEvents(_source, EventLevel.Informational, _keywords, _arguments); + } + } + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if(eventData.EventName != "Message" && eventData.EventName != "Error" && eventData.Payload[0].ToString() != _sessionId) + { + return; + } + lock (this) + { + Events.Add(eventData); + } + _output.WriteLine($"[{DateTime.Now:hh:mm:ss:fffff}] Event {eventData.EventName}"); + for (int i = 0; i < eventData.Payload.Count; i++) + { + if(eventData.Payload[i] is DateTime) + { + _output.WriteLine($" {eventData.PayloadNames[i]}: {((DateTime)eventData.Payload[i]).ToLocalTime():hh:mm:ss:fffff}"); + } + else + { + _output.WriteLine($" {eventData.PayloadNames[i]}: {eventData.Payload[i]}"); + } + + } + _autoResetEvent.Set(); + } + + public void WaitForCollectionStop(TimeSpan timeout, int numStops) + { + DateTime startTime = DateTime.Now; + DateTime stopTime = startTime + timeout; + int initialStopCount = GetCountCollectionStops(); + while (true) + { + if (GetCountCollectionStops() >= numStops) + { + return; + } + TimeSpan remainingTime = stopTime - DateTime.Now; + if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) + { + int currentStopCount = GetCountCollectionStops(); + throw new TimeoutException("Timed out waiting for a StopCollection event. " + + $"StartTime={startTime} stopTime={stopTime} initialStopCount={initialStopCount} currentStopCount={currentStopCount} targetStopCount={numStops}"); + } + } + } + + public void WaitForEndInstrumentReporting(TimeSpan timeout, int numEvents) + { + DateTime startTime = DateTime.Now; + DateTime stopTime = startTime + timeout; + int initialEventCount = GetCountEndInstrumentReporting(); + while (true) + { + if (GetCountEndInstrumentReporting() >= numEvents) + { + return; + } + TimeSpan remainingTime = stopTime - DateTime.Now; + if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) + { + int currentEventCount = GetCountEndInstrumentReporting(); + throw new TimeoutException("Timed out waiting for a EndInstrumentReporting event. " + + $"StartTime={startTime} stopTime={stopTime} initialEventCount={initialEventCount} currentEventCount={currentEventCount} targetEventCount={numEvents}"); + } + } + } + + public void WaitForEnumerationComplete(TimeSpan timeout) + { + DateTime startTime = DateTime.Now; + DateTime stopTime = startTime + timeout; + int initialEventCount = GetCountEnumerationComplete(); + while (true) + { + if (GetCountEnumerationComplete() >= 1) + { + return; + } + TimeSpan remainingTime = stopTime - DateTime.Now; + if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) + { + int currentEventCount = GetCountEnumerationComplete(); + throw new TimeoutException("Timed out waiting for a EndInstrumentReporting event. " + + $"StartTime={startTime} stopTime={stopTime} initialEventCount={initialEventCount} currentEventCount={currentEventCount}"); + } + } + } + + private void AssertOnError() + { + lock (this) + { + var errorEvent = Events.Where(e => e.EventName == "Error").FirstOrDefault(); + if (errorEvent != null) + { + string message = errorEvent.Payload[1].ToString(); + string stackTrace = errorEvent.Payload[2].ToString(); + Assert.True(errorEvent == null, "Unexpected Error event: " + message + Environment.NewLine + stackTrace); + } + } + } + + private int GetCountCollectionStops() + { + lock (this) + { + AssertOnError(); + return Events.Where(e => e.EventName == "CollectionStop").Count(); + } + } + + private int GetCountEndInstrumentReporting() + { + lock (this) + { + AssertOnError(); + return Events.Where(e => e.EventName == "EndInstrumentReporting").Count(); + } + } + + private int GetCountEnumerationComplete() + { + lock (this) + { + return Events.Where(e => e.EventName == "InitialInstrumentEnumerationComplete").Count(); + } + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj index a2f9daa3fee..3d38a949317 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj @@ -1,16 +1,32 @@ true - $(NetCoreAppCurrent);net48-windows + $(NetCoreAppCurrent);net48 + + + $(DefineConstants);MEMORYMARSHAL_SUPPORT + + + + + + + + + + + + + @@ -20,7 +36,9 @@ - + + + \ No newline at end of file From 2be4fdc81f7f190ac35646ccc18799deb05da7dd Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Wed, 7 Jul 2021 22:09:23 +0200 Subject: [PATCH 318/926] nit: fix doc comments spaces (#55272) --- .../Text/Json/Serialization/JsonSerializer.Read.Stream.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs index 617485fed7d..680438767cc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs @@ -28,7 +28,7 @@ namespace System.Text.Json /// The which may be used to cancel the read operation. /// /// - /// is . + /// is . /// /// /// Thrown when the JSON is invalid, @@ -61,7 +61,7 @@ namespace System.Text.Json /// JSON data to parse. /// Options to control the behavior during reading. /// - /// is . + /// is . /// /// /// Thrown when the JSON is invalid, From 0ef56df4a7c0753296a736b071946d6bc833fdc2 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 7 Jul 2021 19:32:43 -0400 Subject: [PATCH 319/926] [wasm] Update Wasm.Build.Tests to build with net6.0 (#54936) Co-authored-by: Larry Ewing --- eng/testing/tests.targets | 6 +- src/libraries/sendtohelixhelp.proj | 2 +- src/mono/wasm/build/WasmApp.LocalBuild.props | 3 + .../wasm/build/WasmApp.LocalBuild.targets | 6 +- src/mono/wasm/build/WasmApp.Native.targets | 11 +- .../Wasm.Build.Tests/BlazorWasmTests.cs | 47 ++++ .../Wasm.Build.Tests/BuildEnvironment.cs | 136 +++++++++++ .../Wasm.Build.Tests/BuildTestBase.cs | 223 +++++++----------- .../Wasm.Build.Tests/CommandResult.cs | 45 ++++ .../Wasm.Build.Tests/DotNetCommand.cs | 19 ++ .../Wasm.Build.Tests/LocalEMSDKTests.cs | 62 +++++ .../Wasm.Build.Tests/NativeBuildTests.cs | 20 -- .../Wasm.Build.Tests/ProcessExtensions.cs | 136 +++++++++++ .../Wasm.Build.Tests/RebuildTests.cs | 6 +- .../SatelliteAssembliesTests.cs | 33 ++- .../SharedBuildPerTestClassFixture.cs | 2 +- .../Wasm.Build.Tests/ToolCommand.cs | 184 +++++++++++++++ .../BuildWasmApps/Wasm.Build.Tests/Utils.cs | 58 +++++ .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 60 +++-- .../Wasm.Build.Tests/WasmBuildAppTest.cs | 22 -- .../Wasm.Build.Tests/WorkloadTests.cs | 109 +++++++++ .../data/Blazor.Directory.Build.props} | 0 .../data/Blazor.Directory.Build.targets | 77 ++++++ .../data/Local.Directory.Build.props | 14 ++ .../data/Local.Directory.Build.targets | 30 +++ .../data/RunScriptTemplate.sh | 40 ++++ .../Wasm.Build.Tests/data/nuget6.config | 16 ++ .../Directory.Build.targets | 1 - .../LibraryWithResources.csproj | 10 - .../LibraryWithResources/Class1.cs | 0 .../LibraryWithResources.csproj | 5 + .../resx/words.es-ES.resx | 127 ++++++++++ .../resx/words.ja-JP.resx | 127 ++++++++++ .../LibraryWithResources/resx/words.resx | 127 ++++++++++ .../resx/words.es-ES.resx | 127 ++++++++++ .../resx/words.ja-JP.resx | 127 ++++++++++ .../SatelliteAssemblyInMain/resx/words.resx | 127 ++++++++++ .../WebAssembly/Browser/AOT/Program.cs | 22 -- .../AOT/WebAssembly.Browser.AOT.Test.csproj | 20 -- .../WebAssembly/Browser/AOT/index.html | 55 ----- .../WebAssembly/Browser/AOT/runtime.js | 39 --- .../Browser/NormalInterp/Program.cs | 22 -- ...bAssembly.Browser.NormalInterp.Test.csproj | 20 -- .../Browser/NormalInterp/index.html | 55 ----- .../Browser/NormalInterp/runtime.js | 39 --- .../WebAssembly/Console/AOT/Program.cs | 15 -- .../AOT/WebAssembly.Console.AOT.Test.csproj | 11 - .../Console/NormalInterp/Program.cs | 15 -- ...bAssembly.Console.NormalInterp.Test.csproj | 10 - .../TopLevel/NormalInterp/Program.cs | 9 - ...Assembly.TopLevel.NormalInterp.Test.csproj | 10 - 51 files changed, 1906 insertions(+), 581 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs rename src/tests/BuildWasmApps/{testassets/LibraryWithResources/Directory.Build.props => Wasm.Build.Tests/data/Blazor.Directory.Build.props} (100%) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config delete mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets delete mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj rename src/tests/BuildWasmApps/testassets/{ => SatelliteAssemblyFromProjectRef}/LibraryWithResources/Class1.cs (100%) create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx create mode 100644 src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html delete mode 100644 src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js delete mode 100644 src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs delete mode 100644 src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj delete mode 100644 src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs delete mode 100644 src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj delete mode 100644 src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs delete mode 100644 src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index dd40856e629..1e7ec3b173d 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -1,13 +1,15 @@ - + RunnerTemplate.cmd RunnerTemplate.sh AppleRunnerTemplate.sh AndroidRunnerTemplate.sh WasmRunnerTemplate.sh WasmRunnerTemplate.cmd + - $(MSBuildThisFileDirectory)$(RunScriptInputName) + + $(MSBuildThisFileDirectory)$(RunScriptInputName) RunTests.sh RunTests.cmd diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 12d402fcb7e..d6d9a78c22e 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -311,7 +311,7 @@ - + diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.props b/src/mono/wasm/build/WasmApp.LocalBuild.props index 01003f59172..69a719a0e6d 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.props +++ b/src/mono/wasm/build/WasmApp.LocalBuild.props @@ -22,6 +22,9 @@ + + true + <_NetCoreAppToolCurrent>net6.0 diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets index 636f0b1d257..fac5252a4ef 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.targets +++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets @@ -37,14 +37,14 @@ false - - + $(MicrosoftNetCoreAppRuntimePackLocationToUse) + - + diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 50e491ac060..4b8335a74d6 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -31,11 +31,6 @@ <_ExeExt Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">.exe true - $([MSBuild]::NormalizeDirectory($(NuGetPackageRoot), 'microsoft.netcore.app.runtime.mono.browser-wasm', '$(BundledNETCoreAppPackageVersion)')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) <_EmccPropsPath>$(_WasmRuntimePackSrcDir)Emcc.props @@ -178,11 +173,6 @@ <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> - - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) - <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) - - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> @@ -361,6 +351,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs new file mode 100644 index 00000000000..c58c4b55f3d --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class BlazorWasmTests : BuildTestBase + { + public BlazorWasmTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + public void PublishTemplateProject() + { + InitPaths("id"); + if (Directory.Exists(_projectDir)) + Directory.Delete(_projectDir, recursive: true); + Directory.CreateDirectory(_projectDir); + Directory.CreateDirectory(Path.Combine(_projectDir, ".nuget")); + + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "nuget6.config"), Path.Combine(_projectDir, "nuget.config")); + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.props"), Path.Combine(_projectDir, "Directory.Build.props")); + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets")); + + new DotNetCommand(s_buildEnv) + .WithWorkingDirectory(_projectDir) + .ExecuteWithCapturedOutput("new blazorwasm") + .EnsureSuccessful(); + + new DotNetCommand(s_buildEnv) + .WithWorkingDirectory(_projectDir) + .ExecuteWithCapturedOutput("publish -bl -p:RunAOTCompilation=true") + .EnsureSuccessful(); + + //TODO: validate the build somehow? + // compare dotnet.wasm? + // playwright? + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs new file mode 100644 index 00000000000..231b215f88b --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; + +namespace Wasm.Build.Tests +{ + public class BuildEnvironment + { + public string DotNet { get; init; } + public string RuntimePackDir { get; init; } + public bool IsWorkload { get; init; } + public string DefaultBuildArgs { get; init; } + public IDictionary EnvVars { get; init; } + public string DirectoryBuildPropsContents { get; init; } + public string DirectoryBuildTargetsContents { get; init; } + public string RuntimeNativeDir { get; init; } + public string LogRootPath { get; init; } + + public static readonly string RelativeTestAssetsPath = @"..\testassets\"; + public static readonly string TestAssetsPath = Path.Combine(AppContext.BaseDirectory, "testassets"); + public static readonly string TestDataPath = Path.Combine(AppContext.BaseDirectory, "data"); + + private static string s_runtimeConfig = "Release"; + private const string s_testLogPathEnvVar = "TEST_LOG_PATH"; + + public BuildEnvironment() + { + DirectoryInfo? solutionRoot = new (AppContext.BaseDirectory); + while (solutionRoot != null) + { + if (File.Exists(Path.Combine(solutionRoot.FullName, "NuGet.config"))) + { + break; + } + + solutionRoot = solutionRoot.Parent; + } + + string? sdkForWorkloadPath = Environment.GetEnvironmentVariable("SDK_FOR_WORKLOAD_TESTING_PATH"); + if (!string.IsNullOrEmpty(sdkForWorkloadPath)) + { + + DotNet = Path.Combine(sdkForWorkloadPath, "dotnet"); + var workloadPacksVersion = Environment.GetEnvironmentVariable("WORKLOAD_PACKS_VER"); + if (string.IsNullOrEmpty(workloadPacksVersion)) + throw new Exception($"Cannot test with workloads without WORKLOAD_PACKS_VER environment variable being set"); + + RuntimePackDir = Path.Combine(sdkForWorkloadPath, "packs", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", workloadPacksVersion); + DirectoryBuildPropsContents = s_directoryBuildPropsForWorkloads; + DirectoryBuildTargetsContents = s_directoryBuildTargetsForWorkloads; + EnvVars = new Dictionary() + { + // `runtime` repo's build environment sets these, and they + // mess up the build for the test project, which is using a different + // dotnet + ["DOTNET_INSTALL_DIR"] = sdkForWorkloadPath, + ["DOTNET_MULTILEVEL_LOOKUP"] = "0", + ["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1", + ["MSBuildSDKsPath"] = string.Empty, + ["PATH"] = $"{sdkForWorkloadPath}{Path.PathSeparator}{Environment.GetEnvironmentVariable("PATH")}" + }; + + var appRefDir = Environment.GetEnvironmentVariable("AppRefDir"); + if (string.IsNullOrEmpty(appRefDir)) + throw new Exception($"Cannot test with workloads without AppRefDir environment variable being set"); + + DefaultBuildArgs = $" /p:AppRefDir={appRefDir}"; + IsWorkload = true; + } + else + { + string emsdkPath; + if (solutionRoot == null) + { + string? buildDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); + + if (buildDir == null || !Directory.Exists(buildDir)) + throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); + + emsdkPath = Path.Combine(buildDir, "emsdk"); + RuntimePackDir = Path.Combine(buildDir, "microsoft.netcore.app.runtime.browser-wasm"); + DefaultBuildArgs = $" /p:WasmBuildSupportDir={buildDir} /p:EMSDK_PATH={emsdkPath} "; + } + else + { + string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); + RuntimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); + + string? emsdkEnvValue = Environment.GetEnvironmentVariable("EMSDK_PATH"); + if (string.IsNullOrEmpty(emsdkEnvValue)) + emsdkPath = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); + else + emsdkPath = emsdkEnvValue; + + DefaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={emsdkPath} "; + } + + IsWorkload = false; + DotNet = "dotnet"; + EnvVars = new Dictionary() + { + ["EMSDK_PATH"] = emsdkPath + }; + + DirectoryBuildPropsContents = s_directoryBuildPropsForLocal; + DirectoryBuildTargetsContents = s_directoryBuildTargetsForLocal; + } + + RuntimeNativeDir = Path.Combine(RuntimePackDir, "runtimes", "browser-wasm", "native"); + + string? logPathEnvVar = Environment.GetEnvironmentVariable(s_testLogPathEnvVar); + if (!string.IsNullOrEmpty(logPathEnvVar)) + { + LogRootPath = logPathEnvVar; + if (!Directory.Exists(LogRootPath)) + { + Directory.CreateDirectory(LogRootPath); + } + } + else + { + LogRootPath = Environment.CurrentDirectory; + } + } + + // FIXME: update these to use Workload variants of the file, with the workload support + protected static string s_directoryBuildPropsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props")); + protected static string s_directoryBuildTargetsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets")); + + protected static string s_directoryBuildPropsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props")); + protected static string s_directoryBuildTargetsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets")); + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b87ff5c70bf..021ce531b17 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using Xunit; using Xunit.Abstractions; using Xunit.Sdk; @@ -20,15 +21,9 @@ namespace Wasm.Build.Tests { public abstract class BuildTestBase : IClassFixture, IDisposable { - protected const string TestLogPathEnvVar = "TEST_LOG_PATH"; protected const string SkipProjectCleanupEnvVar = "SKIP_PROJECT_CLEANUP"; protected const string XHarnessRunnerCommandEnvVar = "XHARNESS_CLI_PATH"; - protected const string s_targetFramework = "net5.0"; - protected static string s_runtimeConfig = "Release"; - protected static string s_runtimePackDir; - protected static string s_defaultBuildArgs; - protected static readonly string s_logRoot; - protected static readonly string s_emsdkPath; + protected const string s_targetFramework = "net6.0"; protected static readonly bool s_skipProjectCleanup; protected static readonly string s_xharnessRunnerCommand; protected string? _projectDir; @@ -37,76 +32,53 @@ namespace Wasm.Build.Tests protected bool _enablePerTestCleanup = false; protected SharedBuildPerTestClassFixture _buildContext; + protected static BuildEnvironment s_buildEnv; + private const string s_runtimePackPathPattern = "\\*\\* MicrosoftNetCoreAppRuntimePackDir : ([^ ]*)"; + private static Regex s_runtimePackPathRegex; + + public static bool IsUsingWorkloads => s_buildEnv.IsWorkload; + public static bool IsNotUsingWorkloads => !s_buildEnv.IsWorkload; + static BuildTestBase() { - DirectoryInfo? solutionRoot = new (AppContext.BaseDirectory); - while (solutionRoot != null) - { - if (File.Exists(Path.Combine(solutionRoot.FullName, "NuGet.config"))) - { - break; - } - - solutionRoot = solutionRoot.Parent; - } - - if (solutionRoot == null) - { - string? buildDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); - - if (buildDir == null || !Directory.Exists(buildDir)) - throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); - - s_emsdkPath = Path.Combine(buildDir, "emsdk"); - s_runtimePackDir = Path.Combine(buildDir, "microsoft.netcore.app.runtime.browser-wasm"); - s_defaultBuildArgs = $" /p:WasmBuildSupportDir={buildDir} /p:EMSDK_PATH={s_emsdkPath} "; - } - else - { - string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); - s_runtimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); - - string? emsdk = Environment.GetEnvironmentVariable("EMSDK_PATH"); - if (string.IsNullOrEmpty(emsdk)) - emsdk = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); - s_emsdkPath = emsdk; - - s_defaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={s_emsdkPath} "; - } - - string? logPathEnvVar = Environment.GetEnvironmentVariable(TestLogPathEnvVar); - if (!string.IsNullOrEmpty(logPathEnvVar)) - { - s_logRoot = logPathEnvVar; - if (!Directory.Exists(s_logRoot)) - { - Directory.CreateDirectory(s_logRoot); - } - } - else - { - s_logRoot = Environment.CurrentDirectory; - } + s_buildEnv = new BuildEnvironment(); + s_runtimePackPathRegex = new Regex(s_runtimePackPathPattern); string? cleanupVar = Environment.GetEnvironmentVariable(SkipProjectCleanupEnvVar); s_skipProjectCleanup = !string.IsNullOrEmpty(cleanupVar) && cleanupVar == "1"; - string? harnessVar = Environment.GetEnvironmentVariable(XHarnessRunnerCommandEnvVar); - if (string.IsNullOrEmpty(harnessVar)) + s_xharnessRunnerCommand = GetEnvironmentVariableOrDefault(XHarnessRunnerCommandEnvVar, "xharness"); + + string? nugetPackagesPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); + if (!string.IsNullOrEmpty(nugetPackagesPath)) { - s_xharnessRunnerCommand = "xharness"; + if (!Directory.Exists(nugetPackagesPath)) + { + Directory.CreateDirectory(nugetPackagesPath); + Console.WriteLine ($"-- Created {nugetPackagesPath}"); + } + else + { + Console.WriteLine ($"-- already exists {nugetPackagesPath}"); + } } else { - s_xharnessRunnerCommand = $"exec {harnessVar}"; + Console.WriteLine ($"-- NUGET_PACKAGES envvar was empty"); } + + Console.WriteLine (""); + Console.WriteLine ($"=============================================================================================="); + Console.WriteLine ($"=============== Running with {(s_buildEnv.IsWorkload ? "Workloads" : "EMSDK")} ==============="); + Console.WriteLine ($"=============================================================================================="); + Console.WriteLine (""); } public BuildTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) { _buildContext = buildContext; _testOutput = output; - _logPath = s_logRoot; // FIXME: + _logPath = s_buildEnv.LogRootPath; // FIXME: } /* @@ -154,11 +126,16 @@ namespace Wasm.Build.Tests envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { - envVars["EMSDK_PATH"] = s_emsdkPath; envVars["MONO_LOG_LEVEL"] = "debug"; envVars["MONO_LOG_MASK"] = "aot"; } + if (s_buildEnv.EnvVars != null) + { + foreach (var kvp in s_buildEnv.EnvVars) + envVars[kvp.Key] = kvp.Value; + } + string bundleDir = Path.Combine(GetBinDir(baseDir: buildDir, config: buildArgs.Config), "AppBundle"); (string testCommand, string extraXHarnessArgs) = host switch { @@ -215,7 +192,7 @@ namespace Wasm.Build.Tests // App arguments if (envVars != null) { - var setenv = string.Join(' ', envVars.Select(kvp => $"--setenv={kvp.Key}={kvp.Value}").ToArray()); + var setenv = string.Join(' ', envVars.Select(kvp => $"\"--setenv={kvp.Key}={kvp.Value}\"").ToArray()); args.Append($" {setenv}"); } @@ -224,7 +201,7 @@ namespace Wasm.Build.Tests _testOutput.WriteLine(string.Empty); _testOutput.WriteLine($"---------- Running with {testCommand} ---------"); - var (exitCode, output) = RunProcess("dotnet", _testOutput, + var (exitCode, output) = RunProcess(s_buildEnv.DotNet, _testOutput, args: args.ToString(), workingDir: bundleDir, envVars: envVars, @@ -235,7 +212,8 @@ namespace Wasm.Build.Tests if (exitCode != xharnessExitCode) { _testOutput.WriteLine($"Exit code: {exitCode}"); - Assert.True(exitCode == expectedAppExitCode, $"[{testCommand}] Exit code, expected {expectedAppExitCode} but got {exitCode}"); + if (exitCode != expectedAppExitCode) + throw new XunitException($"[{testCommand}] Exit code, expected {expectedAppExitCode} but got {exitCode} for command: {testCommand} {args}"); } return output; @@ -245,7 +223,7 @@ namespace Wasm.Build.Tests protected void InitPaths(string id) { _projectDir = Path.Combine(AppContext.BaseDirectory, id); - _logPath = Path.Combine(s_logRoot, id); + _logPath = Path.Combine(s_buildEnv.LogRootPath, id); Directory.CreateDirectory(_logPath); } @@ -253,8 +231,11 @@ namespace Wasm.Build.Tests protected static void InitProjectDir(string dir) { Directory.CreateDirectory(dir); - File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_directoryBuildProps); - File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_directoryBuildTargets); + File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_buildEnv.DirectoryBuildPropsContents); + File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_buildEnv.DirectoryBuildTargetsContents); + + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "nuget6.config"), Path.Combine(dir, "nuget.config")); + Directory.CreateDirectory(Path.Combine(dir, ".nuget")); } protected const string SimpleProjectTemplate = @@ -285,8 +266,8 @@ namespace Wasm.Build.Tests } public (string projectDir, string buildOutput) BuildProject(BuildArgs buildArgs, - Action initProject, string id, + Action? initProject = null, bool? dotnetWasmFromRuntimePack = null, bool hasIcudt = true, bool useCache = true, @@ -301,7 +282,7 @@ namespace Wasm.Build.Tests _projectDir = product.ProjectDir; // use this test's id for the run logs - _logPath = Path.Combine(s_logRoot, id); + _logPath = Path.Combine(s_buildEnv.LogRootPath, id); return (_projectDir, "FIXME"); } @@ -321,13 +302,14 @@ namespace Wasm.Build.Tests StringBuilder sb = new(); sb.Append("publish"); - sb.Append(s_defaultBuildArgs); + sb.Append($" {s_buildEnv.DefaultBuildArgs}"); sb.Append($" /p:Configuration={buildArgs.Config}"); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog"); _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); + Console.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); if (buildArgs.ExtraBuildArgs != null) sb.Append($" {buildArgs.ExtraBuildArgs} "); @@ -337,12 +319,16 @@ namespace Wasm.Build.Tests (int exitCode, string buildOutput) result; try { - result = AssertBuild(sb.ToString(), id, expectSuccess: expectSuccess); + result = AssertBuild(sb.ToString(), id, expectSuccess: expectSuccess, envVars: s_buildEnv.EnvVars); + + //AssertRuntimePackPath(result.buildOutput); + + // check that we are using the correct runtime pack! + if (expectSuccess) { string bundleDir = Path.Combine(GetBinDir(config: buildArgs.Config), "AppBundle"); - dotnetWasmFromRuntimePack ??= !buildArgs.AOT; - AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, hasIcudt, dotnetWasmFromRuntimePack.Value); + AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, hasIcudt, dotnetWasmFromRuntimePack ?? !buildArgs.AOT); } if (useCache) @@ -361,8 +347,20 @@ namespace Wasm.Build.Tests } } + static void AssertRuntimePackPath(string buildOutput) + { + var match = s_runtimePackPathRegex.Match(buildOutput); + if (!match.Success || match.Groups.Count != 2) + throw new XunitException($"Could not find the pattern in the build output: '{s_runtimePackPathPattern}'.{Environment.NewLine}Build output: {buildOutput}"); + + string actualPath = match.Groups[1].Value; + if (string.Compare(actualPath, s_buildEnv.RuntimePackDir) != 0) + throw new XunitException($"Runtime pack path doesn't match.{Environment.NewLine}Expected: {s_buildEnv.RuntimePackDir}{Environment.NewLine}Actual: {actualPath}"); + } + protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true) { + Console.WriteLine ($"AssertBasicAppBundle: {dotnetWasmFromRuntimePack}"); AssertFilesExist(bundleDir, new [] { "index.html", @@ -398,16 +396,15 @@ namespace Wasm.Build.Tests protected static void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack) { - string nativeDir = GetRuntimeNativeDir(); + AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"), + Path.Combine(bundleDir, "dotnet.wasm"), + "Expected dotnet.wasm to be same as the runtime pack", + same: fromRuntimePack); - AssertNativeFile("dotnet.wasm"); - AssertNativeFile("dotnet.js"); - - void AssertNativeFile(string file) - => AssertFile(Path.Combine(nativeDir, file), - Path.Combine(bundleDir, file), - $"Expected {file} to be {(fromRuntimePack ? "the same as" : "different from")} the runtime pack", - same: fromRuntimePack); + AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"), + Path.Combine(bundleDir, "dotnet.js"), + "Expected dotnet.js to be same as the runtime pack", + same: fromRuntimePack); } protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) @@ -454,9 +451,9 @@ namespace Wasm.Build.Tests Assert.True(finfo0.Length != finfo1.Length, $"{label}: File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})"); } - protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true) + protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true, IDictionary? envVars=null) { - var result = RunProcess("dotnet", _testOutput, args, workingDir: _projectDir, label: label); + var result = RunProcess(s_buildEnv.DotNet, _testOutput, args, workingDir: _projectDir, label: label, envVars: envVars); if (expectSuccess) Assert.True(0 == result.exitCode, $"Build process exited with non-zero exit code: {result.exitCode}"); else @@ -465,9 +462,6 @@ namespace Wasm.Build.Tests return result; } - // protected string GetObjDir(string targetFramework=s_targetFramework, string? baseDir=null, string config="Debug") - // => Path.Combine(baseDir ?? _projectDir, "obj", config, targetFramework, "browser-wasm", "wasm"); - protected string GetBinDir(string config, string targetFramework=s_targetFramework, string? baseDir=null) { var dir = baseDir ?? _projectDir; @@ -475,12 +469,6 @@ namespace Wasm.Build.Tests return Path.Combine(dir!, "bin", config, targetFramework, "browser-wasm"); } - protected static string GetRuntimePackDir() => s_runtimePackDir; - - protected static string GetRuntimeNativeDir() - => Path.Combine(GetRuntimePackDir(), "runtimes", "browser-wasm", "native"); - - public static (int exitCode, string buildOutput) RunProcess(string path, ITestOutputHelper _testOutput, string args = "", @@ -570,6 +558,12 @@ namespace Wasm.Build.Tests _buildContext.RemoveFromCache(_projectDir); } + private static string GetEnvironmentVariableOrDefault(string envVarName, string defaultValue) + { + string? value = Environment.GetEnvironmentVariable(envVarName); + return string.IsNullOrEmpty(value) ? defaultValue : value; + } + protected static string s_mainReturns42 = @" public class TestClass { public static int Main() @@ -577,49 +571,6 @@ namespace Wasm.Build.Tests return 42; } }"; - - protected static string s_directoryBuildProps = @" - - <_WasmTargetsDir Condition=""'$(RuntimeSrcDir)' != ''"">$(RuntimeSrcDir)\src\mono\wasm\build\ - <_WasmTargetsDir Condition=""'$(WasmBuildSupportDir)' != ''"">$(WasmBuildSupportDir)\wasm\ - $(WasmBuildSupportDir)\emsdk\ - - - - - - - PrepareForWasmBuild;$(WasmBuildAppDependsOn) - -"; - - protected static string s_directoryBuildTargets = @" - - - - - - - - - - - - - - - - - - -"; - } public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs new file mode 100644 index 00000000000..24228f652a0 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Text; +using Xunit.Sdk; + +namespace Wasm.Build.Tests +{ + // taken from https://github.com/dotnet/arcade/blob/main/src/Common/Microsoft.Arcade.Common/CommandResult.cs + public struct CommandResult + { + public static readonly CommandResult Empty = new CommandResult(); + + public ProcessStartInfo StartInfo { get; } + public int ExitCode { get; } + public string Output { get; } + + public CommandResult(ProcessStartInfo startInfo, int exitCode, string output) + { + StartInfo = startInfo; + ExitCode = exitCode; + Output = output; + } + + public void EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false) + { + if (ExitCode != 0) + { + StringBuilder message = new StringBuilder($"{messagePrefix} Command failed with exit code {ExitCode}: {StartInfo.FileName} {StartInfo.Arguments}"); + + if (!suppressOutput) + { + if (!string.IsNullOrEmpty(Output)) + { + message.AppendLine($"{Environment.NewLine}Standard Output:{Environment.NewLine}{Output}"); + } + } + + throw new XunitException(message.ToString()); + } + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs new file mode 100644 index 00000000000..2fefb87c0f0 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Wasm.Build.Tests +{ + public class DotNetCommand : ToolCommand + { + private BuildEnvironment _buildEnvironment; + + public DotNetCommand(BuildEnvironment buildEnv) : base(buildEnv.DotNet) + { + _buildEnvironment = buildEnv; + WithEnvironmentVariables(buildEnv.EnvVars); + } + + protected override string GetFullArgs(params string[] args) + => $"{_buildEnvironment.DefaultBuildArgs} {string.Join(" ", args)}"; + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs new file mode 100644 index 00000000000..30ed330bf17 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class LocalEMSDKTests : BuildTestBase + { + public LocalEMSDKTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) : base(output, buildContext) + {} + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "", "error :.*emscripten.*required for AOT" })] + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for AOT" })] + public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) + { + string projectName = $"missing_emsdk"; + buildArgs = buildArgs with { + ProjectName = projectName, + ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" + }; + buildArgs = ExpandBuildArgs(buildArgs); + + (_, string buildOutput) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + id: id, + expectSuccess: false); + + Assert.Matches(errorPattern, buildOutput); + } + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] + [BuildAndRun(host: RunHost.None, parameters: new object[] + { "", "error.*emscripten.*required for building native files" })] + [BuildAndRun(host: RunHost.None, parameters: new object[] + { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for building native files" })] + public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) + { + string projectName = $"simple_native_build"; + buildArgs = buildArgs with { + ProjectName = projectName, + ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" + }; + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); + + (_, string buildOutput) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + id: id, + expectSuccess: false); + + Assert.Matches(errorPattern, buildOutput); + } + } + } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 9f87757175c..130e59e0ea2 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -27,27 +27,7 @@ namespace Wasm.Build.Tests public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id); - [Theory] - [BuildAndRun(host: RunHost.None, parameters: new object[] - { "", "error.*emscripten.*required for building native files" })] - [BuildAndRun(host: RunHost.None, parameters: new object[] - { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for building native files" })] - public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) - { - string projectName = $"simple_native_build"; - buildArgs = buildArgs with { - ProjectName = projectName, - ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" - }; - buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); - (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - id: id, - expectSuccess: false); - - Assert.Matches(errorPattern, buildOutput); - } private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs new file mode 100644 index 00000000000..ca60d32d22a --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs @@ -0,0 +1,136 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Wasm.Build.Tests +{ + internal static class ProcessExtensions + { +#if NET451 + private static readonly bool _isWindows = true; +#else + private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); + + public static void KillTree(this Process process) + { + process.KillTree(_defaultTimeout); + } + + public static void KillTree(this Process process, TimeSpan timeout) + { + string stdout; + if (_isWindows) + { + RunProcessAndWaitForExit( + "taskkill", + $"/T /F /PID {process.Id}", + timeout, + out stdout); + } + else + { + var children = new HashSet(); + GetAllChildIdsUnix(process.Id, children, timeout); + foreach (var childId in children) + { + KillProcessUnix(childId, timeout); + } + KillProcessUnix(process.Id, timeout); + } + } + + private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) + { + string stdout; + var exitCode = RunProcessAndWaitForExit( + "pgrep", + $"-P {parentId}", + timeout, + out stdout); + + if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) + { + using (var reader = new StringReader(stdout)) + { + while (true) + { + var text = reader.ReadLine(); + if (text == null) + { + return; + } + + int id; + if (int.TryParse(text, out id)) + { + children.Add(id); + // Recursively get the children + GetAllChildIdsUnix(id, children, timeout); + } + } + } + } + } + + private static void KillProcessUnix(int processId, TimeSpan timeout) + { + string stdout; + RunProcessAndWaitForExit( + "kill", + $"-TERM {processId}", + timeout, + out stdout); + } + + private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) + { + var startInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + + stdout = null; + if (process.WaitForExit((int)timeout.TotalMilliseconds)) + { + stdout = process.StandardOutput.ReadToEnd(); + } + else + { + process.Kill(); + } + + return process.ExitCode; + } + + public static Task StartAndWaitForExitAsync(this Process subject) + { + var taskCompletionSource = new TaskCompletionSource(); + + subject.EnableRaisingEvents = true; + + subject.Exited += (s, a) => + { + taskCompletionSource.SetResult(null); + + subject.Dispose(); + }; + + subject.Start(); + + return taskCompletionSource.Task; + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index ecbcbdd75c0..769d22fa660 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -5,6 +5,7 @@ using System; using System.IO; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -38,7 +39,7 @@ namespace Wasm.Build.Tests Run(); if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) - Assert.True(false, $"Test bug: could not get the build product in the cache"); + throw new XunitException($"Test bug: could not get the build product in the cache"); File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog")); @@ -46,9 +47,8 @@ namespace Wasm.Build.Tests // no-op Rebuild BuildProject(buildArgs, - () => {}, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id, + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, createProject: false, useCache: false); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs index e9d973650dc..45ce2b37ed2 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -44,11 +45,14 @@ namespace Wasm.Build.Tests buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, projectTemplate: s_resourcesProjectTemplate, - extraProperties: $"{(nativeRelink ? "true" : "false")}", - extraItems: $""); + extraProperties: $"{(nativeRelink ? "true" : "false")}"); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + initProject: () => + { + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "resx"), Path.Combine(_projectDir!, "resx")); + CreateProgramForCultureTest(_projectDir!, $"{projectName}.resx.words", "TestClass"); + }, dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id); @@ -71,7 +75,7 @@ namespace Wasm.Build.Tests RunHost host, string id) { - string projectName = $"sat_asm_proj_ref"; + string projectName = $"SatelliteAssemblyFromProjectRef"; bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; buildArgs = buildArgs with { ProjectName = projectName }; @@ -81,9 +85,17 @@ namespace Wasm.Build.Tests extraItems: $""); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest("LibraryWithResources.words", "LibraryWithResources.Class1"), dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - id: id); + id: id, + initProject: () => + { + string rootDir = _projectDir!; + _projectDir = Path.Combine(rootDir, projectName); + + Directory.CreateDirectory(_projectDir); + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "SatelliteAssemblyFromProjectRef"), rootDir); + CreateProgramForCultureTest(_projectDir, "LibraryWithResources.resx.words", "LibraryWithResources.Class1"); + }); string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, @@ -105,11 +117,10 @@ namespace Wasm.Build.Tests extraProperties: $@" -O0 -O0", - extraItems: $""); + extraItems: $""); - System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + initProject: () => CreateProgramForCultureTest(_projectDir!, $"{projectName}.words", "TestClass"), dotnetWasmFromRuntimePack: false, id: id); @@ -124,8 +135,8 @@ namespace Wasm.Build.Tests } #pragma warning restore xUnit1026 - private void CreateProgramForCultureTest(string resourceName, string typeName) - => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), + private void CreateProgramForCultureTest(string dir, string resourceName, string typeName) + => File.WriteAllText(Path.Combine(dir, "Program.cs"), s_cultureResourceTestProgram .Replace("##RESOURCE_NAME##", resourceName) .Replace("##TYPE_NAME##", typeName)); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index e84a151bd5b..2c43614ea41 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -44,7 +44,7 @@ namespace Wasm.Build.Tests { try { - Directory.Delete(path, recursive: true); + Directory.Delete(path, recursive: true); } catch (Exception ex) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs new file mode 100644 index 00000000000..b45fefac9ac --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs @@ -0,0 +1,184 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class ToolCommand + { + private string _label; + + protected string _command; + + public Process? CurrentProcess { get; private set; } + + public Dictionary Environment { get; } = new Dictionary(); + + public event DataReceivedEventHandler? ErrorDataReceived; + + public event DataReceivedEventHandler? OutputDataReceived; + + public string? WorkingDirectory { get; set; } + + public ToolCommand(string command, string label="") + { + _command = command; + _label = label; + } + + public ToolCommand WithWorkingDirectory(string dir) + { + WorkingDirectory = dir; + return this; + } + + public ToolCommand WithEnvironmentVariable(string key, string value) + { + Environment[key] = value; + return this; + } + + public ToolCommand WithEnvironmentVariables(IDictionary? extraEnvVars) + { + if (extraEnvVars != null) + { + foreach ((string key, string value) in extraEnvVars) + Environment[key] = value; + } + + return this; + } + + public virtual CommandResult Execute(params string[] args) + { + return Task.Run(async () => await ExecuteAsync(args)).Result; + } + + public async virtual Task ExecuteAsync(params string[] args) + { + var resolvedCommand = _command; + string fullArgs = GetFullArgs(args); + Console.WriteLine($"[{_label}] Executing - {resolvedCommand} {fullArgs} - {WorkingDirectoryInfo()}"); + return await ExecuteAsyncInternal(resolvedCommand, fullArgs); + } + + public virtual CommandResult ExecuteWithCapturedOutput(params string[] args) + { + var resolvedCommand = _command; + string fullArgs = GetFullArgs(args); + Console.WriteLine($"[{_label}] Executing (Captured Output) - {resolvedCommand} {fullArgs} - {WorkingDirectoryInfo()}"); + return Task.Run(async () => await ExecuteAsyncInternal(resolvedCommand, fullArgs)).Result; + } + + protected virtual string GetFullArgs(params string[] args) => string.Join(" ", args); + + private async Task ExecuteAsyncInternal(string executable, string args) + { + var output = new List(); + CurrentProcess = CreateProcess(executable, args); + CurrentProcess.ErrorDataReceived += (s, e) => + { + if (e.Data == null) + return; + + output.Add($"[{_label}] {e.Data}"); + ErrorDataReceived?.Invoke(s, e); + }; + + CurrentProcess.OutputDataReceived += (s, e) => + { + if (e.Data == null) + return; + + output.Add($"[{_label}] {e.Data}"); + OutputDataReceived?.Invoke(s, e); + }; + + var completionTask = CurrentProcess.StartAndWaitForExitAsync(); + CurrentProcess.BeginOutputReadLine(); + CurrentProcess.BeginErrorReadLine(); + await completionTask; + + CurrentProcess.WaitForExit(); + RemoveNullTerminator(output); + + return new CommandResult( + CurrentProcess.StartInfo, + CurrentProcess.ExitCode, + string.Join(System.Environment.NewLine, output)); + } + + private Process CreateProcess(string executable, string args) + { + var psi = new ProcessStartInfo + { + FileName = executable, + Arguments = args, + RedirectStandardError = true, + RedirectStandardOutput = true, + RedirectStandardInput = true, + UseShellExecute = false + }; + + psi.Environment["DOTNET_MULTILEVEL_LOOKUP"] = "0"; + psi.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1"; + + AddEnvironmentVariablesTo(psi); + AddWorkingDirectoryTo(psi); + var process = new Process + { + StartInfo = psi + }; + + process.EnableRaisingEvents = true; + return process; + } + + private string WorkingDirectoryInfo() + { + if (WorkingDirectory == null) + { + return ""; + } + + return $" in pwd {WorkingDirectory}"; + } + + private void RemoveNullTerminator(List strings) + { + var count = strings.Count; + + if (count < 1) + { + return; + } + + if (strings[count - 1] == null) + { + strings.RemoveAt(count - 1); + } + } + + private void AddEnvironmentVariablesTo(ProcessStartInfo psi) + { + foreach (var item in Environment) + { + psi.Environment[item.Key] = item.Value; + } + } + + private void AddWorkingDirectoryTo(ProcessStartInfo psi) + { + if (!string.IsNullOrWhiteSpace(WorkingDirectory)) + { + psi.WorkingDirectory = WorkingDirectory; + } + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs new file mode 100644 index 00000000000..048368cc430 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +internal static class Utils +{ + public static void DirectoryCopy(string sourceDirName, string destDirName, Func? predicate=null, bool copySubDirs=true, bool silent=false) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + + // If the destination directory doesn't exist, create it. + Directory.CreateDirectory(destDirName); + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string fullPath = file.ToString(); + if (predicate != null && !predicate(fullPath)) + { + // if (!silent) + // e(MessageImportance.Low, $"Skipping {fullPath}"); + continue; + } + + string tempPath = Path.Combine(destDirName, file.Name); + // if (!silent) + // Logger?.LogMessage(MessageImportance.Low, $"Copying {fullPath} to {tempPath}"); + file.CopyTo(tempPath, false); + } + + // If copying subdirectories, copy them and their contents to new location. + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath, predicate, copySubDirs, silent); + } + } + } + +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 20a19724307..0f06b6f08df 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -13,31 +13,53 @@ TEST_DEBUG_CONFIG_ALSO false + true - - - - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">WasmBuildSupportDir=%24{HELIX_CORRELATION_PAYLOAD}/build - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">set WasmBuildSupportDir=%HELIX_CORRELATION_PAYLOAD%/build & - - - - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) TEST_LOG_PATH=%24{XHARNESS_OUT}/logs - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) HARNESS_RUNNER=%24{HARNESS_RUNNER} - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set TEST_LOG_PATH=%XHARNESS_OUT%\logs & - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set HARNESS_RUNNER=%HARNESS_RUNNER% & - - $(_PreCommand) dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml - $(_PreCommand) dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml - $(RunScriptCommand) -nocolor - $(RunScriptCommand) -verbose + RunScriptTemplate.sh + $(MSBuildThisFileDirectory)data\$(RunScriptInputName) - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml + $(RunScriptCommand) -nocolor + $(RunScriptCommand) -verbose + + $(RunScriptCommand) -method $(XUnitMethodName) + $(RunScriptCommand) -class $(XUnitClassName) + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index e426c9523d9..59b82318a12 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -60,28 +60,6 @@ namespace Wasm.Build.Tests } }", buildArgs, host, id); - [Theory] - [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] - { "", "error :.*emscripten.*required for AOT" })] - [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] - { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for AOT" })] - public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) - { - string projectName = $"missing_emsdk"; - buildArgs = buildArgs with { - ProjectName = projectName, - ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" - }; - buildArgs = ExpandBuildArgs(buildArgs); - - (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - id: id, - expectSuccess: false); - - Assert.Matches(errorPattern, buildOutput); - } - private static string s_bug49588_ProgramCS = @" using System; public class TestClass { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs new file mode 100644 index 00000000000..89b656ee444 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class WorkloadTests : BuildTestBase + { + public WorkloadTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + public void FilesInUnixFilesPermissionsXmlExist() + { + // not doing any project generation here + _enablePerTestCleanup = false; + + // find all the UnixFilePermissions .. + string packsDir = Path.Combine(Path.GetDirectoryName(s_buildEnv.DotNet)!, "packs"); + Assert.True(Directory.Exists(packsDir), $"Could not find packs directory {packsDir}"); + + var unixPermFiles = Directory.EnumerateFiles(packsDir, "UnixFilePermissions.xml", new EnumerationOptions { RecurseSubdirectories = true }); + foreach (string unixPermFile in unixPermFiles) + { + Assert.True(File.Exists(unixPermFile), $"Could not find {unixPermFile}"); + FileList? list = FileList.Deserialize(unixPermFile); + if (list == null) + throw new Exception($"Could not read unix permissions file {unixPermFile}"); + + // File is in ///data/UnixFilePermissions.xml + // and + string thisPackDir = Path.Combine(Path.GetDirectoryName(unixPermFile)!, ".."); + foreach (FileListFile flf in list.File) + { + if (flf.Path == null) + throw new Exception($"Path for FileListFile should not be null. xml: {unixPermFile}"); + + var targetFile = Path.Combine(thisPackDir, flf.Path); + Assert.True(File.Exists(targetFile), $"Expected file {targetFile} to exist in the pack, as it is referenced in {unixPermFile}"); + } + } + + // We don't install the cross compiler pack from nupkg, so we don't + // have the unixFilePermissions for that + // Expect just the emscripten ones here for now + Assert.Equal(3, unixPermFiles.Count()); + } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class FileList + { + private FileListFile[]? fileField; + + [XmlElement("File")] + public FileListFile[] File + { + get => fileField ?? Array.Empty(); + set => fileField = value; + } + + public static FileList? Deserialize(string pathToXml) + { + var serializer = new XmlSerializer(typeof(FileList)); + + using var fs = new FileStream(pathToXml, FileMode.Open, FileAccess.Read); + var reader = XmlReader.Create(fs); + FileList? fileList = (FileList?)serializer.Deserialize(reader); + return fileList; + } + } + + // From https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/NugetPackageDownloader/WorkloadUnixFilePermissionsFileList.cs + [Serializable] + [XmlType(AnonymousType = true)] + public class FileListFile + { + private string? pathField; + + private string? permissionField; + + [XmlAttribute] + public string? Path + { + get => pathField; + set => pathField = value; + } + + [XmlAttribute] + public string? Permission + { + get => permissionField; + set => permissionField = value; + } + } +} diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.props similarity index 100% rename from src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props rename to src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.props diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets new file mode 100644 index 00000000000..1ae8c2cd6ff --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets @@ -0,0 +1,77 @@ + + + <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ + Microsoft.NETCore.App + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_targetingPackReferenceExclusion Include="$(TargetName)" /> + <_targetingPackReferenceExclusion Include="@(_ResolvedProjectReferencePaths->'%(Filename)')" /> + <_targetingPackReferenceExclusion Include="@(DefaultReferenceExclusion)" /> + + + + <_targetingPackReferenceWithExclusion Include="@(Reference)"> + %(_targetingPackReferenceExclusion.Identity) + + + + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props new file mode 100644 index 00000000000..1a9c112e747 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props @@ -0,0 +1,14 @@ + + + <_WasmTargetsDir Condition="'$(RuntimeSrcDir)' != ''">$(RuntimeSrcDir)\src\mono\wasm\build\ + <_WasmTargetsDir Condition="'$(WasmBuildSupportDir)' != ''">$(WasmBuildSupportDir)\wasm\ + $(WasmBuildSupportDir)\emsdk\ + true + + + + + + PrepareForWasmBuild;$(WasmBuildAppDependsOn) + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets new file mode 100644 index 00000000000..8c0b1e7e46e --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh new file mode 100644 index 00000000000..d210c70ad06 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +EXECUTION_DIR=$(dirname $0) + +cd $EXECUTION_DIR + +if [ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]; then + XHARNESS_OUT="$EXECUTION_DIR/xharness-output" +else + XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output" +fi + +if [ ! -z "$XHARNESS_CLI_PATH" ]; then + # When running in CI, we only have the .NET runtime available + # We need to call the XHarness CLI DLL directly via dotnet exec + HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH" +else + HARNESS_RUNNER="dotnet xharness" +fi + +function set_env_vars() +{ + if [ "x$TEST_USING_WORKLOADS" = "xtrue" ]; then + export PATH=$BASE_DIR/dotnet-workload:$PATH + export SDK_FOR_WORKLOAD_TESTING_PATH=$BASE_DIR/dotnet-workload + export AppRefDir=$BASE_DIR/microsoft.netcore.app.ref + elif [ ! -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]; then + export WasmBuildSupportDir=$BASE_DIR/build + fi +} + +export TEST_LOG_PATH=${XHARNESS_OUT}/logs + +[[RunCommands]] + +_exitCode=$? + +echo "XHarness artifacts: $XHARNESS_OUT" + +exit $_exitCode diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config new file mode 100644 index 00000000000..434eac84083 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets deleted file mode 100644 index 058246e4086..00000000000 --- a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj deleted file mode 100644 index 91961dd7d6b..00000000000 --- a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net5.0 - - - - - - diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/Class1.cs similarity index 100% rename from src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs rename to src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/Class1.cs diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj new file mode 100644 index 00000000000..5198d9c2109 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj @@ -0,0 +1,5 @@ + + + net6.0 + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx new file mode 100644 index 00000000000..775397b15a2 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx new file mode 100644 index 00000000000..c843811244c --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ã•よã†ãªã‚‰ + + + + ã“ã‚“ã«ã¡ã¯ + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx new file mode 100644 index 00000000000..c3d5a787420 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx new file mode 100644 index 00000000000..775397b15a2 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx new file mode 100644 index 00000000000..c843811244c --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ã•よã†ãªã‚‰ + + + + ã“ã‚“ã«ã¡ã¯ + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx new file mode 100644 index 00000000000..c3d5a787420 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs deleted file mode 100644 index 743f481896f..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.CompilerServices; - -namespace Sample -{ - public class Test - { - public static void Main(string[] args) - { - Console.WriteLine ("Hello, World!"); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TestMeaning() - { - return 42; - } - } -} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj deleted file mode 100644 index 06ffa371ace..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - true - WasmTestOnBrowser - 42 - true - runtime.js - - - - - - Always - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html deleted file mode 100644 index 642987d23c5..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - TESTS - - - - - - Result from Sample.Test.TestMeaning: - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js deleted file mode 100644 index 11f8d64f60c..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -var Module = { - - config: null, - - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly - }, - - onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { - console.log("No config found"); - test_exit(1); - throw(Module.config.error); - } - - Module.config.loaded_cb = function () { - try { - App.init (); - } catch (error) { - test_exit(1); - throw (error); - } - }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); - } - - try - { - MONO.mono_load_runtime_and_bcl_args (Module.config); - } catch (error) { - test_exit(1); - throw(error); - } - }, -}; diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs deleted file mode 100644 index 743f481896f..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.CompilerServices; - -namespace Sample -{ - public class Test - { - public static void Main(string[] args) - { - Console.WriteLine ("Hello, World!"); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TestMeaning() - { - return 42; - } - } -} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj deleted file mode 100644 index db384e014d6..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - true - false - true - WasmTestOnBrowser - 42 - runtime.js - - - - - Always - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html deleted file mode 100644 index 9de05f5031b..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - TESTS - - - - - - Result from Sample.Test.TestMeaning: - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js deleted file mode 100644 index b5227472674..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -var Module = { - - config: null, - - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly - }, - - onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { - console.log("No config found"); - test_exit(1); - throw(Module.config.error); - } - - Module.config.loaded_cb = function () { - try { - App.init (); - } catch (error) { - test_exit(1); - throw (error); - } - }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); - } - - try - { - MONO.mono_load_runtime_and_bcl_args (Module.config); - } catch (error) { - test_exit(1); - throw(error); - } - }, -}; diff --git a/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs b/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs deleted file mode 100644 index 20df95f3a57..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading.Tasks; - -public class Test -{ - public static async Task Main(string[] args) - { - await Task.Delay(1); - Console.WriteLine("Hello Wasm AOT Functional Test!"); - return 42; - } -} \ No newline at end of file diff --git a/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj deleted file mode 100644 index 60f8759a10e..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - true - true - 42 - true - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs deleted file mode 100644 index 5d2a7280f14..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading.Tasks; - -public class Test -{ - public static async Task Main(string[] args) - { - await Task.Delay(1); - Console.WriteLine("Hello From Wasm!"); - return 42; - } -} \ No newline at end of file diff --git a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj deleted file mode 100644 index 407030c25ac..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - false - true - 42 - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs deleted file mode 100644 index 787fe939765..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading.Tasks; - -await Task.Delay(1); -Console.WriteLine("Hello From Wasm!"); -return 42; diff --git a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj deleted file mode 100644 index 407030c25ac..00000000000 --- a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - false - true - 42 - - - - - From 3d10d70a450e13d7cdbf1f843ff0b6bf768448cc Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 7 Jul 2021 16:34:17 -0700 Subject: [PATCH 320/926] Add crash report file for VS4Mac Watson (#54934) Add crash report file for VS4Mac Watson Add the --crashreport createdump command line option to enable this feature. Add crash thread (--crashthread) and signal number (--signal) command line options. Use std::vector for g_argCreateDump. Add the COMPlus_EnableCrashReport environment variable to enable this feature. Add simple json writer (JsonWriter). Remote unwinder: special case when encoding == 0 and IP after syscall opcode to popping return address Remove unwinder: add functionStart return from remote unwind API Add ModuleInfo struct containing the info about a native or managed module. Add StackFrame struct containing the info about a native or managed stack frame. Add signal number to PROCAbort. Currently the Linux crash report is stubbed out (empty). Better createdump logging. Add CrashReportWriter class. --- src/coreclr/debug/createdump/CMakeLists.txt | 4 + src/coreclr/debug/createdump/crashinfo.cpp | 184 +++++++-- src/coreclr/debug/createdump/crashinfo.h | 19 +- src/coreclr/debug/createdump/crashinfomac.cpp | 22 +- .../debug/createdump/crashinfounix.cpp | 4 +- .../debug/createdump/crashreportwriter.cpp | 357 ++++++++++++++++++ .../debug/createdump/crashreportwriter.h | 43 +++ src/coreclr/debug/createdump/createdump.h | 10 +- .../debug/createdump/createdumpunix.cpp | 18 +- .../debug/createdump/createdumpwindows.cpp | 2 +- src/coreclr/debug/createdump/datatarget.cpp | 21 +- src/coreclr/debug/createdump/dumpwriter.cpp | 4 +- src/coreclr/debug/createdump/dumpwriterelf.h | 3 +- .../debug/createdump/dumpwritermacho.cpp | 2 + .../debug/createdump/dumpwritermacho.h | 3 +- src/coreclr/debug/createdump/main.cpp | 29 +- src/coreclr/debug/createdump/memoryregion.h | 44 +-- src/coreclr/debug/createdump/moduleinfo.h | 61 +++ src/coreclr/debug/createdump/stackframe.h | 52 +++ src/coreclr/debug/createdump/threadinfo.cpp | 144 ++++++- src/coreclr/debug/createdump/threadinfo.h | 15 + .../debug/createdump/threadinfomac.cpp | 2 + .../debug/createdump/threadinfounix.cpp | 4 +- src/coreclr/debug/daccess/dacfn.cpp | 4 +- src/coreclr/pal/inc/pal.h | 2 +- .../pal/src/exception/remote-unwind.cpp | 82 +++- src/coreclr/pal/src/exception/signal.cpp | 11 +- src/coreclr/pal/src/include/pal/process.h | 10 +- src/coreclr/pal/src/thread/process.cpp | 128 +++++-- 29 files changed, 1122 insertions(+), 162 deletions(-) create mode 100644 src/coreclr/debug/createdump/crashreportwriter.cpp create mode 100644 src/coreclr/debug/createdump/crashreportwriter.h create mode 100644 src/coreclr/debug/createdump/moduleinfo.h create mode 100644 src/coreclr/debug/createdump/stackframe.h diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 55b7b51aaaa..5d766d53da1 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -49,6 +49,9 @@ else(CLR_CMAKE_HOST_WIN32) add_definitions(-DPAL_STDCPP_COMPAT) + # This is so we can include "version.c" + include_directories(${CMAKE_BINARY_DIR}) + set(CREATEDUMP_SOURCES main.cpp dumpname.cpp @@ -57,6 +60,7 @@ else(CLR_CMAKE_HOST_WIN32) threadinfo.cpp datatarget.cpp dumpwriter.cpp + crashreportwriter.cpp ) if(CLR_CMAKE_HOST_OSX) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 49f7cd2c172..bc444815415 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -6,10 +6,13 @@ // This is for the PAL_VirtualUnwindOutOfProc read memory adapter. CrashInfo* g_crashInfo; -CrashInfo::CrashInfo(pid_t pid) : +CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal) : m_ref(1), m_pid(pid), - m_ppid(-1) + m_ppid(-1), + m_gatherFrames(gatherFrames), + m_crashThread(crashThread), + m_signal(signal) { g_crashInfo = this; #ifdef __APPLE__ @@ -307,27 +310,16 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) { - ArrayHolder wszUnicodeName = new (std::nothrow) WCHAR[MAX_LONGPATH + 1]; - if (wszUnicodeName == nullptr) - { - fprintf(stderr, "Allocating unicode module name FAILED\n"); - result = false; - break; - } + ArrayHolder wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) { - ArrayHolder pszName = new (std::nothrow) char[MAX_LONGPATH + 1]; - if (pszName == nullptr) - { - fprintf(stderr, "Allocating ascii module name FAILED\n"); - result = false; - break; - } - sprintf_s(pszName.GetPtr(), MAX_LONGPATH, "%S", (WCHAR*)wszUnicodeName); - TRACE(" %s\n", pszName.GetPtr()); + std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr()); // Change the module mapping name - ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, std::string(pszName.GetPtr())); + ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName); + + // Add managed module info + AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName); } else { TRACE("\nModule.GetFileName FAILED %08x\n", hr); @@ -369,7 +361,7 @@ CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) // Replace an existing module mapping with one with a different name. // void -CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName) +CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& name) { uint64_t start = (uint64_t)baseAddress; uint64_t end = ((baseAddress + size) + (PAGE_SIZE - 1)) & PAGE_MASK; @@ -387,7 +379,7 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const if (found == m_moduleMappings.end()) { // On MacOS the assemblies are always added. - MemoryRegion newRegion(flags, start, end, 0, pszName); + MemoryRegion newRegion(flags, start, end, 0, name); m_moduleMappings.insert(newRegion); if (g_diagnostics) { @@ -395,10 +387,10 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const newRegion.Trace(); } } - else if (found->FileName().compare(pszName) != 0) + else if (found->FileName().compare(name) != 0) { // Create the new memory region with the managed assembly name. - MemoryRegion newRegion(*found, pszName); + MemoryRegion newRegion(*found, name); // Remove and cleanup the old one m_moduleMappings.erase(found); @@ -416,9 +408,10 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const // // Returns the module base address for the IP or 0. Used by the thread unwind code. // -uint64_t CrashInfo::GetBaseAddress(uint64_t ip) +uint64_t +CrashInfo::GetBaseAddressFromAddress(uint64_t address) { - MemoryRegion search(0, ip, ip, 0); + MemoryRegion search(0, address, address, 0); const MemoryRegion* found = SearchMemoryRegions(m_moduleAddresses, search); if (found == nullptr) { return 0; @@ -427,6 +420,105 @@ uint64_t CrashInfo::GetBaseAddress(uint64_t ip) return found->Offset(); } +// +// Returns the module base address for the given module name or 0 if not found. +// +uint64_t +CrashInfo::GetBaseAddressFromName(const char* moduleName) +{ + for (const ModuleInfo& moduleInfo : m_moduleInfos) + { + std::string name = GetFileName(moduleInfo.ModuleName()); +#ifdef __APPLE__ + // Module names are case insenstive on MacOS + if (strcasecmp(name.c_str(), moduleName) == 0) +#else + if (name.compare(moduleName) == 0) +#endif + { + return moduleInfo.BaseAddress(); + } + } + return 0; +} + +// +// Return the module info for the base address +// +const ModuleInfo* +CrashInfo::GetModuleInfoFromBaseAddress(uint64_t baseAddress) +{ + ModuleInfo search(baseAddress); + const auto& found = m_moduleInfos.find(search); + if (found != m_moduleInfos.end()) + { + return &*found; + } + return nullptr; +} + +// +// Adds module address range for IP lookup +// +void +CrashInfo::AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress) +{ + // Add module segment to base address lookup + MemoryRegion region(0, startAddress, endAddress, baseAddress); + m_moduleAddresses.insert(region); +} + +// +// Adds module info (baseAddress, module name, etc) +// +void +CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName) +{ + ModuleInfo moduleInfo(baseAddress); + const auto& found = m_moduleInfos.find(moduleInfo); + if (found == m_moduleInfos.end()) + { + uint32_t timeStamp = 0; + uint32_t imageSize = 0; + GUID mvid; + if (isManaged) + { + IMAGE_DOS_HEADER dosHeader; + if (ReadMemory((void*)baseAddress, &dosHeader, sizeof(dosHeader))) + { + WORD magic; + if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS, OptionalHeader.Magic)), &magic, sizeof(magic))) + { + if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + IMAGE_NT_HEADERS32 header; + if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew), &header, sizeof(header))) + { + imageSize = header.OptionalHeader.SizeOfImage; + timeStamp = header.FileHeader.TimeDateStamp; + } + } + else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + IMAGE_NT_HEADERS64 header; + if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew), &header, sizeof(header))) + { + imageSize = header.OptionalHeader.SizeOfImage; + timeStamp = header.FileHeader.TimeDateStamp; + } + } + } + } + if (pClrDataModule != nullptr) + { + pClrDataModule->GetVersionId(&mvid); + } + TRACE("MODULE: timestamp %08x size %08x %s %s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), moduleName.c_str()); + } + m_moduleInfos.insert(ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName)); + } +} + // // ReadMemory from target and add to memory regions list // @@ -644,3 +736,45 @@ CrashInfo::TraceVerbose(const char* format, ...) va_end(args); } } + +// +// Returns just the file name portion of a file path +// +const std::string +GetFileName(const std::string& fileName) +{ + size_t last = fileName.rfind(DIRECTORY_SEPARATOR_STR_A); + if (last != std::string::npos) { + last++; + } + else { + last = 0; + } + return fileName.substr(last); +} + +// +// Formats a std::string with printf syntax. The final formated string is limited +// to MAX_LONGPATH (1024) chars. Returns an empty string on any error. +// +std::string +FormatString(const char* format, ...) +{ + ArrayHolder buffer = new char[MAX_LONGPATH + 1]; + va_list args; + va_start(args, format); + int result = vsprintf_s(buffer, MAX_LONGPATH, format, args); + va_end(args); + return result > 0 ? std::string(buffer) : std::string(); +} + +// +// Format a guid +// +std::string +FormatGuid(const GUID* guid) +{ + uint8_t* bytes = (uint8_t*)guid; + return FormatString("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); +} diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index a4ca0e55ddb..f315b98dd28 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -30,6 +30,10 @@ typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; #endif +extern const std::string GetFileName(const std::string& fileName); +extern std::string FormatString(const char* format, ...); +extern std::string FormatGuid(const GUID* guid); + class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, #ifdef __APPLE__ public MachOReader @@ -42,6 +46,9 @@ private: pid_t m_pid; // pid pid_t m_ppid; // parent pid pid_t m_tgid; // process group + bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info + pid_t m_crashThread; // crashing thread id or 0 if none + uint32_t m_signal; // crash signal code or 0 if none std::string m_name; // exe name #ifdef __APPLE__ vm_map_t m_task; // the mach task for the process @@ -61,9 +68,10 @@ private: std::set m_otherMappings; // other memory mappings std::set m_memoryRegions; // memory regions from DAC, etc. std::set m_moduleAddresses; // memory region to module base address + std::set m_moduleInfos; // module infos (base address and module name) public: - CrashInfo(pid_t pid); + CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal); virtual ~CrashInfo(); bool Initialize(); @@ -72,7 +80,11 @@ public: bool GatherCrashInfo(MINIDUMP_TYPE minidumpType); bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory - uint64_t GetBaseAddress(uint64_t ip); + uint64_t GetBaseAddressFromAddress(uint64_t address); + uint64_t GetBaseAddressFromName(const char* moduleName); + const ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress); + void AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress); + void AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName); void InsertMemoryRegion(uint64_t address, size_t size); static const MemoryRegion* SearchMemoryRegions(const std::set& regions, const MemoryRegion& search); @@ -82,6 +94,9 @@ public: #ifdef __APPLE__ inline vm_map_t Task() const { return m_task; } #endif + inline const bool GatherFrames() const { return m_gatherFrames; } + inline const pid_t CrashThread() const { return m_crashThread; } + inline const uint32_t Signal() const { return m_signal; } inline const std::string& Name() const { return m_name; } inline const std::vector Threads() const { return m_threads; } diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 2dc418215bd..4461d2a8c96 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -171,7 +171,7 @@ CrashInfo::EnumerateMemoryRegions() { if (region.Contains(*found)) { - MemoryRegion gap(region.Flags(), previousEndAddress, found->StartAddress(), region.Offset(), std::string()); + MemoryRegion gap(region.Flags(), previousEndAddress, found->StartAddress(), region.Offset()); if (gap.Size() > 0) { TRACE(" Gap: "); @@ -182,7 +182,7 @@ CrashInfo::EnumerateMemoryRegions() } } - MemoryRegion endgap(region.Flags(), previousEndAddress, region.EndAddress(), region.Offset(), std::string()); + MemoryRegion endgap(region.Flags(), previousEndAddress, region.EndAddress(), region.Offset()); if (endgap.Size() > 0) { TRACE(" EndGap:"); @@ -237,17 +237,12 @@ CrashInfo::TryFindDyLinker(mach_vm_address_t address, mach_vm_size_t size, bool* void CrashInfo::VisitModule(MachOModule& module) { + AddModuleInfo(false, module.BaseAddress(), nullptr, module.Name()); + // Get the process name from the executable module file type if (m_name.empty() && module.Header().filetype == MH_EXECUTE) { - size_t last = module.Name().rfind(DIRECTORY_SEPARATOR_STR_A); - if (last != std::string::npos) { - last++; - } - else { - last = 0; - } - m_name = module.Name().substr(last); + m_name = GetFileName(module.Name()); } // Save the runtime module path if (m_coreclrPath.empty()) @@ -291,7 +286,7 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm _ASSERTE(end > 0); // Add module memory region if not already on the list - MemoryRegion moduleRegion(regionFlags, start, end, offset, module.Name()); + MemoryRegion moduleRegion(regionFlags, start, end, offset); const auto& found = m_moduleMappings.find(moduleRegion); if (found == m_moduleMappings.end()) { @@ -303,9 +298,8 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm // Add this module segment to the module mappings list m_moduleMappings.insert(moduleRegion); - // Add module segment ip to base address lookup - MemoryRegion addressRegion(0, start, end, module.BaseAddress()); - m_moduleAddresses.insert(addressRegion); + // Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip. + AddModuleAddressRange(start, end, module.BaseAddress()); } else { diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 2ce100a61c0..2cc1eb6c688 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -263,6 +263,7 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName) if (baseAddress == 0 || baseAddress == m_auxvValues[AT_SYSINFO_EHDR]) { return; } + AddModuleInfo(false, baseAddress, nullptr, moduleName); if (m_coreclrPath.empty()) { size_t last = moduleName.rfind(DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("coreclr")); @@ -302,8 +303,7 @@ CrashInfo::VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, Phdr* phd break; case PT_LOAD: - MemoryRegion region(0, loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress); - m_moduleAddresses.insert(region); + AddModuleAddressRange(loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress); break; } } diff --git a/src/coreclr/debug/createdump/crashreportwriter.cpp b/src/coreclr/debug/createdump/crashreportwriter.cpp new file mode 100644 index 00000000000..9cac899968f --- /dev/null +++ b/src/coreclr/debug/createdump/crashreportwriter.cpp @@ -0,0 +1,357 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "createdump.h" + +// Include the .NET Core version string instead of link because it is "static". +#include "version.c" + +CrashReportWriter::CrashReportWriter(CrashInfo& crashInfo) : + m_crashInfo(crashInfo) +{ + m_fd = -1; + m_indent = JSON_INDENT_VALUE; + m_comma = false; + m_crashInfo.AddRef(); +} + +CrashReportWriter::~CrashReportWriter() +{ + m_crashInfo.Release(); + if (m_fd != -1) + { + close(m_fd); + m_fd = -1; + } +} + +// +// Write the crash report info to the json file +// +void +CrashReportWriter::WriteCrashReport(const std::string& dumpFileName) +{ + std::string crashReportFile(dumpFileName); + crashReportFile.append(".crashreport.json"); + printf("Writing crash report to file %s\n", crashReportFile.c_str()); + try + { + if (!OpenWriter(crashReportFile.c_str())) { + return; + } + WriteCrashReport(); + CloseWriter(); + } + catch (const std::exception& e) + { + fprintf(stderr, "Writing the crash report file FAILED\n"); + + // Delete the partial json file on error + remove(crashReportFile.c_str()); + } +} + +#ifdef __APPLE__ + +void +CrashReportWriter::WriteCrashReport() +{ + const char* exceptionType = nullptr; + OpenObject("payload"); + WriteValue("protocol_version", "0.0.7"); + + OpenObject("configuration"); +#if defined(__x86_64__) + WriteValue("architecture", "amd64"); +#elif defined(__aarch64__) + WriteValue("architecture", "arm64"); +#endif + std::string version; + assert(strncmp(sccsid, "@(#)Version ", 12) == 0); + version.append(sccsid + 12); // skip "@(#)Version " + version.append(" "); // the analyzer requires a space after the version + WriteValue("version", version.c_str()); + CloseObject(); // configuration + + OpenArray("threads"); + for (const ThreadInfo* thread : m_crashInfo.Threads()) + { + OpenObject(); + bool crashed = false; + if (thread->ManagedExceptionObject() != 0) + { + crashed = true; + exceptionType = "0x05000000"; // ManagedException + } + else + { + if (thread->Tid() == m_crashInfo.CrashThread()) + { + crashed = true; + switch (m_crashInfo.Signal()) + { + case SIGILL: + exceptionType = "0x50000000"; + break; + + case SIGFPE: + exceptionType = "0x70000000"; + break; + + case SIGBUS: + exceptionType = "0x60000000"; + break; + + case SIGTRAP: + exceptionType = "0x03000000"; + break; + + case SIGSEGV: + exceptionType = "0x20000000"; + break; + + case SIGTERM: + exceptionType = "0x02000000"; + break; + + case SIGABRT: + default: + exceptionType = "0x30000000"; + break; + } + } + } + WriteValueBool("is_managed", thread->IsManaged()); + WriteValueBool("crashed", crashed); + if (thread->ManagedExceptionObject() != 0) + { + WriteValue64("managed_exception_object", thread->ManagedExceptionObject()); + } + if (!thread->ManagedExceptionType().empty()) + { + WriteValue("managed_exception_type", thread->ManagedExceptionType().c_str()); + } + WriteValue64("native_thread_id", thread->Tid()); + OpenObject("ctx"); + WriteValue64("IP", thread->GetInstructionPointer()); + WriteValue64("SP", thread->GetStackPointer()); + WriteValue64("BP", thread->GetFramePointer()); + CloseObject(); // ctx + + OpenArray("unmanaged_frames"); + for (const StackFrame& frame : thread->StackFrames()) + { + WriteStackFrame(frame); + } + CloseArray(); // unmanaged_frames + CloseObject(); + } + CloseArray(); // threads + CloseObject(); // payload + + OpenObject("parameters"); + if (exceptionType != nullptr) + { + WriteValue("ExceptionType", exceptionType); + } + WriteSysctl("kern.osproductversion", "OSVersion"); + WriteSysctl("hw.model", "SystemModel"); + WriteValue("SystemManufacturer", "apple"); + CloseObject(); // parameters +} + +void +CrashReportWriter::WriteStackFrame(const StackFrame& frame) +{ + OpenObject(); + WriteValueBool("is_managed", frame.IsManaged()); + WriteValue64("module_address", frame.ModuleAddress()); + WriteValue64("stack_pointer", frame.StackPointer()); + WriteValue64("native_address", frame.ReturnAddress()); + WriteValue64("native_offset", frame.NativeOffset()); + if (frame.IsManaged()) + { + WriteValue32("token", frame.Token()); + WriteValue32("il_offset", frame.ILOffset()); + } + if (frame.ModuleAddress() != 0) + { + const ModuleInfo* moduleInfo = m_crashInfo.GetModuleInfoFromBaseAddress(frame.ModuleAddress()); + if (moduleInfo != nullptr) + { + std::string moduleName = GetFileName(moduleInfo->ModuleName()); + if (frame.IsManaged()) + { + WriteValue32("timestamp", moduleInfo->TimeStamp()); + WriteValue32("sizeofimage", moduleInfo->ImageSize()); + WriteValue("filename", moduleName.c_str()); + WriteValue("guid", FormatGuid(moduleInfo->Mvid()).c_str()); + } + else + { + WriteValue("native_module", moduleName.c_str()); + } + } + } + CloseObject(); +} + +void +CrashReportWriter::WriteSysctl(const char* sysctlname, const char* valueName) +{ + size_t size = 0; + if (sysctlbyname(sysctlname, nullptr, &size, NULL, 0) >= 0) + { + ArrayHolder buffer = new char[size]; + if (sysctlbyname(sysctlname, buffer, &size, NULL, 0) >= 0) + { + WriteValue(valueName, buffer); + } + else + { + TRACE("sysctlbyname(%s) 1 FAILED %s\n", sysctlname, strerror(errno)); + } + } + else + { + TRACE("sysctlbyname(%s) 2 FAILED %s\n", sysctlname, strerror(errno)); + } +} + +#else // __APPLE__ + +void +CrashReportWriter::WriteCrashReport() +{ +} + +#endif // __APPLE__ + +bool CrashReportWriter::OpenWriter(const char* fileName) +{ + m_fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0664); + if (m_fd == -1) + { + fprintf(stderr, "Could not create json file %s: %d %s\n", fileName, errno, strerror(errno)); + return false; + } + Write("{\n"); + return true; +} + +void CrashReportWriter::CloseWriter() +{ + assert(m_indent == JSON_INDENT_VALUE); + Write("\n}\n"); +} + +void CrashReportWriter::Write(const std::string& text) +{ + if (!DumpWriter::WriteData(m_fd, (void*)text.c_str(), text.length())) + { + throw std::exception(); + } +} + +void CrashReportWriter::Write(const char* buffer) +{ + std::string text(buffer); + Write(text); +} + +void CrashReportWriter::Indent(std::string& text) +{ + assert(m_indent >= 0); + text.append(m_indent, ' '); +} + +void CrashReportWriter::WriteSeperator(std::string& text) +{ + if (m_comma) + { + text.append(1, ','); + text.append(1, '\n'); + } + Indent(text); +} + +void CrashReportWriter::OpenValue(const char* key, char marker) +{ + std::string text; + WriteSeperator(text); + if (key != nullptr) + { + text.append("\""); + text.append(key); + text.append("\" : "); + } + text.append(1, marker); + text.append(1, '\n'); + m_comma = false; + m_indent += JSON_INDENT_VALUE; + Write(text); +} + +void CrashReportWriter::CloseValue(char marker) +{ + std::string text; + text.append(1, '\n'); + assert(m_indent >= JSON_INDENT_VALUE); + m_indent -= JSON_INDENT_VALUE; + Indent(text); + text.append(1, marker); + m_comma = true; + Write(text); +} + +void CrashReportWriter::WriteValue(const char* key, const char* value) +{ + std::string text; + WriteSeperator(text); + text.append("\""); + text.append(key); + text.append("\" : \""); + text.append(value); + text.append("\""); + m_comma = true; + Write(text); +} + +void CrashReportWriter::WriteValueBool(const char* key, bool value) +{ + WriteValue(key, value ? "true" : "false"); +} + +void CrashReportWriter::WriteValue32(const char* key, uint32_t value) +{ + char buffer[16]; + snprintf(buffer, sizeof(buffer), "0x%x", value); + WriteValue(key, buffer); +} + +void CrashReportWriter::WriteValue64(const char* key, uint64_t value) +{ + char buffer[32]; + snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value); + WriteValue(key, buffer); +} + +void CrashReportWriter::OpenObject(const char* key) +{ + OpenValue(key, '{'); +} + +void CrashReportWriter::CloseObject() +{ + CloseValue('}'); +} + +void CrashReportWriter::OpenArray(const char* key) +{ + OpenValue(key, '['); +} + +void CrashReportWriter::CloseArray() +{ + CloseValue(']'); +} diff --git a/src/coreclr/debug/createdump/crashreportwriter.h b/src/coreclr/debug/createdump/crashreportwriter.h new file mode 100644 index 00000000000..ef77bfcac55 --- /dev/null +++ b/src/coreclr/debug/createdump/crashreportwriter.h @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#define JSON_INDENT_VALUE 1 + +class CrashReportWriter +{ +private: + int m_fd; + int m_indent; + bool m_comma; + CrashInfo& m_crashInfo; + +public: + CrashReportWriter(CrashInfo& crashInfo); + virtual ~CrashReportWriter(); + void WriteCrashReport(const std::string& dumpFileName); + +private: + void WriteCrashReport(); +#ifdef __APPLE__ + void WriteStackFrame(const StackFrame& frame); + void WriteSysctl(const char* sysctlname, const char* valueName); +#endif + void Write(const std::string& text); + void Write(const char* buffer); + void Indent(std::string& text); + void WriteSeperator(std::string& text); + void OpenValue(const char* key, char marker); + void CloseValue(char marker); + bool OpenWriter(const char* fileName); + void CloseWriter(); + void WriteValue(const char* key, const char* value); + void WriteValueBool(const char* key, bool value); + void WriteValue32(const char* key, uint32_t value); + void WriteValue64(const char* key, uint64_t value); + void OpenObject(const char* key = nullptr); + void CloseObject(); + void OpenArray(const char* key); + void CloseArray(); +}; diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index b63b147ea43..95f63f460e4 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -23,6 +23,11 @@ extern bool g_diagnosticsVerbose; #define TRACE_VERBOSE(args, ...) #endif +#ifdef HOST_64BIT +#define PRIA "016" +#else +#define PRIA "08" +#endif #ifdef HOST_UNIX #include "config.h" @@ -87,10 +92,13 @@ typedef int T_CONTEXT; #include #include #endif +#include "moduleinfo.h" #include "datatarget.h" +#include "stackframe.h" #include "threadinfo.h" #include "memoryregion.h" #include "crashinfo.h" +#include "crashreportwriter.h" #include "dumpwriter.h" #endif @@ -99,5 +107,5 @@ typedef int T_CONTEXT; #endif bool FormatDumpName(std::string& name, const char* pattern, const char* exename, int pid); -bool CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType); +bool CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType, bool crashReport, int crashThread, int signal); diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index da74ddaee5c..b789c98b820 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -7,9 +7,9 @@ // The Linux/MacOS create dump code // bool -CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType) +CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType, bool crashReport, int crashThread, int signal) { - ReleaseHolder crashInfo = new CrashInfo(pid); + ReleaseHolder crashInfo = new CrashInfo(pid, crashReport, crashThread, signal); DumpWriter dumpWriter(*crashInfo); std::string dumpPath; bool result = false; @@ -21,6 +21,11 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP } printf("Gathering state for process %d %s\n", pid, crashInfo->Name().c_str()); + if (signal != 0 || crashThread != 0) + { + printf("Crashing thread %08x signal %08x\n", crashThread, signal); + } + // Suspend all the threads in the target process and build the list of threads if (!crashInfo->EnumerateAndSuspendThreads()) { @@ -36,6 +41,12 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP { goto exit; } + // Write the crash report json file if enabled + if (crashReport) + { + CrashReportWriter crashReportWriter(*crashInfo); + crashReportWriter.WriteCrashReport(dumpPath); + } printf("Writing %s to file %s\n", dumpType, dumpPath.c_str()); // Write the actual dump file @@ -46,6 +57,9 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP if (!dumpWriter.WriteDump()) { fprintf(stderr, "Writing dump FAILED\n"); + + // Delete the partial dump file on error + remove(dumpPath.c_str()); goto exit; } result = true; diff --git a/src/coreclr/debug/createdump/createdumpwindows.cpp b/src/coreclr/debug/createdump/createdumpwindows.cpp index 1eed949603e..6d474d3475d 100644 --- a/src/coreclr/debug/createdump/createdumpwindows.cpp +++ b/src/coreclr/debug/createdump/createdumpwindows.cpp @@ -8,7 +8,7 @@ // The Windows create dump code // bool -CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType) +CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType, bool crashReport, int crashThread, int signal) { HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hProcess = NULL; diff --git a/src/coreclr/debug/createdump/datatarget.cpp b/src/coreclr/debug/createdump/datatarget.cpp index e5361ec5d42..7d2ab1973ff 100644 --- a/src/coreclr/debug/createdump/datatarget.cpp +++ b/src/coreclr/debug/createdump/datatarget.cpp @@ -96,25 +96,10 @@ DumpDataTarget::GetImageBase( int length = WideCharToMultiByte(CP_ACP, 0, moduleName, -1, tempModuleName, sizeof(tempModuleName), NULL, NULL); if (length > 0) { - for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) - { - const char *name = strrchr(image.FileName().c_str(), '/'); - if (name != nullptr) - { - name++; - } - else - { - name = image.FileName().c_str(); - } - if (strcmp(name, tempModuleName) == 0) - { - *baseAddress = image.StartAddress(); - return S_OK; - } - } + *baseAddress = m_crashInfo.GetBaseAddressFromName(tempModuleName); } - return E_FAIL; + + return *baseAddress != 0 ? S_OK : E_FAIL; } HRESULT STDMETHODCALLTYPE diff --git a/src/coreclr/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index d7816cd4484..f4532a58ca9 100644 --- a/src/coreclr/debug/createdump/dumpwriter.cpp +++ b/src/coreclr/debug/createdump/dumpwriter.cpp @@ -34,7 +34,7 @@ DumpWriter::OpenDump(const char* dumpFileName) // Write all of the given buffer, handling short writes and EINTR. Return true iff successful. bool -DumpWriter::WriteData(const void* buffer, size_t length) +DumpWriter::WriteData(int fd, const void* buffer, size_t length) { const uint8_t* data = (const uint8_t*)buffer; @@ -42,7 +42,7 @@ DumpWriter::WriteData(const void* buffer, size_t length) while (done < length) { ssize_t written; do { - written = write(m_fd, data + done, length - done); + written = write(fd, data + done, length - done); } while (written == -1 && errno == EINTR); if (written < 1) { diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h index 4bd885cefa8..ac4dc10fd2f 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.h +++ b/src/coreclr/debug/createdump/dumpwriterelf.h @@ -41,6 +41,7 @@ public: virtual ~DumpWriter(); bool OpenDump(const char* dumpFileName); bool WriteDump(); + static bool WriteData(int fd, const void* buffer, size_t length); private: bool WriteProcessInfo(); @@ -48,7 +49,7 @@ private: size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr); bool WriteNTFileInfo(); bool WriteThread(const ThreadInfo& thread, int fatal_signal); - bool WriteData(const void* buffer, size_t length); + bool WriteData(const void* buffer, size_t length) { return WriteData(m_fd, buffer, length); } size_t GetProcessInfoSize() const { return sizeof(Nhdr) + 8 + sizeof(prpsinfo_t); } size_t GetAuxvInfoSize() const { return sizeof(Nhdr) + 8 + m_crashInfo.GetAuxvSize(); } diff --git a/src/coreclr/debug/createdump/dumpwritermacho.cpp b/src/coreclr/debug/createdump/dumpwritermacho.cpp index e1098c20d50..e5cefe9ffbe 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.cpp +++ b/src/coreclr/debug/createdump/dumpwritermacho.cpp @@ -4,7 +4,9 @@ #include "createdump.h" #include "specialthreadinfo.h" +// // Write the core dump file +// bool DumpWriter::WriteDump() { diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h index 7a934339ec5..6be2aa7742b 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.h +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -35,11 +35,12 @@ public: virtual ~DumpWriter(); bool OpenDump(const char* dumpFileName); bool WriteDump(); + static bool WriteData(int fd, const void* buffer, size_t length); private: void BuildSegmentLoadCommands(); void BuildThreadLoadCommands(); bool WriteHeader(uint64_t* pFileOffset); bool WriteSegments(); - bool WriteData(const void* buffer, size_t length); + bool WriteData(const void* buffer, size_t length) { return WriteData(m_fd, buffer, length); } }; diff --git a/src/coreclr/debug/createdump/main.cpp b/src/coreclr/debug/createdump/main.cpp index b70a4f4e65d..4ed6366d467 100644 --- a/src/coreclr/debug/createdump/main.cpp +++ b/src/coreclr/debug/createdump/main.cpp @@ -22,7 +22,13 @@ const char* g_help = "createdump [options] pid\n" "-t, --triage - create triage minidump.\n" "-u, --full - create full core dump.\n" "-d, --diag - enable diagnostic messages.\n" -"-vd, --verbose - enable verbose diagnostic messages.\n"; +"-v, --verbose - enable verbose diagnostic messages.\n" +#ifdef HOST_UNIX +"--crashreport - write crash report file.\n" +"--crashthread - the thread id of the crashing thread.\n" +"--signal - the signal code of the crash.\n" +#endif +; bool g_diagnostics = false; bool g_diagnosticsVerbose = false; @@ -41,6 +47,9 @@ int __cdecl main(const int argc, const char* argv[]) MiniDumpWithTokenInformation); const char* dumpType = "minidump with heap"; const char* dumpPathTemplate = nullptr; + bool crashReport = false; + int signal = 0; + int crashThread = 0; int exitCode = 0; int pid = 0; @@ -102,11 +111,25 @@ int __cdecl main(const int argc, const char* argv[]) MiniDumpWithThreadInfo | MiniDumpWithTokenInformation); } +#ifdef HOST_UNIX + else if (strcmp(*argv, "--crashreport") == 0) + { + crashReport = true; + } + else if (strcmp(*argv, "--crashthread") == 0) + { + crashThread = atoi(*++argv); + } + else if (strcmp(*argv, "--signal") == 0) + { + signal = atoi(*++argv); + } +#endif else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0)) { g_diagnostics = true; } - else if ((strcmp(*argv, "-vd") == 0) || (strcmp(*argv, "--verbose") == 0)) + else if ((strcmp(*argv, "-v") == 0) || (strcmp(*argv, "--verbose") == 0)) { g_diagnostics = true; g_diagnosticsVerbose = true; @@ -138,7 +161,7 @@ int __cdecl main(const int argc, const char* argv[]) dumpPathTemplate = tmpPath; } - if (CreateDump(dumpPathTemplate, pid, dumpType, minidumpType)) + if (CreateDump(dumpPathTemplate, pid, dumpType, minidumpType, crashReport, crashThread, signal)) { printf("Dump successfully written\n"); } diff --git a/src/coreclr/debug/createdump/memoryregion.h b/src/coreclr/debug/createdump/memoryregion.h index 9176d3576ea..b5bb44f67a2 100644 --- a/src/coreclr/debug/createdump/memoryregion.h +++ b/src/coreclr/debug/createdump/memoryregion.h @@ -8,12 +8,6 @@ #undef PAGE_MASK #define PAGE_MASK (~(PAGE_SIZE-1)) -#ifdef HOST_64BIT -#define PRIA "016" -#else -#define PRIA "08" -#endif - enum MEMORY_REGION_FLAGS : uint32_t { // PF_X = 0x01, // Execute @@ -37,16 +31,6 @@ private: std::string m_fileName; public: - MemoryRegion(uint32_t flags, uint64_t start, uint64_t end) : - m_flags(flags), - m_startAddress(start), - m_endAddress(end), - m_offset(0) - { - assert((start & ~PAGE_MASK) == 0); - assert((end & ~PAGE_MASK) == 0); - } - MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t offset, const std::string& filename) : m_flags(flags), m_startAddress(start), @@ -58,17 +42,23 @@ public: assert((end & ~PAGE_MASK) == 0); } - // This is a special constructor for the module base address - // set where the start/end are not page aligned and "offset" - // is reused as the module base address. - MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t baseAddress) : + MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t offset) : m_flags(flags), m_startAddress(start), m_endAddress(end), - m_offset(baseAddress) + m_offset(offset) { } + MemoryRegion(uint32_t flags, uint64_t start, uint64_t end) : + m_flags(flags), + m_startAddress(start), + m_endAddress(end) + { + assert((start & ~PAGE_MASK) == 0); + assert((end & ~PAGE_MASK) == 0); + } + // copy with new file name constructor MemoryRegion(const MemoryRegion& region, const std::string& fileName) : m_flags(region.m_flags), @@ -103,13 +93,13 @@ public: } uint32_t Permissions() const { return m_flags & MEMORY_REGION_FLAG_PERMISSIONS_MASK; } - uint32_t Flags() const { return m_flags; } + inline uint32_t Flags() const { return m_flags; } bool IsBackedByMemory() const { return (m_flags & MEMORY_REGION_FLAG_MEMORY_BACKED) != 0; } - uint64_t StartAddress() const { return m_startAddress; } - uint64_t EndAddress() const { return m_endAddress; } - uint64_t Size() const { return m_endAddress - m_startAddress; } - uint64_t Offset() const { return m_offset; } - const std::string& FileName() const { return m_fileName; } + inline uint64_t StartAddress() const { return m_startAddress; } + inline uint64_t EndAddress() const { return m_endAddress; } + inline uint64_t Size() const { return m_endAddress - m_startAddress; } + inline uint64_t Offset() const { return m_offset; } + inline const std::string& FileName() const { return m_fileName; } bool operator<(const MemoryRegion& rhs) const { diff --git a/src/coreclr/debug/createdump/moduleinfo.h b/src/coreclr/debug/createdump/moduleinfo.h new file mode 100644 index 00000000000..f07e50d592f --- /dev/null +++ b/src/coreclr/debug/createdump/moduleinfo.h @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +struct ModuleInfo +{ +private: + uint64_t m_baseAddress; + uint32_t m_timeStamp; + uint32_t m_imageSize; + GUID m_mvid; + std::string m_moduleName; + bool m_isManaged; + +public: + ModuleInfo(uint64_t baseAddress) : + m_baseAddress(baseAddress) + { + } + + ModuleInfo(bool isManaged, uint64_t baseAddress, uint32_t timeStamp, uint32_t imageSize, GUID* mvid, const std::string& moduleName) : + m_baseAddress(baseAddress), + m_timeStamp(timeStamp), + m_imageSize(imageSize), + m_mvid(*mvid), + m_moduleName(moduleName), + m_isManaged(isManaged) + { + } + + // copy constructor + ModuleInfo(const ModuleInfo& moduleInfo) : + m_baseAddress(moduleInfo.m_baseAddress), + m_timeStamp(moduleInfo.m_timeStamp), + m_imageSize(moduleInfo.m_imageSize), + m_mvid(moduleInfo.m_mvid), + m_moduleName(moduleInfo.m_moduleName), + m_isManaged(moduleInfo.m_isManaged) + { + } + + ~ModuleInfo() + { + } + + inline bool IsManaged() const { return m_isManaged; } + inline uint64_t BaseAddress() const { return m_baseAddress; } + inline uint32_t TimeStamp() const { return m_timeStamp; } + inline uint32_t ImageSize() const { return m_imageSize; } + inline const GUID* Mvid() const { return &m_mvid; } + inline const std::string& ModuleName() const { return m_moduleName; } + + bool operator<(const ModuleInfo& rhs) const + { + return m_baseAddress < rhs.m_baseAddress; + } + + void Trace() const + { + TRACE("%" PRIA PRIx64 " %s\n", m_baseAddress, m_moduleName.c_str()); + } +}; diff --git a/src/coreclr/debug/createdump/stackframe.h b/src/coreclr/debug/createdump/stackframe.h new file mode 100644 index 00000000000..90db2697b81 --- /dev/null +++ b/src/coreclr/debug/createdump/stackframe.h @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +struct StackFrame +{ +private: + uint64_t m_moduleAddress; + uint64_t m_returnAddress; + uint64_t m_stackPointer; + uint32_t m_nativeOffset; + uint32_t m_token; + uint32_t m_ilOffset; + bool m_isManaged; + +public: + // Create native stack frame + StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset) : + m_moduleAddress(moduleAddress), + m_returnAddress(returnAddress), + m_stackPointer(stackPointer), + m_nativeOffset(nativeOffset), + m_token(0), + m_ilOffset(0), + m_isManaged(false) + { + } + + // Create managed stack frame + StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset, uint64_t token, uint32_t ilOffset) : + m_moduleAddress(moduleAddress), + m_returnAddress(returnAddress), + m_stackPointer(stackPointer), + m_nativeOffset(nativeOffset), + m_token(token), + m_ilOffset(ilOffset), + m_isManaged(true) + { + } + + inline uint64_t ModuleAddress() const { return m_moduleAddress; } + inline uint64_t ReturnAddress() const { return m_returnAddress; } + inline uint64_t StackPointer() const { return m_stackPointer; } + inline uint32_t NativeOffset() const { return m_nativeOffset; } + inline uint32_t Token() const { return m_token; } + inline uint32_t ILOffset() const { return m_ilOffset; } + inline bool IsManaged() const { return m_isManaged; } + + bool operator<(const StackFrame& rhs) const + { + return m_stackPointer < rhs.m_stackPointer; + } +}; diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 330af44c1f8..99284ed0402 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -53,8 +53,8 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) uint64_t ip = 0, sp = 0; GetFrameLocation(pContext, &ip, &sp); - TRACE("Unwind: sp %" PRIA PRIx64 " ip %" PRIA PRIx64 "\n", sp, ip); if (ip == 0 || sp <= previousSp) { + TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip); break; } // Break out of the endless loop if the IP matches over a 1000 times. This is a fallback @@ -67,7 +67,7 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) { if (ipMatchCount++ > 1000) { - TRACE("Unwind: same ip %" PRIA PRIx64 " over 1000 times\n", ip); + TRACE("Unwind: same ip %p over 1000 times\n", (void*)ip); break; } } @@ -80,18 +80,27 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) m_crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2); // Look up the ip address to get the module base address - uint64_t baseAddress = m_crashInfo.GetBaseAddress(ip); + uint64_t baseAddress = m_crashInfo.GetBaseAddressFromAddress(ip); if (baseAddress == 0) { - TRACE("Unwind: module base not found ip %" PRIA PRIx64 "\n", ip); + TRACE_VERBOSE("Unwind: module base not found ip %p\n", (void*)ip); break; } - // Unwind the native frame adding all the memory accessed to the - // core dump via the read memory adapter. - if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, baseAddress, ReadMemoryAdapter)) { + // Unwind the native frame adding all the memory accessed to the core dump via the read memory adapter. + ULONG64 functionStart; + if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, &functionStart, baseAddress, ReadMemoryAdapter)) { TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n"); break; } + + if (m_crashInfo.GatherFrames()) + { + // Add stack frame for the crash report. The function start returned by the unwinder is for + // "ip" and not for the new context returned in pContext. + StackFrame frame(baseAddress, ip, sp, ip - functionStart); + AddStackFrame(frame); + } + previousSp = sp; previousIp = ip; } @@ -129,6 +138,36 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) if (pStackwalk != nullptr) { TRACE("Unwind: managed frames\n"); + m_managed = true; + + ReleaseHolder pException; + if (SUCCEEDED(pTask->GetCurrentExceptionState(&pException))) + { + TRACE("Unwind: found managed exception\n"); + + ReleaseHolder pExceptionValue; + if (SUCCEEDED(pException->GetManagedObject(&pExceptionValue))) + { + CLRDATA_ADDRESS exceptionObject; + if (SUCCEEDED(pExceptionValue->GetAddress(&exceptionObject))) + { + m_exceptionObject = exceptionObject; + TRACE("Unwind: exception object %p\n", (void*)exceptionObject); + } + ReleaseHolder pExceptionType; + if (SUCCEEDED(pExceptionValue->GetType(&pExceptionType))) + { + ArrayHolder typeName = new WCHAR[MAX_LONGPATH + 1]; + if (SUCCEEDED(pExceptionType->GetName(0, MAX_LONGPATH, nullptr, typeName.GetPtr()))) + { + m_exceptionType = FormatString("%S", typeName.GetPtr()); + TRACE("Unwind: exception type %s\n", m_exceptionType.c_str()); + } + } + } + } + + // For each managed stack frame do { // Get the managed stack frame context @@ -137,6 +176,12 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) break; } + // Get and save more detail information for the crash report if enabled + if (m_crashInfo.GatherFrames()) + { + GatherStackFrames(&context, pStackwalk); + } + // Unwind all the native frames after the managed frame UnwindNativeFrames(&context); @@ -147,6 +192,91 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) return true; } +void +ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk) +{ + uint64_t ip = 0, sp = 0; + GetFrameLocation(pContext, &ip, &sp); + + uint64_t moduleAddress = 0; + mdMethodDef token = 0; + uint32_t nativeOffset = 0; + uint32_t ilOffset = 0; + + ReleaseHolder pFrame; + if (SUCCEEDED(pStackwalk->GetFrame(&pFrame))) + { + CLRDataSimpleFrameType simpleType; + CLRDataDetailedFrameType detailedType; + pFrame->GetFrameType(&simpleType, &detailedType); + + if ((simpleType & (CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE)) != 0) + { + ReleaseHolder pMethod; + if (SUCCEEDED(pFrame->GetMethodInstance(&pMethod))) + { + ReleaseHolder pModule; + if (SUCCEEDED(pMethod->GetTokenAndScope(&token, &pModule))) + { + DacpGetModuleData moduleData; + if (SUCCEEDED(moduleData.Request(pModule))) + { + moduleAddress = moduleData.LoadedPEAddress; + } + else + { + TRACE("Unwind: DacpGetModuleData.Request sp %p ip %p FAILED\n", (void*)sp, (void*)ip); + } + } + else + { + TRACE("Unwind: GetTokenAndScope sp %p ip %p FAILED\n", (void*)sp, (void*)ip); + } + if (FAILED(pMethod->GetILOffsetsByAddress(ip, 1, NULL, &ilOffset))) + { + TRACE("Unwind: GetILOffsetsByAddress sp %p ip %p FAILED\n", (void*)sp, (void*)ip); + } + CLRDATA_ADDRESS startAddress; + if (SUCCEEDED(pMethod->GetRepresentativeEntryAddress(&startAddress))) + { + nativeOffset = ip - startAddress; + } + else + { + TRACE("Unwind: GetRepresentativeEntryAddress sp %p ip %p FAILED\n", (void*)sp, (void*)ip); + } + } + else + { + TRACE("Unwind: GetMethodInstance sp %p ip %p FAILED\n", (void*)sp, (void*)ip); + } + } + else + { + TRACE("Unwind: simpleType %08x detailedType %08x\n", simpleType, detailedType); + } + } + + // Add managed stack frame for the crash info notes + StackFrame frame(moduleAddress, ip, sp, nativeOffset, token, ilOffset); + AddStackFrame(frame); +} + +void +ThreadInfo::AddStackFrame(const StackFrame& frame) +{ + std::pair::iterator,bool> result = m_frames.insert(frame); + if (result.second) + { + TRACE("Unwind: sp %p ip %p off %08x mod %p%c\n", + (void*)frame.StackPointer(), + (void*)frame.ReturnAddress(), + frame.NativeOffset(), + (void*)frame.ModuleAddress(), + frame.IsManaged() ? '*' : ' '); + } +} + void ThreadInfo::GetThreadStack() { diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h index ac42d056da5..1a690157908 100644 --- a/src/coreclr/debug/createdump/threadinfo.h +++ b/src/coreclr/debug/createdump/threadinfo.h @@ -40,6 +40,10 @@ private: pid_t m_tid; // thread id pid_t m_ppid; // parent process pid_t m_tgid; // thread group + bool m_managed; // if true, thread has managed code running + uint64_t m_exceptionObject; // exception object address + std::string m_exceptionType; // exception type + std::set m_frames; // stack frames #ifdef __APPLE__ mach_port_t m_port; // MacOS thread port @@ -77,14 +81,23 @@ public: inline pid_t Ppid() const { return m_ppid; } inline pid_t Tgid() const { return m_tgid; } + inline bool IsManaged() const { return m_managed; } + inline uint64_t ManagedExceptionObject() const { return m_exceptionObject; } + inline std::string ManagedExceptionType() const { return m_exceptionType; } + inline const std::set StackFrames() const { return m_frames; } + #ifdef __APPLE__ #if defined(__x86_64__) inline const x86_thread_state64_t* GPRegisters() const { return &m_gpRegisters; } inline const x86_float_state64_t* FPRegisters() const { return &m_fpRegisters; } + inline const uint64_t GetInstructionPointer() const { return m_gpRegisters.__rip; } + inline const uint64_t GetFramePointer() const { return m_gpRegisters.__rbp; } inline const uint64_t GetStackPointer() const { return m_gpRegisters.__rsp; } #elif defined(__aarch64__) inline const arm_thread_state64_t* GPRegisters() const { return &m_gpRegisters; } inline const arm_neon_state64_t* FPRegisters() const { return &m_fpRegisters; } + inline const uint64_t GetInstructionPointer() const { return arm_thread_state64_get_pc(m_gpRegisters); } + inline const uint64_t GetFramePointer() const { return arm_thread_state64_get_fp(m_gpRegisters); } inline const uint64_t GetStackPointer() const { return arm_thread_state64_get_sp(m_gpRegisters); } #endif #else // __APPLE__ @@ -109,6 +122,8 @@ public: private: void UnwindNativeFrames(CONTEXT* pContext); + void GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk); + void AddStackFrame(const StackFrame& frame); #ifndef __APPLE__ bool GetRegistersWithPTrace(); #endif diff --git a/src/coreclr/debug/createdump/threadinfomac.cpp b/src/coreclr/debug/createdump/threadinfomac.cpp index d5c0f9d7495..3ea9151a649 100644 --- a/src/coreclr/debug/createdump/threadinfomac.cpp +++ b/src/coreclr/debug/createdump/threadinfomac.cpp @@ -6,6 +6,8 @@ ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid, mach_port_t port) : m_crashInfo(crashInfo), m_tid(tid), + m_managed(false), + m_exceptionObject(0), m_port(port) { } diff --git a/src/coreclr/debug/createdump/threadinfounix.cpp b/src/coreclr/debug/createdump/threadinfounix.cpp index e0ef048380d..c1e5ca1154c 100644 --- a/src/coreclr/debug/createdump/threadinfounix.cpp +++ b/src/coreclr/debug/createdump/threadinfounix.cpp @@ -24,7 +24,9 @@ bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name); ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid) : m_crashInfo(crashInfo), - m_tid(tid) + m_tid(tid), + m_managed(false), + m_exceptionObject(0) { } diff --git a/src/coreclr/debug/daccess/dacfn.cpp b/src/coreclr/debug/daccess/dacfn.cpp index 2aad5dcdfdf..a76e6bec4f5 100644 --- a/src/coreclr/debug/daccess/dacfn.cpp +++ b/src/coreclr/debug/daccess/dacfn.cpp @@ -250,7 +250,7 @@ typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size extern BOOL -PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback); +PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback); #endif HRESULT @@ -284,7 +284,7 @@ DacVirtualUnwind(ULONG32 threadId, PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_P hr = S_OK; SIZE_T baseAddress = DacGlobalBase(); - if (baseAddress == 0 || !PAL_VirtualUnwindOutOfProc(context, contextPointers, baseAddress, DacReadAllAdapter)) + if (baseAddress == 0 || !PAL_VirtualUnwindOutOfProc(context, contextPointers, nullptr, baseAddress, DacReadAllAdapter)) { hr = E_FAIL; } diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 532c3e82d3d..cd96f5753cc 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -2410,7 +2410,7 @@ typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size PALIMPORT BOOL PALAPI PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers); -PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback); +PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback); #define GetLogicalProcessorCacheSizeFromOS PAL_GetLogicalProcessorCacheSizeFromOS diff --git a/src/coreclr/pal/src/exception/remote-unwind.cpp b/src/coreclr/pal/src/exception/remote-unwind.cpp index b82960cc723..af0293ba5bc 100644 --- a/src/coreclr/pal/src/exception/remote-unwind.cpp +++ b/src/coreclr/pal/src/exception/remote-unwind.cpp @@ -164,6 +164,7 @@ typedef struct _libunwindInfo { SIZE_T BaseAddress; CONTEXT *Context; + ULONG64 FunctionStart; UnwindReadMemoryCallback ReadMemory; } libunwindInfo; @@ -957,7 +958,7 @@ ExtractProcInfoFromFde( static bool SearchCompactEncodingSection( - const libunwindInfo* info, + libunwindInfo* info, unw_word_t ip, unw_word_t compactUnwindSectionAddr, unw_proc_info_t *pip) @@ -966,7 +967,11 @@ SearchCompactEncodingSection( if (!info->ReadMemory((PVOID)compactUnwindSectionAddr, §ionHeader, sizeof(sectionHeader))) { return false; } - TRACE("Unwind ver %d common off: %08x common cnt: %d pers off: %08x pers cnt: %d index off: %08x index cnt: %d\n", + int32_t offset = ip - info->BaseAddress; + + TRACE("Unwind %p offset %08x ver %d common off: %08x common cnt: %d pers off: %08x pers cnt: %d index off: %08x index cnt: %d\n", + (void*)compactUnwindSectionAddr, + offset, sectionHeader.version, sectionHeader.commonEncodingsArraySectionOffset, sectionHeader.commonEncodingsArrayCount, @@ -979,7 +984,6 @@ SearchCompactEncodingSection( return false; } - int32_t offset = ip - info->BaseAddress; unwind_info_section_header_index_entry entry; unwind_info_section_header_index_entry entryNext; bool found; @@ -1060,6 +1064,8 @@ SearchCompactEncodingSection( TRACE("Second level compressed pageEntry not found start %p end %p\n", (void*)funcStart, (void*)funcEnd); } + TRACE("Second level compressed: funcStart %p funcEnd %p pageEntry %08x pageEntryNext %08x\n", (void*)funcStart, (void*)funcEnd, pageEntry, pageEntryNext); + if (ip < funcStart || ip > funcEnd) { ERROR("ip %p not in compressed second level\n", (void*)ip); return false; @@ -1073,7 +1079,7 @@ SearchCompactEncodingSection( if (!ReadValue32(info, &addr, &encoding)) { return false; } - TRACE("Second level compressed common table: %08x for offset %08x\n", encoding, pageOffset); + TRACE("Second level compressed common table: %08x for offset %08x encodingIndex %d\n", encoding, pageOffset, encodingIndex); } else { @@ -1134,6 +1140,7 @@ SearchCompactEncodingSection( } } + info->FunctionStart = funcStart; pip->start_ip = funcStart; pip->end_ip = funcEnd; pip->lsda = lsda; @@ -1171,6 +1178,7 @@ SearchDwarfSection( ERROR("ExtractFde FAILED for ip %p\n", (void*)ip); break; } + if (ip >= ipStart && ip < ipEnd) { if (!ExtractProcInfoFromFde(info, &fdeAddr, pip, need_unwind_info)) { ERROR("ExtractProcInfoFromFde FAILED for ip %p\n", (void*)ip); @@ -1185,7 +1193,7 @@ SearchDwarfSection( static bool -GetProcInfo(unw_word_t ip, unw_proc_info_t *pip, const libunwindInfo* info, bool* step, int need_unwind_info) +GetProcInfo(unw_word_t ip, unw_proc_info_t *pip, libunwindInfo* info, bool* step, int need_unwind_info) { memset(pip, 0, sizeof(*pip)); *step = false; @@ -1297,11 +1305,12 @@ GetProcInfo(unw_word_t ip, unw_proc_info_t *pip, const libunwindInfo* info, bool } } - ERROR("Unwind info not found for %p format %08x\n", (void*)ip, pip->format); + ERROR("Unwind info not found for %p format %08x ehframeSectionAddr %p ehframeSectionSize %p\n", (void*)ip, pip->format, (void*)ehframeSectionAddr, (void*)ehframeSectionSize); return false; } #if defined(TARGET_AMD64) + static bool StepWithCompactEncodingRBPFrame(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding) { @@ -1370,9 +1379,49 @@ StepWithCompactEncodingRBPFrame(const libunwindInfo* info, compact_unwind_encodi compactEncoding, (void*)context->Rip, (void*)context->Rsp, (void*)context->Rbp); return true; } -#endif + +#define AMD64_SYSCALL_OPCODE 0x050f + +static bool +StepWithCompactNoEncoding(const libunwindInfo* info) +{ + // We get here because we found the function the IP is in the compact unwind info, but the encoding is 0. This + // usually ends the unwind but here we check that the function is a syscall "wrapper" and assume there is no + // frame and pop the return address. + uint16_t opcode; + unw_word_t addr = info->Context->Rip - sizeof(opcode); + if (!ReadValue16(info, &addr, &opcode)) { + return false; + } + // Is the IP pointing just after a "syscall" opcode? + if (opcode != AMD64_SYSCALL_OPCODE) { + // There are cases where the IP points one byte after the syscall; not sure why. + addr = info->Context->Rip - sizeof(opcode) + 1; + if (!ReadValue16(info, &addr, &opcode)) { + return false; + } + // Is the IP pointing just after a "syscall" opcode + 1? + if (opcode != AMD64_SYSCALL_OPCODE) { + ERROR("StepWithCompactNoEncoding: not in syscall wrapper function\n"); + return false; + } + } + // Pop the return address from the stack + uint64_t ip; + addr = info->Context->Rsp; + if (!ReadValue64(info, &addr, &ip)) { + return false; + } + info->Context->Rip = ip; + info->Context->Rsp += sizeof(uint64_t); + TRACE("StepWithCompactNoEncoding: SUCCESS new rip %p rsp %p\n", (void*)info->Context->Rip, (void*)info->Context->Rsp); + return true; +} + +#endif // TARGET_AMD64 #if defined(TARGET_ARM64) + inline static bool ReadCompactEncodingRegister(const libunwindInfo* info, unw_word_t* addr, DWORD64* reg) { @@ -1502,7 +1551,8 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_ compactEncoding, (void*)context->Pc, (void*)context->Sp, (void*)context->Fp, (void*)context->Lr); return true; } -#endif + +#endif // TARGET_ARM64 static bool StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, unw_word_t functionStart) @@ -1510,8 +1560,7 @@ StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t com #if defined(TARGET_AMD64) if (compactEncoding == 0) { - TRACE("Compact unwind missing for %p\n", (void*)info->Context->Rip); - return false; + return StepWithCompactNoEncoding(info); } switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { @@ -1817,7 +1866,7 @@ get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg) { - const auto *info = (libunwindInfo*)arg; + auto *info = (libunwindInfo*)arg; #ifdef __APPLE__ bool step; if (!GetProcInfo(ip, pip, info, &step, need_unwind_info)) { @@ -1999,6 +2048,7 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int nee TRACE("ip %p not in range start_ip %p end_ip %p\n", ip, pip->start_ip, pip->end_ip); return -UNW_ENOINFO; } + info->FunctionStart = pip->start_ip; return UNW_ESUCCESS; #else return _OOP_find_proc_info(start_ip, end_ip, ehFrameHdrAddr, ehFrameHdrLen, exidxFrameHdrAddr, exidxFrameHdrLen, as, ip, pip, need_unwind_info, arg); @@ -2048,12 +2098,13 @@ Function: Parameters: context - the start context in the target contextPointers - the context of the next frame + functionStart - the pointer to return the starting address of the function or nullptr baseAddress - base address of the module to find the unwind info readMemoryCallback - reads memory from the target --*/ BOOL PALAPI -PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) +PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) { unw_addr_space_t addrSpace = 0; unw_cursor_t cursor; @@ -2063,6 +2114,7 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont info.BaseAddress = baseAddress; info.Context = context; + info.FunctionStart = 0; info.ReadMemory = readMemoryCallback; #ifdef __APPLE__ @@ -2113,6 +2165,10 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont result = TRUE; exit: + if (functionStart) + { + *functionStart = info.FunctionStart; + } if (addrSpace != 0) { unw_destroy_addr_space(addrSpace); @@ -2124,7 +2180,7 @@ exit: BOOL PALAPI -PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) +PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) { return FALSE; } diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index 9ab675c8df6..14dc01dca6a 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -376,7 +376,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t if (signalRestarts) { // This signal mustn't be ignored because it will be restarted. - PROCAbort(); + PROCAbort(code); } return; } @@ -391,7 +391,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t { // We can't invoke the original handler because returning from the // handler doesn't restart the exception. - PROCAbort(); + PROCAbort(code); } } else @@ -403,7 +403,8 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t } PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCCreateCrashDumpIfEnabled(); + + PROCCreateCrashDumpIfEnabled(code); } /*++ @@ -575,13 +576,13 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) if (SwitchStackAndExecuteHandler(code | StackOverflowFlag, siginfo, context, (size_t)handlerStackTop)) { - PROCAbort(); + PROCAbort(SIGSEGV); } } else { (void)!write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1); - PROCAbort(); + PROCAbort(SIGSEGV); } } diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index 575aa49caac..b1de472ad42 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -149,10 +149,13 @@ Function: Aborts the process after calling the shutdown cleanup handler. This function should be called instead of calling abort() directly. +Parameters: + signal - POSIX signal number + Does not return --*/ PAL_NORETURN -VOID PROCAbort(); +VOID PROCAbort(int signal = SIGABRT); /*++ Function: @@ -172,9 +175,12 @@ Function: Creates crash dump of the process (if enabled). Can be called from the unhandled native exception handler. +Parameters: + signal - POSIX signal number + (no return value) --*/ -VOID PROCCreateCrashDumpIfEnabled(); +VOID PROCCreateCrashDumpIfEnabled(int signal); /*++ Function: diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 54e08b7abd9..0ae3f2eecaf 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -61,6 +61,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include #include #include +#include #ifdef __linux__ #include // __NR_membarrier @@ -232,7 +233,7 @@ static_assert_no_msg(CLR_SEM_MAX_NAMELEN <= MAX_PATH); Volatile g_shutdownCallback = nullptr; // Crash dump generating program arguments. Initialized in PROCAbortInitialize(). -char* g_argvCreateDump[8] = { nullptr }; +std::vector g_argvCreateDump; // // Key used for associating CPalThread's with the underlying pthread @@ -1334,9 +1335,10 @@ static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUncon { // abort() has the semantics that // (1) it doesn't run atexit handlers - // (2) can invoke CrashReporter or produce a coredump, - // which is appropriate for TerminateProcess calls - PROCAbort(); + // (2) can invoke CrashReporter or produce a coredump, which is appropriate for TerminateProcess calls + // TerminationRequestHandlingRoutine in synchmanager.cpp sets the exit code to this special value. The + // Watson analyzer needs to know that the process was terminated with a SIGTERM. + PROCAbort(uExitCode == (128 + SIGTERM) ? SIGTERM : SIGABRT); } else { @@ -3084,6 +3086,28 @@ PROCNotifyProcessShutdownDestructor() PROCNotifyProcessShutdown(/* isExecutingOnAltStack */ false); } +/*++ +Function: + PROCFormatInt + + Helper function to format an ULONG32 as a string. + +--*/ +char* +PROCFormatInt(ULONG32 value) +{ + char* buffer = (char*)InternalMalloc(128); + if (buffer != nullptr) + { + if (sprintf_s(buffer, 128, "%d", value) == -1) + { + free(buffer); + buffer = nullptr; + } + } + return buffer; +} + /*++ Function PROCBuildCreateDumpCommandLine @@ -3097,12 +3121,13 @@ Return --*/ BOOL PROCBuildCreateDumpCommandLine( - const char** argv, + std::vector& argv, char** pprogram, char** ppidarg, char* dumpName, char* dumpType, - BOOL diag) + BOOL diag, + BOOL crashReport) { if (g_szCoreCLRPath == nullptr) { @@ -3132,50 +3157,51 @@ PROCBuildCreateDumpCommandLine( { return FALSE; } - char* pidarg = *ppidarg = (char*)InternalMalloc(128); - if (pidarg == nullptr) + *ppidarg = PROCFormatInt(gPID); + if (*ppidarg == nullptr) { return FALSE; } - if (sprintf_s(pidarg, 128, "%d", gPID) == -1) - { - return FALSE; - } - *argv++ = program; + argv.push_back(program); if (dumpName != nullptr) { - *argv++ = "--name"; - *argv++ = dumpName; + argv.push_back("--name"); + argv.push_back(dumpName); } if (dumpType != nullptr) { if (strcmp(dumpType, "1") == 0) { - *argv++ = "--normal"; + argv.push_back("--normal"); } else if (strcmp(dumpType, "2") == 0) { - *argv++ = "--withheap"; + argv.push_back("--withheap"); } else if (strcmp(dumpType, "3") == 0) { - *argv++ = "--triage"; + argv.push_back("--triage"); } else if (strcmp(dumpType, "4") == 0) { - *argv++ = "--full"; + argv.push_back("--full"); } } if (diag) { - *argv++ = "--diag"; + argv.push_back("--diag"); } - *argv++ = pidarg; - *argv = nullptr; + if (crashReport) + { + argv.push_back("--crashreport"); + } + + argv.push_back(*ppidarg); + argv.push_back(nullptr); return TRUE; } @@ -3190,7 +3216,7 @@ Function: (no return value) --*/ BOOL -PROCCreateCrashDump(char** argv) +PROCCreateCrashDump(std::vector& argv) { // Fork the core dump child process. pid_t childpid = fork(); @@ -3204,7 +3230,7 @@ PROCCreateCrashDump(char** argv) else if (childpid == 0) { // Child process - if (execve(argv[0], argv, palEnvironment) == -1) + if (execve(argv[0], (char**)argv.data(), palEnvironment) == -1) { ERROR("PROCCreateCrashDump: execve() FAILED %d (%s)\n", errno, strerror(errno)); return false; @@ -3258,10 +3284,12 @@ PROCAbortInitialize() char* dumpType = getenv("COMPlus_DbgMiniDumpType"); char* diagStr = getenv("COMPlus_CreateDumpDiagnostics"); BOOL diag = diagStr != nullptr && strcmp(diagStr, "1") == 0; + char* crashReportStr = getenv("COMPlus_EnableCrashReport"); + BOOL crashReport = crashReportStr != nullptr && strcmp(crashReportStr, "1") == 0; char* program = nullptr; char* pidarg = nullptr; - if (!PROCBuildCreateDumpCommandLine((const char **)g_argvCreateDump, &program, &pidarg, dumpName, dumpType, diag)) + if (!PROCBuildCreateDumpCommandLine(g_argvCreateDump, &program, &pidarg, dumpName, dumpType, diag, crashReport)) { return FALSE; } @@ -3296,7 +3324,7 @@ PAL_GenerateCoreDump( INT dumpType, BOOL diag) { - char* argvCreateDump[8] = { nullptr }; + std::vector argvCreateDump; char dumpTypeStr[16]; if (dumpType < 1 || dumpType > 4) @@ -3313,7 +3341,7 @@ PAL_GenerateCoreDump( } char* program = nullptr; char* pidarg = nullptr; - BOOL result = PROCBuildCreateDumpCommandLine((const char **)argvCreateDump, &program, &pidarg, (char*)dumpName, dumpTypeStr, diag); + BOOL result = PROCBuildCreateDumpCommandLine(argvCreateDump, &program, &pidarg, (char*)dumpName, dumpTypeStr, diag, false); if (result) { result = PROCCreateCrashDump(argvCreateDump); @@ -3330,15 +3358,48 @@ Function: Creates crash dump of the process (if enabled). Can be called from the unhandled native exception handler. +Parameters: + signal - POSIX signal number + (no return value) --*/ VOID -PROCCreateCrashDumpIfEnabled() +PROCCreateCrashDumpIfEnabled(int signal) { // If enabled, launch the create minidump utility and wait until it completes - if (g_argvCreateDump[0] != nullptr) + if (!g_argvCreateDump.empty()) { - PROCCreateCrashDump(g_argvCreateDump); + std::vector argv(g_argvCreateDump); + char* signalArg = nullptr; + char* crashThreadArg = nullptr; + + if (signal != 0) + { + // Remove the terminating nullptr + argv.pop_back(); + + // Add the Windows exception code to the command line + signalArg = PROCFormatInt(signal); + if (signalArg != nullptr) + { + argv.push_back("--signal"); + argv.push_back(signalArg); + } + + // Add the current thread id to the command line. This function is always called on the crashing thread. + crashThreadArg = PROCFormatInt(THREADSilentGetCurrentThreadId()); + if (crashThreadArg != nullptr) + { + argv.push_back("--crashthread"); + argv.push_back(crashThreadArg); + } + argv.push_back(nullptr); + } + + PROCCreateCrashDump(argv); + + free(signalArg); + free(crashThreadArg); } } @@ -3349,16 +3410,19 @@ Function: Aborts the process after calling the shutdown cleanup handler. This function should be called instead of calling abort() directly. +Parameters: + signal - POSIX signal number + Does not return --*/ PAL_NORETURN VOID -PROCAbort() +PROCAbort(int signal) { // Do any shutdown cleanup before aborting or creating a core dump PROCNotifyProcessShutdown(); - PROCCreateCrashDumpIfEnabled(); + PROCCreateCrashDumpIfEnabled(signal); // Restore the SIGABORT handler to prevent recursion SEHCleanupAbort(); From a25bece2b1916745eb9774e99d2511f08072fe8e Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 8 Jul 2021 02:02:25 +0200 Subject: [PATCH 321/926] Implement PosixSignal API (#54136) * Implement PosixSignal API * PosixSignalRegistration: implement finalizer * Support using positive PosixSignal values as raw signal numbers * Reduce nr of TargetFrameworks * #ifdef SIGRTMAX * Don't throw Win32Exception (for now) * Rename PosixSignalRegistration.cs to PosixSignalRegistration.Unsupported.cs * Spawn new Thread only for specific signals * Add PosixSignal.SIGCONT, SIGWINCH, SIGTTIN, SIGTTOU, SIGTSTP * SystemNative_DefaultSignalHandler: remove SuppressGCTransition attribute * Update comment in SystemNative_InitializeTerminalAndSignalHandling * Run original handler except for cancelable signals. * Cleanup ControlCHandlerRegistrar * Handle TryGetTarget returning false due to finalized PosixSignalRegistrations * Use Thread.UnsafeStart * Remove SuppressGCTransition from methods that take a lock * Support canceling terminal configuration on SIGCHLD/SIGCONT * Throw for errno using Interop.CheckIO * Rename DefaultSignalHandler to HandleNonCanceledPosixSignal * Register Console SIGTTOU with SA_RESETHAND * Allocate storage for tracking signals dynamically * Propagate sigaction errors when enabling PosixSignal * Add some tests * Fix entrypoints * Fix compilation * Add some more tests * Move tests to PosixSignalRegistrationTests.Unix file * Add SignalCanCancelTermination test * ConfigureTerminalForChildProcesses: split Unix and iOS * Fix iOS compilation failure * Skip signals that cause xunit to exit with non zero * Try fix Android compilation * Remove SIGSTOP from UninstallableSignalsThrow because it doesn't throw on OSX * Cleanup * Handle case where all handlers got disposed by the time we want to call them. * GenerateReferenceAssemblySource * Replace PosixSignalRegistration.Unsupported with .Windows/.Browser file * Remove assert(g_hasTty) from UninitializeTerminal * Initialize signal handling when ifndef HAS_CONSOLE_SIGNALS * Fix broken Cancel * Use SIGRTMAX as highest usable signal number. * SIGRTMAX is not defined on all platforms, fall back to NSIG * Resend signal only when restored to SIG_DFL * SignalCanCancelTermination test: increase timeout * Increase timeout to 10min * Update expected SIGQUIT exit code for mono * Change isMono check * Use PlatformDetection.IsMonoRuntime * Add IsSignalThatTerminates method * Add some documentation xml * Console.ManualTests: test terminal echo during and after child process used terminal. * Maintain flags and mask of original handler * PR feedback * SystemNative_HandleNonCanceledPosixSignal: update handling based on default dispositions. * Apply suggestions from code review Co-authored-by: Stephen Toub * Don't call sa_sigaction/sa_handler twice from SystemNative_HandleNonCanceledPosixSignal. * Don't remove WeakReferences while iterating. * Move static fields to the top of the class * Remove -Wcast-qual ignore * PosixSignalContext: add XML docs * Fix Android compilation * Fix Android compilation, take II * Fix comment Co-authored-by: Stephen Toub * Add PosixSignalContext constructor test * SystemNative_HandleNonCanceledPosixSignal: add SIG_IGN case Co-authored-by: Stephen Toub --- .../Unix/System.Native/Interop.PosixSignal.cs | 27 + .../System.Native/Interop.RegisterForCtrlC.cs | 24 - .../Interop.RegisterForSigChld.cs | 2 +- ...yedSigChildConsoleConfigurationHandler.cs} | 5 +- .../Interop.SetTerminalInvalidationHandler.cs | 1 - .../Native/Unix/System.Native/entrypoints.c | 9 +- .../Native/Unix/System.Native/pal_console.c | 23 +- .../Native/Unix/System.Native/pal_signal.c | 558 ++++++++++++++---- .../Native/Unix/System.Native/pal_signal.h | 91 ++- .../System.Console/src/System.Console.csproj | 7 - .../System.Console/src/System/Console.cs | 3 +- .../src/System/ConsolePal.Unix.cs | 46 +- .../tests/ManualTests/ManualTests.cs | 28 + .../System.Console.Manual.Tests.csproj | 3 + .../src/System.Diagnostics.Process.csproj | 7 +- ...ConfigureTerminalForChildProcesses.Unix.cs | 64 ++ ....ConfigureTerminalForChildProcesses.iOS.cs | 21 + ...Unix.ConfigureTerminalForChildProcesses.cs | 36 -- .../src/System/Diagnostics/Process.Unix.cs | 27 +- .../Diagnostics/ProcessWaitState.Unix.cs | 10 +- .../ref/System.Runtime.InteropServices.cs | 26 + .../src/Resources/Strings.resx | 36 ++ .../src/System.Runtime.InteropServices.csproj | 23 +- .../Runtime/InteropServices/PosixSignal.cs | 19 + .../InteropServices/PosixSignalContext.cs | 40 ++ .../PosixSignalRegistration.Browser.cs | 18 + .../PosixSignalRegistration.Unix.cs | 292 +++++++++ .../PosixSignalRegistration.Windows.cs | 18 + ...ystem.Runtime.InteropServices.Tests.csproj | 8 +- .../PosixSignalContextTests.cs | 22 + .../PosixSignalRegistrationTests.Unix.cs | 236 ++++++++ 31 files changed, 1450 insertions(+), 280 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs delete mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs rename src/libraries/Common/src/Interop/Unix/System.Native/{Interop.RestoreAndHandleCtrl.cs => Interop.SetDelayedSigChildConsoleConfigurationHandler.cs} (61%) create mode 100644 src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.Unix.cs create mode 100644 src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.iOS.cs delete mode 100644 src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs new file mode 100644 index 00000000000..b42b5138ec6 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetPosixSignalHandler")] + [SuppressGCTransition] + internal static extern unsafe void SetPosixSignalHandler(delegate* unmanaged handler); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_EnablePosixSignalHandling", SetLastError = true)] + internal static extern bool EnablePosixSignalHandling(int signal); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_DisablePosixSignalHandling")] + internal static extern void DisablePosixSignalHandling(int signal); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_HandleNonCanceledPosixSignal")] + internal static extern bool HandleNonCanceledPosixSignal(int signal, int handlersDisposed); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")] + [SuppressGCTransition] + internal static extern int GetPlatformSignalNumber(PosixSignal signal); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs deleted file mode 100644 index cb06c67651c..00000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Sys - { - internal enum CtrlCode - { - Interrupt = 0, - Break = 1 - } - - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_RegisterForCtrl")] - [SuppressGCTransition] - internal static extern unsafe void RegisterForCtrl(delegate* unmanaged handler); - - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_UnregisterForCtrl")] - [SuppressGCTransition] - internal static extern void UnregisterForCtrl(); - } -} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs index 3082c661cba..e11fcfaf0e6 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs @@ -8,6 +8,6 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_RegisterForSigChld")] - internal static extern unsafe void RegisterForSigChld(delegate* unmanaged handler); + internal static extern unsafe void RegisterForSigChld(delegate* unmanaged handler); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetDelayedSigChildConsoleConfigurationHandler.cs similarity index 61% rename from src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs rename to src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetDelayedSigChildConsoleConfigurationHandler.cs index 927c2d27068..bd09aa12abc 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetDelayedSigChildConsoleConfigurationHandler.cs @@ -7,7 +7,8 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_RestoreAndHandleCtrl")] - internal static extern void RestoreAndHandleCtrl(CtrlCode ctrlCode); + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetDelayedSigChildConsoleConfigurationHandler")] + [SuppressGCTransition] + internal static extern unsafe void SetDelayedSigChildConsoleConfigurationHandler(delegate* unmanaged callback); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs index a12194e1737..3082cb96b71 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs @@ -8,7 +8,6 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetTerminalInvalidationHandler")] - [SuppressGCTransition] internal static extern unsafe void SetTerminalInvalidationHandler(delegate* unmanaged handler); } } diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index cf6485ecf84..b1b5a92e5f3 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -216,10 +216,8 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_GetOSArchitecture) DllImportEntry(SystemNative_GetProcessArchitecture) DllImportEntry(SystemNative_SearchPath) - DllImportEntry(SystemNative_RegisterForCtrl) - DllImportEntry(SystemNative_UnregisterForCtrl) DllImportEntry(SystemNative_RegisterForSigChld) - DllImportEntry(SystemNative_RestoreAndHandleCtrl) + DllImportEntry(SystemNative_SetDelayedSigChildConsoleConfigurationHandler) DllImportEntry(SystemNative_SetTerminalInvalidationHandler) DllImportEntry(SystemNative_InitializeTerminalAndSignalHandling) DllImportEntry(SystemNative_SNPrintF) @@ -251,6 +249,11 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_PWrite) DllImportEntry(SystemNative_PReadV) DllImportEntry(SystemNative_PWriteV) + DllImportEntry(SystemNative_EnablePosixSignalHandling) + DllImportEntry(SystemNative_DisablePosixSignalHandling) + DllImportEntry(SystemNative_HandleNonCanceledPosixSignal) + DllImportEntry(SystemNative_SetPosixSignalHandler) + DllImportEntry(SystemNative_GetPlatformSignalNumber) }; EXTERN_C const void* SystemResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Native/pal_console.c b/src/libraries/Native/Unix/System.Native/pal_console.c index c18e3f82620..1fd55aa3d04 100644 --- a/src/libraries/Native/Unix/System.Native/pal_console.c +++ b/src/libraries/Native/Unix/System.Native/pal_console.c @@ -97,23 +97,11 @@ static bool g_hasTty = false; // cache we are not a tty static volatile bool g_receivedSigTtou = false; -static void ttou_handler(int signo) +static void ttou_handler() { - (void)signo; g_receivedSigTtou = true; } -static void InstallTTOUHandler(void (*handler)(int), int flags) -{ - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = handler; - action.sa_flags = flags; - int rvSigaction = sigaction(SIGTTOU, &action, NULL); - assert(rvSigaction == 0); - (void)rvSigaction; -} - static bool TcSetAttr(struct termios* termios, bool blockIfBackground) { if (g_terminalUninitialized) @@ -131,7 +119,7 @@ static bool TcSetAttr(struct termios* termios, bool blockIfBackground) // stdout. We set SA_RESETHAND to avoid that handler's write loops infinitly // on EINTR when the process is running in background and the terminal // configured with TOSTOP. - InstallTTOUHandler(ttou_handler, (int)SA_RESETHAND); + InstallTTOUHandlerForConsole(ttou_handler); g_receivedSigTtou = false; } @@ -147,8 +135,7 @@ static bool TcSetAttr(struct termios* termios, bool blockIfBackground) rv = true; } - // Restore default SIGTTOU handler. - InstallTTOUHandler(SIG_DFL, 0); + UninstallTTOUHandlerForConsole(); } // On success, update the cached value. @@ -205,8 +192,6 @@ static bool ConfigureTerminal(bool signalForBreak, bool forChild, uint8_t minCha void UninitializeTerminal() { - assert(g_hasTty); - // This method is called on SIGQUIT/SIGINT from the signal dispatching thread // and on atexit. @@ -473,7 +458,7 @@ int32_t SystemNative_InitializeTerminalAndSignalHandling() { static int32_t initialized = 0; - // Both the Process and Console class call this method for initialization. + // The Process, Console and PosixSignalRegistration classes call this method for initialization. if (pthread_mutex_lock(&g_lock) == 0) { if (initialized == 0) diff --git a/src/libraries/Native/Unix/System.Native/pal_signal.c b/src/libraries/Native/Unix/System.Native/pal_signal.c index dfe30adb9e8..4c4017f6547 100644 --- a/src/libraries/Native/Unix/System.Native/pal_signal.c +++ b/src/libraries/Native/Unix/System.Native/pal_signal.c @@ -17,33 +17,212 @@ #include static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; -static struct sigaction g_origSigIntHandler, g_origSigQuitHandler; // saved signal handlers for ctrl handling -static struct sigaction g_origSigContHandler, g_origSigChldHandler; // saved signal handlers for reinitialization -static struct sigaction g_origSigWinchHandler; // saved signal handlers for SIGWINCH -static volatile CtrlCallback g_ctrlCallback = NULL; // Callback invoked for SIGINT/SIGQUIT -static volatile TerminalInvalidationCallback g_terminalInvalidationCallback = NULL; // Callback invoked for SIGCHLD/SIGCONT/SIGWINCH -static volatile SigChldCallback g_sigChldCallback = NULL; // Callback invoked for SIGCHLD + +// Saved signal handlers +static struct sigaction* g_origSigHandler; +static bool* g_handlerIsInstalled; + +// Callback invoked for SIGCHLD/SIGCONT/SIGWINCH +static volatile TerminalInvalidationCallback g_terminalInvalidationCallback = NULL; +// Callback invoked for SIGCHLD +static volatile SigChldCallback g_sigChldCallback = NULL; +static volatile bool g_sigChldConsoleConfigurationDelayed; +static void (*g_sigChldConsoleConfigurationCallback)(void); +// Callback invoked for for SIGTTOU while terminal settings are changed. +static volatile ConsoleSigTtouHandler g_consoleTtouHandler; + +// Callback invoked for PosixSignal handling. +static PosixSignalHandler g_posixSignalHandler = NULL; +// Tracks whether there are PosixSignal handlers registered. +static volatile bool* g_hasPosixSignalRegistrations; + static int g_signalPipe[2] = {-1, -1}; // Pipe used between signal handler and worker +static int GetSignalMax() // Returns the highest usable signal number. +{ +#ifdef SIGRTMAX + return SIGRTMAX; +#else + return NSIG; +#endif +} + +static bool IsCancelableTerminationSignal(int sig) +{ + return sig == SIGINT || + sig == SIGQUIT || + sig == SIGTERM; +} + +static bool IsSaSigInfo(struct sigaction* action) +{ + assert(action); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // sa_flags is unsigned on Android. + return (action->sa_flags & SA_SIGINFO) != 0; +#pragma clang diagnostic pop +} + +static bool IsSigIgn(struct sigaction* action) +{ + assert(action); + return !IsSaSigInfo(action) && action->sa_handler == SIG_IGN; +} + +static bool IsSigDfl(struct sigaction* action) +{ + assert(action); + return !IsSaSigInfo(action) && action->sa_handler == SIG_DFL; +} + +static bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal) +{ + assert(posixSignal != NULL); + + switch (signalCode) + { + case SIGHUP: + *posixSignal = PosixSignalSIGHUP; + return true; + + case SIGINT: + *posixSignal = PosixSignalSIGINT; + return true; + + case SIGQUIT: + *posixSignal = PosixSignalSIGQUIT; + return true; + + case SIGTERM: + *posixSignal = PosixSignalSIGTERM; + return true; + + case SIGCHLD: + *posixSignal = PosixSignalSIGCHLD; + return true; + + case SIGWINCH: + *posixSignal = PosixSignalSIGWINCH; + return true; + + case SIGCONT: + *posixSignal = PosixSignalSIGCONT; + return true; + + case SIGTTIN: + *posixSignal = PosixSignalSIGTTIN; + return true; + + case SIGTTOU: + *posixSignal = PosixSignalSIGTTOU; + return true; + + case SIGTSTP: + *posixSignal = PosixSignalSIGTSTP; + return true; + + default: + *posixSignal = signalCode; + return false; + } +} + +int32_t SystemNative_GetPlatformSignalNumber(PosixSignal signal) +{ + switch (signal) + { + case PosixSignalSIGHUP: + return SIGHUP; + + case PosixSignalSIGINT: + return SIGINT; + + case PosixSignalSIGQUIT: + return SIGQUIT; + + case PosixSignalSIGTERM: + return SIGTERM; + + case PosixSignalSIGCHLD: + return SIGCHLD; + + case PosixSignalSIGWINCH: + return SIGWINCH; + + case PosixSignalSIGCONT: + return SIGCONT; + + case PosixSignalSIGTTIN: + return SIGTTIN; + + case PosixSignalSIGTTOU: + return SIGTTOU; + + case PosixSignalSIGTSTP: + return SIGTSTP; + + case PosixSignalInvalid: + break; + } + + if (signal > 0 && signal <= GetSignalMax()) + { + return signal; + } + + return 0; +} + +void SystemNative_SetPosixSignalHandler(PosixSignalHandler signalHandler) +{ + assert(signalHandler); + assert(g_posixSignalHandler == NULL || g_posixSignalHandler == signalHandler); + + g_posixSignalHandler = signalHandler; +} + static struct sigaction* OrigActionFor(int sig) { - switch (sig) - { - case SIGINT: return &g_origSigIntHandler; - case SIGQUIT: return &g_origSigQuitHandler; - case SIGCONT: return &g_origSigContHandler; - case SIGCHLD: return &g_origSigChldHandler; - case SIGWINCH: return &g_origSigWinchHandler; - } + return &g_origSigHandler[sig - 1]; +} - assert(false); - return NULL; +static void RestoreSignalHandler(int sig) +{ + g_handlerIsInstalled[sig - 1] = false; + sigaction(sig, OrigActionFor(sig), NULL); } static void SignalHandler(int sig, siginfo_t* siginfo, void* context) { - // Signal handler for signals where we want our background thread to do the real processing. - // It simply writes the signal code to a pipe that's read by the thread. + if (sig == SIGCONT) + { + ConsoleSigTtouHandler consoleTtouHandler = g_consoleTtouHandler; + if (consoleTtouHandler != NULL) + { + consoleTtouHandler(); + } + } + + // For these signals, the runtime original sa_sigaction/sa_handler will terminate the app. + // This termination can be canceled using the PosixSignal API. + // For other signals, we immediately invoke the original handler. + if (!IsCancelableTerminationSignal(sig)) + { + struct sigaction* origHandler = OrigActionFor(sig); + if (IsSaSigInfo(origHandler)) + { + assert(origHandler->sa_sigaction); + origHandler->sa_sigaction(sig, siginfo, context); + } + else if (origHandler->sa_handler != SIG_IGN && + origHandler->sa_handler != SIG_DFL) + { + origHandler->sa_handler(sig); + } + } + + // Perform further processing on background thread. + // Write the signal code to a pipe that's read by the thread. uint8_t signalCodeByte = (uint8_t)sig; ssize_t writtenBytes; while ((writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1)) < 0 && errno == EINTR); @@ -52,19 +231,69 @@ static void SignalHandler(int sig, siginfo_t* siginfo, void* context) { abort(); // fatal error } +} - // Delegate to any saved handler we may have - // We assume the original SIGCHLD handler will not reap our children. - if (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH) +int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t handlersDisposed) +{ + switch (signalCode) { - struct sigaction* origHandler = OrigActionFor(sig); - if (origHandler->sa_sigaction != NULL && - (void*)origHandler->sa_sigaction != (void*)SIG_DFL && - (void*)origHandler->sa_sigaction != (void*)SIG_IGN) - { - origHandler->sa_sigaction(sig, siginfo, context); - } + case SIGCONT: + // Default disposition is Continue. +#ifdef HAS_CONSOLE_SIGNALS + ReinitializeTerminal(); +#endif + break; + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + // Default disposition is Stop. + // no-op. + break; + case SIGCHLD: + // Default disposition is Ignore. + if (g_sigChldConsoleConfigurationDelayed) + { + g_sigChldConsoleConfigurationDelayed = false; + + assert(g_sigChldConsoleConfigurationCallback); + g_sigChldConsoleConfigurationCallback(); + } + break; + case SIGURG: + case SIGWINCH: + // Default disposition is Ignore. + // no-op. + break; + default: + // Default disposition is Terminate. + if (!IsCancelableTerminationSignal(signalCode) && !IsSigDfl(OrigActionFor(signalCode))) + { + // We've already called the original handler in SignalHandler. + break; + } + if (IsSigIgn(OrigActionFor(signalCode))) + { + // Original handler doesn't do anything. + break; + } + if (handlersDisposed && g_hasPosixSignalRegistrations[signalCode - 1]) + { + // New handlers got registered. + return 0; + } + // Restore and invoke the original handler. + pthread_mutex_lock(&lock); + { + RestoreSignalHandler(signalCode); + } + pthread_mutex_unlock(&lock); +#ifdef HAS_CONSOLE_SIGNALS + UninitializeTerminal(); +#endif + kill(getpid(), signalCode); + break; } + return 1; } // Entrypoint for the thread that handles signals where our handling @@ -106,25 +335,12 @@ static void* SignalHandlerLoop(void* arg) } } - if (signalCode == SIGQUIT || signalCode == SIGINT) - { - // We're now handling SIGQUIT and SIGINT. Invoke the callback, if we have one. - CtrlCallback callback = g_ctrlCallback; - CtrlCode ctrlCode = signalCode == SIGQUIT ? Break : Interrupt; - if (callback != NULL) - { - callback(ctrlCode); - } - else - { - SystemNative_RestoreAndHandleCtrl(ctrlCode); - } - } - else if (signalCode == SIGCHLD) + bool usePosixSignalHandler = g_hasPosixSignalRegistrations[signalCode - 1]; + if (signalCode == SIGCHLD) { // When the original disposition is SIG_IGN, children that terminated did not become zombies. // Since we overwrote the disposition, we have become responsible for reaping those processes. - bool reapAll = (void*)OrigActionFor(signalCode)->sa_sigaction == (void*)SIG_IGN; + bool reapAll = IsSigIgn(OrigActionFor(signalCode)); SigChldCallback callback = g_sigChldCallback; // double-checked locking @@ -149,18 +365,27 @@ static void* SignalHandlerLoop(void* arg) if (callback != NULL) { - callback(reapAll ? 1 : 0); + if (callback(reapAll ? 1 : 0, usePosixSignalHandler ? 0 : 1 /* configureConsole */)) + { + g_sigChldConsoleConfigurationDelayed = true; + } } } - else if (signalCode == SIGCONT) + + if (usePosixSignalHandler) { -#ifdef HAS_CONSOLE_SIGNALS - ReinitializeTerminal(); -#endif + assert(g_posixSignalHandler != NULL); + PosixSignal signal; + if (!TryConvertSignalCodeToPosixSignal(signalCode, &signal)) + { + signal = PosixSignalInvalid; + } + usePosixSignalHandler = g_posixSignalHandler(signalCode, signal) != 0; } - else if (signalCode != SIGWINCH) + + if (!usePosixSignalHandler) { - assert_msg(false, "invalid signalCode", (int)signalCode); + SystemNative_HandleNonCanceledPosixSignal(signalCode, 0); } } } @@ -175,72 +400,102 @@ static void CloseSignalHandlingPipe() g_signalPipe[1] = -1; } -void SystemNative_RegisterForCtrl(CtrlCallback callback) +static bool InstallSignalHandler(int sig, int flags) { - assert(callback != NULL); - assert(g_ctrlCallback == NULL); - g_ctrlCallback = callback; -} + int rv; + struct sigaction* orig = OrigActionFor(sig); + bool* isInstalled = &g_handlerIsInstalled[sig - 1]; -void SystemNative_UnregisterForCtrl() -{ - assert(g_ctrlCallback != NULL); - g_ctrlCallback = NULL; -} + if (*isInstalled) + { + // Already installed. + return true; + } -void SystemNative_RestoreAndHandleCtrl(CtrlCode ctrlCode) -{ - int signalCode = ctrlCode == Break ? SIGQUIT : SIGINT; -#ifdef HAS_CONSOLE_SIGNALS - UninitializeTerminal(); -#endif - sigaction(signalCode, OrigActionFor(signalCode), NULL); - kill(getpid(), signalCode); + // We respect ignored signals. + // Setting up a handler for them causes child processes to reset to the + // default handler on exec, which means they will terminate on some signals + // which were set to ignore. + rv = sigaction(sig, NULL, orig); + if (rv != 0) + { + return false; + } + if (IsSigIgn(orig)) + { + *isInstalled = true; + return true; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // sa_flags is unsigned on Android. + struct sigaction newAction; + if (!IsSigDfl(orig)) + { + // Maintain flags and mask of original handler. + newAction = *orig; + newAction.sa_flags = orig->sa_flags & ~(SA_RESTART | SA_RESETHAND); + } + else + { + memset(&newAction, 0, sizeof(struct sigaction)); + } + newAction.sa_flags |= flags | SA_SIGINFO; +#pragma clang diagnostic pop + newAction.sa_sigaction = &SignalHandler; + + rv = sigaction(sig, &newAction, orig); + if (rv != 0) + { + return false; + } + *isInstalled = true; + return true; } void SystemNative_SetTerminalInvalidationHandler(TerminalInvalidationCallback callback) { assert(callback != NULL); assert(g_terminalInvalidationCallback == NULL); - g_terminalInvalidationCallback = callback; + bool installed; + (void)installed; // only used for assert + + pthread_mutex_lock(&lock); + { + g_terminalInvalidationCallback = callback; + + installed = InstallSignalHandler(SIGCONT, SA_RESTART); + assert(installed); + installed = InstallSignalHandler(SIGCHLD, SA_RESTART); + assert(installed); + installed = InstallSignalHandler(SIGWINCH, SA_RESTART); + assert(installed); + } + pthread_mutex_unlock(&lock); } void SystemNative_RegisterForSigChld(SigChldCallback callback) { assert(callback != NULL); assert(g_sigChldCallback == NULL); + bool installed; + (void)installed; // only used for assert pthread_mutex_lock(&lock); { g_sigChldCallback = callback; + + installed = InstallSignalHandler(SIGCHLD, SA_RESTART); + assert(installed); } pthread_mutex_unlock(&lock); } -static void InstallSignalHandler(int sig, bool skipWhenSigIgn) +void SystemNative_SetDelayedSigChildConsoleConfigurationHandler(void (*callback)(void)) { - int rv; - (void)rv; // only used for assert - struct sigaction* orig = OrigActionFor(sig); + assert(g_sigChldConsoleConfigurationCallback == NULL); - if (skipWhenSigIgn) - { - rv = sigaction(sig, NULL, orig); - assert(rv == 0); - if ((void*)orig->sa_sigaction == (void*)SIG_IGN) - { - return; - } - } - - struct sigaction newAction; - memset(&newAction, 0, sizeof(struct sigaction)); - newAction.sa_flags = SA_RESTART | SA_SIGINFO; - sigemptyset(&newAction.sa_mask); - newAction.sa_sigaction = &SignalHandler; - - rv = sigaction(sig, &newAction, orig); - assert(rv == 0); + g_sigChldConsoleConfigurationCallback = callback; } static bool CreateSignalHandlerThread(int* readFdPtr) @@ -276,6 +531,24 @@ static bool CreateSignalHandlerThread(int* readFdPtr) int32_t InitializeSignalHandlingCore() { + size_t signalMax = (size_t)GetSignalMax(); + g_origSigHandler = (struct sigaction*)calloc(sizeof(struct sigaction), signalMax); + g_handlerIsInstalled = (bool*)calloc(sizeof(bool), signalMax); + g_hasPosixSignalRegistrations = (bool*)calloc(sizeof(bool), signalMax); + if (g_origSigHandler == NULL || + g_handlerIsInstalled == NULL || + g_hasPosixSignalRegistrations == NULL) + { + free(g_origSigHandler); + free(g_handlerIsInstalled); + free((void*)(size_t)g_hasPosixSignalRegistrations); + g_origSigHandler = NULL; + g_handlerIsInstalled = NULL; + g_hasPosixSignalRegistrations = NULL; + errno = ENOMEM; + return 0; + } + // Create a pipe we'll use to communicate with our worker // thread. We can't do anything interesting in the signal handler, // so we instead send a message to another thread that'll do @@ -308,23 +581,104 @@ int32_t InitializeSignalHandlingCore() return 0; } - // Finally, register our signal handlers - // We don't handle ignored SIGINT/SIGQUIT signals. If we'd setup a handler, our child - // processes would reset to the default on exec causing them to terminate on these signals. - InstallSignalHandler(SIGINT , /* skipWhenSigIgn */ true); - InstallSignalHandler(SIGQUIT, /* skipWhenSigIgn */ true); - InstallSignalHandler(SIGCONT, /* skipWhenSigIgn */ false); - InstallSignalHandler(SIGCHLD, /* skipWhenSigIgn */ false); - InstallSignalHandler(SIGWINCH, /* skipWhenSigIgn */ false); - return 1; } +int32_t SystemNative_EnablePosixSignalHandling(int signalCode) +{ + assert(g_posixSignalHandler != NULL); + assert(signalCode > 0 && signalCode <= GetSignalMax()); + + bool installed; + pthread_mutex_lock(&lock); + { + installed = InstallSignalHandler(signalCode, SA_RESTART); + + g_hasPosixSignalRegistrations[signalCode - 1] = installed; + } + pthread_mutex_unlock(&lock); + + return installed ? 1 : 0; +} + +void SystemNative_DisablePosixSignalHandling(int signalCode) +{ + assert(signalCode > 0 && signalCode <= GetSignalMax()); + + pthread_mutex_lock(&lock); + { + g_hasPosixSignalRegistrations[signalCode - 1] = false; + + if (!(g_consoleTtouHandler && signalCode == SIGTTOU) && + !(g_sigChldCallback && signalCode == SIGCHLD) && + !(g_terminalInvalidationCallback && (signalCode == SIGCONT || + signalCode == SIGCHLD || + signalCode == SIGWINCH))) + { + RestoreSignalHandler(signalCode); + } + } + pthread_mutex_unlock(&lock); +} + +void InstallTTOUHandlerForConsole(ConsoleSigTtouHandler handler) +{ + bool installed; + + pthread_mutex_lock(&lock); + { + assert(g_consoleTtouHandler == NULL); + g_consoleTtouHandler = handler; + + // When the process is running in background, changing terminal settings + // will stop it (default SIGTTOU action). + // We change SIGTTOU's disposition to get EINTR instead. + // This thread may be used to run a signal handler, which may write to + // stdout. We set SA_RESETHAND to avoid that handler's write loops infinitly + // on EINTR when the process is running in background and the terminal + // configured with TOSTOP. + RestoreSignalHandler(SIGTTOU); + installed = InstallSignalHandler(SIGTTOU, (int)SA_RESETHAND); + assert(installed); + } + pthread_mutex_unlock(&lock); +} + +void UninstallTTOUHandlerForConsole(void) +{ + bool installed; + (void)installed; // only used for assert + pthread_mutex_lock(&lock); + { + g_consoleTtouHandler = NULL; + + RestoreSignalHandler(SIGTTOU); + if (g_hasPosixSignalRegistrations[SIGTTOU - 1]) + { + installed = InstallSignalHandler(SIGTTOU, SA_RESTART); + assert(installed); + } + } + pthread_mutex_unlock(&lock); +} + #ifndef HAS_CONSOLE_SIGNALS int32_t SystemNative_InitializeTerminalAndSignalHandling() { - return 0; + static int32_t initialized = 0; + + // The Process, Console and PosixSignalRegistration classes call this method for initialization. + if (pthread_mutex_lock(&lock) == 0) + { + if (initialized == 0) + { + initialized = InitializeSignalHandlingCore(); + } + pthread_mutex_unlock(&lock); + } + + return initialized; } #endif diff --git a/src/libraries/Native/Unix/System.Native/pal_signal.h b/src/libraries/Native/Unix/System.Native/pal_signal.h index 6b0882c0413..138f37835a8 100644 --- a/src/libraries/Native/Unix/System.Native/pal_signal.h +++ b/src/libraries/Native/Unix/System.Native/pal_signal.h @@ -13,29 +13,7 @@ */ int32_t InitializeSignalHandlingCore(void); -/** - * Hooks up the specified callback for notifications when SIGINT or SIGQUIT is received. - * - * Not thread safe. Caller must provide its owns synchronization to ensure RegisterForCtrl - * is not called concurrently with itself or with UnregisterForCtrl. - * - * Should only be called when a callback is not currently registered. - */ -PALEXPORT void SystemNative_RegisterForCtrl(CtrlCallback callback); - -/** - * Unregisters the previously registered ctrlCCallback. - * - * Not thread safe. Caller must provide its owns synchronization to ensure UnregisterForCtrl - * is not called concurrently with itself or with RegisterForCtrl. - * - * Should only be called when a callback is currently registered. The pointer - * previously registered must remain valid until all ctrl handling activity - * has quiesced. - */ -PALEXPORT void SystemNative_UnregisterForCtrl(void); - -typedef void (*SigChldCallback)(int reapAll); +typedef int32_t (*SigChldCallback)(int32_t reapAll, int32_t configureConsole); /** * Hooks up the specified callback for notifications when SIGCHLD is received. @@ -44,22 +22,73 @@ typedef void (*SigChldCallback)(int reapAll); */ PALEXPORT void SystemNative_RegisterForSigChld(SigChldCallback callback); -/** - * Remove our handler and reissue the signal to be picked up by the previously registered handler. - * - * In the most common case, this will be the default handler, causing the process to be torn down. - * It could also be a custom handler registered by other code before us. - */ -PALEXPORT void SystemNative_RestoreAndHandleCtrl(CtrlCode ctrlCode); - typedef void (*TerminalInvalidationCallback)(void); +PALEXPORT void SystemNative_SetDelayedSigChildConsoleConfigurationHandler(void (*callback)(void)); + /** * Hooks up the specified callback for notifications when SIGCHLD, SIGCONT, SIGWINCH are received. * */ PALEXPORT void SystemNative_SetTerminalInvalidationHandler(TerminalInvalidationCallback callback); +typedef enum +{ + PosixSignalInvalid = 0, + PosixSignalSIGHUP = -1, + PosixSignalSIGINT = -2, + PosixSignalSIGQUIT = -3, + PosixSignalSIGTERM = -4, + PosixSignalSIGCHLD = -5, + PosixSignalSIGWINCH = -6, + PosixSignalSIGCONT = -7, + PosixSignalSIGTTIN = -8, + PosixSignalSIGTTOU = -9, + PosixSignalSIGTSTP = -10 +} PosixSignal; + +typedef int32_t (*PosixSignalHandler)(int32_t signalCode, PosixSignal signal); + +/** + * Hooks up the specified callback for handling PosixSignalRegistrations. + * + * Should only be called when a callback is not currently registered. + */ +PALEXPORT void SystemNative_SetPosixSignalHandler(PosixSignalHandler signalHandler); + +/** + * Converts a PosixSignal value to the platform signal number. + * When the signal is out of range, the function returns zero. + */ +PALEXPORT int32_t SystemNative_GetPlatformSignalNumber(PosixSignal signal); + +/** + * Enables calling the PosixSignalHandler for the specified signal. + */ +PALEXPORT int32_t SystemNative_EnablePosixSignalHandling(int signalCode); + +/** + * Disables calling the PosixSignalHandler for the specified signal. + */ +PALEXPORT void SystemNative_DisablePosixSignalHandling(int signalCode); + +/** + * Performs the default runtime action for a non-canceled PosixSignal. + */ +PALEXPORT int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t handlersDisposed); + +typedef void (*ConsoleSigTtouHandler)(void); + +/** + * Hooks up callback to be called from the signal handler directly on SIGTTOU. + */ +void InstallTTOUHandlerForConsole(ConsoleSigTtouHandler handler); + +/** + * Uninstalls the SIGTTOU handler. + */ +void UninstallTTOUHandlerForConsole(void); + #ifndef HAS_CONSOLE_SIGNALS /** diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index b4b5eb0bcb2..87a0fc8b164 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -196,10 +196,6 @@ Link="Common\Interop\Unix\Interop.GetEUid.cs" /> - - - - - diff --git a/src/libraries/System.Console/src/System/Console.cs b/src/libraries/System.Console/src/System/Console.cs index 6bbcb9f0dea..097e4ac9b80 100644 --- a/src/libraries/System.Console/src/System/Console.cs +++ b/src/libraries/System.Console/src/System/Console.cs @@ -960,7 +960,7 @@ namespace System Out.Write(value); } - internal static bool HandleBreakEvent(ConsoleSpecialKey controlKey) + internal static bool HandleBreakEvent(ConsoleSpecialKey controlKey, bool cancel = false) { ConsoleCancelEventHandler? handler = s_cancelCallbacks; if (handler == null) @@ -969,6 +969,7 @@ namespace System } var args = new ConsoleCancelEventArgs(controlKey); + args.Cancel = cancel; handler(null, args); return args.Cancel; } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index d581ae795ce..c438413c801 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -1444,50 +1444,32 @@ namespace System internal sealed class ControlCHandlerRegistrar { - private bool _handlerRegistered; + private PosixSignalRegistration? _sigIntRegistration; + private PosixSignalRegistration? _sigQuitRegistration; internal unsafe void Register() { - Debug.Assert(s_initialized); // by CancelKeyPress add. + Debug.Assert(_sigIntRegistration is null); - Debug.Assert(!_handlerRegistered); - Interop.Sys.RegisterForCtrl(&OnBreakEvent); - _handlerRegistered = true; + _sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, HandlePosixSignal); + _sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandlePosixSignal); } internal void Unregister() { - Debug.Assert(_handlerRegistered); - _handlerRegistered = false; - Interop.Sys.UnregisterForCtrl(); + Debug.Assert(_sigIntRegistration is not null); + + _sigIntRegistration?.Dispose(); + _sigQuitRegistration?.Dispose(); } - [UnmanagedCallersOnly] - private static void OnBreakEvent(Interop.Sys.CtrlCode ctrlCode) + private static void HandlePosixSignal(PosixSignalContext ctx) { - // This is called on the native signal handling thread. We need to move to another thread so - // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends - // on work triggered from the signal handling thread. - // We use a new thread rather than queueing to the ThreadPool in order to prioritize handling - // in case the ThreadPool is saturated. - Thread handlerThread = new Thread(HandleBreakEvent) - { - IsBackground = true, - Name = ".NET Console Break" - }; - handlerThread.Start(ctrlCode); - } + Debug.Assert(ctx.Signal == PosixSignal.SIGINT || ctx.Signal == PosixSignal.SIGQUIT); - private static void HandleBreakEvent(object? state) - { - Debug.Assert(state != null); - var ctrlCode = (Interop.Sys.CtrlCode)state; - ConsoleSpecialKey controlKey = (ctrlCode == Interop.Sys.CtrlCode.Break ? ConsoleSpecialKey.ControlBreak : ConsoleSpecialKey.ControlC); - bool cancel = Console.HandleBreakEvent(controlKey); - if (!cancel) - { - Interop.Sys.RestoreAndHandleCtrl(ctrlCode); - } + ConsoleSpecialKey controlKey = ctx.Signal == PosixSignal.SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; + bool cancel = Console.HandleBreakEvent(controlKey, ctx.Cancel); + ctx.Cancel = cancel; } } diff --git a/src/libraries/System.Console/tests/ManualTests/ManualTests.cs b/src/libraries/System.Console/tests/ManualTests/ManualTests.cs index cf0ea897254..6c4b0195fc5 100644 --- a/src/libraries/System.Console/tests/ManualTests/ManualTests.cs +++ b/src/libraries/System.Console/tests/ManualTests/ManualTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using System.IO; using Xunit; @@ -243,6 +244,33 @@ namespace System AssertUserExpectedResults("the arrow keys move around the screen as expected with no other bad artifacts"); } + [ConditionalFact(nameof(ManualTestsEnabled))] + [PlatformSpecific(TestPlatforms.AnyUnix)] // .NET echo handling is Unix specific. + public static void EchoWorksDuringAndAfterProcessThatUsesTerminal() + { + Console.WriteLine($"Please type \"test\" without the quotes and press Enter."); + string line = Console.ReadLine(); + Assert.Equal("test", line); + AssertUserExpectedResults("the characters you typed properly echoed as you typed"); + + Console.WriteLine($"Now type \"test\" without the quotes and press Ctrl+D twice."); + using Process p = Process.Start(new ProcessStartInfo + { + FileName = "cat", + RedirectStandardOutput = true, + }); + string stdout = p.StandardOutput.ReadToEnd(); + p.WaitForExit(); + Assert.Equal("test", stdout); + Console.WriteLine(); + AssertUserExpectedResults("the characters you typed properly echoed as you typed"); + + Console.WriteLine($"Please type \"test\" without the quotes and press Enter."); + line = Console.ReadLine(); + Assert.Equal("test", line); + AssertUserExpectedResults("the characters you typed properly echoed as you typed"); + } + [ConditionalFact(nameof(ManualTestsEnabled))] public static void EncodingTest() { diff --git a/src/libraries/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj b/src/libraries/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj index e1daf6a964e..ddd698d0d02 100644 --- a/src/libraries/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj +++ b/src/libraries/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj @@ -6,4 +6,7 @@ + + + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index cdd65c22c44..424a7a87b31 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -257,6 +257,8 @@ Link="Common\Interop\Unix\Interop.ReadLink.cs" /> + - + + + + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.Unix.cs new file mode 100644 index 00000000000..b51957fb881 --- /dev/null +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.Unix.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Runtime.InteropServices; + +namespace System.Diagnostics +{ + public partial class Process + { + private static int s_childrenUsingTerminalCount; + + internal static void ConfigureTerminalForChildProcesses(int increment, bool configureConsole = true) + { + Debug.Assert(increment != 0); + + int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); + if (increment > 0) + { + Debug.Assert(s_processStartLock.IsReadLockHeld); + Debug.Assert(configureConsole); + + // At least one child is using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); + } + else + { + Debug.Assert(s_processStartLock.IsWriteLockHeld); + + if (childrenUsingTerminalRemaining == 0 && configureConsole) + { + // No more children are using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); + } + } + } + + private static unsafe void SetDelayedSigChildConsoleConfigurationHandler() + { + Interop.Sys.SetDelayedSigChildConsoleConfigurationHandler(&DelayedSigChildConsoleConfiguration); + } + + [UnmanagedCallersOnly] + private static void DelayedSigChildConsoleConfiguration() + { + // Lock to avoid races with Process.Start + s_processStartLock.EnterWriteLock(); + try + { + if (s_childrenUsingTerminalCount == 0) + { + // No more children are using the terminal. + Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); + } + } + finally + { + s_processStartLock.ExitWriteLock(); + } + } + + private static bool AreChildrenUsingTerminal => s_childrenUsingTerminalCount > 0; + } +} diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.iOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.iOS.cs new file mode 100644 index 00000000000..3d9f85f7a43 --- /dev/null +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.ConfigureTerminalForChildProcesses.iOS.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Diagnostics +{ + public partial class Process + { + /// These methods are used on other Unix systems to track how many children use the terminal, + /// and update the terminal configuration when necessary. + + internal static void ConfigureTerminalForChildProcesses(int increment, bool configureConsole = true) + { } + + private static unsafe void SetDelayedSigChildConsoleConfigurationHandler() + { } + + private static bool AreChildrenUsingTerminal => false; + } +} diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs deleted file mode 100644 index 6f84319b0c0..00000000000 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.ConfigureTerminalForChildProcesses.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Threading; - -namespace System.Diagnostics -{ - public partial class Process - { - private static int s_childrenUsingTerminalCount; - - static partial void ConfigureTerminalForChildProcessesInner(int increment) - { - Debug.Assert(increment != 0); - - int childrenUsingTerminalRemaining = Interlocked.Add(ref s_childrenUsingTerminalCount, increment); - if (increment > 0) - { - Debug.Assert(s_processStartLock.IsReadLockHeld); - - // At least one child is using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: true); - } - else - { - Debug.Assert(s_processStartLock.IsWriteLockHeld); - - if (childrenUsingTerminalRemaining == 0) - { - // No more children are using the terminal. - Interop.Sys.ConfigureTerminalForChildProcess(childUsesTerminal: false); - } - } - } - } -} diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 1b52e4d141e..cce32e37601 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -1023,6 +1023,7 @@ namespace System.Diagnostics // Register our callback. Interop.Sys.RegisterForSigChld(&OnSigChild); + SetDelayedSigChildConsoleConfigurationHandler(); s_initialized = true; } @@ -1030,29 +1031,29 @@ namespace System.Diagnostics } [UnmanagedCallersOnly] - private static void OnSigChild(int reapAll) + private static int OnSigChild(int reapAll, int configureConsole) { + // configureConsole is non zero when there are PosixSignalRegistrations that + // may Cancel the terminal configuration that happens when there are no more + // children using the terminal. + // When the registrations don't cancel the terminal configuration, + // DelayedSigChildConsoleConfiguration will be called. + // Lock to avoid races with Process.Start s_processStartLock.EnterWriteLock(); try { - ProcessWaitState.CheckChildren(reapAll != 0); + bool childrenUsingTerminalPre = AreChildrenUsingTerminal; + ProcessWaitState.CheckChildren(reapAll != 0, configureConsole != 0); + bool childrenUsingTerminalPost = AreChildrenUsingTerminal; + + // return whether console configuration was skipped. + return childrenUsingTerminalPre && !childrenUsingTerminalPost && configureConsole == 0 ? 1 : 0; } finally { s_processStartLock.ExitWriteLock(); } } - - /// - /// This method is called when the number of child processes that are using the terminal changes. - /// It updates the terminal configuration if necessary. - /// - internal static void ConfigureTerminalForChildProcesses(int increment) - { - ConfigureTerminalForChildProcessesInner(increment); - } - - static partial void ConfigureTerminalForChildProcessesInner(int increment); } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs index 03e93a725c4..482a627918f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs @@ -552,7 +552,7 @@ namespace System.Diagnostics }, cancellationToken); } - private bool TryReapChild() + private bool TryReapChild(bool configureConsole) { lock (_gate) { @@ -572,7 +572,7 @@ namespace System.Diagnostics if (_usesTerminal) { // Update terminal settings before calling SetExited. - Process.ConfigureTerminalForChildProcesses(-1); + Process.ConfigureTerminalForChildProcesses(-1, configureConsole); } SetExited(); @@ -593,7 +593,7 @@ namespace System.Diagnostics } } - internal static void CheckChildren(bool reapAll) + internal static void CheckChildren(bool reapAll, bool configureConsole) { // This is called on SIGCHLD from a native thread. // A lock in Process ensures no new processes are spawned while we are checking. @@ -612,7 +612,7 @@ namespace System.Diagnostics if (s_childProcessWaitStates.TryGetValue(pid, out ProcessWaitState? pws)) { // Known Process. - if (pws.TryReapChild()) + if (pws.TryReapChild(configureConsole)) { pws.ReleaseRef(); } @@ -645,7 +645,7 @@ namespace System.Diagnostics foreach (KeyValuePair kv in s_childProcessWaitStates) { ProcessWaitState pws = kv.Value; - if (pws.TryReapChild()) + if (pws.TryReapChild(configureConsole)) { if (firstToRemove == null) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 02e739ad731..8be17302389 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -796,6 +796,32 @@ namespace System.Runtime.InteropServices { public OptionalAttribute() { } } + public enum PosixSignal + { + SIGTSTP = -10, + SIGTTOU = -9, + SIGTTIN = -8, + SIGWINCH = -7, + SIGCONT = -6, + SIGCHLD = -5, + SIGTERM = -4, + SIGQUIT = -3, + SIGINT = -2, + SIGHUP = -1, + } + public sealed partial class PosixSignalContext + { + public PosixSignalContext(System.Runtime.InteropServices.PosixSignal signal) { } + public bool Cancel { get { throw null; } set { } } + public System.Runtime.InteropServices.PosixSignal Signal { get { throw null; } } + } + public sealed partial class PosixSignalRegistration : System.IDisposable + { + internal PosixSignalRegistration() { } + public static System.Runtime.InteropServices.PosixSignalRegistration Create(System.Runtime.InteropServices.PosixSignal signal, System.Action handler) { throw null; } + public void Dispose() { } + ~PosixSignalRegistration() { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=false)] public sealed partial class PreserveSigAttribute : System.Attribute { diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index b65b6b5d5b1..7834b952cc9 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -75,4 +75,40 @@ Event invocation for COM objects requires event to be attributed with DispIdAttribute. + + The file '{0}' already exists. + + + Unable to find the specified file. + + + Could not find file '{0}'. + + + Could not find a part of the path. + + + Could not find a part of the path '{0}'. + + + The specified file name or path is too long, or a component of the specified path is too long. + + + The process cannot access the file '{0}' because it is being used by another process. + + + The process cannot access the file because it is being used by another process. + + + The path '{0}' is too long, or a component of the specified path is too long. + + + Access to the path '{0}' is denied. + + + Access to the path is denied. + + + Specified file length was too large for the file system. + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index 0565ac47a77..c6ba2fbe496 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -2,7 +2,7 @@ true enable - $(NetCoreAppCurrent) + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser @@ -33,6 +33,8 @@ + + @@ -48,6 +50,25 @@ + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs new file mode 100644 index 00000000000..f1cb32a362b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public enum PosixSignal + { + SIGHUP = -1, + SIGINT = -2, + SIGQUIT = -3, + SIGTERM = -4, + SIGCHLD = -5, + SIGCONT = -6, + SIGWINCH = -7, + SIGTTIN = -8, + SIGTTOU = -9, + SIGTSTP = -10 + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs new file mode 100644 index 00000000000..19ba4f96868 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// + /// Provides data for a event. + /// + public sealed class PosixSignalContext + { + /// + /// Initializes a new instance of the class. + /// + public PosixSignalContext(PosixSignal signal) + { + Signal = signal; + } + + /// + /// Gets the signal that occurred. + /// + public PosixSignal Signal + { + get; + internal set; + } + + /// + /// Gets or sets a value that indicates whether to cancel the default handling of the signal. The default is . + /// + public bool Cancel + { + get; + set; + } + + internal PosixSignalContext() + { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs new file mode 100644 index 00000000000..7c88a92cae9 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.InteropServices +{ + public sealed class PosixSignalRegistration : IDisposable + { + private PosixSignalRegistration() { } + + public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + => throw new PlatformNotSupportedException(); + + public void Dispose() + => throw new PlatformNotSupportedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs new file mode 100644 index 00000000000..9a3144f5e81 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs @@ -0,0 +1,292 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading; + +namespace System.Runtime.InteropServices +{ + /// + /// Handles a . + /// + public sealed class PosixSignalRegistration : IDisposable + { + private static volatile bool s_initialized; + private static readonly Dictionary?>> s_registrations = new(); + + private readonly Action _handler; + private readonly PosixSignal _signal; + private readonly int _signo; + private bool _registered; + private readonly object _gate = new object(); + + private PosixSignalRegistration(PosixSignal signal, int signo, Action handler) + { + _signal = signal; + _signo = signo; + _handler = handler; + } + + /// + /// Registers a that is invoked when the occurs. + /// + /// The signal to register for. + /// The handler that gets invoked. + /// A instance that can be disposed to unregister. + /// is . + /// is out the range of expected values for the platform. + /// An error occurred while setting up the signal handling or while installing the handler for the specified signal. + /// + /// Raw values can be provided for by casting them to . + /// + /// Default handling of the signal can be canceled through . + /// For SIGTERM, SIGINT, and SIGQUIT process termination can be canceled. + /// For SIGCHLD, and SIGCONT terminal configuration can be canceled. + /// + public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler == null) + { + throw new ArgumentNullException(nameof(handler)); + } + int signo = Interop.Sys.GetPlatformSignalNumber(signal); + if (signo == 0) + { + throw new ArgumentOutOfRangeException(nameof(signal)); + } + PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler); + registration.Register(); + return registration; + } + + /// + /// Unregister the handler. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private unsafe void Register() + { + if (!s_initialized) + { + if (!Interop.Sys.InitializeTerminalAndSignalHandling()) + { + // We can't use Win32Exception because that causes a cycle with + // Microsoft.Win32.Primitives. + Interop.CheckIo(-1); + } + + Interop.Sys.SetPosixSignalHandler(&OnPosixSignal); + s_initialized = true; + } + lock (s_registrations) + { + if (!s_registrations.TryGetValue(_signo, out List?>? signalRegistrations)) + { + signalRegistrations = new List?>(); + s_registrations.Add(_signo, signalRegistrations); + } + + if (signalRegistrations.Count == 0) + { + if (!Interop.Sys.EnablePosixSignalHandling(_signo)) + { + // We can't use Win32Exception because that causes a cycle with + // Microsoft.Win32.Primitives. + Interop.CheckIo(-1); + } + } + + signalRegistrations.Add(new WeakReference(this)); + } + _registered = true; + } + + private bool CallHandler(PosixSignalContext context) + { + lock (_gate) + { + if (_registered) + { + _handler(context); + return true; + } + return false; + } + } + + [UnmanagedCallersOnly] + private static int OnPosixSignal(int signo, PosixSignal signal) + { + PosixSignalRegistration?[]? registrations = GetRegistrations(signo); + if (registrations != null) + { + // This is called on the native signal handling thread. We need to move to another thread so + // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends + // on work triggered from the signal handling thread. + + // For terminate/interrupt signals we use a dedicated Thread + // in case the ThreadPool is saturated. + bool useDedicatedThread = signal == PosixSignal.SIGINT || + signal == PosixSignal.SIGQUIT || + signal == PosixSignal.SIGTERM; + if (useDedicatedThread) + { + Thread handlerThread = new Thread(HandleSignal) + { + IsBackground = true, + Name = ".NET Signal Handler" + }; + handlerThread.UnsafeStart((signo, registrations)); + } + else + { + ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations)); + } + return 1; + } + return 0; + } + + private static PosixSignalRegistration?[]? GetRegistrations(int signo) + { + lock (s_registrations) + { + if (s_registrations.TryGetValue(signo, out List?>? signalRegistrations)) + { + if (signalRegistrations.Count != 0) + { + var registrations = new PosixSignalRegistration?[signalRegistrations.Count]; + bool hasRegistrations = false; + bool pruneWeakReferences = false; + for (int i = 0; i < signalRegistrations.Count; i++) + { + if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) + { + registrations[i] = registration; + hasRegistrations = true; + } + else + { + // WeakReference no longer holds an object. PosixSignalRegistration got finalized. + signalRegistrations[i] = null; + pruneWeakReferences = true; + } + } + + if (pruneWeakReferences) + { + signalRegistrations.RemoveAll(item => item is null); + } + + if (hasRegistrations) + { + return registrations; + } + else + { + Interop.Sys.DisablePosixSignalHandling(signo); + } + } + } + return null; + } + } + + private static void HandleSignal(object? state) + { + HandleSignal(((int, PosixSignalRegistration?[]))state!); + } + + private static void HandleSignal((int signo, PosixSignalRegistration?[]? registrations) state) + { + do + { + bool handlersCalled = false; + if (state.registrations != null) + { + PosixSignalContext ctx = new(); + foreach (PosixSignalRegistration? registration in state.registrations) + { + if (registration != null) + { + // Different values for PosixSignal map to the same signo. + // Match the PosixSignal value used when registering. + ctx.Signal = registration._signal; + if (registration.CallHandler(ctx)) + { + handlersCalled = true; + } + } + } + + if (ctx.Cancel) + { + return; + } + } + + if (Interop.Sys.HandleNonCanceledPosixSignal(state.signo, handlersCalled ? 0 : 1)) + { + return; + } + + // HandleNonCanceledPosixSignal returns false when handlers got registered. + state.registrations = GetRegistrations(state.signo); + } while (true); + } + + ~PosixSignalRegistration() + => Dispose(false); + + private void Dispose(bool disposing) + { + if (_registered) + { + lock (s_registrations) + { + List?> signalRegistrations = s_registrations[_signo]; + bool pruneWeakReferences = false; + for (int i = 0; i < signalRegistrations.Count; i++) + { + if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) + { + if (object.ReferenceEquals(this, registration)) + { + signalRegistrations.RemoveAt(i); + break; + } + } + else + { + // WeakReference no longer holds an object. PosixSignalRegistration got finalized. + signalRegistrations[i] = null; + pruneWeakReferences = true; + } + } + + if (pruneWeakReferences) + { + signalRegistrations.RemoveAll(item => item is null); + } + + if (signalRegistrations.Count == 0) + { + Interop.Sys.DisablePosixSignalHandling(_signo); + } + } + + // Synchronize with _handler invocations. + lock (_gate) + { + _registered = false; + } + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs new file mode 100644 index 00000000000..7c88a92cae9 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.InteropServices +{ + public sealed class PosixSignalRegistration : IDisposable + { + private PosixSignalRegistration() { } + + public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + => throw new PlatformNotSupportedException(); + + public void Dispose() + => throw new PlatformNotSupportedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 9ed8c6ff8a7..4bcb75b0b81 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -1,7 +1,7 @@ true - $(NetCoreAppCurrent);$(NetCoreAppCurrent)-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser true true @@ -150,6 +150,7 @@ + @@ -183,4 +184,9 @@ + + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs new file mode 100644 index 00000000000..3d8ca635ac2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using System.Runtime.InteropServices; + +namespace System.Tests +{ + public class PosixSignalContextTests + { + [Theory] + [InlineData(0)] + [InlineData(3)] + [InlineData(-1000)] + [InlineData(1000)] + public void Constructor(int value) + { + var ctx = new PosixSignalContext((PosixSignal)value); + Assert.Equal(value, (int)ctx.Signal); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs new file mode 100644 index 00000000000..b006af50150 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -0,0 +1,236 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Microsoft.DotNet.RemoteExecutor; + +namespace System.Tests +{ + public class PosixSignalRegistrationTests + { + private static TimeSpan Timeout => TimeSpan.FromSeconds(30); + + [Fact] + public void HandlerNullThrows() + { + Assert.Throws(() => PosixSignalRegistration.Create(PosixSignal.SIGCONT, null)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1000)] + [InlineData(1000)] + public void InvalidSignalValueThrows(int value) + { + Assert.Throws(() => PosixSignalRegistration.Create((PosixSignal)value, ctx => { })); + } + + [Theory] + [InlineData((PosixSignal)9)] // SIGKILL + public void UninstallableSignalsThrow(PosixSignal signal) + { + Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + } + + [Theory] + [MemberData(nameof(PosixSignalValues))] + public void CanRegisterForKnownValues(PosixSignal signal) + { + using var _ = PosixSignalRegistration.Create(signal, ctx => { }); + } + + [Theory] + [MemberData(nameof(PosixSignalValues))] + public void SignalHandlerCalledForKnownSignals(PosixSignal s) + { + RemoteExecutor.Invoke((signalStr) => { + PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); + using var _ = PosixSignalRegistration.Create(signal, ctx => + { + Assert.Equal(signal, ctx.Signal); + + // Ensure signal doesn't cause the process to terminate. + ctx.Cancel = true; + + semaphore.Release(); + }); + kill(signal); + bool entered = semaphore.Wait(Timeout); + Assert.True(entered); + }, s.ToString()).Dispose(); + } + + [Theory] + [MemberData(nameof(PosixSignalAsRawValues))] + public void SignalHandlerCalledForRawSignals(PosixSignal s) + { + RemoteExecutor.Invoke((signalStr) => { + PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); + using var _ = PosixSignalRegistration.Create(signal, ctx => + { + Assert.Equal(signal, ctx.Signal); + + // Ensure signal doesn't cause the process to terminate. + ctx.Cancel = true; + + semaphore.Release(); + }); + kill(signal); + bool entered = semaphore.Wait(Timeout); + Assert.True(entered); + }, s.ToString()).Dispose(); + } + + [Fact] + public void SignalHandlerWorksForSecondRegistration() + { + PosixSignal signal = PosixSignal.SIGCONT; + + for (int i = 0; i < 2; i++) + { + using SemaphoreSlim semaphore = new(0); + using var _ = PosixSignalRegistration.Create(signal, ctx => + { + Assert.Equal(signal, ctx.Signal); + + // Ensure signal doesn't cause the process to terminate. + ctx.Cancel = true; + + semaphore.Release(); + }); + kill(signal); + bool entered = semaphore.Wait(Timeout); + Assert.True(entered); + } + } + + [Fact] + public void SignalHandlerNotCalledWhenDisposed() + { + PosixSignal signal = PosixSignal.SIGCONT; + + using var registration = PosixSignalRegistration.Create(signal, ctx => + { + Assert.False(true, "Signal handler was called."); + }); + registration.Dispose(); + + kill(signal); + Thread.Sleep(100); + } + + [Fact] + public void SignalHandlerNotCalledWhenFinalized() + { + PosixSignal signal = PosixSignal.SIGCONT; + + CreateDanglingRegistration(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + + kill(signal); + Thread.Sleep(100); + + [MethodImpl(MethodImplOptions.NoInlining)] + void CreateDanglingRegistration() + { + PosixSignalRegistration.Create(signal, ctx => + { + Assert.False(true, "Signal handler was called."); + }); + } + } + + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(PosixSignal.SIGINT, true, 0)] + [InlineData(PosixSignal.SIGINT, false, 130)] + [InlineData(PosixSignal.SIGTERM, true, 0)] + [InlineData(PosixSignal.SIGTERM, false, 143)] + [InlineData(PosixSignal.SIGQUIT, true, 0)] + [InlineData(PosixSignal.SIGQUIT, false, 131)] + public void SignalCanCancelTermination(PosixSignal signal, bool cancel, int expectedExitCode) + { + // Mono doesn't restore and call SIG_DFL on SIGQUIT. + if (PlatformDetection.IsMonoRuntime && signal == PosixSignal.SIGQUIT && cancel == false) + { + expectedExitCode = 0; + } + + RemoteExecutor.Invoke((signalStr, cancelStr, expectedStr) => + { + PosixSignal signalArg = Enum.Parse(signalStr); + bool cancelArg = bool.Parse(cancelStr); + int expected = int.Parse(expectedStr); + + using SemaphoreSlim semaphore = new(0); + using var _ = PosixSignalRegistration.Create(signalArg, ctx => + { + ctx.Cancel = cancelArg; + + semaphore.Release(); + }); + + kill(signalArg); + + bool entered = semaphore.Wait(Timeout); + Assert.True(entered); + + // Give the default signal handler a chance to run. + Thread.Sleep(expected == 0 ? TimeSpan.FromSeconds(2) : TimeSpan.FromMinutes(10)); + + return 0; + }, signal.ToString(), cancel.ToString(), expectedExitCode.ToString(), + new RemoteInvokeOptions() { ExpectedExitCode = expectedExitCode, TimeOut = 10 * 60 * 1000 }).Dispose(); + } + + public static TheoryData PosixSignalValues + { + get + { + var data = new TheoryData(); + foreach (var value in Enum.GetValues(typeof(PosixSignal))) + { + data.Add((PosixSignal)value); + } + return data; + } + } + + public static TheoryData PosixSignalAsRawValues + { + get + { + var data = new TheoryData(); + foreach (var value in Enum.GetValues(typeof(PosixSignal))) + { + int signo = GetPlatformSignalNumber((PosixSignal)value); + Assert.True(signo > 0, "Expected raw signal number to be greater than 0."); + data.Add((PosixSignal)signo); + } + return data; + } + } + + [DllImport("libc", SetLastError = true)] + private static extern int kill(int pid, int sig); + + private static void kill(PosixSignal sig) + { + int signo = GetPlatformSignalNumber(sig); + Assert.NotEqual(0, signo); + int rv = kill(Environment.ProcessId, signo); + Assert.Equal(0, rv); + } + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")] + [SuppressGCTransition] + private static extern int GetPlatformSignalNumber(PosixSignal signal); + } +} From c7ffa32ba5f2fc961df0af2d0449c305914bba04 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 8 Jul 2021 02:04:57 +0200 Subject: [PATCH 322/926] Implement dynamic HTTP/2 window scaling (#54755) Fixes #43086 by introducing automatic scaling of the HTTP/2 stream receive window based on measuring RTT of PING frames. --- .../Net/Http/Http2LoopbackConnection.cs | 66 +++- .../System/Net/Http/Http2LoopbackServer.cs | 2 + .../Net/Http/HttpClientHandlerTestBase.cs | 2 + .../System.Net.Http/ref/System.Net.Http.cs | 1 + .../src/Resources/Strings.resx | 3 + .../src/System.Net.Http.csproj | 3 + .../BrowserHttpHandler/SocketsHttpHandler.cs | 7 +- .../src/System/Net/Http/DiagnosticsHandler.cs | 43 +-- .../src/System/Net/Http/GlobalHttpSettings.cs | 81 +++++ .../src/System/Net/Http/HttpClientHandler.cs | 2 +- .../System/Net/Http/HttpHandlerDefaults.cs | 6 + .../SocketsHttpHandler/Http2Connection.cs | 71 +++- .../Http/SocketsHttpHandler/Http2Stream.cs | 52 +-- .../Http2StreamWindowManager.cs | 271 +++++++++++++++ .../HttpConnectionSettings.cs | 69 +--- .../RuntimeSettingParser.cs | 65 ++++ .../SocketsHttpHandler/SocketsHttpHandler.cs | 26 ++ .../HttpClientHandlerTest.Http2.cs | 221 ++++++------ .../tests/FunctionalTests/HttpClientTest.cs | 1 - ...SocketsHttpHandlerTest.Http2FlowControl.cs | 320 ++++++++++++++++++ .../FunctionalTests/SocketsHttpHandlerTest.cs | 27 ++ .../System.Net.Http.Functional.Tests.csproj | 1 + .../UnitTests/RuntimeSettingParserTest.cs | 153 +++++++++ .../System.Net.Http.Unit.Tests.csproj | 5 +- 24 files changed, 1226 insertions(+), 272 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2StreamWindowManager.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs create mode 100644 src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs create mode 100644 src/libraries/System.Net.Http/tests/UnitTests/RuntimeSettingParserTest.cs diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs index 97787e65849..4840ad7ab44 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs @@ -25,8 +25,10 @@ namespace System.Net.Test.Common private TaskCompletionSource _ignoredSettingsAckPromise; private bool _ignoreWindowUpdates; private TaskCompletionSource _expectPingFrame; + private bool _transparentPingResponse; private readonly TimeSpan _timeout; private int _lastStreamId; + private bool _expectClientDisconnect; private readonly byte[] _prefix = new byte[24]; public string PrefixString => Encoding.UTF8.GetString(_prefix, 0, _prefix.Length); @@ -34,11 +36,12 @@ namespace System.Net.Test.Common public Stream Stream => _connectionStream; public Task SettingAckWaiter => _ignoredSettingsAckPromise?.Task; - private Http2LoopbackConnection(SocketWrapper socket, Stream stream, TimeSpan timeout) + private Http2LoopbackConnection(SocketWrapper socket, Stream stream, TimeSpan timeout, bool transparentPingResponse) { _connectionSocket = socket; _connectionStream = stream; _timeout = timeout; + _transparentPingResponse = transparentPingResponse; } public static Task CreateAsync(SocketWrapper socket, Stream stream, Http2Options httpOptions) @@ -76,7 +79,7 @@ namespace System.Net.Test.Common stream = sslStream; } - var con = new Http2LoopbackConnection(socket, stream, timeout); + var con = new Http2LoopbackConnection(socket, stream, timeout, httpOptions.EnableTransparentPingResponse); await con.ReadPrefixAsync().ConfigureAwait(false); return con; @@ -121,11 +124,11 @@ namespace System.Net.Test.Common clientSettings = await ReadFrameAsync(_timeout).ConfigureAwait(false); } - public async Task WriteFrameAsync(Frame frame) + public async Task WriteFrameAsync(Frame frame, CancellationToken cancellationToken = default) { byte[] writeBuffer = new byte[Frame.FrameHeaderLength + frame.Length]; frame.WriteTo(writeBuffer); - await _connectionStream.WriteAsync(writeBuffer, 0, writeBuffer.Length).ConfigureAwait(false); + await _connectionStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false); } // Read until the buffer is full @@ -159,7 +162,7 @@ namespace System.Net.Test.Common return await ReadFrameAsync(timeoutCts.Token).ConfigureAwait(false); } - private async Task ReadFrameAsync(CancellationToken cancellationToken) + public async Task ReadFrameAsync(CancellationToken cancellationToken) { // First read the frame headers, which should tell us how long the rest of the frame is. byte[] headerBytes = new byte[Frame.FrameHeaderLength]; @@ -198,11 +201,12 @@ namespace System.Net.Test.Common return await ReadFrameAsync(cancellationToken).ConfigureAwait(false); } - if (_expectPingFrame != null && header.Type == FrameType.Ping) + if (header.Type == FrameType.Ping && (_expectPingFrame != null || _transparentPingResponse)) { - _expectPingFrame.SetResult(PingFrame.ReadFrom(header, data)); - _expectPingFrame = null; - return await ReadFrameAsync(cancellationToken).ConfigureAwait(false); + PingFrame pingFrame = PingFrame.ReadFrom(header, data); + + bool processed = await TryProcessExpectedPingFrameAsync(pingFrame); + return processed ? await ReadFrameAsync(cancellationToken).ConfigureAwait(false) : pingFrame; } // Construct the correct frame type and return it. @@ -224,11 +228,37 @@ namespace System.Net.Test.Common return GoAwayFrame.ReadFrom(header, data); case FrameType.Continuation: return ContinuationFrame.ReadFrom(header, data); + case FrameType.WindowUpdate: + return WindowUpdateFrame.ReadFrom(header, data); default: return header; } } + private async Task TryProcessExpectedPingFrameAsync(PingFrame pingFrame) + { + if (_expectPingFrame != null) + { + _expectPingFrame.SetResult(pingFrame); + _expectPingFrame = null; + return true; + } + else if (_transparentPingResponse && !pingFrame.AckFlag) + { + try + { + await SendPingAckAsync(pingFrame.Data); + } + catch (IOException ex) when (_expectClientDisconnect && ex.InnerException is SocketException se && se.SocketErrorCode == SocketError.Shutdown) + { + // couldn't send PING ACK, because client is already disconnected + _transparentPingResponse = false; + } + return true; + } + return false; + } + // Reset and return underlying networking objects. public (SocketWrapper, Stream) ResetNetwork() { @@ -263,11 +293,18 @@ namespace System.Net.Test.Common _ignoreWindowUpdates = true; } - // Set up loopback server to expect PING frames among other frames. + // Set up loopback server to expect a PING frame among other frames. // Once PING frame is read in ReadFrameAsync, the returned task is completed. // The returned task is canceled in ReadPingAsync if no PING frame has been read so far. + // Does not work when Http2Options.EnableTransparentPingResponse == true public Task ExpectPingFrameAsync() { + if (_transparentPingResponse) + { + throw new InvalidOperationException( + $"{nameof(Http2LoopbackConnection)}.{nameof(ExpectPingFrameAsync)} can not be used when transparent PING response is enabled."); + } + _expectPingFrame ??= new TaskCompletionSource(); return _expectPingFrame.Task; } @@ -297,6 +334,7 @@ namespace System.Net.Test.Common { IgnoreWindowUpdates(); + _expectClientDisconnect = true; Frame frame = await ReadFrameAsync(_timeout).ConfigureAwait(false); if (frame != null) { @@ -720,14 +758,18 @@ namespace System.Net.Test.Common PingFrame ping = new PingFrame(pingData, FrameFlags.None, 0); await WriteFrameAsync(ping).ConfigureAwait(false); PingFrame pingAck = (PingFrame)await ReadFrameAsync(_timeout).ConfigureAwait(false); + if (pingAck == null || pingAck.Type != FrameType.Ping || !pingAck.AckFlag) { - throw new Exception("Expected PING ACK"); + string faultDetails = pingAck == null ? "" : $" frame.Type:{pingAck.Type} frame.AckFlag: {pingAck.AckFlag}"; + throw new Exception("Expected PING ACK" + faultDetails); } Assert.Equal(pingData, pingAck.Data); } + public Task ReadPingAsync() => ReadPingAsync(_timeout); + public async Task ReadPingAsync(TimeSpan timeout) { _expectPingFrame?.TrySetCanceled(); @@ -743,7 +785,7 @@ namespace System.Net.Test.Common return Assert.IsAssignableFrom(frame); } - public async Task SendPingAckAsync(long payload) + public async Task SendPingAckAsync(long payload, CancellationToken cancellationToken = default) { PingFrame pingAck = new PingFrame(payload, FrameFlags.Ack, 0); await WriteFrameAsync(pingAck).ConfigureAwait(false); diff --git a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs index 45df14c0380..edbefefb6ac 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs @@ -179,6 +179,8 @@ namespace System.Net.Test.Common { public bool ClientCertificateRequired { get; set; } + public bool EnableTransparentPingResponse { get; set; } = true; + public Http2Options() { SslProtocols = SslProtocols.Tls12; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs index 26a084b969f..7d56c4ce488 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs @@ -50,6 +50,8 @@ namespace System.Net.Http.Functional.Tests #endif }; + public const int DefaultInitialWindowSize = 65535; + public static readonly bool[] BoolValues = new[] { true, false }; // For use by remote server tests diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index ea669da03d3..875a4714189 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -353,6 +353,7 @@ namespace System.Net.Http public sealed partial class SocketsHttpHandler : System.Net.Http.HttpMessageHandler { public SocketsHttpHandler() { } + public int InitialHttp2StreamWindowSize { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute("browser")] public static bool IsSupported { get { throw null; } } public bool AllowAutoRedirect { get { throw null; } set { } } diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 0f725d661b2..35ce6f88feb 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -429,6 +429,9 @@ An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. + + The initial HTTP/2 stream window size must be between {0} and {1}. + This method is not implemented by this class. diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 0fee14684e2..613416b90aa 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -62,6 +62,7 @@ + @@ -160,6 +161,7 @@ + @@ -495,6 +497,7 @@ + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 4a29ff1ea2b..4805010002b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -135,6 +135,12 @@ namespace System.Net.Http set => throw new PlatformNotSupportedException(); } + public int InitialHttp2StreamWindowSize + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + public TimeSpan KeepAlivePingDelay { get => throw new PlatformNotSupportedException(); @@ -147,7 +153,6 @@ namespace System.Net.Http set => throw new PlatformNotSupportedException(); } - public HttpKeepAlivePingPolicy KeepAlivePingPolicy { get => throw new PlatformNotSupportedException(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index 4e4d730c4c1..f3de5028d91 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -15,6 +15,9 @@ namespace System.Net.Http /// internal sealed class DiagnosticsHandler : DelegatingHandler { + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); + /// /// DiagnosticHandler constructor /// @@ -28,13 +31,10 @@ namespace System.Net.Http { // check if there is a parent Activity (and propagation is not suppressed) // or if someone listens to HttpHandlerDiagnosticListener - return IsGloballyEnabled() && (Activity.Current != null || Settings.s_diagnosticListener.IsEnabled()); + return IsGloballyEnabled && (Activity.Current != null || s_diagnosticListener.IsEnabled()); } - internal static bool IsGloballyEnabled() - { - return Settings.s_activityPropagationEnabled; - } + internal static bool IsGloballyEnabled => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; // SendAsyncCore returns already completed ValueTask for when async: false is passed. // Internally, it calls the synchronous Send method of the base class. @@ -59,7 +59,7 @@ namespace System.Net.Http } Activity? activity = null; - DiagnosticListener diagnosticListener = Settings.s_diagnosticListener; + DiagnosticListener diagnosticListener = s_diagnosticListener; // if there is no listener, but propagation is enabled (with previous IsEnabled() check) // do not write any events just start/stop Activity and propagate Ids @@ -269,37 +269,6 @@ namespace System.Net.Http public override string ToString() => $"{{ {nameof(Response)} = {Response}, {nameof(LoggingRequestId)} = {LoggingRequestId}, {nameof(Timestamp)} = {Timestamp}, {nameof(RequestTaskStatus)} = {RequestTaskStatus} }}"; } - private static class Settings - { - private const string EnableActivityPropagationEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION"; - private const string EnableActivityPropagationAppCtxSettingName = "System.Net.Http.EnableActivityPropagation"; - - public static readonly bool s_activityPropagationEnabled = GetEnableActivityPropagationValue(); - - private static bool GetEnableActivityPropagationValue() - { - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(EnableActivityPropagationAppCtxSettingName, out bool enableActivityPropagation)) - { - return enableActivityPropagation; - } - - // AppContext switch wasn't used. Check the environment variable to determine which handler should be used. - string? envVar = Environment.GetEnvironmentVariable(EnableActivityPropagationEnvironmentVariableSettingName); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Suppress Activity propagation. - return false; - } - - // Defaults to enabling Activity propagation. - return true; - } - - public static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); - } - private static void InjectHeaders(Activity currentActivity, HttpRequestMessage request) { if (currentActivity.IdFormat == ActivityIdFormat.W3C) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs new file mode 100644 index 00000000000..7382f4ca0da --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Net.Http +{ + /// + /// Exposes process-wide settings for handlers. + /// + internal static class GlobalHttpSettings + { + internal static class DiagnosticsHandler + { + public static bool EnableActivityPropagation { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.Http.EnableActivityPropagation", + "DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION", + true); + } + +#if !BROWSER + internal static class SocketsHttpHandler + { + // Default to allowing HTTP/2, but enable that to be overridden by an + // AppContext switch, or by an environment variable being set to false/0. + public static bool AllowHttp2 { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.Http.SocketsHttpHandler.Http2Support", + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2SUPPORT", + true); + + // Default to allowing draft HTTP/3, but enable that to be overridden + // by an AppContext switch, or by an environment variable being set to false/0. + public static bool AllowDraftHttp3 { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.SocketsHttpHandler.Http3DraftSupport", + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3DRAFTSUPPORT", + true); + + // Switch to disable the HTTP/2 dynamic window scaling algorithm. Enabled by default. + public static bool DisableDynamicHttp2WindowSizing { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.SocketsHttpHandler.Http2FlowControl.DisableDynamicWindowSizing", + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2FLOWCONTROL_DISABLEDYNAMICWINDOWSIZING", + false); + + // The maximum size of the HTTP/2 stream receive window. Defaults to 16 MB. + public static int MaxHttp2StreamWindowSize { get; } = GetMaxHttp2StreamWindowSize(); + + // Defaults to 1.0. Higher values result in shorter window, but slower downloads. + public static double Http2StreamWindowScaleThresholdMultiplier { get; } = GetHttp2StreamWindowScaleThresholdMultiplier(); + + public const int DefaultHttp2MaxStreamWindowSize = 16 * 1024 * 1024; + public const double DefaultHttp2StreamWindowScaleThresholdMultiplier = 1.0; + + private static int GetMaxHttp2StreamWindowSize() + { + int value = RuntimeSettingParser.ParseInt32EnvironmentVariableValue( + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_MAXSTREAMWINDOWSIZE", + DefaultHttp2MaxStreamWindowSize); + + // Disallow small values: + if (value < HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize) + { + value = HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize; + } + return value; + } + + private static double GetHttp2StreamWindowScaleThresholdMultiplier() + { + double value = RuntimeSettingParser.ParseDoubleEnvironmentVariableValue( + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_STREAMWINDOWSCALETHRESHOLDMULTIPLIER", + DefaultHttp2StreamWindowScaleThresholdMultiplier); + + // Disallow negative values: + if (value < 0) + { + value = DefaultHttp2StreamWindowScaleThresholdMultiplier; + } + return value; + } + } +#endif + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 2e3289643cf..89cb508d220 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -28,7 +28,7 @@ namespace System.Net.Http public HttpClientHandler() { _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled()) + if (DiagnosticsHandler.IsGloballyEnabled) { _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpHandlerDefaults.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpHandlerDefaults.cs index 21cc00a7444..9e2b938c424 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpHandlerDefaults.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpHandlerDefaults.cs @@ -13,5 +13,11 @@ namespace System.Net.Http public static readonly TimeSpan DefaultKeepAlivePingTimeout = TimeSpan.FromSeconds(20); public static readonly TimeSpan DefaultKeepAlivePingDelay = Timeout.InfiniteTimeSpan; public const HttpKeepAlivePingPolicy DefaultKeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always; + + // This is the default value for SocketsHttpHandler.InitialHttp2StreamWindowSize, + // which defines the value we communicate in stream SETTINGS frames. + // Should not be confused with Http2Connection.DefaultInitialWindowSize, which defines the RFC default. + // Unlike that value, DefaultInitialHttp2StreamWindowSize might be changed in the future. + public const int DefaultInitialHttp2StreamWindowSize = 65535; } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index c60370d6d80..61ddafcff96 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -38,10 +38,11 @@ namespace System.Net.Http private readonly CreditManager _connectionWindow; private readonly CreditManager _concurrentStreams; + private RttEstimator _rttEstimator; private int _nextStream; private bool _expectingSettingsAck; - private int _initialWindowSize; + private int _initialServerStreamWindowSize; private int _maxConcurrentStreams; private int _pendingWindowUpdate; private long _idleSinceTickCount; @@ -79,21 +80,24 @@ namespace System.Net.Http #else private const int InitialConnectionBufferSize = 4096; #endif - - private const int DefaultInitialWindowSize = 65535; + // The default initial window size for streams and connections according to the RFC: + // https://datatracker.ietf.org/doc/html/rfc7540#section-5.2.1 + // Unlike HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize, this value should never be changed. + internal const int DefaultInitialWindowSize = 65535; // We don't really care about limiting control flow at the connection level. // We limit it per stream, and the user controls how many streams are created. // So set the connection window size to a large value. private const int ConnectionWindowSize = 64 * 1024 * 1024; - // We hold off on sending WINDOW_UPDATE until we hit thi minimum threshold. + // We hold off on sending WINDOW_UPDATE until we hit the minimum threshold. // This value is somewhat arbitrary; the intent is to ensure it is much smaller than // the window size itself, or we risk stalling the server because it runs out of window space. // If we want to further reduce the frequency of WINDOW_UPDATEs, it's probably better to // increase the window size (and thus increase the threshold proportionally) // rather than just increase the threshold. - private const int ConnectionWindowThreshold = ConnectionWindowSize / 8; + private const int ConnectionWindowUpdateRatio = 8; + private const int ConnectionWindowThreshold = ConnectionWindowSize / ConnectionWindowUpdateRatio; // When buffering outgoing writes, we will automatically buffer up to this number of bytes. // Single writes that are larger than the buffer can cause the buffer to expand beyond @@ -131,10 +135,12 @@ namespace System.Net.Http _connectionWindow = new CreditManager(this, nameof(_connectionWindow), DefaultInitialWindowSize); _concurrentStreams = new CreditManager(this, nameof(_concurrentStreams), InitialMaxConcurrentStreams); + _rttEstimator = RttEstimator.Create(); + _writeChannel = Channel.CreateUnbounded(s_channelOptions); _nextStream = 1; - _initialWindowSize = DefaultInitialWindowSize; + _initialServerStreamWindowSize = DefaultInitialWindowSize; _maxConcurrentStreams = InitialMaxConcurrentStreams; _pendingWindowUpdate = 0; @@ -178,21 +184,30 @@ namespace System.Net.Http s_http2ConnectionPreface.AsSpan().CopyTo(_outgoingBuffer.AvailableSpan); _outgoingBuffer.Commit(s_http2ConnectionPreface.Length); - // Send SETTINGS frame. Disable push promise. - FrameHeader.WriteTo(_outgoingBuffer.AvailableSpan, FrameHeader.SettingLength, FrameType.Settings, FrameFlags.None, streamId: 0); + // Send SETTINGS frame. Disable push promise & set initial window size. + FrameHeader.WriteTo(_outgoingBuffer.AvailableSpan, 2 * FrameHeader.SettingLength, FrameType.Settings, FrameFlags.None, streamId: 0); _outgoingBuffer.Commit(FrameHeader.Size); BinaryPrimitives.WriteUInt16BigEndian(_outgoingBuffer.AvailableSpan, (ushort)SettingId.EnablePush); _outgoingBuffer.Commit(2); BinaryPrimitives.WriteUInt32BigEndian(_outgoingBuffer.AvailableSpan, 0); _outgoingBuffer.Commit(4); + BinaryPrimitives.WriteUInt16BigEndian(_outgoingBuffer.AvailableSpan, (ushort)SettingId.InitialWindowSize); + _outgoingBuffer.Commit(2); + BinaryPrimitives.WriteUInt32BigEndian(_outgoingBuffer.AvailableSpan, (uint)_pool.Settings._initialHttp2StreamWindowSize); + _outgoingBuffer.Commit(4); - // Send initial connection-level WINDOW_UPDATE + // The connection-level window size can not be initialized by SETTINGS frames: + // https://datatracker.ietf.org/doc/html/rfc7540#section-6.9.2 + // Send an initial connection-level WINDOW_UPDATE to setup the desired ConnectionWindowSize: + uint windowUpdateAmount = ConnectionWindowSize - DefaultInitialWindowSize; + if (NetEventSource.Log.IsEnabled()) Trace($"Initial connection-level WINDOW_UPDATE, windowUpdateAmount={windowUpdateAmount}"); FrameHeader.WriteTo(_outgoingBuffer.AvailableSpan, FrameHeader.WindowUpdateLength, FrameType.WindowUpdate, FrameFlags.None, streamId: 0); _outgoingBuffer.Commit(FrameHeader.Size); - BinaryPrimitives.WriteUInt32BigEndian(_outgoingBuffer.AvailableSpan, ConnectionWindowSize - DefaultInitialWindowSize); + BinaryPrimitives.WriteUInt32BigEndian(_outgoingBuffer.AvailableSpan, windowUpdateAmount); _outgoingBuffer.Commit(4); await _stream.WriteAsync(_outgoingBuffer.ActiveMemory).ConfigureAwait(false); + _rttEstimator.OnInitialSettingsSent(); _outgoingBuffer.Discard(_outgoingBuffer.ActiveLength); _expectingSettingsAck = true; @@ -439,6 +454,7 @@ namespace System.Net.Http if (http2Stream != null) { http2Stream.OnHeadersStart(); + _rttEstimator.OnDataOrHeadersReceived(this); headersHandler = http2Stream; } else @@ -457,6 +473,7 @@ namespace System.Net.Http while (!frameHeader.EndHeadersFlag) { frameHeader = await ReadFrameAsync().ConfigureAwait(false); + if (frameHeader.Type != FrameType.Continuation || frameHeader.StreamId != streamId) { @@ -570,6 +587,11 @@ namespace System.Net.Http bool endStream = frameHeader.EndStreamFlag; http2Stream.OnResponseData(frameData, endStream); + + if (!endStream && frameData.Length > 0) + { + _rttEstimator.OnDataOrHeadersReceived(this); + } } if (frameData.Length > 0) @@ -604,6 +626,7 @@ namespace System.Net.Http // We only send SETTINGS once initially, so we don't need to do anything in response to the ACK. // Just remember that we received one and we won't be expecting any more. _expectingSettingsAck = false; + _rttEstimator.OnInitialSettingsAckReceived(this); } else { @@ -691,8 +714,8 @@ namespace System.Net.Http lock (SyncObject) { - int delta = newSize - _initialWindowSize; - _initialWindowSize = newSize; + int delta = newSize - _initialServerStreamWindowSize; + _initialServerStreamWindowSize = newSize; // Adjust existing streams foreach (KeyValuePair kvp in _httpStreams) @@ -1395,7 +1418,7 @@ namespace System.Net.Http // assigning the stream ID to ensure only one stream gets an ID, and it must be held // across setting the initial window size (available credit) and storing the stream into // collection such that window size updates are able to atomically affect all known streams. - s.http2Stream.Initialize(s.thisRef._nextStream, s.thisRef._initialWindowSize); + s.http2Stream.Initialize(s.thisRef._nextStream, s.thisRef._initialServerStreamWindowSize); // Client-initiated streams are always odd-numbered, so increase by 2. s.thisRef._nextStream += 2; @@ -1666,6 +1689,9 @@ namespace System.Net.Http // we could hold pool lock while trying to grab connection lock in Dispose(). _pool.InvalidateHttp2Connection(this); + // There is no point sending more PING frames for RTT estimation: + _rttEstimator.OnGoAwayReceived(); + List streamsToAbort = new List(); lock (SyncObject) @@ -1975,11 +2001,20 @@ namespace System.Net.Http private void ProcessPingAck(long payload) { - if (_keepAliveState != KeepAliveState.PingSent) - ThrowProtocolError(); - if (Interlocked.Read(ref _keepAlivePingPayload) != payload) - ThrowProtocolError(); - _keepAliveState = KeepAliveState.None; + // RttEstimator is using negative values in PING payloads. + // _keepAlivePingPayload is always non-negative. + if (payload < 0) // RTT ping + { + _rttEstimator.OnPingAckReceived(payload, this); + } + else // Keepalive ping + { + if (_keepAliveState != KeepAliveState.PingSent) + ThrowProtocolError(); + if (Interlocked.Read(ref _keepAlivePingPayload) != payload) + ThrowProtocolError(); + _keepAliveState = KeepAliveState.None; + } } private void VerifyKeepAlive() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 87ebe079038..0414a4f3fc3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -36,7 +36,7 @@ namespace System.Net.Http private HttpResponseHeaders? _trailers; private MultiArrayBuffer _responseBuffer; // mutable struct, do not make this readonly - private int _pendingWindowUpdate; + private Http2StreamWindowManager _windowManager; private CreditWaiter? _creditWaiter; private int _availableCredit; private readonly object _creditSyncObject = new object(); // split from SyncObject to avoid lock ordering problems with Http2Connection.SyncObject @@ -85,11 +85,6 @@ namespace System.Net.Http private int _headerBudgetRemaining; - private const int StreamWindowSize = DefaultInitialWindowSize; - - // See comment on ConnectionWindowThreshold. - private const int StreamWindowThreshold = StreamWindowSize / 8; - public Http2Stream(HttpRequestMessage request, Http2Connection connection) { _request = request; @@ -102,7 +97,8 @@ namespace System.Net.Http _responseBuffer = new MultiArrayBuffer(InitialStreamBufferSize); - _pendingWindowUpdate = 0; + _windowManager = new Http2StreamWindowManager(connection, this); + _headerBudgetRemaining = connection._pool.Settings._maxResponseHeadersLength * 1024; if (_request.Content == null) @@ -149,6 +145,10 @@ namespace System.Net.Http public bool SendRequestFinished => _requestCompletionState != StreamCompletionState.InProgress; + public bool ExpectResponseData => _responseProtocolState == ResponseProtocolState.ExpectingData; + + public Http2Connection Connection => _connection; + public HttpResponseMessage GetAndClearResponse() { // Once SendAsync completes, the Http2Stream should no longer hold onto the response message. @@ -781,6 +781,10 @@ namespace System.Net.Http Debug.Assert(_requestCompletionState != StreamCompletionState.Failed); } + if (_responseProtocolState == ResponseProtocolState.ExpectingData) + { + _windowManager.Start(); + } signalWaiter = _hasWaiter; _hasWaiter = false; } @@ -811,7 +815,7 @@ namespace System.Net.Http break; } - if (_responseBuffer.ActiveMemory.Length + buffer.Length > StreamWindowSize) + if (_responseBuffer.ActiveMemory.Length + buffer.Length > _windowManager.StreamWindowSize) { // Window size exceeded. ThrowProtocolError(Http2ProtocolErrorCode.FlowControlError); @@ -1019,30 +1023,6 @@ namespace System.Net.Http } } - private void ExtendWindow(int amount) - { - Debug.Assert(amount > 0); - Debug.Assert(_pendingWindowUpdate < StreamWindowThreshold); - - if (_responseProtocolState != ResponseProtocolState.ExpectingData) - { - // We are not expecting any more data (because we've either completed or aborted). - // So no need to send any more WINDOW_UPDATEs. - return; - } - - _pendingWindowUpdate += amount; - if (_pendingWindowUpdate < StreamWindowThreshold) - { - return; - } - - int windowUpdateSize = _pendingWindowUpdate; - _pendingWindowUpdate = 0; - - _connection.LogExceptions(_connection.SendWindowUpdateAsync(StreamId, windowUpdateSize)); - } - private (bool wait, int bytesRead) TryReadFromBuffer(Span buffer, bool partOfSyncRead = false) { Debug.Assert(buffer.Length > 0); @@ -1095,7 +1075,7 @@ namespace System.Net.Http if (bytesRead != 0) { - ExtendWindow(bytesRead); + _windowManager.AdjustWindow(bytesRead, this); } else { @@ -1124,7 +1104,7 @@ namespace System.Net.Http if (bytesRead != 0) { - ExtendWindow(bytesRead); + _windowManager.AdjustWindow(bytesRead, this); } else { @@ -1154,7 +1134,7 @@ namespace System.Net.Http if (bytesRead != 0) { - ExtendWindow(bytesRead); + _windowManager.AdjustWindow(bytesRead, this); destination.Write(new ReadOnlySpan(buffer, 0, bytesRead)); } else @@ -1190,7 +1170,7 @@ namespace System.Net.Http if (bytesRead != 0) { - ExtendWindow(bytesRead); + _windowManager.AdjustWindow(bytesRead, this); await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); } else diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2StreamWindowManager.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2StreamWindowManager.cs new file mode 100644 index 00000000000..4a1f7ee6b56 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2StreamWindowManager.cs @@ -0,0 +1,271 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + internal sealed partial class Http2Connection + { + // Maintains a dynamically-sized stream receive window, and sends WINDOW_UPDATE frames to the server. + private struct Http2StreamWindowManager + { + private static readonly double StopWatchToTimesSpan = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency; + private static double WindowScaleThresholdMultiplier => GlobalHttpSettings.SocketsHttpHandler.Http2StreamWindowScaleThresholdMultiplier; + private static int MaxStreamWindowSize => GlobalHttpSettings.SocketsHttpHandler.MaxHttp2StreamWindowSize; + private static bool WindowScalingEnabled => !GlobalHttpSettings.SocketsHttpHandler.DisableDynamicHttp2WindowSizing; + + private int _deliveredBytes; + private int _streamWindowSize; + private long _lastWindowUpdate; + + public Http2StreamWindowManager(Http2Connection connection, Http2Stream stream) + { + HttpConnectionSettings settings = connection._pool.Settings; + _streamWindowSize = settings._initialHttp2StreamWindowSize; + _deliveredBytes = 0; + _lastWindowUpdate = default; + + if (NetEventSource.Log.IsEnabled()) stream.Trace($"[FlowControl] InitialClientStreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}, WindowScaleThresholdMultiplier: {WindowScaleThresholdMultiplier}"); + } + + // We hold off on sending WINDOW_UPDATE until we hit the minimum threshold. + // This value is somewhat arbitrary; the intent is to ensure it is much smaller than + // the window size itself, or we risk stalling the server because it runs out of window space. + public const int StreamWindowUpdateRatio = 8; + internal int StreamWindowThreshold => _streamWindowSize / StreamWindowUpdateRatio; + + internal int StreamWindowSize => _streamWindowSize; + + public void Start() + { + _lastWindowUpdate = Stopwatch.GetTimestamp(); + } + + public void AdjustWindow(int bytesConsumed, Http2Stream stream) + { + Debug.Assert(_lastWindowUpdate != default); // Make sure Start() has been invoked, otherwise we should not be receiving DATA. + Debug.Assert(bytesConsumed > 0); + Debug.Assert(_deliveredBytes < StreamWindowThreshold); + + if (!stream.ExpectResponseData) + { + // We are not expecting any more data (because we've either completed or aborted). + // So no need to send any more WINDOW_UPDATEs. + return; + } + + if (WindowScalingEnabled) + { + AdjustWindowDynamic(bytesConsumed, stream); + } + else + { + AjdustWindowStatic(bytesConsumed, stream); + } + } + + private void AjdustWindowStatic(int bytesConsumed, Http2Stream stream) + { + _deliveredBytes += bytesConsumed; + if (_deliveredBytes < StreamWindowThreshold) + { + return; + } + + int windowUpdateIncrement = _deliveredBytes; + _deliveredBytes = 0; + + Http2Connection connection = stream.Connection; + Task sendWindowUpdateTask = connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement); + connection.LogExceptions(sendWindowUpdateTask); + } + + private void AdjustWindowDynamic(int bytesConsumed, Http2Stream stream) + { + _deliveredBytes += bytesConsumed; + + if (_deliveredBytes < StreamWindowThreshold) + { + return; + } + + int windowUpdateIncrement = _deliveredBytes; + long currentTime = Stopwatch.GetTimestamp(); + Http2Connection connection = stream.Connection; + + TimeSpan rtt = connection._rttEstimator.MinRtt; + if (rtt > TimeSpan.Zero && _streamWindowSize < MaxStreamWindowSize) + { + TimeSpan dt = StopwatchTicksToTimeSpan(currentTime - _lastWindowUpdate); + + // We are detecting bursts in the amount of data consumed within a single 'dt' window update period. + // The value "_deliveredBytes / dt" correlates with the bandwidth of the connection. + // We need to extend the window, if the bandwidth-delay product grows over the current window size. + // To enable empirical fine tuning, we apply a configurable multiplier (_windowScaleThresholdMultiplier) to the window size, which defaults to 1.0 + // + // The condition to extend the window is: + // (_deliveredBytes / dt) * rtt > _streamWindowSize * _windowScaleThresholdMultiplier + // + // Which is reordered into the form below, to avoid the division: + if (_deliveredBytes * (double)rtt.Ticks > _streamWindowSize * dt.Ticks * WindowScaleThresholdMultiplier) + { + int extendedWindowSize = Math.Min(MaxStreamWindowSize, _streamWindowSize * 2); + windowUpdateIncrement += extendedWindowSize - _streamWindowSize; + _streamWindowSize = extendedWindowSize; + + if (NetEventSource.Log.IsEnabled()) stream.Trace($"[FlowControl] Updated Stream Window. StreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}"); + + Debug.Assert(_streamWindowSize <= MaxStreamWindowSize); + if (_streamWindowSize == MaxStreamWindowSize) + { + if (NetEventSource.Log.IsEnabled()) stream.Trace($"[FlowControl] StreamWindowSize reached the configured maximum of {MaxStreamWindowSize}."); + } + } + } + + _deliveredBytes = 0; + + Task sendWindowUpdateTask = connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement); + connection.LogExceptions(sendWindowUpdateTask); + + _lastWindowUpdate = currentTime; + } + + private static TimeSpan StopwatchTicksToTimeSpan(long stopwatchTicks) + { + long ticks = (long)(StopWatchToTimesSpan * stopwatchTicks); + return new TimeSpan(ticks); + } + } + + // Estimates Round Trip Time between the client and the server by sending PING frames, and measuring the time interval until a PING ACK is received. + // Assuming that the network characteristics of the connection wouldn't change much within its lifetime, we are maintaining a running minimum value. + // The more PINGs we send, the more accurate is the estimation of MinRtt, however we should be careful not to send too many of them, + // to avoid triggering the server's PING flood protection which may result in an unexpected GOAWAY. + // With most servers we are fine to send PINGs, as long as we are reading their data, this rule is well formalized for gRPC: + // https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md + // As a rule of thumb, we can send send a PING whenever we receive DATA or HEADERS, however, there are some servers which allow receiving only + // a limited amount of PINGs within a given timeframe. + // To deal with the conflicting requirements: + // - We send an initial burst of 'InitialBurstCount' PINGs, to get a relatively good estimation fast + // - Afterwards, we send PINGs with the maximum frequency of 'PingIntervalInSeconds' PINGs per second + // + // Threading: + // OnInitialSettingsSent() is called during initialization, all other methods are triggered by HttpConnection.ProcessIncomingFramesAsync(), + // therefore the assumption is that the invocation of RttEstimator's methods is sequential, and there is no race beetween them. + // Http2StreamWindowManager is reading MinRtt from another concurrent thread, therefore its value has to be changed atomically. + private struct RttEstimator + { + private enum State + { + Disabled, + Init, + Waiting, + PingSent, + TerminatingMayReceivePingAck + } + + private const double PingIntervalInSeconds = 2; + private const int InitialBurstCount = 4; + private static readonly long PingIntervalInTicks = (long)(PingIntervalInSeconds * Stopwatch.Frequency); + + private State _state; + private long _pingSentTimestamp; + private long _pingCounter; + private int _initialBurst; + private long _minRtt; + + public TimeSpan MinRtt => new TimeSpan(_minRtt); + + public static RttEstimator Create() + { + RttEstimator e = default; + e._state = GlobalHttpSettings.SocketsHttpHandler.DisableDynamicHttp2WindowSizing ? State.Disabled : State.Init; + e._initialBurst = InitialBurstCount; + return e; + } + + internal void OnInitialSettingsSent() + { + if (_state == State.Disabled) return; + _pingSentTimestamp = Stopwatch.GetTimestamp(); + } + + internal void OnInitialSettingsAckReceived(Http2Connection connection) + { + if (_state == State.Disabled) return; + RefreshRtt(connection); + _state = State.Waiting; + } + + internal void OnDataOrHeadersReceived(Http2Connection connection) + { + if (_state != State.Waiting) return; + + long now = Stopwatch.GetTimestamp(); + bool initial = _initialBurst > 0; + if (initial || now - _pingSentTimestamp > PingIntervalInTicks) + { + if (initial) _initialBurst--; + + // Send a PING + _pingCounter--; + connection.LogExceptions(connection.SendPingAsync(_pingCounter, isAck: false)); + _pingSentTimestamp = now; + _state = State.PingSent; + } + } + + internal void OnPingAckReceived(long payload, Http2Connection connection) + { + if (_state != State.PingSent && _state != State.TerminatingMayReceivePingAck) + { + ThrowProtocolError(); + } + + if (_state == State.TerminatingMayReceivePingAck) + { + _state = State.Disabled; + return; + } + + // RTT PINGs always carry negative payload, positive values indicate a response to KeepAlive PING. + Debug.Assert(payload < 0); + + if (_pingCounter != payload) + ThrowProtocolError(); + + RefreshRtt(connection); + _state = State.Waiting; + } + + internal void OnGoAwayReceived() + { + if (_state == State.PingSent) + { + // We may still receive a PING ACK, but we should not send anymore PING: + _state = State.TerminatingMayReceivePingAck; + } + else + { + _state = State.Disabled; + } + } + + private void RefreshRtt(Http2Connection connection) + { + long elapsedTicks = Stopwatch.GetTimestamp() - _pingSentTimestamp; + long prevRtt = _minRtt == 0 ? long.MaxValue : _minRtt; + TimeSpan currentRtt = TimeSpan.FromSeconds(elapsedTicks / (double)Stopwatch.Frequency); + long minRtt = Math.Min(prevRtt, currentRtt.Ticks); + + Interlocked.Exchange(ref _minRtt, minRtt); // MinRtt is being queried from another thread + + if (NetEventSource.Log.IsEnabled()) connection.Trace($"[FlowControl] Updated MinRtt: {MinRtt.TotalMilliseconds} ms"); + } + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs index 3c19737ae40..03cb3e9a406 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Net.Security; using System.IO; -using System.Net.Quic; using System.Net.Quic.Implementations; using System.Runtime.Versioning; using System.Threading; @@ -15,11 +14,6 @@ namespace System.Net.Http /// Provides a state bag of settings for configuring HTTP connections. internal sealed class HttpConnectionSettings { - private const string Http2SupportEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2SUPPORT"; - private const string Http2SupportAppCtxSettingName = "System.Net.Http.SocketsHttpHandler.Http2Support"; - private const string Http3DraftSupportEnvironmentVariableSettingName = "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3DRAFTSUPPORT"; - private const string Http3DraftSupportAppCtxSettingName = "System.Net.SocketsHttpHandler.Http3DraftSupport"; - internal DecompressionMethods _automaticDecompression = HttpHandlerDefaults.DefaultAutomaticDecompression; internal bool _useCookies = HttpHandlerDefaults.DefaultUseCookies; @@ -67,11 +61,15 @@ namespace System.Net.Http internal IDictionary? _properties; + // Http2 flow control settings: + internal int _initialHttp2StreamWindowSize = HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize; + public HttpConnectionSettings() { - bool allowHttp2 = AllowHttp2; + bool allowHttp2 = GlobalHttpSettings.SocketsHttpHandler.AllowHttp2; + bool allowHttp3 = GlobalHttpSettings.SocketsHttpHandler.AllowDraftHttp3; _maxHttpVersion = - AllowDraftHttp3 && allowHttp2 ? HttpVersion.Version30 : + allowHttp3 && allowHttp2 ? HttpVersion.Version30 : allowHttp2 ? HttpVersion.Version20 : HttpVersion.Version11; _defaultCredentialsUsedForProxy = _proxy != null && (_proxy.Credentials == CredentialCache.DefaultCredentials || _defaultProxyCredentials == CredentialCache.DefaultCredentials); @@ -119,7 +117,8 @@ namespace System.Net.Http _responseHeaderEncodingSelector = _responseHeaderEncodingSelector, _enableMultipleHttp2Connections = _enableMultipleHttp2Connections, _connectCallback = _connectCallback, - _plaintextStreamFilter = _plaintextStreamFilter + _plaintextStreamFilter = _plaintextStreamFilter, + _initialHttp2StreamWindowSize = _initialHttp2StreamWindowSize, }; // TODO: Remove if/when QuicImplementationProvider is removed from System.Net.Quic. @@ -131,58 +130,6 @@ namespace System.Net.Http return settings; } - private static bool AllowHttp2 - { - get - { - // Default to allowing HTTP/2, but enable that to be overridden by an - // AppContext switch, or by an environment variable being set to false/0. - - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(Http2SupportAppCtxSettingName, out bool allowHttp2)) - { - return allowHttp2; - } - - // AppContext switch wasn't used. Check the environment variable. - string? envVar = Environment.GetEnvironmentVariable(Http2SupportEnvironmentVariableSettingName); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Disallow HTTP/2 protocol. - return false; - } - - // Default to a maximum of HTTP/2. - return true; - } - } - - private static bool AllowDraftHttp3 - { - get - { - // Default to allowing draft HTTP/3, but enable that to be overridden - // by an AppContext switch, or by an environment variable being set to false/0. - - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(Http3DraftSupportAppCtxSettingName, out bool allowHttp3)) - { - return allowHttp3; - } - - // AppContext switch wasn't used. Check the environment variable. - string? envVar = Environment.GetEnvironmentVariable(Http3DraftSupportEnvironmentVariableSettingName); - if (envVar != null && (envVar.Equals("false", StringComparison.OrdinalIgnoreCase) || envVar.Equals("0"))) - { - // Disallow HTTP/3 protocol for HTTP endpoints. - return false; - } - - // Default to allow. - return true; - } - } - public bool EnableMultipleHttp2Connections => _enableMultipleHttp2Connections; private byte[]? _http3SettingsFrame; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs new file mode 100644 index 00000000000..2fc23028f9b --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Net.Http +{ + internal static class RuntimeSettingParser + { + /// + /// Parse a value from an AppContext switch or an environment variable. + /// + public static bool QueryRuntimeSettingSwitch(string appCtxSettingName, string environmentVariableSettingName, bool defaultValue) + { + bool value; + + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(appCtxSettingName, out value)) + { + return value; + } + + // AppContext switch wasn't used. Check the environment variable. + string? envVar = Environment.GetEnvironmentVariable(environmentVariableSettingName); + + if (bool.TryParse(envVar, out value)) + { + return value; + } + else if (uint.TryParse(envVar, out uint intVal)) + { + return intVal != 0; + } + + return defaultValue; + } + + /// + /// Parse an environment variable for an value. + /// + public static int ParseInt32EnvironmentVariableValue(string environmentVariableSettingName, int defaultValue) + { + string? envVar = Environment.GetEnvironmentVariable(environmentVariableSettingName); + + if (int.TryParse(envVar, NumberStyles.Any, CultureInfo.InvariantCulture, out int value)) + { + return value; + } + return defaultValue; + } + + /// + /// Parse an environment variable for a value. + /// + public static double ParseDoubleEnvironmentVariableValue(string environmentVariableSettingName, double defaultValue) + { + string? envVar = Environment.GetEnvironmentVariable(environmentVariableSettingName); + if (double.TryParse(envVar, NumberStyles.Any, CultureInfo.InvariantCulture, out double value)) + { + return value; + } + return defaultValue; + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 76080c6cfc5..157ef2dde7e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -285,6 +285,32 @@ namespace System.Net.Http } } + /// + /// Defines the initial HTTP2 stream receive window size for all connections opened by the this . + /// + /// + /// Larger the values may lead to faster download speed, but potentially higher memory footprint. + /// The property must be set to a value between 65535 and the configured maximum window size, which is 16777216 by default. + /// + public int InitialHttp2StreamWindowSize + { + get => _settings._initialHttp2StreamWindowSize; + set + { + if (value < HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize || value > GlobalHttpSettings.SocketsHttpHandler.MaxHttp2StreamWindowSize) + { + string message = SR.Format( + SR.net_http_http2_invalidinitialstreamwindowsize, + HttpHandlerDefaults.DefaultInitialHttp2StreamWindowSize, + GlobalHttpSettings.SocketsHttpHandler.MaxHttp2StreamWindowSize); + + throw new ArgumentOutOfRangeException(nameof(InitialHttp2StreamWindowSize), message); + } + CheckDisposedOrStarted(); + _settings._initialHttp2StreamWindowSize = value; + } + } + /// /// Gets or sets the keep alive ping delay. The client will send a keep alive ping to the server if it /// doesn't receive any frames on a connection for this period of time. This property is used together with diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index 7f28c57cf75..39e125abe1c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -12,6 +12,7 @@ using System.Net.Test.Common; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Xunit; using Xunit.Abstractions; @@ -815,7 +816,7 @@ namespace System.Net.Http.Functional.Tests await AssertProtocolErrorAsync(sendTask, ProtocolErrors.PROTOCOL_ERROR); // The client should close the connection as this is a fatal connection level error. - Assert.Null(await connection.ReadFrameAsync(TimeSpan.FromSeconds(30))); + await connection.WaitForClientDisconnectAsync(); } } @@ -1470,8 +1471,6 @@ namespace System.Net.Http.Functional.Tests return bytesReceived; } - const int DefaultInitialWindowSize = 65535; - [OuterLoop("Uses Task.Delay")] [ConditionalFact(nameof(SupportsAlpn))] public async Task Http2_FlowControl_ClientDoesNotExceedWindows() @@ -1796,121 +1795,135 @@ namespace System.Net.Http.Functional.Tests [MemberData(nameof(KeepAliveTestDataSource))] [ConditionalTheory(nameof(SupportsAlpn))] [ActiveIssue("https://github.com/dotnet/runtime/issues/41929")] - public async Task Http2_PingKeepAlive(TimeSpan keepAlivePingDelay, HttpKeepAlivePingPolicy keepAlivePingPolicy, bool expectRequestFail) + public void Http2_PingKeepAlive(TimeSpan keepAlivePingDelay, HttpKeepAlivePingPolicy keepAlivePingPolicy, bool expectRequestFail) { - TimeSpan pingTimeout = TimeSpan.FromSeconds(5); - // Simulate failure by delaying the pong, otherwise send it immediately. - TimeSpan pongDelay = expectRequestFail ? pingTimeout * 2 : TimeSpan.Zero; - // Pings are send only if KeepAlivePingDelay is not infinite. - bool expectStreamPing = keepAlivePingDelay != Timeout.InfiniteTimeSpan; - // Pings (regardless ongoing communication) are send only if sending is on and policy is set to always. - bool expectPingWithoutStream = expectStreamPing && keepAlivePingPolicy == HttpKeepAlivePingPolicy.Always; + RemoteExecutor.Invoke(RunTest, keepAlivePingDelay.Ticks.ToString(), keepAlivePingPolicy.ToString(), expectRequestFail.ToString()).Dispose(); - TaskCompletionSource serverFinished = new TaskCompletionSource(); + static async Task RunTest(string keepAlivePingDelayString, string keepAlivePingPolicyString, string expectRequestFailString) + { + // We should refactor this test so it can react to RTT PINGs. + // For now, avoid interference by disabling them: + AppContext.SetSwitch("System.Net.SocketsHttpHandler.Http2FlowControl.DisableDynamicWindowSizing", true); - await Http2LoopbackServer.CreateClientAndServerAsync( - async uri => - { - SocketsHttpHandler handler = new SocketsHttpHandler() + bool expectRequestFail = bool.Parse(expectRequestFailString); + TimeSpan keepAlivePingDelay = TimeSpan.FromTicks(long.Parse(keepAlivePingDelayString)); + HttpKeepAlivePingPolicy keepAlivePingPolicy = Enum.Parse(keepAlivePingPolicyString); + + TimeSpan pingTimeout = TimeSpan.FromSeconds(5); + // Simulate failure by delaying the pong, otherwise send it immediately. + TimeSpan pongDelay = expectRequestFail ? pingTimeout * 2 : TimeSpan.Zero; + // Pings are send only if KeepAlivePingDelay is not infinite. + bool expectStreamPing = keepAlivePingDelay != Timeout.InfiniteTimeSpan; + // Pings (regardless ongoing communication) are send only if sending is on and policy is set to always. + bool expectPingWithoutStream = expectStreamPing && keepAlivePingPolicy == HttpKeepAlivePingPolicy.Always; + + TaskCompletionSource serverFinished = new TaskCompletionSource(); + + await Http2LoopbackServer.CreateClientAndServerAsync( + async uri => { - KeepAlivePingTimeout = pingTimeout, - KeepAlivePingPolicy = keepAlivePingPolicy, - KeepAlivePingDelay = keepAlivePingDelay - }; - handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; - - using HttpClient client = new HttpClient(handler); - client.DefaultRequestVersion = HttpVersion.Version20; - - // Warmup request to create connection. - await client.GetStringAsync(uri); - // Request under the test scope. - if (expectRequestFail) - { - await Assert.ThrowsAsync(() => client.GetStringAsync(uri)); - // As stream is closed we don't want to continue with sending data. - return; - } - else - { - await client.GetStringAsync(uri); - } - - // Let connection live until server finishes. - try - { - await serverFinished.Task.WaitAsync(pingTimeout * 3); - } - catch (TimeoutException) { } - }, - async server => - { - using Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); - - Task receivePingTask = expectStreamPing ? connection.ExpectPingFrameAsync() : null; - - // Warmup the connection. - int streamId1 = await connection.ReadRequestHeaderAsync(); - await connection.SendDefaultResponseAsync(streamId1); - - // Request under the test scope. - int streamId2 = await connection.ReadRequestHeaderAsync(); - - // Test ping with active stream. - if (!expectStreamPing) - { - await Assert.ThrowsAsync(() => connection.ReadPingAsync(pingTimeout)); - } - else - { - PingFrame ping; - if (receivePingTask != null && receivePingTask.IsCompleted) + SocketsHttpHandler handler = new SocketsHttpHandler() { - ping = await receivePingTask; + KeepAlivePingTimeout = pingTimeout, + KeepAlivePingPolicy = keepAlivePingPolicy, + KeepAlivePingDelay = keepAlivePingDelay + }; + handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; + + using HttpClient client = new HttpClient(handler); + client.DefaultRequestVersion = HttpVersion.Version20; + + // Warmup request to create connection. + await client.GetStringAsync(uri); + // Request under the test scope. + if (expectRequestFail) + { + await Assert.ThrowsAsync(() => client.GetStringAsync(uri)); + // As stream is closed we don't want to continue with sending data. + return; } else { - ping = await connection.ReadPingAsync(pingTimeout); - } - if (pongDelay > TimeSpan.Zero) - { - await Task.Delay(pongDelay); + await client.GetStringAsync(uri); } - await connection.SendPingAckAsync(ping.Data); - } - - // Send response and close the stream. - if (expectRequestFail) - { - await Assert.ThrowsAsync(() => connection.SendDefaultResponseAsync(streamId2)); - // As stream is closed we don't want to continue with sending data. - return; - } - await connection.SendDefaultResponseAsync(streamId2); - // Test ping with no active stream. - if (expectPingWithoutStream) - { - PingFrame ping = await connection.ReadPingAsync(pingTimeout); - await connection.SendPingAckAsync(ping.Data); - } - else - { - // If the pings were recently coming, just give the connection time to clear up streams - // and still accept one stray ping. - if (expectStreamPing) + // Let connection live until server finishes. + try { - try + await serverFinished.Task.WaitAsync(pingTimeout * 3); + } + catch (TimeoutException) { } + }, + async server => + { + using Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); + + Task receivePingTask = expectStreamPing ? connection.ExpectPingFrameAsync() : null; + + // Warmup the connection. + int streamId1 = await connection.ReadRequestHeaderAsync(); + await connection.SendDefaultResponseAsync(streamId1); + + // Request under the test scope. + int streamId2 = await connection.ReadRequestHeaderAsync(); + + // Test ping with active stream. + if (!expectStreamPing) + { + await Assert.ThrowsAsync(() => connection.ReadPingAsync(pingTimeout)); + } + else + { + PingFrame ping; + if (receivePingTask != null && receivePingTask.IsCompleted) { - await connection.ReadPingAsync(pingTimeout); + ping = await receivePingTask; } - catch (OperationCanceledException) { } // if it failed once, it will fail again + else + { + ping = await connection.ReadPingAsync(pingTimeout); + } + if (pongDelay > TimeSpan.Zero) + { + await Task.Delay(pongDelay); + } + + await connection.SendPingAckAsync(ping.Data); } - await Assert.ThrowsAsync(() => connection.ReadPingAsync(pingTimeout)); - } - serverFinished.SetResult(); - await connection.WaitForClientDisconnectAsync(true); - }); + + // Send response and close the stream. + if (expectRequestFail) + { + await Assert.ThrowsAsync(() => connection.SendDefaultResponseAsync(streamId2)); + // As stream is closed we don't want to continue with sending data. + return; + } + await connection.SendDefaultResponseAsync(streamId2); + // Test ping with no active stream. + if (expectPingWithoutStream) + { + PingFrame ping = await connection.ReadPingAsync(pingTimeout); + await connection.SendPingAckAsync(ping.Data); + } + else + { + // If the pings were recently coming, just give the connection time to clear up streams + // and still accept one stray ping. + if (expectStreamPing) + { + try + { + await connection.ReadPingAsync(pingTimeout); + } + catch (OperationCanceledException) { } // if it failed once, it will fail again + } + await Assert.ThrowsAsync(() => connection.ReadPingAsync(pingTimeout)); + } + serverFinished.SetResult(); + await connection.WaitForClientDisconnectAsync(true); + }, + new Http2Options() { EnableTransparentPingResponse = false }); + } } [OuterLoop("Uses Task.Delay")] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 451bb20d58f..a5448b012d2 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.Quic; using System.Net.Security; using System.Net.Sockets; using System.Net.Test.Common; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs new file mode 100644 index 00000000000..427b8fcc0e1 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs @@ -0,0 +1,320 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Linq; +using System.Net.Test.Common; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using Xunit.Abstractions; + +namespace System.Net.Http.Functional.Tests +{ + [CollectionDefinition(nameof(NonParallelTestCollection), DisableParallelization = true)] + public class NonParallelTestCollection + { + } + + // This test class contains tests which are strongly timing-dependent. + // There are two mitigations avoid flaky behavior on CI: + // - Parallel test execution is disabled + // - Using extreme parameters, and checks which are very unlikely to fail, if the implementation is correct + [Collection(nameof(NonParallelTestCollection))] + [ConditionalClass(typeof(SocketsHttpHandler_Http2FlowControl_Test), nameof(IsSupported))] + public sealed class SocketsHttpHandler_Http2FlowControl_Test : HttpClientHandlerTestBase + { + public static readonly bool IsSupported = PlatformDetection.SupportsAlpn && PlatformDetection.IsNotBrowser; + + protected override Version UseVersion => HttpVersion20.Value; + + public SocketsHttpHandler_Http2FlowControl_Test(ITestOutputHelper output) : base(output) + { + } + + private static Http2Options NoAutoPingResponseHttp2Options => new Http2Options() { EnableTransparentPingResponse = false }; + + [Fact] + public async Task InitialHttp2StreamWindowSize_SentInSettingsFrame() + { + const int WindowSize = 123456; + using Http2LoopbackServer server = Http2LoopbackServer.CreateServer(); + using var handler = CreateHttpClientHandler(); + GetUnderlyingSocketsHttpHandler(handler).InitialHttp2StreamWindowSize = WindowSize; + using HttpClient client = CreateHttpClient(handler); + + Task clientTask = client.GetAsync(server.Address); + Http2LoopbackConnection connection = await server.AcceptConnectionAsync().ConfigureAwait(false); + SettingsFrame clientSettingsFrame = await connection.ReadSettingsAsync().ConfigureAwait(false); + SettingsEntry entry = clientSettingsFrame.Entries.First(e => e.SettingId == SettingId.InitialWindowSize); + + Assert.Equal(WindowSize, (int)entry.Value); + } + + [Fact] + public Task InvalidRttPingResponse_RequestShouldFail() + { + return Http2LoopbackServer.CreateClientAndServerAsync(async uri => + { + using var handler = CreateHttpClientHandler(); + using HttpClient client = CreateHttpClient(handler); + HttpRequestException exception = await Assert.ThrowsAsync(() => client.GetAsync(uri)); + _output.WriteLine(exception.Message + exception.StatusCode); + }, + async server => + { + Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); + (int streamId, _) = await connection.ReadAndParseRequestHeaderAsync(); + await connection.SendDefaultResponseHeadersAsync(streamId); + PingFrame pingFrame = await connection.ReadPingAsync(); // expect an RTT PING + await connection.SendPingAckAsync(-6666); // send an invalid PING response + await connection.SendResponseDataAsync(streamId, new byte[] { 1, 2, 3 }, true); // otherwise fine response + }, + NoAutoPingResponseHttp2Options); + } + + + [OuterLoop("Runs long")] + [Fact] + public async Task HighBandwidthDelayProduct_ClientStreamReceiveWindowWindowScalesUp() + { + int maxCredit = await TestClientWindowScalingAsync( + TimeSpan.FromMilliseconds(30), + TimeSpan.Zero, + 2 * 1024 * 1024, + _output); + + // Expect the client receive window to grow over 1MB: + Assert.True(maxCredit > 1024 * 1024); + } + + [OuterLoop("Runs long")] + [Fact] + public void DisableDynamicWindowScaling_HighBandwidthDelayProduct_WindowRemainsConstant() + { + static async Task RunTest() + { + AppContext.SetSwitch("System.Net.SocketsHttpHandler.Http2FlowControl.DisableDynamicWindowSizing", true); + + int maxCredit = await TestClientWindowScalingAsync( + TimeSpan.FromMilliseconds(30), + TimeSpan.Zero, + 2 * 1024 * 1024, + null); + + Assert.Equal(DefaultInitialWindowSize, maxCredit); + } + + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [OuterLoop("Runs long")] + [Fact] + public void MaxStreamWindowSize_WhenSet_WindowDoesNotScaleAboveMaximum() + { + const int MaxWindow = 654321; + + static async Task RunTest() + { + int maxCredit = await TestClientWindowScalingAsync( + TimeSpan.FromMilliseconds(30), + TimeSpan.Zero, + 2 * 1024 * 1024, + null); + + Assert.True(maxCredit <= MaxWindow); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_MAXSTREAMWINDOWSIZE"] = MaxWindow.ToString(); + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [OuterLoop("Runs long")] + [Fact] + public void StreamWindowScaleThresholdMultiplier_HighValue_WindowScalesSlower() + { + static async Task RunTest() + { + int maxCredit = await TestClientWindowScalingAsync( + TimeSpan.FromMilliseconds(30), + TimeSpan.Zero, + 2 * 1024 * 1024, + null); + + Assert.True(maxCredit <= 128 * 1024); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_STREAMWINDOWSCALETHRESHOLDMULTIPLIER"] = "1000"; // Extreme value + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [OuterLoop("Runs long")] + [Fact] + public void StreamWindowScaleThresholdMultiplier_LowValue_WindowScalesFaster() + { + static async Task RunTest() + { + int maxCredit = await TestClientWindowScalingAsync( + TimeSpan.Zero, + TimeSpan.FromMilliseconds(15), // Low bandwidth * delay product + 2 * 1024 * 1024, + null); + + Assert.True(maxCredit >= 256 * 1024); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_STREAMWINDOWSCALETHRESHOLDMULTIPLIER"] = "0.00001"; // Extreme value + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + private static async Task TestClientWindowScalingAsync( + TimeSpan networkDelay, + TimeSpan slowBandwidthSimDelay, + int bytesToDownload, + ITestOutputHelper output = null, + int maxWindowForPingStopValidation = int.MaxValue, // set to actual maximum to test if we stop sending PING when window reached maximum + Action configureHandler = null) + { + TimeSpan timeout = TimeSpan.FromSeconds(30); + CancellationTokenSource timeoutCts = new CancellationTokenSource(timeout); + + HttpClientHandler handler = CreateHttpClientHandler(HttpVersion20.Value); + configureHandler?.Invoke(GetUnderlyingSocketsHttpHandler(handler)); + + using Http2LoopbackServer server = Http2LoopbackServer.CreateServer(NoAutoPingResponseHttp2Options); + using HttpClient client = new HttpClient(handler, true); + client.DefaultRequestVersion = HttpVersion20.Value; + + Task clientTask = client.GetAsync(server.Address, timeoutCts.Token); + Http2LoopbackConnection connection = await server.AcceptConnectionAsync().ConfigureAwait(false); + SettingsFrame clientSettingsFrame = await connection.ReadSettingsAsync().ConfigureAwait(false); + + // send server SETTINGS: + await connection.WriteFrameAsync(new SettingsFrame()).ConfigureAwait(false); + + // Initial client SETTINGS also works as a PING. Do not send ACK immediately to avoid low RTT estimation + await Task.Delay(networkDelay); + await connection.WriteFrameAsync(new SettingsFrame(FrameFlags.Ack, new SettingsEntry[0])); + + // Expect SETTINGS ACK from client: + await connection.ExpectSettingsAckAsync(); + + int maxCredit = (int)clientSettingsFrame.Entries.SingleOrDefault(e => e.SettingId == SettingId.InitialWindowSize).Value; + if (maxCredit == default) maxCredit = DefaultInitialWindowSize; + int credit = maxCredit; + + int streamId = await connection.ReadRequestHeaderAsync(); + // Write the response. + await connection.SendDefaultResponseHeadersAsync(streamId); + + using SemaphoreSlim creditReceivedSemaphore = new SemaphoreSlim(0); + using SemaphoreSlim writeSemaphore = new SemaphoreSlim(1); + int remainingBytes = bytesToDownload; + + bool pingReceivedAfterReachingMaxWindow = false; + bool unexpectedFrameReceived = false; + CancellationTokenSource stopFrameProcessingCts = new CancellationTokenSource(); + + CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(stopFrameProcessingCts.Token, timeoutCts.Token); + Task processFramesTask = ProcessIncomingFramesAsync(linkedCts.Token); + byte[] buffer = new byte[16384]; + + while (remainingBytes > 0) + { + Wait(slowBandwidthSimDelay); + while (credit == 0) await creditReceivedSemaphore.WaitAsync(timeout); + int bytesToSend = Math.Min(Math.Min(buffer.Length, credit), remainingBytes); + + Memory responseData = buffer.AsMemory(0, bytesToSend); + + int nextRemainingBytes = remainingBytes - bytesToSend; + bool endStream = nextRemainingBytes == 0; + + await writeSemaphore.WaitAsync(); + Interlocked.Add(ref credit, -bytesToSend); + await connection.SendResponseDataAsync(streamId, responseData, endStream); + writeSemaphore.Release(); + output?.WriteLine($"Sent {bytesToSend}, credit reduced to: {credit}"); + + remainingBytes = nextRemainingBytes; + } + + using HttpResponseMessage response = await clientTask; + + stopFrameProcessingCts.Cancel(); + await processFramesTask; + + int dataReceived = (await response.Content.ReadAsByteArrayAsync()).Length; + Assert.Equal(bytesToDownload, dataReceived); + Assert.False(pingReceivedAfterReachingMaxWindow, "Server received a PING after reaching max window"); + Assert.False(unexpectedFrameReceived, "Server received an unexpected frame, see test output for more details."); + + return maxCredit; + + async Task ProcessIncomingFramesAsync(CancellationToken cancellationToken) + { + // If credit > 90% of the maximum window, we are safe to assume we reached the max window. + // We should not receive any more RTT PING's after this point + int maxWindowCreditThreshold = (int) (0.9 * maxWindowForPingStopValidation); + output?.WriteLine($"maxWindowCreditThreshold: {maxWindowCreditThreshold} maxWindowForPingStopValidation: {maxWindowForPingStopValidation}"); + + try + { + while (remainingBytes > 0 && !cancellationToken.IsCancellationRequested) + { + Frame frame = await connection.ReadFrameAsync(cancellationToken); + + if (frame is PingFrame pingFrame) + { + // Simulate network delay for RTT PING + Wait(networkDelay); + + output?.WriteLine($"Received PING ({pingFrame.Data})"); + + if (maxCredit > maxWindowCreditThreshold) + { + output?.WriteLine("PING was unexpected"); + Volatile.Write(ref pingReceivedAfterReachingMaxWindow, true); + } + + await writeSemaphore.WaitAsync(cancellationToken); + await connection.SendPingAckAsync(pingFrame.Data, cancellationToken); + writeSemaphore.Release(); + } + else if (frame is WindowUpdateFrame windowUpdateFrame) + { + // Ignore connection window: + if (windowUpdateFrame.StreamId != streamId) continue; + + int currentCredit = Interlocked.Add(ref credit, windowUpdateFrame.UpdateSize); + maxCredit = Math.Max(currentCredit, maxCredit); // Detect if client grows the window + creditReceivedSemaphore.Release(); + + output?.WriteLine($"UpdateSize:{windowUpdateFrame.UpdateSize} currentCredit:{currentCredit} MaxCredit: {maxCredit}"); + } + else if (frame is not null) + { + Volatile.Write(ref unexpectedFrameReceived, true); + output?.WriteLine("Received unexpected frame: " + frame); + } + } + } + catch (OperationCanceledException) + { + } + + + output?.WriteLine("ProcessIncomingFramesAsync finished"); + } + + static void Wait(TimeSpan dt) { if (dt != TimeSpan.Zero) Thread.Sleep(dt); } + } + } +} diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 9902e490ce5..10e81d5f679 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -1927,6 +1927,30 @@ namespace System.Net.Http.Functional.Tests } } + [Fact] + public void InitialHttp2StreamWindowSize_GetSet_Roundtrips() + { + using var handler = new SocketsHttpHandler(); + Assert.Equal(HttpClientHandlerTestBase.DefaultInitialWindowSize, handler.InitialHttp2StreamWindowSize); // default value + + handler.InitialHttp2StreamWindowSize = 1048576; + Assert.Equal(1048576, handler.InitialHttp2StreamWindowSize); + + handler.InitialHttp2StreamWindowSize = HttpClientHandlerTestBase.DefaultInitialWindowSize; + Assert.Equal(HttpClientHandlerTestBase.DefaultInitialWindowSize, handler.InitialHttp2StreamWindowSize); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(65534)] + [InlineData(32 * 1024 * 1024)] + public void InitialHttp2StreamWindowSize_InvalidValue_ThrowsArgumentOutOfRangeException(int value) + { + using var handler = new SocketsHttpHandler(); + Assert.Throws(() => handler.InitialHttp2StreamWindowSize = value); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -1966,6 +1990,7 @@ namespace System.Net.Http.Functional.Tests Assert.True(handler.UseProxy); Assert.Null(handler.ConnectCallback); Assert.Null(handler.PlaintextStreamFilter); + Assert.Equal(HttpClientHandlerTestBase.DefaultInitialWindowSize, handler.InitialHttp2StreamWindowSize); Assert.Throws(expectedExceptionType, () => handler.AllowAutoRedirect = false); Assert.Throws(expectedExceptionType, () => handler.AutomaticDecompression = DecompressionMethods.GZip); @@ -1987,6 +2012,7 @@ namespace System.Net.Http.Functional.Tests Assert.Throws(expectedExceptionType, () => handler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.WithActiveRequests); Assert.Throws(expectedExceptionType, () => handler.ConnectCallback = (context, token) => default); Assert.Throws(expectedExceptionType, () => handler.PlaintextStreamFilter = (context, token) => default); + Assert.Throws(expectedExceptionType, () => handler.InitialHttp2StreamWindowSize = 128 * 1024); } } } @@ -2268,6 +2294,7 @@ namespace System.Net.Http.Functional.Tests { Task warmUpTask = client.GetAsync(server.Address); Http2LoopbackConnection connection = await GetConnection(server, maxConcurrentStreams, readTimeout).WaitAsync(TestHelper.PassingTestTimeout * 2).ConfigureAwait(false); + // Wait until the client confirms MaxConcurrentStreams setting took into effect. Task settingAckReceived = connection.SettingAckWaiter; while (true) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 7a33b8cac5c..b3413cbef30 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -130,6 +130,7 @@ Link="Common\System\Net\Http\HttpClientHandlerTest.DefaultProxyCredentials.cs" /> + diff --git a/src/libraries/System.Net.Http/tests/UnitTests/RuntimeSettingParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/RuntimeSettingParserTest.cs new file mode 100644 index 00000000000..9b933215a84 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/UnitTests/RuntimeSettingParserTest.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Net.Http.Tests +{ + public class RuntimeSettingParserTest + { + public static bool SupportsRemoteExecutor = RemoteExecutor.IsSupported; + + [ConditionalTheory(nameof(SupportsRemoteExecutor))] + [InlineData(false)] + [InlineData(true)] + public void QueryRuntimeSettingSwitch_WhenNotSet_DefaultIsUsed(bool defaultValue) + { + static void RunTest(string defaultValueStr) + { + bool expected = bool.Parse(defaultValueStr); + bool actual = RuntimeSettingParser.QueryRuntimeSettingSwitch("Foo.Bar", "FOO_BAR", expected); + Assert.Equal(expected, actual); + } + + RemoteExecutor.Invoke(RunTest, defaultValue.ToString()).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void QueryRuntimeSettingSwitch_AppContextHasPriority() + { + static void RunTest() + { + AppContext.SetSwitch("Foo.Bar", false); + bool actual = RuntimeSettingParser.QueryRuntimeSettingSwitch("Foo.Bar", "FOO_BAR", true); + Assert.False(actual); + } + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "true"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void QueryRuntimeSettingSwitch_EnvironmentVariable() + { + static void RunTest() + { + bool actual = RuntimeSettingParser.QueryRuntimeSettingSwitch("Foo.Bar", "FOO_BAR", true); + Assert.False(actual); + } + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "false"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void QueryRuntimeSettingSwitch_InvalidValue_FallbackToDefault() + { + static void RunTest() + { + bool actual = RuntimeSettingParser.QueryRuntimeSettingSwitch("Foo.Bar", "FOO_BAR", true); + Assert.True(actual); + } + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "cheese"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseInt32EnvironmentVariableValue_WhenNotSet_DefaultIsUsed() + { + static void RunTest() + { + int actual = RuntimeSettingParser.ParseInt32EnvironmentVariableValue("FOO_BAR", -42); + Assert.Equal(-42, actual); + } + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseInt32EnvironmentVariableValue_ValidValue() + { + static void RunTest() + { + int actual = RuntimeSettingParser.ParseInt32EnvironmentVariableValue("FOO_BAR", -42); + Assert.Equal(84, actual); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "84"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseInt32EnvironmentVariableValue_InvalidValue_FallbackToDefault() + { + static void RunTest() + { + int actual = RuntimeSettingParser.ParseInt32EnvironmentVariableValue("FOO_BAR", -42); + Assert.Equal(-42, actual); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "-~4!"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseDoubleEnvironmentVariableValue_WhenNotSet_DefaultIsUsed() + { + static void RunTest() + { + double actual = RuntimeSettingParser.ParseDoubleEnvironmentVariableValue("FOO_BAR", -0.42); + Assert.Equal(-0.42, actual); + } + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseDoubleEnvironmentVariableValue_ValidValue() + { + static void RunTest() + { + double actual = RuntimeSettingParser.ParseDoubleEnvironmentVariableValue("FOO_BAR", -0.42); + Assert.Equal(0.84, actual); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "0.84"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + + [ConditionalFact(nameof(SupportsRemoteExecutor))] + public void ParseDoubleEnvironmentVariableValue_InvalidValue_FallbackToDefault() + { + static void RunTest() + { + double actual = RuntimeSettingParser.ParseDoubleEnvironmentVariableValue("FOO_BAR", -0.42); + Assert.Equal(-0.42, actual); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["FOO_BAR"] = "-~4!"; + + RemoteExecutor.Invoke(RunTest, options).Dispose(); + } + } +} diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 186f1cbd888..3b950f2a36c 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -1,4 +1,4 @@ - + ../../src/Resources/Strings.resx true @@ -236,6 +236,8 @@ Link="ProductionCode\System\Net\Http\StreamToStreamCopy.cs" /> + + From 30d770d7385f2c2b6ce9f6cbafdb0d29a0ed94cd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 8 Jul 2021 02:09:15 +0200 Subject: [PATCH 323/926] Auto-enable SO_REUSE_UNICASTPORT for ConnectEx (#54903) Fixes #48219. --- .../src/System/Net/Sockets/Socket.Windows.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 6f4e2ed63e7..736015bb014 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -235,9 +235,34 @@ namespace System.Net.Sockets if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, csep.IPEndPoint); + if (_socketType == SocketType.Stream && _protocolType == ProtocolType.Tcp) + { + EnableReuseUnicastPort(); + } + DoBind(csep.IPEndPoint, csep.SocketAddress); } + private void EnableReuseUnicastPort() + { + // By enabling SO_REUSE_UNICASTPORT, we defer actual port allocation until the ConnectEx call, + // so it can bind to ports from the Windows auto-reuse port range, if configured by an admin. + // The socket option is supported on Windows 10+, we are ignoring the SocketError in case setsockopt fails. + int optionValue = 1; + SocketError error = Interop.Winsock.setsockopt( + _handle, + SocketOptionLevel.Socket, + SocketOptionName.ReuseUnicastPort, + ref optionValue, + sizeof(int)); + + if (NetEventSource.Log.IsEnabled() && error != SocketError.Success) + { + error = SocketPal.GetLastSocketError(); + NetEventSource.Info($"Enabling SO_REUSE_UNICASTPORT failed with error code: {error}"); + } + } + internal unsafe bool ConnectEx(SafeSocketHandle socketHandle, IntPtr socketAddress, int socketAddressSize, From 893739ffe77df0eb927d3755e08647597dae32a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 7 Jul 2021 20:49:33 -0400 Subject: [PATCH 324/926] [mono] new Sdk that selects mono runtime components (#54432) * Work in progress: new Sdk that selects mono runtime components * Add props and targets description to the components doc * condition the _MonoRuntimeAvailableComponents by RuntimeIdentifier * [cmake] Write a component-manifest.props file during build If we're not building a mono aot cross compiler, generate a component-manifest.props file in artifacts/obj/mono// that indicates if the host will use static or dynamic components, and a list of the available components, and the properties for constructing their names. * Build Microsoft.NETCore.App.Runtime.Mono..Sdk shared framework nuget It seems to also generate a symbols nuget. And in the nuget there's a tools/mono-sdk-what-is-this.deps.json file from the SharedFrameworkHostFileNameOverride property. It would be nice to exclude that stuff. * put the compoonent-manifest.targets into the Sdk * delete WIP in mono/nuget/ * fixup static component names in component-manifest.targets * delete fixed fixme * add missing $ * fix whitespace * [cmake] switch to configure_file instead of file(CONFIGURE) * add missing trailing slashes in .props.in file * Add new Sdk packs to the workload manifest * rework component-manifest.targets to use ItemGroups; move to new SDK * Rename shared framework to Microsoft.NETCore.App.Runtime.Mono..Props.Sdk And only include component-manifest.props, not the targets * Update manifest to include the new Props.Sdk and MonoTargets.Sdk * Move RuntimeConfigParserTask into Microsoft.NET.Runtime.MonoTargets.Sdk Consolidate all platform-independent tasks and targets into a single Sdk * Add iossimulator-x86 props * update components design doc * Fix typo * improve docs * Add _MonoRuntimeComponentDontLink target output * Drop component-manifest.props into runtime pack build/ directory Remove from the Microsoft.NETCore.App.Mono.Props.Sdk workload nuget * remove Microsoft.NETCore.App.Mono.Props.Sdk * Import component-manifest.props from the runtime pack Move the MonoTargets.Sdk import to each target platform that we support * Fix typos Co-authored-by: Rolf Bjarne Kvinge * Apply suggestions from code review Co-authored-by: Ankit Jain * Add JsonToItemsTaskFactory * fix whitespace * Do some validation earlier in _MonoComputeAvailableComponentDefinitions * Read component-manifest.json using the JsonToItemsTaskFactory and bundle it in Microsoft.NET.Runtime.MonoTargets.Sdk * remove ResolvedRuntimePack import from WorkloadManifest.targets it's too early, and we have the JsonToItemsTaskFactory now to read the manifest * Generate component-manifest.json in CMakeLists.txt * Fix some copy-paste nits * Use RuntimeFlavor==mono for runtime pack build directory instead of TargetsMobile. We want the build files (mono-components.json) in every mono runtime pack, not just on mobile targets * Apply suggestions from code review Co-authored-by: Ankit Jain * rename component-manifest to RuntimeComponentManifest * fixup nullability annotations * fix whitespace * fix formatting * Misc fixes to JsonToItemsTaskFactory * Rename MonoRuntimeComponentManifestReadTask from MonoRuntimeComponentsReadManifestTask * undo nullability annotation Build doesn't like it for some reason (probably net472) * fix incorrect task parameter name * Remove Identity metadata from dictionary at json parsing time Also improve nullability a bit by making the properties immutable * Throw correct json deserializer exceptions * Catch JsonException in an async function We get nice error messages now like ``` src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets(8,5): error : Failed to deserialize json from file 'artifacts/bin/mono/iOSSimulator.x64.Release/build/RuntimeComponentManifest.json', JSON Path: $.items._MonoRuntimeAvailableComponents[2], Line: 14, Position: 1 [component-manifest.sample.proj] src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets(8,5): error : JsonException: The JSON value could not be converted to System.Collections.Generic.Dictionary`2[System.String,System.String]. Path: $.identity | LineNumber: 0 | BytePositionInLine: 16. Path: $.items._MonoRuntimeAvailableComponents[2] | LineNumber: 14 | BytePositionInLine: 1. [component-manifest.sample.proj] src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets(8,5): error : InvalidOperationException: Cannot get the value of a token type 'Number' as a string. [component-manifest.sample.proj] src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets(8,5): error : [component-manifest.sample.proj] ``` * fixup comments Co-authored-by: Rolf Bjarne Kvinge Co-authored-by: Ankit Jain --- docs/design/mono/components.md | 82 +++- .../Microsoft.NETCore.App.Runtime.props | 6 + src/mono/mono.proj | 9 + src/mono/mono/component/CMakeLists.txt | 22 + .../RuntimeComponentManifest.json.in | 16 + ...rosoft.NET.Runtime.MonoTargets.Sdk.pkgproj | 23 + .../README.md | 38 ++ .../Sdk/RuntimeComponentManifest.props | 14 + .../Sdk/RuntimeComponentManifest.targets | 112 +++++ .../Sdk/RuntimeConfigParserTask.props} | 0 .../Sdk/Sdk.props | 4 + .../Sdk/Sdk.targets | 3 + ...crosoft.NET.Runtime.MonoTargets.Sdk.props} | 0 ...T.Runtime.RuntimeConfigParser.Task.pkgproj | 16 - .../README.md | 29 -- .../WorkloadManifest.json.in | 8 +- .../WorkloadManifest.targets | 28 +- src/mono/nuget/mono-packages.proj | 5 +- .../JsonToItemsTaskFactory.cs | 398 ++++++++++++++++++ .../JsonToItemsTaskFactory.csproj | 32 ++ src/tasks/JsonToItemsTaskFactory/README.md | 86 ++++ 21 files changed, 855 insertions(+), 76 deletions(-) create mode 100644 src/mono/mono/component/RuntimeComponentManifest.json.in create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/README.md create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.props create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets rename src/mono/nuget/{Microsoft.NET.Runtime.RuntimeConfigParser.Task/Sdk/Sdk.props => Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeConfigParserTask.props} (100%) create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props create mode 100644 src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.targets rename src/mono/nuget/{Microsoft.NET.Runtime.RuntimeConfigParser.Task/build/Microsoft.NET.Runtime.RuntimeConfigParser.Task.props => Microsoft.NET.Runtime.MonoTargets.Sdk/build/Microsoft.NET.Runtime.MonoTargets.Sdk.props} (100%) delete mode 100644 src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Microsoft.NET.Runtime.RuntimeConfigParser.Task.pkgproj delete mode 100644 src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/README.md create mode 100644 src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.cs create mode 100644 src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj create mode 100644 src/tasks/JsonToItemsTaskFactory/README.md diff --git a/docs/design/mono/components.md b/docs/design/mono/components.md index 2dc982dbeaa..ef17836c864 100644 --- a/docs/design/mono/components.md +++ b/docs/design/mono/components.md @@ -164,10 +164,10 @@ To implement `feature_X` as a component. Carry out the following steps: { feature_X_cleanup }, feature_X_hello, }; - + MonoComponentFeatureX * mono_component_feature_X_init (void) { return &fn_table; } - + void feature_X_cleanup (MonoComponent *self) { static int cleaned = 0; @@ -207,10 +207,10 @@ To implement `feature_X` as a component. Carry out the following steps: { feature_X_cleanup }, feature_X_hello, }; - + MonoComponentFeatureX * mono_component_feature_X_init (void) { return &fn_table; } - + void feature_X_cleanup (MonoComponent *self) { static int cleaned = 0; @@ -229,7 +229,7 @@ To implement `feature_X` as a component. Carry out the following steps: ```c MonoComponentFeatureX* mono_component_feature_X (void); - + ... MonoComponentFeatureX* mono_component_feature_X_stub_init (void); @@ -238,7 +238,7 @@ To implement `feature_X` as a component. Carry out the following steps: * Add an entry to the `components` list to load the component to `mono/metadata/components.c`, and also implement the getter for the component: ```c static MonoComponentFeatureX *feature_X = NULL; - + MonoComponentEntry components[] = { ... {"feature_X", "feature_X", COMPONENT_INIT_FUNC (feature_X), (MonoComponent**)&feature_X, NULL }, @@ -265,16 +265,66 @@ To implement `feature_X` as a component. Carry out the following steps: ## Detailed design - Packaging and runtime packs The components are building blocks to put together a functional runtime. The -runtime pack includes the base runtime and the components and additional -properties and targets that enable the workload to construct a runtime for -various scenarios. +runtime pack includes the base runtime and the components. The mono workload +includes the runtime pack and additional tasks, properties and targets that +enable the workload to construct a runtime for various scenarios. -In each runtime pack we include: +For the target RID, we expose: -- The compiled compnents for the apropriate host architectures in a well-known subdirectory -- An MSBuild props file that defines an item group that list each component name and has metadata that indicates: - - the path to the component in the runtime pack - - the path to the stub component in the runtime pack (if components are static) -- An MSBuild targets file that defines targets to copy a specified set of components to the app publish folder (if components are dynamic); or to link the runtime together with stubs and a set of enabled components (if components are static) +- `@(_MonoRuntimeComponentLinking)` set to either `'static'` or `'dynamic'` depending on whether the + current runtime pack for the current target includes runtime components as static archives or as + shared libraries, respectively. +- `@(_MonoRuntimeComponentSharedLibExt)` and `@(_MonoRuntimeComponentStaticLibExt)` set to the file + extension of the runtime components for the current target (ie, `'.a', '.so', '.dylib'` etc). +- `@(_MonoRuntimeAvailableComponents)` a list of component names without the `lib` prefix (if any) + or file extensions. For example: `'hot_reload; diagnostics_tracing'`. -** TODO ** Write this up in more detail +Each of the above item lists has `RuntimeIdentifier` metadata. For technical reasons the mono +workload will provide a single `@(_MonoRuntimeAvailableComponent)` item list for all platforms. We +use the `RuntimeIdentifier` metadata to filter out the details applicable for the current platform. + +- The target `_MonoSelectRuntimeComponents` that has the following inputs and outputs: + - input `@(_MonoComponent)` (to be set by the workload) : a list of components that a workload wants to use for the current + app. It is an error if this specifies any unknown component name. + - output `@(_MonoRuntimeSelectedComponents)` and `@(_MonoRuntimeSelectedStubComponents)` The names + of the components that were (resp, were not) selected. For example `'hot_reload; + diagnostics_tracing'`. Each item has two metadata properties `ComponentLib` and + `ComponentStubLib` (which may be empty) that specify the name of the static or dynamic library + of the component. This is not the main output of the target, it's primarily for debugging. + - output `@(_MonoRuntimeComponentLink)` a list of library names (relative to the `native/` + subdirectory of the runtime pack) that (for dynamic components) must be placed next to the + runtime in the application bundle, or (for static components) that must be linked with the + runtime to enable the components' functionality. Each item in the list has metadata + `ComponentName` (e.g. `'hot_reload'`), `IsStub` (`true` or `false`), `Linking` (`'static'` or + `'dynamic'`). This output should be used by the workloads when linking the app and runtime if + the workload uses an allow list of native libraries to link or bundle. + - output `@(_MonoRuntimeComponentDontLink)` a list of library names (relative to the `native/` + subdirectory of the runtime pack) that should be excluded from the application bundle (for + dynamic linking) or that should not be passed to the native linker (for static linking). This + output should be used by workloads that just link or bundle every native library from `native/` + in order to filter the contents of the subdirectory to exclude the disabled components (and to + exclude the static library stubs for the enabled components when static linking). + +Generally workloads should only use one of `@(_MonoRuntimeComponentLink)` or +`@(_MonoRuntimeComponentDontLink)`, depending on whether they use an allow or block list for the +contents of the `native/` subdirectory. + +Example fragment (assuming the mono workload has been imported): + +```xml + + + <_MonoComponent Include="hot_reload;diagnostics_tracing" /> + + + + + + + + + + + + +``` diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props index 2489544cad8..4eea2cf850f 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props @@ -70,6 +70,12 @@ runtimes/$(RuntimeIdentifier)/native/include/%(RecursiveDir) + + runtimes/$(RuntimeIdentifier)/build/%(RecursiveDir) + + runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native diff --git a/src/mono/mono.proj b/src/mono/mono.proj index e459f872177..c26eb9f4e2e 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -431,6 +431,9 @@ <_MonoCMakeArgs Include="-DSTATIC_COMPONENTS=1" /> + + <_MonoCMakeArgs Include="-DMONO_COMPONENTS_RID=$(TargetOS)-$(TargetArchitecture)" /> + <_MonoCFLAGSOption>-DCMAKE_C_FLAGS="@(_MonoCPPFLAGS, ' ') @(_MonoCFLAGS, ' ')" @@ -770,6 +773,7 @@ $(RuntimeBinDir)cross\$(PackageRID)\opt$(ExeExt) <_MonoIncludeArtifacts Include="$(MonoObjDir)out\include\**" /> + <_MonoRuntimeBuildArtifacts Include="$(MonoObjDir)\build\**" /> <_MonoRuntimeArtifacts Condition="'$(_MonoIncludeInterpStaticFiles)' == 'true'" Include="$(MonoObjDir)out\lib\libmono-ee-interp.a"> $(RuntimeBinDir)libmono-ee-interp.a @@ -816,6 +820,11 @@ SkipUnchangedFiles="true" Condition="'$(MonoGenerateOffsetsOSGroups)' == '' and ('$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true' or '$(TargetsAndroid)' == 'true' or '$(TargetsBrowser)' == 'true')"/> + + diff --git a/src/mono/mono/component/CMakeLists.txt b/src/mono/mono/component/CMakeLists.txt index 62ab5b34528..1bc4eadb672 100644 --- a/src/mono/mono/component/CMakeLists.txt +++ b/src/mono/mono/component/CMakeLists.txt @@ -82,6 +82,7 @@ set(${MONO_DIAGNOSTICS_TRACING_COMPONENT_NAME}-dependencies #define a library for each component and component stub function(define_component_libs) + # NOTE: keep library naming pattern in sync with RuntimeComponentManifest.targets if (NOT DISABLE_LIBS) foreach(component IN LISTS components) add_library("mono-component-${component}-static" STATIC $) @@ -121,6 +122,7 @@ endforeach() if(NOT DISABLE_COMPONENTS AND NOT STATIC_COMPONENTS) # define a shared library for each component foreach(component IN LISTS components) + # NOTE: keep library naming pattern in sync with RuntimeComponentManifest.targets if(HOST_WIN32) add_library("mono-component-${component}" SHARED "${${component}-sources}") target_compile_definitions("mono-component-${component}" PRIVATE -DCOMPILING_COMPONENT_DYNAMIC;-DMONO_DLL_IMPORT) @@ -172,6 +174,26 @@ foreach(component IN LISTS components) list(APPEND mono-components-stub-objects $) endforeach() +if(NOT MONO_CROSS_COMPILE) + set(TemplateMonoRuntimeComponentSharedLibExt "${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(TemplateMonoRuntimeComponentStaticLibExt "${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(TemplateRuntimeIdentifier "${MONO_COMPONENTS_RID}") + if(DISABLE_COMPONENTS) + set(TemplateMonoRuntimeComponentLinking "static") + set(TemplateMonoRuntimeAvailableComponents "") + else() + list(TRANSFORM components REPLACE "^(.+)$" "{ \"identity\": \"\\1\", \"RuntimeIdentifier\": \"${TemplateRuntimeIdentifier}\" }," OUTPUT_VARIABLE TemplateMonoRuntimeAvailableComponentsList) + list(JOIN TemplateMonoRuntimeAvailableComponentsList "\n" TemplateMonoRuntimeAvailableComponents) + if(STATIC_COMPONENTS) + set(TemplateMonoRuntimeComponentLinking "static") + else() + set(TemplateMonoRuntimeComponentLinking "dynamic") + endif() + endif() + # Write a RuntimeComponentManifest.json file in the artifacts/obj/mono//build/ directory + # without the ../.. the file would go in artifacts/obj/mono//mono/mini + configure_file( "${MONO_COMPONENT_PATH}/RuntimeComponentManifest.json.in" "../../build/RuntimeComponentManifest.json") +endif() # component tests set(MONO_EVENTPIPE_TEST_SOURCE_PATH "${MONO_EVENTPIPE_SHIM_SOURCE_PATH}/test") diff --git a/src/mono/mono/component/RuntimeComponentManifest.json.in b/src/mono/mono/component/RuntimeComponentManifest.json.in new file mode 100644 index 00000000000..a7ee6a8a83f --- /dev/null +++ b/src/mono/mono/component/RuntimeComponentManifest.json.in @@ -0,0 +1,16 @@ +{ + "items": { + "_MonoRuntimeComponentLinking": [ + { "identity": "${TemplateMonoRuntimeComponentLinking}", "RuntimeIdentifier": "${TemplateRuntimeIdentifier}" }, + ], + "_MonoRuntimeComponentSharedLibExt": [ + { "identity": "${TemplateMonoRuntimeComponentSharedLibExt}", "RuntimeIdentifier": "${TemplateRuntimeIdentifier}" }, + ], + "_MonoRuntimeComponentStaticLibExt": [ + { "identity": "${TemplateMonoRuntimeComponentStaticLibExt}", "RuntimeIdentifier": "${TemplateRuntimeIdentifier}" }, + ], + "_MonoRuntimeAvailableComponents": [ + ${TemplateMonoRuntimeAvailableComponents} + ], + } +} diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj new file mode 100644 index 00000000000..adf1b80c2f3 --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Microsoft.NET.Runtime.MonoTargets.Sdk.pkgproj @@ -0,0 +1,23 @@ + + + + + Provides the tasks+targets, for consumption by mono-based workloads + + + + + + + + + + + + + + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/README.md b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/README.md new file mode 100644 index 00000000000..9cb722152af --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/README.md @@ -0,0 +1,38 @@ +# Mono Runtime Host support targets + +This Sdk provides additional tasks and targets for workloads hosting the MonoVM .NET runtime. + +## component-manifest.targets + +See https://github.com/dotnet/runtime/blob/main/docs/design/mono/components.md + +## RuntimeConfigParserTask +The `RuntimeConfigParserTask` task converts a json `runtimeconfig.json` to a binary blob for MonoVM's `monovm_runtimeconfig_initialize` API. +To use the task in a project, reference the NuGet package, with the appropriate nuget source. + +### NuGet.config +```xml + + + + + + +``` + +### In the project file +```xml + + + + + + + + + + +``` diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.props new file mode 100644 index 00000000000..d173ff67c2b --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.props @@ -0,0 +1,14 @@ + + + $(MSBuildThisFileDirectory)..\tasks\net6.0\JsonToItemsTaskFactory.dll + $(MSBuildThisFileDirectory)..\tasks\net472\JsonToItemsTaskFactory.dll + + + + <_MonoRuntimeComponentSharedLibExt ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" /> + <_MonoRuntimeComponentStaticLibExt ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" /> + <_MonoRuntimeComponentLinking ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" /> + <_MonoRuntimeAvailableComponents ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" /> + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets new file mode 100644 index 00000000000..411aeec01e5 --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeComponentManifest.targets @@ -0,0 +1,112 @@ + + + + + <_MonoRuntimeComponentManifestJsonFilePath Condition="'$(_MonoRuntimeComponentManifestJsonFilePath)' == ''">%(ResolvedRuntimePack.PackageDirectory)\runtimes\$(RuntimeIdentifier)\build\RuntimeComponentManifest.json + + + + + + + + + + + + + + + <_MonoRuntimeComponentCurrentSharedLibExt>@(_MonoRuntimeComponentSharedLibExt->WithMetadataValue('RuntimeIdentifier', '$(RuntimeIdentifier)')) + <_MonoRuntimeComponentCurrentStaticLibExt>@(_MonoRuntimeComponentStaticLibExt->WithMetadataValue('RuntimeIdentifier', '$(RuntimeIdentifier)')) + <_MonoRuntimeComponentCurrentLinking>@(_MonoRuntimeComponentLinking->WithMetadataValue('RuntimeIdentifier', '$(RuntimeIdentifier)')) + + + + + <_MonoRuntimeComponentName Include="@(_MonoRuntimeAvailableComponents)" Condition="'%(RuntimeIdentifier)' == '$(RuntimeIdentifier)'"> + libmono-component-%(Identity)$(_MonoRuntimeComponentCurrentSharedLibExt) + + + + <_MonoRuntimeComponentName Include="@(_MonoRuntimeAvailableComponents)" Condition="'%(RuntimeIdentifier)' == '$(RuntimeIdentifier)'"> + libmono-component-%(Identity)-static$(_MonoRuntimeComponentCurrentStaticLibExt) + libmono-component-%(Identity)-stub-static$(_MonoRuntimeComponentCurrentStaticLibExt) + + + + + + + + + + + + + + + + + + + + + + <_MonoRuntimeComponentNameForRid Include="@(_MonoRuntimeComponentName)" Condition="'%(RuntimeIdentifier)' == '$(RuntimeIdentifier)'" /> + <_MonoRuntimeSelectedComponents Include="@(_MonoRuntimeComponentNameForRid)" Condition="'@(_MonoRuntimeComponentNameForRid)' == '@(_MonoComponent)' and '%(Identity)' != ''" /> + <_MonoRuntimeUnSelectedComponents Include="@(_MonoRuntimeComponentNameForRid)" Exclude="@(_MonoComponent)" /> + <_MonoRuntimeComponentLink Include="%(_MonoRuntimeSelectedComponents.ComponentLib)"> + $(_MonoRuntimeComponentCurrentLinking) + false + %(_MonoRuntimeSelectedComponents.Identity) + + <_MonoRuntimeComponentDontLink Include="%(_MonoRuntimeUnSelectedComponents.ComponentLib)"> + $(_MonoRuntimeComponentCurrentLinking) + false + %(_MonoRuntimeUnSelectedComponents.Identity) + + + <_MonoRuntimeSelectedMissingComponents Include="@(_MonoComponent)" Exclude="@(_MonoRuntimeComponentNameForRid)" /> + + + + + <_MonoRuntimeSelectedStubComponents Include="@(_MonoRuntimeComponentNameForRid)" Exclude="@(_MonoComponent)" /> + <_MonoRuntimeUnSelectedStubComponents Include="@(_MonoRuntimeComponentNameForRid)" Condition="'@(_MonoRuntimeComponentNameForRid)' == '@(_MonoComponent)' and '%(Identity)' != ''" /> + <_MonoRuntimeComponentLink Include="%(_MonoRuntimeSelectedStubComponents.ComponentStubLib)"> + $(_MonoRuntimeComponentCurrentLinking) + true + %(_MonoRuntimeSelectedStubComponents.Identity) + + <_MonoRuntimeComponentDontLink Include="%(_MonoRuntimeUnSelectedStubComponents.ComponentStubLib)"> + $(_MonoRuntimeComponentCurrentLinking) + true + %(_MonoRuntimeUnSelectedStubComponents.Identity) + + + + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Sdk/Sdk.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeConfigParserTask.props similarity index 100% rename from src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Sdk/Sdk.props rename to src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/RuntimeConfigParserTask.props diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props new file mode 100644 index 00000000000..d292fe15f27 --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props @@ -0,0 +1,4 @@ + + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.targets b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.targets new file mode 100644 index 00000000000..5fdf494324c --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/build/Microsoft.NET.Runtime.RuntimeConfigParser.Task.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/build/Microsoft.NET.Runtime.MonoTargets.Sdk.props similarity index 100% rename from src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/build/Microsoft.NET.Runtime.RuntimeConfigParser.Task.props rename to src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/build/Microsoft.NET.Runtime.MonoTargets.Sdk.props diff --git a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Microsoft.NET.Runtime.RuntimeConfigParser.Task.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Microsoft.NET.Runtime.RuntimeConfigParser.Task.pkgproj deleted file mode 100644 index 9591b89b4b9..00000000000 --- a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/Microsoft.NET.Runtime.RuntimeConfigParser.Task.pkgproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Provides the RuntimeConfigParser task - - - - - - - - - - - diff --git a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/README.md b/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/README.md deleted file mode 100644 index 1d55762a69d..00000000000 --- a/src/mono/nuget/Microsoft.NET.Runtime.RuntimeConfigParser.Task/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# RuntimeConfigParser MSBuild Task NuPkg -The `RuntimeConfigParser` MSBuild task is also useful outside the context of `dotnet/runtime`. The task is made available through a NuGet Package containing the `RuntimeConfigParser.dll` assembly produced from building `RuntimeConfigParser.csproj`. To use the task in a project, reference the NuGet package, with the appropriate nuget source. - -## NuGet.config -``` - - - - - - -``` - -## In the project file -``` - - - - - - - - - - -``` diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index 059efeed70c..e6a7ff1ee2a 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -83,17 +83,17 @@ "abstract": true, "description": "Shared native build tooling for Mono runtime", "packs": [ - "Microsoft.NET.Runtime.RuntimeConfigParser.Task", "Microsoft.NET.Runtime.MonoAOTCompiler.Task", + "Microsoft.NET.Runtime.MonoTargets.Sdk", ], } }, "packs": { - "Microsoft.NET.Runtime.RuntimeConfigParser.Task": { + "Microsoft.NET.Runtime.MonoAOTCompiler.Task": { "kind": "Sdk", "version": "${PackageVersion}" }, - "Microsoft.NET.Runtime.MonoAOTCompiler.Task": { + "Microsoft.NET.Runtime.MonoTargets.Sdk": { "kind": "Sdk", "version": "${PackageVersion}" }, @@ -270,4 +270,4 @@ "version": "${PackageVersion}" }, } -} \ No newline at end of file +} diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets index 4176041469b..c93e50175e9 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets @@ -10,7 +10,10 @@ - + + + + @@ -18,32 +21,37 @@ - - - + + - - - + + - - - + + + + + + + + + + diff --git a/src/mono/nuget/mono-packages.proj b/src/mono/nuget/mono-packages.proj index 9403ca9fb34..a56c63f8827 100644 --- a/src/mono/nuget/mono-packages.proj +++ b/src/mono/nuget/mono-packages.proj @@ -16,6 +16,9 @@ - + + + + diff --git a/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.cs b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.cs new file mode 100644 index 00000000000..2f7d1dc7ebf --- /dev/null +++ b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.cs @@ -0,0 +1,398 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#if NET472 +namespace System.Diagnostics.CodeAnalysis { + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + + public class NotNullWhenAttribute : Attribute { + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + public bool ReturnValue { get; } + } +} +#endif + +namespace JsonToItemsTaskFactory +{ + + /// Reads a json input blob and populates some output items + /// + /// JSON should follow this structure - the toplevel "properties" and "items" keys are exact, other keys are arbitrary. + /// + /// { + /// "properties" : { + /// "propName1": "value1", + /// "propName2": "value" + /// }, + /// "items" : { + /// "itemName1": [ "stringValue", { "identity": "anotherValue", "metadataKey": "metadataValue", ... }, "thirdValue" ], + /// "itemName2": [ ... ] + /// } + /// + /// + /// A task can be declared by + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// And then used in a target. The `JsonFilePath' attribute is used to specify the json file to read. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public class JsonToItemsTaskFactory : ITaskFactory + { + private const string JsonFilePath = "JsonFilePath"; + private TaskPropertyInfo[]? _taskProperties; + private string? _taskName; + + private bool _logDebugTask; + + public JsonToItemsTaskFactory() {} + + public string FactoryName => "JsonToItemsTaskFactory"; + + public Type TaskType => typeof(JsonToItemsTask); + + public bool Initialize(string taskName, IDictionary parameterGroup, string? taskBody, IBuildEngine taskFactoryLoggingHost) + { + _taskName = taskName; + if (taskBody != null && taskBody.StartsWith("debug", StringComparison.InvariantCultureIgnoreCase)) + _logDebugTask = true; + var log = new TaskLoggingHelper(taskFactoryLoggingHost, _taskName); + if (!ValidateParameterGroup (parameterGroup, log)) + return false; + _taskProperties = new TaskPropertyInfo[parameterGroup.Count + 1]; + _taskProperties[0] = new TaskPropertyInfo(nameof(JsonFilePath), typeof(string), output: false, required: true); + parameterGroup.Values.CopyTo(_taskProperties, 1); + return true; + } + + public TaskPropertyInfo[] GetTaskParameters() => _taskProperties!; + + public ITask CreateTask(IBuildEngine taskFactoryLoggingHost) + { + var log = new TaskLoggingHelper(taskFactoryLoggingHost, _taskName); + if (_logDebugTask) log.LogMessage(MessageImportance.Low, "CreateTask called"); + return new JsonToItemsTask(_taskName!, _logDebugTask); + } + + public void CleanupTask(ITask task) {} + + internal bool ValidateParameterGroup(IDictionary parameterGroup, TaskLoggingHelper log) + { + var taskName = _taskName ?? ""; + foreach (var kvp in parameterGroup) + { + var propName = kvp.Key; + var propInfo = kvp.Value; + if (string.Equals(propName, nameof(JsonFilePath), StringComparison.InvariantCultureIgnoreCase)) + { + log.LogError($"Task {taskName}: {nameof(JsonFilePath)} parameter must not be declared. It is implicitly added by the task."); + continue; + } + + if (!propInfo.Output) + { + log.LogError($"Task {taskName}: parameter {propName} is not an output. All parameters except {nameof(JsonFilePath)} must be outputs"); + continue; + } + if (propInfo.Required) + { + log.LogError($"Task {taskName}: parameter {propName} is an output but is marked required. That's not supported."); + } + if (typeof(ITaskItem[]).IsAssignableFrom(propInfo.PropertyType)) + continue; // ok, an item list + if (typeof(string).IsAssignableFrom(propInfo.PropertyType)) + continue; // ok, a string property + + log.LogError($"Task {taskName}: parameter {propName} is not an output of type System.String or Microsoft.Build.Framework.ITaskItem[]"); + } + return !log.HasLoggedErrors; + } + + public class JsonToItemsTask : IGeneratedTask + { + private IBuildEngine? _buildEngine; + public IBuildEngine BuildEngine { get => _buildEngine!; set { _buildEngine = value; SetBuildEngine(value);} } + public ITaskHost? HostObject { get; set; } + + private TaskLoggingHelper? _log; + private TaskLoggingHelper Log { get => _log!; set { _log = value; } } + + private void SetBuildEngine(IBuildEngine buildEngine) + { + Log = new TaskLoggingHelper(buildEngine, TaskName); + } + + public static JsonSerializerOptions JsonOptions => new() + { + PropertyNameCaseInsensitive = true, + AllowTrailingCommas = true, + }; + private string? jsonFilePath; + + private readonly bool _logDebugTask; // print stuff to the log for debugging the task + + private JsonModelRoot? jsonModel; + public string TaskName {get;} + public JsonToItemsTask(string taskName, bool logDebugTask = false) + { + TaskName = taskName; + _logDebugTask = logDebugTask; + } + + public bool Execute() + { + if (jsonFilePath == null) + { + Log.LogError($"no {nameof(JsonFilePath)} specified"); + return false; + } + if (!TryGetJson(jsonFilePath, out var json)) + return false; + + if (_logDebugTask) + { + LogParsedJson(json); + } + jsonModel = json; + return true; + } + + public bool TryGetJson(string jsonFilePath, [NotNullWhen(true)] out JsonModelRoot? json) + { + FileStream? file = null; + try + { + try + { + file = File.OpenRead(jsonFilePath); + } + catch (FileNotFoundException fnfe) + { + Log.LogErrorFromException(fnfe); + json = null; + return false; + } + json = GetJsonAsync(jsonFilePath, file).Result; + if (json == null) + { + // the async task may have already caught an exception and logged it. + if (!Log.HasLoggedErrors) Log.LogError($"Failed to deserialize json from file {jsonFilePath}"); + return false; + } + return true; + } + finally + { + if (file != null) + file.Dispose(); + } + } + + public async Task GetJsonAsync(string jsonFilePath, FileStream file) + { + JsonModelRoot? json = null; + try + { + json = await JsonSerializer.DeserializeAsync(file, JsonOptions).ConfigureAwait(false); + } + catch (JsonException e) + { + Log.LogError($"Failed to deserialize json from file '{jsonFilePath}', JSON Path: {e.Path}, Line: {e.LineNumber}, Position: {e.BytePositionInLine}"); + Log.LogErrorFromException(e, showStackTrace: false, showDetail: true, file: null); + } + return json; + } + + internal void LogParsedJson (JsonModelRoot json) + { + if (json.Properties != null) + { + Log.LogMessage(MessageImportance.Low, "json has properties: "); + foreach (var property in json.Properties) + { + Log.LogMessage(MessageImportance.Low, $" {property.Key} = {property.Value}"); + } + } + if (json.Items != null) + { + Log.LogMessage(MessageImportance.Low, "items: "); + foreach (var item in json.Items) + { + Log.LogMessage(MessageImportance.Low, $" {item.Key} = ["); + foreach (var value in item.Value) + { + Log.LogMessage(MessageImportance.Low, $" {value.Identity}"); + if (value.Metadata != null) + { + Log.LogMessage(MessageImportance.Low, " and some metadata, too"); + } + } + Log.LogMessage(MessageImportance.Low, " ]"); + } + } + } + + public object? GetPropertyValue(TaskPropertyInfo property) + { + bool isItem = false; + if (typeof(ITaskItem[]).IsAssignableFrom(property.PropertyType)) + { + if (_logDebugTask) Log.LogMessage(MessageImportance.Low, "GetPropertyValue called with @({0})", property.Name); + isItem = true; + } + else + { + if (_logDebugTask) Log.LogMessage(MessageImportance.Low, "GetPropertyValue called with $({0})", property.Name); + } + if (!isItem) + { + if (jsonModel?.Properties != null && jsonModel.Properties.TryGetValue(property.Name, out var value)) + { + return value; + } + Log.LogError("Property {0} not found in {1}", property.Name, jsonFilePath); + throw new Exception(); + } + else + { + if (jsonModel?.Items != null && jsonModel.Items.TryGetValue(property.Name, out var itemModels)) + { + return ConvertItems(itemModels); + } + + } + return null; + } + + public static ITaskItem[] ConvertItems(JsonModelItem[] itemModels) + { + var items = new ITaskItem[itemModels.Length]; + for (int i = 0; i < itemModels.Length; i++) + { + var itemModel = itemModels[i]; + var item = new TaskItem(itemModel.Identity); + if (itemModel.Metadata != null) + { + // assume Identity key was already removed in JsonModelItem + foreach (var metadata in itemModel.Metadata) + { + item.SetMetadata(metadata.Key, metadata.Value); + } + } + items[i] = item; + } + return items; + } + + public void SetPropertyValue(TaskPropertyInfo property, object? value) + { + if (_logDebugTask) Log.LogMessage(MessageImportance.Low, "SetPropertyValue called with {0}", property.Name); + if (property.Name == "JsonFilePath") + { + jsonFilePath = (string)value!; + } + else + throw new Exception($"JsonToItemsTask {TaskName} cannot set property {property.Name}"); + } + + } + + public class JsonModelRoot + { + [JsonConverter(typeof(CaseInsensitiveDictionaryConverter))] + public Dictionary? Properties {get; set;} + public Dictionary? Items {get; set;} + + public JsonModelRoot() {} + } + + [JsonConverter(typeof(JsonModelItemConverter))] + public class JsonModelItem + { + public string Identity {get;} + // n.b. will be deserialized case insensitive + public Dictionary? Metadata {get;} + + public JsonModelItem(string identity, Dictionary? metadata) + { + Identity = identity; + Metadata = metadata; + } + } + + public class CaseInsensitiveDictionaryConverter : JsonConverter> + { + public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var dict = JsonSerializer.Deserialize>(ref reader, options); + if (dict == null) + return null!; + return new Dictionary(dict, StringComparer.OrdinalIgnoreCase); + } + public override void Write(Utf8JsonWriter writer, Dictionary? value, JsonSerializerOptions options) => + JsonSerializer.Serialize(writer, value, options); + } + public class JsonModelItemConverter : JsonConverter + { + public JsonModelItemConverter() {} + + public override JsonModelItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringItem = reader.GetString(); + if (string.IsNullOrEmpty(stringItem)) + throw new JsonException ("deserialized json string item was null or the empty string"); + return new JsonModelItem(stringItem!, metadata: null); + case JsonTokenType.StartObject: + var dict = JsonSerializer.Deserialize>(ref reader, options); + if (dict == null) + return null!; + var idict = new Dictionary (dict, StringComparer.OrdinalIgnoreCase); + if (!idict.TryGetValue("Identity", out var identity) || string.IsNullOrEmpty(identity)) + throw new JsonException ("deserialized json dictionary item did not have a non-empty Identity metadata"); + else + idict.Remove("Identity"); + return new JsonModelItem(identity, metadata: idict); + default: + throw new NotSupportedException(); + } + } + public override void Write(Utf8JsonWriter writer, JsonModelItem value, JsonSerializerOptions options) + { + if (value.Metadata == null) + JsonSerializer.Serialize(writer, value.Identity); + else + JsonSerializer.Serialize(writer, value.Metadata); /* assumes Identity is in there */ + } + } + } +} diff --git a/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj new file mode 100644 index 00000000000..d6df214fce7 --- /dev/null +++ b/src/tasks/JsonToItemsTaskFactory/JsonToItemsTaskFactory.csproj @@ -0,0 +1,32 @@ + + + $(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks) + Library + true + false + enable + + + + + + + + + + + + + + + + + + <_PublishFramework Remove="@(_PublishFramework)" /> + <_PublishFramework Include="$(TargetFrameworks)" /> + + + + + + diff --git a/src/tasks/JsonToItemsTaskFactory/README.md b/src/tasks/JsonToItemsTaskFactory/README.md new file mode 100644 index 00000000000..619991ac13e --- /dev/null +++ b/src/tasks/JsonToItemsTaskFactory/README.md @@ -0,0 +1,86 @@ +# JsonToItemsTaskFactory + +A utility for reading json blobs into MSBuild items and properties. + +## Json blob format + +The json data must be a single toplevel dictionary with a `"properties"` or an `"items"` key (both are optional). + +The `"properties"` value must be a dictionary with more string values. The keys are case-insensitive and duplicates are not allowed. + +The `"items"` value must be an array of either string or dictionary elements (or a mix of both). +String elements use the string value as the `Identity`. +Dictionary elements must have strings as values, and must include an `"Identity"` key, and as many other metadata key/value pairs as desired. This dictionary is also case-insensitive and duplicate metadata keys are also not allowed. + +#### Example + +```json +{ + "properties": { + "x1": "val1", + "X2": "val2", + }, + "items" : { + "FunFiles": ["funFile1.txt", "funFile2.txt"], + "FilesWithMeta": [{"identity": "funFile3.txt", "TargetPath": "bin/fun3"}, + "funFile3.and.a.half.txt", + {"identity": "funFile4.txt", "TargetPath": "bin/fun4"}] + } +} +``` + +## UsingTask and Writing Targets + +To use the task, you need to reference the assembly and add the task to the project, as well as declare the task parameters that correspond to the properties and items you want to retrieve from the json blob. + +```xml + + + + + + + +``` + +The parameter group parameters are all optional. They must be non-required outputs of type `System.String` or `Microsoft.Build.Framework.ITaskItem[]`. The former declares properties to capture from the file, while the latter declares item lists. + +The above declares a task `MyJsonReader` which will be used to retries the `X1` property and the `FunFiles` and `FilesWithMeta` items. + +To use the task, a `JsonFilePath` attribute specifies the file to read. + +```xml + + + + + + + + + + + + + + + +``` + +When the target `RunMe` runs, the task will read the json file and populate the outputs. Running the target, the output will be: + +```console +$ dotnet build Example + X1 = val1 + FunFiles = funFile1.txt;funFile2.txt + FilesWithMeta = funFile3.txt TargetPath='bin/fun3' + FilesWithMeta = funFile4.txt TargetPath='bin/fun4' + FilesWithMeta = funFile3.and.a.half.txt (No TargetPath) + +Build succeeded. + 0 Warning(s) + 0 Error(s) +Time Elapsed 00:00:00.15 +``` + From 7f88911fe5f89836d1de7efd23f8541514a5ed05 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 7 Jul 2021 17:51:52 -0700 Subject: [PATCH 325/926] Use correct assignedInterval for SPILL_COST calculation (#55247) --- src/coreclr/jit/lsra.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index a2bb138ad0c..ecd4dbd9e46 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -11264,8 +11264,8 @@ void LinearScan::RegisterSelection::try_SPILL_COST() RefPosition* recentRefPosition = spillCandidateRegRecord->assignedInterval != nullptr ? spillCandidateRegRecord->assignedInterval->recentRefPosition : nullptr; - if ((recentRefPosition != nullptr) && (recentRefPosition->RegOptional()) && - !(currentInterval->isLocalVar && recentRefPosition->IsActualRef())) + if ((recentRefPosition != nullptr) && (recentRefPosition->RegOptional() && + !(spillCandidateRegRecord->assignedInterval->isLocalVar && recentRefPosition->IsActualRef()))) { // We do not "spillAfter" if previous (recent) refPosition was regOptional or if it // is not an actual ref. In those cases, we will reload in future (next) refPosition. From e30c2004c63d81b3579cd894b357cfc51c5b2c8d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 7 Jul 2021 22:20:36 -0400 Subject: [PATCH 326/926] Use Task.WaitAsync in SemaphoreSlim.WaitAsync (#55262) * Use Task.WaitAsync in SemaphoreSlim * Fix failing test The Cancel_WaitAsync_ContinuationInvokedAsynchronously test was failing, highlighting that we were no longer invoking the continuation asynchronously from the Cancel call. But in fact we were incompletely doing so in the past, such that we'd only force that asynchrony if no timeout was provided... if both a timeout and a token were provided, then we wouldn't. I've enhanced the test to validate both cases, and made sure we now pass. --- .../src/System/Threading/SemaphoreSlim.cs | 52 +++++++++---------- .../System/Threading/Tasks/TaskScheduler.cs | 28 ++++++++-- .../tests/SemaphoreSlimCancellationTests.cs | 11 ++-- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs index b9a28f2e4c4..bfa8ccd8680 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs @@ -701,35 +701,22 @@ namespace System.Threading Debug.Assert(asyncWaiter != null, "Waiter should have been constructed"); Debug.Assert(Monitor.IsEntered(m_lockObjAndDisposed), "Requires the lock be held"); - if (millisecondsTimeout != Timeout.Infinite) + await new ConfiguredNoThrowAwaiter(asyncWaiter.WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken)); + + if (cancellationToken.IsCancellationRequested) { - // Wait until either the task is completed, cancellation is requested, or the timeout occurs. - // We need to ensure that the Task.Delay task is appropriately cleaned up if the await - // completes due to the asyncWaiter completing, so we use our own token that we can explicitly - // cancel, and we chain the caller's supplied token into it. - using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) - { - if (asyncWaiter == await Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token)).ConfigureAwait(false)) - { - cts.Cancel(); // ensure that the Task.Delay task is cleaned up - return true; // successfully acquired - } - } - } - else // millisecondsTimeout == Timeout.Infinite - { - // Wait until either the task is completed or cancellation is requested. - var cancellationTask = new Task(null, TaskCreationOptions.RunContinuationsAsynchronously, promiseStyle: true); - using (cancellationToken.UnsafeRegister(static s => ((Task)s!).TrySetResult(), cancellationTask)) - { - if (asyncWaiter == await Task.WhenAny(asyncWaiter, cancellationTask).ConfigureAwait(false)) - { - return true; // successfully acquired - } - } + // If we might be running as part of a cancellation callback, force the completion to be asynchronous + // so as to maintain semantics similar to when no token is passed (neither Release nor Cancel would invoke + // continuations off of this task). + await TaskScheduler.Default; } - // If we get here, the wait has timed out or been canceled. + if (asyncWaiter.IsCompleted) + { + return true; // successfully acquired + } + + // The wait has timed out or been canceled. // If the await completed synchronously, we still hold the lock. If it didn't, // we no longer hold the lock. As such, acquire it. @@ -750,6 +737,19 @@ namespace System.Threading return await asyncWaiter.ConfigureAwait(false); } + // TODO https://github.com/dotnet/runtime/issues/22144: Replace with official nothrow await solution once available. + /// Awaiter used to await a task.ConfigureAwait(false) but without throwing any exceptions for faulted or canceled tasks. + private readonly struct ConfiguredNoThrowAwaiter : ICriticalNotifyCompletion + { + private readonly Task _task; + public ConfiguredNoThrowAwaiter(Task task) => _task = task; + public ConfiguredNoThrowAwaiter GetAwaiter() => this; + public bool IsCompleted => _task.IsCompleted; + public void GetResult() { } + public void UnsafeOnCompleted(Action continuation) => _task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(continuation); + public void OnCompleted(Action continuation) => _task.ConfigureAwait(false).GetAwaiter().OnCompleted(continuation); + } + /// /// Exits the once. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs index e416fe36519..2d901838707 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs @@ -529,11 +529,33 @@ namespace System.Threading.Tasks // returns the scheduler's GetScheduledTasks public IEnumerable? ScheduledTasks => m_taskScheduler.GetScheduledTasks(); } + + // TODO https://github.com/dotnet/runtime/issues/20025: Consider exposing publicly. + /// Gets an awaiter used to queue a continuation to this TaskScheduler. + internal TaskSchedulerAwaiter GetAwaiter() => new TaskSchedulerAwaiter(this); + + /// Awaiter used to queue a continuation to a specified task scheduler. + internal readonly struct TaskSchedulerAwaiter : ICriticalNotifyCompletion + { + private readonly TaskScheduler _scheduler; + public TaskSchedulerAwaiter(TaskScheduler scheduler) => _scheduler = scheduler; + public bool IsCompleted => false; + public void GetResult() { } + public void OnCompleted(Action continuation) => Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.DenyChildAttach, _scheduler); + public void UnsafeOnCompleted(Action continuation) + { + if (ReferenceEquals(_scheduler, Default)) + { + ThreadPool.UnsafeQueueUserWorkItem(s => s(), continuation, preferLocal: true); + } + else + { + OnCompleted(continuation); + } + } + } } - - - /// /// A TaskScheduler implementation that executes all tasks queued to it through a call to /// on the diff --git a/src/libraries/System.Threading/tests/SemaphoreSlimCancellationTests.cs b/src/libraries/System.Threading/tests/SemaphoreSlimCancellationTests.cs index 5b1263f84fd..91f2a1ff079 100644 --- a/src/libraries/System.Threading/tests/SemaphoreSlimCancellationTests.cs +++ b/src/libraries/System.Threading/tests/SemaphoreSlimCancellationTests.cs @@ -49,8 +49,10 @@ namespace System.Threading.Tests // currently we don't expose this.. but it was verified manually } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public static async Task Cancel_WaitAsync_ContinuationInvokedAsynchronously() + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [InlineData(false)] + [InlineData(true)] + public static async Task Cancel_WaitAsync_ContinuationInvokedAsynchronously(bool withTimeout) { await Task.Run(async () => // escape xunit's SynchronizationContext { @@ -60,7 +62,10 @@ namespace System.Threading.Tests var sentinel = new object(); var sem = new SemaphoreSlim(0); - Task continuation = sem.WaitAsync(cts.Token).ContinueWith(prev => + Task waitTask = withTimeout ? + sem.WaitAsync(TimeSpan.FromDays(1), cts.Token) : + sem.WaitAsync(cts.Token); + Task continuation = waitTask.ContinueWith(prev => { Assert.Equal(TaskStatus.Canceled, prev.Status); Assert.NotSame(sentinel, tl.Value); From 989ebf948ff48dd7b788994e2284017aa2827c6e Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Wed, 7 Jul 2021 21:19:56 -0700 Subject: [PATCH 327/926] update MsQuic version (#55291) * update MsQuic version * add also Versions.props --- eng/Version.Details.xml | 2 +- eng/Versions.props | 2 +- .../tests/FunctionalTests/QuicConnectionTests.cs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8301d951525..410d96a54a3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,7 +4,7 @@ https://github.com/dotnet/icu e7626ad8c04b150de635f920b5e8dede0aafaf73 - + https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b diff --git a/eng/Versions.props b/eng/Versions.props index 5240e72cbb2..ba4255d77e5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -169,7 +169,7 @@ 6.0.0-preview.7.21328.1 - 6.0.0-preview.7.21328.2 + 6.0.0-preview.7.21357.1 11.1.0-alpha.1.21328.1 11.1.0-alpha.1.21328.1 diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index 0e626962f96..8c81ce800dc 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -35,8 +35,6 @@ namespace System.Net.Quic.Tests Assert.Equal(ApplicationProtocol.ToString(), serverConnection.NegotiatedApplicationProtocol.ToString()); } - // this started to fail after updating msquic from cc104e836a5d4a5e0d324bc08b42136d2acac997 to 8e21db733f22533a613d404d2a86e64588019132 - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Fact] public async Task AcceptStream_ConnectionAborted_ByClient_Throws() { From b2bc284d64c82d02f82292cf7bd52ba158afcc62 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 8 Jul 2021 07:05:44 +0200 Subject: [PATCH 328/926] FileSystem.Exists.Unix: eliminate a stat call when checking a non-existing file/directory. (#55210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FileSystem.Exists.Unix: eliminate a stat call when checking a non-existing file/directory. When checking for a directory, we can use `stat`, there is no need to make an `lstat` call. When checking for a file, use `lstat` which will give us what we need in most cases. Only when it is a link, we need to call `stat` to check if the final target is not a directory. * Add back comment * Remove empty line Co-authored-by: David Cantú --- .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 6 +-- .../src/System/IO/FileSystem.Exists.Unix.cs | 52 +++++++++++-------- .../src/System/IO/FileSystem.Unix.cs | 3 +- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 1ce14b82abe..e247bf65dbf 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -98,11 +98,7 @@ namespace Microsoft.Win32.SafeHandles { Interop.Sys.FileStatus fileinfo; - // First use stat, as we want to follow symlinks. If that fails, it could be because the symlink - // is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate - // based on the symlink itself. - if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 && - Interop.Sys.LStat(fullPath, out fileinfo) < 0) + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0) { return false; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Exists.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Exists.Unix.cs index c0210d5b43b..1037b4020f7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Exists.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Exists.Unix.cs @@ -16,12 +16,29 @@ namespace System.IO private static bool DirectoryExists(ReadOnlySpan fullPath, out Interop.ErrorInfo errorInfo) { - return FileExists(fullPath, Interop.Sys.FileTypes.S_IFDIR, out errorInfo); + Interop.Sys.FileStatus fileinfo; + errorInfo = default(Interop.ErrorInfo); + + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0) + { + errorInfo = Interop.Sys.GetLastErrorInfo(); + return false; + } + + return (fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR; } public static bool FileExists(ReadOnlySpan fullPath) { Interop.ErrorInfo ignored; + return FileExists(fullPath, out ignored); + } + + private static bool FileExists(ReadOnlySpan fullPath, out Interop.ErrorInfo errorInfo) + { + Interop.Sys.FileStatus fileinfo; + errorInfo = default(Interop.ErrorInfo); + // File.Exists() explicitly checks for a trailing separator and returns false if found. FileInfo.Exists and all other // internal usages do not check for the trailing separator. Historically we've always removed the trailing separator // when getting attributes as trailing separators are generally not accepted by Windows APIs. Unix will take @@ -29,32 +46,25 @@ namespace System.IO // our historical behavior (outside of File.Exists()), we need to trim. // // See http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 for details. - return FileExists(Path.TrimEndingDirectorySeparator(fullPath), Interop.Sys.FileTypes.S_IFREG, out ignored); - } + fullPath = Path.TrimEndingDirectorySeparator(fullPath); - private static bool FileExists(ReadOnlySpan fullPath, int fileType, out Interop.ErrorInfo errorInfo) - { - Debug.Assert(fileType == Interop.Sys.FileTypes.S_IFREG || fileType == Interop.Sys.FileTypes.S_IFDIR); - - Interop.Sys.FileStatus fileinfo; - errorInfo = default(Interop.ErrorInfo); - - // First use stat, as we want to follow symlinks. If that fails, it could be because the symlink - // is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate - // based on the symlink itself. - if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 && - Interop.Sys.LStat(fullPath, out fileinfo) < 0) + if (Interop.Sys.LStat(fullPath, out fileinfo) < 0) { errorInfo = Interop.Sys.GetLastErrorInfo(); return false; } - // Something exists at this path. If the caller is asking for a directory, return true if it's - // a directory and false for everything else. If the caller is asking for a file, return false for - // a directory and true for everything else. - return - (fileType == Interop.Sys.FileTypes.S_IFDIR) == - ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); + // Something exists at this path. Return false for a directory and true for everything else. + // When the path is a link, get its target info. + if ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK) + { + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0) + { + return true; + } + } + + return (fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFDIR; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 224de5abcd5..9a759e7b2b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -219,8 +219,7 @@ namespace System.IO // Input allows trailing separators in order to match Windows behavior // Unix does not accept trailing separators, so must be trimmed - if (!FileExists(Path.TrimEndingDirectorySeparator(fullPath), - Interop.Sys.FileTypes.S_IFREG, out fileExistsError) && + if (!FileExists(fullPath, out fileExistsError) && fileExistsError.Error == Interop.Error.ENOENT) { return; From b877bcdf05863d9fd54f07ebba632f083e5fac43 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Wed, 7 Jul 2021 22:07:35 -0700 Subject: [PATCH 329/926] fix GCHandle free for connection and related fixes and asserts (#55303) Co-authored-by: Geoffrey Kizer --- .../MsQuic/MsQuicConnection.cs | 21 +++++++++---- .../Implementations/MsQuic/MsQuicListener.cs | 30 +++++++++++++++++-- .../Implementations/MsQuic/MsQuicStream.cs | 12 ++++++-- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 9e4005a6b5a..a90f03467b0 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -84,7 +84,6 @@ namespace System.Net.Quic.Implementations.MsQuic if (releaseHandles) { Handle?.Dispose(); - StateGCHandle.Free(); } } @@ -235,19 +234,22 @@ namespace System.Net.Quic.Implementations.MsQuic state.ConnectTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); } - state.AcceptQueue.Writer.Complete(); + state.AcceptQueue.Writer.TryComplete(); return MsQuicStatusCodes.Success; } private static uint HandleEventShutdownInitiatedByPeer(State state, ref ConnectionEvent connectionEvent) { state.AbortErrorCode = (long)connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; - state.AcceptQueue.Writer.Complete(); + state.AcceptQueue.Writer.TryComplete(); return MsQuicStatusCodes.Success; } private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent connectionEvent) { + // This is the final event on the connection, so free the GCHandle used by the event callback. + state.StateGCHandle.Free(); + state.Connection = null; state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success); @@ -533,6 +535,8 @@ namespace System.Net.Quic.Implementations.MsQuic _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; + Debug.Assert(_state.StateGCHandle.IsAllocated); + _state.Connection = this; try { @@ -547,6 +551,7 @@ namespace System.Net.Quic.Implementations.MsQuic } catch { + _state.StateGCHandle.Free(); _state.Connection = null; throw; } @@ -593,7 +598,10 @@ namespace System.Net.Quic.Implementations.MsQuic IntPtr context, ref ConnectionEvent connectionEvent) { - var state = (State)GCHandle.FromIntPtr(context).Target!; + GCHandle gcHandle = GCHandle.FromIntPtr(context); + Debug.Assert(gcHandle.IsAllocated); + Debug.Assert(gcHandle.Target is not null); + var state = (State)gcHandle.Target; if (NetEventSource.Log.IsEnabled()) { @@ -626,9 +634,11 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex.Message}"); + NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); } + Debug.Fail($"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); + // TODO: trigger an exception on any outstanding async calls. return MsQuicStatusCodes.InternalError; @@ -692,7 +702,6 @@ namespace System.Net.Quic.Implementations.MsQuic { // We may not be fully initialized if constructor fails. _state.Handle?.Dispose(); - if (_state.StateGCHandle.IsAllocated) _state.StateGCHandle.Free(); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs index 7d18fa08a47..97c1973e081 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; using System.Net.Quic.Implementations.MsQuic.Internal; using System.Net.Security; using System.Runtime.InteropServices; @@ -59,7 +60,6 @@ namespace System.Net.Quic.Implementations.MsQuic } catch { - _state.Handle?.Dispose(); _stateHandle.Free(); throw; } @@ -109,7 +109,15 @@ namespace System.Net.Quic.Implementations.MsQuic Stop(); _state?.Handle?.Dispose(); + + // Note that it's safe to free the state GCHandle here, because: + // (1) We called ListenerStop above, which will block until all listener events are processed. So we will not receive any more listener events. + // (2) This class is finalizable, which means we will always get called even if the user doesn't explicitly Dispose us. + // If we ever change this class to not be finalizable, and instead rely on the SafeHandle finalization, then we will need to make + // the SafeHandle responsible for freeing this GCHandle, since it will have the only chance to do so when finalized. + if (_stateHandle.IsAllocated) _stateHandle.Free(); + _state?.ConnectionConfiguration?.Dispose(); _disposed = true; } @@ -123,6 +131,8 @@ namespace System.Net.Quic.Implementations.MsQuic uint status; + Debug.Assert(_stateHandle.IsAllocated); + MemoryHandle[]? handles = null; QuicBuffer[]? buffers = null; try @@ -130,6 +140,11 @@ namespace System.Net.Quic.Implementations.MsQuic MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers); status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, ref address); } + catch + { + _stateHandle.Free(); + throw; + } finally { MsQuicAlpnHelper.Return(ref handles, ref buffers); @@ -167,7 +182,11 @@ namespace System.Net.Quic.Implementations.MsQuic return MsQuicStatusCodes.InternalError; } - State state = (State)GCHandle.FromIntPtr(context).Target!; + GCHandle gcHandle = GCHandle.FromIntPtr(context); + Debug.Assert(gcHandle.IsAllocated); + Debug.Assert(gcHandle.Target is not null); + var state = (State)gcHandle.Target; + SafeMsQuicConnectionHandle? connectionHandle = null; try @@ -197,6 +216,13 @@ namespace System.Net.Quic.Implementations.MsQuic } catch (Exception ex) { + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Error(state, $"[Listener#{state.GetHashCode()}] Exception occurred during handling {(QUIC_LISTENER_EVENT)evt.Type} connection callback: {ex}"); + } + + Debug.Fail($"[Listener#{state.GetHashCode()}] Exception occurred during handling {(QUIC_LISTENER_EVENT)evt.Type} connection callback: {ex}"); + // This handle will be cleaned up by MsQuic by returning InternalError. connectionHandle?.SetHandleAsInvalid(); state.AcceptConnectionQueue.Writer.TryComplete(ex); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 1d4d43ef72f..a94a7efeb27 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -695,7 +695,11 @@ namespace System.Net.Quic.Implementations.MsQuic IntPtr context, ref StreamEvent streamEvent) { - var state = (State)GCHandle.FromIntPtr(context).Target!; + GCHandle gcHandle = GCHandle.FromIntPtr(context); + Debug.Assert(gcHandle.IsAllocated); + Debug.Assert(gcHandle.Target is not null); + var state = (State)gcHandle.Target; + return HandleEvent(state, ref streamEvent); } @@ -746,9 +750,13 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex.Message}"); + NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex}"); } + // This is getting hit currently + // See https://github.com/dotnet/runtime/issues/55302 + //Debug.Fail($"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex}"); + return MsQuicStatusCodes.InternalError; } } From ae9b5c741adaf7a40414ffd37e81f59cce801cff Mon Sep 17 00:00:00 2001 From: Anirudh Agnihotry Date: Wed, 7 Jul 2021 23:01:16 -0700 Subject: [PATCH 330/926] add analyzers (#55306) --- src/libraries/Directory.Build.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index b330933c280..a3553ca940f 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -12,6 +12,7 @@ + IncludeAnalyzersInPackage;$(GenerateNuspecDependsOn) $(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrentBuildSettings)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'testhost', '$(NetCoreAppCurrentBuildSettings)')) From 9d4f795cf9b0bb1a1e33b03ea1fcc9db6bf025d5 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 7 Jul 2021 23:15:47 -0700 Subject: [PATCH 331/926] Sync HPack encoding changes from aspnetcore (#55288) --- .../aspnetcore/Http2/Hpack/HPackEncoder.cs | 30 +++++++++---------- .../aspnetcore/Http2/Hpack/HeaderField.cs | 2 +- .../SocketsHttpHandler/Http2Connection.cs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs index 67a61c3c69f..a7f33d2c929 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs @@ -78,7 +78,7 @@ namespace System.Net.Http.HPack } /// Encodes a "Literal Header Field without Indexing". - public static bool EncodeLiteralHeaderFieldWithoutIndexing(int index, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldWithoutIndexing(int index, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.2 // ------------------------------------------------------ @@ -97,7 +97,7 @@ namespace System.Net.Http.HPack if (IntegerEncoder.Encode(index, 4, destination, out int indexLength)) { Debug.Assert(indexLength >= 1); - if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength)) + if (EncodeStringLiteral(value, valueEncoding, destination.Slice(indexLength), out int nameLength)) { bytesWritten = indexLength + nameLength; return true; @@ -110,7 +110,7 @@ namespace System.Net.Http.HPack } /// Encodes a "Literal Header Field never Indexing". - public static bool EncodeLiteralHeaderFieldNeverIndexing(int index, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldNeverIndexing(int index, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.3 // ------------------------------------------------------ @@ -129,7 +129,7 @@ namespace System.Net.Http.HPack if (IntegerEncoder.Encode(index, 4, destination, out int indexLength)) { Debug.Assert(indexLength >= 1); - if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength)) + if (EncodeStringLiteral(value, valueEncoding, destination.Slice(indexLength), out int nameLength)) { bytesWritten = indexLength + nameLength; return true; @@ -142,7 +142,7 @@ namespace System.Net.Http.HPack } /// Encodes a "Literal Header Field with Indexing". - public static bool EncodeLiteralHeaderFieldIndexing(int index, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldIndexing(int index, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.2 // ------------------------------------------------------ @@ -161,7 +161,7 @@ namespace System.Net.Http.HPack if (IntegerEncoder.Encode(index, 6, destination, out int indexLength)) { Debug.Assert(indexLength >= 1); - if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength)) + if (EncodeStringLiteral(value, valueEncoding, destination.Slice(indexLength), out int nameLength)) { bytesWritten = indexLength + nameLength; return true; @@ -209,7 +209,7 @@ namespace System.Net.Http.HPack } /// Encodes a "Literal Header Field with Indexing - New Name". - public static bool EncodeLiteralHeaderFieldIndexingNewName(string name, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldIndexingNewName(string name, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.2 // ------------------------------------------------------ @@ -226,11 +226,11 @@ namespace System.Net.Http.HPack // | Value String (Length octets) | // +-------------------------------+ - return EncodeLiteralHeaderNewNameCore(0x40, name, value, destination, out bytesWritten); + return EncodeLiteralHeaderNewNameCore(0x40, name, value, valueEncoding, destination, out bytesWritten); } /// Encodes a "Literal Header Field without Indexing - New Name". - public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.2 // ------------------------------------------------------ @@ -247,11 +247,11 @@ namespace System.Net.Http.HPack // | Value String (Length octets) | // +-------------------------------+ - return EncodeLiteralHeaderNewNameCore(0, name, value, destination, out bytesWritten); + return EncodeLiteralHeaderNewNameCore(0, name, value, valueEncoding, destination, out bytesWritten); } /// Encodes a "Literal Header Field never Indexing - New Name". - public static bool EncodeLiteralHeaderFieldNeverIndexingNewName(string name, string value, Span destination, out int bytesWritten) + public static bool EncodeLiteralHeaderFieldNeverIndexingNewName(string name, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { // From https://tools.ietf.org/html/rfc7541#section-6.2.3 // ------------------------------------------------------ @@ -268,16 +268,16 @@ namespace System.Net.Http.HPack // | Value String (Length octets) | // +-------------------------------+ - return EncodeLiteralHeaderNewNameCore(0x10, name, value, destination, out bytesWritten); + return EncodeLiteralHeaderNewNameCore(0x10, name, value, valueEncoding, destination, out bytesWritten); } - private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, string value, Span destination, out int bytesWritten) + private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, string value, Encoding? valueEncoding, Span destination, out int bytesWritten) { if ((uint)destination.Length >= 3) { destination[0] = mask; if (EncodeLiteralHeaderName(name, destination.Slice(1), out int nameLength) && - EncodeStringLiteral(value, valueEncoding: null, destination.Slice(1 + nameLength), out int valueLength)) + EncodeStringLiteral(value, valueEncoding, destination.Slice(1 + nameLength), out int valueLength)) { bytesWritten = 1 + nameLength + valueLength; return true; @@ -643,7 +643,7 @@ namespace System.Net.Http.HPack #endif while (true) { - if (EncodeLiteralHeaderFieldWithoutIndexing(index, value, span, out int length)) + if (EncodeLiteralHeaderFieldWithoutIndexing(index, value, valueEncoding: null, span, out int length)) { return span.Slice(0, length).ToArray(); } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs index 5127e6fb953..aff43658c3a 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs @@ -36,7 +36,7 @@ namespace System.Net.Http.HPack { if (Name != null) { - return Encoding.ASCII.GetString(Name) + ": " + Encoding.ASCII.GetString(Value); + return Encoding.Latin1.GetString(Name) + ": " + Encoding.Latin1.GetString(Value); } else { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 61ddafcff96..af132084673 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1085,7 +1085,7 @@ namespace System.Net.Http if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(index)}={index}, {nameof(value)}={value}"); int bytesWritten; - while (!HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(index, value, headerBuffer.AvailableSpan, out bytesWritten)) + while (!HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(index, value, valueEncoding: null, headerBuffer.AvailableSpan, out bytesWritten)) { headerBuffer.EnsureAvailableSpace(headerBuffer.AvailableLength + 1); } From fdafe556e50ebaba73fa0fc2de730139772d1396 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Wed, 7 Jul 2021 23:40:59 -0700 Subject: [PATCH 332/926] Add EditMetadataByCreatingNewTypeAttribute (#55245) * Add EditMetadataByCreatingNewTypeAttribute * PR feedback: add the attribute to a ref assembly, make sealed, and update comment * Add EditMetadataByCreatingNewTypeAttribute to System.Private.CoreLib.Shared.projitems * Rename EditMetadataByCreatingNewTypeAttribute to CreateNewOnMetadataUpdateAttribute * Update src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CreateNewOnMetadataUpdateAttribute.cs Co-authored-by: Stephen Toub --- .../src/System.Private.CoreLib.Shared.projitems | 1 + .../CreateNewOnMetadataUpdateAttribute.cs | 13 +++++++++++++ .../ref/System.Runtime.Loader.cs | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CreateNewOnMetadataUpdateAttribute.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f344b005f93..2d0fb8a283d 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -692,6 +692,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CreateNewOnMetadataUpdateAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CreateNewOnMetadataUpdateAttribute.cs new file mode 100644 index 00000000000..b007cac6740 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CreateNewOnMetadataUpdateAttribute.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates a type should be replaced rather than updated when applying metadata updates. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] + public sealed class CreateNewOnMetadataUpdateAttribute : Attribute + { + } +} diff --git a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs index 6555a3093ee..830e9e6b1ec 100644 --- a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs +++ b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs @@ -25,6 +25,13 @@ namespace System.Reflection.Metadata public System.Type HandlerType { get { throw null; } } } } +namespace System.Runtime.CompilerServices +{ + [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false)] + public sealed class CreateNewOnMetadataUpdateAttribute : System.Attribute + { + } +} namespace System.Runtime.Loader { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] From 589942e876290fe051c62dfef3f4b463c5b34eeb Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 8 Jul 2021 00:09:03 -0700 Subject: [PATCH 333/926] Fix perf regressions with a recent change. (#55300) * Fix the perf regressions. * improve comments * Update src/coreclr/jit/lower.cpp Co-authored-by: Kunal Pathak Co-authored-by: Kunal Pathak --- src/coreclr/jit/compiler.h | 2 ++ src/coreclr/jit/compiler.hpp | 5 ----- src/coreclr/jit/lclvars.cpp | 27 +++++++++++++++++++++------ src/coreclr/jit/lower.cpp | 10 ++++++++++ src/coreclr/jit/morph.cpp | 10 ++++++++++ 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 36acfe6adec..360bc97a6b3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3406,6 +3406,8 @@ public: void lvaSetVarLiveInOutOfHandler(unsigned varNum); bool lvaVarDoNotEnregister(unsigned varNum); + void lvSetMinOptsDoNotEnreg(); + bool lvaEnregEHVars; bool lvaEnregMultiRegVars; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 0d6336d089b..d05bfc6bbeb 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1585,11 +1585,6 @@ inline unsigned Compiler::lvaGrabTemp(bool shortLifetime DEBUGARG(const char* re lvaTable[tempNum].lvIsTemp = shortLifetime; lvaTable[tempNum].lvOnFrame = true; - if (!compEnregLocals()) - { - lvaSetVarDoNotEnregister(tempNum DEBUGARG(Compiler::DNER_NoRegVars)); - } - // If we've started normal ref counting, bump the ref count of this // local, as we no longer do any incremental counting, and we presume // this new local will be referenced. diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 852c2586caa..603c6747c21 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1431,11 +1431,6 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, } #endif - if (!compEnregLocals()) - { - lvaSetVarDoNotEnregister(varNum DEBUGARG(Compiler::DNER_NoRegVars)); - } - #ifdef DEBUG varDsc->SetStackOffset(BAD_STK_OFFS); #endif @@ -1588,6 +1583,23 @@ bool Compiler::lvaVarDoNotEnregister(unsigned varNum) return varDsc->lvDoNotEnregister; } +//------------------------------------------------------------------------ +// lvInitializeDoNotEnregFlag: a helper to initialize `lvDoNotEnregister` flag +// for locals that were created before the compiler decided its optimization level. +// +// Assumptions: +// compEnregLocals() value is finalized and is set to false. +// +void Compiler::lvSetMinOptsDoNotEnreg() +{ + JITDUMP("compEnregLocals() is false, setting doNotEnreg flag for all locals."); + assert(!compEnregLocals()); + for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++) + { + lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_NoRegVars)); + } +} + /***************************************************************************** * Returns the handle to the class of the local variable varNum */ @@ -2626,7 +2638,6 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister break; case DNER_NoRegVars: JITDUMP("opts.compFlags & CLFLG_REGVAR is not set\n"); - assert((opts.compFlags & CLFLG_REGVAR) == 0); assert(!compEnregLocals()); break; case DNER_MinOptsGC: @@ -3503,6 +3514,10 @@ void Compiler::lvaSortByRefCount() varDsc->lvTracked = 0; lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_MinOptsGC)); } + if (!compEnregLocals()) + { + lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_NoRegVars)); + } #if defined(JIT32_GCENCODER) && defined(FEATURE_EH_FUNCLETS) if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0) { diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 8cbef1709fa..d2a0191648e 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -5886,6 +5886,16 @@ PhaseStatus Lowering::DoPhase() } #endif // !defined(TARGET_64BIT) + if (!comp->compEnregLocals()) + { + // Lowering is checking if lvDoNotEnregister is already set for contained optimizations. + // If we are running without `CLFLG_REGVAR` flag set (`compEnregLocals() == false`) + // then we already know that we won't enregister any locals and it is better to set + // `lvDoNotEnregister` flag before we start reading it. + // The main reason why this flag is not set is that we are running in minOpts. + comp->lvSetMinOptsDoNotEnreg(); + } + for (BasicBlock* const block : comp->Blocks()) { /* Make the block publicly available */ diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 432db3b0426..2b40d8548c6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -16004,6 +16004,16 @@ void Compiler::fgMorphBlocks() #endif + if (!compEnregLocals()) + { + // Morph is checking if lvDoNotEnregister is already set for some optimizations. + // If we are running without `CLFLG_REGVAR` flag set (`compEnregLocals() == false`) + // then we already know that we won't enregister any locals and it is better to set + // this flag before we start reading it. + // The main reason why this flag is not set is that we are running in minOpts. + lvSetMinOptsDoNotEnreg(); + } + /*------------------------------------------------------------------------- * Process all basic blocks in the function */ From 7b0cdaf184a6f9a1c72973c90a13d611f0b91cff Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 8 Jul 2021 09:19:30 +0200 Subject: [PATCH 334/926] Fix ILLink warnings related to SchemaMapping.SetupSchema* in S.Data.Common (#54897) * Fix ILLink warnings related to SchemaMapping.SetupSchemaWithoutKeyInfo in S.Data.Common * address feedback * remove ctor, clarify messages --- .../ref/System.Data.Common.cs | 25 ++++++++++++++++++- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------- .../src/System/Data/Common/DBSchemaRow.cs | 1 + .../src/System/Data/Common/DataAdapter.cs | 22 ++++++++++++++-- .../System/Data/Common/DataRecordInternal.cs | 2 ++ .../src/System/Data/Common/DbDataAdapter.cs | 14 +++++++++++ .../src/System/Data/Common/DbDataReader.cs | 3 +++ .../src/System/Data/Common/DbDataRecord.cs | 1 + .../src/System/Data/DataSet.cs | 3 +++ .../src/System/Data/DataTable.cs | 3 +++ .../src/System/Data/DataTableReader.cs | 3 +++ .../src/System/Data/IDataAdapter.cs | 4 +++ .../src/System/Data/IDataRecord.cs | 3 +++ .../Data/ProviderBase/DataReaderContainer.cs | 4 +++ .../System/Data/ProviderBase/SchemaMapping.cs | 9 ++++++- .../src/ILLink/ILLink.Suppressions.xml | 8 +++++- .../src/ILLink/ILLink.Suppressions.xml | 6 +++++ 17 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index c2dcc5261c3..5a2e754ba36 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -572,8 +572,11 @@ namespace System.Data public void InferXmlSchema(System.Xml.XmlReader? reader, string[]? nsArray) { } protected virtual void InitializeDerivedDataSet() { } protected bool IsBinarySerialized(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(System.Data.IDataReader reader, System.Data.LoadOption loadOption, params System.Data.DataTable[] tables) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public virtual void Load(System.Data.IDataReader reader, System.Data.LoadOption loadOption, System.Data.FillErrorEventHandler? errorHandler, params System.Data.DataTable[] tables) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(System.Data.IDataReader reader, System.Data.LoadOption loadOption, params string[] tables) { } public void Merge(System.Data.DataRow[] rows) { } public void Merge(System.Data.DataRow[] rows, bool preserveChanges, System.Data.MissingSchemaAction missingSchemaAction) { } @@ -762,8 +765,11 @@ namespace System.Data protected virtual System.Type GetRowType() { throw null; } protected virtual System.Xml.Schema.XmlSchema? GetSchema() { throw null; } public void ImportRow(System.Data.DataRow? row) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(System.Data.IDataReader reader) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(System.Data.IDataReader reader, System.Data.LoadOption loadOption) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public virtual void Load(System.Data.IDataReader reader, System.Data.LoadOption loadOption, System.Data.FillErrorEventHandler? errorHandler) { } public System.Data.DataRow LoadDataRow(object?[] values, bool fAcceptChanges) { throw null; } public System.Data.DataRow LoadDataRow(object?[] values, System.Data.LoadOption loadOption) { throw null; } @@ -1309,8 +1315,10 @@ namespace System.Data System.Data.MissingSchemaAction MissingSchemaAction { get; set; } System.Data.ITableMappingCollection TableMappings { get; } int Fill(System.Data.DataSet dataSet); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType); System.Data.IDataParameter[] GetFillParameters(); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] int Update(System.Data.DataSet dataSet); } public partial interface IDataParameter @@ -1357,6 +1365,7 @@ namespace System.Data System.DateTime GetDateTime(int i); decimal GetDecimal(int i); double GetDouble(int i); + [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields)] System.Type GetFieldType(int i); float GetFloat(int i); System.Guid GetGuid(int i); @@ -1756,7 +1765,7 @@ namespace System.Data.Common [System.ComponentModel.DefaultValueAttribute(false)] public bool ContinueUpdateOnError { get { throw null; } set { } } [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] - public System.Data.LoadOption FillLoadOption { get { throw null; } set { } } + public System.Data.LoadOption FillLoadOption { get { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] set { } } [System.ComponentModel.DefaultValueAttribute(System.Data.MissingMappingAction.Passthrough)] public System.Data.MissingMappingAction MissingMappingAction { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(System.Data.MissingSchemaAction.Add)] @@ -1775,8 +1784,11 @@ namespace System.Data.Common protected virtual int Fill(System.Data.DataSet dataSet, string srcTable, System.Data.IDataReader dataReader, int startRecord, int maxRecords) { throw null; } protected virtual int Fill(System.Data.DataTable dataTable, System.Data.IDataReader dataReader) { throw null; } protected virtual int Fill(System.Data.DataTable[] dataTables, System.Data.IDataReader dataReader, int startRecord, int maxRecords) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public virtual System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("dataReader's schema table types cannot be statically analyzed.")] protected virtual System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType, string srcTable, System.Data.IDataReader dataReader) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("dataReader's schema table types cannot be statically analyzed.")] protected virtual System.Data.DataTable? FillSchema(System.Data.DataTable dataTable, System.Data.SchemaType schemaType, System.Data.IDataReader dataReader) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public virtual System.Data.IDataParameter[] GetFillParameters() { throw null; } @@ -1789,6 +1801,7 @@ namespace System.Data.Common [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual bool ShouldSerializeFillLoadOption() { throw null; } protected virtual bool ShouldSerializeTableMappings() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public virtual int Update(System.Data.DataSet dataSet) { throw null; } } public sealed partial class DataColumnMapping : System.MarshalByRefObject, System.Data.IColumnMapping, System.ICloneable @@ -2256,10 +2269,15 @@ namespace System.Data.Common protected virtual int Fill(System.Data.DataTable dataTable, System.Data.IDbCommand command, System.Data.CommandBehavior behavior) { throw null; } protected virtual int Fill(System.Data.DataTable[] dataTables, int startRecord, int maxRecords, System.Data.IDbCommand command, System.Data.CommandBehavior behavior) { throw null; } public int Fill(int startRecord, int maxRecords, params System.Data.DataTable[] dataTables) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public override System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from command) schema table types cannot be statically analyzed.")] protected virtual System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType, System.Data.IDbCommand command, string srcTable, System.Data.CommandBehavior behavior) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public System.Data.DataTable[] FillSchema(System.Data.DataSet dataSet, System.Data.SchemaType schemaType, string srcTable) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public System.Data.DataTable? FillSchema(System.Data.DataTable dataTable, System.Data.SchemaType schemaType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from command) schema table types cannot be statically analyzed.")] protected virtual System.Data.DataTable? FillSchema(System.Data.DataTable dataTable, System.Data.SchemaType schemaType, System.Data.IDbCommand command, System.Data.CommandBehavior behavior) { throw null; } protected virtual System.Data.IDataParameter GetBatchedParameter(int commandIdentifier, int parameterIndex) { throw null; } protected virtual bool GetBatchedRecordsAffected(int commandIdentifier, out int recordsAffected, out System.Exception? error) { throw null; } @@ -2270,10 +2288,15 @@ namespace System.Data.Common protected virtual void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs value) { } object System.ICloneable.Clone() { throw null; } protected virtual void TerminateBatching() { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(System.Data.DataRow[] dataRows) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] protected virtual int Update(System.Data.DataRow[] dataRows, System.Data.Common.DataTableMapping tableMapping) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public override int Update(System.Data.DataSet dataSet) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(System.Data.DataSet dataSet, string srcTable) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(System.Data.DataTable dataTable) { throw null; } } public abstract partial class DbDataReader : System.MarshalByRefObject, System.Collections.IEnumerable, System.Data.IDataReader, System.Data.IDataRecord, System.IDisposable, System.IAsyncDisposable diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 46c0140e4ac..2eda63ecd06 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -73,23 +73,5 @@ member M:System.Data.DataView.System.ComponentModel.IBindingListView.set_Filter(System.String) - - ILLink - IL2072 - member - M:System.Data.ProviderBase.SchemaMapping.SetupSchemaWithKeyInfo(System.Data.MissingMappingAction,System.Data.MissingSchemaAction,System.Boolean,System.Data.DataColumn,System.Object) - - - ILLink - IL2026 - member - M:System.Data.ProviderBase.SchemaMapping.SetupSchemaWithKeyInfo(System.Data.MissingMappingAction,System.Data.MissingSchemaAction,System.Boolean,System.Data.DataColumn,System.Object) - - - ILLink - IL2072 - member - M:System.Data.ProviderBase.SchemaMapping.SetupSchemaWithoutKeyInfo(System.Data.MissingMappingAction,System.Data.MissingSchemaAction,System.Boolean,System.Data.DataColumn,System.Object) - diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DBSchemaRow.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DBSchemaRow.cs index 544ef90481d..621f41a8b31 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DBSchemaRow.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DBSchemaRow.cs @@ -302,6 +302,7 @@ namespace System.Data.Common internal Type? DataType { + [RequiresUnreferencedCode("DataRow's DataType cannot be statically analyzed")] get { if (null != _schemaTable.DataType) diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs index 0ef33036cb5..d53035f77a8 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataAdapter.cs @@ -89,6 +89,7 @@ namespace System.Data.Common LoadOption fillLoadOption = _fillLoadOption; return ((0 != fillLoadOption) ? _fillLoadOption : LoadOption.OverwriteChanges); } + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] // See SchemaMapping.AddAdditionalPropertiesIfLoadOptionsSet set { switch (value) @@ -247,11 +248,13 @@ namespace System.Data.Common base.Dispose(disposing); // notify base classes } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public virtual DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType) { throw ADP.NotSupported(); } + [RequiresUnreferencedCode("dataReader's schema table types cannot be statically analyzed.")] protected virtual DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, string srcTable, IDataReader dataReader) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataSet, schemaType={1}, srcTable, dataReader", ObjectID, schemaType); @@ -284,6 +287,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("dataReader's schema table types cannot be statically analyzed.")] protected virtual DataTable? FillSchema(DataTable dataTable, SchemaType schemaType, IDataReader dataReader) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataTable, schemaType, dataReader", ObjectID); @@ -312,6 +316,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("dataReader's schema table types cannot be statically analyzed.")] internal object? FillSchemaFromReader(DataSet? dataset, DataTable? datatable, SchemaType schemaType, string? srcTable, IDataReader dataReader) { DataTable[]? dataTables = null; @@ -396,7 +401,7 @@ namespace System.Data.Common } // user must Close/Dispose of the dataReader DataReaderContainer readerHandler = DataReaderContainer.Create(dataReader, ReturnProviderSpecificTypes); - return FillFromReader(dataSet, null, srcTable, readerHandler, startRecord, maxRecords, null, null); + return FillFromReader(dataSet, null, srcTable, readerHandler, startRecord, maxRecords); } finally { @@ -475,7 +480,7 @@ namespace System.Data.Common } // user must Close/Dispose of the dataReader // user will have to call NextResult to access remaining results - int count = FillFromReader(null, dataTables[i], null, readerHandler, startRecord, maxRecords, null, null); + int count = FillFromReader(null, dataTables[i], null, readerHandler, startRecord, maxRecords); if (0 == i) { result = count; @@ -502,6 +507,14 @@ namespace System.Data.Common } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "parentChapterValue is not used here")] + internal int FillFromReader(DataSet? dataset, DataTable? datatable, string? srcTable, DataReaderContainer dataReader, int startRecord, int maxRecords) + { + return FillFromReader(dataset, datatable, srcTable, dataReader, startRecord, maxRecords, null, null); + } + + [RequiresUnreferencedCode("parentChapterValue's type cannot be statically analyzed")] internal int FillFromReader(DataSet? dataset, DataTable? datatable, string? srcTable, DataReaderContainer dataReader, int startRecord, int maxRecords, DataColumn? parentChapterColumn, object? parentChapterValue) { int rowsAddedToDataSet = 0; @@ -562,6 +575,7 @@ namespace System.Data.Common return rowsAddedToDataSet; } + [RequiresUnreferencedCode("Row chapter column types cannot be statically analyzed")] private int FillLoadDataRowChunk(SchemaMapping mapping, int startRecord, int maxRecords) { DataReaderContainer dataReader = mapping.DataReader; @@ -609,6 +623,7 @@ namespace System.Data.Common return rowsAddedToDataSet; } + [RequiresUnreferencedCode("Row chapter column types cannot be statically analyzed")] private int FillLoadDataRow(SchemaMapping mapping) { int rowsAddedToDataSet = 0; @@ -643,6 +658,7 @@ namespace System.Data.Common return rowsAddedToDataSet; } + [RequiresUnreferencedCode("parentChapterValue's type cannot be statically analyzed")] private SchemaMapping FillMappingInternal(DataSet? dataset, DataTable? datatable, string? srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn? parentChapterColumn, object? parentChapterValue) { bool withKeyInfo = (Data.MissingSchemaAction.AddWithKey == MissingSchemaAction); @@ -654,6 +670,7 @@ namespace System.Data.Common return new SchemaMapping(this, dataset, datatable, dataReader, withKeyInfo, SchemaType.Mapped, tmp, true, parentChapterColumn, parentChapterValue); } + [RequiresUnreferencedCode("parentChapterValue's type cannot be statically analyzed")] private SchemaMapping? FillMapping(DataSet? dataset, DataTable? datatable, string? srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn? parentChapterColumn, object? parentChapterValue) { SchemaMapping? mapping = null; @@ -740,6 +757,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public virtual int Update(DataSet dataSet) { throw ADP.NotSupported(); diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs index e9f240e9d74..ba5f6006da2 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs @@ -65,6 +65,7 @@ namespace System.Data.Common return _schemaInfo[i].typeName; } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public override Type GetFieldType(int i) { return _schemaInfo[i].type; @@ -381,6 +382,7 @@ namespace System.Data.Common { public string name; public string typeName; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public Type type; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataAdapter.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataAdapter.cs index 1579d01e143..0dbff5981b2 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataAdapter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataAdapter.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Collections.Generic; using System.Data.ProviderBase; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Data.Common { @@ -287,6 +288,7 @@ namespace System.Data.Common throw ADP.NotSupported(); } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public DataTable? FillSchema(DataTable dataTable, SchemaType schemaType) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataTable, schemaType={1}", ObjectID, schemaType); @@ -302,6 +304,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public override DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataSet, schemaType={1}", ObjectID, schemaType); @@ -321,6 +324,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, string srcTable) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataSet, schemaType={1}, srcTable={2}", ObjectID, (int)schemaType, srcTable); @@ -336,6 +340,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from command) schema table types cannot be statically analyzed.")] protected virtual DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataSet, schemaType, command, srcTable, behavior={1}", ObjectID, behavior); @@ -366,6 +371,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from command) schema table types cannot be statically analyzed.")] protected virtual DataTable? FillSchema(DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataTable, schemaType, command, behavior={1}", ObjectID, behavior); @@ -397,6 +403,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from command) schema table types cannot be statically analyzed.")] private object? FillSchemaInternal(DataSet? dataset, DataTable? datatable, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) { object? dataTables = null; @@ -834,11 +841,13 @@ namespace System.Data.Common throw ADP.NotSupported(); } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public override int Update(DataSet dataSet) { return Update(dataSet, DbDataAdapter.DefaultSourceTableName); } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(DataRow[] dataRows) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataRows[]", ObjectID); @@ -877,6 +886,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(DataTable dataTable) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataTable", ObjectID); @@ -909,6 +919,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] public int Update(DataSet dataSet, string srcTable) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataSet, srcTable='{1}'", ObjectID, srcTable); @@ -949,6 +960,7 @@ namespace System.Data.Common } } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] protected virtual int Update(DataRow[] dataRows, DataTableMapping tableMapping) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, dataRows[], tableMapping", ObjectID); @@ -1470,6 +1482,7 @@ namespace System.Data.Common return connection.State; } + [RequiresUnreferencedCode("IDataReader (built from _IDbDataAdapter command) schema table rows DataTypes cannot be statically analyzed.")] private int UpdateFromDataTable(DataTable dataTable, DataTableMapping tableMapping) { int rowsAffected = 0; @@ -1481,6 +1494,7 @@ namespace System.Data.Common return rowsAffected; } + [RequiresUnreferencedCode("IDataReader (built from dataCommand) schema table rows DataTypes cannot be statically analyzed.")] private void UpdateRowExecute(RowUpdatedEventArgs rowUpdatedEvent, IDbCommand dataCommand, StatementType cmdIndex) { Debug.Assert(null != rowUpdatedEvent, "null rowUpdatedEvent"); diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataReader.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataReader.cs index 1770e10355a..b58b7d367f8 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataReader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataReader.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.IO; using System.Threading.Tasks; using System.Threading; +using System.Diagnostics.CodeAnalysis; namespace System.Data.Common { @@ -67,6 +68,7 @@ namespace System.Data.Common [EditorBrowsable(EditorBrowsableState.Never)] public abstract IEnumerator GetEnumerator(); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public abstract Type GetFieldType(int ordinal); public abstract string GetName(int ordinal); @@ -182,6 +184,7 @@ namespace System.Data.Common public abstract long GetInt64(int ordinal); [EditorBrowsable(EditorBrowsableState.Never)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public virtual Type GetProviderSpecificFieldType(int ordinal) { // NOTE: This is virtual because not all providers may choose to support diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs index b2ee9ec5857..56e74a8dc9a 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs @@ -41,6 +41,7 @@ namespace System.Data.Common public abstract double GetDouble(int i); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public abstract Type GetFieldType(int i); public abstract float GetFloat(int i); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index 7543cb5b3b1..ad2d4e43aa1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -3509,6 +3509,7 @@ namespace System.Data } #pragma warning restore 8632 + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public virtual void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler? errorHandler, params DataTable[] tables) { long logScopeId = DataCommonEventSource.Log.EnterScope(" reader, loadOption={0}", loadOption); @@ -3543,9 +3544,11 @@ namespace System.Data } } + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(IDataReader reader, LoadOption loadOption, params DataTable[] tables) => Load(reader, loadOption, null, tables); + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(IDataReader reader, LoadOption loadOption, params string[] tables) { ADP.CheckArgumentNull(tables, nameof(tables)); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index c0300defa1b..b01b56b8b9c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -4975,10 +4975,13 @@ namespace System.Data } } + [RequiresUnreferencedCode("Members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(IDataReader reader) => Load(reader, LoadOption.PreserveChanges, null); + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public void Load(IDataReader reader, LoadOption loadOption) => Load(reader, loadOption, null); + [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public virtual void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler? errorHandler) { long logScopeId = DataCommonEventSource.Log.EnterScope(" {0}, loadOption={1}", ObjectID, loadOption); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTableReader.cs b/src/libraries/System.Data.Common/src/System/Data/DataTableReader.cs index 59b2a5cff7c..2a14fa8f444 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTableReader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTableReader.cs @@ -3,6 +3,7 @@ using System.Data.Common; using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -308,6 +309,7 @@ namespace System.Data } } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public override Type GetProviderSpecificFieldType(int ordinal) { ValidateOpen(nameof(GetProviderSpecificFieldType)); @@ -525,6 +527,7 @@ namespace System.Data } } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] public override Type GetFieldType(int ordinal) { ValidateOpen(nameof(GetFieldType)); diff --git a/src/libraries/System.Data.Common/src/System/Data/IDataAdapter.cs b/src/libraries/System.Data.Common/src/System/Data/IDataAdapter.cs index 745fd5c873f..3c4f7a9b60c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/IDataAdapter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/IDataAdapter.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Data { public interface IDataAdapter @@ -8,9 +10,11 @@ namespace System.Data MissingMappingAction MissingMappingAction { get; set; } MissingSchemaAction MissingSchemaAction { get; set; } ITableMappingCollection TableMappings { get; } + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType); int Fill(DataSet dataSet); IDataParameter[] GetFillParameters(); + [RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")] int Update(DataSet dataSet); } } diff --git a/src/libraries/System.Data.Common/src/System/Data/IDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/IDataRecord.cs index 8dad2b2c18b..b0ac4fbfbfc 100644 --- a/src/libraries/System.Data.Common/src/System/Data/IDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/IDataRecord.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Data { public interface IDataRecord @@ -10,6 +12,7 @@ namespace System.Data object this[string name] { get; } string GetName(int i); string GetDataTypeName(int i); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] Type GetFieldType(int i); object GetValue(int i); int GetValues(object[] values); diff --git a/src/libraries/System.Data.Common/src/System/Data/ProviderBase/DataReaderContainer.cs b/src/libraries/System.Data.Common/src/System/Data/ProviderBase/DataReaderContainer.cs index 830983f36f1..1936388285d 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ProviderBase/DataReaderContainer.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ProviderBase/DataReaderContainer.cs @@ -3,6 +3,7 @@ using System.Data.Common; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Data.ProviderBase { @@ -41,6 +42,7 @@ namespace System.Data.ProviderBase internal abstract bool ReturnProviderSpecificTypes { get; } protected abstract int VisibleFieldCount { get; } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] internal abstract Type GetFieldType(int ordinal); internal abstract object GetValue(int ordinal); internal abstract int GetValues(object[] values); @@ -98,6 +100,7 @@ namespace System.Data.ProviderBase } } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] internal override Type GetFieldType(int ordinal) { Type fieldType = _providerSpecificDataReader.GetProviderSpecificFieldType(ordinal); @@ -138,6 +141,7 @@ namespace System.Data.ProviderBase } } + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] internal override Type GetFieldType(int ordinal) { return _dataReader.GetFieldType(ordinal); diff --git a/src/libraries/System.Data.Common/src/System/Data/ProviderBase/SchemaMapping.cs b/src/libraries/System.Data.Common/src/System/Data/ProviderBase/SchemaMapping.cs index 432e0eb7142..97fd18c9ae1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ProviderBase/SchemaMapping.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ProviderBase/SchemaMapping.cs @@ -56,6 +56,7 @@ namespace System.Data.ProviderBase private readonly LoadOption _loadOption; + [RequiresUnreferencedCode("chapterValue and dataReader schema table rows DataTypes type cannot be statically analyzed.")] internal SchemaMapping(DataAdapter adapter, DataSet? dataset, DataTable? datatable, DataReaderContainer dataReader, bool keyInfo, SchemaType schemaType, string? sourceTableName, bool gettingData, DataColumn? parentChapterColumn, object? parentChapterValue) @@ -382,6 +383,7 @@ namespace System.Data.ProviderBase return _mappedDataValues!; } + [RequiresUnreferencedCode("Row chapter column types cannot be statically analyzed")] internal void LoadDataRowWithClear() { // for FillErrorEvent to ensure no values leftover from previous row @@ -392,6 +394,7 @@ namespace System.Data.ProviderBase LoadDataRow(); } + [RequiresUnreferencedCode("Row chapter column types cannot be statically analyzed")] internal void LoadDataRow() { try @@ -447,6 +450,7 @@ namespace System.Data.ProviderBase } } + [RequiresUnreferencedCode("Row chapter column types cannot be statically analyzed")] internal int LoadDataRowChapters(DataRow dataRow) { int datarowadded = 0; @@ -565,6 +569,7 @@ namespace System.Data.ProviderBase } } + [RequiresUnreferencedCode("chapterValue's type cannot be statically analyzed")] private object[]? SetupSchemaWithoutKeyInfo(MissingMappingAction mappingAction, MissingSchemaAction schemaAction, bool gettingData, DataColumn? parentChapterColumn, object? chapterValue) { Debug.Assert(_dataTable != null); @@ -760,6 +765,7 @@ namespace System.Data.ProviderBase return dataValues; } + [RequiresUnreferencedCode("chapterValue and _schemaTable schema rows DataTypes type cannot be statically analyzed. When _loadOption is set, members from types used in the expression column may be trimmed if not referenced directly.")] private object[]? SetupSchemaWithKeyInfo(MissingMappingAction mappingAction, MissingSchemaAction schemaAction, bool gettingData, DataColumn? parentChapterColumn, object? chapterValue) { Debug.Assert(_dataTable != null); @@ -1005,6 +1011,7 @@ namespace System.Data.ProviderBase { AddAdditionalProperties(dataColumn, schemaRow.DataRow); } + AddItemToAllowRollback(ref addedItems, dataColumn); columnCollection.Add(dataColumn); } @@ -1139,7 +1146,7 @@ namespace System.Data.ProviderBase return dataValues; } - [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode("Members from types used in the expression column may be trimmed if not referenced directly.")] private void AddAdditionalProperties(DataColumn targetColumn, DataRow schemaRow) { DataColumnCollection columns = schemaRow.Table.Columns; diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 0504df00e76..6ea564dbbf6 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -55,5 +55,11 @@ member M:System.Data.Odbc.OdbcMetaDataFactory.NewDataTableFromReader(System.Data.IDataReader,System.Object[]@,System.String) + + ILLink + IL2093 + member + M:System.Data.Odbc.OdbcDataReader.GetFieldType(System.Int32) + - \ No newline at end of file + diff --git a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml index 5386e4bd294..63d7bd6ef89 100644 --- a/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.OleDb/src/ILLink/ILLink.Suppressions.xml @@ -91,5 +91,11 @@ member M:System.Data.OleDb.OleDbMetaDataFactory.GetDataSourceInformationTable(System.Data.OleDb.OleDbConnection,System.Data.OleDb.OleDbConnectionInternal) + + ILLink + IL2093 + member + M:System.Data.OleDb.OleDbDataReader.GetFieldType(System.Int32) + \ No newline at end of file From 590e41ada9e1c5165c5eb1a7518851ab27239a7d Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 8 Jul 2021 11:56:48 +0300 Subject: [PATCH 335/926] [mono] Add constructor for jagged arrays (#55180) Vectors normally have a single constructor that receives an argument representing the length. However, a vector of vectors can also receive two arguments, the length of the vector and the length of every element. This constructor is not specified in the spec, however it is implemented by coreclr. --- src/mono/mono/metadata/object-internals.h | 3 +++ src/mono/mono/metadata/object.c | 33 +++++++++++++++++++++++ src/mono/mono/mini/interp/interp.c | 8 +++++- src/mono/mono/mini/jit-icalls.c | 31 ++++++++++++--------- src/mono/mono/mini/jit-icalls.h | 2 ++ src/mono/mono/mini/method-to-ir.c | 3 --- src/tests/issues.targets | 3 --- 7 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 19f00b0630f..53112ed0ea3 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -1493,6 +1493,9 @@ mono_array_new_checked (MonoClass *eclass, uintptr_t n, MonoError *error); MONO_COMPONENT_API MonoArray* mono_array_new_full_checked (MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error); +MonoArray* +mono_array_new_jagged_checked (MonoClass *klass, int n, uintptr_t *lengths, MonoError *error); + ICALL_EXPORT MonoArray* ves_icall_array_new_specific (MonoVTable *vtable, uintptr_t n); diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 5b5434682c5..5e15a0f74b9 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -5686,6 +5686,39 @@ mono_array_new_full_checked (MonoClass *array_class, uintptr_t *lengths, intptr_ return array; } +static MonoArray* +mono_array_new_jagged_helper (MonoClass *klass, int n, uintptr_t *lengths, int index, MonoError *error) +{ + HANDLE_FUNCTION_ENTER (); + + MonoArray *ret = mono_array_new_full_checked (klass, &lengths [index], NULL, error); + goto_if_nok (error, exit); + + MONO_HANDLE_PIN (ret); + + if (index < (n - 1)) { + // We have a new dimension argument. This means the elements in the allocated array + // are also arrays and we allocate each one of them. + MonoClass *element_class = m_class_get_element_class (klass); + g_assert (m_class_get_rank (element_class) == 1); + for (int i = 0; i < lengths [index]; i++) { + MonoArray *o = mono_array_new_jagged_helper (element_class, n, lengths, index + 1, error); + goto_if_nok (error, exit); + mono_array_setref_fast (ret, i, o); + } + } + +exit: + HANDLE_FUNCTION_RETURN_VAL (ret); +} + +MonoArray * +mono_array_new_jagged_checked (MonoClass *klass, int n, uintptr_t *lengths, MonoError *error) +{ + return mono_array_new_jagged_helper (klass, n, lengths, 0, error); +} + + /** * mono_array_new: * \param domain domain where the object is created diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 2038284d4f9..dad5f095b00 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1060,7 +1060,13 @@ ves_array_create (MonoClass *klass, int param_count, stackval *values, MonoError int rank = m_class_get_rank (klass); uintptr_t *lengths = g_newa (uintptr_t, rank * 2); intptr_t *lower_bounds = NULL; - if (2 * rank == param_count) { + + if (param_count > rank && m_class_get_byval_arg (klass)->type == MONO_TYPE_SZARRAY) { + // Special constructor for jagged arrays + for (int i = 0; i < param_count; ++i) + lengths [i] = values [i].data.i; + return (MonoObject*) mono_array_new_jagged_checked (klass, param_count, lengths, error); + } else if (2 * rank == param_count) { for (int l = 0; l < 2; ++l) { int src = l; int dst = l * rank; diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 189eae79016..84b800d69e6 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -728,24 +728,29 @@ mono_array_new_n_icall (MonoMethod *cm, gint32 pcount, intptr_t *params) const int pcount_sig = mono_method_signature_internal (cm)->param_count; const int rank = m_class_get_rank (cm->klass); g_assert (pcount == pcount_sig); - g_assert (rank == pcount || rank * 2 == pcount); uintptr_t *lengths = (uintptr_t*)params; + MonoArray *arr; - if (rank == pcount) { - /* Only lengths provided. */ - if (m_class_get_byval_arg (cm->klass)->type == MONO_TYPE_ARRAY) { - lower_bounds = g_newa (intptr_t, rank); - memset (lower_bounds, 0, sizeof (intptr_t) * rank); - } + if (pcount > rank && m_class_get_byval_arg (cm->klass)->type == MONO_TYPE_SZARRAY) { + // Special constructor for jagged arrays + arr = mono_array_new_jagged_checked (cm->klass, pcount, lengths, error); } else { - g_assert (pcount == (rank * 2)); - /* lower bounds are first. */ - lower_bounds = params; - lengths += rank; - } + if (rank == pcount) { + /* Only lengths provided. */ + if (m_class_get_byval_arg (cm->klass)->type == MONO_TYPE_ARRAY) { + lower_bounds = g_newa (intptr_t, rank); + memset (lower_bounds, 0, sizeof (intptr_t) * rank); + } + } else { + g_assert (pcount == (rank * 2)); + /* lower bounds are first. */ + lower_bounds = params; + lengths += rank; + } - MonoArray *arr = mono_array_new_full_checked (cm->klass, lengths, lower_bounds, error); + arr = mono_array_new_full_checked (cm->klass, lengths, lower_bounds, error); + } return mono_error_set_pending_exception (error) ? NULL : arr; } diff --git a/src/mono/mono/mini/jit-icalls.h b/src/mono/mono/mini/jit-icalls.h index 1994caca499..16653209221 100644 --- a/src/mono/mono/mini/jit-icalls.h +++ b/src/mono/mono/mini/jit-icalls.h @@ -56,6 +56,8 @@ ICALL_EXTERN_C gint64 mono_lshr (gint64 a, gint32 shamt); // For param_count > 4. ICALL_EXTERN_C MonoArray *mono_array_new_n_icall (MonoMethod *cm, gint32 param_count, intptr_t *params); +ICALL_EXTERN_C MonoArray *mono_array_new_2_jagged (MonoMethod *cm, guint32 length1, guint32 length2); + ICALL_EXTERN_C MonoArray *mono_array_new_1 (MonoMethod *cm, guint32 length); ICALL_EXTERN_C MonoArray *mono_array_new_2 (MonoMethod *cm, guint32 length1, guint32 length2); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index c0052d7a9c7..b28e16d6a52 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -8785,9 +8785,6 @@ calli_end: } } - /* Instancing jagged arrays should not end up here since ctor (int32, int32) for an array with rank 1 represents length and lbound. */ - g_assert (!(rank == 1 && fsig->param_count == 2 && m_class_get_rank (m_class_get_element_class (cmethod->klass)))); - /* Regular case, rank > 4 or legnth, lbound specified per rank. */ if (function == MONO_JIT_ICALL_ZeroIsReserved) { // FIXME Maximum value of param_count? Realistically 64. Fits in imm? diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 9aa21e9c19c..ccf5fcc479c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1051,9 +1051,6 @@ https://github.com/dotnet/runtime/issues/46174 - - https://github.com/dotnet/runtime/issues/47458 - Tests features specific to coreclr From 75fb0c3a05656bb11ea7524eb39966a504970b1d Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Thu, 8 Jul 2021 02:03:18 -0700 Subject: [PATCH 336/926] fix handling of peer read abort (#55309) Co-authored-by: Geoffrey Kizer --- .../Quic/Implementations/MsQuic/MsQuicStream.cs | 2 +- .../tests/FunctionalTests/QuicStreamTests.cs | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index a94a7efeb27..8260dfdd68f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -875,7 +875,7 @@ namespace System.Net.Quic.Implementations.MsQuic shouldReadComplete = true; } - if (state.ReadState != ReadState.ConnectionClosed) + if (state.ReadState != ReadState.ConnectionClosed && state.ReadState != ReadState.Aborted) { state.ReadState = ReadState.ReadsCompleted; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 9c8574e9827..dacd04a448c 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -436,7 +436,6 @@ namespace System.Net.Quic.Tests }).WaitAsync(TimeSpan.FromSeconds(15)); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/53530")] [Fact] public async Task StreamAbortedWithoutWriting_ReadThrows() { @@ -493,13 +492,7 @@ namespace System.Net.Quic.Tests byte[] buffer = new byte[1024 * 1024]; - // TODO: it should always throw QuicStreamAbortedException, but sometimes it does not https://github.com/dotnet/runtime/issues/53530 - //QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadAll(stream, buffer)); - try - { - await ReadAll(stream, buffer); - } - catch (QuicStreamAbortedException) { } + QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadAll(stream, buffer)); await stream.ShutdownCompleted(); } @@ -555,13 +548,7 @@ namespace System.Net.Quic.Tests } } - // TODO: it should always throw QuicStreamAbortedException, but sometimes it does not https://github.com/dotnet/runtime/issues/53530 - //QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadUntilAborted()); - try - { - await ReadUntilAborted().WaitAsync(TimeSpan.FromSeconds(3)); - } - catch { } + QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadUntilAborted()); await stream.ShutdownCompleted(); } From 5f689e29bb7e7851abbb2ccdf30cd19cd8ffba4d Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Thu, 8 Jul 2021 03:40:15 -0700 Subject: [PATCH 337/926] Improve MetricEventSource error message (#55319) The Error event wasn't capturing inner exception info in its message. Switching from Exception.Message to Exception.ToString() captures more useful messages. We no longer split out the stack separately, but that wasn't important. The error messages are likely only useful for humans anyway. --- .../System/Diagnostics/Metrics/MetricsEventSource.cs | 10 +++++----- .../tests/MetricEventSourceTests.cs | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs index 669039c0c80..80baa185a09 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -138,9 +138,9 @@ namespace System.Diagnostics.Metrics } [Event(9, Keywords = Keywords.TimeSeriesValues | Keywords.Messages | Keywords.InstrumentPublishing)] - public void Error(string sessionId, string errorMessage, string errorStack) + public void Error(string sessionId, string errorMessage) { - WriteEvent(9, sessionId, errorMessage, errorStack); + WriteEvent(9, sessionId, errorMessage); } [Event(10, Keywords = Keywords.TimeSeriesValues | Keywords.InstrumentPublishing)] @@ -203,7 +203,7 @@ namespace System.Diagnostics.Metrics // This limitation shouldn't really matter because browser also doesn't support out-of-proc EventSource communication // which is the intended scenario for this EventSource. If it matters in the future AggregationManager can be // modified to have some other fallback path that works for browser. - Log.Error("", "System.Diagnostics.Metrics EventSource not supported on browser", ""); + Log.Error("", "System.Diagnostics.Metrics EventSource not supported on browser"); return; } #endif @@ -301,7 +301,7 @@ namespace System.Diagnostics.Metrics i => Log.EndInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description), i => Log.InstrumentPublished(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description), () => Log.InitialInstrumentEnumerationComplete(sessionId), - e => Log.Error(sessionId, e.Message, e.StackTrace?.ToString() ?? ""), + e => Log.Error(sessionId, e.ToString()), () => Log.TimeSeriesLimitReached(sessionId), () => Log.HistogramLimitReached(sessionId)); @@ -328,7 +328,7 @@ namespace System.Diagnostics.Metrics private bool LogError(Exception e) { - Log.Error(_sessionId, e.Message, e.StackTrace?.ToString() ?? ""); + Log.Error(_sessionId, e.ToString()); // this code runs as an exception filter // returning false ensures the catch handler isn't run return false; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs index f0b77bf2af4..c63aaa1a871 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -1001,8 +1001,7 @@ namespace System.Diagnostics.Metrics.Tests if (errorEvent != null) { string message = errorEvent.Payload[1].ToString(); - string stackTrace = errorEvent.Payload[2].ToString(); - Assert.True(errorEvent == null, "Unexpected Error event: " + message + Environment.NewLine + stackTrace); + Assert.True(errorEvent == null, "Unexpected Error event: " + message); } } } From a48a11b1c1a4d0189047d142090b68563a94c590 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 8 Jul 2021 13:54:21 +0300 Subject: [PATCH 338/926] [mono] Optimize Type.IsRuntimeImplemented (#55260) Avoid virtual call to Type.UnderlyingSystemType. This is same as coreclr. --- src/mono/System.Private.CoreLib/src/System/Type.Mono.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs index bd6f2f792af..7dc0d31530b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs @@ -20,7 +20,7 @@ namespace System return _impl.Value; } - internal bool IsRuntimeImplemented() => this.UnderlyingSystemType is RuntimeType; + internal bool IsRuntimeImplemented() => this is RuntimeType; internal virtual bool IsTypeBuilder() => false; From 1ca17cd7f7b16b5dfe7703e8e676d645da86af4d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 8 Jul 2021 08:12:55 -0400 Subject: [PATCH 339/926] Override Read(Span)/ReadByte on PipeReaderStream (#55086) We can do better than the base implementations for reading. For writing, there's not a lot we can do better than the base. --- .../System/IO/Pipelines/PipeReaderStream.cs | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReaderStream.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReaderStream.cs index 8bc68d9e8c8..3dc9b49e3e1 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReaderStream.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReaderStream.cs @@ -45,8 +45,28 @@ namespace System.IO.Pipelines { } - public override int Read(byte[] buffer, int offset, int count) => - ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + return ReadInternal(new Span(buffer, offset, count)); + } + + public override int ReadByte() + { + Span oneByte = stackalloc byte[1]; + return ReadInternal(oneByte) == 0 ? -1 : oneByte[0]; + } + + private int ReadInternal(Span buffer) + { + ValueTask vt = _pipeReader.ReadAsync(); + ReadResult result = vt.IsCompletedSuccessfully ? vt.Result : vt.AsTask().Result; + return HandleReadResult(result, buffer); + } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); @@ -71,6 +91,11 @@ namespace System.IO.Pipelines } #if (!NETSTANDARD2_0 && !NETFRAMEWORK) + public override int Read(Span buffer) + { + return ReadInternal(buffer); + } + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { return ReadAsyncInternal(buffer, cancellationToken); @@ -80,7 +105,11 @@ namespace System.IO.Pipelines private async ValueTask ReadAsyncInternal(Memory buffer, CancellationToken cancellationToken) { ReadResult result = await _pipeReader.ReadAsync(cancellationToken).ConfigureAwait(false); + return HandleReadResult(result, buffer.Span); + } + private int HandleReadResult(ReadResult result, Span buffer) + { if (result.IsCanceled) { ThrowHelper.ThrowOperationCanceledException_ReadCanceled(); @@ -98,7 +127,7 @@ namespace System.IO.Pipelines ReadOnlySequence slice = actual == bufferLength ? sequence : sequence.Slice(0, actual); consumed = slice.End; - slice.CopyTo(buffer.Span); + slice.CopyTo(buffer); return actual; } From 46e5fe1c9a4584b006601d46063b353338219af0 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 8 Jul 2021 15:20:30 +0300 Subject: [PATCH 340/926] JIT: Drop redundant static initializations from (Equality)Comparer.Default (#50446) Co-authored-by: Andy Ayers --- src/coreclr/jit/compiler.h | 4 +- src/coreclr/jit/fginline.cpp | 95 +++++---------------------------- src/coreclr/jit/flowgraph.cpp | 15 ------ src/coreclr/jit/gentree.cpp | 65 +++++++++++++++++++--- src/coreclr/jit/gentree.h | 2 - src/coreclr/jit/importer.cpp | 86 +++++------------------------ src/coreclr/jit/inline.def | 1 + src/coreclr/jit/valuenum.cpp | 28 ++++++++++ src/coreclr/jit/valuenumfuncs.h | 3 ++ 9 files changed, 115 insertions(+), 184 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 360bc97a6b3..620036ed7c8 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4022,8 +4022,6 @@ protected: CORINFO_CALL_INFO* callInfo, IL_OFFSET rawILOffset); - CORINFO_CLASS_HANDLE impGetSpecialIntrinsicExactReturnType(CORINFO_METHOD_HANDLE specialIntrinsicHandle); - bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo, CorInfoCallConvExtension callConv); GenTree* impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HANDLE retClsHnd); @@ -4071,7 +4069,6 @@ protected: var_types callType, NamedIntrinsic intrinsicName, bool tailCall); - NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); GenTree* impUnsupportedNamedIntrinsic(unsigned helper, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, @@ -4172,6 +4169,7 @@ public: CHECK_SPILL_NONE = -2 }; + NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); void impBeginTreeList(); void impEndTreeList(BasicBlock* block, Statement* firstStmt, Statement* lastStmt); void impEndTreeList(BasicBlock* block); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 29aabbaf40a..d663cb1c684 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -1623,8 +1623,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) if (argInfo.argHasSideEff) { noway_assert(argInfo.argIsUsed == false); - newStmt = nullptr; - bool append = true; + newStmt = nullptr; if (argNode->gtOper == GT_OBJ || argNode->gtOper == GT_MKREFANY) { @@ -1633,92 +1632,22 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) // Just hang the address here in case there are side-effect. newStmt = gtNewStmt(gtUnusedValNode(argNode->AsOp()->gtOp1), callILOffset); } - else + + // If we don't have something custom to append, + // just append the arg node as an unused value. + if (newStmt == nullptr) { - // In some special cases, unused args with side effects can - // trigger further changes. - // - // (1) If the arg is a static field access and the field access - // was produced by a call to EqualityComparer.get_Default, the - // helper call to ensure the field has a value can be suppressed. - // This helper call is marked as a "Special DCE" helper during - // importation, over in fgGetStaticsCCtorHelper. - // - // (2) NYI. If, after tunneling through GT_RET_VALs, we find that - // the actual arg expression has no side effects, we can skip - // appending all together. This will help jit TP a bit. - // - // Chase through any GT_RET_EXPRs to find the actual argument - // expression. - GenTree* actualArgNode = argNode->gtRetExprVal(&bbFlags); - - // For case (1) - // - // Look for the following tree shapes - // prejit: (IND (ADD (CONST, CALL(special dce helper...)))) - // jit : (COMMA (CALL(special dce helper...), (FIELD ...))) - if (actualArgNode->gtOper == GT_COMMA) - { - // Look for (COMMA (CALL(special dce helper...), (FIELD ...))) - GenTree* op1 = actualArgNode->AsOp()->gtOp1; - GenTree* op2 = actualArgNode->AsOp()->gtOp2; - if (op1->IsCall() && - ((op1->AsCall()->gtCallMoreFlags & GTF_CALL_M_HELPER_SPECIAL_DCE) != 0) && - (op2->gtOper == GT_FIELD) && ((op2->gtFlags & GTF_EXCEPT) == 0)) - { - JITDUMP("\nPerforming special dce on unused arg [%06u]:" - " actual arg [%06u] helper call [%06u]\n", - argNode->gtTreeID, actualArgNode->gtTreeID, op1->gtTreeID); - // Drop the whole tree - append = false; - } - } - else if (actualArgNode->gtOper == GT_IND) - { - // Look for (IND (ADD (CONST, CALL(special dce helper...)))) - GenTree* addr = actualArgNode->AsOp()->gtOp1; - - if (addr->gtOper == GT_ADD) - { - GenTree* op1 = addr->AsOp()->gtOp1; - GenTree* op2 = addr->AsOp()->gtOp2; - if (op1->IsCall() && - ((op1->AsCall()->gtCallMoreFlags & GTF_CALL_M_HELPER_SPECIAL_DCE) != 0) && - op2->IsCnsIntOrI()) - { - // Drop the whole tree - JITDUMP("\nPerforming special dce on unused arg [%06u]:" - " actual arg [%06u] helper call [%06u]\n", - argNode->gtTreeID, actualArgNode->gtTreeID, op1->gtTreeID); - append = false; - } - } - } + newStmt = gtNewStmt(gtUnusedValNode(argNode), callILOffset); } - if (!append) - { - assert(newStmt == nullptr); - JITDUMP("Arg tree side effects were discardable, not appending anything for arg\n"); - } - else - { - // If we don't have something custom to append, - // just append the arg node as an unused value. - if (newStmt == nullptr) - { - newStmt = gtNewStmt(gtUnusedValNode(argNode), callILOffset); - } - - fgInsertStmtAfter(block, afterStmt, newStmt); - afterStmt = newStmt; + fgInsertStmtAfter(block, afterStmt, newStmt); + afterStmt = newStmt; #ifdef DEBUG - if (verbose) - { - gtDispStmt(afterStmt); - } -#endif // DEBUG + if (verbose) + { + gtDispStmt(afterStmt); } +#endif // DEBUG } else if (argNode->IsBoxedValue()) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 04faed33f50..1f3e71ade8a 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -910,21 +910,6 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; - - // If we're importing the special EqualityComparer.Default or Comparer.Default - // intrinsics, flag the helper call. Later during inlining, we can - // remove the helper call if the associated field lookup is unused. - if ((info.compFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) - { - NamedIntrinsic ni = lookupNamedIntrinsic(info.compMethodHnd); - if ((ni == NI_System_Collections_Generic_EqualityComparer_get_Default) || - (ni == NI_System_Collections_Generic_Comparer_get_Default)) - { - JITDUMP("\nmarking helper call [%06u] as special dce...\n", result->gtTreeID); - result->gtCallMoreFlags |= GTF_CALL_M_HELPER_SPECIAL_DCE; - } - } - return result; } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 35b01ad27ec..b56372d18d2 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -882,6 +882,17 @@ bool GenTreeCall::IsPure(Compiler* compiler) const // true if this call has any side-effects; false otherwise. bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool ignoreCctors) const { + // Some named intrinsics are known to have ignorable side effects. + if (gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) + { + NamedIntrinsic ni = compiler->lookupNamedIntrinsic(gtCallMethHnd); + if ((ni == NI_System_Collections_Generic_Comparer_get_Default) || + (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)) + { + return false; + } + } + // Generally all GT_CALL nodes are considered to have side-effects, but we may have extra information about helper // calls that can prove them side-effect-free. if (gtCallType != CT_HELPER) @@ -15920,9 +15931,10 @@ void Compiler::gtExtractSideEffList(GenTree* expr, } // Generally all GT_CALL nodes are considered to have side-effects. - // So if we get here it must be a helper call that we decided it does + // So if we get here it must be a helper call or a special intrinsic that we decided it does // not have side effects that we needed to keep. - assert(!node->OperIs(GT_CALL) || (node->AsCall()->gtCallType == CT_HELPER)); + assert(!node->OperIs(GT_CALL) || (node->AsCall()->gtCallType == CT_HELPER) || + (node->AsCall()->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC)); } if ((m_flags & GTF_IS_IN_CSE) != 0) @@ -17743,13 +17755,50 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b break; } - CORINFO_CLASS_HANDLE specialObjClass = impGetSpecialIntrinsicExactReturnType(call->gtCallMethHnd); - if (specialObjClass != nullptr) + // Try to get the actual type of [Equality]Comparer<>.Default + if ((ni == NI_System_Collections_Generic_Comparer_get_Default) || + (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)) { - objClass = specialObjClass; - *pIsExact = true; - *pIsNonNull = true; - break; + CORINFO_SIG_INFO sig; + info.compCompHnd->getMethodSig(call->gtCallMethHnd, &sig); + assert(sig.sigInst.classInstCount == 1); + CORINFO_CLASS_HANDLE typeHnd = sig.sigInst.classInst[0]; + assert(typeHnd != nullptr); + + // Lookup can incorrect when we have __Canon as it won't appear to implement any interface types. + // And if we do not have a final type, devirt & inlining is unlikely to result in much + // simplification. We can use CORINFO_FLG_FINAL to screen out both of these cases. + const DWORD typeAttribs = info.compCompHnd->getClassAttribs(typeHnd); + const bool isFinalType = ((typeAttribs & CORINFO_FLG_FINAL) != 0); + + if (isFinalType) + { + if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) + { + objClass = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); + } + else + { + assert(ni == NI_System_Collections_Generic_Comparer_get_Default); + objClass = info.compCompHnd->getDefaultComparerClass(typeHnd); + } + } + + if (objClass == nullptr) + { + // Don't re-visit this intrinsic in this case. + call->gtCallMoreFlags &= ~GTF_CALL_M_SPECIAL_INTRINSIC; + JITDUMP("Special intrinsic for type %s: type not final, so deferring opt\n", + eeGetClassName(typeHnd)) + } + else + { + JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd), + eeGetClassName(objClass)) + *pIsExact = true; + *pIsNonNull = true; + break; + } } } if (call->IsInlineCandidate()) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index be5d7769cbb..b0439564688 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3796,8 +3796,6 @@ enum GenTreeCallFlags : unsigned int // to restore real function address and load hidden argument // as the first argument for calli. It is CoreRT replacement for instantiating // stubs, because executable code cannot be generated at runtime. - GTF_CALL_M_HELPER_SPECIAL_DCE = 0x00020000, // this helper call can be removed if it is part of a comma and - // the comma result is unused. GTF_CALL_M_DEVIRTUALIZED = 0x00040000, // this call was devirtualized GTF_CALL_M_UNBOXED = 0x00080000, // this call was optimized to use the unboxed entry point GTF_CALL_M_GUARDED_DEVIRT = 0x00100000, // this call is a candidate for guarded devirtualization diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e2a4257897d..d49a12ffd13 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -19254,6 +19254,19 @@ void Compiler::impCheckCanInline(GenTreeCall* call, goto _exit; } + // It's better for JIT to keep these methods not inlined for CQ. + NamedIntrinsic ni; + if (pParam->call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) + { + ni = pParam->pThis->lookupNamedIntrinsic(pParam->call->gtCallMethHnd); + if ((ni == NI_System_Collections_Generic_Comparer_get_Default) || + (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)) + { + pParam->result->NoteFatal(InlineObservation::CALLEE_SPECIAL_INTRINSIC); + goto _exit; + } + } + // Speculatively check if initClass() can be done. // If it can be done, we will try to inline the method. initClassResult = @@ -21493,79 +21506,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, #endif // FEATURE_READYTORUN_COMPILER } -//------------------------------------------------------------------------ -// impGetSpecialIntrinsicExactReturnType: Look for special cases where a call -// to an intrinsic returns an exact type -// -// Arguments: -// methodHnd -- handle for the special intrinsic method -// -// Returns: -// Exact class handle returned by the intrinsic call, if known. -// Nullptr if not known, or not likely to lead to beneficial optimization. - -CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_METHOD_HANDLE methodHnd) -{ - JITDUMP("Special intrinsic: looking for exact type returned by %s\n", eeGetMethodFullName(methodHnd)); - - CORINFO_CLASS_HANDLE result = nullptr; - - // See what intrinisc we have... - const NamedIntrinsic ni = lookupNamedIntrinsic(methodHnd); - switch (ni) - { - case NI_System_Collections_Generic_Comparer_get_Default: - case NI_System_Collections_Generic_EqualityComparer_get_Default: - { - // Expect one class generic parameter; figure out which it is. - CORINFO_SIG_INFO sig; - info.compCompHnd->getMethodSig(methodHnd, &sig); - assert(sig.sigInst.classInstCount == 1); - CORINFO_CLASS_HANDLE typeHnd = sig.sigInst.classInst[0]; - assert(typeHnd != nullptr); - - // Lookup can incorrect when we have __Canon as it won't appear - // to implement any interface types. - // - // And if we do not have a final type, devirt & inlining is - // unlikely to result in much simplification. - // - // We can use CORINFO_FLG_FINAL to screen out both of these cases. - const DWORD typeAttribs = info.compCompHnd->getClassAttribs(typeHnd); - const bool isFinalType = ((typeAttribs & CORINFO_FLG_FINAL) != 0); - - if (isFinalType) - { - if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) - { - result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); - } - else - { - assert(ni == NI_System_Collections_Generic_Comparer_get_Default); - result = info.compCompHnd->getDefaultComparerClass(typeHnd); - } - JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd), - result != nullptr ? eeGetClassName(result) : "unknown"); - } - else - { - JITDUMP("Special intrinsic for type %s: type not final, so deferring opt\n", eeGetClassName(typeHnd)); - } - - break; - } - - default: - { - JITDUMP("This special intrinsic not handled, sorry...\n"); - break; - } - } - - return result; -} - //------------------------------------------------------------------------ // impAllocateToken: create CORINFO_RESOLVED_TOKEN into jit-allocated memory and init it. // diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def index f21e4f929ea..a16d82888b6 100644 --- a/src/coreclr/jit/inline.def +++ b/src/coreclr/jit/inline.def @@ -57,6 +57,7 @@ INLINE_OBSERVATION(STFLD_NEEDS_HELPER, bool, "stfld needs helper", INLINE_OBSERVATION(TOO_MANY_ARGUMENTS, bool, "too many arguments", FATAL, CALLEE) INLINE_OBSERVATION(TOO_MANY_LOCALS, bool, "too many locals", FATAL, CALLEE) INLINE_OBSERVATION(EXPLICIT_TAIL_PREFIX, bool, "explicit tail prefix in callee", FATAL, CALLEE) +INLINE_OBSERVATION(SPECIAL_INTRINSIC, bool, "skipped for special intrinsic", FATAL, CALLEE) // ------ Callee Performance ------- diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 110730f15e6..c69e8bc3cbd 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -9629,6 +9629,34 @@ void Compiler::fgValueNumberCall(GenTreeCall* call) } else { + if (call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) + { + NamedIntrinsic ni = lookupNamedIntrinsic(call->gtCallMethHnd); + if ((ni == NI_System_Collections_Generic_Comparer_get_Default) || + (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)) + { + bool isExact = false; + bool isNotNull = false; + CORINFO_CLASS_HANDLE cls = gtGetClassHandle(call, &isExact, &isNotNull); + if ((cls != nullptr) && isExact && isNotNull) + { + ValueNum clsVN = vnStore->VNForHandle(ssize_t(cls), GTF_ICON_CLASS_HDL); + ValueNum funcVN; + if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) + { + funcVN = vnStore->VNForFunc(call->TypeGet(), VNF_GetDefaultEqualityComparer, clsVN); + } + else + { + assert(ni == NI_System_Collections_Generic_Comparer_get_Default); + funcVN = vnStore->VNForFunc(call->TypeGet(), VNF_GetDefaultComparer, clsVN); + } + call->gtVNPair.SetBoth(funcVN); + return; + } + } + } + if (call->TypeGet() == TYP_VOID) { call->gtVNPair.SetBoth(ValueNumStore::VNForVoid()); diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index aa13273c877..2ddfff34dfa 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -128,6 +128,9 @@ ValueNumFuncDef(GetsharedNongcthreadstaticBaseNoctor, 2, false, true, true) ValueNumFuncDef(GetsharedGcthreadstaticBaseDynamicclass, 2, false, true, true) ValueNumFuncDef(GetsharedNongcthreadstaticBaseDynamicclass, 2, false, true, true) +ValueNumFuncDef(GetDefaultComparer, 1, false, true, false) +ValueNumFuncDef(GetDefaultEqualityComparer, 1, false, true, false) + ValueNumFuncDef(ClassinitSharedDynamicclass, 2, false, false, false) ValueNumFuncDef(RuntimeHandleMethod, 2, false, true, false) ValueNumFuncDef(RuntimeHandleClass, 2, false, true, false) From ffd7462ee6372337b6ec075eb965851e49ad15d7 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 8 Jul 2021 07:50:09 -0500 Subject: [PATCH 341/926] Trim Linq.Expressions when using DependencyInjection (#55246) * Trim Linq.Expressions when using DependencyInjection Because ILEmitResolverBuilder (the default resolver) has a dependency on ExpressionResolverBuilder, this forces apps that use DI to bring in System.Linq.Expressions. Refactoring the logic so Expressions can be trimmed in apps using DI. * Remove unused method --- .../Expressions/ExpressionResolverBuilder.cs | 37 ++++------------ .../ILEmit/ILEmitResolverBuilder.cs | 15 ++++--- .../src/ServiceLookup/ServiceLookupHelpers.cs | 42 +++++++++++++++++++ 3 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index 76e54ec2814..4fbed795a9a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -4,26 +4,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Threading; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class ExpressionResolverBuilder : CallSiteVisitor { - internal static readonly MethodInfo InvokeFactoryMethodInfo = GetMethodInfo, IServiceProvider>>((a, b) => a.Invoke(b)); - internal static readonly MethodInfo CaptureDisposableMethodInfo = GetMethodInfo>((a, b) => a.CaptureDisposable(b)); - internal static readonly MethodInfo TryGetValueMethodInfo = GetMethodInfo, ServiceCacheKey, object, bool>>((a, b, c) => a.TryGetValue(b, out c)); - internal static readonly MethodInfo ResolveCallSiteAndScopeMethodInfo = GetMethodInfo>((a, b, c) => a.Resolve(b, c)); - internal static readonly MethodInfo AddMethodInfo = GetMethodInfo, ServiceCacheKey, object>>((a, b, c) => a.Add(b, c)); - internal static readonly MethodInfo MonitorEnterMethodInfo = GetMethodInfo>((lockObj, lockTaken) => Monitor.Enter(lockObj, ref lockTaken)); - internal static readonly MethodInfo MonitorExitMethodInfo = GetMethodInfo>(lockObj => Monitor.Exit(lockObj)); - - private static readonly MethodInfo ArrayEmptyMethodInfo = typeof(Array).GetMethod(nameof(Array.Empty)); - private static readonly ParameterExpression ScopeParameter = Expression.Parameter(typeof(ServiceProviderEngineScope)); private static readonly ParameterExpression ResolvedServices = Expression.Variable(typeof(IDictionary), ScopeParameter.Name + "resolvedServices"); @@ -42,7 +30,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup private static readonly ParameterExpression CaptureDisposableParameter = Expression.Parameter(typeof(object)); private static readonly LambdaExpression CaptureDisposable = Expression.Lambda( - Expression.Call(ScopeParameter, CaptureDisposableMethodInfo, CaptureDisposableParameter), + Expression.Call(ScopeParameter, ServiceLookupHelpers.CaptureDisposableMethodInfo, CaptureDisposableParameter), CaptureDisposableParameter); private static readonly ConstantExpression CallSiteRuntimeResolverInstanceExpression = Expression.Constant( @@ -127,7 +115,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup if (callSite.ServiceCallSites.Length == 0) { return Expression.Constant( - GetArrayEmptyMethodInfo(callSite.ItemType) + ServiceLookupHelpers.GetArrayEmptyMethodInfo(callSite.ItemType) .Invoke(obj: null, parameters: Array.Empty())); } @@ -148,11 +136,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup VisitCallSiteMain(callSite, context)); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", - Justification = "Calling Array.Empty() is safe since the T doesn't have trimming annotations.")] - internal static MethodInfo GetArrayEmptyMethodInfo(Type itemType) => - ArrayEmptyMethodInfo.MakeGenericMethod(itemType); - private Expression TryCaptureDisposable(ServiceCallSite callSite, ParameterExpression scope, Expression service) { if (!callSite.CaptureDisposable) @@ -212,7 +195,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // Avoid the compilation for singletons (or promoted singletons) MethodCallExpression resolveRootScopeExpression = Expression.Call( CallSiteRuntimeResolverInstanceExpression, - ResolveCallSiteAndScopeMethodInfo, + ServiceLookupHelpers.ResolveCallSiteAndScopeMethodInfo, callSiteExpression, ScopeParameter); @@ -226,7 +209,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup MethodCallExpression tryGetValueExpression = Expression.Call( resolvedServices, - TryGetValueMethodInfo, + ServiceLookupHelpers.TryGetValueMethodInfo, keyExpression, resolvedVariable); @@ -238,7 +221,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup MethodCallExpression addValueExpression = Expression.Call( resolvedServices, - AddMethodInfo, + ServiceLookupHelpers.AddMethodInfo, keyExpression, resolvedVariable); @@ -261,8 +244,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup ParameterExpression lockWasTaken = Expression.Variable(typeof(bool), "lockWasTaken"); ParameterExpression sync = Sync; - MethodCallExpression monitorEnter = Expression.Call(MonitorEnterMethodInfo, sync, lockWasTaken); - MethodCallExpression monitorExit = Expression.Call(MonitorExitMethodInfo, sync); + MethodCallExpression monitorEnter = Expression.Call(ServiceLookupHelpers.MonitorEnterMethodInfo, sync, lockWasTaken); + MethodCallExpression monitorExit = Expression.Call(ServiceLookupHelpers.MonitorExitMethodInfo, sync); BlockExpression tryBody = Expression.Block(monitorEnter, blockExpression); ConditionalExpression finallyBody = Expression.IfThen(lockWasTaken, monitorExit); @@ -280,12 +263,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup ); } - private static MethodInfo GetMethodInfo(Expression expr) - { - var mc = (MethodCallExpression)expr.Body; - return mc.Method; - } - public Expression GetCaptureDisposable(ParameterExpression scope) { if (scope != ScopeParameter) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs index 61690bdd451..6cc6ba36b96 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs @@ -6,7 +6,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using Microsoft.Extensions.DependencyInjection.ServiceLookup; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { @@ -198,7 +197,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { if (enumerableCallSite.ServiceCallSites.Length == 0) { - argument.Generator.Emit(OpCodes.Call, ExpressionResolverBuilder.GetArrayEmptyMethodInfo(enumerableCallSite.ItemType)); + argument.Generator.Emit(OpCodes.Call, ServiceLookupHelpers.GetArrayEmptyMethodInfo(enumerableCallSite.ItemType)); } else { @@ -245,7 +244,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup argument.Generator.Emit(OpCodes.Ldelem, typeof(Func)); argument.Generator.Emit(OpCodes.Ldarg_1); - argument.Generator.Emit(OpCodes.Call, ExpressionResolverBuilder.InvokeFactoryMethodInfo); + argument.Generator.Emit(OpCodes.Call, ServiceLookupHelpers.InvokeFactoryMethodInfo); argument.Factories.Add(factoryCallSite.Factory); return null; @@ -357,7 +356,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // Load address of lockTaken context.Generator.Emit(OpCodes.Ldloca_S, lockTakenLocal.LocalIndex); // Monitor.Enter - context.Generator.Emit(OpCodes.Call, ExpressionResolverBuilder.MonitorEnterMethodInfo); + context.Generator.Emit(OpCodes.Call, ServiceLookupHelpers.MonitorEnterMethodInfo); // Load resolved services Ldloc(context.Generator, resolvedServicesLocal.LocalIndex); @@ -366,7 +365,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // Load address of result local context.Generator.Emit(OpCodes.Ldloca_S, resultLocal.LocalIndex); // .TryGetValue - context.Generator.Emit(OpCodes.Callvirt, ExpressionResolverBuilder.TryGetValueMethodInfo); + context.Generator.Emit(OpCodes.Callvirt, ServiceLookupHelpers.TryGetValueMethodInfo); // Jump to the end if already in cache context.Generator.Emit(OpCodes.Brtrue, skipCreationLabel); @@ -391,7 +390,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // load value Ldloc(context.Generator, resultLocal.LocalIndex); // .Add - context.Generator.Emit(OpCodes.Callvirt, ExpressionResolverBuilder.AddMethodInfo); + context.Generator.Emit(OpCodes.Callvirt, ServiceLookupHelpers.AddMethodInfo); context.Generator.MarkLabel(skipCreationLabel); @@ -404,7 +403,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup // Load syncLocal Ldloc(context.Generator, syncLocal.LocalIndex); // Monitor.Exit - context.Generator.Emit(OpCodes.Call, ExpressionResolverBuilder.MonitorExitMethodInfo); + context.Generator.Emit(OpCodes.Call, ServiceLookupHelpers.MonitorExitMethodInfo); context.Generator.MarkLabel(returnLabel); @@ -438,7 +437,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup private static void EndCaptureDisposable(ILEmitResolverBuilderContext argument) { // Call CaptureDisposabl we expect calee and arguments to be on the stackcontext.Generator.BeginExceptionBlock - argument.Generator.Emit(OpCodes.Callvirt, ExpressionResolverBuilder.CaptureDisposableMethodInfo); + argument.Generator.Emit(OpCodes.Callvirt, ServiceLookupHelpers.CaptureDisposableMethodInfo); } private void Ldloc(ILGenerator generator, int index) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs new file mode 100644 index 00000000000..c17b501ef4b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceLookupHelpers.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Threading; + +namespace Microsoft.Extensions.DependencyInjection.ServiceLookup +{ + internal static class ServiceLookupHelpers + { + private const BindingFlags LookupFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + private static readonly MethodInfo ArrayEmptyMethodInfo = typeof(Array).GetMethod(nameof(Array.Empty)); + + internal static readonly MethodInfo InvokeFactoryMethodInfo = typeof(Func) + .GetMethod(nameof(Func.Invoke), LookupFlags); + + internal static readonly MethodInfo CaptureDisposableMethodInfo = typeof(ServiceProviderEngineScope) + .GetMethod(nameof(ServiceProviderEngineScope.CaptureDisposable), LookupFlags); + + internal static readonly MethodInfo TryGetValueMethodInfo = typeof(IDictionary) + .GetMethod(nameof(IDictionary.TryGetValue), LookupFlags); + + internal static readonly MethodInfo ResolveCallSiteAndScopeMethodInfo = typeof(CallSiteRuntimeResolver) + .GetMethod(nameof(CallSiteRuntimeResolver.Resolve), LookupFlags); + + internal static readonly MethodInfo AddMethodInfo = typeof(IDictionary) + .GetMethod(nameof(IDictionary.Add), LookupFlags); + + internal static readonly MethodInfo MonitorEnterMethodInfo = typeof(Monitor) + .GetMethod(nameof(Monitor.Enter), BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(bool).MakeByRefType() }, null); + internal static readonly MethodInfo MonitorExitMethodInfo = typeof(Monitor) + .GetMethod(nameof(Monitor.Exit), BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object) }, null); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", + Justification = "Calling Array.Empty() is safe since the T doesn't have trimming annotations.")] + internal static MethodInfo GetArrayEmptyMethodInfo(Type itemType) => + ArrayEmptyMethodInfo.MakeGenericMethod(itemType); + } +} From 594901aa0ae58d9df1cf5daaee15b5f055a9deda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:21:59 +0200 Subject: [PATCH 342/926] Updated readme about msquic packaging (#55326) * Update readme to reflect packaging and msquic version * Update src/libraries/System.Net.Quic/readme.md Co-authored-by: Natalia Kondratyeva Co-authored-by: Natalia Kondratyeva --- src/libraries/System.Net.Quic/readme.md | 29 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Quic/readme.md b/src/libraries/System.Net.Quic/readme.md index f3b27a0fa42..2b5a5d5384c 100644 --- a/src/libraries/System.Net.Quic/readme.md +++ b/src/libraries/System.Net.Quic/readme.md @@ -1,13 +1,34 @@ # MsQuic `System.Net.Quic` depends on [MsQuic](https://github.com/microsoft/msquic), Microsoft, cross-platform, native implementation of the [QUIC](https://datatracker.ietf.org/wg/quic/about/) protocol. -Currently, `System.Net.Quic` depends on [**msquic@26cff1a8de7890cf7ff77709ee14b51bc84e330e**](https://github.com/microsoft/msquic/commit/26cff1a8de7890cf7ff77709ee14b51bc84e330e) revision. +Currently, `System.Net.Quic` depends on [**msquic@3e40721bff04845208bc07eb4ee0c5e421e6388d**](https://github.com/microsoft/msquic/commit/3e40721bff04845208bc07eb4ee0c5e421e6388d) revision. ## Usage +MsQuic library in now being published so there's no need to compile it yourself. + +For a reference, the packaging repository is in https://github.com/dotnet/msquic. + +### Windows +Prerequisites: +- Latest [Windows Insider Builds](https://insider.windows.com/en-us/), Insiders Fast build. This is required for SChannel support for QUIC. + - To confirm you have a new enough build, run winver on command line and confirm you version is greater than Version 2004 (OS Build 20145.1000). +- Turned on TLS 1.3 + - It is turned on by default, to confirm you can check the appropriate registry `Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols` (empty means default which means enabled). + +During the build, the `msquic.dll` is automatically downloaded and placed in correct directories in order to be picked up by the runtime. It is also published as part of the runtime for Windows. + +### Linux + +On Linux, `libmsquic` is published via Microsoft official Linux package repository `packages.microsoft.com`. In order to consume it, you have to add it manually, see https://docs.microsoft.com/en-us/windows-server/administration/linux-package-repository-for-microsoft-software. After that, you should be able to install it via the package manager of your distro, e.g. for Ubuntu: +``` +apt install libmsquic +``` + ### Build MsQuic [MsQuic build docs](https://github.com/microsoft/msquic/blob/main/docs/BUILD.md) +You might want to test some `msquic` changes which hasn't propagated into the released package. For that, you need to build `msquic` yourself. #### Linux Prerequisites: @@ -20,7 +41,6 @@ Prerequisites: - microsoft.logging.clog - microsoft.logging.clog2text.lttng - Run inside the msquic directory (for **Debug** build with logging on): ```bash # build msquic in debug with logging @@ -37,7 +57,6 @@ yes | cp -rf bin/Debug/libmsquic.* /src/libraries/System.Net.Qu #### Windows Prerequisites: - Latest [Windows Insider Builds](https://insider.windows.com/en-us/), Insiders Fast build. This is required for SChannel support for QUIC. - - To confirm you have a new enough build, run winver on command line and confirm you version is greater than Version 2004 (OS Build 20145.1000). - -TODO + - To confirm you have a new enough build, run `winver` on command line and confirm you version is greater than Version 2004 (OS Build 20145.1000). +Follow the instructions from msquic build documentation. From 885296a7da5f2699195df37e97e261f46d20faf2 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 8 Jul 2021 16:18:06 +0200 Subject: [PATCH 343/926] Add nullable annotations to System.ComponentModel.TypeConverter (#54961) * Add nullable annotations to System.ComponentModel.TypeConverter * Annotate System.Data.Common * Annotations in depending projects * address feedback --- .../MetadataPropertyDescriptorWrapper.cs | 4 +- .../DataAnnotations/RangeAttribute.cs | 8 +- .../System.ComponentModel.TypeConverter.cs | 1098 ++++++++--------- ...System.ComponentModel.TypeConverter.csproj | 1 + .../Linq/ComponentModel/XComponentModel.cs | 120 +- ...System.ComponentModel.TypeConverter.csproj | 1 + .../ComponentModel/AddingNewEventArgs.cs | 4 +- .../ComponentModel/AddingNewEventHandler.cs | 2 +- .../ComponentModel/AmbientValueAttribute.cs | 8 +- .../System/ComponentModel/ArrayConverter.cs | 13 +- .../ComponentModel/AttributeCollection.cs | 32 +- .../AttributeProviderAttribute.cs | 4 +- .../ComponentModel/BaseComponentEditor.cs | 2 +- .../ComponentModel/BaseNumberConverter.cs | 16 +- .../ComponentModel/BindableAttribute.cs | 4 +- .../src/System/ComponentModel/BindingList.cs | 32 +- .../System/ComponentModel/BooleanConverter.cs | 12 +- .../System/ComponentModel/ByteConverter.cs | 4 +- .../ComponentModel/CancelEventHandler.cs | 2 +- .../System/ComponentModel/CharConverter.cs | 6 +- .../CollectionChangeEventArgs.cs | 4 +- .../CollectionChangeEventHandler.cs | 2 +- .../ComponentModel/CollectionConverter.cs | 4 +- .../ComplexBindingPropertiesAttribute.cs | 12 +- .../ComponentModel/ComponentConverter.cs | 4 +- .../ComponentResourceManager.cs | 36 +- .../src/System/ComponentModel/Container.cs | 50 +- .../ComponentModel/CultureInfoConverter.cs | 28 +- .../ComponentModel/CustomTypeDescriptor.cs | 20 +- .../ComponentModel/DataObjectAttribute.cs | 4 +- .../DataObjectFieldAttribute.cs | 4 +- .../DataObjectMethodAttribute.cs | 6 +- .../ComponentModel/DateTimeConverter.cs | 18 +- .../ComponentModel/DateTimeOffsetConverter.cs | 20 +- .../System/ComponentModel/DecimalConverter.cs | 10 +- .../DefaultBindingPropertyAttribute.cs | 8 +- .../ComponentModel/DefaultEventAttribute.cs | 8 +- .../DefaultPropertyAttribute.cs | 8 +- .../DelegatingTypeDescriptionProvider.cs | 16 +- .../Design/ActiveDesignerEventArgs.cs | 6 +- .../Design/ActiveDocumentEventHandler.cs | 2 +- .../Design/CheckoutException.cs | 6 +- .../System/ComponentModel/Design/CommandID.cs | 3 +- .../Design/ComponentChangedEventArgs.cs | 10 +- .../Design/ComponentChangedEventHandler.cs | 2 +- .../Design/ComponentChangingEventArgs.cs | 6 +- .../Design/ComponentChangingEventHandler.cs | 2 +- .../Design/ComponentEventArgs.cs | 4 +- .../Design/ComponentEventHandler.cs | 2 +- .../Design/ComponentRenameEventArgs.cs | 8 +- .../Design/ComponentRenameEventHandler.cs | 2 +- .../Design/DesignerCollection.cs | 8 +- .../Design/DesignerEventArgs.cs | 4 +- .../Design/DesignerEventHandler.cs | 2 +- .../Design/DesignerOptionService.cs | 63 +- .../DesignerTransactionCloseEventHandler.cs | 2 +- .../ComponentModel/Design/DesignerVerb.cs | 4 +- .../Design/DesignerVerbCollection.cs | 18 +- .../Design/DesigntimeLicenseContext.cs | 24 +- .../DesigntimeLicenseContextSerializer.cs | 6 +- .../Design/HelpKeywordAttribute.cs | 5 +- .../Design/IComponentChangeService.cs | 4 +- .../Design/IComponentDiscoveryService.cs | 2 +- .../Design/IComponentInitializer.cs | 4 +- .../System/ComponentModel/Design/IDesigner.cs | 2 +- .../Design/IDesignerEventService.cs | 2 +- .../ComponentModel/Design/IDesignerHost.cs | 4 +- .../Design/IDesignerOptionService.cs | 2 +- .../Design/IDictionaryService.cs | 6 +- .../Design/IEventPropertyService.cs | 2 +- .../Design/IMenuCommandService.cs | 2 +- .../Design/IReferenceService.cs | 6 +- .../ComponentModel/Design/IResourceService.cs | 2 +- .../Design/ISelectionService.cs | 4 +- .../ComponentModel/Design/ITreeDesigner.cs | 2 +- .../Design/ITypeDiscoveryService.cs | 2 +- .../Design/ITypeResolutionService.cs | 12 +- .../ComponentModel/Design/MenuCommand.cs | 10 +- .../Design/PropertyTabAttribute.cs | 29 +- .../Design/Serialization/ContextStack.cs | 14 +- .../DefaultSerializationProviderAttribute.cs | 2 +- .../Serialization/IDesignerLoaderHost.cs | 2 +- .../Serialization/IDesignerLoaderService.cs | 2 +- .../IDesignerSerializationProvider.cs | 2 +- .../Serialization/INameCreationService.cs | 2 +- .../Serialization/InstanceDescriptor.cs | 16 +- .../MemberRelationshipService.cs | 20 +- .../Serialization/ResolveNameEventArgs.cs | 6 +- .../Serialization/ResolveNameEventHandler.cs | 2 +- .../RootDesignerSerializerAttribute.cs | 8 +- .../ComponentModel/Design/ServiceContainer.cs | 36 +- .../Design/ServiceCreatorCallback.cs | 2 +- .../DesignTimeVisibleAttribute.cs | 3 +- .../System/ComponentModel/DoubleConverter.cs | 4 +- .../System/ComponentModel/EnumConverter.cs | 42 +- .../System/ComponentModel/EventDescriptor.cs | 4 +- .../EventDescriptorCollection.cs | 66 +- .../ExpandableObjectConverter.cs | 4 +- .../ExtendedPropertyDescriptor.cs | 20 +- .../ExtenderProvidedPropertyAttribute.cs | 15 +- .../System/ComponentModel/GuidConverter.cs | 12 +- .../ComponentModel/HandledEventHandler.cs | 2 +- .../src/System/ComponentModel/IBindingList.cs | 4 +- .../System/ComponentModel/IBindingListView.cs | 2 +- .../IComNativeDescriptorHandler.cs | 4 +- .../ComponentModel/ICustomTypeDescriptor.cs | 16 +- .../src/System/ComponentModel/INestedSite.cs | 2 +- .../ComponentModel/InheritanceAttribute.cs | 6 +- .../ComponentModel/InstallerTypeAttribute.cs | 8 +- .../ComponentModel/InstanceCreationEditor.cs | 2 +- .../System/ComponentModel/Int16Converter.cs | 4 +- .../System/ComponentModel/Int32Converter.cs | 4 +- .../System/ComponentModel/Int64Converter.cs | 4 +- .../ComponentModel/InterlockedBitVector32.cs | 3 +- .../ComponentModel/LicFileLicenseProvider.cs | 18 +- .../System/ComponentModel/LicenseContext.cs | 4 +- .../System/ComponentModel/LicenseException.cs | 12 +- .../LicenseManager.LicenseInteropHelper.cs | 18 +- .../System/ComponentModel/LicenseManager.cs | 56 +- .../System/ComponentModel/LicenseProvider.cs | 2 +- .../LicenseProviderAttribute.cs | 16 +- .../ComponentModel/ListBindableAttribute.cs | 4 +- .../ComponentModel/ListChangedEventArgs.cs | 6 +- .../ComponentModel/ListChangedEventHandler.cs | 2 +- .../ComponentModel/ListSortDescription.cs | 4 +- .../ListSortDescriptionCollection.cs | 19 +- .../LookupBindingPropertiesAttribute.cs | 12 +- .../ComponentModel/MarshalByValueComponent.cs | 18 +- .../ComponentModel/MaskedTextProvider.cs | 12 +- .../System/ComponentModel/MemberDescriptor.cs | 48 +- .../MultilineStringConverter.cs | 6 +- .../System/ComponentModel/NestedContainer.cs | 22 +- .../ComponentModel/NullableConverter.cs | 36 +- .../PasswordPropertyTextAttribute.cs | 4 +- .../ComponentModel/PropertyDescriptor.cs | 74 +- .../PropertyDescriptorCollection.cs | 74 +- .../ProvidePropertyAttribute.cs | 4 +- .../RecommendedAsConfigurableAttribute.cs | 4 +- .../ComponentModel/ReferenceConverter.cs | 34 +- .../ComponentModel/ReflectEventDescriptor.cs | 61 +- .../ReflectPropertyDescriptor.cs | 186 +-- ...peDescriptionProvider.ReflectedTypeData.cs | 82 +- .../ReflectTypeDescriptionProvider.cs | 204 +-- .../ReflectionCachesUpdateHandler.cs | 1 - .../System/ComponentModel/RefreshEventArgs.cs | 8 +- .../ComponentModel/RunInstallerAttribute.cs | 4 +- .../System/ComponentModel/SByteConverter.cs | 4 +- .../SettingsBindableAttribute.cs | 4 +- .../System/ComponentModel/SingleConverter.cs | 4 +- .../System/ComponentModel/StringConverter.cs | 4 +- .../ComponentModel/TimeSpanConverter.cs | 10 +- .../ComponentModel/ToolboxItemAttribute.cs | 8 +- .../ToolboxItemFilterAttribute.cs | 8 +- .../System/ComponentModel/TypeConverter.cs | 88 +- .../ComponentModel/TypeDescriptionProvider.cs | 24 +- .../System/ComponentModel/TypeDescriptor.cs | 403 +++--- .../ComponentModel/TypeListConverter.cs | 18 +- .../System/ComponentModel/UInt16Converter.cs | 4 +- .../System/ComponentModel/UInt32Converter.cs | 4 +- .../System/ComponentModel/UInt64Converter.cs | 4 +- .../System/ComponentModel/UriTypeConverter.cs | 14 +- .../System/ComponentModel/VersionConverter.cs | 12 +- .../System/ComponentModel/WarningException.cs | 16 +- .../System/ComponentModel/WeakHashtable.cs | 6 +- .../src/System/Drawing/ColorConverter.cs | 20 +- .../src/System/Drawing/PointConverter.cs | 26 +- .../src/System/Drawing/RectangleConverter.cs | 30 +- .../src/System/Drawing/SizeConverter.cs | 26 +- .../src/System/Drawing/SizeFConverter.cs | 26 +- .../src/System/InvariantComparer.cs | 2 +- .../ExtendedProtectionPolicyTypeConverter.cs | 12 +- .../src/System/Timers/ElapsedEventHandler.cs | 2 +- .../src/System/Timers/Timer.cs | 22 +- .../Timers/TimersDescriptionAttribute.cs | 2 +- .../ref/System.Data.Common.cs | 58 +- .../src/System/Data/ColumnTypeConverter.cs | 16 +- .../System/Data/Common/AdapterUtil.Common.cs | 2 +- .../System/Data/Common/DataColumnMapping.cs | 4 +- .../System/Data/Common/DataRecordInternal.cs | 19 +- .../System/Data/Common/DataTableMapping.cs | 4 +- .../Data/Common/DbConnectionStringBuilder.cs | 19 +- .../DbConnectionStringBuilderDescriptor.cs | 4 +- .../src/System/Data/Common/DbDataRecord.cs | 19 +- .../src/System/Data/Common/DbEnumerator.cs | 4 +- .../src/System/Data/ConstraintConverter.cs | 4 +- .../Data/DataColumnPropertyDescriptor.cs | 11 +- .../Data/DataRelationPropertyDescriptor.cs | 9 +- .../src/System/Data/DataRowComparer.cs | 2 - .../src/System/Data/DataRowView.cs | 21 +- .../src/System/Data/DataSet.cs | 15 +- .../src/System/Data/DataTable.cs | 16 +- .../Data/DataTablePropertyDescriptor.cs | 9 +- .../System/Data/DataTableReaderListener.cs | 4 +- .../src/System/Data/DataTableTypeConverter.cs | 2 +- .../src/System/Data/DataView.cs | 34 +- .../src/System/Data/DataViewListener.cs | 6 +- .../src/System/Data/DataViewManager.cs | 12 +- .../System/Data/DefaultValueTypeConverter.cs | 7 +- .../src/System/Data/LinqDataView.cs | 5 +- .../System/Data/PrimaryKeyTypeConverter.cs | 6 +- .../src/System/Data/RelationshipConverter.cs | 4 +- .../src/System/Data/XDRSchema.cs | 49 +- .../src/System/Data/xmlsaver.cs | 4 +- .../src/System/Xml/XmlDataDocument.cs | 10 +- .../src/OleDbConnectionStringBuilder.cs | 26 +- .../System.Data.OleDb/src/OleDbException.cs | 2 +- .../System.Data.OleDb/src/OleDbParameter.cs | 4 +- .../Design/DirectoryEntryConverter.cs | 14 +- .../ref/System.Drawing.Common.cs | 32 +- .../src/System/Drawing/FontConverter.cs | 16 +- .../src/System/Drawing/IconConverter.cs | 8 +- .../src/System/Drawing/ImageConverter.cs | 2 +- .../System/Drawing/ImageFormatConverter.cs | 6 +- .../Drawing/Printing/MarginsConverter.cs | 8 +- .../Extensions/DeserializingResourceReader.cs | 4 +- .../Extensions/PreserializedResourceWriter.cs | 2 +- 216 files changed, 2289 insertions(+), 2338 deletions(-) diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs index f771e0e340d..b3f21b853e2 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.DataAnnotations public override Type ComponentType { get { return _descriptor.ComponentType; } } - public override object GetValue(object component) { return _descriptor.GetValue(component); } + public override object? GetValue(object? component) { return _descriptor.GetValue(component); } public override bool IsReadOnly { @@ -45,7 +45,7 @@ namespace System.ComponentModel.DataAnnotations public override void ResetValue(object component) { _descriptor.ResetValue(component); } - public override void SetValue(object component, object value) { _descriptor.SetValue(component, value); } + public override void SetValue(object? component, object? value) { _descriptor.SetValue(component, value); } public override bool ShouldSerializeValue(object component) { return _descriptor.ShouldSerializeValue(component); } diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs index e20ebdbf8b4..69d0b136c56 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs @@ -207,11 +207,11 @@ namespace System.ComponentModel.DataAnnotations TypeConverter converter = GetOperandTypeConverter(); IComparable min = (IComparable)(ParseLimitsInInvariantCulture - ? converter.ConvertFromInvariantString((string)minimum) - : converter.ConvertFromString((string)minimum)); + ? converter.ConvertFromInvariantString((string)minimum)! + : converter.ConvertFromString((string)minimum))!; IComparable max = (IComparable)(ParseLimitsInInvariantCulture - ? converter.ConvertFromInvariantString((string)maximum) - : converter.ConvertFromString((string)maximum)); + ? converter.ConvertFromInvariantString((string)maximum)! + : converter.ConvertFromString((string)maximum))!; Func conversion; if (ConvertValueInInvariantCulture) diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index d8ca41c78fb..e222d17e5aa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -9,11 +9,11 @@ namespace System public partial class UriTypeConverter : System.ComponentModel.TypeConverter { public UriTypeConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override bool IsValid(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } } } namespace System.ComponentModel @@ -21,10 +21,10 @@ namespace System.ComponentModel public partial class AddingNewEventArgs : System.EventArgs { public AddingNewEventArgs() { } - public AddingNewEventArgs(object newObject) { } - public object NewObject { get { throw null; } set { } } + public AddingNewEventArgs(object? newObject) { } + public object? NewObject { get { throw null; } set { } } } - public delegate void AddingNewEventHandler(object sender, System.ComponentModel.AddingNewEventArgs e); + public delegate void AddingNewEventHandler(object? sender, System.ComponentModel.AddingNewEventArgs e); [System.AttributeUsageAttribute(System.AttributeTargets.All)] public sealed partial class AmbientValueAttribute : System.Attribute { @@ -35,45 +35,46 @@ namespace System.ComponentModel public AmbientValueAttribute(short value) { } public AmbientValueAttribute(int value) { } public AmbientValueAttribute(long value) { } - public AmbientValueAttribute(object value) { } + public AmbientValueAttribute(object? value) { } public AmbientValueAttribute(float value) { } - public AmbientValueAttribute(string value) { } + public AmbientValueAttribute(string? value) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] public AmbientValueAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string value) { } - public object Value { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public object? Value { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } public partial class ArrayConverter : System.ComponentModel.CollectionConverter { public ArrayConverter() { } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("value")] + public override System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class AttributeCollection : System.Collections.ICollection, System.Collections.IEnumerable { public static readonly System.ComponentModel.AttributeCollection Empty; protected AttributeCollection() { } - public AttributeCollection(params System.Attribute[] attributes) { } + public AttributeCollection(params System.Attribute[]? attributes) { } protected virtual System.Attribute[] Attributes { get { throw null; } } public int Count { get { throw null; } } public virtual System.Attribute this[int index] { get { throw null; } } - public virtual System.Attribute this[[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type attributeType] { get { throw null; } } + public virtual System.Attribute? this[[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type attributeType] { get { throw null; } } int System.Collections.ICollection.Count { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public bool Contains(System.Attribute attribute) { throw null; } + public bool Contains(System.Attribute? attribute) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public bool Contains(System.Attribute[] attributes) { throw null; } + public bool Contains(System.Attribute[]? attributes) { throw null; } public void CopyTo(System.Array array, int index) { } - public static System.ComponentModel.AttributeCollection FromExisting(System.ComponentModel.AttributeCollection existing, params System.Attribute[] newAttributes) { throw null; } - protected System.Attribute GetDefaultAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type attributeType) { throw null; } + public static System.ComponentModel.AttributeCollection FromExisting(System.ComponentModel.AttributeCollection existing, params System.Attribute[]? newAttributes) { throw null; } + protected System.Attribute? GetDefaultAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type attributeType) { throw null; } public System.Collections.IEnumerator GetEnumerator() { throw null; } - public bool Matches(System.Attribute attribute) { throw null; } - public bool Matches(System.Attribute[] attributes) { throw null; } + public bool Matches(System.Attribute? attribute) { throw null; } + public bool Matches(System.Attribute[]? attributes) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Property)] @@ -82,17 +83,17 @@ namespace System.ComponentModel public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] string typeName) { } public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] string typeName, string propertyName) { } public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] System.Type type) { } - public string PropertyName { get { throw null; } } + public string? PropertyName { get { throw null; } } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] - public string TypeName { get { throw null; } } + public string? TypeName { get { throw null; } } } public abstract partial class BaseNumberConverter : System.ComponentModel.TypeConverter { internal BaseNumberConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public sealed partial class BindableAttribute : System.Attribute @@ -106,7 +107,7 @@ namespace System.ComponentModel public BindableAttribute(System.ComponentModel.BindableSupport flags, System.ComponentModel.BindingDirection direction) { } public bool Bindable { get { throw null; } } public System.ComponentModel.BindingDirection Direction { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -133,7 +134,7 @@ namespace System.ComponentModel protected virtual bool IsSortedCore { get { throw null; } } public bool RaiseListChangedEvents { get { throw null; } set { } } protected virtual System.ComponentModel.ListSortDirection SortDirectionCore { get { throw null; } } - protected virtual System.ComponentModel.PropertyDescriptor SortPropertyCore { get { throw null; } } + protected virtual System.ComponentModel.PropertyDescriptor? SortPropertyCore { get { throw null; } } protected virtual bool SupportsChangeNotificationCore { get { throw null; } } protected virtual bool SupportsSearchingCore { get { throw null; } } protected virtual bool SupportsSortingCore { get { throw null; } } @@ -142,7 +143,7 @@ namespace System.ComponentModel bool System.ComponentModel.IBindingList.AllowRemove { get { throw null; } } bool System.ComponentModel.IBindingList.IsSorted { get { throw null; } } System.ComponentModel.ListSortDirection System.ComponentModel.IBindingList.SortDirection { get { throw null; } } - System.ComponentModel.PropertyDescriptor System.ComponentModel.IBindingList.SortProperty { get { throw null; } } + System.ComponentModel.PropertyDescriptor? System.ComponentModel.IBindingList.SortProperty { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsChangeNotification { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSearching { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSorting { get { throw null; } } @@ -150,7 +151,7 @@ namespace System.ComponentModel public event System.ComponentModel.AddingNewEventHandler AddingNew { add { } remove { } } public event System.ComponentModel.ListChangedEventHandler ListChanged { add { } remove { } } public T AddNew() { throw null; } - protected virtual object AddNewCore() { throw null; } + protected virtual object? AddNewCore() { throw null; } protected virtual void ApplySortCore(System.ComponentModel.PropertyDescriptor prop, System.ComponentModel.ListSortDirection direction) { } public virtual void CancelNew(int itemIndex) { } protected override void ClearItems() { } @@ -174,23 +175,23 @@ namespace System.ComponentModel public partial class BooleanConverter : System.ComponentModel.TypeConverter { public BooleanConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class ByteConverter : System.ComponentModel.BaseNumberConverter { public ByteConverter() { } } - public delegate void CancelEventHandler(object sender, System.ComponentModel.CancelEventArgs e); + public delegate void CancelEventHandler(object? sender, System.ComponentModel.CancelEventArgs e); public partial class CharConverter : System.ComponentModel.TypeConverter { public CharConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } public enum CollectionChangeAction { @@ -200,42 +201,41 @@ namespace System.ComponentModel } public partial class CollectionChangeEventArgs : System.EventArgs { - public CollectionChangeEventArgs(System.ComponentModel.CollectionChangeAction action, object element) { } + public CollectionChangeEventArgs(System.ComponentModel.CollectionChangeAction action, object? element) { } public virtual System.ComponentModel.CollectionChangeAction Action { get { throw null; } } - public virtual object Element { get { throw null; } } + public virtual object? Element { get { throw null; } } } - public delegate void CollectionChangeEventHandler(object sender, System.ComponentModel.CollectionChangeEventArgs e); + public delegate void CollectionChangeEventHandler(object? sender, System.ComponentModel.CollectionChangeEventArgs e); public partial class CollectionConverter : System.ComponentModel.TypeConverter { public CollectionConverter() { } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class ComplexBindingPropertiesAttribute : System.Attribute { public static readonly System.ComponentModel.ComplexBindingPropertiesAttribute Default; public ComplexBindingPropertiesAttribute() { } - public ComplexBindingPropertiesAttribute(string dataSource) { } - public ComplexBindingPropertiesAttribute(string dataSource, string dataMember) { } - public string DataMember { get { throw null; } } - public string DataSource { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public ComplexBindingPropertiesAttribute(string? dataSource) { } + public ComplexBindingPropertiesAttribute(string? dataSource, string? dataMember) { } + public string? DataMember { get { throw null; } } + public string? DataSource { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } public partial class ComponentConverter : System.ComponentModel.ReferenceConverter { public ComponentConverter(System.Type type) : base (default(System.Type)) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public abstract partial class ComponentEditor { protected ComponentEditor() { } - public abstract bool EditComponent(System.ComponentModel.ITypeDescriptorContext context, object component); + public abstract bool EditComponent(System.ComponentModel.ITypeDescriptorContext? context, object component); public bool EditComponent(object component) { throw null; } } public partial class ComponentResourceManager : System.Resources.ResourceManager @@ -245,24 +245,24 @@ namespace System.ComponentModel [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] public void ApplyResources(object value, string objectName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] - public virtual void ApplyResources(object value, string objectName, System.Globalization.CultureInfo culture) { } + public virtual void ApplyResources(object value, string objectName, System.Globalization.CultureInfo? culture) { } } public partial class Container : System.ComponentModel.IContainer, System.IDisposable { public Container() { } public virtual System.ComponentModel.ComponentCollection Components { get { throw null; } } - public virtual void Add(System.ComponentModel.IComponent component) { } + public virtual void Add(System.ComponentModel.IComponent? component) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of components in the container cannot be statically discovered to validate the name.")] - public virtual void Add(System.ComponentModel.IComponent component, string name) { } - protected virtual System.ComponentModel.ISite CreateSite(System.ComponentModel.IComponent component, string name) { throw null; } + public virtual void Add(System.ComponentModel.IComponent? component, string? name) { } + protected virtual System.ComponentModel.ISite CreateSite(System.ComponentModel.IComponent component, string? name) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } ~Container() { } - protected virtual object GetService(System.Type service) { throw null; } - public virtual void Remove(System.ComponentModel.IComponent component) { } - protected void RemoveWithoutUnsiting(System.ComponentModel.IComponent component) { } + protected virtual object? GetService(System.Type service) { throw null; } + public virtual void Remove(System.ComponentModel.IComponent? component) { } + protected void RemoveWithoutUnsiting(System.ComponentModel.IComponent? component) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of components in the container cannot be statically discovered.")] - protected virtual void ValidateName(System.ComponentModel.IComponent component, string name) { } + protected virtual void ValidateName(System.ComponentModel.IComponent component, string? name) { } } public abstract partial class ContainerFilterService { @@ -272,38 +272,38 @@ namespace System.ComponentModel public partial class CultureInfoConverter : System.ComponentModel.TypeConverter { public CultureInfoConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } protected virtual string GetCultureName(System.Globalization.CultureInfo culture) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public abstract partial class CustomTypeDescriptor : System.ComponentModel.ICustomTypeDescriptor { protected CustomTypeDescriptor() { } - protected CustomTypeDescriptor(System.ComponentModel.ICustomTypeDescriptor parent) { } + protected CustomTypeDescriptor(System.ComponentModel.ICustomTypeDescriptor? parent) { } public virtual System.ComponentModel.AttributeCollection GetAttributes() { throw null; } - public virtual string GetClassName() { throw null; } - public virtual string GetComponentName() { throw null; } + public virtual string? GetClassName() { throw null; } + public virtual string? GetComponentName() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] public virtual System.ComponentModel.TypeConverter GetConverter() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - public virtual System.ComponentModel.EventDescriptor GetDefaultEvent() { throw null; } + public virtual System.ComponentModel.EventDescriptor? GetDefaultEvent() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] - public virtual System.ComponentModel.PropertyDescriptor GetDefaultProperty() { throw null; } + public virtual System.ComponentModel.PropertyDescriptor? GetDefaultProperty() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - public virtual object GetEditor(System.Type editorBaseType) { throw null; } + public virtual object? GetEditor(System.Type editorBaseType) { throw null; } public virtual System.ComponentModel.EventDescriptorCollection GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public virtual System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[] attributes) { throw null; } + public virtual System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[] attributes) { throw null; } - public virtual object GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes) { throw null; } + public virtual object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DataObjectAttribute : System.Attribute @@ -314,7 +314,7 @@ namespace System.ComponentModel public DataObjectAttribute() { } public DataObjectAttribute(bool isDataObject) { } public bool IsDataObject { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -329,7 +329,7 @@ namespace System.ComponentModel public bool IsNullable { get { throw null; } } public int Length { get { throw null; } } public bool PrimaryKey { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] @@ -339,9 +339,9 @@ namespace System.ComponentModel public DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType methodType, bool isDefault) { } public bool IsDefault { get { throw null; } } public System.ComponentModel.DataObjectMethodType MethodType { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } - public override bool Match(object obj) { throw null; } + public override bool Match([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } } public enum DataObjectMethodType { @@ -354,51 +354,51 @@ namespace System.ComponentModel public partial class DateTimeConverter : System.ComponentModel.TypeConverter { public DateTimeConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } public partial class DateTimeOffsetConverter : System.ComponentModel.TypeConverter { public DateTimeOffsetConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } public partial class DecimalConverter : System.ComponentModel.BaseNumberConverter { public DecimalConverter() { } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DefaultBindingPropertyAttribute : System.Attribute { public static readonly System.ComponentModel.DefaultBindingPropertyAttribute Default; public DefaultBindingPropertyAttribute() { } - public DefaultBindingPropertyAttribute(string name) { } - public string Name { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public DefaultBindingPropertyAttribute(string? name) { } + public string? Name { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DefaultEventAttribute : System.Attribute { public static readonly System.ComponentModel.DefaultEventAttribute Default; - public DefaultEventAttribute(string name) { } - public string Name { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public DefaultEventAttribute(string? name) { } + public string? Name { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DefaultPropertyAttribute : System.Attribute { public static readonly System.ComponentModel.DefaultPropertyAttribute Default; - public DefaultPropertyAttribute(string name) { } - public string Name { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public DefaultPropertyAttribute(string? name) { } + public string? Name { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface)] @@ -410,7 +410,7 @@ namespace System.ComponentModel public DesignTimeVisibleAttribute() { } public DesignTimeVisibleAttribute(bool visible) { } public bool Visible { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -424,21 +424,21 @@ namespace System.ComponentModel protected virtual System.Collections.IComparer Comparer { get { throw null; } } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] protected System.Type EnumType { get { throw null; } } - protected System.ComponentModel.TypeConverter.StandardValuesCollection Values { get { throw null; } set { } } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + protected System.ComponentModel.TypeConverter.StandardValuesCollection? Values { get { throw null; } set { } } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool IsValid(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } } public abstract partial class EventDescriptor : System.ComponentModel.MemberDescriptor { protected EventDescriptor(System.ComponentModel.MemberDescriptor descr) : base (default(string)) { } - protected EventDescriptor(System.ComponentModel.MemberDescriptor descr, System.Attribute[] attrs) : base (default(string)) { } - protected EventDescriptor(string name, System.Attribute[] attrs) : base (default(string)) { } + protected EventDescriptor(System.ComponentModel.MemberDescriptor descr, System.Attribute[]? attrs) : base (default(string)) { } + protected EventDescriptor(string name, System.Attribute[]? attrs) : base (default(string)) { } public abstract System.Type ComponentType { get; } public abstract System.Type EventType { get; } public abstract bool IsMulticast { get; } @@ -448,67 +448,67 @@ namespace System.ComponentModel public partial class EventDescriptorCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public static readonly System.ComponentModel.EventDescriptorCollection Empty; - public EventDescriptorCollection(System.ComponentModel.EventDescriptor[] events) { } - public EventDescriptorCollection(System.ComponentModel.EventDescriptor[] events, bool readOnly) { } + public EventDescriptorCollection(System.ComponentModel.EventDescriptor[]? events) { } + public EventDescriptorCollection(System.ComponentModel.EventDescriptor[]? events, bool readOnly) { } public int Count { get { throw null; } } - public virtual System.ComponentModel.EventDescriptor this[int index] { get { throw null; } } - public virtual System.ComponentModel.EventDescriptor this[string name] { get { throw null; } } + public virtual System.ComponentModel.EventDescriptor? this[int index] { get { throw null; } } + public virtual System.ComponentModel.EventDescriptor? this[string name] { get { throw null; } } int System.Collections.ICollection.Count { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } - object System.Collections.IList.this[int index] { get { throw null; } set { } } - public int Add(System.ComponentModel.EventDescriptor value) { throw null; } + object? System.Collections.IList.this[int index] { get { throw null; } set { } } + public int Add(System.ComponentModel.EventDescriptor? value) { throw null; } public void Clear() { } - public bool Contains(System.ComponentModel.EventDescriptor value) { throw null; } - public virtual System.ComponentModel.EventDescriptor Find(string name, bool ignoreCase) { throw null; } + public bool Contains(System.ComponentModel.EventDescriptor? value) { throw null; } + public virtual System.ComponentModel.EventDescriptor? Find(string name, bool ignoreCase) { throw null; } public System.Collections.IEnumerator GetEnumerator() { throw null; } - public int IndexOf(System.ComponentModel.EventDescriptor value) { throw null; } - public void Insert(int index, System.ComponentModel.EventDescriptor value) { } - protected void InternalSort(System.Collections.IComparer sorter) { } - protected void InternalSort(string[] names) { } - public void Remove(System.ComponentModel.EventDescriptor value) { } + public int IndexOf(System.ComponentModel.EventDescriptor? value) { throw null; } + public void Insert(int index, System.ComponentModel.EventDescriptor? value) { } + protected void InternalSort(System.Collections.IComparer? sorter) { } + protected void InternalSort(string[]? names) { } + public void Remove(System.ComponentModel.EventDescriptor? value) { } public void RemoveAt(int index) { } public virtual System.ComponentModel.EventDescriptorCollection Sort() { throw null; } public virtual System.ComponentModel.EventDescriptorCollection Sort(System.Collections.IComparer comparer) { throw null; } public virtual System.ComponentModel.EventDescriptorCollection Sort(string[] names) { throw null; } public virtual System.ComponentModel.EventDescriptorCollection Sort(string[] names, System.Collections.IComparer comparer) { throw null; } - void System.Collections.ICollection.CopyTo(System.Array array, int index) { } + void System.Collections.ICollection.CopyTo(System.Array? array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - int System.Collections.IList.Add(object value) { throw null; } + int System.Collections.IList.Add(object? value) { throw null; } void System.Collections.IList.Clear() { } - bool System.Collections.IList.Contains(object value) { throw null; } - int System.Collections.IList.IndexOf(object value) { throw null; } - void System.Collections.IList.Insert(int index, object value) { } - void System.Collections.IList.Remove(object value) { } + bool System.Collections.IList.Contains(object? value) { throw null; } + int System.Collections.IList.IndexOf(object? value) { throw null; } + void System.Collections.IList.Insert(int index, object? value) { } + void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } } public partial class ExpandableObjectConverter : System.ComponentModel.TypeConverter { public ExpandableObjectConverter() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public sealed partial class ExtenderProvidedPropertyAttribute : System.Attribute { public ExtenderProvidedPropertyAttribute() { } - public System.ComponentModel.PropertyDescriptor ExtenderProperty { get { throw null; } } - public System.ComponentModel.IExtenderProvider Provider { get { throw null; } } - public System.Type ReceiverType { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public System.ComponentModel.PropertyDescriptor? ExtenderProperty { get { throw null; } } + public System.ComponentModel.IExtenderProvider? Provider { get { throw null; } } + public System.Type? ReceiverType { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } public partial class GuidConverter : System.ComponentModel.TypeConverter { public GuidConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } public partial class HandledEventArgs : System.EventArgs { @@ -516,7 +516,7 @@ namespace System.ComponentModel public HandledEventArgs(bool defaultHandledValue) { } public bool Handled { get { throw null; } set { } } } - public delegate void HandledEventHandler(object sender, System.ComponentModel.HandledEventArgs e); + public delegate void HandledEventHandler(object? sender, System.ComponentModel.HandledEventArgs e); public partial interface IBindingList : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { bool AllowEdit { get; } @@ -524,13 +524,13 @@ namespace System.ComponentModel bool AllowRemove { get; } bool IsSorted { get; } System.ComponentModel.ListSortDirection SortDirection { get; } - System.ComponentModel.PropertyDescriptor SortProperty { get; } + System.ComponentModel.PropertyDescriptor? SortProperty { get; } bool SupportsChangeNotification { get; } bool SupportsSearching { get; } bool SupportsSorting { get; } event System.ComponentModel.ListChangedEventHandler ListChanged; void AddIndex(System.ComponentModel.PropertyDescriptor property); - object AddNew(); + object? AddNew(); void ApplySort(System.ComponentModel.PropertyDescriptor property, System.ComponentModel.ListSortDirection direction); int Find(System.ComponentModel.PropertyDescriptor property, object key); void RemoveIndex(System.ComponentModel.PropertyDescriptor property); @@ -538,7 +538,7 @@ namespace System.ComponentModel } public partial interface IBindingListView : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.ComponentModel.IBindingList { - string Filter { get; set; } + string? Filter { get; set; } System.ComponentModel.ListSortDescriptionCollection SortDescriptions { get; } bool SupportsAdvancedSorting { get; } bool SupportsFiltering { get; } @@ -550,7 +550,6 @@ namespace System.ComponentModel void CancelNew(int itemIndex); void EndNew(int itemIndex); } - [System.ObsoleteAttribute("This interface has been deprecated. Add a TypeDescriptionProvider to handle type TypeDescriptor.ComObjectType instead. https://go.microsoft.com/fwlink/?linkid=14202")] public partial interface IComNativeDescriptorHandler { System.ComponentModel.AttributeCollection GetAttributes(object component); @@ -560,33 +559,33 @@ namespace System.ComponentModel System.ComponentModel.PropertyDescriptor GetDefaultProperty(object component); object GetEditor(object component, System.Type baseEditorType); System.ComponentModel.EventDescriptorCollection GetEvents(object component); - System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[] attributes); + System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[]? attributes); string GetName(object component); - System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes); + System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[]? attributes); object GetPropertyValue(object component, int dispid, ref bool success); object GetPropertyValue(object component, string propertyName, ref bool success); } public partial interface ICustomTypeDescriptor { System.ComponentModel.AttributeCollection GetAttributes(); - string GetClassName(); - string GetComponentName(); + string? GetClassName(); + string? GetComponentName(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] System.ComponentModel.TypeConverter GetConverter(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - System.ComponentModel.EventDescriptor GetDefaultEvent(); + System.ComponentModel.EventDescriptor? GetDefaultEvent(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] - System.ComponentModel.PropertyDescriptor GetDefaultProperty(); + System.ComponentModel.PropertyDescriptor? GetDefaultProperty(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object GetEditor(System.Type editorBaseType); + object? GetEditor(System.Type editorBaseType); System.ComponentModel.EventDescriptorCollection GetEvents(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[] attributes); + System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[]? attributes); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptorCollection GetProperties(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[] attributes); - object GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd); + System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes); + object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd); } public partial interface IDataErrorInfo { @@ -616,7 +615,7 @@ namespace System.ComponentModel } public partial interface INestedSite : System.ComponentModel.ISite, System.IServiceProvider { - string FullName { get; } + string? FullName { get; } } [System.AttributeUsageAttribute(System.AttributeTargets.Event | System.AttributeTargets.Field | System.AttributeTargets.Property)] public sealed partial class InheritanceAttribute : System.Attribute @@ -628,7 +627,7 @@ namespace System.ComponentModel public InheritanceAttribute() { } public InheritanceAttribute(System.ComponentModel.InheritanceLevel inheritanceLevel) { } public System.ComponentModel.InheritanceLevel InheritanceLevel { get { throw null; } } - public override bool Equals(object value) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } public override string ToString() { throw null; } @@ -642,18 +641,18 @@ namespace System.ComponentModel [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public partial class InstallerTypeAttribute : System.Attribute { - public InstallerTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { } + public InstallerTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string? typeName) { } public InstallerTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type installerType) { } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - public virtual System.Type InstallerType { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public virtual System.Type? InstallerType { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } public abstract partial class InstanceCreationEditor { protected InstanceCreationEditor() { } public virtual string Text { get { throw null; } } - public abstract object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Type instanceType); + public abstract object? CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Type instanceType); } public partial class Int16Converter : System.ComponentModel.BaseNumberConverter { @@ -699,18 +698,18 @@ namespace System.ComponentModel { public LicenseContext() { } public virtual System.ComponentModel.LicenseUsageMode UsageMode { get { throw null; } } - public virtual string GetSavedLicenseKey(System.Type type, System.Reflection.Assembly resourceAssembly) { throw null; } - public virtual object GetService(System.Type type) { throw null; } + public virtual string? GetSavedLicenseKey(System.Type type, System.Reflection.Assembly? resourceAssembly) { throw null; } + public virtual object? GetService(System.Type type) { throw null; } public virtual void SetSavedLicenseKey(System.Type type, string key) { } } public partial class LicenseException : System.SystemException { protected LicenseException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public LicenseException(System.Type type) { } - public LicenseException(System.Type type, object instance) { } - public LicenseException(System.Type type, object instance, string message) { } - public LicenseException(System.Type type, object instance, string message, System.Exception innerException) { } - public System.Type LicensedType { get { throw null; } } + public LicenseException(System.Type? type) { } + public LicenseException(System.Type? type, object? instance) { } + public LicenseException(System.Type? type, object? instance, string? message) { } + public LicenseException(System.Type? type, object? instance, string? message, System.Exception? innerException) { } + public System.Type? LicensedType { get { throw null; } } public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed partial class LicenseManager @@ -719,33 +718,33 @@ namespace System.ComponentModel public static System.ComponentModel.LicenseContext CurrentContext { get { throw null; } set { } } public static System.ComponentModel.LicenseUsageMode UsageMode { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public static object CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext) { throw null; } + public static object? CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public static object CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext, object[] args) { throw null; } + public static object? CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext, object[] args) { throw null; } public static bool IsLicensed(System.Type type) { throw null; } public static bool IsValid(System.Type type) { throw null; } - public static bool IsValid(System.Type type, object instance, out System.ComponentModel.License license) { throw null; } + public static bool IsValid(System.Type type, object? instance, out System.ComponentModel.License? license) { throw null; } public static void LockContext(object contextUser) { } public static void UnlockContext(object contextUser) { } public static void Validate(System.Type type) { } - public static System.ComponentModel.License Validate(System.Type type, object instance) { throw null; } + public static System.ComponentModel.License? Validate(System.Type type, object? instance) { throw null; } } public abstract partial class LicenseProvider { protected LicenseProvider() { } - public abstract System.ComponentModel.License GetLicense(System.ComponentModel.LicenseContext context, System.Type type, object instance, bool allowExceptions); + public abstract System.ComponentModel.License? GetLicense(System.ComponentModel.LicenseContext context, System.Type type, object? instance, bool allowExceptions); } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false, Inherited=false)] public sealed partial class LicenseProviderAttribute : System.Attribute { public static readonly System.ComponentModel.LicenseProviderAttribute Default; public LicenseProviderAttribute() { } - public LicenseProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { } + public LicenseProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string? typeName) { } public LicenseProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type type) { } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - public System.Type LicenseProvider { get { throw null; } } + public System.Type? LicenseProvider { get { throw null; } } public override object TypeId { get { throw null; } } - public override bool Equals(object value) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw null; } public override int GetHashCode() { throw null; } } public enum LicenseUsageMode @@ -757,8 +756,8 @@ namespace System.ComponentModel { public LicFileLicenseProvider() { } protected virtual string GetKey(System.Type type) { throw null; } - public override System.ComponentModel.License GetLicense(System.ComponentModel.LicenseContext context, System.Type type, object instance, bool allowExceptions) { throw null; } - protected virtual bool IsKeyValid(string key, System.Type type) { throw null; } + public override System.ComponentModel.License? GetLicense(System.ComponentModel.LicenseContext context, System.Type type, object? instance, bool allowExceptions) { throw null; } + protected virtual bool IsKeyValid(string? key, System.Type type) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public sealed partial class ListBindableAttribute : System.Attribute @@ -769,22 +768,22 @@ namespace System.ComponentModel public ListBindableAttribute(bool listBindable) { } public ListBindableAttribute(System.ComponentModel.BindableSupport flags) { } public bool ListBindable { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } public partial class ListChangedEventArgs : System.EventArgs { - public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, System.ComponentModel.PropertyDescriptor propDesc) { } + public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, System.ComponentModel.PropertyDescriptor? propDesc) { } public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, int newIndex) { } - public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, int newIndex, System.ComponentModel.PropertyDescriptor propDesc) { } + public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, int newIndex, System.ComponentModel.PropertyDescriptor? propDesc) { } public ListChangedEventArgs(System.ComponentModel.ListChangedType listChangedType, int newIndex, int oldIndex) { } public System.ComponentModel.ListChangedType ListChangedType { get { throw null; } } public int NewIndex { get { throw null; } } public int OldIndex { get { throw null; } } - public System.ComponentModel.PropertyDescriptor PropertyDescriptor { get { throw null; } } + public System.ComponentModel.PropertyDescriptor? PropertyDescriptor { get { throw null; } } } - public delegate void ListChangedEventHandler(object sender, System.ComponentModel.ListChangedEventArgs e); + public delegate void ListChangedEventHandler(object? sender, System.ComponentModel.ListChangedEventArgs e); public enum ListChangedType { Reset = 0, @@ -798,29 +797,29 @@ namespace System.ComponentModel } public partial class ListSortDescription { - public ListSortDescription(System.ComponentModel.PropertyDescriptor property, System.ComponentModel.ListSortDirection direction) { } - public System.ComponentModel.PropertyDescriptor PropertyDescriptor { get { throw null; } set { } } + public ListSortDescription(System.ComponentModel.PropertyDescriptor? property, System.ComponentModel.ListSortDirection direction) { } + public System.ComponentModel.PropertyDescriptor? PropertyDescriptor { get { throw null; } set { } } public System.ComponentModel.ListSortDirection SortDirection { get { throw null; } set { } } } public partial class ListSortDescriptionCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public ListSortDescriptionCollection() { } - public ListSortDescriptionCollection(System.ComponentModel.ListSortDescription[] sorts) { } + public ListSortDescriptionCollection(System.ComponentModel.ListSortDescription?[]? sorts) { } public int Count { get { throw null; } } - public System.ComponentModel.ListSortDescription this[int index] { get { throw null; } set { } } + public System.ComponentModel.ListSortDescription? this[int index] { get { throw null; } set { } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } - object System.Collections.IList.this[int index] { get { throw null; } set { } } - public bool Contains(object value) { throw null; } + object? System.Collections.IList.this[int index] { get { throw null; } set { } } + public bool Contains(object? value) { throw null; } public void CopyTo(System.Array array, int index) { } - public int IndexOf(object value) { throw null; } + public int IndexOf(object? value) { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - int System.Collections.IList.Add(object value) { throw null; } + int System.Collections.IList.Add(object? value) { throw null; } void System.Collections.IList.Clear() { } - void System.Collections.IList.Insert(int index, object value) { } - void System.Collections.IList.Remove(object value) { } + void System.Collections.IList.Insert(int index, object? value) { } + void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } } public enum ListSortDirection @@ -834,11 +833,11 @@ namespace System.ComponentModel public static readonly System.ComponentModel.LookupBindingPropertiesAttribute Default; public LookupBindingPropertiesAttribute() { } public LookupBindingPropertiesAttribute(string dataSource, string displayMember, string valueMember, string lookupMember) { } - public string DataSource { get { throw null; } } - public string DisplayMember { get { throw null; } } - public string LookupMember { get { throw null; } } - public string ValueMember { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public string? DataSource { get { throw null; } } + public string? DisplayMember { get { throw null; } } + public string? LookupMember { get { throw null; } } + public string? ValueMember { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.ComponentModel.DesignerAttribute("System.Windows.Forms.Design.ComponentDocumentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.IRootDesigner, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] @@ -849,20 +848,20 @@ namespace System.ComponentModel public MarshalByValueComponent() { } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] - public virtual System.ComponentModel.IContainer Container { get { throw null; } } + public virtual System.ComponentModel.IContainer? Container { get { throw null; } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] public virtual bool DesignMode { get { throw null; } } protected System.ComponentModel.EventHandlerList Events { get { throw null; } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] - public virtual System.ComponentModel.ISite Site { get { throw null; } set { } } - public event System.EventHandler Disposed { add { } remove { } } + public virtual System.ComponentModel.ISite? Site { get { throw null; } set { } } + public event System.EventHandler? Disposed { add { } remove { } } public void Dispose() { } protected virtual void Dispose(bool disposing) { } ~MarshalByValueComponent() { } - public virtual object GetService(System.Type service) { throw null; } - public override string ToString() { throw null; } + public virtual object? GetService(System.Type service) { throw null; } + public override string? ToString() { throw null; } } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public partial class MaskedTextProvider : System.ICloneable @@ -870,10 +869,10 @@ namespace System.ComponentModel public MaskedTextProvider(string mask) { } public MaskedTextProvider(string mask, bool restrictToAscii) { } public MaskedTextProvider(string mask, char passwordChar, bool allowPromptAsInput) { } - public MaskedTextProvider(string mask, System.Globalization.CultureInfo culture) { } - public MaskedTextProvider(string mask, System.Globalization.CultureInfo culture, bool restrictToAscii) { } - public MaskedTextProvider(string mask, System.Globalization.CultureInfo culture, bool allowPromptAsInput, char promptChar, char passwordChar, bool restrictToAscii) { } - public MaskedTextProvider(string mask, System.Globalization.CultureInfo culture, char passwordChar, bool allowPromptAsInput) { } + public MaskedTextProvider(string mask, System.Globalization.CultureInfo? culture) { } + public MaskedTextProvider(string mask, System.Globalization.CultureInfo? culture, bool restrictToAscii) { } + public MaskedTextProvider(string mask, System.Globalization.CultureInfo? culture, bool allowPromptAsInput, char promptChar, char passwordChar, bool restrictToAscii) { } + public MaskedTextProvider(string mask, System.Globalization.CultureInfo? culture, char passwordChar, bool allowPromptAsInput) { } public bool AllowPromptAsInput { get { throw null; } } public bool AsciiOnly { get { throw null; } } public int AssignedEditPositionCount { get { throw null; } } @@ -970,10 +969,10 @@ namespace System.ComponentModel public abstract partial class MemberDescriptor { protected MemberDescriptor(System.ComponentModel.MemberDescriptor descr) { } - protected MemberDescriptor(System.ComponentModel.MemberDescriptor oldMemberDescriptor, System.Attribute[] newAttributes) { } + protected MemberDescriptor(System.ComponentModel.MemberDescriptor oldMemberDescriptor, System.Attribute[]? newAttributes) { } protected MemberDescriptor(string name) { } - protected MemberDescriptor(string name, System.Attribute[] attributes) { } - protected virtual System.Attribute[] AttributeArray { get { throw null; } set { } } + protected MemberDescriptor(string name, System.Attribute[]? attributes) { } + protected virtual System.Attribute[]? AttributeArray { get { throw null; } set { } } public virtual System.ComponentModel.AttributeCollection Attributes { get { throw null; } } public virtual string Category { get { throw null; } } public virtual string Description { get { throw null; } } @@ -983,32 +982,31 @@ namespace System.ComponentModel public virtual string Name { get { throw null; } } protected virtual int NameHashCode { get { throw null; } } protected virtual System.ComponentModel.AttributeCollection CreateAttributeCollection() { throw null; } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } protected virtual void FillAttributes(System.Collections.IList attributeList) { } - protected static System.Reflection.MethodInfo FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType) { throw null; } - protected static System.Reflection.MethodInfo FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType, bool publicOnly) { throw null; } + protected static System.Reflection.MethodInfo? FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType) { throw null; } + protected static System.Reflection.MethodInfo? FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType, bool publicOnly) { throw null; } public override int GetHashCode() { throw null; } - protected virtual object GetInvocationTarget(System.Type type, object instance) { throw null; } - [System.ObsoleteAttribute("This method has been deprecated. Use GetInvocationTarget instead. https://go.microsoft.com/fwlink/?linkid=14202")] + protected virtual object? GetInvocationTarget(System.Type type, object instance) { throw null; } protected static object GetInvokee(System.Type componentClass, object component) { throw null; } - protected static System.ComponentModel.ISite GetSite(object component) { throw null; } + protected static System.ComponentModel.ISite? GetSite(object? component) { throw null; } } public partial class MultilineStringConverter : System.ComponentModel.TypeConverter { public MultilineStringConverter() { } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class NestedContainer : System.ComponentModel.Container, System.ComponentModel.IContainer, System.ComponentModel.INestedContainer, System.IDisposable { public NestedContainer(System.ComponentModel.IComponent owner) { } public System.ComponentModel.IComponent Owner { get { throw null; } } - protected virtual string OwnerName { get { throw null; } } - protected override System.ComponentModel.ISite CreateSite(System.ComponentModel.IComponent component, string name) { throw null; } + protected virtual string? OwnerName { get { throw null; } } + protected override System.ComponentModel.ISite CreateSite(System.ComponentModel.IComponent component, string? name) { throw null; } protected override void Dispose(bool disposing) { } - protected override object GetService(System.Type service) { throw null; } + protected override object? GetService(System.Type service) { throw null; } } public partial class NullableConverter : System.ComponentModel.TypeConverter { @@ -1017,19 +1015,19 @@ namespace System.ComponentModel public System.Type NullableType { get { throw null; } } public System.Type UnderlyingType { get { throw null; } } public System.ComponentModel.TypeConverter UnderlyingTypeConverter { get { throw null; } } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object? CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection? GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool IsValid(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public sealed partial class PasswordPropertyTextAttribute : System.Attribute @@ -1040,15 +1038,15 @@ namespace System.ComponentModel public PasswordPropertyTextAttribute() { } public PasswordPropertyTextAttribute(bool password) { } public bool Password { get { throw null; } } - public override bool Equals(object o) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? o) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } public abstract partial class PropertyDescriptor : System.ComponentModel.MemberDescriptor { protected PropertyDescriptor(System.ComponentModel.MemberDescriptor descr) : base (default(string)) { } - protected PropertyDescriptor(System.ComponentModel.MemberDescriptor descr, System.Attribute[] attrs) : base (default(string)) { } - protected PropertyDescriptor(string name, System.Attribute[] attrs) : base (default(string)) { } + protected PropertyDescriptor(System.ComponentModel.MemberDescriptor descr, System.Attribute[]? attrs) : base (default(string)) { } + protected PropertyDescriptor(string name, System.Attribute[]? attrs) : base (default(string)) { } public abstract System.Type ComponentType { get; } public virtual System.ComponentModel.TypeConverter Converter { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] get { throw null; } } public virtual bool IsLocalizable { get { throw null; } } @@ -1058,8 +1056,8 @@ namespace System.ComponentModel public virtual bool SupportsChangeEvents { get { throw null; } } public virtual void AddValueChanged(object component, System.EventHandler handler) { } public abstract bool CanResetValue(object component); - protected object CreateInstance([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } - public override bool Equals(object obj) { throw null; } + protected object? CreateInstance([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } protected override void FillAttributes(System.Collections.IList attributeList) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetChildProperties() { throw null; } @@ -1068,69 +1066,69 @@ namespace System.ComponentModel [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetChildProperties(object instance) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public virtual System.ComponentModel.PropertyDescriptorCollection GetChildProperties(object instance, System.Attribute[] filter) { throw null; } + public virtual System.ComponentModel.PropertyDescriptorCollection GetChildProperties(object? instance, System.Attribute[]? filter) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. PropertyDescriptor's PropertyType cannot be statically discovered.")] - public virtual object GetEditor(System.Type editorBaseType) { throw null; } + public virtual object? GetEditor(System.Type editorBaseType) { throw null; } public override int GetHashCode() { throw null; } - protected override object GetInvocationTarget(System.Type type, object instance) { throw null; } + protected override object? GetInvocationTarget(System.Type type, object instance) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Calls ComponentType.Assembly.GetType on the non-fully qualified typeName, which the trimmer cannot recognize.")] [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - protected System.Type GetTypeFromName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { throw null; } - public abstract object GetValue(object component); - protected internal System.EventHandler GetValueChangedHandler(object component) { throw null; } - protected virtual void OnValueChanged(object component, System.EventArgs e) { } + protected System.Type? GetTypeFromName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string? typeName) { throw null; } + public abstract object? GetValue(object? component); + protected internal System.EventHandler? GetValueChangedHandler(object component) { throw null; } + protected virtual void OnValueChanged(object? component, System.EventArgs e) { } public virtual void RemoveValueChanged(object component, System.EventHandler handler) { } public abstract void ResetValue(object component); - public abstract void SetValue(object component, object value); + public abstract void SetValue(object? component, object? value); public abstract bool ShouldSerializeValue(object component); } public partial class PropertyDescriptorCollection : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.Collections.IList { public static readonly System.ComponentModel.PropertyDescriptorCollection Empty; - public PropertyDescriptorCollection(System.ComponentModel.PropertyDescriptor[] properties) { } - public PropertyDescriptorCollection(System.ComponentModel.PropertyDescriptor[] properties, bool readOnly) { } + public PropertyDescriptorCollection(System.ComponentModel.PropertyDescriptor[]? properties) { } + public PropertyDescriptorCollection(System.ComponentModel.PropertyDescriptor[]? properties, bool readOnly) { } public int Count { get { throw null; } } public virtual System.ComponentModel.PropertyDescriptor this[int index] { get { throw null; } } - public virtual System.ComponentModel.PropertyDescriptor this[string name] { get { throw null; } } + public virtual System.ComponentModel.PropertyDescriptor? this[string name] { get { throw null; } } int System.Collections.ICollection.Count { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IDictionary.IsFixedSize { get { throw null; } } bool System.Collections.IDictionary.IsReadOnly { get { throw null; } } - object System.Collections.IDictionary.this[object key] { get { throw null; } set { } } + object? System.Collections.IDictionary.this[object key] { get { throw null; } set { } } System.Collections.ICollection System.Collections.IDictionary.Keys { get { throw null; } } System.Collections.ICollection System.Collections.IDictionary.Values { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } - object System.Collections.IList.this[int index] { get { throw null; } set { } } + object? System.Collections.IList.this[int index] { get { throw null; } set { } } public int Add(System.ComponentModel.PropertyDescriptor value) { throw null; } public void Clear() { } public bool Contains(System.ComponentModel.PropertyDescriptor value) { throw null; } public void CopyTo(System.Array array, int index) { } - public virtual System.ComponentModel.PropertyDescriptor Find(string name, bool ignoreCase) { throw null; } + public virtual System.ComponentModel.PropertyDescriptor? Find(string name, bool ignoreCase) { throw null; } public virtual System.Collections.IEnumerator GetEnumerator() { throw null; } - public int IndexOf(System.ComponentModel.PropertyDescriptor value) { throw null; } + public int IndexOf(System.ComponentModel.PropertyDescriptor? value) { throw null; } public void Insert(int index, System.ComponentModel.PropertyDescriptor value) { } - protected void InternalSort(System.Collections.IComparer sorter) { } - protected void InternalSort(string[] names) { } - public void Remove(System.ComponentModel.PropertyDescriptor value) { } + protected void InternalSort(System.Collections.IComparer? sorter) { } + protected void InternalSort(string[]? names) { } + public void Remove(System.ComponentModel.PropertyDescriptor? value) { } public void RemoveAt(int index) { } public virtual System.ComponentModel.PropertyDescriptorCollection Sort() { throw null; } - public virtual System.ComponentModel.PropertyDescriptorCollection Sort(System.Collections.IComparer comparer) { throw null; } - public virtual System.ComponentModel.PropertyDescriptorCollection Sort(string[] names) { throw null; } - public virtual System.ComponentModel.PropertyDescriptorCollection Sort(string[] names, System.Collections.IComparer comparer) { throw null; } - void System.Collections.IDictionary.Add(object key, object value) { } + public virtual System.ComponentModel.PropertyDescriptorCollection Sort(System.Collections.IComparer? comparer) { throw null; } + public virtual System.ComponentModel.PropertyDescriptorCollection Sort(string[]? names) { throw null; } + public virtual System.ComponentModel.PropertyDescriptorCollection Sort(string[]? names, System.Collections.IComparer? comparer) { throw null; } + void System.Collections.IDictionary.Add(object key, object? value) { } void System.Collections.IDictionary.Clear() { } bool System.Collections.IDictionary.Contains(object key) { throw null; } System.Collections.IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator() { throw null; } void System.Collections.IDictionary.Remove(object key) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - int System.Collections.IList.Add(object value) { throw null; } + int System.Collections.IList.Add(object? value) { throw null; } void System.Collections.IList.Clear() { } - bool System.Collections.IList.Contains(object value) { throw null; } - int System.Collections.IList.IndexOf(object value) { throw null; } - void System.Collections.IList.Insert(int index, object value) { } - void System.Collections.IList.Remove(object value) { } + bool System.Collections.IList.Contains(object? value) { throw null; } + int System.Collections.IList.IndexOf(object? value) { throw null; } + void System.Collections.IList.Insert(int index, object? value) { } + void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] @@ -1142,14 +1140,14 @@ namespace System.ComponentModel public PropertyTabAttribute(System.Type tabClass) { } public PropertyTabAttribute(System.Type tabClass, System.ComponentModel.PropertyTabScope tabScope) { } public System.Type[] TabClasses { get { throw null; } } - protected string[] TabClassNames { get { throw null; } } + protected string[]? TabClassNames { get { throw null; } } public System.ComponentModel.PropertyTabScope[] TabScopes { get { throw null; } } public bool Equals(System.ComponentModel.PropertyTabAttribute other) { throw null; } - public override bool Equals(object other) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? other) { throw null; } public override int GetHashCode() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Types referenced by tabClassNames may be trimmed.")] - protected void InitializeArrays(string[] tabClassNames, System.ComponentModel.PropertyTabScope[] tabScopes) { } - protected void InitializeArrays(System.Type[] tabClasses, System.ComponentModel.PropertyTabScope[] tabScopes) { } + protected void InitializeArrays(string[]? tabClassNames, System.ComponentModel.PropertyTabScope[]? tabScopes) { } + protected void InitializeArrays(System.Type[]? tabClasses, System.ComponentModel.PropertyTabScope[]? tabScopes) { } } public enum PropertyTabScope { @@ -1167,11 +1165,10 @@ namespace System.ComponentModel [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string ReceiverTypeName { get { throw null; } } public override object TypeId { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Property)] - [System.ObsoleteAttribute("Use System.ComponentModel.SettingsBindableAttribute instead to work with the new settings model.")] public partial class RecommendedAsConfigurableAttribute : System.Attribute { public static readonly System.ComponentModel.RecommendedAsConfigurableAttribute Default; @@ -1179,27 +1176,27 @@ namespace System.ComponentModel public static readonly System.ComponentModel.RecommendedAsConfigurableAttribute Yes; public RecommendedAsConfigurableAttribute(bool recommendedAsConfigurable) { } public bool RecommendedAsConfigurable { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } public partial class ReferenceConverter : System.ComponentModel.TypeConverter { public ReferenceConverter(System.Type type) { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } protected virtual bool IsValueAllowed(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } } public partial class RefreshEventArgs : System.EventArgs { - public RefreshEventArgs(object componentChanged) { } - public RefreshEventArgs(System.Type typeChanged) { } - public object ComponentChanged { get { throw null; } } - public System.Type TypeChanged { get { throw null; } } + public RefreshEventArgs(object? componentChanged) { } + public RefreshEventArgs(System.Type? typeChanged) { } + public object? ComponentChanged { get { throw null; } } + public System.Type? TypeChanged { get { throw null; } } } public delegate void RefreshEventHandler(System.ComponentModel.RefreshEventArgs e); [System.AttributeUsageAttribute(System.AttributeTargets.Class)] @@ -1210,7 +1207,7 @@ namespace System.ComponentModel public static readonly System.ComponentModel.RunInstallerAttribute Yes; public RunInstallerAttribute(bool runInstaller) { } public bool RunInstaller { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -1225,7 +1222,7 @@ namespace System.ComponentModel public static readonly System.ComponentModel.SettingsBindableAttribute Yes; public SettingsBindableAttribute(bool bindable) { } public bool Bindable { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } } public partial class SingleConverter : System.ComponentModel.BaseNumberConverter @@ -1235,8 +1232,8 @@ namespace System.ComponentModel public partial class StringConverter : System.ComponentModel.TypeConverter { public StringConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } } public static partial class SyntaxCheck { @@ -1247,10 +1244,10 @@ namespace System.ComponentModel public partial class TimeSpanConverter : System.ComponentModel.TypeConverter { public TimeSpanConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.All)] public partial class ToolboxItemAttribute : System.Attribute @@ -1261,10 +1258,10 @@ namespace System.ComponentModel public ToolboxItemAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string toolboxItemTypeName) { } public ToolboxItemAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type toolboxItemType) { } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - public System.Type ToolboxItemType { get { throw null; } } + public System.Type? ToolboxItemType { get { throw null; } } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public string ToolboxItemTypeName { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -1276,9 +1273,9 @@ namespace System.ComponentModel public string FilterString { get { throw null; } } public System.ComponentModel.ToolboxItemFilterType FilterType { get { throw null; } } public override object TypeId { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } - public override bool Match(object obj) { throw null; } + public override bool Match([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override string ToString() { throw null; } } public enum ToolboxItemFilterType @@ -1291,51 +1288,51 @@ namespace System.ComponentModel public partial class TypeConverter { public TypeConverter() { } - public virtual bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } + public virtual bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } public bool CanConvertFrom(System.Type sourceType) { throw null; } - public virtual bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } + public virtual bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } public bool CanConvertTo(System.Type destinationType) { throw null; } - public virtual object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public object ConvertFrom(object value) { throw null; } - public object ConvertFromInvariantString(System.ComponentModel.ITypeDescriptorContext context, string text) { throw null; } - public object ConvertFromInvariantString(string text) { throw null; } - public object ConvertFromString(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, string text) { throw null; } - public object ConvertFromString(System.ComponentModel.ITypeDescriptorContext context, string text) { throw null; } - public object ConvertFromString(string text) { throw null; } - public virtual object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public object ConvertTo(object value, System.Type destinationType) { throw null; } - public string ConvertToInvariantString(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } - public string ConvertToInvariantString(object value) { throw null; } - public string ConvertToString(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public string ConvertToString(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } - public string ConvertToString(object value) { throw null; } - public object CreateInstance(System.Collections.IDictionary propertyValues) { throw null; } - public virtual object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - protected System.Exception GetConvertFromException(object value) { throw null; } - protected System.Exception GetConvertToException(object value, System.Type destinationType) { throw null; } + public virtual object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public object? ConvertFrom(object value) { throw null; } + public object? ConvertFromInvariantString(System.ComponentModel.ITypeDescriptorContext? context, string text) { throw null; } + public object? ConvertFromInvariantString(string text) { throw null; } + public object? ConvertFromString(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, string text) { throw null; } + public object? ConvertFromString(System.ComponentModel.ITypeDescriptorContext? context, string text) { throw null; } + public object? ConvertFromString(string text) { throw null; } + public virtual object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public object? ConvertTo(object? value, System.Type destinationType) { throw null; } + public string? ConvertToInvariantString(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } + public string? ConvertToInvariantString(object? value) { throw null; } + public string? ConvertToString(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value) { throw null; } + public string? ConvertToString(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } + public string? ConvertToString(object? value) { throw null; } + public object? CreateInstance(System.Collections.IDictionary propertyValues) { throw null; } + public virtual object? CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + protected System.Exception GetConvertFromException(object? value) { throw null; } + protected System.Exception GetConvertToException(object? value, System.Type destinationType) { throw null; } public bool GetCreateInstanceSupported() { throw null; } - public virtual bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public virtual bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] - public System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + public System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } + public virtual System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] - public System.ComponentModel.PropertyDescriptorCollection GetProperties(object value) { throw null; } + public System.ComponentModel.PropertyDescriptorCollection? GetProperties(object value) { throw null; } public bool GetPropertiesSupported() { throw null; } - public virtual bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public System.Collections.ICollection GetStandardValues() { throw null; } - public virtual System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public virtual bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public System.Collections.ICollection? GetStandardValues() { throw null; } + public virtual System.ComponentModel.TypeConverter.StandardValuesCollection? GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public bool GetStandardValuesExclusive() { throw null; } - public virtual bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public virtual bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public bool GetStandardValuesSupported() { throw null; } - public virtual bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public virtual bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + public virtual bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public virtual bool IsValid(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } public bool IsValid(object value) { throw null; } protected System.ComponentModel.PropertyDescriptorCollection SortProperties(System.ComponentModel.PropertyDescriptorCollection props, string[] names) { throw null; } protected abstract partial class SimplePropertyDescriptor : System.ComponentModel.PropertyDescriptor { protected SimplePropertyDescriptor(System.Type componentType, string name, System.Type propertyType) : base (default(string), default(System.Attribute[])) { } - protected SimplePropertyDescriptor(System.Type componentType, string name, System.Type propertyType, System.Attribute[] attributes) : base (default(string), default(System.Attribute[])) { } + protected SimplePropertyDescriptor(System.Type componentType, string name, System.Type propertyType, System.Attribute[]? attributes) : base (default(string), default(System.Attribute[])) { } public override System.Type ComponentType { get { throw null; } } public override bool IsReadOnly { get { throw null; } } public override System.Type PropertyType { get { throw null; } } @@ -1345,9 +1342,9 @@ namespace System.ComponentModel } public partial class StandardValuesCollection : System.Collections.ICollection, System.Collections.IEnumerable { - public StandardValuesCollection(System.Collections.ICollection values) { } + public StandardValuesCollection(System.Collections.ICollection? values) { } public int Count { get { throw null; } } - public object this[int index] { get { throw null; } } + public object? this[int index] { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } public void CopyTo(System.Array array, int index) { } @@ -1358,174 +1355,145 @@ namespace System.ComponentModel { protected TypeDescriptionProvider() { } protected TypeDescriptionProvider(System.ComponentModel.TypeDescriptionProvider parent) { } - public virtual object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } - public virtual System.Collections.IDictionary GetCache(object instance) { throw null; } + public virtual object? CreateInstance(System.IServiceProvider? provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[]? argTypes, object[]? args) { throw null; } + public virtual System.Collections.IDictionary? GetCache(object instance) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public virtual System.ComponentModel.ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) { throw null; } protected internal virtual System.ComponentModel.IExtenderProvider[] GetExtenderProviders(object instance) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public virtual string GetFullComponentName(object component) { throw null; } + public virtual string? GetFullComponentName(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("GetReflectionType is not trim compatible because the Type of object cannot be statically discovered.")] public System.Type GetReflectionType(object instance) { throw null; } [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public System.Type GetReflectionType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type objectType) { throw null; } [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - public virtual System.Type GetReflectionType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type objectType, object instance) { throw null; } + public virtual System.Type GetReflectionType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type objectType, object? instance) { throw null; } public virtual System.Type GetRuntimeType(System.Type reflectionType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] - public System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor(object instance) { throw null; } - public System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType) { throw null; } - public virtual System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object instance) { throw null; } + public System.ComponentModel.ICustomTypeDescriptor? GetTypeDescriptor(object instance) { throw null; } + public System.ComponentModel.ICustomTypeDescriptor? GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType) { throw null; } + public virtual System.ComponentModel.ICustomTypeDescriptor? GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object? instance) { throw null; } public virtual bool IsSupportedType(System.Type type) { throw null; } } public sealed partial class TypeDescriptor { internal TypeDescriptor() { } + [System.Diagnostics.CodeAnalysis.DisallowNullAttribute] [System.ObsoleteAttribute("This property has been deprecated. Use a type description provider to supply type information for COM types instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public static System.ComponentModel.IComNativeDescriptorHandler ComNativeDescriptorHandler { get { throw null; } set { } } + public static System.ComponentModel.IComNativeDescriptorHandler? ComNativeDescriptorHandler { get { throw null; } set { } } public static System.Type ComObjectType { [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] get { throw null; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static System.Type InterfaceType { [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] get { throw null; } } - public static event System.ComponentModel.RefreshEventHandler Refreshed { add { } remove { } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static event System.ComponentModel.RefreshEventHandler? Refreshed { add { } remove { } } public static System.ComponentModel.TypeDescriptionProvider AddAttributes(object instance, params System.Attribute[] attributes) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static System.ComponentModel.TypeDescriptionProvider AddAttributes(System.Type type, params System.Attribute[] attributes) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Types specified in table may be trimmed, or have their static construtors trimmed.")] public static void AddEditorTable(System.Type editorBaseType, System.Collections.Hashtable table) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void AddProvider(System.ComponentModel.TypeDescriptionProvider provider, object instance) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void AddProvider(System.ComponentModel.TypeDescriptionProvider provider, System.Type type) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void AddProviderTransparent(System.ComponentModel.TypeDescriptionProvider provider, object instance) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void AddProviderTransparent(System.ComponentModel.TypeDescriptionProvider provider, System.Type type) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void CreateAssociation(object primary, object secondary) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static System.ComponentModel.Design.IDesigner CreateDesigner(System.ComponentModel.IComponent component, System.Type designerBaseType) { throw null; } + public static System.ComponentModel.Design.IDesigner? CreateDesigner(System.ComponentModel.IComponent component, System.Type designerBaseType) { throw null; } public static System.ComponentModel.EventDescriptor CreateEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.ComponentModel.EventDescriptor oldEventDescriptor, params System.Attribute[] attributes) { throw null; } public static System.ComponentModel.EventDescriptor CreateEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } - public static object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } + public static object? CreateInstance(System.IServiceProvider? provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[]? argTypes, object[]? args) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.ComponentModel.PropertyDescriptor oldPropertyDescriptor, params System.Attribute[] attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static object GetAssociation(System.Type type, object primary) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.AttributeCollection GetAttributes(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.AttributeCollection GetAttributes(object component, bool noCustomTypeDesc) { throw null; } public static System.ComponentModel.AttributeCollection GetAttributes([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static string GetClassName(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static string? GetClassName(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static string GetClassName(object component, bool noCustomTypeDesc) { throw null; } - public static string GetClassName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + public static string? GetClassName(object component, bool noCustomTypeDesc) { throw null; } + public static string? GetClassName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static string GetComponentName(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static string? GetComponentName(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static string GetComponentName(object component, bool noCustomTypeDesc) { throw null; } + public static string? GetComponentName(object component, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. The Type of component cannot be statically discovered.")] public static System.ComponentModel.TypeConverter GetConverter(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. The Type of component cannot be statically discovered.")] public static System.ComponentModel.TypeConverter GetConverter(object component, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] public static System.ComponentModel.TypeConverter GetConverter([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. The Type of component cannot be statically discovered.")] - public static System.ComponentModel.EventDescriptor GetDefaultEvent(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.ComponentModel.EventDescriptor? GetDefaultEvent(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. The Type of component cannot be statically discovered.")] - public static System.ComponentModel.EventDescriptor GetDefaultEvent(object component, bool noCustomTypeDesc) { throw null; } + public static System.ComponentModel.EventDescriptor? GetDefaultEvent(object component, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - public static System.ComponentModel.EventDescriptor GetDefaultEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + public static System.ComponentModel.EventDescriptor? GetDefaultEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] - public static System.ComponentModel.PropertyDescriptor GetDefaultProperty(object component) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.ComponentModel.PropertyDescriptor? GetDefaultProperty(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] - public static System.ComponentModel.PropertyDescriptor GetDefaultProperty(object component, bool noCustomTypeDesc) { throw null; } + public static System.ComponentModel.PropertyDescriptor? GetDefaultProperty(object component, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] - public static System.ComponentModel.PropertyDescriptor GetDefaultProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + public static System.ComponentModel.PropertyDescriptor? GetDefaultProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. The Type of component cannot be statically discovered.")] - public static object GetEditor(object component, System.Type editorBaseType) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static object? GetEditor(object component, System.Type editorBaseType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. The Type of component cannot be statically discovered.")] - public static object GetEditor(object component, System.Type editorBaseType, bool noCustomTypeDesc) { throw null; } + public static object? GetEditor(object component, System.Type editorBaseType, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - public static object GetEditor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, System.Type editorBaseType) { throw null; } + public static object? GetEditor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, System.Type editorBaseType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[] attributes) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[] attributes, bool noCustomTypeDesc) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[]? attributes, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, bool noCustomTypeDesc) { throw null; } public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] - public static string GetFullComponentName(object component) { throw null; } + public static string? GetFullComponentName(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes) { throw null; } + public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes, bool noCustomTypeDesc) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[]? attributes, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, bool noCustomTypeDesc) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[]? attributes) { throw null; } public static System.ComponentModel.TypeDescriptionProvider GetProvider(object instance) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static System.ComponentModel.TypeDescriptionProvider GetProvider(System.Type type) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("GetReflectionType is not trim compatible because the Type of object cannot be statically discovered.")] public static System.Type GetReflectionType(object instance) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public static System.Type GetReflectionType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type type) { throw null; } public static void Refresh(object component) { } public static void Refresh(System.Reflection.Assembly assembly) { } public static void Refresh(System.Reflection.Module module) { } public static void Refresh(System.Type type) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveAssociation(object primary, object secondary) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveAssociations(object primary) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveProvider(System.ComponentModel.TypeDescriptionProvider provider, object instance) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveProvider(System.ComponentModel.TypeDescriptionProvider provider, System.Type type) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveProviderTransparent(System.ComponentModel.TypeDescriptionProvider provider, object instance) { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void RemoveProviderTransparent(System.ComponentModel.TypeDescriptionProvider provider, System.Type type) { } public static void SortDescriptorArray(System.Collections.IList infos) { } } public abstract partial class TypeListConverter : System.ComponentModel.TypeConverter { protected TypeListConverter(System.Type[] types) { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class UInt16Converter : System.ComponentModel.BaseNumberConverter { @@ -1542,22 +1510,22 @@ namespace System.ComponentModel public partial class VersionConverter : System.ComponentModel.TypeConverter { public VersionConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override bool IsValid(System.ComponentModel.ITypeDescriptorContext? context, object? value) { throw null; } } public partial class WarningException : System.SystemException { public WarningException() { } protected WarningException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public WarningException(string message) { } - public WarningException(string message, System.Exception innerException) { } - public WarningException(string message, string helpUrl) { } - public WarningException(string message, string helpUrl, string helpTopic) { } - public string HelpTopic { get { throw null; } } - public string HelpUrl { get { throw null; } } + public WarningException(string? message) { } + public WarningException(string? message, System.Exception? innerException) { } + public WarningException(string? message, string? helpUrl) { } + public WarningException(string? message, string? helpUrl, string? helpTopic) { } + public string? HelpTopic { get { throw null; } } + public string? HelpUrl { get { throw null; } } public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } @@ -1565,65 +1533,65 @@ namespace System.ComponentModel.Design { public partial class ActiveDesignerEventArgs : System.EventArgs { - public ActiveDesignerEventArgs(System.ComponentModel.Design.IDesignerHost oldDesigner, System.ComponentModel.Design.IDesignerHost newDesigner) { } - public System.ComponentModel.Design.IDesignerHost NewDesigner { get { throw null; } } - public System.ComponentModel.Design.IDesignerHost OldDesigner { get { throw null; } } + public ActiveDesignerEventArgs(System.ComponentModel.Design.IDesignerHost? oldDesigner, System.ComponentModel.Design.IDesignerHost? newDesigner) { } + public System.ComponentModel.Design.IDesignerHost? NewDesigner { get { throw null; } } + public System.ComponentModel.Design.IDesignerHost? OldDesigner { get { throw null; } } } - public delegate void ActiveDesignerEventHandler(object sender, System.ComponentModel.Design.ActiveDesignerEventArgs e); + public delegate void ActiveDesignerEventHandler(object? sender, System.ComponentModel.Design.ActiveDesignerEventArgs e); public partial class CheckoutException : System.Runtime.InteropServices.ExternalException { public static readonly System.ComponentModel.Design.CheckoutException Canceled; public CheckoutException() { } protected CheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public CheckoutException(string message) { } - public CheckoutException(string message, System.Exception innerException) { } - public CheckoutException(string message, int errorCode) { } + public CheckoutException(string? message) { } + public CheckoutException(string? message, System.Exception? innerException) { } + public CheckoutException(string? message, int errorCode) { } } public partial class CommandID { public CommandID(System.Guid menuGroup, int commandID) { } public virtual System.Guid Guid { get { throw null; } } public virtual int ID { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override string ToString() { throw null; } } public sealed partial class ComponentChangedEventArgs : System.EventArgs { - public ComponentChangedEventArgs(object component, System.ComponentModel.MemberDescriptor member, object oldValue, object newValue) { } - public object Component { get { throw null; } } - public System.ComponentModel.MemberDescriptor Member { get { throw null; } } - public object NewValue { get { throw null; } } - public object OldValue { get { throw null; } } + public ComponentChangedEventArgs(object? component, System.ComponentModel.MemberDescriptor? member, object? oldValue, object? newValue) { } + public object? Component { get { throw null; } } + public System.ComponentModel.MemberDescriptor? Member { get { throw null; } } + public object? NewValue { get { throw null; } } + public object? OldValue { get { throw null; } } } - public delegate void ComponentChangedEventHandler(object sender, System.ComponentModel.Design.ComponentChangedEventArgs e); + public delegate void ComponentChangedEventHandler(object? sender, System.ComponentModel.Design.ComponentChangedEventArgs e); public sealed partial class ComponentChangingEventArgs : System.EventArgs { - public ComponentChangingEventArgs(object component, System.ComponentModel.MemberDescriptor member) { } - public object Component { get { throw null; } } - public System.ComponentModel.MemberDescriptor Member { get { throw null; } } + public ComponentChangingEventArgs(object? component, System.ComponentModel.MemberDescriptor? member) { } + public object? Component { get { throw null; } } + public System.ComponentModel.MemberDescriptor? Member { get { throw null; } } } - public delegate void ComponentChangingEventHandler(object sender, System.ComponentModel.Design.ComponentChangingEventArgs e); + public delegate void ComponentChangingEventHandler(object? sender, System.ComponentModel.Design.ComponentChangingEventArgs e); public partial class ComponentEventArgs : System.EventArgs { - public ComponentEventArgs(System.ComponentModel.IComponent component) { } - public virtual System.ComponentModel.IComponent Component { get { throw null; } } + public ComponentEventArgs(System.ComponentModel.IComponent? component) { } + public virtual System.ComponentModel.IComponent? Component { get { throw null; } } } - public delegate void ComponentEventHandler(object sender, System.ComponentModel.Design.ComponentEventArgs e); + public delegate void ComponentEventHandler(object? sender, System.ComponentModel.Design.ComponentEventArgs e); public partial class ComponentRenameEventArgs : System.EventArgs { - public ComponentRenameEventArgs(object component, string oldName, string newName) { } - public object Component { get { throw null; } } - public virtual string NewName { get { throw null; } } - public virtual string OldName { get { throw null; } } + public ComponentRenameEventArgs(object? component, string? oldName, string? newName) { } + public object? Component { get { throw null; } } + public virtual string? NewName { get { throw null; } } + public virtual string? OldName { get { throw null; } } } - public delegate void ComponentRenameEventHandler(object sender, System.ComponentModel.Design.ComponentRenameEventArgs e); + public delegate void ComponentRenameEventHandler(object? sender, System.ComponentModel.Design.ComponentRenameEventArgs e); public partial class DesignerCollection : System.Collections.ICollection, System.Collections.IEnumerable { - public DesignerCollection(System.Collections.IList designers) { } - public DesignerCollection(System.ComponentModel.Design.IDesignerHost[] designers) { } + public DesignerCollection(System.Collections.IList? designers) { } + public DesignerCollection(System.ComponentModel.Design.IDesignerHost[]? designers) { } public int Count { get { throw null; } } - public virtual System.ComponentModel.Design.IDesignerHost this[int index] { get { throw null; } } + public virtual System.ComponentModel.Design.IDesignerHost? this[int index] { get { throw null; } } int System.Collections.ICollection.Count { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } @@ -1633,10 +1601,10 @@ namespace System.ComponentModel.Design } public partial class DesignerEventArgs : System.EventArgs { - public DesignerEventArgs(System.ComponentModel.Design.IDesignerHost host) { } - public System.ComponentModel.Design.IDesignerHost Designer { get { throw null; } } + public DesignerEventArgs(System.ComponentModel.Design.IDesignerHost? host) { } + public System.ComponentModel.Design.IDesignerHost? Designer { get { throw null; } } } - public delegate void DesignerEventHandler(object sender, System.ComponentModel.Design.DesignerEventArgs e); + public delegate void DesignerEventHandler(object? sender, System.ComponentModel.Design.DesignerEventArgs e); public abstract partial class DesignerOptionService : System.ComponentModel.Design.IDesignerOptionService { protected DesignerOptionService() { } @@ -1653,26 +1621,26 @@ namespace System.ComponentModel.Design { internal DesignerOptionCollection() { } public int Count { get { throw null; } } - public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection this[int index] { get { throw null; } } - public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection this[string name] { get { throw null; } } + public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection? this[int index] { get { throw null; } } + public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection? this[string name] { get { throw null; } } public string Name { get { throw null; } } - public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection Parent { get { throw null; } } + public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection? Parent { get { throw null; } } public System.ComponentModel.PropertyDescriptorCollection Properties { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of DesignerOptionCollection's value cannot be statically discovered.")] get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } - object System.Collections.IList.this[int index] { get { throw null; } set { } } + object? System.Collections.IList.this[int index] { get { throw null; } set { } } public void CopyTo(System.Array array, int index) { } public System.Collections.IEnumerator GetEnumerator() { throw null; } public int IndexOf(System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection value) { throw null; } public bool ShowDialog() { throw null; } - int System.Collections.IList.Add(object value) { throw null; } + int System.Collections.IList.Add(object? value) { throw null; } void System.Collections.IList.Clear() { } - bool System.Collections.IList.Contains(object value) { throw null; } - int System.Collections.IList.IndexOf(object value) { throw null; } - void System.Collections.IList.Insert(int index, object value) { } - void System.Collections.IList.Remove(object value) { } + bool System.Collections.IList.Contains(object? value) { throw null; } + int System.Collections.IList.IndexOf(object? value) { throw null; } + void System.Collections.IList.Insert(int index, object? value) { } + void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } } } @@ -1693,13 +1661,13 @@ namespace System.ComponentModel.Design } public partial class DesignerTransactionCloseEventArgs : System.EventArgs { - [System.ObsoleteAttribute("This constructor is obsolete. Use DesignerTransactionCloseEventArgs(bool, bool) instead. https://go.microsoft.com/fwlink/?linkid=14202")] + [System.ObsoleteAttribute("This constructor is obsolete. Use DesignerTransactionCloseEventArgs(bool, bool) instead. https://go.microsoft.com/fwlink/?linkid=14202")] public DesignerTransactionCloseEventArgs(bool commit) { } public DesignerTransactionCloseEventArgs(bool commit, bool lastTransaction) { } public bool LastTransaction { get { throw null; } } public bool TransactionCommitted { get { throw null; } } } - public delegate void DesignerTransactionCloseEventHandler(object sender, System.ComponentModel.Design.DesignerTransactionCloseEventArgs e); + public delegate void DesignerTransactionCloseEventHandler(object? sender, System.ComponentModel.Design.DesignerTransactionCloseEventArgs e); public partial class DesignerVerb : System.ComponentModel.Design.MenuCommand { public DesignerVerb(string text, System.EventHandler handler) : base (default(System.EventHandler), default(System.ComponentModel.Design.CommandID)) { } @@ -1712,26 +1680,22 @@ namespace System.ComponentModel.Design { public DesignerVerbCollection() { } public DesignerVerbCollection(System.ComponentModel.Design.DesignerVerb[] value) { } - public System.ComponentModel.Design.DesignerVerb this[int index] { get { throw null; } set { } } - public int Add(System.ComponentModel.Design.DesignerVerb value) { throw null; } + public System.ComponentModel.Design.DesignerVerb? this[int index] { get { throw null; } set { } } + public int Add(System.ComponentModel.Design.DesignerVerb? value) { throw null; } public void AddRange(System.ComponentModel.Design.DesignerVerbCollection value) { } - public void AddRange(System.ComponentModel.Design.DesignerVerb[] value) { } - public bool Contains(System.ComponentModel.Design.DesignerVerb value) { throw null; } - public void CopyTo(System.ComponentModel.Design.DesignerVerb[] array, int index) { } - public int IndexOf(System.ComponentModel.Design.DesignerVerb value) { throw null; } - public void Insert(int index, System.ComponentModel.Design.DesignerVerb value) { } - protected override void OnClear() { } - protected override void OnInsert(int index, object value) { } - protected override void OnRemove(int index, object value) { } - protected override void OnSet(int index, object oldValue, object newValue) { } + public void AddRange(System.ComponentModel.Design.DesignerVerb?[] value) { } + public bool Contains(System.ComponentModel.Design.DesignerVerb? value) { throw null; } + public void CopyTo(System.ComponentModel.Design.DesignerVerb?[] array, int index) { } + public int IndexOf(System.ComponentModel.Design.DesignerVerb? value) { throw null; } + public void Insert(int index, System.ComponentModel.Design.DesignerVerb? value) { } protected override void OnValidate(object value) { } - public void Remove(System.ComponentModel.Design.DesignerVerb value) { } + public void Remove(System.ComponentModel.Design.DesignerVerb? value) { } } public partial class DesigntimeLicenseContext : System.ComponentModel.LicenseContext { public DesigntimeLicenseContext() { } public override System.ComponentModel.LicenseUsageMode UsageMode { get { throw null; } } - public override string GetSavedLicenseKey(System.Type type, System.Reflection.Assembly resourceAssembly) { throw null; } + public override string? GetSavedLicenseKey(System.Type type, System.Reflection.Assembly? resourceAssembly) { throw null; } public override void SetSavedLicenseKey(System.Type type, string key) { } } public partial class DesigntimeLicenseContextSerializer @@ -1753,8 +1717,8 @@ namespace System.ComponentModel.Design public HelpKeywordAttribute() { } public HelpKeywordAttribute(string keyword) { } public HelpKeywordAttribute(System.Type t) { } - public string HelpKeyword { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public string? HelpKeyword { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public override bool IsDefaultAttribute() { throw null; } } @@ -1773,28 +1737,28 @@ namespace System.ComponentModel.Design event System.ComponentModel.Design.ComponentEventHandler ComponentRemoved; event System.ComponentModel.Design.ComponentEventHandler ComponentRemoving; event System.ComponentModel.Design.ComponentRenameEventHandler ComponentRename; - void OnComponentChanged(object component, System.ComponentModel.MemberDescriptor member, object oldValue, object newValue); - void OnComponentChanging(object component, System.ComponentModel.MemberDescriptor member); + void OnComponentChanged(object component, System.ComponentModel.MemberDescriptor? member, object? oldValue, object? newValue); + void OnComponentChanging(object component, System.ComponentModel.MemberDescriptor? member); } public partial interface IComponentDiscoveryService { - System.Collections.ICollection GetComponentTypes(System.ComponentModel.Design.IDesignerHost designerHost, System.Type baseType); + System.Collections.ICollection GetComponentTypes(System.ComponentModel.Design.IDesignerHost? designerHost, System.Type? baseType); } public partial interface IComponentInitializer { - void InitializeExistingComponent(System.Collections.IDictionary defaultValues); - void InitializeNewComponent(System.Collections.IDictionary defaultValues); + void InitializeExistingComponent(System.Collections.IDictionary? defaultValues); + void InitializeNewComponent(System.Collections.IDictionary? defaultValues); } public partial interface IDesigner : System.IDisposable { System.ComponentModel.IComponent Component { get; } - System.ComponentModel.Design.DesignerVerbCollection Verbs { get; } + System.ComponentModel.Design.DesignerVerbCollection? Verbs { get; } void DoDefaultAction(); void Initialize(System.ComponentModel.IComponent component); } public partial interface IDesignerEventService { - System.ComponentModel.Design.IDesignerHost ActiveDesigner { get; } + System.ComponentModel.Design.IDesignerHost? ActiveDesigner { get; } System.ComponentModel.Design.DesignerCollection Designers { get; } event System.ComponentModel.Design.ActiveDesignerEventHandler ActiveDesignerChanged; event System.ComponentModel.Design.DesignerEventHandler DesignerCreated; @@ -1831,8 +1795,8 @@ namespace System.ComponentModel.Design System.ComponentModel.Design.DesignerTransaction CreateTransaction(); System.ComponentModel.Design.DesignerTransaction CreateTransaction(string description); void DestroyComponent(System.ComponentModel.IComponent component); - System.ComponentModel.Design.IDesigner GetDesigner(System.ComponentModel.IComponent component); - System.Type GetType(string typeName); + System.ComponentModel.Design.IDesigner? GetDesigner(System.ComponentModel.IComponent component); + System.Type? GetType(string typeName); } public partial interface IDesignerHostTransactionState { @@ -1841,21 +1805,21 @@ namespace System.ComponentModel.Design public partial interface IDesignerOptionService { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] - object GetOptionValue(string pageName, string valueName); + object? GetOptionValue(string pageName, string valueName); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] void SetOptionValue(string pageName, string valueName, object value); } public partial interface IDictionaryService { - object GetKey(object value); - object GetValue(object key); - void SetValue(object key, object value); + object? GetKey(object? value); + object? GetValue(object key); + void SetValue(object key, object? value); } public partial interface IEventBindingService { string CreateUniqueMethodName(System.ComponentModel.IComponent component, System.ComponentModel.EventDescriptor e); System.Collections.ICollection GetCompatibleMethods(System.ComponentModel.EventDescriptor e); - System.ComponentModel.EventDescriptor GetEvent(System.ComponentModel.PropertyDescriptor property); + System.ComponentModel.EventDescriptor? GetEvent(System.ComponentModel.PropertyDescriptor property); System.ComponentModel.PropertyDescriptorCollection GetEventProperties(System.ComponentModel.EventDescriptorCollection events); System.ComponentModel.PropertyDescriptor GetEventProperty(System.ComponentModel.EventDescriptor e); bool ShowCode(); @@ -1891,7 +1855,7 @@ namespace System.ComponentModel.Design System.ComponentModel.Design.DesignerVerbCollection Verbs { get; } void AddCommand(System.ComponentModel.Design.MenuCommand command); void AddVerb(System.ComponentModel.Design.DesignerVerb verb); - System.ComponentModel.Design.MenuCommand FindCommand(System.ComponentModel.Design.CommandID commandID); + System.ComponentModel.Design.MenuCommand? FindCommand(System.ComponentModel.Design.CommandID commandID); bool GlobalInvoke(System.ComponentModel.Design.CommandID commandID); void RemoveCommand(System.ComponentModel.Design.MenuCommand command); void RemoveVerb(System.ComponentModel.Design.DesignerVerb verb); @@ -1899,15 +1863,15 @@ namespace System.ComponentModel.Design } public partial interface IReferenceService { - System.ComponentModel.IComponent GetComponent(object reference); - string GetName(object reference); - object GetReference(string name); + System.ComponentModel.IComponent? GetComponent(object reference); + string? GetName(object reference); + object? GetReference(string name); object[] GetReferences(); object[] GetReferences(System.Type baseType); } public partial interface IResourceService { - System.Resources.IResourceReader GetResourceReader(System.Globalization.CultureInfo info); + System.Resources.IResourceReader? GetResourceReader(System.Globalization.CultureInfo info); System.Resources.IResourceWriter GetResourceWriter(System.Globalization.CultureInfo info); } public partial interface IRootDesigner : System.ComponentModel.Design.IDesigner, System.IDisposable @@ -1923,8 +1887,8 @@ namespace System.ComponentModel.Design event System.EventHandler SelectionChanging; bool GetComponentSelected(object component); System.Collections.ICollection GetSelectedComponents(); - void SetSelectedComponents(System.Collections.ICollection components); - void SetSelectedComponents(System.Collections.ICollection components, System.ComponentModel.Design.SelectionTypes selectionType); + void SetSelectedComponents(System.Collections.ICollection? components); + void SetSelectedComponents(System.Collections.ICollection? components, System.ComponentModel.Design.SelectionTypes selectionType); } public partial interface IServiceContainer : System.IServiceProvider { @@ -1938,7 +1902,7 @@ namespace System.ComponentModel.Design public partial interface ITreeDesigner : System.ComponentModel.Design.IDesigner, System.IDisposable { System.Collections.ICollection Children { get; } - System.ComponentModel.Design.IDesigner Parent { get; } + System.ComponentModel.Design.IDesigner? Parent { get; } } public partial interface ITypeDescriptorFilterService { @@ -1948,32 +1912,32 @@ namespace System.ComponentModel.Design } public partial interface ITypeDiscoveryService { - System.Collections.ICollection GetTypes(System.Type baseType, bool excludeGlobalTypes); + System.Collections.ICollection GetTypes(System.Type? baseType, bool excludeGlobalTypes); } public partial interface ITypeResolutionService { - System.Reflection.Assembly GetAssembly(System.Reflection.AssemblyName name); - System.Reflection.Assembly GetAssembly(System.Reflection.AssemblyName name, bool throwOnError); - string GetPathOfAssembly(System.Reflection.AssemblyName name); + System.Reflection.Assembly? GetAssembly(System.Reflection.AssemblyName name); + System.Reflection.Assembly? GetAssembly(System.Reflection.AssemblyName name, bool throwOnError); + string? GetPathOfAssembly(System.Reflection.AssemblyName name); [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); + System.Type? GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); + System.Type? GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); + System.Type? GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); void ReferenceAssembly(System.Reflection.AssemblyName name); } public partial class MenuCommand { - public MenuCommand(System.EventHandler handler, System.ComponentModel.Design.CommandID command) { } + public MenuCommand(System.EventHandler? handler, System.ComponentModel.Design.CommandID? command) { } public virtual bool Checked { get { throw null; } set { } } - public virtual System.ComponentModel.Design.CommandID CommandID { get { throw null; } } + public virtual System.ComponentModel.Design.CommandID? CommandID { get { throw null; } } public virtual bool Enabled { get { throw null; } set { } } public virtual int OleStatus { get { throw null; } } public virtual System.Collections.IDictionary Properties { get { throw null; } } public virtual bool Supported { get { throw null; } set { } } public virtual bool Visible { get { throw null; } set { } } - public event System.EventHandler CommandChanged { add { } remove { } } + public event System.EventHandler? CommandChanged { add { } remove { } } public virtual void Invoke() { } public virtual void Invoke(object arg) { } protected virtual void OnCommandChanged(System.EventArgs e) { } @@ -2002,7 +1966,7 @@ namespace System.ComponentModel.Design public partial class ServiceContainer : System.ComponentModel.Design.IServiceContainer, System.IDisposable, System.IServiceProvider { public ServiceContainer() { } - public ServiceContainer(System.IServiceProvider parentProvider) { } + public ServiceContainer(System.IServiceProvider? parentProvider) { } protected virtual System.Type[] DefaultServices { get { throw null; } } public void AddService(System.Type serviceType, System.ComponentModel.Design.ServiceCreatorCallback callback) { } public virtual void AddService(System.Type serviceType, System.ComponentModel.Design.ServiceCreatorCallback callback, bool promote) { } @@ -2010,11 +1974,11 @@ namespace System.ComponentModel.Design public virtual void AddService(System.Type serviceType, object serviceInstance, bool promote) { } public void Dispose() { } protected virtual void Dispose(bool disposing) { } - public virtual object GetService(System.Type serviceType) { throw null; } + public virtual object? GetService(System.Type serviceType) { throw null; } public void RemoveService(System.Type serviceType) { } public virtual void RemoveService(System.Type serviceType, bool promote) { } } - public delegate object ServiceCreatorCallback(System.ComponentModel.Design.IServiceContainer container, System.Type serviceType); + public delegate object? ServiceCreatorCallback(System.ComponentModel.Design.IServiceContainer container, System.Type serviceType); public partial class StandardCommands { public static readonly System.ComponentModel.Design.CommandID AlignBottom; @@ -2121,11 +2085,11 @@ namespace System.ComponentModel.Design.Serialization public sealed partial class ContextStack { public ContextStack() { } - public object Current { get { throw null; } } - public object this[int level] { get { throw null; } } - public object this[System.Type type] { get { throw null; } } + public object? Current { get { throw null; } } + public object? this[int level] { get { throw null; } } + public object? this[System.Type type] { get { throw null; } } public void Append(object context) { } - public object Pop() { throw null; } + public object? Pop() { throw null; } public void Push(object context) { } } [System.AttributeUsageAttribute(System.AttributeTargets.Class, Inherited=false)] @@ -2145,7 +2109,7 @@ namespace System.ComponentModel.Design.Serialization } public partial interface IDesignerLoaderHost : System.ComponentModel.Design.IDesignerHost, System.ComponentModel.Design.IServiceContainer, System.IServiceProvider { - void EndLoad(string baseClassName, bool successful, System.Collections.ICollection errorCollection); + void EndLoad(string baseClassName, bool successful, System.Collections.ICollection? errorCollection); void Reload(); } public partial interface IDesignerLoaderHost2 : System.ComponentModel.Design.IDesignerHost, System.ComponentModel.Design.IServiceContainer, System.ComponentModel.Design.Serialization.IDesignerLoaderHost, System.IServiceProvider @@ -2156,7 +2120,7 @@ namespace System.ComponentModel.Design.Serialization public partial interface IDesignerLoaderService { void AddLoadDependency(); - void DependentLoadComplete(bool successful, System.Collections.ICollection errorCollection); + void DependentLoadComplete(bool successful, System.Collections.ICollection? errorCollection); bool Reload(); } public partial interface IDesignerSerializationManager : System.IServiceProvider @@ -2177,7 +2141,7 @@ namespace System.ComponentModel.Design.Serialization } public partial interface IDesignerSerializationProvider { - object GetSerializer(System.ComponentModel.Design.Serialization.IDesignerSerializationManager manager, object currentSerializer, System.Type objectType, System.Type serializerType); + object? GetSerializer(System.ComponentModel.Design.Serialization.IDesignerSerializationManager manager, object currentSerializer, System.Type objectType, System.Type serializerType); } public partial interface IDesignerSerializationService { @@ -2186,18 +2150,18 @@ namespace System.ComponentModel.Design.Serialization } public partial interface INameCreationService { - string CreateName(System.ComponentModel.IContainer container, System.Type dataType); + string CreateName(System.ComponentModel.IContainer? container, System.Type dataType); bool IsValidName(string name); void ValidateName(string name); } public sealed partial class InstanceDescriptor { - public InstanceDescriptor(System.Reflection.MemberInfo member, System.Collections.ICollection arguments) { } - public InstanceDescriptor(System.Reflection.MemberInfo member, System.Collections.ICollection arguments, bool isComplete) { } + public InstanceDescriptor(System.Reflection.MemberInfo? member, System.Collections.ICollection? arguments) { } + public InstanceDescriptor(System.Reflection.MemberInfo? member, System.Collections.ICollection? arguments, bool isComplete) { } public System.Collections.ICollection Arguments { get { throw null; } } public bool IsComplete { get { throw null; } } - public System.Reflection.MemberInfo MemberInfo { get { throw null; } } - public object Invoke() { throw null; } + public System.Reflection.MemberInfo? MemberInfo { get { throw null; } } + public object? Invoke() { throw null; } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct MemberRelationship @@ -2208,8 +2172,8 @@ namespace System.ComponentModel.Design.Serialization public MemberRelationship(object owner, System.ComponentModel.MemberDescriptor member) { throw null; } public bool IsEmpty { get { throw null; } } public System.ComponentModel.MemberDescriptor Member { get { throw null; } } - public object Owner { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public object? Owner { get { throw null; } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } public static bool operator ==(System.ComponentModel.Design.Serialization.MemberRelationship left, System.ComponentModel.Design.Serialization.MemberRelationship right) { throw null; } public static bool operator !=(System.ComponentModel.Design.Serialization.MemberRelationship left, System.ComponentModel.Design.Serialization.MemberRelationship right) { throw null; } @@ -2225,21 +2189,21 @@ namespace System.ComponentModel.Design.Serialization } public partial class ResolveNameEventArgs : System.EventArgs { - public ResolveNameEventArgs(string name) { } - public string Name { get { throw null; } } - public object Value { get { throw null; } set { } } + public ResolveNameEventArgs(string? name) { } + public string? Name { get { throw null; } } + public object? Value { get { throw null; } set { } } } - public delegate void ResolveNameEventHandler(object sender, System.ComponentModel.Design.Serialization.ResolveNameEventArgs e); + public delegate void ResolveNameEventHandler(object? sender, System.ComponentModel.Design.Serialization.ResolveNameEventArgs e); [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple=true, Inherited=true)] [System.ObsoleteAttribute("This attribute has been deprecated. Use DesignerSerializerAttribute instead. For example, to specify a root designer for CodeDom, use DesignerSerializerAttribute(...,typeof(TypeCodeDomSerializer)). https://go.microsoft.com/fwlink/?linkid=14202")] public sealed partial class RootDesignerSerializerAttribute : System.Attribute { - public RootDesignerSerializerAttribute(string serializerTypeName, string baseSerializerTypeName, bool reloadable) { } + public RootDesignerSerializerAttribute(string? serializerTypeName, string? baseSerializerTypeName, bool reloadable) { } public RootDesignerSerializerAttribute(string serializerTypeName, System.Type baseSerializerType, bool reloadable) { } public RootDesignerSerializerAttribute(System.Type serializerType, System.Type baseSerializerType, bool reloadable) { } public bool Reloadable { get { throw null; } } - public string SerializerBaseTypeName { get { throw null; } } - public string SerializerTypeName { get { throw null; } } + public string? SerializerBaseTypeName { get { throw null; } } + public string? SerializerTypeName { get { throw null; } } public override object TypeId { get { throw null; } } } public abstract partial class SerializationStore : System.IDisposable @@ -2257,64 +2221,64 @@ namespace System.Drawing public partial class ColorConverter : System.ComponentModel.TypeConverter { public ColorConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class PointConverter : System.ComponentModel.TypeConverter { public PointConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class RectangleConverter : System.ComponentModel.TypeConverter { public RectangleConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class SizeConverter : System.ComponentModel.TypeConverter { public SizeConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } public partial class SizeFConverter : System.ComponentModel.TypeConverter { public SizeFConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } - public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { throw null; } - public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } } namespace System.Security.Authentication.ExtendedProtection @@ -2322,9 +2286,9 @@ namespace System.Security.Authentication.ExtendedProtection public partial class ExtendedProtectionPolicyTypeConverter : System.ComponentModel.TypeConverter { public ExtendedProtectionPolicyTypeConverter() { } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } } namespace System.Timers @@ -2334,7 +2298,7 @@ namespace System.Timers internal ElapsedEventArgs() { } public System.DateTime SignalTime { get { throw null; } } } - public delegate void ElapsedEventHandler(object sender, System.Timers.ElapsedEventArgs e); + public delegate void ElapsedEventHandler(object? sender, System.Timers.ElapsedEventArgs e); [System.ComponentModel.DefaultEventAttribute("Elapsed")] [System.ComponentModel.DefaultPropertyAttribute("Interval")] public partial class Timer : System.ComponentModel.Component, System.ComponentModel.ISupportInitialize @@ -2347,9 +2311,9 @@ namespace System.Timers public bool Enabled { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(100d)] public double Interval { get { throw null; } set { } } - public override System.ComponentModel.ISite Site { get { throw null; } set { } } + public override System.ComponentModel.ISite? Site { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(null)] - public System.ComponentModel.ISynchronizeInvoke SynchronizingObject { get { throw null; } set { } } + public System.ComponentModel.ISynchronizeInvoke? SynchronizingObject { get { throw null; } set { } } public event System.Timers.ElapsedEventHandler Elapsed { add { } remove { } } public void BeginInit() { } public void Close() { } diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.csproj index 5aec7aa537e..10a6d2f9570 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent) + enable diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs b/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs index 67df1e5bbac..102ca666d2e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs @@ -17,7 +17,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel { } - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) + public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance) { return new XTypeDescriptor(base.GetTypeDescriptor(type, instance)); } @@ -25,7 +25,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XTypeDescriptor : CustomTypeDescriptor { - public XTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) + public XTypeDescriptor(ICustomTypeDescriptor? parent) : base(parent) { } @@ -36,7 +36,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel } [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(Attribute[]? attributes) { PropertyDescriptorCollection properties = new PropertyDescriptorCollection(null); if (attributes == null) @@ -95,7 +95,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel base.AddValueChanged(component, handler); if (hasValueChangedHandler) return; - T c = component as T; + T? c = component as T; if (c != null && GetValueChangedHandler(component) != null) { c.Changing += new EventHandler(OnChanging); @@ -111,7 +111,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel public override void RemoveValueChanged(object component, EventHandler handler) { base.RemoveValueChanged(component, handler); - T c = component as T; + T? c = component as T; if (c != null && GetValueChangedHandler(component) == null) { c.Changing -= new EventHandler(OnChanging); @@ -123,7 +123,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel { } - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { } @@ -132,37 +132,37 @@ namespace MS.Internal.Xml.Linq.ComponentModel return false; } - protected virtual void OnChanged(object sender, XObjectChangeEventArgs args) + protected virtual void OnChanged(object? sender, XObjectChangeEventArgs args) { } - protected virtual void OnChanging(object sender, XObjectChangeEventArgs args) + protected virtual void OnChanging(object? sender, XObjectChangeEventArgs args) { } } internal sealed class XElementAttributePropertyDescriptor : XPropertyDescriptor { - private XDeferredSingleton _value; - private XAttribute _changeState; + private XDeferredSingleton? _value; + private XAttribute? _changeState; public XElementAttributePropertyDescriptor() : base("Attribute") { } - public override object GetValue(object component) + public override object GetValue(object? component) { - return _value = new XDeferredSingleton((e, n) => e.Attribute(n), component as XElement, null); + return _value = new XDeferredSingleton((e, n) => e.Attribute(n), (component as XElement)!, null); } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Add: - XAttribute a = sender as XAttribute; + XAttribute? a = sender as XAttribute; if (a != null && _value.element == a.Parent && _value.name == a.Name) { OnValueChanged(_value.element, EventArgs.Empty); @@ -179,14 +179,14 @@ namespace MS.Internal.Xml.Linq.ComponentModel } } - protected override void OnChanging(object sender, XObjectChangeEventArgs args) + protected override void OnChanging(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Remove: - XAttribute a = sender as XAttribute; + XAttribute? a = sender as XAttribute; _changeState = a != null && _value.element == a.Parent && _value.name == a.Name ? a : null; break; } @@ -195,19 +195,19 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XElementDescendantsPropertyDescriptor : XPropertyDescriptor> { - private XDeferredAxis _value; - private XName _changeState; + private XDeferredAxis? _value; + private XName? _changeState; public XElementDescendantsPropertyDescriptor() : base("Descendants") { } - public override object GetValue(object component) + public override object GetValue(object? component) { - return _value = new XDeferredAxis((e, n) => n != null ? e.Descendants(n) : e.Descendants(), component as XElement, null); + return _value = new XDeferredAxis((e, n) => n != null ? e.Descendants(n) : e.Descendants(), (component as XElement)!, null); } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; @@ -215,7 +215,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel { case XObjectChange.Add: case XObjectChange.Remove: - XElement e = sender as XElement; + XElement? e = sender as XElement; if (e != null && (_value.name == e.Name || _value.name == null)) { OnValueChanged(_value.element, EventArgs.Empty); @@ -232,14 +232,14 @@ namespace MS.Internal.Xml.Linq.ComponentModel } } - protected override void OnChanging(object sender, XObjectChangeEventArgs args) + protected override void OnChanging(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Name: - XElement e = sender as XElement; + XElement? e = sender as XElement; _changeState = e != null ? e.Name : null; break; } @@ -248,26 +248,26 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XElementElementPropertyDescriptor : XPropertyDescriptor { - private XDeferredSingleton _value; - private XElement _changeState; + private XDeferredSingleton? _value; + private XElement? _changeState; public XElementElementPropertyDescriptor() : base("Element") { } - public override object GetValue(object component) + public override object GetValue(object? component) { - return _value = new XDeferredSingleton((e, n) => e.Element(n), component as XElement, null); + return _value = new XDeferredSingleton((e, n) => e.Element(n), (component as XElement)!, null); } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Add: - XElement e = sender as XElement; + XElement? e = sender as XElement; if (e != null && _value.element == e.Parent && _value.name == e.Name && _value.element.Element(_value.name) == e) { OnValueChanged(_value.element, EventArgs.Empty); @@ -299,7 +299,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel } } - protected override void OnChanging(object sender, XObjectChangeEventArgs args) + protected override void OnChanging(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; @@ -307,7 +307,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel { case XObjectChange.Remove: case XObjectChange.Name: - XElement e = sender as XElement; + XElement? e = sender as XElement; _changeState = e != null && _value.element == e.Parent && _value.name == e.Name && _value.element.Element(_value.name) == e ? e : null; break; } @@ -316,26 +316,26 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XElementElementsPropertyDescriptor : XPropertyDescriptor> { - private XDeferredAxis _value; - private object _changeState; + private XDeferredAxis? _value; + private object? _changeState; public XElementElementsPropertyDescriptor() : base("Elements") { } - public override object GetValue(object component) + public override object GetValue(object? component) { - return _value = new XDeferredAxis((e, n) => n != null ? e.Elements(n) : e.Elements(), component as XElement, null); + return _value = new XDeferredAxis((e, n) => n != null ? e.Elements(n) : e.Elements(), (component as XElement)!, null); } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Add: - XElement e = sender as XElement; + XElement? e = sender as XElement; if (e != null && _value.element == e.Parent && (_value.name == e.Name || _value.name == null)) { OnValueChanged(_value.element, EventArgs.Empty); @@ -360,14 +360,14 @@ namespace MS.Internal.Xml.Linq.ComponentModel } } - protected override void OnChanging(object sender, XObjectChangeEventArgs args) + protected override void OnChanging(object? sender, XObjectChangeEventArgs args) { if (_value == null) return; switch (args.ObjectChange) { case XObjectChange.Remove: - XElement e = sender as XElement; + XElement? e = sender as XElement; _changeState = e != null ? e.Parent : null; break; case XObjectChange.Name: @@ -380,7 +380,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XElementValuePropertyDescriptor : XPropertyDescriptor { - private XElement _element; + private XElement? _element; public XElementValuePropertyDescriptor() : base("Value") { @@ -391,7 +391,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel get { return false; } } - public override object GetValue(object component) + public override object GetValue(object? component) { _element = component as XElement; if (_element == null) @@ -399,15 +399,15 @@ namespace MS.Internal.Xml.Linq.ComponentModel return _element.Value; } - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { _element = component as XElement; if (_element == null) return; - _element.Value = value as string; + _element.Value = (value as string)!; } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_element == null) return; @@ -432,13 +432,13 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XElementXmlPropertyDescriptor : XPropertyDescriptor { - private XElement _element; + private XElement? _element; public XElementXmlPropertyDescriptor() : base("Xml") { } - public override object GetValue(object component) + public override object GetValue(object? component) { _element = component as XElement; if (_element == null) @@ -446,7 +446,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel return _element.ToString(SaveOptions.DisableFormatting); } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_element == null) return; @@ -456,7 +456,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XAttributeValuePropertyDescriptor : XPropertyDescriptor { - private XAttribute _attribute; + private XAttribute? _attribute; public XAttributeValuePropertyDescriptor() : base("Value") { @@ -467,7 +467,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel get { return false; } } - public override object GetValue(object component) + public override object GetValue(object? component) { _attribute = component as XAttribute; if (_attribute == null) @@ -475,15 +475,15 @@ namespace MS.Internal.Xml.Linq.ComponentModel return _attribute.Value; } - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { _attribute = component as XAttribute; if (_attribute == null) return; - _attribute.Value = value as string; + _attribute.Value = (value as string)!; } - protected override void OnChanged(object sender, XObjectChangeEventArgs args) + protected override void OnChanged(object? sender, XObjectChangeEventArgs args) { if (_attribute == null) return; @@ -496,11 +496,11 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XDeferredAxis : IEnumerable, IEnumerable where T : XObject { - private readonly Func> _func; + private readonly Func> _func; internal XElement element; - internal XName name; + internal XName? name; - public XDeferredAxis(Func> func, XElement element, XName name) + public XDeferredAxis(Func> func, XElement element, XName? name) { if (func == null) throw new ArgumentNullException(nameof(func)); @@ -542,11 +542,11 @@ namespace MS.Internal.Xml.Linq.ComponentModel internal sealed class XDeferredSingleton where T : XObject { - private readonly Func _func; + private readonly Func _func; internal XElement element; - internal XName name; + internal XName? name; - public XDeferredSingleton(Func func, XElement element, XName name) + public XDeferredSingleton(Func func, XElement element, XName? name) { if (func == null) throw new ArgumentNullException(nameof(func)); @@ -557,7 +557,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel this.name = name; } - public T this[string expandedName] + public T? this[string expandedName] { get { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index 5312108604e..db7c6d6c275 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -3,6 +3,7 @@ true true $(NetCoreAppCurrent) + enable diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventArgs.cs index 8efc8ebb089..3ee3f6a3e49 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventArgs.cs @@ -23,7 +23,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class, /// with the specified object defined as the default new object. /// - public AddingNewEventArgs(object newObject) + public AddingNewEventArgs(object? newObject) { NewObject = newObject; } @@ -31,6 +31,6 @@ namespace System.ComponentModel /// /// Gets or sets the new object that will be added to the list. /// - public object NewObject { get; set; } + public object? NewObject { get; set; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventHandler.cs index f2eb69c8a96..f7c6c412a75 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AddingNewEventHandler.cs @@ -7,5 +7,5 @@ namespace System.ComponentModel /// Represents the method that will handle the AddingNew event on a list, /// and provide the new object to be added to the list. /// - public delegate void AddingNewEventHandler(object sender, AddingNewEventArgs e); + public delegate void AddingNewEventHandler(object? sender, AddingNewEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs index 3a04957915f..79b4d31603b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs @@ -106,7 +106,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class using a . /// - public AmbientValueAttribute(string value) + public AmbientValueAttribute(string? value) { Value = value; } @@ -115,7 +115,7 @@ namespace System.ComponentModel /// Initializes a new instance of the /// class. /// - public AmbientValueAttribute(object value) + public AmbientValueAttribute(object? value) { Value = value; } @@ -123,9 +123,9 @@ namespace System.ComponentModel /// /// Gets the ambient value of the property this attribute is bound to. /// - public object Value { get; } + public object? Value { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs index b9ae3f3b94e..f71ed58a86c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs @@ -15,7 +15,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is Array) { @@ -29,7 +29,8 @@ namespace System.ComponentModel /// Gets a collection of properties for the type of array specified by the value parameter. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + [return: NotNullIfNotNull("value")] + public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object? value, Attribute[]? attributes) { if (value == null) { @@ -44,7 +45,7 @@ namespace System.ComponentModel PropertyDescriptor[] props = new PropertyDescriptor[length]; Type arrayType = value.GetType(); - Type elementType = arrayType.GetElementType(); + Type elementType = arrayType.GetElementType()!; for (int i = 0; i < length; i++) { @@ -57,7 +58,7 @@ namespace System.ComponentModel /// /// Gets a value indicating whether this object supports properties. /// - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; private sealed class ArrayPropertyDescriptor : SimplePropertyDescriptor { @@ -69,7 +70,7 @@ namespace System.ComponentModel _index = index; } - public override object GetValue(object instance) + public override object? GetValue(object? instance) { if (instance is Array array && array.GetLength(0) > _index) { @@ -79,7 +80,7 @@ namespace System.ComponentModel return null; } - public override void SetValue(object instance, object value) + public override void SetValue(object? instance, object? value) { if (instance is Array array) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeCollection.cs index 31fac2df248..93447545609 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeCollection.cs @@ -19,7 +19,7 @@ namespace System.ComponentModel /// public static readonly AttributeCollection Empty = new AttributeCollection(null); - private static Hashtable s_defaultAttributes; + private static Hashtable? s_defaultAttributes; private readonly Attribute[] _attributes; @@ -33,14 +33,14 @@ namespace System.ComponentModel private const int FoundTypesLimit = 5; - private AttributeEntry[] _foundAttributeTypes; + private AttributeEntry[]? _foundAttributeTypes; private int _index; /// /// Creates a new AttributeCollection. /// - public AttributeCollection(params Attribute[] attributes) + public AttributeCollection(params Attribute[]? attributes) { _attributes = attributes ?? Array.Empty(); @@ -60,7 +60,7 @@ namespace System.ComponentModel /// /// Creates a new AttributeCollection from an existing AttributeCollection /// - public static AttributeCollection FromExisting(AttributeCollection existing, params Attribute[] newAttributes) + public static AttributeCollection FromExisting(AttributeCollection existing, params Attribute[]? newAttributes) { if (existing == null) { @@ -135,7 +135,7 @@ namespace System.ComponentModel /// /// Gets the attribute with the specified type. /// - public virtual Attribute this[[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] Type attributeType] + public virtual Attribute? this[[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] Type attributeType] { get { @@ -219,14 +219,14 @@ namespace System.ComponentModel /// Determines if this collection of attributes has the specified attribute. /// [RequiresUnreferencedCode(FilterRequiresUnreferencedCodeMessage)] - public bool Contains(Attribute attribute) + public bool Contains(Attribute? attribute) { if (attribute == null) { return false; } - Attribute attr = this[attribute.GetType()]; + Attribute? attr = this[attribute.GetType()]; return attr != null && attr.Equals(attribute); } @@ -235,7 +235,7 @@ namespace System.ComponentModel /// the specified attributes in the attribute array. /// [RequiresUnreferencedCode(FilterRequiresUnreferencedCodeMessage)] - public bool Contains(Attribute[] attributes) + public bool Contains(Attribute[]? attributes) { if (attributes == null) { @@ -257,7 +257,7 @@ namespace System.ComponentModel /// Returns the default value for an attribute. This uses the following heuristic: /// 1. It looks for a public static field named "Default". /// - protected Attribute GetDefaultAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] Type attributeType) + protected Attribute? GetDefaultAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] Type attributeType) { if (attributeType == null) { @@ -274,22 +274,22 @@ namespace System.ComponentModel // If we have already encountered this, use what's in the table. if (s_defaultAttributes.ContainsKey(attributeType)) { - return (Attribute)s_defaultAttributes[attributeType]; + return (Attribute?)s_defaultAttributes[attributeType]; } - Attribute attr = null; + Attribute? attr = null; // Not in the table, so do the legwork to discover the default value. Type reflect = TypeDescriptor.GetReflectionType(attributeType); - FieldInfo field = reflect.GetField("Default", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField); + FieldInfo? field = reflect.GetField("Default", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField); if (field != null && field.IsStatic) { - attr = (Attribute)field.GetValue(null); + attr = (Attribute?)field.GetValue(null); } else { - ConstructorInfo ci = reflect.UnderlyingSystemType.GetConstructor(Type.EmptyTypes); + ConstructorInfo? ci = reflect.UnderlyingSystemType.GetConstructor(Type.EmptyTypes); if (ci != null) { attr = (Attribute)ci.Invoke(Array.Empty()); @@ -317,7 +317,7 @@ namespace System.ComponentModel /// Determines if a specified attribute is the same as an attribute /// in the collection. /// - public bool Matches(Attribute attribute) + public bool Matches(Attribute? attribute) { for (int i = 0; i < Attributes.Length; i++) { @@ -333,7 +333,7 @@ namespace System.ComponentModel /// Determines if the attributes in the specified array are /// the same as the attributes in the collection. /// - public bool Matches(Attribute[] attributes) + public bool Matches(Attribute[]? attributes) { if (attributes == null) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs index 973771580be..69e13363d62 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs @@ -45,11 +45,11 @@ namespace System.ComponentModel /// passed into the constructor. /// [DynamicallyAccessedMembers(RequiredMemberTypes)] - public string TypeName { get; } + public string? TypeName { get; } /// /// The TypeName property returns the property name that will be used to query attributes from. /// - public string PropertyName { get; } + public string? PropertyName { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseComponentEditor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseComponentEditor.cs index 0caf9273cea..766504fc860 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseComponentEditor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseComponentEditor.cs @@ -16,6 +16,6 @@ namespace System.ComponentModel /// /// Gets a value indicating whether the component was modified. /// - public abstract bool EditComponent(ITypeDescriptorContext context, object component); + public abstract bool EditComponent(ITypeDescriptorContext? context, object component); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs index 1133a73dc49..24158ec5b2c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs @@ -30,18 +30,18 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal abstract object FromString(string value, NumberFormatInfo formatInfo); + internal abstract object FromString(string value, NumberFormatInfo? formatInfo); /// /// Convert the given value from a string using the given formatInfo /// - internal abstract string ToString(object value, NumberFormatInfo formatInfo); + internal abstract string ToString(object value, NumberFormatInfo? formatInfo); /// /// Gets a value indicating whether this converter can convert an object in the /// given source type to the TargetType object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -49,7 +49,7 @@ namespace System.ComponentModel /// /// Converts the given value object to an object of Type TargetType. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -73,7 +73,7 @@ namespace System.ComponentModel culture = CultureInfo.CurrentCulture; } - NumberFormatInfo formatInfo = (NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo)); + NumberFormatInfo? formatInfo = (NumberFormatInfo?)culture.GetFormat(typeof(NumberFormatInfo)); return FromString(text, formatInfo); } } @@ -89,7 +89,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -103,7 +103,7 @@ namespace System.ComponentModel culture = CultureInfo.CurrentCulture; } - NumberFormatInfo formatInfo = (NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo)); + NumberFormatInfo? formatInfo = (NumberFormatInfo?)culture.GetFormat(typeof(NumberFormatInfo)); return ToString(value, formatInfo); } @@ -115,7 +115,7 @@ namespace System.ComponentModel return base.ConvertTo(context, culture, value, destinationType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (destinationType != null && destinationType.IsPrimitive) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindableAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindableAttribute.cs index ab823f26e1e..da3c0dfe507 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindableAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindableAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -73,7 +75,7 @@ namespace System.ComponentModel /// public BindingDirection Direction { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs index 74c42161519..9e5faa75ec3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs @@ -20,16 +20,16 @@ namespace System.ComponentModel private bool raiseItemChangedEvents; // Do not rename (binary serialization) [NonSerialized] - private PropertyDescriptorCollection _itemTypeProperties; + private PropertyDescriptorCollection? _itemTypeProperties; [NonSerialized] - private PropertyChangedEventHandler _propertyChangedEventHandler; + private PropertyChangedEventHandler? _propertyChangedEventHandler; [NonSerialized] - private AddingNewEventHandler _onAddingNew; + private AddingNewEventHandler? _onAddingNew; [NonSerialized] - private ListChangedEventHandler _onListChanged; + private ListChangedEventHandler? _onListChanged; [NonSerialized] private int _lastChangeIndex = -1; @@ -124,7 +124,7 @@ namespace System.ComponentModel protected virtual void OnAddingNew(AddingNewEventArgs e) => _onAddingNew?.Invoke(this, e); // Private helper method - private object FireAddingNew() + private object? FireAddingNew() { AddingNewEventArgs e = new AddingNewEventArgs(null); OnAddingNew(e); @@ -287,12 +287,12 @@ namespace System.ComponentModel /// changes the contents of the list (such as an Insert or Remove). When an add operation is /// cancelled, the new item is removed from the list. /// - public T AddNew() => (T)((this as IBindingList).AddNew()); + public T AddNew() => (T)((this as IBindingList).AddNew())!; - object IBindingList.AddNew() + object? IBindingList.AddNew() { // Create new item and add it to list - object newItem = AddNewCore(); + object? newItem = AddNewCore(); // Record position of new item (to support cancellation later on) addNewPos = (newItem != null) ? IndexOf((T)newItem) : -1; @@ -310,10 +310,10 @@ namespace System.ComponentModel /// supply a custom item to add to the list. Otherwise an item of type T is created. /// The new item is then added to the end of the list. /// - protected virtual object AddNewCore() + protected virtual object? AddNewCore() { // Allow event handler to supply the new item for us - object newItem = FireAddingNew(); + object? newItem = FireAddingNew(); // If event hander did not supply new item, create one ourselves if (newItem == null) @@ -323,7 +323,7 @@ namespace System.ComponentModel // Add item to end of list. Note: If event handler returned an item not of type T, // the cast below will trigger an InvalidCastException. This is by design. - Add((T)newItem); + Add((T)newItem!); // Return new item to caller return newItem; @@ -405,9 +405,9 @@ namespace System.ComponentModel protected virtual bool IsSortedCore => false; - PropertyDescriptor IBindingList.SortProperty => SortPropertyCore; + PropertyDescriptor? IBindingList.SortProperty => SortPropertyCore; - protected virtual PropertyDescriptor SortPropertyCore => null; + protected virtual PropertyDescriptor? SortPropertyCore => null; ListSortDirection IBindingList.SortDirection => SortDirectionCore; @@ -475,7 +475,7 @@ namespace System.ComponentModel } [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] - private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void Child_PropertyChanged(object? sender, PropertyChangedEventArgs? e) { if (RaiseListChangedEvents) { @@ -506,7 +506,7 @@ namespace System.ComponentModel // somehow the item has been removed from our list without our knowledge. int pos = _lastChangeIndex; - if (pos < 0 || pos >= Count || !this[pos].Equals(item)) + if (pos < 0 || pos >= Count || !this[pos]!.Equals(item)) { pos = IndexOf(item); _lastChangeIndex = pos; @@ -529,7 +529,7 @@ namespace System.ComponentModel Debug.Assert(_itemTypeProperties != null); } - PropertyDescriptor pd = _itemTypeProperties.Find(e.PropertyName, true); + PropertyDescriptor? pd = _itemTypeProperties.Find(e.PropertyName, true); // Create event args. If there was no matching property descriptor, // we raise the list changed anyway. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BooleanConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BooleanConverter.cs index 14ae4552f66..b0f6c7c74b0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BooleanConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BooleanConverter.cs @@ -10,13 +10,13 @@ namespace System.ComponentModel /// public class BooleanConverter : TypeConverter { - private static volatile StandardValuesCollection s_values; + private static volatile StandardValuesCollection? s_values; /// /// Gets a value indicating whether this converter can convert an object /// in the given source type to a Boolean object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -25,7 +25,7 @@ namespace System.ComponentModel /// Converts the given value /// object to a Boolean object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -45,7 +45,7 @@ namespace System.ComponentModel /// /// Gets a collection of standard values for the Boolean data type. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { return s_values ?? (s_values = new StandardValuesCollection(new object[] { true, false })); } @@ -56,7 +56,7 @@ namespace System.ComponentModel /// is an exclusive list. /// /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true; /// /// @@ -64,6 +64,6 @@ namespace System.ComponentModel /// be picked from a list. /// /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ByteConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ByteConverter.cs index 3ac8d837751..99fb1690716 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ByteConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ByteConverter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given string to a Byte using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return byte.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((byte)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CancelEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CancelEventHandler.cs index 6eb19c574aa..7d893419fd8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CancelEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CancelEventHandler.cs @@ -6,5 +6,5 @@ namespace System.ComponentModel /// /// Represents the method that will handle the event raised when canceling an event. /// - public delegate void CancelEventHandler(object sender, CancelEventArgs e); + public delegate void CancelEventHandler(object? sender, CancelEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CharConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CharConverter.cs index baf29a1afb0..788319cfb24 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CharConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CharConverter.cs @@ -15,7 +15,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the given /// source type to a Unicode character object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -23,7 +23,7 @@ namespace System.ComponentModel /// /// Converts the given object to another type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is char charValue) { @@ -39,7 +39,7 @@ namespace System.ComponentModel /// /// Converts the given object to a Unicode character object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventArgs.cs index ae0fc47bfba..17c977be18c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventArgs.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public CollectionChangeEventArgs(CollectionChangeAction action, object element) + public CollectionChangeEventArgs(CollectionChangeAction action, object? element) { Action = action; Element = element; @@ -25,6 +25,6 @@ namespace System.ComponentModel /// /// Gets the instance of the collection with the change. /// - public virtual object Element { get; } + public virtual object? Element { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventHandler.cs index 17e15743f1a..b21cd58298e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionChangeEventHandler.cs @@ -9,5 +9,5 @@ namespace System.ComponentModel /// raised when adding elements to or removing elements from a collection. /// /// - public delegate void CollectionChangeEventHandler(object sender, CollectionChangeEventArgs e); + public delegate void CollectionChangeEventHandler(object? sender, CollectionChangeEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs index d78d0142489..52b332bc803 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs @@ -16,7 +16,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is ICollection) { @@ -31,7 +31,7 @@ namespace System.ComponentModel /// parameter using the specified context and attributes. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { return new PropertyDescriptorCollection(null); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComplexBindingPropertiesAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComplexBindingPropertiesAttribute.cs index bb2510e4f3a..362e0a43342 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComplexBindingPropertiesAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComplexBindingPropertiesAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -19,7 +21,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public ComplexBindingPropertiesAttribute(string dataSource) + public ComplexBindingPropertiesAttribute(string? dataSource) { DataSource = dataSource; } @@ -27,7 +29,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public ComplexBindingPropertiesAttribute(string dataSource, string dataMember) + public ComplexBindingPropertiesAttribute(string? dataSource, string? dataMember) { DataSource = dataSource; DataMember = dataMember; @@ -37,13 +39,13 @@ namespace System.ComponentModel /// Gets the name of the data source property for the component this attribute is /// bound to. /// - public string DataSource { get; } + public string? DataSource { get; } /// /// Gets the name of the data member property for the component this attribute is /// bound to. /// - public string DataMember { get; } + public string? DataMember { get; } /// /// Specifies the default value for the , which is . This @@ -51,7 +53,7 @@ namespace System.ComponentModel /// public static readonly ComplexBindingPropertiesAttribute Default = new ComplexBindingPropertiesAttribute(); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return obj is ComplexBindingPropertiesAttribute other && other.DataSource == DataSource && diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentConverter.cs index 23f8591136c..247f0053ca8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentConverter.cs @@ -23,7 +23,7 @@ namespace System.ComponentModel /// specified by the value parameter. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { return TypeDescriptor.GetProperties(value, attributes); } @@ -32,6 +32,6 @@ namespace System.ComponentModel /// Gets a value indicating whether this object supports properties using the /// specified context. /// - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentResourceManager.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentResourceManager.cs index 88f6bcf92ac..fdfc8a3c3ee 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentResourceManager.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ComponentResourceManager.cs @@ -17,8 +17,8 @@ namespace System.ComponentModel /// public class ComponentResourceManager : ResourceManager { - private Hashtable _resourceSets; - private CultureInfo _neutralResourcesCulture; + private Hashtable? _resourceSets; + private CultureInfo? _neutralResourcesCulture; public ComponentResourceManager() { @@ -32,7 +32,7 @@ namespace System.ComponentModel /// The culture of the main assembly's neutral resources. If someone is asking for this culture's resources, /// we don't need to walk up the parent chain. /// - private CultureInfo NeutralResourcesCulture + private CultureInfo? NeutralResourcesCulture { get { @@ -63,7 +63,7 @@ namespace System.ComponentModel /// property the resource will be ignored. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] - public virtual void ApplyResources(object value, string objectName, CultureInfo culture) + public virtual void ApplyResources(object value, string objectName, CultureInfo? culture) { if (value == null) { @@ -89,20 +89,20 @@ namespace System.ComponentModel // The reason we use a SortedDictionary here is to ensure the resources are applied // in an order consistent with codedom deserialization. - SortedList resources; + SortedList? resources; if (_resourceSets == null) { _resourceSets = new Hashtable(); - resources = FillResources(culture, out ResourceSet dummy); + resources = FillResources(culture, out ResourceSet? dummy); _resourceSets[culture] = resources; } else { - resources = (SortedList)_resourceSets[culture]; + resources = (SortedList?)_resourceSets[culture]; if (resources == null || (resources.Comparer.Equals(StringComparer.OrdinalIgnoreCase) != IgnoreCase)) { - resources = FillResources(culture, out ResourceSet dummy); + resources = FillResources(culture, out ResourceSet? dummy); _resourceSets[culture] = resources; } } @@ -116,14 +116,14 @@ namespace System.ComponentModel bool componentReflect = false; if (value is IComponent) { - ISite site = ((IComponent)value).Site; + ISite? site = ((IComponent)value).Site; if (site != null && site.DesignMode) { componentReflect = true; } } - foreach (KeyValuePair kvp in resources) + foreach (KeyValuePair kvp in resources) { // See if this key matches our object. string key = kvp.Key; @@ -156,7 +156,7 @@ namespace System.ComponentModel if (componentReflect) { - PropertyDescriptor prop = TypeDescriptor.GetProperties(value).Find(propName, IgnoreCase); + PropertyDescriptor? prop = TypeDescriptor.GetProperties(value).Find(propName, IgnoreCase); if (prop != null && !prop.IsReadOnly && (kvp.Value == null || prop.PropertyType.IsInstanceOfType(kvp.Value))) { @@ -165,7 +165,7 @@ namespace System.ComponentModel } else { - PropertyInfo prop = null; + PropertyInfo? prop = null; try { @@ -175,7 +175,7 @@ namespace System.ComponentModel { // Looks like we ran into a conflict between a declared property and an inherited one. // In such cases, we choose the most declared one. - Type t = value.GetType(); + Type? t = value.GetType(); do { prop = t.GetProperty(propName, flags | BindingFlags.DeclaredOnly); @@ -195,10 +195,10 @@ namespace System.ComponentModel /// Recursive routine that creates a resource hashtable populated with /// resources for culture and all parent cultures. /// - private SortedList FillResources(CultureInfo culture, out ResourceSet resourceSet) + private SortedList FillResources(CultureInfo culture, out ResourceSet? resourceSet) { - SortedList sd; - ResourceSet parentResourceSet = null; + SortedList sd; + ResourceSet? parentResourceSet = null; // Traverse parents first, so we always replace more // specific culture values with less specific. @@ -211,11 +211,11 @@ namespace System.ComponentModel // We're at the bottom, so create the sorted dictionary if (IgnoreCase) { - sd = new SortedList(StringComparer.OrdinalIgnoreCase); + sd = new SortedList(StringComparer.OrdinalIgnoreCase); } else { - sd = new SortedList(StringComparer.Ordinal); + sd = new SortedList(StringComparer.Ordinal); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs index 3975633bc49..9449116e8ca 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs @@ -10,10 +10,10 @@ namespace System.ComponentModel /// public class Container : IContainer { - private ISite[] _sites; + private ISite?[]? _sites; private int _siteCount; - private ComponentCollection _components; - private ContainerFilterService _filter; + private ComponentCollection? _components; + private ContainerFilterService? _filter; private bool _checkedFilter; private readonly object _syncObj = new object(); @@ -25,7 +25,7 @@ namespace System.ComponentModel /// The component is unnamed. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "No name is provided.")] - public virtual void Add(IComponent component) => Add(component, null); + public virtual void Add(IComponent? component) => Add(component, null); // Adds a component to the container. /// @@ -33,7 +33,7 @@ namespace System.ComponentModel /// a name to it. /// [RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered to validate the name.")] - public virtual void Add(IComponent component, string name) + public virtual void Add(IComponent? component, string? name) { lock (_syncObj) { @@ -42,7 +42,7 @@ namespace System.ComponentModel return; } - ISite site = component.Site; + ISite? site = component.Site; if (site != null && site.Container == this) { return; @@ -65,7 +65,7 @@ namespace System.ComponentModel } } - site?.Container.Remove(component); + site?.Container!.Remove(component); ISite newSite = CreateSite(component, name); _sites[_siteCount++] = newSite; @@ -78,7 +78,7 @@ namespace System.ComponentModel /// Creates a Site for the given /// and assigns the given name to the site. /// - protected virtual ISite CreateSite(IComponent component, string name) + protected virtual ISite CreateSite(IComponent component, string? name) { return new Site(component, this, name); } @@ -117,7 +117,7 @@ namespace System.ComponentModel { while (_siteCount > 0) { - ISite site = _sites[--_siteCount]; + ISite site = _sites![--_siteCount]!; site.Component.Site = null; site.Component.Dispose(); } @@ -127,7 +127,7 @@ namespace System.ComponentModel } } - protected virtual object GetService(Type service) => service == typeof(IContainer) ? this : null; + protected virtual object? GetService(Type service) => service == typeof(IContainer) ? this : null; /// /// Gets all the components in the . @@ -143,7 +143,7 @@ namespace System.ComponentModel IComponent[] result = new IComponent[_siteCount]; for (int i = 0; i < _siteCount; i++) { - result[i] = _sites[i].Component; + result[i] = _sites![i]!.Component; } _components = new ComponentCollection(result); @@ -178,13 +178,13 @@ namespace System.ComponentModel /// /// Removes a component from the . /// - public virtual void Remove(IComponent component) => Remove(component, false); + public virtual void Remove(IComponent? component) => Remove(component, false); - private void Remove(IComponent component, bool preserveSite) + private void Remove(IComponent? component, bool preserveSite) { lock (_syncObj) { - ISite site = component?.Site; + ISite? site = component?.Site; if (site == null || site.Container != this) { return; @@ -192,12 +192,12 @@ namespace System.ComponentModel if (!preserveSite) { - component.Site = null; + component!.Site = null; } for (int i = 0; i < _siteCount; i++) { - if (_sites[i] == site) + if (_sites![i] == site) { _siteCount--; Array.Copy(_sites, i + 1, _sites, i, _siteCount - i); @@ -209,7 +209,7 @@ namespace System.ComponentModel } } - protected void RemoveWithoutUnsiting(IComponent component) => Remove(component, true); + protected void RemoveWithoutUnsiting(IComponent? component) => Remove(component, true); /// /// Validates that the given name is valid for a component. The default implementation @@ -217,7 +217,7 @@ namespace System.ComponentModel /// components in the container. /// [RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered.")] - protected virtual void ValidateName(IComponent component, string name) + protected virtual void ValidateName(IComponent component, string? name) { if (component == null) { @@ -226,13 +226,13 @@ namespace System.ComponentModel if (name != null) { - for (int i = 0; i < Math.Min(_siteCount, _sites.Length); i++) + for (int i = 0; i < Math.Min(_siteCount, _sites!.Length); i++) { - ISite s = _sites[i]; + ISite? s = _sites[i]; if (s?.Name != null && string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase) && s.Component != component) { - InheritanceAttribute inheritanceAttribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(s.Component)[typeof(InheritanceAttribute)]; + InheritanceAttribute inheritanceAttribute = (InheritanceAttribute)TypeDescriptor.GetAttributes(s.Component)[typeof(InheritanceAttribute)]!; if (inheritanceAttribute.InheritanceLevel != InheritanceLevel.InheritedReadOnly) { throw new ArgumentException(SR.Format(SR.DuplicateComponentName, name)); @@ -244,9 +244,9 @@ namespace System.ComponentModel private sealed class Site : ISite { - private string _name; + private string? _name; - internal Site(IComponent component, Container container, string name) + internal Site(IComponent component, Container container, string? name) { Component = component; Container = container; @@ -263,7 +263,7 @@ namespace System.ComponentModel /// public IContainer Container { get; } - public object GetService(Type service) + public object? GetService(Type service) { return ((service == typeof(ISite)) ? this : ((Container)Container).GetService(service)); } @@ -276,7 +276,7 @@ namespace System.ComponentModel /// /// The name of the component. /// - public string Name + public string? Name { get => _name; [RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered to validate the name.")] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs index ccc0709679f..b675da60e69 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel.Design.Serialization; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; @@ -16,7 +17,7 @@ namespace System.ComponentModel /// public class CultureInfoConverter : TypeConverter { - private StandardValuesCollection _values; + private StandardValuesCollection? _values; /// /// Retrieves the "default" name for our culture. @@ -42,7 +43,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the given /// source type to a System.Globalization.CultureInfo object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -51,7 +52,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object to /// the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -60,7 +61,7 @@ namespace System.ComponentModel /// Converts the specified value object to a /// object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { // Only when GetCultureName returns culture.Name, we use CultureInfoMapper // (Since CultureInfoMapper will transfer Culture.DisplayName to Culture.Name). @@ -79,7 +80,7 @@ namespace System.ComponentModel } // Look for the default culture info. - CultureInfo retVal = null; + CultureInfo? retVal = null; if (string.IsNullOrEmpty(text) || string.Equals(text, defaultCultureString, StringComparison.Ordinal)) { retVal = CultureInfo.InvariantCulture; @@ -113,7 +114,7 @@ namespace System.ComponentModel // Finally, try to find a partial match. if (retVal == null) { - foreach (CultureInfo info in _values) + foreach (CultureInfo info in _values!) { if (info != null && GetCultureName(info).StartsWith(text, StringComparison.CurrentCulture)) { @@ -138,7 +139,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { @@ -172,11 +173,12 @@ namespace System.ComponentModel /// Gets a collection of standard values collection for a System.Globalization.CultureInfo /// object using the specified context. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + [MemberNotNull(nameof(_values))] + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { if (_values == null) { - CultureInfo[] installedCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures | CultureTypes.NeutralCultures); + CultureInfo?[] installedCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures | CultureTypes.NeutralCultures); int invariantIndex = Array.IndexOf(installedCultures, CultureInfo.InvariantCulture); CultureInfo[] array; @@ -212,13 +214,13 @@ namespace System.ComponentModel /// Gets a value indicating whether the list of standard values returned from /// System.ComponentModel.CultureInfoConverter.GetStandardValues is an exclusive list. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => false; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => false; /// /// Gets a value indicating whether this object supports a standard set /// of values that can be picked from a list using the specified context. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; /// /// IComparer object used for sorting CultureInfos @@ -234,7 +236,7 @@ namespace System.ComponentModel _converter = cultureConverter; } - public int Compare(object item1, object item2) + public int Compare(object? item1, object? item2) { if (item1 == null) { @@ -555,7 +557,7 @@ namespace System.ComponentModel public static string GetCultureInfoName(string cultureInfoDisplayName) { - return s_cultureInfoNameMap.TryGetValue(cultureInfoDisplayName, out string name) ? + return s_cultureInfoNameMap.TryGetValue(cultureInfoDisplayName, out string? name) ? name : cultureInfoDisplayName; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs index 03de81f6433..17923164ab3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel { public abstract class CustomTypeDescriptor : ICustomTypeDescriptor { - private readonly ICustomTypeDescriptor _parent; + private readonly ICustomTypeDescriptor? _parent; /// /// Creates a new CustomTypeDescriptor object. There are two versions @@ -31,7 +31,7 @@ namespace System.ComponentModel /// non-null, CustomTypeDescriptor calls the parent's version of /// the method. /// - protected CustomTypeDescriptor(ICustomTypeDescriptor parent) + protected CustomTypeDescriptor(ICustomTypeDescriptor? parent) { _parent = parent; } @@ -57,13 +57,13 @@ namespace System.ComponentModel /// this method causes the TypeDescriptor object to return the /// default class name. /// - public virtual string GetClassName() => _parent?.GetClassName(); + public virtual string? GetClassName() => _parent?.GetClassName(); /// /// The GetComponentName method returns the name of the component instance /// this type descriptor is describing. /// - public virtual string GetComponentName() => _parent?.GetComponentName(); + public virtual string? GetComponentName() => _parent?.GetComponentName(); /// /// The GetConverter method returns a type converter for the type this type @@ -85,21 +85,21 @@ namespace System.ComponentModel /// event on the object this type descriptor is representing. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - public virtual EventDescriptor GetDefaultEvent() => _parent?.GetDefaultEvent(); + public virtual EventDescriptor? GetDefaultEvent() => _parent?.GetDefaultEvent(); /// /// The GetDefaultProperty method returns the property descriptor for the /// default property on the object this type descriptor is representing. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - public virtual PropertyDescriptor GetDefaultProperty() => _parent?.GetDefaultProperty(); + public virtual PropertyDescriptor? GetDefaultProperty() => _parent?.GetDefaultProperty(); /// /// The GetEditor method returns an editor of the given type that is /// to be associated with the class this type descriptor is representing. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] - public virtual object GetEditor(Type editorBaseType) => _parent?.GetEditor(editorBaseType); + public virtual object? GetEditor(Type editorBaseType) => _parent?.GetEditor(editorBaseType); /// /// The GetEvents method returns a collection of event descriptors @@ -126,7 +126,7 @@ namespace System.ComponentModel /// event collection. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public virtual EventDescriptorCollection GetEvents(Attribute[] attributes) + public virtual EventDescriptorCollection GetEvents(Attribute[]? attributes) { if (_parent != null) { @@ -162,7 +162,7 @@ namespace System.ComponentModel /// property collection. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) + public virtual PropertyDescriptorCollection GetProperties(Attribute[]? attributes) { if (_parent != null) { @@ -179,6 +179,6 @@ namespace System.ComponentModel /// returned. Returning null from this method causes the TypeDescriptor object /// to use its default type description services. /// - public virtual object GetPropertyOwner(PropertyDescriptor pd) => _parent?.GetPropertyOwner(pd); + public virtual object? GetPropertyOwner(PropertyDescriptor pd) => _parent?.GetPropertyOwner(pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectAttribute.cs index fddc41fc294..386f5552a42 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { [AttributeUsage(AttributeTargets.Class)] @@ -23,7 +25,7 @@ namespace System.ComponentModel public bool IsDataObject { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectFieldAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectFieldAttribute.cs index 71a5496ffd8..f2d7389df75 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectFieldAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectFieldAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -38,7 +40,7 @@ namespace System.ComponentModel public bool PrimaryKey { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectMethodAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectMethodAttribute.cs index cab4b60862d..435003afa84 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectMethodAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DataObjectMethodAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { [AttributeUsage(AttributeTargets.Method)] @@ -20,7 +22,7 @@ namespace System.ComponentModel public DataObjectMethodType MethodType { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { @@ -32,7 +34,7 @@ namespace System.ComponentModel public override int GetHashCode() => ((int)MethodType).GetHashCode() ^ IsDefault.GetHashCode(); - public override bool Match(object obj) + public override bool Match([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeConverter.cs index e879aaa221f..c1e63c01285 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeConverter.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel /// object in the given source type to a /// object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -27,7 +27,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object /// to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -35,7 +35,7 @@ namespace System.ComponentModel /// /// Converts the given value object to a object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -48,11 +48,11 @@ namespace System.ComponentModel try { // See if we have a culture info to parse with. If so, then use it. - DateTimeFormatInfo formatInfo = null; + DateTimeFormatInfo? formatInfo = null; if (culture != null) { - formatInfo = (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo)); + formatInfo = (DateTimeFormatInfo?)culture.GetFormat(typeof(DateTimeFormatInfo)); } if (formatInfo != null) @@ -77,7 +77,7 @@ namespace System.ComponentModel /// Converts the given value object to a /// object using the arguments. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is DateTime) { @@ -92,7 +92,7 @@ namespace System.ComponentModel culture = CultureInfo.CurrentCulture; } - DateTimeFormatInfo formatInfo = (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo)); + DateTimeFormatInfo? formatInfo = (DateTimeFormatInfo?)culture.GetFormat(typeof(DateTimeFormatInfo)); if (culture == CultureInfo.InvariantCulture) { @@ -109,11 +109,11 @@ namespace System.ComponentModel string format; if (dt.TimeOfDay.TotalSeconds == 0) { - format = formatInfo.ShortDatePattern; + format = formatInfo!.ShortDatePattern; } else { - format = formatInfo.ShortDatePattern + " " + formatInfo.ShortTimePattern; + format = formatInfo!.ShortDatePattern + " " + formatInfo.ShortTimePattern; } return dt.ToString(format, CultureInfo.CurrentCulture); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeOffsetConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeOffsetConverter.cs index d9500ccff11..37d2a6f635c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeOffsetConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DateTimeOffsetConverter.cs @@ -17,7 +17,7 @@ namespace System.ComponentModel /// object in the given source type to a /// object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -26,7 +26,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an /// object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -35,7 +35,7 @@ namespace System.ComponentModel /// Converts the given value object to a /// object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -48,11 +48,11 @@ namespace System.ComponentModel try { // See if we have a culture info to parse with. If so, then use it. - DateTimeFormatInfo formatInfo = null; + DateTimeFormatInfo? formatInfo = null; if (culture != null) { - formatInfo = (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo)); + formatInfo = (DateTimeFormatInfo?)culture.GetFormat(typeof(DateTimeFormatInfo)); } if (formatInfo != null) @@ -77,7 +77,7 @@ namespace System.ComponentModel /// Converts the given value object to a /// object using the arguments. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { // logic is exactly as in DateTimeConverter, only the offset pattern ' zzz' is added to the default // ConvertToString pattern. @@ -94,8 +94,8 @@ namespace System.ComponentModel culture = CultureInfo.CurrentCulture; } - DateTimeFormatInfo formatInfo = null; - formatInfo = (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo)); + DateTimeFormatInfo? formatInfo = null; + formatInfo = (DateTimeFormatInfo?)culture.GetFormat(typeof(DateTimeFormatInfo)); string format; if (culture == CultureInfo.InvariantCulture) @@ -119,13 +119,13 @@ namespace System.ComponentModel { // pattern just like DateTimeConverter when DateTime.TimeOfDay.TotalSeconds==0 // but with ' zzz' offset pattern added. - format = formatInfo.ShortDatePattern + " zzz"; + format = formatInfo!.ShortDatePattern + " zzz"; } else { // pattern just like DateTimeConverter when DateTime.TimeOfDay.TotalSeconds!=0 // but with ' zzz' offset pattern added. - format = formatInfo.ShortDatePattern + " " + formatInfo.ShortTimePattern + " zzz"; + format = formatInfo!.ShortDatePattern + " " + formatInfo.ShortTimePattern + " zzz"; } return dto.ToString(format, CultureInfo.CurrentCulture); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DecimalConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DecimalConverter.cs index 524c2bdddcd..7a1983ecceb 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DecimalConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DecimalConverter.cs @@ -28,7 +28,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an /// object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -40,11 +40,11 @@ namespace System.ComponentModel /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor) && value is decimal decimalValue) { - ConstructorInfo ctor = typeof(decimal).GetConstructor(new Type[] { typeof(int[]) }); + ConstructorInfo? ctor = typeof(decimal).GetConstructor(new Type[] { typeof(int[]) }); Debug.Assert(ctor != null, "Expected constructor to exist."); return new InstanceDescriptor(ctor, new object[] { decimal.GetBits(decimalValue) }); } @@ -63,7 +63,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return decimal.Parse(value, NumberStyles.Float, formatInfo); } @@ -71,7 +71,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((decimal)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultBindingPropertyAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultBindingPropertyAttribute.cs index ccdabb781f2..0757cd08ff2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultBindingPropertyAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultBindingPropertyAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -19,7 +21,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public DefaultBindingPropertyAttribute(string name) + public DefaultBindingPropertyAttribute(string? name) { Name = name; } @@ -28,7 +30,7 @@ namespace System.ComponentModel /// Gets the name of the default binding property for the component this attribute is /// bound to. /// - public string Name { get; } + public string? Name { get; } /// /// Specifies the default value for the , which is . This @@ -36,7 +38,7 @@ namespace System.ComponentModel /// public static readonly DefaultBindingPropertyAttribute Default = new DefaultBindingPropertyAttribute(); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return obj is DefaultBindingPropertyAttribute other && other.Name == Name; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultEventAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultEventAttribute.cs index 5d9ec717b46..487bb0a89f3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultEventAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultEventAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -12,7 +14,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public DefaultEventAttribute(string name) + public DefaultEventAttribute(string? name) { Name = name; } @@ -20,7 +22,7 @@ namespace System.ComponentModel /// /// Gets the name of the default event for the component this attribute is bound to. /// - public string Name { get; } + public string? Name { get; } /// /// Specifies the default value for the , which is @@ -29,7 +31,7 @@ namespace System.ComponentModel /// public static readonly DefaultEventAttribute Default = new DefaultEventAttribute(null); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is DefaultEventAttribute other) && other.Name == Name; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultPropertyAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultPropertyAttribute.cs index 49fff0be6ff..99940093d3f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultPropertyAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DefaultPropertyAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -12,7 +14,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public DefaultPropertyAttribute(string name) + public DefaultPropertyAttribute(string? name) { Name = name; } @@ -21,7 +23,7 @@ namespace System.ComponentModel /// Gets the name of the default property for the component this attribute is /// bound to. /// - public string Name { get; } + public string? Name { get; } /// /// Specifies the default value for the , which is . This @@ -29,7 +31,7 @@ namespace System.ComponentModel /// public static readonly DefaultPropertyAttribute Default = new DefaultPropertyAttribute(null); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is DefaultPropertyAttribute other) && other.Name == Name; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs index b82e5924ffa..ed6f408f6e5 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs @@ -32,11 +32,11 @@ namespace System.ComponentModel /// data type. If the method is not interested in providing a substitute /// instance, it should call base. /// - public override object CreateInstance( - IServiceProvider provider, + public override object? CreateInstance( + IServiceProvider? provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, - Type[] argTypes, - object[] args) + Type[]? argTypes, + object[]? args) { return Provider.CreateInstance(provider, objectType, argTypes, args); } @@ -52,7 +52,7 @@ namespace System.ComponentModel /// The GetCache method returns an instance of this cache. GetCache will return /// null if there is no supported cache for an object. /// - public override IDictionary GetCache(object instance) => Provider.GetCache(instance); + public override IDictionary? GetCache(object instance) => Provider.GetCache(instance); /// /// The name of the specified component, or null if the component has no name. @@ -64,7 +64,7 @@ namespace System.ComponentModel /// GetTypeDescriptor.GetComponentName. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public override string GetFullComponentName(object component) => Provider.GetFullComponentName(component); + public override string? GetFullComponentName(object component) => Provider.GetFullComponentName(component); /// /// This method returns an extended custom type descriptor for the given object. @@ -99,7 +99,7 @@ namespace System.ComponentModel [return: DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] public override Type GetReflectionType( [DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] Type objectType, - object instance) + object? instance) { return Provider.GetReflectionType(objectType, instance); } @@ -114,7 +114,7 @@ namespace System.ComponentModel /// interested in providing type information for the object it should /// return null. /// - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + public override ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { return Provider.GetTypeDescriptor(objectType, instance); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDesignerEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDesignerEventArgs.cs index edb6f0d946f..04b15d46729 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDesignerEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDesignerEventArgs.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Design /// Initializes a new instance of the /// class. /// - public ActiveDesignerEventArgs(IDesignerHost oldDesigner, IDesignerHost newDesigner) + public ActiveDesignerEventArgs(IDesignerHost? oldDesigner, IDesignerHost? newDesigner) { OldDesigner = oldDesigner; NewDesigner = newDesigner; @@ -22,11 +22,11 @@ namespace System.ComponentModel.Design /// /// Gets or sets the document that is losing activation. /// - public IDesignerHost OldDesigner { get; } + public IDesignerHost? OldDesigner { get; } /// /// Gets or sets the document that is gaining activation. /// - public IDesignerHost NewDesigner { get; } + public IDesignerHost? NewDesigner { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDocumentEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDocumentEventHandler.cs index 104ca80563e..c1131eff65a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDocumentEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ActiveDocumentEventHandler.cs @@ -7,5 +7,5 @@ namespace System.ComponentModel.Design /// Represents the method that will handle the /// event raised on changes to the currently active document. /// - public delegate void ActiveDesignerEventHandler(object sender, ActiveDesignerEventArgs e); + public delegate void ActiveDesignerEventHandler(object? sender, ActiveDesignerEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs index 17f27f6e275..7605b016a1a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs @@ -34,7 +34,7 @@ namespace System.ComponentModel.Design /// Initializes a new instance of the /// class with the specified message. /// - public CheckoutException(string message) : base(message) + public CheckoutException(string? message) : base(message) { } @@ -42,7 +42,7 @@ namespace System.ComponentModel.Design /// Initializes a new instance of the /// class with the specified message and error code. /// - public CheckoutException(string message, int errorCode) : base(message, errorCode) + public CheckoutException(string? message, int errorCode) : base(message, errorCode) { } @@ -59,7 +59,7 @@ namespace System.ComponentModel.Design /// reference to the inner exception that is the cause of this exception. /// FxCop CA1032: Multiple constructors are required to correctly implement a custom exception. /// - public CheckoutException(string message, Exception innerException) : base(message, innerException) + public CheckoutException(string? message, Exception? innerException) : base(message, innerException) { } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs index e8d431c741c..5640c431c5e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.ComponentModel.Design @@ -29,7 +30,7 @@ namespace System.ComponentModel.Design /// /// Overrides Object's Equals method. /// - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return obj is CommandID cid && cid.Guid.Equals(Guid) && cid.ID == ID; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventArgs.cs index 220affd617a..3521487f9a9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventArgs.cs @@ -11,27 +11,27 @@ namespace System.ComponentModel.Design /// /// Gets or sets the component that is the cause of this event. /// - public object Component { get; } + public object? Component { get; } /// /// Gets or sets the member that is about to change. /// - public MemberDescriptor Member { get; } + public MemberDescriptor? Member { get; } /// /// Gets or sets the new value of the changed member. /// - public object NewValue { get; } + public object? NewValue { get; } /// /// Gets or sets the old value of the changed member. /// - public object OldValue { get; } + public object? OldValue { get; } /// /// Initializes a new instance of the class. /// - public ComponentChangedEventArgs(object component, MemberDescriptor member, object oldValue, object newValue) + public ComponentChangedEventArgs(object? component, MemberDescriptor? member, object? oldValue, object? newValue) { Component = component; Member = member; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventHandler.cs index 080b3c84edd..6e9eae8de47 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangedEventHandler.cs @@ -6,5 +6,5 @@ namespace System.ComponentModel.Design /// /// Represents the method that will handle a event. /// - public delegate void ComponentChangedEventHandler(object sender, ComponentChangedEventArgs e); + public delegate void ComponentChangedEventHandler(object? sender, ComponentChangedEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventArgs.cs index 3c01af96879..1b04ca43092 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventArgs.cs @@ -11,17 +11,17 @@ namespace System.ComponentModel.Design /// /// Gets or sets the component that is being changed or that is the parent container of the member being changed. /// - public object Component { get; } + public object? Component { get; } /// /// Gets or sets the member of the component that is about to be changed. /// - public MemberDescriptor Member { get; } + public MemberDescriptor? Member { get; } /// /// Initializes a new instance of the class. /// - public ComponentChangingEventArgs(object component, MemberDescriptor member) + public ComponentChangingEventArgs(object? component, MemberDescriptor? member) { Component = component; Member = member; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventHandler.cs index 89210360e56..461fa60bcc7 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentChangingEventHandler.cs @@ -6,5 +6,5 @@ namespace System.ComponentModel.Design /// /// Represents the method that will handle a ComponentChangingEvent event. /// - public delegate void ComponentChangingEventHandler(object sender, ComponentChangingEventArgs e); + public delegate void ComponentChangingEventHandler(object? sender, ComponentChangingEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventArgs.cs index d54d4727e14..006bc3e92f3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventArgs.cs @@ -12,12 +12,12 @@ namespace System.ComponentModel.Design /// /// Gets or sets the component associated with the event. /// - public virtual IComponent Component { get; } + public virtual IComponent? Component { get; } /// /// Initializes a new instance of the System.ComponentModel.Design.ComponentEventArgs class. /// - public ComponentEventArgs(IComponent component) + public ComponentEventArgs(IComponent? component) { Component = component; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventHandler.cs index 0cb7b8d1efa..66ec6a6e334 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentEventHandler.cs @@ -8,5 +8,5 @@ namespace System.ComponentModel.Design /// event raised /// for component-level events. /// - public delegate void ComponentEventHandler(object sender, ComponentEventArgs e); + public delegate void ComponentEventHandler(object? sender, ComponentEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventArgs.cs index c7a202f577b..ffdc9dcf8b8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventArgs.cs @@ -11,23 +11,23 @@ namespace System.ComponentModel.Design /// /// Gets or sets the component that is being renamed. /// - public object Component { get; } + public object? Component { get; } /// /// Gets or sets the name of the component before the rename. /// - public virtual string OldName { get; } + public virtual string? OldName { get; } /// /// Gets or sets the current name of the component. /// - public virtual string NewName { get; } + public virtual string? NewName { get; } /// /// Initializes a new instance of the /// class. /// - public ComponentRenameEventArgs(object component, string oldName, string newName) + public ComponentRenameEventArgs(object? component, string? oldName, string? newName) { OldName = oldName; NewName = newName; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventHandler.cs index f303b2282f8..ba51a5fa3c8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ComponentRenameEventHandler.cs @@ -6,5 +6,5 @@ namespace System.ComponentModel.Design /// /// Represents the method that will handle a event. /// - public delegate void ComponentRenameEventHandler(object sender, ComponentRenameEventArgs e); + public delegate void ComponentRenameEventHandler(object? sender, ComponentRenameEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerCollection.cs index 04fb3e94541..483eb87d2a2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerCollection.cs @@ -17,7 +17,7 @@ namespace System.ComponentModel.Design /// that stores an array with a pointer to each /// for each document in the collection. /// - public DesignerCollection(IDesignerHost[] designers) + public DesignerCollection(IDesignerHost[]? designers) { if (designers != null) { @@ -34,7 +34,7 @@ namespace System.ComponentModel.Design /// that stores an array with a pointer to each /// for each document in the collection. /// - public DesignerCollection(IList designers) + public DesignerCollection(IList? designers) { _designers = designers ?? new ArrayList(); } @@ -47,7 +47,7 @@ namespace System.ComponentModel.Design /// /// Gets or sets the document at the specified index. /// - public virtual IDesignerHost this[int index] => (IDesignerHost)_designers[index]; + public virtual IDesignerHost? this[int index] => (IDesignerHost?)_designers[index]; /// /// Creates and retrieves a new enumerator for this collection. @@ -58,7 +58,7 @@ namespace System.ComponentModel.Design bool ICollection.IsSynchronized => false; - object ICollection.SyncRoot => null; + object ICollection.SyncRoot => null!; void ICollection.CopyTo(Array array, int index) => _designers.CopyTo(array, index); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventArgs.cs index d660f9904a6..0d59cb4cff6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventArgs.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Design /// Initializes a new instance of the System.ComponentModel.Design.DesignerEventArgs /// class. /// - public DesignerEventArgs(IDesignerHost host) + public DesignerEventArgs(IDesignerHost? host) { Designer = host; } @@ -21,6 +21,6 @@ namespace System.ComponentModel.Design /// /// Gets or sets the host of the document. /// - public IDesignerHost Designer { get; } + public IDesignerHost? Designer { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventHandler.cs index 4e74c2f9f3f..a564d044de0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerEventHandler.cs @@ -7,5 +7,5 @@ namespace System.ComponentModel.Design /// Represents the method that will handle the System.ComponentModel.Design.IDesignerEventService.DesignerEvent /// event raised when a document is created or disposed. /// - public delegate void DesignerEventHandler(object sender, DesignerEventArgs e); + public delegate void DesignerEventHandler(object? sender, DesignerEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs index ea4b253b923..df182f2dff2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.Design /// public abstract class DesignerOptionService : IDesignerOptionService { - private DesignerOptionCollection _options; + private DesignerOptionCollection? _options; /// /// Returns the options collection for this service. There is @@ -57,7 +57,7 @@ namespace System.ComponentModel.Design /// null if the property couldn't be found. /// [RequiresUnreferencedCode("The Type of DesignerOptionCollection's value cannot be statically discovered.")] - private PropertyDescriptor GetOptionProperty(string pageName, string valueName) + private PropertyDescriptor? GetOptionProperty(string pageName, string valueName) { if (pageName == null) { @@ -71,7 +71,7 @@ namespace System.ComponentModel.Design string[] optionNames = pageName.Split('\\'); - DesignerOptionCollection options = Options; + DesignerOptionCollection? options = Options; foreach (string optionName in optionNames) { options = options[optionName]; @@ -101,9 +101,9 @@ namespace System.ComponentModel.Design /// Gets the value of an option defined in this package. /// [RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] - object IDesignerOptionService.GetOptionValue(string pageName, string valueName) + object? IDesignerOptionService.GetOptionValue(string pageName, string valueName) { - PropertyDescriptor optionProp = GetOptionProperty(pageName, valueName); + PropertyDescriptor? optionProp = GetOptionProperty(pageName, valueName); return optionProp?.GetValue(null); } @@ -113,7 +113,7 @@ namespace System.ComponentModel.Design [RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] void IDesignerOptionService.SetOptionValue(string pageName, string valueName, object value) { - PropertyDescriptor optionProp = GetOptionProperty(pageName, valueName); + PropertyDescriptor? optionProp = GetOptionProperty(pageName, valueName); optionProp?.SetValue(null, value); } @@ -129,14 +129,14 @@ namespace System.ComponentModel.Design public sealed class DesignerOptionCollection : IList { private readonly DesignerOptionService _service; - private readonly object _value; - private ArrayList _children; - private PropertyDescriptorCollection _properties; + private readonly object? _value; + private ArrayList? _children; + private PropertyDescriptorCollection? _properties; /// /// Creates a new DesignerOptionCollection. /// - internal DesignerOptionCollection(DesignerOptionService service, DesignerOptionCollection parent, string name, object value) + internal DesignerOptionCollection(DesignerOptionService service, DesignerOptionCollection? parent, string name, object? value) { _service = service; Parent = parent; @@ -145,7 +145,7 @@ namespace System.ComponentModel.Design if (Parent != null) { - parent._properties = null; + parent!._properties = null; if (Parent._children == null) { Parent._children = new ArrayList(1); @@ -175,7 +175,7 @@ namespace System.ComponentModel.Design /// /// Returns the parent collection object, or null if there is no parent. /// - public DesignerOptionCollection Parent { get; } + public DesignerOptionCollection? Parent { get; } /// /// The collection of properties that this OptionCollection, along with all of @@ -227,7 +227,7 @@ namespace System.ComponentModel.Design /// /// Retrieves the child collection at the given index. /// - public DesignerOptionCollection this[int index] + public DesignerOptionCollection? this[int index] { get { @@ -236,7 +236,7 @@ namespace System.ComponentModel.Design { throw new IndexOutOfRangeException(nameof(index)); } - return (DesignerOptionCollection)_children[index]; + return (DesignerOptionCollection?)_children[index]; } } @@ -244,7 +244,7 @@ namespace System.ComponentModel.Design /// Retrieves the child collection at the given name. The name search is case /// insensitive. /// - public DesignerOptionCollection this[string name] + public DesignerOptionCollection? this[string name] { get { @@ -272,6 +272,7 @@ namespace System.ComponentModel.Design /// /// Called before any access to our collection to force it to become populated. /// + [MemberNotNull(nameof(_children))] private void EnsurePopulated() { if (_children == null) @@ -305,7 +306,7 @@ namespace System.ComponentModel.Design /// /// Locates the value object to use for getting or setting a property. /// - private static object RecurseFindValue(DesignerOptionCollection options) + private static object? RecurseFindValue(DesignerOptionCollection options) { if (options._value != null) { @@ -314,7 +315,7 @@ namespace System.ComponentModel.Design foreach (DesignerOptionCollection child in options) { - object value = RecurseFindValue(child); + object? value = RecurseFindValue(child); if (value != null) { return value; @@ -330,7 +331,7 @@ namespace System.ComponentModel.Design /// public bool ShowDialog() { - object value = RecurseFindValue(this); + object? value = RecurseFindValue(this); if (value == null) { @@ -363,7 +364,7 @@ namespace System.ComponentModel.Design /// /// Private IList implementation. /// - object IList.this[int index] + object? IList.this[int index] { get => this[index]; set => throw new NotSupportedException(); @@ -372,7 +373,7 @@ namespace System.ComponentModel.Design /// /// Private IList implementation. /// - int IList.Add(object value) => throw new NotSupportedException(); + int IList.Add(object? value) => throw new NotSupportedException(); /// /// Private IList implementation. @@ -382,7 +383,7 @@ namespace System.ComponentModel.Design /// /// Private IList implementation. /// - bool IList.Contains(object value) + bool IList.Contains(object? value) { EnsurePopulated(); return _children.Contains(value); @@ -391,7 +392,7 @@ namespace System.ComponentModel.Design /// /// Private IList implementation. /// - int IList.IndexOf(object value) + int IList.IndexOf(object? value) { EnsurePopulated(); return _children.IndexOf(value); @@ -400,12 +401,12 @@ namespace System.ComponentModel.Design /// /// Private IList implementation. /// - void IList.Insert(int index, object value) => throw new NotSupportedException(); + void IList.Insert(int index, object? value) => throw new NotSupportedException(); /// /// Private IList implementation. /// - void IList.Remove(object value) => throw new NotSupportedException(); + void IList.Remove(object? value) => throw new NotSupportedException(); /// /// Private IList implementation. @@ -438,11 +439,11 @@ namespace System.ComponentModel.Design public override bool CanResetValue(object component) => _property.CanResetValue(_target); - public override object GetValue(object component) => _property.GetValue(_target); + public override object? GetValue(object? component) => _property.GetValue(_target); public override void ResetValue(object component) => _property.ResetValue(_target); - public override void SetValue(object component, object value) => _property.SetValue(_target, value); + public override void SetValue(object? component, object? value) => _property.SetValue(_target, value); public override bool ShouldSerializeValue(object component) => _property.ShouldSerializeValue(_target); } @@ -453,10 +454,10 @@ namespace System.ComponentModel.Design /// internal sealed class DesignerOptionConverter : TypeConverter { - public override bool GetPropertiesSupported(ITypeDescriptorContext cxt) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? cxt) => true; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext cxt, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? cxt, object value, Attribute[]? attributes) { PropertyDescriptorCollection props = new PropertyDescriptorCollection(null); if (!(value is DesignerOptionCollection options)) @@ -476,7 +477,7 @@ namespace System.ComponentModel.Design return props; } - public override object ConvertTo(ITypeDescriptorContext cxt, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? cxt, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { @@ -502,13 +503,13 @@ namespace System.ComponentModel.Design public override bool CanResetValue(object component) => false; - public override object GetValue(object component) => _option; + public override object GetValue(object? component) => _option; public override void ResetValue(object component) { } - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerTransactionCloseEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerTransactionCloseEventHandler.cs index 51c4e59a3cf..9892ba9e274 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerTransactionCloseEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerTransactionCloseEventHandler.cs @@ -3,5 +3,5 @@ namespace System.ComponentModel.Design { - public delegate void DesignerTransactionCloseEventHandler(object sender, DesignerTransactionCloseEventArgs e); + public delegate void DesignerTransactionCloseEventHandler(object? sender, DesignerTransactionCloseEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerb.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerb.cs index 5294587965e..d74d5d6d5aa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerb.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerb.cs @@ -35,7 +35,7 @@ namespace System.ComponentModel.Design { get { - object result = Properties["Description"]; + object? result = Properties["Description"]; if (result == null) { return string.Empty; @@ -53,7 +53,7 @@ namespace System.ComponentModel.Design { get { - object result = Properties["Text"]; + object? result = Properties["Text"]; if (result == null) { return string.Empty; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerbCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerbCollection.cs index b53eb3ea6d6..fcd934ab68b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerbCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerVerbCollection.cs @@ -13,15 +13,15 @@ namespace System.ComponentModel.Design public DesignerVerbCollection(DesignerVerb[] value) => AddRange(value); - public DesignerVerb this[int index] + public DesignerVerb? this[int index] { - get => (DesignerVerb)(List[index]); + get => (DesignerVerb?)(List[index]); set => List[index] = value; } - public int Add(DesignerVerb value) => List.Add(value); + public int Add(DesignerVerb? value) => List.Add(value); - public void AddRange(DesignerVerb[] value) + public void AddRange(DesignerVerb?[] value) { if (value == null) { @@ -48,15 +48,15 @@ namespace System.ComponentModel.Design } } - public void Insert(int index, DesignerVerb value) => List.Insert(index, value); + public void Insert(int index, DesignerVerb? value) => List.Insert(index, value); - public int IndexOf(DesignerVerb value) => List.IndexOf(value); + public int IndexOf(DesignerVerb? value) => List.IndexOf(value); - public bool Contains(DesignerVerb value) => List.Contains(value); + public bool Contains(DesignerVerb? value) => List.Contains(value); - public void Remove(DesignerVerb value) => List.Remove(value); + public void Remove(DesignerVerb? value) => List.Remove(value); - public void CopyTo(DesignerVerb[] array, int index) => List.CopyTo(array, index); + public void CopyTo(DesignerVerb?[] array, int index) => List.CopyTo(array, index); protected override void OnValidate(object value) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs index 758af025e35..b72d91e01c9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs @@ -25,7 +25,7 @@ namespace System.ComponentModel.Design /// /// Gets a saved license key. /// - public override string GetSavedLicenseKey(Type type, Assembly resourceAssembly) => null; + public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) => null; /// /// Sets a saved license key. @@ -37,13 +37,13 @@ namespace System.ComponentModel.Design throw new ArgumentNullException(nameof(type)); } - _savedLicenseKeys[type.AssemblyQualifiedName] = key; + _savedLicenseKeys[type.AssemblyQualifiedName!] = key; } } internal sealed class RuntimeLicenseContext : LicenseContext { - internal Hashtable _savedLicenseKeys; + internal Hashtable? _savedLicenseKeys; /// /// This method takes a file URL and converts it to a local path. The trick here is that @@ -60,9 +60,9 @@ namespace System.ComponentModel.Design [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", Justification = "Suppressing the warning until gets fixed, see https://github.com/dotnet/runtime/issues/50821")] - public override string GetSavedLicenseKey(Type type, Assembly resourceAssembly) + public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) { - if (_savedLicenseKeys == null || _savedLicenseKeys[type.AssemblyQualifiedName] == null) + if (_savedLicenseKeys == null || _savedLicenseKeys[type.AssemblyQualifiedName!] == null) { if (_savedLicenseKeys == null) { @@ -87,7 +87,7 @@ namespace System.ComponentModel.Design string fileName = new FileInfo(location).Name; - Stream s = asm.GetManifestResourceStream(fileName + ".licenses"); + Stream? s = asm.GetManifestResourceStream(fileName + ".licenses"); if (s == null) { // Since the casing may be different depending on how the assembly was loaded, @@ -111,12 +111,12 @@ namespace System.ComponentModel.Design string licResourceName = fileName + ".licenses"; // First try the filename - Stream s = resourceAssembly.GetManifestResourceStream(licResourceName); + Stream? s = resourceAssembly.GetManifestResourceStream(licResourceName); if (s == null) { - string resolvedName = null; + string? resolvedName = null; CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo; - string shortAssemblyName = resourceAssembly.GetName().Name; + string shortAssemblyName = resourceAssembly.GetName().Name!; // If the assembly has been renamed, we try our best to find a good match in the available resources // by looking at the assembly name (which doesn't change even after a file rename) + ".exe.licenses" or + ".dll.licenses" foreach (string existingName in resourceAssembly.GetManifestResourceNames()) @@ -141,7 +141,7 @@ namespace System.ComponentModel.Design } } } - return (string)_savedLicenseKeys[type.AssemblyQualifiedName]; + return (string?)_savedLicenseKeys[type.AssemblyQualifiedName!]; } /** @@ -150,14 +150,14 @@ namespace System.ComponentModel.Design * we are attempting to locate could have different casing * depending on how the assembly was loaded. **/ - private Stream CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name) + private Stream? CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name) { CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo; // Loop through the resource names in the assembly. // We try to handle the case where the assembly file name has been renamed // by trying to guess the original file name based on the assembly name. - string assemblyShortName = satellite.GetName().Name; + string assemblyShortName = satellite.GetName().Name!; foreach (string existingName in satellite.GetManifestResourceNames()) { if (comparer.Compare(existingName, name, CompareOptions.IgnoreCase) == 0 || diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs index e9aa1bea911..5c391cf9690 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs @@ -43,8 +43,8 @@ namespace System.ComponentModel.Design writer.Write(context._savedLicenseKeys.Count); foreach (DictionaryEntry keyAndValue in context._savedLicenseKeys) { - writer.Write(keyAndValue.Key.ToString()); - writer.Write(keyAndValue.Value.ToString()); + writer.Write(keyAndValue.Key.ToString()!); + writer.Write(keyAndValue.Value!.ToString()!); } } } @@ -166,7 +166,7 @@ namespace System.ComponentModel.Design int numEntries = reader.ReadInt32(); if (streamCryptoKey == cryptoKey) { - context._savedLicenseKeys.Clear(); + context._savedLicenseKeys!.Clear(); for (int i = 0; i < numEntries; i++) { string key = reader.ReadString(); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/HelpKeywordAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/HelpKeywordAttribute.cs index 28ec5125ecb..460ec9b91d2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/HelpKeywordAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/HelpKeywordAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel.Design { @@ -79,12 +80,12 @@ namespace System.ComponentModel.Design /// /// Retrieves the HelpKeyword this attribute supplies. /// - public string HelpKeyword { get; } + public string? HelpKeyword { get; } /// /// Two instances of a HelpKeywordAttribute are equal if they're HelpKeywords are equal. /// - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentChangeService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentChangeService.cs index 3bca9da3702..4783db07e64 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentChangeService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentChangeService.cs @@ -46,11 +46,11 @@ namespace System.ComponentModel.Design /// /// Announces to the component change service that a particular component has changed. /// - void OnComponentChanged(object component, MemberDescriptor member, object oldValue, object newValue); + void OnComponentChanged(object component, MemberDescriptor? member, object? oldValue, object? newValue); /// /// Announces to the component change service that a particular component is changing. /// - void OnComponentChanging(object component, MemberDescriptor member); + void OnComponentChanging(object component, MemberDescriptor? member); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentDiscoveryService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentDiscoveryService.cs index 70304fe2ae4..424407793e1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentDiscoveryService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentDiscoveryService.cs @@ -16,6 +16,6 @@ namespace System.ComponentModel.Design /// IComponent. If baseType is null, all components are retrieved; otherwise /// only component types derived from the specified baseType are returned. /// - ICollection GetComponentTypes(IDesignerHost designerHost, Type baseType); + ICollection GetComponentTypes(IDesignerHost? designerHost, Type? baseType); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentInitializer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentInitializer.cs index 93e4277e8ee..88fffd2338b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentInitializer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IComponentInitializer.cs @@ -23,7 +23,7 @@ namespace System.ComponentModel.Design /// dictionary, because this is an existing component that may already have properties /// set on it. /// - void InitializeExistingComponent(IDictionary defaultValues); + void InitializeExistingComponent(IDictionary? defaultValues); /// /// This method is called when a component is first initialized, typically after being first added @@ -32,6 +32,6 @@ namespace System.ComponentModel.Design /// are specified. You may perform any initialization of this component that you like, and you /// may even ignore the defaultValues dictionary altogether if you wish. /// - void InitializeNewComponent(IDictionary defaultValues); + void InitializeNewComponent(IDictionary? defaultValues); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesigner.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesigner.cs index 2e50448c7b4..a334f397a86 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesigner.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesigner.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel.Design /// /// Gets or sets the design-time verbs supported by the designer. /// - DesignerVerbCollection Verbs { get; } + DesignerVerbCollection? Verbs { get; } /// /// Performs the default action for this designer. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerEventService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerEventService.cs index e247764e5ad..662e9ebc4c1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerEventService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerEventService.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Design /// /// Gets the currently active designer. /// - IDesignerHost ActiveDesigner { get; } + IDesignerHost? ActiveDesigner { get; } /// /// Gets or sets a collection of running design documents in the development environment. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerHost.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerHost.cs index d757b4a7abb..b74eeaf316c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerHost.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerHost.cs @@ -118,11 +118,11 @@ namespace System.ComponentModel.Design /// /// Gets the designer instance for the specified component. /// - IDesigner GetDesigner(IComponent component); + IDesigner? GetDesigner(IComponent component); /// /// Gets the type instance for the specified fully qualified type name . /// - Type GetType(string typeName); + Type? GetType(string typeName); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerOptionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerOptionService.cs index 49909f5327c..be0ebe973a0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerOptionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDesignerOptionService.cs @@ -14,7 +14,7 @@ namespace System.ComponentModel.Design /// Gets the value of an option defined in this package. /// [RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] - object GetOptionValue(string pageName, string valueName); + object? GetOptionValue(string pageName, string valueName); /// /// Sets the value of an option defined in this package. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDictionaryService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDictionaryService.cs index 5bd7e739474..682d4a61698 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDictionaryService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IDictionaryService.cs @@ -12,16 +12,16 @@ namespace System.ComponentModel.Design /// /// Gets the key corresponding to the specified value. /// - object GetKey(object value); + object? GetKey(object? value); /// /// Gets the value corresponding to the specified key. /// - object GetValue(object key); + object? GetValue(object key); /// /// Sets the specified key-value pair. /// - void SetValue(object key, object value); + void SetValue(object key, object? value); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IEventPropertyService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IEventPropertyService.cs index a6e19ab4707..0a76830197e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IEventPropertyService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IEventPropertyService.cs @@ -27,7 +27,7 @@ namespace System.ComponentModel.Design /// For properties that are representing events, this will return the event /// that the property represents. /// - EventDescriptor GetEvent(PropertyDescriptor property); + EventDescriptor? GetEvent(PropertyDescriptor property); /// /// Converts a set of event descriptors to a set of property descriptors. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IMenuCommandService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IMenuCommandService.cs index d6fbb18532f..60f12ecb087 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IMenuCommandService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IMenuCommandService.cs @@ -29,7 +29,7 @@ namespace System.ComponentModel.Design /// Searches for the given command ID and returns the /// associated with it. /// - MenuCommand FindCommand(CommandID commandID); + MenuCommand? FindCommand(CommandID commandID); /// /// Invokes a command on the local form or in the global environment. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IReferenceService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IReferenceService.cs index 2fae605a68e..e859fa45204 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IReferenceService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IReferenceService.cs @@ -12,17 +12,17 @@ namespace System.ComponentModel.Design /// /// Gets the base component that anchors this reference. /// - IComponent GetComponent(object reference); + IComponent? GetComponent(object reference); /// /// Gets a reference for the specified name. /// - object GetReference(string name); + object? GetReference(string name); /// /// Gets the name for this reference. /// - string GetName(object reference); + string? GetName(object reference); /// /// Gets all available references. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IResourceService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IResourceService.cs index 507b3dd9120..75db6bb1c33 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IResourceService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/IResourceService.cs @@ -15,7 +15,7 @@ namespace System.ComponentModel.Design /// Locates the resource reader for the specified culture and /// returns it. /// - IResourceReader GetResourceReader(CultureInfo info); + IResourceReader? GetResourceReader(CultureInfo info); /// /// Locates the resource writer for the specified culture diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ISelectionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ISelectionService.cs index 7912913c9f0..e852de88fb6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ISelectionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ISelectionService.cs @@ -43,11 +43,11 @@ namespace System.ComponentModel.Design /// /// Sets the currently selected set of components. /// - void SetSelectedComponents(ICollection components); + void SetSelectedComponents(ICollection? components); /// /// Sets the currently selected set of components to those with the specified selection type within the specified array of components. /// - void SetSelectedComponents(ICollection components, SelectionTypes selectionType); + void SetSelectedComponents(ICollection? components, SelectionTypes selectionType); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITreeDesigner.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITreeDesigner.cs index aa5db63ecc3..a3d2a0ebe6c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITreeDesigner.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITreeDesigner.cs @@ -21,6 +21,6 @@ namespace System.ComponentModel.Design /// Retrieves the parent designer for this designer. This may return null if /// there is no parent. /// - IDesigner Parent { get; } + IDesigner? Parent { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeDiscoveryService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeDiscoveryService.cs index cc24ce904c4..f143495b5c1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeDiscoveryService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeDiscoveryService.cs @@ -18,6 +18,6 @@ namespace System.ComponentModel.Design /// types from all referenced assemblies are checked. Otherwise, /// only types from non-GAC referenced assemblies are checked. /// - ICollection GetTypes(Type baseType, bool excludeGlobalTypes); + ICollection GetTypes(Type? baseType, bool excludeGlobalTypes); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs index b02ea35f3bb..df4b96cf5b9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs @@ -14,30 +14,30 @@ namespace System.ComponentModel.Design /// /// Retrieves the requested assembly. /// - Assembly GetAssembly(AssemblyName name); + Assembly? GetAssembly(AssemblyName name); /// /// Retrieves the requested assembly. /// - Assembly GetAssembly(AssemblyName name, bool throwOnError); + Assembly? GetAssembly(AssemblyName name, bool throwOnError); /// /// Loads a type with the given name. /// [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); + Type? GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); /// /// Loads a type with the given name. /// [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); + Type? GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); /// /// Loads a type with the given name. /// [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); + Type? GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); /// /// References the given assembly name. Once an assembly has @@ -49,6 +49,6 @@ namespace System.ComponentModel.Design /// /// Returns the path to the file name from which the assembly was loaded. /// - string GetPathOfAssembly(AssemblyName name); + string? GetPathOfAssembly(AssemblyName name); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/MenuCommand.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/MenuCommand.cs index abd7d8d3fdf..54ffd3ae7aa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/MenuCommand.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/MenuCommand.cs @@ -13,10 +13,10 @@ namespace System.ComponentModel.Design public class MenuCommand { // Events that we suface or call on - private readonly EventHandler _execHandler; + private readonly EventHandler? _execHandler; private int _status; - private IDictionary _properties; + private IDictionary? _properties; /// /// Indicates that the given command is enabled. An enabled command may @@ -44,7 +44,7 @@ namespace System.ComponentModel.Design /// /// Initializes a new instance of . /// - public MenuCommand(EventHandler handler, CommandID command) + public MenuCommand(EventHandler? handler, CommandID? command) { _execHandler = handler; CommandID = command; @@ -112,13 +112,13 @@ namespace System.ComponentModel.Design /// /// Occurs when the menu command changes. /// - public event EventHandler CommandChanged; + public event EventHandler? CommandChanged; /// /// Gets the associated with this menu command. /// - public virtual CommandID CommandID { get; } + public virtual CommandID? CommandID { get; } /// /// Invokes a menu item. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs index 73eca9e8a4f..92ee6a87a46 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -13,8 +14,8 @@ namespace System.ComponentModel [AttributeUsage(AttributeTargets.All)] public class PropertyTabAttribute : Attribute { - private Type[] _tabClasses; - private string[] _tabClassNames; + private Type[]? _tabClasses; + private string[]? _tabClassNames; /// /// Basic constructor that creates a PropertyTabAttribute. Use this ctor to derive from this @@ -87,7 +88,7 @@ namespace System.ComponentModel { InitializeTabClasses(); } - return _tabClasses; + return _tabClasses!; } } @@ -95,14 +96,16 @@ namespace System.ComponentModel Justification = "The APIs that specify _tabClassNames are either marked with DynamicallyAccessedMembers or RequiresUnreferencedCode.")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", Justification = "The APIs that specify _tabClassNames are either marked with DynamicallyAccessedMembers or RequiresUnreferencedCode.")] + [MemberNotNull(nameof(_tabClasses))] private void InitializeTabClasses() { + Debug.Assert(_tabClassNames != null); _tabClasses = new Type[_tabClassNames.Length]; for (int i = 0; i < _tabClassNames.Length; i++) { int commaIndex = _tabClassNames[i].IndexOf(','); - string className = null; - string assemblyName = null; + string? className = null; + string? assemblyName = null; if (commaIndex != -1) { @@ -114,7 +117,7 @@ namespace System.ComponentModel className = _tabClassNames[i]; } - _tabClasses[i] = Type.GetType(className, false); + _tabClasses[i] = Type.GetType(className, false)!; if (_tabClasses[i] == null) { @@ -123,7 +126,7 @@ namespace System.ComponentModel Assembly a = Assembly.Load(assemblyName); if (a != null) { - _tabClasses[i] = a.GetType(className, true); + _tabClasses[i] = a.GetType(className, true)!; } } else @@ -134,14 +137,14 @@ namespace System.ComponentModel } } - protected string[] TabClassNames => (string[])_tabClassNames?.Clone(); + protected string[]? TabClassNames => (string[]?)_tabClassNames?.Clone(); /// /// Gets the scopes of tabs for this System.ComponentModel.Design.PropertyTabAttribute, from System.ComponentModel.Design.PropertyTabScope. /// public PropertyTabScope[] TabScopes { get; private set; } - public override bool Equals(object other) + public override bool Equals([NotNullWhen(true)] object? other) { if (other is PropertyTabAttribute propertyTabAttribute) { @@ -182,7 +185,7 @@ namespace System.ComponentModel /// Utiliity function to set the types of tab classes this PropertyTabAttribute specifies. /// [RequiresUnreferencedCode("The Types referenced by tabClassNames may be trimmed.")] - protected void InitializeArrays(string[] tabClassNames, PropertyTabScope[] tabScopes) + protected void InitializeArrays(string[]? tabClassNames, PropertyTabScope[]? tabScopes) { InitializeArrays(tabClassNames, null, tabScopes); } @@ -190,12 +193,12 @@ namespace System.ComponentModel /// /// Utiliity function to set the types of tab classes this PropertyTabAttribute specifies. /// - protected void InitializeArrays(Type[] tabClasses, PropertyTabScope[] tabScopes) + protected void InitializeArrays(Type[]? tabClasses, PropertyTabScope[]? tabScopes) { InitializeArrays(null, tabClasses, tabScopes); } - private void InitializeArrays(string[] tabClassNames, Type[] tabClasses, PropertyTabScope[] tabScopes) + private void InitializeArrays(string[]? tabClassNames, Type[]? tabClasses, PropertyTabScope[]? tabScopes) { if (tabClasses != null) { @@ -232,7 +235,7 @@ namespace System.ComponentModel } else { - TabScopes = new PropertyTabScope[tabClasses.Length]; + TabScopes = new PropertyTabScope[tabClasses!.Length]; for (int i = 0; i < TabScopes.Length; i++) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ContextStack.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ContextStack.cs index 16b59f14830..2e7841929e1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ContextStack.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ContextStack.cs @@ -23,13 +23,13 @@ namespace System.ComponentModel.Design.Serialization /// public sealed class ContextStack { - private ArrayList _contextStack; + private ArrayList? _contextStack; /// /// Retrieves the current object on the stack, or null /// if no objects have been pushed. /// - public object Current + public object? Current { get { @@ -45,7 +45,7 @@ namespace System.ComponentModel.Design.Serialization /// Retrieves the object on the stack at the given /// level, or null if no object exists at that level. /// - public object this[int level] + public object? this[int level] { get { @@ -66,7 +66,7 @@ namespace System.ComponentModel.Design.Serialization /// inherits from or implements the given type, or /// null if no object on the stack implements the type. /// - public object this[Type type] + public object? this[Type type] { get { @@ -80,7 +80,7 @@ namespace System.ComponentModel.Design.Serialization int level = _contextStack.Count; while (level > 0) { - object value = _contextStack[--level]; + object value = _contextStack[--level]!; if (type.IsInstanceOfType(value)) { return value; @@ -117,9 +117,9 @@ namespace System.ComponentModel.Design.Serialization /// Pops the current object off of the stack, returning /// its value. /// - public object Pop() + public object? Pop() { - object context = null; + object? context = null; if (_contextStack != null && _contextStack.Count > 0) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/DefaultSerializationProviderAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/DefaultSerializationProviderAttribute.cs index a539d1a2bbf..c1c610a1bb2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/DefaultSerializationProviderAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/DefaultSerializationProviderAttribute.cs @@ -23,7 +23,7 @@ namespace System.ComponentModel.Design.Serialization throw new ArgumentNullException(nameof(providerType)); } - ProviderTypeName = providerType.AssemblyQualifiedName; + ProviderTypeName = providerType.AssemblyQualifiedName!; } /// diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderHost.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderHost.cs index 8af48576f15..a51c0ad6902 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderHost.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderHost.cs @@ -22,7 +22,7 @@ namespace System.ComponentModel.Design.Serialization /// loader host may just call ToString on them). If the load was successful then /// errorCollection should either be null or contain an empty collection. /// - void EndLoad(string baseClassName, bool successful, ICollection errorCollection); + void EndLoad(string baseClassName, bool successful, ICollection? errorCollection); /// /// This is called by the designer loader when it wishes to reload the diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderService.cs index 497f5345830..c18d30b5fc6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerLoaderService.cs @@ -31,7 +31,7 @@ namespace System.ComponentModel.Design.Serialization /// a successful load, or a collection of exceptions that indicate the /// reason(s) for failure. /// - void DependentLoadComplete(bool successful, ICollection errorCollection); + void DependentLoadComplete(bool successful, ICollection? errorCollection); /// /// This can be called by an outside object to request that the loader diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerSerializationProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerSerializationProvider.cs index 255f9a2db18..780fac8166d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerSerializationProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/IDesignerSerializationProvider.cs @@ -27,6 +27,6 @@ namespace System.ComponentModel.Design.Serialization /// either return it or return null to prevent an infinite /// loop. /// - object GetSerializer(IDesignerSerializationManager manager, object currentSerializer, Type objectType, Type serializerType); + object? GetSerializer(IDesignerSerializationManager manager, object currentSerializer, Type objectType, Type serializerType); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/INameCreationService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/INameCreationService.cs index dd066d9f04e..bab60b98b78 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/INameCreationService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/INameCreationService.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel.Design.Serialization /// derive a name from the data type's name. The container /// parameter can be null if no container search is needed. /// - string CreateName(IContainer container, Type dataType); + string CreateName(IContainer? container, Type dataType); /// /// Determines if the given name is valid. A name diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/InstanceDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/InstanceDescriptor.cs index c1391c2e804..34b4ebd330e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/InstanceDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/InstanceDescriptor.cs @@ -16,14 +16,14 @@ namespace System.ComponentModel.Design.Serialization /// /// Creates a new InstanceDescriptor. /// - public InstanceDescriptor(MemberInfo member, ICollection arguments) : this(member, arguments, true) + public InstanceDescriptor(MemberInfo? member, ICollection? arguments) : this(member, arguments, true) { } /// /// Creates a new InstanceDescriptor. /// - public InstanceDescriptor(MemberInfo member, ICollection arguments, bool isComplete) + public InstanceDescriptor(MemberInfo? member, ICollection? arguments, bool isComplete) { MemberInfo = member; IsComplete = isComplete; @@ -78,7 +78,7 @@ namespace System.ComponentModel.Design.Serialization { throw new ArgumentException(SR.InstanceDescriptorMustBeReadable); } - MethodInfo getMethod = pi.GetGetMethod(); + MethodInfo? getMethod = pi.GetGetMethod(); if (getMethod != null && !getMethod.IsStatic) { throw new ArgumentException(SR.InstanceDescriptorMustBeStatic); @@ -104,24 +104,24 @@ namespace System.ComponentModel.Design.Serialization /// The MemberInfo object that was passed into the constructor /// of this InstanceDescriptor. /// - public MemberInfo MemberInfo { get; } + public MemberInfo? MemberInfo { get; } /// /// Invokes this instance descriptor, returning the object /// the descriptor describes. /// - public object Invoke() + public object? Invoke() { - object[] translatedArguments = new object[Arguments.Count]; + object?[] translatedArguments = new object[Arguments.Count]; Arguments.CopyTo(translatedArguments, 0); // Instance descriptors can contain other instance // descriptors. Translate them if necessary. for (int i = 0; i < translatedArguments.Length; i++) { - if (translatedArguments[i] is InstanceDescriptor) + if (translatedArguments[i] is InstanceDescriptor instanceDescriptor) { - translatedArguments[i] = ((InstanceDescriptor)translatedArguments[i]).Invoke(); + translatedArguments[i] = instanceDescriptor.Invoke(); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs index 463b159310b..6691792d7fc 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs @@ -107,7 +107,7 @@ namespace System.ComponentModel.Design.Serialization { if (_relationships.TryGetValue(new RelationshipEntry(source), out RelationshipEntry retVal) && retVal._owner.IsAlive) { - return new MemberRelationship(retVal._owner.Target, retVal._member); + return new MemberRelationship(retVal._owner.Target!, retVal._member); } return MemberRelationship.Empty; @@ -132,15 +132,15 @@ namespace System.ComponentModel.Design.Serialization Justification = "GetComponentName is only used to create a nice exception message, and has a fallback when null is returned.")] private static void ThrowRelationshipNotSupported(MemberRelationship source, MemberRelationship relationship) { - string sourceName = TypeDescriptor.GetComponentName(source.Owner); - string relName = TypeDescriptor.GetComponentName(relationship.Owner); + string? sourceName = TypeDescriptor.GetComponentName(source.Owner!); + string? relName = TypeDescriptor.GetComponentName(relationship.Owner!); if (sourceName == null) { - sourceName = source.Owner.ToString(); + sourceName = source.Owner!.ToString(); } if (relName == null) { - relName = relationship.Owner.ToString(); + relName = relationship.Owner!.ToString(); } throw new ArgumentException(SR.Format(SR.MemberRelationshipService_RelationshipNotSupported, sourceName, source.Member.Name, relName, relationship.Member.Name)); } @@ -166,7 +166,7 @@ namespace System.ComponentModel.Design.Serialization _hashCode = rel.Owner == null ? 0 : rel.Owner.GetHashCode(); } - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { Debug.Assert(o is RelationshipEntry, "This is only called indirectly from a dictionary only containing RelationshipEntry structs."); return this == (RelationshipEntry)o; @@ -174,8 +174,8 @@ namespace System.ComponentModel.Design.Serialization public static bool operator ==(RelationshipEntry re1, RelationshipEntry re2) { - object owner1 = (re1._owner.IsAlive ? re1._owner.Target : null); - object owner2 = (re2._owner.IsAlive ? re2._owner.Target : null); + object? owner1 = (re1._owner.IsAlive ? re1._owner.Target : null); + object? owner2 = (re2._owner.IsAlive ? re2._owner.Target : null); return owner1 == owner2 && re1._member.Equals(re2._member); } @@ -217,12 +217,12 @@ namespace System.ComponentModel.Design.Serialization /// /// The object owning the member. /// - public object Owner { get; } + public object? Owner { get; } /// /// Infrastructure support to make this a first class struct /// - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return obj is MemberRelationship rel && rel.Owner == Owner && rel.Member == Member; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventArgs.cs index b4b4a65915d..224e563b253 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventArgs.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Design.Serialization /// /// Creates a new resolve name event args object. /// - public ResolveNameEventArgs(string name) + public ResolveNameEventArgs(string? name) { Name = name; Value = null; @@ -22,11 +22,11 @@ namespace System.ComponentModel.Design.Serialization /// /// The name of the object that needs to be resolved. /// - public string Name { get; } + public string? Name { get; } /// /// The object that matches the name. /// - public object Value { get; set; } + public object? Value { get; set; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventHandler.cs index 09100b2836b..ab6bd20ae4f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/ResolveNameEventHandler.cs @@ -7,5 +7,5 @@ namespace System.ComponentModel.Design.Serialization /// This delegate is used to resolve object names when performing /// serialization and deserialization. /// - public delegate void ResolveNameEventHandler(object sender, ResolveNameEventArgs e); + public delegate void ResolveNameEventHandler(object? sender, ResolveNameEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/RootDesignerSerializerAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/RootDesignerSerializerAttribute.cs index bfcca80487b..e26b821d021 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/RootDesignerSerializerAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/RootDesignerSerializerAttribute.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.Design.Serialization [Obsolete("This attribute has been deprecated. Use DesignerSerializerAttribute instead. For example, to specify a root designer for CodeDom, use DesignerSerializerAttribute(...,typeof(TypeCodeDomSerializer)). https://go.microsoft.com/fwlink/?linkid=14202")] public sealed class RootDesignerSerializerAttribute : Attribute { - private string _typeId; + private string? _typeId; /// /// Creates a new designer serialization attribute. @@ -51,7 +51,7 @@ namespace System.ComponentModel.Design.Serialization /// /// Creates a new designer serialization attribute. /// - public RootDesignerSerializerAttribute(string serializerTypeName, string baseSerializerTypeName, bool reloadable) + public RootDesignerSerializerAttribute(string? serializerTypeName, string? baseSerializerTypeName, bool reloadable) { SerializerTypeName = serializerTypeName; SerializerBaseTypeName = baseSerializerTypeName; @@ -68,12 +68,12 @@ namespace System.ComponentModel.Design.Serialization /// /// Retrieves the fully qualified type name of the serializer. /// - public string SerializerTypeName { get; } + public string? SerializerTypeName { get; } /// /// Retrieves the fully qualified type name of the serializer base type. /// - public string SerializerBaseTypeName { get; } + public string? SerializerBaseTypeName { get; } /// /// This defines a unique ID for this attribute type. It is used diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceContainer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceContainer.cs index d5cedfba0f3..116bbf564c0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceContainer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceContainer.cs @@ -11,8 +11,8 @@ namespace System.ComponentModel.Design /// public class ServiceContainer : IServiceContainer, IDisposable { - private ServiceCollection _services; - private readonly IServiceProvider _parentProvider; + private ServiceCollection? _services; + private readonly IServiceProvider? _parentProvider; private static readonly Type[] s_defaultServices = new Type[] { typeof(IServiceContainer), typeof(ServiceContainer) }; /// @@ -25,7 +25,7 @@ namespace System.ComponentModel.Design /// /// Creates a new service object container. /// - public ServiceContainer(IServiceProvider parentProvider) + public ServiceContainer(IServiceProvider? parentProvider) { _parentProvider = parentProvider; } @@ -33,7 +33,7 @@ namespace System.ComponentModel.Design /// /// Retrieves the parent service container, or null if there is no parent container. /// - private IServiceContainer Container + private IServiceContainer? Container { get => _parentProvider?.GetService(typeof(IServiceContainer)) as IServiceContainer; } @@ -50,7 +50,7 @@ namespace System.ComponentModel.Design /// Our collection of services. The service collection is demand /// created here. /// - private ServiceCollection Services => _services ?? (_services = new ServiceCollection()); + private ServiceCollection Services => _services ?? (_services = new ServiceCollection()); /// /// Adds the given service to the service container. @@ -67,7 +67,7 @@ namespace System.ComponentModel.Design { if (promote) { - IServiceContainer container = Container; + IServiceContainer? container = Container; if (container != null) { container.AddService(serviceType, serviceInstance, promote); @@ -108,7 +108,7 @@ namespace System.ComponentModel.Design { if (promote) { - IServiceContainer container = Container; + IServiceContainer? container = Container; if (container != null) { container.AddService(serviceType, callback, promote); @@ -147,11 +147,11 @@ namespace System.ComponentModel.Design { if (disposing) { - ServiceCollection serviceCollection = _services; + ServiceCollection? serviceCollection = _services; _services = null; if (serviceCollection != null) { - foreach (object o in serviceCollection.Values) + foreach (object? o in serviceCollection.Values) { if (o is IDisposable) { @@ -165,9 +165,9 @@ namespace System.ComponentModel.Design /// /// Retrieves the requested service. /// - public virtual object GetService(Type serviceType) + public virtual object? GetService(Type serviceType) { - object service = null; + object? service = null; // Try locally. We first test for services we // implement and then look in our service collection. @@ -189,8 +189,8 @@ namespace System.ComponentModel.Design // Is the service a creator delegate? if (service is ServiceCreatorCallback) { - service = ((ServiceCreatorCallback)service)(this, serviceType); - if (service != null && !service.GetType().IsCOMObject && !serviceType.IsInstanceOfType(service)) + service = ((ServiceCreatorCallback)service)(this, serviceType!); + if (service != null && !service.GetType().IsCOMObject && !serviceType!.IsInstanceOfType(service)) { // Callback passed us a bad service. NULL it, rather than throwing an exception. // Callers here do not need to be prepared to handle bad callback implemetations. @@ -198,12 +198,12 @@ namespace System.ComponentModel.Design } // And replace the callback with our new service. - Services[serviceType] = service; + Services[serviceType!] = service; } if (service == null && _parentProvider != null) { - service = _parentProvider.GetService(serviceType); + service = _parentProvider.GetService(serviceType!); } return service; @@ -224,7 +224,7 @@ namespace System.ComponentModel.Design { if (promote) { - IServiceContainer container = Container; + IServiceContainer? container = Container; if (container != null) { container.RemoveService(serviceType, promote); @@ -254,9 +254,9 @@ namespace System.ComponentModel.Design private sealed class EmbeddedTypeAwareTypeComparer : IEqualityComparer { - public bool Equals(Type x, Type y) => x.IsEquivalentTo(y); + public bool Equals(Type? x, Type? y) => x!.IsEquivalentTo(y); - public int GetHashCode(Type obj) => obj.FullName.GetHashCode(); + public int GetHashCode(Type obj) => obj.FullName!.GetHashCode(); } public ServiceCollection() : base(s_serviceTypeComparer) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceCreatorCallback.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceCreatorCallback.cs index 69f52e68915..5939b4bdeb0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceCreatorCallback.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ServiceCreatorCallback.cs @@ -6,5 +6,5 @@ namespace System.ComponentModel.Design /// /// Declares a callback function to create an instance of a service on demand. /// - public delegate object ServiceCreatorCallback(IServiceContainer container, Type serviceType); + public delegate object? ServiceCreatorCallback(IServiceContainer container, Type serviceType); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DesignTimeVisibleAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DesignTimeVisibleAttribute.cs index 2f021057a2b..6cf7b1c7e65 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DesignTimeVisibleAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DesignTimeVisibleAttribute.cs @@ -4,6 +4,7 @@ namespace System.ComponentModel { using System; + using System.Diagnostics.CodeAnalysis; /// /// DesignTimeVisibileAttribute marks a component's visibility. If @@ -51,7 +52,7 @@ namespace System.ComponentModel /// public static readonly DesignTimeVisibleAttribute Default = Yes; - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DoubleConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DoubleConverter.cs index 68a7a66a812..173ca6960a0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DoubleConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DoubleConverter.cs @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return double.Parse(value, NumberStyles.Float, formatInfo); } @@ -40,7 +40,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((double)value).ToString("R", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs index 8820290a6e9..c043cdd6e10 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs @@ -28,13 +28,13 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] protected Type EnumType { get; } - protected StandardValuesCollection Values { get; set; } + protected StandardValuesCollection? Values { get; set; } /// /// Gets a value indicating whether this converter can convert an object in the given /// source type to an enumeration object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == typeof(string) || sourceType == typeof(Enum[])) { @@ -47,7 +47,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object to the /// given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (destinationType == typeof(Enum[]) || destinationType == typeof(InstanceDescriptor)) { @@ -62,7 +62,7 @@ namespace System.ComponentModel /// protected virtual IComparer Comparer => InvariantComparer.Default; - private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, CultureInfo culture) + private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, CultureInfo? culture) { return isUnderlyingTypeUInt64 ? unchecked((long)Convert.ToUInt64(enumVal, culture)) : @@ -72,7 +72,7 @@ namespace System.ComponentModel /// /// Converts the specified value object to an enumeration object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -115,7 +115,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -136,7 +136,7 @@ namespace System.ComponentModel if (destinationType == typeof(InstanceDescriptor) && value != null) { - string enumName = ConvertToInvariantString(context, value); + string enumName = ConvertToInvariantString(context, value)!; if (EnumType.IsDefined(typeof(FlagsAttribute), false) && enumName.Contains(',')) { @@ -150,7 +150,7 @@ namespace System.ComponentModel { object convertedValue = ((IConvertible)value).ToType(underlyingType, culture); - MethodInfo method = typeof(Enum).GetMethod("ToObject", new Type[] { typeof(Type), underlyingType }); + MethodInfo? method = typeof(Enum).GetMethod("ToObject", new Type[] { typeof(Type), underlyingType }); if (method != null) { return new InstanceDescriptor(method, new object[] { EnumType, convertedValue }); @@ -159,7 +159,7 @@ namespace System.ComponentModel } else { - FieldInfo info = EnumType.GetField(enumName); + FieldInfo? info = EnumType.GetField(enumName); if (info != null) { return new InstanceDescriptor(info, null); @@ -178,7 +178,7 @@ namespace System.ComponentModel long[] ulValues = new long[objValues.Length]; for (int idx = 0; idx < objValues.Length; idx++) { - ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, (Enum)objValues.GetValue(idx), culture); + ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, (Enum)objValues.GetValue(idx)!, culture); } long longValue = GetEnumValue(isUnderlyingTypeUInt64, (Enum)value, culture); @@ -223,7 +223,7 @@ namespace System.ComponentModel /// Gets a collection of standard values for the data type this validator is /// designed for. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { if (Values == null) { @@ -232,8 +232,8 @@ namespace System.ComponentModel // the behavior is undefined, since what we return are just enum values, not names. Type reflectType = TypeDescriptor.GetReflectionType(EnumType) ?? EnumType; - FieldInfo[] fields = reflectType.GetFields(BindingFlags.Public | BindingFlags.Static); - ArrayList objValues = null; + FieldInfo[]? fields = reflectType.GetFields(BindingFlags.Public | BindingFlags.Static); + ArrayList? objValues = null; if (fields != null && fields.Length > 0) { @@ -242,9 +242,9 @@ namespace System.ComponentModel if (objValues != null) { - foreach (FieldInfo field in fields) + foreach (FieldInfo field in fields!) { - BrowsableAttribute browsableAttr = null; + BrowsableAttribute? browsableAttr = null; foreach (Attribute attr in field.GetCustomAttributes(typeof(BrowsableAttribute), false)) { browsableAttr = attr as BrowsableAttribute; @@ -252,7 +252,7 @@ namespace System.ComponentModel if (browsableAttr == null || browsableAttr.Browsable) { - object value = null; + object? value = null; try { @@ -273,14 +273,14 @@ namespace System.ComponentModel } } - IComparer comparer = Comparer; + IComparer? comparer = Comparer; if (comparer != null) { objValues.Sort(comparer); } } - Array arr = objValues?.ToArray(); + Array? arr = objValues?.ToArray(); Values = new StandardValuesCollection(arr); } return Values; @@ -291,7 +291,7 @@ namespace System.ComponentModel /// /// is an exclusive list using the specified context. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) { return !EnumType.IsDefined(typeof(FlagsAttribute), false); } @@ -300,11 +300,11 @@ namespace System.ComponentModel /// Gets a value indicating whether this object supports a standard set of values /// that can be picked from a list using the specified context. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; /// /// Gets a value indicating whether the given object value is valid for this type. /// - public override bool IsValid(ITypeDescriptorContext context, object value) => Enum.IsDefined(EnumType, value); + public override bool IsValid(ITypeDescriptorContext? context, object? value) => Enum.IsDefined(EnumType, value!); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs index 3c4ac1998ac..8d79711b7e0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs @@ -14,7 +14,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with the /// specified name and attribute array. /// - protected EventDescriptor(string name, Attribute[] attrs) : base(name, attrs) + protected EventDescriptor(string name, Attribute[]? attrs) : base(name, attrs) { } @@ -31,7 +31,7 @@ namespace System.ComponentModel /// the name in the specified and the /// attributes in both the and the array. /// - protected EventDescriptor(MemberDescriptor descr, Attribute[] attrs) : base(descr, attrs) + protected EventDescriptor(MemberDescriptor descr, Attribute[]? attrs) : base(descr, attrs) { } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs index 13c6ea9622f..6a193bacd8f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs @@ -13,9 +13,9 @@ namespace System.ComponentModel /// public class EventDescriptorCollection : ICollection, IList { - private EventDescriptor[] _events; - private readonly string[] _namedSort; - private readonly IComparer _comparer; + private EventDescriptor?[] _events; + private readonly string[]? _namedSort; + private readonly IComparer? _comparer; private bool _eventsOwned; private bool _needSort; private readonly bool _readOnly; @@ -28,7 +28,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - public EventDescriptorCollection(EventDescriptor[] events) + public EventDescriptorCollection(EventDescriptor[]? events) { if (events == null) { @@ -46,12 +46,12 @@ namespace System.ComponentModel /// Initializes a new instance of an event descriptor collection, and allows you to mark the /// collection as read-only so it cannot be modified. /// - public EventDescriptorCollection(EventDescriptor[] events, bool readOnly) : this(events) + public EventDescriptorCollection(EventDescriptor[]? events, bool readOnly) : this(events) { _readOnly = readOnly; } - private EventDescriptorCollection(EventDescriptor[] events, int eventCount, string[] namedSort, IComparer comparer) + private EventDescriptorCollection(EventDescriptor?[] events, int eventCount, string[]? namedSort, IComparer? comparer) { _eventsOwned = false; if (namedSort != null) @@ -72,7 +72,7 @@ namespace System.ComponentModel /// /// Gets the event with the specified index number. /// - public virtual EventDescriptor this[int index] + public virtual EventDescriptor? this[int index] { get { @@ -88,9 +88,9 @@ namespace System.ComponentModel /// /// Gets the event with the specified name. /// - public virtual EventDescriptor this[string name] => Find(name, false); + public virtual EventDescriptor? this[string name] => Find(name, false); - public int Add(EventDescriptor value) + public int Add(EventDescriptor? value) { if (_readOnly) { @@ -112,7 +112,7 @@ namespace System.ComponentModel Count = 0; } - public bool Contains(EventDescriptor value) => IndexOf(value) >= 0; + public bool Contains(EventDescriptor? value) => IndexOf(value) >= 0; void ICollection.CopyTo(Array array, int index) { @@ -166,15 +166,15 @@ namespace System.ComponentModel /// Gets the description of the event with the specified /// name in the collection. /// - public virtual EventDescriptor Find(string name, bool ignoreCase) + public virtual EventDescriptor? Find(string name, bool ignoreCase) { - EventDescriptor p = null; + EventDescriptor? p = null; if (ignoreCase) { for (int i = 0; i < Count; i++) { - if (string.Equals(_events[i].Name, name, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(_events[i]!.Name, name, StringComparison.OrdinalIgnoreCase)) { p = _events[i]; break; @@ -185,7 +185,7 @@ namespace System.ComponentModel { for (int i = 0; i < Count; i++) { - if (string.Equals(_events[i].Name, name, StringComparison.Ordinal)) + if (string.Equals(_events[i]!.Name, name, StringComparison.Ordinal)) { p = _events[i]; break; @@ -196,9 +196,9 @@ namespace System.ComponentModel return p; } - public int IndexOf(EventDescriptor value) => Array.IndexOf(_events, value, 0, Count); + public int IndexOf(EventDescriptor? value) => Array.IndexOf(_events, value, 0, Count); - public void Insert(int index, EventDescriptor value) + public void Insert(int index, EventDescriptor? value) { if (_readOnly) { @@ -214,7 +214,7 @@ namespace System.ComponentModel Count++; } - public void Remove(EventDescriptor value) + public void Remove(EventDescriptor? value) { if (_readOnly) { @@ -300,7 +300,7 @@ namespace System.ComponentModel /// Sorts the members of this EventDescriptorCollection. Any specified NamedSort arguments will /// be applied first, followed by sort using the specified IComparer. /// - protected void InternalSort(string[] names) + protected void InternalSort(string[]? names) { if (_events.Length == 0) { @@ -311,7 +311,7 @@ namespace System.ComponentModel if (names != null && names.Length > 0) { - List eventList = new List(_events); + List eventList = new List(_events); int foundCount = 0; int eventCount = _events.Length; @@ -319,7 +319,7 @@ namespace System.ComponentModel { for (int j = 0; j < eventCount; j++) { - EventDescriptor currentEvent = eventList[j]; + EventDescriptor? currentEvent = eventList[j]; // Found a matching event. Here, we add it to our array. We also // mark it as null in our array list so we don't add it twice later. @@ -353,7 +353,7 @@ namespace System.ComponentModel /// /// Sorts the members of this EventDescriptorCollection using the specified IComparer. /// - protected void InternalSort(IComparer sorter) + protected void InternalSort(IComparer? sorter) { if (sorter == null) { @@ -367,13 +367,13 @@ namespace System.ComponentModel bool ICollection.IsSynchronized => false; - object ICollection.SyncRoot => null; + object ICollection.SyncRoot => null!; int ICollection.Count => Count; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - object IList.this[int index] + object? IList.this[int index] { get => this[index]; set @@ -388,21 +388,21 @@ namespace System.ComponentModel throw new IndexOutOfRangeException(); } EnsureEventsOwned(); - _events[index] = (EventDescriptor)value; + _events[index] = (EventDescriptor?)value; } } - int IList.Add(object value) => Add((EventDescriptor)value); + int IList.Add(object? value) => Add((EventDescriptor?)value); - bool IList.Contains(object value) => Contains((EventDescriptor)value); + bool IList.Contains(object? value) => Contains((EventDescriptor?)value); void IList.Clear() => Clear(); - int IList.IndexOf(object value) => IndexOf((EventDescriptor)value); + int IList.IndexOf(object? value) => IndexOf((EventDescriptor?)value); - void IList.Insert(int index, object value) => Insert(index, (EventDescriptor)value); + void IList.Insert(int index, object? value) => Insert(index, (EventDescriptor?)value); - void IList.Remove(object value) => Remove((EventDescriptor)value); + void IList.Remove(object? value) => Remove((EventDescriptor?)value); void IList.RemoveAt(int index) => RemoveAt(index); @@ -412,11 +412,11 @@ namespace System.ComponentModel private sealed class ArraySubsetEnumerator : IEnumerator { - private readonly Array _array; + private readonly Array? _array; private readonly int _total; private int _current; - public ArraySubsetEnumerator(Array array, int count) + public ArraySubsetEnumerator(Array? array, int count) { Debug.Assert(count == 0 || array != null, "if array is null, count should be 0"); Debug.Assert(array == null || count <= array.Length, "Trying to enumerate more than the array contains"); @@ -441,7 +441,7 @@ namespace System.ComponentModel public void Reset() => _current = -1; - public object Current + public object? Current { get { @@ -451,7 +451,7 @@ namespace System.ComponentModel } else { - return _array.GetValue(_current); + return _array!.GetValue(_current); } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExpandableObjectConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExpandableObjectConverter.cs index 53cf1494d03..180b7ebc5f3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExpandableObjectConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExpandableObjectConverter.cs @@ -23,7 +23,7 @@ namespace System.ComponentModel /// specified by the value parameter. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { return TypeDescriptor.GetProperties(value, attributes); } @@ -32,6 +32,6 @@ namespace System.ComponentModel /// Gets a value indicating whether this object supports properties using the /// specified context. /// - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs index 12c86e0e8dd..959ce0dcb3e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs @@ -19,13 +19,13 @@ namespace System.ComponentModel /// Creates a new extended property info. Callers can then treat this as /// a standard property. /// - public ExtendedPropertyDescriptor(ReflectPropertyDescriptor extenderInfo, Type receiverType, IExtenderProvider provider, Attribute[] attributes) + public ExtendedPropertyDescriptor(ReflectPropertyDescriptor extenderInfo, Type? receiverType, IExtenderProvider provider, Attribute[]? attributes) : base(extenderInfo, attributes) { Debug.Assert(extenderInfo != null, "ExtendedPropertyDescriptor must have extenderInfo"); Debug.Assert(provider != null, "ExtendedPropertyDescriptor must have provider"); - List attrList = new List(AttributeArray) + List attrList = new List(AttributeArray!) { ExtenderProvidedPropertyAttribute.Create(extenderInfo, receiverType, provider) }; @@ -46,17 +46,17 @@ namespace System.ComponentModel { Debug.Assert(extender != null, "The original PropertyDescriptor must be non-null"); - ExtenderProvidedPropertyAttribute attr = extender.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; + ExtenderProvidedPropertyAttribute? attr = extender.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; Debug.Assert(attr != null, "The original PropertyDescriptor does not have an ExtenderProvidedPropertyAttribute"); - ReflectPropertyDescriptor reflectDesc = attr.ExtenderProperty as ReflectPropertyDescriptor; + ReflectPropertyDescriptor? reflectDesc = attr.ExtenderProperty as ReflectPropertyDescriptor; Debug.Assert(reflectDesc != null, "The original PropertyDescriptor has an invalid ExtenderProperty"); _extenderInfo = reflectDesc; - _provider = attr.Provider; + _provider = attr.Provider!; } /// @@ -75,7 +75,7 @@ namespace System.ComponentModel /// /// Determines if the property can be written to. /// - public override bool IsReadOnly => Attributes[typeof(ReadOnlyAttribute)].Equals(ReadOnlyAttribute.Yes); + public override bool IsReadOnly => Attributes[typeof(ReadOnlyAttribute)]!.Equals(ReadOnlyAttribute.Yes); /// /// Retrieves the data type of the property. @@ -95,8 +95,8 @@ namespace System.ComponentModel if (!(Attributes[typeof(DisplayNameAttribute)] is DisplayNameAttribute displayNameAttr) || displayNameAttr.IsDefaultAttribute()) { - ISite site = GetSite(_provider); - string providerName = site?.Name; + ISite? site = GetSite(_provider); + string? providerName = site?.Name; if (providerName != null && providerName.Length > 0) { name = SR.Format(SR.MetaExtenderName, name, providerName); @@ -110,7 +110,7 @@ namespace System.ComponentModel /// Retrieves the value of the property for the given component. This will /// throw an exception if the component does not have this property. /// - public override object GetValue(object comp) => _extenderInfo.ExtenderGetValue(_provider, comp); + public override object? GetValue(object? comp) => _extenderInfo.ExtenderGetValue(_provider, comp); /// /// Resets the value of this property on comp to the default value. @@ -120,7 +120,7 @@ namespace System.ComponentModel /// /// Sets the value of this property on the given component. /// - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { _extenderInfo.ExtenderSetValue(_provider, component, value, this); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtenderProvidedPropertyAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtenderProvidedPropertyAttribute.cs index 2c5f58e6eb5..4db47e8d445 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtenderProvidedPropertyAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtenderProvidedPropertyAttribute.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel { @@ -15,7 +16,7 @@ namespace System.ComponentModel /// /// Creates a new ExtenderProvidedPropertyAttribute. /// - internal static ExtenderProvidedPropertyAttribute Create(PropertyDescriptor extenderProperty, Type receiverType, IExtenderProvider provider) + internal static ExtenderProvidedPropertyAttribute Create(PropertyDescriptor? extenderProperty, Type? receiverType, IExtenderProvider? provider) { return new ExtenderProvidedPropertyAttribute { @@ -35,19 +36,19 @@ namespace System.ComponentModel /// /// PropertyDescriptor of the property that is being provided. /// - public PropertyDescriptor ExtenderProperty { get; private set; } + public PropertyDescriptor? ExtenderProperty { get; private set; } /// /// Extender provider that is providing the property. /// - public IExtenderProvider Provider { get; private set; } + public IExtenderProvider? Provider { get; private set; } /// /// The type of object that can receive these properties. /// - public Type ReceiverType { get; private set; } + public Type? ReceiverType { get; private set; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { @@ -74,8 +75,8 @@ namespace System.ComponentModel } return other.ExtenderProperty.Equals(ExtenderProperty) - && other.Provider.Equals(Provider) - && other.ReceiverType.Equals(ReceiverType); + && other.Provider!.Equals(Provider) + && other.ReceiverType!.Equals(ReceiverType); } public override int GetHashCode() => base.GetHashCode(); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/GuidConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/GuidConverter.cs index 61d87530eed..4ca6225e69f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/GuidConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/GuidConverter.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the given source /// type to a globally unique identifier object using the context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -27,7 +27,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object to /// the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -35,7 +35,7 @@ namespace System.ComponentModel /// /// Converts the given object to a globally unique identifier object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -52,13 +52,13 @@ namespace System.ComponentModel /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor) && value is Guid) { - ConstructorInfo ctor = typeof(Guid).GetConstructor(new Type[] { typeof(string) }); + ConstructorInfo? ctor = typeof(Guid).GetConstructor(new Type[] { typeof(string) }); Debug.Assert(ctor != null, "Expected constructor to exist."); - return new InstanceDescriptor(ctor, new object[] { value.ToString() }); + return new InstanceDescriptor(ctor, new object?[] { value.ToString() }); } return base.ConvertTo(context, culture, value, destinationType); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/HandledEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/HandledEventHandler.cs index abf4316f3fa..73f2738d45e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/HandledEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/HandledEventHandler.cs @@ -3,5 +3,5 @@ namespace System.ComponentModel { - public delegate void HandledEventHandler(object sender, HandledEventArgs e); + public delegate void HandledEventHandler(object? sender, HandledEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingList.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingList.cs index 46acf46ecd9..37d1aabfe99 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingList.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingList.cs @@ -10,7 +10,7 @@ namespace System.ComponentModel { bool AllowNew { get; } - object AddNew(); + object? AddNew(); bool AllowEdit { get; } @@ -24,7 +24,7 @@ namespace System.ComponentModel bool IsSorted { get; } - PropertyDescriptor SortProperty { get; } + PropertyDescriptor? SortProperty { get; } ListSortDirection SortDirection { get; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs index 7b3f6ff760b..73b5035800f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel { void ApplySort(ListSortDescriptionCollection sorts); - string Filter { get; set; } + string? Filter { get; set; } ListSortDescriptionCollection SortDescriptions { get; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IComNativeDescriptorHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IComNativeDescriptorHandler.cs index 99d6e43eaf0..671061a3f8a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IComNativeDescriptorHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IComNativeDescriptorHandler.cs @@ -25,9 +25,9 @@ namespace System.ComponentModel EventDescriptorCollection GetEvents(object component); - EventDescriptorCollection GetEvents(object component, Attribute[] attributes); + EventDescriptorCollection GetEvents(object component, Attribute[]? attributes); - PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes); + PropertyDescriptorCollection GetProperties(object component, Attribute[]? attributes); object GetPropertyValue(object component, string propertyName, ref bool success); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs index 699ebc3d227..fd9b681c20e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs @@ -19,12 +19,12 @@ namespace System.ComponentModel /// /// Gets the class name of this object. /// - string GetClassName(); + string? GetClassName(); /// /// Gets the name of this object. /// - string GetComponentName(); + string? GetComponentName(); /// /// Gets a type converter for this object. @@ -36,19 +36,19 @@ namespace System.ComponentModel /// Gets the default event for this object. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - EventDescriptor GetDefaultEvent(); + EventDescriptor? GetDefaultEvent(); /// /// Gets the default property for this object. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - PropertyDescriptor GetDefaultProperty(); + PropertyDescriptor? GetDefaultProperty(); /// /// Gets an editor of the specified type for this object. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] - object GetEditor(Type editorBaseType); + object? GetEditor(Type editorBaseType); /// /// Gets the events for this instance of a component. @@ -60,7 +60,7 @@ namespace System.ComponentModel /// filter. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - EventDescriptorCollection GetEvents(Attribute[] attributes); + EventDescriptorCollection GetEvents(Attribute[]? attributes); /// /// Gets the properties for this instance of a component. @@ -72,11 +72,11 @@ namespace System.ComponentModel /// Gets the properties for this instance of a component using the attribute array as a filter. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - PropertyDescriptorCollection GetProperties(Attribute[] attributes); + PropertyDescriptorCollection GetProperties(Attribute[]? attributes); /// /// Gets the object that directly depends on this value being edited. /// - object GetPropertyOwner(PropertyDescriptor pd); + object? GetPropertyOwner(PropertyDescriptor pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/INestedSite.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/INestedSite.cs index 2ef11333f02..3964f0ea187 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/INestedSite.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/INestedSite.cs @@ -13,6 +13,6 @@ namespace System.ComponentModel /// Returns the full name of the component in this site in the format of <owner>.<component>. /// If this component's site has a null name, FullName also returns null. /// - string FullName { get; } + string? FullName { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs index e3b47c9911e..fb45a60626a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -75,7 +77,7 @@ namespace System.ComponentModel /// Override to test for equality. /// /// - public override bool Equals(object value) + public override bool Equals([NotNullWhen(true)] object? value) { if (value == this) { @@ -104,6 +106,6 @@ namespace System.ComponentModel /// /// Converts this attribute to a string. /// - public override string ToString() => TypeDescriptor.GetConverterTrimUnsafe(typeof(InheritanceLevel)).ConvertToString(InheritanceLevel); + public override string ToString() => TypeDescriptor.GetConverterTrimUnsafe(typeof(InheritanceLevel)).ConvertToString(InheritanceLevel)!; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs index 2ad8d8941c9..79ae3633528 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel public class InstallerTypeAttribute : Attribute { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - private readonly string _typeName; + private readonly string? _typeName; /// /// Initializes a new instance of the System.Windows.Forms.ComponentModel.InstallerTypeAttribute class. @@ -27,7 +27,7 @@ namespace System.ComponentModel _typeName = installerType.AssemblyQualifiedName; } - public InstallerTypeAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) + public InstallerTypeAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string? typeName) { _typeName = typeName; } @@ -36,9 +36,9 @@ namespace System.ComponentModel /// Gets the type of installer associated with this attribute. /// [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - public virtual Type InstallerType => Type.GetType(_typeName); + public virtual Type? InstallerType => Type.GetType(_typeName!); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs index 5e977cf36d6..68a566544d0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs @@ -18,6 +18,6 @@ namespace System.ComponentModel /// The object returned from this method must be an instance of the specified type, or null in which case the editor will do nothing. /// /// - public abstract object CreateInstance(ITypeDescriptorContext context, Type instanceType); + public abstract object? CreateInstance(ITypeDescriptorContext context, Type instanceType); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int16Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int16Converter.cs index 42289cd32dc..fe935b9a757 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int16Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int16Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return short.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((short)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int32Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int32Converter.cs index 746c45498b9..847b5a9c239 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int32Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int32Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return int.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((int)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int64Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int64Converter.cs index ae408d02b2f..6cdb153ceaa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int64Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Int64Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return long.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((long)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs index 76f81e2083e..2190f32299f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InterlockedBitVector32.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace System.ComponentModel @@ -43,7 +44,7 @@ namespace System.ComponentModel return previous == 0 ? 1 : previous << 1; } - public override bool Equals(object o) => o is InterlockedBitVector32 vector && _data == vector._data; + public override bool Equals([NotNullWhen(true)] object? o) => o is InterlockedBitVector32 vector && _data == vector._data; public override int GetHashCode() => base.GetHashCode(); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs index fa4649c916f..cd420c931a9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel /// Determines if the key retrieved by the method is valid /// for the specified type. /// - protected virtual bool IsKeyValid(string key, Type type) + protected virtual bool IsKeyValid(string? key, Type type) { if (key != null) { @@ -40,16 +40,16 @@ namespace System.ComponentModel /// /// Gets a license for the instance of the component and determines if it is valid. /// - public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions) + public override License? GetLicense(LicenseContext context, Type type, object? instance, bool allowExceptions) { - LicFileLicense lic = null; + LicFileLicense? lic = null; Debug.Assert(context != null, "No context provided!"); if (context != null) { if (context.UsageMode == LicenseUsageMode.Runtime) { - string key = context.GetSavedLicenseKey(type, null); + string? key = context.GetSavedLicenseKey(type, null); if (key != null && IsKeyValid(key, type)) { lic = new LicFileLicense(this, key); @@ -58,11 +58,11 @@ namespace System.ComponentModel if (lic == null) { - string modulePath = null; + string? modulePath = null; if (context != null) { - ITypeResolutionService resolver = (ITypeResolutionService)context.GetService(typeof(ITypeResolutionService)); + ITypeResolutionService? resolver = (ITypeResolutionService?)context.GetService(typeof(ITypeResolutionService)); if (resolver != null) { modulePath = resolver.GetPathOfAssembly(type.Assembly.GetName()); @@ -74,7 +74,7 @@ namespace System.ComponentModel modulePath = type.Module.FullyQualifiedName; } - string moduleDir = Path.GetDirectoryName(modulePath); + string? moduleDir = Path.GetDirectoryName(modulePath); string licenseFile = moduleDir + "\\" + type.FullName + ".lic"; Debug.WriteLine($"Looking for license in: {licenseFile}"); @@ -82,7 +82,7 @@ namespace System.ComponentModel { Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read); StreamReader sr = new StreamReader(licStream); - string s = sr.ReadLine(); + string? s = sr.ReadLine(); sr.Close(); if (IsKeyValid(s, type)) { @@ -92,7 +92,7 @@ namespace System.ComponentModel if (lic != null) { - context.SetSavedLicenseKey(type, lic.LicenseKey); + context!.SetSavedLicenseKey(type, lic.LicenseKey); } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseContext.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseContext.cs index ace379bf950..a337a450c79 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseContext.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseContext.cs @@ -19,12 +19,12 @@ namespace System.ComponentModel /// When overridden in a derived class, gets a saved license /// key for the specified type, from the specified resource assembly. /// - public virtual string GetSavedLicenseKey(Type type, Assembly resourceAssembly) => null; + public virtual string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) => null; /// /// When overridden in a derived class, will return an object that implements the asked for service. /// - public virtual object GetService(Type type) => null; + public virtual object? GetService(Type type) => null; /// /// When overridden in a derived class, sets a license key for the specified type. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs index ce226a26677..715ba7b9a8e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs @@ -16,27 +16,27 @@ namespace System.ComponentModel { private const int LicenseHResult = unchecked((int)0x80131901); - private readonly object _instance; + private readonly object? _instance; /// /// Initializes a new instance of the class for the /// specified type. /// - public LicenseException(Type type) : this(type, null, SR.Format(SR.LicExceptionTypeOnly, type?.FullName)) + public LicenseException(Type? type) : this(type, null, SR.Format(SR.LicExceptionTypeOnly, type?.FullName)) { } /// /// Initializes a new instance of the class for the /// specified type and instance. /// - public LicenseException(Type type, object instance) : this(type, null, SR.Format(SR.LicExceptionTypeAndInstance, type?.FullName, instance?.GetType().FullName)) + public LicenseException(Type? type, object? instance) : this(type, null, SR.Format(SR.LicExceptionTypeAndInstance, type?.FullName, instance?.GetType().FullName)) { } /// /// Initializes a new instance of the class for the /// specified type and instance with the specified message. /// - public LicenseException(Type type, object instance, string message) : base(message) + public LicenseException(Type? type, object? instance, string? message) : base(message) { LicensedType = type; _instance = instance; @@ -47,7 +47,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class for the /// specified innerException, type and instance with the specified message. /// - public LicenseException(Type type, object instance, string message, Exception innerException) : base(message, innerException) + public LicenseException(Type? type, object? instance, string? message, Exception? innerException) : base(message, innerException) { LicensedType = type; _instance = instance; @@ -64,7 +64,7 @@ namespace System.ComponentModel /// /// Gets the type of the component that was not granted a license. /// - public Type LicensedType { get; } + public Type? LicensedType { get; } /// /// Need this since Exception implements ISerializable. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs index 113b4a60d0e..94476ab5096 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs @@ -16,7 +16,7 @@ namespace System.ComponentModel private sealed class CLRLicenseContext : LicenseContext { private readonly Type _type; - private string _key; + private string? _key; private CLRLicenseContext(Type type, LicenseUsageMode mode) { @@ -29,7 +29,7 @@ namespace System.ComponentModel return new CLRLicenseContext(type, LicenseUsageMode.Designtime); } - public static CLRLicenseContext CreateRuntimeContext(Type type, string key) + public static CLRLicenseContext CreateRuntimeContext(Type type, string? key) { var cxt = new CLRLicenseContext(type, LicenseUsageMode.Runtime); if (key != null) @@ -42,7 +42,7 @@ namespace System.ComponentModel public override LicenseUsageMode UsageMode { get; } - public override string GetSavedLicenseKey(Type type, Assembly resourceAssembly) + public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) { if (type == _type) { @@ -52,7 +52,7 @@ namespace System.ComponentModel return null; } - public override void SetSavedLicenseKey(Type type, string key) + public override void SetSavedLicenseKey(Type type, string? key) { if (type == _type) { @@ -70,11 +70,11 @@ namespace System.ComponentModel public override LicenseUsageMode UsageMode => LicenseUsageMode.Designtime; - public override string GetSavedLicenseKey(Type type, Assembly resourceAssembly) => null; + public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) => null; public override void SetSavedLicenseKey(Type type, string key) { - _savedLicenseKeys[type.AssemblyQualifiedName] = key; + _savedLicenseKeys[type.AssemblyQualifiedName!] = key; } } @@ -87,8 +87,8 @@ namespace System.ComponentModel public static bool ValidateAndRetrieveLicenseDetails( LicenseContext context, Type type, - out License license, - out string licenseKey) + out License? license, + out string? licenseKey) { if (context == null) { @@ -107,7 +107,7 @@ namespace System.ComponentModel // The CLR invokes this when instantiating an unmanaged COM // object. The purpose is to decide which IClassFactory method to // use. - public static LicenseContext GetCurrentContextInfo(Type type, out bool isDesignTime, out string key) + public static LicenseContext GetCurrentContextInfo(Type type, out bool isDesignTime, out string? key) { LicenseContext licContext = LicenseManager.CurrentContext; isDesignTime = licContext.UsageMode == LicenseUsageMode.Designtime; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs index d3acf78aff5..31556e2ce55 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs @@ -19,10 +19,10 @@ namespace System.ComponentModel { private static readonly object s_selfLock = new object(); - private static volatile LicenseContext s_context; - private static object s_contextLockHolder; - private static volatile Hashtable s_providers; - private static volatile Hashtable s_providerInstances; + private static volatile LicenseContext? s_context; + private static object? s_contextLockHolder; + private static volatile Hashtable? s_providers; + private static volatile Hashtable? s_providerInstances; private static readonly object s_internalSyncObject = new object(); // not creatable... @@ -85,7 +85,7 @@ namespace System.ComponentModel /// Caches the provider, both in the instance cache, and the type /// cache. /// - private static void CacheProvider(Type type, LicenseProvider provider) + private static void CacheProvider(Type type, LicenseProvider? provider) { if (s_providers == null) { @@ -119,7 +119,7 @@ namespace System.ComponentModel /// as the context in which the licensed instance can be used. /// [UnsupportedOSPlatform("browser")] - public static object CreateWithContext( + public static object? CreateWithContext( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, LicenseContext creationContext) { @@ -132,12 +132,12 @@ namespace System.ComponentModel /// instance can be used. /// [UnsupportedOSPlatform("browser")] - public static object CreateWithContext( + public static object? CreateWithContext( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, LicenseContext creationContext, object[] args) { - object created = null; + object? created = null; lock (s_internalSyncObject) { @@ -152,7 +152,7 @@ namespace System.ComponentModel } catch (TargetInvocationException e) { - throw e.InnerException; + throw e.InnerException!; } } finally @@ -184,9 +184,9 @@ namespace System.ComponentModel /// Retrieves a cached instance of the provider associated with the /// specified type. /// - private static LicenseProvider GetCachedProvider(Type type) + private static LicenseProvider? GetCachedProvider(Type type) { - return (LicenseProvider)s_providers?[type]; + return (LicenseProvider?)s_providers?[type]; } @@ -194,10 +194,10 @@ namespace System.ComponentModel /// Retrieves a cached instance of the provider of the specified /// type. /// - private static LicenseProvider GetCachedProviderInstance(Type providerType) + private static LicenseProvider? GetCachedProviderInstance(Type providerType) { Debug.Assert(providerType != null, "Type cannot ever be null"); - return (LicenseProvider)s_providerInstances?[providerType]; + return (LicenseProvider?)s_providerInstances?[providerType]; } /// @@ -206,7 +206,7 @@ namespace System.ComponentModel public static bool IsLicensed(Type type) { Debug.Assert(type != null, "IsValid Type cannot ever be null"); - bool value = ValidateInternal(type, null, false, out License license); + bool value = ValidateInternal(type, null, false, out License? license); if (license != null) { license.Dispose(); @@ -221,7 +221,7 @@ namespace System.ComponentModel public static bool IsValid(Type type) { Debug.Assert(type != null, "IsValid Type cannot ever be null"); - bool value = ValidateInternal(type, null, false, out License license); + bool value = ValidateInternal(type, null, false, out License? license); if (license != null) { license.Dispose(); @@ -234,7 +234,7 @@ namespace System.ComponentModel /// Determines if a valid license can be granted for the /// specified instance of the type. This method creates a valid . /// - public static bool IsValid(Type type, object instance, out License license) + public static bool IsValid(Type type, object? instance, out License? license) { return ValidateInternal(type, instance, false, out license); } @@ -266,14 +266,14 @@ namespace System.ComponentModel /// /// Internal validation helper. /// - private static bool ValidateInternal(Type type, object instance, bool allowExceptions, out License license) + private static bool ValidateInternal(Type type, object? instance, bool allowExceptions, out License? license) { return ValidateInternalRecursive(CurrentContext, type, instance, allowExceptions, out license, - out string licenseKey); + out string? licenseKey); } @@ -282,18 +282,18 @@ namespace System.ComponentModel /// give an instance, we need another helper method to walk up /// the chain... /// - private static bool ValidateInternalRecursive(LicenseContext context, Type type, object instance, bool allowExceptions, out License license, out string licenseKey) + private static bool ValidateInternalRecursive(LicenseContext context, Type type, object? instance, bool allowExceptions, out License? license, out string? licenseKey) { - LicenseProvider provider = GetCachedProvider(type); + LicenseProvider? provider = GetCachedProvider(type); if (provider == null && !GetCachedNoLicenseProvider(type)) { // NOTE : Must look directly at the class, we want no inheritance. - LicenseProviderAttribute attr = (LicenseProviderAttribute)Attribute.GetCustomAttribute(type, typeof(LicenseProviderAttribute), false); + LicenseProviderAttribute? attr = (LicenseProviderAttribute?)Attribute.GetCustomAttribute(type, typeof(LicenseProviderAttribute), false); if (attr != null) { - Type providerType = attr.LicenseProvider; - provider = GetCachedProviderInstance(providerType) ?? (LicenseProvider)Activator.CreateInstance(providerType); + Type providerType = attr.LicenseProvider!; + provider = GetCachedProviderInstance(providerType) ?? (LicenseProvider)Activator.CreateInstance(providerType)!; } CacheProvider(type, provider); @@ -323,7 +323,7 @@ namespace System.ComponentModel // from more than one provider. if (isValid && instance == null) { - Type baseType = type.BaseType; + Type? baseType = type.BaseType; if (baseType != typeof(object) && baseType != null) { if (license != null) @@ -331,7 +331,7 @@ namespace System.ComponentModel license.Dispose(); license = null; } - string temp; + string? temp; isValid = ValidateInternalRecursive(context, baseType, null, allowExceptions, out license, out temp); if (license != null) { @@ -350,7 +350,7 @@ namespace System.ComponentModel /// public static void Validate(Type type) { - if (!ValidateInternal(type, null, true, out License lic)) + if (!ValidateInternal(type, null, true, out License? lic)) { throw new LicenseException(type); } @@ -366,9 +366,9 @@ namespace System.ComponentModel /// /// Determines if a license can be granted for the instance of the specified type. /// - public static License Validate(Type type, object instance) + public static License? Validate(Type type, object? instance) { - if (!ValidateInternal(type, instance, true, out License lic)) + if (!ValidateInternal(type, instance, true, out License? lic)) { throw new LicenseException(type, instance); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProvider.cs index a13639069f0..6ce5a73329a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProvider.cs @@ -12,6 +12,6 @@ namespace System.ComponentModel /// When overridden in a derived class, gets a license for an or /// of component. /// - public abstract License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions); + public abstract License? GetLicense(LicenseContext context, Type type, object? instance, bool allowExceptions); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs index 6b7fd361594..f17207d3c73 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs @@ -18,15 +18,15 @@ namespace System.ComponentModel public static readonly LicenseProviderAttribute Default = new LicenseProviderAttribute(); [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - private Type _licenseProviderType; + private Type? _licenseProviderType; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - private readonly string _licenseProviderName; + private readonly string? _licenseProviderName; /// /// Initializes a new instance of the class without a license /// provider. /// - public LicenseProviderAttribute() : this((string)null) + public LicenseProviderAttribute() : this((string?)null) { } @@ -34,7 +34,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with /// the specified type. /// - public LicenseProviderAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) + public LicenseProviderAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string? typeName) { _licenseProviderName = typeName; } @@ -52,7 +52,7 @@ namespace System.ComponentModel /// Gets the license provider to use with the associated class. /// [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - public Type LicenseProvider + public Type? LicenseProvider { get { @@ -75,7 +75,7 @@ namespace System.ComponentModel { get { - string typeName = _licenseProviderName; + string? typeName = _licenseProviderName; if (typeName == null && _licenseProviderType != null) { @@ -85,11 +85,11 @@ namespace System.ComponentModel } } - public override bool Equals(object value) + public override bool Equals([NotNullWhen(true)] object? value) { if (value is LicenseProviderAttribute && value != null) { - Type type = ((LicenseProviderAttribute)value).LicenseProvider; + Type? type = ((LicenseProviderAttribute)value).LicenseProvider; if (type == LicenseProvider) { return true; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListBindableAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListBindableAttribute.cs index f78f94d4eea..14aa6aca196 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListBindableAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListBindableAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { [AttributeUsage(AttributeTargets.All)] @@ -27,7 +29,7 @@ namespace System.ComponentModel public bool ListBindable { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventArgs.cs index 79f46861c91..c06b3dbfc4a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventArgs.cs @@ -12,13 +12,13 @@ namespace System.ComponentModel { } - public ListChangedEventArgs(ListChangedType listChangedType, int newIndex, PropertyDescriptor propDesc) : this(listChangedType, newIndex) + public ListChangedEventArgs(ListChangedType listChangedType, int newIndex, PropertyDescriptor? propDesc) : this(listChangedType, newIndex) { PropertyDescriptor = propDesc; OldIndex = newIndex; } - public ListChangedEventArgs(ListChangedType listChangedType, PropertyDescriptor propDesc) + public ListChangedEventArgs(ListChangedType listChangedType, PropertyDescriptor? propDesc) { ListChangedType = listChangedType; PropertyDescriptor = propDesc; @@ -37,6 +37,6 @@ namespace System.ComponentModel public int OldIndex { get; } - public PropertyDescriptor PropertyDescriptor { get; } + public PropertyDescriptor? PropertyDescriptor { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventHandler.cs index a4dd61da40d..3eb78034617 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListChangedEventHandler.cs @@ -3,5 +3,5 @@ namespace System.ComponentModel { - public delegate void ListChangedEventHandler(object sender, ListChangedEventArgs e); + public delegate void ListChangedEventHandler(object? sender, ListChangedEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescription.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescription.cs index bf01a0464e2..030fc749215 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescription.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescription.cs @@ -5,13 +5,13 @@ namespace System.ComponentModel { public class ListSortDescription { - public ListSortDescription(PropertyDescriptor property, ListSortDirection direction) + public ListSortDescription(PropertyDescriptor? property, ListSortDirection direction) { PropertyDescriptor = property; SortDirection = direction; } - public PropertyDescriptor PropertyDescriptor { get; set; } + public PropertyDescriptor? PropertyDescriptor { get; set; } public ListSortDirection SortDirection { get; set; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescriptionCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescriptionCollection.cs index b61e04cd161..8eab5fd0dca 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescriptionCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ListSortDescriptionCollection.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel { @@ -13,7 +14,7 @@ namespace System.ComponentModel { } - public ListSortDescriptionCollection(ListSortDescription[] sorts) + public ListSortDescriptionCollection(ListSortDescription?[]? sorts) { if (sorts != null) { @@ -24,9 +25,9 @@ namespace System.ComponentModel } } - public ListSortDescription this[int index] + public ListSortDescription? this[int index] { - get => (ListSortDescription)_sorts[index]; + get => (ListSortDescription?)_sorts[index]; set => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); } @@ -36,23 +37,23 @@ namespace System.ComponentModel bool IList.IsReadOnly => true; - object IList.this[int index] + object? IList.this[int index] { get => this[index]; set => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); } - int IList.Add(object value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); + int IList.Add(object? value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); void IList.Clear() => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); - public bool Contains(object value) => ((IList)_sorts).Contains(value); + public bool Contains(object? value) => ((IList)_sorts).Contains(value); - public int IndexOf(object value) => ((IList)_sorts).IndexOf(value); + public int IndexOf(object? value) => ((IList)_sorts).IndexOf(value); - void IList.Insert(int index, object value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); + void IList.Insert(int index, object? value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); - void IList.Remove(object value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); + void IList.Remove(object? value) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); void IList.RemoveAt(int index) => throw new InvalidOperationException(SR.CantModifyListSortDescriptionCollection); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LookupBindingPropertiesAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LookupBindingPropertiesAttribute.cs index 4cc7ba48886..2b204889754 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LookupBindingPropertiesAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LookupBindingPropertiesAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -37,25 +39,25 @@ namespace System.ComponentModel /// Gets the name of the data source property for the component this attribute is /// bound to. /// - public string DataSource { get; } + public string? DataSource { get; } /// /// Gets the name of the display member property for the component this attribute is /// bound to. /// - public string DisplayMember { get; } + public string? DisplayMember { get; } /// /// Gets the name of the value member property for the component this attribute is /// bound to. /// - public string ValueMember { get; } + public string? ValueMember { get; } /// /// Gets the name of the member property for the component this attribute is /// bound to. /// - public string LookupMember { get; } + public string? LookupMember { get; } /// /// Specifies the default value for the , which is . This @@ -63,7 +65,7 @@ namespace System.ComponentModel /// public static readonly LookupBindingPropertiesAttribute Default = new LookupBindingPropertiesAttribute(); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return obj is LookupBindingPropertiesAttribute other && other.DataSource == DataSource && diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MarshalByValueComponent.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MarshalByValueComponent.cs index 33db604c416..40926230627 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MarshalByValueComponent.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MarshalByValueComponent.cs @@ -21,8 +21,8 @@ namespace System.ComponentModel /// private static readonly object s_eventDisposed = new object(); - private ISite _site; - private EventHandlerList _events; + private ISite? _site; + private EventHandlerList? _events; /// /// Initializes a new instance of the class. @@ -36,7 +36,7 @@ namespace System.ComponentModel /// /// Adds an event handler to listen to the Disposed event on the component. /// - public event EventHandler Disposed + public event EventHandler? Disposed { add => Events.AddHandler(s_eventDisposed, value); remove => Events.RemoveHandler(s_eventDisposed, value); @@ -51,7 +51,7 @@ namespace System.ComponentModel /// Gets or sets the site of the component. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public virtual ISite Site + public virtual ISite? Site { get => _site; set => _site = value; @@ -130,7 +130,7 @@ namespace System.ComponentModel lock (this) { _site?.Container?.Remove(this); - ((EventHandler)_events?[s_eventDisposed])?.Invoke(this, EventArgs.Empty); + ((EventHandler?)_events?[s_eventDisposed])?.Invoke(this, EventArgs.Empty); } } } @@ -139,12 +139,12 @@ namespace System.ComponentModel /// Gets the container for the component. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public virtual IContainer Container => _site?.Container; + public virtual IContainer? Container => _site?.Container; /// /// Gets the implementer of the . /// - public virtual object GetService(Type service) => _site?.GetService(service); + public virtual object? GetService(Type service) => _site?.GetService(service); /// /// Gets a value indicating whether the component is currently in design mode. @@ -157,9 +157,9 @@ namespace System.ComponentModel /// overridden. For /// internal use only. /// - public override string ToString() + public override string? ToString() { - ISite s = _site; + ISite? s = _site; if (s != null) return s.Name + " [" + GetType().FullName + "]"; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs index 5314ebaa3d6..77a965c6648 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs @@ -180,7 +180,7 @@ namespace System.ComponentModel /// 'culture' is used to set the separator characters to the corresponding locale character; if null, the current /// culture is used. /// - public MaskedTextProvider(string mask, CultureInfo culture) + public MaskedTextProvider(string mask, CultureInfo? culture) : this(mask, culture, DEFAULT_ALLOW_PROMPT, DEFAULT_PROMPT_CHAR, NULL_PASSWORD_CHAR, false) { } @@ -191,7 +191,7 @@ namespace System.ComponentModel /// culture is used. /// 'restrictToAscii' specifies whether the input characters should be restricted to ASCII characters only. /// - public MaskedTextProvider(string mask, CultureInfo culture, bool restrictToAscii) + public MaskedTextProvider(string mask, CultureInfo? culture, bool restrictToAscii) : this(mask, culture, DEFAULT_ALLOW_PROMPT, DEFAULT_PROMPT_CHAR, NULL_PASSWORD_CHAR, restrictToAscii) { } @@ -211,7 +211,7 @@ namespace System.ComponentModel /// 'passwordChar' specifies the character to be used in the password string. /// 'allowPromptAsInput' specifies whether the prompt character should be accepted as a valid input or not. /// - public MaskedTextProvider(string mask, CultureInfo culture, char passwordChar, bool allowPromptAsInput) + public MaskedTextProvider(string mask, CultureInfo? culture, char passwordChar, bool allowPromptAsInput) : this(mask, culture, allowPromptAsInput, DEFAULT_PROMPT_CHAR, passwordChar, false) { } @@ -225,7 +225,7 @@ namespace System.ComponentModel /// 'passwordChar' specifies the character to be used in the password string. /// 'restrictToAscii' specifies whether the input characters should be restricted to ASCII characters only. /// - public MaskedTextProvider(string mask, CultureInfo culture, bool allowPromptAsInput, char promptChar, char passwordChar, bool restrictToAscii) + public MaskedTextProvider(string mask, CultureInfo? culture, bool allowPromptAsInput, char promptChar, char passwordChar, bool restrictToAscii) { if (string.IsNullOrEmpty(mask)) { @@ -300,6 +300,8 @@ namespace System.ComponentModel /// Initializes the test string according to the mask and populates the character descriptor table /// (stringDescriptor). /// + [MemberNotNull(nameof(_testString))] + [MemberNotNull(nameof(_stringDescriptor))] private void Initialize() { _testString = new StringBuilder(); @@ -497,7 +499,7 @@ namespace System.ComponentModel AsciiOnly }; - clonedProvider = Activator.CreateInstance(providerType, parameters) as MaskedTextProvider; + clonedProvider = (Activator.CreateInstance(providerType, parameters) as MaskedTextProvider)!; } clonedProvider.ResetOnPrompt = false; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs index ade2a381a3c..e62e2647cfb 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs @@ -15,17 +15,17 @@ namespace System.ComponentModel /// public abstract class MemberDescriptor { - private readonly string _name; + private readonly string? _name; private readonly string _displayName; private readonly int _nameHash; - private AttributeCollection _attributeCollection; - private Attribute[] _attributes; - private Attribute[] _originalAttributes; + private AttributeCollection? _attributeCollection; + private Attribute[]? _attributes; + private Attribute[]? _originalAttributes; private bool _attributesFiltered; private bool _attributesFilled; private int _metadataVersion; - private string _category; - private string _description; + private string? _category; + private string? _description; private readonly object _lockCookie = new object(); /// @@ -38,7 +38,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class with the specified and array. /// - protected MemberDescriptor(string name, Attribute[] attributes) + protected MemberDescriptor(string name, Attribute[]? attributes) { if (name == null) { @@ -89,7 +89,7 @@ namespace System.ComponentModel /// and the attributes /// in both the old and the array. /// - protected MemberDescriptor(MemberDescriptor oldMemberDescriptor, Attribute[] newAttributes) + protected MemberDescriptor(MemberDescriptor oldMemberDescriptor, Attribute[]? newAttributes) { if (oldMemberDescriptor == null) { @@ -128,7 +128,7 @@ namespace System.ComponentModel /// /// Gets or sets an array of attributes. /// - protected virtual Attribute[] AttributeArray + protected virtual Attribute[]? AttributeArray { get { @@ -156,7 +156,7 @@ namespace System.ComponentModel get { CheckAttributesValid(); - AttributeCollection attrs = _attributeCollection; + AttributeCollection? attrs = _attributeCollection; if (attrs == null) { lock (_lockCookie) @@ -173,19 +173,19 @@ namespace System.ComponentModel /// Gets the name of the category that the member belongs to, as specified /// in the . /// - public virtual string Category => _category ?? (_category = ((CategoryAttribute)Attributes[typeof(CategoryAttribute)]).Category); + public virtual string Category => _category ?? (_category = ((CategoryAttribute)Attributes[typeof(CategoryAttribute)]!).Category); /// /// Gets the description of the member as specified in the . /// public virtual string Description => _description ?? - (_description = ((DescriptionAttribute)Attributes[typeof(DescriptionAttribute)]).Description); + (_description = ((DescriptionAttribute)Attributes[typeof(DescriptionAttribute)]!).Description); /// /// Gets a value indicating whether the member is browsable as specified in the /// . /// - public virtual bool IsBrowsable => ((BrowsableAttribute)Attributes[typeof(BrowsableAttribute)]).Browsable; + public virtual bool IsBrowsable => ((BrowsableAttribute)Attributes[typeof(BrowsableAttribute)]!).Browsable; /// /// Gets the name of the member. @@ -249,7 +249,7 @@ namespace System.ComponentModel /// Compares this instance to the specified to see if they are equivalent. /// NOTE: If you make a change here, you likely need to change GetHashCode() as well. /// - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (this == obj) { @@ -275,13 +275,13 @@ namespace System.ComponentModel } if ((mdObj._category == null) != (_category == null) || - (_category != null && !mdObj._category.Equals(_category))) + (_category != null && !mdObj._category!.Equals(_category))) { return false; } if ((mdObj._description == null) != (_description == null) || - (_description != null && !mdObj._description.Equals(_description))) + (_description != null && !mdObj._description!.Equals(_description))) { return false; } @@ -295,7 +295,7 @@ namespace System.ComponentModel if (_attributes != null) { - if (_attributes.Length != mdObj._attributes.Length) + if (_attributes.Length != mdObj._attributes!.Length) { return false; } @@ -351,7 +351,7 @@ namespace System.ComponentModel } else { - list = new List(_attributes); + list = new List(_attributes!); } var map = new Dictionary(); @@ -359,7 +359,7 @@ namespace System.ComponentModel for (int i = 0; i < list.Count;) { int savedIndex = -1; - object typeId = list[i]?.TypeId; + object? typeId = list[i]?.TypeId; if (typeId == null) { list.RemoveAt(i); @@ -393,7 +393,7 @@ namespace System.ComponentModel /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", Justification = "This method only looks for public methods by hard-coding publicOnly=true")] - protected static MethodInfo FindMethod( + protected static MethodInfo? FindMethod( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type componentClass, string name, Type[] args, @@ -405,7 +405,7 @@ namespace System.ComponentModel /// /// Finds the given method through reflection. /// - protected static MethodInfo FindMethod( + protected static MethodInfo? FindMethod( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type componentClass, string name, Type[] args, @@ -417,7 +417,7 @@ namespace System.ComponentModel throw new ArgumentNullException(nameof(componentClass)); } - MethodInfo result = null; + MethodInfo? result = null; if (publicOnly) { result = componentClass.GetMethod(name, args); @@ -445,7 +445,7 @@ namespace System.ComponentModel /// someone associated another object with this instance, or if the instance is a /// custom type descriptor, GetInvocationTarget may return a different value. /// - protected virtual object GetInvocationTarget(Type type, object instance) + protected virtual object? GetInvocationTarget(Type type, object instance) { if (type == null) { @@ -463,7 +463,7 @@ namespace System.ComponentModel /// /// Gets a component site for the given component. /// - protected static ISite GetSite(object component) => (component as IComponent)?.Site; + protected static ISite? GetSite(object? component) => (component as IComponent)?.Site; [Obsolete("This method has been deprecated. Use GetInvocationTarget instead. https://go.microsoft.com/fwlink/?linkid=14202")] protected static object GetInvokee(Type componentClass, object component) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs index 65db30246f3..656191ebfaf 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs @@ -14,7 +14,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -34,7 +34,7 @@ namespace System.ComponentModel /// parameter using the specified context and attributes. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { return null; } @@ -42,6 +42,6 @@ namespace System.ComponentModel /// /// Gets a value indicating whether this object supports properties. /// - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => false; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => false; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs index dee44cf54d8..87728cf2c6b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs @@ -35,11 +35,11 @@ namespace System.ComponentModel /// INestedSite and calls FullName, or ISite.Name if there is no nested site. /// If neither is available, this returns null. /// - protected virtual string OwnerName + protected virtual string? OwnerName { get { - string ownerName = null; + string? ownerName = null; if (Owner != null && Owner.Site != null) { if (Owner.Site is INestedSite nestedOwnerSite) @@ -59,7 +59,7 @@ namespace System.ComponentModel /// /// Creates a site for the component within the container. /// - protected override ISite CreateSite(IComponent component, string name) + protected override ISite CreateSite(IComponent component, string? name) { if (component == null) { @@ -80,7 +80,7 @@ namespace System.ComponentModel base.Dispose(disposing); } - protected override object GetService(Type service) + protected override object? GetService(Type service) { if (service == typeof(INestedContainer)) { @@ -95,7 +95,7 @@ namespace System.ComponentModel /// /// Called when our owning component is destroyed. /// - private void OnOwnerDisposed(object sender, EventArgs e) => Dispose(); + private void OnOwnerDisposed(object? sender, EventArgs e) => Dispose(); /// /// Simple site implementation. We do some special processing to name the site, but @@ -103,9 +103,9 @@ namespace System.ComponentModel /// private sealed class Site : INestedSite { - private string _name; + private string? _name; - internal Site(IComponent component, NestedContainer container, string name) + internal Site(IComponent component, NestedContainer container, string? name) { Component = component; Container = container; @@ -118,7 +118,7 @@ namespace System.ComponentModel // The container in which the component is sited. public IContainer Container { get; } - public object GetService(Type service) + public object? GetService(Type service) { return ((service == typeof(ISite)) ? this : ((NestedContainer)Container).GetService(service)); } @@ -137,13 +137,13 @@ namespace System.ComponentModel } } - public string FullName + public string? FullName { get { if (_name != null) { - string ownerName = ((NestedContainer)Container).OwnerName; + string? ownerName = ((NestedContainer)Container).OwnerName; string childName = _name; if (ownerName != null) { @@ -158,7 +158,7 @@ namespace System.ComponentModel } // The name of the component. - public string Name + public string? Name { get => _name; [RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered to validate the name.")] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs index 08a077b037d..e0a06176054 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs @@ -25,7 +25,7 @@ namespace System.ComponentModel { NullableType = type; - UnderlyingType = Nullable.GetUnderlyingType(type); + UnderlyingType = Nullable.GetUnderlyingType(type)!; if (UnderlyingType == null) { throw new ArgumentException(SR.NullableConverterBadCtorArg, nameof(type)); @@ -38,7 +38,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the /// given source type to the underlying simple type or a null. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == UnderlyingType) { @@ -55,7 +55,7 @@ namespace System.ComponentModel /// /// Converts the given value to the converter's underlying simple type or a null. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { if (value == null || value.GetType() == UnderlyingType) { @@ -76,7 +76,7 @@ namespace System.ComponentModel /// /// Gets a value indicating whether this converter can convert a value object to the destination type. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (destinationType == UnderlyingType) { @@ -97,7 +97,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -112,7 +112,7 @@ namespace System.ComponentModel { ConstructorInfo ci = (ConstructorInfo)NullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor); Debug.Assert(ci != null, "Couldn't find constructor"); - return new InstanceDescriptor(ci, new object[] { value }, true); + return new InstanceDescriptor(ci, new object?[] { value }, true); } else if (value == null) { @@ -132,11 +132,11 @@ namespace System.ComponentModel /// /// - public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + public override object? CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { if (UnderlyingTypeConverter != null) { - object instance = UnderlyingTypeConverter.CreateInstance(context, propertyValues); + object? instance = UnderlyingTypeConverter.CreateInstance(context, propertyValues); return instance; } @@ -148,7 +148,7 @@ namespace System.ComponentModel /// to create a new value, /// using the specified context. /// - public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) { if (UnderlyingTypeConverter != null) { @@ -163,7 +163,7 @@ namespace System.ComponentModel /// parameter using the specified context and attributes. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { if (UnderlyingTypeConverter != null) { @@ -177,7 +177,7 @@ namespace System.ComponentModel /// /// Gets a value indicating whether this object supports properties using the specified context. /// - public override bool GetPropertiesSupported(ITypeDescriptorContext context) + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) { if (UnderlyingTypeConverter != null) { @@ -190,15 +190,15 @@ namespace System.ComponentModel /// /// Gets a collection of standard values for the data type this type converter is designed for. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context) { if (UnderlyingTypeConverter != null) { - StandardValuesCollection values = UnderlyingTypeConverter.GetStandardValues(context); + StandardValuesCollection? values = UnderlyingTypeConverter.GetStandardValues(context); if (GetStandardValuesSupported(context) && values != null) { // Create a set of standard values around nullable instances. - object[] wrappedValues = new object[values.Count + 1]; + object?[] wrappedValues = new object[values.Count + 1]; int idx = 0; wrappedValues[idx++] = null; @@ -219,7 +219,7 @@ namespace System.ComponentModel /// is an exclusive /// list of possible values, using the specified context. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) { if (UnderlyingTypeConverter != null) { @@ -233,7 +233,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this object supports a standard set of values that can /// be picked from a list using the specified context. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) { if (UnderlyingTypeConverter != null) { @@ -246,11 +246,11 @@ namespace System.ComponentModel /// /// Gets a value indicating whether the given value object is valid for this type. /// - public override bool IsValid(ITypeDescriptorContext context, object value) + public override bool IsValid(ITypeDescriptorContext? context, object value) { if (UnderlyingTypeConverter != null) { - object unwrappedValue = value; + object? unwrappedValue = value; if (unwrappedValue == null) { return true; // null is valid for nullable. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PasswordPropertyTextAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PasswordPropertyTextAttribute.cs index 521410fa0e1..0299f4ad471 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PasswordPropertyTextAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PasswordPropertyTextAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -52,7 +54,7 @@ namespace System.ComponentModel /// /// Overload for object equality /// - public override bool Equals(object o) + public override bool Equals([NotNullWhen(true)] object? o) { if (o is PasswordPropertyTextAttribute) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 92da2abeffa..9ffaaa702da 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -14,17 +14,17 @@ namespace System.ComponentModel { internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered."; - private TypeConverter _converter; - private Hashtable _valueChangedHandlers; - private object[] _editors; - private Type[] _editorTypes; + private TypeConverter? _converter; + private Hashtable? _valueChangedHandlers; + private object?[]? _editors; + private Type[]? _editorTypes; private int _editorCount; /// /// Initializes a new instance of the class with the specified name and /// attributes. /// - protected PropertyDescriptor(string name, Attribute[] attrs) : base(name, attrs) + protected PropertyDescriptor(string name, Attribute[]? attrs) : base(name, attrs) { } @@ -44,7 +44,7 @@ namespace System.ComponentModel /// array. /// /// - protected PropertyDescriptor(MemberDescriptor descr, Attribute[] attrs) : base(descr, attrs) + protected PropertyDescriptor(MemberDescriptor descr, Attribute[]? attrs) : base(descr, attrs) { } @@ -68,13 +68,13 @@ namespace System.ComponentModel if (_converter == null) { - TypeConverterAttribute attr = (TypeConverterAttribute)attrs[typeof(TypeConverterAttribute)]; + TypeConverterAttribute attr = (TypeConverterAttribute)attrs[typeof(TypeConverterAttribute)]!; if (attr.ConverterTypeName != null && attr.ConverterTypeName.Length > 0) { - Type converterType = GetTypeFromName(attr.ConverterTypeName); + Type? converterType = GetTypeFromName(attr.ConverterTypeName); if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) { - _converter = (TypeConverter)CreateInstance(converterType); + _converter = (TypeConverter)CreateInstance(converterType)!; } } @@ -108,7 +108,7 @@ namespace System.ComponentModel { get { - DesignerSerializationVisibilityAttribute attr = (DesignerSerializationVisibilityAttribute)Attributes[typeof(DesignerSerializationVisibilityAttribute)]; + DesignerSerializationVisibilityAttribute attr = (DesignerSerializationVisibilityAttribute)Attributes[typeof(DesignerSerializationVisibilityAttribute)]!; return attr.Visibility; } } @@ -137,7 +137,7 @@ namespace System.ComponentModel _valueChangedHandlers = new Hashtable(); } - EventHandler h = (EventHandler)_valueChangedHandlers[component]; + EventHandler? h = (EventHandler?)_valueChangedHandlers[component]; _valueChangedHandlers[component] = Delegate.Combine(h, handler); } @@ -153,7 +153,7 @@ namespace System.ComponentModel /// to see if they are equivalent. /// NOTE: If you make a change here, you likely need to change GetHashCode() as well. /// - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { try { @@ -186,11 +186,11 @@ namespace System.ComponentModel /// /// Creates an instance of the specified type. /// - protected object CreateInstance( + protected object? CreateInstance( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) { Type[] typeArgs = new Type[] { typeof(Type) }; - ConstructorInfo ctor = type.GetConstructor(typeArgs); + ConstructorInfo? ctor = type.GetConstructor(typeArgs); if (ctor != null) { return TypeDescriptor.CreateInstance(null, type, typeArgs, new object[] { PropertyType }); @@ -229,7 +229,7 @@ namespace System.ComponentModel /// Retrieves the properties /// [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public virtual PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) + public virtual PropertyDescriptorCollection GetChildProperties(object? instance, Attribute[]? filter) { if (instance == null) { @@ -245,9 +245,9 @@ namespace System.ComponentModel /// Gets an editor of the specified type. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " " + PropertyDescriptorPropertyTypeMessage)] - public virtual object GetEditor(Type editorBaseType) + public virtual object? GetEditor(Type editorBaseType) { - object editor = null; + object? editor = null; // Always grab the attribute collection first here, because if the metadata version // changes it will invalidate our editor cache. @@ -260,7 +260,7 @@ namespace System.ComponentModel { if (_editorTypes[i] == editorBaseType) { - return _editors[i]; + return _editors![i]; } } } @@ -275,11 +275,11 @@ namespace System.ComponentModel continue; } - Type editorType = GetTypeFromName(attr.EditorBaseTypeName); + Type? editorType = GetTypeFromName(attr.EditorBaseTypeName); if (editorBaseType == editorType) { - Type type = GetTypeFromName(attr.EditorTypeName); + Type? type = GetTypeFromName(attr.EditorTypeName); if (type != null) { editor = CreateInstance(type); @@ -305,7 +305,7 @@ namespace System.ComponentModel if (_editorCount >= _editorTypes.Length) { Type[] newTypes = new Type[_editorTypes.Length * 2]; - object[] newEditors = new object[_editors.Length * 2]; + object[] newEditors = new object[_editors!.Length * 2]; Array.Copy(_editorTypes, newTypes, _editorTypes.Length); Array.Copy(_editors, newEditors, _editors.Length); _editorTypes = newTypes; @@ -313,7 +313,7 @@ namespace System.ComponentModel } _editorTypes[_editorCount] = editorBaseType; - _editors[_editorCount++] = editor; + _editors![_editorCount++] = editor; } return editor; @@ -331,9 +331,9 @@ namespace System.ComponentModel /// someone associated another object with this instance, or if the instance is a /// custom type descriptor, GetInvocationTarget may return a different value. /// - protected override object GetInvocationTarget(Type type, object instance) + protected override object? GetInvocationTarget(Type type, object instance) { - object target = base.GetInvocationTarget(type, instance); + object? target = base.GetInvocationTarget(type, instance); if (target is ICustomTypeDescriptor td) { target = td.GetPropertyOwner(this); @@ -347,8 +347,8 @@ namespace System.ComponentModel /// [RequiresUnreferencedCode("Calls ComponentType.Assembly.GetType on the non-fully qualified typeName, which the trimmer cannot recognize.")] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - protected Type GetTypeFromName( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) + protected Type? GetTypeFromName( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string? typeName) { if (typeName == null || typeName.Length == 0) { @@ -356,16 +356,16 @@ namespace System.ComponentModel } // try the generic method. - Type typeFromGetType = Type.GetType(typeName); + Type? typeFromGetType = Type.GetType(typeName); // If we didn't get a type from the generic method, or if the assembly we found the type // in is the same as our Component's assembly, use or Component's assembly instead. This is // because the CLR may have cached an older version if the assembly's version number didn't change - Type typeFromComponent = null; + Type? typeFromComponent = null; if (ComponentType != null) { if ((typeFromGetType == null) || - (ComponentType.Assembly.FullName.Equals(typeFromGetType.Assembly.FullName))) + (ComponentType.Assembly.FullName!.Equals(typeFromGetType.Assembly.FullName))) { int comma = typeName.IndexOf(','); @@ -382,17 +382,17 @@ namespace System.ComponentModel /// /// When overridden in a derived class, gets the current value of the property on a component. /// - public abstract object GetValue(object component); + public abstract object? GetValue(object? component); /// /// This should be called by your property descriptor implementation /// when the property value has changed. /// - protected virtual void OnValueChanged(object component, EventArgs e) + protected virtual void OnValueChanged(object? component, EventArgs e) { if (component != null) { - ((EventHandler)_valueChangedHandlers?[component])?.Invoke(component, e); + ((EventHandler?)_valueChangedHandlers?[component])?.Invoke(component, e); } } @@ -412,8 +412,8 @@ namespace System.ComponentModel if (_valueChangedHandlers != null) { - EventHandler h = (EventHandler)_valueChangedHandlers[component]; - h = (EventHandler)Delegate.Remove(h, handler); + EventHandler? h = (EventHandler?)_valueChangedHandlers[component]; + h = (EventHandler?)Delegate.Remove(h, handler); if (h != null) { _valueChangedHandlers[component] = h; @@ -430,11 +430,11 @@ namespace System.ComponentModel /// component, in the form of a combined multicast event handler. /// Returns null if no event handlers currently assigned to component. /// - protected internal EventHandler GetValueChangedHandler(object component) + protected internal EventHandler? GetValueChangedHandler(object component) { if (component != null && _valueChangedHandlers != null) { - return (EventHandler)_valueChangedHandlers[component]; + return (EventHandler?)_valueChangedHandlers[component]; } else { @@ -451,7 +451,7 @@ namespace System.ComponentModel /// When overridden in a derived class, sets the value of /// the component to a different value. /// - public abstract void SetValue(object component, object value); + public abstract void SetValue(object? component, object? value); /// /// When overridden in a derived class, indicates whether the diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs index 1e9d93f199e..672a32ec8a5 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs @@ -18,11 +18,11 @@ namespace System.ComponentModel /// public static readonly PropertyDescriptorCollection Empty = new PropertyDescriptorCollection(null, true); - private IDictionary _cachedFoundProperties; + private IDictionary? _cachedFoundProperties; private bool _cachedIgnoreCase; private PropertyDescriptor[] _properties; - private readonly string[] _namedSort; - private readonly IComparer _comparer; + private readonly string[]? _namedSort; + private readonly IComparer? _comparer; private bool _propsOwned; private bool _needSort; private readonly bool _readOnly; @@ -33,7 +33,7 @@ namespace System.ComponentModel /// Initializes a new instance of the /// class. /// - public PropertyDescriptorCollection(PropertyDescriptor[] properties) + public PropertyDescriptorCollection(PropertyDescriptor[]? properties) { if (properties == null) { @@ -52,12 +52,12 @@ namespace System.ComponentModel /// Initializes a new instance of a property descriptor collection, and allows you to mark the /// collection as read-only so it cannot be modified. /// - public PropertyDescriptorCollection(PropertyDescriptor[] properties, bool readOnly) : this(properties) + public PropertyDescriptorCollection(PropertyDescriptor[]? properties, bool readOnly) : this(properties) { _readOnly = readOnly; } - private PropertyDescriptorCollection(PropertyDescriptor[] properties, int propCount, string[] namedSort, IComparer comparer) + private PropertyDescriptorCollection(PropertyDescriptor[] properties, int propCount, string[]? namedSort, IComparer? comparer) { _propsOwned = false; if (namedSort != null) @@ -87,14 +87,14 @@ namespace System.ComponentModel throw new IndexOutOfRangeException(); } EnsurePropsOwned(); - return _properties[index]; + return _properties[index]!; } } /// /// Gets the property with the specified name. /// - public virtual PropertyDescriptor this[string name] => Find(name, false); + public virtual PropertyDescriptor? this[string name] => Find(name, false); public int Add(PropertyDescriptor value) { @@ -172,11 +172,11 @@ namespace System.ComponentModel /// /// Gets the description of the property with the specified name. /// - public virtual PropertyDescriptor Find(string name, bool ignoreCase) + public virtual PropertyDescriptor? Find(string name, bool ignoreCase) { lock (_internalSyncObject) { - PropertyDescriptor p = null; + PropertyDescriptor? p = null; if (_cachedFoundProperties == null || _cachedIgnoreCase != ignoreCase) { @@ -193,7 +193,7 @@ namespace System.ComponentModel // first try to find it in the cache // - object cached = _cachedFoundProperties[name]; + object? cached = _cachedFoundProperties[name]; if (cached != null) { @@ -229,7 +229,7 @@ namespace System.ComponentModel } } - public int IndexOf(PropertyDescriptor value) => Array.IndexOf(_properties, value, 0, Count); + public int IndexOf(PropertyDescriptor? value) => Array.IndexOf(_properties, value, 0, Count); public void Insert(int index, PropertyDescriptor value) { @@ -247,7 +247,7 @@ namespace System.ComponentModel Count++; } - public void Remove(PropertyDescriptor value) + public void Remove(PropertyDescriptor? value) { if (_readOnly) { @@ -273,7 +273,7 @@ namespace System.ComponentModel { Array.Copy(_properties, index + 1, _properties, index, Count - index - 1); } - _properties[Count - 1] = null; + _properties[Count - 1] = null!; Count--; } @@ -291,7 +291,7 @@ namespace System.ComponentModel /// Sorts the members of this PropertyDescriptorCollection. Any specified NamedSort arguments will /// be applied first, followed by sort using the specified IComparer. /// - public virtual PropertyDescriptorCollection Sort(string[] names) + public virtual PropertyDescriptorCollection Sort(string[]? names) { return new PropertyDescriptorCollection(_properties, Count, names, _comparer); } @@ -300,7 +300,7 @@ namespace System.ComponentModel /// Sorts the members of this PropertyDescriptorCollection. Any specified NamedSort arguments will /// be applied first, followed by sort using the specified IComparer. /// - public virtual PropertyDescriptorCollection Sort(string[] names, IComparer comparer) + public virtual PropertyDescriptorCollection Sort(string[]? names, IComparer? comparer) { return new PropertyDescriptorCollection(_properties, Count, names, comparer); } @@ -309,7 +309,7 @@ namespace System.ComponentModel /// Sorts the members of this PropertyDescriptorCollection, using the specified IComparer to compare, /// the PropertyDescriptors contained in the collection. /// - public virtual PropertyDescriptorCollection Sort(IComparer comparer) + public virtual PropertyDescriptorCollection Sort(IComparer? comparer) { return new PropertyDescriptorCollection(_properties, Count, _namedSort, comparer); } @@ -318,7 +318,7 @@ namespace System.ComponentModel /// Sorts the members of this PropertyDescriptorCollection. Any specified NamedSort arguments will /// be applied first, followed by sort using the specified IComparer. /// - protected void InternalSort(string[] names) + protected void InternalSort(string[]? names) { if (_properties.Length == 0) { @@ -329,7 +329,7 @@ namespace System.ComponentModel if (names != null && names.Length > 0) { - List propList = new List(_properties); + List propList = new List(_properties); int foundCount = 0; int propCount = _properties.Length; @@ -337,7 +337,7 @@ namespace System.ComponentModel { for (int j = 0; j < propCount; j++) { - PropertyDescriptor currentProp = propList[j]; + PropertyDescriptor? currentProp = propList[j]; // Found a matching property. Here, we add it to our array. We also // mark it as null in our array list so we don't add it twice later. @@ -360,7 +360,7 @@ namespace System.ComponentModel { if (propList[i] != null) { - _properties[foundCount++] = propList[i]; + _properties[foundCount++] = propList[i]!; } } @@ -371,7 +371,7 @@ namespace System.ComponentModel /// /// Sorts the members of this PropertyDescriptorCollection using the specified IComparer. /// - protected void InternalSort(IComparer sorter) + protected void InternalSort(IComparer? sorter) { if (sorter == null) { @@ -401,7 +401,7 @@ namespace System.ComponentModel bool ICollection.IsSynchronized => false; - object ICollection.SyncRoot => null; + object ICollection.SyncRoot => null!; int ICollection.Count => Count; @@ -413,7 +413,7 @@ namespace System.ComponentModel void IList.RemoveAt(int index) => RemoveAt(index); - void IDictionary.Add(object key, object value) + void IDictionary.Add(object key, object? value) { if (!(value is PropertyDescriptor newProp)) { @@ -437,7 +437,7 @@ namespace System.ComponentModel bool IDictionary.IsReadOnly => _readOnly; - object IDictionary.this[object key] + object? IDictionary.this[object key] { get { @@ -475,7 +475,7 @@ namespace System.ComponentModel { for (int i = 0; i < Count; i++) { - if (_properties[i].Name.Equals((string)key)) + if (_properties[i]!.Name.Equals((string)key)) { index = i; break; @@ -489,12 +489,12 @@ namespace System.ComponentModel if (index == -1) { - Add((PropertyDescriptor)value); + Add((PropertyDescriptor)value!); } else { EnsurePropsOwned(); - _properties[index] = (PropertyDescriptor)value; + _properties[index] = (PropertyDescriptor)value!; if (_cachedFoundProperties != null && key is string) { _cachedFoundProperties[key] = value; @@ -510,7 +510,7 @@ namespace System.ComponentModel string[] keys = new string[Count]; for (int i = 0; i < Count; i++) { - keys[i] = _properties[i].Name; + keys[i] = _properties[i]!.Name; } return keys; } @@ -538,7 +538,7 @@ namespace System.ComponentModel { if (key is string) { - PropertyDescriptor pd = this[(string)key]; + PropertyDescriptor? pd = this[(string)key]; if (pd != null) { ((IList)this).Remove(pd); @@ -546,21 +546,21 @@ namespace System.ComponentModel } } - int IList.Add(object value) => Add((PropertyDescriptor)value); + int IList.Add(object? value) => Add((PropertyDescriptor)value!); - bool IList.Contains(object value) => Contains((PropertyDescriptor)value); + bool IList.Contains(object? value) => Contains((PropertyDescriptor)value!); - int IList.IndexOf(object value) => IndexOf((PropertyDescriptor)value); + int IList.IndexOf(object? value) => IndexOf((PropertyDescriptor)value!); - void IList.Insert(int index, object value) => Insert(index, (PropertyDescriptor)value); + void IList.Insert(int index, object? value) => Insert(index, (PropertyDescriptor)value!); bool IList.IsReadOnly => _readOnly; bool IList.IsFixedSize => _readOnly; - void IList.Remove(object value) => Remove((PropertyDescriptor)value); + void IList.Remove(object? value) => Remove((PropertyDescriptor?)value); - object IList.this[int index] + object? IList.this[int index] { get => this[index]; set @@ -582,7 +582,7 @@ namespace System.ComponentModel } EnsurePropsOwned(); - _properties[index] = (PropertyDescriptor)value; + _properties[index] = (PropertyDescriptor)value!; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs index 7eca6e49fda..4f49461af5c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel } PropertyName = propertyName; - ReceiverTypeName = receiverType.AssemblyQualifiedName; + ReceiverTypeName = receiverType.AssemblyQualifiedName!; } /// @@ -50,7 +50,7 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string ReceiverTypeName { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RecommendedAsConfigurableAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RecommendedAsConfigurableAttribute.cs index 4aea3c3a8dd..880f09a10b1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RecommendedAsConfigurableAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RecommendedAsConfigurableAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -43,7 +45,7 @@ namespace System.ComponentModel /// public static readonly RecommendedAsConfigurableAttribute Default = No; - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs index 43e9ff92464..5f011122588 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs @@ -30,7 +30,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the /// given source type to a reference object using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == typeof(string) && context != null) { @@ -43,7 +43,7 @@ namespace System.ComponentModel /// /// Converts the given object to the reference type. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -53,7 +53,7 @@ namespace System.ComponentModel // Try the reference service first. if (context.GetService(typeof(IReferenceService)) is IReferenceService refSvc) { - object obj = refSvc.GetReference(text); + object? obj = refSvc.GetReference(text); if (obj != null) { return obj; @@ -61,10 +61,10 @@ namespace System.ComponentModel } // Now try IContainer - IContainer cont = context.Container; + IContainer? cont = context.Container; if (cont != null) { - object obj = cont.Components[text]; + object? obj = cont.Components[text]; if (obj != null) { return obj; @@ -81,7 +81,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the reference type using the specified context and arguments. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { @@ -90,7 +90,7 @@ namespace System.ComponentModel // Try the reference service first. if (context?.GetService(typeof(IReferenceService)) is IReferenceService refSvc) { - string name = refSvc.GetName(value); + string? name = refSvc.GetName(value); if (name != null) { return name; @@ -100,8 +100,8 @@ namespace System.ComponentModel // Now see if this is an IComponent. if (!Marshal.IsComObject(value) && value is IComponent comp) { - ISite site = comp.Site; - string name = site?.Name; + ISite? site = comp.Site; + string? name = site?.Name; if (name != null) { return name; @@ -121,13 +121,13 @@ namespace System.ComponentModel /// /// Gets a collection of standard values for the reference data type. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { - List components = null; + List? components = null; if (context != null) { - components = new List { null }; + components = new List { null }; // Try the reference service first. if (context.GetService(typeof(IReferenceService)) is IReferenceService refSvc) @@ -171,13 +171,13 @@ namespace System.ComponentModel /// Gets a value indicating whether the list of standard values returned from /// is an exclusive list. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true; /// /// Gets a value indicating whether this object supports a standard set of values /// that can be picked from a list. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; /// /// Gets a value indicating whether a particular value can be added to @@ -197,10 +197,10 @@ namespace System.ComponentModel _converter = converter; } - public int Compare(object item1, object item2) + public int Compare(object? item1, object? item2) { - string itemName1 = _converter.ConvertToString(item1); - string itemName2 = _converter.ConvertToString(item2); + string? itemName1 = _converter.ConvertToString(item1); + string? itemName2 = _converter.ConvertToString(item2); return string.Compare(itemName1, itemName2, StringComparison.InvariantCulture); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs index e8e3fee1656..348c0126b67 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs @@ -58,13 +58,13 @@ namespace System.ComponentModel /// internal sealed class ReflectEventDescriptor : EventDescriptor { - private Type _type; // the delegate type for the event + private Type? _type; // the delegate type for the event [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _componentClass; // the class of the component this info is for - private MethodInfo _addMethod; // the method to use when adding an event - private MethodInfo _removeMethod; // the method to use when removing an event - private EventInfo _realEvent; // actual event info... may be null + private MethodInfo? _addMethod; // the method to use when adding an event + private MethodInfo? _removeMethod; // the method to use when removing an event + private EventInfo? _realEvent; // actual event info... may be null private bool _filledMethods; // did we already call FillMethods() once? /// @@ -95,7 +95,7 @@ namespace System.ComponentModel EventInfo eventInfo) : base(eventInfo.Name, Array.Empty()) { - Debug.Assert(eventInfo.ReflectedType.IsAssignableFrom(componentClass), "eventInfo.ReflectedType is used below, but only componentClass is annotated with DynamicallyAccessedMembers. Ensure ReflectedType is in componentClass's hierarchy."); + Debug.Assert(eventInfo.ReflectedType!.IsAssignableFrom(componentClass), "eventInfo.ReflectedType is used below, but only componentClass is annotated with DynamicallyAccessedMembers. Ensure ReflectedType is in componentClass's hierarchy."); _componentClass = componentClass ?? throw new ArgumentException(SR.Format(SR.InvalidNullArgument, nameof(componentClass))); _realEvent = eventInfo; @@ -135,7 +135,7 @@ namespace System.ComponentModel get { FillMethods(); - return _type; + return _type!; } } @@ -154,13 +154,13 @@ namespace System.ComponentModel if (component != null) { - ISite site = GetSite(component); - IComponentChangeService changeService = null; + ISite? site = GetSite(component); + IComponentChangeService? changeService = null; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } if (changeService != null) @@ -189,10 +189,10 @@ namespace System.ComponentModel { throw new ArgumentException(SR.Format(SR.ErrorInvalidEventHandler, Name)); } - IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService)); + IDictionaryService? dict = (IDictionaryService?)site.GetService(typeof(IDictionaryService)); if (dict != null) { - Delegate eventdesc = (Delegate)dict.GetValue(this); + Delegate? eventdesc = (Delegate?)dict.GetValue(this); eventdesc = Delegate.Combine(eventdesc, value); dict.SetValue(this, eventdesc); shadowed = true; @@ -201,7 +201,7 @@ namespace System.ComponentModel if (!shadowed) { - _addMethod.Invoke(component, new[] { value }); + _addMethod!.Invoke(component, new[] { value }); } // Now notify the change service that the change was successful. @@ -253,7 +253,7 @@ namespace System.ComponentModel { string eventName = realEventInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; - Type currentReflectType = realEventInfo.ReflectedType; + Type currentReflectType = realEventInfo.ReflectedType!; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); Debug.Assert(currentReflectType.IsAssignableFrom(_componentClass), "currentReflectType must be in _componentClass's hierarchy"); int depth = 0; @@ -263,19 +263,19 @@ namespace System.ComponentModel while (currentReflectType != typeof(object)) { depth++; - currentReflectType = currentReflectType.BaseType; + currentReflectType = currentReflectType.BaseType!; } if (depth > 0) { // Now build up an array in reverse order - currentReflectType = realEventInfo.ReflectedType; + currentReflectType = realEventInfo.ReflectedType!; Attribute[][] attributeStack = new Attribute[depth][]; while (currentReflectType != typeof(object)) { // Fill in our member info so we can get at the custom attributes. - MemberInfo memberInfo = currentReflectType.GetEvent(eventName, bindingFlags); + MemberInfo? memberInfo = currentReflectType!.GetEvent(eventName, bindingFlags); // Get custom attributes for the member info. if (memberInfo != null) @@ -284,7 +284,7 @@ namespace System.ComponentModel } // Ready for the next loop iteration. - currentReflectType = currentReflectType.BaseType; + currentReflectType = currentReflectType.BaseType!; } // Now trawl the attribute stack so that we add attributes @@ -308,22 +308,23 @@ namespace System.ComponentModel /// private void FillMethods() { - if (_filledMethods) return; + if (_filledMethods) + return; if (_realEvent != null) { _addMethod = _realEvent.GetAddMethod(); _removeMethod = _realEvent.GetRemoveMethod(); - EventInfo defined = null; + EventInfo? defined = null; if (_addMethod == null || _removeMethod == null) { - Type start = _componentClass.BaseType; + Type? start = _componentClass.BaseType; while (start != null && start != typeof(object)) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - EventInfo test = start.GetEvent(_realEvent.Name, bindingFlags); + EventInfo test = start.GetEvent(_realEvent.Name, bindingFlags)!; if (test.GetAddMethod() != null) { defined = test; @@ -354,7 +355,7 @@ namespace System.ComponentModel return; } - Type[] argsType = new Type[] { _type }; + Type[] argsType = new Type[] { _type! }; _addMethod = FindMethod(_componentClass, "AddOn" + Name, argsType, typeof(void)); _removeMethod = FindMethod(_componentClass, "RemoveOn" + Name, argsType, typeof(void)); if (_addMethod == null || _removeMethod == null) @@ -373,7 +374,7 @@ namespace System.ComponentModel { string methodName = realMethodInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; - Type currentReflectType = realMethodInfo.ReflectedType; + Type? currentReflectType = realMethodInfo.ReflectedType; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); Debug.Assert(currentReflectType.IsAssignableFrom(_componentClass), "currentReflectType must be in _componentClass's hierarchy"); @@ -397,7 +398,7 @@ namespace System.ComponentModel while (currentReflectType != null && currentReflectType != typeof(object)) { // Fill in our member info so we can get at the custom attributes. - MemberInfo memberInfo = currentReflectType.GetMethod(methodName, bindingFlags); + MemberInfo? memberInfo = currentReflectType.GetMethod(methodName, bindingFlags); // Get custom attributes for the member info. if (memberInfo != null) @@ -434,13 +435,13 @@ namespace System.ComponentModel if (component != null) { - ISite site = GetSite(component); - IComponentChangeService changeService = null; + ISite? site = GetSite(component); + IComponentChangeService? changeService = null; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } if (changeService != null) @@ -464,10 +465,10 @@ namespace System.ComponentModel if (site != null && site.DesignMode) { - IDictionaryService dict = (IDictionaryService)site.GetService(typeof(IDictionaryService)); + IDictionaryService? dict = (IDictionaryService?)site.GetService(typeof(IDictionaryService)); if (dict != null) { - Delegate del = (Delegate)dict.GetValue(this); + Delegate? del = (Delegate?)dict.GetValue(this); del = Delegate.Remove(del, value); dict.SetValue(this, del); shadowed = true; @@ -476,7 +477,7 @@ namespace System.ComponentModel if (!shadowed) { - _removeMethod.Invoke(component, new[] { value }); + _removeMethod!.Invoke(component, new[] { value }); } // Now notify the change service that the change was successful. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs index 8178284444b..873ad225e3e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs @@ -63,16 +63,16 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _componentClass; // used to determine if we should all on us or on the designer private readonly Type _type; // the data type of the property - private object _defaultValue; // the default value of the property (or noValue) - private object _ambientValue; // the ambient value of the property (or noValue) - private PropertyInfo _propInfo; // the property info - private MethodInfo _getMethod; // the property get method - private MethodInfo _setMethod; // the property set method - private MethodInfo _shouldSerializeMethod; // the should serialize method - private MethodInfo _resetMethod; // the reset property method - private EventDescriptor _realChangedEvent; // Changed event handler on object - private EventDescriptor _realIPropChangedEvent; // INotifyPropertyChanged.PropertyChanged event handler on object - private readonly Type _receiverType; // Only set if we are an extender + private object? _defaultValue; // the default value of the property (or noValue) + private object? _ambientValue; // the ambient value of the property (or noValue) + private PropertyInfo? _propInfo; // the property info + private MethodInfo? _getMethod; // the property get method + private MethodInfo? _setMethod; // the property set method + private MethodInfo? _shouldSerializeMethod; // the should serialize method + private MethodInfo? _resetMethod; // the reset property method + private EventDescriptor? _realChangedEvent; // Changed event handler on object + private EventDescriptor? _realIPropChangedEvent; // INotifyPropertyChanged.PropertyChanged event handler on object + private readonly Type? _receiverType; // Only set if we are an extender /// /// The main constructor for ReflectPropertyDescriptors. @@ -82,7 +82,7 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, string name, Type type, - Attribute[] attributes) + Attribute[]? attributes) : base(name, attributes) { Debug.WriteLine($"Creating ReflectPropertyDescriptor for {componentClass?.FullName}.{name}"); @@ -104,7 +104,7 @@ namespace System.ComponentModel } catch (Exception t) { - Debug.Fail($"Property '{name}' on component {componentClass.FullName} failed to init."); + Debug.Fail($"Property '{name}' on component {componentClass?.FullName} failed to init."); Debug.Fail(t.ToString()); throw; } @@ -120,8 +120,8 @@ namespace System.ComponentModel Type type, PropertyInfo propInfo, MethodInfo getMethod, - MethodInfo setMethod, - Attribute[] attrs) + MethodInfo? setMethod, + Attribute[]? attrs) : this(componentClass, name, type, attrs) { _propInfo = propInfo; @@ -143,8 +143,8 @@ namespace System.ComponentModel Type type, Type receiverType, MethodInfo getMethod, - MethodInfo setMethod, - Attribute[] attrs) + MethodInfo? setMethod, + Attribute[]? attrs) : this(componentClass, name, type, attrs) { _receiverType = receiverType; @@ -226,7 +226,7 @@ namespace System.ComponentModel { if (!_state[s_bitAmbientValueQueried]) { - Attribute a = Attributes[typeof(AmbientValueAttribute)]; + Attribute? a = Attributes[typeof(AmbientValueAttribute)]; if (a != null) { _ambientValue = ((AmbientValueAttribute)a).Value; @@ -237,7 +237,7 @@ namespace System.ComponentModel } _state[s_bitAmbientValueQueried] = true; } - return _ambientValue; + return _ambientValue!; } } @@ -254,7 +254,7 @@ namespace System.ComponentModel _state[s_bitChangedQueried] = true; } - return _realChangedEvent; + return _realChangedEvent!; } } @@ -275,7 +275,7 @@ namespace System.ComponentModel _state[s_bitIPropChangedQueried] = true; } - return _realIPropChangedEvent; + return _realIPropChangedEvent!; } } @@ -293,14 +293,14 @@ namespace System.ComponentModel { if (!_state[s_bitDefaultValueQueried]) { - Attribute a = Attributes[typeof(DefaultValueAttribute)]; + Attribute? a = Attributes[typeof(DefaultValueAttribute)]; if (a != null) { // Default values for enums are often stored as their underlying integer type: - object defaultValue = ((DefaultValueAttribute)a).Value; + object? defaultValue = ((DefaultValueAttribute)a).Value; bool storedAsUnderlyingType = defaultValue != null && PropertyType.IsEnum && PropertyType.GetEnumUnderlyingType() == defaultValue.GetType(); _defaultValue = storedAsUnderlyingType ? - Enum.ToObject(PropertyType, _defaultValue) : + Enum.ToObject(PropertyType, _defaultValue!) : defaultValue; } else @@ -309,7 +309,7 @@ namespace System.ComponentModel } _state[s_bitDefaultValueQueried] = true; } - return _defaultValue; + return _defaultValue!; } } @@ -348,7 +348,7 @@ namespace System.ComponentModel } _state[s_bitGetQueried] = true; } - return _getMethod; + return _getMethod!; } } @@ -360,7 +360,7 @@ namespace System.ComponentModel /// /// Indicates whether this property is read only. /// - public override bool IsReadOnly => SetMethodValue == null || ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly; + public override bool IsReadOnly => SetMethodValue == null || ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]!).IsReadOnly; /// /// Retrieves the type of the property. @@ -370,7 +370,7 @@ namespace System.ComponentModel /// /// Access to the reset method, if one exists for this property. /// - private MethodInfo ResetMethodValue + private MethodInfo? ResetMethodValue { get { @@ -398,20 +398,20 @@ namespace System.ComponentModel /// /// Accessor for the set method /// - private MethodInfo SetMethodValue + private MethodInfo? SetMethodValue { get { if (!_state[s_bitSetQueried] && _state[s_bitSetOnDemand]) { - string name = _propInfo.Name; + string name = _propInfo!.Name; if (_setMethod == null) { - for (Type t = _componentClass.BaseType; t != null && t != typeof(object); t = t.BaseType) + for (Type? t = _componentClass.BaseType; t != null && t != typeof(object); t = t.BaseType) { BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance; - PropertyInfo p = t.GetProperty(name, bindingFlags, binder: null, PropertyType, Type.EmptyTypes, null); + PropertyInfo? p = t.GetProperty(name, bindingFlags, binder: null, PropertyType, Type.EmptyTypes, null); if (p != null) { _setMethod = p.GetSetMethod(nonPublic: false); @@ -454,7 +454,7 @@ namespace System.ComponentModel /// /// Accessor for the ShouldSerialize method. /// - private MethodInfo ShouldSerializeMethodValue + private MethodInfo? ShouldSerializeMethodValue { get { @@ -521,16 +521,16 @@ namespace System.ComponentModel return !Equals(ExtenderGetValue(provider, component), _defaultValue); } - MethodInfo reset = ResetMethodValue; + MethodInfo? reset = ResetMethodValue; if (reset != null) { - MethodInfo shouldSerialize = ShouldSerializeMethodValue; + MethodInfo? shouldSerialize = ShouldSerializeMethodValue; if (shouldSerialize != null) { try { - provider = (IExtenderProvider)GetInvocationTarget(_componentClass, provider); - return (bool)shouldSerialize.Invoke(provider, new object[] { component }); + IExtenderProvider? prov = (IExtenderProvider?)GetInvocationTarget(_componentClass, provider); + return (bool)shouldSerialize.Invoke(prov, new object[] { component })!; } catch { } } @@ -540,16 +540,16 @@ namespace System.ComponentModel return false; } - internal Type ExtenderGetReceiverType() => _receiverType; + internal Type? ExtenderGetReceiverType() => _receiverType; internal Type ExtenderGetType(IExtenderProvider provider) => PropertyType; - internal object ExtenderGetValue(IExtenderProvider provider, object component) + internal object? ExtenderGetValue(IExtenderProvider? provider, object? component) { if (provider != null) { - provider = (IExtenderProvider)GetInvocationTarget(_componentClass, provider); - return GetMethodValue.Invoke(provider, new object[] { component }); + IExtenderProvider? prov = (IExtenderProvider?)GetInvocationTarget(_componentClass, provider); + return GetMethodValue.Invoke(prov, new object?[] { component }); } return null; } @@ -566,15 +566,15 @@ namespace System.ComponentModel } else if (ResetMethodValue != null) { - ISite site = GetSite(component); - IComponentChangeService changeService = null; - object oldValue = null; - object newValue; + ISite? site = GetSite(component); + IComponentChangeService? changeService = null; + object? oldValue = null; + object? newValue; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } // Make sure that it is ok to send the onchange events @@ -595,33 +595,33 @@ namespace System.ComponentModel } } - provider = (IExtenderProvider)GetInvocationTarget(_componentClass, provider); + IExtenderProvider? prov = (IExtenderProvider?)GetInvocationTarget(_componentClass, provider); if (ResetMethodValue != null) { - ResetMethodValue.Invoke(provider, new object[] { component }); + ResetMethodValue.Invoke(prov, new object[] { component }); // Now notify the change service that the change was successful. if (changeService != null) { - newValue = ExtenderGetValue(provider, component); + newValue = ExtenderGetValue(prov, component); changeService.OnComponentChanged(component, notifyDesc, oldValue, newValue); } } } } - internal void ExtenderSetValue(IExtenderProvider provider, object component, object value, PropertyDescriptor notifyDesc) + internal void ExtenderSetValue(IExtenderProvider? provider, object? component, object? value, PropertyDescriptor notifyDesc) { if (provider != null) { - ISite site = GetSite(component); - IComponentChangeService changeService = null; - object oldValue = null; + ISite? site = GetSite(component); + IComponentChangeService? changeService = null; + object? oldValue = null; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } // Make sure that it is ok to send the onchange events @@ -630,7 +630,7 @@ namespace System.ComponentModel oldValue = ExtenderGetValue(provider, component); try { - changeService.OnComponentChanging(component, notifyDesc); + changeService.OnComponentChanging(component!, notifyDesc); } catch (CheckoutException coEx) { @@ -642,21 +642,21 @@ namespace System.ComponentModel } } - provider = (IExtenderProvider)GetInvocationTarget(_componentClass, provider); + IExtenderProvider? prov = (IExtenderProvider?)GetInvocationTarget(_componentClass, provider); if (SetMethodValue != null) { - SetMethodValue.Invoke(provider, new object[] { component, value }); + SetMethodValue.Invoke(prov, new object?[] { component, value }); // Now notify the change service that the change was successful. - changeService?.OnComponentChanged(component, notifyDesc, oldValue, value); + changeService?.OnComponentChanged(component!, notifyDesc, oldValue, value); } } } internal bool ExtenderShouldSerializeValue(IExtenderProvider provider, object component) { - provider = (IExtenderProvider)GetInvocationTarget(_componentClass, provider); + IExtenderProvider? prov = (IExtenderProvider?)GetInvocationTarget(_componentClass, provider); if (IsReadOnly) { @@ -664,7 +664,7 @@ namespace System.ComponentModel { try { - return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] { component }); + return (bool)ShouldSerializeMethodValue.Invoke(prov, new object[] { component })!; } catch { } } @@ -676,13 +676,13 @@ namespace System.ComponentModel { try { - return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] { component }); + return (bool)ShouldSerializeMethodValue.Invoke(prov, new object[] { component })!; } catch { } } return true; } - return !Equals(DefaultValue, ExtenderGetValue(provider, component)); + return !Equals(DefaultValue, ExtenderGetValue(prov, component)); } [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields, typeof(DesignerSerializationVisibilityAttribute))] @@ -713,10 +713,10 @@ namespace System.ComponentModel { if (ShouldSerializeMethodValue != null) { - component = GetInvocationTarget(_componentClass, component); + component = GetInvocationTarget(_componentClass, component)!; try { - return (bool)ShouldSerializeMethodValue.Invoke(component, null); + return (bool)ShouldSerializeMethodValue.Invoke(component, null)!; } catch { } } @@ -772,7 +772,7 @@ namespace System.ComponentModel // : may ave come from a base class, meaning we will request custom metadata for this // : class twice. - Type currentReflectType = _componentClass; + Type? currentReflectType = _componentClass; int depth = 0; // First, calculate the depth of the object hierarchy. We do this so we can do a single @@ -791,14 +791,14 @@ namespace System.ComponentModel while (currentReflectType != null && currentReflectType != typeof(object)) { - MemberInfo memberInfo = null; + MemberInfo? memberInfo = null; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly; // Fill in our member info so we can get at the custom attributes. if (IsExtender) { //receiverType is used to avoid ambitiousness when there are overloads for the get method. - memberInfo = currentReflectType.GetMethod("Get" + Name, bindingFlags, binder: null, new Type[] { _receiverType }, modifiers: null); + memberInfo = currentReflectType.GetMethod("Get" + Name, bindingFlags, binder: null, new Type[] { _receiverType! }, modifiers: null); } else { @@ -824,11 +824,11 @@ namespace System.ComponentModel { if (attr is AttributeProviderAttribute sta) { - Type specificType = Type.GetType(sta.TypeName); + Type? specificType = Type.GetType(sta.TypeName!); if (specificType != null) { - Attribute[] stAttrs = null; + Attribute[]? stAttrs = null; if (!string.IsNullOrEmpty(sta.PropertyName)) { @@ -885,7 +885,7 @@ namespace System.ComponentModel /// invoking the getXXX method. An exception in the getXXX /// method will pass through. /// - public override object GetValue(object component) + public override object? GetValue(object? component) { Debug.WriteLine($"[{Name}]: GetValue({component?.GetType().Name ?? "(null)"})"); @@ -899,7 +899,7 @@ namespace System.ComponentModel if (component != null) { - component = GetInvocationTarget(_componentClass, component); + component = GetInvocationTarget(_componentClass, component)!; try @@ -908,9 +908,9 @@ namespace System.ComponentModel } catch (Exception t) { - string name = null; - IComponent comp = component as IComponent; - ISite site = comp?.Site; + string? name = null; + IComponent? comp = component as IComponent; + ISite? site = comp?.Site; if (site?.Name != null) { name = site.Name; @@ -923,7 +923,7 @@ namespace System.ComponentModel if (t is TargetInvocationException) { - t = t.InnerException; + t = t.InnerException!; } string message = t.Message ?? t.GetType().Name; @@ -939,7 +939,7 @@ namespace System.ComponentModel /// Handles INotifyPropertyChanged.PropertyChange events from components. /// If event pertains to this property, issue a ValueChanged event. /// - internal void OnINotifyPropertyChanged(object component, PropertyChangedEventArgs e) + internal void OnINotifyPropertyChanged(object? component, PropertyChangedEventArgs e) { if (string.IsNullOrEmpty(e.PropertyName) || string.Compare(e.PropertyName, Name, true, CultureInfo.InvariantCulture) == 0) @@ -952,7 +952,7 @@ namespace System.ComponentModel /// This should be called by your property descriptor implementation /// when the property value has changed. /// - protected override void OnValueChanged(object component, EventArgs e) + protected override void OnValueChanged(object? component, EventArgs e) { if (_state[s_bitChangedQueried] && _realChangedEvent == null) { @@ -963,7 +963,7 @@ namespace System.ComponentModel /// /// Allows interested objects to be notified when this property changes. /// - public override void RemoveValueChanged(object component, EventHandler handler) + public override void RemoveValueChanged(object? component, EventHandler handler) { if (component == null) { @@ -1008,7 +1008,7 @@ namespace System.ComponentModel /// public override void ResetValue(object component) { - object invokee = GetInvocationTarget(_componentClass, component); + object? invokee = GetInvocationTarget(_componentClass, component); if (DefaultValue != s_noValue) { @@ -1020,15 +1020,15 @@ namespace System.ComponentModel } else if (ResetMethodValue != null) { - ISite site = GetSite(component); - IComponentChangeService changeService = null; - object oldValue = null; - object newValue; + ISite? site = GetSite(component); + IComponentChangeService? changeService = null; + object? oldValue = null; + object? newValue; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } // Make sure that it is ok to send the onchange events @@ -1072,25 +1072,25 @@ namespace System.ComponentModel /// property so that getXXX following a setXXX should return the value /// passed in if no exception was thrown in the setXXX call. /// - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { Debug.WriteLine($"[{Name}]: SetValue({component?.GetType().Name ?? "(null)"}, {value?.GetType().Name ?? "(null)"})"); if (component != null) { - ISite site = GetSite(component); - object oldValue = null; + ISite? site = GetSite(component); + object? oldValue = null; - object invokee = GetInvocationTarget(_componentClass, component); + object? invokee = GetInvocationTarget(_componentClass, component); if (!IsReadOnly) { - IComponentChangeService changeService = null; + IComponentChangeService? changeService = null; // Announce that we are about to change this component if (site != null) { - changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService)); + changeService = (IComponentChangeService?)site.GetService(typeof(IComponentChangeService)); } // Make sure that it is ok to send the onchange events @@ -1115,7 +1115,7 @@ namespace System.ComponentModel { try { - SetMethodValue.Invoke(invokee, new object[] { value }); + SetMethodValue!.Invoke(invokee, new object?[] { value }); OnValueChanged(invokee, EventArgs.Empty); } catch (Exception t) @@ -1162,7 +1162,7 @@ namespace System.ComponentModel /// public override bool ShouldSerializeValue(object component) { - component = GetInvocationTarget(_componentClass, component); + component = GetInvocationTarget(_componentClass, component)!; if (IsReadOnly) { @@ -1170,7 +1170,7 @@ namespace System.ComponentModel { try { - return (bool)ShouldSerializeMethodValue.Invoke(component, null); + return (bool)ShouldSerializeMethodValue.Invoke(component, null)!; } catch { } } @@ -1182,7 +1182,7 @@ namespace System.ComponentModel { try { - return (bool)ShouldSerializeMethodValue.Invoke(component, null); + return (bool)ShouldSerializeMethodValue.Invoke(component, null)!; } catch { } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs index 230b0ec3eb7..7237e3f94b0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs @@ -19,12 +19,12 @@ namespace System.ComponentModel { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _type; - private AttributeCollection _attributes; - private EventDescriptorCollection _events; - private PropertyDescriptorCollection _properties; - private TypeConverter _converter; - private object[] _editors; - private Type[] _editorTypes; + private AttributeCollection? _attributes; + private EventDescriptorCollection? _events; + private PropertyDescriptorCollection? _properties; + private TypeConverter? _converter; + private object[]? _editors; + private Type[]? _editorTypes; private int _editorCount; internal ReflectedTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) @@ -84,7 +84,7 @@ namespace System.ComponentModel // var attributes = new List(ReflectGetAttributes(_type)); - Type baseType = _type.BaseType; + Type? baseType = _type.BaseType; while (baseType != null && baseType != typeof(object)) { @@ -149,18 +149,18 @@ namespace System.ComponentModel /// /// Retrieves the class name for our type. /// - internal string GetClassName(object instance) => _type.FullName; + internal string? GetClassName(object? instance) => _type.FullName; /// /// Retrieves the component name from the site. /// - internal string GetComponentName(object instance) + internal string? GetComponentName(object? instance) { - IComponent comp = instance as IComponent; - ISite site = comp?.Site; + IComponent? comp = instance as IComponent; + ISite? site = comp?.Site; if (site != null) { - INestedSite nestedSite = site as INestedSite; + INestedSite? nestedSite = site as INestedSite; return (nestedSite?.FullName) ?? site.Name; } @@ -173,9 +173,9 @@ namespace System.ComponentModel /// will be used. /// [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered. The Type of instance cannot be statically discovered.")] - internal TypeConverter GetConverter(object instance) + internal TypeConverter GetConverter(object? instance) { - TypeConverterAttribute typeAttr = null; + TypeConverterAttribute? typeAttr = null; // For instances, the design time object for them may want to redefine the // attributes. So, we search the attribute here based on the instance. If found, @@ -184,14 +184,14 @@ namespace System.ComponentModel // to override these attributes, so we want to be smart here. if (instance != null) { - typeAttr = (TypeConverterAttribute)TypeDescriptor.GetAttributes(_type)[typeof(TypeConverterAttribute)]; - TypeConverterAttribute instanceAttr = (TypeConverterAttribute)TypeDescriptor.GetAttributes(instance)[typeof(TypeConverterAttribute)]; + typeAttr = (TypeConverterAttribute?)TypeDescriptor.GetAttributes(_type)[typeof(TypeConverterAttribute)]; + TypeConverterAttribute instanceAttr = (TypeConverterAttribute)TypeDescriptor.GetAttributes(instance)[typeof(TypeConverterAttribute)]!; if (typeAttr != instanceAttr) { - Type converterType = GetTypeFromName(instanceAttr.ConverterTypeName); + Type? converterType = GetTypeFromName(instanceAttr.ConverterTypeName); if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) { - return (TypeConverter)ReflectTypeDescriptionProvider.CreateInstance(converterType, _type); + return (TypeConverter)ReflectTypeDescriptionProvider.CreateInstance(converterType, _type)!; } } } @@ -201,15 +201,15 @@ namespace System.ComponentModel { if (typeAttr == null) { - typeAttr = (TypeConverterAttribute)TypeDescriptor.GetAttributes(_type)[typeof(TypeConverterAttribute)]; + typeAttr = (TypeConverterAttribute?)TypeDescriptor.GetAttributes(_type)[typeof(TypeConverterAttribute)]; } if (typeAttr != null) { - Type converterType = GetTypeFromName(typeAttr.ConverterTypeName); + Type? converterType = GetTypeFromName(typeAttr.ConverterTypeName); if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) { - _converter = (TypeConverter)CreateInstance(converterType, _type); + _converter = (TypeConverter)CreateInstance(converterType, _type)!; } } @@ -230,7 +230,7 @@ namespace System.ComponentModel /// presence of a DefaultEventAttribute on the class. /// [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] - internal EventDescriptor GetDefaultEvent(object instance) + internal EventDescriptor? GetDefaultEvent(object? instance) { AttributeCollection attributes; @@ -243,7 +243,7 @@ namespace System.ComponentModel attributes = TypeDescriptor.GetAttributes(_type); } - DefaultEventAttribute attr = (DefaultEventAttribute)attributes[typeof(DefaultEventAttribute)]; + DefaultEventAttribute? attr = (DefaultEventAttribute?)attributes[typeof(DefaultEventAttribute)]; if (attr != null && attr.Name != null) { if (instance != null) @@ -263,7 +263,7 @@ namespace System.ComponentModel /// Return the default property. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered.")] - internal PropertyDescriptor GetDefaultProperty(object instance) + internal PropertyDescriptor? GetDefaultProperty(object? instance) { AttributeCollection attributes; @@ -276,7 +276,7 @@ namespace System.ComponentModel attributes = TypeDescriptor.GetAttributes(_type); } - DefaultPropertyAttribute attr = (DefaultPropertyAttribute)attributes[typeof(DefaultPropertyAttribute)]; + DefaultPropertyAttribute? attr = (DefaultPropertyAttribute?)attributes[typeof(DefaultPropertyAttribute)]; if (attr != null && attr.Name != null) { if (instance != null) @@ -296,9 +296,9 @@ namespace System.ComponentModel /// Retrieves the editor for the given base type. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] - internal object GetEditor(object instance, Type editorBaseType) + internal object? GetEditor(object? instance, Type editorBaseType) { - EditorAttribute typeAttr; + EditorAttribute? typeAttr; // For instances, the design time object for them may want to redefine the // attributes. So, we search the attribute here based on the instance. If found, @@ -308,10 +308,10 @@ namespace System.ComponentModel if (instance != null) { typeAttr = GetEditorAttribute(TypeDescriptor.GetAttributes(_type), editorBaseType); - EditorAttribute instanceAttr = GetEditorAttribute(TypeDescriptor.GetAttributes(instance), editorBaseType); + EditorAttribute? instanceAttr = GetEditorAttribute(TypeDescriptor.GetAttributes(instance), editorBaseType); if (typeAttr != instanceAttr) { - Type editorType = GetTypeFromName(instanceAttr.EditorTypeName); + Type? editorType = GetTypeFromName(instanceAttr!.EditorTypeName); if (editorType != null && editorBaseType.IsAssignableFrom(editorType)) { return CreateInstance(editorType, _type); @@ -324,20 +324,20 @@ namespace System.ComponentModel { for (int idx = 0; idx < _editorCount; idx++) { - if (_editorTypes[idx] == editorBaseType) + if (_editorTypes![idx] == editorBaseType) { - return _editors[idx]; + return _editors![idx]; } } } // Editor is not cached yet. Look in the attributes. - object editor = null; + object? editor = null; typeAttr = GetEditorAttribute(TypeDescriptor.GetAttributes(_type), editorBaseType); if (typeAttr != null) { - Type editorType = GetTypeFromName(typeAttr.EditorTypeName); + Type? editorType = GetTypeFromName(typeAttr.EditorTypeName); if (editorType != null && editorBaseType.IsAssignableFrom(editorType)) { editor = CreateInstance(editorType, _type); @@ -347,7 +347,7 @@ namespace System.ComponentModel // Editor is not in the attributes. Search intrinsic tables. if (editor == null) { - Hashtable intrinsicEditors = GetEditorTable(editorBaseType); + Hashtable? intrinsicEditors = GetEditorTable(editorBaseType); if (intrinsicEditors != null) { editor = GetIntrinsicTypeEditor(intrinsicEditors, _type); @@ -376,7 +376,7 @@ namespace System.ComponentModel if (_editorTypes != null) { _editorTypes.CopyTo(newTypes, 0); - _editors.CopyTo(newEditors, 0); + _editors!.CopyTo(newEditors, 0); } _editorTypes = newTypes; @@ -394,13 +394,13 @@ namespace System.ComponentModel /// /// Helper method to return an editor attribute of the correct base type. /// - private static EditorAttribute GetEditorAttribute(AttributeCollection attributes, Type editorBaseType) + private static EditorAttribute? GetEditorAttribute(AttributeCollection attributes, Type editorBaseType) { foreach (Attribute attr in attributes) { if (attr is EditorAttribute edAttr) { - Type attrEditorBaseType = Type.GetType(edAttr.EditorBaseTypeName); + Type? attrEditorBaseType = Type.GetType(edAttr.EditorBaseTypeName!); if (attrEditorBaseType != null && attrEditorBaseType == editorBaseType) { @@ -425,7 +425,7 @@ namespace System.ComponentModel { EventDescriptor[] eventArray; Dictionary eventList = new Dictionary(16); - Type baseType = _type; + Type? baseType = _type; Type objType = typeof(object); do @@ -460,7 +460,7 @@ namespace System.ComponentModel { PropertyDescriptor[] propertyArray; Dictionary propertyList = new Dictionary(10); - Type baseType = _type; + Type? baseType = _type; Type objType = typeof(object); do @@ -491,7 +491,7 @@ namespace System.ComponentModel Justification = "Calling _type.Assembly.GetType on a non-assembly qualified type will still work. See https://github.com/mono/linker/issues/1895")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", Justification = "Using the non-assembly qualified type name will still work.")] - private Type GetTypeFromName( + private Type? GetTypeFromName( // this method doesn't create the type, but all callers are annotated with PublicConstructors, // so use that value to ensure the Type will be preserved [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) @@ -502,7 +502,7 @@ namespace System.ComponentModel } int commaIndex = typeName.IndexOf(','); - Type t = null; + Type? t = null; if (commaIndex == -1) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 99ba0bf213b..9908aa66c33 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel // Hastable of Type -> ReflectedTypeData. ReflectedTypeData contains all // of the type information we have gathered for a given type. // - private Hashtable _typeData; + private Hashtable? _typeData; // This is the signature we look for when creating types that are generic, but // want to know what type they are dealing with. Enums are a good example of this; @@ -37,8 +37,8 @@ namespace System.ComponentModel // This is where we store the various converters, etc for the intrinsic types. // - private static Hashtable s_editorTables; - private static Dictionary s_intrinsicTypeConverters; + private static Hashtable? s_editorTables; + private static Dictionary? s_intrinsicTypeConverters; // For converters, etc that are bound to class attribute data, rather than a class // type, we have special key sentinel values that we put into the hash table. @@ -56,10 +56,10 @@ namespace System.ComponentModel // in. The keys to the property and event caches are types. // The keys to the attribute cache are either MemberInfos or types. // - private static Hashtable s_propertyCache; - private static Hashtable s_eventCache; - private static Hashtable s_attributeCache; - private static Hashtable s_extendedPropertyCache; + private static Hashtable? s_propertyCache; + private static Hashtable? s_eventCache; + private static Hashtable? s_attributeCache; + private static Hashtable? s_extendedPropertyCache; // These are keys we stuff into our object cache. We use this // cache data to store extender provider info for an object. @@ -102,7 +102,7 @@ namespace System.ComponentModel private readonly bool _cacheConverterInstance; - private TypeConverter _converterInstance; + private TypeConverter? _converterInstance; /// /// Creates a new instance of . @@ -247,15 +247,15 @@ namespace System.ComponentModel /// /// CreateInstance implementation. We delegate to Activator. /// - public override object CreateInstance( - IServiceProvider provider, + public override object? CreateInstance( + IServiceProvider? provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, - Type[] argTypes, - object[] args) + Type[]? argTypes, + object[]? args) { Debug.Assert(objectType != null, "Should have arg-checked before coming in here"); - object obj = null; + object? obj = null; if (argTypes != null) { @@ -295,7 +295,7 @@ namespace System.ComponentModel /// type implements a Type constructor, and if it does it invokes that ctor. /// Otherwise, it just tries to create the type. /// - private static object CreateInstance( + private static object? CreateInstance( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, Type callingType) { @@ -308,22 +308,22 @@ namespace System.ComponentModel /// internal AttributeCollection GetAttributes([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetAttributes(); } /// /// Our implementation of GetCache sits on top of IDictionaryService. /// - public override IDictionary GetCache(object instance) + public override IDictionary? GetCache(object instance) { - IComponent comp = instance as IComponent; + IComponent? comp = instance as IComponent; if (comp != null && comp.Site != null) { - IDictionaryService ds = comp.Site.GetService(typeof(IDictionaryService)) as IDictionaryService; + IDictionaryService? ds = comp.Site.GetService(typeof(IDictionaryService)) as IDictionaryService; if (ds != null) { - IDictionary dict = ds.GetValue(s_dictionaryKey) as IDictionary; + IDictionary? dict = ds.GetValue(s_dictionaryKey) as IDictionary; if (dict == null) { dict = new Hashtable(); @@ -339,18 +339,18 @@ namespace System.ComponentModel /// /// Retrieves the class name for our type. /// - internal string GetClassName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) + internal string? GetClassName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetClassName(null); } /// /// Retrieves the component name from the site. /// - internal string GetComponentName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) + internal string? GetComponentName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetComponentName(instance); } @@ -360,9 +360,9 @@ namespace System.ComponentModel /// will be used. /// [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered. The Type of instance cannot be statically discovered.")] - internal TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) + internal TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetConverter(instance); } @@ -371,9 +371,9 @@ namespace System.ComponentModel /// presence of a DefaultEventAttribute on the class. /// [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] - internal EventDescriptor GetDefaultEvent([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) + internal EventDescriptor? GetDefaultEvent([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetDefaultEvent(instance); } @@ -381,9 +381,9 @@ namespace System.ComponentModel /// Return the default property. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered.")] - internal PropertyDescriptor GetDefaultProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) + internal PropertyDescriptor? GetDefaultProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetDefaultProperty(instance); } @@ -391,9 +391,9 @@ namespace System.ComponentModel /// Retrieves the editor for the given base type. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] - internal object GetEditor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance, Type editorBaseType) + internal object? GetEditor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object? instance, Type editorBaseType) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetEditor(instance, editorBaseType); } @@ -401,10 +401,10 @@ namespace System.ComponentModel /// Retrieves a default editor table for the given editor base type. /// [RequiresUnreferencedCode("The Types specified in EditorTables may be trimmed, or have their static construtors trimmed.")] - private static Hashtable GetEditorTable(Type editorBaseType) + private static Hashtable? GetEditorTable(Type editorBaseType) { Hashtable editorTables = EditorTables; - object table = editorTables[editorBaseType]; + object? table = editorTables[editorBaseType]; if (table == null) { @@ -441,7 +441,7 @@ namespace System.ComponentModel table = null; } - return (Hashtable)table; + return (Hashtable?)table; } /// @@ -449,7 +449,7 @@ namespace System.ComponentModel /// internal EventDescriptorCollection GetEvents([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetEvents(); } @@ -466,7 +466,7 @@ namespace System.ComponentModel /// Retrieves the class name for our type. /// [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] - internal string GetExtendedClassName(object instance) + internal string? GetExtendedClassName(object instance) { return GetClassName(instance.GetType()); } @@ -475,7 +475,7 @@ namespace System.ComponentModel /// Retrieves the component name from the site. /// [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] - internal string GetExtendedComponentName(object instance) + internal string? GetExtendedComponentName(object instance) { return GetComponentName(instance.GetType(), instance); } @@ -495,7 +495,7 @@ namespace System.ComponentModel /// Return the default event. The default event is determined by the /// presence of a DefaultEventAttribute on the class. /// - internal EventDescriptor GetExtendedDefaultEvent(object instance) + internal EventDescriptor? GetExtendedDefaultEvent(object instance) { return null; // we don't support extended events. } @@ -503,7 +503,7 @@ namespace System.ComponentModel /// /// Return the default property. /// - internal PropertyDescriptor GetExtendedDefaultProperty(object instance) + internal PropertyDescriptor? GetExtendedDefaultProperty(object instance) { return null; // extender properties are never the default. } @@ -512,7 +512,7 @@ namespace System.ComponentModel /// Retrieves the editor for the given base type. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] - internal object GetExtendedEditor(object instance, Type editorBaseType) + internal object? GetExtendedEditor(object instance, Type editorBaseType) { return GetEditor(instance.GetType(), instance, editorBaseType); } @@ -544,7 +544,7 @@ namespace System.ComponentModel // cache. // IExtenderProvider[] extenders = GetExtenderProviders(instance); - IDictionary cache = TypeDescriptor.GetCache(instance); + IDictionary? cache = TypeDescriptor.GetCache(instance); if (extenders.Length == 0) { @@ -555,7 +555,7 @@ namespace System.ComponentModel // are properties already in our object cache. If there aren't, // then we will need to create them. // - PropertyDescriptorCollection properties = null; + PropertyDescriptorCollection? properties = null; if (cache != null) { @@ -570,7 +570,7 @@ namespace System.ComponentModel // Unlike normal properties, it is fine for there to be properties with // duplicate names here. // - List propertyList = null; + List? propertyList = null; for (int idx = 0; idx < extenders.Length; idx++) { @@ -584,12 +584,12 @@ namespace System.ComponentModel for (int propIdx = 0; propIdx < propertyArray.Length; propIdx++) { PropertyDescriptor prop = propertyArray[propIdx]; - ExtenderProvidedPropertyAttribute eppa = prop.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; + ExtenderProvidedPropertyAttribute? eppa = prop.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; Debug.Assert(eppa != null, $"Extender property {prop.Name} has no provider attribute. We will skip it."); if (eppa != null) { - Type receiverType = eppa.ReceiverType; + Type? receiverType = eppa.ReceiverType; if (receiverType != null) { if (receiverType.IsAssignableFrom(componentType)) @@ -630,11 +630,11 @@ namespace System.ComponentModel throw new ArgumentNullException(nameof(instance)); } - IComponent component = instance as IComponent; + IComponent? component = instance as IComponent; if (component != null && component.Site != null) { - IExtenderListService extenderList = component.Site.GetService(typeof(IExtenderListService)) as IExtenderListService; - IDictionary cache = TypeDescriptor.GetCache(instance); + IExtenderListService? extenderList = component.Site.GetService(typeof(IExtenderListService)) as IExtenderListService; + IDictionary? cache = TypeDescriptor.GetCache(instance); if (extenderList != null) { @@ -663,11 +663,11 @@ namespace System.ComponentModel /// save the updated extender list. If there is no /// discrepancy this will simply return the cached list. /// - private static IExtenderProvider[] GetExtenders(ICollection components, object instance, IDictionary cache) + private static IExtenderProvider[] GetExtenders(ICollection components, object instance, IDictionary? cache) { bool newExtenders = false; int extenderCount = 0; - IExtenderProvider[] existingExtenders = null; + IExtenderProvider[]? existingExtenders = null; //CanExtend is expensive. We will remember results of CanExtend for the first 64 extenders and using "long canExtend" as a bit vector. // we want to avoid memory allocation as well so we don't use some more sophisticated data structure like an array of booleans @@ -679,7 +679,7 @@ namespace System.ComponentModel // opaque collection. We spend a great deal of energy here to avoid // copying or allocating memory because this method is called every // time a component is asked for its properties. - IExtenderProvider[] currentExtenders = components as IExtenderProvider[]; + IExtenderProvider[]? currentExtenders = components as IExtenderProvider[]; if (cache != null) { @@ -704,7 +704,7 @@ namespace System.ComponentModel // Performance:We would like to call CanExtend as little as possible therefore we remember its result if (curIdx < maxCanExtendResults) canExtend |= (ulong)1 << curIdx; - if (!newExtenders && (idx >= existingExtenders.Length || currentExtenders[curIdx] != existingExtenders[idx++])) + if (!newExtenders && (idx >= existingExtenders!.Length || currentExtenders[curIdx] != existingExtenders[idx++])) { newExtenders = true; } @@ -715,13 +715,13 @@ namespace System.ComponentModel { foreach (object obj in components) { - IExtenderProvider prov = obj as IExtenderProvider; + IExtenderProvider? prov = obj as IExtenderProvider; if (prov != null && prov.CanExtend(instance)) { extenderCount++; if (curIdx < maxCanExtendResults) canExtend |= (ulong)1 << curIdx; - if (!newExtenders && (idx >= existingExtenders.Length || prov != existingExtenders[idx++])) + if (!newExtenders && (idx >= existingExtenders!.Length || prov != existingExtenders[idx++])) { newExtenders = true; } @@ -758,9 +758,9 @@ namespace System.ComponentModel } else if (extenderCount > 0) { - foreach (var component in components) + foreach (var component in components!) { - IExtenderProvider p = component as IExtenderProvider; + IExtenderProvider? p = component as IExtenderProvider; if (p != null && ((curIdx < maxCanExtendResults && (canExtend & ((ulong)1 << curIdx)) != 0) || (curIdx >= maxCanExtendResults && p.CanExtend(instance)))) @@ -783,7 +783,7 @@ namespace System.ComponentModel } else { - currentExtenders = existingExtenders; + currentExtenders = existingExtenders!; } return currentExtenders; } @@ -818,10 +818,10 @@ namespace System.ComponentModel /// GetComponentName. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public override string GetFullComponentName(object component) + public override string? GetFullComponentName(object component) { - IComponent comp = component as IComponent; - INestedSite ns = comp?.Site as INestedSite; + IComponent? comp = component as IComponent; + INestedSite? ns = comp?.Site as INestedSite; if (ns != null) { return ns.FullName; @@ -840,7 +840,7 @@ namespace System.ComponentModel lock (s_internalSyncObject) { - Hashtable typeData = _typeData; + Hashtable? typeData = _typeData; if (typeData != null) { // Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations. @@ -849,7 +849,7 @@ namespace System.ComponentModel { DictionaryEntry de = e.Entry; Type type = (Type)de.Key; - if (type.Module == module && ((ReflectedTypeData)de.Value).IsPopulated) + if (type.Module == module && ((ReflectedTypeData)de.Value!).IsPopulated) { typeList.Add(type); } @@ -866,7 +866,7 @@ namespace System.ComponentModel [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] internal PropertyDescriptorCollection GetProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { - ReflectedTypeData td = GetTypeData(type, true); + ReflectedTypeData td = GetTypeData(type, true)!; return td.GetProperties(); } @@ -885,7 +885,7 @@ namespace System.ComponentModel [return: DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] public override Type GetReflectionType( [DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] Type objectType, - object instance) + object? instance) { Debug.Assert(objectType != null, "Should have arg-checked before coming in here"); return objectType; @@ -896,13 +896,13 @@ namespace System.ComponentModel /// null if there is no type data for the type yet and /// createIfNeeded is false. /// - private ReflectedTypeData GetTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool createIfNeeded) + private ReflectedTypeData? GetTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool createIfNeeded) { - ReflectedTypeData td = null; + ReflectedTypeData? td = null; if (_typeData != null) { - td = (ReflectedTypeData)_typeData[type]; + td = (ReflectedTypeData?)_typeData[type]; if (td != null) { return td; @@ -913,7 +913,7 @@ namespace System.ComponentModel { if (_typeData != null) { - td = (ReflectedTypeData)_typeData[type]; + td = (ReflectedTypeData?)_typeData[type]; } if (td == null && createIfNeeded) @@ -938,7 +938,7 @@ namespace System.ComponentModel /// interested in providing type information for the object it should /// return null. /// - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { Debug.Fail("This should never be invoked. TypeDescriptionNode should wrap for us."); return null; @@ -950,11 +950,11 @@ namespace System.ComponentModel [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", Justification = "typeName is annotated with DynamicallyAccessedMembers, which will preserve the type. " + "Using the non-assembly qualified type name will still work.")] - private static Type GetTypeFromName( + private static Type? GetTypeFromName( // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { - Type t = Type.GetType(typeName); + Type? t = Type.GetType(typeName); if (t == null) { @@ -986,7 +986,7 @@ namespace System.ComponentModel Justification = "ReflectedTypeData is not being created here, just checking if was already created.")] internal bool IsPopulated(Type type) { - ReflectedTypeData td = GetTypeData(type, createIfNeeded: false); + ReflectedTypeData? td = GetTypeData(type, createIfNeeded: false); if (td != null) { return td.IsPopulated; @@ -1004,7 +1004,7 @@ namespace System.ComponentModel internal static Attribute[] ReflectGetAttributes(Type type) { Hashtable attributeCache = AttributeCache; - Attribute[] attrs = (Attribute[])attributeCache[type]; + Attribute[]? attrs = (Attribute[]?)attributeCache[type]; if (attrs != null) { return attrs; @@ -1012,7 +1012,7 @@ namespace System.ComponentModel lock (s_internalSyncObject) { - attrs = (Attribute[])attributeCache[type]; + attrs = (Attribute[]?)attributeCache[type]; if (attrs == null) { // Get the type's attributes. @@ -1032,7 +1032,7 @@ namespace System.ComponentModel internal static Attribute[] ReflectGetAttributes(MemberInfo member) { Hashtable attributeCache = AttributeCache; - Attribute[] attrs = (Attribute[])attributeCache[member]; + Attribute[]? attrs = (Attribute[]?)attributeCache[member]; if (attrs != null) { return attrs; @@ -1040,7 +1040,7 @@ namespace System.ComponentModel lock (s_internalSyncObject) { - attrs = (Attribute[])attributeCache[member]; + attrs = (Attribute[]?)attributeCache[member]; if (attrs == null) { // Get the member's attributes. @@ -1061,7 +1061,7 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { Hashtable eventCache = EventCache; - EventDescriptor[] events = (EventDescriptor[])eventCache[type]; + EventDescriptor[]? events = (EventDescriptor[]?)eventCache[type]; if (events != null) { return events; @@ -1069,7 +1069,7 @@ namespace System.ComponentModel lock (s_internalSyncObject) { - events = (EventDescriptor[])eventCache[type]; + events = (EventDescriptor[]?)eventCache[type]; if (events == null) { BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance; @@ -1092,7 +1092,7 @@ namespace System.ComponentModel // GetEvents returns events that are on nonpublic types // if those types are from our assembly. Screen these. // - if ((!(eventInfo.DeclaringType.IsPublic || eventInfo.DeclaringType.IsNestedPublic)) && (eventInfo.DeclaringType.Assembly == typeof(ReflectTypeDescriptionProvider).Assembly)) + if ((!(eventInfo.DeclaringType!.IsPublic || eventInfo.DeclaringType.IsNestedPublic)) && (eventInfo.DeclaringType.Assembly == typeof(ReflectTypeDescriptionProvider).Assembly)) { Debug.Fail("Hey, assumption holds true. Rip this assert."); continue; @@ -1143,8 +1143,8 @@ namespace System.ComponentModel [RequiresUnreferencedCode("The type of provider cannot be statically discovered.")] private static PropertyDescriptor[] ReflectGetExtendedProperties(IExtenderProvider provider) { - IDictionary cache = TypeDescriptor.GetCache(provider); - PropertyDescriptor[] properties; + IDictionary? cache = TypeDescriptor.GetCache(provider); + PropertyDescriptor[]? properties; if (cache != null) { @@ -1161,12 +1161,12 @@ namespace System.ComponentModel // Type providerType = provider.GetType(); Hashtable extendedPropertyCache = ExtendedPropertyCache; - ReflectPropertyDescriptor[] extendedProperties = (ReflectPropertyDescriptor[])extendedPropertyCache[providerType]; + ReflectPropertyDescriptor[]? extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType]; if (extendedProperties == null) { lock (s_internalSyncObject) { - extendedProperties = (ReflectPropertyDescriptor[])extendedPropertyCache[providerType]; + extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType]; // Our class-based property store failed as well, so we need to build up the set of // extended properties here. @@ -1178,19 +1178,19 @@ namespace System.ComponentModel foreach (Attribute attr in attributes) { - ProvidePropertyAttribute provideAttr = attr as ProvidePropertyAttribute; + ProvidePropertyAttribute? provideAttr = attr as ProvidePropertyAttribute; if (provideAttr != null) { - Type receiverType = GetTypeFromName(provideAttr.ReceiverTypeName); + Type? receiverType = GetTypeFromName(provideAttr.ReceiverTypeName); if (receiverType != null) { - MethodInfo getMethod = providerType.GetMethod("Get" + provideAttr.PropertyName, new Type[] { receiverType }); + MethodInfo? getMethod = providerType.GetMethod("Get" + provideAttr.PropertyName, new Type[] { receiverType }); if (getMethod != null && !getMethod.IsStatic && getMethod.IsPublic) { - MethodInfo setMethod = providerType.GetMethod("Set" + provideAttr.PropertyName, new Type[] { receiverType, getMethod.ReturnType }); + MethodInfo? setMethod = providerType.GetMethod("Set" + provideAttr.PropertyName, new Type[] { receiverType, getMethod.ReturnType }); if (setMethod != null && (setMethod.IsStatic || !setMethod.IsPublic)) { @@ -1238,7 +1238,7 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { Hashtable propertyCache = PropertyCache; - PropertyDescriptor[] properties = (PropertyDescriptor[])propertyCache[type]; + PropertyDescriptor[]? properties = (PropertyDescriptor[]?)propertyCache[type]; if (properties != null) { return properties; @@ -1246,7 +1246,7 @@ namespace System.ComponentModel lock (s_internalSyncObject) { - properties = (PropertyDescriptor[])propertyCache[type]; + properties = (PropertyDescriptor[]?)propertyCache[type]; if (properties == null) { @@ -1275,8 +1275,8 @@ namespace System.ComponentModel continue; } - MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: false); - MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: false); + MethodInfo? getMethod = propertyInfo.GetGetMethod(nonPublic: false); + MethodInfo? setMethod = propertyInfo.GetSetMethod(nonPublic: false); string name = propertyInfo.Name; // If the property only overrode "set", then we don't @@ -1321,7 +1321,7 @@ namespace System.ComponentModel Justification = "ReflectedTypeData is not being created here, just checking if was already created.")] internal void Refresh(Type type) { - ReflectedTypeData td = GetTypeData(type, createIfNeeded: false); + ReflectedTypeData? td = GetTypeData(type, createIfNeeded: false); td?.Refresh(); } @@ -1335,9 +1335,9 @@ namespace System.ComponentModel /// for the base type, and for the original component type, for fast access. /// [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] - private static object GetIntrinsicTypeEditor(Hashtable table, Type callingType) + private static object? GetIntrinsicTypeEditor(Hashtable table, Type callingType) { - object hashEntry = null; + object? hashEntry = null; // We take a lock on this table. Nothing in this code calls out to // other methods that lock, so it should be fairly safe to grab this @@ -1346,7 +1346,7 @@ namespace System.ComponentModel // lock (table) { - Type baseType = callingType; + Type? baseType = callingType; while (baseType != null && baseType != typeof(object)) { hashEntry = table[baseType]; @@ -1354,7 +1354,7 @@ namespace System.ComponentModel // If the entry is a late-bound type, then try to // resolve it. // - string typeString = hashEntry as string; + string? typeString = hashEntry as string; if (typeString != null) { hashEntry = Type.GetType(typeString); @@ -1382,12 +1382,12 @@ namespace System.ComponentModel while (e.MoveNext()) { DictionaryEntry de = e.Entry; - Type keyType = de.Key as Type; + Type? keyType = de.Key as Type; if (keyType != null && keyType.IsInterface && keyType.IsAssignableFrom(callingType)) { hashEntry = de.Value; - string typeString = hashEntry as string; + string? typeString = hashEntry as string; if (typeString != null) { @@ -1436,7 +1436,7 @@ namespace System.ComponentModel // We can only do this if the object doesn't want a type // in its constructor. // - Type type = hashEntry as Type; + Type? type = hashEntry as Type; if (type != null) { @@ -1465,7 +1465,7 @@ namespace System.ComponentModel // other methods that lock, so it should be fairly safe to grab this lock. lock (IntrinsicTypeConverters) { - if (!IntrinsicTypeConverters.TryGetValue(callingType, out IntrinsicTypeConverterData converterData)) + if (!IntrinsicTypeConverters.TryGetValue(callingType, out IntrinsicTypeConverterData? converterData)) { if (callingType.IsEnum) { @@ -1495,9 +1495,9 @@ namespace System.ComponentModel // We should have fetched converters for these types above. Debug.Assert(callingType != typeof(Uri) && callingType != typeof(CultureInfo)); - Type key = null; + Type? key = null; - Type baseType = callingType.BaseType; + Type? baseType = callingType.BaseType; while (baseType != null && baseType != typeof(object)) { if (baseType == typeof(Uri) || baseType == typeof(CultureInfo)) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs index 590a87023be..7ca5634f7bd 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable enable using System.ComponentModel; using System.Reflection; using System.Reflection.Metadata; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RefreshEventArgs.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RefreshEventArgs.cs index d496d043d18..29178bbbfec 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RefreshEventArgs.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RefreshEventArgs.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with /// the component that has changed. /// - public RefreshEventArgs(object componentChanged) + public RefreshEventArgs(object? componentChanged) { ComponentChanged = componentChanged; TypeChanged = componentChanged?.GetType(); @@ -22,7 +22,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with /// the type of component that has changed. /// - public RefreshEventArgs(Type typeChanged) + public RefreshEventArgs(Type? typeChanged) { TypeChanged = typeChanged; } @@ -30,11 +30,11 @@ namespace System.ComponentModel /// /// Gets the component that has changed its properties, events, or extenders. /// - public object ComponentChanged { get; } + public object? ComponentChanged { get; } /// /// Gets the type that has changed its properties, or events. /// - public Type TypeChanged { get; } + public Type? TypeChanged { get; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RunInstallerAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RunInstallerAttribute.cs index 4346cd94775..affc8dfb0cb 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RunInstallerAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/RunInstallerAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -46,7 +48,7 @@ namespace System.ComponentModel /// public static readonly RunInstallerAttribute Default = No; - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SByteConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SByteConverter.cs index b87c5417d6b..f15ef638cc0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SByteConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SByteConverter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return sbyte.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((sbyte)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SettingsBindableAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SettingsBindableAttribute.cs index 837d4f15801..bd61f39df62 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SettingsBindableAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SettingsBindableAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -30,7 +32,7 @@ namespace System.ComponentModel /// public bool Bindable { get; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SingleConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SingleConverter.cs index bf9d3ad7c61..164f5486ce3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SingleConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/SingleConverter.cs @@ -29,7 +29,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return float.Parse(value, NumberStyles.Float, formatInfo); } @@ -37,7 +37,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((float)value).ToString("R", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/StringConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/StringConverter.cs index e5933b100bd..2421a54a6f6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/StringConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/StringConverter.cs @@ -15,7 +15,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the /// given source type to a string using the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -23,7 +23,7 @@ namespace System.ComponentModel /// /// Converts the specified value object to a string object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { if (value is string) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TimeSpanConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TimeSpanConverter.cs index 236fb98e11e..617a6a2c164 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TimeSpanConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TimeSpanConverter.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel /// convert an object in the given source type to a object using the /// specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -27,7 +27,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object to the given /// destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -36,7 +36,7 @@ namespace System.ComponentModel /// Converts the given object to a /// object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string text) { @@ -61,13 +61,13 @@ namespace System.ComponentModel /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor) && value is TimeSpan) { return new InstanceDescriptor( typeof(TimeSpan).GetMethod(nameof(TimeSpan.Parse), new Type[] { typeof(string) }), - new object[] { value.ToString() } + new object?[] { value.ToString() } ); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs index ce6f1eb1acd..f974e4f2275 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs @@ -12,9 +12,9 @@ namespace System.ComponentModel public class ToolboxItemAttribute : Attribute { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - private Type _toolboxItemType; + private Type? _toolboxItemType; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - private readonly string _toolboxItemTypeName; + private readonly string? _toolboxItemTypeName; /// /// Initializes a new instance of ToolboxItemAttribute and sets the type to @@ -69,7 +69,7 @@ namespace System.ComponentModel /// Gets the toolbox item's type. /// [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - public Type ToolboxItemType + public Type? ToolboxItemType { get { @@ -94,7 +94,7 @@ namespace System.ComponentModel [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public string ToolboxItemTypeName => _toolboxItemTypeName ?? string.Empty; - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemFilterAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemFilterAttribute.cs index 97a37332b83..d6ec8ccf69d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemFilterAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemFilterAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -41,7 +43,7 @@ namespace System.ComponentModel [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public sealed class ToolboxItemFilterAttribute : Attribute { - private string _typeId; + private string? _typeId; /// /// Initializes a new ToolboxItemFilterAttribute with the provide filter string and a filter type of @@ -78,7 +80,7 @@ namespace System.ComponentModel /// public override object TypeId => _typeId ?? (_typeId = GetType().FullName + FilterString); - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == this) { @@ -96,7 +98,7 @@ namespace System.ComponentModel return FilterString.GetHashCode(); } - public override bool Match(object obj) + public override bool Match([NotNullWhen(true)] object? obj) { return obj is ToolboxItemFilterAttribute other && other.FilterString.Equals(FilterString); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs index 2c3c0936760..f19dff841be 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs @@ -25,7 +25,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the given /// source type to the native type of the converter using the context. /// - public virtual bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public virtual bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(InstanceDescriptor); /// @@ -38,7 +38,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object to the given /// destination type using the context. /// - public virtual bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public virtual bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(string); } @@ -46,12 +46,12 @@ namespace System.ComponentModel /// /// Converts the given value to the converter's native type. /// - public object ConvertFrom(object value) => ConvertFrom(null, CultureInfo.CurrentCulture, value); + public object? ConvertFrom(object value) => ConvertFrom(null, CultureInfo.CurrentCulture, value); /// /// Converts the given object to the converter's native type. /// - public virtual object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public virtual object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is InstanceDescriptor instanceDescriptor) { @@ -63,7 +63,7 @@ namespace System.ComponentModel /// /// Converts the given string to the converter's native type using the invariant culture. /// - public object ConvertFromInvariantString(string text) + public object? ConvertFromInvariantString(string text) { return ConvertFromString(null, CultureInfo.InvariantCulture, text); } @@ -71,7 +71,7 @@ namespace System.ComponentModel /// /// Converts the given string to the converter's native type using the invariant culture. /// - public object ConvertFromInvariantString(ITypeDescriptorContext context, string text) + public object? ConvertFromInvariantString(ITypeDescriptorContext? context, string text) { return ConvertFromString(context, CultureInfo.InvariantCulture, text); } @@ -79,12 +79,12 @@ namespace System.ComponentModel /// /// Converts the specified text into an object. /// - public object ConvertFromString(string text) => ConvertFrom(null, null, text); + public object? ConvertFromString(string text) => ConvertFrom(null, null, text); /// /// Converts the specified text into an object. /// - public object ConvertFromString(ITypeDescriptorContext context, string text) + public object? ConvertFromString(ITypeDescriptorContext? context, string text) { return ConvertFrom(context, CultureInfo.CurrentCulture, text); } @@ -92,7 +92,7 @@ namespace System.ComponentModel /// /// Converts the specified text into an object. /// - public object ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string text) + public object? ConvertFromString(ITypeDescriptorContext? context, CultureInfo? culture, string text) { return ConvertFrom(context, culture, text); } @@ -101,7 +101,7 @@ namespace System.ComponentModel /// Converts the given /// value object to the specified destination type using the arguments. /// - public object ConvertTo(object value, Type destinationType) + public object? ConvertTo(object? value, Type destinationType) { return ConvertTo(null, null, value, destinationType); } @@ -110,7 +110,7 @@ namespace System.ComponentModel /// Converts the given value object to /// the specified destination type using the specified context and arguments. /// - public virtual object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public virtual object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -139,7 +139,7 @@ namespace System.ComponentModel /// /// Converts the specified value to a culture-invariant string representation. /// - public string ConvertToInvariantString(object value) + public string? ConvertToInvariantString(object? value) { return ConvertToString(null, CultureInfo.InvariantCulture, value); } @@ -147,7 +147,7 @@ namespace System.ComponentModel /// /// Converts the specified value to a culture-invariant string representation. /// - public string ConvertToInvariantString(ITypeDescriptorContext context, object value) + public string? ConvertToInvariantString(ITypeDescriptorContext? context, object? value) { return ConvertToString(context, CultureInfo.InvariantCulture, value); } @@ -155,31 +155,31 @@ namespace System.ComponentModel /// /// Converts the specified value to a string representation. /// - public string ConvertToString(object value) + public string? ConvertToString(object? value) { - return (string)ConvertTo(null, CultureInfo.CurrentCulture, value, typeof(string)); + return (string?)ConvertTo(null, CultureInfo.CurrentCulture, value, typeof(string)); } /// /// Converts the specified value to a string representation. /// - public string ConvertToString(ITypeDescriptorContext context, object value) + public string? ConvertToString(ITypeDescriptorContext? context, object? value) { - return (string)ConvertTo(context, CultureInfo.CurrentCulture, value, typeof(string)); + return (string?)ConvertTo(context, CultureInfo.CurrentCulture, value, typeof(string)); } /// /// Converts the specified value to a string representation. /// - public string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, object value) + public string? ConvertToString(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { - return (string)ConvertTo(context, culture, value, typeof(string)); + return (string?)ConvertTo(context, culture, value, typeof(string)); } /// /// Re-creates an given a set of property values for the object. /// - public object CreateInstance(IDictionary propertyValues) + public object? CreateInstance(IDictionary propertyValues) { return CreateInstance(null, propertyValues); } @@ -187,14 +187,14 @@ namespace System.ComponentModel /// /// Re-creates an given a set of property values for the object. /// - public virtual object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) => null; + public virtual object? CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) => null; /// /// Gets a suitable exception to throw when a conversion cannot be performed. /// - protected Exception GetConvertFromException(object value) + protected Exception GetConvertFromException(object? value) { - string valueTypeName = value == null ? SR.Null : value.GetType().FullName; + string? valueTypeName = value == null ? SR.Null : value.GetType().FullName; throw new NotSupportedException(SR.Format(SR.ConvertFromException, GetType().Name, valueTypeName)); } @@ -202,9 +202,9 @@ namespace System.ComponentModel /// Retrieves a suitable exception to throw when a conversion cannot /// be performed. /// - protected Exception GetConvertToException(object value, Type destinationType) + protected Exception GetConvertToException(object? value, Type destinationType) { - string valueTypeName = value == null ? SR.Null : value.GetType().FullName; + string? valueTypeName = value == null ? SR.Null : value.GetType().FullName; throw new NotSupportedException(SR.Format(SR.ConvertToException, GetType().Name, valueTypeName, destinationType.FullName)); } @@ -221,13 +221,13 @@ namespace System.ComponentModel /// using the specified context. /// /// - public virtual bool GetCreateInstanceSupported(ITypeDescriptorContext context) => false; + public virtual bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => false; /// /// Gets a collection of properties for the type of array specified by the value parameter. /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] - public PropertyDescriptorCollection GetProperties(object value) => GetProperties(null, value); + public PropertyDescriptorCollection? GetProperties(object value) => GetProperties(null, value); /// /// @@ -237,7 +237,7 @@ namespace System.ComponentModel /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields, typeof(BrowsableAttribute))] - public PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value) + public PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value) { return GetProperties(context, value, new Attribute[] { BrowsableAttribute.Yes }); } @@ -249,7 +249,7 @@ namespace System.ComponentModel /// /// [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public virtual PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public virtual PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { return null; } @@ -262,17 +262,17 @@ namespace System.ComponentModel /// /// Gets a value indicating whether this object supports properties using the specified context. /// - public virtual bool GetPropertiesSupported(ITypeDescriptorContext context) => false; + public virtual bool GetPropertiesSupported(ITypeDescriptorContext? context) => false; /// /// Gets a collection of standard values for the data type this type converter is designed for. /// - public ICollection GetStandardValues() => GetStandardValues(null); + public ICollection? GetStandardValues() => GetStandardValues(null); /// /// Gets a collection of standard values for the data type this type converter is designed for. /// - public virtual StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) => null; + public virtual StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context) => null; /// /// Gets a value indicating whether the collection of standard values returned from @@ -285,7 +285,7 @@ namespace System.ComponentModel /// is an exclusive /// list of possible values, using the specified context. /// - public virtual bool GetStandardValuesExclusive(ITypeDescriptorContext context) => false; + public virtual bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => false; /// /// Gets a value indicating whether this object supports a standard set of values @@ -297,7 +297,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this object supports a standard set of values that can be picked /// from a list using the specified context. /// - public virtual bool GetStandardValuesSupported(ITypeDescriptorContext context) => false; + public virtual bool GetStandardValuesSupported(ITypeDescriptorContext? context) => false; /// /// Gets a value indicating whether the given value object is valid for this type. @@ -307,7 +307,7 @@ namespace System.ComponentModel /// /// Gets a value indicating whether the given value object is valid for this type. /// - public virtual bool IsValid(ITypeDescriptorContext context, object value) + public virtual bool IsValid(ITypeDescriptorContext? context, object value) { bool isValid = true; try @@ -317,7 +317,7 @@ namespace System.ComponentModel // NullableConverter would consider null value as a valid value. if (value == null || CanConvertFrom(context, value.GetType())) { - ConvertFrom(context, CultureInfo.InvariantCulture, value); + ConvertFrom(context, CultureInfo.InvariantCulture, value!); } else { @@ -356,7 +356,7 @@ namespace System.ComponentModel /// /// Initializes a new instance of the class. /// - protected SimplePropertyDescriptor(Type componentType, string name, Type propertyType, Attribute[] attributes) : base(name, attributes) + protected SimplePropertyDescriptor(Type componentType, string name, Type propertyType, Attribute[]? attributes) : base(name, attributes) { ComponentType = componentType; PropertyType = propertyType; @@ -387,13 +387,13 @@ namespace System.ComponentModel /// public override bool CanResetValue(object component) { - DefaultValueAttribute attr = (DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)]; + DefaultValueAttribute? attr = (DefaultValueAttribute?)Attributes[typeof(DefaultValueAttribute)]; if (attr == null) { return false; } - return attr.Value.Equals(GetValue(component)); + return attr.Value!.Equals(GetValue(component)); } /// @@ -401,7 +401,7 @@ namespace System.ComponentModel /// public override void ResetValue(object component) { - DefaultValueAttribute attr = (DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)]; + DefaultValueAttribute? attr = (DefaultValueAttribute?)Attributes[typeof(DefaultValueAttribute)]; if (attr != null) { SetValue(component, attr.Value); @@ -420,14 +420,14 @@ namespace System.ComponentModel public class StandardValuesCollection : ICollection { private readonly ICollection _values; - private Array _valueArray; + private Array? _valueArray; /// /// /// Initializes a new instance of the class. /// /// - public StandardValuesCollection(ICollection values) + public StandardValuesCollection(ICollection? values) { if (values == null) { @@ -465,7 +465,7 @@ namespace System.ComponentModel /// /// Gets the object at the specified index number. /// - public object this[int index] + public object? this[int index] { get { @@ -505,7 +505,7 @@ namespace System.ComponentModel /// Retrieves the synchronization root for this collection. Because we are not synchronized, /// this returns null. /// - object ICollection.SyncRoot => null; + object ICollection.SyncRoot => null!; } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs index 125a3f16ee0..ce66028a1ac 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs @@ -13,8 +13,8 @@ namespace System.ComponentModel /// public abstract class TypeDescriptionProvider { - private readonly TypeDescriptionProvider _parent; - private EmptyCustomTypeDescriptor _emptyDescriptor; + private readonly TypeDescriptionProvider? _parent; + private EmptyCustomTypeDescriptor? _emptyDescriptor; /// /// There are two versions of the constructor for this class. The empty @@ -50,11 +50,11 @@ namespace System.ComponentModel /// parent provider was passed. If a parent provider was passed, this /// method will invoke the parent provider's CreateInstance method. /// - public virtual object CreateInstance( - IServiceProvider provider, + public virtual object? CreateInstance( + IServiceProvider? provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, - Type[] argTypes, - object[] args) + Type[]? argTypes, + object[]? args) { if (_parent != null) { @@ -80,7 +80,7 @@ namespace System.ComponentModel /// The GetCache method returns an instance of this cache. GetCache will return /// null if there is no supported cache for an object. /// - public virtual IDictionary GetCache(object instance) => _parent?.GetCache(instance); + public virtual IDictionary? GetCache(object instance) => _parent?.GetCache(instance); /// /// This method returns an extended custom type descriptor for the given object. @@ -132,7 +132,7 @@ namespace System.ComponentModel /// GetTypeDescriptor.GetComponentName. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public virtual string GetFullComponentName(object component) + public virtual string? GetFullComponentName(object component) { if (_parent != null) { @@ -182,7 +182,7 @@ namespace System.ComponentModel [return: DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] public virtual Type GetReflectionType( [DynamicallyAccessedMembers(TypeDescriptor.ReflectTypesDynamicallyAccessedMembers)] Type objectType, - object instance) + object? instance) { if (_parent != null) { @@ -226,7 +226,7 @@ namespace System.ComponentModel /// interested in providing type information for the object it should /// return base. /// - public ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType) + public ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType) { return GetTypeDescriptor(objectType, null); } @@ -240,7 +240,7 @@ namespace System.ComponentModel /// return base. /// [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] - public ICustomTypeDescriptor GetTypeDescriptor(object instance) + public ICustomTypeDescriptor? GetTypeDescriptor(object instance) { if (instance == null) { @@ -264,7 +264,7 @@ namespace System.ComponentModel /// this method will invoke the parent provider's GetTypeDescriptor /// method. /// - public virtual ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + public virtual ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { if (_parent != null) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index d7082172663..d8fae944f83 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -28,7 +28,7 @@ namespace System.ComponentModel private static readonly WeakHashtable s_providerTable = new WeakHashtable(); // mapping of type or object hash to a provider list private static readonly Hashtable s_providerTypeTable = new Hashtable(); // A direct mapping from type to provider. private static readonly Hashtable s_defaultProviders = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. - private static WeakHashtable s_associationTable; + private static WeakHashtable? s_associationTable; private static int s_metadataVersion; // a version stamp for our metadata. Used by property descriptors to know when to rebuild attributes. // This is an index that we use to create a unique name for a property in the @@ -101,7 +101,7 @@ namespace System.ComponentModel /// /// Occurs when Refreshed is raised for a component. /// - public static event RefreshEventHandler Refreshed; + public static event RefreshEventHandler? Refreshed; /// /// The AddAttributes method allows you to add class-level attributes for a @@ -331,10 +331,10 @@ namespace System.ComponentModel for (int idx = attrs.Length - 1; idx >= 0; idx--) { TypeDescriptionProviderAttribute pa = (TypeDescriptionProviderAttribute)attrs[idx]; - Type providerType = Type.GetType(pa.TypeName); + Type? providerType = Type.GetType(pa.TypeName); if (providerType != null && typeof(TypeDescriptionProvider).IsAssignableFrom(providerType)) { - TypeDescriptionProvider prov = (TypeDescriptionProvider)Activator.CreateInstance(providerType); + TypeDescriptionProvider prov = (TypeDescriptionProvider)Activator.CreateInstance(providerType)!; AddProvider(prov, type); providerAdded = true; } @@ -343,7 +343,7 @@ namespace System.ComponentModel // If we did not add a provider, check the base class. if (!providerAdded) { - Type baseType = type.BaseType; + Type? baseType = type.BaseType; if (baseType != null && baseType != type) { CheckDefaultProvider(baseType); @@ -378,13 +378,13 @@ namespace System.ComponentModel } WeakHashtable associationTable = AssociationTable; - IList associations = (IList)associationTable[primary]; + IList? associations = (IList?)associationTable[primary]; if (associations == null) { lock (associationTable) { - associations = (IList)associationTable[primary]; + associations = (IList?)associationTable[primary]; if (associations == null) { associations = new ArrayList(4); @@ -396,7 +396,7 @@ namespace System.ComponentModel { for (int idx = associations.Count - 1; idx >= 0; idx--) { - WeakReference r = (WeakReference)associations[idx]; + WeakReference r = (WeakReference)associations[idx]!; if (r.IsAlive && r.Target == secondary) { throw new ArgumentException(SR.TypeDescriptorAlreadyAssociated); @@ -439,11 +439,11 @@ namespace System.ComponentModel /// a TypeDescriptionProvider object that is associated with the given /// data type. If it finds one, it will delegate the call to that object. /// - public static object CreateInstance( - IServiceProvider provider, + public static object? CreateInstance( + IServiceProvider? provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, - Type[] argTypes, - object[] args) + Type[]? argTypes, + object[]? args) { if (objectType == null) { @@ -463,7 +463,7 @@ namespace System.ComponentModel } } - object instance = null; + object? instance = null; // See if the provider wants to offer a TypeDescriptionProvider to delegate to. This allows // a caller to have complete control over all object instantiation. @@ -509,7 +509,7 @@ namespace System.ComponentModel { ExtenderProvidedPropertyAttribute attr = (ExtenderProvidedPropertyAttribute) oldPropertyDescriptor.Attributes[ - typeof(ExtenderProvidedPropertyAttribute)]; + typeof(ExtenderProvidedPropertyAttribute)]!; if (attr.ExtenderProperty is ReflectPropertyDescriptor reflectDesc) { @@ -529,9 +529,9 @@ namespace System.ComponentModel /// NULL if it did not need to filter any members. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - private static ArrayList FilterMembers(IList members, Attribute[] attributes) + private static ArrayList? FilterMembers(IList members, Attribute[] attributes) { - ArrayList newMembers = null; + ArrayList? newMembers = null; int memberCount = members.Count; for (int idx = 0; idx < memberCount; idx++) @@ -540,7 +540,7 @@ namespace System.ComponentModel for (int attrIdx = 0; attrIdx < attributes.Length; attrIdx++) { - if (ShouldHideMember((MemberDescriptor)members[idx], attributes[attrIdx])) + if (ShouldHideMember((MemberDescriptor?)members[idx], attributes[attrIdx])) { hide = true; break; @@ -593,7 +593,7 @@ namespace System.ComponentModel { // Check our association table for a match. Hashtable assocTable = AssociationTable; - IList associations = (IList)assocTable?[primary]; + IList? associations = (IList?)assocTable?[primary]; if (associations != null) { lock (associations) @@ -602,8 +602,8 @@ namespace System.ComponentModel { // Look for an associated object that has a type that // matches the given type. - WeakReference weakRef = (WeakReference)associations[idx]; - object secondary = weakRef.Target; + WeakReference weakRef = (WeakReference)associations[idx]!; + object? secondary = weakRef.Target; if (secondary == null) { associations.RemoveAt(idx); @@ -620,17 +620,17 @@ namespace System.ComponentModel // if that designer is a component. if (associatedObject == primary) { - IComponent component = primary as IComponent; + IComponent? component = primary as IComponent; if (component != null) { - ISite site = component.Site; + ISite? site = component.Site; if (site != null && site.DesignMode) { - IDesignerHost host = site.GetService(typeof(IDesignerHost)) as IDesignerHost; + IDesignerHost? host = site.GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { - object designer = host.GetDesigner(component); + object? designer = host.GetDesigner(component); // We only use the designer if it has a compatible class. If we // got here, we're probably hosed because the user just passed in @@ -660,7 +660,7 @@ namespace System.ComponentModel return new AttributeCollection((Attribute[])null); } - AttributeCollection attributes = GetDescriptor(componentType, nameof(componentType)).GetAttributes(); + AttributeCollection attributes = GetDescriptor(componentType, nameof(componentType))!.GetAttributes(); return attributes; } @@ -704,7 +704,7 @@ namespace System.ComponentModel // descriptor or attribute collection to pass through the entire // pipeline without modification. // - ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc); + ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc)!; ICollection results = typeDesc.GetAttributes(); // If we are handed a custom type descriptor we have several choices of action @@ -735,7 +735,7 @@ namespace System.ComponentModel } else { - IDictionary cache = GetCache(component); + IDictionary? cache = GetCache(component); results = PipelineInitialize(PIPELINE_ATTRIBUTES, results, cache); @@ -762,47 +762,47 @@ namespace System.ComponentModel /// /// Helper function to obtain a cache for the given object. /// - internal static IDictionary GetCache(object instance) => NodeFor(instance).GetCache(instance); + internal static IDictionary? GetCache(object instance) => NodeFor(instance).GetCache(instance); /// /// Gets the name of the class for the specified component. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static string GetClassName(object component) => GetClassName(component, false); + public static string? GetClassName(object component) => GetClassName(component, false); /// /// Gets the name of the class for the specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static string GetClassName(object component, bool noCustomTypeDesc) + public static string? GetClassName(object component, bool noCustomTypeDesc) { - return GetDescriptor(component, noCustomTypeDesc).GetClassName(); + return GetDescriptor(component, noCustomTypeDesc)!.GetClassName(); } /// /// Gets the name of the class for the specified type. /// - public static string GetClassName( + public static string? GetClassName( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { - return GetDescriptor(componentType, nameof(componentType)).GetClassName(); + return GetDescriptor(componentType, nameof(componentType))!.GetClassName(); } /// /// The name of the class for the specified component. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static string GetComponentName(object component) => GetComponentName(component, false); + public static string? GetComponentName(object component) => GetComponentName(component, false); /// /// Gets the name of the class for the specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static string GetComponentName(object component, bool noCustomTypeDesc) + public static string? GetComponentName(object component, bool noCustomTypeDesc) { - return GetDescriptor(component, noCustomTypeDesc).GetComponentName(); + return GetDescriptor(component, noCustomTypeDesc)!.GetComponentName(); } /// @@ -818,7 +818,7 @@ namespace System.ComponentModel [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] public static TypeConverter GetConverter(object component, bool noCustomTypeDesc) { - TypeConverter converter = GetDescriptor(component, noCustomTypeDesc).GetConverter(); + TypeConverter converter = GetDescriptor(component, noCustomTypeDesc)!.GetConverter(); return converter; } @@ -828,7 +828,7 @@ namespace System.ComponentModel [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] public static TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { - return GetDescriptor(type, nameof(type)).GetConverter(); + return GetDescriptor(type, nameof(type))!.GetConverter(); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", @@ -838,7 +838,7 @@ namespace System.ComponentModel // This is called by System.ComponentModel.DefaultValueAttribute via reflection. [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] - private static object ConvertFromInvariantString([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string stringValue) + private static object? ConvertFromInvariantString([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string stringValue) { return GetConverter(type).ConvertFromInvariantString(stringValue); } @@ -847,7 +847,7 @@ namespace System.ComponentModel /// Gets the default event for the specified type of component. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - public static EventDescriptor GetDefaultEvent( + public static EventDescriptor? GetDefaultEvent( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { if (componentType == null) @@ -856,21 +856,21 @@ namespace System.ComponentModel return null; } - return GetDescriptor(componentType, nameof(componentType)).GetDefaultEvent(); + return GetDescriptor(componentType, nameof(componentType))!.GetDefaultEvent(); } /// /// Gets the default event for the specified component. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] - public static EventDescriptor GetDefaultEvent(object component) => GetDefaultEvent(component, false); + public static EventDescriptor? GetDefaultEvent(object component) => GetDefaultEvent(component, false); /// /// Gets the default event for a component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] - public static EventDescriptor GetDefaultEvent(object component, bool noCustomTypeDesc) + public static EventDescriptor? GetDefaultEvent(object component, bool noCustomTypeDesc) { if (component == null) { @@ -878,14 +878,14 @@ namespace System.ComponentModel return null; } - return GetDescriptor(component, noCustomTypeDesc).GetDefaultEvent(); + return GetDescriptor(component, noCustomTypeDesc)!.GetDefaultEvent(); } /// /// Gets the default property for the specified type of component. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - public static PropertyDescriptor GetDefaultProperty( + public static PropertyDescriptor? GetDefaultProperty( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { if (componentType == null) @@ -894,21 +894,21 @@ namespace System.ComponentModel return null; } - return GetDescriptor(componentType, nameof(componentType)).GetDefaultProperty(); + return GetDescriptor(componentType, nameof(componentType))!.GetDefaultProperty(); } /// /// Gets the default property for the specified component. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] - public static PropertyDescriptor GetDefaultProperty(object component) => GetDefaultProperty(component, false); + public static PropertyDescriptor? GetDefaultProperty(object component) => GetDefaultProperty(component, false); /// /// Gets the default property for the specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] - public static PropertyDescriptor GetDefaultProperty(object component, bool noCustomTypeDesc) + public static PropertyDescriptor? GetDefaultProperty(object component, bool noCustomTypeDesc) { if (component == null) { @@ -916,14 +916,14 @@ namespace System.ComponentModel return null; } - return GetDescriptor(component, noCustomTypeDesc).GetDefaultProperty(); + return GetDescriptor(component, noCustomTypeDesc)!.GetDefaultProperty(); } /// /// Returns a custom type descriptor for the given type. /// Performs arg checking so callers don't have to. /// - internal static ICustomTypeDescriptor GetDescriptor( + internal static ICustomTypeDescriptor? GetDescriptor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string typeName) { @@ -942,18 +942,18 @@ namespace System.ComponentModel /// descriptor. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - internal static ICustomTypeDescriptor GetDescriptor(object component, bool noCustomTypeDesc) + internal static ICustomTypeDescriptor? GetDescriptor(object component, bool noCustomTypeDesc) { if (component == null) { throw new ArgumentException(nameof(component)); } - ICustomTypeDescriptor desc = NodeFor(component).GetTypeDescriptor(component); - ICustomTypeDescriptor d = component as ICustomTypeDescriptor; + ICustomTypeDescriptor? desc = NodeFor(component).GetTypeDescriptor(component); + ICustomTypeDescriptor? d = component as ICustomTypeDescriptor; if (!noCustomTypeDesc && d != null) { - desc = new MergedTypeDescriptor(d, desc); + desc = new MergedTypeDescriptor(d, desc!); } return desc; @@ -978,7 +978,7 @@ namespace System.ComponentModel /// specified component. /// [RequiresUnreferencedCode(EditorRequiresUnreferencedCode + " The Type of component cannot be statically discovered.")] - public static object GetEditor(object component, Type editorBaseType) + public static object? GetEditor(object component, Type editorBaseType) { return GetEditor(component, editorBaseType, false); } @@ -989,21 +989,21 @@ namespace System.ComponentModel /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode(EditorRequiresUnreferencedCode + " The Type of component cannot be statically discovered.")] - public static object GetEditor(object component, Type editorBaseType, bool noCustomTypeDesc) + public static object? GetEditor(object component, Type editorBaseType, bool noCustomTypeDesc) { if (editorBaseType == null) { throw new ArgumentNullException(nameof(editorBaseType)); } - return GetDescriptor(component, noCustomTypeDesc).GetEditor(editorBaseType); + return GetDescriptor(component, noCustomTypeDesc)!.GetEditor(editorBaseType); } /// /// Gets an editor with the specified base type for the specified type. /// [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] - public static object GetEditor( + public static object? GetEditor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, Type editorBaseType) { @@ -1012,7 +1012,7 @@ namespace System.ComponentModel throw new ArgumentNullException(nameof(editorBaseType)); } - return GetDescriptor(type, nameof(type)).GetEditor(editorBaseType); + return GetDescriptor(type, nameof(type))!.GetEditor(editorBaseType); } /// @@ -1027,7 +1027,7 @@ namespace System.ComponentModel return new EventDescriptorCollection(null, true); } - return GetDescriptor(componentType, nameof(componentType)).GetEvents(); + return GetDescriptor(componentType, nameof(componentType))!.GetEvents(); } /// @@ -1045,11 +1045,11 @@ namespace System.ComponentModel return new EventDescriptorCollection(null, true); } - EventDescriptorCollection events = GetDescriptor(componentType, nameof(componentType)).GetEvents(attributes); + EventDescriptorCollection events = GetDescriptor(componentType, nameof(componentType))!.GetEvents(attributes); if (attributes != null && attributes.Length > 0) { - ArrayList filteredEvents = FilterMembers(events, attributes); + ArrayList? filteredEvents = FilterMembers(events, attributes); if (filteredEvents != null) { var descriptors = new EventDescriptor[filteredEvents.Count]; @@ -1096,7 +1096,7 @@ namespace System.ComponentModel /// [EditorBrowsable(EditorBrowsableState.Advanced)] [RequiresUnreferencedCode("The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public static EventDescriptorCollection GetEvents(object component, Attribute[] attributes, bool noCustomTypeDesc) + public static EventDescriptorCollection GetEvents(object component, Attribute[]? attributes, bool noCustomTypeDesc) { if (component == null) { @@ -1122,7 +1122,7 @@ namespace System.ComponentModel // descriptor or attribute collection to pass through the entire // pipeline without modification. // - ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc); + ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc)!; ICollection results; // If we are handed a custom type descriptor we have several choices of action @@ -1155,7 +1155,7 @@ namespace System.ComponentModel } else { - IDictionary cache = GetCache(component); + IDictionary? cache = GetCache(component); results = typeDesc.GetEvents(attributes); results = PipelineInitialize(PIPELINE_EVENTS, results, cache); ICustomTypeDescriptor extDesc = GetExtendedDescriptor(component); @@ -1187,16 +1187,16 @@ namespace System.ComponentModel /// extender. Failing that it will fall back to a static /// index that is continually incremented. /// - private static string GetExtenderCollisionSuffix(MemberDescriptor member) + private static string? GetExtenderCollisionSuffix(MemberDescriptor member) { - string suffix = null; + string? suffix = null; - ExtenderProvidedPropertyAttribute exAttr = member.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; - IExtenderProvider prov = exAttr?.Provider; + ExtenderProvidedPropertyAttribute? exAttr = member.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; + IExtenderProvider? prov = exAttr?.Provider; if (prov != null) { - string name = null; + string? name = null; if (prov is IComponent component && component.Site != null) { @@ -1222,7 +1222,7 @@ namespace System.ComponentModel /// return a different fully qualified name. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static string GetFullComponentName(object component) + public static string? GetFullComponentName(object component) { if (component == null) { @@ -1232,7 +1232,7 @@ namespace System.ComponentModel return GetProvider(component).GetFullComponentName(component); } - private static Type GetNodeForBaseType(Type searchType) + private static Type? GetNodeForBaseType(Type searchType) { if (searchType.IsInterface) { @@ -1258,7 +1258,7 @@ namespace System.ComponentModel return new PropertyDescriptorCollection(null, true); } - return GetDescriptor(componentType, nameof(componentType)).GetProperties(); + return GetDescriptor(componentType, nameof(componentType))!.GetProperties(); } /// @@ -1268,7 +1268,7 @@ namespace System.ComponentModel [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public static PropertyDescriptorCollection GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, - Attribute[] attributes) + Attribute[]? attributes) { if (componentType == null) { @@ -1276,11 +1276,11 @@ namespace System.ComponentModel return new PropertyDescriptorCollection(null, true); } - PropertyDescriptorCollection properties = GetDescriptor(componentType, nameof(componentType)).GetProperties(attributes); + PropertyDescriptorCollection properties = GetDescriptor(componentType, nameof(componentType))!.GetProperties(attributes); if (attributes != null && attributes.Length > 0) { - ArrayList filteredProperties = FilterMembers(properties, attributes); + ArrayList? filteredProperties = FilterMembers(properties, attributes); if (filteredProperties != null) { var descriptors = new PropertyDescriptor[filteredProperties.Count]; @@ -1317,7 +1317,7 @@ namespace System.ComponentModel /// as a filter. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public static PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes) + public static PropertyDescriptorCollection GetProperties(object component, Attribute[]? attributes) { return GetProperties(component, attributes, false); } @@ -1328,7 +1328,7 @@ namespace System.ComponentModel /// as a filter. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public static PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes, bool noCustomTypeDesc) + public static PropertyDescriptorCollection GetProperties(object component, Attribute[]? attributes, bool noCustomTypeDesc) { return GetPropertiesImpl(component, attributes, noCustomTypeDesc, false); } @@ -1339,7 +1339,7 @@ namespace System.ComponentModel /// no attribute filter was passed in (as against passing in null). /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - private static PropertyDescriptorCollection GetPropertiesImpl(object component, Attribute[] attributes, bool noCustomTypeDesc, bool noAttributes) + private static PropertyDescriptorCollection GetPropertiesImpl(object component, Attribute[]? attributes, bool noCustomTypeDesc, bool noAttributes) { if (component == null) { @@ -1365,7 +1365,7 @@ namespace System.ComponentModel // descriptor or attribute collection to pass through the entire // pipeline without modification. // - ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc); + ICustomTypeDescriptor typeDesc = GetDescriptor(component, noCustomTypeDesc)!; ICollection results; // If we are handed a custom type descriptor we have several choices of action @@ -1398,7 +1398,7 @@ namespace System.ComponentModel } else { - IDictionary cache = GetCache(component); + IDictionary? cache = GetCache(component); results = noAttributes ? typeDesc.GetProperties() : typeDesc.GetProperties(attributes); results = PipelineInitialize(PIPELINE_PROPERTIES, results, cache); ICustomTypeDescriptor extDesc = GetExtendedDescriptor(component); @@ -1535,23 +1535,23 @@ namespace System.ComponentModel // an existing one removed, the provider type table is torn // down and automatically rebuilt on demand. // - TypeDescriptionNode node = null; + TypeDescriptionNode? node = null; Type searchType = type; while (node == null) { - node = (TypeDescriptionNode)s_providerTypeTable[searchType] ?? - (TypeDescriptionNode)s_providerTable[searchType]; + node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? + (TypeDescriptionNode?)s_providerTable[searchType]; if (node == null) { - Type baseType = GetNodeForBaseType(searchType); + Type? baseType = GetNodeForBaseType(searchType); if (searchType == typeof(object) || baseType == null) { lock (s_providerTable) { - node = (TypeDescriptionNode)s_providerTable[searchType]; + node = (TypeDescriptionNode?)s_providerTable[searchType]; if (node == null) { @@ -1614,7 +1614,7 @@ namespace System.ComponentModel Debug.Assert(instance != null, "Caller should validate"); - TypeDescriptionNode node = (TypeDescriptionNode)s_providerTable[instance]; + TypeDescriptionNode? node = (TypeDescriptionNode?)s_providerTable[instance]; if (node == null) { Type type = instance.GetType(); @@ -1659,9 +1659,9 @@ namespace System.ComponentModel { lock (s_providerTable) { - TypeDescriptionNode head = (TypeDescriptionNode)s_providerTable[key]; - TypeDescriptionNode target = head; - TypeDescriptionNode prev = null; + TypeDescriptionNode? head = (TypeDescriptionNode?)s_providerTable[key]; + TypeDescriptionNode? target = head; + TypeDescriptionNode? prev = null; while (target != null && target.Provider != provider) { @@ -1713,7 +1713,7 @@ namespace System.ComponentModel Type keyType = key as Type ?? key.GetType(); - target.Provider = new DelegatingTypeDescriptionProvider(keyType.BaseType); + target.Provider = new DelegatingTypeDescriptionProvider(keyType.BaseType!); } else { @@ -1732,11 +1732,11 @@ namespace System.ComponentModel /// user-defined filter. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - private static ICollection PipelineAttributeFilter(int pipelineType, ICollection members, Attribute[] filter, object instance, IDictionary cache) + private static ICollection PipelineAttributeFilter(int pipelineType, ICollection members, Attribute[]? filter, object instance, IDictionary? cache) { Debug.Assert(pipelineType != PIPELINE_ATTRIBUTES, "PipelineAttributeFilter is not supported for attributes"); - IList list = members as ArrayList; + IList? list = members as ArrayList; if (filter == null || filter.Length == 0) { @@ -1763,7 +1763,7 @@ namespace System.ComponentModel list = new ArrayList(members); } - ArrayList filterResult = FilterMembers(list, filter); + ArrayList? filterResult = FilterMembers(list, filter); if (filterResult != null) list = filterResult; // And, if we have a cache, store the updated state into it for future reference. @@ -1807,12 +1807,12 @@ namespace System.ComponentModel /// This will use the cache, if available, to store filtered /// metdata. /// - private static ICollection PipelineFilter(int pipelineType, ICollection members, object instance, IDictionary cache) + private static ICollection PipelineFilter(int pipelineType, ICollection members, object instance, IDictionary? cache) { - IComponent component = instance as IComponent; - ITypeDescriptorFilterService componentFilter = null; + IComponent? component = instance as IComponent; + ITypeDescriptorFilterService? componentFilter = null; - ISite site = component?.Site; + ISite? site = component?.Site; if (site != null) { componentFilter = site.GetService(typeof(ITypeDescriptorFilterService)) as ITypeDescriptorFilterService; @@ -1820,7 +1820,7 @@ namespace System.ComponentModel // If we have no filter, there is nothing for us to do. // - IList list = members as ArrayList; + IList? list = members as ArrayList; if (componentFilter == null) { @@ -1855,7 +1855,7 @@ namespace System.ComponentModel { filterTable[attr.TypeId] = attr; } - cacheResults = componentFilter.FilterAttributes(component, filterTable); + cacheResults = componentFilter.FilterAttributes(component!, filterTable); break; case PIPELINE_PROPERTIES: @@ -1881,7 +1881,7 @@ namespace System.ComponentModel // in the table is an extender, so we will // have to check. // - string suffix = GetExtenderCollisionSuffix(desc); + string? suffix = GetExtenderCollisionSuffix(desc); Debug.Assert(suffix != null, "Name collision with non-extender property."); if (suffix != null) { @@ -1890,7 +1890,7 @@ namespace System.ComponentModel // Now, handle the original property. // - MemberDescriptor origDesc = (MemberDescriptor)filterTable[descName]; + MemberDescriptor origDesc = (MemberDescriptor)filterTable[descName]!; suffix = GetExtenderCollisionSuffix(origDesc); if (suffix != null) { @@ -1905,11 +1905,11 @@ namespace System.ComponentModel } if (pipelineType == PIPELINE_PROPERTIES) { - cacheResults = componentFilter.FilterProperties(component, filterTable); + cacheResults = componentFilter.FilterProperties(component!, filterTable); } else { - cacheResults = componentFilter.FilterEvents(component, filterTable); + cacheResults = componentFilter.FilterEvents(component!, filterTable); } break; @@ -2002,7 +2002,7 @@ namespace System.ComponentModel /// This is the first stage in the pipeline. This checks the incoming member collection and if it /// differs from what we have seen in the past, it invalidates all successive pipelines. /// - private static ICollection PipelineInitialize(int pipelineType, ICollection members, IDictionary cache) + private static ICollection PipelineInitialize(int pipelineType, ICollection members, IDictionary? cache) { if (cache != null) { @@ -2042,7 +2042,7 @@ namespace System.ComponentModel /// merges extended metdata with primary metadata, and stores it in /// the cache if it is available. /// - private static ICollection PipelineMerge(int pipelineType, ICollection primary, ICollection secondary, object instance, IDictionary cache) + private static ICollection PipelineMerge(int pipelineType, ICollection primary, ICollection secondary, object instance, IDictionary? cache) { // If there is no secondary collection, there is nothing to merge. if (secondary == null || secondary.Count == 0) @@ -2144,14 +2144,14 @@ namespace System.ComponentModel // This volatility prevents the JIT from making certain optimizations // that could cause this firing pattern to break. Although the likelihood // the JIT makes those changes is mostly theoretical - RefreshEventHandler handler = Volatile.Read(ref Refreshed); + RefreshEventHandler? handler = Volatile.Read(ref Refreshed); handler?.Invoke(new RefreshEventArgs(component)); } private static void RaiseRefresh(Type type) { - RefreshEventHandler handler = Volatile.Read(ref Refreshed); + RefreshEventHandler? handler = Volatile.Read(ref Refreshed); handler?.Invoke(new RefreshEventArgs(type)); } @@ -2191,10 +2191,10 @@ namespace System.ComponentModel while (e.MoveNext()) { DictionaryEntry de = e.Entry; - Type nodeType = de.Key as Type; + Type? nodeType = de.Key as Type; if (nodeType != null && type.IsAssignableFrom(nodeType) || nodeType == typeof(object)) { - TypeDescriptionNode node = (TypeDescriptionNode)de.Value; + TypeDescriptionNode? node = (TypeDescriptionNode?)de.Value; while (node != null && !(node.Provider is ReflectTypeDescriptionProvider)) { found = true; @@ -2225,7 +2225,7 @@ namespace System.ComponentModel // Refresh on an instance). // Now, clear any cached data for the instance. - IDictionary cache = GetCache(component); + IDictionary? cache = GetCache(component); if (found || cache != null) { if (cache != null) @@ -2275,10 +2275,10 @@ namespace System.ComponentModel while (e.MoveNext()) { DictionaryEntry de = e.Entry; - Type nodeType = de.Key as Type; + Type? nodeType = de.Key as Type; if (nodeType != null && type.IsAssignableFrom(nodeType) || nodeType == typeof(object)) { - TypeDescriptionNode node = (TypeDescriptionNode)de.Value; + TypeDescriptionNode? node = (TypeDescriptionNode?)de.Value; while (node != null && !(node.Provider is ReflectTypeDescriptionProvider)) { found = true; @@ -2327,7 +2327,7 @@ namespace System.ComponentModel // each type that is a derived type of the given // object. We will invalidate the metadata at // each of these levels. - Hashtable refreshedTypes = null; + Hashtable? refreshedTypes = null; lock (s_providerTable) { @@ -2336,10 +2336,10 @@ namespace System.ComponentModel while (e.MoveNext()) { DictionaryEntry de = e.Entry; - Type nodeType = de.Key as Type; + Type? nodeType = de.Key as Type; if (nodeType != null && nodeType.Module.Equals(module) || nodeType == typeof(object)) { - TypeDescriptionNode node = (TypeDescriptionNode)de.Value; + TypeDescriptionNode? node = (TypeDescriptionNode?)de.Value; while (node != null && !(node.Provider is ReflectTypeDescriptionProvider)) { if (refreshedTypes == null) @@ -2405,21 +2405,21 @@ namespace System.ComponentModel } [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public static IDesigner CreateDesigner(IComponent component, Type designerBaseType) + public static IDesigner? CreateDesigner(IComponent component, Type designerBaseType) { - Type type = null; - IDesigner result = null; + Type? type = null; + IDesigner? result = null; AttributeCollection attributes = GetAttributes(component); for (int i = 0; i < attributes.Count; i++) { if (attributes[i] is DesignerAttribute designerAttribute) { - Type type2 = Type.GetType(designerAttribute.DesignerBaseTypeName); + Type? type2 = Type.GetType(designerAttribute.DesignerBaseTypeName); if (type2 != null && type2 == designerBaseType) { - ISite site = component.Site; + ISite? site = component.Site; bool flag = false; - ITypeResolutionService typeResolutionService = (ITypeResolutionService)site?.GetService(typeof(ITypeResolutionService)); + ITypeResolutionService? typeResolutionService = (ITypeResolutionService?)site?.GetService(typeof(ITypeResolutionService)); if (typeResolutionService != null) { flag = true; @@ -2438,18 +2438,19 @@ namespace System.ComponentModel } if (type != null) { - result = (IDesigner)Activator.CreateInstance(type); + result = (IDesigner?)Activator.CreateInstance(type); } return result; } [Obsolete("This property has been deprecated. Use a type description provider to supply type information for COM types instead. https://go.microsoft.com/fwlink/?linkid=14202")] - public static IComNativeDescriptorHandler ComNativeDescriptorHandler + [DisallowNull] + public static IComNativeDescriptorHandler? ComNativeDescriptorHandler { get { - TypeDescriptionNode typeDescriptionNode = NodeFor(ComObjectType); - ComNativeDescriptionProvider comNativeDescriptionProvider; + TypeDescriptionNode? typeDescriptionNode = NodeFor(ComObjectType); + ComNativeDescriptionProvider? comNativeDescriptionProvider; do { comNativeDescriptionProvider = (typeDescriptionNode.Provider as ComNativeDescriptionProvider); @@ -2460,7 +2461,7 @@ namespace System.ComponentModel } set { - TypeDescriptionNode typeDescriptionNode = NodeFor(ComObjectType); + TypeDescriptionNode? typeDescriptionNode = NodeFor(ComObjectType); while (typeDescriptionNode != null && !(typeDescriptionNode.Provider is ComNativeDescriptionProvider)) { typeDescriptionNode = typeDescriptionNode.Next; @@ -2491,7 +2492,7 @@ namespace System.ComponentModel } Hashtable assocTable = AssociationTable; - IList associations = (IList)assocTable?[primary]; + IList? associations = (IList?)assocTable?[primary]; if (associations != null) { lock (associations) @@ -2500,8 +2501,8 @@ namespace System.ComponentModel { // Look for an associated object that has a type that // matches the given type. - WeakReference weakRef = (WeakReference)associations[idx]; - object secondaryItem = weakRef.Target; + WeakReference weakRef = (WeakReference)associations[idx]!; + object? secondaryItem = weakRef.Target; if (secondaryItem == null || secondaryItem == secondary) { associations.RemoveAt(idx); @@ -2626,14 +2627,14 @@ namespace System.ComponentModel /// for the attribute matches the passed in attribute. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - private static bool ShouldHideMember(MemberDescriptor member, Attribute attribute) + private static bool ShouldHideMember(MemberDescriptor? member, Attribute? attribute) { if (member == null || attribute == null) { return true; } - Attribute memberAttribute = member.Attributes[attribute.GetType()]; + Attribute? memberAttribute = member.Attributes[attribute.GetType()]; if (memberAttribute == null) { return !attribute.IsDefaultAttribute(); @@ -2679,7 +2680,8 @@ namespace System.ComponentModel /// Implements GetTypeDescriptor. This creates a custom type /// descriptor that walks the linked list for each of its calls. /// - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + [return: NotNullIfNotNull("instance")] + public override ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { if (objectType == null) { @@ -2723,7 +2725,7 @@ namespace System.ComponentModel string ICustomTypeDescriptor.GetClassName() => _handler.GetClassName(_instance); - string ICustomTypeDescriptor.GetComponentName() => null; + string? ICustomTypeDescriptor.GetComponentName() => null; [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter ICustomTypeDescriptor.GetConverter() => _handler.GetConverter(_instance); @@ -2752,7 +2754,7 @@ namespace System.ComponentModel } [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { return _handler.GetEvents(_instance, attributes); } @@ -2764,7 +2766,7 @@ namespace System.ComponentModel } [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { return _handler.GetProperties(_instance, attributes); } @@ -2793,7 +2795,7 @@ namespace System.ComponentModel /// /// Creates a custom type descriptor that replaces the attributes. /// - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { return new AttributeTypeDescriptor(_attrs, base.GetTypeDescriptor(objectType, instance)); } @@ -2809,7 +2811,7 @@ namespace System.ComponentModel /// Creates a new custom type descriptor that can merge /// the provided set of attributes with the existing set. /// - internal AttributeTypeDescriptor(Attribute[] attrs, ICustomTypeDescriptor parent) : base(parent) + internal AttributeTypeDescriptor(Attribute[] attrs, ICustomTypeDescriptor? parent) : base(parent) { _attributeArray = attrs; } @@ -2822,7 +2824,7 @@ namespace System.ComponentModel /// public override AttributeCollection GetAttributes() { - Attribute[] finalAttr = null; + Attribute[]? finalAttr = null; AttributeCollection existing = base.GetAttributes(); Attribute[] newAttrs = _attributeArray; Attribute[] newArray = new Attribute[existing.Count + newAttrs.Length]; @@ -2933,10 +2935,10 @@ namespace System.ComponentModel { public static readonly MemberDescriptorComparer Instance = new MemberDescriptorComparer(); - public int Compare(object left, object right) + public int Compare(object? left, object? right) { - MemberDescriptor leftMember = left as MemberDescriptor; - MemberDescriptor rightMember = right as MemberDescriptor; + MemberDescriptor? leftMember = left as MemberDescriptor; + MemberDescriptor? rightMember = right as MemberDescriptor; return CultureInfo.InvariantCulture.CompareInfo.Compare(leftMember?.Name, rightMember?.Name); } } @@ -2958,11 +2960,12 @@ namespace System.ComponentModel "When System.Windows.Forms is available, the type will be seen by the trimmer and the ctor will be preserved.")] public ComNativeDescriptorProxy() { - Type realComNativeDescriptor = Type.GetType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor, System.Windows.Forms", throwOnError: true); - _comNativeDescriptor = (TypeDescriptionProvider)Activator.CreateInstance(realComNativeDescriptor); + Type realComNativeDescriptor = Type.GetType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor, System.Windows.Forms", throwOnError: true)!; + _comNativeDescriptor = (TypeDescriptionProvider)Activator.CreateInstance(realComNativeDescriptor)!; } - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + [return: NotNullIfNotNull("instance")] + public override ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { return _comNativeDescriptor.GetTypeDescriptor(objectType, instance); } @@ -3004,7 +3007,7 @@ namespace System.ComponentModel /// string ICustomTypeDescriptor.GetClassName() { - string className = _primary.GetClassName() ?? _secondary.GetClassName(); + string? className = _primary.GetClassName() ?? _secondary.GetClassName(); Debug.Assert(className != null, "Someone should have handled this"); return className; @@ -3013,7 +3016,7 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - string ICustomTypeDescriptor.GetComponentName() + string? ICustomTypeDescriptor.GetComponentName() { return _primary.GetComponentName() ?? _secondary.GetComponentName(); } @@ -3034,7 +3037,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() { return _primary.GetDefaultEvent() ?? _secondary.GetDefaultEvent(); } @@ -3043,7 +3046,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() { return _primary.GetDefaultProperty() ?? _secondary.GetDefaultProperty(); } @@ -3052,14 +3055,14 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) { throw new ArgumentNullException(nameof(editorBaseType)); } - object editor = _primary.GetEditor(editorBaseType) ?? _secondary.GetEditor(editorBaseType); + object? editor = _primary.GetEditor(editorBaseType) ?? _secondary.GetEditor(editorBaseType); return editor; } @@ -3079,7 +3082,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { EventDescriptorCollection events = _primary.GetEvents(attributes) ?? _secondary.GetEvents(attributes); @@ -3103,7 +3106,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { PropertyDescriptorCollection properties = _primary.GetProperties(attributes); if (properties == null) @@ -3118,7 +3121,7 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return _primary.GetPropertyOwner(pd) ?? _secondary.GetPropertyOwner(pd); } @@ -3135,7 +3138,7 @@ namespace System.ComponentModel /// private sealed class TypeDescriptionNode : TypeDescriptionProvider { - internal TypeDescriptionNode Next; + internal TypeDescriptionNode? Next; internal TypeDescriptionProvider Provider; /// @@ -3150,11 +3153,11 @@ namespace System.ComponentModel /// Implements CreateInstance. This just walks the linked list /// looking for someone who implements the call. /// - public override object CreateInstance( - IServiceProvider provider, + public override object? CreateInstance( + IServiceProvider? provider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, - Type[] argTypes, - object[] args) + Type[]? argTypes, + object[]? args) { if (objectType == null) { @@ -3181,7 +3184,7 @@ namespace System.ComponentModel /// Implements GetCache. This just walks the linked /// list looking for someone who implements the call. /// - public override IDictionary GetCache(object instance) + public override IDictionary? GetCache(object instance) { if (instance == null) { @@ -3226,7 +3229,7 @@ namespace System.ComponentModel /// GetTypeDescriptor.GetComponentName. /// [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] - public override string GetFullComponentName(object component) + public override string? GetFullComponentName(object component) { if (component == null) { @@ -3243,7 +3246,7 @@ namespace System.ComponentModel [return: DynamicallyAccessedMembers(ReflectTypesDynamicallyAccessedMembers)] public override Type GetReflectionType( [DynamicallyAccessedMembers(ReflectTypesDynamicallyAccessedMembers)] Type objectType, - object instance) + object? instance) { if (objectType == null) { @@ -3267,7 +3270,7 @@ namespace System.ComponentModel /// Implements GetTypeDescriptor. This creates a custom type /// descriptor that walks the linked list for each of its calls. /// - public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object instance) + public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, object? instance) { if (objectType == null) { @@ -3337,7 +3340,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] - string ICustomTypeDescriptor.GetClassName() + string? ICustomTypeDescriptor.GetClassName() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3351,7 +3354,7 @@ namespace System.ComponentModel ICustomTypeDescriptor desc = p.GetExtendedTypeDescriptor(_instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetExtendedTypeDescriptor")); - string name = desc.GetClassName() ?? _instance.GetType().FullName; + string? name = desc.GetClassName() ?? _instance.GetType().FullName; return name; } @@ -3359,7 +3362,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] - string ICustomTypeDescriptor.GetComponentName() + string? ICustomTypeDescriptor.GetComponentName() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3403,7 +3406,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3424,7 +3427,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3444,7 +3447,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) { @@ -3491,7 +3494,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3540,7 +3543,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3595,7 +3598,7 @@ namespace System.ComponentModel private readonly TypeDescriptionNode _node; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _objectType; - private readonly object _instance; + private readonly object? _instance; /// /// Creates a new WalkingTypeDescriptor. @@ -3603,7 +3606,7 @@ namespace System.ComponentModel internal DefaultTypeDescriptor( TypeDescriptionNode node, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type objectType, - object instance) + object? instance) { _node = node; _objectType = objectType; @@ -3626,7 +3629,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); attrs = desc.GetAttributes(); @@ -3640,20 +3643,20 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - string ICustomTypeDescriptor.GetClassName() + string? ICustomTypeDescriptor.GetClassName() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - string name; + string? name; if (p is ReflectTypeDescriptionProvider rp) { name = rp.GetClassName(_objectType); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); name = desc.GetClassName() ?? _objectType.FullName; @@ -3665,20 +3668,20 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - string ICustomTypeDescriptor.GetComponentName() + string? ICustomTypeDescriptor.GetComponentName() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - string name; + string? name; if (p is ReflectTypeDescriptionProvider rp) { name = rp.GetComponentName(_objectType, _instance); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); name = desc.GetComponentName(); @@ -3704,7 +3707,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); converter = desc.GetConverter(); @@ -3719,20 +3722,20 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - EventDescriptor defaultEvent; + EventDescriptor? defaultEvent; if (p is ReflectTypeDescriptionProvider rp) { defaultEvent = rp.GetDefaultEvent(_objectType, _instance); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); defaultEvent = desc.GetDefaultEvent(); @@ -3745,20 +3748,20 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - PropertyDescriptor defaultProperty; + PropertyDescriptor? defaultProperty; if (p is ReflectTypeDescriptionProvider rp) { defaultProperty = rp.GetDefaultProperty(_objectType, _instance); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); defaultProperty = desc.GetDefaultProperty(); @@ -3771,7 +3774,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) { @@ -3782,14 +3785,14 @@ namespace System.ComponentModel // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - object editor; + object? editor; if (p is ReflectTypeDescriptionProvider rp) { editor = rp.GetEditor(_objectType, _instance, editorBaseType); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); editor = desc.GetEditor(editorBaseType); @@ -3814,7 +3817,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); events = desc.GetEvents(); @@ -3829,7 +3832,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3842,7 +3845,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); events = desc.GetEvents(attributes); @@ -3870,7 +3873,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); properties = desc.GetProperties(); @@ -3885,7 +3888,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3898,7 +3901,7 @@ namespace System.ComponentModel } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); properties = desc.GetProperties(attributes); @@ -3912,20 +3915,20 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another // custom type descriptor TypeDescriptionProvider p = _node.Provider; - object owner; + object? owner; if (p is ReflectTypeDescriptionProvider rp) { - owner = rp.GetPropertyOwner(_objectType, _instance, pd); + owner = rp.GetPropertyOwner(_objectType, _instance!, pd); } else { - ICustomTypeDescriptor desc = p.GetTypeDescriptor(_objectType, _instance); + ICustomTypeDescriptor? desc = p.GetTypeDescriptor(_objectType, _instance); if (desc == null) throw new InvalidOperationException(SR.Format(SR.TypeDescriptorProviderError, _node.Provider.GetType().FullName, "GetTypeDescriptor")); owner = desc.GetPropertyOwner(pd) ?? _instance; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs index 492c6a6d223..779fc781923 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel public abstract class TypeListConverter : TypeConverter { private readonly Type[] _types; - private StandardValuesCollection _values; + private StandardValuesCollection? _values; /// /// Initializes a new instance of the class using @@ -28,7 +28,7 @@ namespace System.ComponentModel /// can convert an object in the given source type to an enumeration object using /// the specified context. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -37,7 +37,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object /// to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -45,7 +45,7 @@ namespace System.ComponentModel /// /// Converts the specified value object to an enumeration object. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string) { @@ -64,7 +64,7 @@ namespace System.ComponentModel /// /// Converts the given value object to the specified destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -89,11 +89,11 @@ namespace System.ComponentModel /// /// Gets a collection of standard values for the data type this validator is designed for. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { if (_values == null) { - object[] objTypes; + object[]? objTypes; if (_types != null) { @@ -114,12 +114,12 @@ namespace System.ComponentModel /// Gets a value indicating whether the list of standard values returned from /// is an exclusive list. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true; /// /// Gets a value indicating whether this object supports a standard set of values that can be /// picked from a list using the specified context. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt16Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt16Converter.cs index e97e63d9688..838dcd15c0d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt16Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt16Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return ushort.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((ushort)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt32Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt32Converter.cs index 29f452c4334..1c7b8614823 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt32Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt32Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return uint.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((uint)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt64Converter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt64Converter.cs index 123f222903e..74dd07db73a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt64Converter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UInt64Converter.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// /// Convert the given value to a string using the given formatInfo /// - internal override object FromString(string value, NumberFormatInfo formatInfo) + internal override object FromString(string value, NumberFormatInfo? formatInfo) { return ulong.Parse(value, NumberStyles.Integer, formatInfo); } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// /// Convert the given value from a string using the given formatInfo /// - internal override string ToString(object value, NumberFormatInfo formatInfo) + internal override string ToString(object value, NumberFormatInfo? formatInfo) { return ((ulong)value).ToString("G", formatInfo); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UriTypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UriTypeConverter.cs index c1c5f6d14e3..6324f57626c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UriTypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/UriTypeConverter.cs @@ -19,7 +19,7 @@ namespace System /// Gets a value indicating whether this converter can convert an object in the /// given source type to a Uri. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(Uri) || base.CanConvertFrom(context, sourceType); } @@ -28,7 +28,7 @@ namespace System /// Gets a value indicating whether this converter can /// convert an object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(Uri) || destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -36,7 +36,7 @@ namespace System /// /// Converts the given object to the a Uri. /// - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { if (value is string uriString) { @@ -61,7 +61,7 @@ namespace System /// Converts the given value object to /// the specified destination type using the specified context and arguments. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -72,7 +72,7 @@ namespace System { if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(Uri).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(UriKind) }, null); + ConstructorInfo? ctor = typeof(Uri).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(UriKind) }, null); Debug.Assert(ctor != null, "Couldn't find constructor"); return new InstanceDescriptor(ctor, new object[] { uri.OriginalString, GetUriKind(uri) }); } @@ -91,11 +91,11 @@ namespace System throw GetConvertToException(value, destinationType); } - public override bool IsValid(ITypeDescriptorContext context, object value) + public override bool IsValid(ITypeDescriptorContext? context, object? value) { if (value is string text) { - return Uri.TryCreate(text, UriKind.RelativeOrAbsolute, out Uri uri); + return Uri.TryCreate(text, UriKind.RelativeOrAbsolute, out Uri? uri); } return value is Uri; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs index 32d567c5b3c..616de8a7895 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can convert an object in the /// given source type to a Version. /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(Version) || base.CanConvertFrom(context, sourceType); } @@ -27,7 +27,7 @@ namespace System.ComponentModel /// Gets a value indicating whether this converter can /// convert an object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(Version) || destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } @@ -36,7 +36,7 @@ namespace System.ComponentModel /// Converts the given object to a Version. /// /// is not a valid version string - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string versionString) { @@ -62,7 +62,7 @@ namespace System.ComponentModel /// Converts the given value object to /// the specified destination type using the specified context and arguments. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -73,7 +73,7 @@ namespace System.ComponentModel { if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(Version).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }, null); + ConstructorInfo? ctor = typeof(Version).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }, null); Debug.Assert(ctor != null, "Couldn't find constructor"); return new InstanceDescriptor(ctor, new object[] { version.Major, version.Minor, version.Build, version.Revision }); } @@ -92,7 +92,7 @@ namespace System.ComponentModel return base.ConvertTo(context, culture, value, destinationType); } - public override bool IsValid(ITypeDescriptorContext context, object value) + public override bool IsValid(ITypeDescriptorContext? context, object? value) { if (value is string version) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs index 9931a75e7e6..211ee6781b8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs @@ -24,7 +24,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with /// the specified message and no Help file. /// - public WarningException(string message) : this(message, null, null) + public WarningException(string? message) : this(message, null, null) { } @@ -32,7 +32,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with /// the specified message, and with access to the specified Help file. /// - public WarningException(string message, string helpUrl) : this(message, helpUrl, null) + public WarningException(string? message, string? helpUrl) : this(message, helpUrl, null) { } @@ -41,7 +41,7 @@ namespace System.ComponentModel /// reference to the inner exception that is the cause of this exception. /// FxCop CA1032: Multiple constructors are required to correctly implement a custom exception. /// - public WarningException(string message, Exception innerException) : base(message, innerException) + public WarningException(string? message, Exception? innerException) : base(message, innerException) { } @@ -49,7 +49,7 @@ namespace System.ComponentModel /// Initializes a new instance of the class with the /// specified message, and with access to the specified Help file and topic. /// - public WarningException(string message, string helpUrl, string helpTopic) : base(message) + public WarningException(string? message, string? helpUrl, string? helpTopic) : base(message) { HelpUrl = helpUrl; HelpTopic = helpTopic; @@ -60,19 +60,19 @@ namespace System.ComponentModel /// protected WarningException(SerializationInfo info, StreamingContext context) : base(info, context) { - HelpUrl = (string)info.GetValue("helpUrl", typeof(string)); - HelpTopic = (string)info.GetValue("helpTopic", typeof(string)); + HelpUrl = (string?)info.GetValue("helpUrl", typeof(string)); + HelpTopic = (string?)info.GetValue("helpTopic", typeof(string)); } /// /// Specifies the Help file associated with the warning. This field is read-only. /// - public string HelpUrl { get; } + public string? HelpUrl { get; } /// /// Specifies the Help topic associated with the warning. This field is read-only. /// - public string HelpTopic { get; } + public string? HelpTopic { get; } public override void GetObjectData(SerializationInfo info, StreamingContext context) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs index 4234963d248..1d8da04d89b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs @@ -74,7 +74,7 @@ namespace System.ComponentModel { // Perform a scavenge through our keys, looking // for dead references. - List cleanupList = null; + List? cleanupList = null; foreach (object o in Keys) { if (o is WeakReference wr && !wr.IsAlive) @@ -103,7 +103,7 @@ namespace System.ComponentModel private sealed class WeakKeyComparer : IEqualityComparer { - bool IEqualityComparer.Equals(object x, object y) + bool IEqualityComparer.Equals(object? x, object? y) { if (x == null) { @@ -155,7 +155,7 @@ namespace System.ComponentModel _hashCode = o.GetHashCode(); } - public override bool Equals(object o) + public override bool Equals(object? o) { if (o?.GetHashCode() != _hashCode) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs index b1358ee9a61..1c5629e4643 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs @@ -25,17 +25,17 @@ namespace System.Drawing { } - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -45,7 +45,7 @@ namespace System.Drawing return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -79,12 +79,12 @@ namespace System.Drawing string sep = culture.TextInfo.ListSeparator + " "; TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); - string[] args; + string?[] args; int nArg = 0; if (c.A < 255) { - args = new string[4]; + args = new string?[4]; args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.A); } else @@ -101,8 +101,8 @@ namespace System.Drawing } else if (destinationType == typeof(InstanceDescriptor)) { - MemberInfo member = null; - object[] args = null; + MemberInfo? member = null; + object[]? args = null; if (c.IsEmpty) { @@ -143,12 +143,12 @@ namespace System.Drawing return base.ConvertTo(context, culture, value, destinationType); } - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { return s_valuesLazy.Value; } - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; private sealed class ColorComparer : IComparer { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs index fbabbeef338..7bf0030eccd 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs @@ -12,17 +12,17 @@ namespace System.Drawing { public class PointConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -45,7 +45,7 @@ namespace System.Drawing for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. - values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i])!; } if (values.Length == 2) @@ -61,7 +61,7 @@ namespace System.Drawing return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -81,7 +81,7 @@ namespace System.Drawing TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertFromString will raise exception if value cannot be converted. - var args = new string[] + var args = new string?[] { intConverter.ConvertToString(context, culture, pt.X), intConverter.ConvertToString(context, culture, pt.Y) @@ -90,7 +90,7 @@ namespace System.Drawing } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(Point).GetConstructor(new Type[] { typeof(int), typeof(int) }); + ConstructorInfo? ctor = typeof(Point).GetConstructor(new Type[] { typeof(int), typeof(int) }); if (ctor != null) { return new InstanceDescriptor(ctor, new object[] { pt.X, pt.Y }); @@ -101,15 +101,15 @@ namespace System.Drawing return base.ConvertTo(context, culture, value, destinationType); } - public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { if (propertyValues == null) { throw new ArgumentNullException(nameof(propertyValues)); } - object x = propertyValues["X"]; - object y = propertyValues["Y"]; + object? x = propertyValues["X"]; + object? y = propertyValues["Y"]; if (x == null || y == null || !(x is int) || !(y is int)) { @@ -119,17 +119,17 @@ namespace System.Drawing return new Point((int)x, (int)y); } - public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; private static readonly string[] s_propertySort = { "X", "Y" }; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object? value, Attribute[]? attributes) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Point), attributes); return props.Sort(s_propertySort); } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs index 6b5f2090e0b..9037db51752 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs @@ -12,17 +12,17 @@ namespace System.Drawing { public class RectangleConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -45,7 +45,7 @@ namespace System.Drawing for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. - values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i])!; } if (values.Length != 4) @@ -59,7 +59,7 @@ namespace System.Drawing return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -79,7 +79,7 @@ namespace System.Drawing TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertToString will raise exception if value cannot be converted. - var args = new string[] + var args = new string?[] { intConverter.ConvertToString(context, culture, rect.X), intConverter.ConvertToString(context, culture, rect.Y), @@ -90,7 +90,7 @@ namespace System.Drawing } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(Rectangle).GetConstructor(new Type[] { + ConstructorInfo? ctor = typeof(Rectangle).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int)}); if (ctor != null) @@ -104,17 +104,17 @@ namespace System.Drawing return base.ConvertTo(context, culture, value, destinationType); } - public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { if (propertyValues == null) { throw new ArgumentNullException(nameof(propertyValues)); } - object x = propertyValues["X"]; - object y = propertyValues["Y"]; - object width = propertyValues["Width"]; - object height = propertyValues["Height"]; + object? x = propertyValues["X"]; + object? y = propertyValues["Y"]; + object? width = propertyValues["Width"]; + object? height = propertyValues["Height"]; if (x == null || y == null || width == null || height == null || !(x is int) || !(y is int) || !(width is int) || !(height is int)) @@ -125,17 +125,17 @@ namespace System.Drawing return new Rectangle((int)x, (int)y, (int)width, (int)height); } - public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; private static readonly string[] s_propertySort = { "X", "Y", "Width", "Height" }; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object? value, Attribute[]? attributes) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Rectangle), attributes); return props.Sort(s_propertySort); } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs index c2884b04b79..b19113721a4 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs @@ -12,17 +12,17 @@ namespace System.Drawing { public class SizeConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -45,7 +45,7 @@ namespace System.Drawing for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. - values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i])!; } if (values.Length != 2) @@ -59,7 +59,7 @@ namespace System.Drawing return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -79,7 +79,7 @@ namespace System.Drawing TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertToString will raise exception if value cannot be converted. - var args = new string[] + var args = new string?[] { intConverter.ConvertToString(context, culture, size.Width), intConverter.ConvertToString(context, culture, size.Height) @@ -88,7 +88,7 @@ namespace System.Drawing } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(Size).GetConstructor(new Type[] { typeof(int), typeof(int) }); + ConstructorInfo? ctor = typeof(Size).GetConstructor(new Type[] { typeof(int), typeof(int) }); if (ctor != null) { return new InstanceDescriptor(ctor, new object[] { size.Width, size.Height }); @@ -99,15 +99,15 @@ namespace System.Drawing return base.ConvertTo(context, culture, value, destinationType); } - public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { if (propertyValues == null) { throw new ArgumentNullException(nameof(propertyValues)); } - object width = propertyValues["Width"]; - object height = propertyValues["Height"]; + object? width = propertyValues["Width"]; + object? height = propertyValues["Height"]; if (width == null || height == null || !(width is int) || !(height is int)) { @@ -117,17 +117,17 @@ namespace System.Drawing return new Size((int)width, (int)height); } - public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; private static readonly string[] s_propertySort = { "Width", "Height" }; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Size), attributes); return props.Sort(s_propertySort); } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs index 25f038d73ef..c48c0f4cd66 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs @@ -12,17 +12,17 @@ namespace System.Drawing { public class SizeFConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { @@ -44,7 +44,7 @@ namespace System.Drawing TypeConverter floatConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(float)); for (int i = 0; i < values.Length; i++) { - values[i] = (float)floatConverter.ConvertFromString(context, culture, tokens[i]); + values[i] = (float)floatConverter.ConvertFromString(context, culture, tokens[i])!; } if (values.Length != 2) @@ -58,7 +58,7 @@ namespace System.Drawing return base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -76,7 +76,7 @@ namespace System.Drawing string sep = culture.TextInfo.ListSeparator + " "; TypeConverter floatConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(float)); - var args = new string[] + var args = new string?[] { floatConverter.ConvertToString(context, culture, size.Width), floatConverter.ConvertToString(context, culture, size.Height) @@ -85,7 +85,7 @@ namespace System.Drawing } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ctor = typeof(SizeF).GetConstructor(new Type[] { typeof(float), typeof(float) }); + ConstructorInfo? ctor = typeof(SizeF).GetConstructor(new Type[] { typeof(float), typeof(float) }); if (ctor != null) { return new InstanceDescriptor(ctor, new object[] { size.Width, size.Height }); @@ -96,15 +96,15 @@ namespace System.Drawing return base.ConvertTo(context, culture, value, destinationType); } - public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { if (propertyValues == null) { throw new ArgumentNullException(nameof(propertyValues)); } - object width = propertyValues["Width"]; - object height = propertyValues["Height"]; + object? width = propertyValues["Width"]; + object? height = propertyValues["Height"]; if (width == null || height == null || !(width is float) || !(height is float)) { @@ -114,17 +114,17 @@ namespace System.Drawing return new SizeF((float)width, (float)height); } - public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) => true; + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; private static readonly string[] s_propertySort = { "Width", "Height" }; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] - public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(SizeF), attributes); return props.Sort(s_propertySort); } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs index 241b41bc0f6..d7556a1db4e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs @@ -16,7 +16,7 @@ namespace System _compareInfo = CultureInfo.InvariantCulture.CompareInfo; } - public int Compare(object a, object b) + public int Compare(object? a, object? b) { if (a is string sa && b is string sb) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverter.cs index 522a5b984ce..652e1b92acd 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicyTypeConverter.cs @@ -12,20 +12,20 @@ namespace System.Security.Authentication.ExtendedProtection { public class ExtendedProtectionPolicyTypeConverter : TypeConverter { - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); } [UnsupportedOSPlatform("browser")] - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { if (value is ExtendedProtectionPolicy policy) { Type[] parameterTypes; - object[] parameterValues; + object?[] parameterValues; if (policy.PolicyEnforcement == PolicyEnforcement.Never) { @@ -36,17 +36,17 @@ namespace System.Security.Authentication.ExtendedProtection { parameterTypes = new Type[] { typeof(PolicyEnforcement), typeof(ProtectionScenario), typeof(ICollection) }; - object[] customServiceNames = null; + object[]? customServiceNames = null; if (policy.CustomServiceNames?.Count > 0) { customServiceNames = new object[policy.CustomServiceNames.Count]; ((ICollection)policy.CustomServiceNames).CopyTo(customServiceNames, 0); } - parameterValues = new object[] { policy.PolicyEnforcement, policy.ProtectionScenario, customServiceNames }; + parameterValues = new object?[] { policy.PolicyEnforcement, policy.ProtectionScenario, customServiceNames }; } - ConstructorInfo constructor = typeof(ExtendedProtectionPolicy).GetConstructor(parameterTypes); + ConstructorInfo? constructor = typeof(ExtendedProtectionPolicy).GetConstructor(parameterTypes); return new InstanceDescriptor(constructor, parameterValues); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/ElapsedEventHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/ElapsedEventHandler.cs index 506e84d3323..335725e17b2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/ElapsedEventHandler.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/ElapsedEventHandler.cs @@ -3,5 +3,5 @@ namespace System.Timers { - public delegate void ElapsedEventHandler(object sender, ElapsedEventArgs e); + public delegate void ElapsedEventHandler(object? sender, ElapsedEventArgs e); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs index 6812ef00011..b7e8fc9bf31 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs @@ -4,6 +4,7 @@ using System.Threading; using System.ComponentModel; using System.ComponentModel.Design; +using System.Diagnostics; namespace System.Timers { @@ -17,13 +18,13 @@ namespace System.Timers private bool _enabled; private bool _initializing; private bool _delayedEnable; - private ElapsedEventHandler _onIntervalElapsed; + private ElapsedEventHandler? _onIntervalElapsed; private bool _autoReset; - private ISynchronizeInvoke _synchronizingObject; + private ISynchronizeInvoke? _synchronizingObject; private bool _disposed; - private Threading.Timer _timer; + private Threading.Timer? _timer; private readonly TimerCallback _callback; - private object _cookie; + private object? _cookie; /// /// Initializes a new instance of the class, with the properties @@ -141,6 +142,7 @@ namespace System.Timers private void UpdateTimer() { + Debug.Assert(_timer != null, $"{nameof(_timer)} was expected not to be null"); int i = (int)Math.Ceiling(_interval); _timer.Change(i, _autoReset ? i : Timeout.Infinite); } @@ -182,7 +184,7 @@ namespace System.Timers /// /// Sets the enable property in design mode to true by default. /// - public override ISite Site + public override ISite? Site { get => base.Site; set @@ -201,14 +203,14 @@ namespace System.Timers /// an interval has elapsed. /// [DefaultValue(null), TimersDescription(nameof(SR.TimerSynchronizingObject), null)] - public ISynchronizeInvoke SynchronizingObject + public ISynchronizeInvoke? SynchronizingObject { get { if (_synchronizingObject == null && DesignMode) { - IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); - object baseComponent = host?.RootComponent; + IDesignerHost? host = (IDesignerHost?)GetService(typeof(IDesignerHost)); + object? baseComponent = host?.RootComponent; if (baseComponent != null && baseComponent is ISynchronizeInvoke) { _synchronizingObject = (ISynchronizeInvoke)baseComponent; @@ -276,7 +278,7 @@ namespace System.Timers Enabled = false; } - private void MyTimerCallback(object state) + private void MyTimerCallback(object? state) { // System.Threading.Timer will not cancel the work item queued before the timer is stopped. // We don't want to handle the callback after a timer is stopped. @@ -294,7 +296,7 @@ namespace System.Timers try { // To avoid race between remove handler and raising the event - ElapsedEventHandler intervalElapsed = _onIntervalElapsed; + ElapsedEventHandler? intervalElapsed = _onIntervalElapsed; if (intervalElapsed != null) { if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs index 1f991215906..8660f3b5658 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs @@ -24,7 +24,7 @@ namespace System.Timers /// /// Constructs a new localized sys description. /// - internal TimersDescriptionAttribute(string description, string unused) : base(SR.GetResourceString(description)) + internal TimersDescriptionAttribute(string description, string? unused) : base(SR.GetResourceString(description)) { // Needed for overload resolution Debug.Assert(unused == null); diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index 5a2e754ba36..10041da15ce 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -450,10 +450,8 @@ namespace System.Data public object this[string property] { get { throw null; } set { } } public System.Data.DataRow Row { get { throw null; } } public System.Data.DataRowVersion RowVersion { get { throw null; } } -#nullable disable string System.ComponentModel.IDataErrorInfo.Error { get { throw null; } } string System.ComponentModel.IDataErrorInfo.this[string colName] { get { throw null; } } -#nullable enable public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged { add { } remove { } } public void BeginEdit() { } public void CancelEdit() { } @@ -465,7 +463,6 @@ namespace System.Data public void EndEdit() { } public override bool Equals(object? other) { throw null; } public override int GetHashCode() { throw null; } -#nullable disable System.ComponentModel.AttributeCollection System.ComponentModel.ICustomTypeDescriptor.GetAttributes() { throw null; } string System.ComponentModel.ICustomTypeDescriptor.GetClassName() { throw null; } string System.ComponentModel.ICustomTypeDescriptor.GetComponentName() { throw null; } @@ -479,13 +476,12 @@ namespace System.Data object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[] attributes) { throw null; } + System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[] attributes) { throw null; } + System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } -#nullable enable } [System.ComponentModel.DefaultPropertyAttribute("DataSetName")] [System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.Data.VS.DataSetDesigner, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] @@ -529,12 +525,10 @@ namespace System.Data [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] public virtual System.Data.SchemaSerializationMode SchemaSerializationMode { get { throw null; } set { } } -#nullable disable [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] - public override System.ComponentModel.ISite Site { get { throw null; } set { } } + public override System.ComponentModel.ISite? Site { get { throw null; } set { } } bool System.ComponentModel.IListSource.ContainsListCollection { get { throw null; } } -#nullable enable [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] public System.Data.DataTableCollection Tables { get { throw null; } } public event System.EventHandler? Initialized { add { } remove { } } @@ -619,12 +613,10 @@ namespace System.Data public virtual void Reset() { } protected virtual bool ShouldSerializeRelations() { throw null; } protected virtual bool ShouldSerializeTables() { throw null; } -#nullable disable System.Collections.IList System.ComponentModel.IListSource.GetList() { throw null; } System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } -#nullable enable [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] public void WriteXml(System.IO.Stream? stream) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] @@ -727,9 +719,7 @@ namespace System.Data public System.Data.DataRowCollection Rows { get { throw null; } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] -#nullable disable - public override System.ComponentModel.ISite Site { get { throw null; } set { } } -#nullable enable + public override System.ComponentModel.ISite? Site { get { throw null; } set { } } bool System.ComponentModel.IListSource.ContainsListCollection { get { throw null; } } [System.ComponentModel.DefaultValueAttribute("")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -818,10 +808,8 @@ namespace System.Data public System.Data.DataRow[] Select(string? filterExpression, string? sort, System.Data.DataViewRowState recordStates) { throw null; } System.Collections.IList System.ComponentModel.IListSource.GetList() { throw null; } System.Xml.Schema.XmlSchema? System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } -#nullable disable void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } -#nullable enable public override string ToString() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] public void WriteXml(System.IO.Stream? stream) { } @@ -1006,21 +994,19 @@ namespace System.Data bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } object? System.Collections.IList.this[int recordIndex] { get { throw null; } set { } } -#nullable disable bool System.ComponentModel.IBindingList.AllowEdit { get { throw null; } } bool System.ComponentModel.IBindingList.AllowNew { get { throw null; } } bool System.ComponentModel.IBindingList.AllowRemove { get { throw null; } } bool System.ComponentModel.IBindingList.IsSorted { get { throw null; } } System.ComponentModel.ListSortDirection System.ComponentModel.IBindingList.SortDirection { get { throw null; } } - System.ComponentModel.PropertyDescriptor System.ComponentModel.IBindingList.SortProperty { get { throw null; } } + System.ComponentModel.PropertyDescriptor? System.ComponentModel.IBindingList.SortProperty { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsChangeNotification { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSearching { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSorting { get { throw null; } } - string System.ComponentModel.IBindingListView.Filter { get { throw null; } set { } } + string? System.ComponentModel.IBindingListView.Filter { get { throw null; } set { } } System.ComponentModel.ListSortDescriptionCollection System.ComponentModel.IBindingListView.SortDescriptions { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsAdvancedSorting { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsFiltering { get { throw null; } } -#nullable enable public event System.EventHandler? Initialized { add { } remove { } } public event System.ComponentModel.ListChangedEventHandler? ListChanged { add { } remove { } } public virtual System.Data.DataRowView AddNew() { throw null; } @@ -1048,9 +1034,8 @@ namespace System.Data void System.Collections.IList.Insert(int index, object? value) { } void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } -#nullable disable void System.ComponentModel.IBindingList.AddIndex(System.ComponentModel.PropertyDescriptor property) { } - object System.ComponentModel.IBindingList.AddNew() { throw null; } + object? System.ComponentModel.IBindingList.AddNew() { throw null; } void System.ComponentModel.IBindingList.ApplySort(System.ComponentModel.PropertyDescriptor property, System.ComponentModel.ListSortDirection direction) { } int System.ComponentModel.IBindingList.Find(System.ComponentModel.PropertyDescriptor property, object key) { throw null; } void System.ComponentModel.IBindingList.RemoveIndex(System.ComponentModel.PropertyDescriptor property) { } @@ -1059,7 +1044,6 @@ namespace System.Data void System.ComponentModel.IBindingListView.RemoveFilter() { } System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ITypedList.GetItemProperties(System.ComponentModel.PropertyDescriptor[] listAccessors) { throw null; } string System.ComponentModel.ITypedList.GetListName(System.ComponentModel.PropertyDescriptor[] listAccessors) { throw null; } -#nullable enable public System.Data.DataTable ToTable() { throw null; } public System.Data.DataTable ToTable(bool distinct, params string[] columnNames) { throw null; } public System.Data.DataTable ToTable(string? tableName) { throw null; } @@ -1084,17 +1068,15 @@ namespace System.Data bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } object? System.Collections.IList.this[int index] { get { throw null; } set { } } -#nullable disable bool System.ComponentModel.IBindingList.AllowEdit { get { throw null; } } bool System.ComponentModel.IBindingList.AllowNew { get { throw null; } } bool System.ComponentModel.IBindingList.AllowRemove { get { throw null; } } bool System.ComponentModel.IBindingList.IsSorted { get { throw null; } } System.ComponentModel.ListSortDirection System.ComponentModel.IBindingList.SortDirection { get { throw null; } } - System.ComponentModel.PropertyDescriptor System.ComponentModel.IBindingList.SortProperty { get { throw null; } } + System.ComponentModel.PropertyDescriptor? System.ComponentModel.IBindingList.SortProperty { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsChangeNotification { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSearching { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSorting { get { throw null; } } -#nullable enable public event System.ComponentModel.ListChangedEventHandler? ListChanged { add { } remove { } } public System.Data.DataView CreateDataView(System.Data.DataTable table) { throw null; } protected virtual void OnListChanged(System.ComponentModel.ListChangedEventArgs e) { } @@ -1108,16 +1090,14 @@ namespace System.Data void System.Collections.IList.Insert(int index, object? value) { } void System.Collections.IList.Remove(object? value) { } void System.Collections.IList.RemoveAt(int index) { } -#nullable disable void System.ComponentModel.IBindingList.AddIndex(System.ComponentModel.PropertyDescriptor property) { } - object System.ComponentModel.IBindingList.AddNew() { throw null; } + object? System.ComponentModel.IBindingList.AddNew() { throw null; } void System.ComponentModel.IBindingList.ApplySort(System.ComponentModel.PropertyDescriptor property, System.ComponentModel.ListSortDirection direction) { } int System.ComponentModel.IBindingList.Find(System.ComponentModel.PropertyDescriptor property, object key) { throw null; } void System.ComponentModel.IBindingList.RemoveIndex(System.ComponentModel.PropertyDescriptor property) { } void System.ComponentModel.IBindingList.RemoveSort() { } System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ITypedList.GetItemProperties(System.ComponentModel.PropertyDescriptor[] listAccessors) { throw null; } string System.ComponentModel.ITypedList.GetListName(System.ComponentModel.PropertyDescriptor[] listAccessors) { throw null; } -#nullable enable protected virtual void TableCollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e) { } } [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DataViewRowStateEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] @@ -2210,23 +2190,23 @@ namespace System.Data.Common void System.Collections.IDictionary.Remove(object keyword) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } System.ComponentModel.AttributeCollection System.ComponentModel.ICustomTypeDescriptor.GetAttributes() { throw null; } - string System.ComponentModel.ICustomTypeDescriptor.GetClassName() { throw null; } - string System.ComponentModel.ICustomTypeDescriptor.GetComponentName() { throw null; } + string? System.ComponentModel.ICustomTypeDescriptor.GetClassName() { throw null; } + string? System.ComponentModel.ICustomTypeDescriptor.GetComponentName() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent() { throw null; } + System.ComponentModel.EventDescriptor? System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] - System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty() { throw null; } + System.ComponentModel.PropertyDescriptor? System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } + object? System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[] attributes) { throw null; } + System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[] attributes) { throw null; } + System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } public override string ToString() { throw null; } public virtual bool TryGetValue(string keyword, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out object? value) { throw null; } @@ -2400,7 +2380,6 @@ namespace System.Data.Common public abstract object GetValue(int i); public abstract int GetValues(object[] values); public abstract bool IsDBNull(int i); -#nullable disable System.ComponentModel.AttributeCollection System.ComponentModel.ICustomTypeDescriptor.GetAttributes() { throw null; } string System.ComponentModel.ICustomTypeDescriptor.GetClassName() { throw null; } string System.ComponentModel.ICustomTypeDescriptor.GetComponentName() { throw null; } @@ -2414,13 +2393,12 @@ namespace System.Data.Common object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[] attributes) { throw null; } + System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(System.Attribute[]? attributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[] attributes) { throw null; } + System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } -#nullable enable } public abstract partial class DbDataSourceEnumerator { diff --git a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs index 60c883d88fe..015e87d0897 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs @@ -58,14 +58,14 @@ namespace System.Data /// /// Gets a value indicating whether this converter can convert an object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); /// /// Converts the given value object to the specified destination type. /// - public override object? ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object? value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -104,11 +104,11 @@ namespace System.Data return base.ConvertTo(context, culture, value, destinationType); } - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string) || base.CanConvertTo(context, sourceType); - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value != null && value.GetType() == typeof(string)) { @@ -123,13 +123,13 @@ namespace System.Data return typeof(string); } - return base.ConvertFrom(context, culture, value); + return base.ConvertFrom(context, culture, value!); } /// /// Gets a collection of standard values for the data type this validator is designed for. /// - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { if (_values == null) { @@ -154,12 +154,12 @@ namespace System.Data /// Gets a value indicating whether the list of standard values returned from /// is an exclusive list. /// - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true; /// /// Gets a value indicating whether this object supports a /// standard set of values that can be picked from a list using the specified context. /// - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs index 619b5baa871..38695b358c1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable nullability as part of annotation System.Data.{Odbc,OleDb} +// TODO-NULLABLE: Enable nullability as part of annotation System.Data.{Odbc,OleDb} #nullable disable using System.Collections; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataColumnMapping.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataColumnMapping.cs index 6c896aae0c5..e75f5197e8e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataColumnMapping.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataColumnMapping.cs @@ -147,7 +147,7 @@ namespace System.Data.Common { } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (typeof(InstanceDescriptor) == destinationType) { @@ -156,7 +156,7 @@ namespace System.Data.Common return base.CanConvertTo(context, destinationType); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (null == destinationType) { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs index ba5f6006da2..c49d8d33ef0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs @@ -307,14 +307,12 @@ namespace System.Data.Common return new AttributeCollection(null); } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable - string ICustomTypeDescriptor.GetClassName() + string? ICustomTypeDescriptor.GetClassName() { return null; } - string ICustomTypeDescriptor.GetComponentName() + string? ICustomTypeDescriptor.GetComponentName() { return null; } @@ -322,27 +320,26 @@ namespace System.Data.Common [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() { - return null; + return null!; } [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() { return null; } [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() { return null; } [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return null; } -#nullable enable EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { @@ -350,7 +347,7 @@ namespace System.Data.Common } [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { return new EventDescriptorCollection(null); } @@ -362,7 +359,7 @@ namespace System.Data.Common } [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { if (_propertyDescriptors == null) { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataTableMapping.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataTableMapping.cs index 5d89922c19a..80dccef3b21 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataTableMapping.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataTableMapping.cs @@ -169,7 +169,7 @@ namespace System.Data.Common { } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (typeof(InstanceDescriptor) == destinationType) { @@ -178,7 +178,7 @@ namespace System.Data.Common return base.CanConvertTo(context, destinationType); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (null == destinationType) { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 027fe42f0b0..10567391a3e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -546,7 +546,7 @@ namespace System.Data.Common bool match = true; foreach (Attribute attribute in attributes) { - Attribute attr = property.Attributes[attribute.GetType()]; + Attribute? attr = property.Attributes[attribute.GetType()]; if ((attr == null && !attribute.IsDefaultAttribute()) || attr?.Match(attribute) == false) { match = false; @@ -569,11 +569,9 @@ namespace System.Data.Common return new PropertyDescriptorCollection(filteredPropertiesArray); } - // TODO-NULLABLE: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] - string ICustomTypeDescriptor.GetClassName() + string? ICustomTypeDescriptor.GetClassName() { // Below call is necessary to tell the trimmer that it should mark derived types appropriately. // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. @@ -582,7 +580,7 @@ namespace System.Data.Common } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] - string ICustomTypeDescriptor.GetComponentName() + string? ICustomTypeDescriptor.GetComponentName() { // Below call is necessary to tell the trimmer that it should mark derived types appropriately. // We cannot use overload which takes type because the result might differ if derived class implements ICustomTypeDescriptor. @@ -596,7 +594,7 @@ namespace System.Data.Common return TypeDescriptor.GetAttributes(this, true); } [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } @@ -606,7 +604,7 @@ namespace System.Data.Common return TypeDescriptor.GetConverter(this, true); } [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(this, true); } @@ -616,12 +614,12 @@ namespace System.Data.Common return GetProperties(); } [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) { return GetProperties(attributes); } [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } @@ -635,7 +633,7 @@ namespace System.Data.Common return TypeDescriptor.GetEvents(this, true); } [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } @@ -643,6 +641,5 @@ namespace System.Data.Common { return this; } -#nullable enable } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs index 1ab0b414920..e1c6cfa3040 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs @@ -25,7 +25,7 @@ namespace System.Data.Common return ((null != builder) && builder.ShouldSerialize(DisplayName)); } - public override object? GetValue(object component) + public override object? GetValue(object? component) { DbConnectionStringBuilder? builder = (component as DbConnectionStringBuilder); if (null != builder) @@ -53,7 +53,7 @@ namespace System.Data.Common } } - public override void SetValue(object component, object? value) + public override void SetValue(object? component, object? value) { DbConnectionStringBuilder? builder = (component as DbConnectionStringBuilder); if (null != builder) diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs index 56e74a8dc9a..f3e1827f885 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs @@ -70,40 +70,37 @@ namespace System.Data.Common // ICustomTypeDescriptor // -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable AttributeCollection ICustomTypeDescriptor.GetAttributes() => new AttributeCollection(null); - string ICustomTypeDescriptor.GetClassName() => null; + string? ICustomTypeDescriptor.GetClassName() => null; - string ICustomTypeDescriptor.GetComponentName() => null; + string? ICustomTypeDescriptor.GetComponentName() => null; [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] - TypeConverter ICustomTypeDescriptor.GetConverter() => null; + TypeConverter ICustomTypeDescriptor.GetConverter() => null!; [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() => null; [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() => null; [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => new EventDescriptorCollection(null); [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => new EventDescriptorCollection(null); + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) => new EventDescriptorCollection(null); [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => ((ICustomTypeDescriptor)this).GetProperties(null); [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => new PropertyDescriptorCollection(null); object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; -#nullable enable } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbEnumerator.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbEnumerator.cs index 35a5ae90997..4c7fbd2c907 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbEnumerator.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbEnumerator.cs @@ -128,14 +128,14 @@ namespace System.Data.Common public override bool CanResetValue(object component) => false; - public override object GetValue(object component) => ((IDataRecord)component)[_ordinal]; + public override object? GetValue(object? component) => ((IDataRecord)component!)[_ordinal]; public override void ResetValue(object component) { throw ADP.NotSupported(); } - public override void SetValue(object component, object value) + public override void SetValue(object? component, object? value) { throw ADP.NotSupported(); } diff --git a/src/libraries/System.Data.Common/src/System/Data/ConstraintConverter.cs b/src/libraries/System.Data.Common/src/System/Data/ConstraintConverter.cs index 7a074f9d342..664ff2d42b4 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ConstraintConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ConstraintConverter.cs @@ -16,7 +16,7 @@ namespace System.Data /// Gets a value indicating whether this converter can /// convert an object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType); @@ -27,7 +27,7 @@ namespace System.Data /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { diff --git a/src/libraries/System.Data.Common/src/System/Data/DataColumnPropertyDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataColumnPropertyDescriptor.cs index d88eb8c6100..a26a31506ad 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataColumnPropertyDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataColumnPropertyDescriptor.cs @@ -4,6 +4,7 @@ using System.Collections; using System.ComponentModel; using System.Data.Common; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -42,7 +43,7 @@ namespace System.Data public override Type PropertyType => Column.DataType; - public override bool Equals(object other) + public override bool Equals([NotNullWhen(true)] object? other) { if (other is DataColumnPropertyDescriptor) { @@ -66,9 +67,9 @@ namespace System.Data return (!DataStorage.IsObjectNull(dataRowView.GetColumnValue(Column))); } - public override object GetValue(object component) + public override object GetValue(object? component) { - DataRowView dataRowView = (DataRowView)component; + DataRowView dataRowView = (DataRowView)component!; return dataRowView.GetColumnValue(Column); } @@ -78,9 +79,9 @@ namespace System.Data dataRowView.SetColumnValue(Column, DBNull.Value); // no need to ccheck for the col type and set Sql...Null! } - public override void SetValue(object component, object? value) + public override void SetValue(object? component, object? value) { - DataRowView dataRowView = (DataRowView)component; + DataRowView dataRowView = (DataRowView)component!; dataRowView.SetColumnValue(Column, value); OnValueChanged(component, EventArgs.Empty); } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRelationPropertyDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataRelationPropertyDescriptor.cs index 2f8590ad8e5..4732907089c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRelationPropertyDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRelationPropertyDescriptor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -20,7 +21,7 @@ namespace System.Data public override Type PropertyType => typeof(IBindingList); - public override bool Equals(object other) + public override bool Equals([NotNullWhen(true)] object? other) { if (other is DataRelationPropertyDescriptor) { @@ -34,15 +35,15 @@ namespace System.Data public override bool CanResetValue(object component) => false; - public override object GetValue(object component) + public override object GetValue(object? component) { - DataRowView dataRowView = (DataRowView)component; + DataRowView dataRowView = (DataRowView)component!; return dataRowView.CreateChildView(Relation); } public override void ResetValue(object component) { } - public override void SetValue(object component, object value) { } + public override void SetValue(object? component, object? value) { } public override bool ShouldSerializeValue(object component) => false; } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowComparer.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowComparer.cs index b3e042a7010..209e8e51909 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowComparer.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowComparer.cs @@ -74,10 +74,8 @@ namespace System.Data return CompareEquatableArray((int[])a, (int[])b); case TypeCode.Int64: return CompareEquatableArray((long[])a, (long[])b); -#nullable disable case TypeCode.String: return CompareEquatableArray((string[])a, (string[])b); -#nullable enable } } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs index 4b86f5f59fa..dc0dd2e7aad 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs @@ -100,13 +100,10 @@ namespace System.Data } } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable // IDataErrorInfo stuff string IDataErrorInfo.this[string colName] => Row.GetColumnError(colName); string IDataErrorInfo.Error => Row.RowError; -#nullable enable /// /// Gets the current version description of the @@ -232,37 +229,35 @@ namespace System.Data internal void RaisePropertyChangedEvent(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); #region ICustomTypeDescriptor -#nullable disable AttributeCollection ICustomTypeDescriptor.GetAttributes() => new AttributeCollection(null); - string ICustomTypeDescriptor.GetClassName() => null; - string ICustomTypeDescriptor.GetComponentName() => null; + string? ICustomTypeDescriptor.GetClassName() => null; + string? ICustomTypeDescriptor.GetComponentName() => null; [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] - TypeConverter ICustomTypeDescriptor.GetConverter() => null; + TypeConverter ICustomTypeDescriptor.GetConverter() => null!; [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() => null; [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() => null; [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => new EventDescriptorCollection(null); [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => new EventDescriptorCollection(null); + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) => new EventDescriptorCollection(null); [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => ((ICustomTypeDescriptor)this).GetProperties(null); [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => (_dataView.Table != null ? _dataView.Table.GetPropertyDescriptorCollection(attributes) : s_zeroPropertyDescriptorCollection); object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; -#nullable enable #endregion } } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index ad2d4e43aa1..39410be0645 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -602,10 +602,7 @@ namespace System.Data } } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable bool IListSource.ContainsListCollection => true; -#nullable enable /// /// Gets a custom view of the data contained by the , one @@ -949,19 +946,17 @@ namespace System.Data return _cultureUserSet; } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public override ISite Site + public override ISite? Site { get { return base.Site; } set { - ISite oldSite = Site; + ISite? oldSite = Site; if (value == null && oldSite != null) { - IContainer cont = oldSite.Container; + IContainer? cont = oldSite.Container; if (cont != null) { @@ -977,7 +972,6 @@ namespace System.Data base.Site = value; } } -#nullable enable /// /// Get the collection of relations that link tables and @@ -1411,10 +1405,7 @@ namespace System.Data } } - // TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable IList IListSource.GetList() => DefaultViewManager; -#nullable enable [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] internal string GetRemotingDiffGram(DataTable table) diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index b01b56b8b9c..be3c05ef7c3 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -2683,18 +2683,16 @@ namespace System.Data } } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public override ISite Site + public override ISite? Site { get { return base.Site; } set { - ISite oldSite = Site; + ISite? oldSite = Site; if (value == null && oldSite != null) { - IContainer cont = oldSite.Container; + IContainer? cont = oldSite.Container; if (cont != null) { @@ -2710,7 +2708,6 @@ namespace System.Data base.Site = value; } } -#nullable enable internal DataRow AddRecords(int oldRecord, int newRecord) { @@ -3197,12 +3194,9 @@ namespace System.Data return ndx; } - // TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable IList IListSource.GetList() => DefaultView; internal List GetListeners() => _dataViewListeners; -#nullable enable // We need a HashCodeProvider for Case, Kana and Width insensitive internal int GetSpecialHashCode(string name) @@ -6708,8 +6702,6 @@ namespace System.Data return XmlSchema.Read(new XmlTextReader(stream), null); } - // TODO: Enable after System.Private.Xml is annotated -#nullable disable #pragma warning disable 8632 void IXmlSerializable.ReadXml(XmlReader reader) { @@ -6733,7 +6725,6 @@ namespace System.Data WriteXmlCore(writer); } #pragma warning restore 8632 -#nullable enable // This method exists so that suppression can be placed on `IXmlSerializable.WriteXml(XmlWriter writer)` private void WriteXmlCore(XmlWriter writer) @@ -6741,6 +6732,7 @@ namespace System.Data WriteXmlSchema(writer, false); WriteXml(writer, XmlWriteMode.DiffGram, false); } +#pragma warning restore 8632 protected virtual void ReadXmlSerializable(XmlReader? reader) => ReadXml(reader, XmlReadMode.DiffGram, true); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTablePropertyDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataTablePropertyDescriptor.cs index 012b174daf0..766f9db78e7 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTablePropertyDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTablePropertyDescriptor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -20,7 +21,7 @@ namespace System.Data public override Type PropertyType => typeof(IBindingList); - public override bool Equals(object other) + public override bool Equals([NotNullWhen(true)] object? other) { if (other is DataTablePropertyDescriptor) { @@ -34,15 +35,15 @@ namespace System.Data public override bool CanResetValue(object component) => false; - public override object GetValue(object component) + public override object GetValue(object? component) { - DataViewManagerListItemTypeDescriptor dataViewManagerListItem = (DataViewManagerListItemTypeDescriptor)component; + DataViewManagerListItemTypeDescriptor dataViewManagerListItem = (DataViewManagerListItemTypeDescriptor)component!; return dataViewManagerListItem.GetDataView(Table); } public override void ResetValue(object component) { } - public override void SetValue(object component, object value) { } + public override void SetValue(object? component, object? value) { } public override bool ShouldSerializeValue(object component) => false; } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTableReaderListener.cs b/src/libraries/System.Data.Common/src/System/Data/DataTableReaderListener.cs index 5008247f62d..1df1ac6fe7b 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTableReaderListener.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTableReaderListener.cs @@ -97,7 +97,7 @@ namespace System.Data } } - private void SchemaChanged(object sender, CollectionChangeEventArgs e) + private void SchemaChanged(object? sender, CollectionChangeEventArgs e) { DataTableReader? reader = (DataTableReader?)_readerWeak.Target; if (reader != null) @@ -110,7 +110,7 @@ namespace System.Data } } - private void DataChanged(object sender, DataRowChangeEventArgs args) + private void DataChanged(object? sender, DataRowChangeEventArgs args) { DataTableReader? reader = (DataTableReader?)_readerWeak.Target; if (reader != null) diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTableTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/DataTableTypeConverter.cs index fddfa5d77c0..ca96c909e27 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTableTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTableTypeConverter.cs @@ -8,6 +8,6 @@ namespace System.Data internal sealed class DataTableTypeConverter : ReferenceConverter { public DataTableTypeConverter() : base(typeof(DataTable)) { } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => false; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => false; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataView.cs b/src/libraries/System.Data.Common/src/System/Data/DataView.cs index ba1e5fa3f54..c72031a8533 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataView.cs @@ -954,8 +954,6 @@ namespace System.Data #endregion #region IBindingList implementation -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable bool IBindingList.AllowNew => AllowNew; object IBindingList.AddNew() => AddNew(); bool IBindingList.AllowEdit => AllowEdit; @@ -966,9 +964,9 @@ namespace System.Data bool IBindingList.SupportsSorting => true; bool IBindingList.IsSorted => Sort.Length != 0; - PropertyDescriptor IBindingList.SortProperty => GetSortProperty(); + PropertyDescriptor? IBindingList.SortProperty => GetSortProperty(); - internal PropertyDescriptor GetSortProperty() + internal PropertyDescriptor? GetSortProperty() { if (_table != null && _index != null && _index._indexFields.Length == 1) { @@ -980,7 +978,6 @@ namespace System.Data ListSortDirection IBindingList.SortDirection => (_index!._indexFields.Length == 1 && _index._indexFields[0].IsDescending) ? ListSortDirection.Descending : ListSortDirection.Ascending; -#nullable enable #endregion #region ListChanged & Initialized events @@ -1085,7 +1082,7 @@ namespace System.Data { throw ExceptionBuilder.ArgumentContainsNull(nameof(sorts)); } - PropertyDescriptor property = sort.PropertyDescriptor; + PropertyDescriptor? property = sort.PropertyDescriptor; if (property == null) { @@ -1128,8 +1125,6 @@ namespace System.Data return resultString.ToString(); } - // TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Safe because filter is set to empty string.")] void IBindingListView.RemoveFilter() @@ -1138,12 +1133,11 @@ namespace System.Data RowFilter = string.Empty; } - string IBindingListView.Filter + string? IBindingListView.Filter { get { return RowFilter; } set { RowFilter = value; } } -#nullable enable ListSortDescriptionCollection IBindingListView.SortDescriptions => GetSortDescriptions(); @@ -1656,40 +1650,40 @@ namespace System.Data } } - internal void ChildRelationCollectionChanged(object sender, CollectionChangeEventArgs e) + internal void ChildRelationCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataRelationPropertyDescriptor? NullProp = null; OnListChanged( - e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : e.Action == CollectionChangeAction.Refresh ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, NullProp) : - e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : /*default*/ null! // TODO: This will cause an NRE ); } - internal void ParentRelationCollectionChanged(object sender, CollectionChangeEventArgs e) + internal void ParentRelationCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataRelationPropertyDescriptor? NullProp = null; OnListChanged( - e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : e.Action == CollectionChangeAction.Refresh ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, NullProp) : - e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : /*default*/ null! // TODO: This will cause an NRE ); } - protected virtual void ColumnCollectionChanged(object sender, CollectionChangeEventArgs e) + protected virtual void ColumnCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataColumnPropertyDescriptor? NullProp = null; OnListChanged( - e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataColumnPropertyDescriptor((System.Data.DataColumn)e.Element)) : + e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataColumnPropertyDescriptor((System.Data.DataColumn)e.Element!)) : e.Action == CollectionChangeAction.Refresh ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, NullProp) : - e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataColumnPropertyDescriptor((System.Data.DataColumn)e.Element)) : + e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataColumnPropertyDescriptor((System.Data.DataColumn)e.Element!)) : /*default*/ null! // TODO: This will cause an NRE ); } - internal void ColumnCollectionChangedInternal(object sender, CollectionChangeEventArgs e) => + internal void ColumnCollectionChangedInternal(object? sender, CollectionChangeEventArgs e) => ColumnCollectionChanged(sender, e); public DataTable ToTable() => diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewListener.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewListener.cs index db8424731ab..940b866b9ba 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewListener.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewListener.cs @@ -23,7 +23,7 @@ namespace System.Data _dvWeak = new WeakReference(dv); } - private void ChildRelationCollectionChanged(object sender, CollectionChangeEventArgs e) + private void ChildRelationCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataView? dv = (DataView?)_dvWeak.Target; if (dv != null) @@ -36,7 +36,7 @@ namespace System.Data } } - private void ParentRelationCollectionChanged(object sender, CollectionChangeEventArgs e) + private void ParentRelationCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataView? dv = (DataView?)_dvWeak.Target; if (dv != null) @@ -49,7 +49,7 @@ namespace System.Data } } - private void ColumnCollectionChanged(object sender, CollectionChangeEventArgs e) + private void ColumnCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataView? dv = (DataView?)_dvWeak.Target; if (dv != null) diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs index 1303b616edb..02da21f71f0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs @@ -326,24 +326,24 @@ namespace System.Data } } - protected virtual void TableCollectionChanged(object sender, CollectionChangeEventArgs e) + protected virtual void TableCollectionChanged(object? sender, CollectionChangeEventArgs e) { PropertyDescriptor? NullProp = null; OnListChanged( - e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataTablePropertyDescriptor((System.Data.DataTable)e.Element)) : + e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataTablePropertyDescriptor((System.Data.DataTable)e.Element!)) : e.Action == CollectionChangeAction.Refresh ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, NullProp) : - e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataTablePropertyDescriptor((System.Data.DataTable)e.Element)) : + e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataTablePropertyDescriptor((System.Data.DataTable)e.Element!)) : /*default*/ null! // TODO: This is very likely wrong ); } - protected virtual void RelationCollectionChanged(object sender, CollectionChangeEventArgs e) + protected virtual void RelationCollectionChanged(object? sender, CollectionChangeEventArgs e) { DataRelationPropertyDescriptor? NullProp = null; OnListChanged( - e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Add ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : e.Action == CollectionChangeAction.Refresh ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, NullProp) : - e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element)) : + e.Action == CollectionChangeAction.Remove ? new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor((System.Data.DataRelation)e.Element!)) : /*default*/ null! // TODO: This is very likely wrong ); } diff --git a/src/libraries/System.Data.Common/src/System/Data/DefaultValueTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/DefaultValueTypeConverter.cs index 7e2edf3d377..51985dc6bcc 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DefaultValueTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DefaultValueTypeConverter.cs @@ -19,9 +19,7 @@ namespace System.Data { } -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -43,7 +41,7 @@ namespace System.Data return base.ConvertTo(context, culture, value, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { if (value != null && value.GetType() == typeof(string)) { @@ -60,6 +58,5 @@ namespace System.Data return base.ConvertFrom(context, culture, value); } -#nullable enable } } diff --git a/src/libraries/System.Data.Common/src/System/Data/LinqDataView.cs b/src/libraries/System.Data.Common/src/System/Data/LinqDataView.cs index 361ef47e0f2..52083015f65 100644 --- a/src/libraries/System.Data.Common/src/System/Data/LinqDataView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/LinqDataView.cs @@ -231,8 +231,6 @@ namespace System.Data #endregion #region IBindingList - // TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable /// /// Clears both expression-based and DataView's string-based sorting. /// @@ -246,7 +244,7 @@ namespace System.Data /// Overrides IBindingList's SortProperty so that it returns null if expression based sort /// is used in the LinkDataView, otherwise it defers the result to DataView /// - PropertyDescriptor IBindingList.SortProperty + PropertyDescriptor? IBindingList.SortProperty { get { @@ -283,7 +281,6 @@ namespace System.Data return !(base.SortComparison == null && base.Sort.Length == 0); } } -#nullable enable #endregion } } diff --git a/src/libraries/System.Data.Common/src/System/Data/PrimaryKeyTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/PrimaryKeyTypeConverter.cs index 118814e3e0f..67e643f7cfe 100644 --- a/src/libraries/System.Data.Common/src/System/Data/PrimaryKeyTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/PrimaryKeyTypeConverter.cs @@ -13,13 +13,13 @@ namespace System.Data { } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => false; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => false; - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || base.CanConvertTo(context, destinationType); - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { diff --git a/src/libraries/System.Data.Common/src/System/Data/RelationshipConverter.cs b/src/libraries/System.Data.Common/src/System/Data/RelationshipConverter.cs index a14f40eccc1..94450f9c80d 100644 --- a/src/libraries/System.Data.Common/src/System/Data/RelationshipConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/RelationshipConverter.cs @@ -18,7 +18,7 @@ namespace System.Data /// Gets a value indicating whether this converter can /// convert an object to the given destination type using the context. /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (destinationType == typeof(InstanceDescriptor)) { @@ -34,7 +34,7 @@ namespace System.Data /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { diff --git a/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs index 571d343deaf..df76c1da336 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Xml; using System.Collections; using System.Globalization; @@ -17,7 +14,7 @@ namespace System.Data { internal string _schemaName; internal string _schemaUri; - internal XmlElement _schemaRoot; + internal XmlElement? _schemaRoot; internal DataSet _ds; internal XDRSchema(DataSet ds, bool fInline) @@ -50,7 +47,7 @@ namespace System.Data ds.Namespace = _schemaUri; // Walk all the top level Element tags. - for (XmlNode n = schemaRoot.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = schemaRoot.FirstChild; n != null; n = n.NextSibling) { if (!(n is XmlElement)) continue; @@ -68,10 +65,10 @@ namespace System.Data ds.DataSetName = _schemaName; } - internal XmlElement FindTypeNode(XmlElement node) + internal XmlElement? FindTypeNode(XmlElement node) { string strType; - XmlNode vn; + XmlNode? vn; XmlNode vnRoof; Debug.Assert(FEqualIdentity(node, Keywords.XDR_ELEMENT, Keywords.XDRNS) || @@ -107,7 +104,7 @@ namespace System.Data } // Move vn node - if (vn.FirstChild != null) + if (vn!.FirstChild != null) vn = vn.FirstChild; else if (vn.NextSibling != null) vn = vn.NextSibling; @@ -116,7 +113,7 @@ namespace System.Data while (vn != vnRoof) { vn = vn.ParentNode; - if (vn.NextSibling != null) + if (vn!.NextSibling != null) { vn = vn.NextSibling; break; @@ -162,7 +159,7 @@ namespace System.Data if (!IsTextOnlyContent(typeNode)) return false; - for (XmlNode n = typeNode.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = typeNode.FirstChild; n != null; n = n.NextSibling) { if (FEqualIdentity(n, Keywords.XDR_ELEMENT, Keywords.XDRNS) || FEqualIdentity(n, Keywords.XDR_ATTRIBUTE, Keywords.XDRNS)) @@ -180,9 +177,9 @@ namespace System.Data } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal DataTable HandleTable(XmlElement node) + internal DataTable? HandleTable(XmlElement node) { - XmlElement typeNode; + XmlElement? typeNode; Debug.Assert(FEqualIdentity(node, Keywords.XDR_ELEMENTTYPE, Keywords.XDRNS) || FEqualIdentity(node, Keywords.XDR_ELEMENT, Keywords.XDRNS), "Invalid node type"); @@ -227,7 +224,7 @@ namespace System.Data name = n; type = t; } - public int CompareTo(object obj) { return string.Compare(name, (string)obj, StringComparison.Ordinal); } + public int CompareTo(object? obj) { return string.Compare(name, (string?)obj, StringComparison.Ordinal); } }; // XDR spec: http://www.ltg.ed.ac.uk/~ht/XMLData-Reduced.htm @@ -354,8 +351,8 @@ namespace System.Data string strValues; int minOccurs = 0; int maxOccurs = 1; - string strDefault; - DataColumn column; + string? strDefault; + DataColumn? column; // Get the name if (node.Attributes.Count > 0) @@ -390,9 +387,9 @@ namespace System.Data } // Now get the type - XmlElement typeNode = FindTypeNode(node); + XmlElement? typeNode = FindTypeNode(node); - SimpleType xsdType = null; + SimpleType? xsdType = null; if (typeNode == null) { @@ -491,7 +488,7 @@ namespace System.Data } catch (System.FormatException) { - throw ExceptionBuilder.CannotConvert(strDefault, type.FullName); + throw ExceptionBuilder.CannotConvert(strDefault, type.FullName!); } } @@ -544,9 +541,9 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleTypeNode(XmlElement typeNode, DataTable table, ArrayList tableChildren) { - DataTable tableChild; + DataTable? tableChild; - for (XmlNode n = typeNode.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = typeNode.FirstChild; n != null; n = n.NextSibling) { if (!(n is XmlElement)) continue; @@ -575,10 +572,10 @@ namespace System.Data { string typeName = string.Empty; XmlAttributeCollection attrs = node.Attributes; - DataTable table; + DataTable? table; int minOccurs = 1; int maxOccurs = 1; - string keys = null; + string? keys = null; ArrayList tableChildren = new ArrayList(); @@ -619,7 +616,7 @@ namespace System.Data for (int i = 0; i < keyLength; i++) { - DataColumn col = table.Columns[list[i], _schemaUri]; + DataColumn? col = table.Columns[list[i], _schemaUri]; if (col == null) throw ExceptionBuilder.ElementTypeNotFound(list[i]); cols[i] = col; @@ -630,7 +627,7 @@ namespace System.Data foreach (DataTable _tableChild in tableChildren) { - DataRelation relation = null; + DataRelation? relation = null; DataRelationCollection childRelations = table.ChildRelations; @@ -656,7 +653,7 @@ namespace System.Data relation.CheckMultipleNested = false; // disable the check for multiple nested parent relation.Nested = true; - _tableChild.DataSet.Relations.Add(relation); + _tableChild.DataSet!.Relations.Add(relation); relation.CheckMultipleNested = true; // enable the check for multiple nested parent } @@ -668,7 +665,7 @@ namespace System.Data { string typeName; XmlAttributeCollection attrs = node.Attributes; - DataTable table; + DataTable? table; int minOccurs = 1; int maxOccurs = 1; diff --git a/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs b/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs index 509a7f6082f..46ad2ac3a80 100644 --- a/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs +++ b/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs @@ -162,7 +162,7 @@ namespace System.Data return; } - object propInst = pd.GetValue(instance); + object? propInst = pd.GetValue(instance); if (propInst is InternalDataCollectionBase) return; @@ -217,7 +217,7 @@ namespace System.Data } } - string textValue = pd.Converter.ConvertToString(propInst); + string? textValue = pd.Converter.ConvertToString(propInst); root.SetAttribute(pd.Name, Keywords.MSDNS, textValue); return; } diff --git a/src/libraries/System.Data.Common/src/System/Xml/XmlDataDocument.cs b/src/libraries/System.Data.Common/src/System/Xml/XmlDataDocument.cs index dfec09877cb..68fb3bc0e8a 100644 --- a/src/libraries/System.Data.Common/src/System/Xml/XmlDataDocument.cs +++ b/src/libraries/System.Data.Common/src/System/Xml/XmlDataDocument.cs @@ -2203,7 +2203,7 @@ namespace System.Xml if (args.PropertyName == "Namespace") throw new InvalidOperationException(SR.DataDom_TableNamespaceChange); } - private void OnTableColumnsChanging(object oColumnsCollection, CollectionChangeEventArgs args) + private void OnTableColumnsChanging(object? oColumnsCollection, CollectionChangeEventArgs args) { // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh // args.Element is one of either the column (for Add and Remove actions or null, if the entire colection of columns is changing) @@ -2212,7 +2212,7 @@ namespace System.Xml throw new InvalidOperationException(SR.DataDom_TableColumnsChange); } - private void OnDataSetTablesChanging(object oTablesCollection, CollectionChangeEventArgs args) + private void OnDataSetTablesChanging(object? oTablesCollection, CollectionChangeEventArgs args) { // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh // args.Element is a table @@ -2221,13 +2221,13 @@ namespace System.Xml throw new InvalidOperationException(SR.DataDom_DataSetTablesChange); } - private void OnDataSetRelationsChanging(object oRelationsCollection, CollectionChangeEventArgs args) + private void OnDataSetRelationsChanging(object? oRelationsCollection, CollectionChangeEventArgs args) { // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh // args.Element is a DataRelation // Disallow changing the tables collection if there is data loaded and there are nested relationship that are added/refreshed - DataRelation rel = (DataRelation)(args.Element); + DataRelation? rel = (DataRelation?)(args.Element); if (rel != null && rel.Nested) throw new InvalidOperationException(SR.DataDom_DataSetNestedRelationsChange); @@ -2235,7 +2235,7 @@ namespace System.Xml Debug.Assert(!(args.Action == CollectionChangeAction.Add || args.Action == CollectionChangeAction.Remove) || rel!.Nested == false); if (args.Action == CollectionChangeAction.Refresh) { - foreach (DataRelation relTemp in (DataRelationCollection)oRelationsCollection) + foreach (DataRelation relTemp in (DataRelationCollection)oRelationsCollection!) { if (relTemp.Nested) { diff --git a/src/libraries/System.Data.OleDb/src/OleDbConnectionStringBuilder.cs b/src/libraries/System.Data.OleDb/src/OleDbConnectionStringBuilder.cs index 67737585e1e..a61ca836766 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbConnectionStringBuilder.cs @@ -540,17 +540,17 @@ namespace System.Data.OleDb { } - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) { return true; } - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) { return false; } - public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context) { StandardValuesCollection? dataSourceNames = _standardValues; if (null == _standardValues) @@ -608,13 +608,13 @@ namespace System.Data.OleDb { } - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { // Only know how to convert from a string return ((typeof(string) == sourceType) || base.CanConvertFrom(context, sourceType)); } - public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { string? svalue = (value as string); if (null != svalue) @@ -645,13 +645,13 @@ namespace System.Data.OleDb return base.ConvertFrom(context, culture, value); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { // Only know how to convert to the NetworkLibrary enumeration return ((typeof(string) == destinationType) || base.CanConvertTo(context, destinationType)); } - public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, Type destinationType) { if ((typeof(string) == destinationType) && (null != value) && (typeof(int) == value.GetType())) { @@ -660,17 +660,17 @@ namespace System.Data.OleDb return base.ConvertTo(context, culture, value, destinationType); } - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) { return true; } - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) { return false; } - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { StandardValuesCollection? standardValues = _standardValues; if (null == standardValues) @@ -683,7 +683,7 @@ namespace System.Data.OleDb return standardValues; } - public override bool IsValid(ITypeDescriptorContext context, object value) + public override bool IsValid(ITypeDescriptorContext? context, object? value) { return true; //return Enum.IsDefined(type, value); @@ -697,7 +697,7 @@ namespace System.Data.OleDb { } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) { @@ -706,7 +706,7 @@ namespace System.Data.OleDb return base.CanConvertTo(context, destinationType); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { diff --git a/src/libraries/System.Data.OleDb/src/OleDbException.cs b/src/libraries/System.Data.OleDb/src/OleDbException.cs index 410e9cb33da..be01b4b4f00 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbException.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbException.cs @@ -137,7 +137,7 @@ namespace System.Data.OleDb { } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { diff --git a/src/libraries/System.Data.OleDb/src/OleDbParameter.cs b/src/libraries/System.Data.OleDb/src/OleDbParameter.cs index 61053ad7c31..aa829374f4a 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbParameter.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbParameter.cs @@ -628,7 +628,7 @@ namespace System.Data.OleDb { } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) { @@ -637,7 +637,7 @@ namespace System.Data.OleDb return base.CanConvertTo(context, destinationType); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (null == destinationType) { diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs index f8af94cfacd..ee3e353b3a5 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs @@ -12,7 +12,7 @@ namespace System.DirectoryServices.Design private static StandardValuesCollection? s_values; private static readonly Hashtable s_componentsCreated = new Hashtable(StringComparer.OrdinalIgnoreCase); - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == typeof(string)) { @@ -22,7 +22,7 @@ namespace System.DirectoryServices.Design return base.CanConvertFrom(context, sourceType); } - public override object? ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object? value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { if (value != null && value is string) { @@ -49,7 +49,7 @@ namespace System.DirectoryServices.Design return null; } - public override object? ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object? value, Type? destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType != null && destinationType == typeof(string)) { @@ -59,10 +59,10 @@ namespace System.DirectoryServices.Design return SR.DSNotSet; } - return base.ConvertTo(context, culture, value, destinationType); + return base.ConvertTo(context, culture, value, destinationType!); } - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) { if (s_values == null) { @@ -90,8 +90,8 @@ namespace System.DirectoryServices.Design return null; } - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => false; + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => false; - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; } } diff --git a/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs b/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs index 3ac90d7c6ed..6b2e84a87b1 100644 --- a/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs +++ b/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs @@ -341,20 +341,20 @@ namespace System.Drawing public partial class FontConverter : System.ComponentModel.TypeConverter { public FontConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } [Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } - public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } + public override System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public sealed partial class FontNameConverter : System.ComponentModel.TypeConverter, System.IDisposable { public FontNameConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } @@ -700,10 +700,10 @@ namespace System.Drawing public partial class IconConverter : System.ComponentModel.ExpandableObjectConverter { public IconConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } - public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } } public partial interface IDeviceContext : System.IDisposable { @@ -789,9 +789,9 @@ namespace System.Drawing public partial class ImageConverter : System.ComponentModel.TypeConverter { public ImageConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } [Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw null; } @@ -802,8 +802,8 @@ namespace System.Drawing public ImageFormatConverter() { } public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw null; } public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } - public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } @@ -2574,10 +2574,10 @@ namespace System.Drawing.Printing public partial class MarginsConverter : System.ComponentModel.ExpandableObjectConverter { public MarginsConverter() { } - public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw null; } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw null; } public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Type? destinationType) { throw null; } public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw null; } - public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw null; } public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw null; } public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw null; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs index 1ea09376a5c..5bb82adbc80 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs @@ -17,7 +17,7 @@ namespace System.Drawing { private const string StylePrefix = "style="; - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -27,7 +27,7 @@ namespace System.Drawing return (destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor)); } - public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (value is Font font) { @@ -169,7 +169,7 @@ namespace System.Drawing { try { - fontSize = (float)GetFloatConverter().ConvertFromString(context, culture, unitTokens.size); + fontSize = (float)GetFloatConverter().ConvertFromString(context, culture, unitTokens.size)!; } catch { @@ -366,9 +366,9 @@ namespace System.Drawing public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; [RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public override PropertyDescriptorCollection GetProperties( + public override PropertyDescriptorCollection? GetProperties( ITypeDescriptorContext? context, - object? value, + object value, Attribute[]? attributes) { if (value is not Font) @@ -378,7 +378,7 @@ namespace System.Drawing return props.Sort(new string[] { nameof(Font.Name), nameof(Font.Size), nameof(Font.Unit) }); } - public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; public sealed class FontNameConverter : TypeConverter, IDisposable { @@ -393,12 +393,12 @@ namespace System.Drawing { } - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); } - public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { return value is string strValue ? MatchFontName(strValue, context) : base.ConvertFrom(context, culture, value); } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/IconConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/IconConverter.cs index 48803c35261..5e5829d0a01 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/IconConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/IconConverter.cs @@ -9,23 +9,23 @@ namespace System.Drawing { public class IconConverter : ExpandableObjectConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return (sourceType == typeof(byte[])); } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(byte[]) || destinationType == typeof(string) || destinationType == typeof(Image) || destinationType == typeof(Bitmap); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { return value is byte[] bytes ? new Icon(new MemoryStream(bytes)) : base.ConvertFrom(context, culture, value); } - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs index 308df99d1d0..baf8e390485 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs @@ -29,7 +29,7 @@ namespace System.Drawing return destinationType == typeof(byte[]) || destinationType == typeof(string); } - public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is Icon icon) { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs index 5fc55f1f9fe..3b6d12fca7d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs @@ -11,7 +11,7 @@ namespace System.Drawing { public class ImageFormatConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } @@ -25,7 +25,7 @@ namespace System.Drawing return base.CanConvertTo(context, destinationType); } - public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { // we must be able to convert from short names and long names string? strFormat = value as string; @@ -66,7 +66,7 @@ namespace System.Drawing throw new FormatException(SR.Format(SR.ConvertInvalidPrimitive, strFormat, nameof(ImageFormat))); } - public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (value is ImageFormat imgFormat) { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs index af6ac733299..7354b6adbf0 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs @@ -19,7 +19,7 @@ namespace System.Drawing.Printing /// Determines if a converter can convert an object of the given source /// type to the native type of the converter. /// - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == typeof(string)) { @@ -68,7 +68,7 @@ namespace System.Drawing.Printing for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. - values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i])!; } if (values.Length != 4) { @@ -91,7 +91,7 @@ namespace System.Drawing.Printing /// type is string. If this cannot convert to the desitnation type, this will /// throw a NotSupportedException. /// - public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) { @@ -107,7 +107,7 @@ namespace System.Drawing.Printing } string sep = culture.TextInfo.ListSeparator + " "; TypeConverter intConverter = GetIntConverter(); - string[] args = new string[4]; + string?[] args = new string[4]; int nArg = 0; // Note: ConvertToString will raise exception if value cannot be converted. diff --git a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs index ef40926e32a..49cadc13109 100644 --- a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs +++ b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs @@ -168,7 +168,7 @@ namespace System.Resources.Extensions throw new TypeLoadException(SR.Format(SR.TypeLoadException_CannotLoadConverter, type)); } - value = converter.ConvertFrom(data); + value = converter.ConvertFrom(data)!; break; } case SerializationFormat.TypeConverterString: @@ -182,7 +182,7 @@ namespace System.Resources.Extensions throw new TypeLoadException(SR.Format(SR.TypeLoadException_CannotLoadConverter, type)); } - value = converter.ConvertFromInvariantString(stringData); + value = converter.ConvertFromInvariantString(stringData)!; break; } case SerializationFormat.ActivatorStream: diff --git a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs index 53f58ab2ba7..e251455cbfd 100644 --- a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs +++ b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs @@ -98,7 +98,7 @@ namespace System.Resources.Extensions throw new TypeLoadException(SR.Format(SR.TypeLoadException_CannotLoadConverter, primitiveType)); } - object primitiveValue = converter.ConvertFromInvariantString(value); + object primitiveValue = converter.ConvertFromInvariantString(value)!; Debug.Assert(primitiveValue.GetType() == primitiveType); From 14035724e35e13114684ecff312b67b00517a75a Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 8 Jul 2021 07:33:24 -0700 Subject: [PATCH 344/926] fix formatting errors (#55320) --- src/coreclr/jit/lsra.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index ecd4dbd9e46..ddc54425174 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -11247,11 +11247,12 @@ void LinearScan::RegisterSelection::try_SPILL_COST() spillCandidates &= ~spillCandidateBit; regNumber spillCandidateRegNum = genRegNumFromMask(spillCandidateBit); RegRecord* spillCandidateRegRecord = &linearScan->physRegs[spillCandidateRegNum]; + Interval* assignedInterval = spillCandidateRegRecord->assignedInterval; // Can and should the interval in this register be spilled for this one, // if we don't find a better alternative? if ((linearScan->getNextIntervalRef(spillCandidateRegNum, regType) == currentLocation) && - !spillCandidateRegRecord->assignedInterval->getNextRefPosition()->RegOptional()) + !assignedInterval->getNextRefPosition()->RegOptional()) { continue; } @@ -11261,17 +11262,15 @@ void LinearScan::RegisterSelection::try_SPILL_COST() } float currentSpillWeight = 0; - RefPosition* recentRefPosition = spillCandidateRegRecord->assignedInterval != nullptr - ? spillCandidateRegRecord->assignedInterval->recentRefPosition - : nullptr; - if ((recentRefPosition != nullptr) && (recentRefPosition->RegOptional() && - !(spillCandidateRegRecord->assignedInterval->isLocalVar && recentRefPosition->IsActualRef()))) + RefPosition* recentRefPosition = assignedInterval != nullptr ? assignedInterval->recentRefPosition : nullptr; + if ((recentRefPosition != nullptr) && + (recentRefPosition->RegOptional() && !(assignedInterval->isLocalVar && recentRefPosition->IsActualRef()))) { // We do not "spillAfter" if previous (recent) refPosition was regOptional or if it // is not an actual ref. In those cases, we will reload in future (next) refPosition. // For such cases, consider the spill cost of next refposition. // See notes in "spillInterval()". - RefPosition* reloadRefPosition = spillCandidateRegRecord->assignedInterval->getNextRefPosition(); + RefPosition* reloadRefPosition = assignedInterval->getNextRefPosition(); if (reloadRefPosition != nullptr) { currentSpillWeight = linearScan->getWeight(reloadRefPosition); From 50413eafd5a930dc5f3cce1a7fcba743cbb36c80 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 8 Jul 2021 11:28:42 -0400 Subject: [PATCH 345/926] Add UnixFileStreamStrategy (#55191) * Add UnixFileStreamStrategy * Address PR feedback * Fix length caching --- .../tests/FileStream/WriteAsync.cs | 4 +- ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 5 +- ...afeFileHandle.ThreadPoolValueTaskSource.cs | 10 +- .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 5 + .../SafeHandles/SafeFileHandle.Windows.cs | 2 - .../System.Private.CoreLib.Shared.projitems | 5 +- .../src/System/IO/FileStream.cs | 11 +- .../src/System/IO/RandomAccess.Unix.cs | 20 +- .../AsyncWindowsFileStreamStrategy.cs | 6 +- .../Strategies/BufferedFileStreamStrategy.cs | 26 ++- .../IO/Strategies/FileStreamHelpers.Unix.cs | 60 +++++- .../Strategies/FileStreamHelpers.Windows.cs | 44 ++--- .../System/IO/Strategies/FileStreamHelpers.cs | 22 ++- .../IO/Strategies/FileStreamStrategy.cs | 2 - .../Net5CompatFileStreamStrategy.Lock.OSX.cs | 18 -- .../Net5CompatFileStreamStrategy.Lock.Unix.cs | 24 --- .../Net5CompatFileStreamStrategy.Unix.cs | 52 ++--- .../Net5CompatFileStreamStrategy.Windows.cs | 26 +-- .../Net5CompatFileStreamStrategy.cs | 20 +- ...eamStrategy.cs => OSFileStreamStrategy.cs} | 104 +++++----- .../SyncWindowsFileStreamStrategy.cs | 12 +- .../IO/Strategies/UnixFileStreamStrategy.cs | 178 ++++++++++++++++++ .../src/System/OperatingSystem.cs | 7 + .../src/System/Threading/Tasks/ValueTask.cs | 25 +++ 24 files changed, 438 insertions(+), 250 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{WindowsFileStreamStrategy.cs => OSFileStreamStrategy.cs} (76%) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs index 7f7fd8ad240..99f51624cc1 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs @@ -179,11 +179,12 @@ namespace System.IO.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // Browser PNSE: Cannot wait on monitors + [PlatformSpecific(TestPlatforms.Windows)] public Task ManyConcurrentWriteAsyncs() { // For inner loop, just test one case return ManyConcurrentWriteAsyncs_OuterLoop( - useAsync: OperatingSystem.IsWindows(), + useAsync: true, presize: false, exposeHandle: false, cancelable: true, @@ -193,6 +194,7 @@ namespace System.IO.Tests } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [PlatformSpecific(TestPlatforms.Windows)] // testing undocumented feature that's legacy in the Windows implementation [MemberData(nameof(MemberData_FileStreamAsyncWriting))] [OuterLoop] // many combinations: we test just one in inner loop and the rest outer public async Task ManyConcurrentWriteAsyncs_OuterLoop( diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index e9ab0a7365e..5c4ca4d01d7 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -1,9 +1,8 @@ - + true true - - $(NetCoreAppCurrent)-windows + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix --working-dir=/test-dir diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs index e9f06359326..49638af80f3 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs @@ -21,17 +21,12 @@ namespace Microsoft.Win32.SafeHandles internal ThreadPoolValueTaskSource GetThreadPoolValueTaskSource() => Interlocked.Exchange(ref _reusableThreadPoolValueTaskSource, null) ?? new ThreadPoolValueTaskSource(this); - private void TryToReuse(ThreadPoolValueTaskSource source) - { - Interlocked.CompareExchange(ref _reusableThreadPoolValueTaskSource, source, null); - } - /// /// A reusable implementation that /// queues asynchronous operations to /// be completed synchronously on the thread pool. /// - internal sealed class ThreadPoolValueTaskSource : IThreadPoolWorkItem, IValueTaskSource, IValueTaskSource + internal sealed class ThreadPoolValueTaskSource : IThreadPoolWorkItem, IValueTaskSource, IValueTaskSource, IValueTaskSource { private readonly SafeFileHandle _fileHandle; private ManualResetValueTaskSourceCore _source; @@ -68,7 +63,7 @@ namespace Microsoft.Win32.SafeHandles finally { _source.Reset(); - _fileHandle.TryToReuse(this); + Volatile.Write(ref _fileHandle._reusableThreadPoolValueTaskSource, this); } } @@ -77,6 +72,7 @@ namespace Microsoft.Win32.SafeHandles _source.OnCompleted(continuation, state, token, flags); int IValueTaskSource.GetResult(short token) => (int) GetResultAndRelease(token); long IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); + void IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); private void ExecuteInternal() { diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index e247bf65dbf..21b78a00a44 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; using System.IO.Strategies; +using System.Threading; namespace Microsoft.Win32.SafeHandles { @@ -28,6 +29,10 @@ namespace Microsoft.Win32.SafeHandles internal bool CanSeek => !IsClosed && GetCanSeek(); + internal ThreadPoolBoundHandle? ThreadPoolBinding => null; + + internal void EnsureThreadPoolBindingInitialized() { /* nop */ } + /// Opens the specified file with the requested flags and mode. /// The path to the file. /// The flags with which to open the file. diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index 651b49be022..a7c4751a094 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -24,8 +24,6 @@ namespace Microsoft.Win32.SafeHandles internal bool CanSeek => !IsClosed && GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; - internal bool IsPipe => GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE; - internal ThreadPoolBoundHandle? ThreadPoolBinding { get; set; } internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 2d0fb8a283d..d5c9d5ef37c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -459,6 +459,7 @@ + @@ -1814,7 +1815,6 @@ - @@ -2085,8 +2085,7 @@ - - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 9653bd057ba..1805e6cbb3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -479,10 +479,17 @@ namespace System.IO public override ValueTask DisposeAsync() => _strategy.DisposeAsync(); - public override void CopyTo(Stream destination, int bufferSize) => _strategy.CopyTo(destination, bufferSize); + public override void CopyTo(Stream destination, int bufferSize) + { + ValidateCopyToArguments(destination, bufferSize); + _strategy.CopyTo(destination, bufferSize); + } public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - => _strategy.CopyToAsync(destination, bufferSize, cancellationToken); + { + ValidateCopyToArguments(destination, bufferSize); + return _strategy.CopyToAsync(destination, bufferSize, cancellationToken); + } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index bc3a04f2f5d..41de56fcbea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -28,7 +28,12 @@ namespace System.IO { fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { - int result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset); + // The Windows implementation uses ReadFile, which ignores the offset if the handle + // isn't seekable. We do the same manually with PRead vs Read, in order to enable + // the function to be used by FileStream for all the same situations. + int result = handle.CanSeek ? + Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset) : + Interop.Sys.Read(handle, bufPtr, buffer.Length); FileStreamHelpers.CheckFileCall(result, handle.Path); return result; } @@ -67,7 +72,7 @@ namespace System.IO return FileStreamHelpers.CheckFileCall(result, handle.Path); } - private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) => ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); private static ValueTask ReadScatterAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, @@ -78,9 +83,14 @@ namespace System.IO { fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { - int result = Interop.Sys.PWrite(handle, bufPtr, buffer.Length, fileOffset); + // The Windows implementation uses WriteFile, which ignores the offset if the handle + // isn't seekable. We do the same manually with PWrite vs Write, in order to enable + // the function to be used by FileStream for all the same situations. + int result = handle.CanSeek ? + Interop.Sys.PWrite(handle, bufPtr, buffer.Length, fileOffset) : + Interop.Sys.Write(handle, bufPtr, buffer.Length); FileStreamHelpers.CheckFileCall(result, handle.Path); - return result; + return result; } } @@ -117,7 +127,7 @@ namespace System.IO return FileStreamHelpers.CheckFileCall(result, handle.Path); } - private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) => ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index 9329c0fa60b..c83b58921c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -8,7 +8,7 @@ using Microsoft.Win32.SafeHandles; namespace System.IO.Strategies { - internal sealed partial class AsyncWindowsFileStreamStrategy : WindowsFileStreamStrategy + internal sealed partial class AsyncWindowsFileStreamStrategy : OSFileStreamStrategy { internal AsyncWindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) : base(handle, access, share) @@ -98,12 +98,8 @@ namespace System.IO.Strategies return SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, _fileHandle.Path); } - public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { - ValidateCopyToArguments(destination, bufferSize); - // Fail if the file was closed if (_fileHandle.IsClosed) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 22fb3e0c140..11d21121bee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -247,7 +247,7 @@ namespace System.IO.Strategies // If we are reading from a device with no clear EOF like a // serial port or a pipe, this will cause us to block incorrectly. - if (!_strategy.IsPipe) + if (_strategy.CanSeek) { // If we hit the end of the buffer and didn't have enough bytes, we must // read some more from the underlying stream. However, if we got @@ -340,9 +340,9 @@ namespace System.IO.Strategies Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - if (_strategy.IsPipe) // pipes have a very limited support for buffering + if (!_strategy.CanSeek) { - return ReadFromPipeAsync(buffer, cancellationToken); + return ReadFromNonSeekableAsync(buffer, cancellationToken); } SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized(); @@ -383,9 +383,9 @@ namespace System.IO.Strategies return ReadAsyncSlowPath(semaphoreLockTask, buffer, cancellationToken); } - private async ValueTask ReadFromPipeAsync(Memory destination, CancellationToken cancellationToken) + private async ValueTask ReadFromNonSeekableAsync(Memory destination, CancellationToken cancellationToken) { - Debug.Assert(_strategy.IsPipe); + Debug.Assert(!_strategy.CanSeek); // Employ async waiting based on the same synchronization used in BeginRead of the abstract Stream. await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); @@ -426,7 +426,7 @@ namespace System.IO.Strategies private async ValueTask ReadAsyncSlowPath(Task semaphoreLockTask, Memory buffer, CancellationToken cancellationToken) { Debug.Assert(_asyncActiveSemaphore != null); - Debug.Assert(!_strategy.IsPipe); + Debug.Assert(_strategy.CanSeek); // Employ async waiting based on the same synchronization used in BeginRead of the abstract Stream. await semaphoreLockTask.ConfigureAwait(false); @@ -633,13 +633,13 @@ namespace System.IO.Strategies Debug.Assert(!_strategy.IsClosed, "FileStream ensures that strategy is not closed"); Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - Debug.Assert(!_strategy.IsPipe || (_readPos == 0 && _readLen == 0), + Debug.Assert(_strategy.CanSeek || (_readPos == 0 && _readLen == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); - if (_strategy.IsPipe) + if (!_strategy.CanSeek) { // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadFromPipeAsync) - return WriteToPipeAsync(buffer, cancellationToken); + return WriteToNonSeekableAsync(buffer, cancellationToken); } SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized(); @@ -690,9 +690,9 @@ namespace System.IO.Strategies return WriteAsyncSlowPath(semaphoreLockTask, buffer, cancellationToken); } - private async ValueTask WriteToPipeAsync(ReadOnlyMemory source, CancellationToken cancellationToken) + private async ValueTask WriteToNonSeekableAsync(ReadOnlyMemory source, CancellationToken cancellationToken) { - Debug.Assert(_strategy.IsPipe); + Debug.Assert(!_strategy.CanSeek); await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); try @@ -708,7 +708,7 @@ namespace System.IO.Strategies private async ValueTask WriteAsyncSlowPath(Task semaphoreLockTask, ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_asyncActiveSemaphore != null); - Debug.Assert(!_strategy.IsPipe); + Debug.Assert(_strategy.CanSeek); await semaphoreLockTask.ConfigureAwait(false); try @@ -878,7 +878,6 @@ namespace System.IO.Strategies public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { - ValidateCopyToArguments(destination, bufferSize); EnsureNotClosed(); EnsureCanRead(); @@ -921,7 +920,6 @@ namespace System.IO.Strategies public override void CopyTo(Stream destination, int bufferSize) { - ValidateCopyToArguments(destination, bufferSize); EnsureNotClosed(); EnsureCanRead(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs index 868ccb84fd7..1d508870956 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs @@ -8,12 +8,11 @@ namespace System.IO.Strategies // this type defines a set of stateless FileStream/FileStreamStrategy helper methods internal static partial class FileStreamHelpers { - // in the future we are most probably going to introduce more strategies (io_uring etc) - private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) - => new Net5CompatFileStreamStrategy(handle, access, bufferSize == 0 ? 1 : bufferSize, isAsync); + private static OSFileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, bool isAsync) => + new UnixFileStreamStrategy(handle, access, share); - private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) - => new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize == 0 ? 1 : bufferSize, options, preallocationSize); + private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) => + new UnixFileStreamStrategy(path, mode, access, share, options, preallocationSize); internal static long CheckFileCall(long result, string? path, bool ignoreNotSupported = false) { @@ -28,5 +27,56 @@ namespace System.IO.Strategies return result; } + + internal static void ValidateFileTypeForNonExtendedPaths(SafeFileHandle handle, string originalPath) { /* nop */ } + + internal static long Seek(SafeFileHandle handle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) => + CheckFileCall(Interop.Sys.LSeek(handle, offset, (Interop.Sys.SeekWhence)(int)origin), handle.Path); // SeekOrigin values are the same as Interop.libc.SeekWhence values + + internal static void ThrowInvalidArgument(SafeFileHandle handle) => + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.EINVAL), handle.Path); + + internal static unsafe void SetFileLength(SafeFileHandle handle, long length) => + CheckFileCall(Interop.Sys.FTruncate(handle, length), handle.Path); + + /// Flushes the file's OS buffer. + internal static void FlushToDisk(SafeFileHandle handle) + { + if (Interop.Sys.FSync(handle) < 0) + { + Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + switch (errorInfo.Error) + { + case Interop.Error.EROFS: + case Interop.Error.EINVAL: + case Interop.Error.ENOTSUP: + // Ignore failures for special files that don't support synchronization. + // In such cases there's nothing to flush. + break; + default: + throw Interop.GetExceptionForIoErrno(errorInfo, handle.Path, isDirectory: false); + } + } + } + + internal static void Lock(SafeFileHandle handle, bool canWrite, long position, long length) + { + if (OperatingSystem.IsOSXLike()) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); + } + + CheckFileCall(Interop.Sys.LockFileRegion(handle, position, length, canWrite ? Interop.Sys.LockType.F_WRLCK : Interop.Sys.LockType.F_RDLCK), handle.Path); + } + + internal static void Unlock(SafeFileHandle handle, long position, long length) + { + if (OperatingSystem.IsOSXLike()) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); + } + + CheckFileCall(Interop.Sys.LockFileRegion(handle, position, length, Interop.Sys.LockType.F_UNLCK), handle.Path); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 17c03b7f931..3f29ed92b43 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -27,38 +27,15 @@ namespace System.IO.Strategies internal const ulong ResultMask = ((ulong)uint.MaxValue) << 32; } - private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) - { - if (UseNet5CompatStrategy) - { - // The .NET 5 Compat strategy does not support bufferSize == 0. - // To minimize the risk of introducing bugs to it, we just pass 1 to disable the buffering. - return new Net5CompatFileStreamStrategy(handle, access, bufferSize == 0 ? 1 : bufferSize, isAsync); - } + private static OSFileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, bool isAsync) => + isAsync ? + new AsyncWindowsFileStreamStrategy(handle, access, share) : + new SyncWindowsFileStreamStrategy(handle, access, share); - WindowsFileStreamStrategy strategy = isAsync - ? new AsyncWindowsFileStreamStrategy(handle, access, share) - : new SyncWindowsFileStreamStrategy(handle, access, share); - - return EnableBufferingIfNeeded(strategy, bufferSize); - } - - private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) - { - if (UseNet5CompatStrategy) - { - return new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize == 0 ? 1 : bufferSize, options, preallocationSize); - } - - WindowsFileStreamStrategy strategy = (options & FileOptions.Asynchronous) != 0 - ? new AsyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize) - : new SyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize); - - return EnableBufferingIfNeeded(strategy, bufferSize); - } - - internal static FileStreamStrategy EnableBufferingIfNeeded(WindowsFileStreamStrategy strategy, int bufferSize) - => bufferSize > 1 ? new BufferedFileStreamStrategy(strategy, bufferSize) : strategy; + private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) => + (options & FileOptions.Asynchronous) != 0 ? + new AsyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize) : + new SyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize); internal static void FlushToDisk(SafeFileHandle handle) { @@ -87,6 +64,9 @@ namespace System.IO.Strategies return ret; } + internal static void ThrowInvalidArgument(SafeFileHandle handle) => + throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_PARAMETER, handle.Path); + internal static int GetLastWin32ErrorAndDisposeHandleIfInvalid(SafeFileHandle handle) { int errorCode = Marshal.GetLastPInvokeError(); @@ -116,7 +96,7 @@ namespace System.IO.Strategies return errorCode; } - internal static void Lock(SafeFileHandle handle, long position, long length) + internal static void Lock(SafeFileHandle handle, bool canWrite, long position, long length) { int positionLow = unchecked((int)(position)); int positionHigh = unchecked((int)(position >> 32)); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs index 965e9c09f04..2c05acc1b0a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -14,10 +14,28 @@ namespace System.IO.Strategies internal static bool UseNet5CompatStrategy { get; } = AppContextConfigHelper.GetBooleanConfig("System.IO.UseNet5CompatFileStream", "DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) - => WrapIfDerivedType(fileStream, ChooseStrategyCore(handle, access, share, bufferSize, isAsync)); + { + // The .NET 5 Compat strategy does not support bufferSize == 0. + // To minimize the risk of introducing bugs to it, we just pass 1 to disable the buffering. + + FileStreamStrategy strategy = UseNet5CompatStrategy ? + new Net5CompatFileStreamStrategy(handle, access, bufferSize == 0 ? 1 : bufferSize, isAsync) : + EnableBufferingIfNeeded(ChooseStrategyCore(handle, access, share, isAsync), bufferSize); + + return WrapIfDerivedType(fileStream, strategy); + } internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) - => WrapIfDerivedType(fileStream, ChooseStrategyCore(path, mode, access, share, bufferSize, options, preallocationSize)); + { + FileStreamStrategy strategy = UseNet5CompatStrategy ? + new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize == 0 ? 1 : bufferSize, options, preallocationSize) : + EnableBufferingIfNeeded(ChooseStrategyCore(path, mode, access, share, options, preallocationSize), bufferSize); + + return WrapIfDerivedType(fileStream, strategy); + } + + private static FileStreamStrategy EnableBufferingIfNeeded(FileStreamStrategy strategy, int bufferSize) + => bufferSize > 1 ? new BufferedFileStreamStrategy(strategy, bufferSize) : strategy; private static FileStreamStrategy WrapIfDerivedType(FileStream fileStream, FileStreamStrategy strategy) => fileStream.GetType() == typeof(FileStream) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs index c94892a9010..6cec66e7ac8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs @@ -17,8 +17,6 @@ namespace System.IO.Strategies internal abstract bool IsClosed { get; } - internal virtual bool IsPipe => false; - internal abstract void Lock(long position, long length); internal abstract void Unlock(long position, long length); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs deleted file mode 100644 index d3ac0b7cf7f..00000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.IO.Strategies -{ - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - internal override void Lock(long position, long length) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); - } - - internal override void Unlock(long position, long length) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs deleted file mode 100644 index 20e065d6317..00000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.IO.Strategies -{ - internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy - { - /// Prevents other processes from reading from or writing to the FileStream. - /// The beginning of the range to lock. - /// The range to be locked. - internal override void Lock(long position, long length) - { - CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, CanWrite ? Interop.Sys.LockType.F_WRLCK : Interop.Sys.LockType.F_RDLCK)); - } - - /// Allows access by other processes to all or part of a file that was previously locked. - /// The beginning of the range to unlock. - /// The range to be unlocked. - internal override void Unlock(long position, long length) - { - CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_UNLCK)); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs index 6c225186a3b..2feffb5cb6b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs @@ -79,6 +79,18 @@ namespace System.IO.Strategies } } + /// Prevents other processes from reading from or writing to the FileStream. + /// The beginning of the range to lock. + /// The range to be locked. + internal override void Lock(long position, long length) => + FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length); + + /// Allows access by other processes to all or part of a file that was previously locked. + /// The beginning of the range to unlock. + /// The range to be unlocked. + internal override void Unlock(long position, long length) => + FileStreamHelpers.Unlock(_fileHandle, position, length); + /// Releases the unmanaged resources used by the stream. /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) @@ -141,26 +153,6 @@ namespace System.IO.Strategies return base.DisposeAsync(); } - /// Flushes the OS buffer. This does not flush the internal read/write buffer. - private void FlushOSBuffer() - { - if (Interop.Sys.FSync(_fileHandle) < 0) - { - Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); - switch (errorInfo.Error) - { - case Interop.Error.EROFS: - case Interop.Error.EINVAL: - case Interop.Error.ENOTSUP: - // Ignore failures due to the FileStream being bound to a special file that - // doesn't support synchronization. In such cases there's nothing to flush. - break; - default: - throw Interop.GetExceptionForIoErrno(errorInfo, _fileHandle.Path, isDirectory: false); - } - } - } - private void FlushWriteBufferForWriteByte() { #pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/44542 @@ -589,26 +581,6 @@ namespace System.IO.Strategies return pos; } - /// Sets the current position of this stream to the given value. - /// The file handle on which to seek. - /// The point relative to origin from which to begin seeking. - /// - /// Specifies the beginning, the end, or the current position as a reference - /// point for offset, using a value of type SeekOrigin. - /// - /// not used in Unix implementation - /// The new position in the stream. - private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) - { - Debug.Assert(!fileHandle.IsInvalid); - Debug.Assert(fileHandle.CanSeek); - Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); - - long pos = FileStreamHelpers.CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin), fileHandle.Path); // SeekOrigin values are the same as Interop.libc.SeekWhence values - _filePosition = pos; - return pos; - } - private int CheckFileCall(int result, bool ignoreNotSupported = false) { FileStreamHelpers.CheckFileCall(result, _fileHandle?.Path, ignoreNotSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs index 3218baa33e7..e9e4e640a0a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs @@ -179,8 +179,6 @@ namespace System.IO.Strategies } } - private void FlushOSBuffer() => FileStreamHelpers.FlushToDisk(_fileHandle); - // Returns a task that flushes the internal write buffer private Task FlushWriteAsync(CancellationToken cancellationToken) { @@ -318,7 +316,7 @@ namespace System.IO.Strategies // If we are reading from a device with no clear EOF like a // serial port or a pipe, this will cause us to block incorrectly. - if (!_fileHandle.IsPipe) + if (_fileHandle.CanSeek) { // If we hit the end of the buffer and didn't have enough bytes, we must // read some more from the underlying stream. However, if we got @@ -466,16 +464,6 @@ namespace System.IO.Strategies return pos; } - // This doesn't do argument checking. Necessary for SetLength, which must - // set the file pointer beyond the end of the file. This will update the - // internal position - private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) - { - Debug.Assert(fileHandle.CanSeek, "fileHandle.CanSeek"); - - return _filePosition = FileStreamHelpers.Seek(fileHandle, offset, origin, closeInvalidHandle); - } - partial void OnBufferAllocated() { Debug.Assert(_buffer != null); @@ -593,7 +581,7 @@ namespace System.IO.Strategies Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - if (_fileHandle.IsPipe) + if (!_fileHandle.CanSeek) { // Pipes are tricky, at least when you have 2 different pipes // that you want to use simultaneously. When redirecting stdout @@ -624,7 +612,7 @@ namespace System.IO.Strategies } } - Debug.Assert(!_fileHandle.IsPipe, "Should not be a pipe."); + Debug.Assert(_fileHandle.CanSeek, "Should be seekable"); // Handle buffering. if (_writePos > 0) FlushWriteBuffer(); @@ -803,12 +791,12 @@ namespace System.IO.Strategies { Debug.Assert(_useAsyncIO); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); - Debug.Assert(!_fileHandle.IsPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); + Debug.Assert(_fileHandle.CanSeek || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); bool writeDataStoredInBuffer = false; - if (!_fileHandle.IsPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) + if (_fileHandle.CanSeek) // avoid async buffering with non-seekable files (e.g. pipes), as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) { // Ensure the buffer is clear for writing if (_writePos == 0) @@ -1044,8 +1032,6 @@ namespace System.IO.Strategies return base.CopyToAsync(destination, bufferSize, cancellationToken); } - ValidateCopyToArguments(destination, bufferSize); - // Fail if the file was closed if (_fileHandle.IsClosed) { @@ -1113,7 +1099,7 @@ namespace System.IO.Strategies } } - internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, position, length); + internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length); internal override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, position, length); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs index f01791ba963..797f6f5e9c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs @@ -159,7 +159,7 @@ namespace System.IO.Strategies // Read is invoked asynchronously. But we can do so using the base Stream's internal helper // that bypasses delegating to BeginRead, since we already know this is FileStream rather // than something derived from it and what our BeginRead implementation is going to do. - return (Task)base.BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + return BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); } return ReadAsyncTask(buffer, offset, count, cancellationToken); @@ -174,7 +174,7 @@ namespace System.IO.Strategies // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream // rather than something derived from it and what our BeginRead implementation is going to do. return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)base.BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + new ValueTask(BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : base.ReadAsync(buffer, cancellationToken); } @@ -241,7 +241,7 @@ namespace System.IO.Strategies // Write is invoked asynchronously. But we can do so using the base Stream's internal helper // that bypasses delegating to BeginWrite, since we already know this is FileStream rather // than something derived from it and what our BeginWrite implementation is going to do. - return (Task)base.BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + return BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); } return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); @@ -256,7 +256,7 @@ namespace System.IO.Strategies // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream // rather than something derived from it and what our BeginWrite implementation is going to do. return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)base.BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + new ValueTask(BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : base.WriteAsync(buffer, cancellationToken); } @@ -271,7 +271,7 @@ namespace System.IO.Strategies if (flushToDisk && CanWrite) { - FlushOSBuffer(); + FileStreamHelpers.FlushToDisk(_fileHandle); } } @@ -371,6 +371,16 @@ namespace System.IO.Strategies } } + // This doesn't do argument checking. Necessary for SetLength, which must + // set the file pointer beyond the end of the file. This will update the + // internal position + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + { + Debug.Assert(fileHandle.CanSeek, "fileHandle.CanSeek"); + + return _filePosition = FileStreamHelpers.Seek(fileHandle, offset, origin, closeInvalidHandle); + } + internal override bool IsClosed => _fileHandle.IsClosed; /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs similarity index 76% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs index f03c07fd4d0..2be66fffde5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs @@ -2,13 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; namespace System.IO.Strategies { - // this type serves some basic functionality that is common for Async and Sync Windows File Stream Strategies - internal abstract class WindowsFileStreamStrategy : FileStreamStrategy + // this type serves some basic functionality that is common for native OS File Stream Strategies + internal abstract class OSFileStreamStrategy : FileStreamStrategy { protected readonly SafeFileHandle _fileHandle; // only ever null if ctor throws private readonly FileAccess _access; // What file was opened for. @@ -16,10 +17,10 @@ namespace System.IO.Strategies protected long _filePosition; private long _appendStart; // When appending, prevent overwriting file. - private long _length = -1; // When the file is locked for writes (_share <= FileShare.Read) cache file length in-memory, negative means that hasn't been fetched. + private long _length = -1; // When the file is locked for writes on Windows (_share <= FileShare.Read) cache file length in-memory, negative means that hasn't been fetched. private bool _exposedHandle; // created from handle, or SafeFileHandle was used and the handle got exposed - internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) + internal OSFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) { _access = access; _share = share; @@ -41,7 +42,7 @@ namespace System.IO.Strategies _fileHandle = handle; } - internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) + internal OSFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) { string fullPath = Path.GetFullPath(path); @@ -52,7 +53,16 @@ namespace System.IO.Strategies try { - Init(mode, path); + FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, path); + + if (mode == FileMode.Append && CanSeek) + { + _appendStart = _filePosition = Length; + } + else + { + _appendStart = -1; + } } catch { @@ -70,17 +80,18 @@ namespace System.IO.Strategies public sealed override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; - // When the file is locked for writes we can cache file length in memory - // and avoid subsequent native calls which are expensive. public unsafe sealed override long Length { get { - if (_share > FileShare.Read || _exposedHandle) + if (!LengthCachingSupported) { return RandomAccess.GetFileLength(_fileHandle); } + // On Windows, when the file is locked for writes we can cache file length + // in memory and avoid subsequent native calls which are expensive. + if (_length < 0) { _length = RandomAccess.GetFileLength(_fileHandle); @@ -94,7 +105,7 @@ namespace System.IO.Strategies { // Do not update the cached length if the file is not locked // or if the length hasn't been fetched. - if (_share > FileShare.Read || _length < 0 || _exposedHandle) + if (!LengthCachingSupported || _length < 0) { Debug.Assert(_length < 0); return; @@ -106,8 +117,10 @@ namespace System.IO.Strategies } } + private bool LengthCachingSupported => OperatingSystem.IsWindows() && _share <= FileShare.Read && !_exposedHandle; + /// Gets or sets the position within the current stream - public override long Position + public sealed override long Position { get => _filePosition; set => _filePosition = value; @@ -117,8 +130,6 @@ namespace System.IO.Strategies internal sealed override bool IsClosed => _fileHandle.IsClosed; - internal sealed override bool IsPipe => _fileHandle.IsPipe; - // Flushing is the responsibility of BufferedFileStreamStrategy internal sealed override SafeFileHandle SafeFileHandle { @@ -138,17 +149,8 @@ namespace System.IO.Strategies } } - public override unsafe int ReadByte() - { - byte b; - return Read(new Span(&b, 1)) != 0 ? b : -1; - } - - public override unsafe void WriteByte(byte value) => - Write(new ReadOnlySpan(&value, 1)); - // this method just disposes everything (no buffer, no need to flush) - public override ValueTask DisposeAsync() + public sealed override ValueTask DisposeAsync() { if (_fileHandle != null && !_fileHandle.IsClosed) { @@ -162,16 +164,18 @@ namespace System.IO.Strategies internal sealed override void DisposeInternal(bool disposing) => Dispose(disposing); // this method just disposes everything (no buffer, no need to flush) - protected override void Dispose(bool disposing) + protected sealed override void Dispose(bool disposing) { - if (_fileHandle != null && !_fileHandle.IsClosed) + if (disposing && _fileHandle != null && !_fileHandle.IsClosed) { _fileHandle.ThreadPoolBinding?.Dispose(); _fileHandle.Dispose(); } } - public sealed override void Flush() => Flush(flushToDisk: false); // we have nothing to flush as there is no buffer here + public sealed override void Flush() { } // no buffering = nothing to flush + + public sealed override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush internal sealed override void Flush(bool flushToDisk) { @@ -203,7 +207,7 @@ namespace System.IO.Strategies else { // keep throwing the same exception we did when seek was causing actual offset change - throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_PARAMETER); + FileStreamHelpers.ThrowInvalidArgument(_fileHandle); } // Prevent users from overwriting data in a file that was opened in append mode. @@ -216,25 +220,10 @@ namespace System.IO.Strategies return pos; } - internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, position, length); + internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length); internal sealed override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, position, length); - private void Init(FileMode mode, string originalPath) - { - FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, originalPath); - - // For Append mode... - if (mode == FileMode.Append) - { - _appendStart = _filePosition = Length; - } - else - { - _appendStart = -1; - } - } - public sealed override void SetLength(long value) { if (_appendStart != -1 && value < _appendStart) @@ -248,7 +237,10 @@ namespace System.IO.Strategies Debug.Assert(value >= 0, "value >= 0"); FileStreamHelpers.SetFileLength(_fileHandle, value); - _length = value; + if (LengthCachingSupported) + { + _length = value; + } if (_filePosition > value) { @@ -256,11 +248,16 @@ namespace System.IO.Strategies } } - public override int Read(byte[] buffer, int offset, int count) => ReadSpan(new Span(buffer, offset, count)); + public sealed override unsafe int ReadByte() + { + byte b; + return Read(new Span(&b, 1)) != 0 ? b : -1; + } - public override int Read(Span buffer) => ReadSpan(buffer); + public sealed override int Read(byte[] buffer, int offset, int count) => + Read(new Span(buffer, offset, count)); - private unsafe int ReadSpan(Span destination) + public sealed override int Read(Span buffer) { if (_fileHandle.IsClosed) { @@ -271,19 +268,20 @@ namespace System.IO.Strategies ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } - int r = RandomAccess.ReadAtOffset(_fileHandle, destination, _filePosition); + int r = RandomAccess.ReadAtOffset(_fileHandle, buffer, _filePosition); Debug.Assert(r >= 0, $"RandomAccess.ReadAtOffset returned {r}."); _filePosition += r; return r; } - public override void Write(byte[] buffer, int offset, int count) - => WriteSpan(new ReadOnlySpan(buffer, offset, count)); + public sealed override unsafe void WriteByte(byte value) => + Write(new ReadOnlySpan(&value, 1)); - public override void Write(ReadOnlySpan buffer) => WriteSpan(buffer); + public override void Write(byte[] buffer, int offset, int count) => + Write(new ReadOnlySpan(buffer, offset, count)); - private unsafe void WriteSpan(ReadOnlySpan source) + public sealed override void Write(ReadOnlySpan buffer) { if (_fileHandle.IsClosed) { @@ -294,7 +292,7 @@ namespace System.IO.Strategies ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - int r = RandomAccess.WriteAtOffset(_fileHandle, source, _filePosition); + int r = RandomAccess.WriteAtOffset(_fileHandle, buffer, _filePosition); Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); _filePosition += r; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs index 4091b2db65c..50cd8d53649 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs @@ -8,7 +8,7 @@ using Microsoft.Win32.SafeHandles; namespace System.IO.Strategies { - internal sealed class SyncWindowsFileStreamStrategy : WindowsFileStreamStrategy + internal sealed class SyncWindowsFileStreamStrategy : OSFileStreamStrategy { internal SyncWindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) : base(handle, access, share) { @@ -27,7 +27,7 @@ namespace System.IO.Strategies // Read is invoked asynchronously. But we can do so using the base Stream's internal helper // that bypasses delegating to BeginRead, since we already know this is FileStream rather // than something derived from it and what our BeginRead implementation is going to do. - return (Task)BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + return BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); } public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) @@ -37,7 +37,7 @@ namespace System.IO.Strategies // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream // rather than something derived from it and what our BeginRead implementation is going to do. return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + new ValueTask(BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : base.ReadAsync(buffer, cancellationToken); } @@ -47,7 +47,7 @@ namespace System.IO.Strategies // Write is invoked asynchronously. But we can do so using the base Stream's internal helper // that bypasses delegating to BeginWrite, since we already know this is FileStream rather // than something derived from it and what our BeginWrite implementation is going to do. - return (Task)BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + return BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) @@ -57,10 +57,8 @@ namespace System.IO.Strategies // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream // rather than something derived from it and what our BeginWrite implementation is going to do. return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? - new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + new ValueTask(BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : base.WriteAsync(buffer, cancellationToken); } - - public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs new file mode 100644 index 00000000000..c88665bf0b8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + internal sealed partial class UnixFileStreamStrategy : OSFileStreamStrategy + { + private ReadAsyncTaskSource? _readAsyncTaskSource; + + internal UnixFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) : + base(handle, access, share) + { + } + + internal UnixFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) : + base(path, mode, access, share, options, preallocationSize) + { + } + + internal override bool IsAsync => _fileHandle.IsAsync; + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => + TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); + + public override int EndRead(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => + ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken) + { + if (!CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + if (CanSeek) + { + // This implementation updates the file position after the operation completes, rather than before. + // Also, unlike the Net5CompatFileStreamStrategy implementation, this implementation doesn't serialize operations. + ReadAsyncTaskSource rats = Interlocked.Exchange(ref _readAsyncTaskSource, null) ?? new ReadAsyncTaskSource(this); + return rats.QueueRead(destination, cancellationToken); + } + + return RandomAccess.ReadAtOffsetAsync(_fileHandle, destination, fileOffset: -1, cancellationToken); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => + TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => + WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + + public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) => +#pragma warning disable CA2012 // The analyzer doesn't know the internal AsValueTask is safe. + WriteAsyncCore(source, cancellationToken).AsValueTask(); +#pragma warning restore CA2012 + + private ValueTask WriteAsyncCore(ReadOnlyMemory source, CancellationToken cancellationToken) + { + if (!CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + + long filePositionBefore = -1; + if (CanSeek) + { + filePositionBefore = _filePosition; + _filePosition += source.Length; + } + + return RandomAccess.WriteAtOffsetAsync(_fileHandle, source, filePositionBefore, cancellationToken); + } + + /// Provides a reusable ValueTask-backing object for implementing ReadAsync. + private sealed class ReadAsyncTaskSource : IValueTaskSource, IThreadPoolWorkItem + { + private readonly UnixFileStreamStrategy _stream; + private ManualResetValueTaskSourceCore _source; + + private Memory _destination; + private ExecutionContext? _context; + private CancellationToken _cancellationToken; + + public ReadAsyncTaskSource(UnixFileStreamStrategy stream) => _stream = stream; + + public ValueTask QueueRead(Memory destination, CancellationToken cancellationToken) + { + _destination = destination; + _cancellationToken = cancellationToken; + _context = ExecutionContext.Capture(); + + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: true); + return new ValueTask(this, _source.Version); + } + + void IThreadPoolWorkItem.Execute() + { + if (_context is null || _context.IsDefault) + { + Read(); + } + else + { + ExecutionContext.RunForThreadPoolUnsafe(_context, static x => x.Read(), this); + } + } + + private void Read() + { + Exception? error = null; + int result = 0; + + try + { + if (_cancellationToken.IsCancellationRequested) + { + error = new OperationCanceledException(_cancellationToken); + } + else + { + result = _stream.Read(_destination.Span); + } + } + catch (Exception e) + { + error = e; + } + finally + { + _destination = default; + _cancellationToken = default; + _context = null; + } + + if (error is not null) + { + _source.SetException(error); + } + else + { + _source.SetResult(result); + } + } + + int IValueTaskSource.GetResult(short token) + { + try + { + return _source.GetResult(token); + } + finally + { + _source.Reset(); +#pragma warning disable CS0197 + Volatile.Write(ref _stream._readAsyncTaskSource, this); +#pragma warning restore CS0197 + } + } + + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => + _source.GetStatus(token); + + void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _source.OnCompleted(continuation, state, token, flags); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs index ff577894c60..0b483517b97 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -197,6 +197,13 @@ namespace System false; #endif + internal static bool IsOSXLike() => +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + true; +#else + false; +#endif + /// /// Check for the macOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given macOS release. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs index 3118072c757..1b74aa3a236 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs @@ -571,6 +571,31 @@ namespace System.Threading.Tasks /// Gets a that may be used at any point in the future. public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask()); + /// Casts a to . + internal ValueTask AsValueTask() + { + object? obj = _obj; + Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource); + + if (obj is null) + { + return default; + } + + if (obj is Task t) + { + return new ValueTask(t); + } + + if (obj is IValueTaskSource vts) + { + // This assumes the token used with IVTS is the same as used with IVTS. + return new ValueTask(vts, _token); + } + + return new ValueTask(GetTaskForValueTaskSource(Unsafe.As>(obj))); + } + /// Creates a to represent the . /// /// The is passed in rather than reading and casting From 7ab7eaa24c318cf67ff2b5bc9713b61a86c567b4 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Thu, 8 Jul 2021 17:39:38 +0200 Subject: [PATCH 346/926] Support in-process named mutexes in managed implementation (#55199) * Partially addresses https://github.com/dotnet/runtime/issues/48720 * Use a dictionary to perform the name to object lookup * Allow multiple handles to refer to a single waitable object * Abandon mutex only when all handles refering to it are closed * Re-enable test cases disabled due to the above issue --- .../tests/AssemblyInfo.cs | 1 - .../src/System/Threading/Mutex.Unix.cs | 13 ++- .../System/Threading/WaitSubsystem.Unix.cs | 47 +++++++++++ .../WaitSubsystem.WaitableObject.Unix.cs | 79 ++++++++++++++++++- .../System.Threading/tests/MutexTests.cs | 6 -- 5 files changed, 134 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs index 2e17f103806..bd236b298bc 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs @@ -8,5 +8,4 @@ using Xunit; // create unique identities for every test to allow every test to have // it's own store. [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [assembly: SkipOnPlatform(TestPlatforms.Browser, "System.IO.IsolatedStorage is not supported on Browser")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs index 5b1361247ae..ce958a63499 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs @@ -12,10 +12,15 @@ namespace System.Threading { private void CreateMutexCore(bool initiallyOwned, string? name, out bool createdNew) { - // See https://github.com/dotnet/runtime/issues/48720 if (name != null) { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); + SafeWaitHandle? safeWaitHandle = WaitSubsystem.CreateNamedMutex(initiallyOwned, name, out createdNew); + if (safeWaitHandle == null) + { + throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); + } + SafeWaitHandle = safeWaitHandle; + return; } SafeWaitHandle = WaitSubsystem.NewMutex(initiallyOwned); @@ -24,7 +29,9 @@ namespace System.Threading private static OpenExistingResult OpenExistingWorker(string name, out Mutex? result) { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); + OpenExistingResult status = WaitSubsystem.OpenNamedMutex(name, out SafeWaitHandle? safeWaitHandle); + result = status == OpenExistingResult.Success ? new Mutex(safeWaitHandle!) : null; + return status; } public void ReleaseMutex() diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs index e259f385401..95db6481c23 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs @@ -166,6 +166,53 @@ namespace System.Threading return safeWaitHandle; } + public static SafeWaitHandle? CreateNamedMutex(bool initiallyOwned, string name, out bool createdNew) + { + // For initially owned, newly created named mutexes, there is a potential race + // between adding the mutex to the named object table and initially acquiring it. + // To avoid the possibility of another thread retrieving the mutex via its name + // before we managed to acquire it, we perform both steps while holding s_lock. + s_lock.Acquire(); + bool holdingLock = true; + try + { + WaitableObject? waitableObject = WaitableObject.CreateNamedMutex_Locked(name, out createdNew); + if (waitableObject == null) + { + return null; + } + SafeWaitHandle safeWaitHandle = NewHandle(waitableObject); + if (!initiallyOwned || !createdNew) + { + return safeWaitHandle; + } + + // Acquire the mutex. A thread's has a reference to all es locked + // by the thread. See . So, acquire the lock only after all + // possibilities for exceptions have been exhausted. + ThreadWaitInfo waitInfo = Thread.CurrentThread.WaitInfo; + int status = waitableObject.Wait_Locked(waitInfo, timeoutMilliseconds: 0, interruptible: false, prioritize: false); + Debug.Assert(status == 0); + // Wait_Locked has already released s_lock, so we no longer hold it here. + holdingLock = false; + return safeWaitHandle; + } + finally + { + if (holdingLock) + { + s_lock.Release(); + } + } + } + + public static OpenExistingResult OpenNamedMutex(string name, out SafeWaitHandle? result) + { + OpenExistingResult status = WaitableObject.OpenNamedMutex(name, out WaitableObject? mutex); + result = status == OpenExistingResult.Success ? NewHandle(mutex!) : null; + return status; + } + public static void DeleteHandle(IntPtr handle) { HandleManager.DeleteHandle(handle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs index cbce78aaa5c..eec2018c4c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics; namespace System.Threading @@ -14,9 +15,17 @@ namespace System.Threading /// public sealed class WaitableObject { + /// + /// Dictionary to look up named waitable objects. This implementation only supports in-process + /// named waitable objects. Currently only named mutexes are supported. + /// + private static Dictionary? s_namedObjects; + private readonly WaitableObjectType _type; private int _signalCount; private readonly int _maximumSignalCount; + private int _referenceCount; + private readonly string? _name; /// /// Only has a thread ownership requirement, and it's a less common type to be used, so @@ -33,6 +42,7 @@ namespace System.Threading WaitableObjectType type, int initialSignalCount, int maximumSignalCount, + string? name, OwnershipInfo? ownershipInfo) { Debug.Assert(initialSignalCount >= 0); @@ -42,6 +52,8 @@ namespace System.Threading _type = type; _signalCount = initialSignalCount; _maximumSignalCount = maximumSignalCount; + _referenceCount = 1; + _name = name; _ownershipInfo = ownershipInfo; } @@ -56,17 +68,67 @@ namespace System.Threading : WaitableObjectType.AutoResetEvent, initiallySignaled ? 1 : 0, 1, + null, null); } public static WaitableObject NewSemaphore(int initialSignalCount, int maximumSignalCount) { - return new WaitableObject(WaitableObjectType.Semaphore, initialSignalCount, maximumSignalCount, null); + return new WaitableObject(WaitableObjectType.Semaphore, initialSignalCount, maximumSignalCount, null, null); } public static WaitableObject NewMutex() { - return new WaitableObject(WaitableObjectType.Mutex, 1, 1, new OwnershipInfo()); + return new WaitableObject(WaitableObjectType.Mutex, 1, 1, null, new OwnershipInfo()); + } + + public static WaitableObject? CreateNamedMutex_Locked(string name, out bool createdNew) + { + s_lock.VerifyIsLocked(); + + s_namedObjects ??= new Dictionary(); + + if (s_namedObjects.TryGetValue(name, out WaitableObject? result)) + { + createdNew = false; + if (!result.IsMutex) + { + return null; + } + result._referenceCount++; + } + else + { + createdNew = true; + result = new WaitableObject(WaitableObjectType.Mutex, 1, 1, name, new OwnershipInfo()); + s_namedObjects.Add(name, result); + } + + return result; + } + + public static OpenExistingResult OpenNamedMutex(string name, out WaitableObject? result) + { + s_lock.Acquire(); + try + { + if (s_namedObjects == null || !s_namedObjects.TryGetValue(name, out result)) + { + result = null; + return OpenExistingResult.NameNotFound; + } + if (!result.IsMutex) + { + result = null; + return OpenExistingResult.NameInvalid; + } + result._referenceCount++; + return OpenExistingResult.Success; + } + finally + { + s_lock.Release(); + } } public void OnDeleteHandle() @@ -74,6 +136,19 @@ namespace System.Threading s_lock.Acquire(); try { + // Multiple handles may refer to the same named object. Make sure the object + // is only abandoned once the last handle to it is deleted. Also, remove the + // object from the named objects dictionary at this point. + _referenceCount--; + if (_referenceCount > 0) + { + return; + } + if (_name != null) + { + s_namedObjects!.Remove(_name); + } + if (IsMutex && !IsSignaled) { // A thread has a reference to all es locked by it, see diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index 4af1d538bfa..ee3c15cb8a7 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -45,7 +45,6 @@ namespace System.Threading.Tests } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [MemberData(nameof(GetValidNames))] public void Ctor_ValidName(string name) { @@ -97,7 +96,6 @@ namespace System.Threading.Tests } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [MemberData(nameof(GetValidNames))] public void OpenExisting(string name) { @@ -132,7 +130,6 @@ namespace System.Threading.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void OpenExisting_UnavailableName() { string name = Guid.NewGuid().ToString("N"); @@ -228,7 +225,6 @@ namespace System.Threading.Tests } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [MemberData(nameof(AbandonExisting_MemberData))] public void AbandonExisting( string name, @@ -469,7 +465,6 @@ namespace System.Threading.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void NamedMutex_ThreadExitDisposeRaceTest() { var mutexName = Guid.NewGuid().ToString("N"); @@ -530,7 +525,6 @@ namespace System.Threading.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void NamedMutex_DisposeWhenLockedRaceTest() { var mutexName = Guid.NewGuid().ToString("N"); From cbf4020ac41f6f4095ae652d62ad4a6c1bc88a4f Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 8 Jul 2021 12:23:53 -0400 Subject: [PATCH 347/926] Use DSA and ECDSA keys from certificate in SignedXml If the X.509 certificate has a DSA or ECDSA public key, return them from GetAnyPublicKey() if they are available. --- .../System/Security/Cryptography/Xml/Utils.cs | 11 ++++- .../tests/Samples/SigningVerifyingX509Cert.cs | 23 +++++++++++ .../tests/TestHelpers.cs | 40 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs index 512bc11826a..f706c0a807f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs @@ -750,7 +750,16 @@ namespace System.Security.Cryptography.Xml internal static AsymmetricAlgorithm GetAnyPublicKey(X509Certificate2 certificate) { - return (AsymmetricAlgorithm)certificate.GetRSAPublicKey(); + AsymmetricAlgorithm algorithm = (AsymmetricAlgorithm)certificate.GetRSAPublicKey() ?? certificate.GetECDsaPublicKey(); + +#if NETCOREAPP + if (algorithm is null && !OperatingSystem.IsTvOS() && !OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()) + { + algorithm = certificate.GetDSAPublicKey(); + } +#endif + + return algorithm; } internal const int MaxTransformsPerReference = 10; diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs b/src/libraries/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs index ca68ee3e791..37431c1e53c 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs @@ -76,5 +76,28 @@ namespace System.Security.Cryptography.Xml.Tests Assert.True(VerifyXml(xmlDoc.OuterXml, x509cert)); } } + + [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available")] + public void SignedXmlHasDSACertificateVerifiableSignature() + { + using (X509Certificate2 x509cert = TestHelpers.GetSampleDSAX509Certificate()) + { + var xmlDoc = new XmlDocument(); + xmlDoc.PreserveWhitespace = true; + xmlDoc.LoadXml(ExampleXml); + +#if NETCOREAPP + using (DSA key = x509cert.GetDSAPrivateKey()) + { + SignXml(xmlDoc, key); + } +#else //NETFRAMEWORK + SignXml(xmlDoc, x509cert.PrivateKey); +#endif + + Assert.True(VerifyXml(xmlDoc.OuterXml, x509cert)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs index c155f554f2c..bbd5913d367 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs @@ -190,11 +190,51 @@ namespace System.Security.Cryptography.Xml.Tests // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy certificate.")] @"MIIFpQIBAzCCBV8GCSqGSIb3DQEHAaCCBVAEggVMMIIFSDCCAl8GCSqGSIb3DQEHBqCCAlAwggJMAgEAMIICRQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQIGTfVa4+vR1UCAgfQgIICGJuFE9alFWJFkaoeewKDIEnVwRxXfMsi8dcySYnp7jljEUQBfW/GIbOf7Lg2nHd0qxvxYI2YL4Zs+d0jWbqfNHamGFCMPe1dK957Z2PsKXR183vMSgnmlLAHktsIN+Gor7q1GbQ4ljfZkGqZ/rkgUsgsSYZSnJevP/uH0VnvxemljVJ7N7gKMYO0aqrca4qJ0O4YxBYyaerPFUOYunQlvk6DOF3SQXza5oFKcPGrSpE/9eQrnmm64BtbdnUE6qqEjfZfNa6MOD3vOnapLUBsel2TtVCu8tEl7I8FGxozTLXVTXOBkL3k7xLRS52ZtpbcU2JIhlDGpxeFXmjKYzdzHoL20iJubfdkUYtHwB0XjBKKLcI7jfgGgjNauaTLAx8FF+5O9s7Zbj2+SKWv56kqAwdX+iH21VgjAN9EByIXHb3p2ZOvy4ONDXTmfSn7jbuPLZTi+u6bxn2JOLf/gjEA8FiCuQDL9gF247bnUq08Z1uzuAUeaPL13U8mxwEuvCOXx5NEQIuf3cusnaH4+7uIhPk5tnfA5XOaABySetRjZhVN5dC5/g3KTwmaDamlW3Y7Az/NzAC4uKa2ny5jwYKBgHviEKOyJfLDKr5fOMRToOfgxvAdXZohQQTE1+TcBjp+eeV5koDfB1ReCKIRHugPZu5j9SCVcYanwFeJ5M4cEHZ9U1Ytsmzjh0fwV17D/hxQ4aS4VwVpOMypMIIC4QYJKoZIhvcNAQcBoIIC0gSCAs4wggLKMIICxgYLKoZIhvcNAQwKAQKgggKeMIICmjAcBgoqhkiG9w0BDAEDMA4ECBRdKqx022cfAgIH0ASCAnjZx9fvPCHizdH6apVzWWmfy/84HvDPjFOUV1TPehTnDPkNpF/uK/ya4jlbl4Kw0Zfknt5Xydl89SMXIWa2q+nWmxyG3XyfGqOAeBfJBSdCF5K3qkZZnzEfraKZZ5Hh8IEmK+ey45O6sltua6Xl5MRBmKLiwma7vX4ihXQTMfb0WlWDYCXZi85OeF0OlUjRWAwz4PeeiBK4nmI/vNmF1EzDVdZGkrrE8mot3Y4z6bvwqip2tUUbHuMnC+/1ikAcJzCOw4NpnEWCRtIJxgJ9es8E8CUfHESnWKe4nh6tJVJ15B8/7oF7N6j7oq4Oj346JthKoWWkzifNaH79A60/uFh08Rv7zrtJf6kedY6Ve2bR5lhWn0cv9Q6IaoqTmKKTmKJnjdQO9lKRCR6iI2OsYtXBropD8xhNNqsyfpNmP0G6wFiEZZxZjWOkZEJLUzFbH+Su+7l2l4FN9sM7k211/l3/3YF1QJHwZsgL98DZL4qE+nkuZQcdtOUx8QTyTOcVb3IzgCAwZm0rgdXQpJ9yRBgOC/6MnqaCPI0jJuavXF/a28GJWWGlazx7SWTrbzNVJ83ZhQ+pfPEPtMi3t0YVLLvapu3otgpiMkv4ew/ssXwYbg6xBWfotK+NG1cPwVFy9/V9+H5dpdvRI/le2QG0F5xCfCeKh/3AuNiMPEGoVUR5kj5cwFK6eskvt/+74ZenxfNPZ2Uttiw8DsqtTx1gxhcSZeU5YWpO7O78RaYE4Ll4kPbbvIaR18Napb6NKP846z02zvaw+feXARLe0HUY58TlmUjSX3MZRK4PEdyMIQ/URyPimj4rImaDfFrKPAHIjqT3EKv+KuNs8TEVMBMGCSqGSIb3DQEJFTEGBAQBAAAAMD0wITAJBgUrDgMCGgUABBRZOo132cuo2zNyy+SH2c+pN4OGmQQU2nQao3je7DTj2G6Gge8pooPf2ncCAgfQ"); + private static readonly byte[] SampleDsaPfx = Convert.FromBase64String( + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy certificate.")] + "MIIF+QIBAzCCBb8GCSqGSIb3DQEHAaCCBbAEggWsMIIFqDCCA9cGCSqGSIb3DQEH" + + "BqCCA8gwggPEAgEAMIIDvQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI8liC" + + "7ThjN10CAggAgIIDkFQEBVf6+23WfPd7Rn38z2zvzNEeq5ddwJVQQVctbFTaemHH" + + "15ygd126N+BVw5+2RX8SzDb9qwzHnBUgQQ9WThW3i2JDB9wpE94KJOKYbFTy5hOO" + + "eDw1rf+dbS89jRtWkG3kOxQpgr2I8B/MX4IUgb8SFn/Uqd3V/FcYAPBiBiZxPdHX" + + "QXruCNKXjJDeO5lyvXpB2mN4cf6uXrF44tGbKzmdS2+iFg5xmx7E3KB9CGkH6FmK" + + "O9vG35Hqe+3ZmuAR9qnJAOk9ja3Rx5XuQ248a68MZuPSo2GxMLUXh0BH1aimTHH2" + + "YQz6RzD3jBVIujZ3fhecY6BDCHOZKSjkQEnJAl/K0nzeeKaKN/biK+PouO8qlSnR" + + "lwjBng4XeolCwEE6S3EiingOZRq/IYRkwM6YUfCCsHNRnCALprxuYWRj/5vTiYD8" + + "uIIGLTimOynTTh5tOmJiCHzoEmpq0nGtM0fekbgc+xVsslsT10TtdjjRRK26aw/M" + + "BwkZ7MiPjyTKxJ21zTl0Jc6DcoZK1CKOBy4mG1VE2hZP8oGvNmy1KUZGKeFHS9dA" + + "EjMqhkqPTSOL8wZYenPhaD3dWEWX28mFib9WSy/o6n5U37bwRgI/SxeNt5Q3qbuu" + + "KNo9bC8XGnRl+AaoeUsl62353kZ7SXGTovWSSQ482CK7QMrfKU4MID0IhFxggkNP" + + "GkSYZsIGXC6fVcYjsyzdo/q4NwY3+ysCBVi0294ECvd8cAppXSRXFAjGDwtnBPQC" + + "XTN831Op9gEzId1NTFhhOvYl6S+yXJcmJ52aFj7mB58dNa1qXHmVLug70PNp4Z01" + + "ELYQ5psdEK8r2/pNM8X+e9SZETCzt5jBd5l5kQ+FB5byO2v2nVJ1hnASG3ckGDS0" + + "dl5mW1Y+qa91tu4M5AwDe0ECGirROr3qsP1w2RtPPBgYvGD1SiKp7yZ+2GVUaJEs" + + "pGlmNt90wA6ddjXkO/av3nYWhWSjI2qkNPHFcqOzFsNXsoilgbnUUImfth7U81d4" + + "hHJ6cOgDNfN+sxv6pL3nt12eK24lW2dhetyedr3lN8q6bNkC5l+Oq89CTyz0MlZk" + + "dT/eS5pHsXMFXRDF+ZIljoea13J4jRdVZeZJsqIoql3qdY1SvnyUSyRJR/VfBGWD" + + "aIJjmwG/z5VVjKPklpB72SkqVWyzLF/Vra/rqy1TlFYAZ91eYAQJ3faezB251bKl" + + "80rAXicKdsLMHEhsdjCCAckGCSqGSIb3DQEHAaCCAboEggG2MIIBsjCCAa4GCyqG" + + "SIb3DQEMCgECoIIBdjCCAXIwHAYKKoZIhvcNAQwBAzAOBAh2G75W3/WqEAICCAAE" + + "ggFQ/bprf5JYknV8tp8mcxXphVVPmWW6PxeB9YzLngbSKO7KTd8pLxG8m6eDrCbf" + + "fkgffdT+peGSUWYAu9/RgG2x1AdNTd4s9BgQ8y4nDbB7NV1BW7Cm7VOcYR6/+H+2" + + "CfNU4gPWKBwSFiZfC55/cdIkg4fh9UeoS+ImEApDQu7Thzjc+pVUEiCgnDA83wEs" + + "h7oP63eZhaH5WtPrsnOzpYW3tjLdC59LHe4W1goG8wp552MHxnOiHXZXbgNHdmwx" + + "QexumEv5CHbjW6f7NYFMBycYSPkCH22A8DCwIrid+hcvMPlsTR4Z2wL8PTk9zmi0" + + "R5daKIKRQts6hjPQH8VIXuFeb545i8U1lDw3MiF17Dp2z8vAr3TRZswN979mLVfB" + + "jyuLJ/nIyLLEvpc3iIbSsoGGa5+VxQhk0Wal0PLGdzZ/Y2QhBhixeq94JB0w2fo7" + + "Ljj3MSUwIwYJKoZIhvcNAQkVMRYEFDdYxa4ZJsCeAQZDzHLZ+cdpC2z5MDEwITAJ" + + "BgUrDgMCGgUABBQrvSbTT08JKqS+yVmoU8tj2gzcogQIGnaqGGnaa+ACAggA"); + public static X509Certificate2 GetSampleX509Certificate() { return new X509Certificate2(SamplePfx, "mono"); } + public static X509Certificate2 GetSampleDSAX509Certificate() + { + return new X509Certificate2(SampleDsaPfx, "velleity"); + } + public static Stream LoadResourceStream(string resourceName) { return typeof(TestHelpers).Assembly.GetManifestResourceStream(resourceName); From 45b384dd0ab2b53a19ee0e92b6e33ea33afce0d9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 8 Jul 2021 12:46:25 -0400 Subject: [PATCH 348/926] Use EnumerateLines in TryParseStatusFile (#55266) --- .../Interop.ProcFsStat.TryReadStatusFile.cs | 73 ++++++------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs index 74e9f3f22aa..5dc24477bee 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs @@ -55,88 +55,57 @@ internal static partial class Interop } ParsedStatus results = default(ParsedStatus); - ReadOnlySpan statusFileContents = fileContents.AsSpan(); - int unitSliceLength = -1; -#if DEBUG - int nonUnitSliceLength = -1; -#endif - while (!statusFileContents.IsEmpty) + foreach (ReadOnlySpan line in fileContents.AsSpan().EnumerateLines()) { - int startIndex = statusFileContents.IndexOf(':'); + int startIndex = line.IndexOf(':'); if (startIndex == -1) { - // Reached end of file break; } - ReadOnlySpan title = statusFileContents.Slice(0, startIndex); - statusFileContents = statusFileContents.Slice(startIndex + 1); - int endIndex = statusFileContents.IndexOf('\n'); - if (endIndex == -1) - { - endIndex = statusFileContents.Length - 1; - unitSliceLength = statusFileContents.Length - 3; -#if DEBUG - nonUnitSliceLength = statusFileContents.Length; -#endif - } - else - { - unitSliceLength = endIndex - 3; -#if DEBUG - nonUnitSliceLength = endIndex; -#endif - } - - ReadOnlySpan value = default; + ReadOnlySpan name = line.Slice(0, startIndex); + ReadOnlySpan value = line.Slice(startIndex + 1); bool valueParsed = true; + #if DEBUG - if (title.SequenceEqual("Pid".AsSpan())) + if (name.SequenceEqual("Pid")) { - value = statusFileContents.Slice(0, nonUnitSliceLength); valueParsed = int.TryParse(value, out results.Pid); } + else #endif - if (title.SequenceEqual("VmHWM".AsSpan())) + if (name.SequenceEqual("VmHWM")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out results.VmHWM); + valueParsed = ulong.TryParse(value[..^3], out results.VmHWM); } - else if (title.SequenceEqual("VmRSS".AsSpan())) + else if (name.SequenceEqual("VmRSS")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out results.VmRSS); + valueParsed = ulong.TryParse(value[..^3], out results.VmRSS); } - else if (title.SequenceEqual("VmData".AsSpan())) + else if (name.SequenceEqual("VmData")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out ulong vmData); + valueParsed = ulong.TryParse(value[..^3], out ulong vmData); results.VmData += vmData; } - else if (title.SequenceEqual("VmSwap".AsSpan())) + else if (name.SequenceEqual("VmSwap")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out results.VmSwap); + valueParsed = ulong.TryParse(value[..^3], out results.VmSwap); } - else if (title.SequenceEqual("VmSize".AsSpan())) + else if (name.SequenceEqual("VmSize")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out results.VmSize); + valueParsed = ulong.TryParse(value[..^3], out results.VmSize); } - else if (title.SequenceEqual("VmPeak".AsSpan())) + else if (name.SequenceEqual("VmPeak")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out results.VmPeak); + valueParsed = ulong.TryParse(value[..^3], out results.VmPeak); } - else if (title.SequenceEqual("VmStk".AsSpan())) + else if (name.SequenceEqual("VmStk")) { - value = statusFileContents.Slice(0, unitSliceLength); - valueParsed = ulong.TryParse(value, out ulong vmStack); + valueParsed = ulong.TryParse(value[..^3], out ulong vmStack); results.VmData += vmStack; } Debug.Assert(valueParsed); - statusFileContents = statusFileContents.Slice(endIndex + 1); } results.VmData *= 1024; From f30c848bfec03cadb0b9ec85234d9ec82698b78e Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 8 Jul 2021 09:50:26 -0700 Subject: [PATCH 349/926] Add missing extra jit flag dump entries (#55280) --- src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp index 5fedda99fd9..14d5100cc6d 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp @@ -285,6 +285,8 @@ std::string SpmiDumpHelper::DumpJitFlags(unsigned long long flags) AddFlagNumeric(HAS_EDGE_PROFILE, EXTRA_JIT_FLAGS::HAS_EDGE_PROFILE); AddFlagNumeric(HAS_CLASS_PROFILE, EXTRA_JIT_FLAGS::HAS_CLASS_PROFILE); AddFlagNumeric(HAS_LIKELY_CLASS, EXTRA_JIT_FLAGS::HAS_LIKELY_CLASS); + AddFlagNumeric(HAS_STATIC_PROFILE, EXTRA_JIT_FLAGS::HAS_STATIC_PROFILE); + AddFlagNumeric(HAS_DYNAMIC_PROFILE, EXTRA_JIT_FLAGS::HAS_DYNAMIC_PROFILE); #undef AddFlag #undef AddFlagNumeric From 9270b34a5ddda080390c3203c2378186225e4cb1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 8 Jul 2021 10:39:29 -0700 Subject: [PATCH 350/926] Fix race condition caused by updating interface map with exact target (#55308) - Change interface map reading to reliably perform a volatile load (without barrier) - When performing scan for exact interface match/match trhough SpecialMarkerTypeForGenericCasting, be careful to only read the map once and use that value for all comparisons - All other uses of InterfaceMapIterator have been audited, and look to be safe --- src/coreclr/vm/methodtable.cpp | 6 ++++-- src/coreclr/vm/methodtable.h | 24 +++++++++++++++++++++--- src/coreclr/vm/methodtable.inl | 11 ++++++++++- src/coreclr/vm/siginfo.cpp | 2 ++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 2fc25cf8133..834d3cfc8c4 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -713,8 +713,10 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule return pItfMT; } -#endif MethodTable * pItfMT = m_pMethodTable.GetValue(); +#else + MethodTable * pItfMT = GetMethodTable(); +#endif ClassLoader::EnsureLoaded(TypeHandle(pItfMT), CLASS_LOAD_APPROXPARENTS); return pItfMT; } @@ -9269,7 +9271,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* { if (it.CurrentInterfaceMatches(this, pInterfaceType)) { - // This is the variant interface check logic, skip the + // This is the variant interface check logic, skip the exact matches as they were handled above continue; } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 476a1cd3faa..4063e50b7b6 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -105,24 +105,36 @@ struct InterfaceInfo_t #endif // Method table of the interface +#ifdef FEATURE_PREJIT #if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) RelativeFixupPointer m_pMethodTable; #else FixupPointer m_pMethodTable; #endif +#else + PTR_MethodTable m_pMethodTable; +#endif public: FORCEINLINE PTR_MethodTable GetMethodTable() { LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_PREJIT return ReadPointerMaybeNull(this, &InterfaceInfo_t::m_pMethodTable); +#else + return VolatileLoadWithoutBarrier(&m_pMethodTable); +#endif } #ifndef DACCESS_COMPILE void SetMethodTable(MethodTable * pMT) { LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_PREJIT m_pMethodTable.SetValueMaybeNull(pMT); +#else + return VolatileStoreWithoutBarrier(&m_pMethodTable, pMT); +#endif } // Get approximate method table. This is used by the type loader before the type is fully loaded. @@ -132,7 +144,11 @@ public: #ifndef DACCESS_COMPILE InterfaceInfo_t(InterfaceInfo_t &right) { +#ifdef FEATURE_PREJIT m_pMethodTable.SetValueMaybeNull(right.m_pMethodTable.GetValueMaybeNull()); +#else + VolatileStoreWithoutBarrier(&m_pMethodTable, VolatileLoadWithoutBarrier(&right.m_pMethodTable)); +#endif } #else // !DACCESS_COMPILE private: @@ -2222,12 +2238,14 @@ public: } CONTRACT_END; - bool exactMatch = m_pMap->GetMethodTable() == pMT; + MethodTable *pCurrentMethodTable = m_pMap->GetMethodTable(); + + bool exactMatch = pCurrentMethodTable == pMT; if (!exactMatch) { - if (m_pMap->GetMethodTable()->HasSameTypeDefAs(pMT) && + if (pCurrentMethodTable->HasSameTypeDefAs(pMT) && pMT->HasInstantiation() && - m_pMap->GetMethodTable()->IsSpecialMarkerTypeForGenericCasting() && + pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() && pMT->GetInstantiation().ContainsAllOneType(pMTOwner)) { exactMatch = true; diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 688fc2630bf..a3adc702cbe 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1578,7 +1578,16 @@ FORCEINLINE BOOL MethodTable::ImplementsInterfaceInline(MethodTable *pInterface) do { - if (pInfo->GetMethodTable()->HasSameTypeDefAs(pInterface) && pInfo->GetMethodTable()->IsSpecialMarkerTypeForGenericCasting()) + MethodTable *pInterfaceInMap = pInfo->GetMethodTable(); + if (pInterfaceInMap == pInterface) + { + // Since there is no locking on updating the interface with an exact match + // It is possible to reach here for a match which would ideally have been handled above + // GetMethodTable uses a VolatileLoadWithoutBarrier to prevent compiler optimizations + // from interfering with this check + return TRUE; + } + if (pInterfaceInMap->HasSameTypeDefAs(pInterface) && pInterfaceInMap->IsSpecialMarkerTypeForGenericCasting()) { // Extensible RCW's need to be handled specially because they can have interfaces // in their map that are added at runtime. These interfaces will have a start offset diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 2d0889c5324..df7315fc879 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1417,11 +1417,13 @@ TypeHandle SigPointer::GetTypeHandleThrowing( Instantiation genericLoadInst(thisinst, ntypars); +#ifndef FEATURE_PREJIT // PREJIT doesn't support the volatile update semantics in the interface map that this requires if (pMTInterfaceMapOwner != NULL && genericLoadInst.ContainsAllOneType(pMTInterfaceMapOwner)) { thRet = ClassLoader::LoadTypeDefThrowing(pGenericTypeModule, tkGenericType, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, level); } else +#endif // FEATURE_PREJIT { // Group together the current signature type context and substitution chain, which // we may later use to instantiate constraints of type arguments that turn out to be From df7f02c492f73a6b44826ce93b300f8e0e9aaf19 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 8 Jul 2021 10:46:15 -0700 Subject: [PATCH 351/926] Do not setup both an inlined call frame and perform a reverse pinvoke in the same function (#55092) - Pushing/popping the frame chain requires that the thread be in cooperative mode - Before the reverse pinvoke logic engages, the thread is in preemptive mode - The current implementation of InlinedCallFrame and reverse p/invoke sets up the InlinedCallFrame before the reverse p/invoke transition, which is unsafe - A reverse pinvoke function directly calling back into native code is fairly rare, and so optimizing the situation is of marginal utility. - The fix is to use the pinvoke helpers logic to setup the pinvoke instead of relying on the InlinedCallFrame logic. This avoid the problem of incorrect ordering in the prolog/epilog, but moving inlined call frame handling to point of use. Fix bug #45326 --- src/coreclr/jit/compiler.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 620036ed7c8..356fe6498d1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9218,10 +9218,14 @@ public: #endif // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating - // PInvoke transitions inline. + // PInvoke transitions inline. Normally used by R2R, but also used when generating a reverse pinvoke frame, as + // the current logic for frame setup initializes and pushes + // the InlinedCallFrame before performing the Reverse PInvoke transition, which is invalid (as frames cannot + // safely be pushed/popped while the thread is in a preemptive state.). bool ShouldUsePInvokeHelpers() { - return jitFlags->IsSet(JitFlags::JIT_FLAG_USE_PINVOKE_HELPERS); + return jitFlags->IsSet(JitFlags::JIT_FLAG_USE_PINVOKE_HELPERS) || + jitFlags->IsSet(JitFlags::JIT_FLAG_REVERSE_PINVOKE); } // true if we should use insert the REVERSE_PINVOKE_{ENTER,EXIT} helpers in the method From 8bab2effec2a3951315ea2e43687e91fb05ee277 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 8 Jul 2021 20:50:16 +0300 Subject: [PATCH 352/926] JIT: Set default value for JitExtDefaultPolicyProfTrust 0 (#55229) --- src/coreclr/jit/inlinepolicy.cpp | 10 +++++++++- src/coreclr/jit/jitconfigvalues.h | 11 ++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index c328233cdf5..fdf4ae19341 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -1648,7 +1648,15 @@ double ExtendedDefaultPolicy::DetermineMultiplier() const double profileTrustCoef = (double)JitConfig.JitExtDefaultPolicyProfTrust() / 10.0; const double profileScale = (double)JitConfig.JitExtDefaultPolicyProfScale() / 10.0; - multiplier *= (1.0 - profileTrustCoef) + min(m_ProfileFrequency, 1.0) * profileScale; + if (m_RootCompiler->fgPgoSource == ICorJitInfo::PgoSource::Dynamic) + { + // For now we only "trust" dynamic profiles. + multiplier *= (1.0 - profileTrustCoef) + min(m_ProfileFrequency, 1.0) * profileScale; + } + else + { + multiplier *= min(m_ProfileFrequency, 1.0) * profileScale; + } JITDUMP("\nCallsite has profile data: %g.", m_ProfileFrequency); } diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 7527fd49221..cc10adc0b88 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -462,8 +462,17 @@ CONFIG_STRING(JitInlineReplayFile, W("JitInlineReplayFile")) CONFIG_INTEGER(JitExtDefaultPolicy, W("JitExtDefaultPolicy"), 1) CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x64) CONFIG_INTEGER(JitExtDefaultPolicyMaxBB, W("JitExtDefaultPolicyMaxBB"), 7) -CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfScale"), 0x2A) + +// Inliner uses the following formula for PGO-driven decisions: +// +// BM = BM * ((1.0 - ProfTrust) + ProfWeight * ProfScale) +// +// Where BM is a benefit multiplier composed from various observations (e.g. "const arg makes a branch foldable"). +// If a profile data can be trusted for 100% we can safely just give up on inlining anything inside cold blocks +// (except the cases where inlining in cold blocks improves type info/escape analysis for the whole caller). +// For now, it's only applied for dynamic PGO. CONFIG_INTEGER(JitExtDefaultPolicyProfTrust, W("JitExtDefaultPolicyProfTrust"), 0x7) +CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfScale"), 0x2A) CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) From 89d23da715c2a7ef99d1a3bc510522fffa50ef99 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 8 Jul 2021 10:53:12 -0700 Subject: [PATCH 353/926] Fix NRE in EncodingProvider (#55238) * Issue 55233: fix NRE in EncodingProvider * Add test Co-authored-by: Stephen Toub --- .../src/System/Text/EncodingProvider.cs | 2 +- .../tests/System/Text/EncodingTests.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs index 8857f0fa713..3e8723c9fca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs @@ -71,7 +71,7 @@ namespace System.Text var newProviders = new EncodingProvider[providers.Length + 1]; Array.Copy(providers, newProviders, providers.Length); - providers[^1] = provider; + newProviders[^1] = provider; if (Interlocked.CompareExchange(ref s_providers, newProviders, providers) == providers) { diff --git a/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs b/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs index b598a219885..4aec989f3e7 100644 --- a/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs +++ b/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.RemoteExecutor; using Moq; using Xunit; @@ -106,6 +107,25 @@ namespace System.Text.Tests }); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void RegisterProvider_EncodingsAreUsable() + { + RemoteExecutor.Invoke(() => + { + for (int i = 0; i < 10; i++) + { + Encoding.RegisterProvider(new NullEncodingProvider()); + Assert.Same(Encoding.UTF8, Encoding.GetEncoding(65001)); + } + }).Dispose(); + } + + private sealed class NullEncodingProvider : EncodingProvider + { + public override Encoding GetEncoding(int codepage) => null; + public override Encoding GetEncoding(string name) => null; + } + private sealed class ThreadStaticEncodingProvider : EncodingProvider { private static readonly object _globalRegistrationLockObj = new object(); From ab64ce5f71479549222c99da3a15fd56f8328fe4 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 8 Jul 2021 20:31:52 +0200 Subject: [PATCH 354/926] Clean-up sendtohelix and fix prop ordering issue (#55265) The ordering issue was discovered in https://github.com/dotnet/runtime/pull/55074#issuecomment-875296646. Fixing it by not reading from properties which aren't defined in props inside the project file but from within an initial target. --- src/libraries/sendtohelix.proj | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/libraries/sendtohelix.proj b/src/libraries/sendtohelix.proj index 940bb713331..6dff5f9b455 100644 --- a/src/libraries/sendtohelix.proj +++ b/src/libraries/sendtohelix.proj @@ -12,22 +12,26 @@ "correlation payload", which is the set of files used by all Helix submissions (which we compress into a single file). --> - + - $(NetCoreAppToolCurrent) - $([MSBuild]::ValueOrDefault('$(BuildTargetFramework)', '$(NetCoreAppCurrent)')) + $(NetCoreAppCurrent) true - - $(TestArchiveRuntimeRoot)test-runtime-$(NetCoreAppCurrentBuildSettings).zip - SetStressModes_$(Scenario).cmd SetStressModes_$(Scenario).sh + + + + + $(TestArchiveRuntimeRoot)test-runtime-$(NetCoreAppCurrentBuildSettings).zip + + @@ -83,12 +87,8 @@ - - - - @@ -157,7 +157,6 @@ Outputs="$(TestArchiveRuntimeFile)" Condition="'$(TargetsMobile)' != 'true' and '$(TestArchiveRuntimeFile)' != ''"> - @@ -168,7 +167,6 @@ - 3.10.0 3.10.0 - 6.0.0-rc1.21324.1 + 6.0.0-rc1.21356.1 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 2.5.1-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 - 6.0.0-beta.21330.2 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 2.5.1-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 + 6.0.0-beta.21357.3 6.0.0-preview.1.102 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21355.1 3.1.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 5.0.0 4.3.0 @@ -107,27 +107,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21321.2 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21355.1 4.5.4 4.5.0 - 6.0.0-preview.7.21321.2 + 6.0.0-preview.7.21355.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21307.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 - 6.0.0-beta.21314.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 + 6.0.0-beta.21356.1 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 - 1.0.0-prerelease.21330.5 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21357.3 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -151,9 +151,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21330.2 - 1.0.0-prerelease.21330.2 - 1.0.1-alpha.0.21330.1 + 1.0.0-prerelease.21357.4 + 1.0.0-prerelease.21357.4 + 1.0.1-alpha.0.21355.1 2.4.1 2.4.2 1.3.0 @@ -164,23 +164,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21330.1 + 6.0.100-preview.6.21357.1 $(MicrosoftNETILLinkTasksVersion) 6.0.0-preview.7.21328.1 6.0.0-preview.7.21357.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 - 11.1.0-alpha.1.21328.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21357.1 - 6.0.0-preview.7.21330.1 + 6.0.0-preview.7.21355.1 $(MicrosoftNETWorkloadEmscriptenManifest60100) diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index a0b5fc37f43..18823840b11 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -158,4 +158,10 @@ if ($dotnet5Source -ne $null) { AddPackageSource -Sources $sources -SourceName "dotnet5-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password } +$dotnet6Source = $sources.SelectSingleNode("add[@key='dotnet6']") +if ($dotnet6Source -ne $null) { + AddPackageSource -Sources $sources -SourceName "dotnet6-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2" -Creds $creds -Username $userName -Password $Password + AddPackageSource -Sources $sources -SourceName "dotnet6-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password +} + $doc.Save($filename) diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index 2734601c13c..ad3fb74fd2c 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -129,6 +129,30 @@ if [ "$?" == "0" ]; then PackageSources+=('dotnet5-internal-transport') fi +# Ensure dotnet6-internal and dotnet6-internal-transport are in the packageSources if the public dotnet6 feeds are present +grep -i "" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet6-internal') + + grep -i "" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding dotnet6-internal-transport to the packageSources." + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + fi + PackageSources+=('dotnet6-internal-transport') +fi + # I want things split line by line PrevIFS=$IFS IFS=$'\n' diff --git a/global.json b/global.json index ca21d08dc3b..99807111962 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21330.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21357.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21330.2", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21330.2", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21357.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21355.1" } } diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index d6d9a78c22e..b5f603cee4f 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -318,7 +318,8 @@ + Condition="'$(IncludeHelixCorrelationPayload)' == 'true' and '$(TargetOS)' != 'Browser'" + AsArchive="$(HelixCorrelationPayload.EndsWith('.zip'))" /> From d9f1ade5f0ec01c6e65b01f1e6d2280d959dfd9a Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Thu, 8 Jul 2021 13:15:12 -0700 Subject: [PATCH 358/926] fix read abort handling and revert CanRead/CanWrite to previous behavior (#55341) Co-authored-by: Geoffrey Kizer --- .../Quic/Implementations/Mock/MockStream.cs | 29 ++++++++++++--- .../Implementations/MsQuic/MsQuicStream.cs | 27 ++++++++++++-- .../tests/FunctionalTests/QuicStreamTests.cs | 37 +++++++++++++++---- .../tests/FunctionalTests/QuicTestBase.cs | 9 +++++ 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs index bd814f690d9..1d09b633174 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs @@ -70,7 +70,7 @@ namespace System.Net.Quic.Implementations.Mock int bytesRead = await streamBuffer.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { - long errorCode = _isInitiator ? _streamState._inboundErrorCode : _streamState._outboundErrorCode; + long errorCode = _isInitiator ? _streamState._inboundReadErrorCode : _streamState._outboundReadErrorCode; if (errorCode != 0) { throw new QuicStreamAbortedException(errorCode); @@ -121,6 +121,12 @@ namespace System.Net.Quic.Implementations.Mock throw new NotSupportedException(); } + long errorCode = _isInitiator ? _streamState._inboundWriteErrorCode : _streamState._outboundWriteErrorCode; + if (errorCode != 0) + { + throw new QuicStreamAbortedException(errorCode); + } + using var registration = cancellationToken.UnsafeRegister(static s => { var stream = (MockStream)s!; @@ -171,18 +177,27 @@ namespace System.Net.Quic.Implementations.Mock internal override void AbortRead(long errorCode) { - throw new NotImplementedException(); + if (_isInitiator) + { + _streamState._outboundWriteErrorCode = errorCode; + } + else + { + _streamState._inboundWriteErrorCode = errorCode; + } + + ReadStreamBuffer?.AbortRead(); } internal override void AbortWrite(long errorCode) { if (_isInitiator) { - _streamState._outboundErrorCode = errorCode; + _streamState._outboundReadErrorCode = errorCode; } else { - _streamState._inboundErrorCode = errorCode; + _streamState._inboundReadErrorCode = errorCode; } WriteStreamBuffer?.EndWrite(); @@ -255,8 +270,10 @@ namespace System.Net.Quic.Implementations.Mock public readonly long _streamId; public StreamBuffer _outboundStreamBuffer; public StreamBuffer? _inboundStreamBuffer; - public long _outboundErrorCode; - public long _inboundErrorCode; + public long _outboundReadErrorCode; + public long _inboundReadErrorCode; + public long _outboundWriteErrorCode; + public long _inboundWriteErrorCode; private const int InitialBufferSize = #if DEBUG diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 8260dfdd68f..fed61e11e56 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -20,6 +20,9 @@ namespace System.Net.Quic.Implementations.MsQuic private readonly State _state = new State(); + private readonly bool _canRead; + private readonly bool _canWrite; + // Backing for StreamId private long _streamId = -1; @@ -80,8 +83,10 @@ namespace System.Net.Quic.Implementations.MsQuic internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { _state.Handle = streamHandle; + _canRead = true; + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _started = true; - if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + if (!_canWrite) { _state.SendState = SendState.Closed; } @@ -122,8 +127,11 @@ namespace System.Net.Quic.Implementations.MsQuic { Debug.Assert(connectionState.Handle != null); + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); + _canWrite = true; + _state.StateGCHandle = GCHandle.Alloc(_state); - if (flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL)) + if (!_canRead) { _state.ReadState = ReadState.Closed; } @@ -167,9 +175,9 @@ namespace System.Net.Quic.Implementations.MsQuic } } - internal override bool CanRead => _disposed == 0 && _state.ReadState < ReadState.Aborted; + internal override bool CanRead => _disposed == 0 && _canRead; - internal override bool CanWrite => _disposed == 0 && _state.SendState < SendState.Aborted; + internal override bool CanWrite => _disposed == 0 && _canWrite; internal override long StreamId { @@ -242,6 +250,11 @@ namespace System.Net.Quic.Implementations.MsQuic } else if ( _state.SendState == SendState.Aborted) { + if (_state.SendErrorCode != -1) + { + throw new QuicStreamAbortedException(_state.SendErrorCode); + } + throw new OperationCanceledException(cancellationToken); } @@ -292,6 +305,12 @@ namespace System.Net.Quic.Implementations.MsQuic if (_state.SendState == SendState.Aborted) { cancellationToken.ThrowIfCancellationRequested(); + + if (_state.SendErrorCode != -1) + { + throw new QuicStreamAbortedException(_state.SendErrorCode); + } + throw new OperationCanceledException(SR.net_quic_sending_aborted); } else if (_state.SendState == SendState.ConnectionClosed) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index dacd04a448c..7b409036518 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -437,17 +437,15 @@ namespace System.Net.Quic.Tests } [Fact] - public async Task StreamAbortedWithoutWriting_ReadThrows() + public async Task WriteAbortedWithoutWriting_ReadThrows() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => { await using QuicStream stream = connection.OpenUnidirectionalStream(); stream.AbortWrite(expectedErrorCode); - - await stream.ShutdownCompleted(); }, serverFunction: async connection => { @@ -458,7 +456,32 @@ namespace System.Net.Quic.Tests QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => ReadAll(stream, buffer)); Assert.Equal(expectedErrorCode, ex.ErrorCode); - await stream.ShutdownCompleted(); + // We should still return true from CanRead, even though the read has been aborted. + Assert.True(stream.CanRead); + } + ); + } + + [Fact] + public async Task ReadAbortedWithoutReading_WriteThrows() + { + const long expectedErrorCode = 1234; + + await RunClientServer( + clientFunction: async connection => + { + await using QuicStream stream = connection.OpenBidirectionalStream(); + stream.AbortRead(expectedErrorCode); + }, + serverFunction: async connection => + { + await using QuicStream stream = await connection.AcceptStreamAsync(); + + QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => WriteForever(stream)); + Assert.Equal(expectedErrorCode, ex.ErrorCode); + + // We should still return true from CanWrite, even though the write has been aborted. + Assert.True(stream.CanWrite); } ); } @@ -466,7 +489,7 @@ namespace System.Net.Quic.Tests [Fact] public async Task WritePreCanceled_Throws() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => @@ -502,7 +525,7 @@ namespace System.Net.Quic.Tests [Fact] public async Task WriteCanceled_NextWriteThrows() { - long expectedErrorCode = 1234; + const long expectedErrorCode = 1234; await RunClientServer( clientFunction: async connection => diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index ee7501868be..c19242342f2 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -130,6 +130,15 @@ namespace System.Net.Quic.Tests return bytesRead; } + internal static async Task WriteForever(QuicStream stream) + { + Memory buffer = new byte[] { 123 }; + while (true) + { + await stream.WriteAsync(buffer); + } + } + internal static void AssertArrayEqual(byte[] expected, byte[] actual) { for (int i = 0; i < expected.Length; ++i) From 7ae4d1acbc73b5d8e3f79797e5e73d5a546e8470 Mon Sep 17 00:00:00 2001 From: imhameed Date: Thu, 8 Jul 2021 13:28:20 -0700 Subject: [PATCH 359/926] [mono] Make Mono CMake Windows build work without mono.proj (#54855) --- src/mono/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 52a82116bc4..4c25d97b7a5 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -16,7 +16,7 @@ if(NOT MONO_LIB_NAME) endif() if(NOT MONO_SHARED_LIB_NAME) - set(MONO_SHARED_LIB_NAME "$(MONO_LIB_NAME)") + set(MONO_SHARED_LIB_NAME "${MONO_LIB_NAME}") endif() include(GNUInstallDirs) @@ -712,8 +712,15 @@ endif() ###################################### # EXTRACT VERSION ###################################### -if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/_version.h") - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "static char sccsid[] __attribute__((used)) = \"@(#)Version 42.42.42.42424 @Commit: AAA\";\n") +if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/_version.h") + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "#undef VER_PRODUCTVERSION_STR\n#define VER_PRODUCTVERSION_STR \"42.42.42.42424\"\n") + else() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_version.h" "static char sccsid[] __attribute__((used)) = \"@(#)Version 42.42.42.42424 @Commit: AAA\";\n") + endif() +endif() +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows" AND NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/NativeVersion.rc") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/NativeVersion.rc" "\n") endif() if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/runtime_version.h") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/runtime_version.h" From 24329262b88fd29951a7b561169c12e82c668b19 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 8 Jul 2021 15:29:10 -0500 Subject: [PATCH 360/926] Fix NullReferenceException in background thread emitting IL in DependencyInjection (#55340) * Fix NullReferenceException in background thread emitting IL in DependencyInjection When ILEmitResolverBuilder is getting created, it grabs the "Root" scope off of the ServiceProvider. However, the Root scope isn't set on ServiceProvider yet. So later when it tries to get used, it null refs. But this exception gets caught and eaten since it happens on a background thread. The fix is to set Root before creating the ServiceProviderEngine. * Add a debug assert that we shouldn't get exceptions from the background compilation thread --- .../src/ServiceLookup/DynamicServiceProviderEngine.cs | 3 +++ .../src/ServiceProvider.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index ee124f31be8..0237e8ad096 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Threading; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup @@ -37,6 +38,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup catch (Exception ex) { DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex); + + Debug.Fail($"We should never get exceptions from the background compilation.{Environment.NewLine}{ex}"); } }, null); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index b93a11d9566..afe118acf79 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -32,11 +32,12 @@ namespace Microsoft.Extensions.DependencyInjection internal ServiceProvider(IEnumerable serviceDescriptors, ServiceProviderOptions options) { + // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root + Root = new ServiceProviderEngineScope(this, isRootScope: true); _engine = GetEngine(); _createServiceAccessor = CreateServiceAccessor; _realizedServices = new ConcurrentDictionary>(); - Root = new ServiceProviderEngineScope(this, isRootScope: true); CallSiteFactory = new CallSiteFactory(serviceDescriptors); // The list of built in services that aren't part of the list of service descriptors // keep this in sync with CallSiteFactory.IsService From f3b777546cceaeea7693b22a16bc08c797ec38bc Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 9 Jul 2021 00:14:21 +0300 Subject: [PATCH 361/926] Print indices of assertions instead of raw bitsets (#54928) * Add JITDUMPEXEC macro For use in contexts where some printing method should only be executed when "verbose" is true. * Add helpers for printing assertion indexes * Print assertion indices instead of raw bitsets To aid in understanding what assertions are being propagated and merged when reading the dumps. * Don't print VNs for the same assertion twice * Also correctly print VNs in CopyProp * Align "in"s with "out"s for final assertions * Don't print the assertion dataflow in usual dumps It can still be enabled if needed. --- src/coreclr/jit/assertionprop.cpp | 184 ++++++++++++++++++++++-------- src/coreclr/jit/compiler.h | 5 + src/coreclr/jit/copyprop.cpp | 4 +- src/coreclr/jit/jit.h | 6 + src/coreclr/jit/rangecheck.cpp | 15 +-- 5 files changed, 160 insertions(+), 54 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 981879142bf..fb869c89fee 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -558,7 +558,7 @@ void Compiler::optAssertionInit(bool isLocalProp) } #ifdef DEBUG -void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* =0 */) +void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* = 0 */) { if (curAssertion->op1.kind == O1K_EXACT_TYPE) { @@ -590,10 +590,6 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse printf("?assertion classification? "); } printf("Assertion: "); - if (!optLocalAssertionProp) - { - printf("(%d, %d) ", curAssertion->op1.vn, curAssertion->op2.vn); - } if (!optLocalAssertionProp) { @@ -778,13 +774,68 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse if (assertionIndex > 0) { - printf(" index=#%02u, mask=", assertionIndex); - printf("%s", BitVecOps::ToString(apTraits, BitVecOps::MakeSingleton(apTraits, assertionIndex - 1))); + printf(", index = "); + optPrintAssertionIndex(assertionIndex); } printf("\n"); } + +void Compiler::optPrintAssertionIndex(AssertionIndex index) +{ + if (index == NO_ASSERTION_INDEX) + { + printf("#NA"); + return; + } + + printf("#%02u", index); +} + +void Compiler::optPrintAssertionIndices(ASSERT_TP assertions) +{ + if (BitVecOps::IsEmpty(apTraits, assertions)) + { + optPrintAssertionIndex(NO_ASSERTION_INDEX); + return; + } + + BitVecOps::Iter iter(apTraits, assertions); + unsigned bitIndex = 0; + if (iter.NextElem(&bitIndex)) + { + optPrintAssertionIndex(static_cast(bitIndex + 1)); + while (iter.NextElem(&bitIndex)) + { + printf(" "); + optPrintAssertionIndex(static_cast(bitIndex + 1)); + } + } +} #endif // DEBUG +/* static */ +void Compiler::optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer /* = nullptr */) +{ +#ifdef DEBUG + Compiler* compiler = JitTls::GetCompiler(); + if (compiler->verbose) + { + printf(header); + compiler->optPrintAssertionIndices(assertions); + if (footer != nullptr) + { + printf(footer); + } + } +#endif // DEBUG +} + +/* static */ +void Compiler::optDumpAssertionIndices(ASSERT_TP assertions, const char* footer /* = nullptr */) +{ + optDumpAssertionIndices("", assertions, footer); +} + /****************************************************************************** * * Helper to retrieve the "assertIndex" assertion. Note that assertIndex 0 @@ -4511,7 +4562,7 @@ void Compiler::optImpliedByConstAssertion(AssertionDsc* constAssertion, ASSERT_T if (verbose) { AssertionDsc* firstAssertion = optGetAssertion(1); - printf("\nCompiler::optImpliedByConstAssertion: constAssertion #%02d , implies assertion #%02d", + printf("Compiler::optImpliedByConstAssertion: const assertion #%02d implies assertion #%02d\n", (constAssertion - firstAssertion) + 1, (impAssertion - firstAssertion) + 1); } #endif @@ -4721,8 +4772,12 @@ public: // At the start of the merge function of the dataflow equations, initialize premerge state (to detect change.) void StartMerge(BasicBlock* block) { - JITDUMP("AssertionPropCallback::StartMerge: " FMT_BB " in -> %s\n", block->bbNum, - BitVecOps::ToString(apTraits, block->bbAssertionIn)); + if (VerboseDataflow()) + { + JITDUMP("StartMerge: " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n"); + } + BitVecOps::Assign(apTraits, preMergeOut, block->bbAssertionOut); BitVecOps::Assign(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum]); } @@ -4743,11 +4798,14 @@ public: assert(predBlock->bbNext == block); BitVecOps::IntersectionD(apTraits, pAssertionOut, predBlock->bbAssertionOut); - JITDUMP("AssertionPropCallback::Merge : Duplicate flow, " FMT_BB " in -> %s, predBlock " FMT_BB - " out1 -> %s, out2 -> %s\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum, - BitVecOps::ToString(apTraits, mJumpDestOut[predBlock->bbNum]), - BitVecOps::ToString(apTraits, predBlock->bbAssertionOut)); + if (VerboseDataflow()) + { + JITDUMP("Merge : Duplicate flow, " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; "); + JITDUMP("pred " FMT_BB " ", predBlock->bbNum); + Compiler::optDumpAssertionIndices("out1 -> ", mJumpDestOut[predBlock->bbNum], "; "); + Compiler::optDumpAssertionIndices("out2 -> ", predBlock->bbAssertionOut, "\n"); + } } } else @@ -4755,9 +4813,14 @@ public: pAssertionOut = predBlock->bbAssertionOut; } - JITDUMP("AssertionPropCallback::Merge : " FMT_BB " in -> %s, predBlock " FMT_BB " out -> %s\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum, - BitVecOps::ToString(apTraits, pAssertionOut)); + if (VerboseDataflow()) + { + JITDUMP("Merge : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; "); + JITDUMP("pred " FMT_BB " ", predBlock->bbNum); + Compiler::optDumpAssertionIndices("out -> ", pAssertionOut, "\n"); + } + BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, pAssertionOut); } @@ -4781,8 +4844,11 @@ public: // At the end of the merge store results of the dataflow equations, in a postmerge state. bool EndMerge(BasicBlock* block) { - JITDUMP("AssertionPropCallback::EndMerge : " FMT_BB " in -> %s\n\n", block->bbNum, - BitVecOps::ToString(apTraits, block->bbAssertionIn)); + if (VerboseDataflow()) + { + JITDUMP("EndMerge : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n\n"); + } BitVecOps::DataFlowD(apTraits, block->bbAssertionOut, block->bbAssertionGen, block->bbAssertionIn); BitVecOps::DataFlowD(apTraits, mJumpDestOut[block->bbNum], mJumpDestGen[block->bbNum], block->bbAssertionIn); @@ -4790,24 +4856,35 @@ public: bool changed = (!BitVecOps::Equal(apTraits, preMergeOut, block->bbAssertionOut) || !BitVecOps::Equal(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum])); - if (changed) + if (VerboseDataflow()) { - JITDUMP("AssertionPropCallback::Changed : " FMT_BB " before out -> %s; after out -> %s;\n" - "\t\tjumpDest before out -> %s; jumpDest after out -> %s;\n\n", - block->bbNum, BitVecOps::ToString(apTraits, preMergeOut), - BitVecOps::ToString(apTraits, block->bbAssertionOut), - BitVecOps::ToString(apTraits, preMergeJumpDestOut), - BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum])); - } - else - { - JITDUMP("AssertionPropCallback::Unchanged : " FMT_BB " out -> %s; \t\tjumpDest out -> %s\n\n", - block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionOut), - BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum])); + if (changed) + { + JITDUMP("Changed : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("before out -> ", preMergeOut, "; "); + Compiler::optDumpAssertionIndices("after out -> ", block->bbAssertionOut, ";\n "); + Compiler::optDumpAssertionIndices("jumpDest before out -> ", preMergeJumpDestOut, "; "); + Compiler::optDumpAssertionIndices("jumpDest after out -> ", mJumpDestOut[block->bbNum], ";\n\n"); + } + else + { + JITDUMP("Unchanged : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("out -> ", block->bbAssertionOut, "; "); + Compiler::optDumpAssertionIndices("jumpDest out -> ", mJumpDestOut[block->bbNum], "\n\n"); + } } return changed; } + + // Can be enabled to get detailed debug output about dataflow for assertions. + bool VerboseDataflow() + { +#if 0 + return VERBOSE; +#endif + return false; + } }; /***************************************************************************** @@ -4894,16 +4971,28 @@ ASSERT_TP* Compiler::optComputeAssertionGen() #ifdef DEBUG if (verbose) { - printf(FMT_BB " valueGen = %s", block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionGen)); + if (block == fgFirstBB) + { + printf("\n"); + } + + printf(FMT_BB " valueGen = ", block->bbNum); + optPrintAssertionIndices(block->bbAssertionGen); if (block->bbJumpKind == BBJ_COND) { - printf(" => " FMT_BB " valueGen = %s,", block->bbJumpDest->bbNum, - BitVecOps::ToString(apTraits, jumpDestGen[block->bbNum])); + printf(" => " FMT_BB " valueGen = ", block->bbJumpDest->bbNum); + optPrintAssertionIndices(jumpDestGen[block->bbNum]); } printf("\n"); + + if (block == fgLastBB) + { + printf("\n"); + } } #endif } + return jumpDestGen; } @@ -5408,6 +5497,10 @@ void Compiler::optAssertionPropMain() // Modified dataflow algorithm for available expressions. DataFlow flow(this); AssertionPropFlowCallback ap(this, bbJtrueAssertionOut, jumpDestGen); + if (ap.VerboseDataflow()) + { + JITDUMP("AssertionPropFlowCallback:\n\n") + } flow.ForwardAnalysis(ap); for (BasicBlock* const block : Blocks()) @@ -5419,16 +5512,15 @@ void Compiler::optAssertionPropMain() #ifdef DEBUG if (verbose) { - printf("\n"); for (BasicBlock* const block : Blocks()) { - printf("\n" FMT_BB, block->bbNum); - printf(" valueIn = %s", BitVecOps::ToString(apTraits, block->bbAssertionIn)); - printf(" valueOut = %s", BitVecOps::ToString(apTraits, block->bbAssertionOut)); + printf(FMT_BB ":\n", block->bbNum); + optDumpAssertionIndices(" in = ", block->bbAssertionIn, "\n"); + optDumpAssertionIndices(" out = ", block->bbAssertionOut, "\n"); if (block->bbJumpKind == BBJ_COND) { - printf(" => " FMT_BB, block->bbJumpDest->bbNum); - printf(" valueOut= %s", BitVecOps::ToString(apTraits, bbJtrueAssertionOut[block->bbNum])); + printf(" " FMT_BB " = ", block->bbJumpDest->bbNum); + optDumpAssertionIndices(bbJtrueAssertionOut[block->bbNum], "\n"); } } printf("\n"); @@ -5473,9 +5565,11 @@ void Compiler::optAssertionPropMain() // and thus we must morph, set order, re-link for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) { - JITDUMP("Propagating %s assertions for " FMT_BB ", stmt " FMT_STMT ", tree [%06d], tree -> %d\n", - BitVecOps::ToString(apTraits, assertions), block->bbNum, stmt->GetID(), dspTreeID(tree), - tree->GetAssertionInfo().GetAssertionIndex()); + optDumpAssertionIndices("Propagating ", assertions, " "); + JITDUMP("for " FMT_BB ", stmt " FMT_STMT ", tree [%06d]", block->bbNum, stmt->GetID(), dspTreeID(tree)); + JITDUMP(", tree -> "); + JITDUMPEXEC(optPrintAssertionIndex(tree->GetAssertionInfo().GetAssertionIndex())); + JITDUMP("\n"); GenTree* newTree = optAssertionProp(assertions, tree, stmt, block); if (newTree) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 356fe6498d1..639b67bb15c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7491,9 +7491,14 @@ public: #ifdef DEBUG void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex = 0); + void optPrintAssertionIndex(AssertionIndex index); + void optPrintAssertionIndices(ASSERT_TP assertions); void optDebugCheckAssertion(AssertionDsc* assertion); void optDebugCheckAssertions(AssertionIndex AssertionIndex); #endif + static void optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer = nullptr); + static void optDumpAssertionIndices(ASSERT_TP assertions, const char* footer = nullptr); + void optAddCopies(); #endif // ASSERTION_PROP diff --git a/src/coreclr/jit/copyprop.cpp b/src/coreclr/jit/copyprop.cpp index c0d72123e1a..625e2eee5e8 100644 --- a/src/coreclr/jit/copyprop.cpp +++ b/src/coreclr/jit/copyprop.cpp @@ -270,9 +270,9 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc { JITDUMP("VN based copy assertion for "); printTreeID(tree); - printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative)); + printf(" V%02d " FMT_VN " by ", lclNum, tree->GetVN(VNK_Conservative)); printTreeID(op); - printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative)); + printf(" V%02d " FMT_VN ".\n", newLclNum, op->GetVN(VNK_Conservative)); gtDispTree(tree, nullptr, nullptr, true); } #endif diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 2dd39559c28..fb39fda019c 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -487,6 +487,11 @@ const bool dspGCtbls = true; if (JitTls::GetCompiler()->verbose) \ logf(__VA_ARGS__); \ } +#define JITDUMPEXEC(x) \ + { \ + if (JitTls::GetCompiler()->verbose) \ + x; \ + } #define JITLOG(x) \ { \ JitLogEE x; \ @@ -521,6 +526,7 @@ const bool dspGCtbls = true; #define VERBOSE JitTls::GetCompiler()->verbose #else // !DEBUG #define JITDUMP(...) +#define JITDUMPEXEC(x) #define JITLOG(x) #define JITLOG_THIS(t, x) #define DBEXEC(flg, expr) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 5c95b91883c..a37f11cedae 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -865,16 +865,16 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE if (pred->bbFallsThrough() && pred->bbNext == block) { assertions = pred->bbAssertionOut; - JITDUMP("Merge assertions from pred " FMT_BB " edge: %s\n", pred->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, assertions)); + JITDUMP("Merge assertions from pred " FMT_BB " edge: ", pred->bbNum); + Compiler::optDumpAssertionIndices(assertions, "\n"); } else if ((pred->bbJumpKind == BBJ_COND || pred->bbJumpKind == BBJ_ALWAYS) && pred->bbJumpDest == block) { if (m_pCompiler->bbJtrueAssertionOut != nullptr) { assertions = m_pCompiler->bbJtrueAssertionOut[pred->bbNum]; - JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: %s\n", pred->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, assertions)); + JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: ", pred->bbNum); + Compiler::optDumpAssertionIndices(assertions, "\n"); } } } @@ -1012,9 +1012,10 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block, Range range = GetRange(ssaDef->GetBlock(), ssaDef->GetAssignment()->gtGetOp2(), monIncreasing DEBUGARG(indent)); if (!BitVecOps::MayBeUninit(block->bbAssertionIn) && (m_pCompiler->GetAssertionCount() > 0)) { - JITDUMP("Merge assertions from " FMT_BB ":%s for assignment about [%06d]\n", block->bbNum, - BitVecOps::ToString(m_pCompiler->apTraits, block->bbAssertionIn), - Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1())); + JITDUMP("Merge assertions from " FMT_BB ": ", block->bbNum); + Compiler::optDumpAssertionIndices(block->bbAssertionIn, " "); + JITDUMP("for assignment about [%06d]\n", Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1())) + MergeEdgeAssertions(ssaDef->GetAssignment()->gtGetOp1()->AsLclVarCommon(), block->bbAssertionIn, &range); JITDUMP("done merging\n"); } From fdbca22b23727ec640666d385b3c41ffe4b7b914 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Fri, 9 Jul 2021 00:15:20 +0300 Subject: [PATCH 362/926] Don't use GT_ARR_ELEM as a location/value (#54780) * Don't use GT_ARR_ELEM as a location It represents an address. No diffs. * Clarify the purpose of GenTreeArrElem --- src/coreclr/jit/assertionprop.cpp | 7 ------- src/coreclr/jit/gcinfo.cpp | 1 - src/coreclr/jit/gentree.h | 8 ++++---- src/coreclr/jit/morph.cpp | 1 - 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index fb869c89fee..ffad32a61b2 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -1248,13 +1248,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, toType = op2->gtType; goto SUBRANGE_COMMON; - case GT_ARR_ELEM: - - /* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */ - - toType = op2->gtType; - goto SUBRANGE_COMMON; - case GT_LCL_FLD: /* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */ diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp index 1b4d50fe875..cc8e1cd585f 100644 --- a/src/coreclr/jit/gcinfo.cpp +++ b/src/coreclr/jit/gcinfo.cpp @@ -272,7 +272,6 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree case GT_LEA: return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base()); - case GT_ARR_ELEM: /* Definitely in the managed heap */ case GT_CLS_VAR: return WBF_BarrierUnchecked; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b0439564688..c12e46f02bd 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5366,9 +5366,9 @@ struct GenTreeBoundsChk : public GenTree } }; -// gtArrElem -- general array element (GT_ARR_ELEM), for non "SZ_ARRAYS" -// -- multidimensional arrays, or 1-d arrays with non-zero lower bounds. - +// GenTreeArrElem - bounds checked address (byref) of a general array element, +// for multidimensional arrays, or 1-d arrays with non-zero lower bounds. +// struct GenTreeArrElem : public GenTree { GenTree* gtArrObj; @@ -5384,7 +5384,7 @@ struct GenTreeArrElem : public GenTree // This has caused VSW 571394. var_types gtArrElemType; // The array element type - // Requires that "inds" is a pointer to an array of "rank" GenTreePtrs for the indices. + // Requires that "inds" is a pointer to an array of "rank" nodes for the indices. GenTreeArrElem( var_types type, GenTree* arr, unsigned char rank, unsigned char elemSize, var_types elemType, GenTree** inds) : GenTree(GT_ARR_ELEM, type), gtArrObj(arr), gtArrRank(rank), gtArrElemSize(elemSize), gtArrElemType(elemType) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2b40d8548c6..9f734adaa71 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -639,7 +639,6 @@ OPTIMIZECAST: case GT_IND: case GT_CLS_VAR: case GT_LCL_FLD: - case GT_ARR_ELEM: oper->gtType = dstType; // We're changing the type here so we need to update the VN; // in other cases we discard the cast without modifying oper From 9dd6d2e8f626eaf1fb3020f8fae50d701a9d658f Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 8 Jul 2021 16:43:11 -0500 Subject: [PATCH 363/926] Remove pack references (#55359) Remove duplicate pack references --- .../WorkloadManifest.json.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index e6a7ff1ee2a..b4ddda95fe0 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -9,10 +9,7 @@ "packs": [ "Microsoft.NET.Runtime.WebAssembly.Sdk", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", - "Microsoft.NETCore.App.Runtime.AOT.Cross.browser-wasm", - "Microsoft.NET.Runtime.Emscripten.Node", - "Microsoft.NET.Runtime.Emscripten.Python", - "Microsoft.NET.Runtime.Emscripten.Sdk" + "Microsoft.NETCore.App.Runtime.AOT.Cross.browser-wasm" ], "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "linux-x64", "osx-x64", "osx-arm64" ] From b2a5499230f005108b882ac8bd76b3868d2573e4 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 8 Jul 2021 15:38:40 -0700 Subject: [PATCH 364/926] Loop alignment: Handle blocks added in loop as part of split edges of LSRA (#55047) * Handle blocks added in loop as part of split edges of LSRA If there are new blocks added by LSRA and modifies the flow of blocks that are in loop, then make sure that we do not align such loops if they intersect with last aligned loop. * Retain LOOP_ALIGN flag of loops whose start are same * jit format * review feedback --- src/coreclr/jit/emit.cpp | 112 +++++++++++++++++++++++++++++---------- src/coreclr/jit/emit.h | 12 ++--- 2 files changed, 89 insertions(+), 35 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 352d1b2acfc..913951568fd 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -1023,9 +1023,9 @@ void emitter::emitBegFN(bool hasFramePtr emitIGbuffSize = 0; #if FEATURE_LOOP_ALIGN - emitLastAlignedIgNum = 0; - emitLastInnerLoopStartIgNum = 0; - emitLastInnerLoopEndIgNum = 0; + emitLastAlignedIgNum = 0; + emitLastLoopStart = 0; + emitLastLoopEnd = 0; #endif /* Record stack frame info (the temp size is just an estimate) */ @@ -4820,58 +4820,112 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG // if currIG has back-edge to dstIG. // // Notes: -// If the current loop encloses a loop that is already marked as align, then remove -// the alignment flag present on IG before dstIG. +// Despite we align only inner most loop, we might see intersected loops because of control flow +// re-arrangement like adding a split edge in LSRA. +// +// If there is an intersection of current loop with last loop that is already marked as align, +// then *do not align* one of the loop that completely encloses the other one. Or if they both intersect, +// then *do not align* either of them because since the flow is complicated enough that aligning one of them +// will not improve the performance. // void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock) { - insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie; + insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie; + bool alignCurrentLoop = true; + bool alignLastLoop = true; // With (dstIG != nullptr), ensure that only back edges are tracked. // If there is forward jump, dstIG is not yet generated. // // We don't rely on (block->bbJumpDest->bbNum <= block->bbNum) because the basic // block numbering is not guaranteed to be sequential. - if ((dstIG != nullptr) && (dstIG->igNum <= emitCurIG->igNum)) { unsigned currLoopStart = dstIG->igNum; unsigned currLoopEnd = emitCurIG->igNum; // Only mark back-edge if current loop starts after the last inner loop ended. - if (emitLastInnerLoopEndIgNum < currLoopStart) + if (emitLastLoopEnd < currLoopStart) { emitCurIG->igLoopBackEdge = dstIG; JITDUMP("** IG%02u jumps back to IG%02u forming a loop.\n", currLoopEnd, currLoopStart); - emitLastInnerLoopStartIgNum = currLoopStart; - emitLastInnerLoopEndIgNum = currLoopEnd; + emitLastLoopStart = currLoopStart; + emitLastLoopEnd = currLoopEnd; } - // Otherwise, mark the dstIG->prevIG as no alignment needed. - // - // Note: If current loop's back-edge target is same as emitLastInnerLoopStartIgNum, - // retain the alignment flag of dstIG->prevIG so the loop - // (emitLastInnerLoopStartIgNum ~ emitLastInnerLoopEndIgNum) is still aligned. - else if (emitLastInnerLoopStartIgNum != currLoopStart) + else if (currLoopStart == emitLastLoopStart) { - // Find the IG before dstIG... - instrDescAlign* alignInstr = emitAlignList; - while ((alignInstr != nullptr) && (alignInstr->idaIG->igNext != dstIG)) + // Note: If current and last loop starts at same point, + // retain the alignment flag of the smaller loop. + // | + // .---->|<----. + // last | | | + // loop | | | current + // .---->| | loop + // | | + // |-----. + // + } + else if ((currLoopStart < emitLastLoopStart) && (emitLastLoopEnd < currLoopEnd)) + { + // if current loop completely encloses last loop, + // then current loop should not be aligned. + alignCurrentLoop = false; + } + else if ((emitLastLoopStart < currLoopStart) && (currLoopEnd < emitLastLoopEnd)) + { + // if last loop completely encloses current loop, + // then last loop should not be aligned. + alignLastLoop = false; + } + else + { + // The loops intersect and should not align either of the loops + alignLastLoop = false; + alignCurrentLoop = false; + } + + if (!alignLastLoop || !alignCurrentLoop) + { + instrDescAlign* alignInstr = emitAlignList; + bool markedLastLoop = alignLastLoop; + bool markedCurrLoop = alignCurrentLoop; + while ((alignInstr != nullptr)) { + // Find the IG before current loop and clear the IGF_LOOP_ALIGN flag + if (!alignCurrentLoop && (alignInstr->idaIG->igNext == dstIG)) + { + assert(!markedCurrLoop); + alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; + markedCurrLoop = true; + JITDUMP("** Skip alignment for current loop IG%02u ~ IG%02u because it encloses an aligned loop " + "IG%02u ~ IG%02u.\n", + currLoopStart, currLoopEnd, emitLastLoopStart, emitLastLoopEnd); + } + + // Find the IG before the last loop and clear the IGF_LOOP_ALIGN flag + if (!alignLastLoop && (alignInstr->idaIG->igNext != nullptr) && + (alignInstr->idaIG->igNext->igNum == emitLastLoopStart)) + { + assert(!markedLastLoop); + assert(alignInstr->idaIG->isLoopAlign()); + alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; + markedLastLoop = true; + JITDUMP("** Skip alignment for aligned loop IG%02u ~ IG%02u because it encloses the current loop " + "IG%02u ~ IG%02u.\n", + emitLastLoopStart, emitLastLoopEnd, currLoopStart, currLoopEnd); + } + + if (markedLastLoop && markedCurrLoop) + { + break; + } + alignInstr = alignInstr->idaNext; } - // ...and clear the IGF_LOOP_ALIGN flag - if (alignInstr != nullptr) - { - assert(alignInstr->idaIG->igNext == dstIG); - alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN; - } - - JITDUMP( - "** Skip alignment for loop IG%02u ~ IG%02u, because it encloses an aligned loop IG%02u ~ IG%02u.\n", - currLoopStart, currLoopEnd, emitLastInnerLoopStartIgNum, emitLastInnerLoopEndIgNum); + assert(markedLastLoop && markedCurrLoop); } } } diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 666201fc98b..2748acf4637 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1762,12 +1762,12 @@ private: void emitJumpDistBind(); // Bind all the local jumps in method #if FEATURE_LOOP_ALIGN - instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG - unsigned emitLastInnerLoopStartIgNum; // Start IG of last inner loop - unsigned emitLastInnerLoopEndIgNum; // End IG of last inner loop - unsigned emitLastAlignedIgNum; // last IG that has align instruction - instrDescAlign* emitAlignList; // list of local align instructions in method - instrDescAlign* emitAlignLast; // last align instruction in method + instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG + unsigned emitLastLoopStart; // Start IG of last inner loop + unsigned emitLastLoopEnd; // End IG of last inner loop + unsigned emitLastAlignedIgNum; // last IG that has align instruction + instrDescAlign* emitAlignList; // list of local align instructions in method + instrDescAlign* emitAlignLast; // last align instruction in method unsigned getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG_ARG(bool isAlignAdjusted)); // Get the smallest loop size void emitLoopAlignment(); From 03cb9460334e1b145c33771a77462df3d1e99db4 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 8 Jul 2021 23:17:47 -0400 Subject: [PATCH 365/926] Move DiagnosticsHandler.IsGloballyEnabled back to a method (#55351) Intent is to make linker substitutions happy and try to unblock dotnet/sdk#18801 --- .../System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs | 4 ++-- .../System.Net.Http/src/System/Net/Http/HttpClientHandler.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index f3de5028d91..21b161f9fbb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -31,10 +31,10 @@ namespace System.Net.Http { // check if there is a parent Activity (and propagation is not suppressed) // or if someone listens to HttpHandlerDiagnosticListener - return IsGloballyEnabled && (Activity.Current != null || s_diagnosticListener.IsEnabled()); + return IsGloballyEnabled() && (Activity.Current != null || s_diagnosticListener.IsEnabled()); } - internal static bool IsGloballyEnabled => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; + internal static bool IsGloballyEnabled() => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; // SendAsyncCore returns already completed ValueTask for when async: false is passed. // Internally, it calls the synchronous Send method of the base class. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 89cb508d220..2e3289643cf 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -28,7 +28,7 @@ namespace System.Net.Http public HttpClientHandler() { _underlyingHandler = new HttpHandlerType(); - if (DiagnosticsHandler.IsGloballyEnabled) + if (DiagnosticsHandler.IsGloballyEnabled()) { _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); } From ede3733b1cf5902899e56caf748492ed993c98c8 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 8 Jul 2021 23:32:15 -0700 Subject: [PATCH 366/926] Enregister structs on win x64. (#55045) * Enreg structs x64 windows. * try to get zero diffs on other platforms. * fix comment --- src/coreclr/jit/codegencommon.cpp | 14 ++++---------- src/coreclr/jit/codegenlinear.cpp | 16 +++++----------- src/coreclr/jit/compiler.h | 11 ++++++++++- src/coreclr/jit/jitconfigvalues.h | 8 ++++++-- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/lower.cpp | 23 ++++++++++++----------- src/coreclr/jit/lowerxarch.cpp | 6 ++++++ src/coreclr/jit/lsra.cpp | 2 +- 8 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 78926e64fda..99852d7f446 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4355,16 +4355,10 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere #endif // defined(UNIX_AMD64_ABI) noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg); -#ifndef TARGET_64BIT -#ifndef TARGET_ARM - // Right now we think that incoming arguments are not pointer sized. When we eventually - // understand the calling convention, this still won't be true. But maybe we'll have a better - // idea of how to ignore it. - - // On Arm, a long can be passed in register - noway_assert(genTypeSize(genActualType(varDsc->TypeGet())) == TARGET_POINTER_SIZE); -#endif -#endif // TARGET_64BIT +#ifdef TARGET_X86 + // On x86 we don't enregister args that are not pointer sized. + noway_assert(genTypeSize(varDsc->GetActualRegisterType()) == TARGET_POINTER_SIZE); +#endif // TARGET_X86 noway_assert(varDsc->lvIsInReg() && !regArgTab[argNum].circular); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 8bf1f852ffd..eb942332554 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1191,12 +1191,12 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) assert(spillType != TYP_UNDEF); // TODO-Cleanup: The following code could probably be further merged and cleaned up. -#ifdef TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) // Load local variable from its home location. // In most cases the tree type will indicate the correct type to use for the load. // However, if it is NOT a normalizeOnLoad lclVar (i.e. NOT a small int that always gets - // widened when loaded into a register), and its size is not the same as genActualType of - // the type of the lclVar, then we need to change the type of the tree node when loading. + // widened when loaded into a register), and its size is not the same as the actual register type + // of the lclVar, then we need to change the type of the tree node when loading. // This situation happens due to "optimizations" that avoid a cast and // simply retype the node when using long type lclVar as an int. // While loading the int in that case would work for this use of the lclVar, if it is @@ -1210,13 +1210,6 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) assert(!varTypeIsGC(varDsc)); spillType = lclActualType; } -#elif defined(TARGET_ARM64) - var_types targetType = unspillTree->gtType; - if (spillType != genActualType(varDsc->lvType) && !varTypeIsGC(spillType) && !varDsc->lvNormalizeOnLoad()) - { - assert(!varTypeIsGC(varDsc)); - spillType = genActualType(varDsc->lvType); - } #elif defined(TARGET_ARM) // No normalizing for ARM #else @@ -1465,7 +1458,8 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()]; if (varDsc->GetRegNum() != REG_STK) { - inst_Mov(tree->TypeGet(), tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true); + var_types regType = varDsc->GetRegisterType(lcl); + inst_Mov(regType, tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true); } } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 639b67bb15c..f816e6917a6 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1016,11 +1016,20 @@ public: var_types GetActualRegisterType() const; - bool IsEnregisterable() const + bool IsEnregisterableType() const { return GetRegisterType() != TYP_UNDEF; } + bool IsEnregisterableLcl() const + { + if (lvDoNotEnregister) + { + return false; + } + return IsEnregisterableType(); + } + bool CanBeReplacedWithItsField(Compiler* comp) const; #ifdef DEBUG diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index cc10adc0b88..7254e205fbc 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -552,8 +552,12 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave #endif // defined(TARGET_ARM64) #endif // DEBUG -// Allow to enregister locals with struct type. -CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) +CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 1) // Allow to enregister locals with struct type. +#else +CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) // Don't allow to enregister locals with struct type + // yet. +#endif #undef CONFIG_INTEGER #undef CONFIG_STRING diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 603c6747c21..b2ca56b406b 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3493,7 +3493,7 @@ void Compiler::lvaSortByRefCount() { varDsc->lvTracked = 0; } - else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct) + else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct && !compEnregStructLocals()) { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d2a0191648e..9499ac5d817 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3160,7 +3160,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) #endif // !WINDOWS_AMD64_ABI convertToStoreObj = false; } - else if (!varDsc->IsEnregisterable()) + else if (!varDsc->IsEnregisterableType()) { convertToStoreObj = true; } @@ -3418,11 +3418,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) lclNum = fieldLclNum; varDsc = comp->lvaGetDesc(lclNum); } - else if (!varDsc->lvRegStruct && !varTypeIsEnregisterable(varDsc)) - + else if (varDsc->lvPromoted) { - // TODO-1stClassStructs: We can no longer promote or enregister this struct, - // since it is referenced as a whole. + // TODO-1stClassStructs: We can no longer independently promote + // or enregister this struct, since it is referenced as a whole. comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_BlockOp)); } @@ -3434,9 +3433,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) } else { - var_types lclVarType = varDsc->GetRegisterType(lclVar); + const var_types lclVarType = varDsc->GetRegisterType(lclVar); assert(lclVarType != TYP_UNDEF); - lclVar->ChangeType(lclVarType); + const var_types actualType = genActualType(lclVarType); + lclVar->ChangeType(actualType); if (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(lclVarType)) { @@ -4039,12 +4039,12 @@ void Lowering::InsertPInvokeMethodProlog() GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, argList); // some sanity checks on the frame list root vardsc - LclVarDsc* varDsc = &comp->lvaTable[comp->info.compLvFrameListRoot]; + const unsigned lclNum = comp->info.compLvFrameListRoot; + const LclVarDsc* varDsc = comp->lvaGetDesc(lclNum); noway_assert(!varDsc->lvIsParam); noway_assert(varDsc->lvType == TYP_I_IMPL); - GenTree* store = - new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, comp->info.compLvFrameListRoot); + GenTree* store = new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, lclNum); store->AsOp()->gtOp1 = call; store->gtFlags |= GTF_VAR_DEF; @@ -4065,6 +4065,7 @@ void Lowering::InsertPInvokeMethodProlog() GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP); storeSP->gtOp1 = PhysReg(REG_SPBASE); storeSP->gtFlags |= GTF_VAR_DEF; + comp->lvaSetVarDoNotEnregister(comp->lvaInlinedPInvokeFrameVar DEBUGARG(Compiler::DNER_LocalField)); firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeSP)); DISPTREERANGE(firstBlockRange, storeSP); @@ -6534,7 +6535,7 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret) assert(varDsc->lvIsMultiRegRet || (varDsc->lvIsHfa() && varTypeIsValidHfaType(varDsc->lvType))); // Mark var as contained if not enregisterable. - if (!varTypeIsEnregisterable(op1)) + if (!varDsc->IsEnregisterableLcl()) { if (!op1->IsMultiRegLclVar()) { diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 55bfab94f6f..ed889f7f383 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -269,6 +269,12 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // address, not knowing that GT_IND is part of a block op that has containment restrictions. src->AsIndir()->Addr()->ClearContained(); } + else if (src->OperIs(GT_LCL_VAR)) + { + // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register. + const unsigned srcLclNum = src->AsLclVar()->GetLclNum(); + comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } if (blkNode->OperIs(GT_STORE_OBJ)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index ddc54425174..f810ca3d3aa 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -1490,7 +1490,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc) // or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning") // references when using the general GC encoding. unsigned lclNum = (unsigned)(varDsc - compiler->lvaTable); - if (varDsc->lvAddrExposed || !varDsc->IsEnregisterable() || + if (varDsc->lvAddrExposed || !varDsc->IsEnregisterableType() || (!compiler->compEnregStructLocals() && (varDsc->lvType == TYP_STRUCT))) { #ifdef DEBUG From d431d6a15726797965336176efe9ae15aba241fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Fri, 9 Jul 2021 09:41:39 +0200 Subject: [PATCH 367/926] H/3 stress (#55098) * Fixed event source name * Updated docker files, updated stress test to H/3 * SSL stress updated as well * Fixed docker and compilation warnings * H/3 fixes * Fixed nano-server version and nuget reference * Debian stable version revert: bullseye --> buster * Use msquic package in Linux stress container * Bullseye is back, needs 6.0 SDK current preview * Fixed pulling images in docker compose * Disabled problematic error in H/2 --- .../libraries-sdk-aspnetcore.linux.Dockerfile | 2 +- ...ibraries-sdk-aspnetcore.windows.Dockerfile | 2 +- eng/docker/libraries-sdk.linux.Dockerfile | 2 +- eng/docker/libraries-sdk.windows.Dockerfile | 2 +- eng/pipelines/libraries/stress/http.yml | 10 +++++++++- .../HttpStress/ClientOperations.cs | 10 +++++++++- .../tests/StressTests/HttpStress/Dockerfile | 14 +++++++++++++- .../StressTests/HttpStress/HttpStress.csproj | 3 ++- .../tests/StressTests/HttpStress/NuGet.config | 7 +++++++ .../tests/StressTests/HttpStress/Program.cs | 4 ++++ .../StressTests/HttpStress/StressClient.cs | 11 ++++++++--- .../StressTests/HttpStress/StressServer.cs | 19 ++++++++++++++++--- .../StressTests/HttpStress/windows.Dockerfile | 2 +- .../System/Net/Quic/NetEventSource.Quic.cs | 2 +- .../tests/StressTests/SslStress/Dockerfile | 4 ++-- .../StressTests/SslStress/SslStress.csproj | 7 +++---- .../SslStress/run-docker-compose.sh | 7 +++++++ .../StressTests/SslStress/windows.Dockerfile | 4 ++-- 18 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config diff --git a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile index 08adb4359e0..2a232ee4524 100644 --- a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile +++ b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile @@ -1,6 +1,6 @@ # Builds and copies library artifacts into target dotnet sdk image ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754 -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $BUILD_BASE_IMAGE as corefxbuild diff --git a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile index dd306fc4ff1..9fcb11a9a0c 100644 --- a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile +++ b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile @@ -1,6 +1,6 @@ # escape=` # Simple Dockerfile which copies library build artifacts into target dotnet sdk image -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE as target ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost" diff --git a/eng/docker/libraries-sdk.linux.Dockerfile b/eng/docker/libraries-sdk.linux.Dockerfile index 1d704ecbc42..fd4f071da19 100644 --- a/eng/docker/libraries-sdk.linux.Dockerfile +++ b/eng/docker/libraries-sdk.linux.Dockerfile @@ -1,6 +1,6 @@ # Builds and copies library artifacts into target dotnet sdk image ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754 -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $BUILD_BASE_IMAGE as corefxbuild diff --git a/eng/docker/libraries-sdk.windows.Dockerfile b/eng/docker/libraries-sdk.windows.Dockerfile index 564378f4467..c8d993b18d3 100644 --- a/eng/docker/libraries-sdk.windows.Dockerfile +++ b/eng/docker/libraries-sdk.windows.Dockerfile @@ -1,6 +1,6 @@ # escape=` # Simple Dockerfile which copies clr and library build artifacts into target dotnet sdk image -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE as target ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost" diff --git a/eng/pipelines/libraries/stress/http.yml b/eng/pipelines/libraries/stress/http.yml index 08b7611d211..ba3a9f875f9 100644 --- a/eng/pipelines/libraries/stress/http.yml +++ b/eng/pipelines/libraries/stress/http.yml @@ -25,7 +25,7 @@ variables: jobs: - job: linux displayName: Docker Linux - timeoutInMinutes: 150 + timeoutInMinutes: 180 pool: name: NetCorePublic-Pool queue: BuildPool.Ubuntu.1804.Amd64.Open @@ -47,6 +47,14 @@ jobs: name: buildStress displayName: Build HttpStress + - bash: | + cd '$(httpStressProject)' + export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 3.0" + export HTTPSTRESS_SERVER_ARGS="$HTTPSTRESS_SERVER_ARGS -http 3.0" + docker-compose up --abort-on-container-exit --no-color + displayName: Run HttpStress - HTTP 3.0 + condition: and(eq(variables['buildRuntime.succeeded'], 'true'), eq(variables['buildStress.succeeded'], 'true')) + - bash: | cd '$(httpStressProject)' export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 2.0" diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs index 2a0f211808b..5708e48277e 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs @@ -42,7 +42,8 @@ namespace HttpStress public int TaskNum { get; } public bool IsCancellationRequested { get; private set; } - public Version HttpVersion => _config.HttpVersion; + public Version HttpVersion => _client.DefaultRequestVersion; + public HttpVersionPolicy VersionPolicy => _client.DefaultVersionPolicy; public int MaxRequestParameters => _config.MaxParameters; public int MaxRequestUriSize => _config.MaxRequestUriSize; public int MaxRequestHeaderCount => _config.MaxRequestHeaderCount; @@ -54,6 +55,7 @@ namespace HttpStress public async Task SendAsync(HttpRequestMessage request, HttpCompletionOption httpCompletion = HttpCompletionOption.ResponseContentRead, CancellationToken? token = null) { request.Version = HttpVersion; + request.VersionPolicy = VersionPolicy; if (token != null) { @@ -480,6 +482,12 @@ namespace HttpStress private static void ValidateStatusCode(HttpResponseMessage m, HttpStatusCode expectedStatus = HttpStatusCode.OK) { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55261")] + if (m.StatusCode == HttpStatusCode.InternalServerError) + { + throw new Exception("IGNORE"); + } + if (m.StatusCode != expectedStatus) { throw new Exception($"Expected status code {expectedStatus}, got {m.StatusCode}"); diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile index 00b1dd4e35e..a097b5033d0 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Dockerfile @@ -1,4 +1,4 @@ -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $SDK_BASE_IMAGE RUN echo "DOTNET_SDK_VERSION="$DOTNET_SDK_VERSION @@ -7,6 +7,18 @@ RUN echo "DOTNET_VERSION="$DOTNET_VERSION WORKDIR /app COPY . . +# Pulling the msquic Debian package from msquic-ci public pipeline and from a hardcoded build. +# Note that this is a temporary solution until we have properly published Linux packages. +# Also note that in order to update to a newer msquic build, you have update this link. +ARG MSQUIC_PACKAGE=libmsquic_1.5.0_amd64.deb +ARG PACKAGES_DIR=LinuxPackages +RUN wget 'https://dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_apis/build/builds/1223883/artifacts?artifactName=LinuxPackages&api-version=6.0&%24format=zip' -O "$PACKAGES_DIR".zip +RUN apt-get update +RUN apt-get install unzip +RUN unzip $PACKAGES_DIR.zip +RUN dpkg -i $PACKAGES_DIR/$MSQUIC_PACKAGE +RUN rm -rf $PACKAGES_DIR* + ARG CONFIGURATION=Release RUN dotnet build -c $CONFIGURATION diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj index 57cbb0859dc..db333cc634f 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net6.0 preview enable @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config new file mode 100644 index 00000000000..0992c432038 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs index 70d2cdbf149..407c0acd0db 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs @@ -9,10 +9,14 @@ using System.IO; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.Versioning; using System.Threading.Tasks; using System.Net; using HttpStress; +[assembly:SupportedOSPlatform("windows")] +[assembly:SupportedOSPlatform("linux")] + /// /// Simple HttpClient stress app that launches Kestrel in-proc and runs many concurrent requests of varying types against it. /// diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs index 17db8110360..be0dd1c42b0 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs @@ -65,8 +65,8 @@ namespace HttpStress } } - return new HttpClient(CreateHttpHandler()) - { + return new HttpClient(CreateHttpHandler()) + { BaseAddress = _baseAddress, Timeout = _config.DefaultTimeout, DefaultRequestVersion = _config.HttpVersion, @@ -209,6 +209,11 @@ namespace HttpStress { _aggregator.RecordCancellation(opIndex, stopwatch.Elapsed); } + catch (Exception e) when (e.Message == "IGNORE") + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55261")] + // See ClientOperations.ValidateStatusCode + } catch (Exception e) { _aggregator.RecordFailure(e, opIndex, stopwatch.Elapsed, requestContext.IsCancellationRequested, taskNum: taskNum, iteration: i); @@ -283,7 +288,7 @@ namespace HttpStress public void RecordFailure(Exception exn, int operationIndex, TimeSpan elapsed, bool isCancelled, int taskNum, long iteration) { DateTime timestamp = DateTime.Now; - + Interlocked.Increment(ref _totalRequests); Interlocked.Increment(ref _failures[operationIndex]); diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs index 6012965d2a5..32927f7b1ae 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs @@ -45,7 +45,7 @@ namespace HttpStress (string scheme, string hostname, int port) = ParseServerUri(configuration.ServerUri); IWebHostBuilder host = WebHost.CreateDefaultBuilder(); - if (configuration.UseHttpSys) + if (configuration.UseHttpSys && OperatingSystem.IsWindows()) { // Use http.sys. This requires additional manual configuration ahead of time; // see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys?view=aspnetcore-2.2#configure-windows-server. @@ -108,16 +108,29 @@ namespace HttpStress } listenOptions.UseHttps(cert); } + if (configuration.HttpVersion == HttpVersion.Version30) + { + listenOptions.Protocols = HttpProtocols.Http3; + } } else { listenOptions.Protocols = - configuration.HttpVersion == new Version(2,0) ? + configuration.HttpVersion == HttpVersion.Version20 ? HttpProtocols.Http2 : HttpProtocols.Http1 ; } } }); + + if (configuration.HttpVersion == HttpVersion.Version30) + { + host = host.UseQuic(options => + { + options.Alpn = "h3-29"; + options.IdleTimeout = TimeSpan.FromMinutes(1); + }); + } }; LoggerConfiguration loggerConfiguration = new LoggerConfiguration(); @@ -161,7 +174,7 @@ namespace HttpStress private static void MapRoutes(IEndpointRouteBuilder endpoints) { var loggerFactory = endpoints.ServiceProvider.GetService(); - var logger = loggerFactory.CreateLogger(); + var logger = loggerFactory?.CreateLogger(); var head = new[] { "HEAD" }; endpoints.MapGet("/", async context => diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile index 43f1da1deca..17cb0567199 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/windows.Dockerfile @@ -1,5 +1,5 @@ # escape=` -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE # Use powershell as the default shell diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs index d86d7c6fc77..66d66f0e409 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/NetEventSource.Quic.cs @@ -5,7 +5,7 @@ using System.Diagnostics.Tracing; namespace System.Net { - [EventSource(Name = "Microsoft-System-Net-Quic")] + [EventSource(Name = "Private.InternalDiagnostics.System.Net.Quic")] internal sealed partial class NetEventSource : EventSource { } diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile b/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile index dcf3b9ecc9f..8e97f642a73 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/Dockerfile @@ -1,4 +1,4 @@ -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim FROM $SDK_BASE_IMAGE RUN echo "DOTNET_SDK_VERSION="$DOTNET_SDK_VERSION @@ -6,7 +6,7 @@ RUN echo "DOTNET_VERSION="$DOTNET_VERSION WORKDIR /app COPY . . -WORKDIR /app/System.Net.Security/tests/StressTests/SslStress +WORKDIR /app/System.Net.Security/tests/StressTests/SslStress ARG CONFIGURATION=Release RUN dotnet build -c $CONFIGURATION diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj b/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj index 3221dd6cd13..8b0a7a0aea1 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/SslStress.csproj @@ -1,15 +1,14 @@ Exe - net5.0 + net6.0 enable - + - + diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh b/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh index e18b80fca1d..91872f5e8a2 100755 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/run-docker-compose.sh @@ -89,6 +89,13 @@ fi compose_file="$scriptroot/docker-compose.yml" +if ! docker-compose --file "$compose_file" pull client; then + exit $? +fi +if ! docker-compose --file "$compose_file" pull server; then + exit $? +fi + if ! docker-compose --file "$compose_file" build $build_args; then exit $? fi diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile b/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile index 02e23f5c1c9..a1449eb4d54 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/windows.Dockerfile @@ -1,5 +1,5 @@ # escape=` -ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809 +ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809 FROM $SDK_BASE_IMAGE # Use powershell as the default shell @@ -10,7 +10,7 @@ RUN echo "DOTNET_VERSION="$env:DOTNET_VERSION WORKDIR /app COPY . . -WORKDIR /app/System.Net.Security/tests/StressTests/SslStress +WORKDIR /app/System.Net.Security/tests/StressTests/SslStress ARG CONFIGURATION=Release RUN dotnet build -c $env:CONFIGURATION From 46a860877ab143c8ae41d622a12129943e85b235 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Fri, 9 Jul 2021 05:37:24 -0400 Subject: [PATCH 368/926] [mono] Enable icall export for ios device builds. (#55344) Fixes https://github.com/dotnet/runtime/issues/55000 --- src/mono/mono.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/mono.proj b/src/mono/mono.proj index cc9fc41b546..a329c0ac299 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -347,6 +347,7 @@ <_MonoCMakeArgs Include="-DENABLE_VISIBILITY_HIDDEN=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_SIGALTSTACK=0"/> + <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCFLAGS Include="-Werror=partial-availability" /> <_MonoCFLAGS Condition="'$(TargetstvOS)' == 'true'" Include="-fno-gnu-inline-asm" /> <_MonoCFLAGS Include="-fexceptions" /> From e4e942f91e6e2e32988a610c0e7f754df7df56b1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 06:32:54 -0400 Subject: [PATCH 369/926] Add private Task.StateFlags property to help with debugging (#55297) It's a pain when you want to debug something with tasks, you look at its m_stateFlags, and you're met with an integer you have to decode by looking at consts defined in Task. This changes those consts to be an enum and adds a private property that returns the flags as that enum, to help with debugging. --- .../src/ILLink/ILLink.Descriptors.Shared.xml | 1 + .../src/System/Threading/Tasks/Future.cs | 8 +- .../src/System/Threading/Tasks/Task.cs | 230 +++++++++--------- .../Threading/Tasks/TaskContinuation.cs | 4 +- .../System/Threading/Tasks/TaskScheduler.cs | 4 +- 5 files changed, 124 insertions(+), 123 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml index 1989afaeda1..401fe001de2 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml @@ -13,6 +13,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs index f2dd5b2e37e..ac42990b31d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @@ -378,8 +378,8 @@ namespace System.Threading.Tasks // been recorded, and (4) Cancellation has not been requested. // // If the reservation is successful, then set the result and finish completion processing. - if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + if (AtomicStateUpdate((int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { m_result = result; @@ -390,7 +390,7 @@ namespace System.Threading.Tasks // However, that goes through a windy code path, involves many non-inlineable functions // and which can be summarized more concisely with the following snippet from // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult. - Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); + Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.RanToCompletion); ContingentProperties? props = m_contingentProperties; if (props != null) { @@ -425,7 +425,7 @@ namespace System.Threading.Tasks else { m_result = result; - m_stateFlags |= TASK_STATE_RAN_TO_COMPLETION; + m_stateFlags |= (int)TaskStateFlags.RanToCompletion; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 923c4cf71bc..a64dc008429 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -138,36 +138,36 @@ namespace System.Threading.Tasks private Task? ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags + private TaskStateFlags StateFlags => (TaskStateFlags)(m_stateFlags & ~(int)TaskStateFlags.OptionsMask); // Private property used to help with debugging - // State constants for m_stateFlags; - // The bits of m_stateFlags are allocated as follows: - // 0x40000000 - TaskBase state flag - // 0x3FFF0000 - Task state flags - // 0x0000FF00 - internal TaskCreationOptions flags - // 0x000000FF - publicly exposed TaskCreationOptions flags - // - // See TaskCreationOptions for bit values associated with TaskCreationOptions - // - private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111 - internal const int TASK_STATE_STARTED = 0x10000; // bin: 0000 0000 0000 0001 0000 0000 0000 0000 - internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; // bin: 0000 0000 0000 0010 0000 0000 0000 0000 - internal const int TASK_STATE_DISPOSED = 0x40000; // bin: 0000 0000 0000 0100 0000 0000 0000 0000 - internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; // bin: 0000 0000 0000 1000 0000 0000 0000 0000 - internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; // bin: 0000 0000 0001 0000 0000 0000 0000 0000 - internal const int TASK_STATE_FAULTED = 0x200000; // bin: 0000 0000 0010 0000 0000 0000 0000 0000 - internal const int TASK_STATE_CANCELED = 0x400000; // bin: 0000 0000 0100 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; // bin: 0000 0000 1000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; // bin: 0000 0001 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; // bin: 0000 0010 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; // bin: 0000 0100 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; // bin: 0001 0000 0000 0000 0000 0000 0000 0000 - // This could be moved to InternalTaskOptions enum - internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; // bin: 0010 0000 0000 0000 0000 0000 0000 0000 - internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; // bin: 0100 0000 0000 0000 0000 0000 0000 0000 + [Flags] + internal enum TaskStateFlags + { + // State constants for m_stateFlags; + // The bits of m_stateFlags are allocated as follows: + // 0x7FFF0000 - Task state flags + // 0x0000FF00 - internal TaskCreationOptions flags + // 0x000000FF - publicly exposed TaskCreationOptions flags + // See TaskCreationOptions for bit values associated with TaskCreationOptions - // A mask for all of the final states a task may be in. - // SOS DumpAsync command depends on these values. - private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION; + Started = 0x10000, // bin: 0000 0000 0000 0001 0000 0000 0000 0000 + DelegateInvoked = 0x20000, // bin: 0000 0000 0000 0010 0000 0000 0000 0000 + Disposed = 0x40000, // bin: 0000 0000 0000 0100 0000 0000 0000 0000 + ExceptionObservedByParent = 0x80000, // bin: 0000 0000 0000 1000 0000 0000 0000 0000 + CancellationAcknowledged = 0x100000, // bin: 0000 0000 0001 0000 0000 0000 0000 0000 + Faulted = 0x200000, // bin: 0000 0000 0010 0000 0000 0000 0000 0000 + Canceled = 0x400000, // bin: 0000 0000 0100 0000 0000 0000 0000 0000 + WaitingOnChildren = 0x800000, // bin: 0000 0000 1000 0000 0000 0000 0000 0000 + RanToCompletion = 0x1000000, // bin: 0000 0001 0000 0000 0000 0000 0000 0000 + WaitingForActivation = 0x2000000, // bin: 0000 0010 0000 0000 0000 0000 0000 0000 + CompletionReserved = 0x4000000, // bin: 0000 0100 0000 0000 0000 0000 0000 0000 + WaitCompletionNotification = 0x10000000, // bin: 0001 0000 0000 0000 0000 0000 0000 0000 + ExecutionContextIsNull = 0x20000000, // bin: 0010 0000 0000 0000 0000 0000 0000 0000 + TaskScheduledWasFired = 0x40000000, // bin: 0100 0000 0000 0000 0000 0000 0000 0000 + + CompletedMask = Canceled | Faulted | RanToCompletion, // A mask for all of the final states a task may be in. SOS DumpAsync command depends on these values. + OptionsMask = 0xFFFF, // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111 + } // Values for ContingentProperties.m_internalCancellationRequested. private const int CANCELLATION_REQUESTED = 0x1; @@ -294,7 +294,7 @@ namespace System.Threading.Tasks int optionFlags = (int)creationOptions; if (canceled) { - m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags; + m_stateFlags = (int)TaskStateFlags.Canceled | (int)TaskStateFlags.CancellationAcknowledged | optionFlags; m_contingentProperties = new ContingentProperties() // can't have children, so just instantiate directly { m_cancellationToken = ct, @@ -303,14 +303,14 @@ namespace System.Threading.Tasks } else { - m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags; + m_stateFlags = (int)TaskStateFlags.RanToCompletion | optionFlags; } } /// Constructor for use with promise-style tasks that aren't configurable. internal Task() { - m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | (int)InternalTaskOptions.PromiseTask; + m_stateFlags = (int)TaskStateFlags.WaitingForActivation | (int)InternalTaskOptions.PromiseTask; } // Special constructor for use with promise-style tasks. @@ -559,10 +559,10 @@ namespace System.Threading.Tasks // Assign options to m_stateAndOptionsFlag. Debug.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags"); - Debug.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits"); + Debug.Assert((((int)creationOptions) | (int)TaskStateFlags.OptionsMask) == (int)TaskStateFlags.OptionsMask, "TaskConstructorCore: options take too many bits"); int tmpFlags = (int)creationOptions | (int)internalOptions; // one write to the volatile m_stateFlags instead of two when setting the above options m_stateFlags = m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != 0 ? - tmpFlags | TASK_STATE_WAITINGFORACTIVATION : + tmpFlags | (int)TaskStateFlags.WaitingForActivation : tmpFlags; // Now is the time to add the new task to the children list @@ -674,8 +674,8 @@ namespace System.Threading.Tasks // a read of the volatile m_stateFlags field. internal static TaskCreationOptions OptionsMethod(int flags) { - Debug.Assert((OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get"); - return (TaskCreationOptions)(flags & OptionsMask); + Debug.Assert(((int)TaskStateFlags.OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get"); + return (TaskCreationOptions)(flags & (int)TaskStateFlags.OptionsMask); } // Atomically OR-in newBits to m_stateFlags, while making sure that @@ -720,7 +720,7 @@ namespace System.Threading.Tasks } /// - /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit. + /// Sets or clears the TaskStateFlags.WaitCompletionNotification state bit. /// The debugger sets this bit to aid it in "stepping out" of an async method body. /// If enabled is true, this must only be called on a task that has not yet been completed. /// If enabled is false, this may be called on completed tasks. @@ -734,15 +734,15 @@ namespace System.Threading.Tasks if (enabled) { - // Atomically set the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit - bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION, - TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED); + // Atomically set the TaskStateFlags.WaitCompletionNotification bit + bool success = AtomicStateUpdate((int)TaskStateFlags.WaitCompletionNotification, + (int)TaskStateFlags.CompletedMask | (int)TaskStateFlags.CompletionReserved); Debug.Assert(success, "Tried to set enabled on completed Task"); } else { - // Atomically clear the TASK_STATE_WAIT_COMPLETION_NOTIFICATION bit - Interlocked.And(ref m_stateFlags, ~TASK_STATE_WAIT_COMPLETION_NOTIFICATION); + // Atomically clear the TaskStateFlags.WaitCompletionNotification bit + Interlocked.And(ref m_stateFlags, ~(int)TaskStateFlags.WaitCompletionNotification); } } @@ -785,8 +785,8 @@ namespace System.Threading.Tasks internal bool IsWaitNotificationEnabledOrNotRanToCompletion { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (m_stateFlags & (Task.TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task.TASK_STATE_RAN_TO_COMPLETION)) - != Task.TASK_STATE_RAN_TO_COMPLETION; + get => (m_stateFlags & ((int)TaskStateFlags.WaitCompletionNotification | (int)TaskStateFlags.RanToCompletion)) + != (int)TaskStateFlags.RanToCompletion; } /// @@ -812,7 +812,7 @@ namespace System.Threading.Tasks /// Gets whether the task's debugger notification for wait completion bit is set. /// true if the bit is set; false if it's not set. internal bool IsWaitNotificationEnabled => // internal only to enable unit tests; would otherwise be private - (m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION) != 0; + (m_stateFlags & (int)TaskStateFlags.WaitCompletionNotification) != 0; /// Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized. /// All joins with a task should end up calling this if their debugger notification bit is set. @@ -834,14 +834,14 @@ namespace System.Threading.Tasks // Atomically mark a Task as started while making sure that it is not canceled. internal bool MarkStarted() { - return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED); + return AtomicStateUpdate((int)TaskStateFlags.Started, (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Started); } internal void FireTaskScheduledIfNeeded(TaskScheduler ts) { - if ((m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0) + if ((m_stateFlags & (int)TaskStateFlags.TaskScheduledWasFired) == 0) { - m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED; + m_stateFlags |= (int)TaskStateFlags.TaskScheduledWasFired; if (TplEventSource.Log.IsEnabled()) { @@ -1119,7 +1119,7 @@ namespace System.Threading.Tasks } else { - Debug.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Canceled) != 0, "Task.RunSynchronously: expected TaskStateFlags.Canceled to be set"); // Can't call this method on canceled task. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted); } @@ -1269,13 +1269,13 @@ namespace System.Threading.Tasks // execution of this method. int sf = m_stateFlags; - if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted; - else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled; - else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion; - else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete; - else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running; - else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun; - else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation; + if ((sf & (int)TaskStateFlags.Faulted) != 0) rval = TaskStatus.Faulted; + else if ((sf & (int)TaskStateFlags.Canceled) != 0) rval = TaskStatus.Canceled; + else if ((sf & (int)TaskStateFlags.RanToCompletion) != 0) rval = TaskStatus.RanToCompletion; + else if ((sf & (int)TaskStateFlags.WaitingOnChildren) != 0) rval = TaskStatus.WaitingForChildrenToComplete; + else if ((sf & (int)TaskStateFlags.DelegateInvoked) != 0) rval = TaskStatus.Running; + else if ((sf & (int)TaskStateFlags.Started) != 0) rval = TaskStatus.WaitingToRun; + else if ((sf & (int)TaskStateFlags.WaitingForActivation) != 0) rval = TaskStatus.WaitingForActivation; else rval = TaskStatus.Created; return rval; @@ -1295,7 +1295,7 @@ namespace System.Threading.Tasks /// public bool IsCanceled => // Return true if canceled bit is set and faulted bit is not set - (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED; + (m_stateFlags & ((int)TaskStateFlags.Canceled | (int)TaskStateFlags.Faulted)) == (int)TaskStateFlags.Canceled; /// /// Returns true if this task has a cancellation token and it was signaled. @@ -1353,7 +1353,7 @@ namespace System.Threading.Tasks /// /// Gets whether this threw an OperationCanceledException while its CancellationToken was signaled. /// - internal bool IsCancellationAcknowledged => (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; + internal bool IsCancellationAcknowledged => (m_stateFlags & (int)TaskStateFlags.CancellationAcknowledged) != 0; /// /// Gets whether this Task has completed. @@ -1377,10 +1377,10 @@ namespace System.Threading.Tasks // rather than reading the volatile m_stateFlags field. private static bool IsCompletedMethod(int flags) { - return (flags & TASK_STATE_COMPLETED_MASK) != 0; + return (flags & (int)TaskStateFlags.CompletedMask) != 0; } - public bool IsCompletedSuccessfully => (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; + public bool IsCompletedSuccessfully => (m_stateFlags & (int)TaskStateFlags.CompletedMask) == (int)TaskStateFlags.RanToCompletion; /// /// Gets the TaskCreationOptions used @@ -1418,7 +1418,7 @@ namespace System.Threading.Tasks // forces allocation of a true WaitHandle when called. get { - bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0; + bool isDisposed = (m_stateFlags & (int)TaskStateFlags.Disposed) != 0; if (isDisposed) { ThrowHelper.ThrowObjectDisposedException(ExceptionResource.Task_ThrowIfDisposed); @@ -1513,18 +1513,18 @@ namespace System.Threading.Tasks /// public bool IsFaulted => // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted. - (m_stateFlags & TASK_STATE_FAULTED) != 0; + (m_stateFlags & (int)TaskStateFlags.Faulted) != 0; /// /// The captured execution context for the current task to run inside - /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise + /// If the TaskStateFlags.ExecutionContextIsNull flag is set, this means ExecutionContext.Capture returned null, otherwise /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context /// internal ExecutionContext? CapturedContext { get { - if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL) + if ((m_stateFlags & (int)TaskStateFlags.ExecutionContextIsNull) == (int)TaskStateFlags.ExecutionContextIsNull) { return null; } @@ -1538,7 +1538,7 @@ namespace System.Threading.Tasks // There is no need to atomically set this bit because this set() method is only called during construction, and therefore there should be no contending accesses to m_stateFlags if (value == null) { - m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL; + m_stateFlags |= (int)TaskStateFlags.ExecutionContextIsNull; } else if (value != ExecutionContext.Default) // not the default context, then inflate the contingent properties and set it { @@ -1629,10 +1629,10 @@ namespace System.Threading.Tasks // We OR the flags to indicate the object has been disposed. The task // has already completed at this point, and the only conceivable race condition would - // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which + // be with the unsetting of the TaskStateFlags.WaitCompletionNotification flag, which // is extremely unlikely and also benign. (Worst case: we hit a breakpoint // twice instead of once in the debugger. Weird, but not lethal.) - m_stateFlags |= TASK_STATE_DISPOSED; + m_stateFlags |= (int)TaskStateFlags.Disposed; } ///////////// @@ -1641,17 +1641,17 @@ namespace System.Threading.Tasks /// /// Schedules the task for execution. /// - /// If true, TASK_STATE_STARTED bit is turned on in - /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set - /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This + /// If true, TaskStateFlags.Started bit is turned on in + /// an atomic fashion, making sure that TaskStateFlags.Canceled does not get set + /// underneath us. If false, TaskStateFlags.Started bit is OR-ed right in. This /// allows us to streamline things a bit for StartNew(), where competing cancellations /// are not a problem. internal void ScheduleAndStart(bool needsProtection) { Debug.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected"); - Debug.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Started) == 0, "task has already started"); - // Set the TASK_STATE_STARTED bit + // Set the TaskStateFlags.Started bit if (needsProtection) { if (!MarkStarted()) @@ -1662,7 +1662,7 @@ namespace System.Threading.Tasks } else { - m_stateFlags |= TASK_STATE_STARTED; + m_stateFlags |= (int)TaskStateFlags.Started; } if (s_asyncDebuggingEnabled) @@ -1915,7 +1915,7 @@ namespace System.Threading.Tasks /// /// Checks whether this is an attached task, and whether we are being called by the parent task. - /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that. + /// And sets the TaskStateFlags.ExceptionObservedByParent status flag based on that. /// /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children @@ -1932,21 +1932,21 @@ namespace System.Threading.Tasks && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) && Task.InternalCurrent == parent) { - m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT; + m_stateFlags |= (int)TaskStateFlags.ExceptionObservedByParent; } } /// - /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set, + /// Checks whether the TaskStateFlags.ExceptionObservedByParent status flag is set, /// This will only be used by the implicit wait to prevent double throws /// /// - internal bool IsExceptionObservedByParent => (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0; + internal bool IsExceptionObservedByParent => (m_stateFlags & (int)TaskStateFlags.ExceptionObservedByParent) != 0; /// /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task. /// - internal bool IsDelegateInvoked => (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0; + internal bool IsDelegateInvoked => (m_stateFlags & (int)TaskStateFlags.DelegateInvoked) != 0; /// /// Signals completion of this particular task. @@ -2000,11 +2000,11 @@ namespace System.Threading.Tasks // We have to use an atomic update for this and make sure not to overwrite a final state, // because at this very moment the last child's thread may be concurrently completing us. - // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task. - // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag, + // Otherwise we risk overwriting the TaskStateFlags.RanToCompletion, _CANCELED or _FAULTED bit which may have been set by that child task. + // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TaskStateFlags.WaitingOnChildren flag, // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo - AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION); + AtomicStateUpdate((int)TaskStateFlags.WaitingOnChildren, (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled | (int)TaskStateFlags.RanToCompletion); } // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw. @@ -2039,7 +2039,7 @@ namespace System.Threading.Tasks int completionState; if (ExceptionRecorded) { - completionState = TASK_STATE_FAULTED; + completionState = (int)TaskStateFlags.Faulted; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Error); @@ -2048,14 +2048,14 @@ namespace System.Threading.Tasks } else if (IsCancellationRequested && IsCancellationAcknowledged) { - // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation, + // We transition into the TaskStateFlags.Canceled final state if the task's CT was signalled for cancellation, // and the user delegate acknowledged the cancellation request by throwing an OCE, - // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED) + // and the task hasn't otherwise transitioned into faulted state. (TaskStateFlags.Faulted trumps TaskStateFlags.Canceled) // // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state), // then we regard it as a regular exception - completionState = TASK_STATE_CANCELED; + completionState = (int)TaskStateFlags.Canceled; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Canceled); @@ -2064,7 +2064,7 @@ namespace System.Threading.Tasks } else { - completionState = TASK_STATE_RAN_TO_COMPLETION; + completionState = (int)TaskStateFlags.RanToCompletion; if (TplEventSource.Log.IsEnabled()) TplEventSource.Log.TraceOperationEnd(this.Id, AsyncCausalityStatus.Completed); @@ -2124,7 +2124,7 @@ namespace System.Threading.Tasks Task? parent = m_contingentProperties?.m_parent; if (parent != null && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0) - && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0) + && (((TaskCreationOptions)(m_stateFlags & (int)TaskStateFlags.OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0) { parent.ProcessChildCompletion(this); } @@ -2227,9 +2227,9 @@ namespace System.Threading.Tasks // However we don't want this exception to be throw if the task was already canceled, because it's a // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) int previousState = 0; - if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED, - TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK, - ref previousState) && (previousState & TASK_STATE_CANCELED) == 0) + if (!AtomicStateUpdate((int)TaskStateFlags.DelegateInvoked, + (int)TaskStateFlags.DelegateInvoked | (int)TaskStateFlags.CompletedMask, + ref previousState) && (previousState & (int)TaskStateFlags.Canceled) == 0) { // This task has already been invoked. Don't invoke it again. return false; @@ -2258,7 +2258,7 @@ namespace System.Threading.Tasks internal void ExecuteEntryUnsafe(Thread? threadPoolThread) // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent { // Remember that we started running the task delegate. - m_stateFlags |= TASK_STATE_DELEGATE_INVOKED; + m_stateFlags |= (int)TaskStateFlags.DelegateInvoked; if (!IsCancellationRequested & !IsCanceled) { @@ -2274,8 +2274,8 @@ namespace System.Threading.Tasks { if (!IsCanceled) { - int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); - if ((prevState & TASK_STATE_CANCELED) == 0) + int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled); + if ((prevState & (int)TaskStateFlags.Canceled) == 0) { CancellationCleanupLogic(); } @@ -2727,7 +2727,7 @@ namespace System.Threading.Tasks ThrowIfExceptional(true); } - Debug.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state."); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.Faulted) == 0, "Task.Wait() completing when in Faulted state."); return true; } @@ -3060,7 +3060,7 @@ namespace System.Threading.Tasks bool popped = false; // If started, and running in a task context, we can try to pop the chore. - if ((m_stateFlags & TASK_STATE_STARTED) != 0) + if ((m_stateFlags & (int)TaskStateFlags.Started) != 0) { TaskScheduler? ts = m_taskScheduler; try @@ -3081,24 +3081,24 @@ namespace System.Threading.Tasks // Determine whether we need to clean up // This will be the case - // 1) if we were able to pop, and we are able to update task state to TASK_STATE_CANCELED + // 1) if we were able to pop, and we are able to update task state to TaskStateFlags.Canceled // 2) if the task seems to be yet unstarted, and we can transition to - // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or + // TaskStateFlags.Canceled before anyone else can transition into _STARTED or _CANCELED or // _RAN_TO_COMPLETION or _FAULTED - // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style + // Note that we do not check for TaskStateFlags.CompletionReserved. That only applies to promise-style // tasks, and a promise-style task should not enter into this codepath. bool mustCleanup = false; if (popped) { - // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where + // Include TaskStateFlags.DelegateInvoked in "illegal" bits to protect against the situation where // TS.TryDequeue() returns true but the task is still left on the queue. - mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED); + mustCleanup = AtomicStateUpdate((int)TaskStateFlags.Canceled, (int)TaskStateFlags.Canceled | (int)TaskStateFlags.DelegateInvoked); } - else if ((m_stateFlags & TASK_STATE_STARTED) == 0) + else if ((m_stateFlags & (int)TaskStateFlags.Started) == 0) { - mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, - TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION | - TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED); + mustCleanup = AtomicStateUpdate((int)TaskStateFlags.Canceled, + (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Started | (int)TaskStateFlags.RanToCompletion | + (int)TaskStateFlags.Faulted | (int)TaskStateFlags.DelegateInvoked); } // do the cleanup (i.e. set completion event and finish continuations) @@ -3125,12 +3125,12 @@ namespace System.Threading.Tasks // - it was canceled, which won't have happened without a token // - it was run as a continuation, which won't have happened because this method is only invoked once // As a result, we can take an optimized path that avoids inflating contingent properties. - const int IllegalFlags = TASK_STATE_STARTED | TASK_STATE_COMPLETED_MASK | TASK_STATE_DELEGATE_INVOKED; + const int IllegalFlags = (int)TaskStateFlags.Started | (int)TaskStateFlags.CompletedMask | (int)TaskStateFlags.DelegateInvoked; Debug.Assert((m_stateFlags & IllegalFlags) == 0, "The continuation was in an invalid state."); - Debug.Assert((m_stateFlags & TASK_STATE_WAITINGFORACTIVATION) != 0, "Expected continuation to be waiting for activation"); + Debug.Assert((m_stateFlags & (int)TaskStateFlags.WaitingForActivation) != 0, "Expected continuation to be waiting for activation"); Debug.Assert(m_contingentProperties is null || m_contingentProperties.m_cancellationToken == default); - m_stateFlags |= TASK_STATE_CANCELED; // no synchronization necessary, per above comment + m_stateFlags |= (int)TaskStateFlags.Canceled; // no synchronization necessary, per above comment CancellationCleanupLogic(); } @@ -3181,14 +3181,14 @@ namespace System.Threading.Tasks // And this method should be called at most once per task. internal void CancellationCleanupLogic() { - Debug.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved."); + Debug.Assert((m_stateFlags & ((int)TaskStateFlags.Canceled | (int)TaskStateFlags.CompletionReserved)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved."); // We'd like to be able to: // Debug.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set."); // However, there is a small window for a race condition. If someone calls Wait() between InternalCancel() and // here, that will set m_completionEvent, leading to a meaningless/harmless assertion. // This may have been set already, but we need to make sure. - Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED); + Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled); // Fire completion event if it has been lazily initialized ContingentProperties? cp = Volatile.Read(ref m_contingentProperties); @@ -3216,7 +3216,7 @@ namespace System.Threading.Tasks Debug.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task"); Debug.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled"); - m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED; + m_stateFlags |= (int)TaskStateFlags.CancellationAcknowledged; } /// Completes a promise task as RanToCompletion. @@ -3227,8 +3227,8 @@ namespace System.Threading.Tasks Debug.Assert(m_action == null, "Task.TrySetResult(): non-null m_action"); if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { ContingentProperties? props = m_contingentProperties; if (props != null) @@ -3275,8 +3275,8 @@ namespace System.Threading.Tasks // anyway. Some downstream logic may depend upon an inflated m_contingentProperties. EnsureContingentPropertiesInitialized(); if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + (int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.RanToCompletion | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.Canceled)) { AddException(exceptionObject); // handles singleton exception or exception collection Finish(false); @@ -3315,8 +3315,8 @@ namespace System.Threading.Tasks // // If the reservation is successful, then record the cancellation and finish completion processing. if (AtomicStateUpdate( - TASK_STATE_COMPLETION_RESERVED, - TASK_STATE_COMPLETION_RESERVED | TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION)) + (int)TaskStateFlags.CompletionReserved, + (int)TaskStateFlags.CompletionReserved | (int)TaskStateFlags.Canceled | (int)TaskStateFlags.Faulted | (int)TaskStateFlags.RanToCompletion)) { RecordInternalCancellationRequest(tokenToRecord, cancellationException); CancellationCleanupLogic(); // perform cancellation cleanup actions diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs index 18feb9a2266..05576dae666 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs @@ -216,7 +216,7 @@ namespace System.Threading.Tasks Debug.Assert(task != null); Debug.Assert(task.m_taskScheduler != null); - // Set the TASK_STATE_STARTED flag. This only needs to be done + // Set the TaskStateFlags.Started flag. This only needs to be done // if the task may be canceled or if someone else has a reference to it // that may try to execute it. if (needsProtection) @@ -226,7 +226,7 @@ namespace System.Threading.Tasks } else { - task.m_stateFlags |= Task.TASK_STATE_STARTED; + task.m_stateFlags |= (int)Task.TaskStateFlags.Started; } // Try to inline it but queue if we can't diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs index 2d901838707..e8d3ef13357 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs @@ -166,7 +166,7 @@ namespace System.Threading.Tasks { // Do not inline unstarted tasks (i.e., task.ExecutingTaskScheduler == null). // Do not inline TaskCompletionSource-style (a.k.a. "promise") tasks. - // No need to attempt inlining if the task body was already run (i.e. either TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bits set) + // No need to attempt inlining if the task body was already run (i.e. either TaskStateFlags.DelegateInvoked or TaskStateFlags.Canceled bits set) TaskScheduler? ets = task.ExecutingTaskScheduler; // Delegate cross-scheduler inlining requests to target scheduler @@ -189,7 +189,7 @@ namespace System.Threading.Tasks bool inlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued); - // If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set + // If the custom scheduler returned true, we should either have the TaskStateFlags.DelegateInvoked or TaskStateFlags.Canceled bit set // Otherwise the scheduler is buggy if (inlined && !(task.IsDelegateInvoked || task.IsCanceled)) { From 5af27a545d9246a8472bc087720f91bf0aa0eadc Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Fri, 9 Jul 2021 12:41:14 +0200 Subject: [PATCH 370/926] Remove few more ILLink warnings in System.Data.Common (#55335) --- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------------ .../src/System/Data/ColumnTypeConverter.cs | 3 +++ 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 2eda63ecd06..9825521322c 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,18 +1,6 @@  - - ILLink - IL2026 - member - M:System.Data.ColumnTypeConverter.ConvertTo(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type) - - - ILLink - IL2060 - member - M:System.Data.DataRowExtensions.UnboxT`1.Create - ILLink IL2026 @@ -61,12 +49,6 @@ member M:System.Data.DataTable.WriteXmlCore(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataView.SetRowFilter(System.String) - ILLink IL2026 diff --git a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs index 015e87d0897..f259f711384 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ColumnTypeConverter.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Data.SqlTypes; using System.Reflection; @@ -65,6 +66,8 @@ namespace System.Data /// /// Converts the given value object to the specified destination type. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "InstanceDescriptor calls GetType(string) on AssemblyQualifiedName of instance of type we already have in here.")] public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == null) From f1bf5eee390fa118441cae14a157773f1e2d751d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 06:46:29 -0400 Subject: [PATCH 371/926] Add Windows support to PosixSignal (#55333) * Add Windows support to PosixSignal * Address PR feedback --- .../Interop.SetConsoleCtrlHandler.Delegate.cs | 17 +++ .../Kernel32/Interop.SetConsoleCtrlHandler.cs | 9 +- .../src/Microsoft.Win32.SystemEvents.csproj | 4 +- .../System.Console/src/System.Console.csproj | 2 - .../System.Console/src/System/Console.cs | 43 +++--- .../src/System/ConsolePal.Android.cs | 9 -- .../src/System/ConsolePal.Unix.cs | 31 ----- .../src/System/ConsolePal.WebAssembly.cs | 9 -- .../src/System/ConsolePal.Windows.cs | 47 ------- .../src/System/ConsolePal.iOS.cs | 9 -- .../ref/System.Runtime.InteropServices.cs | 11 ++ .../src/Resources/Strings.resx | 5 +- .../src/System.Runtime.InteropServices.csproj | 24 ++-- .../Runtime/InteropServices/PosixSignal.cs | 28 ++++ .../InteropServices/PosixSignalContext.cs | 20 +-- .../PosixSignalRegistration.Browser.cs | 18 --- .../PosixSignalRegistration.Unix.cs | 58 +++----- .../PosixSignalRegistration.Unsupported.cs | 22 +++ .../PosixSignalRegistration.Windows.cs | 130 +++++++++++++++++- .../PosixSignalRegistration.cs | 38 +++++ ...ystem.Runtime.InteropServices.Tests.csproj | 12 +- .../PosixSignalContextTests.cs | 32 ++++- .../PosixSignalRegistrationTests.Browser.cs | 29 ++++ .../PosixSignalRegistrationTests.Unix.cs | 80 ++++------- .../PosixSignalRegistrationTests.Windows.cs | 30 ++++ .../PosixSignalRegistrationTests.cs | 74 ++++++++++ 26 files changed, 503 insertions(+), 288 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs new file mode 100644 index 00000000000..159c26438c2 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + internal delegate bool ConsoleCtrlHandlerRoutine(int controlType); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs index c6ee59a77a2..112d5b4d5a8 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Win32.SafeHandles; -using System; using System.Runtime.InteropServices; internal static partial class Interop @@ -11,10 +9,11 @@ internal static partial class Interop { internal const int CTRL_C_EVENT = 0; internal const int CTRL_BREAK_EVENT = 1; - - internal delegate bool ConsoleCtrlHandlerRoutine(int controlType); + internal const int CTRL_CLOSE_EVENT = 2; + internal const int CTRL_LOGOFF_EVENT = 5; + internal const int CTRL_SHUTDOWN_EVENT = 6; [DllImport(Libraries.Kernel32, SetLastError = true)] - internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove); + internal static extern unsafe bool SetConsoleCtrlHandler(delegate* unmanaged HandlerRoutine, bool Add); } } diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj index 2929b90c61a..97cc92a8ef3 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft.Win32.SystemEvents.csproj @@ -82,8 +82,8 @@ Link="Common\Interop\Windows\Kernel32\Interop.LoadLibrary.cs" /> - + - handler = HandlePosixSignal; + s_sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, handler); + s_sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, handler); } } } @@ -602,10 +607,13 @@ namespace System lock (s_syncObject) { s_cancelCallbacks -= value; - if (s_registrar != null && s_cancelCallbacks == null) + + // If there are no more callbacks, unregister registered posix signal handlers. + if (s_cancelCallbacks == null) { - s_registrar.Unregister(); - s_registrar = null; + s_sigIntRegistration?.Dispose(); + s_sigQuitRegistration?.Dispose(); + s_sigIntRegistration = s_sigQuitRegistration = null; } } } @@ -960,18 +968,17 @@ namespace System Out.Write(value); } - internal static bool HandleBreakEvent(ConsoleSpecialKey controlKey, bool cancel = false) + private static void HandlePosixSignal(PosixSignalContext ctx) { - ConsoleCancelEventHandler? handler = s_cancelCallbacks; - if (handler == null) - { - return false; - } + Debug.Assert(ctx.Signal == PosixSignal.SIGINT || ctx.Signal == PosixSignal.SIGQUIT); - var args = new ConsoleCancelEventArgs(controlKey); - args.Cancel = cancel; - handler(null, args); - return args.Cancel; + if (s_cancelCallbacks is ConsoleCancelEventHandler handler) + { + var args = new ConsoleCancelEventArgs(ctx.Signal == PosixSignal.SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak); + args.Cancel = ctx.Cancel; + handler(null, args); + ctx.Cancel = args.Cancel; + } } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Android.cs b/src/libraries/System.Console/src/System/ConsolePal.Android.cs index 910a33ad824..3777952799f 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Android.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Android.cs @@ -152,14 +152,5 @@ namespace System public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index c438413c801..eacf15a203e 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -1442,37 +1442,6 @@ namespace System } } - internal sealed class ControlCHandlerRegistrar - { - private PosixSignalRegistration? _sigIntRegistration; - private PosixSignalRegistration? _sigQuitRegistration; - - internal unsafe void Register() - { - Debug.Assert(_sigIntRegistration is null); - - _sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, HandlePosixSignal); - _sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandlePosixSignal); - } - - internal void Unregister() - { - Debug.Assert(_sigIntRegistration is not null); - - _sigIntRegistration?.Dispose(); - _sigQuitRegistration?.Dispose(); - } - - private static void HandlePosixSignal(PosixSignalContext ctx) - { - Debug.Assert(ctx.Signal == PosixSignal.SIGINT || ctx.Signal == PosixSignal.SIGQUIT); - - ConsoleSpecialKey controlKey = ctx.Signal == PosixSignal.SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; - bool cancel = Console.HandleBreakEvent(controlKey, ctx.Cancel); - ctx.Cancel = cancel; - } - } - private sealed class ReadOnlyMemoryContentComparer : IEqualityComparer> { public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) => diff --git a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs index 405326185eb..ae4dc31078d 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs @@ -224,14 +224,5 @@ namespace System public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs index 95cebc12c48..a4f1241e069 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @@ -1224,52 +1224,5 @@ namespace System return errorCode; } } - - internal sealed class ControlCHandlerRegistrar - { - private bool _handlerRegistered; - private readonly Interop.Kernel32.ConsoleCtrlHandlerRoutine _handler; - - internal ControlCHandlerRegistrar() - { - _handler = new Interop.Kernel32.ConsoleCtrlHandlerRoutine(BreakEvent); - } - - internal void Register() - { - Debug.Assert(!_handlerRegistered); - - bool r = Interop.Kernel32.SetConsoleCtrlHandler(_handler, true); - if (!r) - { - throw Win32Marshal.GetExceptionForLastWin32Error(); - } - - _handlerRegistered = true; - } - - internal void Unregister() - { - Debug.Assert(_handlerRegistered); - - bool r = Interop.Kernel32.SetConsoleCtrlHandler(_handler, false); - if (!r) - { - throw Win32Marshal.GetExceptionForLastWin32Error(); - } - _handlerRegistered = false; - } - - private static bool BreakEvent(int controlType) - { - if (controlType != Interop.Kernel32.CTRL_C_EVENT && - controlType != Interop.Kernel32.CTRL_BREAK_EVENT) - { - return false; - } - - return Console.HandleBreakEvent(controlType == Interop.Kernel32.CTRL_C_EVENT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak); - } - } } } diff --git a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs index fa1f2d120a8..5279df26401 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs @@ -155,14 +155,5 @@ namespace System public static void SetWindowPosition(int left, int top) => throw new PlatformNotSupportedException(); public static void SetWindowSize(int width, int height) => throw new PlatformNotSupportedException(); - - internal sealed class ControlCHandlerRegistrar - { - internal ControlCHandlerRegistrar() => throw new PlatformNotSupportedException(); - - internal void Register() => throw new PlatformNotSupportedException(); - - internal void Unregister() => throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 8be17302389..374a3f14c55 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -798,11 +798,17 @@ namespace System.Runtime.InteropServices } public enum PosixSignal { + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTSTP = -10, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTTOU = -9, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGTTIN = -8, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGWINCH = -7, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGCONT = -6, + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")] SIGCHLD = -5, SIGTERM = -4, SIGQUIT = -3, @@ -818,6 +824,11 @@ namespace System.Runtime.InteropServices public sealed partial class PosixSignalRegistration : System.IDisposable { internal PosixSignalRegistration() { } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Runtime.InteropServices.PosixSignalRegistration Create(System.Runtime.InteropServices.PosixSignal signal, System.Action handler) { throw null; } public void Dispose() { } ~PosixSignalRegistration() { } diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index 7834b952cc9..d7644789f28 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -111,4 +111,7 @@ Specified file length was too large for the file system. - \ No newline at end of file + + Cannot create '{0}' because a file or directory with the same name already exists. + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index c6ba2fbe496..7137155589b 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -35,6 +35,7 @@ + @@ -52,22 +53,23 @@ - - - - - + + + + + + + + + + + - + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs index f1cb32a362b..e1f278fbd6f 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs @@ -1,19 +1,47 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.Versioning; + namespace System.Runtime.InteropServices { + /// Specifies a POSIX signal number. public enum PosixSignal { + /// Hangup SIGHUP = -1, + + /// Interrupt SIGINT = -2, + + /// Quit SIGQUIT = -3, + + /// Termination SIGTERM = -4, + + /// Child stopped + [UnsupportedOSPlatform("windows")] SIGCHLD = -5, + + /// Continue if stopped + [UnsupportedOSPlatform("windows")] SIGCONT = -6, + + /// Window resized + [UnsupportedOSPlatform("windows")] SIGWINCH = -7, + + /// Terminal input for background process + [UnsupportedOSPlatform("windows")] SIGTTIN = -8, + + /// Terminal output for background process + [UnsupportedOSPlatform("windows")] SIGTTOU = -9, + + /// Stop typed at terminal + [UnsupportedOSPlatform("windows")] SIGTSTP = -10 } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs index 19ba4f96868..cf73dd62dc7 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs @@ -11,30 +11,16 @@ namespace System.Runtime.InteropServices /// /// Initializes a new instance of the class. /// - public PosixSignalContext(PosixSignal signal) - { - Signal = signal; - } + public PosixSignalContext(PosixSignal signal) => Signal = signal; /// /// Gets the signal that occurred. /// - public PosixSignal Signal - { - get; - internal set; - } + public PosixSignal Signal { get; internal set; } /// /// Gets or sets a value that indicates whether to cancel the default handling of the signal. The default is . /// - public bool Cancel - { - get; - set; - } - - internal PosixSignalContext() - { } + public bool Cancel { get; set; } } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs deleted file mode 100644 index 7c88a92cae9..00000000000 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Browser.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace System.Runtime.InteropServices -{ - public sealed class PosixSignalRegistration : IDisposable - { - private PosixSignalRegistration() { } - - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) - => throw new PlatformNotSupportedException(); - - public void Dispose() - => throw new PlatformNotSupportedException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs index 9a3144f5e81..f01b2de0130 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs @@ -1,17 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.ComponentModel; using System.Threading; namespace System.Runtime.InteropServices { - /// - /// Handles a . - /// - public sealed class PosixSignalRegistration : IDisposable + public sealed partial class PosixSignalRegistration { private static volatile bool s_initialized; private static readonly Dictionary?>> s_registrations = new(); @@ -29,47 +24,24 @@ namespace System.Runtime.InteropServices _handler = handler; } - /// - /// Registers a that is invoked when the occurs. - /// - /// The signal to register for. - /// The handler that gets invoked. - /// A instance that can be disposed to unregister. - /// is . - /// is out the range of expected values for the platform. - /// An error occurred while setting up the signal handling or while installing the handler for the specified signal. - /// - /// Raw values can be provided for by casting them to . - /// - /// Default handling of the signal can be canceled through . - /// For SIGTERM, SIGINT, and SIGQUIT process termination can be canceled. - /// For SIGCHLD, and SIGCONT terminal configuration can be canceled. - /// - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } + int signo = Interop.Sys.GetPlatformSignalNumber(signal); if (signo == 0) { - throw new ArgumentOutOfRangeException(nameof(signal)); + throw new PlatformNotSupportedException(); } + PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler); registration.Register(); return registration; } - /// - /// Unregister the handler. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - private unsafe void Register() { if (!s_initialized) @@ -84,6 +56,7 @@ namespace System.Runtime.InteropServices Interop.Sys.SetPosixSignalHandler(&OnPosixSignal); s_initialized = true; } + lock (s_registrations) { if (!s_registrations.TryGetValue(_signo, out List?>? signalRegistrations)) @@ -104,6 +77,7 @@ namespace System.Runtime.InteropServices signalRegistrations.Add(new WeakReference(this)); } + _registered = true; } @@ -116,6 +90,7 @@ namespace System.Runtime.InteropServices _handler(context); return true; } + return false; } } @@ -133,8 +108,9 @@ namespace System.Runtime.InteropServices // For terminate/interrupt signals we use a dedicated Thread // in case the ThreadPool is saturated. bool useDedicatedThread = signal == PosixSignal.SIGINT || - signal == PosixSignal.SIGQUIT || - signal == PosixSignal.SIGTERM; + signal == PosixSignal.SIGQUIT || + signal == PosixSignal.SIGTERM; + if (useDedicatedThread) { Thread handlerThread = new Thread(HandleSignal) @@ -148,8 +124,10 @@ namespace System.Runtime.InteropServices { ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations)); } + return 1; } + return 0; } @@ -164,6 +142,7 @@ namespace System.Runtime.InteropServices var registrations = new PosixSignalRegistration?[signalRegistrations.Count]; bool hasRegistrations = false; bool pruneWeakReferences = false; + for (int i = 0; i < signalRegistrations.Count; i++) { if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) @@ -210,7 +189,7 @@ namespace System.Runtime.InteropServices bool handlersCalled = false; if (state.registrations != null) { - PosixSignalContext ctx = new(); + PosixSignalContext ctx = new(0); foreach (PosixSignalRegistration? registration in state.registrations) { if (registration != null) @@ -241,10 +220,7 @@ namespace System.Runtime.InteropServices } while (true); } - ~PosixSignalRegistration() - => Dispose(false); - - private void Dispose(bool disposing) + public partial void Dispose() { if (_registered) { @@ -256,7 +232,7 @@ namespace System.Runtime.InteropServices { if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) { - if (object.ReferenceEquals(this, registration)) + if (ReferenceEquals(this, registration)) { signalRegistrations.RemoveAt(i); break; diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs new file mode 100644 index 00000000000..4bfd2eae0be --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public sealed partial class PosixSignalRegistration + { + private PosixSignalRegistration() { } + + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + throw new PlatformNotSupportedException(); + } + + public partial void Dispose() { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs index 7c88a92cae9..d3e5405053e 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs @@ -1,18 +1,134 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; +using System.Collections.Generic; +using System.IO; namespace System.Runtime.InteropServices { - public sealed class PosixSignalRegistration : IDisposable + public sealed unsafe partial class PosixSignalRegistration { - private PosixSignalRegistration() { } + private static readonly HashSet s_handlers = new(); - public static PosixSignalRegistration Create(PosixSignal signal, Action handler) - => throw new PlatformNotSupportedException(); + private Token? _token; - public void Dispose() - => throw new PlatformNotSupportedException(); + private PosixSignalRegistration(Token token) => _token = token; + + private static object SyncObj => s_handlers; + + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + lock (SyncObj) + { + switch (signal) + { + case PosixSignal.SIGINT: + case PosixSignal.SIGQUIT: + case PosixSignal.SIGTERM: + case PosixSignal.SIGHUP: + break; + + default: + throw new PlatformNotSupportedException(); + } + + if (s_handlers.Count == 0 && + !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: true)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + + var token = new Token(signal, handler); + s_handlers.Add(token); + return new PosixSignalRegistration(token); + } + } + + public partial void Dispose() + { + lock (SyncObj) + { + if (_token is Token token) + { + _token = null; + + s_handlers.Remove(token); + if (s_handlers.Count == 0 && + !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: false)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + } + } + + [UnmanagedCallersOnly] + private static Interop.BOOL HandlerRoutine(int dwCtrlType) + { + PosixSignal signal; + switch (dwCtrlType) + { + case Interop.Kernel32.CTRL_C_EVENT: + signal = PosixSignal.SIGINT; + break; + + case Interop.Kernel32.CTRL_BREAK_EVENT: + signal = PosixSignal.SIGQUIT; + break; + + case Interop.Kernel32.CTRL_SHUTDOWN_EVENT: + signal = PosixSignal.SIGTERM; + break; + + case Interop.Kernel32.CTRL_CLOSE_EVENT: + signal = PosixSignal.SIGHUP; + break; + + default: + return Interop.BOOL.FALSE; + } + + List? tokens = null; + lock (SyncObj) + { + foreach (Token token in s_handlers) + { + if (token.Signal == signal) + { + (tokens ??= new()).Add(token); + } + } + } + + if (tokens is null) + { + return Interop.BOOL.FALSE; + } + + var context = new PosixSignalContext(signal); + foreach (Token handler in tokens) + { + handler.Handler(context); + } + + return context.Cancel ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + } + + private sealed class Token + { + public Token(PosixSignal signal, Action handler) + { + Signal = signal; + Handler = handler; + } + + public PosixSignal Signal { get; } + public Action Handler { get; } + } } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs new file mode 100644 index 00000000000..fb2c675acb8 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices +{ + /// Handles a . + public sealed partial class PosixSignalRegistration : IDisposable + { + /// Registers a that is invoked when the occurs. + /// The signal to register for. + /// The handler that gets invoked. + /// A instance that can be disposed to unregister the handler. + /// is . + /// is not supported by the platform. + /// An error occurred while setting up the signal handling or while installing the handler for the specified signal. + /// + /// Raw values can be provided for on Unix by casting them to . + /// Default handling of the signal can be canceled through . + /// and can be canceled on both + /// Windows and on Unix platforms; can only be canceled on Unix. + /// On Unix, terminal configuration can be canceled for and . + /// + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] + public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler); + + /// Unregister the handler. + public partial void Dispose(); + + ~PosixSignalRegistration() => Dispose(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 4bcb75b0b81..33204d0ee7c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -151,6 +151,7 @@ + @@ -184,9 +185,14 @@ - + + + + - + + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs index 3d8ca635ac2..14dc8ff4010 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalContextTests.cs @@ -9,14 +9,32 @@ namespace System.Tests public class PosixSignalContextTests { [Theory] - [InlineData(0)] - [InlineData(3)] - [InlineData(-1000)] - [InlineData(1000)] - public void Constructor(int value) + [InlineData(PosixSignal.SIGINT)] + [InlineData((PosixSignal)0)] + [InlineData((PosixSignal)1000)] + [InlineData((PosixSignal)(-1000))] + public void Constructor(PosixSignal value) { - var ctx = new PosixSignalContext((PosixSignal)value); - Assert.Equal(value, (int)ctx.Signal); + var ctx = new PosixSignalContext(value); + Assert.Equal(value, ctx.Signal); + Assert.False(ctx.Cancel); + } + + [Fact] + public void Cancel_Roundtrips() + { + var ctx = new PosixSignalContext(PosixSignal.SIGINT); + Assert.Equal(PosixSignal.SIGINT, ctx.Signal); + Assert.False(ctx.Cancel); + + for (int i = 0; i < 2; i++) + { + ctx.Cancel = true; + Assert.True(ctx.Cancel); + + ctx.Cancel = false; + Assert.False(ctx.Cancel); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs new file mode 100644 index 00000000000..2202ed25744 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + public static IEnumerable UninstallableSignals() => Enumerable.Empty(); + + public static IEnumerable SupportedSignals() => Enumerable.Empty(); + + public static IEnumerable UnsupportedSignals() + { + foreach (PosixSignal signal in Enum.GetValues()) + { + yield return new object[] { signal }; + } + + yield return new object[] { 0 }; + yield return new object[] { 3 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs index b006af50150..1716a3ba24f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -2,54 +2,42 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; -using System.IO; using System.Threading; -using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Microsoft.DotNet.RemoteExecutor; +using System.Collections.Generic; namespace System.Tests { - public class PosixSignalRegistrationTests + public partial class PosixSignalRegistrationTests { - private static TimeSpan Timeout => TimeSpan.FromSeconds(30); - - [Fact] - public void HandlerNullThrows() + public static IEnumerable UninstallableSignals() { - Assert.Throws(() => PosixSignalRegistration.Create(PosixSignal.SIGCONT, null)); + yield return new object[] { (PosixSignal)9 }; } - [Theory] - [InlineData(0)] - [InlineData(-1000)] - [InlineData(1000)] - public void InvalidSignalValueThrows(int value) + public static IEnumerable SupportedSignals() { - Assert.Throws(() => PosixSignalRegistration.Create((PosixSignal)value, ctx => { })); + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; } - [Theory] - [InlineData((PosixSignal)9)] // SIGKILL - public void UninstallableSignalsThrow(PosixSignal signal) + public static IEnumerable UnsupportedSignals() { - Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + yield return new object[] { 0 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; } - [Theory] - [MemberData(nameof(PosixSignalValues))] - public void CanRegisterForKnownValues(PosixSignal signal) - { - using var _ = PosixSignalRegistration.Create(signal, ctx => { }); - } - - [Theory] - [MemberData(nameof(PosixSignalValues))] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [MemberData(nameof(SupportedSignals))] public void SignalHandlerCalledForKnownSignals(PosixSignal s) { - RemoteExecutor.Invoke((signalStr) => { + RemoteExecutor.Invoke(signalStr => + { PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); using var _ = PosixSignalRegistration.Create(signal, ctx => { @@ -60,18 +48,21 @@ namespace System.Tests semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); }, s.ToString()).Dispose(); } - [Theory] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(PosixSignalAsRawValues))] public void SignalHandlerCalledForRawSignals(PosixSignal s) { - RemoteExecutor.Invoke((signalStr) => { + RemoteExecutor.Invoke((signalStr) => + { PosixSignal signal = Enum.Parse(signalStr); + using SemaphoreSlim semaphore = new(0); using var _ = PosixSignalRegistration.Create(signal, ctx => { @@ -82,8 +73,9 @@ namespace System.Tests semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); }, s.ToString()).Dispose(); } @@ -105,8 +97,9 @@ namespace System.Tests semaphore.Release(); }); + kill(signal); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); } } @@ -116,11 +109,10 @@ namespace System.Tests { PosixSignal signal = PosixSignal.SIGCONT; - using var registration = PosixSignalRegistration.Create(signal, ctx => + PosixSignalRegistration.Create(signal, ctx => { Assert.False(true, "Signal handler was called."); - }); - registration.Dispose(); + }).Dispose(); kill(signal); Thread.Sleep(100); @@ -179,7 +171,7 @@ namespace System.Tests kill(signalArg); - bool entered = semaphore.Wait(Timeout); + bool entered = semaphore.Wait(SuccessTimeout); Assert.True(entered); // Give the default signal handler a chance to run. @@ -190,19 +182,6 @@ namespace System.Tests new RemoteInvokeOptions() { ExpectedExitCode = expectedExitCode, TimeOut = 10 * 60 * 1000 }).Dispose(); } - public static TheoryData PosixSignalValues - { - get - { - var data = new TheoryData(); - foreach (var value in Enum.GetValues(typeof(PosixSignal))) - { - data.Add((PosixSignal)value); - } - return data; - } - } - public static TheoryData PosixSignalAsRawValues { get @@ -230,7 +209,6 @@ namespace System.Tests } [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")] - [SuppressGCTransition] private static extern int GetPlatformSignalNumber(PosixSignal signal); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs new file mode 100644 index 00000000000..ad5099bb850 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Windows.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + public static IEnumerable UninstallableSignals() => Enumerable.Empty(); + + public static IEnumerable SupportedSignals() => SupportedPosixSignals.Select(p => new object[] { p }); + + public static IEnumerable UnsupportedSignals() + { + foreach (PosixSignal signal in Enum.GetValues().Except(SupportedPosixSignals)) + { + yield return new object[] { signal }; + } + + yield return new object[] { 0 }; + yield return new object[] { -1000 }; + yield return new object[] { 1000 }; + } + + private static IEnumerable SupportedPosixSignals => new[] { PosixSignal.SIGINT, PosixSignal.SIGQUIT, PosixSignal.SIGTERM, PosixSignal.SIGHUP }; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs new file mode 100644 index 00000000000..599d1adc6c6 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using Xunit; + +namespace System.Tests +{ + public partial class PosixSignalRegistrationTests + { + private static TimeSpan SuccessTimeout => TimeSpan.FromSeconds(30); + + [Fact] + public void Create_NullHandler_Throws() + { + AssertExtensions.Throws("handler", () => PosixSignalRegistration.Create(PosixSignal.SIGCONT, null)); + } + + [Theory] + [MemberData(nameof(UnsupportedSignals))] + public void Create_InvalidSignal_Throws(PosixSignal signal) + { + Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + } + + [Theory] + [MemberData(nameof(UninstallableSignals))] + public void Create_UninstallableSignal_Throws(PosixSignal signal) + { + Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); + } + + [Theory] + [MemberData(nameof(SupportedSignals))] + public void Create_ValidSignal_Success(PosixSignal signal) + { + PosixSignalRegistration.Create(signal, ctx => { }).Dispose(); + } + + [Theory] + [MemberData(nameof(SupportedSignals))] + public void Dispose_Idempotent(PosixSignal signal) + { + PosixSignalRegistration registration = PosixSignalRegistration.Create(signal, ctx => { }); + registration.Dispose(); + registration.Dispose(); + } + + [Fact] + public void Create_RegisterForMultipleSignalsMultipletimes_Success() + { + var registrations = new List(); + for (int i = 0; i < 3; i++) + { + foreach (object[] signal in SupportedSignals()) + { + registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + } + + foreach (object[] signal in SupportedSignals()) + { + registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + } + + foreach (PosixSignalRegistration registration in registrations) + { + registration.Dispose(); + } + } + } + } +} From dcce0f56e10f5ac9539354b049341a2d7c0cdebf Mon Sep 17 00:00:00 2001 From: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> Date: Fri, 9 Jul 2021 04:17:31 -0700 Subject: [PATCH 372/926] Add symbolic link APIs (#54253) Co-authored-by: David Cantu Co-authored-by: carlossanlop Co-authored-by: carlossanlop --- .../Unix/Interop.DefaultPathBufferSize.cs | 9 + .../Unix/System.Native/Interop.GetCwd.cs | 8 +- .../Unix/System.Native/Interop.ReadLink.cs | 30 +- .../Unix/System.Native/Interop.Stat.Span.cs | 8 +- .../Unix/System.Native/Interop.SymLink.cs | 14 + .../src/Interop/Windows/Interop.Errors.cs | 1 + .../Kernel32/Interop.CreateSymbolicLink.cs | 70 +++ .../Kernel32/Interop.DeviceIoControl.cs | 26 + .../Kernel32/Interop.FileOperations.cs | 2 + .../Interop.GetFinalPathNameByHandle.cs | 23 + .../Kernel32/Interop.REPARSE_DATA_BUFFER.cs | 37 ++ .../IO/FileSystem.Attributes.Windows.cs | 36 +- .../System/IO/FileCleanupTestBase.cs | 58 ++ .../src/Microsoft.IO.Redist.csproj | 12 +- .../src/Resources/Strings.resx | 3 + .../Native/Unix/System.Native/entrypoints.c | 1 + .../Native/Unix/System.Native/pal_io.c | 7 + .../Native/Unix/System.Native/pal_io.h | 9 +- .../src/System.Diagnostics.Process.csproj | 4 + .../src/System.IO.FileSystem.Watcher.csproj | 2 + .../tests/Utility/FileSystemWatcherTest.cs | 62 -- .../tests/Base/BaseSymbolicLinks.cs | 18 - .../BaseSymbolicLinks.FileSystem.cs | 531 ++++++++++++++++++ .../BaseSymbolicLinks.FileSystemInfo.cs | 101 ++++ .../SymbolicLinks/BaseSymbolicLinks.Unix.cs | 19 + .../BaseSymbolicLinks.Windows.cs | 66 +++ .../Base/SymbolicLinks/BaseSymbolicLinks.cs | 34 ++ .../tests/Directory/SymbolicLinks.cs | 67 ++- .../tests/DirectoryInfo/SymbolicLinks.cs | 64 ++- .../tests/File/SymbolicLinks.cs | 55 ++ .../tests/FileInfo/SymbolicLinks.cs | 51 ++ .../tests/FileSystemTest.cs | 26 - ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 6 + .../tests/System.IO.FileSystem.Tests.csproj | 15 +- .../src/System.Net.Ping.csproj | 4 + .../System.Net.Ping.Functional.Tests.csproj | 4 + .../src/Resources/Strings.resx | 6 + .../System.Private.CoreLib.Shared.projitems | 19 + .../src/System/IO/Directory.cs | 42 ++ .../src/System/IO/File.cs | 40 ++ .../src/System/IO/FileSystem.Unix.cs | 91 +++ .../src/System/IO/FileSystem.Windows.cs | 241 +++++++- .../src/System/IO/FileSystem.cs | 29 + .../src/System/IO/FileSystemInfo.Unix.cs | 8 +- .../src/System/IO/FileSystemInfo.Windows.cs | 15 +- .../src/System/IO/FileSystemInfo.cs | 62 ++ .../src/System/IO/Path.cs | 2 +- .../src/System/IO/PathInternal.cs | 8 + .../System.Runtime/ref/System.Runtime.cs | 7 + ...urity.Cryptography.X509Certificates.csproj | 4 + 50 files changed, 1883 insertions(+), 174 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs delete mode 100644 src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs diff --git a/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs new file mode 100644 index 00000000000..d9807b427bf --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths + // without putting too much pressure on the stack. + internal const int DefaultPathBufferSize = 256; +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs index 1faef8cc0be..78da5a66731 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs @@ -14,18 +14,16 @@ internal static partial class Interop internal static unsafe string GetCwd() { - const int StackLimit = 256; - // First try to get the path into a buffer on the stack - byte* stackBuf = stackalloc byte[StackLimit]; - string? result = GetCwdHelper(stackBuf, StackLimit); + byte* stackBuf = stackalloc byte[DefaultPathBufferSize]; + string? result = GetCwdHelper(stackBuf, DefaultPathBufferSize); if (result != null) { return result; } // If that was too small, try increasing large buffer sizes - int bufferSize = StackLimit; + int bufferSize = DefaultPathBufferSize; while (true) { checked { bufferSize *= 2; } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs index 8f0f6a15fed..94f37d4ccc3 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using System.Buffers; using System.Text; +using System; internal static partial class Interop { @@ -20,24 +21,31 @@ internal static partial class Interop /// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error. /// [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)] - private static extern int ReadLink(string path, byte[] buffer, int bufferSize); + private static extern int ReadLink(ref byte path, byte[] buffer, int bufferSize); /// /// Takes a path to a symbolic link and returns the link target path. /// - /// The path to the symlink - /// - /// Returns the link to the target path on success; and null otherwise. - /// - public static string? ReadLink(string path) + /// The path to the symlink. + /// Returns the link to the target path on success; and null otherwise. + internal static string? ReadLink(ReadOnlySpan path) { - int bufferSize = 256; + int outputBufferSize = 1024; + + // Use an initial buffer size that prevents disposing and renting + // a second time when calling ConvertAndTerminateString. + using var converter = new ValueUtf8Converter(stackalloc byte[1024]); + while (true) { - byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + byte[] buffer = ArrayPool.Shared.Rent(outputBufferSize); try { - int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length); + int resultLength = Interop.Sys.ReadLink( + ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), + buffer, + buffer.Length); + if (resultLength < 0) { // error @@ -54,8 +62,8 @@ internal static partial class Interop ArrayPool.Shared.Return(buffer); } - // buffer was too small, loop around again and try with a larger buffer. - bufferSize *= 2; + // Output buffer was too small, loop around again and try with a larger buffer. + outputBufferSize = buffer.Length * 2; } } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs index 3c638cb60aa..85028fd0fd0 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs @@ -9,16 +9,12 @@ internal static partial class Interop { internal static partial class Sys { - // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths - // without putting too much pressure on the stack. - private const int StackBufferSize = 256; - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)] internal static extern int Stat(ref byte path, out FileStatus output); internal static int Stat(ReadOnlySpan path, out FileStatus output) { - var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]); + var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]); int result = Stat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output); converter.Dispose(); return result; @@ -29,7 +25,7 @@ internal static partial class Interop internal static int LStat(ReadOnlySpan path, out FileStatus output) { - var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]); + var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]); int result = LStat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output); converter.Dispose(); return result; diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs new file mode 100644 index 00000000000..922ecd5bc66 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SymLink", SetLastError = true)] + internal static extern int SymLink(string target, string linkPath); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs index 338706ea849..d5f6d163750 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs @@ -91,5 +91,6 @@ internal static partial class Interop internal const int ERROR_EVENTLOG_FILE_CHANGED = 0x5DF; internal const int ERROR_TRUSTED_RELATIONSHIP_FAILURE = 0x6FD; internal const int ERROR_RESOURCE_LANG_NOT_FOUND = 0x717; + internal const int ERROR_NOT_A_REPARSE_POINT = 0x1126; } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs new file mode 100644 index 00000000000..9ecd41c46bd --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + /// + /// The link target is a directory. + /// + internal const int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1; + + /// + /// Allows creation of symbolic links from a process that is not elevated. Requires Windows 10 Insiders build 14972 or later. + /// Developer Mode must first be enabled on the machine before this option will function. + /// + internal const int SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2; + + [DllImport(Libraries.Kernel32, EntryPoint = "CreateSymbolicLinkW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + private static extern bool CreateSymbolicLinkPrivate(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); + + /// + /// Creates a symbolic link. + /// + /// The symbolic link to be created. + /// The name of the target for the symbolic link to be created. + /// If it has a device name associated with it, the link is treated as an absolute link; otherwise, the link is treated as a relative link. + /// if the link target is a directory; otherwise. + internal static void CreateSymbolicLink(string symlinkFileName, string targetFileName, bool isDirectory) + { + string originalPath = symlinkFileName; + symlinkFileName = PathInternal.EnsureExtendedPrefixIfNeeded(symlinkFileName); + targetFileName = PathInternal.EnsureExtendedPrefixIfNeeded(targetFileName); + + int flags = 0; + + bool isAtLeastWin10Build14972 = + Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 14972 || + Environment.OSVersion.Version.Major >= 11; + + if (isAtLeastWin10Build14972) + { + flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + if (isDirectory) + { + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; + } + + bool success = CreateSymbolicLinkPrivate(symlinkFileName, targetFileName, flags); + + int error; + if (!success) + { + throw Win32Marshal.GetExceptionForLastWin32Error(originalPath); + } + // In older versions we need to check GetLastWin32Error regardless of the return value of CreateSymbolicLink, + // e.g: if the user doesn't have enough privileges to create a symlink the method returns success which we can consider as a silent failure. + else if (!isAtLeastWin10Build14972 && (error = Marshal.GetLastWin32Error()) != 0) + { + throw Win32Marshal.GetExceptionForWin32Error(error, originalPath); + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs new file mode 100644 index 00000000000..be8def21517 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point + internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8; + + [DllImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern bool DeviceIoControl( + SafeHandle hDevice, + uint dwIoControlCode, + IntPtr lpInBuffer, + uint nInBufferSize, + byte[] lpOutBuffer, + uint nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs index f2a3872299d..cc4896c1c52 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs @@ -9,6 +9,7 @@ internal static partial class Interop { internal const uint IO_REPARSE_TAG_FILE_PLACEHOLDER = 0x80000015; internal const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; + internal const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C; } internal static partial class FileOperations @@ -18,6 +19,7 @@ internal static partial class Interop internal const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; internal const int FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000; + internal const int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; internal const int FILE_FLAG_OVERLAPPED = 0x40000000; internal const int FILE_LIST_DIRECTORY = 0x0001; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs new file mode 100644 index 00000000000..756b1bbd72d --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + internal const uint FILE_NAME_NORMALIZED = 0x0; + + // https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew (kernel32) + [DllImport(Libraries.Kernel32, EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static unsafe extern uint GetFinalPathNameByHandle( + SafeFileHandle hFile, + char* lpszFilePath, + uint cchFilePath, + uint dwFlags); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs new file mode 100644 index 00000000000..3bcb9162d57 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + // https://docs.microsoft.com/windows-hardware/drivers/ifs/fsctl-get-reparse-point + internal const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; + + internal const uint SYMLINK_FLAG_RELATIVE = 1; + + // https://msdn.microsoft.com/library/windows/hardware/ff552012.aspx + // We don't need all the struct fields; omitting the rest. + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct REPARSE_DATA_BUFFER + { + internal uint ReparseTag; + internal ushort ReparseDataLength; + internal ushort Reserved; + internal SymbolicLinkReparseBuffer ReparseBufferSymbolicLink; + + [StructLayout(LayoutKind.Sequential)] + internal struct SymbolicLinkReparseBuffer + { + internal ushort SubstituteNameOffset; + internal ushort SubstituteNameLength; + internal ushort PrintNameOffset; + internal ushort PrintNameLength; + internal uint Flags; + } + } + } +} diff --git a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs index 60e7a0aa466..ad087304b4e 100644 --- a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs +++ b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs @@ -64,19 +64,7 @@ namespace System.IO { errorCode = Marshal.GetLastWin32Error(); - if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND - && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND - && errorCode != Interop.Errors.ERROR_NOT_READY - && errorCode != Interop.Errors.ERROR_INVALID_NAME - && errorCode != Interop.Errors.ERROR_BAD_PATHNAME - && errorCode != Interop.Errors.ERROR_BAD_NETPATH - && errorCode != Interop.Errors.ERROR_BAD_NET_NAME - && errorCode != Interop.Errors.ERROR_INVALID_PARAMETER - && errorCode != Interop.Errors.ERROR_NETWORK_UNREACHABLE - && errorCode != Interop.Errors.ERROR_NETWORK_ACCESS_DENIED - && errorCode != Interop.Errors.ERROR_INVALID_HANDLE // eg from \\.\CON - && errorCode != Interop.Errors.ERROR_FILENAME_EXCED_RANGE // Path is too long - ) + if (!IsPathUnreachableError(errorCode)) { // Assert so we can track down other cases (if any) to add to our test suite Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION || errorCode == Interop.Errors.ERROR_SEM_TIMEOUT, @@ -127,5 +115,27 @@ namespace System.IO return errorCode; } + + internal static bool IsPathUnreachableError(int errorCode) + { + switch (errorCode) + { + case Interop.Errors.ERROR_FILE_NOT_FOUND: + case Interop.Errors.ERROR_PATH_NOT_FOUND: + case Interop.Errors.ERROR_NOT_READY: + case Interop.Errors.ERROR_INVALID_NAME: + case Interop.Errors.ERROR_BAD_PATHNAME: + case Interop.Errors.ERROR_BAD_NETPATH: + case Interop.Errors.ERROR_BAD_NET_NAME: + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NETWORK_UNREACHABLE: + case Interop.Errors.ERROR_NETWORK_ACCESS_DENIED: + case Interop.Errors.ERROR_INVALID_HANDLE: // eg from \\.\CON + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: // Path is too long + return true; + default: + return false; + } + } } } diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index 02ffa607c94..a45aab12165 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -127,5 +127,63 @@ namespace System.IO lineNumber, index.GetValueOrDefault(), Guid.NewGuid().ToString("N").Substring(0, 8)); // randomness to avoid collisions between derived test classes using same base method concurrently + + /// + /// In some cases (such as when running without elevated privileges), + /// the symbolic link may fail to create. Only run this test if it creates + /// links successfully. + /// + protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; + + private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => + { + bool success = true; + + // Verify file symlink creation + string path = Path.GetTempFileName(); + string linkPath = path + ".link"; + success = CreateSymLink(path, linkPath, isDirectory: false); + try { File.Delete(path); } catch { } + try { File.Delete(linkPath); } catch { } + + // Verify directory symlink creation + path = Path.GetTempFileName(); + linkPath = path + ".link"; + success = success && CreateSymLink(path, linkPath, isDirectory: true); + try { Directory.Delete(path); } catch { } + try { Directory.Delete(linkPath); } catch { } + + return success; + }); + + protected static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory) + { +#if NETFRAMEWORK + bool isWindows = true; +#else + if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsBrowser()) // OSes that don't support Process.Start() + { + return false; + } + bool isWindows = OperatingSystem.IsWindows(); +#endif + Process symLinkProcess = new Process(); + if (isWindows) + { + symLinkProcess.StartInfo.FileName = "cmd"; + symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath)); + } + else + { + symLinkProcess.StartInfo.FileName = "/bin/ln"; + symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath)); + } + symLinkProcess.StartInfo.RedirectStandardOutput = true; + symLinkProcess.Start(); + + symLinkProcess.WaitForExit(); + return (0 == symLinkProcess.ExitCode); + } + } } diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj index db9472c1432..f22780b1a5e 100644 --- a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj +++ b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj @@ -1,4 +1,4 @@ - + net472 $(DefineConstants);MS_IO_REDIST @@ -34,6 +34,8 @@ Link="Microsoft\IO\File.cs" /> + + + + + The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. + + The link's file system entry type is inconsistent with that of its target: {0} + Could not find a part of the path. diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index b1b5a92e5f3..f39360810b5 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -81,6 +81,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_Access) DllImportEntry(SystemNative_LSeek) DllImportEntry(SystemNative_Link) + DllImportEntry(SystemNative_SymLink) DllImportEntry(SystemNative_MksTemps) DllImportEntry(SystemNative_MMap) DllImportEntry(SystemNative_MUnmap) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index d7eb6c4ab23..9e02aa7c2e6 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -715,6 +715,13 @@ int32_t SystemNative_Link(const char* source, const char* linkTarget) return result; } +int32_t SystemNative_SymLink(const char* target, const char* linkPath) +{ + int32_t result; + while ((result = symlink(target, linkPath)) < 0 && errno == EINTR); + return result; +} + intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength) { intptr_t result; diff --git a/src/libraries/Native/Unix/System.Native/pal_io.h b/src/libraries/Native/Unix/System.Native/pal_io.h index 1dc70387ad5..6461881a91e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.h +++ b/src/libraries/Native/Unix/System.Native/pal_io.h @@ -527,12 +527,19 @@ PALEXPORT int32_t SystemNative_Access(const char* path, int32_t mode); PALEXPORT int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, int32_t whence); /** - * Creates a hard-link at link pointing to source. + * Creates a hard-link at linkTarget pointing to source. * * Returns 0 on success; otherwise, returns -1 and errno is set. */ PALEXPORT int32_t SystemNative_Link(const char* source, const char* linkTarget); +/** + * Creates a symbolic link at linkPath pointing to target. + * + * Returns 0 on success; otherwise, returns -1 and errno is set. + */ +PALEXPORT int32_t SystemNative_SymLink(const char* target, const char* linkPath); + /** * Creates a file name that adheres to the specified template, creates the file on disk with * 0600 permissions, and returns an open r/w File Descriptor on the file. diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 424a7a87b31..882b1beb85f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -233,6 +233,8 @@ Link="Common\Interop\Unix\Interop.Errors.cs" /> + + + - /// In some cases (such as when running without elevated privileges), - /// the symbolic link may fail to create. Only run this test if it creates - /// links successfully. - /// - protected static bool CanCreateSymbolicLinks - { - get - { - bool success = true; - - // Verify file symlink creation - string path = Path.GetTempFileName(); - string linkPath = path + ".link"; - success = CreateSymLink(path, linkPath, isDirectory: false); - try { File.Delete(path); } catch { } - try { File.Delete(linkPath); } catch { } - - // Verify directory symlink creation - path = Path.GetTempFileName(); - linkPath = path + ".link"; - success = success && CreateSymLink(path, linkPath, isDirectory: true); - try { Directory.Delete(path); } catch { } - try { Directory.Delete(linkPath); } catch { } - - return success; - } - } - - public static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory) - { - if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst()) // OSes that don't support Process.Start() - { - return false; - } - - Process symLinkProcess = new Process(); - if (OperatingSystem.IsWindows()) - { - symLinkProcess.StartInfo.FileName = "cmd"; - symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath)); - } - else - { - symLinkProcess.StartInfo.FileName = "/bin/ln"; - symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath)); - } - symLinkProcess.StartInfo.RedirectStandardOutput = true; - symLinkProcess.Start(); - - if (symLinkProcess != null) - { - symLinkProcess.WaitForExit(); - return (0 == symLinkProcess.ExitCode); - } - else - { - return false; - } - } - - public static IEnumerable FilterTypes() { foreach (NotifyFilters filter in Enum.GetValues(typeof(NotifyFilters))) diff --git a/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs deleted file mode 100644 index caa5ae9c3e1..00000000000 --- a/src/libraries/System.IO.FileSystem/tests/Base/BaseSymbolicLinks.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -namespace System.IO.Tests -{ - public abstract class BaseSymbolicLinks : FileSystemTest - { - protected DirectoryInfo CreateDirectoryContainingSelfReferencingSymbolicLink() - { - DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); - string pathToLink = Path.Join(testDirectory.FullName, GetTestFileName()); - Assert.True(MountHelper.CreateSymbolicLink(pathToLink, pathToLink, isDirectory: true)); // Create a symlink cycle - return testDirectory; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs new file mode 100644 index 00000000000..6f9a1ace400 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs @@ -0,0 +1,531 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.IO.Tests +{ + // Contains test methods that can be used for FileInfo, DirectoryInfo, File or Directory. + public abstract class BaseSymbolicLinks_FileSystem : BaseSymbolicLinks + { + protected abstract bool IsDirectoryTest { get; } + + /// Creates a new file or directory depending on the implementing class. + /// If createOpposite is true, creates a directory if the implementing class is for File or FileInfo, or + /// creates a file if the implementing class is for Directory or DirectoryInfo. + protected abstract void CreateFileOrDirectory(string path, bool createOpposite = false); + + protected abstract void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo); + + protected abstract void AssertLinkExists(FileSystemInfo linkInfo); + + /// Calls the actual public API for creating a symbolic link. + protected abstract FileSystemInfo CreateSymbolicLink(string path, string pathToTarget); + + private void CreateSymbolicLink_Opposite(string path, string pathToTarget) + { + if (IsDirectoryTest) + { + File.CreateSymbolicLink(path, pathToTarget); + } + else + { + Directory.CreateSymbolicLink(path, pathToTarget); + } + } + + /// Calls the actual public API for resolving the symbolic link target. + protected abstract FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget); + + [Fact] + public void CreateSymbolicLink_NullPathToTarget() + { + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), pathToTarget: null)); + } + + [Theory] + [InlineData("")] + [InlineData("\0")] + public void CreateSymbolicLink_InvalidPathToTarget(string pathToTarget) + { + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), pathToTarget)); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_TargetExists() + { + // /path/to/link -> /path/to/existingtarget + + string linkPath = GetRandomLinkPath(); + string existentTarget = GetRandomFileName(); + string targetPath = Path.Join(Path.GetDirectoryName(linkPath), existentTarget); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_TargetExists_WithRedundantSegments() + { + // /path/to/link -> /path/to/../to/existingtarget + + string linkPath = GetRandomLinkPath(); + string fileName = GetRandomFileName(); + string dirPath = Path.GetDirectoryName(linkPath); + string dirName = Path.GetFileName(dirPath); + string targetPath = Path.Join(dirPath, fileName); + string existentTarget = Path.Join("..", dirName, fileName); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_TargetExists() + { + // /path/to/link -> /path/to/existingtarget + string linkPath = GetRandomLinkPath(); + string targetPath = GetRandomFilePath(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: targetPath, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_TargetExists_WithRedundantSegments() + { + // /path/to/link -> /path/to/../to/existingtarget + + string linkPath = GetRandomLinkPath(); + string fileName = GetRandomFileName(); + string dirPath = Path.GetDirectoryName(linkPath); + string dirName = Path.GetFileName(dirPath); + string targetPath = Path.Join(dirPath, fileName); + string existentTarget = Path.Join(dirPath, "..", dirName, fileName); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: existentTarget, + targetPath: targetPath); + } + + [Fact] + public void CreateSymbolicLink_RelativeTargetPath_NonExistentTarget() + { + // /path/to/link -> /path/to/nonexistenttarget + + string linkPath = GetRandomLinkPath(); + string nonExistentTarget = GetRandomFileName(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: nonExistentTarget, + targetPath: null); // do not create target + } + + [Fact] + public void CreateSymbolicLink_AbsoluteTargetPath_NonExistentTarget() + { + // /path/to/link -> /path/to/nonexistenttarget + + string linkPath = GetRandomLinkPath(); + string nonExistentTarget = GetRandomFilePath(); + VerifySymbolicLinkAndResolvedTarget( + linkPath: linkPath, + expectedLinkTarget: nonExistentTarget, + targetPath: null); // do not create target + } + + protected void ResolveLinkTarget_Throws_NotExists_Internal() where T : Exception + { + string path = GetRandomFilePath(); + Assert.Throws(() => ResolveLinkTarget(path, returnFinalTarget: false)); + Assert.Throws(() => ResolveLinkTarget(path, returnFinalTarget: true)); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ResolveLinkTarget_ReturnsNull_NotALink(bool returnFinalTarget) + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); + Assert.Null(ResolveLinkTarget(path, returnFinalTarget)); + } + + [Theory] + [MemberData(nameof(ResolveLinkTarget_PathToTarget_Data))] + public void ResolveLinkTarget_Succeeds(string pathToTarget, bool returnFinalTarget) + { + string linkPath = GetRandomLinkPath(); + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, pathToTarget); + AssertLinkExists(linkInfo); + AssertIsCorrectTypeAndDirectoryAttribute(linkInfo); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + FileSystemInfo targetInfo = ResolveLinkTarget(linkPath, returnFinalTarget); + Assert.NotNull(targetInfo); + Assert.False(targetInfo.Exists); + + string expectedTargetFullName = Path.IsPathFullyQualified(pathToTarget) ? + pathToTarget : Path.GetFullPath(Path.Join(Path.GetDirectoryName(linkPath), pathToTarget)); + + Assert.Equal(expectedTargetFullName, targetInfo.FullName); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ResolveLinkTarget_FileSystemEntryExistsButIsNotALink(bool returnFinalTarget) + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); // entry exists as a normal file, not as a link + + FileSystemInfo target = ResolveLinkTarget(path, returnFinalTarget); + Assert.Null(target); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Absolute() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: link2Path, + link2Path: link2Path, + link2Target: filePath, + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Absolute_WithRedundantSegments() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string dirPath = Path.GetDirectoryName(filePath); + string dirName = Path.GetFileName(dirPath); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: Path.Join(dirPath, "..", dirName, link2FileName), + link2Path: link2Path, + link2Target: Path.Join(dirPath, "..", dirName, fileName), + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Relative() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: link2FileName, + link2Path: link2Path, + link2Target: fileName, + filePath: filePath); + } + + [Fact] + public void ResolveLinkTarget_ReturnFinalTarget_Relative_WithRedundantSegments() + { + string link1Path = GetRandomLinkPath(); + string link2Path = GetRandomLinkPath(); + string filePath = GetRandomFilePath(); + + string dirPath = Path.GetDirectoryName(filePath); + string dirName = Path.GetFileName(dirPath); + + string link2FileName = Path.GetFileName(link2Path); + string fileName = Path.GetFileName(filePath); + + ResolveLinkTarget_ReturnFinalTarget( + link1Path: link1Path, + link1Target: Path.Join("..", dirName, link2FileName), + link2Path: link2Path, + link2Target: Path.Join("..", dirName, fileName), + filePath: filePath); + } + + [Theory] + [InlineData(1, false)] + [InlineData(10, false)] + [InlineData(20, false)] + [InlineData(1, true)] + [InlineData(10, true)] + [InlineData(20, true)] + public void ResolveLinkTarget_ReturnFinalTarget_ChainOfLinks_Succeeds(int length, bool relative) + { + string target = GetRandomFilePath(); + CreateFileOrDirectory(target); + + string tail = CreateChainOfLinks(target, length, relative); + FileSystemInfo targetInfo = ResolveLinkTarget(tail, returnFinalTarget: true); + Assert.Equal(target, targetInfo.FullName); + } + + [Theory] + // 100 is way beyond the limit (63 in Windows and 40 in Unix), we just want to make sure that a nice exception is thrown when its exceeded. + // We also don't want to test for a very precise limit given that it is very inconsistent across Windows versions. + [InlineData(100, false)] + [InlineData(100, true)] + public void ResolveLinkTarget_ReturnFinalTarget_ChainOfLinks_ExceedsLimit_Throws(int length, bool relative) + { + string target = GetRandomFilePath(); + CreateFileOrDirectory(target); + + string tail = CreateChainOfLinks(target, length, relative); + Assert.Throws(() => ResolveLinkTarget(tail, returnFinalTarget: true)); + } + + private string CreateChainOfLinks(string target, int length, bool relative) + { + string previousPath = target; + + for (int i = 0; i < length; i++) + { + string currentLinkPath = GetRandomLinkPath(); + CreateSymbolicLink(currentLinkPath, relative ? Path.GetFileName(previousPath) : previousPath); + previousPath = currentLinkPath; + } + + return previousPath; + } + + [Fact] + public void DetectSymbolicLinkCycle() + { + // link1 -> link2 -> link1 (cycle) + + string link2Path = GetRandomFilePath(); + string link1Path = GetRandomFilePath(); + + FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link2Path); + FileSystemInfo link2Info = CreateSymbolicLink(link2Path, link1Path); + + // Can get targets without following symlinks + FileSystemInfo link1Target = ResolveLinkTarget(link1Path, returnFinalTarget: false); + FileSystemInfo link2Target = ResolveLinkTarget(link2Path, returnFinalTarget: false); + + // Cannot get target when following symlinks + Assert.Throws(() => ResolveLinkTarget(link1Path, returnFinalTarget: true)); + Assert.Throws(() => ResolveLinkTarget(link2Path, returnFinalTarget: true)); + } + + [Fact] + public void DetectLinkReferenceToSelf() + { + // link -> link (reference to itself) + + string linkPath = GetRandomFilePath(); + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, linkPath); + + // Can get target without following symlinks + FileSystemInfo linkTarget = ResolveLinkTarget(linkPath, returnFinalTarget: false); + + // Cannot get target when following symlinks + Assert.Throws(() => ResolveLinkTarget(linkPath, returnFinalTarget: true)); + } + + [Fact] + public void CreateSymbolicLink_WrongTargetType_Throws() + { + // dirLink -> file + // fileLink -> dir + + string targetPath = GetRandomFilePath(); + CreateFileOrDirectory(targetPath, createOpposite: true); // The underlying file system entry needs to be different + Assert.Throws(() => CreateSymbolicLink(GetRandomFilePath(), targetPath)); + } + + [Fact] + public void CreateSymbolicLink_WrongTargetType_Indirect_Throws() + { + // link-2 (dir) -> link-1 (file) -> file + // link-2 (file) -> link-1 (dir) -> dir + string targetPath = GetRandomFilePath(); + string firstLinkPath = GetRandomFilePath(); + string secondLinkPath = GetRandomFilePath(); + + CreateFileOrDirectory(targetPath, createOpposite: true); + CreateSymbolicLink_Opposite(firstLinkPath, targetPath); + + Assert.Throws(() => CreateSymbolicLink(secondLinkPath, firstLinkPath)); + } + + [Fact] + public void CreateSymbolicLink_CorrectTargetType_Indirect_Succeeds() + { + // link-2 (file) -> link-1 (file) -> file + // link-2 (dir) -> link-1 (dir) -> dir + string targetPath = GetRandomFilePath(); + string firstLinkPath = GetRandomFilePath(); + string secondLinkPath = GetRandomFilePath(); + + CreateFileOrDirectory(targetPath, createOpposite: false); + CreateSymbolicLink(firstLinkPath, targetPath); + + FileSystemInfo secondLinkInfo = CreateSymbolicLink(secondLinkPath, firstLinkPath); + Assert.Equal(firstLinkPath, secondLinkInfo.LinkTarget); + Assert.Equal(targetPath, secondLinkInfo.ResolveLinkTarget(true).FullName); + } + + private void VerifySymbolicLinkAndResolvedTarget(string linkPath, string expectedLinkTarget, string targetPath = null) + { + // linkPath -> expectedLinkTarget (created in targetPath if not null) + + if (targetPath != null) + { + CreateFileOrDirectory(targetPath); + } + + FileSystemInfo link = CreateSymbolicLink(linkPath, expectedLinkTarget); + if (targetPath == null) + { + // Behavior different between files and directories when target does not exist + AssertLinkExists(link); + } + else + { + Assert.True(link.Exists); // The target file or directory was created above, so we report Exists of the target for both + } + + FileSystemInfo target = ResolveLinkTarget(linkPath, returnFinalTarget: false); + AssertIsCorrectTypeAndDirectoryAttribute(target); + Assert.True(Path.IsPathFullyQualified(target.FullName)); + } + + /// + /// Creates and Resolves a chain of links. + /// link1 -> link2 -> file + /// + private void ResolveLinkTarget_ReturnFinalTarget(string link1Path, string link1Target, string link2Path, string link2Target, string filePath) + { + Assert.True(Path.IsPathFullyQualified(link1Path)); + Assert.True(Path.IsPathFullyQualified(link2Path)); + Assert.True(Path.IsPathFullyQualified(filePath)); + + CreateFileOrDirectory(filePath); + + // link2 to file + FileSystemInfo link2Info = CreateSymbolicLink(link2Path, link2Target); + Assert.True(link2Info.Exists); + Assert.True(link2Info.Attributes.HasFlag(FileAttributes.ReparsePoint)); + AssertIsCorrectTypeAndDirectoryAttribute(link2Info); + Assert.Equal(link2Target, link2Info.LinkTarget); + + // link1 to link2 + FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link1Target); + Assert.True(link1Info.Exists); + Assert.True(link1Info.Attributes.HasFlag(FileAttributes.ReparsePoint)); + AssertIsCorrectTypeAndDirectoryAttribute(link1Info); + Assert.Equal(link1Target, link1Info.LinkTarget); + + // link1: do not follow symlinks + FileSystemInfo link1TargetInfo = ResolveLinkTarget(link1Path, returnFinalTarget: false); + Assert.True(link1TargetInfo.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(link1TargetInfo); + Assert.True(link1TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(link2Path, link1TargetInfo.FullName); + Assert.Equal(link2Target, link1TargetInfo.LinkTarget); + + // link2: do not follow symlinks + FileSystemInfo link2TargetInfo = ResolveLinkTarget(link2Path, returnFinalTarget: false); + Assert.True(link2TargetInfo.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(link2TargetInfo); + Assert.False(link2TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(filePath, link2TargetInfo.FullName); + Assert.Null(link2TargetInfo.LinkTarget); + + // link1: follow symlinks + FileSystemInfo finalTarget = ResolveLinkTarget(link1Path, returnFinalTarget: true); + Assert.True(finalTarget.Exists); + AssertIsCorrectTypeAndDirectoryAttribute(finalTarget); + Assert.False(finalTarget.Attributes.HasFlag(FileAttributes.ReparsePoint)); + Assert.Equal(filePath, finalTarget.FullName); + } + + protected void CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(bool createOpposite) + { + string tempCwd = GetRandomDirPath(); + Directory.CreateDirectory(tempCwd); + Directory.SetCurrentDirectory(tempCwd); + + // Create a dummy file or directory in cwd. + string fileOrDirectoryInCwd = GetRandomFileName(); + CreateFileOrDirectory(fileOrDirectoryInCwd, createOpposite); + + string oneLevelUpPath = Path.Combine(tempCwd, "one-level-up"); + Directory.CreateDirectory(oneLevelUpPath); + string linkPath = Path.Combine(oneLevelUpPath, GetRandomLinkName()); + + // Create a link with a similar Target Path to the one of our dummy file or directory. + FileSystemInfo linkInfo = CreateSymbolicLink(linkPath, fileOrDirectoryInCwd); + FileSystemInfo targetInfo = linkInfo.ResolveLinkTarget(returnFinalTarget: false); + + // Verify that Target is resolved and is relative to Link's directory and not to the cwd. + Assert.False(targetInfo.Exists); + Assert.Equal(Path.GetDirectoryName(linkInfo.FullName), Path.GetDirectoryName(targetInfo.FullName)); + } + + public static IEnumerable ResolveLinkTarget_PathToTarget_Data + { + get + { + foreach (string path in PathToTargetData) + { + yield return new object[] { path, false }; + yield return new object[] { path, true }; + } + } + } + + internal static IEnumerable PathToTargetData + { + get + { + if (OperatingSystem.IsWindows()) + { + //Non-rooted relative + yield return "foo"; + yield return @".\foo"; + yield return @"..\foo"; + // Rooted relative + yield return @"\foo"; + // Rooted absolute + yield return Path.Combine(Path.GetTempPath(), "foo"); + // Extended DOS + yield return Path.Combine(@"\\?\", Path.GetTempPath(), "foo"); + // UNC + yield return @"\\SERVER\share\path"; + } + else + { + //Non-rooted relative + yield return "foo"; + yield return "./foo"; + yield return "../foo"; + // Rooted relative + yield return "/foo"; + // Rooted absolute + Path.Combine(Path.GetTempPath(), "foo"); + } + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs new file mode 100644 index 00000000000..e82864248eb --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystemInfo.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using Xunit; + +namespace System.IO.Tests +{ + // Contains test methods that can be used for FileInfo and DirectoryInfo. + public abstract class BaseSymbolicLinks_FileSystemInfo : BaseSymbolicLinks_FileSystem + { + // Creates and returns FileSystemInfo instance by calling either the DirectoryInfo or FileInfo constructor and passing the path. + protected abstract FileSystemInfo GetFileSystemInfo(string path); + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + FileSystemInfo link = GetFileSystemInfo(path); + link.CreateAsSymbolicLink(pathToTarget); + return link; + } + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) + => GetFileSystemInfo(linkPath).ResolveLinkTarget(returnFinalTarget); + + private void Delete(string path) + { + if (IsDirectoryTest) + { + Directory.Delete(path); + } + else + { + File.Delete(path); + } + } + + [Fact] + public void LinkTarget_ReturnsNull_NotExists() + { + FileSystemInfo info = GetFileSystemInfo(GetRandomLinkPath()); + Assert.Null(info.LinkTarget); + } + + [Fact] + public void LinkTarget_ReturnsNull_NotALink() + { + string path = GetRandomFilePath(); + CreateFileOrDirectory(path); + FileSystemInfo info = GetFileSystemInfo(path); + + Assert.True(info.Exists); + Assert.Null(info.LinkTarget); + } + + [Theory] + [MemberData(nameof(LinkTarget_PathToTarget_Data))] + public void LinkTarget_Succeeds(string pathToTarget) + { + FileSystemInfo linkInfo = CreateSymbolicLink(GetRandomLinkPath(), pathToTarget); + + AssertLinkExists(linkInfo); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + } + + [Fact] + public void LinkTarget_RefreshesCorrectly() + { + string path = GetRandomLinkPath(); + string pathToTarget = GetRandomFilePath(); + CreateFileOrDirectory(pathToTarget); + FileSystemInfo linkInfo = CreateSymbolicLink(path, pathToTarget); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + Delete(path); + Assert.Equal(pathToTarget, linkInfo.LinkTarget); + + linkInfo.Refresh(); + Assert.Null(linkInfo.LinkTarget); + + string newPathToTarget = GetRandomFilePath(); + CreateFileOrDirectory(newPathToTarget); + FileSystemInfo newLinkInfo = CreateSymbolicLink(path, newPathToTarget); + + linkInfo.Refresh(); + Assert.Equal(newPathToTarget, linkInfo.LinkTarget); + Assert.Equal(newLinkInfo.LinkTarget, linkInfo.LinkTarget); + } + + public static IEnumerable LinkTarget_PathToTarget_Data + { + get + { + foreach (string path in PathToTargetData) + { + yield return new object[] { path }; + } + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs new file mode 100644 index 00000000000..8acc040a8cb --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + private string GetTestDirectoryActualCasing() => TestDirectory; + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs new file mode 100644 index 00000000000..4572d48544f --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + private const int OPEN_EXISTING = 3; + private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + + // Some Windows versions like Windows Nano Server have the %TEMP% environment variable set to "C:\TEMP" but the + // actual folder name is "C:\Temp", which prevents asserting path values using Assert.Equal due to case sensitiveness. + // So instead of using TestDirectory directly, we retrieve the real path with proper casing of the initial folder path. + private unsafe string GetTestDirectoryActualCasing() + { + try + { + using SafeFileHandle handle = Interop.Kernel32.CreateFile( + TestDirectory, + dwDesiredAccess: 0, + dwShareMode: FileShare.ReadWrite | FileShare.Delete, + dwCreationDisposition: FileMode.Open, + dwFlagsAndAttributes: + OPEN_EXISTING | + FILE_FLAG_BACKUP_SEMANTICS // Necessary to obtain a handle to a directory + ); + + if (!handle.IsInvalid) + { + const int InitialBufferSize = 4096; + char[]? buffer = ArrayPool.Shared.Rent(InitialBufferSize); + uint result = GetFinalPathNameByHandle(handle, buffer); + + // Remove extended prefix + int skip = PathInternal.IsExtended(buffer) ? 4 : 0; + + return new string( + buffer, + skip, + (int)result - skip); + } + } + catch { } + + return TestDirectory; + } + + private unsafe uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) + { + fixed (char* bufPtr = buffer) + { + return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); + } + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs new file mode 100644 index 00000000000..d8869ff1581 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Tests +{ + [ConditionalClass(typeof(BaseSymbolicLinks), nameof(CanCreateSymbolicLinks))] + // Contains helper methods that are shared by all symbolic link test classes. + public abstract partial class BaseSymbolicLinks : FileSystemTest + { + protected DirectoryInfo CreateDirectoryContainingSelfReferencingSymbolicLink() + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetRandomDirPath()); + string pathToLink = Path.Join(testDirectory.FullName, GetRandomDirName()); + Assert.True(MountHelper.CreateSymbolicLink(pathToLink, pathToLink, isDirectory: true)); // Create a symlink cycle + return testDirectory; + } + + protected string GetRandomFileName() => GetTestFileName() + ".txt"; + protected string GetRandomLinkName() => GetTestFileName() + ".link"; + protected string GetRandomDirName() => GetTestFileName() + "_dir"; + + protected string GetRandomFilePath() => Path.Join(ActualTestDirectory.Value, GetRandomFileName()); + protected string GetRandomLinkPath() => Path.Join(ActualTestDirectory.Value, GetRandomLinkName()); + protected string GetRandomDirPath() => Path.Join(ActualTestDirectory.Value, GetRandomDirName()); + + private Lazy ActualTestDirectory => new Lazy(() => GetTestDirectoryActualCasing()); + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs index 970c784c7f8..d3d04ca2e54 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs @@ -1,15 +1,61 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.IO.Tests { - public class Directory_SymbolicLinks : BaseSymbolicLinks + public class Directory_SymbolicLinks : BaseSymbolicLinks_FileSystem { - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + protected override bool IsDirectoryTest => true; + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + Directory.CreateDirectory(path); + } + else + { + File.Create(path).Dispose(); + } + } + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) => + Directory.CreateSymbolicLink(path, pathToTarget); + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) => + Directory.ResolveLinkTarget(linkPath, returnFinalTarget); + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.True(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is DirectoryInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) + { + if (PlatformDetection.IsWindows) + { + Assert.True(link.Exists); + } + else + { + // Unix implementation detail: + // When the directory target does not exist FileStatus.GetExists returns false because: + // - We check _exists (which whould be true because the link itself exists). + // - We check InitiallyDirectory, which is the initial expected object type (which would be true). + // - We check _directory (false because the target directory does not exist) + Assert.False(link.Exists); + } + } + + [Fact] public void EnumerateDirectories_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -19,7 +65,7 @@ namespace System.IO.Tests Assert.Equal(expected, Directory.EnumerateDirectories(testDirectory.FullName).Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFiles_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -29,11 +75,22 @@ namespace System.IO.Tests Assert.Equal(expected, Directory.EnumerateFiles(testDirectory.FullName).Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFileSystemEntries_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); Assert.Single(Directory.EnumerateFileSystemEntries(testDirectory.FullName)); } + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs index 5dedc0b7b45..e7ee5d0c672 100644 --- a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs @@ -1,15 +1,58 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.IO.Tests { - public class DirectoryInfo_SymbolicLinks : BaseSymbolicLinks + public class DirectoryInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo { - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + protected override bool IsDirectoryTest => true; + + protected override FileSystemInfo GetFileSystemInfo(string path) => + new DirectoryInfo(path); + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + Directory.CreateDirectory(path); + } + else + { + File.Create(path).Dispose(); + } + } + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.True(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is DirectoryInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) + { + if (PlatformDetection.IsWindows) + { + Assert.True(link.Exists); + } + else + { + // Unix implementation detail: + // When the directory target does not exist FileStatus.GetExists returns false because: + // - We check _exists (which whould be true because the link itself exists). + // - We check InitiallyDirectory, which is the initial expected object type (which would be true). + // - We check _directory (false because the target directory does not exist) + Assert.False(link.Exists); + } + } + + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateDirectories_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -31,7 +74,7 @@ namespace System.IO.Tests } } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateFiles_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -53,7 +96,7 @@ namespace System.IO.Tests } } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false)] [InlineData(true)] public void EnumerateFileSystemInfos_LinksWithCycles_ThrowsTooManyLevelsOfSymbolicLinks(bool recurse) @@ -74,5 +117,16 @@ namespace System.IO.Tests Assert.Throws(() => testDirectory.GetFileSystemInfos("*", options).Count()); } } + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs new file mode 100644 index 00000000000..4c41fa7bb16 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.IO.Tests +{ + public class File_SymbolicLinks : BaseSymbolicLinks_FileSystem + { + protected override bool IsDirectoryTest => false; + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + File.Create(path).Dispose(); + } + else + { + Directory.CreateDirectory(path); + } + } + + protected override FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) => + File.CreateSymbolicLink(path, pathToTarget); + + protected override FileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) => + File.ResolveLinkTarget(linkPath, returnFinalTarget); + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.False(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is FileInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) => + Assert.True(link.Exists); + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs new file mode 100644 index 00000000000..fa334dd1ca3 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.IO.Tests +{ + public class FileInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo + { + protected override bool IsDirectoryTest => false; + + protected override FileSystemInfo GetFileSystemInfo(string path) => + new FileInfo(path); + + protected override void CreateFileOrDirectory(string path, bool createOpposite = false) + { + if (!createOpposite) + { + File.Create(path).Dispose(); + } + else + { + Directory.CreateDirectory(path); + } + } + + protected override void AssertIsCorrectTypeAndDirectoryAttribute(FileSystemInfo linkInfo) + { + if (linkInfo.Exists) + { + Assert.False(linkInfo.Attributes.HasFlag(FileAttributes.Directory)); + } + Assert.True(linkInfo is FileInfo); + } + + protected override void AssertLinkExists(FileSystemInfo link) => + Assert.True(link.Exists); + + [Fact] + public void ResolveLinkTarget_Throws_NotExists() => + ResolveLinkTarget_Throws_NotExists_Internal(); + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void CreateSymbolicLink_PathToTarget_RelativeToLinkPath() + { + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(false)).Dispose(); + RemoteExecutor.Invoke(() => CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(true)).Dispose(); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs index d82346f6f4f..9300660f141 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs @@ -55,32 +55,6 @@ namespace System.IO.Tests } } - /// - /// In some cases (such as when running without elevated privileges), - /// the symbolic link may fail to create. Only run this test if it creates - /// links successfully. - /// - protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; - - private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => - { - // Verify file symlink creation - string path = Path.GetTempFileName(); - string linkPath = path + ".link"; - bool success = MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false); - try { File.Delete(path); } catch { } - try { File.Delete(linkPath); } catch { } - - // Verify directory symlink creation - path = Path.GetTempFileName(); - linkPath = path + ".link"; - success = success && MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true); - try { Directory.Delete(path); } catch { } - try { Directory.Delete(linkPath); } catch { } - - return success; - }); - public static string GetNamedPipeServerStreamName() { if (PlatformDetection.IsInAppContainer) diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 5c4ca4d01d7..dd67a63fed1 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -22,10 +22,16 @@ + + + + + + diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 91135c4439c..0decd976d8e 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -14,13 +14,16 @@ - + + + + @@ -64,12 +67,14 @@ + + @@ -78,13 +83,20 @@ + + + + + + + @@ -137,6 +149,7 @@ + diff --git a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj index d3fd26f7079..d7becfa7963 100644 --- a/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj +++ b/src/libraries/System.Net.Ping/src/System.Net.Ping.csproj @@ -46,6 +46,8 @@ + + diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj index 86a7dacccae..90acc7605d8 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj @@ -19,6 +19,8 @@ Link="SocketCommon\Configuration.cs" /> + + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 05cc719ed05..ce9ef00df03 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2659,6 +2659,9 @@ BindHandle for ThreadPool failed on this handle. + + The link's file system entry type is inconsistent with that of its target: {0} + The file '{0}' already exists. @@ -2710,6 +2713,9 @@ The path '{0}' is too long, or a component of the specified path is too long. + + Too many levels of symbolic links in '{0}'. + [Unknown] diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d5c9d5ef37c..df96b372a88 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -422,6 +422,7 @@ + @@ -1428,9 +1429,15 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile_IntPtr.cs + + Common\Interop\Windows\Kernel32\Interop.CreateSymbolicLink.cs + Common\Interop\Windows\Kernel32\Interop.CriticalSection.cs + + Common\Interop\Windows\Kernel32\Interop.DeviceIoControl.cs + Common\Interop\Windows\Kernel32\Interop.ExpandEnvironmentStrings.cs @@ -1509,6 +1516,9 @@ Common\Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs + + Common\Interop\Windows\Kernel32\Interop.GetFinalPathNameByHandle.cs + Common\Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs @@ -1620,6 +1630,9 @@ Common\Interop\Windows\Kernel32\Interop.RemoveDirectory.cs + + Common\Interop\Windows\Kernel32\Interop.REPARSE_DATA_BUFFER.cs + Common\Interop\Windows\Kernel32\Interop.ReplaceFile.cs @@ -1893,6 +1906,9 @@ Common\Interop\Unix\Interop.Libraries.cs + + Common\Interop\Unix\Interop.DefaultPathBufferSize.cs + Common\Interop\Unix\System.Native\Interop.Access.cs @@ -2034,6 +2050,9 @@ Common\Interop\Unix\System.Native\Interop.Stat.Span.cs + + Common\Interop\Unix\System.Native\Interop.SymLink.cs + Common\Interop\Unix\System.Native\Interop.SysConf.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index 3fbf585db5d..f6bc4f42b06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -315,5 +315,47 @@ namespace System.IO { return FileSystem.GetLogicalDrives(); } + + /// + /// Creates a directory symbolic link identified by that points to . + /// + /// The absolute path where the symbolic link should be created. + /// The target directory of the symbolic link. + /// A instance that wraps the newly created directory symbolic link. + /// or is . + /// or is empty. + /// -or- + /// is not an absolute path. + /// -or- + /// or contains invalid path characters. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public static FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + string fullPath = Path.GetFullPath(path); + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + + FileSystem.CreateSymbolicLink(path, pathToTarget, isDirectory: true); + return new DirectoryInfo(originalPath: path, fullPath: fullPath, isNormalized: true); + } + + /// + /// Gets the target of the specified directory link. + /// + /// The path of the directory link. + /// to follow links to the final target; to return the immediate next link. + /// A instance if exists, independently if the target exists or not. if is not a link. + /// The directory on does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); + return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: true); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 626dc761e80..8480e46a094 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1007,5 +1007,45 @@ namespace System.IO ? Task.FromCanceled(cancellationToken) : InternalWriteAllLinesAsync(AsyncStreamWriter(path, encoding, append: true), contents, cancellationToken); } + + /// + /// Creates a file symbolic link identified by that points to . + /// + /// The path where the symbolic link should be created. + /// The path of the target to which the symbolic link points. + /// A instance that wraps the newly created file symbolic link. + /// or is . + /// or is empty. + /// -or- + /// or contains a null character. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public static FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) + { + string fullPath = Path.GetFullPath(path); + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + + FileSystem.CreateSymbolicLink(path, pathToTarget, isDirectory: false); + return new FileInfo(originalPath: path, fullPath: fullPath, isNormalized: true); + } + + /// + /// Gets the target of the specified file link. + /// + /// The path of the file link. + /// to follow links to the final target; to return the immediate next link. + /// A instance if exists, independently if the target exists or not. if is not a link. + /// The file on does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); + return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: false); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 9a759e7b2b0..0a4ef7cdef5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Text; namespace System.IO { @@ -11,6 +12,10 @@ namespace System.IO { internal const int DefaultBufferSize = 4096; + // On Linux, the maximum number of symbolic links that are followed while resolving a pathname is 40. + // See: https://man7.org/linux/man-pages/man7/path_resolution.7.html + private const int MaxFollowedLinks = 40; + public static void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) { // If the destination path points to a directory, we throw to match Windows behaviour @@ -529,5 +534,91 @@ namespace System.IO { return DriveInfoInternal.GetLogicalDrives(); } + + internal static string? GetLinkTarget(ReadOnlySpan linkPath, bool isDirectory) => Interop.Sys.ReadLink(linkPath); + + internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) + { + string pathToTargetFullPath = PathInternal.GetLinkTargetFullPath(path, pathToTarget); + + // Fail if the target exists but is not consistent with the expected filesystem entry type + if (Interop.Sys.Stat(pathToTargetFullPath, out Interop.Sys.FileStatus targetInfo) == 0) + { + if (isDirectory != ((targetInfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR)) + { + throw new IOException(SR.Format(SR.IO_InconsistentLinkType, path)); + } + } + + Interop.CheckIo(Interop.Sys.SymLink(pathToTarget, path), path, isDirectory); + } + + internal static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget, bool isDirectory) + { + ValueStringBuilder sb = new(Interop.DefaultPathBufferSize); + sb.Append(linkPath); + + string? linkTarget = GetLinkTarget(linkPath, isDirectory: false /* Irrelevant in Unix */); + if (linkTarget == null) + { + sb.Dispose(); + Interop.Error error = Interop.Sys.GetLastError(); + // Not a link, return null + if (error == Interop.Error.EINVAL) + { + return null; + } + + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(error), linkPath, isDirectory); + } + + if (!returnFinalTarget) + { + GetLinkTargetFullPath(ref sb, linkTarget); + } + else + { + string? current = linkTarget; + int visitCount = 1; + + while (current != null) + { + if (visitCount > MaxFollowedLinks) + { + sb.Dispose(); + // We went over the limit and couldn't reach the final target + throw new IOException(SR.Format(SR.IO_TooManySymbolicLinkLevels, linkPath)); + } + + GetLinkTargetFullPath(ref sb, current); + current = GetLinkTarget(sb.AsSpan(), isDirectory: false); + visitCount++; + } + } + + Debug.Assert(sb.Length > 0); + linkTarget = sb.ToString(); // ToString disposes + + return isDirectory ? + new DirectoryInfo(linkTarget) : + new FileInfo(linkTarget); + + // In case of link target being relative: + // Preserve the full path of the directory of the previous path + // so the final target is returned with a valid full path + static void GetLinkTargetFullPath(ref ValueStringBuilder sb, ReadOnlySpan linkTarget) + { + if (PathInternal.IsPartiallyQualified(linkTarget)) + { + sb.Length = Path.GetDirectoryNameOffset(sb.AsSpan()); + sb.Append(PathInternal.DirectorySeparatorChar); + } + else + { + sb.Length = 0; + } + sb.Append(linkTarget); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 5b5f9a86bc7..5f88f53a7c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.IO; using System.Text; +using System.Buffers; #if MS_IO_REDIST namespace Microsoft.IO @@ -185,7 +186,7 @@ namespace System.IO } Interop.Kernel32.WIN32_FIND_DATA findData = default; - GetFindData(fullPath, ref findData); + GetFindData(fullPath, isDirectory: true, ref findData); if (IsNameSurrogateReparsePoint(ref findData)) { // Don't recurse @@ -199,18 +200,16 @@ namespace System.IO RemoveDirectoryRecursive(fullPath, ref findData, topLevel: true); } - private static void GetFindData(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData) + private static void GetFindData(string fullPath, bool isDirectory, ref Interop.Kernel32.WIN32_FIND_DATA findData) { - using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.TrimEndingDirectorySeparator(fullPath), ref findData)) + using SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.TrimEndingDirectorySeparator(fullPath), ref findData); + if (handle.IsInvalid) { - if (handle.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - // File not found doesn't make much sense coming from a directory delete. - if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) - errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; - throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - } + int errorCode = Marshal.GetLastWin32Error(); + // File not found doesn't make much sense coming from a directory. + if (isDirectory && errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) + errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; + throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } } @@ -407,5 +406,225 @@ namespace System.IO public static string[] GetLogicalDrives() => DriveInfoInternal.GetLogicalDrives(); + + internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) + { + string pathToTargetFullPath = PathInternal.GetLinkTargetFullPath(path, pathToTarget); + + Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default; + int errorCode = FillAttributeInfo(pathToTargetFullPath, ref data, returnErrorOnNotFound: true); + if (errorCode == Interop.Errors.ERROR_SUCCESS && + data.dwFileAttributes != -1 && + isDirectory != ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0)) + { + throw new IOException(SR.Format(SR.IO_InconsistentLinkType, path)); + } + + Interop.Kernel32.CreateSymbolicLink(path, pathToTarget, isDirectory); + } + + internal static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget, bool isDirectory) + { + string? targetPath = returnFinalTarget ? + GetFinalLinkTarget(linkPath, isDirectory) : + GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: true, returnFullPath: true); + + return targetPath == null ? null : + isDirectory ? new DirectoryInfo(targetPath) : new FileInfo(targetPath); + } + + internal static string? GetLinkTarget(string linkPath, bool isDirectory) + => GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: false); + + /// + /// Gets reparse point information associated to . + /// + /// The immediate link target, absolute or relative or null if the file is not a supported link. + internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnUnreachable, bool returnFullPath) + { + using SafeFileHandle handle = OpenSafeFileHandle(linkPath, + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | + Interop.Kernel32.FileOperations.FILE_FLAG_OPEN_REPARSE_POINT); + + if (handle.IsInvalid) + { + int error = Marshal.GetLastWin32Error(); + + if (!throwOnUnreachable && IsPathUnreachableError(error)) + { + return null; + } + + // File not found doesn't make much sense coming from a directory. + if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND) + { + error = Interop.Errors.ERROR_PATH_NOT_FOUND; + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + byte[] buffer = ArrayPool.Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + try + { + bool success = Interop.Kernel32.DeviceIoControl( + handle, + dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT, + lpInBuffer: IntPtr.Zero, + nInBufferSize: 0, + lpOutBuffer: buffer, + nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + out _, + IntPtr.Zero); + + if (!success) + { + int error = Marshal.GetLastWin32Error(); + // The file or directory is not a reparse point. + if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT) + { + return null; + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + Span bufferSpan = new(buffer); + success = MemoryMarshal.TryRead(bufferSpan, out Interop.Kernel32.REPARSE_DATA_BUFFER rdb); + Debug.Assert(success); + + // Only symbolic links are supported at the moment. + if ((rdb.ReparseTag & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0) + { + return null; + } + + // We use PrintName instead of SubstitutneName given that we don't want to return a NT path when the link wasn't created with such NT path. + // Unlike SubstituteName and GetFinalPathNameByHandle(), PrintName doesn't start with a prefix. + // Another nuance is that SubstituteName does not contain redundant path segments while PrintName does. + // PrintName can ONLY return a NT path if the link was created explicitly targeting a file/folder in such way. e.g: mklink /D linkName \??\C:\path\to\target. + int printNameNameOffset = sizeof(Interop.Kernel32.REPARSE_DATA_BUFFER) + rdb.ReparseBufferSymbolicLink.PrintNameOffset; + int printNameNameLength = rdb.ReparseBufferSymbolicLink.PrintNameLength; + + Span targetPath = MemoryMarshal.Cast(bufferSpan.Slice(printNameNameOffset, printNameNameLength)); + Debug.Assert((rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) == 0 || !PathInternal.IsExtended(targetPath)); + + if (returnFullPath && (rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) != 0) + { + // Target path is relative and is for ResolveLinkTarget(), we need to append the link directory. + return Path.Join(Path.GetDirectoryName(linkPath.AsSpan()), targetPath); + } + + return targetPath.ToString(); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + private static unsafe string? GetFinalLinkTarget(string linkPath, bool isDirectory) + { + Interop.Kernel32.WIN32_FIND_DATA data = default; + GetFindData(linkPath, isDirectory, ref data); + + // The file or directory is not a reparse point. + if ((data.dwFileAttributes & (uint)FileAttributes.ReparsePoint) == 0 || + // Only symbolic links are supported at the moment. + (data.dwReserved0 & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0) + { + return null; + } + + // We try to open the final file since they asked for the final target. + using SafeFileHandle handle = OpenSafeFileHandle(linkPath, + Interop.Kernel32.FileOperations.OPEN_EXISTING | + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS); + + if (handle.IsInvalid) + { + // If the handle fails because it is unreachable, is because the link was broken. + // We need to fallback to manually traverse the links and return the target of the last resolved link. + int error = Marshal.GetLastWin32Error(); + if (IsPathUnreachableError(error)) + { + return GetFinalLinkTargetSlow(linkPath); + } + + throw Win32Marshal.GetExceptionForWin32Error(error, linkPath); + } + + const int InitialBufferSize = 4096; + char[] buffer = ArrayPool.Shared.Rent(InitialBufferSize); + try + { + uint result = GetFinalPathNameByHandle(handle, buffer); + + // If the function fails because lpszFilePath is too small to hold the string plus the terminating null character, + // the return value is the required buffer size, in TCHARs. This value includes the size of the terminating null character. + if (result > buffer.Length) + { + ArrayPool.Shared.Return(buffer); + buffer = ArrayPool.Shared.Rent((int)result); + + result = GetFinalPathNameByHandle(handle, buffer); + } + + // If the function fails for any other reason, the return value is zero. + if (result == 0) + { + throw Win32Marshal.GetExceptionForLastWin32Error(linkPath); + } + + Debug.Assert(PathInternal.IsExtended(new string(buffer, 0, (int)result).AsSpan())); + // GetFinalPathNameByHandle always returns with extended DOS prefix even if the link target was created without one. + // While this does not interfere with correct behavior, it might be unexpected. + // Hence we trim it if the passed-in path to the link wasn't extended. + int start = PathInternal.IsExtended(linkPath.AsSpan()) ? 0 : 4; + return new string(buffer, start, (int)result - start); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) + { + fixed (char* bufPtr = buffer) + { + return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); + } + } + + string? GetFinalLinkTargetSlow(string linkPath) + { + // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span. + // I am not sure if it's possible to change CreateFile's param to ROS and avoid all these allocations. + string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: true); + string? prev = null; + + while (current != null) + { + prev = current; + current = GetImmediateLinkTarget(current, isDirectory, throwOnUnreachable: false, returnFullPath: true); + } + + return prev; + } + } + + private static unsafe SafeFileHandle OpenSafeFileHandle(string path, int flags) + { + SafeFileHandle handle = Interop.Kernel32.CreateFile( + path, + dwDesiredAccess: 0, + FileShare.ReadWrite | FileShare.Delete, + lpSecurityAttributes: (Interop.Kernel32.SECURITY_ATTRIBUTES*)IntPtr.Zero, + FileMode.Open, + dwFlagsAndAttributes: flags, + hTemplateFile: IntPtr.Zero); + + return handle; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs new file mode 100644 index 00000000000..a1d506e9218 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if MS_IO_REDIST +using System; + +namespace Microsoft.IO +#else +namespace System.IO +#endif +{ + internal static partial class FileSystem + { + internal static void VerifyValidPath(string path, string argName) + { + if (path == null) + { + throw new ArgumentNullException(argName); + } + else if (path.Length == 0) + { + throw new ArgumentException(SR.Arg_PathEmpty, argName); + } + else if (path.Contains('\0')) + { + throw new ArgumentException(SR.Argument_InvalidPathChars, argName); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs index 9bcae393120..d59c90f2310 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs @@ -27,7 +27,7 @@ namespace System.IO return info; } - internal void Invalidate() => _fileStatus.InvalidateCaches(); + internal void InvalidateCore() => _fileStatus.InvalidateCaches(); internal unsafe void Init(ref FileStatus fileStatus) { @@ -63,7 +63,11 @@ namespace System.IO internal long LengthCore => _fileStatus.GetLength(FullPath); - public void Refresh() => _fileStatus.RefreshCaches(FullPath); + public void Refresh() + { + _linkTargetIsValid = false; + _fileStatus.RefreshCaches(FullPath); + } internal static void ThrowNotFound(string path) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs index 47d064e8fe1..bb144085173 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Windows.cs @@ -42,10 +42,7 @@ namespace System.IO return info; } - internal void Invalidate() - { - _dataInitialized = -1; - } + internal void InvalidateCore() => _dataInitialized = -1; internal unsafe void Init(Interop.NtDll.FILE_FULL_DIR_INFORMATION* info) { @@ -77,7 +74,7 @@ namespace System.IO get { if (_dataInitialized == -1) - Refresh(); + RefreshCore(); if (_dataInitialized != 0) { // Refresh was unable to initialize the data. @@ -145,7 +142,7 @@ namespace System.IO if (_dataInitialized == -1) { _data = default; - Refresh(); + RefreshCore(); } if (_dataInitialized != 0) // Refresh was unable to initialize the data @@ -153,6 +150,12 @@ namespace System.IO } public void Refresh() + { + _linkTargetIsValid = false; + RefreshCore(); + } + + private void RefreshCore() { // This should not throw, instead we store the result so that we can throw it // when someone actually accesses a property diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs index 98bf36b6fd0..3beed9e2563 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.cs @@ -19,11 +19,20 @@ namespace System.IO internal string _name = null!; // Fields initiated in derived classes + private string? _linkTarget; + private bool _linkTargetIsValid; + protected FileSystemInfo(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); } + internal void Invalidate() + { + _linkTargetIsValid = false; + InvalidateCore(); + } + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); @@ -107,6 +116,59 @@ namespace System.IO set => LastWriteTimeCore = File.GetUtcDateTimeOffset(value); } + /// + /// If this instance represents a link, returns the link target's path. + /// If a link does not exist in , or this instance does not represent a link, returns . + /// + public string? LinkTarget + { + get + { + if (_linkTargetIsValid) + { + return _linkTarget; + } + + _linkTarget = FileSystem.GetLinkTarget(FullPath, this is DirectoryInfo); + _linkTargetIsValid = true; + return _linkTarget; + } + } + + /// + /// Creates a symbolic link located in that points to the specified . + /// + /// The path of the symbolic link target. + /// is . + /// is empty. + /// -or- + /// This instance was not created passing an absolute path. + /// -or- + /// contains invalid path characters. + /// A file or directory already exists in the location of . + /// -or- + /// An I/O error occurred. + public void CreateAsSymbolicLink(string pathToTarget) + { + FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); + FileSystem.CreateSymbolicLink(OriginalPath, pathToTarget, this is DirectoryInfo); + Invalidate(); + } + + /// + /// Gets the target of the specified link. + /// + /// to follow links to the final target; to return the immediate next link. + /// A instance if the link exists, independently if the target exists or not; if this file or directory is not a link. + /// The file or directory does not exist. + /// -or- + /// The link's file system entry type is inconsistent with that of its target. + /// -or- + /// Too many levels of symbolic links. + /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. + public FileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) => + FileSystem.ResolveLinkTarget(FullPath, returnFinalTarget, this is DirectoryInfo); + /// /// Returns the original path. Use FullName or Name properties for the full path or file/directory name. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index 104704de7b7..cdc96b5f452 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -125,7 +125,7 @@ namespace System.IO return end >= 0 ? path.Slice(0, end) : ReadOnlySpan.Empty; } - private static int GetDirectoryNameOffset(ReadOnlySpan path) + internal static int GetDirectoryNameOffset(ReadOnlySpan path) { int rootLength = PathInternal.GetRootLength(path); int end = path.Length; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs index fcbeece65a7..ff9d79caee2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs @@ -245,5 +245,13 @@ namespace System.IO /// internal static bool EndsInDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]); + + internal static string GetLinkTargetFullPath(string path, string pathToTarget) + => IsPartiallyQualified(pathToTarget.AsSpan()) ? +#if MS_IO_REDIST + Path.Combine(Path.GetDirectoryName(path), pathToTarget) : pathToTarget; +#else + Path.Join(Path.GetDirectoryName(path.AsSpan()), pathToTarget.AsSpan()) : pathToTarget; +#endif } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 67adfd0c6d3..1b9b1d241b8 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10175,6 +10175,7 @@ namespace System.IO public static partial class Directory { public static System.IO.DirectoryInfo CreateDirectory(string path) { throw null; } + public static System.IO.FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) { throw null; } public static void Delete(string path) { } public static void Delete(string path, bool recursive) { } public static System.Collections.Generic.IEnumerable EnumerateDirectories(string path) { throw null; } @@ -10213,6 +10214,7 @@ namespace System.IO public static string[] GetLogicalDrives() { throw null; } public static System.IO.DirectoryInfo? GetParent(string path) { throw null; } public static void Move(string sourceDirName, string destDirName) { } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } public static void SetCurrentDirectory(string path) { } @@ -10237,10 +10239,13 @@ namespace System.IO public System.DateTime LastAccessTimeUtc { get { throw null; } set { } } public System.DateTime LastWriteTime { get { throw null; } set { } } public System.DateTime LastWriteTimeUtc { get { throw null; } set { } } + public string? LinkTarget { get { throw null; } } public abstract string Name { get; } + public void CreateAsSymbolicLink(string pathToTarget) { } public abstract void Delete(); public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public void Refresh() { } + public System.IO.FileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) { throw null; } public override string ToString() { throw null; } } public sealed partial class DirectoryInfo : System.IO.FileSystemInfo @@ -10325,6 +10330,7 @@ namespace System.IO public static System.IO.FileStream Create(string path) { throw null; } public static System.IO.FileStream Create(string path, int bufferSize) { throw null; } public static System.IO.FileStream Create(string path, int bufferSize, System.IO.FileOptions options) { throw null; } + public static System.IO.FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) { throw null; } public static System.IO.StreamWriter CreateText(string path) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static void Decrypt(string path) { } @@ -10363,6 +10369,7 @@ namespace System.IO public static System.Collections.Generic.IEnumerable ReadLines(string path, System.Text.Encoding encoding) { throw null; } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName) { } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) { } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } public static void SetAttributes(string path, System.IO.FileAttributes fileAttributes) { } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 2c0921f9fcd..505d487f1d4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -324,10 +324,14 @@ Link="Common\Interop\Unix\Interop.Libraries.cs" /> + + Date: Fri, 9 Jul 2021 13:48:27 +0200 Subject: [PATCH 373/926] use fstatfs to detect whether current file system supports shared locks for files opened for writing (#55256) --- .../Interop.MountPoints.FormatInfo.cs | 123 --------------- .../Interop.UnixFileSystemTypes.cs | 145 ++++++++++++++++++ .../Native/Unix/Common/pal_config.h.in | 2 + .../Native/Unix/System.Native/entrypoints.c | 1 + .../Native/Unix/System.Native/pal_io.c | 19 +++ .../Native/Unix/System.Native/pal_io.h | 5 + src/libraries/Native/Unix/configure.cmake | 22 +++ .../src/System.IO.FileSystem.DriveInfo.csproj | 2 + .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 49 +++++- .../System.Private.CoreLib.Shared.projitems | 3 + 10 files changed, 244 insertions(+), 127 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs index fb59b175346..05d74c84e78 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs @@ -22,129 +22,6 @@ internal static partial class Interop private const int MountPointFormatBufferSizeInBytes = 32; - /// - /// Internal FileSystem names and magic numbers taken from man(2) statfs - /// - /// - /// These value names MUST be kept in sync with those in GetDriveType below, - /// where this enum must be a subset of the GetDriveType list, with the enum - /// values here exactly matching a string there. - /// - internal enum UnixFileSystemTypes : long - { - adfs = 0xADF5, - affs = 0xADFF, - afs = 0x5346414F, - anoninode = 0x09041934, - aufs = 0x61756673, - autofs = 0x0187, - autofs4 = 0x6D4A556D, - befs = 0x42465331, - bdevfs = 0x62646576, - bfs = 0x1BADFACE, - binfmt_misc = 0x42494E4D, - bootfs = 0xA56D3FF9, - btrfs = 0x9123683E, - ceph = 0x00C36400, - cgroupfs = 0x0027E0EB, - cgroup2fs = 0x63677270, - cifs = 0xFF534D42, - coda = 0x73757245, - coherent = 0x012FF7B7, - configfs = 0x62656570, - cramfs = 0x28CD3D45, - debugfs = 0x64626720, - devfs = 0x1373, - devpts = 0x1CD1, - ecryptfs = 0xF15F, - efs = 0x00414A53, - exofs = 0x5DF5, - ext = 0x137D, - ext2_old = 0xEF51, - ext2 = 0xEF53, - ext3 = 0xEF53, - ext4 = 0xEF53, - fat = 0x4006, - fd = 0xF00D1E, - fhgfs = 0x19830326, - fuse = 0x65735546, - fuseblk = 0x65735546, - fusectl = 0x65735543, - futexfs = 0x0BAD1DEA, - gfsgfs2 = 0x1161970, - gfs2 = 0x01161970, - gpfs = 0x47504653, - hfs = 0x4244, - hfsplus = 0x482B, - hpfs = 0xF995E849, - hugetlbfs = 0x958458F6, - inodefs = 0x11307854, - inotifyfs = 0x2BAD1DEA, - isofs = 0x9660, - // isofs = 0x4004, // R_WIN - // isofs = 0x4000, // WIN - jffs = 0x07C0, - jffs2 = 0x72B6, - jfs = 0x3153464A, - kafs = 0x6B414653, - lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */ - logfs = 0xC97E8168, - lustre = 0x0BD00BD0, - minix_old = 0x137F, /* orig. minix */ - minix = 0x138F, /* 30 char minix */ - minix2 = 0x2468, /* minix V2 */ - minix2v2 = 0x2478, /* MINIX V2, 30 char names */ - minix3 = 0x4D5A, - mqueue = 0x19800202, - msdos = 0x4D44, - nfs = 0x6969, - nfsd = 0x6E667364, - nilfs = 0x3434, - novell = 0x564C, - ntfs = 0x5346544E, - openprom = 0x9FA1, - ocfs2 = 0x7461636F, - omfs = 0xC2993D87, - overlay = 0x794C7630, - overlayfs = 0x794C764F, - panfs = 0xAAD7AAEA, - pipefs = 0x50495045, - proc = 0x9FA0, - pstorefs = 0x6165676C, - qnx4 = 0x002F, - qnx6 = 0x68191122, - ramfs = 0x858458F6, - reiserfs = 0x52654973, - romfs = 0x7275, - rootfs = 0x53464846, - rpc_pipefs = 0x67596969, - samba = 0x517B, - securityfs = 0x73636673, - selinux = 0xF97CFF8C, - smb = 0x517B, - sockfs = 0x534F434B, - squashfs = 0x73717368, - sysfs = 0x62656572, - sysv2 = 0x012FF7B6, - sysv4 = 0x012FF7B5, - tmpfs = 0x01021994, - ubifs = 0x24051905, - udf = 0x15013346, - ufs = 0x00011954, - ufscigam = 0x54190100, // ufs byteswapped - ufs2 = 0x19540119, - usbdevice = 0x9FA2, - v9fs = 0x01021997, - vmhgfs = 0xBACBACBC, - vxfs = 0xA501FCF5, - vzfs = 0x565A4653, - xenfs = 0xABBA1974, - xenix = 0x012FF7B4, - xfs = 0x58465342, - xia = 0x012FD16D, - zfs = 0x2FC12FC1, - } - [StructLayout(LayoutKind.Sequential)] internal struct MountPointInformation { diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs new file mode 100644 index 00000000000..fd34f874182 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Internal FileSystem names and magic numbers taken from man(2) statfs + /// + /// + /// These value names MUST be kept in sync with those in GetDriveType (moved to Interop.MountPoints.FormatInfo.cs), + /// where this enum must be a subset of the GetDriveType list, with the enum + /// values here exactly matching a string there. + /// + internal enum UnixFileSystemTypes : long + { + adfs = 0xADF5, + affs = 0xADFF, + afs = 0x5346414F, + anoninode = 0x09041934, + aufs = 0x61756673, + autofs = 0x0187, + autofs4 = 0x6D4A556D, + befs = 0x42465331, + bdevfs = 0x62646576, + bfs = 0x1BADFACE, + binfmt_misc = 0x42494E4D, + bootfs = 0xA56D3FF9, + btrfs = 0x9123683E, + ceph = 0x00C36400, + cgroupfs = 0x0027E0EB, + cgroup2fs = 0x63677270, + cifs = 0xFF534D42, + coda = 0x73757245, + coherent = 0x012FF7B7, + configfs = 0x62656570, + cramfs = 0x28CD3D45, + debugfs = 0x64626720, + devfs = 0x1373, + devpts = 0x1CD1, + ecryptfs = 0xF15F, + efs = 0x00414A53, + exofs = 0x5DF5, + ext = 0x137D, + ext2_old = 0xEF51, + ext2 = 0xEF53, + ext3 = 0xEF53, + ext4 = 0xEF53, + fat = 0x4006, + fd = 0xF00D1E, + fhgfs = 0x19830326, + fuse = 0x65735546, + fuseblk = 0x65735546, + fusectl = 0x65735543, + futexfs = 0x0BAD1DEA, + gfsgfs2 = 0x1161970, + gfs2 = 0x01161970, + gpfs = 0x47504653, + hfs = 0x4244, + hfsplus = 0x482B, + hpfs = 0xF995E849, + hugetlbfs = 0x958458F6, + inodefs = 0x11307854, + inotifyfs = 0x2BAD1DEA, + isofs = 0x9660, + // isofs = 0x4004, // R_WIN + // isofs = 0x4000, // WIN + jffs = 0x07C0, + jffs2 = 0x72B6, + jfs = 0x3153464A, + kafs = 0x6B414653, + lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */ + logfs = 0xC97E8168, + lustre = 0x0BD00BD0, + minix_old = 0x137F, /* orig. minix */ + minix = 0x138F, /* 30 char minix */ + minix2 = 0x2468, /* minix V2 */ + minix2v2 = 0x2478, /* MINIX V2, 30 char names */ + minix3 = 0x4D5A, + mqueue = 0x19800202, + msdos = 0x4D44, + nfs = 0x6969, + nfsd = 0x6E667364, + nilfs = 0x3434, + novell = 0x564C, + ntfs = 0x5346544E, + openprom = 0x9FA1, + ocfs2 = 0x7461636F, + omfs = 0xC2993D87, + overlay = 0x794C7630, + overlayfs = 0x794C764F, + panfs = 0xAAD7AAEA, + pipefs = 0x50495045, + proc = 0x9FA0, + pstorefs = 0x6165676C, + qnx4 = 0x002F, + qnx6 = 0x68191122, + ramfs = 0x858458F6, + reiserfs = 0x52654973, + romfs = 0x7275, + rootfs = 0x53464846, + rpc_pipefs = 0x67596969, + samba = 0x517B, + securityfs = 0x73636673, + selinux = 0xF97CFF8C, + smb = 0x517B, + smb2 = 0xFE534D42, + sockfs = 0x534F434B, + squashfs = 0x73717368, + sysfs = 0x62656572, + sysv2 = 0x012FF7B6, + sysv4 = 0x012FF7B5, + tmpfs = 0x01021994, + ubifs = 0x24051905, + udf = 0x15013346, + ufs = 0x00011954, + ufscigam = 0x54190100, // ufs byteswapped + ufs2 = 0x19540119, + usbdevice = 0x9FA2, + v9fs = 0x01021997, + vmhgfs = 0xBACBACBC, + vxfs = 0xA501FCF5, + vzfs = 0x565A4653, + xenfs = 0xABBA1974, + xenix = 0x012FF7B4, + xfs = 0x58465342, + xia = 0x012FD16D, + zfs = 0x2FC12FC1, + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetFileSystemType")] + private static extern long GetFileSystemType(SafeFileHandle fd); + + internal static bool TryGetFileSystemType(SafeFileHandle fd, out UnixFileSystemTypes fileSystemType) + { + long fstatfsResult = GetFileSystemType(fd); + fileSystemType = (UnixFileSystemTypes)fstatfsResult; + return fstatfsResult != -1; + } + } +} diff --git a/src/libraries/Native/Unix/Common/pal_config.h.in b/src/libraries/Native/Unix/Common/pal_config.h.in index 2b86ed15d4c..9a026105341 100644 --- a/src/libraries/Native/Unix/Common/pal_config.h.in +++ b/src/libraries/Native/Unix/Common/pal_config.h.in @@ -5,6 +5,8 @@ #cmakedefine01 HAVE_MMAP64 #cmakedefine01 HAVE_FTRUNCATE64 #cmakedefine01 HAVE_POSIX_FADVISE64 +#cmakedefine01 HAVE_STATFS_VFS +#cmakedefine01 HAVE_STATFS_MOUNT #cmakedefine01 HAVE_FLOCK64 #cmakedefine01 HAVE_F_DUPFD_CLOEXEC #cmakedefine01 HAVE_F_FULLFSYNC diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index f39360810b5..bf6f98953f9 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -104,6 +104,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_INotifyRemoveWatch) DllImportEntry(SystemNative_RealPath) DllImportEntry(SystemNative_GetPeerID) + DllImportEntry(SystemNative_GetFileSystemType) DllImportEntry(SystemNative_LockFileRegion) DllImportEntry(SystemNative_LChflags) DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 9e02aa7c2e6..59b8f2770c1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -35,6 +35,11 @@ #if HAVE_INOTIFY #include #endif +#if HAVE_STATFS_VFS // Linux +#include +#elif HAVE_STATFS_MOUNT // BSD +#include +#endif #ifdef _AIX #include @@ -1386,6 +1391,20 @@ static int16_t ConvertLockType(int16_t managedLockType) } } +int64_t SystemNative_GetFileSystemType(intptr_t fd) +{ +#if HAVE_STATFS_VFS || HAVE_STATFS_MOUNT + int statfsRes; + struct statfs statfsArgs; + // for our needs (get file system type) statfs is always enough and there is no need to use statfs64 + // which got deprecated in macOS 10.6, in favor of statfs + while ((statfsRes = fstatfs(ToFileDescriptor(fd), &statfsArgs)) == -1 && errno == EINTR) ; + return statfsRes == -1 ? (int64_t)-1 : (int64_t)statfsArgs.f_type; +#else + #error "Platform doesn't support fstatfs" +#endif +} + int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType) { int16_t unixLockType = ConvertLockType(lockType); diff --git a/src/libraries/Native/Unix/System.Native/pal_io.h b/src/libraries/Native/Unix/System.Native/pal_io.h index 6461881a91e..3f0db054d43 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.h +++ b/src/libraries/Native/Unix/System.Native/pal_io.h @@ -717,6 +717,11 @@ PALEXPORT char* SystemNative_RealPath(const char* path); */ PALEXPORT int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid); +/** +* Returns file system type on success, or -1 on error. +*/ +PALEXPORT int64_t SystemNative_GetFileSystemType(intptr_t fd); + /** * Attempts to lock/unlock the region of the file "fd" specified by the offset and length. lockType * can be set to F_UNLCK (2) for unlock or F_WRLCK (3) for lock. diff --git a/src/libraries/Native/Unix/configure.cmake b/src/libraries/Native/Unix/configure.cmake index b2cfdb3bc40..18afacc2dbc 100644 --- a/src/libraries/Native/Unix/configure.cmake +++ b/src/libraries/Native/Unix/configure.cmake @@ -108,6 +108,28 @@ check_c_source_compiles( # /in_pktinfo +check_c_source_compiles( + " + #include + int main(void) + { + struct statfs s; + return 0; + } + " + HAVE_STATFS_VFS) + +check_c_source_compiles( + " + #include + int main(void) + { + struct statfs s; + return 0; + } + " + HAVE_STATFS_MOUNT) + check_c_source_compiles( " #include diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj index c516eb543e3..d07e89ec979 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj @@ -56,6 +56,8 @@ Link="Common\Interop\Unix\Interop.Libraries.cs" /> + = 0)) { // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or @@ -320,6 +327,40 @@ namespace Microsoft.Win32.SafeHandles } } + private bool CanLockTheFile(Interop.Sys.LockOperations lockOperation, FileAccess access) + { + Debug.Assert(lockOperation == Interop.Sys.LockOperations.LOCK_EX || lockOperation == Interop.Sys.LockOperations.LOCK_SH); + + if (DisableFileLocking) + { + return false; + } + else if (lockOperation == Interop.Sys.LockOperations.LOCK_EX) + { + return true; // LOCK_EX is always OK + } + else if ((access & FileAccess.Write) == 0) + { + return true; // LOCK_SH is always OK when reading + } + + if (!Interop.Sys.TryGetFileSystemType(this, out Interop.Sys.UnixFileSystemTypes unixFileSystemType)) + { + return false; // assume we should not acquire the lock if we don't know the File System + } + + switch (unixFileSystemType) + { + case Interop.Sys.UnixFileSystemTypes.nfs: // #44546 + case Interop.Sys.UnixFileSystemTypes.smb: + case Interop.Sys.UnixFileSystemTypes.smb2: // #53182 + case Interop.Sys.UnixFileSystemTypes.cifs: + return false; // LOCK_SH is not OK when writing to NFS, CIFS or SMB + default: + return true; // in all other situations it should be OK + } + } + private bool GetCanSeek() { Debug.Assert(!IsClosed); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index df96b372a88..a1e270dbde9 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1927,6 +1927,9 @@ Common\Interop\Unix\System.Native\Interop.ErrNo.cs + + Common\Interop\Unix\System.Native\Interop.UnixFileSystemTypes.cs + Common\Interop\Unix\System.Native\Interop.FLock.cs From cb1beaac6fc314b0a91a79179184fe88f2fa601b Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 9 Jul 2021 15:19:54 +0300 Subject: [PATCH 374/926] Remove redundant pinvokes in S.N.Http/Mail on tvOS (#54812) Addressed xamarin/xamarin-macios#12011 (comment) --- .../src/System/Net/NTAuthentication.Common.cs | 2 ++ .../src/System.Net.Http.csproj | 33 ++++++++++++------- .../AuthenticationHelper.NtAuth.tvOS.cs | 30 +++++++++++++++++ .../src/System.Net.Mail.csproj | 18 ++++++---- 4 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs diff --git a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs index b98f2711831..148d1dc9a07 100644 --- a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs @@ -5,10 +5,12 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Security.Authentication.ExtendedProtection; namespace System.Net { + [UnsupportedOSPlatform("tvos")] internal sealed partial class NTAuthentication { private bool _isServer; diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 613416b90aa..bea0e12f9fe 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -143,7 +143,7 @@ - + @@ -192,7 +192,7 @@ - @@ -324,28 +324,37 @@ Link="Common\System\Net\Security\Unix\SafeFreeCredentials.cs" /> - - - - - + + + + + + + + + + + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs new file mode 100644 index 00000000000..86b2eacbb18 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.tvOS.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + internal static partial class AuthenticationHelper + { + private static Task InnerSendAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + return isProxyAuth ? + SendWithProxyAuthAsync(request, authUri, async, credentials, true, connectionPool, cancellationToken).AsTask() : + SendWithRequestAuthAsync(request, async, credentials, true, connectionPool, cancellationToken).AsTask(); + } + + public static Task SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, bool async, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + return InnerSendAsync(request, proxyUri, async, proxyCredentials, isProxyAuth: true, connection, connectionPool, cancellationToken); + } + + public static Task SendWithNtConnectionAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) + { + Debug.Assert(request.RequestUri != null); + return InnerSendAsync(request, request.RequestUri, async, credentials, isProxyAuth: false, connection, connectionPool, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj index b3350746d81..a90d602bead 100644 --- a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj +++ b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj @@ -153,16 +153,22 @@ Link="Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs" /> + + + + + - - - + + + From 3319fa811bae3dedfbc952a2d7ed258886df7504 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 9 Jul 2021 16:01:33 +0300 Subject: [PATCH 375/926] Make LambdaCompiler prefer interpretation to compilation on iOS (#54970) This is an attempt to address #47112 based on Egor's suggestion. The changes: - set IsInterpreting MSBuild property to 'true' in case of iOS; - made System.Linq.Expressions.ExpressionCreator class internal static in order not to introduce new public type; otherwise it throws Type 'System.Linq.Expressions.ExpressionCreator' does not exist in the reference but it does exist in the implementation. -updated the iOS simulator functional test for AOT to verify the fix - added PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly which checks whether the System.Linq.Expressions is built with IsInterpreting property set to true. To do so, it uses reflection to verify the Expression.Accept method doesn't exist. - disabled some failing library tests using ActiveIssue + PlatformDetection. - updated the invariant functional test for the iOS simulator (b/c of Allow restricting cultures creation with any arbitrary names in Globalization Invariant Mode #54247) Co-authored-by: Stephen Toub --- .../TestUtilities/System/PlatformDetection.cs | 17 +++++++++ .../tests/NewLateBindingTests.cs | 2 +- ...rmance.dynamic.context.indexer.genclass.cs | 1 + ...dynamic.context.operator.compound.basic.cs | 2 ++ ...ic.declarations.formalParameter.Methods.cs | 36 +++++++++++++++++++ ...eclarations.localVariable.blockVariable.cs | 3 ++ ...ormance.dynamic.dynamicType.conversions.cs | 3 ++ ...loadResolution.Indexers.1class2indexers.cs | 3 ++ ...erloadResolution.Methods.1class2methods.cs | 3 ++ .../Conformance.dynamic.Variance.assign.cs | 3 ++ .../tests/System.Dynamic.Runtime.Tests.csproj | 2 +- .../src/System.Linq.Expressions.csproj | 3 +- .../Linq/Expressions/LambdaExpression.cs | 2 +- .../tests/Array/ArrayArrayIndexTests.cs | 1 - .../tests/Array/ArrayIndexTests.cs | 1 - .../tests/Array/NullableArrayIndexTests.cs | 1 - .../Arithmetic/BinaryModuloTests.cs | 1 - .../tests/Block/NoParameterBlockTests.cs | 1 - .../tests/Cast/CastTests.cs | 1 - .../tests/Dynamic/InvokeMemberBindingTests.cs | 2 ++ .../tests/Member/MemberAccessTests.cs | 1 - .../tests/SequenceTests/SequenceTests.cs | 1 - .../System.Linq.Expressions.Tests.csproj | 3 +- .../tests/TypeBinary/TypeIs.cs | 1 - .../tests/Unary/UnaryConvertTests.cs | 1 - .../tests/Unary/UnaryUnboxTests.cs | 1 - src/libraries/tests.proj | 3 ++ .../iOS/Simulator/AOT/Program.cs | 3 +- .../InvariantCultureOnlyMode/Program.cs | 12 +++++-- .../Simulator/LambdaCompilerAOT/Program.cs | 28 +++++++++++++++ ...OS.Simulator.LambdaCompilerAot.Test.csproj | 19 ++++++++++ 31 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index fc829692c03..5038c4f0197 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; +using System.Linq.Expressions; using System.Security; using System.Security.Authentication; using System.Reflection; @@ -66,6 +67,22 @@ namespace System public static bool IsUsingLimitedCultures => !IsNotMobile; public static bool IsNotUsingLimitedCultures => IsNotMobile; + public static bool IsLinqExpressionsBuiltWithIsInterpretingOnly => s_LinqExpressionsBuiltWithIsInterpretingOnly.Value; + public static bool IsNotLinqExpressionsBuiltWithIsInterpretingOnly => !IsLinqExpressionsBuiltWithIsInterpretingOnly; + private static readonly Lazy s_LinqExpressionsBuiltWithIsInterpretingOnly = new Lazy(GetLinqExpressionsBuiltWithIsInterpretingOnly); + private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() + { + Type type = typeof(LambdaExpression); + if (type != null) + { + // The "Accept" method is under FEATURE_COMPILE conditional so it should not exist + MethodInfo methodInfo = type.GetMethod("Accept", BindingFlags.NonPublic | BindingFlags.Static); + return methodInfo == null; + } + + return false; + } + // Please make sure that you have the libgdiplus dependency installed. // For details, see https://docs.microsoft.com/dotnet/core/install/dependencies?pivots=os-macos&tabs=netcore31#libgdiplus public static bool IsDrawingSupported diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs index 5437590241a..38b21a9eb11 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs @@ -94,7 +94,7 @@ namespace Microsoft.VisualBasic.CompilerServices.Tests static object[] CreateData(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) => new object[] { memberName, arguments, typeArguments, expectedValue }; } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51834", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] [MemberData(nameof(LateCall_OptionalValues_Data))] public void LateCall_OptionalValues(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs index a31ab30422e..79d677b3c2a 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs @@ -774,6 +774,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.indexer.genclas public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55118", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void TryCatchFinally() { dynamic dy = new MemberClass(); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs index 0c49adf396b..381b89b8198 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs @@ -239,10 +239,12 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.operate.compoun // // // + using System; public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs index f6ceeddfa49..6306863d44e 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs @@ -98,6 +98,8 @@ public class MyClass namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.extensionmethod006.extensionmethod006 { + using System; + static // Extension method that extends dynamic // // @@ -115,6 +117,7 @@ public class MyClass public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -189,6 +192,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method001.method001 { + using System; + public class Test { private static bool s_ok = false; @@ -203,6 +208,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -260,6 +266,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method003.method003 { + using System; + public class Test { private static bool s_ok = false; @@ -273,6 +281,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -294,6 +303,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method004.method004 { + using System; + public class Test { private static bool s_ok = false; @@ -306,6 +317,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -330,6 +342,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method005.method005 { + using System; + public class Test { private static bool s_ok = false; @@ -345,6 +359,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -367,6 +382,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method007.method007 { + using System; + public class Test { private static bool s_ok = false; @@ -380,6 +397,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -403,6 +421,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method008.method008 { + using System; + public class Test { private static bool s_ok = false; @@ -416,6 +436,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -438,6 +459,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method009.method009 { + using System; + public class Test { private static bool s_ok = false; @@ -451,6 +474,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -473,6 +497,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method010.method010 { + using System; + public class Test { private static bool s_ok = false; @@ -488,6 +514,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -509,6 +536,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method011.method011 { + using System; + public class Test { private static bool s_ok = false; @@ -523,6 +552,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -543,6 +573,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method012.method012 { + using System; + public class Test { private static bool s_ok = false; @@ -555,6 +587,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -579,6 +612,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method013.method013 { + using System; + public class Test { private static bool s_ok = false; @@ -591,6 +626,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalPara } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs index 1aa3f5b55af..46c8372b14a 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs @@ -1189,9 +1189,12 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.localVaria namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.localVariable.blockVariable.trycatch002.trycatch002 { + using System; + public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs index d27e05bff28..cb63fac7995 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs @@ -4356,6 +4356,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions.dlgate003.dlgate003 { + using System; + // Delegate conversions // // Tests to figure out if the right conversion from method groups to delegates are applied @@ -4375,6 +4377,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs index 4a3ef8ebc82..5b08d4e447e 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs @@ -665,6 +665,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Inde namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Indexers.Oneclass2indexers.onedynamicparam004.onedynamicparam004 { + using System; + // Tests overload resolution for 1 class and 2 methods // // @@ -720,6 +722,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Inde public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs index 5cf0a1ffe9d..72f5998f656 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs @@ -501,6 +501,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Meth namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Methods.Oneclass2methods.onedynamicparam004.onedynamicparam004 { + using System; + // Tests overload resolution for 1 class and 2 methods // // @@ -530,6 +532,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Meth public class Test { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs index 7e277d276d4..d9dd1adb356 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs @@ -476,6 +476,8 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.Variance.assign.assignm namespace ManagedTests.DynamicCSharp.Conformance.dynamic.Variance.assign.assignment07.assignment07 { + using System; + // variance // assignment Contravariant delegates // contravariance on delegates assigned to arrays @@ -499,6 +501,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.Variance.assign.assignm private static dynamic s_array3; [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55119", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj b/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj index 9b70ef3b077..57195bfe5aa 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj +++ b/src/libraries/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj @@ -2,7 +2,7 @@ true 67,168,219,414,162,184,458,464,78,169,114,693,108,1981,649,109,1066,3021,3026,3002,3014,3022,660,661,429;xUnit1013 - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst diff --git a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj index 7972cdb54f1..0a9d7eba08b 100644 --- a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj +++ b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj @@ -1,9 +1,10 @@ true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst enable false + true $(DefineConstants);FEATURE_DLG_INVOKE;FEATURE_FAST_CREATE $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs index a615e89056a..0c591d6f3ae 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs @@ -327,7 +327,7 @@ namespace System.Linq.Expressions #if !FEATURE_COMPILE // Separate expression creation class to hide the CreateExpressionFunc function from users reflecting on Expression - public class ExpressionCreator + internal static class ExpressionCreator { public static Expression CreateExpressionFunc(Expression body, string? name, bool tailCall, ReadOnlyCollection parameters) { diff --git a/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs index 9430253687a..1bd056bba9a 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/ArrayArrayIndexTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class ArrayArrayIndexTests { #region Boolean tests diff --git a/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs index 6eed3897f93..47079261232 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/ArrayIndexTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class ArrayIndexTests { #region Boolean tests diff --git a/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs b/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs index e0932eef784..91754a96bc2 100644 --- a/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Array/NullableArrayIndexTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class NullableArrayIndexTests { #region NullableBool tests diff --git a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs index 4873b7769c3..c1a41f30861 100644 --- a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Arithmetic/BinaryModuloTests.cs @@ -107,7 +107,6 @@ namespace System.Linq.Expressions.Tests [Theory] [ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void CheckLongModuloTest(bool useInterpreter) { long[] array = new long[] { 0, 1, -1, long.MinValue, long.MaxValue }; diff --git a/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs b/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs index 9848e3a86f9..4891d348c29 100644 --- a/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Block/NoParameterBlockTests.cs @@ -292,7 +292,6 @@ namespace System.Linq.Expressions.Tests [Theory] [MemberData(nameof(ConstantValuesAndSizes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public void ResultPropertyFromEnumerable(object value, int blockSize) { ConstantExpression constant = Expression.Constant(value, value.GetType()); diff --git a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs index bb83b061ec6..090878f8fee 100644 --- a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs @@ -7,7 +7,6 @@ using Xunit; namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class CastTests { #region Test methods diff --git a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs index f5abaf81ae1..ee4962af5ff 100644 --- a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs @@ -54,6 +54,7 @@ namespace System.Dynamic.Tests yield return new[] {new object()}; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55070", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] [Theory, MemberData(nameof(ObjectArguments))] public void InvokeVirtualMethod(object value) { @@ -411,6 +412,7 @@ namespace System.Dynamic.Tests public OutAction OutDelegate; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55071", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] [Fact] public void InvokeFuncMember() { diff --git a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs index 497416457ed..cca9a0e08c8 100644 --- a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs @@ -8,7 +8,6 @@ using Xunit; namespace System.Linq.Expressions.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static class MemberAccessTests { private class UnreadableIndexableClass diff --git a/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs b/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs index 44f50b5bdf8..871721f81f4 100644 --- a/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs @@ -1486,7 +1486,6 @@ namespace System.Linq.Expressions.Tests [Theory] [ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void ConvertNullToInt(bool useInterpreter) { Assert.Throws(() => diff --git a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj index b0a74eabbce..c2efe998ee9 100644 --- a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj +++ b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj @@ -2,9 +2,10 @@ true false + true $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-MacCatalyst diff --git a/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs b/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs index f789d71f3b0..26edcd31eff 100644 --- a/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs +++ b/src/libraries/System.Linq.Expressions/tests/TypeBinary/TypeIs.cs @@ -57,7 +57,6 @@ namespace System.Linq.Expressions.Tests [Theory] [MemberData(nameof(ExpressionAndTypeCombinations))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public void TypePropertyMatches(Expression expression, Type type) { Assert.Equal(type, Expression.TypeIs(expression, type).TypeOperand); diff --git a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs index 4cf5c45f2df..d0b82224e67 100644 --- a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryConvertTests.cs @@ -57,7 +57,6 @@ namespace System.Linq.Expressions.Tests } [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void ConvertNullToNonNullableValueTest(bool useInterpreter) { foreach (var e in ConvertNullToNonNullableValue()) diff --git a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs index 184828d0ff4..2ec61247eab 100644 --- a/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Unary/UnaryUnboxTests.cs @@ -10,7 +10,6 @@ namespace System.Linq.Expressions.Tests #region Test methods [Theory, ClassData(typeof(CompilationTypes))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51952", TestPlatforms.tvOS)] public static void CheckUnaryUnboxTest(bool useInterpreter) { VerifyUnbox(42, typeof(int), false, useInterpreter); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 1460b3be141..b1f20442aac 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -205,6 +205,9 @@ + + + diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs index 437f6843a36..b82804305a1 100644 --- a/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs +++ b/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Runtime.InteropServices; public static class Program { @@ -14,6 +14,7 @@ public static class Program public static async Task Main(string[] args) { mono_ios_set_summary($"Starting functional test"); + Console.WriteLine("Done!"); await Task.Delay(5000); diff --git a/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs index c5221ce4bc3..93dd7fc3c73 100644 --- a/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs +++ b/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode/Program.cs @@ -15,10 +15,18 @@ public static class Program public static async Task Main(string[] args) { mono_ios_set_summary($"Starting functional test"); + CultureInfo culture; + try + { + culture = new CultureInfo("es-ES", false); + } + catch + { + culture = new CultureInfo("", false); + } - var culture = new CultureInfo("es-ES", false); // https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#cultures-and-culture-data - int result = culture.LCID == 0x1000 && culture.NativeName == "Invariant Language (Invariant Country)" ? 42 : 1; + int result = culture.LCID == CultureInfo.InvariantCulture.LCID && culture.NativeName == "Invariant Language (Invariant Country)" ? 42 : 1; Console.WriteLine("Done!"); await Task.Delay(5000); diff --git a/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs new file mode 100644 index 00000000000..df03d367acb --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/Program.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +public static class Program +{ + [DllImport("__Internal")] + public static extern void mono_ios_set_summary (string value); + + public static async Task Main(string[] args) + { + mono_ios_set_summary($"Starting functional test"); + + // https://github.com/dotnet/runtime/issues/47112 + var foos = new string [] { "hi", "bye" }; + string f = foos.AsQueryable ().First (); + + Console.WriteLine("Done!"); + await Task.Delay(5000); + + return 42; + } +} diff --git a/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj new file mode 100644 index 00000000000..b2dc64908e2 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT/iOS.Simulator.LambdaCompilerAot.Test.csproj @@ -0,0 +1,19 @@ + + + + Exe + false + true + true + $(NetCoreAppCurrent) + iOSSimulator + iOS.Simulator.LambdaCompilerAot.Test.dll + false + 42 + true + + + + + + From 8b42b7fa31c7c1a55b1b36213db9ef6f562ed61e Mon Sep 17 00:00:00 2001 From: Mansoor Saqib Date: Fri, 9 Jul 2021 07:21:01 -0700 Subject: [PATCH 376/926] Optimize index bounds check for immutable sorted set and list (#53266) * Optimize index bounds check for immutable sorted set and list - The bounds check to determine if a given index is >= 0 and < this.Count is only necessary on the first call to ItemRef. - The recursive steps within ItemRef do not need to continuously do this bounds check on these immutable data structures. - Proof: Elimination of index >= 0 bounds check: The first call to ItemRef checks if index >= 0. If we recurse on the left node, the index value does not change. If we recurse on the right node, index > _left._count. Then index - _left._count - 1 >= 0. Elimination of index < this.Count: The first call to ItemRef checks if index < this.Count. Then the given index must lie somewhere in this tree and (**) index < this.Count == left.Count + right.Count + 1. If we recurse on the left node, the index value does not change and a check is already made to determine that index < _left.Count. If we recurse on the right node, then we need to be sure that index - _left.count - 1 < _right.Count. But this is just a rearrangement of (**). * Remove redundant code * Remove redundant assert * Apply suggestions from code review Change from internal to private for unchecked methods. Co-authored-by: Stephen Toub Co-authored-by: Stephen Toub --- .../Collections/Immutable/ImmutableList_1.Node.cs | 9 +++++++-- .../Collections/Immutable/ImmutableSortedSet_1.Node.cs | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs index ed4ee0a12f0..78480e7d044 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs @@ -195,15 +195,20 @@ namespace System.Collections.Immutable { Requires.Range(index >= 0 && index < this.Count, nameof(index)); + return ref ItemRefUnchecked(index); + } + + private ref readonly T ItemRefUnchecked(int index) + { Debug.Assert(_left != null && _right != null); if (index < _left._count) { - return ref _left.ItemRef(index); + return ref _left.ItemRefUnchecked(index); } if (index > _left._count) { - return ref _right.ItemRef(index - _left._count - 1); + return ref _right.ItemRefUnchecked(index - _left._count - 1); } return ref _key; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs index aea4caff1be..c1c53978e4b 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs @@ -260,16 +260,22 @@ namespace System.Collections.Immutable internal ref readonly T ItemRef(int index) { Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + return ref ItemRefUnchecked(index); + } + + private ref readonly T ItemRefUnchecked(int index) + { Debug.Assert(_left != null && _right != null); if (index < _left._count) { - return ref _left.ItemRef(index); + return ref _left.ItemRefUnchecked(index); } if (index > _left._count) { - return ref _right.ItemRef(index - _left._count - 1); + return ref _right.ItemRefUnchecked(index - _left._count - 1); } return ref _key; From 784ddf83e79dd0d95664f12e67422ade046345cb Mon Sep 17 00:00:00 2001 From: petrogavriluk Date: Fri, 9 Jul 2021 17:24:27 +0300 Subject: [PATCH 377/926] Fix incorrect comparison (#54834) (#55094) Add test to check fix. Checking comparison result against 1 or -1 causes problems when custom comparer is used. As a result Min and Max properties return incorrect values. --- .../Collections/Generic/SortedSet.TreeSubSet.cs | 4 ++-- .../Generic/SortedSet/SortedSet.Generic.cs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs index 2786b1d4d4e..945c5b03a43 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs @@ -132,7 +132,7 @@ namespace System.Collections.Generic { int comp = _lBoundActive ? Comparer.Compare(_min, current.Item) : -1; - if (comp == 1) + if (comp > 0) { current = current.Right; } @@ -161,7 +161,7 @@ namespace System.Collections.Generic while (current != null) { int comp = _uBoundActive ? Comparer.Compare(_max, current.Item) : 1; - if (comp == -1) + if (comp < 0) { current = current.Left; } diff --git a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs index 80daed73197..5e94a196c74 100644 --- a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs +++ b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs @@ -130,6 +130,23 @@ namespace System.Collections.Tests { return new SortedSet(new Comparer_SameAsDefaultComparer()); } + + [Fact] + public void SortedSet_Generic_GetViewBetween_MinMax_WithCustomComparer() + { + var set = (SortedSet)CreateSortedSet(new[] { 5, 15, 25, 35, 45 }, 5, 5); + + for (int i = 0; i <= 40; i += 10) + { + for (int j = i + 10; j <= 50; j += 10) + { + SortedSet view = set.GetViewBetween(i, j); + + Assert.Equal(i + 5, view.Min); + Assert.Equal(j - 5, view.Max); + } + } + } } [OuterLoop] From 7cfa95b98db757288895751b256c5558fdb59e37 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 9 Jul 2021 07:30:28 -0700 Subject: [PATCH 378/926] Shim gss api on Linux to delay loading libgssapi_krb5.so (#55037) * shim one function for starters * do all functions * drop static dependency on libgssapi_krb5.so * remap all gss method calls * keep one lib instance open. * tolerate absence of API in `gss_indicate_mechs`. It may be used for API probing. * init from a static constructor * move the lib name definition to the shim * no indirection * do not try optimizing multiple initialization attempts --- ...terop.NetSecurityNative.IsNtlmInstalled.cs | 24 ++++ .../System.Net.Security.Native/entrypoints.c | 1 + .../extra_libs.cmake | 8 +- .../System.Net.Security.Native/pal_gssapi.c | 111 ++++++++++++++++++ .../System.Net.Security.Native/pal_gssapi.h | 6 + 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs index 79edbf6adad..24b5b9ad5a7 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs @@ -10,5 +10,29 @@ internal static partial class Interop { [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_IsNtlmInstalled")] internal static extern bool IsNtlmInstalled(); + + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")] + private static extern int EnsureGssInitialized(); + + static NetSecurityNative() + { + GssInitializer.Initialize(); + } + + internal static class GssInitializer + { + static GssInitializer() + { + if (EnsureGssInitialized() != 0) + { + throw new InvalidOperationException(); + } + } + + internal static void Initialize() + { + // No-op that exists to provide a hook for other static constructors. + } + } } } diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c index ac702e90400..56d2bde8ce6 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c @@ -13,6 +13,7 @@ static const Entry s_securityNative[] = DllImportEntry(NetSecurityNative_DeleteSecContext) DllImportEntry(NetSecurityNative_DisplayMajorStatus) DllImportEntry(NetSecurityNative_DisplayMinorStatus) + DllImportEntry(NetSecurityNative_EnsureGssInitialized) DllImportEntry(NetSecurityNative_GetUser) DllImportEntry(NetSecurityNative_ImportPrincipalName) DllImportEntry(NetSecurityNative_ImportUserName) diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake index 49c6ff49610..0744078474b 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake @@ -19,5 +19,11 @@ macro(append_extra_security_libs NativeLibsExtra) endif() endif() - list(APPEND ${NativeLibsExtra} ${LIBGSS}) + if(CLR_CMAKE_TARGET_LINUX) + # On Linux libgssapi_krb5.so is loaded on demand to tolerate its absense in singlefile apps that do not use it + list(APPEND ${NativeLibsExtra} dl) + add_definitions(-DGSS_SHIM) + else() + list(APPEND ${NativeLibsExtra} ${LIBGSS}) + endif() endmacro() diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 9628318b773..2a37649f56c 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -20,6 +20,11 @@ #include #include +#if defined(GSS_SHIM) +#include +#include "pal_atomic.h" +#endif + c_static_assert(PAL_GSS_C_DELEG_FLAG == GSS_C_DELEG_FLAG); c_static_assert(PAL_GSS_C_MUTUAL_FLAG == GSS_C_MUTUAL_FLAG); c_static_assert(PAL_GSS_C_REPLAY_FLAG == GSS_C_REPLAY_FLAG); @@ -48,6 +53,103 @@ static gss_OID_desc gss_mech_ntlm_OID_desc = {.length = ARRAY_SIZE(gss_ntlm_oid_ .elements = gss_ntlm_oid_value}; #endif +#if defined(GSS_SHIM) + +#define FOR_ALL_GSS_FUNCTIONS \ + PER_FUNCTION_BLOCK(gss_accept_sec_context) \ + PER_FUNCTION_BLOCK(gss_acquire_cred) \ + PER_FUNCTION_BLOCK(gss_acquire_cred_with_password) \ + PER_FUNCTION_BLOCK(gss_delete_sec_context) \ + PER_FUNCTION_BLOCK(gss_display_name) \ + PER_FUNCTION_BLOCK(gss_display_status) \ + PER_FUNCTION_BLOCK(gss_import_name) \ + PER_FUNCTION_BLOCK(gss_indicate_mechs) \ + PER_FUNCTION_BLOCK(gss_init_sec_context) \ + PER_FUNCTION_BLOCK(gss_inquire_context) \ + PER_FUNCTION_BLOCK(gss_mech_krb5) \ + PER_FUNCTION_BLOCK(gss_oid_equal) \ + PER_FUNCTION_BLOCK(gss_release_buffer) \ + PER_FUNCTION_BLOCK(gss_release_cred) \ + PER_FUNCTION_BLOCK(gss_release_name) \ + PER_FUNCTION_BLOCK(gss_release_oid_set) \ + PER_FUNCTION_BLOCK(gss_unwrap) \ + PER_FUNCTION_BLOCK(gss_wrap) \ + PER_FUNCTION_BLOCK(GSS_C_NT_USER_NAME) \ + PER_FUNCTION_BLOCK(GSS_C_NT_HOSTBASED_SERVICE) + +#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + +#define FOR_ALL_GSS_FUNCTIONS FOR_ALL_GSS_FUNCTIONS \ + PER_FUNCTION_BLOCK(gss_set_cred_option) + +#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + +// define indirection pointers for all functions, like +// static TYPEOF(gss_accept_sec_context)* gss_accept_sec_context_ptr; +#define PER_FUNCTION_BLOCK(fn) \ +static TYPEOF(fn)* fn##_ptr; + +FOR_ALL_GSS_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +static void* volatile s_gssLib = NULL; + +// remap gss function use to use indirection pointers +#define gss_accept_sec_context(...) gss_accept_sec_context_ptr(__VA_ARGS__) +#define gss_acquire_cred(...) gss_acquire_cred_ptr(__VA_ARGS__) +#define gss_acquire_cred_with_password(...) gss_acquire_cred_with_password_ptr(__VA_ARGS__) +#define gss_delete_sec_context(...) gss_delete_sec_context_ptr(__VA_ARGS__) +#define gss_display_name(...) gss_display_name_ptr(__VA_ARGS__) +#define gss_display_status(...) gss_display_status_ptr(__VA_ARGS__) +#define gss_import_name(...) gss_import_name_ptr(__VA_ARGS__) +#define gss_indicate_mechs(...) gss_indicate_mechs_ptr(__VA_ARGS__) +#define gss_init_sec_context(...) gss_init_sec_context_ptr(__VA_ARGS__) +#define gss_inquire_context(...) gss_inquire_context_ptr(__VA_ARGS__) +#define gss_oid_equal(...) gss_oid_equal_ptr(__VA_ARGS__) +#define gss_release_buffer(...) gss_release_buffer_ptr(__VA_ARGS__) +#define gss_release_cred(...) gss_release_cred_ptr(__VA_ARGS__) +#define gss_release_name(...) gss_release_name_ptr(__VA_ARGS__) +#define gss_release_oid_set(...) gss_release_oid_set_ptr(__VA_ARGS__) +#define gss_unwrap(...) gss_unwrap_ptr(__VA_ARGS__) +#define gss_wrap(...) gss_wrap_ptr(__VA_ARGS__) + +#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X +#define gss_set_cred_option(...) gss_set_cred_option_ptr(__VA_ARGS__) +#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X + + +#define GSS_C_NT_USER_NAME (*GSS_C_NT_USER_NAME_ptr) +#define GSS_C_NT_HOSTBASED_SERVICE (*GSS_C_NT_HOSTBASED_SERVICE_ptr) +#define gss_mech_krb5 (*gss_mech_krb5_ptr) + +#define gss_lib_name "libgssapi_krb5.so" + +static int32_t ensure_gss_shim_initialized() +{ + void* lib = dlopen(gss_lib_name, RTLD_LAZY); + if (lib == NULL) { fprintf(stderr, "Cannot load library %s \nError: %s\n", gss_lib_name, dlerror()); return -1; } + + // check is someone else has opened and published s_gssLib already + if (!pal_atomic_cas_ptr(&s_gssLib, lib, NULL)) + { + dlclose(lib); + } + + // initialize indirection pointers for all functions, like: + // gss_accept_sec_context_ptr = (TYPEOF(gss_accept_sec_context)*)dlsym(s_gssLib, "gss_accept_sec_context"); + // if (gss_accept_sec_context_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from %s \nError: %s\n", "gss_accept_sec_context", gss_lib_name, dlerror()); return -1; } +#define PER_FUNCTION_BLOCK(fn) \ + fn##_ptr = (TYPEOF(fn)*)dlsym(s_gssLib, #fn); \ + if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from %s \nError: %s\n", gss_lib_name, dlerror()); return -1; } + + FOR_ALL_GSS_FUNCTIONS +#undef PER_FUNCTION_BLOCK + + return 0; +} + +#endif // GSS_SHIM + // transfers ownership of the underlying data from gssBuffer to PAL_GssBuffer static void NetSecurityNative_MoveBuffer(gss_buffer_t gssBuffer, PAL_GssBuffer* targetBuffer) { @@ -567,3 +669,12 @@ uint32_t NetSecurityNative_IsNtlmInstalled() return foundNtlm; } + +int32_t NetSecurityNative_EnsureGssInitialized() +{ +#if defined(GSS_SHIM) + return ensure_gss_shim_initialized(); +#else + return 0; +#endif +} diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 5c1fe55163e..b270569c388 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -193,3 +193,9 @@ Shims gss_inquire_context and gss_display_name to get the remote user principal PALEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, GssCtxId* contextHandle, PAL_GssBuffer* outBuffer); + +/* +Performs initialization of GSS shim, if necessary. +Return value 0 indicates a success. +*/ +PALEXPORT int32_t NetSecurityNative_EnsureGssInitialized(void); From a423f69e61e59eaa64eff348d8838a1226724972 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 9 Jul 2021 10:30:47 -0400 Subject: [PATCH 379/926] Use RetryHelper with AIA tests (#55230) * Use RetryHelper with AIA tests. The AIA tests depend on many external factors including the underlying operation system, network, and the resources available on the system. To accomodate this we will re-run the tests to improve reliability. * Narrow the use of RetryHelper --- .../tests/RevocationTests/AiaTests.cs | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index 0a2e8b78cff..88cf0c20b8c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -29,22 +29,26 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests using (CertificateAuthority intermediate1 = intermediates[0]) using (CertificateAuthority intermediate2 = intermediates[1]) using (endEntity) - using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 intermediate2Cert = intermediate2.CloneIssuerCert()) { responder.RespondEmpty = true; - X509Chain chain = holder.Chain; - chain.ChainPolicy.ExtraStore.Add(intermediate2Cert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + { + X509Chain chain = holder.Chain; + chain.ChainPolicy.ExtraStore.Add(intermediate2Cert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - Assert.False(chain.Build(endEntity)); - X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); - Assert.True(chainFlags.HasFlag(X509ChainStatusFlags.PartialChain), $"expected partial chain flags, got {chainFlags}"); - Assert.Equal(2, chain.ChainElements.Count); + Assert.False(chain.Build(endEntity)); + X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); + Assert.True(chainFlags.HasFlag(X509ChainStatusFlags.PartialChain), $"expected partial chain flags, got {chainFlags}"); + Assert.Equal(2, chain.ChainElements.Count); + } + }); } } @@ -65,72 +69,76 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests using (root) using (intermediate) using (endEntity) - using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = root.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediate.CloneIssuerCert()) - using (var cuCaStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) { - cuCaStore.Open(OpenFlags.ReadWrite); - - X509Chain chain = holder.Chain; - - // macOS combines revocation and AIA fetching in to a single flag. Both need to be disabled - // to prevent AIA fetches. - if (PlatformDetection.IsOSX) - { - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - } - - chain.ChainPolicy.DisableCertificateDownloads = true; - chain.ChainPolicy.CustomTrustStore.Add(rootCert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; - - Assert.False(chain.Build(endEntity), "Chain build with no intermediate, AIA disabled"); - - // If a previous run of this test leaves contamination in the CU\CA store on Windows - // the Windows chain engine will match the bad issuer and report NotSignatureValid instead - // of PartialChain. - X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); - - if (chainFlags.HasFlag(X509ChainStatusFlags.NotSignatureValid)) - { - Assert.Equal(3, chain.ChainElements.Count); - - foreach (X509Certificate2 storeCert in cuCaStore.Certificates) + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + using (var cuCaStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) { - if (storeCert.Subject.Equals(intermediateCert.Subject)) + cuCaStore.Open(OpenFlags.ReadWrite); + + X509Chain chain = holder.Chain; + + // macOS combines revocation and AIA fetching in to a single flag. Both need to be disabled + // to prevent AIA fetches. + if (PlatformDetection.IsOSX) { - cuCaStore.Remove(storeCert); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; } - storeCert.Dispose(); + chain.ChainPolicy.DisableCertificateDownloads = true; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; + + Assert.False(chain.Build(endEntity), "Chain build with no intermediate, AIA disabled"); + + // If a previous run of this test leaves contamination in the CU\CA store on Windows + // the Windows chain engine will match the bad issuer and report NotSignatureValid instead + // of PartialChain. + X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); + + if (chainFlags.HasFlag(X509ChainStatusFlags.NotSignatureValid)) + { + Assert.Equal(3, chain.ChainElements.Count); + + foreach (X509Certificate2 storeCert in cuCaStore.Certificates) + { + if (storeCert.Subject.Equals(intermediateCert.Subject)) + { + cuCaStore.Remove(storeCert); + } + + storeCert.Dispose(); + } + + holder.DisposeChainElements(); + + // Try again, with no caching side effect. + Assert.False(chain.Build(endEntity), "Chain build 2 with no intermediate, AIA disabled"); + } + + Assert.Equal(1, chain.ChainElements.Count); + Assert.Contains(X509ChainStatusFlags.PartialChain, chain.ChainStatus.Select(s => s.Status)); + holder.DisposeChainElements(); + + chain.ChainPolicy.ExtraStore.Add(intermediateCert); + Assert.True(chain.Build(endEntity), "Chain build with intermediate, AIA disabled"); + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); + holder.DisposeChainElements(); + + chain.ChainPolicy.DisableCertificateDownloads = false; + chain.ChainPolicy.ExtraStore.Clear(); + Assert.True(chain.Build(endEntity), "Chain build with no intermediate, AIA enabled"); + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); + + cuCaStore.Remove(intermediateCert); } - - holder.DisposeChainElements(); - - // Try again, with no caching side effect. - Assert.False(chain.Build(endEntity), "Chain build 2 with no intermediate, AIA disabled"); - } - - Assert.Equal(1, chain.ChainElements.Count); - Assert.Contains(X509ChainStatusFlags.PartialChain, chain.ChainStatus.Select(s => s.Status)); - holder.DisposeChainElements(); - - chain.ChainPolicy.ExtraStore.Add(intermediateCert); - Assert.True(chain.Build(endEntity), "Chain build with intermediate, AIA disabled"); - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); - holder.DisposeChainElements(); - - chain.ChainPolicy.DisableCertificateDownloads = false; - chain.ChainPolicy.ExtraStore.Clear(); - Assert.True(chain.Build(endEntity), "Chain build with no intermediate, AIA enabled"); - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); - - cuCaStore.Remove(intermediateCert); + }); } } } From e4ca022605758b914ca7202d26d6516eb25167ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 9 Jul 2021 17:04:39 +0200 Subject: [PATCH 380/926] Fix compiling System.Net.Security.Native with HAVE_HEIMDAL_HEADERS (#54682) We are using malloc/free but don't declare a header. It likely happens to be included from GSS/GSS.h which is why it builds now, but not from the Heimdal headers. --- .../Native/Unix/System.Net.Security.Native/pal_gssapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 2a37649f56c..d5b0e0bcdda 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -19,6 +19,7 @@ #include #include +#include #if defined(GSS_SHIM) #include From 03eb03e5754f52328c9450439736d276dc591e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 9 Jul 2021 11:37:28 -0400 Subject: [PATCH 381/926] Build all tasks, regardless of target (#55346) In a dirty tree, different architectures require different tasks. For example, ``` ./build.sh --os iossimulator -c Release ./build.sh --os android -c Release ``` the second step will complain that the AndroidAppBuilder task does not exist, because the build-semaphore.txt was created during the first build which built the AppleAppBuilder, but not the AndroidAppBuilder. The solution is to build all the tasks, regardless of target Simplifies building for different targets from the same dirty tree. All the tasks are always built once and then remain unchanged as long as their sources don't change. --- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 2 +- src/tasks/tasks.proj | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 9bd0cd7d0e7..0b20d27f388 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -161,7 +161,7 @@ public class AppleAppBuilderTask : Task throw new ArgumentException($"MainLibraryFileName='{MainLibraryFileName}' was not found in AppDir='{AppDir}'"); } - if (ProjectName.Contains(" ")) + if (ProjectName.Contains(' ')) { throw new ArgumentException($"ProjectName='{ProjectName}' should not contain spaces"); } diff --git a/src/tasks/tasks.proj b/src/tasks/tasks.proj index e681cfd2740..9c62f8b76b9 100644 --- a/src/tasks/tasks.proj +++ b/src/tasks/tasks.proj @@ -1,18 +1,6 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -8044,6 +8968,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst index 285e9101c63..4ac4fe405d9 100644 --- a/src/coreclr/vm/ClrEtwAllMeta.lst +++ b/src/coreclr/vm/ClrEtwAllMeta.lst @@ -633,3 +633,33 @@ nomac:StressLogTask:::StressLogEvent_V1 # StackWalk events ################## nomac:CLRStackStress:::CLRStackWalkStress + +################################# +# Events from the Mono profiler provider +################################# +nostack::MonoProfiler::ExceptionClause +nostack::MonoProfiler::MonoProfilerMethodEnter +nostack::MonoProfiler::MonoProfilerMethodLeave +nostack::MonoProfiler::MonoProfilerMethodTailCall +nostack::MonoProfiler::MonoProfilerMethodExceptionLeave +nostack::MonoProfiler::MonoProfilerMethodFree +nostack::MonoProfiler::MonoProfilerMethodBeginInvoke +nostack::MonoProfiler::MonoProfilerMethodEndInvoke +nostack::MonoProfiler::MonoProfilerGCEvent +nostack::MonoProfiler::MonoProfilerGCMoves +nostack::MonoProfiler::MonoProfilerGCResize +nostack::MonoProfiler::MonoProfilerGCFinalizing +nostack::MonoProfiler::MonoProfilerGCFinalized +nostack::MonoProfiler::MonoProfilerGCFinalizingObject +nostack::MonoProfiler::MonoProfilerGCFinalizedObject +nostack::MonoProfiler::MonoProfilerGCRootRegister +nostack::MonoProfiler::MonoProfilerGCRootUnregister +nostack::MonoProfiler::MonoProfilerGCRoots +nostack::MonoProfiler::MonoProfilerGCHeapDumpStart +nostack::MonoProfiler::MonoProfilerGCHeapDumpStop +nostack::MonoProfiler::MonoProfilerGCHeapDumpObjectReference +nostack::MonoProfiler::MonoProfilerThreadStarted +nostack::MonoProfiler::MonoProfilerThreadStopping +nostack::MonoProfiler::MonoProfilerThreadStopped +nostack::MonoProfiler::MonoProfilerThreadExited +nostack::MonoProfiler::MonoProfilerThreadName \ No newline at end of file diff --git a/src/mono/mono/component/event_pipe.c b/src/mono/mono/component/event_pipe.c index 6ed6171875a..24aa0952ce7 100644 --- a/src/mono/mono/component/event_pipe.c +++ b/src/mono/mono/component/event_pipe.c @@ -11,6 +11,8 @@ #include #include +static bool _event_pipe_component_inited = false; + struct _EventPipeProviderConfigurationNative { gunichar2 *provider_name; uint64_t keywords; @@ -284,5 +286,11 @@ event_pipe_thread_ctrl_activity_id ( MonoComponentEventPipe * mono_component_event_pipe_init (void) { + if (!_event_pipe_component_inited) { + extern void ep_rt_mono_component_init (void); + ep_rt_mono_component_init (); + _event_pipe_component_inited = true; + } + return &fn_table; } diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 9a83d80061b..683d94a61bb 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -16,11 +16,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include "mono/utils/mono-logger-internals.h" #include #include @@ -49,8 +53,11 @@ char *_ep_rt_mono_managed_cmd_line = NULL; static GArray * _ep_rt_mono_sampled_thread_callstacks = NULL; static uint32_t _ep_rt_mono_max_sampled_thread_count = 32; -// Mono profiler. -static MonoProfilerHandle _ep_rt_mono_profiler = NULL; +// Mono profilers. +static MonoProfilerHandle _ep_rt_default_profiler = NULL; +static MonoProfilerHandle _ep_rt_dotnet_runtime_profiler_provider = NULL; +static MonoProfilerHandle _ep_rt_dotnet_mono_profiler_provider = NULL; +static MonoCallSpec _ep_rt_dotnet_mono_profiler_provider_callspec = {0}; // Phantom JIT compile method. MonoMethod *_ep_rt_mono_runtime_helper_compile_method = NULL; @@ -196,6 +203,38 @@ typedef struct _AssemblyEventData AssemblyEventData; #define EXCEPTION_THROWN_FLAGS_IS_CSE 0x8 #define EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT 0x10 +// Provider keyword flags. +#define GC_KEYWORD 0x1 +#define GC_HANDLE_KEYWORD 0x2 +#define LOADER_KEYWORD 0x8 +#define JIT_KEYWORD 0x10 +#define APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD 0x800 +#define CONTENTION_KEYWORD 0x4000 +#define EXCEPTION_KEYWORD 0x8000 +#define THREADING_KEYWORD 0x10000 +#define GC_ALLOCATION_KEYWORD 0x200000 +#define GC_MOVES_KEYWORD 0x400000 +#define GC_ROOT_KEYWORD 0x800000 +#define GC_FINALIZATION_KEYWORD 0x1000000 +#define GC_RESIZE_KEYWORD 0x2000000 +#define METHOD_TRACING_KEYWORD 0x20000000 +#define TYPE_DIAGNOSTIC_KEYWORD 0x8000000000 +#define TYPE_LOADING_KEYWORD 0x8000000000 +#define MONITOR_KEYWORD 0x10000000000 +#define METHOD_INSTRUMENTATION_KEYWORD 0x40000000000 + +// GC provider types. + +typedef struct _GCObjectAddressData { + MonoObject *object; + void *address; +} GCObjectAddressData; + +typedef struct _GCAddressObjectData { + void *address; + MonoObject *object; +} GCAddressObjectData; + /* * Forward declares of all static functions. */ @@ -303,6 +342,10 @@ profiler_eventpipe_thread_exited ( MonoProfiler *prof, uintptr_t tid); +static +bool +parse_mono_profiler_options (const ep_char8_t *option); + static bool get_module_event_data ( @@ -328,86 +371,86 @@ get_exception_ip_func ( static void -profiler_jit_begin ( +runtime_profiler_jit_begin ( MonoProfiler *prof, MonoMethod *method); static void -profiler_jit_failed ( +runtime_profiler_jit_failed ( MonoProfiler *prof, MonoMethod *method); static void -profiler_jit_done ( +runtime_profiler_jit_done ( MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji); static void -profiler_image_loaded ( +runtime_profiler_image_loaded ( MonoProfiler *prof, MonoImage *image); static void -profiler_image_unloaded ( +runtime_profiler_image_unloaded ( MonoProfiler *prof, MonoImage *image); static void -profiler_assembly_loaded ( +runtime_profiler_assembly_loaded ( MonoProfiler *prof, MonoAssembly *assembly); static void -profiler_assembly_unloaded ( +runtime_profiler_assembly_unloaded ( MonoProfiler *prof, MonoAssembly *assembly); static void -profiler_thread_started ( +runtime_profiler_thread_started ( MonoProfiler *prof, uintptr_t tid); static void -profiler_thread_stopped ( +runtime_profiler_thread_stopped ( MonoProfiler *prof, uintptr_t tid); static void -profiler_class_loading ( +runtime_profiler_class_loading ( MonoProfiler *prof, MonoClass *klass); static void -profiler_class_failed ( +runtime_profiler_class_failed ( MonoProfiler *prof, MonoClass *klass); static void -profiler_class_loaded ( +runtime_profiler_class_loaded ( MonoProfiler *prof, MonoClass *klass); static void -profiler_exception_throw ( +runtime_profiler_exception_throw ( MonoProfiler *prof, MonoObject *exception); static void -profiler_exception_clause ( +runtime_profiler_exception_clause ( MonoProfiler *prof, MonoMethod *method, uint32_t clause_num, @@ -416,35 +459,390 @@ profiler_exception_clause ( static void -profiler_monitor_contention ( +runtime_profiler_monitor_contention ( MonoProfiler *prof, MonoObject *obj); static void -profiler_monitor_acquired ( +runtime_profiler_monitor_acquired ( MonoProfiler *prof, MonoObject *obj); static void -profiler_monitor_failed ( +runtime_profiler_monitor_failed ( MonoProfiler *prof, MonoObject *obj); static void -profiler_jit_code_buffer ( +runtime_profiler_jit_code_buffer ( MonoProfiler *prof, const mono_byte *buffer, uint64_t size, MonoProfilerCodeBufferType type, const void *data); +static +void +mono_profiler_app_domain_loading ( + MonoProfiler *prof, + MonoDomain *domain); + +static +void +mono_profiler_app_domain_loaded ( + MonoProfiler *prof, + MonoDomain *domain); + +static +void +mono_profiler_app_domain_unloading ( + MonoProfiler *prof, + MonoDomain *domain); + +static +void +mono_profiler_app_domain_unloaded ( + MonoProfiler *prof, + MonoDomain *domain); + +static +void +mono_profiler_app_domain_name ( + MonoProfiler *prof, + MonoDomain *domain, + const char *name); + +static +void +mono_profiler_jit_begin ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +mono_profiler_jit_failed ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +mono_profiler_jit_done ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji); + +static +void +mono_profiler_jit_chunk_created ( + MonoProfiler *prof, + const mono_byte *chunk, + uintptr_t size); + +static +void +mono_profiler_jit_chunk_destroyed ( + MonoProfiler *prof, + const mono_byte *chunk); + +static +void +mono_profiler_jit_code_buffer ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data); + +static +void +mono_profiler_class_loading ( + MonoProfiler *prof, + MonoClass *klass); + +static +void +mono_profiler_class_failed ( + MonoProfiler *prof, + MonoClass *klass); + +static +void +mono_profiler_class_loaded ( + MonoProfiler *prof, + MonoClass *klass); + +static +void +mono_profiler_vtable_loading ( + MonoProfiler *prof, + MonoVTable *vtable); + +static +void +mono_profiler_vtable_failed ( + MonoProfiler *prof, + MonoVTable *vtable); + +static +void +mono_profiler_vtable_loaded ( + MonoProfiler *prof, + MonoVTable *vtable); + +static +void +mono_profiler_module_loading ( + MonoProfiler *prof, + MonoImage *image); + +static +void +mono_profiler_module_failed ( + MonoProfiler *prof, + MonoImage *image); + +static +void +mono_profiler_module_loaded ( + MonoProfiler *prof, + MonoImage *image); + +static +void +mono_profiler_module_unloading ( + MonoProfiler *prof, + MonoImage *image); + +static +void +mono_profiler_module_unloaded ( + MonoProfiler *prof, + MonoImage *image); + +static +void +mono_profiler_assembly_loading ( + MonoProfiler *prof, + MonoAssembly *assembly); + +static +void +mono_profiler_assembly_loaded ( + MonoProfiler *prof, + MonoAssembly *assembly); + +static +void +mono_profiler_assembly_unloading ( + MonoProfiler *prof, + MonoAssembly *assembly); + +static +void +mono_profiler_assembly_unloaded ( + MonoProfiler *prof, + MonoAssembly *assembly); + +static +void +mono_profiler_method_enter ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context); + +static +void +mono_profiler_method_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context); + +static +void +mono_profiler_method_tail_call ( + MonoProfiler *prof, + MonoMethod *method, + MonoMethod *target_method); + +static +void +mono_profiler_method_exception_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoObject *exc); + +static +void +mono_profiler_method_free ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +mono_profiler_method_begin_invoke ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +mono_profiler_method_end_invoke ( + MonoProfiler *prof, + MonoMethod *method); + +static +MonoProfilerCallInstrumentationFlags +mono_profiler_method_instrumentation ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +mono_profiler_exception_throw ( + MonoProfiler *prof, + MonoObject *exc); + +static +void +mono_profiler_exception_clause ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc); + +static +void +mono_profiler_gc_event ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial); + +static +void +mono_profiler_gc_allocation ( + MonoProfiler *prof, + MonoObject *object); + +static +void +mono_profiler_gc_moves ( + MonoProfiler *prof, + MonoObject *const* objects, + uint64_t count); + +static +void +mono_profiler_gc_resize ( + MonoProfiler *prof, + uintptr_t size); + +static +void +mono_profiler_gc_handle_created ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type, + MonoObject * object); + +static +void +mono_profiler_gc_handle_deleted ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type); + +static +void +mono_profiler_gc_finalizing (MonoProfiler *prof); + +static +void +mono_profiler_gc_finalized (MonoProfiler *prof); + +static +void +mono_profiler_gc_root_register ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name); + +static +void +mono_profiler_gc_root_unregister ( + MonoProfiler *prof, + const mono_byte *start); + +static +void +mono_profiler_gc_roots ( + MonoProfiler *prof, + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects); + +static +void +mono_profiler_monitor_contention ( + MonoProfiler *prof, + MonoObject *object); + +static +void +mono_profiler_monitor_failed ( + MonoProfiler *prof, + MonoObject *object); + +static +void +mono_profiler_monitor_acquired ( + MonoProfiler *prof, + MonoObject *object); + +static +void +mono_profiler_thread_started ( + MonoProfiler *prof, + uintptr_t tid); + +static +void +mono_profiler_thread_stopping ( + MonoProfiler *prof, + uintptr_t tid); + +static +void +mono_profiler_thread_stopped ( + MonoProfiler *prof, + uintptr_t tid); + +static +void +mono_profiler_thread_exited ( + MonoProfiler *prof, + uintptr_t tid); + +static +void +mono_profiler_thread_name ( + MonoProfiler *prof, + uintptr_t tid, + const char *name); + /* * Forward declares of all private functions (accessed using extern in ep-rt-mono.h). */ +void +ep_rt_mono_component_init (void); + void ep_rt_mono_init (void); @@ -526,6 +924,14 @@ ep_rt_mono_method_get_full_name ( void ep_rt_mono_execute_rundown (ep_rt_execution_checkpoint_array_t *execution_checkpoints); +static +inline +bool +profiler_callback_is_enabled (uint64_t enabled_keywords, uint64_t keyword) +{ + return (enabled_keywords & keyword) == keyword; +} + static inline uint16_t @@ -1173,6 +1579,87 @@ profiler_eventpipe_thread_exited ( ep_rt_mono_thread_exited (); } +static bool +parse_mono_profiler_options (const ep_char8_t *option) +{ + do { + if (!*option) + return false; + + if (!strncmp (option, "alloc", 5)) { + mono_profiler_enable_allocations (); + option += 5; + } else if (!strncmp (option, "exception", 9)) { + mono_profiler_enable_clauses (); + option += 9; + /*} else if (!strncmp (option, "sample", 6)) { + mono_profiler_enable_sampling (_ep_rt_dotnet_mono_profiler_provider); + option += 6;*/ + } else { + return false; + } + + if (*option == ',') + option++; + } while (*option); + + return true; +} + +void +ep_rt_mono_component_init (void) +{ + _ep_rt_default_profiler = mono_profiler_create (NULL); + _ep_rt_dotnet_runtime_profiler_provider = mono_profiler_create (NULL); + _ep_rt_dotnet_mono_profiler_provider = mono_profiler_create (NULL); + + char *diag_env = g_getenv("MONO_DIAGNOSTICS"); + if (diag_env) { + int diag_argc = 1; + char **diag_argv = g_new (char *, 1); + if (diag_argv) { + diag_argv [0] = NULL; + if (!mono_parse_options_from (diag_env, &diag_argc, &diag_argv)) { + for (int i = 0; i < diag_argc; ++i) { + if (diag_argv [i]) { + if (strncmp (diag_argv [i], "--diagnostic-mono-profiler=", 27) == 0) { + if (!parse_mono_profiler_options (diag_argv [i] + 27)) + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable option: %s", diag_argv [i]); + } else if (strncmp (diag_argv [i], "--diagnostic-mono-profiler-callspec=", 36) == 0) { + char *errstr = NULL; + if (!mono_callspec_parse (diag_argv [i] + 36, &_ep_rt_dotnet_mono_profiler_provider_callspec, &errstr)) { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing '%s': %s", diag_argv [i], errstr); + g_free (errstr); + mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); + } else { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); + } + } else if (strncmp (diag_argv [i], "--diagnostic-ports=", 19) == 0) { + char *diag_ports_env = g_getenv("DOTNET_DiagnosticPorts"); + if (diag_ports_env) + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DIAGNOSTICS, "DOTNET_DiagnosticPorts environment variable already set, ignoring --diagnostic-ports used in MONO_DIAGNOSTICS environment variable"); + else + g_setenv ("DOTNET_DiagnosticPorts", diag_argv [i] + 19, TRUE); + g_free (diag_ports_env); + + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable, unknown option: %s", diag_argv [i]); + } + + g_free (diag_argv [i]); + diag_argv [i] = NULL; + } + } + + g_free (diag_argv); + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable"); + } + } + } + g_free (diag_env); +} + void ep_rt_mono_init (void) { @@ -1184,8 +1671,11 @@ ep_rt_mono_init (void) _ep_rt_mono_initialized = TRUE; - _ep_rt_mono_profiler = mono_profiler_create (NULL); - mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_eventpipe_thread_exited); + EP_ASSERT (_ep_rt_default_profiler != NULL); + EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); + EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); + + mono_profiler_set_thread_stopped_callback (_ep_rt_default_profiler, profiler_eventpipe_thread_exited); MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); if (method_signature) { @@ -1204,11 +1694,13 @@ ep_rt_mono_init (void) mono_error_cleanup (error); mono_metadata_free_method_signature (method_signature); - _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); if (_ep_rt_mono_runtime_helper_compile_method) { - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; - _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_runtime_helper_compile_method) { + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); + _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; + _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; + } } } @@ -1222,11 +1714,13 @@ ep_rt_mono_init (void) _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); mono_method_desc_free (desc); - _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; + if (_ep_rt_mono_monitor_enter_v4_method) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); + _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; + } } } @@ -1235,11 +1729,13 @@ ep_rt_mono_init (void) _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); mono_method_desc_free (desc); - _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_method_jitinfo) { - _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); - _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; + if (_ep_rt_mono_monitor_enter_method ) { + _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_ep_rt_mono_monitor_enter_method_jitinfo) { + _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); + _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; + _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; + } } } } @@ -1290,6 +1786,11 @@ ep_rt_mono_fini (void) _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; _ep_rt_mono_monitor_enter_v4_method = NULL; + if (_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); + } + _ep_rt_mono_sampled_thread_callstacks = NULL; _ep_rt_mono_rand_provider = NULL; _ep_rt_mono_initialized = FALSE; @@ -1671,7 +2172,8 @@ ep_rt_mono_providers_validate_all_disabled (void) { return (!MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled && !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled); + !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled && + !MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled); } void @@ -2809,7 +3311,7 @@ ep_rt_write_event_threadpool_working_thread_count ( static void -profiler_jit_begin ( +runtime_profiler_jit_begin ( MonoProfiler *prof, MonoMethod *method) { @@ -2818,7 +3320,7 @@ profiler_jit_begin ( static void -profiler_jit_failed ( +runtime_profiler_jit_failed ( MonoProfiler *prof, MonoMethod *method) { @@ -2827,7 +3329,7 @@ profiler_jit_failed ( static void -profiler_jit_done ( +runtime_profiler_jit_done ( MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji) @@ -2838,7 +3340,7 @@ profiler_jit_done ( static void -profiler_image_loaded ( +runtime_profiler_image_loaded ( MonoProfiler *prof, MonoImage *image) { @@ -2848,7 +3350,7 @@ profiler_image_loaded ( static void -profiler_image_unloaded ( +runtime_profiler_image_unloaded ( MonoProfiler *prof, MonoImage *image) { @@ -2858,7 +3360,7 @@ profiler_image_unloaded ( static void -profiler_assembly_loaded ( +runtime_profiler_assembly_loaded ( MonoProfiler *prof, MonoAssembly *assembly) { @@ -2867,7 +3369,7 @@ profiler_assembly_loaded ( static void -profiler_assembly_unloaded ( +runtime_profiler_assembly_unloaded ( MonoProfiler *prof, MonoAssembly *assembly) { @@ -2876,7 +3378,7 @@ profiler_assembly_unloaded ( static void -profiler_thread_started ( +runtime_profiler_thread_started ( MonoProfiler *prof, uintptr_t tid) { @@ -2885,7 +3387,7 @@ profiler_thread_started ( static void -profiler_thread_stopped ( +runtime_profiler_thread_stopped ( MonoProfiler *prof, uintptr_t tid) { @@ -2894,7 +3396,7 @@ profiler_thread_stopped ( static void -profiler_class_loading ( +runtime_profiler_class_loading ( MonoProfiler *prof, MonoClass *klass) { @@ -2903,7 +3405,7 @@ profiler_class_loading ( static void -profiler_class_failed ( +runtime_profiler_class_failed ( MonoProfiler *prof, MonoClass *klass) { @@ -2912,7 +3414,7 @@ profiler_class_failed ( static void -profiler_class_loaded ( +runtime_profiler_class_loaded ( MonoProfiler *prof, MonoClass *klass) { @@ -2921,7 +3423,7 @@ profiler_class_loaded ( static void -profiler_exception_throw ( +runtime_profiler_exception_throw ( MonoProfiler *prof, MonoObject *exc) { @@ -2930,7 +3432,7 @@ profiler_exception_throw ( static void -profiler_exception_clause ( +runtime_profiler_exception_clause ( MonoProfiler *prof, MonoMethod *method, uint32_t clause_num, @@ -2942,7 +3444,7 @@ profiler_exception_clause ( static void -profiler_monitor_contention ( +runtime_profiler_monitor_contention ( MonoProfiler *prof, MonoObject *obj) { @@ -2951,7 +3453,7 @@ profiler_monitor_contention ( static void -profiler_monitor_acquired ( +runtime_profiler_monitor_acquired ( MonoProfiler *prof, MonoObject *obj) { @@ -2960,7 +3462,7 @@ profiler_monitor_acquired ( static void -profiler_monitor_failed ( +runtime_profiler_monitor_failed ( MonoProfiler *prof, MonoObject *obj) { @@ -2969,7 +3471,7 @@ profiler_monitor_failed ( static void -profiler_jit_code_buffer ( +runtime_profiler_jit_code_buffer ( MonoProfiler *prof, const mono_byte *buffer, uint64_t size, @@ -2992,55 +3494,99 @@ EventPipeEtwCallbackDotNETRuntime ( ep_rt_config_requires_lock_not_held (); EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; - EP_ASSERT (_ep_rt_mono_profiler != NULL); + EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); + + match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; EP_LOCK_ENTER (section1) - if (is_enabled == 1 && !MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled) { - // Add profiler callbacks for DotNETRuntime provider events. - mono_profiler_set_jit_begin_callback (_ep_rt_mono_profiler, profiler_jit_begin); - mono_profiler_set_jit_failed_callback (_ep_rt_mono_profiler, profiler_jit_failed); - mono_profiler_set_jit_done_callback (_ep_rt_mono_profiler, profiler_jit_done); - mono_profiler_set_image_loaded_callback (_ep_rt_mono_profiler, profiler_image_loaded); - mono_profiler_set_image_unloaded_callback (_ep_rt_mono_profiler, profiler_image_unloaded); - mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_profiler, profiler_assembly_loaded); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_profiler, profiler_assembly_unloaded); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, profiler_thread_started); - mono_profiler_set_thread_stopped_callback (_ep_rt_mono_profiler, profiler_thread_stopped); - mono_profiler_set_class_loading_callback (_ep_rt_mono_profiler, profiler_class_loading); - mono_profiler_set_class_failed_callback (_ep_rt_mono_profiler, profiler_class_failed); - mono_profiler_set_class_loaded_callback (_ep_rt_mono_profiler, profiler_class_loaded); - mono_profiler_set_exception_throw_callback (_ep_rt_mono_profiler, profiler_exception_throw); - mono_profiler_set_exception_clause_callback (_ep_rt_mono_profiler, profiler_exception_clause); - mono_profiler_set_monitor_contention_callback (_ep_rt_mono_profiler, profiler_monitor_contention); - mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_profiler, profiler_monitor_acquired); - mono_profiler_set_monitor_failed_callback (_ep_rt_mono_profiler, profiler_monitor_failed); - mono_profiler_set_jit_code_buffer_callback (_ep_rt_mono_profiler, profiler_jit_code_buffer); - } else if (is_enabled == 0 && MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled) { - // Remove profiler callbacks for DotNETRuntime provider events. - mono_profiler_set_jit_code_buffer_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_monitor_contention_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_exception_clause_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_exception_throw_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_class_loading_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_thread_started_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_image_unloaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_image_loaded_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_done_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_failed_callback (_ep_rt_mono_profiler, NULL); - mono_profiler_set_jit_begin_callback (_ep_rt_mono_profiler, NULL); - } - EP_LOCK_EXIT (section1) + uint64_t enabled_keywords = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_begin); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_failed); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_done); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_loaded); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_unloaded); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_loaded); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_unloaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_started); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_stopped); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD | THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loading); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_failed); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_throw); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_clause); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, CONTENTION_KEYWORD)) { + if (!profiler_callback_is_enabled(enabled_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_contention); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_acquired); + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_failed); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); + } + } + + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + EP_LOCK_EXIT (section1) ep_on_exit: ep_rt_config_requires_lock_not_held (); @@ -3095,6 +3641,1536 @@ EventPipeEtwCallbackDotNETRuntimeStress ( MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); } +static +void +mono_profiler_app_domain_loading ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoading ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainLoading ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_app_domain_loaded ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoaded ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainLoaded ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_app_domain_unloading ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloading ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainUnloading ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_app_domain_unloaded ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloaded ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainUnloaded ( + clr_instance_get_id (), + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_app_domain_name ( + MonoProfiler *prof, + MonoDomain *domain, + const char *name) +{ + if (!EventEnabledMonoProfilerAppDomainName ()) + return; + + uint64_t domain_id = (uint64_t)domain; + FireEtwMonoProfilerAppDomainName ( + clr_instance_get_id (), + domain_id, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); +} + +static +inline +void +get_jit_data ( + MonoMethod *method, + uint64_t *method_id, + uint64_t *module_id, + uint32_t *method_token) +{ + *method_id = (uint64_t)method; + *module_id = 0; + *method_token = 0; + + if (method) { + *method_token = method->token; + if (method->klass) + *module_id = (uint64_t)m_class_get_image (method->klass); + } +} + +static +void +mono_profiler_jit_begin ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitBegin()) + return; + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + get_jit_data (method, &method_id, &module_id, &method_token); + + FireEtwMonoProfilerJitBegin ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} + +static +void +mono_profiler_jit_failed ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitFailed()) + return; + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + get_jit_data (method, &method_id, &module_id, &method_token); + + FireEtwMonoProfilerJitFailed ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} + +static +void +mono_profiler_jit_done ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMonoProfilerJitDone()) + return; + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + get_jit_data (method, &method_id, &module_id, &method_token); + + FireEtwMonoProfilerJitDone ( + clr_instance_get_id (), + method_id, + module_id, + method_token, + NULL, + NULL); +} + +static +void +mono_profiler_jit_chunk_created ( + MonoProfiler *prof, + const mono_byte *chunk, + uintptr_t size) +{ + if (!EventEnabledMonoProfilerJitChunkCreated()) + return; + + FireEtwMonoProfilerJitChunkCreated ( + clr_instance_get_id (), + chunk, + (uint64_t)size, + NULL, + NULL); +} + +static +void +mono_profiler_jit_chunk_destroyed ( + MonoProfiler *prof, + const mono_byte *chunk) +{ + if (!EventEnabledMonoProfilerJitChunkDestroyed()) + return; + + FireEtwMonoProfilerJitChunkDestroyed ( + clr_instance_get_id (), + chunk, + NULL, + NULL); +} + +static +void +mono_profiler_jit_code_buffer ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) +{ + if (!EventEnabledMonoProfilerJitCodeBuffer()) + return; + + FireEtwMonoProfilerJitCodeBuffer ( + clr_instance_get_id (), + buffer, + size, + (uint8_t)type, + NULL, + NULL); +} + +static +inline +void +get_class_data ( + MonoClass *klass, + uint64_t *class_id, + uint64_t *module_id, + ep_char8_t **class_name) +{ + *class_id = (uint64_t)klass; + *module_id = 0; + + if (klass) + *module_id = (uint64_t)m_class_get_image (klass); + + if (klass && class_name) + *class_name = (ep_char8_t *)mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_IL); + else if (class_name) + *class_name = NULL; +} + +static +void +mono_profiler_class_loading ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassLoading()) + return; + + uint64_t class_id; + uint64_t module_id; + + get_class_data (klass, &class_id, &module_id, NULL); + + FireEtwMonoProfilerClassLoading ( + clr_instance_get_id (), + class_id, + module_id, + NULL, + NULL); +} + +static +void +mono_profiler_class_failed ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassFailed()) + return; + + uint64_t class_id; + uint64_t module_id; + + get_class_data (klass, &class_id, &module_id, NULL); + + FireEtwMonoProfilerClassFailed ( + clr_instance_get_id (), + class_id, + module_id, + NULL, + NULL); +} + +static +void +mono_profiler_class_loaded ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassLoaded()) + return; + + uint64_t class_id; + uint64_t module_id; + ep_char8_t *class_name; + + get_class_data (klass, &class_id, &module_id, &class_name); + + FireEtwMonoProfilerClassLoaded ( + clr_instance_get_id (), + class_id, + module_id, + class_name ? class_name : "", + NULL, + NULL); + + g_free (class_name); +} + +static +inline +void +get_vtable_data ( + MonoVTable *vtable, + uint64_t *vtable_id, + uint64_t *class_id, + uint64_t *domain_id) +{ + *vtable_id = (uint64_t)vtable; + *class_id = 0; + *domain_id = 0; + + if (vtable) { + *class_id = (uint64_t)mono_vtable_class_internal (vtable); + *domain_id = (uint64_t)mono_vtable_domain_internal (vtable); + } +} + +static +void +mono_profiler_vtable_loading ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableLoading()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + FireEtwMonoProfilerVTableLoading ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_vtable_failed ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableFailed()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + FireEtwMonoProfilerVTableFailed ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_vtable_loaded ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableLoaded()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + FireEtwMonoProfilerVTableLoaded ( + clr_instance_get_id (), + vtable_id, + class_id, + domain_id, + NULL, + NULL); +} + +static +void +mono_profiler_module_loading ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleLoading ()) + return; + + FireEtwMonoProfilerModuleLoading ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); +} + +static +void +mono_profiler_module_failed ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleFailed ()) + return; + + FireEtwMonoProfilerModuleFailed ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); +} + +static +void +mono_profiler_module_loaded ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleLoaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; + + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) + module_path = (const ep_char8_t *)module_data.module_il_path; + module_guid = (const ep_char8_t *)mono_image_get_guid (image); + } + + FireEtwMonoProfilerModuleLoaded ( + clr_instance_get_id (), + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", + NULL, + NULL); +} + +static +void +mono_profiler_module_unloading ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleUnloading ()) + return; + + FireEtwMonoProfilerModuleUnloading ( + clr_instance_get_id (), + (uint64_t)image, + NULL, + NULL); +} + +static +void +mono_profiler_module_unloaded ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleUnloaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; + + if (image) { + ModuleEventData module_data; + if (get_module_event_data (image, &module_data)) + module_path = (const ep_char8_t *)module_data.module_il_path; + module_guid = (const ep_char8_t *)mono_image_get_guid (image); + } + + FireEtwMonoProfilerModuleUnloaded ( + clr_instance_get_id (), + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", + NULL, + NULL); +} + +static +inline +void +get_assembly_data ( + MonoAssembly *assembly, + uint64_t *assembly_id, + uint64_t *module_id, + ep_char8_t **assembly_name) +{ + *assembly_id = (uint64_t)assembly; + *module_id = 0; + + if (assembly) + *module_id = (uint64_t)mono_assembly_get_image_internal (assembly); + + if (assembly && assembly_name) + *assembly_name = (ep_char8_t *)mono_stringify_assembly_name (&assembly->aname); + else if (assembly_name) + *assembly_name = NULL; +} + +static +void +mono_profiler_assembly_loading ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyLoading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + + get_assembly_data (assembly, &assembly_id, &module_id, NULL); + + FireEtwMonoProfilerAssemblyLoading ( + clr_instance_get_id (), + assembly_id, + module_id, + NULL, + NULL); +} + +static +void +mono_profiler_assembly_loaded ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyLoaded ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; + + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); + + FireEtwMonoProfilerAssemblyLoaded ( + clr_instance_get_id (), + assembly_id, + module_id, + assembly_name ? assembly_name : "", + NULL, + NULL); + + g_free (assembly_name); +} + +static +void +mono_profiler_assembly_unloading ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyUnloading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + + get_assembly_data (assembly, &assembly_id, &module_id, NULL); + + FireEtwMonoProfilerAssemblyUnloading ( + clr_instance_get_id (), + assembly_id, + module_id, + NULL, + NULL); +} + +static +void +mono_profiler_assembly_unloaded ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyUnloaded ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; + + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); + + FireEtwMonoProfilerAssemblyUnloaded ( + clr_instance_get_id (), + assembly_id, + module_id, + assembly_name ? assembly_name : "", + NULL, + NULL); + + g_free (assembly_name); +} + +static +void +mono_profiler_method_enter ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context) +{ + if (!EventEnabledMonoProfilerMethodEnter ()) + return; + + FireEtwMonoProfilerMethodEnter ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context) +{ + if (!EventEnabledMonoProfilerMethodLeave ()) + return; + + FireEtwMonoProfilerMethodLeave ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_tail_call ( + MonoProfiler *prof, + MonoMethod *method, + MonoMethod *target_method) +{ + if (!EventEnabledMonoProfilerMethodTailCall ()) + return; + + FireEtwMonoProfilerMethodTailCall ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_exception_leave ( + MonoProfiler *prof, + MonoMethod *method, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerMethodExceptionLeave ()) + return; + + FireEtwMonoProfilerMethodExceptionLeave ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_free ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodFree ()) + return; + + FireEtwMonoProfilerMethodFree ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_begin_invoke ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodBeginInvoke ()) + return; + + FireEtwMonoProfilerMethodBeginInvoke ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +void +mono_profiler_method_end_invoke ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodEndInvoke ()) + return; + + FireEtwMonoProfilerMethodEndInvoke ( + clr_instance_get_id (), + (uint64_t)method, + NULL, + NULL); +} + +static +MonoProfilerCallInstrumentationFlags +mono_profiler_method_instrumentation ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (_ep_rt_dotnet_mono_profiler_provider_callspec.len > 0 && !mono_callspec_eval (method, &_ep_rt_dotnet_mono_profiler_provider_callspec)) + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + + return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; +} + +static +void +mono_profiler_exception_throw ( + MonoProfiler *prof, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerExceptionThrow ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + FireEtwMonoProfilerExceptionThrow ( + clr_instance_get_id (), + type_id, + SGEN_POINTER_UNTAG_ALL (exc), + NULL, + NULL); +} + +static +void +mono_profiler_exception_clause ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerExceptionClause ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + FireEtwMonoProfilerExceptionClause ( + clr_instance_get_id (), + (uint8_t)clause_type, + clause_num, + (uint64_t)method, + type_id, + SGEN_POINTER_UNTAG_ALL (exc), + NULL, + NULL); +} + +static +void +mono_profiler_gc_event ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial) +{ + if (!EventEnabledMonoProfilerGCEvent ()) + return; + + // TODO: Needs to be async safe. + /*FireEtwMonoProfilerGCEvent ( + clr_instance_get_id (), + (uint8_t)gc_event, + generation, + NULL, + NULL);*/ +} + +static +void +mono_profiler_gc_allocation ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCAllocation ()) + return; + + uint64_t vtable_id = 0; + uint64_t object_size = 0; + + if (object) { + vtable_id = (uint64_t)mono_object_get_vtable_internal (object); + object_size = (uint64_t)mono_object_get_size_internal (object); + + /* account for object alignment */ + object_size += 7; + object_size &= ~7; + } + + FireEtwMonoProfilerGCAllocation ( + clr_instance_get_id (), + vtable_id, + SGEN_POINTER_UNTAG_ALL (object), + object_size, + NULL, + NULL); +} + +static +void +mono_profiler_gc_moves ( + MonoProfiler *prof, + MonoObject *const* objects, + uint64_t count) +{ + if (!EventEnabledMonoProfilerGCMoves ()) + return; + + // TODO: Needs to be async safe. + /*uint64_t obj_count = count / 2; + + GCObjectAddressData data [32]; + uint64_t data_chunks = obj_count / G_N_ELEMENTS (data); + uint64_t data_rest = obj_count % G_N_ELEMENTS (data); + uint64_t current_obj = 0; + + for (int chunk = 0; chunk < data_chunks; chunk++) { + for (int i = 0; i < G_N_ELEMENTS (data); i++) { + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj++]); + data [i].address = objects [current_obj++]; + } + + FireEtwMonoProfilerGCMoves ( + clr_instance_get_id (), + G_N_ELEMENTS (data), + sizeof (GCObjectAddressData), + data, + NULL, + NULL); + } + + if ((data_rest != 0)&& (data_rest % 2 == 0)) { + for (int i = 0; i < data_rest; i++) { + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj++]); + data [i].address = objects [current_obj++]; + } + + FireEtwMonoProfilerGCMoves ( + clr_instance_get_id (), + data_rest, + sizeof (GCObjectAddressData), + data, + NULL, + NULL); + }*/ +} + +static +void +mono_profiler_gc_resize ( + MonoProfiler *prof, + uintptr_t size) +{ + if (!EventEnabledMonoProfilerGCResize ()) + return; + + // TODO: Needs to be async safe. + /*FireEtwMonoProfilerGCResize ( + clr_instance_get_id (), + (uint64_t)size, + NULL, + NULL);*/ +} + +static +void +mono_profiler_gc_handle_created ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCHandleCreated ()) + return; + + FireEtwMonoProfilerGCHandleCreated ( + clr_instance_get_id (), + handle, + (uint8_t)type, + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_gc_handle_deleted ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type) +{ + if (!EventEnabledMonoProfilerGCHandleDeleted ()) + return; + + FireEtwMonoProfilerGCHandleDeleted ( + clr_instance_get_id (), + handle, + (uint8_t)type, + NULL, + NULL); +} + +static +void +mono_profiler_gc_finalizing (MonoProfiler *prof) +{ + if (!EventEnabledMonoProfilerGCFinalizing ()) + return; + + FireEtwMonoProfilerGCFinalizing ( + clr_instance_get_id (), + NULL, + NULL); +} + +static +void +mono_profiler_gc_finalized (MonoProfiler *prof) +{ + if (!EventEnabledMonoProfilerGCFinalized ()) + return; + + FireEtwMonoProfilerGCFinalized ( + clr_instance_get_id (), + NULL, + NULL); +} + +static +void +mono_profiler_gc_finalizing_object ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCFinalizingObject ()) + return; + + FireEtwMonoProfilerGCFinalizingObject ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_gc_finalized_object ( + MonoProfiler *prof, + MonoObject * object) +{ + if (!EventEnabledMonoProfilerGCFinalizedObject ()) + return; + + FireEtwMonoProfilerGCFinalizedObject ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_gc_root_register ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name) +{ + if (!EventEnabledMonoProfilerGCRootRegister ()) + return; + + FireEtwMonoProfilerGCRootRegister ( + clr_instance_get_id (), + start, + (uint64_t)size, + (uint8_t) source, + (uint64_t)key, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); +} + +static +void +mono_profiler_gc_root_unregister ( + MonoProfiler *prof, + const mono_byte *start) +{ + if (!EventEnabledMonoProfilerGCRootUnregister ()) + return; + + FireEtwMonoProfilerGCRootUnregister ( + clr_instance_get_id (), + start, + NULL, + NULL); +} + +static +void +mono_profiler_gc_roots ( + MonoProfiler *prof, + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects) +{ + if (!EventEnabledMonoProfilerGCRoots ()) + return; + + // TODO: Needs to be async safe. + /*GCAddressObjectData data [32]; + uint64_t data_chunks = count / G_N_ELEMENTS (data); + uint64_t data_rest = count % G_N_ELEMENTS (data); + uint64_t current_obj = 0; + + for (int chunk = 0; chunk < data_chunks; chunk++) { + for (int i = 0; i < G_N_ELEMENTS (data); i++) { + data [i].address = addresses [current_obj]; + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj]); + current_obj++; + } + + FireEtwMonoProfilerGCRoots ( + clr_instance_get_id (), + G_N_ELEMENTS (data), + sizeof (GCAddressObjectData), + data, + NULL, + NULL); + } + + if (data_rest != 0) { + for (int i = 0; i < data_rest; i++) { + data [i].address = addresses [current_obj]; + data [i].object = SGEN_POINTER_UNTAG_ALL (objects [current_obj]); + current_obj++; + } + + FireEtwMonoProfilerGCRoots ( + clr_instance_get_id (), + data_rest, + sizeof (GCAddressObjectData), + data, + NULL, + NULL); + }*/ +} + +static +void +mono_profiler_monitor_contention ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorContention ()) + return; + + FireEtwMonoProfilerMonitorContention ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_monitor_failed ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorFailed ()) + return; + + FireEtwMonoProfilerMonitorFailed ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_monitor_acquired ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorAcquired ()) + return; + + FireEtwMonoProfilerMonitorAcquired ( + clr_instance_get_id (), + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); +} + +static +void +mono_profiler_thread_started ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStarted ()) + return; + + FireEtwMonoProfilerThreadStarted ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); +} + +static +void +mono_profiler_thread_stopping ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStopping ()) + return; + + FireEtwMonoProfilerThreadStopping ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); +} + +static +void +mono_profiler_thread_stopped ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStopped ()) + return; + + FireEtwMonoProfilerThreadStopped ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); +} + +static +void +mono_profiler_thread_exited ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadExited ()) + return; + + FireEtwMonoProfilerThreadExited ( + clr_instance_get_id (), + (uint64_t)tid, + NULL, + NULL); +} + +static +void +mono_profiler_thread_name ( + MonoProfiler *prof, + uintptr_t tid, + const char *name) +{ + if (!EventEnabledMonoProfilerThreadName ()) + return; + + FireEtwMonoProfilerThreadName ( + clr_instance_get_id (), + (uint64_t)tid, + (ep_char8_t *)(name ? name : ""), + NULL, + NULL); +} + +void +EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + ep_rt_config_requires_lock_not_held (); + + EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; + EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); + + match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; + + EP_LOCK_ENTER (section1) + uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; + + if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loading); + mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loaded); + mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloading); + mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloaded); + mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_name); + mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loading); + mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_failed); + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loaded); + mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloading); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloaded); + mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loading); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loaded); + mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloading); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { + mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_begin); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_failed); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_done); + mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_created); + mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_destroyed); + mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_code_buffer); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, TYPE_LOADING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loading); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_failed); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loaded); + mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loading); + mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_failed); + mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loaded); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, METHOD_TRACING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { + mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_enter); + mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_leave); + mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_tail_call); + mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_exception_leave); + mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_free); + mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_begin_invoke); + mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_end_invoke); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { + mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_throw); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_clause); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) { + mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_event); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) { + mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_allocation); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ALLOCATION_KEYWORD)) { + mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_moves); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_MOVES_KEYWORD)) { + mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_resize); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_RESIZE_KEYWORD)) { + mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_created); + mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_deleted); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_HANDLE_KEYWORD)) { + mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing); + mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized); + mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing_object); + mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized_object); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_FINALIZATION_KEYWORD)) { + mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_register); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_unregister); + mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_roots); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD | GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_contention); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD | CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_failed); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_acquired); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { + mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (profiler_callback_is_enabled(match_any_keywords, THREADING_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_started); + mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopping); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopped); + mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_exited); + mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_name); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + + if (!_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { + if (profiler_callback_is_enabled(match_any_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + if (!profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); + } + } else { + if (profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); + } + } + } + + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.Level = level; + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; + MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); + EP_LOCK_EXIT (section1) + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + #endif /* ENABLE_PERFTRACING */ MONO_EMPTY_SOURCE_FILE(eventpipe_rt_mono); diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 2c3a5f7e36d..49b6456cdb9 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -2344,6 +2344,16 @@ EventPipeEtwCallbackDotNETRuntimeStress ( EventFilterDescriptor *filter_data, void *callback_data); +void +EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data); + #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_RT_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst index f74d476889e..ad52dd9f8c8 100644 --- a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst +++ b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst @@ -45,3 +45,4 @@ ThreadPoolWorkingThreadCount ThreadTerminated TypeLoadStart TypeLoadStop +Microsoft-DotNETRuntimeMonoProfiler:* \ No newline at end of file From b7ce14c329dabbbdf5f25349fb7b4665042774cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 9 Jul 2021 17:55:31 -0400 Subject: [PATCH 394/926] Disallow TypedReference and byref types in MakeArrayType (#55403) Fixes https://github.com/dotnet/runtime/issues/39001 --- .../tests/System/Type/TypeTests.cs | 1 - src/mono/mono/metadata/icall.c | 20 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs index ca984b43318..f8816f13a62 100644 --- a/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs +++ b/src/libraries/System.Runtime/tests/System/Type/TypeTests.cs @@ -305,7 +305,6 @@ namespace System.Tests [Theory] [MemberData(nameof(MakeArrayType_ByRef_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/39001", TestRuntimes.Mono)] public void MakeArrayType_ByRef_ThrowsTypeLoadException(Type t) { Assert.Throws(() => t.MakeArrayType()); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index d725b1c3b5f..7c3f33fc792 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6283,15 +6283,25 @@ ves_icall_System_Reflection_RuntimeModule_ResolveSignature (MonoImage *image, gu } static void -check_for_invalid_array_type (MonoClass *klass, MonoError *error) +check_for_invalid_array_type (MonoType *type, MonoError *error) { + gboolean allowed = TRUE; char *name; error_init (error); - if (m_class_get_byval_arg (klass)->type != MONO_TYPE_TYPEDBYREF) - return; + if (type->byref) + allowed = FALSE; + else if (type->type == MONO_TYPE_TYPEDBYREF) + allowed = FALSE; + MonoClass *klass = mono_class_from_mono_type_internal (type); + + if (m_class_is_byreflike (klass)) + allowed = FALSE; + + if (allowed) + return; name = mono_type_get_full_name (klass); mono_error_set_type_load_name (error, name, g_strdup (""), ""); } @@ -6307,9 +6317,9 @@ ves_icall_RuntimeType_make_array_type (MonoReflectionTypeHandle ref_type, int ra { MonoType *type = MONO_HANDLE_GETVAL (ref_type, type); - MonoClass *klass = mono_class_from_mono_type_internal (type); - check_for_invalid_array_type (klass, error); + check_for_invalid_array_type (type, error); return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE)); + MonoClass *klass = mono_class_from_mono_type_internal (type); MonoClass *aklass; if (rank == 0) //single dimension array From c90aa4d8ab36bd454e5937abd903924fe7e859fe Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 01:05:11 +0300 Subject: [PATCH 395/926] [interp] Add a few fixes for conversion opcodes (#55418) * [interp] Add missing conversion from r4 to uint * [interp] Remove duplicated and inconsistent code for conv.ovf.i.un and conv.ovf.u.un Just use the existing path for the i4/i8/u4/u8 opcodes. --- src/mono/mono/mini/interp/transform.c | 45 +++++++++------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 4a17c9410b7..0eed6c7146e 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -5308,6 +5308,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U4_R8); #else interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U8_R8); +#endif + break; + case STACK_TYPE_R4: +#if SIZEOF_VOID_P == 4 + interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U4_R4); +#else + interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_U8_R4); #endif break; case STACK_TYPE_I4: @@ -6133,36 +6140,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; break; } +#if SIZEOF_VOID_P == 8 case CEE_CONV_OVF_I_UN: case CEE_CONV_OVF_U_UN: - CHECK_STACK (td, 1); - switch (td->sp [-1].type) { - case STACK_TYPE_R8: -#if SIZEOF_VOID_P == 8 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I8_UN_R8); -#else - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_UN_R8); #endif - break; - case STACK_TYPE_I8: -#if SIZEOF_VOID_P == 4 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_U8); -#endif - break; - case STACK_TYPE_I4: -#if SIZEOF_VOID_P == 8 - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_I8_U4); -#elif SIZEOF_VOID_P == 4 - if (*td->ip == CEE_CONV_OVF_I_UN) - interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I, MINT_CONV_OVF_I4_U4); -#endif - break; - default: - g_assert_not_reached (); - break; - } - ++td->ip; - break; case CEE_CONV_OVF_I8_UN: case CEE_CONV_OVF_U8_UN: CHECK_STACK (td, 1); @@ -6171,7 +6152,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_OVF_I8_UN_R8); break; case STACK_TYPE_I8: - if (*td->ip == CEE_CONV_OVF_I8_UN) + if (*td->ip == CEE_CONV_OVF_I8_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_OVF_I8_U8); break; case STACK_TYPE_I4: @@ -6619,6 +6600,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_I: + case CEE_CONV_OVF_I_UN: #endif case CEE_CONV_OVF_I4: case CEE_CONV_OVF_I4_UN: @@ -6631,11 +6613,11 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_R8); break; case STACK_TYPE_I4: - if (*td->ip == CEE_CONV_OVF_I4_UN) + if (*td->ip == CEE_CONV_OVF_I4_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_U4); break; case STACK_TYPE_I8: - if (*td->ip == CEE_CONV_OVF_I4_UN) + if (*td->ip == CEE_CONV_OVF_I4_UN || *td->ip == CEE_CONV_OVF_I_UN) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_U8); else interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_I4_I8); @@ -6647,6 +6629,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_U: + case CEE_CONV_OVF_U_UN: #endif case CEE_CONV_OVF_U4: case CEE_CONV_OVF_U4_UN: @@ -6659,7 +6642,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_U4_R8); break; case STACK_TYPE_I4: - if (*td->ip != CEE_CONV_OVF_U4_UN) + if (*td->ip == CEE_CONV_OVF_U4 || *td->ip == CEE_CONV_OVF_U) interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I4, MINT_CONV_OVF_U4_I4); break; case STACK_TYPE_I8: From fe9005ff523faa681448e3e5ea67f48b3352e3da Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 19:47:40 -0400 Subject: [PATCH 396/926] Use RandomAccess/File.OpenHandle in a few more places (#55150) Places where we don't need to pay for the FileStream and the access pattern is easily replaced. --- .../src/System/IO/File.cs | 36 +++++++------------ .../src/System/IO/FileSystem.Unix.cs | 7 ++-- .../src/System/IO/RandomAccess.Windows.cs | 4 +-- .../src/System/TimeZoneInfo.Unix.cs | 8 +++-- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 8480e46a094..1f24874266c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -10,6 +10,7 @@ using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; #if MS_IO_REDIST using System; @@ -373,19 +374,13 @@ namespace System.IO if (bytes == null) throw new ArgumentNullException(nameof(bytes)); - InternalWriteAllBytes(path, bytes); - } - - private static void InternalWriteAllBytes(string path, byte[] bytes) - { - Debug.Assert(path != null); - Debug.Assert(path.Length != 0); - Debug.Assert(bytes != null); - - using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - fs.Write(bytes, 0, bytes.Length); - } +#if MS_IO_REDIST + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); + fs.Write(bytes, 0, bytes.Length); +#else + using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read); + RandomAccess.WriteAtOffset(sfh, bytes, 0); +#endif } public static string[] ReadAllLines(string path) { @@ -836,22 +831,17 @@ namespace System.IO return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) - : InternalWriteAllBytesAsync(path, bytes, cancellationToken); - } + : Core(path, bytes, cancellationToken); - private static async Task InternalWriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken) - { - Debug.Assert(!string.IsNullOrEmpty(path)); - Debug.Assert(bytes != null); - - using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan)) + static async Task Core(string path, byte[] bytes, CancellationToken cancellationToken) { #if MS_IO_REDIST + using FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 1, FileOptions.Asynchronous | FileOptions.SequentialScan); await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); #else - await fs.WriteAsync(new ReadOnlyMemory(bytes), cancellationToken).ConfigureAwait(false); + using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous | FileOptions.SequentialScan); + await RandomAccess.WriteAtOffsetAsync(sfh, bytes, 0, cancellationToken).ConfigureAwait(false); #endif - await fs.FlushAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 0a4ef7cdef5..c80e585eda8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using Microsoft.Win32.SafeHandles; namespace System.IO { @@ -25,10 +26,10 @@ namespace System.IO } // Copy the contents of the file from the source to the destination, creating the destination in the process - using (var src = new FileStream(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.None)) - using (var dst = new FileStream(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, DefaultBufferSize, FileOptions.None)) + using (SafeFileHandle src = File.OpenHandle(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.None)) + using (SafeFileHandle dst = File.OpenHandle(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, FileOptions.None)) { - Interop.CheckIo(Interop.Sys.CopyFile(src.SafeFileHandle, dst.SafeFileHandle)); + Interop.CheckIo(Interop.Sys.CopyFile(src, dst)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 20dcbdeac8e..63639cc8503 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -208,7 +208,7 @@ namespace System.IO } } - private static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) => handle.IsAsync ? Map(QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken)) : ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); @@ -269,7 +269,7 @@ namespace System.IO return (vts, -1); } - private static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) => handle.IsAsync ? Map(QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken)) : ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index ebc626a6221..1356e65758c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -11,6 +11,7 @@ using System.IO; using System.Text; using System.Threading; using System.Security; +using Microsoft.Win32.SafeHandles; namespace System { @@ -615,16 +616,17 @@ namespace System try { // bufferSize == 1 used to avoid unnecessary buffer in FileStream - using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1)) + using (SafeFileHandle sfh = File.OpenHandle(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == rawData.Length) + long fileLength = RandomAccess.GetLength(sfh); + if (fileLength == rawData.Length) { int index = 0; int count = rawData.Length; while (count > 0) { - int n = stream.Read(buffer, index, count); + int n = RandomAccess.Read(sfh, buffer.AsSpan(index, count), index); if (n == 0) ThrowHelper.ThrowEndOfFileException(); From e0b9fe5146741af0c0bab434cbece4f67a3a677e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 19:56:02 -0400 Subject: [PATCH 397/926] Fix File.Copy with procfs (#55358) --- .../Native/Unix/System.Native/pal_io.c | 41 +++++++++++-------- .../System.IO.FileSystem/tests/File/Copy.cs | 12 ++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 59b8f2770c1..65342650856 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -1208,41 +1208,46 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) return -1; } - // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. uint64_t size = (uint64_t)sourceStat.st_size; - - // Note that per man page for large files, you have to iterate until the - // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). - while (size > 0) + if (size != 0) { - ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); - if (sent < 0) + // Note that per man page for large files, you have to iterate until the + // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). + while (size > 0) { - if (errno != EINVAL && errno != ENOSYS) + ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); + if (sent < 0) { - return -1; + if (errno != EINVAL && errno != ENOSYS) + { + return -1; + } + else + { + break; + } } else { - break; + assert((size_t)sent <= size); + size -= (size_t)sent; } } - else + + if (size == 0) { - assert((size_t)sent <= size); - size -= (size_t)sent; + copied = true; } } - if (size == 0) - { - copied = true; - } + // sendfile couldn't be used; fall back to a manual copy below. This could happen // if we're on an old kernel, for example, where sendfile could only be used - // with sockets and not regular files. + // with sockets and not regular files. Additionally, certain files (e.g. procfs) + // may return a size of 0 even though reading from then will produce data. As such, + // we avoid using sendfile with the queried size if the size is reported as 0. #endif // HAVE_SENDFILE_4 // Manually read all data from the source and write it to the destination. diff --git a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs index 0654f99dc9c..eb2686807bc 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs @@ -239,6 +239,18 @@ namespace System.IO.Tests Assert.Throws(() => Copy(testFileAlternateStream, testFile2)); Assert.Throws(() => Copy(testFileAlternateStream, testFile2 + alternateStream)); } + + [Theory] + [PlatformSpecific(TestPlatforms.Linux)] + [InlineData("/proc/cmdline")] + [InlineData("/proc/version")] + [InlineData("/proc/filesystems")] + public void Linux_CopyFromProcfsToFile(string path) + { + string testFile = GetTestFilePath(); + File.Copy(path, testFile); + Assert.Equal(File.ReadAllText(path), File.ReadAllText(testFile)); // assumes chosen files won't change between reads + } #endregion } From 0d848f68b46cd34bca7f64b11b79057f4b7bfad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 10 Jul 2021 01:56:54 +0200 Subject: [PATCH 398/926] Add possibility to write ANSI color escape codes when the console output is redirected (#47935) * Add possibility to write ANSI color escape codes when the console output is redirected This introduces an opt-in mechanism to allow writing ANSI color escape codes even when the console output is redirected. To enable ANSI color codes, the `DOTNET_CONSOLE_ANSI_COLOR` environment variable must be set to `true` (case insensitive) or `1`. Fixes #33980 * Fix ANSI color env var handling Co-authored-by: Stephen Toub --- .../src/System/ConsolePal.Unix.cs | 50 +++++++++++++++- src/libraries/System.Console/tests/Color.cs | 60 +++++++++++++++++-- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index eacf15a203e..5f2e485aca5 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -39,6 +39,9 @@ namespace System private static int s_windowHeight; // Cached WindowHeight, invalid when s_windowWidth == -1. private static int s_invalidateCachedSettings = 1; // Tracks whether we should invalidate the cached settings. + /// Whether to output ansi color strings. + private static volatile int s_emitAnsiColorCodes = -1; + public static Stream OpenStandardInput() { return new UnixConsoleStream(SafeFileHandleHelper.Open(() => Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDIN_FILENO)), FileAccess.Read, @@ -779,8 +782,10 @@ namespace System // Changing the color involves writing an ANSI character sequence out to the output stream. // We only want to do this if we know that sequence will be interpreted by the output. // rather than simply displayed visibly. - if (Console.IsOutputRedirected) + if (!EmitAnsiColorCodes) + { return; + } // See if we've already cached a format string for this foreground/background // and specific color choice. If we have, just output that format string again. @@ -813,13 +818,52 @@ namespace System /// Writes out the ANSI string to reset colors. private static void WriteResetColorString() { - // We only want to send the reset string if we're targeting a TTY device - if (!Console.IsOutputRedirected) + if (EmitAnsiColorCodes) { WriteStdoutAnsiString(TerminalFormatStrings.Instance.Reset); } } + /// Get whether to emit ANSI color codes. + private static bool EmitAnsiColorCodes + { + get + { + // The flag starts at -1. If it's no longer -1, it's 0 or 1 to represent false or true. + int emitAnsiColorCodes = s_emitAnsiColorCodes; + if (emitAnsiColorCodes != -1) + { + return Convert.ToBoolean(emitAnsiColorCodes); + } + + // We've not yet computed whether to emit codes or not. Do so now. We may race with + // other threads, and that's ok; this is idempotent unless someone is currently changing + // the value of the relevant environment variables, in which case behavior here is undefined. + + // By default, we emit ANSI color codes if output isn't redirected, and suppress them if output is redirected. + bool enabled = !Console.IsOutputRedirected; + + if (enabled) + { + // We subscribe to the informal standard from https://no-color.org/. If we'd otherwise emit + // ANSI color codes but the NO_COLOR environment variable is set, disable emitting them. + enabled = Environment.GetEnvironmentVariable("NO_COLOR") is null; + } + else + { + // We also support overriding in the other direction. If we'd otherwise avoid emitting color + // codes but the DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION environment variable is + // set to 1 or true, enable color. + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"); + enabled = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); + } + + // Store and return the computed answer. + s_emitAnsiColorCodes = Convert.ToInt32(enabled); + return enabled; + } + } + /// /// The values of the ConsoleColor enums unfortunately don't map to the /// corresponding ANSI values. We need to do the mapping manually. diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 567faad01ca..55843aa2f37 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -2,15 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using System.Text; -using Microsoft.DotNet.XUnitExtensions; +using Microsoft.DotNet.RemoteExecutor; using Xunit; public class Color { + private const char Esc = (char)0x1B; + [Fact] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] public static void InvalidColors() @@ -64,9 +67,58 @@ public class Color Console.ResetColor(); Console.Write('4'); - const char Esc = (char)0x1B; Assert.Equal(0, Encoding.UTF8.GetString(data.ToArray()).ToCharArray().Count(c => c == Esc)); Assert.Equal("1234", Encoding.UTF8.GetString(data.ToArray())); }); } + + public static bool TermIsSet => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TERM")); + + [ConditionalTheory(nameof(TermIsSet))] + [PlatformSpecific(TestPlatforms.AnyUnix)] + [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] + [InlineData(null)] + [InlineData("1")] + [InlineData("true")] + [InlineData("tRuE")] + [InlineData("0")] + [InlineData("false")] + public static void RedirectedOutput_EnvVarSet_EmitsAnsiCodes(string envVar) + { + var psi = new ProcessStartInfo { RedirectStandardOutput = true }; + psi.Environment["DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"] = envVar; + + for (int i = 0; i < 3; i++) + { + Action main = i => + { + Console.Write("SEPARATOR"); + switch (i) + { + case "0": + Console.ForegroundColor = ConsoleColor.Blue; + break; + + case "1": + Console.BackgroundColor = ConsoleColor.Red; + break; + + case "2": + Console.ResetColor(); + break; + } + Console.Write("SEPARATOR"); + }; + + using RemoteInvokeHandle remote = RemoteExecutor.Invoke(main, i.ToString(CultureInfo.InvariantCulture), new RemoteInvokeOptions() { StartInfo = psi }); + + bool expectedEscapes = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); + + string stdout = remote.Process.StandardOutput.ReadToEnd(); + string[] parts = stdout.Split("SEPARATOR"); + Assert.Equal(3, parts.Length); + + Assert.Equal(expectedEscapes, parts[1].Contains(Esc)); + } + } } From 3b8b527ef638c651e2d41299426bf91b93563196 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Sat, 10 Jul 2021 02:00:20 +0200 Subject: [PATCH 399/926] Big-endian fix: ApplyMask in System.Net.WebSockets.ManagedWebSocket (#55422) * Fix mask shifting logic for big-endian systems --- .../src/System/Net/WebSockets/ManagedWebSocket.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index ac0fad583d9..864b7425617 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -1503,7 +1503,15 @@ namespace System.Net.WebSockets maskIndex = (maskIndex + 1) & 3; } - int rolledMask = (int)BitOperations.RotateRight((uint)mask, maskIndex * 8); + int rolledMask; + if (BitConverter.IsLittleEndian) + { + rolledMask = (int)BitOperations.RotateRight((uint)mask, maskIndex * 8); + } + else + { + rolledMask = (int)BitOperations.RotateLeft((uint)mask, maskIndex * 8); + } // use SIMD if possible. From bea43044f8fbf78e77ca3d1085ae0f6bdc05ead9 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 9 Jul 2021 20:01:05 -0400 Subject: [PATCH 400/926] Fix one-shot CBC documentation (#55432) * Fix one-shot CBC documentation. The CBC one-shot documentation made a few references to the ECB one shots. This removes them and uses consistent language that is used on the other one-shot methods. * Change ECB to be consistent --- .../src/System/Security/Cryptography/SymmetricAlgorithm.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs index 857edc6d03c..f5485577c07 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs @@ -561,7 +561,7 @@ namespace System.Security.Cryptography /// is not a valid padding mode. /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . @@ -842,7 +842,7 @@ namespace System.Security.Cryptography /// that is exactly in length, converted to bytes (BlockSize / 8). /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . @@ -872,7 +872,7 @@ namespace System.Security.Cryptography /// that is exactly in length, converted to bytes (BlockSize / 8). /// /// - /// could not encrypt the plaintext. + /// The plaintext could not be encrypted successfully. /// /// /// This method's behavior is defined by . From a54e25b67494bd4ff49a650566d74984b7facdfb Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Sat, 10 Jul 2021 02:02:38 +0200 Subject: [PATCH 401/926] Nullable annotations for the remainder of System.Data.Common (#55409) --- .../System.ComponentModel.TypeConverter.cs | 4 +- .../ComponentModel/CustomTypeDescriptor.cs | 2 +- .../ComponentModel/ICustomTypeDescriptor.cs | 2 +- .../ReflectTypeDescriptionProvider.cs | 4 +- .../System/ComponentModel/TypeDescriptor.cs | 8 +- .../ref/System.Data.Common.cs | 6 +- .../System/Data/Common/AdapterUtil.Common.cs | 19 +- .../System/Data/Common/DataRecordInternal.cs | 2 +- .../src/System/Data/Common/DataStorage.cs | 2 +- .../Data/Common/DbConnectionStringBuilder.cs | 2 +- .../src/System/Data/Common/DbDataRecord.cs | 2 +- .../src/System/Data/Common/ObjectStorage.cs | 2 +- .../src/System/Data/Common/SqlUDTStorage.cs | 2 +- .../src/System/Data/DataColumn.cs | 2 +- .../src/System/Data/DataException.cs | 2 +- .../src/System/Data/DataRowView.cs | 2 +- .../src/System/Data/DataTableCollection.cs | 2 +- .../DataViewManagerListItemTypeDescriptor.cs | 27 +- .../src/System/Data/SimpleType.cs | 4 +- .../src/System/Data/XMLDiffLoader.cs | 79 ++-- .../src/System/Data/XMLSchema.cs | 441 +++++++++--------- .../src/System/Data/XmlDataLoader.cs | 191 ++++---- .../src/System/Data/XmlToDatasetMap.cs | 87 ++-- 23 files changed, 439 insertions(+), 455 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index e222d17e5aa..6a3d62e34a7 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -303,7 +303,7 @@ namespace System.ComponentModel public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes) { throw null; } - public virtual object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + public virtual object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class DataObjectAttribute : System.Attribute @@ -585,7 +585,7 @@ namespace System.ComponentModel System.ComponentModel.PropertyDescriptorCollection GetProperties(); [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[]? attributes); - object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd); + object? GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd); } public partial interface IDataErrorInfo { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs index 17923164ab3..7adad3b3bc0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs @@ -179,6 +179,6 @@ namespace System.ComponentModel /// returned. Returning null from this method causes the TypeDescriptor object /// to use its default type description services. /// - public virtual object? GetPropertyOwner(PropertyDescriptor pd) => _parent?.GetPropertyOwner(pd); + public virtual object? GetPropertyOwner(PropertyDescriptor? pd) => _parent?.GetPropertyOwner(pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs index fd9b681c20e..57b602ac9e9 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs @@ -77,6 +77,6 @@ namespace System.ComponentModel /// /// Gets the object that directly depends on this value being edited. /// - object? GetPropertyOwner(PropertyDescriptor pd); + object? GetPropertyOwner(PropertyDescriptor? pd); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 9908aa66c33..f41b9fca2a1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -791,7 +791,7 @@ namespace System.ComponentModel /// /// Retrieves the owner for a property. /// - internal object GetExtendedPropertyOwner(object instance, PropertyDescriptor pd) + internal object GetExtendedPropertyOwner(object instance, PropertyDescriptor? pd) { return GetPropertyOwner(instance.GetType(), instance, pd); } @@ -873,7 +873,7 @@ namespace System.ComponentModel /// /// Retrieves the owner for a property. /// - internal object GetPropertyOwner(Type type, object instance, PropertyDescriptor pd) + internal object GetPropertyOwner(Type type, object instance, PropertyDescriptor? pd) { return TypeDescriptor.GetAssociation(type, instance); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index d8fae944f83..72f1dadd00c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -2771,7 +2771,7 @@ namespace System.ComponentModel return _handler.GetProperties(_instance, attributes); } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => _instance; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => _instance; } } @@ -3121,7 +3121,7 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return _primary.GetPropertyOwner(pd) ?? _secondary.GetPropertyOwner(pd); } @@ -3570,7 +3570,7 @@ namespace System.ComponentModel /// ICustomTypeDescriptor implementation. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another @@ -3915,7 +3915,7 @@ namespace System.ComponentModel /// /// ICustomTypeDescriptor implementation. /// - object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object? ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. // If so, we can call on it directly rather than creating another diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index 10041da15ce..ca955720c0d 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -481,7 +481,7 @@ namespace System.Data System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } [System.ComponentModel.DefaultPropertyAttribute("DataSetName")] [System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.Data.VS.DataSetDesigner, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] @@ -2207,7 +2207,7 @@ namespace System.Data.Common System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } public override string ToString() { throw null; } public virtual bool TryGetValue(string keyword, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out object? value) { throw null; } } @@ -2398,7 +2398,7 @@ namespace System.Data.Common System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(System.Attribute[]? attributes) { throw null; } - object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } + object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor? pd) { throw null; } } public abstract partial class DbDataSourceEnumerator { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs index 38695b358c1..3bf38b5b09a 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/AdapterUtil.Common.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO-NULLABLE: Enable nullability as part of annotation System.Data.{Odbc,OleDb} -#nullable disable - using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -266,7 +263,7 @@ namespace System.Data.Common // // DbConnectionOptions, DataAccess // - internal static ArgumentException InvalidKeyname(string parameterName) + internal static ArgumentException InvalidKeyname(string? parameterName) { return Argument(SR.ADP_InvalidKey, parameterName); } @@ -286,7 +283,7 @@ namespace System.Data.Common // // Generic Data Provider Collection // - internal static Exception CollectionUniqueValue(Type itemType, string propertyName, string propertyValue) + internal static Exception CollectionUniqueValue(Type itemType, string propertyName, string? propertyValue) { return Argument(SR.Format(SR.ADP_CollectionUniqueValue, itemType.Name, propertyName, propertyValue)); } @@ -306,19 +303,19 @@ namespace System.Data.Common } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaExpression(string srcColumn, string cacheColumn) + internal static InvalidOperationException ColumnSchemaExpression(string? srcColumn, string cacheColumn) { return DataMapping(SR.Format(SR.ADP_ColumnSchemaExpression, srcColumn, cacheColumn)); } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaMismatch(string srcColumn, Type srcType, DataColumn column) + internal static InvalidOperationException ColumnSchemaMismatch(string? srcColumn, Type srcType, DataColumn column) { return DataMapping(SR.Format(SR.ADP_ColumnSchemaMismatch, srcColumn, srcType.Name, column.ColumnName, column.DataType.Name)); } // DataColumnMapping.GetDataColumnBySchemaAction - internal static InvalidOperationException ColumnSchemaMissing(string cacheColumn, string tableName, string srcColumn) + internal static InvalidOperationException ColumnSchemaMissing(string cacheColumn, string tableName, string? srcColumn) { if (string.IsNullOrEmpty(tableName)) { @@ -382,7 +379,7 @@ namespace System.Data.Common { return ParametersIsParent(typeof(DataColumnMapping), collection); } - internal static Exception ColumnsUniqueSourceColumn(string srcColumn) + internal static Exception ColumnsUniqueSourceColumn(string? srcColumn) { return CollectionUniqueValue(typeof(DataColumnMapping), ADP.SourceColumn, srcColumn); } @@ -422,7 +419,7 @@ namespace System.Data.Common { return CollectionIndexString(typeof(DataTableMapping), ADP.SourceTable, srcTable, typeof(DataTableMappingCollection)); } - internal static Exception TablesUniqueSourceTable(string srcTable) + internal static Exception TablesUniqueSourceTable(string? srcTable) { return CollectionUniqueValue(typeof(DataTableMapping), ADP.SourceTable, srcTable); } @@ -840,6 +837,6 @@ namespace System.Data.Common return uniqueIndex; } - internal static int SrcCompare(string strA, string strB) => strA == strB ? 0 : 1; + internal static int SrcCompare(string? strA, string? strB) => strA == strB ? 0 : 1; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs index c49d8d33ef0..ba0a44032fc 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs @@ -368,7 +368,7 @@ namespace System.Data.Common return _propertyDescriptors; } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return this; } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs index 1a8b361c17f..8c28cc2210d 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataStorage.cs @@ -274,7 +274,7 @@ namespace System.Data.Common public abstract object ConvertXmlToObject(string s); [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { return ConvertXmlToObject(xmlReader.Value); } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 10567391a3e..27ffe33faa3 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -637,7 +637,7 @@ namespace System.Data.Common { return TypeDescriptor.GetEvents(this, attributes, true); } - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) { return this; } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs index f3e1827f885..14f3d843877 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs @@ -101,6 +101,6 @@ namespace System.Data.Common PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => new PropertyDescriptorCollection(null); - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs index aa06791989d..7489652d9ba 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs @@ -349,7 +349,7 @@ namespace System.Data.Common // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { object? retValue = null; bool isBaseCLRType = false; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs index 16aea8a6c0b..b804c470ed0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/SqlUDTStorage.cs @@ -173,7 +173,7 @@ namespace System.Data.Common // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { if (null == xmlAttrib) { diff --git a/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs b/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs index 796ea59699d..5d98ebf62ef 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataColumn.cs @@ -1798,7 +1798,7 @@ namespace System.Data } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) + internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute? xmlAttrib) { return InsureStorage().ConvertXmlToObject(xmlReader, xmlAttrib); } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataException.cs b/src/libraries/System.Data.Common/src/System/Data/DataException.cs index dfc6806e2ef..301075bf543 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataException.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataException.cs @@ -677,7 +677,7 @@ namespace System.Data public static Exception DiffgramMissingSQL() => _Data(SR.Xml_MissingSQL); public static Exception DuplicateConstraintRead(string str) => _Data(SR.Format(SR.Xml_DuplicateConstraint, str)); public static Exception ColumnTypeConflict(string name) => _Data(SR.Format(SR.Xml_ColumnConflict, name)); - public static Exception CannotConvert(string name, string type) => _Data(SR.Format(SR.Xml_CannotConvert, name, type)); + public static Exception CannotConvert(string name, string? type) => _Data(SR.Format(SR.Xml_CannotConvert, name, type)); public static Exception MissingRefer(string name) => _Data(SR.Format(SR.Xml_MissingRefer, Keywords.REFER, Keywords.XSD_KEYREF, name)); public static Exception InvalidPrefix(string name) => _Data(SR.Format(SR.Xml_InvalidPrefix_SpecialCharacters, name)); public static Exception CanNotDeserializeObjectType() => _InvalidOperation(SR.Xml_CanNotDeserializeObjectType); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs index dc0dd2e7aad..c12d20bfd03 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs @@ -257,7 +257,7 @@ namespace System.Data PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => (_dataView.Table != null ? _dataView.Table.GetPropertyDescriptorCollection(attributes) : s_zeroPropertyDescriptorCollection); - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; #endregion } } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs b/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs index 5677da8639e..e4dd6960869 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTableCollection.cs @@ -119,7 +119,7 @@ namespace System.Data // Case-sensitive smart search: it will look for a table using the ns only if required to // resolve a conflict - internal DataTable? GetTableSmart(string name, string ns) + internal DataTable? GetTableSmart(string name, string? ns) { int fCount = 0; DataTable? fTable = null; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs index 20b61fc1a36..d991f29e5f1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.ComponentModel.TypeConverter is annotated -#nullable disable - using System.ComponentModel; using System.Diagnostics.CodeAnalysis; @@ -12,7 +9,7 @@ namespace System.Data internal sealed class DataViewManagerListItemTypeDescriptor : ICustomTypeDescriptor { private readonly DataViewManager _dataViewManager; - private PropertyDescriptorCollection _propsCollection; + private PropertyDescriptorCollection? _propsCollection; internal DataViewManagerListItemTypeDescriptor(DataViewManager dataViewManager) { @@ -40,37 +37,37 @@ namespace System.Data /// Retrieves the class name for this object. If null is returned, /// the type name is used. /// - string ICustomTypeDescriptor.GetClassName() => null; + string? ICustomTypeDescriptor.GetClassName() => null; /// /// Retrieves the name for this object. If null is returned, /// the default is used. /// - string ICustomTypeDescriptor.GetComponentName() => null; + string? ICustomTypeDescriptor.GetComponentName() => null; /// /// Retrieves the type converter for this object. /// [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] - TypeConverter ICustomTypeDescriptor.GetConverter() => null; + TypeConverter ICustomTypeDescriptor.GetConverter() => null!; /// /// Retrieves the default event. /// [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] - EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + EventDescriptor? ICustomTypeDescriptor.GetDefaultEvent() => null; /// /// Retrieves the default property. /// [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] - PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + PropertyDescriptor? ICustomTypeDescriptor.GetDefaultProperty() => null; /// /// Retrieves the an editor for this object. /// [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] - object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; + object? ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; /// /// Retrieves an array of events that the given component instance @@ -88,7 +85,7 @@ namespace System.Data /// filtered by the given set of attributes. /// [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[]? attributes) => new EventDescriptorCollection(null); /// @@ -109,15 +106,15 @@ namespace System.Data /// filtered by the given set of attributes. /// [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[]? attributes) => GetPropertiesInternal(); internal PropertyDescriptorCollection GetPropertiesInternal() { if (_propsCollection == null) { - PropertyDescriptor[] props = null; - DataSet dataSet = _dataViewManager.DataSet; + PropertyDescriptor[]? props = null; + DataSet? dataSet = _dataViewManager.DataSet; if (dataSet != null) { int tableCount = dataSet.Tables.Count; @@ -139,6 +136,6 @@ namespace System.Data /// descriptor implementation should return the default object, that is the main /// object that exposes the properties and attributes, /// - object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) => this; + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor? pd) => this; } } diff --git a/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs b/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs index 63dbd993fea..4b13c79f5eb 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SimpleType.cs @@ -121,7 +121,7 @@ namespace System.Data } } - string tempStr = XSDSchema.GetMsdataAttribute(node, Keywords.TARGETNAMESPACE); + string? tempStr = XSDSchema.GetMsdataAttribute(node, Keywords.TARGETNAMESPACE); if (tempStr != null) _ns = tempStr; } @@ -129,7 +129,7 @@ namespace System.Data internal bool IsPlainString() { return ( - XSDSchema.QualifiedName(_baseType) == XSDSchema.QualifiedName("string") && + XSDSchema.QualifiedName(_baseType!) == XSDSchema.QualifiedName("string") && string.IsNullOrEmpty(_name) && _length == -1 && _minLength == -1 && diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs b/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs index 12502ad9da1..df87d51e945 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLDiffLoader.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Diagnostics; using System.Collections; using System.Xml; @@ -14,9 +11,9 @@ namespace System.Data { internal sealed class XMLDiffLoader { - private ArrayList _tables; - private DataSet _dataSet; - private DataTable _dataTable; + private ArrayList? _tables; + private DataSet? _dataSet; + private DataTable? _dataTable; [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void LoadDiffGram(DataSet ds, XmlReader dataTextReader) @@ -42,7 +39,7 @@ namespace System.Data { foreach (DataRelation r in dt.ChildRelations) { - if (!_tables.Contains(r.ChildTable)) + if (!_tables!.Contains(r.ChildTable)) { _tables.Add(r.ChildTable); CreateTablesHierarchy(r.ChildTable); @@ -76,8 +73,8 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessDiffs(DataSet ds, XmlReader ssync) { - DataTable tableBefore; - DataRow row; + DataTable? tableBefore; + DataRow? row; int oldRowRecord; int pos = -1; @@ -89,13 +86,13 @@ namespace System.Data while (iSsyncDepth < ssync.Depth) { tableBefore = null; - string diffId = null; + string? diffId = null; oldRowRecord = -1; // the diffgramm always contains sql:before and sql:after pairs - diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; bool hasErrors = ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE; oldRowRecord = ReadOldRowData(ds, ref tableBefore, ref pos, ssync); if (oldRowRecord == -1) @@ -104,7 +101,7 @@ namespace System.Data if (tableBefore == null) throw ExceptionBuilder.DiffgramMissingSQL(); - row = (DataRow)tableBefore.RowDiffId[diffId]; + row = (DataRow?)tableBefore.RowDiffId[diffId]; if (row != null) { row._oldRecord = oldRowRecord; @@ -129,8 +126,8 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) { - DataTable tableBefore; - DataRow row; + DataTable? tableBefore; + DataRow? row; int oldRowRecord; int pos = -1; @@ -142,13 +139,13 @@ namespace System.Data while (iSsyncDepth < ssync.Depth) { tableBefore = null; - string diffId = null; + string diffId; oldRowRecord = -1; // the diffgramm always contains sql:before and sql:after pairs - diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; bool hasErrors = ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE; oldRowRecord = ReadOldRowData(_dataSet, ref tableBefore, ref pos, ssync); if (oldRowRecord == -1) @@ -157,7 +154,7 @@ namespace System.Data if (tableBefore == null) throw ExceptionBuilder.DiffgramMissingSQL(); - row = (DataRow)tableBefore.RowDiffId[diffId]; + row = (DataRow?)tableBefore.RowDiffId[diffId]; if (row != null) { @@ -183,7 +180,7 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessErrors(DataSet ds, XmlReader ssync) { - DataTable table; + DataTable? table; int iSsyncDepth = ssync.Depth; ssync.Read(); // pass the before node. @@ -193,9 +190,9 @@ namespace System.Data table = ds.Tables.GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI); if (table == null) throw ExceptionBuilder.DiffgramMissingSQL(); - string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); - DataRow row = (DataRow)table.RowDiffId[diffId]; - string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; + DataRow row = (DataRow)table.RowDiffId[diffId]!; + string? rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); if (rowError != null) row.RowError = rowError; int iRowDepth = ssync.Depth; @@ -204,10 +201,10 @@ namespace System.Data { if (XmlNodeType.Element == ssync.NodeType) { - DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]; + DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]!; //if (col == null) // throw exception here - string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS)!; row.SetColumnError(col, colError); } @@ -223,7 +220,7 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void ProcessErrors(ArrayList dt, XmlReader ssync) { - DataTable table; + DataTable? table; int iSsyncDepth = ssync.Depth; ssync.Read(); // pass the before node. @@ -234,14 +231,14 @@ namespace System.Data if (table == null) throw ExceptionBuilder.DiffgramMissingSQL(); - string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS); + string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS)!; - DataRow row = (DataRow)table.RowDiffId[diffId]; + DataRow? row = (DataRow?)table.RowDiffId[diffId]; if (row == null) { for (int i = 0; i < dt.Count; i++) { - row = (DataRow)((DataTable)dt[i]).RowDiffId[diffId]; + row = (DataRow?)((DataTable)dt[i]!).RowDiffId[diffId]; if (row != null) { table = row.Table; @@ -249,9 +246,9 @@ namespace System.Data } } } - string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + string? rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); if (rowError != null) - row.RowError = rowError; + row!.RowError = rowError; int iRowDepth = ssync.Depth; ssync.Read(); // we may be inside a column @@ -259,11 +256,11 @@ namespace System.Data { if (XmlNodeType.Element == ssync.NodeType) { - DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]; + DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI]!; //if (col == null) // throw exception here - string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); - row.SetColumnError(col, colError); + string? colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS); + row!.SetColumnError(col, colError); } ssync.Read(); } @@ -273,17 +270,17 @@ namespace System.Data return; } - private DataTable GetTable(string tableName, string ns) + private DataTable? GetTable(string tableName, string ns) { if (_tables == null) - return _dataSet.Tables.GetTable(tableName, ns); + return _dataSet!.Tables.GetTable(tableName, ns); if (_tables.Count == 0) - return (DataTable)_tables[0]; + return (DataTable?)_tables[0]; for (int i = 0; i < _tables.Count; i++) { - DataTable dt = (DataTable)_tables[i]; + DataTable dt = (DataTable)_tables[i]!; if (string.Equals(dt.TableName, tableName, StringComparison.Ordinal) && string.Equals(dt.Namespace, ns, StringComparison.Ordinal)) return dt; @@ -292,7 +289,7 @@ namespace System.Data } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlReader row) + private int ReadOldRowData(DataSet? ds, ref DataTable? table, ref int pos, XmlReader row) { // read table information if (ds != null) @@ -311,7 +308,7 @@ namespace System.Data } int iRowDepth = row.Depth; - string value = null; + string? value = null; value = row.GetAttribute(Keywords.ROWORDER, Keywords.MSDNS); if (!string.IsNullOrEmpty(value)) @@ -376,7 +373,7 @@ namespace System.Data { string ln = XmlConvert.DecodeName(row.LocalName); string ns = row.NamespaceURI; - DataColumn column = table.Columns[ln, ns]; + DataColumn? column = table.Columns[ln, ns]; if (column == null) { @@ -394,13 +391,13 @@ namespace System.Data (row.GetAttribute(Keywords.TYPE, Keywords.XSINS) != null)); bool skipped = false; - if (column.Table.DataSet != null && column.Table.DataSet._udtIsWrapped) + if (column.Table!.DataSet != null && column.Table.DataSet._udtIsWrapped) { row.Read(); // if UDT is wrapped, skip the wrapper skipped = true; } - XmlRootAttribute xmlAttrib = null; + XmlRootAttribute? xmlAttrib = null; if (!isPolymorphism && !column.ImplementsIXMLSerializable) { // THIS CHECK MAY BE IS WRONG think more diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs index f7cfd54efe8..1094e6ed536 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Data.Common; using System.Xml; using System.Xml.Schema; @@ -44,14 +41,14 @@ namespace System.Data if (name == "Expression" && instance is DataColumn) continue; - PropertyDescriptor pd = TypeDescriptor.GetProperties(instance)[name]; + PropertyDescriptor? pd = TypeDescriptor.GetProperties(instance)[name]; if (pd != null) { // Standard property Type type = pd.PropertyType; TypeConverter converter = XMLSchema.GetConverter(type); - object propValue; + object? propValue; if (converter.CanConvertFrom(typeof(string))) { propValue = converter.ConvertFromInvariantString(value); @@ -74,7 +71,7 @@ namespace System.Data } }// SetProperties - internal static bool FEqualIdentity(XmlNode node, string name, string ns) + internal static bool FEqualIdentity(XmlNode? node, string name, string ns) { if (node != null && node.LocalName == name && node.NamespaceURI == ns) return true; @@ -135,26 +132,26 @@ namespace System.Data internal sealed class XSDSchema : XMLSchema { - private XmlSchemaSet _schemaSet; - private XmlSchemaElement _dsElement; - private DataSet _ds; - private string _schemaName; - private ArrayList _columnExpressions; - private Hashtable _constraintNodes; - private ArrayList _refTables; - private ArrayList _complexTypes; - private XmlSchemaObjectCollection _annotations; - private XmlSchemaObjectCollection _elements; - private Hashtable _attributes; - private Hashtable _elementsTable; - private Hashtable _attributeGroups; - private Hashtable _schemaTypes; - private Hashtable _expressions; - private Dictionary> _tableDictionary; + private XmlSchemaSet? _schemaSet; + private XmlSchemaElement? _dsElement; + private DataSet? _ds; + private string? _schemaName; + private ArrayList? _columnExpressions; + private Hashtable? _constraintNodes; + private ArrayList? _refTables; + private ArrayList? _complexTypes; + private XmlSchemaObjectCollection? _annotations; + private XmlSchemaObjectCollection? _elements; + private Hashtable? _attributes; + private Hashtable? _elementsTable; + private Hashtable? _attributeGroups; + private Hashtable? _schemaTypes; + private Hashtable? _expressions; + private Dictionary>? _tableDictionary; - private Hashtable _udSimpleTypes; + private Hashtable? _udSimpleTypes; - private Hashtable _existingSimpleTypeMap; + private Hashtable? _existingSimpleTypeMap; private bool _fromInference; @@ -189,38 +186,38 @@ namespace System.Data { if (item is XmlSchemaAnnotation) { - _annotations.Add((XmlSchemaAnnotation)item); + _annotations!.Add((XmlSchemaAnnotation)item); } if (item is XmlSchemaElement) { XmlSchemaElement elem = (XmlSchemaElement)item; - _elements.Add(elem); - _elementsTable[elem.QualifiedName] = elem; + _elements!.Add(elem); + _elementsTable![elem.QualifiedName] = elem; } if (item is XmlSchemaAttribute) { XmlSchemaAttribute attr = (XmlSchemaAttribute)item; - _attributes[attr.QualifiedName] = attr; + _attributes![attr.QualifiedName] = attr; } if (item is XmlSchemaAttributeGroup) { XmlSchemaAttributeGroup attr = (XmlSchemaAttributeGroup)item; - _attributeGroups[attr.QualifiedName] = attr; + _attributeGroups![attr.QualifiedName] = attr; } if (item is XmlSchemaType) { - string MSDATATargetNamespace = null; + string? MSDATATargetNamespace = null; if (item is XmlSchemaSimpleType) { MSDATATargetNamespace = XSDSchema.GetMsdataAttribute((XmlSchemaType)item, Keywords.TARGETNAMESPACE); } XmlSchemaType type = (XmlSchemaType)item; - _schemaTypes[type.QualifiedName] = type; + _schemaTypes![type.QualifiedName] = type; // if we have a User Defined simple type, cache it so later we may need for mapping // meanwhile more convinient solution would be to directly use schemaTypes, but it would be more complex to handle - XmlSchemaSimpleType xmlSimpleType = (item as XmlSchemaSimpleType); + XmlSchemaSimpleType? xmlSimpleType = (item as XmlSchemaSimpleType); if (xmlSimpleType != null) { if (_udSimpleTypes == null) @@ -229,9 +226,9 @@ namespace System.Data } _udSimpleTypes[type.QualifiedName.ToString()] = xmlSimpleType; - DataColumn dc = (DataColumn)_existingSimpleTypeMap[type.QualifiedName.ToString()]; + DataColumn? dc = (DataColumn?)_existingSimpleTypeMap![type.QualifiedName.ToString()]; // Assumption is that our simple type qualified name ihas the same output as XmlSchemaSimpleType type.QualifiedName.ToString() - SimpleType tmpSimpleType = (dc != null) ? dc.SimpleType : null; + SimpleType? tmpSimpleType = (dc != null) ? dc.SimpleType : null; if (tmpSimpleType != null) { @@ -239,7 +236,7 @@ namespace System.Data string errorStr = tmpSimpleType.HasConflictingDefinition(tmpDataSimpleType); if (errorStr.Length != 0) { - throw ExceptionBuilder.InvalidDuplicateNamedSimpleTypeDelaration(tmpDataSimpleType.SimpleTypeQualifiedName, errorStr); + throw ExceptionBuilder.InvalidDuplicateNamedSimpleTypeDelaration(tmpDataSimpleType.SimpleTypeQualifiedName!, errorStr); } } } @@ -265,7 +262,7 @@ namespace System.Data } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal static void SetProperties(object instance, XmlAttribute[] attrs) + internal static void SetProperties(object instance, XmlAttribute[]? attrs) { // This is called from both XSD and XDR schemas. // Do we realy need it in XSD ??? @@ -286,7 +283,7 @@ namespace System.Data if (name == "DataType") { - DataColumn col = instance as DataColumn; + DataColumn? col = instance as DataColumn; if (col != null) { col.DataType = DataStorage.GetType(value); @@ -295,14 +292,14 @@ namespace System.Data continue; } - PropertyDescriptor pd = TypeDescriptor.GetProperties(instance)[name]; + PropertyDescriptor? pd = TypeDescriptor.GetProperties(instance)[name]; if (pd != null) { // Standard property Type type = pd.PropertyType; TypeConverter converter = XMLSchema.GetConverter(type); - object propValue; + object? propValue; if (converter.CanConvertFrom(typeof(string))) { propValue = converter.ConvertFromInvariantString(value); @@ -326,9 +323,9 @@ namespace System.Data }// SetProperties [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private static void SetExtProperties(object instance, XmlAttribute[] attrs) + private static void SetExtProperties(object instance, XmlAttribute[]? attrs) { - PropertyCollection props = null; + PropertyCollection? props = null; if (attrs == null) return; for (int i = 0; i < attrs.Length; i++) @@ -337,7 +334,7 @@ namespace System.Data { if (props == null) { - object val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"].GetValue(instance); + object? val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"]!.GetValue(instance); Debug.Assert(val is PropertyCollection, "We can set values only for classes that have ExtendedProperties"); props = (PropertyCollection)val; } @@ -364,11 +361,11 @@ namespace System.Data } }// SetExtProperties - private void HandleColumnExpression(object instance, XmlAttribute[] attrs) + private void HandleColumnExpression(object instance, XmlAttribute[]? attrs) { if (attrs == null) return; - DataColumn dc = instance as DataColumn; + DataColumn? dc = instance as DataColumn; Debug.Assert(dc != null, "HandleColumnExpression is supposed to be called for DataColumn"); if (dc != null) { @@ -381,7 +378,7 @@ namespace System.Data if (_expressions == null) _expressions = new Hashtable(); _expressions[dc] = attrs[i].Value; - _columnExpressions.Add(dc); + _columnExpressions!.Add(dc); break; } } @@ -389,9 +386,9 @@ namespace System.Data } } - internal static string GetMsdataAttribute(XmlSchemaAnnotated node, string ln) + internal static string? GetMsdataAttribute(XmlSchemaAnnotated node, string ln) { - XmlAttribute[] nodeAttributes = node.UnhandledAttributes; + XmlAttribute[]? nodeAttributes = node.UnhandledAttributes; if (nodeAttributes != null) for (int i = 0; i < nodeAttributes.Length; i++) if (nodeAttributes[i].LocalName == ln && nodeAttributes[i].NamespaceURI == Keywords.MSDNS) @@ -402,14 +399,14 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private static void SetExtProperties(object instance, XmlAttributeCollection attrs) { - PropertyCollection props = null; + PropertyCollection? props = null; for (int i = 0; i < attrs.Count; i++) { if (attrs[i].NamespaceURI == Keywords.MSPROPNS) { if (props == null) { - object val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"].GetValue(instance); + object? val = TypeDescriptor.GetProperties(instance)["ExtendedProperties"]!.GetValue(instance); Debug.Assert(val is PropertyCollection, "We can set values only for classes that have ExtendedProperties"); props = (PropertyCollection)val; } @@ -423,7 +420,7 @@ namespace System.Data internal void HandleRefTableProperties(ArrayList RefTables, XmlSchemaElement element) { string typeName = GetInstanceName(element); - DataTable table = _ds.Tables.GetTable(XmlConvert.DecodeName(typeName), element.QualifiedName.Namespace); + DataTable? table = _ds!.Tables.GetTable(XmlConvert.DecodeName(typeName), element.QualifiedName.Namespace); Debug.Assert(table != null, "ref table should have been already created"); SetProperties(table, element.UnhandledAttributes); @@ -441,12 +438,12 @@ namespace System.Data string value; bool fCreateConstraints = false; //if we have a relation, //we do not have constraints - DataRelationCollection rels = _ds.Relations; + DataRelationCollection rels = _ds!.Relations; DataRelation relation; DataColumn[] parentKey; DataColumn[] childKey; - DataTable parent; - DataTable child; + DataTable? parent; + DataTable? child; int keyLength; strName = XmlConvert.DecodeName(node.GetAttribute(Keywords.NAME)); @@ -499,10 +496,10 @@ namespace System.Data for (int i = 0; i < keyLength; i++) { - parentKey[i] = parent.Columns[XmlConvert.DecodeName(parentNames[i])]; + parentKey[i] = parent.Columns[XmlConvert.DecodeName(parentNames[i])]!; if (parentKey[i] == null) throw ExceptionBuilder.ElementTypeNotFound(parentNames[i]); - childKey[i] = child.Columns[XmlConvert.DecodeName(childNames[i])]; + childKey[i] = child.Columns[XmlConvert.DecodeName(childNames[i])]!; if (childKey[i] == null) throw ExceptionBuilder.ElementTypeNotFound(childNames[i]); } @@ -512,7 +509,7 @@ namespace System.Data _ds.Relations.Add(relation); if (FromInference && relation.Nested) { - _tableDictionary[relation.ParentTable].Add(relation.ChildTable); + _tableDictionary![relation.ParentTable].Add(relation.ChildTable); } } @@ -538,7 +535,7 @@ namespace System.Data private bool IsDatasetParticle(XmlSchemaParticle pt) { - XmlSchemaObjectCollection items = GetParticleItems(pt); + XmlSchemaObjectCollection? items = GetParticleItems(pt); if (items == null) return false; // empty element, threat it as table @@ -590,7 +587,7 @@ namespace System.Data return nCount; } - private XmlSchemaElement FindDatasetElement(XmlSchemaObjectCollection elements) + private XmlSchemaElement? FindDatasetElement(XmlSchemaObjectCollection elements) { foreach (XmlSchemaElement XmlElement in elements) { @@ -603,7 +600,7 @@ namespace System.Data if (!GetBooleanAttribute(node, Keywords.MSD_ISDATASET, /*default:*/ true)) return null; - XmlSchemaComplexType ct = node.SchemaType as XmlSchemaComplexType; + XmlSchemaComplexType? ct = node.SchemaType as XmlSchemaComplexType; if (ct == null) return null; @@ -614,7 +611,7 @@ namespace System.Data if (ct.ContentModel is XmlSchemaSimpleContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated? cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; if (cContent is XmlSchemaSimpleContentExtension) { XmlSchemaSimpleContentExtension ccExtension = ((XmlSchemaSimpleContentExtension)cContent); @@ -623,14 +620,14 @@ namespace System.Data } else { - XmlSchemaSimpleContentRestriction ccRestriction = ((XmlSchemaSimpleContentRestriction)cContent); + XmlSchemaSimpleContentRestriction ccRestriction = ((XmlSchemaSimpleContentRestriction)cContent!); if (HasAttributes(ccRestriction.Attributes)) return null; } } - XmlSchemaParticle particle = GetParticle(ct); + XmlSchemaParticle? particle = GetParticle(ct); if (particle != null) { if (!IsDatasetParticle(particle)) @@ -682,7 +679,7 @@ namespace System.Data _schemaName = "NewDataSet"; } ds.DataSetName = XmlConvert.DecodeName(_schemaName); - string ns = schemaRoot.TargetNamespace; + string? ns = schemaRoot.TargetNamespace; if (ds._namespaceURI == null || ds._namespaceURI.Length == 0) {// set just one time, for backward compatibility ds._namespaceURI = (ns == null) ? string.Empty : ns; // see fx\Data\XDO\ReadXml\SchemaM2.xml for more info @@ -753,15 +750,15 @@ namespace System.Data throw ExceptionBuilder.TooManyIsDataSetAttributesInSchema(); } - XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(_dsElement); + XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(_dsElement!)!; if (ct.Particle != null) { - XmlSchemaObjectCollection items = GetParticleItems(ct.Particle); + XmlSchemaObjectCollection? items = GetParticleItems(ct.Particle); if (items != null) { foreach (XmlSchemaAnnotated el in items) { - XmlSchemaElement sel = el as XmlSchemaElement; + XmlSchemaElement? sel = el as XmlSchemaElement; if (null != sel) { if (sel.RefName.Name.Length != 0) @@ -812,11 +809,11 @@ namespace System.Data //just add Expressions, at this point and if ColumnExpressions.Count > 0, this.expressions should not be null for (int i = 0; i < _columnExpressions.Count; i++) { - DataColumn dc = ((DataColumn)(_columnExpressions[i])); - dc.Expression = (string)_expressions[dc]; + DataColumn dc = ((DataColumn)(_columnExpressions[i])!); + dc.Expression = (string)_expressions![dc]!; } - foreach (DataTable dt in ds.Tables) + foreach (DataTable dt in ds!.Tables) { if (dt.NestedParentRelations.Length == 0 && dt.Namespace == ds.Namespace) { @@ -834,14 +831,14 @@ namespace System.Data } } - DataTable tmpTable = ds.Tables[ds.DataSetName, ds.Namespace]; + DataTable? tmpTable = ds.Tables[ds.DataSetName, ds.Namespace]; if (tmpTable != null) // this fix is done to support round-trip problem in case if there is one table with same name and NS tmpTable._fNestedInDataset = true; // this fix is for backward compatability with old inference engine if (FromInference && ds.Tables.Count == 0 && string.Equals(ds.DataSetName, "NewDataSet", StringComparison.Ordinal)) - ds.DataSetName = XmlConvert.DecodeName(((XmlSchemaElement)_elements[0]).Name); + ds.DataSetName = XmlConvert.DecodeName(((XmlSchemaElement)_elements[0]).Name)!; ds._fIsSchemaLoading = false; //reactivate column computations @@ -872,14 +869,14 @@ namespace System.Data foreach (object __items in ann.Items) if (__items is XmlSchemaAppInfo) { - XmlNode[] relations = ((XmlSchemaAppInfo)__items).Markup; + XmlNode[] relations = ((XmlSchemaAppInfo)__items).Markup!; for (int i = 0; i < relations.Length; i++) if (FEqualIdentity(relations[i], Keywords.MSD_RELATION, Keywords.MSDNS)) HandleRelation((XmlElement)relations[i], fNested); } } - internal XmlSchemaObjectCollection GetParticleItems(XmlSchemaParticle pt) + internal XmlSchemaObjectCollection? GetParticleItems(XmlSchemaParticle? pt) { if (pt is XmlSchemaSequence) return ((XmlSchemaSequence)pt).Items; @@ -905,21 +902,21 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleParticle(XmlSchemaParticle pt, DataTable table, ArrayList tableChildren, bool isBase) { - XmlSchemaObjectCollection items = GetParticleItems(pt); + XmlSchemaObjectCollection? items = GetParticleItems(pt); if (items == null) return; foreach (XmlSchemaAnnotated item in items) { - XmlSchemaElement el = item as XmlSchemaElement; + XmlSchemaElement? el = item as XmlSchemaElement; if (el != null) { if (FromInference && pt is XmlSchemaChoice && pt.MaxOccurs > decimal.One && (el.SchemaType is XmlSchemaComplexType)) el.MaxOccurs = pt.MaxOccurs; - DataTable child = null; + DataTable? child = null; // to decide if element is our table, we need to match both name and ns // 286043 - SQL BU Defect Tracking if (((el.Name == null) && (el.RefName.Name == table.EncodedTableName && el.RefName.Namespace == table.Namespace)) || @@ -953,7 +950,7 @@ namespace System.Data } else { - DataRelation relation = null; + DataRelation? relation = null; if (el.Annotation != null) HandleRelations(el.Annotation, true); @@ -1002,8 +999,8 @@ namespace System.Data } else { // XmlSchemaAttributeGroupRef - XmlSchemaAttributeGroupRef groupRef = so as XmlSchemaAttributeGroupRef; - XmlSchemaAttributeGroup schemaGroup = _attributeGroups[groupRef.RefName] as XmlSchemaAttributeGroup; + XmlSchemaAttributeGroupRef? groupRef = so as XmlSchemaAttributeGroupRef; + XmlSchemaAttributeGroup? schemaGroup = _attributeGroups![groupRef!.RefName] as XmlSchemaAttributeGroup; if (schemaGroup != null) { HandleAttributeGroup(schemaGroup, table, isBase); @@ -1024,14 +1021,14 @@ namespace System.Data else { // XmlSchemaAttributeGroupRef XmlSchemaAttributeGroupRef attributeGroupRef = (XmlSchemaAttributeGroupRef)obj; - XmlSchemaAttributeGroup attributeGroupResolved; + XmlSchemaAttributeGroup? attributeGroupResolved; if (attributeGroup.RedefinedAttributeGroup != null && attributeGroupRef.RefName == new XmlQualifiedName(attributeGroup.Name, attributeGroupRef.RefName.Namespace)) { attributeGroupResolved = attributeGroup.RedefinedAttributeGroup; } else { - attributeGroupResolved = (XmlSchemaAttributeGroup)_attributeGroups[attributeGroupRef.RefName]; + attributeGroupResolved = (XmlSchemaAttributeGroup?)_attributeGroups![attributeGroupRef.RefName]; } if (attributeGroupResolved != null) { @@ -1044,8 +1041,8 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayList tableChildren, bool isNillable) { - if (_complexTypes.Contains(ct)) - throw ExceptionBuilder.CircularComplexType(ct.Name); + if (_complexTypes!.Contains(ct)) + throw ExceptionBuilder.CircularComplexType(ct.Name!); bool isBase = false; _complexTypes.Add(ct); @@ -1062,7 +1059,7 @@ namespace System.Data if (ct.ContentModel is XmlSchemaComplexContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated? cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; if (cContent is XmlSchemaComplexContentExtension) { XmlSchemaComplexContentExtension ccExtension = ((XmlSchemaComplexContentExtension)cContent); @@ -1106,7 +1103,7 @@ namespace System.Data else { Debug.Assert(ct.ContentModel is XmlSchemaSimpleContent, "expected simpleContent or complexContent"); - XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated cContent = ((XmlSchemaSimpleContent)(ct.ContentModel)).Content!; if (cContent is XmlSchemaSimpleContentExtension) { XmlSchemaSimpleContentExtension ccExtension = ((XmlSchemaSimpleContentExtension)cContent); @@ -1147,13 +1144,13 @@ namespace System.Data _complexTypes.Remove(ct); } - internal XmlSchemaParticle GetParticle(XmlSchemaComplexType ct) + internal XmlSchemaParticle? GetParticle(XmlSchemaComplexType ct) { if (ct.ContentModel != null) { if (ct.ContentModel is XmlSchemaComplexContent) { - XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content; + XmlSchemaAnnotated cContent = ((XmlSchemaComplexContent)(ct.ContentModel)).Content!; if (cContent is XmlSchemaComplexContentExtension) { return ((XmlSchemaComplexContentExtension)cContent).Particle; @@ -1191,7 +1188,7 @@ namespace System.Data colName = split[split.Length - 1]; colName = XmlConvert.DecodeName(colName); - DataColumn col = table.Columns[colName]; + DataColumn? col = table.Columns[colName]; if (col == null) throw ExceptionBuilder.InvalidField(field); @@ -1209,7 +1206,7 @@ namespace System.Data foreach (XmlSchemaXPath node in keyNode.Fields) { - keyColumns.Add(FindField(table, node.XPath)); + keyColumns.Add(FindField(table, node.XPath!)); } DataColumn[] key = new DataColumn[keyColumns.Count]; @@ -1220,7 +1217,7 @@ namespace System.Data internal bool GetBooleanAttribute(XmlSchemaAnnotated element, string attrName, bool defVal) { - string value = GetMsdataAttribute(element, attrName); + string? value = GetMsdataAttribute(element, attrName); if (value == null || value.Length == 0) { return defVal; @@ -1239,7 +1236,7 @@ namespace System.Data internal string GetStringAttribute(XmlSchemaAnnotated element, string attrName, string defVal) { - string value = GetMsdataAttribute(element, attrName); + string? value = GetMsdataAttribute(element, attrName); if (value == null || value.Length == 0) { return defVal; @@ -1258,7 +1255,7 @@ namespace System.Data */ - internal static AcceptRejectRule TranslateAcceptRejectRule(string strRule) + internal static AcceptRejectRule TranslateAcceptRejectRule(string? strRule) { if (strRule == "Cascade") return AcceptRejectRule.Cascade; @@ -1286,16 +1283,16 @@ namespace System.Data internal void HandleKeyref(XmlSchemaKeyref keyref) { string refer = XmlConvert.DecodeName(keyref.Refer.Name); // check here!!! - string name = XmlConvert.DecodeName(keyref.Name); + string name = XmlConvert.DecodeName(keyref.Name)!; name = GetStringAttribute(keyref, "ConstraintName", /*default:*/ name); // we do not process key defined outside the current node string tableName = GetTableName(keyref); - string tableNs = GetMsdataAttribute(keyref, Keywords.MSD_TABLENS); + string? tableNs = GetMsdataAttribute(keyref, Keywords.MSD_TABLENS); - DataTable table = _ds.Tables.GetTableSmart(tableName, tableNs); + DataTable? table = _ds!.Tables.GetTableSmart(tableName, tableNs); if (table == null) return; @@ -1303,7 +1300,7 @@ namespace System.Data if (refer == null || refer.Length == 0) throw ExceptionBuilder.MissingRefer(name); - ConstraintTable key = (ConstraintTable)_constraintNodes[refer]; + ConstraintTable? key = (ConstraintTable?)_constraintNodes![refer]; if (key == null) { @@ -1313,57 +1310,57 @@ namespace System.Data DataColumn[] pKey = BuildKey(key.constraint, key.table); DataColumn[] fKey = BuildKey(keyref, table); - ForeignKeyConstraint fkc = null; + ForeignKeyConstraint? fkc = null; if (GetBooleanAttribute(keyref, Keywords.MSD_CONSTRAINTONLY, /*default:*/ false)) { - int iExisting = fKey[0].Table.Constraints.InternalIndexOf(name); + int iExisting = fKey[0].Table!.Constraints.InternalIndexOf(name); if (iExisting > -1) { - if (fKey[0].Table.Constraints[iExisting].ConstraintName != name) + if (fKey[0].Table!.Constraints[iExisting].ConstraintName != name) iExisting = -1; } if (iExisting < 0) { fkc = new ForeignKeyConstraint(name, pKey, fKey); - fKey[0].Table.Constraints.Add(fkc); + fKey[0].Table!.Constraints.Add(fkc); } } else { - string relName = XmlConvert.DecodeName(GetStringAttribute(keyref, Keywords.MSD_RELATIONNAME, keyref.Name)); + string relName = XmlConvert.DecodeName(GetStringAttribute(keyref, Keywords.MSD_RELATIONNAME, keyref.Name!)); if (relName == null || relName.Length == 0) relName = name; - int iExisting = fKey[0].Table.DataSet.Relations.InternalIndexOf(relName); + int iExisting = fKey[0].Table!.DataSet!.Relations.InternalIndexOf(relName); if (iExisting > -1) { - if (fKey[0].Table.DataSet.Relations[iExisting].RelationName != relName) + if (fKey[0].Table!.DataSet!.Relations[iExisting].RelationName != relName) iExisting = -1; } - DataRelation relation = null; + DataRelation? relation = null; if (iExisting < 0) { relation = new DataRelation(relName, pKey, fKey); SetExtProperties(relation, keyref.UnhandledAttributes); - pKey[0].Table.DataSet.Relations.Add(relation); + pKey[0].Table!.DataSet!.Relations.Add(relation); if (FromInference && relation.Nested) { - if (_tableDictionary.ContainsKey(relation.ParentTable)) + if (_tableDictionary!.ContainsKey(relation.ParentTable)) { _tableDictionary[relation.ParentTable].Add(relation.ChildTable); } } - fkc = relation.ChildKeyConstraint; + fkc = relation.ChildKeyConstraint!; fkc.ConstraintName = name; } else { - relation = fKey[0].Table.DataSet.Relations[iExisting]; + relation = fKey[0].Table!.DataSet!.Relations[iExisting]; } if (GetBooleanAttribute(keyref, Keywords.MSD_ISNESTED, /*default:*/ false)) { @@ -1371,9 +1368,9 @@ namespace System.Data } } - string acceptRejectRule = GetMsdataAttribute(keyref, Keywords.MSD_ACCEPTREJECTRULE); - string updateRule = GetMsdataAttribute(keyref, Keywords.MSD_UPDATERULE); - string deleteRule = GetMsdataAttribute(keyref, Keywords.MSD_DELETERULE); + string? acceptRejectRule = GetMsdataAttribute(keyref, Keywords.MSD_ACCEPTREJECTRULE); + string? updateRule = GetMsdataAttribute(keyref, Keywords.MSD_UPDATERULE); + string? deleteRule = GetMsdataAttribute(keyref, Keywords.MSD_DELETERULE); if (fkc != null) { @@ -1393,20 +1390,20 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleConstraint(XmlSchemaIdentityConstraint keyNode) { - string name = null; + string? name = null; name = XmlConvert.DecodeName(keyNode.Name); if (name == null || name.Length == 0) throw ExceptionBuilder.MissingAttribute(Keywords.NAME); - if (_constraintNodes.ContainsKey(name)) + if (_constraintNodes!.ContainsKey(name)) throw ExceptionBuilder.DuplicateConstraintRead(name); // we do not process key defined outside the current node string tableName = GetTableName(keyNode); - string tableNs = GetMsdataAttribute(keyNode, Keywords.MSD_TABLENS); + string? tableNs = GetMsdataAttribute(keyNode, Keywords.MSD_TABLENS); - DataTable table = _ds.Tables.GetTableSmart(tableName, tableNs); + DataTable? table = _ds!.Tables.GetTableSmart(tableName, tableNs); if (table == null) return; @@ -1422,19 +1419,19 @@ namespace System.Data if (0 < key.Length) { - UniqueConstraint found = (UniqueConstraint)key[0].Table.Constraints.FindConstraint(new UniqueConstraint(name, key)); + UniqueConstraint? found = (UniqueConstraint?)key[0].Table!.Constraints.FindConstraint(new UniqueConstraint(name, key)); if (found == null) { - key[0].Table.Constraints.Add(name, key, fPrimaryKey); - SetExtProperties(key[0].Table.Constraints[name], keyNode.UnhandledAttributes); + key[0].Table!.Constraints.Add(name, key, fPrimaryKey); + SetExtProperties(key[0].Table!.Constraints[name]!, keyNode.UnhandledAttributes); } else { key = found.ColumnsReference; SetExtProperties(found, keyNode.UnhandledAttributes); if (fPrimaryKey) - key[0].Table.PrimaryKey = key; + key[0].Table!.PrimaryKey = key; } if (keyNode is XmlSchemaKey) { @@ -1447,12 +1444,12 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal DataTable InstantiateSimpleTable(XmlSchemaElement node) { - DataTable table; + DataTable? table; string typeName = XmlConvert.DecodeName(GetInstanceName(node)); string _TableUri; _TableUri = node.QualifiedName.Namespace; - table = _ds.Tables.GetTable(typeName, _TableUri); + table = _ds!.Tables.GetTable(typeName, _TableUri); if (!FromInference && table != null) { @@ -1473,7 +1470,7 @@ namespace System.Data } else { - string prefix = GetPrefix(_TableUri); + string? prefix = GetPrefix(_TableUri); if (prefix != null) table.Prefix = prefix; } @@ -1483,10 +1480,10 @@ namespace System.Data } - XmlSchemaComplexType ct = node.SchemaType as XmlSchemaComplexType; + XmlSchemaComplexType? ct = node.SchemaType as XmlSchemaComplexType; // We assume node.ElementSchemaType.BaseSchemaType to be null for // and not null for - bool isSimpleContent = ((node.ElementSchemaType.BaseXmlSchemaType != null) || (ct != null && ct.ContentModel is XmlSchemaSimpleContent)); + bool isSimpleContent = ((node.ElementSchemaType!.BaseXmlSchemaType != null) || (ct != null && ct.ContentModel is XmlSchemaSimpleContent)); if (!FromInference || (isSimpleContent && table.Columns.Count == 0)) {// for inference backward compatability @@ -1514,7 +1511,7 @@ namespace System.Data _ds.Tables.Add(table); if (FromInference) { - _tableDictionary.Add(table, new List()); + _tableDictionary!.Add(table, new List()); } } @@ -1538,7 +1535,7 @@ namespace System.Data internal string GetInstanceName(XmlSchemaAnnotated node) { - string instanceName = null; + string? instanceName = null; Debug.Assert((node is XmlSchemaElement) || (node is XmlSchemaAttribute), "GetInstanceName should only be called on attribute or elements"); @@ -1562,7 +1559,7 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal DataTable InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType typeNode, bool isRef) { - DataTable table; + DataTable? table; string typeName = GetInstanceName(node); ArrayList tableChildren = new ArrayList(); @@ -1570,7 +1567,7 @@ namespace System.Data _TableUri = node.QualifiedName.Namespace; - table = _ds.Tables.GetTable(XmlConvert.DecodeName(typeName), _TableUri); + table = _ds!.Tables.GetTable(XmlConvert.DecodeName(typeName), _TableUri); // TOD: Do not do this fix // if (table == null && node.RefName.IsEmpty && !IsTopLevelElement(node) && _TableUri != null && _TableUri.Length > 0) { // _TableUri = null; // it means form="qualified", so child element inherits namespace. amirhmy @@ -1587,7 +1584,7 @@ namespace System.Data } if (isRef) - _refTables.Add(_TableUri + ":" + typeName); + _refTables!.Add(_TableUri + ":" + typeName); table = new DataTable(XmlConvert.DecodeName(typeName)); table.TypeName = node.SchemaTypeName; @@ -1596,7 +1593,7 @@ namespace System.Data table.Namespace = GetStringAttribute(node, "targetNamespace", _TableUri); //table.Prefix = node.Prefix; - string value = GetStringAttribute(typeNode, Keywords.MSD_CASESENSITIVE, ""); + string? value = GetStringAttribute(typeNode, Keywords.MSD_CASESENSITIVE, ""); if (value.Length == 0) { value = GetStringAttribute(node, Keywords.MSD_CASESENSITIVE, ""); @@ -1632,7 +1629,7 @@ namespace System.Data } else { - string prefix = GetPrefix(_TableUri); + string? prefix = GetPrefix(_TableUri); if (prefix != null) table.Prefix = prefix; } @@ -1640,13 +1637,13 @@ namespace System.Data _ds.Tables.Add(table); if (FromInference) { - _tableDictionary.Add(table, new List()); + _tableDictionary!.Add(table, new List()); } } - HandleComplexType(typeNode, table, tableChildren, node.IsNillable); + HandleComplexType(typeNode, table!, tableChildren, node.IsNillable); - for (int i = 0; i < table.Columns.Count; i++) + for (int i = 0; i < table!.Columns.Count; i++) table.Columns[i].SetOrdinalInternal(i); /* @@ -1691,7 +1688,7 @@ namespace System.Data { foreach (XmlSchemaIdentityConstraint key in _dsElement.Constraints) { - XmlSchemaKeyref keyref = key as XmlSchemaKeyref; + XmlSchemaKeyref? keyref = key as XmlSchemaKeyref; if (keyref == null) continue; @@ -1700,7 +1697,7 @@ namespace System.Data continue; if (GetTableName(keyref) == _tableChild.TableName) { - if (_tableChild.DataSet.Tables.InternalIndexOf(_tableChild.TableName) < -1) + if (_tableChild.DataSet!.Tables.InternalIndexOf(_tableChild.TableName) < -1) { // if we have multiple tables with the same name if (GetTableNamespace(keyref) == _tableChild.Namespace) { @@ -1715,7 +1712,7 @@ namespace System.Data } } - DataRelation relation = null; + DataRelation? relation = null; DataRelationCollection childRelations = table.ChildRelations; for (int j = 0; j < childRelations.Count; j++) @@ -1762,17 +1759,17 @@ namespace System.Data // setup relationship between parent and this table relation = new DataRelation(table.TableName + "_" + _tableChild.TableName, parentKey, childKey, true); relation.Nested = true; - _tableChild.DataSet.Relations.Add(relation); + _tableChild.DataSet!.Relations.Add(relation); if (FromInference && relation.Nested) { - if (_tableDictionary.ContainsKey(relation.ParentTable)) + if (_tableDictionary!.ContainsKey(relation.ParentTable)) { _tableDictionary[relation.ParentTable].Add(relation.ChildTable); } } } - return (table); + return table; } private sealed class NameType : IComparable @@ -1785,7 +1782,7 @@ namespace System.Data name = n; type = t; } - public int CompareTo(object obj) { return string.Compare(name, (string)obj, StringComparison.Ordinal); } + public int CompareTo(object? obj) { return string.Compare(name, (string?)obj, StringComparison.Ordinal); } }; public static Type XsdtoClr(string xsdTypeName) @@ -1878,7 +1875,7 @@ namespace System.Data { if (_udSimpleTypes != null) { - XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType)_udSimpleTypes[dt]; + XmlSchemaSimpleType? simpleType = (XmlSchemaSimpleType?)_udSimpleTypes[dt]; if (simpleType == null) { // it is not named simple type, it is not XSD type, it should be unsupported type like xs:token throw ExceptionBuilder.UndefinedDatatype(dt); @@ -1889,7 +1886,7 @@ namespace System.Data rootType = rootType.BaseSimpleType; } - return ParseDataType(rootType.BaseType); + return ParseDataType(rootType.BaseType!); } } NameType nt = FindNameType(dt); @@ -1927,39 +1924,39 @@ namespace System.Data } - internal XmlSchemaAnnotated FindTypeNode(XmlSchemaAnnotated node) + internal XmlSchemaAnnotated? FindTypeNode(XmlSchemaAnnotated node) { // this function is returning null // if the typeNode for node is in the XSD namespace. - XmlSchemaAttribute attr = node as XmlSchemaAttribute; - XmlSchemaElement el = node as XmlSchemaElement; + XmlSchemaAttribute? attr = node as XmlSchemaAttribute; + XmlSchemaElement? el = node as XmlSchemaElement; bool isAttr = false; if (attr != null) { isAttr = true; } - string _type = isAttr ? attr.SchemaTypeName.Name : el.SchemaTypeName.Name; - string _typeNs = isAttr ? attr.SchemaTypeName.Namespace : el.SchemaTypeName.Namespace; + string _type = isAttr ? attr!.SchemaTypeName.Name : el!.SchemaTypeName.Name; + string _typeNs = isAttr ? attr!.SchemaTypeName.Namespace : el!.SchemaTypeName.Namespace; if (_typeNs == Keywords.XSDNS) return null; - XmlSchemaAnnotated typeNode; + XmlSchemaAnnotated? typeNode; if (_type == null || _type.Length == 0) { - _type = isAttr ? attr.RefName.Name : el.RefName.Name; + _type = isAttr ? attr!.RefName.Name : el!.RefName.Name; if (_type == null || _type.Length == 0) - typeNode = isAttr ? attr.SchemaType : el.SchemaType; + typeNode = isAttr ? attr!.SchemaType : el!.SchemaType; else - typeNode = isAttr ? FindTypeNode((XmlSchemaAnnotated)_attributes[attr.RefName]) : FindTypeNode((XmlSchemaAnnotated)_elementsTable[el.RefName]); + typeNode = isAttr ? FindTypeNode((XmlSchemaAnnotated)_attributes![attr!.RefName]!) : FindTypeNode((XmlSchemaAnnotated)_elementsTable![el!.RefName]!); } else - typeNode = (XmlSchemaAnnotated)_schemaTypes[isAttr ? ((XmlSchemaAttribute)node).SchemaTypeName : ((XmlSchemaElement)node).SchemaTypeName]; + typeNode = (XmlSchemaAnnotated?)_schemaTypes![isAttr ? ((XmlSchemaAttribute)node).SchemaTypeName : ((XmlSchemaElement)node).SchemaTypeName]; return typeNode; } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, string strType, DataTable table, bool isBase, XmlAttribute[] attrs, bool isNillable) + internal void HandleSimpleTypeSimpleContentColumn(XmlSchemaSimpleType typeNode, string strType, DataTable table, bool isBase, XmlAttribute[]? attrs, bool isNillable) { // disallow multiple simple content columns for the table if (FromInference && table.XmlText != null) @@ -1967,8 +1964,8 @@ namespace System.Data return; } - Type type = null; - SimpleType xsdType = null; + Type? type = null; + SimpleType? xsdType = null; // if (typeNode.QualifiedName.Namespace != Keywords.XSDNS) { // this means UDSimpleType if (typeNode.QualifiedName.Name != null && typeNode.QualifiedName.Name.Length != 0 && typeNode.QualifiedName.Namespace != Keywords.XSDNS) @@ -1979,7 +1976,7 @@ namespace System.Data } else {// previous code V 1.1 - XmlSchemaSimpleType ancestor = typeNode.BaseXmlSchemaType as XmlSchemaSimpleType; + XmlSchemaSimpleType? ancestor = typeNode.BaseXmlSchemaType as XmlSchemaSimpleType; if ((ancestor != null) && (ancestor.QualifiedName.Namespace != Keywords.XSDNS)) { xsdType = new SimpleType(typeNode); @@ -1989,8 +1986,8 @@ namespace System.Data { rootType = rootType.BaseSimpleType; } - type = ParseDataType(rootType.BaseType); - strType = xsdType.Name; + type = ParseDataType(rootType.BaseType!); + strType = xsdType.Name!; } else { @@ -2018,7 +2015,7 @@ namespace System.Data bool isToAdd = true; if ((!isBase) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; } else @@ -2031,7 +2028,7 @@ namespace System.Data SetExtProperties(column, attrs); string tmp = (-1).ToString(CultureInfo.CurrentCulture); - string defValue = null; + string? defValue = null; //try to see if attributes contain allownull column.AllowDBNull = isNillable; @@ -2053,7 +2050,7 @@ namespace System.Data if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } // Update XSD type to point to simple types actual namespace instead of normalized default namespace in case of remoting @@ -2096,7 +2093,7 @@ namespace System.Data } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal void HandleSimpleContentColumn(string strType, DataTable table, bool isBase, XmlAttribute[] attrs, bool isNillable) + internal void HandleSimpleContentColumn(string strType, DataTable table, bool isBase, XmlAttribute[]? attrs, bool isNillable) { // for Named Simple type support : We should not recieved anything here other than string. // there can not be typed simple content @@ -2104,7 +2101,7 @@ namespace System.Data if (FromInference && table.XmlText != null) // backward compatability for inference return; - Type type = null; + Type? type = null; if (strType == null) { return; @@ -2131,7 +2128,7 @@ namespace System.Data if ((!isBase) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; } else @@ -2144,7 +2141,7 @@ namespace System.Data SetExtProperties(column, attrs); string tmp = (-1).ToString(CultureInfo.CurrentCulture); - string defValue = null; + string? defValue = null; //try to see if attributes contain allownull column.AllowDBNull = isNillable; @@ -2166,7 +2163,7 @@ namespace System.Data if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } column.XmlDataType = strType; @@ -2199,13 +2196,13 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleAttributeColumn(XmlSchemaAttribute attrib, DataTable table, bool isBase) { - Type type = null; - XmlSchemaAttribute attr = attrib.Name != null ? attrib : (XmlSchemaAttribute)_attributes[attrib.RefName]; + Type? type = null; + XmlSchemaAttribute? attr = attrib.Name != null ? attrib : (XmlSchemaAttribute)_attributes![attrib.RefName]!; - XmlSchemaAnnotated typeNode = FindTypeNode(attr); - string strType = null; - SimpleType xsdType = null; + XmlSchemaAnnotated? typeNode = FindTypeNode(attr); + string? strType = null; + SimpleType? xsdType = null; if (typeNode == null) { @@ -2225,7 +2222,7 @@ namespace System.Data } else if (typeNode is XmlSchemaSimpleType) { - XmlSchemaSimpleType node = typeNode as XmlSchemaSimpleType; + XmlSchemaSimpleType node = (typeNode as XmlSchemaSimpleType)!; xsdType = new SimpleType(node); if (node.QualifiedName.Name != null && node.QualifiedName.Name.Length != 0 && node.QualifiedName.Namespace != Keywords.XSDNS) { @@ -2235,7 +2232,7 @@ namespace System.Data } else { - type = ParseDataType(xsdType.BaseType); + type = ParseDataType(xsdType.BaseType!); strType = xsdType.Name; if (xsdType.Length == 1 && type == typeof(string)) { @@ -2262,7 +2259,7 @@ namespace System.Data if ((!isBase || FromInference) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; if (FromInference) @@ -2294,12 +2291,12 @@ namespace System.Data if ((column.Expression != null) && (column.Expression.Length != 0)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } if (xsdType != null && xsdType.Name != null && xsdType.Name.Length > 0) { - if (XSDSchema.GetMsdataAttribute(typeNode, Keywords.TARGETNAMESPACE) != null) + if (XSDSchema.GetMsdataAttribute(typeNode!, Keywords.TARGETNAMESPACE) != null) { column.XmlDataType = xsdType.SimpleTypeQualifiedName; } @@ -2330,7 +2327,7 @@ namespace System.Data column.ColumnMapping = MappingType.Hidden; column.AllowDBNull = GetBooleanAttribute(attr, Keywords.MSD_ALLOWDBNULL, true); - string defValue = GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE); + string? defValue = GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE); if (defValue != null) try { @@ -2344,7 +2341,7 @@ namespace System.Data // XDR March change - string strDefault = (attrib.Use == XmlSchemaUse.Required) ? GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE) : attr.DefaultValue; + string? strDefault = (attrib.Use == XmlSchemaUse.Required) ? GetMsdataAttribute(attr, Keywords.MSD_DEFAULTVALUE) : attr.DefaultValue; if ((attr.Use == XmlSchemaUse.Optional) && (strDefault == null)) strDefault = attr.FixedValue; @@ -2362,15 +2359,15 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleElementColumn(XmlSchemaElement elem, DataTable table, bool isBase) { - Type type = null; - XmlSchemaElement el = elem.Name != null ? elem : (XmlSchemaElement)_elementsTable[elem.RefName]; + Type? type = null; + XmlSchemaElement? el = elem.Name != null ? elem : (XmlSchemaElement?)_elementsTable![elem.RefName]; if (el == null) // it's possible due to some XSD compiler optimizations return; // do nothing - XmlSchemaAnnotated typeNode = FindTypeNode(el); - string strType = null; - SimpleType xsdType = null; + XmlSchemaAnnotated? typeNode = FindTypeNode(el); + string? strType = null; + SimpleType? xsdType = null; if (typeNode == null) { @@ -2387,11 +2384,11 @@ namespace System.Data } else if (typeNode is XmlSchemaSimpleType) { - XmlSchemaSimpleType simpleTypeNode = typeNode as XmlSchemaSimpleType; - xsdType = new SimpleType(simpleTypeNode); + XmlSchemaSimpleType? simpleTypeNode = typeNode as XmlSchemaSimpleType; + xsdType = new SimpleType(simpleTypeNode!); // ((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name.Length != 0 check is for annonymos simple type, // it should be user defined Named simple type - if (((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name.Length != 0 && ((XmlSchemaSimpleType)typeNode).QualifiedName.Namespace != Keywords.XSDNS) + if (((XmlSchemaSimpleType)typeNode).Name != null && ((XmlSchemaSimpleType)typeNode).Name!.Length != 0 && ((XmlSchemaSimpleType)typeNode).QualifiedName.Namespace != Keywords.XSDNS) { strType = ((XmlSchemaSimpleType)typeNode).QualifiedName.ToString(); // use qualifed name type = ParseDataType(strType); @@ -2399,17 +2396,17 @@ namespace System.Data else { simpleTypeNode = (xsdType.XmlBaseType != null && xsdType.XmlBaseType.Namespace != Keywords.XSDNS) ? - _schemaTypes[xsdType.XmlBaseType] as XmlSchemaSimpleType : + _schemaTypes![xsdType.XmlBaseType] as XmlSchemaSimpleType : null; while (simpleTypeNode != null) { xsdType.LoadTypeValues(simpleTypeNode); simpleTypeNode = (xsdType.XmlBaseType != null && xsdType.XmlBaseType.Namespace != Keywords.XSDNS) ? - _schemaTypes[xsdType.XmlBaseType] as XmlSchemaSimpleType : + _schemaTypes![xsdType.XmlBaseType] as XmlSchemaSimpleType : null; } - type = ParseDataType(xsdType.BaseType); + type = ParseDataType(xsdType.BaseType!); strType = xsdType.Name; if (xsdType.Length == 1 && type == typeof(string)) @@ -2448,7 +2445,7 @@ namespace System.Data if (((!isBase) || FromInference) && (table.Columns.Contains(columnName, true))) { - column = table.Columns[columnName]; + column = table.Columns[columnName]!; isToAdd = false; if (FromInference) @@ -2479,13 +2476,13 @@ namespace System.Data if (!string.IsNullOrEmpty(column.Expression)) { - _columnExpressions.Add(column); + _columnExpressions!.Add(column); } // Update XSD type to point to simple types actual namespace instead of normalized default namespace in case of remoting if (xsdType != null && xsdType.Name != null && xsdType.Name.Length > 0) { - if (XSDSchema.GetMsdataAttribute(typeNode, Keywords.TARGETNAMESPACE) != null) + if (XSDSchema.GetMsdataAttribute(typeNode!, Keywords.TARGETNAMESPACE) != null) { column.XmlDataType = xsdType.SimpleTypeQualifiedName; } @@ -2512,7 +2509,7 @@ namespace System.Data } else if (elem.Form == XmlSchemaForm.None) { - XmlSchemaObject e = elem.Parent; + XmlSchemaObject e = elem.Parent!; while (e.Parent != null) { e = e.Parent; @@ -2548,7 +2545,7 @@ namespace System.Data column.Prefix = GetPrefix(column.Namespace); // it can inherit its NS from DataTable, if it is null } - string strDefault = el.DefaultValue; + string? strDefault = el.DefaultValue; if (strDefault != null) try { @@ -2563,22 +2560,22 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal void HandleDataSet(XmlSchemaElement node, bool isNewDataSet) { - string dsName = node.Name; + string? dsName = node.Name; string dsNamespace = node.QualifiedName.Namespace; List tableSequenceList = new List(); - string value = GetMsdataAttribute(node, Keywords.MSD_LOCALE); + string? value = GetMsdataAttribute(node, Keywords.MSD_LOCALE); if (null != value) { // set by user if (0 != value.Length) { // <... msdata:Locale="en-US"/> - _ds.Locale = new CultureInfo(value); + _ds!.Locale = new CultureInfo(value); } else { - _ds.Locale = CultureInfo.InvariantCulture; + _ds!.Locale = CultureInfo.InvariantCulture; } } else @@ -2586,12 +2583,12 @@ namespace System.Data // MSD_LOCALE overrides MSD_USECURRENTLOCALE if (GetBooleanAttribute(node, Keywords.MSD_USECURRENTLOCALE, false)) { - _ds.SetLocaleValue(CultureInfo.CurrentCulture, false); + _ds!.SetLocaleValue(CultureInfo.CurrentCulture, false); } else { // Everett behavior before <... msdata:UseCurrentLocale="true"/> - _ds.SetLocaleValue(new CultureInfo(0x409), false); + _ds!.SetLocaleValue(new CultureInfo(0x409), false); } } @@ -2621,10 +2618,10 @@ namespace System.Data if (FromInference) _ds.Prefix = GetPrefix(_ds.Namespace); - XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(node); + XmlSchemaComplexType ct = (XmlSchemaComplexType)FindTypeNode(node)!; if (ct.Particle != null) { - XmlSchemaObjectCollection items = GetParticleItems(ct.Particle); + XmlSchemaObjectCollection? items = GetParticleItems(ct.Particle); if (items == null) { @@ -2643,7 +2640,7 @@ namespace System.Data } else { - DataTable tempTable = _ds.Tables.GetTable(XmlConvert.DecodeName(GetInstanceName((XmlSchemaElement)el)), node.QualifiedName.Namespace); + DataTable? tempTable = _ds.Tables.GetTable(XmlConvert.DecodeName(GetInstanceName((XmlSchemaElement)el)), node.QualifiedName.Namespace); if (tempTable != null) { tableSequenceList.Add(tempTable); // if ref table is created, add it @@ -2661,14 +2658,14 @@ namespace System.Data } } - DataTable child = HandleTable((XmlSchemaElement)el); + DataTable? child = HandleTable((XmlSchemaElement)el); if (child != null) { child._fNestedInDataset = true; } if (FromInference) { - tableSequenceList.Add(child); + tableSequenceList.Add(child!); } } else if (el is XmlSchemaChoice) @@ -2685,7 +2682,7 @@ namespace System.Data if ((((XmlSchemaElement)choiceEl).RefName.Name.Length != 0) && (!FromInference && ((XmlSchemaElement)choiceEl).MaxOccurs != decimal.One && !(((XmlSchemaElement)choiceEl).SchemaType is XmlSchemaComplexType))) continue; - DataTable child = HandleTable((XmlSchemaElement)choiceEl); + DataTable child = HandleTable((XmlSchemaElement)choiceEl)!; if (FromInference) { tableSequenceList.Add(child); @@ -2705,7 +2702,7 @@ namespace System.Data { foreach (XmlSchemaIdentityConstraint key in node.Constraints) { - XmlSchemaKeyref keyref = key as XmlSchemaKeyref; + XmlSchemaKeyref? keyref = key as XmlSchemaKeyref; if (keyref == null) continue; @@ -2732,18 +2729,18 @@ namespace System.Data if (!tableList.Contains(dt)) { tableList.Add(dt); - foreach (DataTable childTable in _tableDictionary[dt]) + foreach (DataTable childTable in _tableDictionary![dt]) { AddTablesToList(tableList, childTable); } } } - private string GetPrefix(string ns) + private string? GetPrefix(string ns) { if (ns == null) return null; - foreach (XmlSchema schemaRoot in _schemaSet.Schemas()) + foreach (XmlSchema schemaRoot in _schemaSet!.Schemas()) { XmlQualifiedName[] qualifiedNames = schemaRoot.Namespaces.ToArray(); for (int i = 0; i < qualifiedNames.Length; i++) @@ -2755,11 +2752,11 @@ namespace System.Data return null; } - private string GetNamespaceFromPrefix(string prefix) + private string? GetNamespaceFromPrefix(string? prefix) { if ((prefix == null) || (prefix.Length == 0)) return null; - foreach (XmlSchema schemaRoot in _schemaSet.Schemas()) + foreach (XmlSchema schemaRoot in _schemaSet!.Schemas()) { XmlQualifiedName[] qualifiedNames = schemaRoot.Namespaces.ToArray(); for (int i = 0; i < qualifiedNames.Length; i++) @@ -2772,9 +2769,9 @@ namespace System.Data } - private string GetTableNamespace(XmlSchemaIdentityConstraint key) + private string? GetTableNamespace(XmlSchemaIdentityConstraint key) { - string xpath = key.Selector.XPath; + string xpath = key.Selector!.XPath!; string[] split = xpath.Split('/'); string prefix = string.Empty; @@ -2795,7 +2792,7 @@ namespace System.Data private string GetTableName(XmlSchemaIdentityConstraint key) { - string xpath = key.Selector.XPath; + string xpath = key.Selector!.XPath!; string[] split = xpath.Split('/', ':'); string tableName = split[split.Length - 1]; //get the last string after '/' and ':' @@ -2811,7 +2808,7 @@ namespace System.Data if (node.MaxOccurs == decimal.Zero) return false; - XmlAttribute[] attribs = node.UnhandledAttributes; + XmlAttribute[]? attribs = node.UnhandledAttributes; if (attribs != null) { for (int i = 0; i < attribs.Length; i++) @@ -2824,7 +2821,7 @@ namespace System.Data } } - object typeNode = FindTypeNode(node); + object? typeNode = FindTypeNode(node); if ((node.MaxOccurs > decimal.One) && typeNode == null) { @@ -2840,7 +2837,7 @@ namespace System.Data XmlSchemaComplexType ctNode = (XmlSchemaComplexType)typeNode; if (ctNode.IsAbstract) - throw ExceptionBuilder.CannotInstantiateAbstract(node.Name); + throw ExceptionBuilder.CannotInstantiateAbstract(node.Name!); return true; } @@ -2849,19 +2846,19 @@ namespace System.Data // return (elements.IndexOf(node) != -1); // } [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - internal DataTable HandleTable(XmlSchemaElement node) + internal DataTable? HandleTable(XmlSchemaElement node) { if (!IsTable(node)) return null; - object typeNode = FindTypeNode(node); + object? typeNode = FindTypeNode(node); if ((node.MaxOccurs > decimal.One) && typeNode == null) { return InstantiateSimpleTable(node); } - DataTable table = InstantiateTable(node, (XmlSchemaComplexType)typeNode, (node.RefName != null)); // this is wrong , correct check should be node.RefName.IsEmpty + DataTable table = InstantiateTable(node, (XmlSchemaComplexType)typeNode!, (node.RefName != null)); // this is wrong , correct check should be node.RefName.IsEmpty table._fNestedInDataset = false; return table; diff --git a/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs b/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs index b5f5eb00980..3f80ecdf1cb 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XmlDataLoader.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Collections; using System.Collections.Generic; using System.Data.Common; @@ -18,17 +15,17 @@ namespace System.Data { internal sealed class XmlDataLoader { - private readonly DataSet _dataSet; - private XmlToDatasetMap _nodeToSchemaMap; + private readonly DataSet? _dataSet; + private XmlToDatasetMap? _nodeToSchemaMap; private readonly Hashtable _nodeToRowMap; - private readonly Stack _childRowsStack; + private readonly Stack? _childRowsStack; private readonly bool _fIsXdr; internal bool _isDiffgram; - private XmlElement _topMostNode; + private XmlElement? _topMostNode; private readonly bool _ignoreSchema; - private readonly DataTable _dataTable; + private readonly DataTable? _dataTable; private readonly bool _isTableLevel; private bool _fromInference; @@ -96,17 +93,17 @@ namespace System.Data // after loading, all detached DataRows are attached to their tables [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private void AttachRows(DataRow parentRow, XmlNode parentElement) + private void AttachRows(DataRow? parentRow, XmlNode parentElement) { if (parentElement == null) return; - for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = parentElement.FirstChild; n != null; n = n.NextSibling) { if (n.NodeType == XmlNodeType.Element) { XmlElement e = (XmlElement)n; - DataRow r = GetRowFromElement(e); + DataRow? r = GetRowFromElement(e); if (r != null && r.RowState == DataRowState.Detached) { if (parentRow != null) @@ -129,7 +126,7 @@ namespace System.Data private int CountNonNSAttributes(XmlNode node) { int count = 0; - for (int i = 0; i < node.Attributes.Count; i++) + for (int i = 0; i < node.Attributes!.Count; i++) { if (!FExcludedNamespace(node.Attributes[i].NamespaceURI)) count++; @@ -137,9 +134,9 @@ namespace System.Data return count; } - private string GetValueForTextOnlyColums(XmlNode n) + private string GetValueForTextOnlyColums(XmlNode? n) { - string value = null; + string? value = null; // don't consider whitespace while (n != null && (n.NodeType == XmlNodeType.Whitespace || !IsTextLikeNode(n.NodeType))) @@ -173,15 +170,15 @@ namespace System.Data return value; } - private string GetInitialTextFromNodes(ref XmlNode n) + private string GetInitialTextFromNodes(ref XmlNode? n) { - string value = null; + string? value = null; if (n != null) { // don't consider whitespace while (n.NodeType == XmlNodeType.Whitespace) - n = n.NextSibling; + n = n.NextSibling!; if (IsTextLikeNode(n.NodeType) && (n.NextSibling == null || !IsTextLikeNode(n.NodeType))) { @@ -207,7 +204,7 @@ namespace System.Data return value; } - private DataColumn GetTextOnlyColumn(DataRow row) + private DataColumn? GetTextOnlyColumn(DataRow row) { DataColumnCollection columns = row.Table.Columns; int cCols = columns.Count; @@ -220,20 +217,20 @@ namespace System.Data return null; } - internal DataRow GetRowFromElement(XmlElement e) + internal DataRow? GetRowFromElement(XmlElement e) { - return (DataRow)_nodeToRowMap[e]; + return (DataRow?)_nodeToRowMap[e]; } internal bool FColumnElement(XmlElement e) { - if (_nodeToSchemaMap.GetColumnSchema(e, FIgnoreNamespace(e)) == null) + if (_nodeToSchemaMap!.GetColumnSchema(e, FIgnoreNamespace(e)) == null) return false; if (CountNonNSAttributes(e) > 0) return false; - for (XmlNode tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) + for (XmlNode? tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) if (tabNode is XmlElement) return false; @@ -247,11 +244,11 @@ namespace System.Data private bool FIgnoreNamespace(XmlNode node) { - XmlNode ownerNode; + XmlNode? ownerNode; if (!_fIsXdr) return false; if (node is XmlAttribute) - ownerNode = ((XmlAttribute)node).OwnerElement; + ownerNode = ((XmlAttribute)node).OwnerElement!; else ownerNode = node; if (ownerNode.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal)) @@ -304,23 +301,23 @@ namespace System.Data if (_isTableLevel) { - saveEnforce = _dataTable.EnforceConstraints; + saveEnforce = _dataTable!.EnforceConstraints; _dataTable.EnforceConstraints = false; } else { - saveEnforce = _dataSet.EnforceConstraints; + saveEnforce = _dataSet!.EnforceConstraints; _dataSet.EnforceConstraints = false; _dataSet._fInReadXml = true; } if (_isTableLevel) { - _nodeToSchemaMap = new XmlToDatasetMap(_dataTable, xdoc.NameTable); + _nodeToSchemaMap = new XmlToDatasetMap(_dataTable!, xdoc.NameTable); } else { - _nodeToSchemaMap = new XmlToDatasetMap(_dataSet, xdoc.NameTable); + _nodeToSchemaMap = new XmlToDatasetMap(_dataSet!, xdoc.NameTable); } /* // Top level table or dataset ? @@ -341,11 +338,11 @@ namespace System.Data } } */ - DataRow topRow = null; + DataRow? topRow = null; if (_isTableLevel || (_dataSet != null && _dataSet._fTopLevelTable)) { XmlElement e = xdoc.DocumentElement; - DataTable topTable = (DataTable)_nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + DataTable? topTable = (DataTable?)_nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); if (topTable != null) { topRow = topTable.CreateEmptyRow(); //enzol perf @@ -363,11 +360,11 @@ namespace System.Data if (_isTableLevel) { - _dataTable.EnforceConstraints = saveEnforce; + _dataTable!.EnforceConstraints = saveEnforce; } else { - _dataSet._fInReadXml = false; + _dataSet!._fInReadXml = false; _dataSet.EnforceConstraints = saveEnforce; } } @@ -375,7 +372,7 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private void LoadRowData(DataRow row, XmlElement rowElement) { - XmlNode n; + XmlNode? n; DataTable table = row.Table; if (FromInference) table.Prefix = rowElement.Prefix; @@ -389,7 +386,7 @@ namespace System.Data n = rowElement.FirstChild; // Look for data to fill the TextOnly column - DataColumn column = GetTextOnlyColumn(row); + DataColumn? column = GetTextOnlyColumn(row); if (column != null) { foundColumns[column] = column; @@ -407,7 +404,7 @@ namespace System.Data { XmlElement e = (XmlElement)n; - object schema = _nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + object? schema = _nodeToSchemaMap!.GetSchemaForNode(e, FIgnoreNamespace(e)); if (schema is DataTable) { if (FColumnElement(e)) @@ -447,7 +444,7 @@ namespace System.Data } // if no more siblings, ascend back toward original element (rowElement) - while (n != rowElement && n.NextSibling == null) + while (n != rowElement && n!.NextSibling == null) { n = n.ParentNode; } @@ -461,7 +458,7 @@ namespace System.Data // foreach (XmlAttribute attr in rowElement.Attributes) { - object schema = _nodeToSchemaMap.GetColumnSchema(attr, FIgnoreNamespace(attr)); + object? schema = _nodeToSchemaMap!.GetColumnSchema(attr, FIgnoreNamespace(attr)); if (schema != null && schema is DataColumn) { DataColumn c = (DataColumn)schema; @@ -504,7 +501,7 @@ namespace System.Data // load all data from tree structre into datarows [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] - private void LoadRows(DataRow parentRow, XmlNode parentElement) + private void LoadRows(DataRow? parentRow, XmlNode parentElement) { if (parentElement == null) return; @@ -515,16 +512,16 @@ namespace System.Data parentElement.LocalName == Keywords.XDR_SCHEMA && parentElement.NamespaceURI == Keywords.XDRNS) return; - for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) + for (XmlNode? n = parentElement.FirstChild; n != null; n = n.NextSibling) { if (n is XmlElement) { XmlElement e = (XmlElement)n; - object schema = _nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e)); + object? schema = _nodeToSchemaMap!.GetSchemaForNode(e, FIgnoreNamespace(e)); if (schema != null && schema is DataTable) { - DataRow r = GetRowFromElement(e); + DataRow? r = GetRowFromElement(e); if (r == null) { // skip columns which has the same name as another table @@ -556,24 +553,24 @@ namespace System.Data row[col] = col.ConvertXmlToObject(xmlText); } - private XmlReader _dataReader; - private object _XSD_XMLNS_NS; - private object _XDR_SCHEMA; - private object _XDRNS; - private object _SQL_SYNC; - private object _UPDGNS; - private object _XSD_SCHEMA; - private object _XSDNS; + private XmlReader? _dataReader; + private object? _XSD_XMLNS_NS; + private object? _XDR_SCHEMA; + private object? _XDRNS; + private object? _SQL_SYNC; + private object? _UPDGNS; + private object? _XSD_SCHEMA; + private object? _XSDNS; - private object _DFFNS; - private object _MSDNS; - private object _DIFFID; - private object _HASCHANGES; - private object _ROWORDER; + private object? _DFFNS; + private object? _MSDNS; + private object? _DIFFID; + private object? _HASCHANGES; + private object? _ROWORDER; private void InitNameTable() { - XmlNameTable nameTable = _dataReader.NameTable; + XmlNameTable nameTable = _dataReader!.NameTable; _XSD_XMLNS_NS = nameTable.Add(Keywords.XSD_XMLNS_NS); _XDR_SCHEMA = nameTable.Add(Keywords.XDR_SCHEMA); @@ -597,23 +594,23 @@ namespace System.Data int entryDepth = _dataReader.Depth; // Store current XML element depth so we'll read // correct portion of the XML and no more - bool fEnforce = _isTableLevel ? _dataTable.EnforceConstraints : _dataSet.EnforceConstraints; + bool fEnforce = _isTableLevel ? _dataTable!.EnforceConstraints : _dataSet!.EnforceConstraints; // Keep constraints status for datataset/table InitNameTable(); // Adds DataSet namespaces to reader's nametable if (_nodeToSchemaMap == null) { // Create XML to dataset map - _nodeToSchemaMap = _isTableLevel ? new XmlToDatasetMap(_dataReader.NameTable, _dataTable) : - new XmlToDatasetMap(_dataReader.NameTable, _dataSet); + _nodeToSchemaMap = _isTableLevel ? new XmlToDatasetMap(_dataReader.NameTable, _dataTable!) : + new XmlToDatasetMap(_dataReader.NameTable, _dataSet!); } if (_isTableLevel) { - _dataTable.EnforceConstraints = false; // Disable constraints + _dataTable!.EnforceConstraints = false; // Disable constraints } else { - _dataSet.EnforceConstraints = false; // Disable constraints + _dataSet!.EnforceConstraints = false; // Disable constraints _dataSet._fInReadXml = true; // We're in ReadXml now } @@ -621,7 +618,7 @@ namespace System.Data { // Do we have top node? if (!_isDiffgram && !_isTableLevel) { // Not a diffgram and not DataSet? - DataTable table = _nodeToSchemaMap.GetSchemaForNode(_topMostNode, FIgnoreNamespace(_topMostNode)) as DataTable; + DataTable? table = _nodeToSchemaMap.GetSchemaForNode(_topMostNode, FIgnoreNamespace(_topMostNode)) as DataTable; // Try to match table in the dataset to this node if (table != null) { // Got the table ? @@ -642,7 +639,7 @@ namespace System.Data _dataReader.Read(); continue; } - DataTable table = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? table = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (table == null) { // Read till table is found @@ -658,11 +655,11 @@ namespace System.Data if (_isTableLevel) { - _dataTable.EnforceConstraints = fEnforce; // Restore constraints and return + _dataTable!.EnforceConstraints = fEnforce; // Restore constraints and return } else { - _dataSet._fInReadXml = false; // We're done. + _dataSet!._fInReadXml = false; // We're done. _dataSet.EnforceConstraints = fEnforce; // Restore constraints and return } } @@ -701,35 +698,35 @@ namespace System.Data Debug.Assert(_topMostNode != null, "topMostNode is null on LoadTopMostTable() entry"); Debug.Assert(!_isDiffgram, "Diffgram mode is on while we have topMostNode table. This is bad."); - bool topNodeIsTable = _isTableLevel || (_dataSet.DataSetName != table.TableName); + bool topNodeIsTable = _isTableLevel || (_dataSet!.DataSetName != table.TableName); // If table name we have matches dataset // name top node could be a DataSet OR a table. // It's a table overwise. - DataRow row = null; // Data row we're going to add to this table + DataRow? row = null; // Data row we're going to add to this table bool matchFound = false; // Assume we found no matching elements - int entryDepth = _dataReader.Depth - 1; // Store current reader depth so we know when to stop reading + int entryDepth = _dataReader!.Depth - 1; // Store current reader depth so we know when to stop reading // Adjust depth by one as we've read top most element // outside this method. string textNodeValue; // Value of a text node we might have Debug.Assert(entryDepth >= 0, "Wrong entry Depth for top most element."); - int entryChild = _childRowsStack.Count; // Memorize child stack level on entry + int entryChild = _childRowsStack!.Count; // Memorize child stack level on entry - DataColumn c; // Hold column here + DataColumn? c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; // This is the columns data we might find - XmlNode n; // Need this to pass by reference + XmlNode? n; // Need this to pass by reference foreach (XmlAttribute attr in _topMostNode.Attributes) { // Check all attributes in this node - c = _nodeToSchemaMap.GetColumnSchema(attr, FIgnoreNamespace(attr)) as DataColumn; + c = _nodeToSchemaMap!.GetColumnSchema(attr, FIgnoreNamespace(attr)) as DataColumn; // Try to match attribute to column if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) { @@ -752,7 +749,7 @@ namespace System.Data switch (_dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element - object o = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? @@ -773,7 +770,7 @@ namespace System.Data } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { // Do we have matched table in DataSet ? @@ -883,12 +880,12 @@ namespace System.Data Debug.Assert(table != null, "Table to be loaded is null on LoadTable() entry"); - DataRow row = null; // Data row we're going to add to this table + DataRow? row = null; // Data row we're going to add to this table - int entryDepth = _dataReader.Depth; // Store current reader depth so we know when to stop reading - int entryChild = _childRowsStack.Count; // Memorize child stack level on entry + int entryDepth = _dataReader!.Depth; // Store current reader depth so we know when to stop reading + int entryChild = _childRowsStack!.Count; // Memorize child stack level on entry - DataColumn c; // Hold column here + DataColumn? c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; @@ -897,7 +894,7 @@ namespace System.Data int rowOrder = -1; // Row to insert data to string diffId = string.Empty; // Diffgram ID string - string hasChanges = null; // Changes string + string? hasChanges = null; // Changes string bool hasErrors = false; // Set this in case of problem string textNodeValue; // Value of a text node we might have @@ -909,7 +906,7 @@ namespace System.Data // Check all attributes one by one _dataReader.MoveToAttribute(i); // Get this attribute - c = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)) as DataColumn; + c = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)) as DataColumn; // Try to get column for this attribute if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) @@ -980,7 +977,7 @@ namespace System.Data switch (_dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element - object o = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? @@ -1000,7 +997,7 @@ namespace System.Data } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { // Do we have matched nested table in DataSet ? @@ -1019,7 +1016,7 @@ namespace System.Data // but we'll try to load it so we could keep compatibility. // We won't try to match to columns as we have no idea // which table this potential column might belong to. - DataTable misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (misplacedTable != null) @@ -1153,9 +1150,9 @@ namespace System.Data // This is how .NET Framework works string text = string.Empty; // Column text. Assume empty string - string xsiNilString = null; // Possible NIL attribute string + string? xsiNilString = null; // Possible NIL attribute string - int entryDepth = _dataReader.Depth; // Store depth so we won't read too much + int entryDepth = _dataReader!.Depth; // Store depth so we won't read too much if (_dataReader.AttributeCount > 0) // If have attributes xsiNilString = _dataReader.GetAttribute(Keywords.XSI_NIL, Keywords.XSINS); @@ -1163,12 +1160,12 @@ namespace System.Data // We have to do it before we move to the next element if (column.IsCustomType) { // Custom type column - object columnValue = null; // Column value we're after. Assume no value. + object? columnValue = null; // Column value we're after. Assume no value. - string xsiTypeString = null; // XSI type name from TYPE attribute - string typeName = null; // Type name from MSD_INSTANCETYPE attribute + string? xsiTypeString = null; // XSI type name from TYPE attribute + string? typeName = null; // Type name from MSD_INSTANCETYPE attribute - XmlRootAttribute xmlAttrib = null; // Might need this attribute for XmlSerializer + XmlRootAttribute? xmlAttrib = null; // Might need this attribute for XmlSerializer if (_dataReader.AttributeCount > 0) { // If have attributes, get attributes we'll need @@ -1209,7 +1206,7 @@ namespace System.Data { // No NIL attribute. Get value bool skipped = false; - if (column.Table.DataSet != null && column.Table.DataSet._udtIsWrapped) + if (column.Table!.DataSet != null && column.Table.DataSet._udtIsWrapped) { _dataReader.Read(); // if UDT is wrapped, skip the wrapper skipped = true; @@ -1257,7 +1254,7 @@ namespace System.Data text = _dataReader.Value; // Get value. // See if we have other text nodes near. In most cases this loop will not be executed. - StringBuilder builder = null; + StringBuilder? builder = null; while (_dataReader.Read() && entryDepth < _dataReader.Depth && IsTextLikeNode(_dataReader.NodeType)) { if (builder == null) @@ -1291,9 +1288,9 @@ namespace System.Data // We've got element which is not supposed to he here. // That might be table which was misplaced. // Or it might be a column inside column (also misplaced). - object o = _nodeToSchemaMap.GetColumnSchema(column.Table, _dataReader, FIgnoreNamespace(_dataReader)); + object? o = _nodeToSchemaMap!.GetColumnSchema(column.Table!, _dataReader, FIgnoreNamespace(_dataReader)); // Get dataset element for this XML element - DataColumn c = o as DataColumn; // Perhaps, it's a column? + DataColumn? c = o as DataColumn; // Perhaps, it's a column? if (c != null) { // Do we have matched column in this table? @@ -1312,7 +1309,7 @@ namespace System.Data } else { - DataTable nestedTable = o as DataTable; + DataTable? nestedTable = o as DataTable; // Perhaps, it's a nested table ? if (nestedTable != null) { @@ -1324,7 +1321,7 @@ namespace System.Data { // Not a nested column nor nested table. // Let's try other tables in the DataSet - DataTable misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); + DataTable? misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader)); // Try to get table for node if (misplacedTable != null) { @@ -1370,7 +1367,7 @@ namespace System.Data [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] private bool ProcessXsdSchema() { - if (((object)_dataReader.LocalName == _XSD_SCHEMA && (object)_dataReader.NamespaceURI == _XSDNS)) + if (((object)_dataReader!.LocalName == _XSD_SCHEMA && (object)_dataReader.NamespaceURI == _XSDNS)) { // Found XSD schema if (_ignoreSchema) @@ -1381,12 +1378,12 @@ namespace System.Data { // Have to load schema. if (_isTableLevel) { // Loading into the DataTable ? - _dataTable.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a table + _dataTable!.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a table _nodeToSchemaMap = new XmlToDatasetMap(_dataReader.NameTable, _dataTable); } // Rebuild XML to DataSet map with new schema. else { // Loading into the DataSet ? - _dataSet.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a DataSet + _dataSet!.ReadXSDSchema(_dataReader, false); // Invoke ReadXSDSchema on a DataSet _nodeToSchemaMap = new XmlToDatasetMap(_dataReader.NameTable, _dataSet); } // Rebuild XML to DataSet map with new schema. } diff --git a/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs b/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs index f74bf3799fa..1c042b21029 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XmlToDatasetMap.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: Enable after System.Private.Xml is annotated -#nullable disable - using System.Xml; using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Data { @@ -18,8 +16,8 @@ namespace System.Data private sealed class XmlNodeIdentety { public string LocalName; - public string NamespaceURI; - public XmlNodeIdentety(string localName, string namespaceURI) + public string? NamespaceURI; + public XmlNodeIdentety(string localName, string? namespaceURI) { LocalName = localName; NamespaceURI = namespaceURI; @@ -28,9 +26,9 @@ namespace System.Data { return LocalName.GetHashCode(); } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { - XmlNodeIdentety id = (XmlNodeIdentety)obj; + XmlNodeIdentety id = (XmlNodeIdentety)obj!; return ( (string.Equals(LocalName, id.LocalName, StringComparison.OrdinalIgnoreCase)) && (string.Equals(NamespaceURI, id.NamespaceURI, StringComparison.OrdinalIgnoreCase)) @@ -46,7 +44,7 @@ namespace System.Data public XmlNodeIdHashtable(int capacity) : base(capacity) { } - public object this[XmlNode node] + public object? this[XmlNode node] { get { @@ -56,7 +54,7 @@ namespace System.Data } } - public object this[XmlReader dataReader] + public object? this[XmlReader dataReader] { get { @@ -66,7 +64,7 @@ namespace System.Data } } - public object this[DataTable table] + public object? this[DataTable table] { get { @@ -76,7 +74,7 @@ namespace System.Data } } - public object this[string name] + public object? this[string name] { get { @@ -100,7 +98,7 @@ namespace System.Data private XmlNodeIdHashtable _tableSchemaMap; // Holds all the tables information - private TableSchemaInfo _lastTableSchemaInfo; + private TableSchemaInfo? _lastTableSchemaInfo; // Used to infer schema @@ -144,7 +142,7 @@ namespace System.Data // Used to infere schema - private TableSchemaInfo AddTableSchema(DataTable table, XmlNameTable nameTable) + private TableSchemaInfo? AddTableSchema(DataTable table, XmlNameTable nameTable) { // SDUB: Because in our case reader already read the document all names that we can meet in the // document already has an entry in NameTable. @@ -153,8 +151,8 @@ namespace System.Data // First case deals with decoded names; Second one with encoded names. // We decided encoded names in first case (instead of decoding them in second) // because it save us time in LoadRows(). We have, as usual, more data them schemas - string tableLocalName = nameTable.Get(table.EncodedTableName); - string tableNamespace = nameTable.Get(table.Namespace); + string? tableLocalName = nameTable.Get(table.EncodedTableName); + string? tableNamespace = nameTable.Get(table.Namespace); if (tableLocalName == null) { // because name of this table isn't present in XML we don't need mapping for it. @@ -178,7 +176,7 @@ namespace System.Data string _tableLocalName = table.EncodedTableName; // Table name - string tableLocalName = nameTable.Get(_tableLocalName); // Look it up in nametable + string? tableLocalName = nameTable.Get(_tableLocalName); // Look it up in nametable if (tableLocalName == null) { // If not found @@ -187,7 +185,7 @@ namespace System.Data table._encodedTableName = tableLocalName; // And set it back - string tableNamespace = nameTable.Get(table.Namespace); // Look ip table namespace + string? tableNamespace = nameTable.Get(table.Namespace); // Look ip table namespace if (tableNamespace == null) { // If not found @@ -211,8 +209,8 @@ namespace System.Data private bool AddColumnSchema(DataColumn col, XmlNameTable nameTable, XmlNodeIdHashtable columns) { - string columnLocalName = nameTable.Get(col.EncodedColumnName); - string columnNamespace = nameTable.Get(col.Namespace); + string? columnLocalName = nameTable.Get(col.EncodedColumnName); + string? columnNamespace = nameTable.Get(col.Namespace); if (columnLocalName == null) { return false; @@ -233,7 +231,7 @@ namespace System.Data private bool AddColumnSchema(XmlNameTable nameTable, DataColumn col, XmlNodeIdHashtable columns) { string _columnLocalName = XmlConvert.EncodeLocalName(col.ColumnName); - string columnLocalName = nameTable.Get(_columnLocalName); // Look it up in a name table + string? columnLocalName = nameTable.Get(_columnLocalName); // Look it up in a name table if (columnLocalName == null) { // Not found? @@ -242,7 +240,7 @@ namespace System.Data col._encodedColumnName = columnLocalName; // And set it back - string columnNamespace = nameTable.Get(col.Namespace); // Get column namespace from nametable + string? columnNamespace = nameTable.Get(col.Namespace); // Get column namespace from nametable if (columnNamespace == null) { // Not found ? @@ -266,13 +264,14 @@ namespace System.Data return true; } + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(DataSet dataSet, XmlNameTable nameTable) { _tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count); foreach (DataTable t in dataSet.Tables) { - TableSchemaInfo tableSchemaInfo = AddTableSchema(t, nameTable); + TableSchemaInfo? tableSchemaInfo = AddTableSchema(t, nameTable); if (tableSchemaInfo != null) { foreach (DataColumn c in t.Columns) @@ -288,7 +287,7 @@ namespace System.Data } // This one is used while reading data with preloaded schema - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) { _tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count); @@ -298,7 +297,7 @@ namespace System.Data // Hash tables with columns schema maps // and child tables schema maps - string dsNamespace = nameTable.Get(dataSet.Namespace); // Attept to look up DataSet namespace + string? dsNamespace = nameTable.Get(dataSet.Namespace); // Attept to look up DataSet namespace // in the name table if (dsNamespace == null) @@ -335,14 +334,14 @@ namespace System.Data // Handle namespaces and names as usuall string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName); - string tableLocalName = nameTable.Get(_tableLocalName); + string? tableLocalName = nameTable.Get(_tableLocalName); if (tableLocalName == null) { tableLocalName = nameTable.Add(_tableLocalName); } - string tableNamespace = nameTable.Get(r.ChildTable.Namespace); + string? tableNamespace = nameTable.Get(r.ChildTable.Namespace); if (tableNamespace == null) { @@ -358,12 +357,12 @@ namespace System.Data } // Used for inference - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(DataTable dataTable, XmlNameTable nameTable) { _tableSchemaMap = new XmlNodeIdHashtable(1); - TableSchemaInfo tableSchemaInfo = AddTableSchema(dataTable, nameTable); + TableSchemaInfo? tableSchemaInfo = AddTableSchema(dataTable, nameTable); if (tableSchemaInfo != null) { foreach (DataColumn c in dataTable.Columns) @@ -378,7 +377,7 @@ namespace System.Data } // This one is used while reading data with preloaded schema - + [MemberNotNull(nameof(_tableSchemaMap))] private void BuildIdentityMap(XmlNameTable nameTable, DataTable dataTable) { ArrayList tableList = GetSelfAndDescendants(dataTable); // Get list of tables we're loading @@ -413,14 +412,14 @@ namespace System.Data // Handle namespaces and names as usuall string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName); - string tableLocalName = nameTable.Get(_tableLocalName); + string? tableLocalName = nameTable.Get(_tableLocalName); if (tableLocalName == null) { tableLocalName = nameTable.Add(_tableLocalName); } - string tableNamespace = nameTable.Get(r.ChildTable.Namespace); + string? tableNamespace = nameTable.Get(r.ChildTable.Namespace); if (tableNamespace == null) { @@ -443,7 +442,7 @@ namespace System.Data while (nCounter < tableList.Count) { - foreach (DataRelation childRelations in ((DataTable)tableList[nCounter]).ChildRelations) + foreach (DataRelation childRelations in ((DataTable)tableList[nCounter]!).ChildRelations) { if (!tableList.Contains(childRelations.ChildTable)) tableList.Add(childRelations.ChildTable); @@ -455,12 +454,12 @@ namespace System.Data } // Used to infer schema and top most node - public object GetColumnSchema(XmlNode node, bool fIgnoreNamespace) + public object? GetColumnSchema(XmlNode node, bool fIgnoreNamespace) { Debug.Assert(node != null, "Argument validation"); - TableSchemaInfo tableSchemaInfo = null; + TableSchemaInfo? tableSchemaInfo = null; - XmlNode nodeRegion = (node.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)node).OwnerElement : node.ParentNode; + XmlNode? nodeRegion = (node.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)node).OwnerElement : node.ParentNode; do { @@ -468,7 +467,7 @@ namespace System.Data { return null; } - tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[nodeRegion.LocalName] : _tableSchemaMap[nodeRegion]); + tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[nodeRegion.LocalName] : _tableSchemaMap[nodeRegion]); nodeRegion = nodeRegion.ParentNode; } while (tableSchemaInfo == null); @@ -480,11 +479,11 @@ namespace System.Data } - public object GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnoreNamespace) + public object? GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnoreNamespace) { if ((_lastTableSchemaInfo == null) || (_lastTableSchemaInfo.TableSchema != table)) { - _lastTableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[table.EncodedTableName] : _tableSchemaMap[table]); + _lastTableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[table.EncodedTableName]! : _tableSchemaMap[table]!); } if (fIgnoreNamespace) @@ -494,13 +493,13 @@ namespace System.Data // Used to infer schema - public object GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) + public object? GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) { - TableSchemaInfo tableSchemaInfo = null; + TableSchemaInfo? tableSchemaInfo = null; if (node.NodeType == XmlNodeType.Element) { // If element - tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); + tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); } // Look up table schema info for it if (tableSchemaInfo != null) @@ -511,9 +510,9 @@ namespace System.Data return GetColumnSchema(node, fIgnoreNamespace); // Attempt to locate column } - public DataTable GetTableForNode(XmlReader node, bool fIgnoreNamespace) + public DataTable? GetTableForNode(XmlReader node, bool fIgnoreNamespace) { - TableSchemaInfo tableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); + TableSchemaInfo? tableSchemaInfo = (TableSchemaInfo?)(fIgnoreNamespace ? _tableSchemaMap[node.LocalName] : _tableSchemaMap[node]); if (tableSchemaInfo != null) { _lastTableSchemaInfo = tableSchemaInfo; @@ -543,7 +542,7 @@ namespace System.Data { nameTable.Add(tempColumnName); } - string columnNamespace = nameTable.Get(col.Namespace); + string? columnNamespace = nameTable.Get(col.Namespace); XmlNodeIdentety idColumn = new XmlNodeIdentety(tempColumnName, columnNamespace); columns[idColumn] = col; } From 9da4d075ef8c581a28c8c171f36ccb05b66f2346 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 9 Jul 2021 17:07:16 -0700 Subject: [PATCH 402/926] Remove Uri scheme validation from HttpRequestMessage (#55035) * Remove Uri scheme validation from HttpRequestMessage * PR feedback Move HttpUtilities to SocketsHttpHandler * Add request scheme check to WinHttpHandler * Skip .NET 6 specific WinHttpHandler test on Framework * Update InteropServices.JavaScript HttpRequestMessage test * Guard HttpMessageInvoker from calling HttpTelemetry with invalid Uris * PR feedback --- .../System/Net/Http/HttpClientHandlerTest.cs | 18 ++++ .../src/Resources/Strings.resx | 6 ++ .../src/System/Net/Http/WinHttpHandler.cs | 11 +++ .../src/Resources/Strings.resx | 9 +- .../src/System.Net.Http.csproj | 4 +- .../HttpUtilities.Browser.cs | 22 ----- .../src/System/Net/Http/HttpClient.cs | 31 ++---- .../src/System/Net/Http/HttpMessageInvoker.cs | 16 ++-- .../src/System/Net/Http/HttpRequestMessage.cs | 65 +++---------- .../System/Net/Http/HttpResponseMessage.cs | 7 +- .../src/System/Net/Http/HttpTelemetry.cs | 2 +- .../System/Net/Http/HttpUtilities.AnyOS.cs | 18 ---- .../Net/Http/MessageProcessingHandler.cs | 4 +- .../{ => SocketsHttpHandler}/HttpUtilities.cs | 32 +------ .../SocketsHttpHandler/SocketsHttpHandler.cs | 14 ++- .../tests/FunctionalTests/HttpClientTest.cs | 12 ++- .../FunctionalTests/HttpRequestMessageTest.cs | 19 +++- .../FunctionalTests/SocketsHttpHandlerTest.cs | 96 +++++++++++++++++++ .../System.Net.Http.Unit.Tests.csproj | 6 +- .../JavaScript/Http/HttpRequestMessageTest.cs | 16 +--- 20 files changed, 219 insertions(+), 189 deletions(-) delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs rename src/libraries/System.Net.Http/src/System/Net/Http/{ => SocketsHttpHandler}/HttpUtilities.cs (50%) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 98c23bdcde0..54497e28618 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -1950,5 +1950,23 @@ namespace System.Net.Http.Functional.Tests await Assert.ThrowsAsync(() => client.GetStringAsync(invalidUri)); } } + + // HttpRequestMessage ctor guards against such Uris before .NET 6. We allow passing relative/unknown Uris to BrowserHttpHandler. + public static bool InvalidRequestUriTest_IsSupported => PlatformDetection.IsNotNetFramework && PlatformDetection.IsNotBrowser; + + [ConditionalFact(nameof(InvalidRequestUriTest_IsSupported))] + public async Task SendAsync_InvalidRequestUri_Throws() + { + using var invoker = new HttpMessageInvoker(CreateHttpClientHandler()); + + var request = new HttpRequestMessage(HttpMethod.Get, (Uri)null); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + + request = new HttpRequestMessage(HttpMethod.Get, new Uri("/relative", UriKind.Relative)); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + + request = new HttpRequestMessage(HttpMethod.Get, new Uri("foo://foo.bar")); + await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None)); + } } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx index 3f4beab8dd8..4fcb089cc50 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx @@ -126,4 +126,10 @@ WinHttpHandler is only supported on .NET Framework and .NET runtimes on Windows. It is not supported for Windows Store Applications (UWP) or Unix platforms. + + The '{0}' scheme is not supported. + + + An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. + diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 63bd0aaf92f..9ce26b2716d 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -578,6 +578,17 @@ namespace System.Net.Http throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } + Uri? requestUri = request.RequestUri; + if (requestUri is null || !requestUri.IsAbsoluteUri) + { + throw new InvalidOperationException(SR.net_http_client_invalid_requesturi); + } + + if (requestUri.Scheme != Uri.UriSchemeHttp && requestUri.Scheme != Uri.UriSchemeHttps) + { + throw new NotSupportedException(SR.Format(SR.net_http_unsupported_requesturi_scheme, requestUri.Scheme)); + } + // Check for invalid combinations of properties. if (_proxy != null && _windowsProxyUsePolicy != WindowsProxyUsePolicy.UseCustomProxy) { diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 35ce6f88feb..03081881762 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -196,13 +196,10 @@ The base address must be an absolute URI. - An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set. + An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. - - Only 'http' and 'https' schemes are allowed. - - - Only 'http', 'https', and 'blob' schemes are allowed. + + The '{0}' scheme is not supported. Value '{0}' is not a valid Base64 string. Error: {1} diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index bea0e12f9fe..49bc41d756e 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -53,7 +53,6 @@ - @@ -180,6 +179,7 @@ + @@ -188,7 +188,6 @@ - @@ -623,7 +622,6 @@ - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs deleted file mode 100644 index 6e9b4b027fe..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/HttpUtilities.Browser.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - -namespace System.Net.Http -{ - internal static partial class HttpUtilities - { - internal static bool IsSupportedNonSecureScheme(string scheme) => - string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) - || IsBlobScheme(scheme) - || IsNonSecureWebSocketScheme(scheme); - - internal static bool IsBlobScheme(string scheme) => - string.Equals(scheme, "blob", StringComparison.OrdinalIgnoreCase); - - internal static string InvalidUriMessage => SR.net_http_client_http_browser_baseaddress_required; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs index 514744fee99..27b0c357cc6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs @@ -26,8 +26,8 @@ namespace System.Net.Http private CancellationTokenSource _pendingRequestsCts; private HttpRequestHeaders? _defaultRequestHeaders; - private Version _defaultRequestVersion = HttpUtilities.DefaultRequestVersion; - private HttpVersionPolicy _defaultVersionPolicy = HttpUtilities.DefaultVersionPolicy; + private Version _defaultRequestVersion = HttpRequestMessage.DefaultRequestVersion; + private HttpVersionPolicy _defaultVersionPolicy = HttpRequestMessage.DefaultVersionPolicy; private Uri? _baseAddress; private TimeSpan _timeout; @@ -78,7 +78,12 @@ namespace System.Net.Http get => _baseAddress; set { - CheckBaseAddress(value, nameof(value)); + // It's OK to not have a base address specified, but if one is, it needs to be absolute. + if (value is not null && !value.IsAbsoluteUri) + { + throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, nameof(value)); + } + CheckDisposedOrStarted(); if (NetEventSource.Log.IsEnabled()) NetEventSource.UriBaseAddress(this, value); @@ -621,7 +626,7 @@ namespace System.Net.Http private static bool StartSend(HttpRequestMessage request) { - if (HttpTelemetry.Log.IsEnabled() && request.RequestUri != null) + if (HttpTelemetry.Log.IsEnabled()) { HttpTelemetry.Log.RequestStart(request); return true; @@ -810,24 +815,6 @@ namespace System.Net.Http return (pendingRequestsCts, DisposeTokenSource: false, pendingRequestsCts); } - private static void CheckBaseAddress(Uri? baseAddress, string parameterName) - { - if (baseAddress == null) - { - return; // It's OK to not have a base address specified. - } - - if (!baseAddress.IsAbsoluteUri) - { - throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, parameterName); - } - - if (!HttpUtilities.IsHttpUri(baseAddress)) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, parameterName); - } - } - private static bool IsNativeHandlerEnabled() { if (!AppContext.TryGetSwitch("System.Net.Http.UseNativeHttpHandler", out bool isEnabled)) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs index 4988469f1ec..9b33c75b4b2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs @@ -34,8 +34,7 @@ namespace System.Net.Http } [UnsupportedOSPlatformAttribute("browser")] - public virtual HttpResponseMessage Send(HttpRequestMessage request, - CancellationToken cancellationToken) + public virtual HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { @@ -43,7 +42,7 @@ namespace System.Net.Http } CheckDisposed(); - if (HttpTelemetry.Log.IsEnabled() && !request.WasSentByHttpClient() && request.RequestUri != null) + if (ShouldSendWithTelemetry(request)) { HttpTelemetry.Log.RequestStart(request); @@ -67,8 +66,7 @@ namespace System.Net.Http } } - public virtual Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + public virtual Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { @@ -76,7 +74,7 @@ namespace System.Net.Http } CheckDisposed(); - if (HttpTelemetry.Log.IsEnabled() && !request.WasSentByHttpClient() && request.RequestUri != null) + if (ShouldSendWithTelemetry(request)) { return SendAsyncWithTelemetry(_handler, request, cancellationToken); } @@ -103,6 +101,12 @@ namespace System.Net.Http } } + private static bool ShouldSendWithTelemetry(HttpRequestMessage request) => + HttpTelemetry.Log.IsEnabled() && + !request.WasSentByHttpClient() && + request.RequestUri is Uri requestUri && + requestUri.IsAbsoluteUri; + internal static bool LogRequestFailed(bool telemetryStarted) { if (HttpTelemetry.Log.IsEnabled() && telemetryStarted) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs index eaa59cd022a..d0c88d60f77 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs @@ -1,17 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Collections.Generic; -using System.Diagnostics; namespace System.Net.Http { public class HttpRequestMessage : IDisposable { + internal static Version DefaultRequestVersion => HttpVersion.Version11; + internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower; + private const int MessageNotYetSent = 0; private const int MessageAlreadySent = 1; @@ -101,29 +102,12 @@ namespace System.Net.Http get { return _requestUri; } set { - if ((value != null) && (value.IsAbsoluteUri) && (!HttpUtilities.IsHttpUri(value))) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, nameof(value)); - } CheckDisposed(); - - // It's OK to set 'null'. HttpClient will add the 'BaseAddress'. If there is no 'BaseAddress' - // sending this message will throw. _requestUri = value; } } - public HttpRequestHeaders Headers - { - get - { - if (_headers == null) - { - _headers = new HttpRequestHeaders(); - } - return _headers; - } - } + public HttpRequestHeaders Headers => _headers ??= new HttpRequestHeaders(); internal bool HasHeaders => _headers != null; @@ -138,23 +122,19 @@ namespace System.Net.Http } public HttpRequestMessage(HttpMethod method, Uri? requestUri) - { - InitializeValues(method, requestUri); - } - - public HttpRequestMessage(HttpMethod method, string? requestUri) { // It's OK to have a 'null' request Uri. If HttpClient is used, the 'BaseAddress' will be added. // If there is no 'BaseAddress', sending this request message will throw. // Note that we also allow the string to be empty: null and empty are considered equivalent. - if (string.IsNullOrEmpty(requestUri)) - { - InitializeValues(method, null); - } - else - { - InitializeValues(method, new Uri(requestUri, UriKind.RelativeOrAbsolute)); - } + _method = method ?? throw new ArgumentNullException(nameof(method)); + _requestUri = requestUri; + _version = DefaultRequestVersion; + _versionPolicy = DefaultVersionPolicy; + } + + public HttpRequestMessage(HttpMethod method, string? requestUri) + : this(method, string.IsNullOrEmpty(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute)) + { } public override string ToString() @@ -179,25 +159,6 @@ namespace System.Net.Http return sb.ToString(); } - [MemberNotNull(nameof(_method))] - [MemberNotNull(nameof(_version))] - private void InitializeValues(HttpMethod method, Uri? requestUri) - { - if (method is null) - { - throw new ArgumentNullException(nameof(method)); - } - if ((requestUri != null) && (requestUri.IsAbsoluteUri) && (!HttpUtilities.IsHttpUri(requestUri))) - { - throw new ArgumentException(HttpUtilities.InvalidUriMessage, nameof(requestUri)); - } - - _method = method; - _requestUri = requestUri; - _version = HttpUtilities.DefaultRequestVersion; - _versionPolicy = HttpUtilities.DefaultVersionPolicy; - } - internal bool MarkAsSent() { return Interlocked.Exchange(ref _sendStatus, MessageAlreadySent) == MessageNotYetSent; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs index 6af856e3585..49f6a6f34a7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs @@ -10,7 +10,8 @@ namespace System.Net.Http { public class HttpResponseMessage : IDisposable { - private const HttpStatusCode defaultStatusCode = HttpStatusCode.OK; + private const HttpStatusCode DefaultStatusCode = HttpStatusCode.OK; + private static Version DefaultResponseVersion => HttpVersion.Version11; private HttpStatusCode _statusCode; private HttpResponseHeaders? _headers; @@ -149,7 +150,7 @@ namespace System.Net.Http } public HttpResponseMessage() - : this(defaultStatusCode) + : this(DefaultStatusCode) { } @@ -161,7 +162,7 @@ namespace System.Net.Http } _statusCode = statusCode; - _version = HttpUtilities.DefaultResponseVersion; + _version = DefaultResponseVersion; } public HttpResponseMessage EnsureSuccessStatusCode() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs index a008637271e..ae52766e081 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpTelemetry.cs @@ -36,7 +36,7 @@ namespace System.Net.Http [NonEvent] public void RequestStart(HttpRequestMessage request) { - Debug.Assert(request.RequestUri != null); + Debug.Assert(request.RequestUri != null && request.RequestUri.IsAbsoluteUri); RequestStart( request.RequestUri.Scheme, diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs deleted file mode 100644 index afa2d7d8551..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.AnyOS.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - -namespace System.Net.Http -{ - internal static partial class HttpUtilities - { - internal static bool IsSupportedNonSecureScheme(string scheme) => - string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) - || IsNonSecureWebSocketScheme(scheme); - - internal static string InvalidUriMessage => SR.net_http_client_http_baseaddress_required; - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs index f903186995e..d0c3748d494 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/MessageProcessingHandler.cs @@ -63,7 +63,7 @@ namespace System.Net.Http // We schedule a continuation task once the inner handler completes in order to trigger the response // processing method. ProcessResponse() is only called if the task wasn't canceled before. - sendAsyncTask.ContinueWithStandard(tcs, static (task, state) => + sendAsyncTask.ContinueWith(static (task, state) => { var sendState = (SendState)state!; MessageProcessingHandler self = sendState._handler; @@ -106,7 +106,7 @@ namespace System.Net.Http // if the operation was canceled: We'll set the Task returned to the user to canceled. Passing the // cancellation token here would result in the continuation task to not be called at all. I.e. we // would never complete the task returned to the caller of SendAsync(). - }); + }, tcs, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } catch (OperationCanceledException e) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs similarity index 50% rename from src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs rename to src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs index 8c9d786fd49..1c2f442d3e2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpUtilities.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpUtilities.cs @@ -1,30 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Threading.Tasks; -using System.Threading; - namespace System.Net.Http { - internal static partial class HttpUtilities + internal static class HttpUtilities { - internal static Version DefaultRequestVersion => HttpVersion.Version11; - - internal static Version DefaultResponseVersion => HttpVersion.Version11; - - internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower; - - internal static bool IsHttpUri(Uri uri) - { - Debug.Assert(uri != null); - return IsSupportedScheme(uri.Scheme); - } - internal static bool IsSupportedScheme(string scheme) => IsSupportedNonSecureScheme(scheme) || IsSupportedSecureScheme(scheme); + internal static bool IsSupportedNonSecureScheme(string scheme) => + string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase) || IsNonSecureWebSocketScheme(scheme); + internal static bool IsSupportedSecureScheme(string scheme) => string.Equals(scheme, "https", StringComparison.OrdinalIgnoreCase) || IsSecureWebSocketScheme(scheme); @@ -41,16 +28,5 @@ namespace System.Net.Http string.Equals(scheme, "socks5", StringComparison.OrdinalIgnoreCase) || string.Equals(scheme, "socks4a", StringComparison.OrdinalIgnoreCase) || string.Equals(scheme, "socks4", StringComparison.OrdinalIgnoreCase); - - // Always specify TaskScheduler.Default to prevent us from using a user defined TaskScheduler.Current. - // - // Since we're not doing any CPU and/or I/O intensive operations, continue on the same thread. - // This results in better performance since the continuation task doesn't get scheduled by the - // scheduler and there are no context switches required. - internal static Task ContinueWithStandard(this Task task, object state, Action, object?> continuation) - { - return task.ContinueWith(continuation, state, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 157ef2dde7e..42361fba080 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Net.Quic; -using System.Net.Quic.Implementations; using System.Net.Security; using System.Runtime.Versioning; using System.Threading; @@ -608,6 +605,17 @@ namespace System.Net.Http } } + Uri? requestUri = request.RequestUri; + if (requestUri is null || !requestUri.IsAbsoluteUri) + { + return new InvalidOperationException(SR.net_http_client_invalid_requesturi); + } + + if (!HttpUtilities.IsSupportedScheme(requestUri.Scheme)) + { + return new NotSupportedException(SR.Format(SR.net_http_unsupported_requesturi_scheme, requestUri.Scheme)); + } + return null; } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index a5448b012d2..61727faa433 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -61,11 +61,21 @@ namespace System.Net.Http.Functional.Tests { using (var client = new HttpClient(new CustomResponseHandler((r, c) => Task.FromResult(new HttpResponseMessage())))) { - AssertExtensions.Throws("value", () => client.BaseAddress = new Uri("ftp://onlyhttpsupported")); AssertExtensions.Throws("value", () => client.BaseAddress = new Uri("/onlyabsolutesupported", UriKind.Relative)); } } + [Fact] + public void BaseAddress_UnknownScheme_DoesNotThrow() + { + using (var client = new HttpClient(new CustomResponseHandler((r, c) => Task.FromResult(new HttpResponseMessage())))) + { + client.BaseAddress = new Uri("blob://foo.bar"); + client.BaseAddress = new Uri("extensions://foo.bar"); + client.BaseAddress = new Uri("foobar://foo.bar"); + } + } + [Fact] public void Timeout_Roundtrip_Equal() { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs index 18b7fa9fe47..5b936425da1 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs @@ -63,6 +63,17 @@ namespace System.Net.Http.Functional.Tests Assert.Null(rm.Content); } + [Fact] + public void Ctor_EmptyStringUri_Accepted() + { + var rm = new HttpRequestMessage(HttpMethod.Put, string.Empty); + + Assert.Null(rm.RequestUri); + Assert.Equal(HttpMethod.Put, rm.Method); + Assert.Equal(_expectedRequestMessageVersion, rm.Version); + Assert.Null(rm.Content); + } + [Fact] public void Ctor_RelativeUri_CorrectValues() { @@ -105,9 +116,9 @@ namespace System.Net.Http.Functional.Tests } [Fact] - public void Ctor_NonHttpUri_ThrowsArgumentException() + public void Ctor_NonHttpUri_DoesNotThrow() { - AssertExtensions.Throws("requestUri", () => new HttpRequestMessage(HttpMethod.Put, "ftp://example.com")); + new HttpRequestMessage(HttpMethod.Put, "ftp://example.com"); } [Fact] @@ -159,10 +170,10 @@ namespace System.Net.Http.Functional.Tests } [Fact] - public void RequestUri_SetNonHttpUri_ThrowsArgumentException() + public void RequestUri_SetNonHttpUri_DoesNotThrow() { var rm = new HttpRequestMessage(); - AssertExtensions.Throws("value", () => { rm.RequestUri = new Uri("ftp://example.com"); }); + rm.RequestUri = new Uri("ftp://example.com"); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 10e81d5f679..d8b7ec4e712 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3205,4 +3205,100 @@ namespace System.Net.Http.Functional.Tests protected override Version UseVersion => HttpVersion.Version30; protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.Mock; } + + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public abstract class SocketsHttpHandler_RequestValidationTest + { + protected abstract bool TestAsync { get; } + + [Fact] + public void Send_NullRequest_ThrowsArgumentNullException() + { + Assert.Throws("request", () => + { + var invoker = new HttpMessageInvoker(new SocketsHttpHandler()); + if (TestAsync) + { + invoker.SendAsync(null, CancellationToken.None); + } + else + { + invoker.Send(null, CancellationToken.None); + } + }); + } + + [Fact] + public void Send_NullRequestUri_ThrowsInvalidOperationException() + { + Throws(new HttpRequestMessage()); + } + + [Fact] + public void Send_RelativeRequestUri_ThrowsInvalidOperationException() + { + Throws(new HttpRequestMessage(HttpMethod.Get, new Uri("/relative", UriKind.Relative))); + } + + [Fact] + public void Send_UnsupportedRequestUriScheme_ThrowsNotSupportedException() + { + Throws(new HttpRequestMessage(HttpMethod.Get, "foo://foo.bar")); + } + + [Fact] + public void Send_MajorVersionZero_ThrowsNotSupportedException() + { + Throws(new HttpRequestMessage { Version = new Version(0, 42) }); + } + + [Fact] + public void Send_TransferEncodingChunkedWithNoContent_ThrowsHttpRequestException() + { + var request = new HttpRequestMessage(); + request.Headers.TransferEncodingChunked = true; + + HttpRequestException exception = Throws(request); + Assert.IsType(exception.InnerException); + } + + [Fact] + public void Send_Http10WithTransferEncodingChunked_ThrowsNotSupportedException() + { + var request = new HttpRequestMessage + { + Content = new StringContent("foo"), + Version = new Version(1, 0) + }; + request.Headers.TransferEncodingChunked = true; + + Throws(request); + } + + private TException Throws(HttpRequestMessage request) + where TException : Exception + { + var invoker = new HttpMessageInvoker(new SocketsHttpHandler()); + if (TestAsync) + { + Task task = invoker.SendAsync(request, CancellationToken.None); + Assert.Equal(TaskStatus.Faulted, task.Status); + return Assert.IsType(task.Exception.InnerException); + } + else + { + return Assert.Throws(() => invoker.Send(request, CancellationToken.None)); + } + } + } + + public sealed class SocketsHttpHandler_RequestValidationTest_Async : SocketsHttpHandler_RequestValidationTest + { + protected override bool TestAsync => true; + } + + public sealed class SocketsHttpHandler_RequestValidationTest_Sync : SocketsHttpHandler_RequestValidationTest + { + protected override bool TestAsync => false; + } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 3b950f2a36c..d6a2cc28d11 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -218,8 +218,6 @@ Link="ProductionCode\System\Net\Http\HttpResponseMessage.cs" /> - + - diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs index 42e34d74156..39bbf05fef4 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs @@ -46,6 +46,7 @@ namespace System.Runtime.InteropServices.JavaScript.Http.Tests [Theory] [InlineData("http://host/absolute/")] [InlineData("blob:http://host/absolute/")] + [InlineData("foo://host/absolute")] public void Ctor_AbsoluteStringUri_CorrectValues(string uri) { var rm = new HttpRequestMessage(HttpMethod.Post, uri); @@ -82,6 +83,7 @@ namespace System.Runtime.InteropServices.JavaScript.Http.Tests [Theory] [InlineData("http://host/absolute/")] [InlineData("blob:http://host/absolute/")] + [InlineData("foo://host/absolute")] public void Ctor_AbsoluteUri_CorrectValues(string uriData) { var uri = new Uri(uriData); @@ -112,12 +114,6 @@ namespace System.Runtime.InteropServices.JavaScript.Http.Tests Assert.Throws(() => new HttpRequestMessage(null, uriData)); } - [Fact] - public void Ctor_NonHttpUri_ThrowsArgumentException() - { - AssertExtensions.Throws("requestUri", () => new HttpRequestMessage(HttpMethod.Put, "ftp://example.com")); - } - [Theory] [InlineData("http://example.com")] [InlineData("blob:http://example.com")] @@ -304,14 +300,6 @@ namespace System.Runtime.InteropServices.JavaScript.Http.Tests Assert.False(streamingEnabledValue); } - - [Fact] - public void RequestUri_SetNonHttpUri_ThrowsArgumentException() - { - var rm = new HttpRequestMessage(); - AssertExtensions.Throws("value", () => { rm.RequestUri = new Uri("ftp://example.com"); }); - } - [Fact] public void Version_SetToNull_ThrowsArgumentNullException() { From be830550d93d9264cce6406222258e9928617b9b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 9 Jul 2021 17:35:25 -0700 Subject: [PATCH 403/926] disable failing quic test (#55440) --- .../System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index c1f2e49beba..b8097a43a6c 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -117,6 +117,7 @@ namespace System.Net.Quic.Tests [Fact] [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue("https://github.com/microsoft/msquic/pull/1728")] public async Task ConnectWithClientCertificate() { bool clientCertificateOK = false; From f22685fbf3ffbbf149ca28ab7ab0d6a3c77b7665 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 9 Jul 2021 20:10:10 -0500 Subject: [PATCH 404/926] Update wasm workload name (#55413) * Update wasm workload name * Remove redirect-to it is not implmented yet --- .../WorkloadManifest.json.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index b4ddda95fe0..05d94220cb8 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -4,8 +4,8 @@ "Microsoft.NET.Workload.Emscripten": "${EmscriptenVersion}" }, "workloads": { - "microsoft-net-sdk-blazorwebassembly-aot": { - "description": "Browser Runtime native performance tools", + "wasm-tools": { + "description": ".NET WebAssembly build tools", "packs": [ "Microsoft.NET.Runtime.WebAssembly.Sdk", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", From ecea9a35d7aca60eeec5f728ddf284275f1c685c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 9 Jul 2021 21:03:04 -0500 Subject: [PATCH 405/926] Re-enable Windows_ZipWithInvalidFileNames tests (#55434) Since we have shipped for 3 years with the current behavior, re-enabling the test so it ensures the current behavior going forward. Extracting .zip files with invalid file names throw an IOException with a message "The filename, directory name, or volume label syntax is incorrect.". Contributes to #25099 --- .../tests/ZipFile.Extract.cs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs index 9bbfd271d2e..e859959c07b 100644 --- a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs +++ b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs @@ -94,15 +94,25 @@ namespace System.IO.Compression.Tests /// when an attempt is made to extract them. /// [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/25099")] - [InlineData("WindowsInvalid_FromUnix", null)] - [InlineData("WindowsInvalid_FromWindows", null)] - [InlineData("NullCharFileName_FromWindows", "path")] - [InlineData("NullCharFileName_FromUnix", "path")] + [InlineData("NullCharFileName_FromWindows")] + [InlineData("NullCharFileName_FromUnix")] [PlatformSpecific(TestPlatforms.Windows)] // Checks Windows-specific invalid file path - public void Windows_ZipWithInvalidFileNames_ThrowsArgumentException(string zipName, string paramName) + public void Windows_ZipWithInvalidFileNames_ThrowsArgumentException(string zipName) { - AssertExtensions.Throws(paramName, null, () => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); + AssertExtensions.Throws("path", null, () => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); + } + + /// + /// This test ensures that a zipfile with path names that are invalid to this OS will throw errors + /// when an attempt is made to extract them. + /// + [Theory] + [InlineData("WindowsInvalid_FromUnix")] + [InlineData("WindowsInvalid_FromWindows")] + [PlatformSpecific(TestPlatforms.Windows)] // Checks Windows-specific invalid file path + public void Windows_ZipWithInvalidFileNames_ThrowsIOException(string zipName) + { + AssertExtensions.Throws(() => ZipFile.ExtractToDirectory(compat(zipName) + ".zip", GetTestFilePath())); } [Theory] From 69d67390e5dd77cab116982085b294159d9ddcf8 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Fri, 9 Jul 2021 19:32:23 -0700 Subject: [PATCH 406/926] Fix the daily build links in docs (#55443) * Fix the daily build links in docs * Update the NuGet feed referenced --- docs/workflow/testing/using-your-build.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/workflow/testing/using-your-build.md b/docs/workflow/testing/using-your-build.md index 682a5dcb523..a6024d4269b 100644 --- a/docs/workflow/testing/using-your-build.md +++ b/docs/workflow/testing/using-your-build.md @@ -16,9 +16,9 @@ assume use of a dogfood build of the .NET SDK. ## Acquire the latest nightly .NET SDK -- [Win 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-win-x64.zip) -- [macOS 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-osx-x64.tar.gz) -- [Others](https://github.com/dotnet/cli/blob/master/README.md#installers-and-binaries) +- [Win 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip) +- [macOS 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.tar.gz) +- [Others](https://github.com/dotnet/installer#installers-and-binaries) To setup the SDK download the zip and extract it somewhere and add the root folder to your [path](../requirements/windows-requirements.md#adding-to-the-default-path-variable) or always fully qualify the path to dotnet in the root of this folder for all the instructions in this document. @@ -73,8 +73,7 @@ dotnet publish - - + ``` From 413f0de990022db48001b0bd6a36a151cc9daa51 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Sat, 10 Jul 2021 06:57:01 +0200 Subject: [PATCH 407/926] ILLink annotation related to ISerializable and IBindingListView in System.Data.Common (#55394) --- .../ref/System.ComponentModel.TypeConverter.cs | 2 +- .../System/ComponentModel/IBindingListView.cs | 2 +- .../ref/System.Data.Common.cs | 2 +- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------------ .../src/System/Data/DataSet.cs | 2 ++ .../src/System/Data/DataTable.cs | 2 ++ .../src/System/Data/DataView.cs | 1 + 7 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index 6a3d62e34a7..b916f1c7878 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -538,7 +538,7 @@ namespace System.ComponentModel } public partial interface IBindingListView : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.ComponentModel.IBindingList { - string? Filter { get; set; } + string? Filter { get; [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members of types used in the filter expression might be trimmed.")] set; } System.ComponentModel.ListSortDescriptionCollection SortDescriptions { get; } bool SupportsAdvancedSorting { get; } bool SupportsFiltering { get; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs index 73b5035800f..f9fef2735be 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/IBindingListView.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel { void ApplySort(ListSortDescriptionCollection sorts); - string? Filter { get; set; } + string? Filter { get; [RequiresUnreferencedCode("Members of types used in the filter expression might be trimmed.")] set; } ListSortDescriptionCollection SortDescriptions { get; } diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index ca955720c0d..38a37217d2f 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -1003,7 +1003,7 @@ namespace System.Data bool System.ComponentModel.IBindingList.SupportsChangeNotification { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSearching { get { throw null; } } bool System.ComponentModel.IBindingList.SupportsSorting { get { throw null; } } - string? System.ComponentModel.IBindingListView.Filter { get { throw null; } set { } } + string? System.ComponentModel.IBindingListView.Filter { get { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members of types used in the filter expression might be trimmed.")] set { } } System.ComponentModel.ListSortDescriptionCollection System.ComponentModel.IBindingListView.SortDescriptions { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsAdvancedSorting { get { throw null; } } bool System.ComponentModel.IBindingListView.SupportsFiltering { get { throw null; } } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index 9825521322c..393abfc15c4 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -1,12 +1,6 @@  - - ILLink - IL2026 - member - M:System.Data.DataSet.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) - ILLink IL2026 @@ -25,12 +19,6 @@ member M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataTable.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) - ILLink IL2026 @@ -49,11 +37,5 @@ member M:System.Data.DataTable.WriteXmlCore(System.Xml.XmlWriter) - - ILLink - IL2026 - member - M:System.Data.DataView.System.ComponentModel.IBindingListView.set_Filter(System.String) - diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index 39410be0645..33c265766ce 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -268,6 +268,8 @@ namespace System.Data DeserializeDataSet(info, context, remotingFormat, schemaSerializationMode); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Binary serialization is unsafe in general and is planned to be obsoleted. We do not want to mark interface or ctors of this class as unsafe as that would show many unnecessary warnings elsewhere.")] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { SerializationFormat remotingFormat = RemotingFormat; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index be3c05ef7c3..bd8257122e2 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -211,6 +211,8 @@ namespace System.Data DeserializeDataTable(info, context, isSingleTable, remotingFormat); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Binary serialization is unsafe in general and is planned to be obsoleted. We do not want to mark interface or ctors of this class as unsafe as that would show many unnecessary warnings elsewhere.")] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { SerializationFormat remotingFormat = RemotingFormat; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataView.cs b/src/libraries/System.Data.Common/src/System/Data/DataView.cs index c72031a8533..6f9793333c5 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataView.cs @@ -1136,6 +1136,7 @@ namespace System.Data string? IBindingListView.Filter { get { return RowFilter; } + [RequiresUnreferencedCode(Select.RequiresUnreferencedCodeMessage)] set { RowFilter = value; } } From 27b564ab96549df11611eda172992b1db5c6e96e Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Fri, 9 Jul 2021 23:22:06 -0700 Subject: [PATCH 408/926] Spill single-def variable at definition to avoid further spilling (#54345) * Print single-def * Rename lvEhWriteThruCandidate->lvSingleDefRegCandidate, introduce isSingleDef * Introduce singleDefSpillAfter If a single-def variable is decided to get spilled in its lifetime, then spill it at the firstRefPosition RefTypeDef so the value of the variable is always valid on the stack. Going forward, no more spills will be needed for such variable or no more resolutions (reg to stack) will be needed for such single-def variables. * jit format * some fixes * wip * Add check of isSingleDef in validateInterval() * Make isSingleDef during buildIntervals * minor fix in lclvars.cpp * some fixes after self CR * Updated some comments * Remove lvSpillAtSingleDef from some asserts * Use singleDefSpill information in getWeight() * Remove lvSpillAtSingleDef from some more checks * Mark lvSpillAtSingleDef whenever refPosition->singleDefSpill==true * Add TODO for SingleDefVarCandidate * Some notes on setting singleDefSpill * jit format * review feedback * review comments --- src/coreclr/jit/codegencommon.cpp | 14 +++--- src/coreclr/jit/codegenlinear.cpp | 27 +++++------ src/coreclr/jit/compiler.h | 27 +++++++++-- src/coreclr/jit/lclvars.cpp | 48 +++++++++++--------- src/coreclr/jit/lsra.cpp | 70 ++++++++++++++++++++++------- src/coreclr/jit/lsra.h | 10 +++++ src/coreclr/jit/lsrabuild.cpp | 38 +++++++++++----- src/coreclr/jit/morph.cpp | 18 +++++--- src/coreclr/jit/treelifeupdater.cpp | 6 +-- 9 files changed, 177 insertions(+), 81 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 99852d7f446..aeecb0553b9 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -552,9 +552,9 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo else { // If this is going live, the register must not have a variable in it, except - // in the case of an exception variable, which may be already treated as live - // in the register. - assert(varDsc->lvLiveInOutOfHndlr || ((regSet.GetMaskVars() & regMask) == 0)); + // in the case of an exception or "spill at single-def" variable, which may be already treated + // as live in the register. + assert(varDsc->IsAlwaysAliveInMemory() || ((regSet.GetMaskVars() & regMask) == 0)); regSet.AddMaskVars(regMask); } } @@ -736,7 +736,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) bool isGCRef = (varDsc->TypeGet() == TYP_REF); bool isByRef = (varDsc->TypeGet() == TYP_BYREF); bool isInReg = varDsc->lvIsInReg(); - bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); if (isInReg) { @@ -777,8 +777,8 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) if (varDsc->lvIsInReg()) { // If this variable is going live in a register, it is no longer live on the stack, - // unless it is an EH var, which always remains live on the stack. - if (!varDsc->lvLiveInOutOfHndlr) + // unless it is an EH/"spill at single-def" var, which always remains live on the stack. + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex)) @@ -11422,7 +11422,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) { varReg = REG_STK; } - if ((varReg == REG_STK) || fieldVarDsc->lvLiveInOutOfHndlr) + if ((varReg == REG_STK) || fieldVarDsc->IsAlwaysAliveInMemory()) { if (!lclNode->AsLclVar()->IsLastUse(i)) { diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index eb942332554..b822f233e66 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -229,7 +229,7 @@ void CodeGen::genCodeForBBlist() { newRegByrefSet |= varDsc->lvRegMask(); } - if (!varDsc->lvLiveInOutOfHndlr) + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (verbose && VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) @@ -240,7 +240,7 @@ void CodeGen::genCodeForBBlist() VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex); } } - if ((!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr) && compiler->lvaIsGCTracked(varDsc)) + if ((!varDsc->lvIsInReg() || varDsc->IsAlwaysAliveInMemory()) && compiler->lvaIsGCTracked(varDsc)) { #ifdef DEBUG if (verbose && !VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) @@ -873,9 +873,9 @@ void CodeGen::genSpillVar(GenTree* tree) var_types lclType = varDsc->GetActualRegisterType(); emitAttr size = emitTypeSize(lclType); - // If this is a write-thru variable, we don't actually spill at a use, but we will kill the var in the reg - // (below). - if (!varDsc->lvLiveInOutOfHndlr) + // If this is a write-thru or a single-def variable, we don't actually spill at a use, + // but we will kill the var in the reg (below). + if (!varDsc->IsAlwaysAliveInMemory()) { instruction storeIns = ins_Store(lclType, compiler->isSIMDTypeLocalAligned(varNum)); assert(varDsc->GetRegNum() == tree->GetRegNum()); @@ -883,7 +883,7 @@ void CodeGen::genSpillVar(GenTree* tree) } // We should only have both GTF_SPILL (i.e. the flag causing this method to be called) and - // GTF_SPILLED on a write-thru def, for which we should not be calling this method. + // GTF_SPILLED on a write-thru/single-def def, for which we should not be calling this method. assert((tree->gtFlags & GTF_SPILLED) == 0); // Remove the live var from the register. @@ -918,8 +918,9 @@ void CodeGen::genSpillVar(GenTree* tree) } else { - // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar. - assert(varDsc->lvLiveInOutOfHndlr && ((tree->gtFlags & GTF_VAR_DEF) != 0)); + // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar + // or a single-def var that is to be spilled at its definition. + assert((varDsc->IsAlwaysAliveInMemory()) && ((tree->gtFlags & GTF_VAR_DEF) != 0)); } #ifdef USING_VARIABLE_LIVE_RANGE @@ -1055,7 +1056,7 @@ void CodeGen::genUnspillLocal( } #endif // USING_VARIABLE_LIVE_RANGE - if (!varDsc->lvLiveInOutOfHndlr) + if (!varDsc->IsAlwaysAliveInMemory()) { #ifdef DEBUG if (VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) @@ -2044,12 +2045,12 @@ void CodeGen::genSpillLocal(unsigned varNum, var_types type, GenTreeLclVar* lclN // We have a register candidate local that is marked with GTF_SPILL. // This flag generally means that we need to spill this local. - // The exception is the case of a use of an EH var use that is being "spilled" + // The exception is the case of a use of an EH/spill-at-single-def var use that is being "spilled" // to the stack, indicated by GTF_SPILL (note that all EH lclVar defs are always - // spilled, i.e. write-thru). - // An EH var use is always valid on the stack (so we don't need to actually spill it), + // spilled, i.e. write-thru. Likewise, single-def vars that are spilled at its definitions). + // An EH or single-def var use is always valid on the stack (so we don't need to actually spill it), // but the GTF_SPILL flag records the fact that the register value is going dead. - if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || !varDsc->lvLiveInOutOfHndlr) + if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || (!varDsc->IsAlwaysAliveInMemory())) { // Store local variable to its home location. // Ensure that lclVar stores are typed correctly. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f816e6917a6..1383b57a7dd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -446,10 +446,19 @@ public: // before lvaMarkLocalVars: identifies ref type locals that can get type updates // after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies - unsigned char lvEhWriteThruCandidate : 1; // variable has a single def and hence is a register candidate if - // if it is an EH variable + unsigned char lvSingleDefRegCandidate : 1; // variable has a single def and hence is a register candidate + // Currently, this is only used to decide if an EH variable can be + // a register candiate or not. - unsigned char lvDisqualifyForEhWriteThru : 1; // tracks variable that are disqualified from register candidancy + unsigned char lvDisqualifySingleDefRegCandidate : 1; // tracks variable that are disqualified from register + // candidancy + + unsigned char lvSpillAtSingleDef : 1; // variable has a single def (as determined by LSRA interval scan) + // and is spilled making it candidate to spill right after the + // first (and only) definition. + // Note: We cannot reuse lvSingleDefRegCandidate because it is set + // in earlier phase and the information might not be appropriate + // in LSRA. #if ASSERTION_PROP unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization @@ -547,7 +556,7 @@ public: unsigned char lvFldOrdinal; #ifdef DEBUG - unsigned char lvDisqualifyEHVarReason = 'H'; + unsigned char lvSingleDefDisqualifyReason = 'H'; #endif #if FEATURE_MULTIREG_ARGS @@ -1030,6 +1039,16 @@ public: return IsEnregisterableType(); } + //----------------------------------------------------------------------------- + // IsAlwaysAliveInMemory: Determines if this variable's value is always + // up-to-date on stack. This is possible if this is an EH-var or + // we decided to spill after single-def. + // + bool IsAlwaysAliveInMemory() const + { + return lvLiveInOutOfHndlr || lvSpillAtSingleDef; + } + bool CanBeReplacedWithItsField(Compiler* comp) const; #ifdef DEBUG diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index b2ca56b406b..33c5abaa30d 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2559,7 +2559,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum) noway_assert(lvaTable[i].lvIsStructField); lvaTable[i].lvLiveInOutOfHndlr = 1; // For now, only enregister an EH Var if it is a single def and whose refCnt > 1. - if (!lvaEnregEHVars || !lvaTable[i].lvEhWriteThruCandidate || lvaTable[i].lvRefCnt() <= 1) + if (!lvaEnregEHVars || !lvaTable[i].lvSingleDefRegCandidate || lvaTable[i].lvRefCnt() <= 1) { lvaSetVarDoNotEnregister(i DEBUGARG(DNER_LiveInOutOfHandler)); } @@ -2567,7 +2567,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum) } // For now, only enregister an EH Var if it is a single def and whose refCnt > 1. - if (!lvaEnregEHVars || !varDsc->lvEhWriteThruCandidate || varDsc->lvRefCnt() <= 1) + if (!lvaEnregEHVars || !varDsc->lvSingleDefRegCandidate || varDsc->lvRefCnt() <= 1) { lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LiveInOutOfHandler)); } @@ -4110,33 +4110,35 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, } } - if (!varDsc->lvDisqualifyForEhWriteThru) // If this EH var already disqualified, we can skip this + if (!varDsc->lvDisqualifySingleDefRegCandidate) // If this var is already disqualified, we can skip this { if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable { - bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0; - bool bbIsReturn = block->bbJumpKind == BBJ_RETURN; + bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0; + bool bbIsReturn = block->bbJumpKind == BBJ_RETURN; + // TODO: Zero-inits in LSRA are created with below condition. Try to use similar condition here as well. + // if (compiler->info.compInitMem || varTypeIsGC(varDsc->TypeGet())) bool needsExplicitZeroInit = fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn); - if (varDsc->lvEhWriteThruCandidate || needsExplicitZeroInit) + if (varDsc->lvSingleDefRegCandidate || needsExplicitZeroInit) { #ifdef DEBUG if (needsExplicitZeroInit) { - varDsc->lvDisqualifyEHVarReason = 'Z'; - JITDUMP("EH Var V%02u needs explicit zero init. Disqualified as a register candidate.\n", + varDsc->lvSingleDefDisqualifyReason = 'Z'; + JITDUMP("V%02u needs explicit zero init. Disqualified as a single-def register candidate.\n", lclNum); } else { - varDsc->lvDisqualifyEHVarReason = 'M'; - JITDUMP("EH Var V%02u has multiple definitions. Disqualified as a register candidate.\n", + varDsc->lvSingleDefDisqualifyReason = 'M'; + JITDUMP("V%02u has multiple definitions. Disqualified as a single-def register candidate.\n", lclNum); } #endif // DEBUG - varDsc->lvEhWriteThruCandidate = false; - varDsc->lvDisqualifyForEhWriteThru = true; + varDsc->lvSingleDefRegCandidate = false; + varDsc->lvDisqualifySingleDefRegCandidate = true; } else { @@ -4146,7 +4148,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, if (!varTypeNeedsPartialCalleeSave(varDsc->lvType)) #endif { - varDsc->lvEhWriteThruCandidate = true; + varDsc->lvSingleDefRegCandidate = true; JITDUMP("Marking EH Var V%02u as a register candidate.\n", lclNum); } } @@ -4521,8 +4523,8 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers) // that was set by past phases. if (!isRecompute) { - varDsc->lvSingleDef = varDsc->lvIsParam; - varDsc->lvEhWriteThruCandidate = varDsc->lvIsParam; + varDsc->lvSingleDef = varDsc->lvIsParam; + varDsc->lvSingleDefRegCandidate = varDsc->lvIsParam; } } @@ -7405,11 +7407,6 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r printf(" HFA(%s) ", varTypeName(varDsc->GetHfaType())); } - if (varDsc->lvLiveInOutOfHndlr) - { - printf(" EH"); - } - if (varDsc->lvDoNotEnregister) { printf(" do-not-enreg["); @@ -7427,7 +7424,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r } if (lvaEnregEHVars && varDsc->lvLiveInOutOfHndlr) { - printf("%c", varDsc->lvDisqualifyEHVarReason); + printf("%c", varDsc->lvSingleDefDisqualifyReason); } if (varDsc->lvLclFieldExpr) { @@ -7500,6 +7497,15 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r { printf(" EH-live"); } + if (varDsc->lvSpillAtSingleDef) + { + printf(" spill-single-def"); + } + else if (varDsc->lvSingleDefRegCandidate) + { + printf(" single-def"); + } + #ifndef TARGET_64BIT if (varDsc->lvStructDoubleAlign) printf(" double-align"); diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index f810ca3d3aa..4bbb877fda8 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -196,9 +196,9 @@ BasicBlock::weight_t LinearScan::getWeight(RefPosition* refPos) if (refPos->getInterval()->isSpilled) { // Decrease the weight if the interval has already been spilled. - if (varDsc->lvLiveInOutOfHndlr) + if (varDsc->lvLiveInOutOfHndlr || refPos->getInterval()->firstRefPosition->singleDefSpill) { - // An EH var is always spilled at defs, and we'll decrease the weight by half, + // An EH-var/single-def is always spilled at defs, and we'll decrease the weight by half, // since only the reload is needed. weight = weight / 2; } @@ -1794,7 +1794,7 @@ void LinearScan::identifyCandidates() if (varDsc->lvLiveInOutOfHndlr) { - newInt->isWriteThru = varDsc->lvEhWriteThruCandidate; + newInt->isWriteThru = varDsc->lvSingleDefRegCandidate; setIntervalAsSpilled(newInt); } @@ -3273,6 +3273,24 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition fromRefPosition->spillAfter = true; } } + + // Only handle the singledef intervals whose firstRefPosition is RefTypeDef and is not yet marked as spillAfter. + // The singledef intervals whose firstRefPositions are already marked as spillAfter, no need to mark them as + // singleDefSpill because they will always get spilled at firstRefPosition. + // This helps in spilling the singleDef at definition + // + // Note: Only mark "singleDefSpill" for those intervals who ever get spilled. The intervals that are never spilled + // will not be marked as "singleDefSpill" and hence won't get spilled at the first definition. + if (interval->isSingleDef && RefTypeIsDef(interval->firstRefPosition->refType) && + !interval->firstRefPosition->spillAfter) + { + // TODO-CQ: Check if it is beneficial to spill at def, meaning, if it is a hot block don't worry about + // doing the spill. Another option is to track number of refpositions and a interval has more than X + // refpositions + // then perform this optimization. + interval->firstRefPosition->singleDefSpill = true; + } + assert(toRefPosition != nullptr); #ifdef DEBUG @@ -3955,16 +3973,16 @@ void LinearScan::unassignIntervalBlockStart(RegRecord* regRecord, VarToRegMap in // // Arguments: // currentBlock - the BasicBlock we are about to allocate registers for -// allocationPass - true if we are currently allocating registers (versus writing them back) // // Return Value: // None // // Notes: -// During the allocation pass, we use the outVarToRegMap of the selected predecessor to -// determine the lclVar locations for the inVarToRegMap. -// During the resolution (write-back) pass, we only modify the inVarToRegMap in cases where -// a lclVar was spilled after the block had been completed. +// During the allocation pass (allocationPassComplete = false), we use the outVarToRegMap +// of the selected predecessor to determine the lclVar locations for the inVarToRegMap. +// During the resolution (write-back when allocationPassComplete = true) pass, we only +// modify the inVarToRegMap in cases where a lclVar was spilled after the block had been +// completed. void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) { // If we have no register candidates we should only call this method during allocation. @@ -5915,7 +5933,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref assert(currentRefPosition->refType == RefTypeExpUse); } } - else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) && + else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) && (treeNode != nullptr) && (!treeNode->IsMultiReg() || treeNode->gtGetOp1()->IsMultiRegNode())) { // In the case of a pure def, don't bother spilling - just assign it to the @@ -5926,10 +5944,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref assert(interval->isSpilled); varDsc->SetRegNum(REG_STK); interval->physReg = REG_NA; - if (treeNode != nullptr) - { - writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA); - } + writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA); } else // Not reload and Not pure-def that's spillAfter { @@ -6018,6 +6033,27 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref } } } + + if (currentRefPosition->singleDefSpill && (treeNode != nullptr)) + { + // This is the first (and only) def of a single-def var (only defs are marked 'singleDefSpill'). + // Mark it as GTF_SPILL, so it is spilled immediately to the stack at definition and + // GTF_SPILLED, so the variable stays live in the register. + // + // TODO: This approach would still create the resolution moves but during codegen, will check for + // `lvSpillAtSingleDef` to decide whether to generate spill or not. In future, see if there is some + // better way to avoid resolution moves, perhaps by updating the varDsc->SetRegNum(REG_STK) in this + // method? + treeNode->gtFlags |= GTF_SPILL; + treeNode->gtFlags |= GTF_SPILLED; + + if (treeNode->IsMultiReg()) + { + treeNode->SetRegSpillFlagByIdx(GTF_SPILLED, currentRefPosition->getMultiRegIdx()); + } + + varDsc->lvSpillAtSingleDef = true; + } } // Update the physRegRecord for the register, so that we know what vars are in @@ -8936,6 +8972,10 @@ void RefPosition::dump(LinearScan* linearScan) { printf(" spillAfter"); } + if (this->singleDefSpill) + { + printf(" singleDefSpill"); + } if (this->writeThru) { printf(" writeThru"); @@ -9678,12 +9718,12 @@ void LinearScan::dumpLsraAllocationEvent( case LSRA_EVENT_DONE_KILL_GC_REFS: dumpRefPositionShort(activeRefPosition, currentBlock); - printf("Done "); + printf("Done "); break; case LSRA_EVENT_NO_GC_KILLS: dumpRefPositionShort(activeRefPosition, currentBlock); - printf("None "); + printf("None "); break; // Block boundaries diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index 10ff1f471b4..b0c17358716 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1928,6 +1928,7 @@ public: , isPartiallySpilled(false) #endif , isWriteThru(false) + , isSingleDef(false) #ifdef DEBUG , intervalIndex(0) #endif @@ -2023,6 +2024,9 @@ public: // True if this interval is associated with a lclVar that is written to memory at each definition. bool isWriteThru : 1; + // True if this interval has a single definition. + bool isSingleDef : 1; + #ifdef DEBUG unsigned int intervalIndex; #endif // DEBUG @@ -2222,6 +2226,10 @@ public: // Spill and Copy info // reload indicates that the value was spilled, and must be reloaded here. // spillAfter indicates that the value is spilled here, so a spill must be added. + // singleDefSpill indicates that it is associated with a single-def var and if it + // is decided to get spilled, it will be spilled at firstRefPosition def. That + // way, the the value of stack will always be up-to-date and no more spills or + // resolutions (from reg to stack) will be needed for such single-def var. // copyReg indicates that the value needs to be copied to a specific register, // but that it will also retain its current assigned register. // moveReg indicates that the value needs to be moved to a different register, @@ -2240,6 +2248,7 @@ public: unsigned char reload : 1; unsigned char spillAfter : 1; + unsigned char singleDefSpill : 1; unsigned char writeThru : 1; // true if this var is defined in a register and also spilled. spillAfter must NOT be // set. @@ -2287,6 +2296,7 @@ public: , lastUse(false) , reload(false) , spillAfter(false) + , singleDefSpill(false) , writeThru(false) , copyReg(false) , moveReg(false) diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index cb04ddddf51..f5f4d781d75 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -620,6 +620,12 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval, associateRefPosWithInterval(newRP); + if (RefTypeIsDef(newRP->refType)) + { + assert(theInterval != nullptr); + theInterval->isSingleDef = theInterval->firstRefPosition == newRP; + } + DBEXEC(VERBOSE, newRP->dump(this)); return newRP; } @@ -2602,20 +2608,20 @@ void LinearScan::buildIntervals() { lsraDumpIntervals("BEFORE VALIDATING INTERVALS"); dumpRefPositions("BEFORE VALIDATING INTERVALS"); - validateIntervals(); } + validateIntervals(); + #endif // DEBUG } #ifdef DEBUG //------------------------------------------------------------------------ -// validateIntervals: A DEBUG-only method that checks that the lclVar RefPositions -// do not reflect uses of undefined values +// validateIntervals: A DEBUG-only method that checks that: +// - the lclVar RefPositions do not reflect uses of undefined values +// - A singleDef interval should have just first RefPosition as RefTypeDef. // -// Notes: If an undefined use is encountered, it merely prints a message. -// -// TODO-Cleanup: This should probably assert, or at least print the message only -// when doing a JITDUMP. +// TODO-Cleanup: If an undefined use is encountered, it merely prints a message +// but probably assert. // void LinearScan::validateIntervals() { @@ -2630,19 +2636,29 @@ void LinearScan::validateIntervals() Interval* interval = getIntervalForLocalVar(i); bool defined = false; - printf("-----------------\n"); + JITDUMP("-----------------\n"); for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition) { - ref->dump(this); + if (VERBOSE) + { + ref->dump(this); + } RefType refType = ref->refType; if (!defined && RefTypeIsUse(refType)) { if (compiler->info.compMethodName != nullptr) { - printf("%s: ", compiler->info.compMethodName); + JITDUMP("%s: ", compiler->info.compMethodName); } - printf("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation); + JITDUMP("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation); } + + // For single-def intervals, the only the first refposition should be a RefTypeDef + if (interval->isSingleDef && RefTypeIsDef(refType)) + { + assert(ref == interval->firstRefPosition); + } + // Note that there can be multiple last uses if they are on disjoint paths, // so we can't really check the lastUse flag if (ref->lastUse) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index daca2c487e2..526a996e1b4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -17441,14 +17441,18 @@ void Compiler::fgRetypeImplicitByRefArgs() #endif // DEBUG // Propagate address-taken-ness and do-not-enregister-ness. - newVarDsc->lvAddrExposed = varDsc->lvAddrExposed; - newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister; + newVarDsc->lvAddrExposed = varDsc->lvAddrExposed; + newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister; + newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr; + newVarDsc->lvSingleDef = varDsc->lvSingleDef; + newVarDsc->lvSingleDefRegCandidate = varDsc->lvSingleDefRegCandidate; + newVarDsc->lvSpillAtSingleDef = varDsc->lvSpillAtSingleDef; #ifdef DEBUG - newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr; - newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr; - newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr; - newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr; - newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall; + newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr; + newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr; + newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr; + newVarDsc->lvSingleDefDisqualifyReason = varDsc->lvSingleDefDisqualifyReason; + newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall; #endif // DEBUG // If the promotion is dependent, the promoted temp would just be committed diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp index 20a9745362b..15c32596cc4 100644 --- a/src/coreclr/jit/treelifeupdater.cpp +++ b/src/coreclr/jit/treelifeupdater.cpp @@ -60,7 +60,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns { regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex); bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; + isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); if (isInReg) { if (isBorn) @@ -259,7 +259,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) compiler->codeGen->genUpdateVarReg(varDsc, tree); } bool isInReg = varDsc->lvIsInReg() && tree->GetRegNum() != REG_NA; - bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); if (isInReg) { compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree)); @@ -283,7 +283,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) unsigned fldVarIndex = fldVarDsc->lvVarIndex; regNumber reg = lclVarTree->AsLclVar()->GetRegNumByIdx(i); bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - bool isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; + bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory(); bool isFieldDying = lclVarTree->AsLclVar()->IsLastUse(i); if ((isBorn && !isFieldDying) || (!isBorn && isFieldDying)) { From 739218439bb6d3b224e240c012f37c516ccd29c9 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sat, 10 Jul 2021 05:02:38 -0400 Subject: [PATCH 409/926] [mono] Remove one more reference to MONO_CORLIB_VERSION. (#55345) --- src/mono/cmake/config.h.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index d684efa2b2e..8e0c462a53e 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -71,9 +71,6 @@ /* Define to the home page for this package. */ #cmakedefine PACKAGE_URL 1 -/* Version of the corlib-runtime interface */ -#cmakedefine MONO_CORLIB_VERSION "@MONO_CORLIB_VERSION@" - /* Disables the IO portability layer */ #cmakedefine DISABLE_PORTABILITY 1 From bb394065f0136ce2bd3afb4349e1dbf46dc9012c Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sat, 10 Jul 2021 06:24:00 -0400 Subject: [PATCH 410/926] [wasm] Fix property name for Emscripten manifest nuget (#55444) .. this allows the version to get updated in `eng/Versions.props` also, when updates flow in. - And update the version to match Version.Details.xml --- eng/Versions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 8debb7be68d..7be0ee173f2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -180,7 +180,7 @@ 11.1.0-alpha.1.21357.1 11.1.0-alpha.1.21357.1 - 6.0.0-preview.7.21355.1 - $(MicrosoftNETWorkloadEmscriptenManifest60100) + 6.0.0-preview.7.21358.1 + $(MicrosoftNETWorkloadEmscriptenManifest60100Version) From 65b472a0f31f2b878601b41a344754ec3e776d1d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 10 Jul 2021 12:38:33 +0200 Subject: [PATCH 411/926] Handle a missing case in zero-extend peephole (#55129) --- src/coreclr/jit/emitxarch.cpp | 7 ++ .../JitBlue/Runtime_55129/Runtime_55129.cs | 101 ++++++++++++++++++ .../Runtime_55129/Runtime_55129.csproj | 10 ++ 3 files changed, 118 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index a0a5e3283d4..217f5f15aaf 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -296,6 +296,13 @@ bool emitter::AreUpper32BitsZero(regNumber reg) return false; } +#ifdef TARGET_AMD64 + if (id->idIns() == INS_movsxd) + { + return false; + } +#endif + // movzx always zeroes the upper 32 bits. if (id->idIns() == INS_movzx) { diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs new file mode 100644 index 00000000000..2ee17477408 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +public class Runtime_55129 +{ + public static int Main() + { + int result = 100; + if (!Runtime_55129_1.Run()) + result |= 1; + if (!Runtime_55129_2.Run()) + result |= 2; + return result; + } +} + +// These tests failed because of a missing zero extension because a peephole +// did not handle that 'movsxd' would sign extend. +public class Runtime_55129_1 +{ + static I s_i = new C(); + static short s_7; + static sbyte[][] s_10 = new sbyte[][]{new sbyte[]{-1}}; + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Run() + { + var vr59 = (uint)M6(s_10[0][0]); + return (long)vr59 == uint.MaxValue; + } + + static ulong M6(sbyte arg0) + { + return (ulong)arg0; + ref short var1 = ref s_7; + s_i.Foo(var1); + } +} + +interface I +{ + void Foo(T val); +} + +class C : I +{ + public void Foo(T val) { } +} + +struct S0 +{ + public long F5; + public S0(int f0, byte f1, ulong f2, byte f3, uint f4, long f5, int f6, int f7) : this() + { + } +} + +class C0 +{ + public long F0; +} + +class C1 +{ + public ulong F1; +} + +public class Runtime_55129_2 +{ + static int[] s_2 = new int[] { -1 }; + static C0 s_4 = new C0(); + static S0 s_5; + static C1[][] s_47 = new C1[][] { new C1[] { new C1() } }; + static bool s_result; + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Run() + { + s_5.F5 = s_2[0]; + C1 vr4 = s_47[0][0]; + var vr6 = vr4.F1; + M6(vr6); + return s_result; + } + + static void M6(ulong arg0) + { + arg0 >>= 0; + if (-1 < (uint)(0U | M7(ref s_4.F0, new S0[][,] { new S0[,] { { new S0(-10, 1, 0, 178, 1671790506U, -2L, 1, -2147483648) } }, new S0[,] { { new S0(1330389305, 255, 1297834355652867458UL, 0, 1777203966U, 4402572156859115751L, -1597826478, 1) } }, new S0[,] { { new S0(2147483646, 15, 18446744073709551614UL, 9, 1089668776U, 8629324174561266356L, 2124906017, -1883510008) } } }, 1, new sbyte[] { -37, -21, 0, 0, 0, 0 }, new S0[] { new S0(219671235, 22, 11763641210444381762UL, 0, 2568868236U, -7432636731544997849L, 1623417447, -479936755), new S0(-2147483647, 108, 0, 1, 4294967294U, 9223372036854775807L, 539462011, 1), new S0(1, 0, 15733997012901423027UL, 212, 4294967294U, 4663434921694141184L, -2147483647, 1196938120), new S0(1, 68, 0, 14, 653907833U, -6962955672558660864L, 1966270988, -378944819) }))) + { + s_result = true; + } + } + + static short M7(ref long arg0, S0[][,] arg1, ushort arg2, sbyte[] arg3, S0[] arg4) + { + long vr20 = s_5.F5; + return (short)vr20; + } +} + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj new file mode 100644 index 00000000000..1100f420532 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55129/Runtime_55129.csproj @@ -0,0 +1,10 @@ + + + Exe + None + True + + + + + From 5e657717a7026638ec03a807114de87cc484c677 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 19:59:49 +0300 Subject: [PATCH 412/926] [interp] Fix pinvokes with HandleRef (#55404) The m2n wrapper marshals HandleRef structs from a vtype to intptr. The MonoMethod for a pinvoke method stores the unmarshalled signature. When locating the args on the stack during the pinvoke call we need to use the marshalled signature instead (which is saved in the code stream for the calli opcode) --- src/mono/mono/mini/interp/interp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index dad5f095b00..f499c9756cc 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1173,12 +1173,16 @@ compute_arg_offset (MonoMethodSignature *sig, int index, int prev_offset) } static guint32* -initialize_arg_offsets (InterpMethod *imethod) +initialize_arg_offsets (InterpMethod *imethod, MonoMethodSignature *csig) { if (imethod->arg_offsets) return imethod->arg_offsets; - MonoMethodSignature *sig = mono_method_signature_internal (imethod->method); + // For pinvokes, csig represents the real signature with marshalled args. If an explicit + // marshalled signature was not provided, we use the managed signature of the method. + MonoMethodSignature *sig = csig; + if (!sig) + sig = mono_method_signature_internal (imethod->method); int arg_count = sig->hasthis + sig->param_count; g_assert (arg_count); guint32 *arg_offsets = (guint32*) g_malloc ((sig->hasthis + sig->param_count) * sizeof (int)); @@ -1201,13 +1205,13 @@ initialize_arg_offsets (InterpMethod *imethod) } static guint32 -get_arg_offset_fast (InterpMethod *imethod, int index) +get_arg_offset_fast (InterpMethod *imethod, MonoMethodSignature *sig, int index) { guint32 *arg_offsets = imethod->arg_offsets; if (arg_offsets) return arg_offsets [index]; - arg_offsets = initialize_arg_offsets (imethod); + arg_offsets = initialize_arg_offsets (imethod, sig); g_assert (arg_offsets); return arg_offsets [index]; } @@ -1216,7 +1220,7 @@ static guint32 get_arg_offset (InterpMethod *imethod, MonoMethodSignature *sig, int index) { if (imethod) { - return get_arg_offset_fast (imethod, index); + return get_arg_offset_fast (imethod, sig, index); } else { g_assert (!sig->hasthis); return compute_arg_offset (sig, index, -1); @@ -2385,7 +2389,7 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame if (cinfo->ret_mt != -1) args [pindex ++] = ret_sp; for (int i = 0; i < rmethod->param_count; ++i) { - stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, stack_index + i)); + stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, NULL, stack_index + i)); if (cinfo->arginfo [i] == JIT_ARG_BYVAL) args [pindex ++] = sval->data.p; else @@ -7199,7 +7203,7 @@ interp_frame_get_arg (MonoInterpFrameHandle frame, int pos) g_assert (iframe->imethod); - return (char*)iframe->stack + get_arg_offset_fast (iframe->imethod, pos + iframe->imethod->hasthis); + return (char*)iframe->stack + get_arg_offset_fast (iframe->imethod, NULL, pos + iframe->imethod->hasthis); } static gpointer From 1ff0b50118985994306052494822585710992410 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 10 Jul 2021 20:00:38 +0300 Subject: [PATCH 413/926] [interp] Fix open delegates used with virtual methods of valuetypes (#55354) * [interp] Fix open delegates used with virtual methods of valuetypes We were trying to resolve the virtual method on the this pointer (which is a managed pointer and not an object). If the delegate target method is declared on a valuetype, the method does not need resolving. Fixes https://github.com/dotnet/runtime/issues/49839 * Re-enable tests --- src/libraries/System.Runtime/tests/System/DelegateTests.cs | 2 -- src/mono/mono/mini/interp/interp.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System/DelegateTests.cs index 269b5f7b218..9a3658457c1 100644 --- a/src/libraries/System.Runtime/tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System/DelegateTests.cs @@ -1118,7 +1118,6 @@ namespace System.Tests Assert.NotNull(ex.Message); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] [Fact] public static void CreateDelegate10_Nullable_Method() { @@ -1130,7 +1129,6 @@ namespace System.Tests Assert.Equal(num.ToString(), s); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] [Fact] public static void CreateDelegate10_Nullable_ClosedDelegate() { diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index f499c9756cc..44d1c70b396 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3437,7 +3437,7 @@ main_loop: del_imethod = mono_interp_get_imethod (mono_marshal_get_native_wrapper (del_imethod->method, FALSE, FALSE), error); mono_error_assert_ok (error); del->interp_invoke_impl = del_imethod; - } else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target) { + } else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target && !m_class_is_valuetype (del_imethod->method->klass)) { // 'this' is passed dynamically, we need to recompute the target method // with each call del_imethod = get_virtual_method (del_imethod, LOCAL_VAR (call_args_offset + MINT_STACK_SLOT_SIZE, MonoObject*)->vtable); From 02ccdacb069ff0608b7ec672afe206721aaa50b7 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 10 Jul 2021 10:17:54 -0700 Subject: [PATCH 414/926] Improve loop cloning, with debugging improvements (#55299) When loop cloning was creating cloning conditions, it was creating unnecessary bounds checks in some multi-dimensional array index cases. When creating a set of cloning conditions, first a null check is done, then an array length check is done, etc. Thus, the array length expression itself won't fault because we've already done a null check. And a subsequent array index expression won't fault (or need a bounds check) because we've already checked the array length (i.e., we've done a manual bounds check). So, stop creating the unnecessary bounds checks, and mark the appropriate instructions as non-faulting by clearing the GTF_EXCEPT bit. Note that I did not turn on the code to clear GTF_EXCEPT for array length checks because it leads to negative downstream effects in CSE. Namely, there end up being array length expressions that are identical except for the exception bit. When CSE sees this, it gives up on creating a CSE, which leads to regressions in some cases where we don't CSE the array length expression. Also, for multi-dimension jagged arrays, when optimizing the fast path, we were not removing as many bounds checks as we could. In particular, we weren't removing outer bounds checks, only inner ones. Add code to handle all the bounds checks. There are some runtime improvements (measured via BenchmarkDotNet on the JIT microbenchmarks), but also some regressions, due, as far as I can tell, to the Intel jcc erratum performance impact. In particular, benchmark ludcmp shows up to a 9% regression due to a `jae` instruction in the hot loop now crossing a 32-byte boundary due to code changes earlier in the function affecting instruction alignment. The hot loop itself is exactly the same (module register allocation differences). As there is nothing that can be done (without mitigating the jcc erratum) -- it's "bad luck". In addition to those functional changes, there are a number of debugging-related improvements: 1. Loop cloning: (a) Improved dumping of cloning conditions and other things, (b) remove an unnecessary member to `LcOptInfo`, (c) convert the `LoopCloneContext` raw arrays to `jitstd::vector` for easier debugging, as clrjit.natvis can be taught to understand them. 2. CSE improvements: (a) Add `getCSEAvailBit` and `getCSEAvailCrossCallBit` functions to avoid multiple hard-codings of these expresions, (b) stop printing all the details of the CSE dataflow to JitDump; just print the result, (c) add `optPrintCSEDataFlowSet` function to print the CSE dataflow set in symbolic form, not just the raw bits, (d) added `FMT_CSE` string to use for formatting CSE candidates, (e) added `optOptimizeCSEs` to the phase structure for JitDump output, (f) remove unused `optCSECandidateTotal` (remnant of Valnum + lexical CSE) 3. Alignment: (a) Moved printing of alignment boundaries from `emitIssue1Instr` to `emitEndCodeGen`, to avoid the possibility of reading an instruction beyond the basic block. Also, improved the Intel jcc erratum criteria calculations, (b) Change `align` instructions of zero size to have a zero PerfScore throughput number (since they don't generate code), (c) Add `COMPlus_JitDasmWithAlignmentBoundaries` to force disasm output to display alignment boundaries. 4. Codegen / Emitter: (a) Added `emitLabelString` function for constructing a string to display for a bound emitter label. Created `emitPrintLabel` to directly print the label, (b) Add `genInsDisplayName` function to create a string for use when outputting an instruction. For xarch, this prepends the "v" for SIMD instructions, as necessary. This is preferable to calling the raw `genInsName` function, (c) For each insGroup, created a debug-only list of basic blocks that contributed code to that insGroup. Display this set of blocks in the JitDump disasm output, with block ID. This is useful for looking at an IG, and finding the blocks in a .dot flow graph visualization that contributed to it, (d) remove unused `instDisp` 5. Clrjit.natvis: (a) add support for `jitstd::vector`, `JitExpandArray`, `JitExpandArrayStack`, `LcOptInfo`. 6. Misc: (a) When compacting an empty loop preheader block with a subsequent block, clear the preheader flag. ## benchmarks.run.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 25504 Total bytes of diff: 25092 Total bytes of delta: -412 (-1.62% of base) Total relative delta: -0.31 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -92 : 14861.dasm (-2.57% of base) -88 : 2430.dasm (-0.77% of base) -68 : 12182.dasm (-3.82% of base) -48 : 24678.dasm (-1.61% of base) -31 : 21598.dasm (-5.13% of base) -26 : 21601.dasm (-4.57% of base) -21 : 25069.dasm (-7.14% of base) -16 : 14859.dasm (-1.38% of base) -11 : 14862.dasm (-1.35% of base) -6 : 21600.dasm (-1.83% of base) -5 : 25065.dasm (-0.58% of base) 11 total files with Code Size differences (11 improved, 0 regressed), 1 unchanged. Top method improvements (bytes): -92 (-2.57% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -88 (-0.77% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -68 (-3.82% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -48 (-1.61% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -31 (-5.13% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -21 (-7.14% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -16 (-1.38% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -6 (-1.83% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -5 (-0.58% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this Top method improvements (percentages): -21 (-7.14% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -31 (-5.13% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -68 (-3.82% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -92 (-2.57% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -6 (-1.83% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -48 (-1.61% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -16 (-1.38% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -88 (-0.77% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -5 (-0.58% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this 11 total methods with Code Size differences (11 improved, 0 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 38374.96 Total PerfScoreUnits of diff: 37914.07000000001 Total PerfScoreUnits of delta: -460.89 (-1.20% of base) Total relative delta: -0.12 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -220.67 : 24678.dasm (-1.74% of base) -99.27 : 14861.dasm (-2.09% of base) -66.30 : 21598.dasm (-1.41% of base) -18.73 : 2430.dasm (-0.28% of base) -18.40 : 21601.dasm (-1.37% of base) -9.73 : 25065.dasm (-0.56% of base) -9.05 : 14859.dasm (-0.77% of base) -5.51 : 21600.dasm (-0.77% of base) -4.15 : 12182.dasm (-0.17% of base) -3.92 : 14860.dasm (-0.32% of base) -3.46 : 25069.dasm (-2.31% of base) -1.70 : 14862.dasm (-0.20% of base) 12 total files with Perf Score differences (12 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -220.67 (-1.74% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -99.27 (-2.09% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -66.30 (-1.41% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.73 (-0.28% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -18.40 (-1.37% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.73 (-0.56% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this -9.05 (-0.77% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -4.15 (-0.17% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -3.92 (-0.32% of base) : 14860.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -3.46 (-2.31% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -1.70 (-0.20% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) Top method improvements (percentages): -3.46 (-2.31% of base) : 25069.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -99.27 (-2.09% of base) : 14861.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -220.67 (-1.74% of base) : 24678.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -66.30 (-1.41% of base) : 21598.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.40 (-1.37% of base) : 21601.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.05 (-0.77% of base) : 14859.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 21600.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -9.73 (-0.56% of base) : 25065.dasm - Benchstone.BenchF.InProd:Test():bool:this -3.92 (-0.32% of base) : 14860.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -18.73 (-0.28% of base) : 2430.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this -1.70 (-0.20% of base) : 14862.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -4.15 (-0.17% of base) : 12182.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) 12 total methods with Perf Score differences (12 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ## coreclr_tests.pmi.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 25430 Total bytes of diff: 24994 Total bytes of delta: -436 (-1.71% of base) Total relative delta: -0.42 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -92 : 194668.dasm (-2.57% of base) -68 : 194589.dasm (-3.82% of base) -48 : 248565.dasm (-1.61% of base) -32 : 249053.dasm (-3.58% of base) -31 : 251012.dasm (-5.13% of base) -26 : 251011.dasm (-4.57% of base) -19 : 248561.dasm (-6.76% of base) -16 : 194667.dasm (-1.38% of base) -15 : 252241.dasm (-0.72% of base) -12 : 252242.dasm (-0.81% of base) -11 : 194669.dasm (-1.35% of base) -9 : 246308.dasm (-1.06% of base) -9 : 246307.dasm (-1.06% of base) -9 : 246245.dasm (-1.06% of base) -9 : 246246.dasm (-1.06% of base) -6 : 228622.dasm (-0.77% of base) -6 : 251010.dasm (-1.83% of base) -5 : 248557.dasm (-0.61% of base) -4 : 249054.dasm (-0.50% of base) -4 : 249052.dasm (-0.47% of base) 22 total files with Code Size differences (22 improved, 0 regressed), 1 unchanged. Top method improvements (bytes): -92 (-2.57% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -68 (-3.82% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -48 (-1.61% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -32 (-3.58% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -31 (-5.13% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -19 (-6.76% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -16 (-1.38% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -15 (-0.72% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -12 (-0.81% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -11 (-1.35% of base) : 194669.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -9 (-1.06% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -6 (-0.77% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -6 (-1.83% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -5 (-0.61% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -4 (-0.50% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -4 (-0.47% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() Top method improvements (percentages): -19 (-6.76% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -31 (-5.13% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -26 (-4.57% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -68 (-3.82% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -32 (-3.58% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -92 (-2.57% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -6 (-1.83% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -48 (-1.61% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -16 (-1.38% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -11 (-1.35% of base) : 194669.dasm - LUDecomp:lubksb(System.Double[][],int,System.Int32[],System.Double[]) -3 (-1.11% of base) : 249057.dasm - SimpleArray_01.Test:Test2() -9 (-1.06% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -9 (-1.06% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -12 (-0.81% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -6 (-0.77% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -15 (-0.72% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -5 (-0.61% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -4 (-0.50% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() 22 total methods with Code Size differences (22 improved, 0 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 161610.68999999997 Total PerfScoreUnits of diff: 160290.10999999996 Total PerfScoreUnits of delta: -1320.58 (-0.82% of base) Total relative delta: -0.20 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -639.25 : 252241.dasm (-0.97% of base) -220.67 : 248565.dasm (-1.74% of base) -132.59 : 252242.dasm (-0.26% of base) -99.27 : 194668.dasm (-2.09% of base) -66.30 : 251012.dasm (-1.41% of base) -62.20 : 249053.dasm (-2.74% of base) -18.40 : 251011.dasm (-1.37% of base) -9.33 : 248557.dasm (-0.54% of base) -9.05 : 194667.dasm (-0.77% of base) -8.32 : 249054.dasm (-0.42% of base) -5.85 : 246308.dasm (-0.52% of base) -5.85 : 246307.dasm (-0.52% of base) -5.85 : 246245.dasm (-0.52% of base) -5.85 : 246246.dasm (-0.52% of base) -5.51 : 251010.dasm (-0.77% of base) -4.36 : 249052.dasm (-0.22% of base) -4.16 : 253363.dasm (-0.21% of base) -4.15 : 194589.dasm (-0.17% of base) -3.92 : 194666.dasm (-0.32% of base) -3.41 : 248561.dasm (-2.29% of base) 23 total files with Perf Score differences (23 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -639.25 (-0.97% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -220.67 (-1.74% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -132.59 (-0.26% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -99.27 (-2.09% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -66.30 (-1.41% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -62.20 (-2.74% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -18.40 (-1.37% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -9.33 (-0.54% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -9.05 (-0.77% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -8.32 (-0.42% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -5.85 (-0.52% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.51 (-0.77% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -4.36 (-0.22% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() -4.16 (-0.21% of base) : 253363.dasm - MatrixMul.Test:MatrixMul() -4.15 (-0.17% of base) : 194589.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) -3.92 (-0.32% of base) : 194666.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -3.41 (-2.29% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) Top method improvements (percentages): -62.20 (-2.74% of base) : 249053.dasm - SimpleArray_01.Test:BadMatrixMul2() -3.41 (-2.29% of base) : 248561.dasm - Benchstone.BenchF.InProd:InnerProduct(byref,System.Double[][],System.Double[][],int,int) -99.27 (-2.09% of base) : 194668.dasm - LUDecomp:ludcmp(System.Double[][],int,System.Int32[],byref):int -220.67 (-1.74% of base) : 248565.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) -2.70 (-1.71% of base) : 249057.dasm - SimpleArray_01.Test:Test2() -66.30 (-1.41% of base) : 251012.dasm - Benchstone.BenchI.Array2:Bench(int):bool -18.40 (-1.37% of base) : 251011.dasm - Benchstone.BenchI.Array2:VerifyCopy(System.Int32[][][],System.Int32[][][]):bool -639.25 (-0.97% of base) : 252241.dasm - Complex_Array_Test:Main(System.String[]):int -9.05 (-0.77% of base) : 194667.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -5.51 (-0.77% of base) : 251010.dasm - Benchstone.BenchI.Array2:Initialize(System.Int32[][][]) -9.33 (-0.54% of base) : 248557.dasm - Benchstone.BenchF.InProd:Bench():bool -5.85 (-0.52% of base) : 246308.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246307.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246245.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagAry(System.Object[][][],int,int):this -5.85 (-0.52% of base) : 246246.dasm - DefaultNamespace.MulDimJagAry:SetThreeDimJagVarAry(System.Object[][][],int,int):this -8.32 (-0.42% of base) : 249054.dasm - SimpleArray_01.Test:BadMatrixMul3() -3.92 (-0.32% of base) : 194666.dasm - LUDecomp:DoLUIteration(System.Double[][],System.Double[],System.Double[][][],System.Double[][],int):long -132.59 (-0.26% of base) : 252242.dasm - Simple_Array_Test:Main(System.String[]):int -1.89 (-0.22% of base) : 228622.dasm - SciMark2.LU:solve(System.Double[][],System.Int32[],System.Double[]) -4.36 (-0.22% of base) : 249052.dasm - SimpleArray_01.Test:BadMatrixMul1() 23 total methods with Perf Score differences (23 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ## libraries.crossgen2.windows.x64.checked.mch: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 10828 Total bytes of diff: 10809 Total bytes of delta: -19 (-0.18% of base) Total relative delta: -0.00 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (bytes): -19 : 72504.dasm (-0.18% of base) 1 total files with Code Size differences (1 improved, 0 regressed), 0 unchanged. Top method improvements (bytes): -19 (-0.18% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method improvements (percentages): -19 (-0.18% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 1 total methods with Code Size differences (1 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Perf Score diffs: (Lower is better) Total PerfScoreUnits of base: 6597.12 Total PerfScoreUnits of diff: 6586.31 Total PerfScoreUnits of delta: -10.81 (-0.16% of base) Total relative delta: -0.00 diff is an improvement. relative diff is an improvement. ```
Detail diffs ``` Top file improvements (PerfScoreUnits): -10.81 : 72504.dasm (-0.16% of base) 1 total files with Perf Score differences (1 improved, 0 regressed), 0 unchanged. Top method improvements (PerfScoreUnits): -10.81 (-0.16% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method improvements (percentages): -10.81 (-0.16% of base) : 72504.dasm - System.DefaultBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 1 total methods with Perf Score differences (1 improved, 0 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- * Increase loop cloning max allowed condition blocks Allows inner loop of 3-nested loops (e.g., Array2 benchmark) to be cloned. * Clear GTF_INX_RNGCHK bit on loop cloning created index nodes to avoid unnecessary bounds checks. Revert max cloning condition blocks to 3; allowing more doesn't seem to improve performance (probably too many conditions before a not-sufficiently-executed loop, at least for the Array2 benchmark) * Remove outer index bounds checks * Convert loop cloning data structures to `vector` for better debugging * Improve CSE dump output 1. "#if 0" the guts of the CSE dataflow; that's not useful to most people. 2. Add readable CSE number output to the CSE dataflow set output 3. Add FMT_CSE to commonize CSE number output. 4. Add PHASE_OPTIMIZE_VALNUM_CSES to the pre-phase output "allow list" and stop doing its own blocks/trees output. 5. Remove unused optCSECandidateTotal 6. Add functions `getCSEAvailBit` and `getCSEAvailCrossCallBit` to avoid hand-coding these bit calculations in multiple places, for the CSE dataflow set bits. * Mark cloned array indexes as non-faulting When generating loop cloning conditions, mark array index expressions as non-faulting, as we have already null- and range-checked the array before generating an index expression. I also added similary code to mark array length expressions as non-faulting, for the same reason. However, that leads to CQ losses because of downstream CSE effects. * Don't count zero-sized align instructions in PerfScore * Add COMPlus_JitDasmWithAlignmentBoundaries This outputs the alignment boundaries without requiring outputting the actual addresses. It makes it easier to diff changes. * Improve bounds check output * Improve emitter label printing Create function for printing bound emitter labels. Also, add debug code to associate a BasicBlock with an insGroup, and output the block number and ID with the emitter label in JitDump, so it's easier to find where a group of generated instructions came from. * Formatting * Clear BBF_LOOP_PREHEADER bit when compacting empty pre-header block * Keep track of all basic blocks that contribute code to an insGroup * Update display of Intel jcc erratum branches in dump For instructions or instruction sequences which match the Intel jcc erratum criteria, note that in the alignment boundary dump. Also, a few fixes: 1. Move the alignment boundary dumping from `emitIssue1Instr` to `emitEndCodeGen` to avoid the possibility of reading the next instruction in a group when there is no next instruction. 2. Create `IsJccInstruction` and `IsJmpInstruction` functions for use by the jcc criteria detection, and fix that detection to fix a few omissions/errors. 3. Change the jcc criteria detection to be hard-coded to 32 byte boundaries instead of assuming `compJitAlignLoopBoundary` is 32. An example: ``` cmp r11d, dword ptr [rax+8] ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (cmp: 0 ; jcc erratum) 32B boundary ............................... jae G_M42486_IG103 ``` In this case, the `cmp` doesn't cross the boundary, it is adjacent (the zero indicates the number of bytes of the instruction which cross the boundary), followed by the `jae` which starts after the boundary. Indicating the jcc erratum criteria can help point out potential performance issues due to unlucky alignment of these instructions in asm diffs. * Display full instruction name in alignment and other messages XArch sometimes prepends a "v" to the instructions names from the instruction table. Add a function `genInsDisplayName` to create the full instruction name that should be displayed, and use that in most places an instruction name will be displayed, such as in the alignment messages, and normal disassembly. Use this instead of the raw `genInsName`. This could be extended to handle arm32 appending an "s", but I didn't want to touch arm32 with this change. * Fix build * Code review feedback 1. Rename GTF_INX_NONFAULTING to GTF_INX_NOFAULT to increase clarity compared to existing GTF_IND_NONFAULTING. 2. Minor cleanup in getInsDisplayName. * Formatting --- src/coreclr/jit/block.h | 5 +- src/coreclr/jit/clrjit.natvis | 51 +++++ src/coreclr/jit/codegen.h | 5 +- src/coreclr/jit/codegencommon.cpp | 7 +- src/coreclr/jit/codegenlinear.cpp | 2 +- src/coreclr/jit/compiler.cpp | 6 + src/coreclr/jit/compiler.h | 65 +++++-- src/coreclr/jit/compiler.hpp | 19 -- src/coreclr/jit/emit.cpp | 296 +++++++++++++++++++---------- src/coreclr/jit/emit.h | 14 +- src/coreclr/jit/emitarm.cpp | 6 +- src/coreclr/jit/emitarm64.cpp | 8 +- src/coreclr/jit/emitxarch.cpp | 85 ++++++--- src/coreclr/jit/emitxarch.h | 18 +- src/coreclr/jit/fgopt.cpp | 3 + src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/instr.cpp | 65 +++---- src/coreclr/jit/jitconfigvalues.h | 3 + src/coreclr/jit/jitstd/algorithm.h | 4 +- src/coreclr/jit/loopcloning.cpp | 188 ++++++++++++++---- src/coreclr/jit/loopcloning.h | 45 ++--- src/coreclr/jit/morph.cpp | 11 +- src/coreclr/jit/optcse.cpp | 186 ++++++++++-------- src/coreclr/jit/optimizer.cpp | 9 +- src/coreclr/jit/phase.cpp | 5 +- 26 files changed, 733 insertions(+), 376 deletions(-) diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 0cbe51281b3..5d42a162653 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -29,7 +29,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jithashtable.h" /*****************************************************************************/ -typedef BitVec EXPSET_TP; +typedef BitVec EXPSET_TP; +typedef BitVec_ValArg_T EXPSET_VALARG_TP; +typedef BitVec_ValRet_T EXPSET_VALRET_TP; + #if LARGE_EXPSET #define EXPSET_SZ 64 #else diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis index 90a9ff703a4..b3c18747490 100644 --- a/src/coreclr/jit/clrjit.natvis +++ b/src/coreclr/jit/clrjit.natvis @@ -5,6 +5,13 @@ Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. --> + @@ -183,4 +190,48 @@ The .NET Foundation licenses this file to you under the MIT license. + + size={m_nSize,d} capacity={m_nCapacity,d} + Empty + + + m_nSize + m_pArray + + + + + + size={m_size,d} + Empty + + + m_size + m_members + + + + + + size={m_size,d} used={m_used,d} + Empty + + + m_used + m_members + + + + + + + + + {optType,en} + + (LcJaggedArrayOptInfo*)this,nd + (LcMdArrayOptInfo*)this,nd + + + diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 132a763c06b..626cb3e5b7b 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -216,6 +216,7 @@ protected: unsigned genCurDispOffset; static const char* genInsName(instruction ins); + const char* genInsDisplayName(emitter::instrDesc* id); #endif // DEBUG //------------------------------------------------------------------------- @@ -1503,10 +1504,6 @@ public: void instGen_Store_Reg_Into_Lcl(var_types dstType, regNumber srcReg, int varNum, int offs); -#ifdef DEBUG - void __cdecl instDisp(instruction ins, bool noNL, const char* fmt, ...); -#endif - #ifdef TARGET_XARCH instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue); #endif // TARGET_XARCH diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index aeecb0553b9..d2e08159ec1 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1070,7 +1070,7 @@ void CodeGen::genDefineTempLabel(BasicBlock* label) { genLogLabel(label); label->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label->bbNum)); + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label)); } // genDefineInlineTempLabel: Define an inline label that does not affect the GC @@ -2064,9 +2064,8 @@ void CodeGen::genInsertNopForUnwinder(BasicBlock* block) // block starts an EH region. If we pointed the existing bbEmitCookie here, then the NOP // would be executed, which we would prefer not to do. - block->bbUnwindNopEmitCookie = - GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - false DEBUG_ARG(block->bbNum)); + block->bbUnwindNopEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block)); instGen(INS_nop); } diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index b822f233e66..aa2fb0f5895 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -356,7 +356,7 @@ void CodeGen::genCodeForBBlist() // Mark a label and update the current set of live GC refs block->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block->bbNum)); + gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block)); } if (block == compiler->fgFirstColdBlock) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7019d8afad6..bf49e63ab0c 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2988,6 +2988,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.disAsmSpilled = false; opts.disDiffable = false; opts.disAddr = false; + opts.disAlignment = false; opts.dspCode = false; opts.dspEHTable = false; opts.dspDebugInfo = false; @@ -3136,6 +3137,11 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.disAddr = true; } + if (JitConfig.JitDasmWithAlignmentBoundaries() != 0) + { + opts.disAlignment = true; + } + if (JitConfig.JitLongAddress() != 0) { opts.compLongAddress = true; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1383b57a7dd..b60353d8e84 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6237,9 +6237,9 @@ private: public: void optInit(); - GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt); - GenTree* Compiler::optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt); - void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt); + GenTree* optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt); + GenTree* optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt); + void optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt); bool optIsRangeCheckRemovable(GenTree* tree); protected: @@ -6728,7 +6728,7 @@ protected: // BitVec trait information for computing CSE availability using the CSE_DataFlow algorithm. // Two bits are allocated per CSE candidate to compute CSE availability // plus an extra bit to handle the initial unvisited case. - // (See CSE_DataFlow::EndMerge for an explaination of why this is necessary) + // (See CSE_DataFlow::EndMerge for an explanation of why this is necessary.) // // The two bits per CSE candidate have the following meanings: // 11 - The CSE is available, and is also available when considering calls as killing availability. @@ -6738,6 +6738,37 @@ protected: // BitVecTraits* cseLivenessTraits; + //----------------------------------------------------------------------------------------------------------------- + // getCSEnum2bit: Return the normalized index to use in the EXPSET_TP for the CSE with the given CSE index. + // Each GenTree has a `gtCSEnum` field. Zero is reserved to mean this node is not a CSE, positive values indicate + // CSE uses, and negative values indicate CSE defs. The caller must pass a non-zero positive value, as from + // GET_CSE_INDEX(). + // + static unsigned genCSEnum2bit(unsigned CSEnum) + { + assert((CSEnum > 0) && (CSEnum <= MAX_CSE_CNT)); + return CSEnum - 1; + } + + //----------------------------------------------------------------------------------------------------------------- + // getCSEAvailBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit for a CSE. + // + static unsigned getCSEAvailBit(unsigned CSEnum) + { + return genCSEnum2bit(CSEnum) * 2; + } + + //----------------------------------------------------------------------------------------------------------------- + // getCSEAvailCrossCallBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit + // for a CSE considering calls as killing availability bit (see description above). + // + static unsigned getCSEAvailCrossCallBit(unsigned CSEnum) + { + return getCSEAvailBit(CSEnum) + 1; + } + + void optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits = true); + EXPSET_TP cseCallKillsMask; // Computed once - A mask that is used to kill available CSEs at callsites /* Generic list of nodes - used by the CSE logic */ @@ -6872,9 +6903,12 @@ protected: return (enckey & ~TARGET_SIGN_BIT) << CSE_CONST_SHARED_LOW_BITS; } - /************************************************************************** - * Value Number based CSEs - *************************************************************************/ +/************************************************************************** + * Value Number based CSEs + *************************************************************************/ + +// String to use for formatting CSE numbers. Note that this is the positive number, e.g., from GET_CSE_INDEX(). +#define FMT_CSE "CSE #%02u" public: void optOptimizeValnumCSEs(); @@ -6882,16 +6916,15 @@ public: protected: void optValnumCSE_Init(); unsigned optValnumCSE_Index(GenTree* tree, Statement* stmt); - unsigned optValnumCSE_Locate(); - void optValnumCSE_InitDataFlow(); - void optValnumCSE_DataFlow(); - void optValnumCSE_Availablity(); - void optValnumCSE_Heuristic(); + bool optValnumCSE_Locate(); + void optValnumCSE_InitDataFlow(); + void optValnumCSE_DataFlow(); + void optValnumCSE_Availablity(); + void optValnumCSE_Heuristic(); bool optDoCSE; // True when we have found a duplicate CSE tree - bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase - unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum - unsigned optCSECandidateCount; // Count of CSE's candidates, reset for Lexical and ValNum CSE's + bool optValnumCSE_phase; // True when we are executing the optOptimizeValnumCSEs() phase + unsigned optCSECandidateCount; // Count of CSE's candidates unsigned optCSEstart; // The first local variable number that is a CSE unsigned optCSEcount; // The total count of CSE's introduced. BasicBlock::weight_t optCSEweight; // The weight of the current block when we are doing PerformCSE @@ -6916,6 +6949,7 @@ protected: bool optConfigDisableCSE(); bool optConfigDisableCSE2(); #endif + void optOptimizeCSEs(); struct isVarAssgDsc @@ -9337,6 +9371,7 @@ public: bool disasmWithGC; // Display GC info interleaved with disassembly. bool disDiffable; // Makes the Disassembly code 'diff-able' bool disAddr; // Display process address next to each instruction in disassembly code + bool disAlignment; // Display alignment boundaries in disassembly code bool disAsm2; // Display native code after it is generated using external disassembler bool dspOrder; // Display names of each of the methods that we ngen/jit bool dspUnwind; // Display the unwind info output diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index d05bfc6bbeb..ca9626ee11c 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -763,25 +763,6 @@ inline double getR8LittleEndian(const BYTE* ptr) return *(double*)&val; } -/***************************************************************************** - * - * Return the normalized index to use in the EXPSET_TP for the CSE with - * the given CSE index. - * Each GenTree has the following field: - * signed char gtCSEnum; // 0 or the CSE index (negated if def) - * So zero is reserved to mean this node is not a CSE - * and postive values indicate CSE uses and negative values indicate CSE defs. - * The caller of this method must pass a non-zero postive value. - * This precondition is checked by the assert on the first line of this method. - */ - -inline unsigned int genCSEnum2bit(unsigned index) -{ - assert((index > 0) && (index <= EXPSET_SZ)); - - return (index - 1); -} - #ifdef DEBUG const char* genES2str(BitVecTraits* traits, EXPSET_TP set); const char* refCntWtd2str(BasicBlock::weight_t refCntWtd); diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 913951568fd..ca6cd0c4a71 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -773,7 +773,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) memcpy(id, emitCurIGfreeBase, sz); #ifdef DEBUG - if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false) + if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false)) { // If there's an error during emission, we may want to connect the post-copy address // of an instrDesc with the pre-copy address (the one that was originally created). This @@ -843,7 +843,7 @@ insGroup* emitter::emitSavIG(bool emitAdd) #ifdef DEBUG if (emitComp->opts.dspCode) { - printf("\n G_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum); + printf("\n %s:", emitLabelString(ig)); if (emitComp->verbose) { printf(" ; offs=%06XH, funclet=%02u, bbWeight=%s", ig->igOffs, ig->igFuncIdx, @@ -1184,7 +1184,7 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const //---------------------------------------------------------------------------------------- // insEvaluateExecutionCost: -// Returns the estimate execution cost fortyhe current instruction +// Returns the estimated execution cost for the current instruction // // Arguments: // id - The current instruction descriptor to be evaluated @@ -1193,8 +1193,6 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const // calls getInsExecutionCharacteristics and uses the result // to compute an estimated execution cost // -// Notes: -// float emitter::insEvaluateExecutionCost(instrDesc* id) { insExecutionCharacteristics result = getInsExecutionCharacteristics(id); @@ -1202,8 +1200,10 @@ float emitter::insEvaluateExecutionCost(instrDesc* id) float latency = result.insLatency; unsigned memAccessKind = result.insMemoryAccessKind; - // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL - assert(throughput > 0.0); + // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL. + // Note that 0.0 throughput is allowed for pseudo-instructions in the instrDesc list that won't actually + // generate code. + assert(throughput >= 0.0); assert(latency >= 0.0); if (memAccessKind == PERFSCORE_MEMORY_WRITE) @@ -1241,7 +1241,7 @@ float emitter::insEvaluateExecutionCost(instrDesc* id) void emitter::perfScoreUnhandledInstruction(instrDesc* id, insExecutionCharacteristics* pResult) { #ifdef DEBUG - printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsName(id->idIns()), + printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsDisplayName(id), emitIfName(id->idInsFmt())); assert(!"PerfScore: unhandled instruction"); #endif @@ -1524,6 +1524,14 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz) emitCurIGinsCnt++; +#ifdef DEBUG + if (emitComp->compCurBB != emitCurIG->lastGeneratedBlock) + { + emitCurIG->igBlocks.push_back(emitComp->compCurBB); + emitCurIG->lastGeneratedBlock = emitComp->compCurBB; + } +#endif // DEBUG + return id; } @@ -2504,7 +2512,7 @@ bool emitter::emitNoGChelper(CORINFO_METHOD_HANDLE methHnd) void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - bool isFinallyTarget DEBUG_ARG(unsigned bbNum)) + bool isFinallyTarget DEBUG_ARG(BasicBlock* block)) { /* Create a new IG if the current one is non-empty */ @@ -2533,7 +2541,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, #endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) #ifdef DEBUG - JITDUMP("Mapped " FMT_BB " to G_M%03u_IG%02u\n", bbNum, emitComp->compMethodID, emitCurIG->igNum); + JITDUMP("Mapped " FMT_BB " to %s\n", block->bbNum, emitLabelString(emitCurIG)); if (EMIT_GC_VERBOSE) { @@ -2548,6 +2556,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars, printf("\n"); } #endif + return emitCurIG; } @@ -2561,6 +2570,40 @@ void* emitter::emitAddInlineLabel() return emitCurIG; } +#ifdef DEBUG + +//----------------------------------------------------------------------------- +// emitPrintLabel: Print the assembly label for an insGroup. We could use emitter::emitLabelString() +// to be consistent, but that seems silly. +// +void emitter::emitPrintLabel(insGroup* ig) +{ + printf("G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); +} + +//----------------------------------------------------------------------------- +// emitLabelString: Return label string for an insGroup, for use in debug output. +// This can be called up to four times in a single 'printf' before the static buffers +// get reused. +// +// Returns: +// String with insGroup label +// +const char* emitter::emitLabelString(insGroup* ig) +{ + const int TEMP_BUFFER_LEN = 40; + static unsigned curBuf = 0; + static char buf[4][TEMP_BUFFER_LEN]; + const char* retbuf; + + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); + retbuf = buf[curBuf]; + curBuf = (curBuf + 1) % 4; + return retbuf; +} + +#endif // DEBUG + #ifdef TARGET_ARMARCH // Does the argument location point to an IG at the end of a function or funclet? @@ -3502,7 +3545,7 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) const int TEMP_BUFFER_LEN = 40; char buff[TEMP_BUFFER_LEN]; - sprintf_s(buff, TEMP_BUFFER_LEN, "G_M%03u_IG%02u: ", emitComp->compMethodID, ig->igNum); + sprintf_s(buff, TEMP_BUFFER_LEN, "%s: ", emitLabelString(ig)); printf("%s; ", buff); // We dump less information when we're only interleaving GC info with a disassembly listing, @@ -3599,9 +3642,10 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) else { const char* separator = ""; + if (jitdump) { - printf("offs=%06XH, size=%04XH", ig->igOffs, ig->igSize); + printf("%soffs=%06XH, size=%04XH", separator, ig->igOffs, ig->igSize); separator = ", "; } @@ -3642,6 +3686,15 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) } #endif // FEATURE_LOOP_ALIGN + if (jitdump && !ig->igBlocks.empty()) + { + for (auto block : ig->igBlocks) + { + printf("%s%s", separator, block->dspToString()); + separator = ", "; + } + } + emitDispIGflags(ig->igFlags); if (ig == emitCurIG) @@ -3733,10 +3786,6 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) { size_t is; -#ifdef DEBUG - size_t beforeAddr = (size_t)*dp; -#endif - /* Record the beginning offset of the instruction */ BYTE* curInsAdr = *dp; @@ -3822,52 +3871,7 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) id->idDebugOnlyInfo()->idNum, is, emitSizeOfInsDsc(id)); assert(is == emitSizeOfInsDsc(id)); } - - // Print the alignment boundary - if ((emitComp->opts.disAsm || emitComp->verbose) && emitComp->opts.disAddr) - { - size_t currAddr = (size_t)*dp; - size_t lastBoundaryAddr = currAddr & ~((size_t)emitComp->opts.compJitAlignLoopBoundary - 1); - - // draw boundary if beforeAddr was before the lastBoundary. - if (beforeAddr < lastBoundaryAddr) - { - printf("; "); - instruction currIns = id->idIns(); - -#if defined(TARGET_XARCH) - - // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf - bool isJccAffectedIns = - ((currIns >= INS_i_jmp && currIns < INS_align) || (currIns == INS_call) || (currIns == INS_ret)); - - instrDesc* nextId = id; - castto(nextId, BYTE*) += is; - instruction nextIns = nextId->idIns(); - if ((currIns == INS_cmp) || (currIns == INS_test) || (currIns == INS_add) || (currIns == INS_sub) || - (currIns == INS_and) || (currIns == INS_inc) || (currIns == INS_dec)) - { - isJccAffectedIns |= (nextIns >= INS_i_jmp && nextIns < INS_align); - } -#else - bool isJccAffectedIns = false; -#endif - - // Indicate if instruction is at at 32B boundary or is splitted - unsigned bytesCrossedBoundary = (currAddr & (emitComp->opts.compJitAlignLoopBoundary - 1)); - if ((bytesCrossedBoundary != 0) || (isJccAffectedIns && bytesCrossedBoundary == 0)) - { - printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)", codeGen->genInsName(id->idIns()), - bytesCrossedBoundary); - } - else - { - printf("..............................."); - } - printf(" %dB boundary ...............................\n", (emitComp->opts.compJitAlignLoopBoundary)); - } - } -#endif +#endif // DEBUG return is; } @@ -4229,7 +4233,7 @@ AGAIN: { if (tgtIG) { - printf(" to G_M%03u_IG%02u\n", emitComp->compMethodID, tgtIG->igNum); + printf(" to %s\n", emitLabelString(tgtIG)); } else { @@ -4687,8 +4691,7 @@ void emitter::emitLoopAlignment() // all IGs that follows this IG and participate in a loop. emitCurIG->igFlags |= IGF_LOOP_ALIGN; - JITDUMP("Adding 'align' instruction of %d bytes in G_M%03u_IG%02u.\n", paddingBytes, emitComp->compMethodID, - emitCurIG->igNum); + JITDUMP("Adding 'align' instruction of %d bytes in %s.\n", paddingBytes, emitLabelString(emitCurIG)); #ifdef DEBUG emitComp->loopAlignCandidates++; @@ -5027,10 +5030,10 @@ void emitter::emitLoopAlignAdjustments() alignInstr = prevAlignInstr; } - JITDUMP("Adjusted alignment of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, - estimatedPaddingNeeded, actualPaddingNeeded); - JITDUMP("Adjusted size of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, - (alignIG->igSize + diff), alignIG->igSize); + JITDUMP("Adjusted alignment of %s from %u to %u.\n", emitLabelString(alignIG), estimatedPaddingNeeded, + actualPaddingNeeded); + JITDUMP("Adjusted size of %s from %u to %u.\n", emitLabelString(alignIG), (alignIG->igSize + diff), + alignIG->igSize); } // Adjust the offset of all IGs starting from next IG until we reach the IG having the next @@ -5039,8 +5042,8 @@ void emitter::emitLoopAlignAdjustments() insGroup* adjOffUptoIG = alignInstr->idaNext != nullptr ? alignInstr->idaNext->idaIG : emitIGlast; while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum)) { - JITDUMP("Adjusted offset of G_M%03u_IG%02u from %04X to %04X\n", emitComp->compMethodID, adjOffIG->igNum, - adjOffIG->igOffs, (adjOffIG->igOffs - alignBytesRemoved)); + JITDUMP("Adjusted offset of %s from %04X to %04X\n", emitLabelString(adjOffIG), adjOffIG->igOffs, + (adjOffIG->igOffs - alignBytesRemoved)); adjOffIG->igOffs -= alignBytesRemoved; adjOffIG = adjOffIG->igNext; } @@ -5099,8 +5102,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs // No padding if loop is already aligned if ((offset & (alignmentBoundary - 1)) == 0) { - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %dB boundary.'\n", - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %dB boundary.'\n", emitLabelString(ig->igNext), + alignmentBoundary); return 0; } @@ -5124,8 +5127,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs // No padding if loop is big if (loopSize > maxLoopSize) { - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is big. LoopSize= %d, MaxLoopSize= %d.'\n", - emitComp->compMethodID, ig->igNext->igNum, loopSize, maxLoopSize); + JITDUMP(";; Skip alignment: 'Loop at %s is big. LoopSize= %d, MaxLoopSize= %d.'\n", emitLabelString(ig->igNext), + loopSize, maxLoopSize); return 0; } @@ -5151,17 +5154,16 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs if (nPaddingBytes == 0) { skipPadding = true; - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %uB boundary.'\n", - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %uB boundary.'\n", + emitLabelString(ig->igNext), alignmentBoundary); } // Check if the alignment exceeds new maxPadding limit else if (nPaddingBytes > nMaxPaddingBytes) { skipPadding = true; - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, " + JITDUMP(";; Skip alignment: 'Loop at %s PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, " "AlignmentBoundary= %dB.'\n", - emitComp->compMethodID, ig->igNext->igNum, nPaddingBytes, nMaxPaddingBytes, loopSize, - alignmentBoundary); + emitLabelString(ig->igNext), nPaddingBytes, nMaxPaddingBytes, loopSize, alignmentBoundary); } } @@ -5183,8 +5185,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs else { // Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment. - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n", - emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n", + emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary); } } } @@ -5212,13 +5214,13 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs else { // Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment. - JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n", - emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary); + JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n", + emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary); } } - JITDUMP(";; Calculated padding to add %d bytes to align G_M%03u_IG%02u at %dB boundary.\n", paddingToAdd, - emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary); + JITDUMP(";; Calculated padding to add %d bytes to align %s at %dB boundary.\n", paddingToAdd, + emitLabelString(ig->igNext), alignmentBoundary); // Either no padding is added because it is too expensive or the offset gets aligned // to the alignment boundary @@ -5900,7 +5902,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, } else { - printf("\nG_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum); + printf("\n%s:", emitLabelString(ig)); if (!emitComp->opts.disDiffable) { printf(" ;; offset=%04XH", emitCurCodeOffs(cp)); @@ -6017,9 +6019,97 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, emitCurIG = ig; - for (unsigned cnt = ig->igInsCnt; cnt; cnt--) + for (unsigned cnt = ig->igInsCnt; cnt > 0; cnt--) { +#ifdef DEBUG + size_t curInstrAddr = (size_t)cp; + instrDesc* curInstrDesc = id; +#endif + castto(id, BYTE*) += emitIssue1Instr(ig, id, &cp); + +#ifdef DEBUG + // Print the alignment boundary + if ((emitComp->opts.disAsm || emitComp->verbose) && (emitComp->opts.disAddr || emitComp->opts.disAlignment)) + { + size_t afterInstrAddr = (size_t)cp; + instruction curIns = curInstrDesc->idIns(); + bool isJccAffectedIns = false; + +#if defined(TARGET_XARCH) + + // Determine if this instruction is part of a set that matches the Intel jcc erratum characteristic + // described here: + // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf + // This is the case when a jump instruction crosses a 32-byte boundary, or ends on a 32-byte boundary. + // "Jump instruction" in this case includes conditional jump (jcc), macro-fused op-jcc (where 'op' is + // one of cmp, test, add, sub, and, inc, or dec), direct unconditional jump, indirect jump, + // direct/indirect call, and return. + + size_t jccAlignBoundary = 32; + size_t jccAlignBoundaryMask = jccAlignBoundary - 1; + size_t jccLastBoundaryAddr = afterInstrAddr & ~jccAlignBoundaryMask; + + if (curInstrAddr < jccLastBoundaryAddr) + { + isJccAffectedIns = IsJccInstruction(curIns) || IsJmpInstruction(curIns) || (curIns == INS_call) || + (curIns == INS_ret); + + // For op-Jcc there are two cases: (1) curIns is the jcc, in which case the above condition + // already covers us. (2) curIns is the `op` and the next instruction is the `jcc`. Note that + // we will never have a `jcc` as the first instruction of a group, so we don't need to worry + // about looking ahead to the next group after a an `op` of `op-Jcc`. + + if (!isJccAffectedIns && (cnt > 1)) + { + // The current `id` is valid, namely, there is another instruction in this group. + instruction nextIns = id->idIns(); + if (((curIns == INS_cmp) || (curIns == INS_test) || (curIns == INS_add) || + (curIns == INS_sub) || (curIns == INS_and) || (curIns == INS_inc) || + (curIns == INS_dec)) && + IsJccInstruction(nextIns)) + { + isJccAffectedIns = true; + } + } + + if (isJccAffectedIns) + { + unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & jccAlignBoundaryMask); + printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d ; jcc erratum) %dB boundary " + "...............................\n", + codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary, jccAlignBoundary); + } + } + +#endif // TARGET_XARCH + + // Jcc affected instruction boundaries were printed above; handle other cases here. + if (!isJccAffectedIns) + { + size_t alignBoundaryMask = (size_t)emitComp->opts.compJitAlignLoopBoundary - 1; + size_t lastBoundaryAddr = afterInstrAddr & ~alignBoundaryMask; + + // draw boundary if beforeAddr was before the lastBoundary. + if (curInstrAddr < lastBoundaryAddr) + { + // Indicate if instruction is at the alignment boundary or is split + unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & alignBoundaryMask); + if (bytesCrossedBoundary != 0) + { + printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)", + codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary); + } + else + { + printf("; ..............................."); + } + printf(" %dB boundary ...............................\n", + emitComp->opts.compJitAlignLoopBoundary); + } + } + } +#endif // DEBUG } #ifdef DEBUG @@ -6862,11 +6952,8 @@ void emitter::emitDispDataSec(dataSecDsc* section) BasicBlock* block = reinterpret_cast(data->dsCont)[i]; insGroup* ig = static_cast(emitCodeGetCookie(block)); - const char* blockLabelFormat = "G_M%03u_IG%02u"; - char blockLabel[64]; - char firstLabel[64]; - sprintf_s(blockLabel, _countof(blockLabel), blockLabelFormat, emitComp->compMethodID, ig->igNum); - sprintf_s(firstLabel, _countof(firstLabel), blockLabelFormat, emitComp->compMethodID, igFirst->igNum); + const char* blockLabel = emitLabelString(ig); + const char* firstLabel = emitLabelString(igFirst); if (isRelative) { @@ -7986,11 +8073,6 @@ insGroup* emitter::emitAllocIG() ig->igSelf = ig; #endif -#if defined(DEBUG) || defined(LATE_DISASM) - ig->igWeight = getCurrentBlockWeight(); - ig->igPerfScore = 0.0; -#endif - #if EMITTER_STATS emitTotalIGcnt += 1; emitTotalIGsize += sz; @@ -8028,6 +8110,11 @@ void emitter::emitInitIG(insGroup* ig) ig->igFlags = 0; +#if defined(DEBUG) || defined(LATE_DISASM) + ig->igWeight = getCurrentBlockWeight(); + ig->igPerfScore = 0.0; +#endif + /* Zero out some fields to avoid printing garbage in JitDumps. These really only need to be set in DEBUG, but do it in all cases to make sure we act the same in non-DEBUG builds. @@ -8040,6 +8127,12 @@ void emitter::emitInitIG(insGroup* ig) #if FEATURE_LOOP_ALIGN ig->igLoopBackEdge = nullptr; #endif + +#ifdef DEBUG + ig->lastGeneratedBlock = nullptr; + // Explicitly call the constructor, since IGs don't actually have a constructor. + ig->igBlocks.jitstd::list::list(emitComp->getAllocator(CMK_LoopOpt)); +#endif } /***************************************************************************** @@ -8104,6 +8197,11 @@ void emitter::emitNxtIG(bool extend) // We've created a new IG; no need to force another one. emitForceNewIG = false; + +#ifdef DEBUG + // We haven't written any code into the IG yet, so clear our record of the last block written to the IG. + emitCurIG->lastGeneratedBlock = nullptr; +#endif } /***************************************************************************** @@ -8638,7 +8736,7 @@ const char* emitter::emitOffsetToLabel(unsigned offs) if (ig->igOffs == offs) { // Found it! - sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum); + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "%s", emitLabelString(ig)); retbuf = buf[curBuf]; curBuf = (curBuf + 1) % 4; return retbuf; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 2748acf4637..ef67148ea96 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -247,6 +247,11 @@ struct insGroup double igPerfScore; // The PerfScore for this insGroup #endif +#ifdef DEBUG + BasicBlock* lastGeneratedBlock; // The last block that generated code into this insGroup. + jitstd::list igBlocks; // All the blocks that generated code into this insGroup. +#endif + UNATIVE_OFFSET igNum; // for ordering (and display) purposes UNATIVE_OFFSET igOffs; // offset of this group within method unsigned int igFuncIdx; // Which function/funclet does this belong to? (Index into Compiler::compFuncInfos array.) @@ -1229,6 +1234,8 @@ protected: #define PERFSCORE_THROUGHPUT_ILLEGAL -1024.0f +#define PERFSCORE_THROUGHPUT_ZERO 0.0f // Only used for pseudo-instructions that don't generate code + #define PERFSCORE_THROUGHPUT_6X (1.0f / 6.0f) // Hextuple issue #define PERFSCORE_THROUGHPUT_5X 0.20f // Pentuple issue #define PERFSCORE_THROUGHPUT_4X 0.25f // Quad issue @@ -1902,13 +1909,18 @@ private: void* emitAddLabel(VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, - bool isFinallyTarget = false DEBUG_ARG(unsigned bbNum = 0)); + bool isFinallyTarget = false DEBUG_ARG(BasicBlock* block = nullptr)); // Same as above, except the label is added and is conceptually "inline" in // the current block. Thus it extends the previous block and the emitter // continues to track GC info as if there was no label. void* emitAddInlineLabel(); +#ifdef DEBUG + void emitPrintLabel(insGroup* ig); + const char* emitLabelString(insGroup* ig); +#endif + #ifdef TARGET_ARMARCH void emitGetInstrDescs(insGroup* ig, instrDesc** id, int* insCnt); diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 6953df5b49a..5b01adbd14a 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -7292,7 +7292,7 @@ void emitter::emitDispInsHelp( lab = (insGroup*)emitCodeGetCookie(*bbp++); assert(lab); - printf("\n DD G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum); + printf("\n DD %s", emitLabelString(lab)); } while (--cnt); } } @@ -7601,7 +7601,7 @@ void emitter::emitDispInsHelp( case IF_T2_M1: // Load Label emitDispReg(id->idReg1(), attr, true); if (id->idIsBound()) - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); else printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); break; @@ -7646,7 +7646,7 @@ void emitter::emitDispInsHelp( } } else if (id->idIsBound()) - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); else printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); } diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 37e19a219d2..84c49d94723 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -12286,7 +12286,7 @@ void emitter::emitDispIns( } else if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12324,7 +12324,7 @@ void emitter::emitDispIns( emitDispReg(id->idReg1(), size, true); if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12338,7 +12338,7 @@ void emitter::emitDispIns( emitDispImm(emitGetInsSC(id), true); if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { @@ -12463,7 +12463,7 @@ void emitter::emitDispIns( } else if (id->idIsBound()) { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } else { diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 217f5f15aaf..c35a6675fe7 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -24,37 +24,37 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "emit.h" #include "codegen.h" -bool IsSSEInstruction(instruction ins) +bool emitter::IsSSEInstruction(instruction ins) { return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_SSE_INSTRUCTION); } -bool IsSSEOrAVXInstruction(instruction ins) +bool emitter::IsSSEOrAVXInstruction(instruction ins) { return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } -bool IsAVXOnlyInstruction(instruction ins) +bool emitter::IsAVXOnlyInstruction(instruction ins) { return (ins >= INS_FIRST_AVX_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } -bool IsFMAInstruction(instruction ins) +bool emitter::IsFMAInstruction(instruction ins) { return (ins >= INS_FIRST_FMA_INSTRUCTION) && (ins <= INS_LAST_FMA_INSTRUCTION); } -bool IsAVXVNNIInstruction(instruction ins) +bool emitter::IsAVXVNNIInstruction(instruction ins) { return (ins >= INS_FIRST_AVXVNNI_INSTRUCTION) && (ins <= INS_LAST_AVXVNNI_INSTRUCTION); } -bool IsBMIInstruction(instruction ins) +bool emitter::IsBMIInstruction(instruction ins) { return (ins >= INS_FIRST_BMI_INSTRUCTION) && (ins <= INS_LAST_BMI_INSTRUCTION); } -regNumber getBmiRegNumber(instruction ins) +regNumber emitter::getBmiRegNumber(instruction ins) { switch (ins) { @@ -81,7 +81,7 @@ regNumber getBmiRegNumber(instruction ins) } } -regNumber getSseShiftRegNumber(instruction ins) +regNumber emitter::getSseShiftRegNumber(instruction ins) { switch (ins) { @@ -123,7 +123,7 @@ regNumber getSseShiftRegNumber(instruction ins) } } -bool emitter::IsAVXInstruction(instruction ins) +bool emitter::IsAVXInstruction(instruction ins) const { return UseVEXEncoding() && IsSSEOrAVXInstruction(ins); } @@ -445,7 +445,7 @@ bool emitter::Is4ByteSSEInstruction(instruction ins) // Returns true if this instruction requires a VEX prefix // All AVX instructions require a VEX prefix -bool emitter::TakesVexPrefix(instruction ins) +bool emitter::TakesVexPrefix(instruction ins) const { // special case vzeroupper as it requires 2-byte VEX prefix // special case the fencing, movnti and the prefetch instructions as they never take a VEX prefix @@ -521,7 +521,7 @@ emitter::code_t emitter::AddVexPrefix(instruction ins, code_t code, emitAttr att } // Returns true if this instruction, for the given EA_SIZE(attr), will require a REX.W prefix -bool TakesRexWPrefix(instruction ins, emitAttr attr) +bool emitter::TakesRexWPrefix(instruction ins, emitAttr attr) { // Because the current implementation of AVX does not have a way to distinguish between the register // size specification (128 vs. 256 bits) and the operand size specification (32 vs. 64 bits), where both are @@ -4299,6 +4299,38 @@ bool emitter::IsMovInstruction(instruction ins) } } +//------------------------------------------------------------------------ +// IsJccInstruction: Determine if an instruction is a conditional jump instruction. +// +// Arguments: +// ins -- The instruction being checked +// +// Return Value: +// true if the instruction qualifies; otherwise, false +// +bool emitter::IsJccInstruction(instruction ins) +{ + return ((ins >= INS_jo) && (ins <= INS_jg)) || ((ins >= INS_l_jo) && (ins <= INS_l_jg)); +} + +//------------------------------------------------------------------------ +// IsJmpInstruction: Determine if an instruction is a jump instruction but NOT a conditional jump instruction. +// +// Arguments: +// ins -- The instruction being checked +// +// Return Value: +// true if the instruction qualifies; otherwise, false +// +bool emitter::IsJmpInstruction(instruction ins) +{ + return +#ifdef TARGET_AMD64 + (ins == INS_rex_jmp) || +#endif + (ins == INS_i_jmp) || (ins == INS_jmp) || (ins == INS_l_jmp); +} + //---------------------------------------------------------------------------------------- // IsRedundantMov: // Check if the current `mov` instruction is redundant and can be omitted. @@ -8459,7 +8491,7 @@ void emitter::emitDispAddrMode(instrDesc* id, bool noDetail) lab = (insGroup*)emitCodeGetCookie(*bbp++); assert(lab); - printf("\n D" SIZE_LETTER " G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum); + printf("\n D" SIZE_LETTER " %s", emitLabelString(lab)); } while (--cnt); } } @@ -8691,22 +8723,16 @@ void emitter::emitDispIns( /* Display the instruction name */ - sstr = codeGen->genInsName(ins); + sstr = codeGen->genInsDisplayName(id); + printf(" %-9s", sstr); - if (IsAVXInstruction(ins) && !IsBMIInstruction(ins)) - { - printf(" v%-8s", sstr); - } - else - { - printf(" %-9s", sstr); - } #ifndef HOST_UNIX - if (strnlen_s(sstr, 10) >= 8) + if (strnlen_s(sstr, 10) >= 9) #else // HOST_UNIX - if (strnlen(sstr, 10) >= 8) + if (strnlen(sstr, 10) >= 9) #endif // HOST_UNIX { + // Make sure there's at least one space after the instruction name, for very long instruction names. printf(" "); } @@ -9656,7 +9682,7 @@ void emitter::emitDispIns( } else { - printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum); + emitPrintLabel(id->idAddr()->iiaIGlabel); } } else @@ -14676,6 +14702,17 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins switch (ins) { case INS_align: +#if FEATURE_LOOP_ALIGN + if (id->idCodeSize() == 0) + { + // We're not going to generate any instruction, so it doesn't count for PerfScore. + result.insThroughput = PERFSCORE_THROUGHPUT_ZERO; + result.insLatency = PERFSCORE_LATENCY_ZERO; + break; + } +#endif + FALLTHROUGH; + case INS_nop: case INS_int3: assert(memFmt == IF_NONE); diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 8260445686b..f952a6f649f 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -83,7 +83,15 @@ code_t insEncodeOpreg(instruction ins, regNumber reg, emitAttr size); unsigned insSSval(unsigned scale); -bool IsAVXInstruction(instruction ins); +static bool IsSSEInstruction(instruction ins); +static bool IsSSEOrAVXInstruction(instruction ins); +static bool IsAVXOnlyInstruction(instruction ins); +static bool IsFMAInstruction(instruction ins); +static bool IsAVXVNNIInstruction(instruction ins); +static bool IsBMIInstruction(instruction ins); +static regNumber getBmiRegNumber(instruction ins); +static regNumber getSseShiftRegNumber(instruction ins); +bool IsAVXInstruction(instruction ins) const; code_t insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, code_t code); code_t AddRexWPrefix(instruction ins, code_t code); @@ -98,6 +106,9 @@ static bool IsMovInstruction(instruction ins); bool IsRedundantMov( instruction ins, insFormat fmt, emitAttr size, regNumber dst, regNumber src, bool canIgnoreSideEffects); +static bool IsJccInstruction(instruction ins); +static bool IsJmpInstruction(instruction ins); + bool AreUpper32BitsZero(regNumber reg); bool AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps treeOps); @@ -116,7 +127,8 @@ bool hasRexPrefix(code_t code) #define VEX_PREFIX_MASK_3BYTE 0xFF000000000000ULL #define VEX_PREFIX_CODE_3BYTE 0xC4000000000000ULL -bool TakesVexPrefix(instruction ins); +bool TakesVexPrefix(instruction ins) const; +static bool TakesRexWPrefix(instruction ins, emitAttr attr); // Returns true if the instruction encoding already contains VEX prefix bool hasVexPrefix(code_t code) @@ -142,7 +154,7 @@ code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr } bool useVEXEncodings; -bool UseVEXEncoding() +bool UseVEXEncoding() const { return useVEXEncodings; } diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 3b1b1739919..92a82c346a5 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1620,6 +1620,9 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) } } bNext->bbPreds = nullptr; + + // `block` can no longer be a loop pre-header (if it was before). + block->bbFlags &= ~BBF_LOOP_PREHEADER; } else { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 9422225c5d3..277cd53e1c1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -10294,7 +10294,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ { if (IS_CSE_INDEX(tree->gtCSEnum)) { - printf("CSE #%02d (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def")); + printf(FMT_CSE " (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def")); } else { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c12e46f02bd..4aeeeb9e486 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -494,6 +494,7 @@ enum GenTreeFlags : unsigned int GTF_INX_RNGCHK = 0x80000000, // GT_INDEX/GT_INDEX_ADDR -- the array reference should be range-checked. GTF_INX_STRING_LAYOUT = 0x40000000, // GT_INDEX -- this uses the special string array layout + GTF_INX_NOFAULT = 0x20000000, // GT_INDEX -- the INDEX does not throw an exception (morph to GTF_IND_NONFAULTING) GTF_IND_TGT_NOT_HEAP = 0x80000000, // GT_IND -- the target is not on the heap GTF_IND_VOLATILE = 0x40000000, // GT_IND -- the load or store must use volatile sematics (this is a nop on X86) diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 9ce10458fb0..752626f2018 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -24,11 +24,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ #ifdef DEBUG -/***************************************************************************** - * - * Returns the string representation of the given CPU instruction. - */ - +//----------------------------------------------------------------------------- +// genInsName: Returns the string representation of the given CPU instruction, as +// it exists in the instruction table. Note that some architectures don't encode the +// name completely in the table: xarch sometimes prepends a "v", and arm sometimes +// appends a "s". Use `genInsDisplayName()` to get a fully-formed name. +// const char* CodeGen::genInsName(instruction ins) { // clang-format off @@ -77,36 +78,36 @@ const char* CodeGen::genInsName(instruction ins) return insNames[ins]; } -void __cdecl CodeGen::instDisp(instruction ins, bool noNL, const char* fmt, ...) +//----------------------------------------------------------------------------- +// genInsDisplayName: Get a fully-formed instruction display name. This only handles +// the xarch case of prepending a "v", not the arm case of appending an "s". +// This can be called up to four times in a single 'printf' before the static buffers +// get reused. +// +// Returns: +// String with instruction name +// +const char* CodeGen::genInsDisplayName(emitter::instrDesc* id) { - if (compiler->opts.dspCode) + instruction ins = id->idIns(); + const char* insName = genInsName(ins); + +#ifdef TARGET_XARCH + const int TEMP_BUFFER_LEN = 40; + static unsigned curBuf = 0; + static char buf[4][TEMP_BUFFER_LEN]; + const char* retbuf; + + if (GetEmitter()->IsAVXInstruction(ins) && !GetEmitter()->IsBMIInstruction(ins)) { - /* Display the instruction offset within the emit block */ - - // printf("[%08X:%04X]", GetEmitter().emitCodeCurBlock(), GetEmitter().emitCodeOffsInBlock()); - - /* Display the FP stack depth (before the instruction is executed) */ - - // printf("[FP=%02u] ", genGetFPstkLevel()); - - /* Display the instruction mnemonic */ - printf(" "); - - printf(" %-8s", genInsName(ins)); - - if (fmt) - { - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - } - - if (!noNL) - { - printf("\n"); - } + sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "v%s", insName); + retbuf = buf[curBuf]; + curBuf = (curBuf + 1) % 4; + return retbuf; } +#endif // TARGET_XARCH + + return insName; } /*****************************************************************************/ diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 7254e205fbc..b14597a8702 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -68,6 +68,9 @@ CONFIG_INTEGER(JitAlignLoopAdaptive, W("JitAlignLoopAdaptive"), 1) // If set, perform adaptive loop alignment that limits number of padding based on loop size. +// Print the alignment boundaries in disassembly. +CONFIG_INTEGER(JitDasmWithAlignmentBoundaries, W("JitDasmWithAlignmentBoundaries"), 0) + CONFIG_INTEGER(JitDirectAlloc, W("JitDirectAlloc"), 0) CONFIG_INTEGER(JitDoubleAlign, W("JitDoubleAlign"), 1) CONFIG_INTEGER(JitDumpASCII, W("JitDumpASCII"), 1) // Uses only ASCII characters in tree dumps diff --git a/src/coreclr/jit/jitstd/algorithm.h b/src/coreclr/jit/jitstd/algorithm.h index 000639a5a1d..9fa6fbb94dd 100644 --- a/src/coreclr/jit/jitstd/algorithm.h +++ b/src/coreclr/jit/jitstd/algorithm.h @@ -102,9 +102,9 @@ void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Less less // // It's not possible for newFirst to go past the end of the sort range: // - If newFirst reaches the pivot before newLast then the pivot is - // swapped to the right and we'll stop again when we reach it. + // swapped to the right and we'll stop again when we reach it. // - If newLast reaches the pivot before newFirst then the pivot is - // swapped to the left and the value at newFirst will take its place + // swapped to the left and the value at newFirst will take its place // to the right so less(newFirst, pivot) will again be false when the // old pivot's position is reached. do diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 0fe22420f78..c3991e663e1 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -12,6 +12,40 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jitpch.h" +#ifdef DEBUG + +//-------------------------------------------------------------------------------------------------- +// ArrIndex::Print - debug print an ArrIndex struct in form: `V01[V02][V03]`. +// +// Arguments: +// dim (Optional) Print up to but not including this dimension. Default: print all dimensions. +// +void ArrIndex::Print(unsigned dim /* = -1 */) +{ + printf("V%02d", arrLcl); + for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) + { + printf("[V%02d]", indLcls.Get(i)); + } +} + +//-------------------------------------------------------------------------------------------------- +// ArrIndex::PrintBoundsCheckNodes - debug print an ArrIndex struct bounds check node tree ids in +// form: `[000125][000113]`. +// +// Arguments: +// dim (Optional) Print up to but not including this dimension. Default: print all dimensions. +// +void ArrIndex::PrintBoundsCheckNodes(unsigned dim /* = -1 */) +{ + for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) + { + Compiler::printTreeID(bndsChks.Get(i)); + } +} + +#endif // DEBUG + //-------------------------------------------------------------------------------------------------- // ToGenTree - Convert an arrLen operation into a gentree node. // @@ -39,11 +73,26 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb) { arr = comp->gtNewIndexRef(TYP_REF, arr, comp->gtNewLclvNode(arrIndex->indLcls[i], comp->lvaTable[arrIndex->indLcls[i]].lvType)); + + // Clear the range check flag and mark the index as non-faulting: we guarantee that all necessary range + // checking has already been done by the time this array index expression is invoked. + arr->gtFlags &= ~(GTF_INX_RNGCHK | GTF_EXCEPT); + arr->gtFlags |= GTF_INX_NOFAULT; } // If asked for arrlen invoke arr length operator. if (oper == ArrLen) { GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb); + + // We already guaranteed (by a sequence of preceding checks) that the array length operator will not + // throw an exception because we null checked the base array. + // So, we should be able to do the following: + // arrLen->gtFlags &= ~GTF_EXCEPT; + // arrLen->gtFlags |= GTF_IND_NONFAULTING; + // However, we then end up with a mix of non-faulting array length operators as well as normal faulting + // array length operators in the slow-path of the cloned loops. CSE doesn't keep these separate, so bails + // out on creating CSEs on this very useful type of CSE, leading to CQ losses in the cloned loop fast path. + // TODO-CQ: fix this. return arrLen; } else @@ -395,27 +444,31 @@ void LoopCloneContext::PrintBlockConditions(unsigned loopNum) { printf("Block conditions:\n"); - JitExpandArrayStack*>* levelCond = blockConditions[loopNum]; - if (levelCond == nullptr || levelCond->Size() == 0) + JitExpandArrayStack*>* blockConds = blockConditions[loopNum]; + if (blockConds == nullptr || blockConds->Size() == 0) { printf("No block conditions\n"); return; } - for (unsigned i = 0; i < levelCond->Size(); ++i) + for (unsigned i = 0; i < blockConds->Size(); ++i) { - printf("%d = {", i); - for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j) - { - if (j != 0) - { - printf(" & "); - } - (*((*levelCond)[i]))[j].Print(); - } - printf("}\n"); + PrintBlockLevelConditions(i, (*blockConds)[i]); } } +void LoopCloneContext::PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond) +{ + printf("%d = ", level); + for (unsigned j = 0; j < levelCond->Size(); ++j) + { + if (j != 0) + { + printf(" & "); + } + (*levelCond)[j].Print(); + } + printf("\n"); +} #endif //-------------------------------------------------------------------------------------------------- @@ -650,19 +703,19 @@ void LoopCloneContext::PrintConditions(unsigned loopNum) { if (conditions[loopNum] == nullptr) { - JITDUMP("NO conditions"); + printf("NO conditions"); return; } if (conditions[loopNum]->Size() == 0) { - JITDUMP("Conditions were optimized away! Will always take cloned path."); + printf("Conditions were optimized away! Will always take cloned path."); return; } for (unsigned i = 0; i < conditions[loopNum]->Size(); ++i) { if (i != 0) { - JITDUMP(" & "); + printf(" & "); } (*conditions[loopNum])[i].Print(); } @@ -711,6 +764,9 @@ void LoopCloneContext::CondToStmtInBlock(Compiler* comp comp->fgInsertStmtAtEnd(block, stmt); // Remorph. + JITDUMP("Loop cloning condition tree before morphing:\n"); + DBEXEC(comp->verbose, comp->gtDispTree(jmpTrueTree)); + JITDUMP("\n"); comp->fgMorphBlockStmt(block, stmt DEBUGARG("Loop cloning condition")); } @@ -965,16 +1021,15 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext for (unsigned i = 0; i < optInfos->Size(); ++i) { - LcOptInfo* optInfo = optInfos->GetRef(i); + LcOptInfo* optInfo = optInfos->Get(i); switch (optInfo->GetOptType()) { case LcOptInfo::LcJaggedArray: { // limit <= arrLen LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); - LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen); - LC_Ident arrLenIdent = LC_Ident(arrLen); - + LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen); + LC_Ident arrLenIdent = LC_Ident(arrLen); LC_Condition cond(GT_LE, LC_Expr(ident), LC_Expr(arrLenIdent)); context->EnsureConditions(loopNum)->Push(cond); @@ -1000,9 +1055,9 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext return false; } } - JITDUMP("Conditions: ("); + JITDUMP("Conditions: "); DBEXEC(verbose, context->PrintConditions(loopNum)); - JITDUMP(")\n"); + JITDUMP("\n"); return true; } return false; @@ -1164,10 +1219,6 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con printf("Deref condition tree:\n"); for (unsigned i = 0; i < nodes.Size(); ++i) { - if (i != 0) - { - printf(","); - } nodes[i]->Print(); printf("\n"); } @@ -1176,6 +1227,7 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con if (maxRank == -1) { + JITDUMP("> maxRank undefined\n"); return false; } @@ -1184,12 +1236,13 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con // So add 1 after rank * 2. unsigned condBlocks = (unsigned)maxRank * 2 + 1; - // Heuristic to not create too many blocks. - // REVIEW: due to the definition of `condBlocks`, above, the effective max is 3 blocks, meaning - // `maxRank` of 1. Question: should the heuristic allow more blocks to be created in some situations? - // REVIEW: make this based on a COMPlus configuration? - if (condBlocks > 4) + // Heuristic to not create too many blocks. Defining as 3 allows, effectively, loop cloning on + // doubly-nested loops. + // REVIEW: make this based on a COMPlus configuration, at least for debug? + const unsigned maxAllowedCondBlocks = 3; + if (condBlocks > maxAllowedCondBlocks) { + JITDUMP("> Too many condition blocks (%u > %u)\n", condBlocks, maxAllowedCondBlocks); return false; } @@ -1254,14 +1307,60 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum); for (unsigned i = 0; i < optInfos->Size(); ++i) { - LcOptInfo* optInfo = optInfos->GetRef(i); + LcOptInfo* optInfo = optInfos->Get(i); switch (optInfo->GetOptType()) { case LcOptInfo::LcJaggedArray: { LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); compCurBB = arrIndexInfo->arrIndex.useBlock; - optRemoveCommaBasedRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt); + + // Remove all bounds checks for this array up to (and including) `arrIndexInfo->dim`. So, if that is 1, + // Remove rank 0 and 1 bounds checks. + + for (unsigned dim = 0; dim <= arrIndexInfo->dim; dim++) + { + GenTree* bndsChkNode = arrIndexInfo->arrIndex.bndsChks[dim]; + +#ifdef DEBUG + if (verbose) + { + printf("Remove bounds check "); + printTreeID(bndsChkNode->gtGetOp1()); + printf(" for " FMT_STMT ", dim% d, ", arrIndexInfo->stmt->GetID(), dim); + arrIndexInfo->arrIndex.Print(); + printf(", bounds check nodes: "); + arrIndexInfo->arrIndex.PrintBoundsCheckNodes(); + printf("\n"); + } +#endif // DEBUG + + if (bndsChkNode->gtGetOp1()->OperIsBoundsCheck()) + { + // This COMMA node will only represent a bounds check if we've haven't already removed this + // bounds check in some other nesting cloned loop. For example, consider: + // for (i = 0; i < x; i++) + // for (j = 0; j < y; j++) + // a[i][j] = i + j; + // If the outer loop is cloned first, it will remove the a[i] bounds check from the optimized + // path. Later, when the inner loop is cloned, we want to remove the a[i][j] bounds check. If + // we clone the inner loop, we know that the a[i] bounds check isn't required because we'll add + // it to the loop cloning conditions. On the other hand, we can clone a loop where we get rid of + // the nested bounds check but nobody has gotten rid of the outer bounds check. As before, we + // know the outer bounds check is not needed because it's been added to the cloning conditions, + // so we can get rid of the bounds check here. + // + optRemoveCommaBasedRangeCheck(bndsChkNode, arrIndexInfo->stmt); + } + else + { + JITDUMP(" Bounds check already removed\n"); + + // If the bounds check node isn't there, it better have been converted to a GT_NOP. + assert(bndsChkNode->gtGetOp1()->OperIs(GT_NOP)); + } + } + DBEXEC(dynamicPath, optDebugLogLoopCloning(arrIndexInfo->arrIndex.useBlock, arrIndexInfo->stmt)); } break; @@ -1474,8 +1573,9 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, BasicBlock* head, BasicBlock* slowHead) { - JITDUMP("Inserting loop cloning conditions\n"); + JITDUMP("Inserting loop " FMT_LP " loop choice conditions\n", loopNum); assert(context->HasBlockConditions(loopNum)); + assert(head->bbJumpKind == BBJ_COND); BasicBlock* curCond = head; JitExpandArrayStack*>* levelCond = context->GetBlockConditions(loopNum); @@ -1484,10 +1584,15 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, bool isHeaderBlock = (curCond == head); // Flip the condition if header block. + JITDUMP("Adding loop " FMT_LP " level %u block conditions to " FMT_BB "\n ", loopNum, i, curCond->bbNum); + DBEXEC(verbose, context->PrintBlockLevelConditions(i, (*levelCond)[i])); context->CondToStmtInBlock(this, *((*levelCond)[i]), curCond, /*reverse*/ isHeaderBlock); // Create each condition block ensuring wiring between them. - BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true); + BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true); + tmp->inheritWeight(head); + tmp->bbNatLoopNum = head->bbNatLoopNum; + curCond->bbJumpDest = isHeaderBlock ? tmp : slowHead; JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", curCond->bbNum, curCond->bbJumpDest->bbNum); @@ -1500,13 +1605,13 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, } curCond = tmp; - - curCond->inheritWeight(head); - curCond->bbNatLoopNum = head->bbNatLoopNum; - JITDUMP("Created new " FMT_BB " for new level %u\n", curCond->bbNum, i); } // Finally insert cloning conditions after all deref conditions have been inserted. + JITDUMP("Adding loop " FMT_LP " cloning conditions to " FMT_BB "\n", loopNum, curCond->bbNum); + JITDUMP(" "); + DBEXEC(verbose, context->PrintConditions(loopNum)); + JITDUMP("\n"); context->CondToStmtInBlock(this, *(context->GetConditions(loopNum)), curCond, /*reverse*/ false); return curCond; } @@ -2222,10 +2327,12 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop #ifdef DEBUG if (verbose) { - printf("Found ArrIndex at tree "); + printf("Found ArrIndex at " FMT_BB " " FMT_STMT " tree ", arrIndex.useBlock->bbNum, info->stmt->GetID()); printTreeID(tree); printf(" which is equivalent to: "); arrIndex.Print(); + printf(", bounds check nodes: "); + arrIndex.PrintBoundsCheckNodes(); printf("\n"); } #endif @@ -2233,6 +2340,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop // Check that the array object local variable is invariant within the loop body. if (!optIsStackLocalInvariant(info->loopNum, arrIndex.arrLcl)) { + JITDUMP("V%02d is not loop invariant\n", arrIndex.arrLcl); return WALK_SKIP_SUBTREES; } diff --git a/src/coreclr/jit/loopcloning.h b/src/coreclr/jit/loopcloning.h index 6cc921c520d..c5ed53c31ba 100644 --- a/src/coreclr/jit/loopcloning.h +++ b/src/coreclr/jit/loopcloning.h @@ -220,14 +220,8 @@ struct ArrIndex } #ifdef DEBUG - void Print(unsigned dim = -1) - { - printf("V%02d", arrLcl); - for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i) - { - printf("[V%02d]", indLcls.GetRef(i)); - } - } + void Print(unsigned dim = -1); + void PrintBoundsCheckNodes(unsigned dim = -1); #endif }; @@ -260,9 +254,8 @@ struct LcOptInfo #include "loopcloningopts.h" }; - void* optInfo; OptType optType; - LcOptInfo(void* optInfo, OptType optType) : optInfo(optInfo), optType(optType) + LcOptInfo(OptType optType) : optType(optType) { } @@ -270,6 +263,7 @@ struct LcOptInfo { return optType; } + #define LC_OPT(en) \ en##OptInfo* As##en##OptInfo() \ { \ @@ -292,7 +286,7 @@ struct LcMdArrayOptInfo : public LcOptInfo ArrIndex* index; // "index" cached computation in the form of an ArrIndex representation. LcMdArrayOptInfo(GenTreeArrElem* arrElem, unsigned dim) - : LcOptInfo(this, LcMdArray), arrElem(arrElem), dim(dim), index(nullptr) + : LcOptInfo(LcMdArray), arrElem(arrElem), dim(dim), index(nullptr) { } @@ -325,7 +319,7 @@ struct LcJaggedArrayOptInfo : public LcOptInfo Statement* stmt; // "stmt" where the optimization opportunity occurs. LcJaggedArrayOptInfo(ArrIndex& arrIndex, unsigned dim, Statement* stmt) - : LcOptInfo(this, LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt) + : LcOptInfo(LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt) { } }; @@ -678,30 +672,24 @@ struct LoopCloneContext CompAllocator alloc; // The allocator // The array of optimization opportunities found in each loop. (loop x optimization-opportunities) - JitExpandArrayStack** optInfo; + jitstd::vector*> optInfo; // The array of conditions that influence which path to take for each loop. (loop x cloning-conditions) - JitExpandArrayStack** conditions; + jitstd::vector*> conditions; // The array of dereference conditions found in each loop. (loop x deref-conditions) - JitExpandArrayStack** derefs; + jitstd::vector*> derefs; // The array of block levels of conditions for each loop. (loop x level x conditions) - JitExpandArrayStack*>** blockConditions; + jitstd::vector*>*> blockConditions; - LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc) + LoopCloneContext(unsigned loopCount, CompAllocator alloc) + : alloc(alloc), optInfo(alloc), conditions(alloc), derefs(alloc), blockConditions(alloc) { - optInfo = new (alloc) JitExpandArrayStack*[loopCount]; - conditions = new (alloc) JitExpandArrayStack*[loopCount]; - derefs = new (alloc) JitExpandArrayStack*[loopCount]; - blockConditions = new (alloc) JitExpandArrayStack*>*[loopCount]; - for (unsigned i = 0; i < loopCount; ++i) - { - optInfo[i] = nullptr; - conditions[i] = nullptr; - derefs[i] = nullptr; - blockConditions[i] = nullptr; - } + optInfo.resize(loopCount, nullptr); + conditions.resize(loopCount, nullptr); + derefs.resize(loopCount, nullptr); + blockConditions.resize(loopCount, nullptr); } // Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true. @@ -739,6 +727,7 @@ struct LoopCloneContext #ifdef DEBUG // Print the block conditions for the loop. void PrintBlockConditions(unsigned loopNum); + void PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond); #endif // Does the loop have block conditions? diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 526a996e1b4..68f31af7636 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5581,8 +5581,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) GenTree* arrRef = asIndex->Arr(); GenTree* index = asIndex->Index(); - bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled - bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0); + bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled + bool indexNonFaulting = ((tree->gtFlags & GTF_INX_NOFAULT) != 0); // if true, mark GTF_IND_NONFAULTING + bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0); GenTree* arrRefDefn = nullptr; // non-NULL if we need to allocate a temp for the arrRef expression GenTree* indexDefn = nullptr; // non-NULL if we need to allocate a temp for the index expression @@ -5742,9 +5743,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) this->compFloatingPointUsed = true; } - // We've now consumed the GTF_INX_RNGCHK, and the node + // We've now consumed the GTF_INX_RNGCHK and GTF_INX_NOFAULT, and the node // is no longer a GT_INDEX node. - tree->gtFlags &= ~GTF_INX_RNGCHK; + tree->gtFlags &= ~(GTF_INX_RNGCHK | GTF_INX_NOFAULT); tree->AsOp()->gtOp1 = addr; @@ -5752,7 +5753,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) tree->gtFlags |= GTF_IND_ARR_INDEX; // If there's a bounds check, the indir won't fault. - if (bndsChk) + if (bndsChk || indexNonFaulting) { tree->gtFlags |= GTF_IND_NONFAULTING; } diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index b4137ec52eb..587349e489b 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -166,7 +166,8 @@ Compiler::fgWalkResult Compiler::optCSE_MaskHelper(GenTree** pTree, fgWalkData* if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned cseIndex = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseBit = genCSEnum2bit(cseIndex); + // Note that we DO NOT use getCSEAvailBit() here, for the CSE_defMask/CSE_useMask + unsigned cseBit = genCSEnum2bit(cseIndex); if (IS_CSE_DEF(tree->gtCSEnum)) { BitVecOps::AddElemD(comp->cseMaskTraits, pUserData->CSE_defMask, cseBit); @@ -424,16 +425,16 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) ValueNum vnLibNorm = vnStore->VNNormalValue(vnLib); // We use the normal value number because we want the CSE candidate to - // represent all expressions that produce the same normal value number + // represent all expressions that produce the same normal value number. // We will handle the case where we have different exception sets when // promoting the candidates. // // We do this because a GT_IND will usually have a NullPtrExc entry in its // exc set, but we may have cleared the GTF_EXCEPT flag and if so, it won't - // have an NullPtrExc, or we may have assigned the value of an GT_IND + // have an NullPtrExc, or we may have assigned the value of an GT_IND // into a LCL_VAR and then read it back later. // - // When we are promoting the CSE candidates we insure that any CSE + // When we are promoting the CSE candidates we ensure that any CSE // uses that we promote have an exc set that is the same as the CSE defs // or have an empty set. And that all of the CSE defs produced the required // set of exceptions for the CSE uses. @@ -502,7 +503,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) key = vnLibNorm; } - // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst + // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst. // Note that when isSharedConst is true then we require that the TARGET_SIGN_BIT is set in the key // and otherwise we require that we never create a ValueNumber with the TARGET_SIGN_BIT set. // @@ -709,7 +710,6 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) C_ASSERT((signed char)MAX_CSE_CNT == MAX_CSE_CNT); unsigned CSEindex = ++optCSECandidateCount; - // EXPSET_TP CSEmask = genCSEnum2bit(CSEindex); /* Record the new CSE index in the hashDsc */ hashDsc->csdIndex = CSEindex; @@ -746,16 +746,14 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) } } -/***************************************************************************** - * - * Locate CSE candidates and assign indices to them - * return 0 if no CSE candidates were found - */ - -unsigned Compiler::optValnumCSE_Locate() +//------------------------------------------------------------------------ +// optValnumCSE_Locate: Locate CSE candidates and assign them indices. +// +// Returns: +// true if there are any CSE candidates, false otherwise +// +bool Compiler::optValnumCSE_Locate() { - // Locate CSE candidates and assign them indices - bool enableConstCSE = true; int configValue = JitConfig.JitConstCSE(); @@ -871,14 +869,14 @@ unsigned Compiler::optValnumCSE_Locate() if (!optDoCSE) { - return 0; + return false; } /* We're finished building the expression lookup table */ optCSEstop(); - return 1; + return true; } //------------------------------------------------------------------------ @@ -890,7 +888,7 @@ unsigned Compiler::optValnumCSE_Locate() // // Arguments: // compare - The compare node to check - +// void Compiler::optCseUpdateCheckedBoundMap(GenTree* compare) { assert(compare->OperIsCompare()); @@ -999,9 +997,9 @@ void Compiler::optValnumCSE_InitDataFlow() // Init traits and cseCallKillsMask bitvectors. cseLivenessTraits = new (getAllocator(CMK_CSE)) BitVecTraits(bitCount, this); cseCallKillsMask = BitVecOps::MakeEmpty(cseLivenessTraits); - for (unsigned inx = 0; inx < optCSECandidateCount; inx++) + for (unsigned inx = 1; inx <= optCSECandidateCount; inx++) { - unsigned cseAvailBit = inx * 2; + unsigned cseAvailBit = getCSEAvailBit(inx); // a one preserves availability and a zero kills the availability // we generate this kind of bit pattern: 101010101010 @@ -1047,7 +1045,7 @@ void Compiler::optValnumCSE_InitDataFlow() block->bbCseGen = BitVecOps::MakeEmpty(cseLivenessTraits); } - // We walk the set of CSE candidates and set the bit corresponsing to the CSEindex + // We walk the set of CSE candidates and set the bit corresponding to the CSEindex // in the block's bbCseGen bitset // for (unsigned inx = 0; inx < optCSECandidateCount; inx++) @@ -1060,16 +1058,16 @@ void Compiler::optValnumCSE_InitDataFlow() while (lst != nullptr) { BasicBlock* block = lst->tslBlock; - unsigned CseAvailBit = genCSEnum2bit(CSEindex) * 2; - unsigned cseAvailCrossCallBit = CseAvailBit + 1; + unsigned cseAvailBit = getCSEAvailBit(CSEindex); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEindex); - // This CSE is generated in 'block', we always set the CseAvailBit + // This CSE is generated in 'block', we always set the cseAvailBit // If this block does not contain a call, we also set cseAvailCrossCallBit // // If we have a call in this block then in the loop below we walk the trees // backwards to find any CSEs that are generated after the last call in the block. // - BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, CseAvailBit); + BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailBit); if ((block->bbFlags & BBF_HAS_CALL) == 0) { BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit); @@ -1113,7 +1111,7 @@ void Compiler::optValnumCSE_InitDataFlow() if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1; + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit); } if (tree->OperGet() == GT_CALL) @@ -1142,15 +1140,16 @@ void Compiler::optValnumCSE_InitDataFlow() bool headerPrinted = false; for (BasicBlock* const block : Blocks()) { - if (block->bbCseGen != nullptr) + if (!BitVecOps::IsEmpty(cseLivenessTraits, block->bbCseGen)) { if (!headerPrinted) { printf("\nBlocks that generate CSE def/uses\n"); headerPrinted = true; } - printf(FMT_BB, block->bbNum); - printf(" cseGen = %s\n", genES2str(cseLivenessTraits, block->bbCseGen)); + printf(FMT_BB " cseGen = ", block->bbNum); + optPrintCSEDataFlowSet(block->bbCseGen); + printf("\n"); } } } @@ -1184,6 +1183,7 @@ public: // BitVecOps::Assign(m_comp->cseLivenessTraits, m_preMergeOut, block->bbCseOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1191,11 +1191,13 @@ public: printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } #endif // DEBUG +#endif // 0 } // Merge: perform the merging of each of the predecessor's liveness values (since this is a forward analysis) void Merge(BasicBlock* block, BasicBlock* predBlock, unsigned dupCount) { +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1204,15 +1206,18 @@ public: printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } #endif // DEBUG +#endif // 0 BitVecOps::IntersectionD(m_comp->cseLivenessTraits, block->bbCseIn, predBlock->bbCseOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { printf(" => cseIn = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseIn)); } #endif // DEBUG +#endif // 0 } //------------------------------------------------------------------------ @@ -1272,6 +1277,7 @@ public: // bool notDone = !BitVecOps::Equal(m_comp->cseLivenessTraits, block->bbCseOut, m_preMergeOut); +#if 0 #ifdef DEBUG if (m_comp->verbose) { @@ -1288,6 +1294,7 @@ public: notDone ? "true" : "false"); } #endif // DEBUG +#endif // 0 return notDone; } @@ -1330,10 +1337,12 @@ void Compiler::optValnumCSE_DataFlow() for (BasicBlock* const block : Blocks()) { - printf(FMT_BB, block->bbNum); - printf(" cseIn = %s,", genES2str(cseLivenessTraits, block->bbCseIn)); - printf(" cseGen = %s,", genES2str(cseLivenessTraits, block->bbCseGen)); - printf(" cseOut = %s", genES2str(cseLivenessTraits, block->bbCseOut)); + printf(FMT_BB " in gen out\n", block->bbNum); + optPrintCSEDataFlowSet(block->bbCseIn); + printf("\n"); + optPrintCSEDataFlowSet(block->bbCseGen); + printf("\n"); + optPrintCSEDataFlowSet(block->bbCseOut); printf("\n"); } @@ -1347,17 +1356,17 @@ void Compiler::optValnumCSE_DataFlow() // // Using the information computed by CSE_DataFlow determine for each // CSE whether the CSE is a definition (if the CSE was not available) -// or if the CSE is a use (if the CSE was previously made available) -// The implementation iterates of all blocks setting 'available_cses' +// or if the CSE is a use (if the CSE was previously made available). +// The implementation iterates over all blocks setting 'available_cses' // to the CSEs that are available at input to the block. // When a CSE expression is encountered it is classified as either // as a definition (if the CSE is not in the 'available_cses' set) or -// as a use (if the CSE is in the 'available_cses' set). If the CSE +// as a use (if the CSE is in the 'available_cses' set). If the CSE // is a definition then it is added to the 'available_cses' set. // // This algorithm uncovers the defs and uses gradually and as it does // so it also builds the exception set that all defs make: 'defExcSetCurrent' -// and the exception set that the uses we have seen depend upon: 'defExcSetPromise' +// and the exception set that the uses we have seen depend upon: 'defExcSetPromise'. // // Typically expressions with the same normal ValueNum generate exactly the // same exception sets. There are two way that we can get different exception @@ -1371,12 +1380,11 @@ void Compiler::optValnumCSE_DataFlow() // 2. We stored an expression into a LclVar or into Memory and read it later // e.g. t = p.a; // e1 = (t + q.b) :: e1 has one NullPtrExc and e2 has two. -// e2 = (p.a + q.b) but both compute the same normal value// +// e2 = (p.a + q.b) but both compute the same normal value // e.g. m.a = p.a; // e1 = (m.a + q.b) :: e1 and e2 have different exception sets. // e2 = (p.a + q.b) but both compute the same normal value // -// void Compiler::optValnumCSE_Availablity() { #ifdef DEBUG @@ -1411,12 +1419,12 @@ void Compiler::optValnumCSE_Availablity() if (IS_CSE_INDEX(tree->gtCSEnum)) { unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned CseAvailBit = genCSEnum2bit(CSEnum) * 2; - unsigned cseAvailCrossCallBit = CseAvailBit + 1; + unsigned cseAvailBit = getCSEAvailBit(CSEnum); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); CSEdsc* desc = optCSEfindDsc(CSEnum); BasicBlock::weight_t stmw = block->getBBWeight(this); - isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, CseAvailBit); + isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, cseAvailBit); isDef = !isUse; // If is isn't a CSE use, it is a CSE def // Is this a "use", that we haven't yet marked as live across a call @@ -1446,7 +1454,7 @@ void Compiler::optValnumCSE_Availablity() printf(FMT_BB " ", block->bbNum); printTreeID(tree); - printf(" %s of CSE #%02u [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw), + printf(" %s of " FMT_CSE " [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw), madeLiveAcrossCall ? " *** Now Live Across Call ***" : ""); } #endif // DEBUG @@ -1477,7 +1485,7 @@ void Compiler::optValnumCSE_Availablity() // Is defExcSetCurrent still set to the uninit marker value of VNForNull() ? if (desc->defExcSetCurrent == vnStore->VNForNull()) { - // This is the first time visited, so record this defs exeception set + // This is the first time visited, so record this defs exception set desc->defExcSetCurrent = theLiberalExcSet; } @@ -1589,7 +1597,7 @@ void Compiler::optValnumCSE_Availablity() tree->gtCSEnum = TO_CSE_DEF(tree->gtCSEnum); // This CSE becomes available after this def - BitVecOps::AddElemD(cseLivenessTraits, available_cses, CseAvailBit); + BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailBit); BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit); } else // We are visiting a CSE use @@ -1636,7 +1644,7 @@ void Compiler::optValnumCSE_Availablity() if (!vnStore->VNExcIsSubset(desc->defExcSetPromise, theLiberalExcSet)) { // We can't safely make this into a CSE use, because this - // CSE use has an exeception set item that is not promised + // CSE use has an exception set item that is not promised // by all of our CSE defs. // // We will omit this CSE use from the graph and proceed, @@ -1660,7 +1668,7 @@ void Compiler::optValnumCSE_Availablity() // In order to determine if a CSE is live across a call, we model availablity using two bits and // kill all of the cseAvailCrossCallBit for each CSE whenever we see a GT_CALL (unless the call - // generates A cse) + // generates a CSE). // if (tree->OperGet() == GT_CALL) { @@ -1690,7 +1698,7 @@ void Compiler::optValnumCSE_Availablity() // available_cses // unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum); - unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1; + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum); BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit); } @@ -2027,14 +2035,14 @@ public: if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey)) { - printf("CSE #%02u, {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", + printf(FMT_CSE ", {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, dsc->csdHashKey, dsc->defExcSetPromise, dsc->csdUseCount, def, use, cost, dsc->csdLiveAcrossCall ? ", call" : " "); } else { size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey); - printf("CSE #%02u, {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, + printf(FMT_CSE ", {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex, dspPtr(kVal), dsc->csdUseCount, def, use, cost, dsc->csdLiveAcrossCall ? ", call" : " "); } @@ -2814,7 +2822,7 @@ public: if (dsc->csdDefCount == 1) { - JITDUMP("CSE #%02u is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex, + JITDUMP(FMT_CSE " is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex, cseLclVarNum); m_pCompiler->lvaTable[cseLclVarNum].lvInSsa = true; @@ -2931,7 +2939,7 @@ public: if (IS_CSE_INDEX(lst->tslTree->gtCSEnum)) { ValueNum currVN = m_pCompiler->vnStore->VNLiberalNormalValue(lst->tslTree->gtVNPair); - printf("0x%x(%s " FMT_VN ") ", lst->tslTree, + printf("[%06d](%s " FMT_VN ") ", m_pCompiler->dspTreeID(lst->tslTree), IS_CSE_USE(lst->tslTree->gtCSEnum) ? "use" : "def", currVN); } lst = lst->tslNext; @@ -2996,7 +3004,7 @@ public: #ifdef DEBUG if (m_pCompiler->verbose) { - printf("\nWorking on the replacement of the CSE #%02u use at ", exp->gtCSEnum); + printf("\nWorking on the replacement of the " FMT_CSE " use at ", exp->gtCSEnum); Compiler::printTreeID(exp); printf(" in " FMT_BB "\n", blk->bbNum); } @@ -3164,7 +3172,7 @@ public: #ifdef DEBUG if (m_pCompiler->verbose) { - printf("\nCSE #%02u def at ", GET_CSE_INDEX(exp->gtCSEnum)); + printf("\n" FMT_CSE " def at ", GET_CSE_INDEX(exp->gtCSEnum)); Compiler::printTreeID(exp); printf(" replaced in " FMT_BB " with def of V%02u\n", blk->bbNum, cseLclVarNum); } @@ -3314,13 +3322,13 @@ public: if (dsc->defExcSetPromise == ValueNumStore::NoVN) { - JITDUMP("Abandoned CSE #%02u because we had defs with different Exc sets\n", candidate.CseIndex()); + JITDUMP("Abandoned " FMT_CSE " because we had defs with different Exc sets\n", candidate.CseIndex()); continue; } if (dsc->csdStructHndMismatch) { - JITDUMP("Abandoned CSE #%02u because we had mismatching struct handles\n", candidate.CseIndex()); + JITDUMP("Abandoned " FMT_CSE " because we had mismatching struct handles\n", candidate.CseIndex()); continue; } @@ -3328,7 +3336,7 @@ public: if (candidate.UseCount() == 0) { - JITDUMP("Skipped CSE #%02u because use count is 0\n", candidate.CseIndex()); + JITDUMP("Skipped " FMT_CSE " because use count is 0\n", candidate.CseIndex()); continue; } @@ -3337,14 +3345,14 @@ public: { if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey)) { - printf("\nConsidering CSE #%02u {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n", + printf("\nConsidering " FMT_CSE " {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), dsc->csdHashKey, dsc->defExcSetPromise, candidate.DefCount(), candidate.UseCount(), candidate.Cost(), dsc->csdLiveAcrossCall ? ", call" : " "); } else { size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey); - printf("\nConsidering CSE #%02u {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), + printf("\nConsidering " FMT_CSE " {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(), dspPtr(kVal), candidate.DefCount(), candidate.UseCount(), candidate.Cost(), dsc->csdLiveAcrossCall ? ", call" : " "); } @@ -3428,11 +3436,6 @@ void Compiler::optValnumCSE_Heuristic() void Compiler::optOptimizeValnumCSEs() { #ifdef DEBUG - if (verbose) - { - printf("\n*************** In optOptimizeValnumCSEs()\n"); - } - if (optConfigDisableCSE()) { return; // Disabled by JitNoCSE @@ -3441,22 +3444,13 @@ void Compiler::optOptimizeValnumCSEs() optValnumCSE_phase = true; - /* Initialize the expression tracking logic */ - optValnumCSE_Init(); - /* Locate interesting expressions and assign indices to them */ - - if (optValnumCSE_Locate() > 0) + if (optValnumCSE_Locate()) { - optCSECandidateTotal += optCSECandidateCount; - optValnumCSE_InitDataFlow(); - optValnumCSE_DataFlow(); - optValnumCSE_Availablity(); - optValnumCSE_Heuristic(); } @@ -3807,21 +3801,11 @@ bool Compiler::optConfigDisableCSE2() void Compiler::optOptimizeCSEs() { -#ifdef DEBUG - if (verbose) - { - printf("\n*************** In optOptimizeCSEs()\n"); - printf("Blocks/Trees at start of optOptimizeCSE phase\n"); - fgDispBasicBlocks(true); - } -#endif // DEBUG - optCSECandidateCount = 0; optCSEstart = lvaCount; INDEBUG(optEnsureClearCSEInfo()); optOptimizeValnumCSEs(); - EndPhase(PHASE_OPTIMIZE_VALNUM_CSES); } /***************************************************************************** @@ -3873,4 +3857,38 @@ void Compiler::optEnsureClearCSEInfo() } } +//------------------------------------------------------------------------ +// optPrintCSEDataFlowSet: Print out one of the CSE dataflow sets bbCseGen, bbCseIn, bbCseOut, +// interpreting the bits in a more useful way for the dump. +// +// Arguments: +// cseDataFlowSet - One of the dataflow sets to display +// includeBits - Display the actual bits of the set as well +// +void Compiler::optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits /* = true */) +{ + if (includeBits) + { + printf("%s ", genES2str(cseLivenessTraits, cseDataFlowSet)); + } + + bool first = true; + for (unsigned cseIndex = 1; cseIndex <= optCSECandidateCount; cseIndex++) + { + unsigned cseAvailBit = getCSEAvailBit(cseIndex); + unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(cseIndex); + + if (BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailBit)) + { + if (!first) + { + printf(", "); + } + const bool isAvailCrossCall = BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailCrossCallBit); + printf(FMT_CSE "%s", cseIndex, isAvailCrossCall ? ".c" : ""); + first = false; + } + } +} + #endif // DEBUG diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index a7df5b95905..748d5feabb5 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -38,7 +38,6 @@ void Compiler::optInit() optNativeCallCount = 0; optAssertionCount = 0; optAssertionDep = nullptr; - optCSECandidateTotal = 0; optCSEstart = UINT_MAX; optCSEcount = 0; } @@ -5627,8 +5626,8 @@ void Compiler::optHoistLoopCode() #endif #if 0 - // The code in this #if has been useful in debugging loop cloning issues, by - // enabling selective enablement of the loop cloning optimization according to + // The code in this #if has been useful in debugging loop hoisting issues, by + // enabling selective enablement of the loop hoisting optimization according to // method hash. #ifdef DEBUG unsigned methHash = info.compMethodHash(); @@ -5650,7 +5649,7 @@ void Compiler::optHoistLoopCode() return; printf("Doing loop hoisting in %s (0x%x).\n", info.compFullName, methHash); #endif // DEBUG -#endif // 0 -- debugging loop cloning issues +#endif // 0 -- debugging loop hoisting issues #ifdef DEBUG if (verbose) @@ -5899,6 +5898,8 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt) printf("\n LOOPV-FP(%d)=", pLoopDsc->lpLoopVarFPCount); lvaDispVarSet(loopFPVars); + + printf("\n"); } #endif } diff --git a/src/coreclr/jit/phase.cpp b/src/coreclr/jit/phase.cpp index 530f8e74cbb..f08134bf115 100644 --- a/src/coreclr/jit/phase.cpp +++ b/src/coreclr/jit/phase.cpp @@ -84,8 +84,9 @@ void Phase::PrePhase() // // Currently the list is just the set of phases that have custom // derivations from the Phase class. - static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_RATIONALIZE, PHASE_LOWERING, PHASE_STACK_LEVEL_SETTER}; - bool doPrePhase = false; + static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_OPTIMIZE_VALNUM_CSES, PHASE_RATIONALIZE, PHASE_LOWERING, + PHASE_STACK_LEVEL_SETTER}; + bool doPrePhase = false; for (size_t i = 0; i < sizeof(s_allowlist) / sizeof(Phases); i++) { From fc5df59819dd58303241ea0f1d602f43154e13ee Mon Sep 17 00:00:00 2001 From: Jeff Schwartz Date: Sat, 10 Jul 2021 10:29:01 -0700 Subject: [PATCH 415/926] add newly added label (#55462) --- docs/area-owners.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/area-owners.md b/docs/area-owners.md index 4188ea7d982..740404f771a 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -79,6 +79,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-System.Diagnostics-mono | @lewing | @thaystg @radical | | | area-System.Diagnostics.Activity | @tommcdon | @tarekgh | | | area-System.Diagnostics.EventLog | @ericstj | @Anipik @ViktorHofer | | +| area-System.Diagnostics.Metric | @tommcdon | @noahfalk | | | area-System.Diagnostics.PerformanceCounter | @ericstj | @Anipik @ViktorHofer | | | area-System.Diagnostics.Process | @jeffhandley | @adamsitnik @carlossanlop @jozkee | | | area-System.Diagnostics.Tracing | @tommcdon | @noahfalk @tommcdon @Anipik @ViktorHofer @tarekgh | Included:
  • System.Diagnostics.DiagnosticSource
  • System.Diagnostics.TraceSource
| From 004feb873271a7e5d9862b4b0dd1edb8e1db9543 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 10 Jul 2021 13:49:58 -0400 Subject: [PATCH 416/926] Fix erroneous "globalizationMode" local naming in PlatformDetection (#55457) --- .../tests/TestUtilities/System/PlatformDetection.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 5038c4f0197..0bd29858a16 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -242,14 +242,9 @@ namespace System private static bool GetStaticNonPublicBooleanPropertyValue(string typeName, string propertyName) { - Type globalizationMode = Type.GetType(typeName); - if (globalizationMode != null) + if (Type.GetType(typeName)?.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod is MethodInfo mi) { - MethodInfo methodInfo = globalizationMode.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; - if (methodInfo != null) - { - return (bool)methodInfo.Invoke(null, null); - } + return (bool)mi.Invoke(null, null); } return false; From 2eb0071689ebe4157269d78cd1b7401b5f81f45b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 10 Jul 2021 20:38:44 +0200 Subject: [PATCH 417/926] use O_TRUNC when file locking is disabled, avoid ftruncate syscall (#55456) * add test project with file locking disabled via config file * use O_TRUNC when file locking is disabled, avoid ftruncate syscall * Apply suggestions from code review Co-authored-by: Stephen Toub --- .../TestUtilities/System/PlatformDetection.cs | 8 +++-- .../System.IO.FileSystem.sln | 7 ++++ .../DisabledFileLockingSwitchTests.cs | 16 ++++++++++ ...ileSystem.DisabledFileLocking.Tests.csproj | 32 +++++++++++++++++++ .../runtimeconfig.template.json | 5 +++ .../System.IO.FileSystem/tests/File/Copy.cs | 2 +- .../System.IO.FileSystem/tests/File/Create.cs | 3 +- .../tests/File/ReadWriteAllBytes.cs | 3 +- .../tests/File/ReadWriteAllBytesAsync.cs | 3 +- .../tests/File/ReadWriteAllLines.cs | 6 ++-- .../tests/File/ReadWriteAllLinesAsync.cs | 3 +- .../tests/File/ReadWriteAllText.cs | 3 +- .../tests/File/ReadWriteAllTextAsync.cs | 3 +- .../tests/FileStream/ctor_str_fm_fa_fs.cs | 3 +- .../FileStream/ctor_str_fm_fa_fs.write.cs | 3 +- ...stem.IO.FileSystem.Net5Compat.Tests.csproj | 1 + .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 20 ++++++++++-- 17 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj create mode 100644 src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 0bd29858a16..e11da7e8d4d 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -70,8 +70,8 @@ namespace System public static bool IsLinqExpressionsBuiltWithIsInterpretingOnly => s_LinqExpressionsBuiltWithIsInterpretingOnly.Value; public static bool IsNotLinqExpressionsBuiltWithIsInterpretingOnly => !IsLinqExpressionsBuiltWithIsInterpretingOnly; private static readonly Lazy s_LinqExpressionsBuiltWithIsInterpretingOnly = new Lazy(GetLinqExpressionsBuiltWithIsInterpretingOnly); - private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() - { + private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() + { Type type = typeof(LambdaExpression); if (type != null) { @@ -285,6 +285,10 @@ namespace System public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value; + private static readonly Lazy s_fileLockingDisabled = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("Microsoft.Win32.SafeHandles.SafeFileHandle", "DisableFileLocking")); + + public static bool IsFileLockingEnabled => IsWindows || !s_fileLockingDisabled.Value; + private static bool GetIsInContainer() { if (IsWindows) diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 93f9297296e..fd94f8bfdca 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Net5Co EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{FEF03BCC-509F-4646-9132-9DE27FA3DA6F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.FileSystem.DisabledFileLocking.Tests", "tests\DisabledFileLockingTests\System.IO.FileSystem.DisabledFileLocking.Tests.csproj", "{D20CD3B7-A332-4D47-851A-FD8C80AE10B9}" +EndProject Global GlobalSection(NestedProjects) = preSolution {D350D6E7-52F1-40A4-B646-C178F6BBB689} = {1A727AF9-4F39-4109-BB8F-813286031DC9} @@ -43,6 +45,7 @@ Global {D7DF8034-3AE5-4DEF-BCC4-6353239391BF} = {D9FB1730-B750-4C0D-8D24-8C992DEB6034} {48E07F12-8597-40DE-8A37-CCBEB9D54012} = {1A727AF9-4F39-4109-BB8F-813286031DC9} {FEF03BCC-509F-4646-9132-9DE27FA3DA6F} = {1A727AF9-4F39-4109-BB8F-813286031DC9} + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9} = {1A727AF9-4F39-4109-BB8F-813286031DC9} EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -97,6 +100,10 @@ Global {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.Build.0 = Release|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D20CD3B7-A332-4D47-851A-FD8C80AE10B9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs new file mode 100644 index 00000000000..736991961ac --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/DisabledFileLockingSwitchTests.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.Tests +{ + public class DisabledFileLockingSwitchTests + { + [Fact] + public static void ConfigSwitchIsHonored() + { + Assert.Equal(OperatingSystem.IsWindows(), PlatformDetection.IsFileLockingEnabled); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj new file mode 100644 index 00000000000..87afaa0fb07 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj @@ -0,0 +1,32 @@ + + + true + true + + $(NetCoreAppCurrent)-Unix + + --working-dir=/test-dir + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json new file mode 100644 index 00000000000..c118f2a0a0a --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/DisabledFileLockingTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.IO.DisableFileLocking": true + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs index eb2686807bc..643d2a98cf2 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Copy.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Copy.cs @@ -355,7 +355,7 @@ namespace System.IO.Tests /// /// Single tests that shouldn't be duplicated by inheritance. /// - [SkipOnPlatform(TestPlatforms.Browser, "https://github.com/dotnet/runtime/issues/40867 - flock not supported")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public sealed class File_Copy_Single : FileSystemTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem/tests/File/Create.cs b/src/libraries/System.IO.FileSystem/tests/File/Create.cs index bf0417dbfe0..fb7f4be892c 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Create.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Create.cs @@ -142,8 +142,7 @@ namespace System.IO.Tests Assert.Throws(() => Create(testFile)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void FileInUse() { DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath()); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs index 7f13447f37d..54c76437663 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs @@ -80,8 +80,7 @@ namespace System.IO.Tests Assert.Equal(overwriteBytes, File.ReadAllBytes(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs index 111ecb545ad..748c01dbffb 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs @@ -94,8 +94,7 @@ namespace System.IO.Tests Assert.Equal(overwriteBytes, await File.ReadAllBytesAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs index c0ba0787d57..99c94b45079 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLines.cs @@ -77,8 +77,7 @@ namespace System.IO.Tests Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); @@ -267,8 +266,7 @@ namespace System.IO.Tests Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs index 76d1541d60c..b2a1288639d 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs @@ -75,8 +75,7 @@ namespace System.IO.Tests Assert.Equal(overwriteLines, await ReadAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs index d24a8445e6d..1dbe3038496 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllText.cs @@ -84,8 +84,7 @@ namespace System.IO.Tests Assert.Equal(overwriteLines, Read(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void OpenFile_ThrowsIOException() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs index 3481bff97b4..3ad76c272ff 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/ReadWriteAllTextAsync.cs @@ -83,8 +83,7 @@ namespace System.IO.Tests Assert.Equal(overwriteLines, await ReadAsync(path)); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public async Task OpenFile_ThrowsIOExceptionAsync() { string path = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs index 386b2992ee3..205e2940e7e 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.cs @@ -120,10 +120,9 @@ namespace System.IO.Tests } } - [Theory] [InlineData(FileMode.Create)] [InlineData(FileMode.Truncate)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void NoTruncateOnFileShareViolation(FileMode fileMode) { string fileName = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs index ae93555bbe8..6ee1c6fc95c 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm_fa_fs.write.cs @@ -37,8 +37,7 @@ namespace System.IO.Tests } } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/40065", TestPlatforms.Browser)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsFileLockingEnabled))] public void FileShareWithoutWriteThrows() { string fileName = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index dd67a63fed1..745ff60368b 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 10dd1eabee0..ee1def85b9e 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -206,15 +206,29 @@ namespace Microsoft.Win32.SafeHandles { default: case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. - case FileMode.Truncate: // We truncate the file after getting the lock + break; + case FileMode.Truncate: + if (DisableFileLocking) + { + // if we don't lock the file, we can truncate it when opening + // otherwise we truncate the file after getting the lock + flags |= Interop.Sys.OpenFlags.O_TRUNC; + } break; case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later case FileMode.OpenOrCreate: - case FileMode.Create: // We truncate the file after getting the lock flags |= Interop.Sys.OpenFlags.O_CREAT; break; + case FileMode.Create: + flags |= Interop.Sys.OpenFlags.O_CREAT; + if (DisableFileLocking) + { + flags |= Interop.Sys.OpenFlags.O_TRUNC; + } + break; + case FileMode.CreateNew: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); break; @@ -292,7 +306,7 @@ namespace Microsoft.Win32.SafeHandles ignoreNotSupported: true); // just a hint. } - if (mode == FileMode.Create || mode == FileMode.Truncate) + if ((mode == FileMode.Create || mode == FileMode.Truncate) && !DisableFileLocking) { // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated // if opened successfully. From 960e7b352d5f4166193b2ca35c2f627ce0b60332 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Sat, 10 Jul 2021 13:01:37 -0700 Subject: [PATCH 418/926] Disable tests blocking CI (#55446) * Turn off test FlushAsync_ThrowsIfWriterReaderWithException * Turn off System.Net.NameResolution.Tests.GetHost* tests for SLES --- .../tests/FlushAsyncTests.cs | 2 +- .../FunctionalTests/GetHostByNameTest.cs | 15 ++++++++++++- .../tests/FunctionalTests/GetHostEntryTest.cs | 22 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs b/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs index 9647a47055b..fa3db4c91ae 100644 --- a/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs +++ b/src/libraries/System.IO.Pipelines/tests/FlushAsyncTests.cs @@ -22,7 +22,7 @@ namespace System.IO.Pipelines.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55435")] public async Task FlushAsync_ThrowsIfWriterReaderWithException() { void ThrowTestException() diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs index 94dba8bdb7f..2b62cb6c771 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs @@ -4,6 +4,8 @@ #pragma warning disable 0618 // use of obsolete methods using System.Net.Sockets; + +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Net.NameResolution.Tests @@ -106,17 +108,28 @@ namespace System.Net.NameResolution.Tests [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void DnsObsoleteGetHostByName_EmptyString_ReturnsHostName() { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } IPHostEntry entry = Dns.GetHostByName(""); // DNS labels should be compared as case insensitive for ASCII characters. See RFC 4343. Assert.Contains(Dns.GetHostName(), entry.HostName, StringComparison.OrdinalIgnoreCase); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process), nameof(PlatformDetection.IsThreadingSupported))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void DnsObsoleteBeginEndGetHostByName_EmptyString_ReturnsHostName() { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + IPHostEntry entry = Dns.EndGetHostByName(Dns.BeginGetHostByName("", null, null)); // DNS labels should be compared as case insensitive for ASCII characters. See RFC 4343. diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index 6fa40dd80c4..f860a2274e8 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -7,6 +7,7 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Net.NameResolution.Tests @@ -21,13 +22,18 @@ namespace System.Net.NameResolution.Tests } [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] - + [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] [InlineData("")] [InlineData(TestSettings.LocalHost)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task Dns_GetHostEntry_HostString_Ok(string hostName) { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + try { await TestGetHostEntryAsync(() => Task.FromResult(Dns.GetHostEntry(hostName))); @@ -72,12 +78,20 @@ namespace System.Net.NameResolution.Tests } [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] [InlineData("")] [InlineData(TestSettings.LocalHost)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) => + public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) + { + if (PlatformDetection.IsSLES) + { + // See https://github.com/dotnet/runtime/issues/55271 + throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + } + await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(hostName)); + } [Fact] public async Task Dns_GetHostEntryAsync_IPString_Ok() => From fce412be7779966533b94ba879e98e4eecbd94fc Mon Sep 17 00:00:00 2001 From: Yauk <33248368+Yauk@users.noreply.github.com> Date: Sat, 10 Jul 2021 15:23:25 -0700 Subject: [PATCH 419/926] Support filtering ObjectAllocated callback for pinned object heap allocation only (#55448) * Prototype allocation profiler * Add callback for pinned objects * Fix the build issue caused by corprof.idl change * Improve the test * Misc changes for the tests Co-authored-by: Andrew Au Co-authored-by: Yauk Jia --- src/coreclr/inc/corprof.idl | 3 + src/coreclr/inc/profilepriv.inl | 15 ++++ src/coreclr/pal/prebuilt/inc/corprof.h | 1 + src/coreclr/vm/eeprofinterfaces.inl | 8 ++ src/coreclr/vm/gchelpers.cpp | 3 +- src/tests/profiler/gc/gcallocate.cs | 33 +++++++++ src/tests/profiler/gc/gcallocate.csproj | 23 ++++++ src/tests/profiler/native/CMakeLists.txt | 1 + src/tests/profiler/native/classfactory.cpp | 2 + .../gcallocateprofiler/gcallocateprofiler.cpp | 73 +++++++++++++++++++ .../gcallocateprofiler/gcallocateprofiler.h | 26 +++++++ .../gcbasicprofiler/gcbasicprofiler.cpp | 4 +- .../profiler/native/gcprofiler/gcprofiler.cpp | 2 +- 13 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 src/tests/profiler/gc/gcallocate.cs create mode 100644 src/tests/profiler/gc/gcallocate.csproj create mode 100644 src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp create mode 100644 src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h diff --git a/src/coreclr/inc/corprof.idl b/src/coreclr/inc/corprof.idl index 8fc965a84f6..d1c58f96cf9 100644 --- a/src/coreclr/inc/corprof.idl +++ b/src/coreclr/inc/corprof.idl @@ -667,6 +667,9 @@ typedef enum COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x00000080, + // Enables the pinned object allocation monitoring. + COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x00000100, + COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS | COR_PRF_HIGH_BASIC_GC | diff --git a/src/coreclr/inc/profilepriv.inl b/src/coreclr/inc/profilepriv.inl index 08ba58f5623..e99591c5ffd 100644 --- a/src/coreclr/inc/profilepriv.inl +++ b/src/coreclr/inc/profilepriv.inl @@ -1860,6 +1860,21 @@ inline BOOL CORProfilerTrackLargeAllocations() (&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED)); } +inline BOOL CORProfilerTrackPinnedAllocations() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + return + (CORProfilerPresent() && + (&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED)); +} + inline BOOL CORProfilerEnableRejit() { CONTRACTL diff --git a/src/coreclr/pal/prebuilt/inc/corprof.h b/src/coreclr/pal/prebuilt/inc/corprof.h index 85ce86870bf..e82623d0c09 100644 --- a/src/coreclr/pal/prebuilt/inc/corprof.h +++ b/src/coreclr/pal/prebuilt/inc/corprof.h @@ -574,6 +574,7 @@ enum __MIDL___MIDL_itf_corprof_0000_0000_0006 COR_PRF_HIGH_REQUIRE_PROFILE_IMAGE = 0, COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED = 0x40, COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x80, + COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x100, COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) , COR_PRF_HIGH_ALLOWABLE_NOTIFICATION_PROFILER = ( ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_DISABLE_TIERED_COMPILATION ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) , COR_PRF_HIGH_MONITOR_IMMUTABLE = COR_PRF_HIGH_DISABLE_TIERED_COMPILATION diff --git a/src/coreclr/vm/eeprofinterfaces.inl b/src/coreclr/vm/eeprofinterfaces.inl index 250b3700f80..da6e9788329 100644 --- a/src/coreclr/vm/eeprofinterfaces.inl +++ b/src/coreclr/vm/eeprofinterfaces.inl @@ -31,5 +31,13 @@ FORCEINLINE BOOL TrackLargeAllocations() #endif // PROFILING_SUPPORTED } +FORCEINLINE BOOL TrackPinnedAllocations() +{ +#ifdef PROFILING_SUPPORTED + return CORProfilerTrackPinnedAllocations(); +#else + return FALSE; +#endif // PROFILING_SUPPORTED +} #endif diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 0cecfc624a7..01ffd5305d9 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -324,7 +324,8 @@ void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags) // Notify the profiler of the allocation // do this after initializing bounds so callback has size information if (TrackAllocations() || - (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP)) + (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP) || + (TrackPinnedAllocations() && flags & GC_ALLOC_PINNED_OBJECT_HEAP)) { OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject); GCPROTECT_BEGIN(objref); diff --git a/src/tests/profiler/gc/gcallocate.cs b/src/tests/profiler/gc/gcallocate.cs new file mode 100644 index 00000000000..b3fbfd9d29c --- /dev/null +++ b/src/tests/profiler/gc/gcallocate.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; + +namespace Profiler.Tests +{ + class GCAllocateTests + { + static readonly Guid GcAllocateEventsProfilerGuid = new Guid("55b9554d-6115-45a2-be1e-c80f7fa35369"); + + public static int RunTest(String[] args) + { + int[] large = new int[100000]; + int[] pinned = GC.AllocateArray(32, true); + Console.WriteLine("Test Passed"); + return 100; + } + + public static int Main(string[] args) + { + if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase)) + { + return RunTest(args); + } + + return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location, + testName: "GCCallbacksAllocate", + profilerClsid: GcAllocateEventsProfilerGuid); + } + } +} diff --git a/src/tests/profiler/gc/gcallocate.csproj b/src/tests/profiler/gc/gcallocate.csproj new file mode 100644 index 00000000000..b2fbdc913b0 --- /dev/null +++ b/src/tests/profiler/gc/gcallocate.csproj @@ -0,0 +1,23 @@ + + + .NETCoreApp + exe + BuildAndRun + true + 0 + true + + true + + true + + + + + + + + diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index 9a0561c3e6d..e068fdc1ea3 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES eventpipeprofiler/eventpipereadingprofiler.cpp eventpipeprofiler/eventpipewritingprofiler.cpp eventpipeprofiler/eventpipemetadatareader.cpp + gcallocateprofiler/gcallocateprofiler.cpp gcbasicprofiler/gcbasicprofiler.cpp gcprofiler/gcprofiler.cpp getappdomainstaticaddress/getappdomainstaticaddress.cpp diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index b41f6ba7bc9..fe999926de5 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -6,6 +6,7 @@ #include "eventpipeprofiler/eventpipereadingprofiler.h" #include "eventpipeprofiler/eventpipewritingprofiler.h" #include "getappdomainstaticaddress/getappdomainstaticaddress.h" +#include "gcallocateprofiler/gcallocateprofiler.h" #include "gcbasicprofiler/gcbasicprofiler.h" #include "gcprofiler/gcprofiler.h" #include "metadatagetdispenser/metadatagetdispenser.h" @@ -61,6 +62,7 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI //A little simplistic, we create an instance of every profiler, then return the one whose CLSID matches Profiler* profilers[] = { + new GCAllocateProfiler(), new GCBasicProfiler(), new ReJITProfiler(), new EventPipeReadingProfiler(), diff --git a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp new file mode 100644 index 00000000000..20e02970430 --- /dev/null +++ b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.cpp @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "gcallocateprofiler.h" + +GUID GCAllocateProfiler::GetClsid() +{ + // {55b9554d-6115-45a2-be1e-c80f7fa35369} + GUID clsid = { 0x55b9554d, 0x6115, 0x45a2,{ 0xbe, 0x1e, 0xc8, 0x0f, 0x7f, 0xa3, 0x53, 0x69 } }; + return clsid; +} + +HRESULT GCAllocateProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) +{ + Profiler::Initialize(pICorProfilerInfoUnk); + + HRESULT hr = S_OK; + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_ENABLE_OBJECT_ALLOCATED, COR_PRF_HIGH_BASIC_GC | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED | COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED))) + { + printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr); + return hr; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE GCAllocateProfiler::ObjectAllocated(ObjectID objectId, ClassID classId) +{ + COR_PRF_GC_GENERATION_RANGE gen; + HRESULT hr = pCorProfilerInfo->GetObjectGeneration(objectId, &gen); + if (FAILED(hr)) + { + printf("GetObjectGeneration failed hr=0x%x\n", hr); + _failures++; + } + else if (gen.generation == COR_PRF_GC_LARGE_OBJECT_HEAP) + { + _gcLOHAllocations++; + } + else if (gen.generation == COR_PRF_GC_PINNED_OBJECT_HEAP) + { + _gcPOHAllocations++; + } + else + { + printf("Unexpected object allocation captured, gen.generation=0x%x\n", gen.generation); + _failures++; + } + + return S_OK; +} + +HRESULT GCAllocateProfiler::Shutdown() +{ + Profiler::Shutdown(); + if (_gcPOHAllocations == 0) + { + printf("There is no POH allocations\n"); + } + else if (_gcLOHAllocations == 0) + { + printf("There is no LOH allocations\n"); + } + else if (_failures == 0) + { + printf("%d LOH objects allocated\n", (int)_gcLOHAllocations); + printf("%d POH objects allocated\n", (int)_gcPOHAllocations); + printf("PROFILER TEST PASSES\n"); + } + fflush(stdout); + + return S_OK; +} diff --git a/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h new file mode 100644 index 00000000000..65fb3b16240 --- /dev/null +++ b/src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "../profiler.h" + +class GCAllocateProfiler : public Profiler +{ +public: + GCAllocateProfiler() : Profiler(), + _gcLOHAllocations(0), + _gcPOHAllocations(0), + _failures(0) + {} + + virtual GUID GetClsid(); + virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); + virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId); + virtual HRESULT STDMETHODCALLTYPE Shutdown(); + +private: + std::atomic _gcLOHAllocations; + std::atomic _gcPOHAllocations; + std::atomic _failures; +}; diff --git a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp index a9f28011eb0..6d377e6d115 100644 --- a/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp +++ b/src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp @@ -15,7 +15,7 @@ HRESULT GCBasicProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) Profiler::Initialize(pICorProfilerInfoUnk); HRESULT hr = S_OK; - if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, 0x10))) + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, COR_PRF_HIGH_BASIC_GC))) { _failures++; printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr); @@ -31,7 +31,7 @@ HRESULT GCBasicProfiler::Shutdown() if (_gcStarts == 0) { - printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n"); + printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n"); } else if (_gcFinishes == 0) { diff --git a/src/tests/profiler/native/gcprofiler/gcprofiler.cpp b/src/tests/profiler/native/gcprofiler/gcprofiler.cpp index 0fa298f59f3..42666a2d8ea 100644 --- a/src/tests/profiler/native/gcprofiler/gcprofiler.cpp +++ b/src/tests/profiler/native/gcprofiler/gcprofiler.cpp @@ -31,7 +31,7 @@ HRESULT GCProfiler::Shutdown() if (_gcStarts == 0) { - printf("GCProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n"); + printf("GCProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n"); } else if (_gcFinishes == 0) { From 8fdc82909aefb4768ddce545d5352958e4874f8e Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sat, 10 Jul 2021 15:55:27 -0700 Subject: [PATCH 420/926] Propagators Support (#55419) --- ...em.Diagnostics.DiagnosticSourceActivity.cs | 15 +- ...System.Diagnostics.DiagnosticSource.csproj | 4 + .../System/Diagnostics/LegacyPropagator.cs | 189 +++++ .../System/Diagnostics/NoOutputPropagator.cs | 23 + .../Diagnostics/PassThroughPropagator.cs | 59 ++ .../System/Diagnostics/TextMapPropagator.cs | 141 ++++ .../tests/PropagatorTests.cs | 664 ++++++++++++++++++ ....Diagnostics.DiagnosticSource.Tests.csproj | 1 + 8 files changed, 1095 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 482a58bb414..7e50f732e42 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -253,7 +253,20 @@ namespace System.Diagnostics public System.Diagnostics.SampleActivity? SampleUsingParentId { get { throw null; } set { throw null; } } public System.Diagnostics.SampleActivity? Sample { get { throw null; } set { throw null; } } public void Dispose() { throw null; } - } + } + public abstract class TextMapPropagator + { + public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out System.Collections.Generic.IEnumerable? fieldValues); + public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue); + public abstract System.Collections.Generic.IReadOnlyCollection Fields { get; } + public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter); + public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState); + public abstract System.Collections.Generic.IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter); + public static TextMapPropagator Current { get; set; } + public static TextMapPropagator CreateDefaultPropagator() { throw null; } + public static TextMapPropagator CreatePassThroughPropagator() { throw null; } + public static TextMapPropagator CreateNoOutputPropagator() { throw null; } + } } namespace System.Diagnostics.Metrics diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index e1dca02a19d..e4a9cbd28d7 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -39,7 +39,11 @@ + + + + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs new file mode 100644 index 00000000000..a469dd5b56b --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace System.Diagnostics +{ + internal sealed class LegacyPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new LegacyPropagator(); + + public override IReadOnlyCollection Fields { get; } = new ReadOnlyCollection(new[] { TraceParent, RequestId, TraceState, Baggage, CorrelationContext }); + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (activity is null || setter is null) + { + return; + } + + string? id = activity.Id; + if (id is null) + { + return; + } + + if (activity.IdFormat == ActivityIdFormat.W3C) + { + setter(carrier, TraceParent, id); + if (!string.IsNullOrEmpty(activity.TraceStateString)) + { + setter(carrier, TraceState, activity.TraceStateString); + } + } + else + { + setter(carrier, RequestId, id); + } + + InjectBaggage(carrier, activity.Baggage, setter); + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) + { + if (getter is null) + { + traceId = null; + traceState = null; + return; + } + + getter(carrier, TraceParent, out traceId, out _); + if (traceId is null) + { + getter(carrier, RequestId, out traceId, out _); + } + + getter(carrier, TraceState, out traceState, out _); + } + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) + { + if (getter is null) + { + return null; + } + + getter(carrier, Baggage, out string? theBaggage, out _); + + IEnumerable>? baggage = null; + if (theBaggage is null || !TryExtractBaggage(theBaggage, out baggage)) + { + getter(carrier, CorrelationContext, out theBaggage, out _); + if (theBaggage is not null) + { + TryExtractBaggage(theBaggage, out baggage); + } + } + + return baggage; + } + + internal static bool TryExtractBaggage(string baggageString, out IEnumerable>? baggage) + { + baggage = null; + List>? baggageList = null; + + if (string.IsNullOrEmpty(baggageString)) + { + return true; + } + + int currentIndex = 0; + + do + { + // Skip spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // No Key exist + } + + int keyStart = currentIndex; + + // Search end of the key + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab && baggageString[currentIndex] != '=') + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; + } + + int keyEnd = currentIndex; + + if (baggageString[currentIndex] != '=') + { + // Skip Spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // Wrong key format + } + + if (baggageString[currentIndex] != '=') + { + break; // wrong key format. + } + } + + currentIndex++; + + // Skip spaces + while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab)) + { + currentIndex++; + } + + if (currentIndex >= baggageString.Length) + { + break; // Wrong value format + } + + int valueStart = currentIndex; + + // Search end of the value + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab && + baggageString[currentIndex] != Comma && baggageString[currentIndex] != Semicolon) + { + currentIndex++; + } + + if (keyStart < keyEnd && valueStart < currentIndex) + { + baggageList ??= new(); + + // Insert in reverse order for asp.net compatability. + baggageList.Insert(0, new KeyValuePair( + WebUtility.UrlDecode(baggageString.Substring(keyStart, keyEnd - keyStart)).Trim(s_trimmingSpaceCharacters), + WebUtility.UrlDecode(baggageString.Substring(valueStart, currentIndex - valueStart)).Trim(s_trimmingSpaceCharacters))); + } + + // Skip to end of values + while (currentIndex < baggageString.Length && baggageString[currentIndex] != Comma) + { + currentIndex++; + } + + currentIndex++; // Move to next key-value entry + } while (currentIndex < baggageString.Length); + + baggage = baggageList; + return baggageList != null; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs new file mode 100644 index 00000000000..f9d503a8f1c --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Diagnostics +{ + internal sealed class NoOutputPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new NoOutputPropagator(); + + public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + // nothing to do. + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter); + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs new file mode 100644 index 00000000000..01bf821a5cb --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Diagnostics +{ + internal sealed class PassThroughPropagator : TextMapPropagator + { + internal static TextMapPropagator Instance { get; } = new PassThroughPropagator(); + + public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (setter is null) + { + return; + } + + GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage); + if (parentId is null) + { + return; + } + + setter(carrier, isW3c ? TraceParent : RequestId, parentId); + + if (!string.IsNullOrEmpty(traceState)) + { + setter(carrier, TraceState, traceState); + } + + if (baggage is not null) + { + InjectBaggage(carrier, baggage, setter); + } + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter); + + private static void GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage) + { + Activity? activity = Activity.Current; + + while (activity?.Parent is Activity parent) + { + activity = parent; + } + + traceState = activity?.TraceStateString; + parentId = activity?.ParentId ?? activity?.Id; + isW3c = parentId is not null ? Activity.TryConvertIdToContext(parentId, traceState, out _) : false; + baggage = activity?.Baggage; + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs new file mode 100644 index 00000000000..0dab622a298 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Diagnostics +{ + /// + /// An implementation of TextMapPropagator determines if and how distributed context information is encoded and decoded as it traverses the network. + /// The encoding can be transported over any network protocol that supports string key-value pairs. For example when using HTTP, each key value pair is an HTTP header. + /// TextMapPropagator inject values into and extracts values from carriers as string key/value pairs. + /// + public abstract class TextMapPropagator + { + private static TextMapPropagator s_current = CreateDefaultPropagator(); + + /// + /// The callback that is used in propagators' extract methods. The callback is invoked to lookup the value of a named field. + /// + /// Carrier is the medium used by Propagators to read values from. + /// The propagation field name. + /// An output string to receive the value corresponds to the input fieldName. This should return non null value if there is only one value for the input field name. + /// An output collection of strings to receive the values corresponds to the input fieldName. This should return non null value if there are more than one value for the input field name. + public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues); + + /// + /// The callback that is used in propagators' inject methods. This callback is invoked to set the value of a named field. + /// Propagators may invoke it multiple times in order to set multiple fields. + /// + /// Carrier is the medium used by Propagators to write values to. + /// The propagation field name. + /// The value corresponds to the input fieldName. + public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue); + + /// + /// The set of field names this propagator is likely to read or write. + /// + /// Returns list of fields that will be used by the TextMapPropagator. + public abstract IReadOnlyCollection Fields { get; } + + /// + /// Injects the trace values stroed in the object into a carrier. For example, into the headers of an HTTP request. + /// + /// The Activity object has the distributed context to inject to the carrier. + /// Carrier is the medium in which the distributed context will be stored. + /// The callback will be invoked to set a named key/value pair on the carrier. + public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter); + + /// + /// Extracts trace Id and trace state from an incoming request represented by the carrier. For example, from the headers of an HTTP request. + /// + /// Carrier is the medium from which values will be read. + /// The callback will be invoked to get the propagation trace Id and trace state from carrier. + /// The extracted trace Id from the carrier. + /// The extracted trace state from the carrier. + public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState); + + /// + /// Extracts the baggage key-value pair list from an incoming request represented by the carrier. For example, from the headers of an HTTP request. + /// + /// Carrier is the medium from which values will be read. + /// The callback will be invoked to get the propagation baggage list from carrier. + /// Returns the extracted key-value pair list from the carrier. + public abstract IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter); + + /// + /// Get or set the process wide propagator object which used as the current selected propagator. + /// + public static TextMapPropagator Current + { + get + { + Debug.Assert(s_current is not null); + return s_current; + } + + set + { + s_current = value ?? throw new ArgumentNullException(nameof(value)); + } + } + + /// + /// returns the default propagator object which Current property will be initialized with. + /// + /// + /// CreateDefaultPropagator will create a propagator instance that can inject and extract the headers with field names "tarcestate", + /// "traceparent" of the identifiers which are formatted as W3C trace parent, "Request-Id" of the identifiers which are formatted as a hierarchical identifier. + /// The returned propagator can inject the baggage key-value pair list with header name "Correlation-Context" and it can extract the baggage values mapped to header names "Correlation-Context" and "baggage". + /// + public static TextMapPropagator CreateDefaultPropagator() => LegacyPropagator.Instance; + + /// + /// Returns a propagator which attempts to act transparently, emitting the same data on outbound network requests that was received on the in-bound request. + /// When encoding the outbound message, this propagator uses information from the request's root Activity, ignoring any intermediate Activities that may have been created while processing the request. + /// + public static TextMapPropagator CreatePassThroughPropagator() => PassThroughPropagator.Instance; + + /// + /// Returns a propagator which does not transmit any distributed context information in outbound network messages. + /// + public static TextMapPropagator CreateNoOutputPropagator() => NoOutputPropagator.Instance; + + // internal stuff + + internal static void InjectBaggage(object? carrier, IEnumerable> baggage, PropagatorSetterCallback setter) + { + using (IEnumerator> e = baggage.GetEnumerator()) + { + if (e.MoveNext()) + { + StringBuilder baggageList = new StringBuilder(); + + do + { + KeyValuePair item = e.Current; + baggageList.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(CommaWithSpace); + } while (e.MoveNext()); + + setter(carrier, CorrelationContext, baggageList.ToString(0, baggageList.Length - 2)); + } + } + } + + internal const string TraceParent = "traceparent"; + internal const string RequestId = "Request-Id"; + internal const string TraceState = "tracestate"; + internal const string Baggage = "baggage"; + internal const string CorrelationContext = "Correlation-Context"; + internal const char Space = ' '; + internal const char Tab = (char)9; + internal const char Comma = ','; + internal const char Semicolon = ';'; + internal const string CommaWithSpace = ", "; + + internal static readonly char [] s_trimmingSpaceCharacters = new char[] { Space, Tab }; + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs new file mode 100644 index 00000000000..a0ade495467 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs @@ -0,0 +1,664 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Linq; +using System.Collections.Generic; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public class PropagatorTests + { + internal const string TraceParent = "traceparent"; + internal const string RequestId = "Request-Id"; + internal const string TraceState = "tracestate"; + internal const string Baggage = "baggage"; + internal const string CorrelationContext = "Correlation-Context"; + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestAllPropagators() + { + RemoteExecutor.Invoke(() => { + Assert.NotNull(TextMapPropagator.Current); + + // + // Default Propagator + // + + Assert.Same(TextMapPropagator.CreateDefaultPropagator(), TextMapPropagator.Current); + + TestDefaultPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "Legacy1=true", + new List>() { new KeyValuePair(" LegacyKey1 ", " LegacyValue1 ") }); + + TestDefaultPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "Legacy2=true", + new List>() { new KeyValuePair("LegacyKey2", "LegacyValue2") }); + + TestFields(TextMapPropagator.Current); + + // + // NoOutput Propagator + // + + TextMapPropagator.Current = TextMapPropagator.CreateNoOutputPropagator(); + Assert.NotNull(TextMapPropagator.Current); + TestNoOutputPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "ActivityState=1", + new List>() { new KeyValuePair("B1", "V1"), new KeyValuePair(" B2 ", " V2 ")}); + + TestNoOutputPropagatorUsingHierarchicalActivity( + TextMapPropagator.Current, + "ActivityState=2", + null); + + TestNoOutputPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "ActivityState=1", + new List>() { new KeyValuePair(" B3 ", " V3"), new KeyValuePair(" B4 ", " V4 "), new KeyValuePair("B5", "V5")}); + + TestNoOutputPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "ActivityState=2", + null); + + TestFields(TextMapPropagator.Current); + + // + // Pass Through Propagator + // + + TextMapPropagator.Current = TextMapPropagator.CreatePassThroughPropagator(); + Assert.NotNull(TextMapPropagator.Current); + TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain( + TextMapPropagator.Current, + "PassThrough=true", + new List>() { new KeyValuePair("PassThroughKey1", "PassThroughValue1"), new KeyValuePair("PassThroughKey2", "PassThroughValue2")}); + + TestPassThroughPropagatorUsingHierarchicalActivityWithParentId( + TextMapPropagator.Current, + "PassThrough1=true", + new List>() { new KeyValuePair("PassThroughKey3", "PassThroughValue3"), new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ")}); + + TestPassThroughPropagatorUsingW3CActivity( + TextMapPropagator.Current, + "PassThrough2=1", + new List>() { new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ") }); + + TestPassThroughPropagatorWithNullCurrent(TextMapPropagator.Current); + + TestFields(TextMapPropagator.Current); + + // + // Test Current + // + + Assert.Throws(() => TextMapPropagator.Current = null); + + }).Dispose(); + } + + private void TestDefaultPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("LegacyW3C1", "LegacyW3CState=1", baggage); + using Activity b = CreateW3CActivity("LegacyW3C2", "LegacyW3CState=2", baggage); + + Assert.NotSame(Activity.Current, a); + + TestDefaultPropagatorUsing(a, propagator, state, baggage); + + Assert.Same(Activity.Current, b); + + TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); + } + + private void TestDefaultPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("LegacyHierarchical1", null, "LegacyHierarchicalState=1", baggage); + using Activity b = CreateHierarchicalActivity("LegacyHierarchical2", null, "LegacyHierarchicalState=2", baggage); + + Assert.NotSame(Activity.Current, a); + + TestDefaultPropagatorUsing(a, propagator, state, baggage); + + Assert.Same(Activity.Current, b); + + TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); + } + + private void TestDefaultPropagatorUsing(Activity a, TextMapPropagator propagator, string state, IEnumerable> baggage) + { + // Test with non-current + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent && a.IdFormat == ActivityIdFormat.W3C) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == RequestId && a.IdFormat != ActivityIdFormat.W3C) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestBaggageExtraction(propagator, a); + } + + private void TestNoOutputPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("NoOutputHierarchical", null, state, baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"Not expected to have the setter callback be called in the NoOutput propgator."); + }); + + TestDefaultExtraction(propagator, a); + + TestBaggageExtraction(propagator, a); + } + + private void TestNoOutputPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("NoOutputW3C", state, baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"Not expected to have the setter callback be called in the NoOutput propgator."); + }); + + TestDefaultExtraction(propagator, a); + + TestBaggageExtraction(propagator, a); + } + + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("PassThrough", null, state, baggage); + using Activity b = CreateHierarchicalActivity("PassThroughChild1", null, state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); + using Activity c = CreateHierarchicalActivity("PassThroughChild2", null, state + "2", new List>() { new KeyValuePair("Child2Key", "Child2Value") } ); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.False(true, $"Unexpected to inject a TraceParent with Hierarchical Activity."); + return; + } + + if (fieldName == RequestId) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestDefaultExtraction(propagator, b); + TestDefaultExtraction(propagator, c); + + TestBaggageExtraction(propagator, a); + TestBaggageExtraction(propagator, b); + TestBaggageExtraction(propagator, c); + } + + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentId(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateHierarchicalActivity("PassThrough", "Parent1", state, baggage); + using Activity b = CreateHierarchicalActivity("PassThroughChild1", "Parent2", state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); + using Activity c = CreateHierarchicalActivity("PassThroughChild2", "Parent3", state + "2", new List>() { new KeyValuePair("Child2Key", "Child2Value") } ); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.False(true, $"Unexpected to inject a TraceParent with Hierarchical Activity."); + return; + } + + if (fieldName == RequestId) + { + Assert.Equal(c.ParentId, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(c.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(c.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestDefaultExtraction(propagator, b); + TestDefaultExtraction(propagator, c); + + TestBaggageExtraction(propagator, a); + TestBaggageExtraction(propagator, b); + TestBaggageExtraction(propagator, c); + } + + private void TestPassThroughPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + { + using Activity a = CreateW3CActivity("PassThroughW3C", "PassThroughW3CState=1", baggage); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.Equal(a.Id, value); + return; + } + + if (fieldName == TraceState) + { + Assert.Equal(a.TraceStateString, value); + return; + } + + if (fieldName == CorrelationContext) + { + Assert.Equal(GetFormattedBaggage(a.Baggage), value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + TestDefaultExtraction(propagator, a); + TestBaggageExtraction(propagator, a); + } + + private void TestPassThroughPropagatorWithNullCurrent(TextMapPropagator propagator) + { + Activity.Current = null; + + propagator.Inject(null, null, (object carrier, string fieldName, string value) => + { + Assert.False(true, $"PassThroughPropagator shouldn't inject anything if the Activity.Current is null"); + }); + + using Activity a = CreateW3CActivity("PassThroughNotNull", "", null); + + propagator.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == TraceParent) + { + Assert.Equal(a.Id, value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + } + + private void TestDefaultExtraction(TextMapPropagator propagator, Activity a) + { + bool traceParentEncountered = false; + + propagator.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValues = null; + fieldValue = null; + + if (fieldName == TraceParent) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + fieldValue = a.Id; + } + else + { + traceParentEncountered = true; + } + return; + } + + if (fieldName == RequestId) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + Assert.True(false, $"Not expected to get RequestId as we expect the request handled using TraceParenet."); + } + else + { + Assert.True(traceParentEncountered, $"Expected to get TraceParent request before getting RequestId."); + fieldValue = a.Id; + } + + return; + } + + if (fieldName == TraceState) + { + fieldValue = a.TraceStateString; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }, out string? traceId, out string? traceState); + + Assert.Equal(a.Id, traceId); + Assert.Equal(a.TraceStateString, traceState); + } + + private void TestBaggageExtraction(TextMapPropagator propagator, Activity a) + { + bool baggageEncountered = false; + + IEnumerable>? b = propagator.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValue = null; + fieldValues = null; + + if (fieldName == Baggage) + { + if (a.IdFormat == ActivityIdFormat.W3C) + { + fieldValue = GetFormattedBaggage(a.Baggage); + } + else + { + baggageEncountered = true; + } + + return; + } + + if (fieldName == CorrelationContext && a.IdFormat != ActivityIdFormat.W3C) + { + Assert.True(baggageEncountered, $"Expected to get Baggage request before getting Correlation-Context."); + fieldValue = GetFormattedBaggage(a.Baggage); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}'"); + }); + + Assert.Equal(GetFormattedBaggage(a.Baggage, false, true), GetFormattedBaggage(b, true)); + } + + private void TestFields(TextMapPropagator propagator) + { + Assert.True(propagator.Fields.Contains(TraceParent)); + Assert.True(propagator.Fields.Contains(RequestId)); + Assert.True(propagator.Fields.Contains(TraceState)); + Assert.True(propagator.Fields.Contains(Baggage)); + Assert.True(propagator.Fields.Contains(CorrelationContext)); + } + + internal static string GetFormattedBaggage(IEnumerable>? b, bool flipOrder = false, bool trimSpaces = false) + { + string formattedBaggage = ""; + + if (b is null) + { + return formattedBaggage; + } + List> list = new List>(b); + + int startIndex = flipOrder ? list.Count - 1 : 0; + int exitIndex = flipOrder ? -1 : list.Count; + int step = flipOrder ? -1 : 1; + + for (int i = startIndex; i != exitIndex; i += step) + { + string key = trimSpaces ? list[i].Key.Trim() : list[i].Key; + string value = trimSpaces ? list[i].Value.Trim() : list[i].Value; + + formattedBaggage += (formattedBaggage.Length > 0 ? ", " : "") + WebUtility.UrlEncode(key) + "=" + WebUtility.UrlEncode(value); + } + + return formattedBaggage; + } + + private Activity CreateHierarchicalActivity(string name, string parentId, string state, IEnumerable>? baggage) + { + Activity a = new Activity(name); + a.SetIdFormat(ActivityIdFormat.Hierarchical); + + if (baggage is not null) + { + foreach (KeyValuePair kvp in baggage) + { + a.SetBaggage(kvp.Key, kvp.Value); + } + } + + a.TraceStateString = state; + + if (parentId is not null) + { + a.SetParentId(parentId); + } + a.Start(); + + return a; + } + + private Activity CreateW3CActivity(string name, string state, IEnumerable>? baggage) + { + Activity a = new Activity(name); + a.SetIdFormat(ActivityIdFormat.W3C); + + if (baggage is not null) + { + foreach (KeyValuePair kvp in baggage) + { + a.SetBaggage(kvp.Key, kvp.Value); + } + } + + a.TraceStateString = state; + a.Start(); + + return a; + } + + internal static IEnumerable>? ParseBaggage(string baggageString) + { + if (baggageString is null) + { + return null; + } + + List> list = new(); + string [] parts = baggageString.Split(','); + + foreach (string part in parts) + { + string [] baggageItem = part.Split('='); + + if (baggageItem.Length != 2) + { + return null; // Invalid format + } + + list.Add(new KeyValuePair(WebUtility.UrlDecode(baggageItem[0]).Trim(), WebUtility.UrlDecode(baggageItem[1]).Trim())); + } + + return list; + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestCustomPropagator() + { + RemoteExecutor.Invoke(() => { + + TextMapPropagator.Current = new CustomPropagator(); + using Activity a = CreateW3CActivity("CustomW3C1", "CustomW3CState=1", new List>() { new KeyValuePair(" CustomKey1 ", " CustomValue1 ") }); + + string traceParent = "x-" + a.Id ; + string traceState = "x-" + a.TraceStateString; + string baggageString = "x=y, " + GetFormattedBaggage(a.Baggage); + + TextMapPropagator.Current.Inject(a, null, (object carrier, string fieldName, string value) => + { + if (fieldName == CustomPropagator.XTraceParent) + { + Assert.Equal(traceParent, value); + return; + } + + if (fieldName == CustomPropagator.XTraceState) + { + Assert.Equal(traceState, value); + return; + } + + if (fieldName == CustomPropagator.XBaggage) + { + Assert.Equal(baggageString, value); + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in the Custom Propagator"); + }); + + TextMapPropagator.Current.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + fieldValues = null; + fieldValue = null; + + if (fieldName == CustomPropagator.XTraceParent) + { + fieldValue = traceParent; + return; + } + + if (fieldName == CustomPropagator.XTraceState) + { + fieldValue = traceState; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in the Custom propagator"); + }, out string? traceId, out string? state); + + Assert.Equal(traceParent, traceId); + Assert.Equal(traceState, state); + + IEnumerable>? b = TextMapPropagator.Current.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + Assert.Null(carrier); + fieldValue = null; + fieldValues = null; + + if (fieldName == CustomPropagator.XBaggage) + { + fieldValue = baggageString; + return; + } + + Assert.False(true, $"Encountered wrong header name '{fieldName}' in custom propagator"); + }); + + Assert.Equal(2, b.Count()); + Assert.Equal(new KeyValuePair("x", "y"), b.ElementAt(0)); + Assert.Equal(new KeyValuePair("CustomKey1", "CustomValue1"), b.ElementAt(1)); + + }).Dispose(); + } + + internal class CustomPropagator : TextMapPropagator + { + internal const string XTraceParent = "x-traceparent"; + internal const string XTraceState = "x-tracestate"; + internal const string XBaggage = "x-baggage"; + + public override IReadOnlyCollection Fields { get; } = new[] { XTraceParent, XTraceState, XBaggage}; + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (activity is null || carrier is null) + { + return; + } + + setter(carrier, XTraceParent, "x-" + activity.Id); + + if (!string.IsNullOrEmpty(activity.TraceStateString)) + { + setter(carrier, XTraceState, "x-" + activity.TraceStateString); + } + + if (activity.Baggage.Count() > 0) + { + setter(carrier, XBaggage, "x=y, " + PropagatorTests.GetFormattedBaggage(activity.Baggage)); + } + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) + { + if (getter is null) + { + traceId = null; + traceState = null; + return; + } + + getter(carrier, XTraceParent, out traceId, out _); + getter(carrier, XTraceState, out traceState, out _); + } + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) + { + if (getter is null) + { + return null; + } + + getter(carrier, XBaggage, out string? theBaggage, out _); + + return PropagatorTests.ParseBaggage(theBaggage); + } + } + } +} diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj index 3d38a949317..24c84423e66 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/System.Diagnostics.DiagnosticSource.Tests.csproj @@ -28,6 +28,7 @@ + From 21c251623d45e2a753cd687c0551ec327f837d51 Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Sat, 10 Jul 2021 17:10:48 -0700 Subject: [PATCH 421/926] Fixing MetricEventSource tests (#55385) * Fixing MetricEventSource tests I've seen two recent non-deterministic failures: 1. After starting the EventListener there is a delay of one collection interval (previously 0.3 seconds) where we expect calls to counter.Add and Histogram.Record() to update stats. On a fast machine this code would run in under a millisecond but in some test runs it still wasn't happening fast enough. 2. We were seeing error events from a previous test get observed in a later test because session id was being ignored for error events. Fixes: 1. Added an initial collection interval where no counter APIs will be invoked and the test will delay up to 60 seconds waiting for this. Hopefully this delay makes it more likely that the Add/Record APIs are ready to execute promptly once the 2nd interval begins. I also increased the intervals to 1 second. If that still isn't sufficient we can either further increase the collection intervals or disable the tests. I also moved these tests to outerloop because they are slow and noisy on the console, but it may have a side benefit lessening the impact if there are future timing related failures. 2. Tightened up the session id matching so only error events with empty id or the expected id are allowed. --- .../tests/MetricEventSourceTests.cs | 216 ++++++++++-------- 1 file changed, 118 insertions(+), 98 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs index c63aaa1a871..9cd9501d8c5 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -16,6 +16,8 @@ namespace System.Diagnostics.Metrics.Tests public class MetricEventSourceTests { ITestOutputHelper _output; + const double IntervalSecs = 1; + static readonly TimeSpan s_waitForEventTimeout = TimeSpan.FromSeconds(60); public MetricEventSourceTests(ITestOutputHelper output) { @@ -23,6 +25,7 @@ namespace System.Diagnostics.Metrics.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithEmptyMetadata() { using Meter meter = new Meter("TestMeter1"); @@ -34,15 +37,15 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter1")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter1")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -52,10 +55,11 @@ namespace System.Diagnostics.Metrics.Tests AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithMetadata() { using Meter meter = new Meter("TestMeter2"); @@ -67,28 +71,29 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter2")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter2")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesForLateMeter() { // this ensures the MetricsEventSource exists when the listener tries to query @@ -102,10 +107,9 @@ namespace System.Diagnostics.Metrics.Tests Histogram h; EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter3")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter3")) { - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); // the Meter is created after the EventSource was already monitoring meter = new Meter("TestMeter3"); @@ -118,10 +122,10 @@ namespace System.Diagnostics.Metrics.Tests c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -131,7 +135,7 @@ namespace System.Diagnostics.Metrics.Tests AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } finally { @@ -140,6 +144,7 @@ namespace System.Diagnostics.Metrics.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesForLateInstruments() { // this ensures the MetricsEventSource exists when the listener tries to query @@ -150,10 +155,9 @@ namespace System.Diagnostics.Metrics.Tests Histogram h; EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter4")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter4")) { - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); // Instruments are created after the EventSource was already monitoring c = meter.CreateCounter("counter1"); @@ -165,10 +169,10 @@ namespace System.Diagnostics.Metrics.Tests c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -178,10 +182,11 @@ namespace System.Diagnostics.Metrics.Tests AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesTimeSeriesWithTags() { using Meter meter = new Meter("TestMeter5"); @@ -209,20 +214,21 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter5")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter5")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5, new KeyValuePair("Color", "red")); c.Add(6, new KeyValuePair("Color", "blue")); h.Record(19, new KeyValuePair("Size", 123)); h.Record(20, new KeyValuePair("Size", 124)); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12, new KeyValuePair("Color", "red")); c.Add(13, new KeyValuePair("Color", "blue")); h.Record(26, new KeyValuePair("Size", 123)); h.Record(27, new KeyValuePair("Size", 124)); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -236,11 +242,12 @@ namespace System.Diagnostics.Metrics.Tests AssertGaugeEventsPresent(events, meter.Name, og.Name, "Color=blue,Size=4", "", "18", "36"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=123", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=124", "", "0.5=20;0.95=20;0.99=20", "0.5=27;0.95=27;0.99=27"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceFiltersInstruments() { using Meter meterA = new Meter("TestMeterA"); @@ -257,10 +264,11 @@ namespace System.Diagnostics.Metrics.Tests Counter c3c = meterC.CreateCounter("counter3"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeterA\\counter3;TestMeterB\\counter1;TestMeterC\\counter2;TestMeterB;TestMeterC\\counter3")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c1a.Add(1); c2a.Add(1); c3a.Add(1); @@ -270,7 +278,7 @@ namespace System.Diagnostics.Metrics.Tests c1c.Add(1); c2c.Add(1); c3c.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c1a.Add(2); c2a.Add(2); @@ -281,7 +289,7 @@ namespace System.Diagnostics.Metrics.Tests c1c.Add(2); c2c.Add(2); c3c.Add(2); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -296,10 +304,11 @@ namespace System.Diagnostics.Metrics.Tests AssertCounterEventsNotPresent(events, meterA.Name, c1a.Name, ""); AssertCounterEventsNotPresent(events, meterA.Name, c2a.Name, ""); AssertCounterEventsNotPresent(events, meterC.Name, c1c.Name, ""); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesMissingDataPoints() { using Meter meter = new Meter("TestMeter6"); @@ -310,7 +319,7 @@ namespace System.Diagnostics.Metrics.Tests { counterState += 7; counterCollectInterval++; - if ((counterCollectInterval % 2) == 1) + if ((counterCollectInterval % 2) == 0) { return new Measurement[] { new Measurement(counterState) }; } @@ -326,7 +335,7 @@ namespace System.Diagnostics.Metrics.Tests { gaugeState += 9; gaugeCollectInterval++; - if ((gaugeCollectInterval % 2) == 1) + if ((gaugeCollectInterval % 2) == 0) { return new Measurement[] { new Measurement(gaugeState) }; } @@ -339,19 +348,20 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meter.CreateHistogram("histogram1"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter6")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter6")) { + // no measurements in interval 1 + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); - // no measurements in interval 2 - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + // no measurements in interval 3 + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); - // no measurements in interval 4 - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 4); + listener.WaitForCollectionStop(s_waitForEventTimeout, 4); + // no measurements in interval 5 + listener.WaitForCollectionStop(s_waitForEventTimeout, 5); events = listener.Events.ToArray(); } @@ -359,12 +369,13 @@ namespace System.Diagnostics.Metrics.Tests AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "0", "12"); AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "0", "14", "0"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "", "27", ""); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "18", "", "36", ""); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "", "0.5=26;0.95=26;0.99=26", ""); - AssertCollectStartStopEventsPresent(events, intervalSecs, 4); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 5); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesEndEventsOnNewListener() { using Meter meter = new Meter("TestMeter7"); @@ -376,32 +387,33 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meter.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter7")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter7")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); // some alternate listener starts listening - using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "ADifferentMeter"); - listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 4); + using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "ADifferentMeter"); + listener.WaitForEndInstrumentReporting(s_waitForEventTimeout, 4); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); AssertEndInstrumentReportingEventsPresent(events, c, oc, og, h); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesEndEventsOnMeterDispose() { using Meter meterA = new Meter("TestMeter8"); @@ -414,36 +426,37 @@ namespace System.Diagnostics.Metrics.Tests Histogram h = meterB.CreateHistogram("histogram1", "a unit", "the description"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter8;TestMeter9")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter8;TestMeter9")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); c.Add(5); h.Record(19); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); meterA.Dispose(); - listener.WaitForEndInstrumentReporting(TimeSpan.FromSeconds(5), 3); + listener.WaitForEndInstrumentReporting(s_waitForEventTimeout, 3); h.Record(21); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 3); + listener.WaitForCollectionStop(s_waitForEventTimeout, 4); events = listener.Events.ToArray(); } AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); AssertInitialEnumerationCompleteEventPresent(events); AssertCounterEventsPresent(events, meterA.Name, c.Name, "", c.Unit, "5", "12"); - AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7"); - AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18"); + AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7", "7"); + AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meterB.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26", "0.5=21;0.95=21;0.99=21"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 3); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 4); AssertEndInstrumentReportingEventsPresent(events, c, oc, og); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesInstruments() { using Meter meterA = new Meter("TestMeter10"); @@ -458,7 +471,7 @@ namespace System.Diagnostics.Metrics.Tests EventWrittenEventArgs[] events; using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.InstrumentPublishing, null, "")) { - listener.WaitForEnumerationComplete(TimeSpan.FromSeconds(5)); + listener.WaitForEnumerationComplete(s_waitForEventTimeout); events = listener.Events.ToArray(); } @@ -467,6 +480,7 @@ namespace System.Diagnostics.Metrics.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourcePublishesAllDataTypes() { using Meter meter = new Meter("TestMeter12"); @@ -479,25 +493,9 @@ namespace System.Diagnostics.Metrics.Tests Counter d = meter.CreateCounter("counterDouble"); EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, "TestMeter12")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter12")) { - i.Add(1_234_567); - s.Add(21_432); - b.Add(1); - l.Add(123_456_789_012); - dec.Add(123_456_789_012_345); - f.Add(123_456.789F); - d.Add(87_654_321_987_654.4); - - i.Add(1); - s.Add(1); - b.Add(1); - l.Add(1); - dec.Add(1); - f.Add(1); - d.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); i.Add(1_234_567); s.Add(21_432); @@ -514,7 +512,24 @@ namespace System.Diagnostics.Metrics.Tests dec.Add(1); f.Add(1); d.Add(1); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + + i.Add(1_234_567); + s.Add(21_432); + b.Add(1); + l.Add(123_456_789_012); + dec.Add(123_456_789_012_345); + f.Add(123_456.789F); + d.Add(87_654_321_987_654.4); + + i.Add(1); + s.Add(1); + b.Add(1); + l.Add(1); + dec.Add(1); + f.Add(1); + d.Add(1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -527,10 +542,11 @@ namespace System.Diagnostics.Metrics.Tests AssertCounterEventsPresent(events, meter.Name, dec.Name, "", "", "123456789012346", "123456789012346"); AssertCounterEventsPresent(events, meter.Name, f.Name, "", "", "123457.7890625", "123457.7890625"); AssertCounterEventsPresent(events, meter.Name, d.Name, "", "", "87654321987655.4", "87654321987655.4"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceEnforcesTimeSeriesLimit() { using Meter meter = new Meter("TestMeter13"); @@ -538,20 +554,21 @@ namespace System.Diagnostics.Metrics.Tests EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 2, 50, "TestMeter13")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, 2, 50, "TestMeter13")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5, new KeyValuePair("Color", "red")); c.Add(6, new KeyValuePair("Color", "blue")); c.Add(7, new KeyValuePair("Color", "green")); c.Add(8, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12, new KeyValuePair("Color", "red")); c.Add(13, new KeyValuePair("Color", "blue")); c.Add(14, new KeyValuePair("Color", "green")); c.Add(15, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -562,10 +579,11 @@ namespace System.Diagnostics.Metrics.Tests AssertTimeSeriesLimitPresent(events); AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=green"); AssertCounterEventsNotPresent(events, meter.Name, c.Name, "Color=yellow"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] public void EventSourceEnforcesHistogramLimit() { using Meter meter = new Meter("TestMeter14"); @@ -573,20 +591,21 @@ namespace System.Diagnostics.Metrics.Tests EventWrittenEventArgs[] events; - double intervalSecs = 0.3; - using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, intervalSecs, 50, 2, "TestMeter14")) + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, 50, 2, "TestMeter14")) { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + h.Record(5, new KeyValuePair("Color", "red")); h.Record(6, new KeyValuePair("Color", "blue")); h.Record(7, new KeyValuePair("Color", "green")); h.Record(8, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 1); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); h.Record(12, new KeyValuePair("Color", "red")); h.Record(13, new KeyValuePair("Color", "blue")); h.Record(14, new KeyValuePair("Color", "green")); h.Record(15, new KeyValuePair("Color", "yellow")); - listener.WaitForCollectionStop(TimeSpan.FromSeconds(5), 2); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -597,7 +616,7 @@ namespace System.Diagnostics.Metrics.Tests AssertHistogramLimitPresent(events); AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=green"); AssertHistogramEventsNotPresent(events, meter.Name, h.Name, "Color=yellow"); - AssertCollectStartStopEventsPresent(events, intervalSecs, 2); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } private void AssertBeginInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) @@ -746,8 +765,8 @@ namespace System.Diagnostics.Metrics.Tests Assert.True(filteredEvents.Length >= expectedValues.Length); for (int i = 0; i < expectedValues.Length; i++) { - Assert.Equal(filteredEvents[i].Unit, expectedUnit); - Assert.Equal(filteredEvents[i].Value, expectedValues[i]); + Assert.Equal(expectedUnit, filteredEvents[i].Unit); + Assert.Equal(expectedValues[i], filteredEvents[i].Value); } } @@ -906,7 +925,8 @@ namespace System.Diagnostics.Metrics.Tests protected override void OnEventWritten(EventWrittenEventArgs eventData) { - if(eventData.EventName != "Message" && eventData.EventName != "Error" && eventData.Payload[0].ToString() != _sessionId) + string sessionId = eventData.Payload[0].ToString(); + if(sessionId != "" && sessionId != _sessionId) { return; } From 83a4d3cc02fb04fce17b24fc09b3cdf77a12ba51 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Sat, 10 Jul 2021 21:04:54 -0700 Subject: [PATCH 422/926] Relax SystemTrustCertificateWithCustomRootTrust test --- .../tests/ChainTests.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 8e9db6ed695..135eed056df 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -245,6 +245,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificateToCustomRootTrust) { using (var microsoftDotCom = new X509Certificate2(TestData.MicrosoftDotComSslCertBytes)) + using (var microsoftDotComIssuer = new X509Certificate2(TestData.MicrosoftDotComIssuerBytes)) using (var testCert = new X509Certificate2(TestFiles.ChainPfxFile, TestData.ChainPfxPassword)) using (var chainHolder = new ChainHolder()) { @@ -252,6 +253,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationTime = microsoftDotCom.NotBefore.AddSeconds(1); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.ExtraStore.Add(microsoftDotComIssuer); if (addCertificateToCustomRootTrust) { @@ -269,16 +271,29 @@ namespace System.Security.Cryptography.X509Certificates.Tests { Assert.False(chain.Build(microsoftDotCom)); - // Linux and Windows do not search the default system root stores when CustomRootTrust is enabled - if (PlatformDetection.UsesAppleCrypto) + // Historically, Windows has not searched system stores when CustomRootTrust is enabled. + // That seems to have recently (as of 2021-07-09) changed. + + Assert.InRange(chain.ChainElements.Count, 2, 3); + + if (chain.ChainElements.Count < 3) { - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } else + { + Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + } + + // Check some known conditions. + + if (PlatformDetection.UsesAppleCrypto) + { + Assert.Equal(3, chain.ChainElements.Count); + } + else if (OperatingSystem.IsLinux()) { Assert.Equal(2, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } } } From 6a47ecf4a8ee670356f5e554f08afb0b32cdac9a Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sun, 11 Jul 2021 16:46:53 +0200 Subject: [PATCH 423/926] W^X support (#54954) * W^X support This change is the last part of enabling the W^X support. It adds the actual executable allocator that handles all double mapped memory allocations and creating the writeable mappings. The platform specific functionality is placed in a new minipal that is going to be a basis for future removal of Windows APIs usage from the native runtime. The last state of the change was tested on all the platforms that we support using coreclr pri 1 tests with both the W^X enabled and disabled using the COMPlus_EnableWriteXorExecute variable. The debugger changes were tested using the managed debugger testing suite on Windows x64, x86 and on Apple Silicon so far. Further testing on other platforms is in progress. * Replace LeafLock in UMEntryThunkFreeList by a new lock * Also allocate LoaderHeapFreeBlock from regular heap. * Set the W^X default to disabled --- src/coreclr/CMakeLists.txt | 3 + src/coreclr/clrdefinitions.cmake | 4 - src/coreclr/debug/ee/arm64/arm64walker.cpp | 9 +- src/coreclr/debug/ee/controller.cpp | 47 +- src/coreclr/debug/ee/controller.h | 18 +- src/coreclr/debug/ee/debugger.cpp | 30 +- src/coreclr/debug/ee/debugger.h | 32 +- src/coreclr/debug/inc/amd64/primitives.h | 22 +- src/coreclr/debug/inc/arm/primitives.h | 13 +- src/coreclr/debug/inc/arm64/primitives.h | 6 +- src/coreclr/debug/inc/i386/primitives.h | 13 +- .../dlls/mscoree/coreclr/CMakeLists.txt | 1 + src/coreclr/inc/CrstTypes.def | 7 + src/coreclr/inc/clrconfigvalues.h | 4 + src/coreclr/inc/crsttypes.h | 218 ++--- src/coreclr/inc/executableallocator.h | 201 ++++- src/coreclr/inc/jithelpers.h | 12 +- src/coreclr/inc/utilcode.h | 29 - src/coreclr/minipal/CMakeLists.txt | 7 + src/coreclr/minipal/Unix/CMakeLists.txt | 4 + src/coreclr/minipal/Unix/doublemapping.cpp | 211 +++++ src/coreclr/minipal/Windows/CMakeLists.txt | 4 + src/coreclr/minipal/Windows/doublemapping.cpp | 205 +++++ src/coreclr/minipal/minipal.h | 78 ++ src/coreclr/utilcode/CMakeLists.txt | 1 + src/coreclr/utilcode/executableallocator.cpp | 755 ++++++++++++++++++ src/coreclr/utilcode/loaderheap.cpp | 119 +-- src/coreclr/utilcode/util.cpp | 162 ---- src/coreclr/vm/CMakeLists.txt | 4 +- src/coreclr/vm/amd64/JitHelpers_Fast.asm | 79 +- src/coreclr/vm/amd64/jithelpers_fast.S | 26 +- src/coreclr/vm/amd64/jitinterfaceamd64.cpp | 20 +- src/coreclr/vm/arm/armsinglestepper.cpp | 30 +- src/coreclr/vm/arm/asmhelpers.S | 10 + src/coreclr/vm/arm/asmhelpers.asm | 12 + src/coreclr/vm/arm/cgencpu.h | 13 + src/coreclr/vm/arm/stubs.cpp | 23 +- src/coreclr/vm/arm64/arm64singlestepper.cpp | 12 +- src/coreclr/vm/arm64/asmhelpers.S | 10 - src/coreclr/vm/arm64/asmhelpers.asm | 35 +- src/coreclr/vm/arm64/cgencpu.h | 13 + src/coreclr/vm/arm64/stubs.cpp | 10 +- src/coreclr/vm/ceemain.cpp | 9 +- src/coreclr/vm/class.cpp | 5 +- src/coreclr/vm/codeman.cpp | 27 +- src/coreclr/vm/comcallablewrapper.cpp | 18 +- src/coreclr/vm/comcallablewrapper.h | 4 + src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/dllimportcallback.cpp | 2 +- src/coreclr/vm/dynamicmethod.cpp | 7 +- src/coreclr/vm/excep.cpp | 2 - src/coreclr/vm/exceptionhandling.cpp | 6 - src/coreclr/vm/gccover.cpp | 4 +- src/coreclr/vm/i386/jithelp.S | 30 +- src/coreclr/vm/i386/jithelp.asm | 35 +- src/coreclr/vm/i386/jitinterfacex86.cpp | 84 +- src/coreclr/vm/i386/stublinkerx86.cpp | 2 +- src/coreclr/vm/i386/stublinkerx86.h | 10 +- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/jitinterface.h | 38 +- src/coreclr/vm/loaderallocator.cpp | 17 +- src/coreclr/vm/loaderallocator.inl | 6 - src/coreclr/vm/method.cpp | 40 - src/coreclr/vm/precode.cpp | 4 +- src/coreclr/vm/stackwalk.cpp | 2 - src/coreclr/vm/stublink.cpp | 14 +- src/coreclr/vm/stublink.h | 2 +- src/coreclr/vm/threads.cpp | 127 ++- src/coreclr/vm/threads.h | 19 +- src/coreclr/vm/virtualcallstub.cpp | 14 +- 70 files changed, 2258 insertions(+), 786 deletions(-) create mode 100644 src/coreclr/minipal/CMakeLists.txt create mode 100644 src/coreclr/minipal/Unix/CMakeLists.txt create mode 100644 src/coreclr/minipal/Unix/doublemapping.cpp create mode 100644 src/coreclr/minipal/Windows/CMakeLists.txt create mode 100644 src/coreclr/minipal/Windows/doublemapping.cpp create mode 100644 src/coreclr/minipal/minipal.h create mode 100644 src/coreclr/utilcode/executableallocator.cpp diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 78aa9694735..b4a48593427 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -119,6 +119,8 @@ add_subdirectory(pal/prebuilt/inc) add_subdirectory(debug/debug-pal) +add_subdirectory(minipal) + if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(gc/sample) endif() @@ -171,6 +173,7 @@ include_directories("classlibnative/cryptography") include_directories("classlibnative/inc") include_directories("${GENERATED_INCLUDE_DIR}") include_directories("hosts/inc") +include_directories("minipal") if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) include_directories("${GENERATED_INCLUDE_DIR}/etw") diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index eeb421cac4c..0485ff99a99 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -224,10 +224,6 @@ if(CLR_CMAKE_TARGET_WIN32) endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) endif(CLR_CMAKE_TARGET_WIN32) -if(CLR_CMAKE_TARGET_OSX) - add_definitions(-DFEATURE_WRITEBARRIER_COPY) -endif(CLR_CMAKE_TARGET_OSX) - if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) add_compile_definitions($<$>>:FEATURE_EH_FUNCLETS>) endif (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/debug/ee/arm64/arm64walker.cpp b/src/coreclr/debug/ee/arm64/arm64walker.cpp index ae6e8c1fc29..6c4dee93497 100644 --- a/src/coreclr/debug/ee/arm64/arm64walker.cpp +++ b/src/coreclr/debug/ee/arm64/arm64walker.cpp @@ -171,7 +171,14 @@ BYTE* NativeWalker::SetupOrSimulateInstructionForPatchSkip(T_CONTEXT * context, { CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, 0xd503201f); //Add Nop in buffer - m_pSharedPatchBypassBuffer->RipTargetFixup = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder ripTargetFixupWriterHolder(&m_pSharedPatchBypassBuffer->RipTargetFixup, sizeof(UINT_PTR)); + UINT_PTR *pRipTargetFixupRW = ripTargetFixupWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + UINT_PTR *pRipTargetFixupRW = &m_pSharedPatchBypassBuffer->RipTargetFixup; +#endif // HOST_OSX && HOST_ARM64 + + *pRipTargetFixupRW = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook LOG((LF_CORDB, LL_INFO100000, "Arm64Walker::Simulate opcode: %x is a Control Flow instr \n", opcode)); if (walk == WALK_CALL) //initialize Lr diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index b17ae8f1150..f9304d16ab0 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -84,8 +84,13 @@ SharedPatchBypassBuffer* DebuggerControllerPatch::GetOrCreateSharedPatchBypassBu if (m_pSharedPatchBypassBuffer == NULL) { void *pSharedPatchBypassBufferRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(SharedPatchBypassBuffer)); +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX, sizeof(SharedPatchBypassBuffer)); - new (sharedPatchBypassBufferWriterHolder.GetRW()) SharedPatchBypassBuffer(); + void *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + void *pSharedPatchBypassBufferRW = pSharedPatchBypassBufferRX; +#endif // HOST_OSX && HOST_ARM64 + new (pSharedPatchBypassBufferRW) SharedPatchBypassBuffer(); m_pSharedPatchBypassBuffer = (SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX; _ASSERTE(m_pSharedPatchBypassBuffer); @@ -4351,7 +4356,15 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // m_pSharedPatchBypassBuffer = patch->GetOrCreateSharedPatchBypassBuffer(); - BYTE* patchBypass = m_pSharedPatchBypassBuffer->PatchBypass; +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)m_pSharedPatchBypassBuffer, sizeof(SharedPatchBypassBuffer)); + SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW(); +#else // HOST_OSX && HOST_ARM64 + SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = m_pSharedPatchBypassBuffer; +#endif // HOST_OSX && HOST_ARM64 + + BYTE* patchBypassRX = m_pSharedPatchBypassBuffer->PatchBypass; + BYTE* patchBypassRW = pSharedPatchBypassBufferRW->PatchBypass; LOG((LF_CORDB, LL_INFO10000, "DPS::DPS: Patch skip for opcode 0x%.4x at address %p buffer allocated at 0x%.8x\n", patch->opcode, patch->address, m_pSharedPatchBypassBuffer)); // Copy the instruction block over to the patch skip @@ -4367,19 +4380,19 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // the 2nd skip executes the new jump-stamp code and not the original method prologue code. Copying // the code every time ensures that we have the most up-to-date version of the code in the buffer. _ASSERTE( patch->IsBound() ); - CopyInstructionBlock(patchBypass, (const BYTE *)patch->address); + CopyInstructionBlock(patchBypassRW, (const BYTE *)patch->address); // Technically, we could create a patch skipper for an inactive patch, but we rely on the opcode being // set here. _ASSERTE( patch->IsActivated() ); - CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, patch->opcode); + CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypassRW, patch->opcode); LOG((LF_CORDB, LL_EVERYTHING, "SetInstruction was called\n")); // // Look at instruction to get some attributes // - NativeWalker::DecodeInstructionForPatchSkip(patchBypass, &(m_instrAttrib)); + NativeWalker::DecodeInstructionForPatchSkip(patchBypassRX, &(m_instrAttrib)); #if defined(TARGET_AMD64) @@ -4395,33 +4408,33 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, // Populate the RIP-relative buffer with the current value if needed // - BYTE* bufferBypass = m_pSharedPatchBypassBuffer->BypassBuffer; + BYTE* bufferBypassRW = pSharedPatchBypassBufferRW->BypassBuffer; // Overwrite the *signed* displacement. - int dwOldDisp = *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]); + int dwOldDisp = *(int*)(&patchBypassRX[m_instrAttrib.m_dwOffsetToDisp]); int dwNewDisp = offsetof(SharedPatchBypassBuffer, BypassBuffer) - (offsetof(SharedPatchBypassBuffer, PatchBypass) + m_instrAttrib.m_cbInstr); - *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp; + *(int*)(&patchBypassRW[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp; // This could be an LEA, which we'll just have to change into a MOV // and copy the original address - if (((patchBypass[0] == 0x4C) || (patchBypass[0] == 0x48)) && (patchBypass[1] == 0x8d)) + if (((patchBypassRX[0] == 0x4C) || (patchBypassRX[0] == 0x48)) && (patchBypassRX[1] == 0x8d)) { - patchBypass[1] = 0x8b; // MOV reg, mem + patchBypassRW[1] = 0x8b; // MOV reg, mem _ASSERTE((int)sizeof(void*) <= SharedPatchBypassBuffer::cbBufferBypass); - *(void**)bufferBypass = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); + *(void**)bufferBypassRW = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); } else { _ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass); // Copy the data into our buffer. - memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); + memcpy(bufferBypassRW, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize); if (m_instrAttrib.m_fIsWrite) { // save the actual destination address and size so when we TriggerSingleStep() we can update the value - m_pSharedPatchBypassBuffer->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); - m_pSharedPatchBypassBuffer->RipTargetFixupSize = m_instrAttrib.m_cOperandSize; + pSharedPatchBypassBufferRW->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp); + pSharedPatchBypassBufferRW->RipTargetFixupSize = m_instrAttrib.m_cOperandSize; } } } @@ -4490,17 +4503,17 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread, #else // FEATURE_EMULATE_SINGLESTEP #ifdef TARGET_ARM64 - patchBypass = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode); + patchBypassRX = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode); #endif //TARGET_ARM64 //set eip to point to buffer... - SetIP(context, (PCODE)patchBypass); + SetIP(context, (PCODE)patchBypassRX); if (context ==(T_CONTEXT*) &c) thread->SetThreadContext(&c); - LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypass, patch->opcode)); + LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypassRX, patch->opcode)); // // Turn on single step (if the platform supports it) so we can diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h index 12b1106f7a4..6996439c31f 100644 --- a/src/coreclr/debug/ee/controller.h +++ b/src/coreclr/debug/ee/controller.h @@ -266,14 +266,28 @@ public: LONG AddRef() { - LONG newRefCount = InterlockedIncrement(&m_refCount); +#if !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG)); + LONG *pRefCountRW = refCountWriterHolder.GetRW(); +#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + LONG *pRefCountRW = &m_refCount; +#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + + LONG newRefCount = InterlockedIncrement(pRefCountRW); _ASSERTE(newRefCount > 0); return newRefCount; } LONG Release() { - LONG newRefCount = InterlockedDecrement(&m_refCount); +#if !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG)); + LONG *pRefCountRW = refCountWriterHolder.GetRW(); +#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + LONG *pRefCountRW = &m_refCount; +#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + + LONG newRefCount = InterlockedDecrement(pRefCountRW); _ASSERTE(newRefCount >= 0); if (newRefCount == 0) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 53ee5555ace..e4563a31757 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -1317,13 +1317,19 @@ DebuggerEval::DebuggerEval(CONTEXT * pContext, DebuggerIPCE_FuncEvalInfo * pEval // Allocate the breakpoint instruction info in executable memory. void *bpInfoSegmentRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(DebuggerEvalBreakpointInfoSegment)); + +#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder bpInfoSegmentWriterHolder((DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX, sizeof(DebuggerEvalBreakpointInfoSegment)); - new (bpInfoSegmentWriterHolder.GetRW()) DebuggerEvalBreakpointInfoSegment(this); + DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = bpInfoSegmentWriterHolder.GetRW(); +#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX; +#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64 + new (bpInfoSegmentRW) DebuggerEvalBreakpointInfoSegment(this); m_bpInfoSegment = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX; // This must be non-zero so that the saved opcode is non-zero, and on IA64 we want it to be 0x16 // so that we can have a breakpoint instruction in any slot in the bundle. - bpInfoSegmentWriterHolder.GetRW()->m_breakpointInstruction[0] = 0x16; + bpInfoSegmentRW->m_breakpointInstruction[0] = 0x16; #if defined(TARGET_ARM) USHORT *bp = (USHORT*)&m_bpInfoSegment->m_breakpointInstruction; *bp = CORDbg_BREAK_INSTRUCTION; @@ -16234,6 +16240,7 @@ void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger) } #endif // DACCESS_COMPILE +#ifndef DACCESS_COMPILE /* ------------------------------------------------------------------------ * * Functions for DebuggerHeap executable memory allocations * ------------------------------------------------------------------------ */ @@ -16378,6 +16385,7 @@ void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(De return page->GetPointerToChunk(chunkNumber); } +#endif // DACCESS_COMPILE /* ------------------------------------------------------------------------ * * DebuggerHeap impl @@ -16412,7 +16420,7 @@ void DebuggerHeap::Destroy() m_hHeap = NULL; } #endif -#ifndef HOST_WINDOWS +#if !defined(HOST_WINDOWS) && !defined(DACCESS_COMPILE) if (m_execMemAllocator != NULL) { delete m_execMemAllocator; @@ -16439,6 +16447,8 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + // Have knob catch if we don't want to lazy init the debugger. _ASSERTE(!g_DbgShouldntUseDebugger); m_fExecutable = fExecutable; @@ -16472,7 +16482,9 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable) return E_OUTOFMEMORY; } } -#endif +#endif + +#endif // !DACCESS_COMPILE return S_OK; } @@ -16549,7 +16561,10 @@ void *DebuggerHeap::Alloc(DWORD size) size += sizeof(InteropHeapCanary); #endif - void *ret; + void *ret = NULL; + +#ifndef DACCESS_COMPILE + #ifdef USE_INTEROPSAFE_HEAP _ASSERTE(m_hHeap != NULL); ret = ::HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); @@ -16585,7 +16600,7 @@ void *DebuggerHeap::Alloc(DWORD size) InteropHeapCanary * pCanary = InteropHeapCanary::GetFromRawAddr(ret); ret = pCanary->GetUserAddr(); #endif - +#endif // !DACCESS_COMPILE return ret; } @@ -16638,6 +16653,8 @@ void DebuggerHeap::Free(void *pMem) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + #ifdef USE_INTEROPSAFE_CANARY // Check for canary @@ -16673,6 +16690,7 @@ void DebuggerHeap::Free(void *pMem) #endif // HOST_WINDOWS } #endif +#endif // !DACCESS_COMPILE } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index f16f8cd6d9d..5503de24590 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -1054,6 +1054,8 @@ constexpr uint64_t CHUNKS_PER_DEBUGGERHEAP=(DEBUGGERHEAP_PAGESIZE / EXPECTED_CHU constexpr uint64_t MAX_CHUNK_MASK=((1ull << CHUNKS_PER_DEBUGGERHEAP) - 1); constexpr uint64_t BOOKKEEPING_CHUNK_MASK (1ull << (CHUNKS_PER_DEBUGGERHEAP - 1)); +#ifndef DACCESS_COMPILE + // Forward declaration struct DebuggerHeapExecutableMemoryPage; @@ -1110,8 +1112,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage inline void SetNextPage(DebuggerHeapExecutableMemoryPage* nextPage) { +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.nextPage = nextPage; + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif + pHeapPageRW->chunks[0].bookkeeping.nextPage = nextPage; } inline uint64_t GetPageOccupancy() const @@ -1124,8 +1131,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage // Can't unset the bookmark chunk! ASSERT((newOccupancy & BOOKKEEPING_CHUNK_MASK) != 0); ASSERT(newOccupancy <= MAX_CHUNK_MASK); +#if defined(HOST_OSX) && defined(HOST_ARM64) ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.pageOccupancy = newOccupancy; + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif + pHeapPageRW->chunks[0].bookkeeping.pageOccupancy = newOccupancy; } inline void* GetPointerToChunk(int chunkNum) const @@ -1136,14 +1148,18 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage DebuggerHeapExecutableMemoryPage() { - ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); - SetPageOccupancy(BOOKKEEPING_CHUNK_MASK); // only the first bit is set. +#if defined(HOST_OSX) && defined(HOST_ARM64) + ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage)); + DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW(); +#else + DebuggerHeapExecutableMemoryPage *pHeapPageRW = this; +#endif for (uint8_t i = 1; i < CHUNKS_PER_DEBUGGERHEAP; i++) { ASSERT(i != 0); - debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.startOfPage = this; - debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.chunkNumber = i; + pHeapPageRW->chunks[i].data.startOfPage = this; + pHeapPageRW->chunks[i].data.chunkNumber = i; } } @@ -1190,6 +1206,8 @@ private: Crst m_execMemAllocMutex; }; +#endif // DACCESS_COMPILE + // ------------------------------------------------------------------------ * // DebuggerHeap class // For interop debugging, we need a heap that: @@ -1201,6 +1219,8 @@ private: #define USE_INTEROPSAFE_HEAP #endif +class DebuggerHeapExecutableMemoryAllocator; + class DebuggerHeap { public: diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h index d8d14b24b54..9d363938519 100644 --- a/src/coreclr/debug/inc/amd64/primitives.h +++ b/src/coreclr/debug/inc/amd64/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - #ifndef CORDB_ADDRESS_TYPE typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -191,14 +187,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address) { LIMITED_METHOD_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch) + *((unsigned char*)address) = 0xCC; // int 3 (single byte patch) FlushInstructionCache(GetCurrentProcess(), address, 1); } @@ -209,14 +198,7 @@ inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder instructionWriterHolder(address, sizeof(unsigned char)); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = + *((unsigned char*)address) = (unsigned char) instruction; // setting one byte is important FlushInstructionCache(GetCurrentProcess(), address, 1); diff --git a/src/coreclr/debug/inc/arm/primitives.h b/src/coreclr/debug/inc/arm/primitives.h index c4e2d28602e..269281eb006 100644 --- a/src/coreclr/debug/inc/arm/primitives.h +++ b/src/coreclr/debug/inc/arm/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - #ifndef THUMB_CODE #define THUMB_CODE 1 #endif @@ -163,14 +159,7 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder instructionWriterHolder(address, sizeof(PRD_TYPE)); - CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)addressRW; + CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)address; _ASSERTE(ptraddr & THUMB_CODE); ptraddr &= ~THUMB_CODE; diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h index 4f4c3f7bcd8..05c03c7b309 100644 --- a/src/coreclr/debug/inc/arm64/primitives.h +++ b/src/coreclr/debug/inc/arm64/primitives.h @@ -150,13 +150,13 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, // In a DAC build, this function assumes the input is an host address. LIMITED_METHOD_DAC_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) +#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) ExecutableWriterHolder instructionWriterHolder((LPVOID)address, sizeof(PRD_TYPE)); ULONGLONG ptraddr = dac_cast(instructionWriterHolder.GetRW()); -#else // !DBI_COMPILE && !DACCESS_COMPILE +#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX ULONGLONG ptraddr = dac_cast(address); -#endif // !DBI_COMPILE && !DACCESS_COMPILE +#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX *(PRD_TYPE *)ptraddr = instruction; FlushInstructionCache(GetCurrentProcess(), address, diff --git a/src/coreclr/debug/inc/i386/primitives.h b/src/coreclr/debug/inc/i386/primitives.h index 313b42c5a19..2f228b3a3a9 100644 --- a/src/coreclr/debug/inc/i386/primitives.h +++ b/src/coreclr/debug/inc/i386/primitives.h @@ -12,10 +12,6 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) -#include "executableallocator.h" -#endif - typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -151,14 +147,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address) { LIMITED_METHOD_CONTRACT; -#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) - ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE); - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW(); -#else // !DBI_COMPILE && !DACCESS_COMPILE - UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address; -#endif // !DBI_COMPILE && !DACCESS_COMPILE - - *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch) + *((unsigned char*)address) = 0xCC; // int 3 (single byte patch) FlushInstructionCache(GetCurrentProcess(), address, 1); } diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index fae55ecdc3e..9b8e4b64986 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -109,6 +109,7 @@ set(CORECLR_LIBRARIES v3binder System.Globalization.Native-Static interop + coreclrminipal ) if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index c48872a0b94..c7266df7dbb 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -201,6 +201,10 @@ End Crst Exception End +Crst ExecutableAllocatorLock + AcquiredAfter LoaderHeap ArgBasedStubCache UMEntryThunkFreeListLock +End + Crst ExecuteManRangeLock End @@ -505,6 +509,9 @@ Crst TypeEquivalenceMap AcquiredBefore LoaderHeap End +Crst UMEntryThunkFreeListLock +End + Crst UniqueStack AcquiredBefore LoaderHeap End diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 0d2a1db98e4..e2f1a63a20f 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -737,6 +737,10 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_DOTNET_DiagnosticPorts, W("DiagnosticPorts"), RETAIL_CONFIG_STRING_INFO(INTERNAL_LTTngConfig, W("LTTngConfig"), "Configuration for LTTng.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") +// +// Executable code +// +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 0, "Enable W^X for executable memory."); #ifdef FEATURE_GDBJIT /// diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h index a1bab2ecb90..7be482c48bb 100644 --- a/src/coreclr/inc/crsttypes.h +++ b/src/coreclr/inc/crsttypes.h @@ -49,92 +49,94 @@ enum CrstType CrstEventPipe = 31, CrstEventStore = 32, CrstException = 33, - CrstExecuteManRangeLock = 34, - CrstExternalObjectContextCache = 35, - CrstFCall = 36, - CrstFuncPtrStubs = 37, - CrstFusionAppCtx = 38, - CrstGCCover = 39, - CrstGlobalStrLiteralMap = 40, - CrstHandleTable = 41, - CrstHostAssemblyMap = 42, - CrstHostAssemblyMapAdd = 43, - CrstIbcProfile = 44, - CrstIJWFixupData = 45, - CrstIJWHash = 46, - CrstILStubGen = 47, - CrstInlineTrackingMap = 48, - CrstInstMethodHashTable = 49, - CrstInterop = 50, - CrstInteropData = 51, - CrstIsJMCMethod = 52, - CrstISymUnmanagedReader = 53, - CrstJit = 54, - CrstJitGenericHandleCache = 55, - CrstJitInlineTrackingMap = 56, - CrstJitPatchpoint = 57, - CrstJitPerf = 58, - CrstJumpStubCache = 59, - CrstLeafLock = 60, - CrstListLock = 61, - CrstLoaderAllocator = 62, - CrstLoaderAllocatorReferences = 63, - CrstLoaderHeap = 64, - CrstManagedObjectWrapperMap = 65, - CrstMethodDescBackpatchInfoTracker = 66, - CrstModule = 67, - CrstModuleFixup = 68, - CrstModuleLookupTable = 69, - CrstMulticoreJitHash = 70, - CrstMulticoreJitManager = 71, - CrstNativeImageEagerFixups = 72, - CrstNativeImageLoad = 73, - CrstNls = 74, - CrstNotifyGdb = 75, - CrstObjectList = 76, - CrstPEImage = 77, - CrstPendingTypeLoadEntry = 78, - CrstPgoData = 79, - CrstPinnedByrefValidation = 80, - CrstProfilerGCRefDataFreeList = 81, - CrstProfilingAPIStatus = 82, - CrstRCWCache = 83, - CrstRCWCleanupList = 84, - CrstReadyToRunEntryPointToMethodDescMap = 85, - CrstReflection = 86, - CrstReJITGlobalRequest = 87, - CrstRetThunkCache = 88, - CrstSavedExceptionInfo = 89, - CrstSaveModuleProfileData = 90, - CrstSecurityStackwalkCache = 91, - CrstSigConvert = 92, - CrstSingleUseLock = 93, - CrstSpecialStatics = 94, - CrstStackSampler = 95, - CrstStressLog = 96, - CrstStubCache = 97, - CrstStubDispatchCache = 98, - CrstStubUnwindInfoHeapSegments = 99, - CrstSyncBlockCache = 100, - CrstSyncHashLock = 101, - CrstSystemBaseDomain = 102, - CrstSystemDomain = 103, - CrstSystemDomainDelayedUnloadList = 104, - CrstThreadIdDispenser = 105, - CrstThreadpoolTimerQueue = 106, - CrstThreadpoolWaitThreads = 107, - CrstThreadpoolWorker = 108, - CrstThreadStore = 109, - CrstTieredCompilation = 110, - CrstTypeEquivalenceMap = 111, - CrstTypeIDMap = 112, - CrstUMEntryThunkCache = 113, - CrstUniqueStack = 114, - CrstUnresolvedClassLock = 115, - CrstUnwindInfoTableLock = 116, - CrstVSDIndirectionCellLock = 117, - CrstWrapperTemplate = 118, - kNumberOfCrstTypes = 119 + CrstExecutableAllocatorLock = 34, + CrstExecuteManRangeLock = 35, + CrstExternalObjectContextCache = 36, + CrstFCall = 37, + CrstFuncPtrStubs = 38, + CrstFusionAppCtx = 39, + CrstGCCover = 40, + CrstGlobalStrLiteralMap = 41, + CrstHandleTable = 42, + CrstHostAssemblyMap = 43, + CrstHostAssemblyMapAdd = 44, + CrstIbcProfile = 45, + CrstIJWFixupData = 46, + CrstIJWHash = 47, + CrstILStubGen = 48, + CrstInlineTrackingMap = 49, + CrstInstMethodHashTable = 50, + CrstInterop = 51, + CrstInteropData = 52, + CrstIsJMCMethod = 53, + CrstISymUnmanagedReader = 54, + CrstJit = 55, + CrstJitGenericHandleCache = 56, + CrstJitInlineTrackingMap = 57, + CrstJitPatchpoint = 58, + CrstJitPerf = 59, + CrstJumpStubCache = 60, + CrstLeafLock = 61, + CrstListLock = 62, + CrstLoaderAllocator = 63, + CrstLoaderAllocatorReferences = 64, + CrstLoaderHeap = 65, + CrstManagedObjectWrapperMap = 66, + CrstMethodDescBackpatchInfoTracker = 67, + CrstModule = 68, + CrstModuleFixup = 69, + CrstModuleLookupTable = 70, + CrstMulticoreJitHash = 71, + CrstMulticoreJitManager = 72, + CrstNativeImageEagerFixups = 73, + CrstNativeImageLoad = 74, + CrstNls = 75, + CrstNotifyGdb = 76, + CrstObjectList = 77, + CrstPEImage = 78, + CrstPendingTypeLoadEntry = 79, + CrstPgoData = 80, + CrstPinnedByrefValidation = 81, + CrstProfilerGCRefDataFreeList = 82, + CrstProfilingAPIStatus = 83, + CrstRCWCache = 84, + CrstRCWCleanupList = 85, + CrstReadyToRunEntryPointToMethodDescMap = 86, + CrstReflection = 87, + CrstReJITGlobalRequest = 88, + CrstRetThunkCache = 89, + CrstSavedExceptionInfo = 90, + CrstSaveModuleProfileData = 91, + CrstSecurityStackwalkCache = 92, + CrstSigConvert = 93, + CrstSingleUseLock = 94, + CrstSpecialStatics = 95, + CrstStackSampler = 96, + CrstStressLog = 97, + CrstStubCache = 98, + CrstStubDispatchCache = 99, + CrstStubUnwindInfoHeapSegments = 100, + CrstSyncBlockCache = 101, + CrstSyncHashLock = 102, + CrstSystemBaseDomain = 103, + CrstSystemDomain = 104, + CrstSystemDomainDelayedUnloadList = 105, + CrstThreadIdDispenser = 106, + CrstThreadpoolTimerQueue = 107, + CrstThreadpoolWaitThreads = 108, + CrstThreadpoolWorker = 109, + CrstThreadStore = 110, + CrstTieredCompilation = 111, + CrstTypeEquivalenceMap = 112, + CrstTypeIDMap = 113, + CrstUMEntryThunkCache = 114, + CrstUMEntryThunkFreeListLock = 115, + CrstUniqueStack = 116, + CrstUnresolvedClassLock = 117, + CrstUnwindInfoTableLock = 118, + CrstVSDIndirectionCellLock = 119, + CrstWrapperTemplate = 120, + kNumberOfCrstTypes = 121 }; #endif // __CRST_TYPES_INCLUDED @@ -147,11 +149,11 @@ int g_rgCrstLevelMap[] = { 10, // CrstAppDomainCache 14, // CrstAppDomainHandleTable - 0, // CrstArgBasedStubCache + 3, // CrstArgBasedStubCache 0, // CrstAssemblyList 12, // CrstAssemblyLoader - 3, // CrstAvailableClass - 4, // CrstAvailableParamTypes + 4, // CrstAvailableClass + 5, // CrstAvailableParamTypes 7, // CrstBaseDomain -1, // CrstCCompRC 13, // CrstClassFactInfoHash @@ -160,7 +162,7 @@ int g_rgCrstLevelMap[] = 6, // CrstCodeFragmentHeap 9, // CrstCodeVersioning 0, // CrstCOMCallWrapper - 4, // CrstCOMWrapperCache + 5, // CrstCOMWrapperCache 3, // CrstDataTest1 0, // CrstDataTest2 0, // CrstDbgTransport @@ -179,9 +181,10 @@ int g_rgCrstLevelMap[] = 18, // CrstEventPipe 0, // CrstEventStore 0, // CrstException + 0, // CrstExecutableAllocatorLock 0, // CrstExecuteManRangeLock 0, // CrstExternalObjectContextCache - 3, // CrstFCall + 4, // CrstFCall 7, // CrstFuncPtrStubs 10, // CrstFusionAppCtx 10, // CrstGCCover @@ -196,25 +199,25 @@ int g_rgCrstLevelMap[] = 3, // CrstInlineTrackingMap 17, // CrstInstMethodHashTable 20, // CrstInterop - 4, // CrstInteropData + 5, // CrstInteropData 0, // CrstIsJMCMethod 7, // CrstISymUnmanagedReader 11, // CrstJit 0, // CrstJitGenericHandleCache 16, // CrstJitInlineTrackingMap - 3, // CrstJitPatchpoint + 4, // CrstJitPatchpoint -1, // CrstJitPerf 6, // CrstJumpStubCache 0, // CrstLeafLock -1, // CrstListLock 15, // CrstLoaderAllocator 16, // CrstLoaderAllocatorReferences - 0, // CrstLoaderHeap + 3, // CrstLoaderHeap 3, // CrstManagedObjectWrapperMap 14, // CrstMethodDescBackpatchInfoTracker - 4, // CrstModule + 5, // CrstModule 15, // CrstModuleFixup - 3, // CrstModuleLookupTable + 4, // CrstModuleLookupTable 0, // CrstMulticoreJitHash 13, // CrstMulticoreJitManager 0, // CrstNativeImageEagerFixups @@ -222,22 +225,22 @@ int g_rgCrstLevelMap[] = 0, // CrstNls 0, // CrstNotifyGdb 2, // CrstObjectList - 4, // CrstPEImage + 5, // CrstPEImage 19, // CrstPendingTypeLoadEntry - 3, // CrstPgoData + 4, // CrstPgoData 0, // CrstPinnedByrefValidation 0, // CrstProfilerGCRefDataFreeList 0, // CrstProfilingAPIStatus - 3, // CrstRCWCache + 4, // CrstRCWCache 0, // CrstRCWCleanupList 10, // CrstReadyToRunEntryPointToMethodDescMap 8, // CrstReflection 17, // CrstReJITGlobalRequest - 3, // CrstRetThunkCache + 4, // CrstRetThunkCache 3, // CrstSavedExceptionInfo 0, // CrstSaveModuleProfileData 0, // CrstSecurityStackwalkCache - 3, // CrstSigConvert + 4, // CrstSigConvert 5, // CrstSingleUseLock 0, // CrstSpecialStatics 0, // CrstStackSampler @@ -247,7 +250,7 @@ int g_rgCrstLevelMap[] = 4, // CrstStubUnwindInfoHeapSegments 3, // CrstSyncBlockCache 0, // CrstSyncHashLock - 4, // CrstSystemBaseDomain + 5, // CrstSystemBaseDomain 13, // CrstSystemDomain 0, // CrstSystemDomainDelayedUnloadList 0, // CrstThreadIdDispenser @@ -256,13 +259,14 @@ int g_rgCrstLevelMap[] = 13, // CrstThreadpoolWorker 12, // CrstThreadStore 8, // CrstTieredCompilation - 3, // CrstTypeEquivalenceMap + 4, // CrstTypeEquivalenceMap 10, // CrstTypeIDMap - 3, // CrstUMEntryThunkCache - 3, // CrstUniqueStack + 4, // CrstUMEntryThunkCache + 3, // CrstUMEntryThunkFreeListLock + 4, // CrstUniqueStack 7, // CrstUnresolvedClassLock 3, // CrstUnwindInfoTableLock - 3, // CrstVSDIndirectionCellLock + 4, // CrstVSDIndirectionCellLock 3, // CrstWrapperTemplate }; @@ -303,6 +307,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstEventPipe", "CrstEventStore", "CrstException", + "CrstExecutableAllocatorLock", "CrstExecuteManRangeLock", "CrstExternalObjectContextCache", "CrstFCall", @@ -383,6 +388,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstTypeEquivalenceMap", "CrstTypeIDMap", "CrstUMEntryThunkCache", + "CrstUMEntryThunkFreeListLock", "CrstUniqueStack", "CrstUnresolvedClassLock", "CrstUnwindInfoTableLock", diff --git a/src/coreclr/inc/executableallocator.h b/src/coreclr/inc/executableallocator.h index ce0c6c22f89..101178f9a4e 100644 --- a/src/coreclr/inc/executableallocator.h +++ b/src/coreclr/inc/executableallocator.h @@ -11,6 +11,191 @@ #include "utilcode.h" #include "ex.h" +#include "minipal.h" + +#ifndef DACCESS_COMPILE + +// This class is responsible for allocation of all the executable memory in the runtime. +class ExecutableAllocator +{ + // RX address range block descriptor + struct BlockRX + { + // Next block in a linked list + BlockRX* next; + // Base address of the block + void* baseRX; + // Size of the block + size_t size; + // Offset of the block in the shared memory + size_t offset; + }; + + // RW address range block descriptor + struct BlockRW + { + // Next block in a linked list + BlockRW* next; + // Base address of the RW mapping of the block + void* baseRW; + // Base address of the RX mapping of the block + void* baseRX; + // Size of the block + size_t size; + // Usage reference count of the RW block. RW blocks can be reused + // when multiple mappings overlap in the VA space at the same time + // (even from multiple threads) + size_t refCount; + }; + + typedef void (*FatalErrorHandler)(UINT errorCode, LPCWSTR pszMessage); + + // Instance of the allocator + static ExecutableAllocator* g_instance; + + // Callback to the runtime to report fatal errors + static FatalErrorHandler g_fatalErrorHandler; + +#if USE_UPPER_ADDRESS + // Preferred region to allocate the code in. + static BYTE* g_codeMinAddr; + static BYTE* g_codeMaxAddr; + static BYTE* g_codeAllocStart; + // Next address to try to allocate for code in the preferred region. + static BYTE* g_codeAllocHint; +#endif // USE_UPPER_ADDRESS + + // Caches the COMPlus_EnableWXORX setting + static bool g_isWXorXEnabled; + + // Head of the linked list of all RX blocks that were allocated by this allocator + BlockRX* m_pFirstBlockRX = NULL; + + // Head of the linked list of free RX blocks that were allocated by this allocator and then backed out + BlockRX* m_pFirstFreeBlockRX = NULL; + + // Head of the linked list of currently mapped RW blocks + BlockRW* m_pFirstBlockRW = NULL; + + // Handle of the double mapped memory mapper + void *m_doubleMemoryMapperHandle = NULL; + + // Maximum size of executable memory this allocator can allocate + size_t m_maxExecutableCodeSize; + + // First free offset in the underlying shared memory. It is not used + // for platforms that don't use shared memory. + size_t m_freeOffset = 0; + + // Last RW mapping cached so that it can be reused for the next mapping + // request if it goes into the same range. + BlockRW* m_cachedMapping = NULL; + + // Synchronization of the public allocator methods + CRITSEC_COOKIE m_CriticalSection; + + // Update currently cached mapping. If the passed in block is the same as the one + // in the cache, it keeps it cached. Otherwise it destroys the currently cached one + // and replaces it by the passed in one. + void UpdateCachedMapping(BlockRW *pBlock); + + // Find existing RW block that maps the whole specified range of RX memory. + // Return NULL if no such block exists. + void* FindRWBlock(void* baseRX, size_t size); + + // Add RW block to the list of existing RW blocks + bool AddRWBlock(void* baseRW, void* baseRX, size_t size); + + // Remove RW block from the list of existing RW blocks and return the base + // address and size the underlying memory was mapped at. + // Return false if no existing RW block contains the passed in address. + bool RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize); + + // Find a free block with the closest size >= the requested size. + // Returns NULL if no such block exists. + BlockRX* FindBestFreeBlock(size_t size); + + // Return memory mapping granularity. + static size_t Granularity(); + + // Allocate a block of executable memory of the specified size. + // It doesn't acquire the actual virtual memory, just the + // range of the underlying shared memory. + BlockRX* AllocateBlock(size_t size, bool* pIsFreeBlock); + + // Backout the block allocated by AllocateBlock in case of an + // error. + void BackoutBlock(BlockRX* pBlock, bool isFreeBlock); + + // Allocate range of offsets in the underlying shared memory + bool AllocateOffset(size_t* pOffset, size_t size); + + // Add RX block to the linked list of existing blocks + void AddRXBlock(BlockRX *pBlock); + + // Return true if double mapping is enabled. + static bool IsDoubleMappingEnabled(); + + // Initialize the allocator instance + bool Initialize(); + +public: + + // Return the ExecuteAllocator singleton instance + static ExecutableAllocator* Instance(); + + // Initialize the static members of the Executable allocator and allocate + // and initialize the instance of it. + static HRESULT StaticInitialize(FatalErrorHandler fatalErrorHandler); + + // Destroy the allocator + ~ExecutableAllocator(); + + // Return true if W^X is enabled + static bool IsWXORXEnabled(); + + // Use this function to initialize the g_codeAllocHint + // during startup. base is runtime .dll base address, + // size is runtime .dll virtual size. + static void InitCodeAllocHint(size_t base, size_t size, int randomPageOffset); + + // Use this function to reset the g_codeAllocHint + // after unloading an AppDomain + static void ResetCodeAllocHint(); + + // Returns TRUE if p is located in near clr.dll that allows us + // to use rel32 IP-relative addressing modes. + static bool IsPreferredExecutableRange(void* p); + + // Reserve the specified amount of virtual address space for executable mapping. + void* Reserve(size_t size); + + // Reserve the specified amount of virtual address space for executable mapping. + // The reserved range must be within the loAddress and hiAddress. If it is not + // possible to reserve memory in such range, the method returns NULL. + void* ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress); + + // Reserve the specified amount of virtual address space for executable mapping + // exactly at the given address. + void* ReserveAt(void* baseAddressRX, size_t size); + + // Commit the specified range of memory. The memory can be committed as executable (RX) + // or non-executable (RW) based on the passed in isExecutable flag. The non-executable + // allocations are used to allocate data structures that need to be close to the + // executable code due to memory addressing performance related reasons. + void* Commit(void* pStart, size_t size, bool isExecutable); + + // Release the executable memory block starting at the passed in address that was allocated + // by one of the ReserveXXX methods. + void Release(void* pRX); + + // Map the specified block of executable memory as RW + void* MapRW(void* pRX, size_t size); + + // Unmap the RW mapping at the specified address + void UnmapRW(void* pRW); +}; + // Holder class to map read-execute memory as read-write so that it can be modified without using read-write-execute mapping. // At the moment the implementation is dummy, returning the same addresses for both cases and expecting them to be read-write-execute. // The class uses the move semantics to ensure proper unmapping in case of re-assigning of the holder value. @@ -30,13 +215,17 @@ class ExecutableWriterHolder void Unmap() { +#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) if (m_addressRX != NULL) { - // TODO: mapping / unmapping for targets using double memory mapping will be added with the double mapped allocator addition -#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) PAL_JitWriteProtect(false); -#endif } +#else + if (m_addressRX != m_addressRW) + { + ExecutableAllocator::Instance()->UnmapRW((void*)m_addressRW); + } +#endif } public: @@ -62,9 +251,11 @@ public: ExecutableWriterHolder(T* addressRX, size_t size) { m_addressRX = addressRX; +#if defined(HOST_OSX) && defined(HOST_ARM64) m_addressRW = addressRX; -#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE) PAL_JitWriteProtect(true); +#else + m_addressRW = (T *)ExecutableAllocator::Instance()->MapRW((void*)addressRX, size); #endif } @@ -79,3 +270,5 @@ public: return m_addressRW; } }; + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index fb65ea9fa61..3c42f085085 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -302,12 +302,12 @@ #endif // !FEATURE_EH_FUNCLETS #ifdef TARGET_X86 - JITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB) - JITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EAX, JIT_CheckedWriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EBX, JIT_CheckedWriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index a47034ee2e0..77df9dfa94d 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -1014,35 +1014,6 @@ void SplitPath(__in SString const &path, #define CLRGetTickCount64() GetTickCount64() -// -// Use this function to initialize the s_CodeAllocHint -// during startup. base is runtime .dll base address, -// size is runtime .dll virtual size. -// -void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset); - - -// -// Use this function to reset the s_CodeAllocHint -// after unloading an AppDomain -// -void ResetCodeAllocHint(); - -// -// Returns TRUE if p is located in near clr.dll that allows us -// to use rel32 IP-relative addressing modes. -// -BOOL IsPreferredExecutableRange(void * p); - -// -// Allocate free memory that will be used for executable code -// Handles the special requirements that we have on 64-bit platforms -// where we want the executable memory to be located near mscorwks -// -BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize, - DWORD flAllocationType, - DWORD flProtect); - // // Allocate free memory within the range [pMinAddr..pMaxAddr] using // ClrVirtualQuery to find free memory and ClrVirtualAlloc to allocate it. diff --git a/src/coreclr/minipal/CMakeLists.txt b/src/coreclr/minipal/CMakeLists.txt new file mode 100644 index 00000000000..3096237d2a2 --- /dev/null +++ b/src/coreclr/minipal/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(.) +if (CLR_CMAKE_HOST_UNIX) + add_subdirectory(Unix) +else (CLR_CMAKE_HOST_UNIX) + add_subdirectory(Windows) +endif (CLR_CMAKE_HOST_UNIX) + diff --git a/src/coreclr/minipal/Unix/CMakeLists.txt b/src/coreclr/minipal/Unix/CMakeLists.txt new file mode 100644 index 00000000000..b56b5017d37 --- /dev/null +++ b/src/coreclr/minipal/Unix/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(coreclrminipal + STATIC + doublemapping.cpp +) diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp new file mode 100644 index 00000000000..a50b326861a --- /dev/null +++ b/src/coreclr/minipal/Unix/doublemapping.cpp @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef TARGET_LINUX +#include +#include // __NR_memfd_create +#endif // TARGET_LINUX +#include "minipal.h" + +#if defined(TARGET_OSX) && defined(TARGET_AMD64) +#include +#endif // TARGET_OSX && TARGET_AMD64 + +#ifndef TARGET_OSX + +#ifdef TARGET_64BIT +static const off_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024; +#else +static const off_t MaxDoubleMappedSize = UINT_MAX; +#endif + +#ifdef TARGET_LINUX +#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__) +#endif // TARGET_LINUX + +#endif // TARGET_OSX + +bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize) +{ +#ifndef TARGET_OSX + +#ifdef TARGET_FREEBSD + int fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, S_IRWXU); +#else // TARGET_FREEBSD + int fd = memfd_create("doublemapper", MFD_CLOEXEC); +#endif // TARGET_FREEBSD + + if (fd == -1) + { + return false; + } + + if (ftruncate(fd, MaxDoubleMappedSize) == -1) + { + close(fd); + return false; + } + + *pMaxExecutableCodeSize = MaxDoubleMappedSize; + *pHandle = (void*)(size_t)fd; +#else // !TARGET_OSX + *pMaxExecutableCodeSize = SIZE_MAX; + *pHandle = NULL; +#endif // !TARGET_OSX + + return true; +} + +void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle) +{ +#ifndef TARGET_OSX + close((int)(size_t)mapperHandle); +#endif +} + +extern "C" void* PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(const void* lpBeginAddress, const void* lpEndAddress, size_t dwSize); + +#ifdef TARGET_OSX +bool IsMapJitFlagNeeded() +{ + static volatile int isMapJitFlagNeeded = -1; + + if (isMapJitFlagNeeded == -1) + { + int mapJitFlagCheckResult = 0; + int pageSize = sysconf(_SC_PAGE_SIZE); + // Try to map a page with read-write-execute protection. It should fail on Mojave hardened runtime and higher. + void* testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (testPage == MAP_FAILED && (errno == EACCES)) + { + // The mapping has failed with EACCES, check if making the same mapping with MAP_JIT flag works + testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_JIT, -1, 0); + if (testPage != MAP_FAILED) + { + mapJitFlagCheckResult = 1; + } + } + + if (testPage != MAP_FAILED) + { + munmap(testPage, pageSize); + } + + isMapJitFlagNeeded = mapJitFlagCheckResult; + } + + return (bool)isMapJitFlagNeeded; +} +#endif // TARGET_OSX + +void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd) +{ + int fd = (int)(size_t)mapperHandle; + + if (rangeStart != NULL || rangeEnd != NULL) + { + void* result = PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(rangeStart, rangeEnd, size); +#ifndef TARGET_OSX + if (result != NULL) + { + // Map the shared memory over the range reserved from the executable memory allocator. + result = mmap(result, size, PROT_NONE, MAP_SHARED | MAP_FIXED, fd, offset); + if (result == MAP_FAILED) + { + assert(false); + result = NULL; + } + } +#endif // TARGET_OSX + + return result; + } + +#ifndef TARGET_OSX + void* result = mmap(NULL, size, PROT_NONE, MAP_SHARED, fd, offset); +#else + int mmapFlags = MAP_ANON | MAP_PRIVATE; + if (IsMapJitFlagNeeded()) + { + mmapFlags |= MAP_JIT; + } + void* result = mmap(NULL, size, PROT_NONE, mmapFlags, -1, 0); +#endif + if (result == MAP_FAILED) + { + assert(false); + result = NULL; + } + return result; +} + +void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable) +{ + if (mprotect(pStart, size, isExecutable ? (PROT_READ | PROT_EXEC) : (PROT_READ | PROT_WRITE)) == -1) + { + return NULL; + } + + return pStart; +} + +bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ +#ifndef TARGET_OSX + int fd = (int)(size_t)mapperHandle; + mmap(pStart, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset); + memset(pStart, 0, size); +#endif // TARGET_OSX + return munmap(pStart, size) != -1; +} + +void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ +#ifndef TARGET_OSX + int fd = (int)(size_t)mapperHandle; + return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); +#else // TARGET_OSX +#ifdef TARGET_AMD64 + vm_address_t startRW; + vm_prot_t curProtection, maxProtection; + kern_return_t kr = vm_remap(mach_task_self(), &startRW, size, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR, + mach_task_self(), (vm_address_t)pStart, FALSE, &curProtection, &maxProtection, VM_INHERIT_NONE); + + if (kr != KERN_SUCCESS) + { + return NULL; + } + + int st = mprotect((void*)startRW, size, PROT_READ | PROT_WRITE); + if (st == -1) + { + munmap((void*)startRW, size); + return NULL; + } + + return (void*)startRW; +#else // TARGET_AMD64 + // This method should not be called on OSX ARM64 + assert(false); + return NULL; +#endif // TARGET_AMD64 +#endif // TARGET_OSX +} + +bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size) +{ + return munmap(pStart, size) != -1; +} diff --git a/src/coreclr/minipal/Windows/CMakeLists.txt b/src/coreclr/minipal/Windows/CMakeLists.txt new file mode 100644 index 00000000000..b56b5017d37 --- /dev/null +++ b/src/coreclr/minipal/Windows/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(coreclrminipal + STATIC + doublemapping.cpp +) diff --git a/src/coreclr/minipal/Windows/doublemapping.cpp b/src/coreclr/minipal/Windows/doublemapping.cpp new file mode 100644 index 00000000000..e265f1d139a --- /dev/null +++ b/src/coreclr/minipal/Windows/doublemapping.cpp @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include +#include "minipal.h" + +#define HIDWORD(_qw) ((ULONG)((_qw) >> 32)) +#define LODWORD(_qw) ((ULONG)(_qw)) + +#ifdef TARGET_64BIT +static const uint64_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024; +#else +static const uint64_t MaxDoubleMappedSize = UINT_MAX; +#endif + +#define VIRTUAL_ALLOC_RESERVE_GRANULARITY (64*1024) // 0x10000 (64 KB) +inline size_t ALIGN_UP( size_t val, size_t alignment ) +{ + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + assert( 0 == (alignment & (alignment - 1)) ); + size_t result = (val + (alignment - 1)) & ~(alignment - 1); + assert( result >= val ); // check for overflow + return result; +} + +template inline T ALIGN_UP(T val, size_t alignment) +{ + return (T)ALIGN_UP((size_t)val, alignment); +} + +inline void *GetTopMemoryAddress(void) +{ + static void *result; // = NULL; + if( NULL == result ) + { + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + result = sysInfo.lpMaximumApplicationAddress; + } + return result; +} + +inline void *GetBotMemoryAddress(void) +{ + static void *result; // = NULL; + if( NULL == result ) + { + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + result = sysInfo.lpMinimumApplicationAddress; + } + return result; +} + +#define TOP_MEMORY (GetTopMemoryAddress()) +#define BOT_MEMORY (GetBotMemoryAddress()) + +bool VMToOSInterface::CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize) +{ + *pMaxExecutableCodeSize = (size_t)MaxDoubleMappedSize; + *pHandle = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_EXECUTE_READWRITE | SEC_RESERVE, // read/write/execute access + HIDWORD(MaxDoubleMappedSize), // maximum object size (high-order DWORD) + LODWORD(MaxDoubleMappedSize), // maximum object size (low-order DWORD) + NULL); + + return *pHandle != NULL; +} + +void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle) +{ + CloseHandle((HANDLE)mapperHandle); +} + +void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *pMinAddr, const void* pMaxAddr) +{ + BYTE *pResult = nullptr; // our return value; + + if (size == 0) + { + return nullptr; + } + + // + // First lets normalize the pMinAddr and pMaxAddr values + // + // If pMinAddr is NULL then set it to BOT_MEMORY + if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY)) + { + pMinAddr = (BYTE *) BOT_MEMORY; + } + + // If pMaxAddr is NULL then set it to TOP_MEMORY + if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY)) + { + pMaxAddr = (BYTE *) TOP_MEMORY; + } + + // If pMaxAddr is not greater than pMinAddr we can not make an allocation + if (pMaxAddr <= pMinAddr) + { + return nullptr; + } + + // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY + // then we can call ClrVirtualAlloc instead + if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY)) + { + return (BYTE*)MapViewOfFile((HANDLE)mapperHandle, + FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size); + } + + // We will do one scan from [pMinAddr .. pMaxAddr] + // First align the tryAddr up to next 64k base address. + // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons. + // + BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY); + bool virtualQueryFailed = false; + bool faultInjected = false; + unsigned virtualQueryCount = 0; + + // Now scan memory and try to find a free block of the size requested. + while ((tryAddr + size) <= (BYTE *) pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbInfo; + + // Use VirtualQuery to find out if this address is MEM_FREE + // + virtualQueryCount++; + if (!VirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo))) + { + // Exit and return nullptr if the VirtualQuery call fails. + virtualQueryFailed = true; + break; + } + + // Is there enough memory free from this start location? + // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0 + if ((mbInfo.State == MEM_FREE) && + (mbInfo.RegionSize >= (SIZE_T) size || mbInfo.RegionSize == 0)) + { + // Try reserving the memory using VirtualAlloc now + pResult = (BYTE*)MapViewOfFileEx((HANDLE)mapperHandle, + FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size, + tryAddr); + + // Normally this will be successful + // + if (pResult != nullptr) + { + // return pResult + break; + } + + // We might fail in a race. So just move on to next region and continue trying + tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY; + } + else + { + // Try another section of memory + tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY, + (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize); + } + } + + return pResult; +} + +void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable) +{ + return VirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READ : PAGE_READWRITE); +} + +bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ + // Zero the memory before the unmapping + VirtualAlloc(pStart, size, MEM_COMMIT, PAGE_READWRITE); + memset(pStart, 0, size); + return UnmapViewOfFile(pStart); +} + +void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size) +{ + return (BYTE*)MapViewOfFile((HANDLE)mapperHandle, + FILE_MAP_READ | FILE_MAP_WRITE, + HIDWORD((int64_t)offset), + LODWORD((int64_t)offset), + size); +} + +bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size) +{ + return UnmapViewOfFile(pStart); +} diff --git a/src/coreclr/minipal/minipal.h b/src/coreclr/minipal/minipal.h new file mode 100644 index 00000000000..39098f9bc12 --- /dev/null +++ b/src/coreclr/minipal/minipal.h @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +#include + +// Interface between the runtime and platform specific functionality +class VMToOSInterface +{ +private: + ~VMToOSInterface() {} +public: + // Create double mapped memory mapper + // Parameters: + // pHandle - receives handle of the double mapped memory mapper + // pMaxExecutableCodeSize - receives the maximum executable memory size it can map + // Return: + // true if it succeeded, false if it failed + static bool CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize); + + // Destroy the double mapped memory mapper represented by the passed in handle + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to destroy + static void DestroyDoubleMemoryMapper(void *mapperHandle); + + // Reserve a block of memory that can be double mapped. + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // offset - offset in the underlying shared memory + // size - size of the block to reserve + // rangeStart + // rangeEnd - Requests reserving virtual memory in the specified range. + // Setting both rangeStart and rangeEnd to 0 means that the + // requested range is not limited. + // When a specific range is requested, it is obligatory. + // Return: + // starting virtual address of the reserved memory or NULL if it failed + static void* ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd); + + // Commit a block of memory in the range previously reserved by the ReserveDoubleMappedMemory + // Parameters: + // pStart - start address of the virtual address range to commit + // size - size of the memory block to commit + // isExecutable - true means that the mapping should be RX, false means RW + // Return: + // Committed range start + static void* CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable); + + // Release a block of virtual memory previously commited by the CommitDoubleMappedMemory + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // pStart - start address of the virtual address range to release. It must be one + // that was previously returned by the CommitDoubleMappedMemory + // offset - offset in the underlying shared memory + // size - size of the memory block to release + // Return: + // true if it succeeded, false if it failed + static bool ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size); + + // Get a RW mapping for the RX block specified by the arguments + // Parameters: + // mapperHandle - handle of the double mapped memory mapper to use + // pStart - start address of the RX virtual address range. + // offset - offset in the underlying shared memory + // size - size of the memory block to map as RW + // Return: + // Starting virtual address of the RW mapping. + static void* GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size); + + // Release RW mapping of the block specified by the arguments + // Parameters: + // pStart - Start address of the RW virtual address range. It must be an address + // previously returned by the GetRWMapping. + // size - Size of the memory block to release. It must be the size previously + // passed to the GetRWMapping that returned the pStart. + // Return: + // true if it succeeded, false if it failed + static bool ReleaseRWMapping(void* pStart, size_t size); +}; diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index 1ae433adbfd..8c57742cb63 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -69,6 +69,7 @@ endif(CLR_CMAKE_TARGET_WIN32) set(UTILCODE_SOURCES ${UTILCODE_COMMON_SOURCES} + executableallocator.cpp ) set(UTILCODE_DAC_SOURCES diff --git a/src/coreclr/utilcode/executableallocator.cpp b/src/coreclr/utilcode/executableallocator.cpp new file mode 100644 index 00000000000..ac4c326c837 --- /dev/null +++ b/src/coreclr/utilcode/executableallocator.cpp @@ -0,0 +1,755 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pedecoder.h" +#include "executableallocator.h" + +#if USE_UPPER_ADDRESS +// Preferred region to allocate the code in. +BYTE * ExecutableAllocator::g_codeMinAddr; +BYTE * ExecutableAllocator::g_codeMaxAddr; +BYTE * ExecutableAllocator::g_codeAllocStart; +// Next address to try to allocate for code in the preferred region. +BYTE * ExecutableAllocator::g_codeAllocHint; +#endif // USE_UPPER_ADDRESS + +bool ExecutableAllocator::g_isWXorXEnabled = false; + +ExecutableAllocator::FatalErrorHandler ExecutableAllocator::g_fatalErrorHandler = NULL; + +ExecutableAllocator* ExecutableAllocator::g_instance = NULL; + +bool ExecutableAllocator::IsDoubleMappingEnabled() +{ + LIMITED_METHOD_CONTRACT; + +#if defined(HOST_OSX) && defined(HOST_ARM64) + return false; +#else + return g_isWXorXEnabled; +#endif +} + +bool ExecutableAllocator::IsWXORXEnabled() +{ + LIMITED_METHOD_CONTRACT; + +#if defined(HOST_OSX) && defined(HOST_ARM64) + return true; +#else + return g_isWXorXEnabled; +#endif +} + +extern SYSTEM_INFO g_SystemInfo; + +size_t ExecutableAllocator::Granularity() +{ + LIMITED_METHOD_CONTRACT; + + return g_SystemInfo.dwAllocationGranularity; +} + +// Use this function to initialize the g_codeAllocHint +// during startup. base is runtime .dll base address, +// size is runtime .dll virtual size. +void ExecutableAllocator::InitCodeAllocHint(size_t base, size_t size, int randomPageOffset) +{ +#if USE_UPPER_ADDRESS + +#ifdef _DEBUG + // If GetForceRelocs is enabled we don't constrain the pMinAddr + if (PEDecoder::GetForceRelocs()) + return; +#endif + + // + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any code heap that doesn't specify an address + // range using [pMinAddr..pMaxAddr] we place it in the + // upper address space + // This enables us to avoid having to use long JumpStubs + // to reach the code for our ngen-ed images. + // Which are also placed in the UPPER_ADDRESS space. + // + SIZE_T reach = 0x7FFF0000u; + + // We will choose the preferred code region based on the address of clr.dll. The JIT helpers + // in clr.dll are the most heavily called functions. + g_codeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0; + g_codeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1; + + BYTE * pStart; + + if (g_codeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS && + (BYTE *)CODEHEAP_START_ADDRESS < g_codeMaxAddr) + { + // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista) + // Use the code head start address that does not cause collisions with NGen images. + // This logic is coupled with scripts that we use to assign base addresses. + pStart = (BYTE *)CODEHEAP_START_ADDRESS; + } + else + if (base > UINT32_MAX) + { + // clr.dll got address assigned by ASLR? + // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned + // addresses. Do not start at g_codeMinAddr exactly so that we can also reach common native images + // that can be placed at higher addresses than clr.dll. + pStart = g_codeMinAddr + (g_codeMaxAddr - g_codeMinAddr) / 8; + } + else + { + // clr.dll missed the base address? + // Try to occupy the space right after it. + pStart = (BYTE *)(base + size); + } + + // Randomize the address space + pStart += GetOsPageSize() * randomPageOffset; + + g_codeAllocStart = pStart; + g_codeAllocHint = pStart; +#endif +} + +// Use this function to reset the g_codeAllocHint +// after unloading an AppDomain +void ExecutableAllocator::ResetCodeAllocHint() +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + g_codeAllocHint = g_codeAllocStart; +#endif +} + +// Returns TRUE if p is located in near clr.dll that allows us +// to use rel32 IP-relative addressing modes. +bool ExecutableAllocator::IsPreferredExecutableRange(void * p) +{ + LIMITED_METHOD_CONTRACT; +#if USE_UPPER_ADDRESS + if (g_codeMinAddr <= (BYTE *)p && (BYTE *)p < g_codeMaxAddr) + return true; +#endif + return false; +} + +ExecutableAllocator* ExecutableAllocator::Instance() +{ + LIMITED_METHOD_CONTRACT; + return g_instance; +} + +ExecutableAllocator::~ExecutableAllocator() +{ + if (IsDoubleMappingEnabled()) + { + VMToOSInterface::DestroyDoubleMemoryMapper(m_doubleMemoryMapperHandle); + } +} + +HRESULT ExecutableAllocator::StaticInitialize(FatalErrorHandler fatalErrorHandler) +{ + LIMITED_METHOD_CONTRACT; + + g_fatalErrorHandler = fatalErrorHandler; + g_isWXorXEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableWriteXorExecute) != 0; + g_instance = new (nothrow) ExecutableAllocator(); + if (g_instance == NULL) + { + return E_OUTOFMEMORY; + } + + if (!g_instance->Initialize()) + { + return E_FAIL; + } + + return S_OK; +} + +bool ExecutableAllocator::Initialize() +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + if (!VMToOSInterface::CreateDoubleMemoryMapper(&m_doubleMemoryMapperHandle, &m_maxExecutableCodeSize)) + { + return false; + } + + m_CriticalSection = ClrCreateCriticalSection(CrstExecutableAllocatorLock,CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); + } + + return true; +} + +//#define ENABLE_CACHED_MAPPINGS + +void ExecutableAllocator::UpdateCachedMapping(BlockRW* pBlock) +{ + LIMITED_METHOD_CONTRACT; +#ifdef ENABLE_CACHED_MAPPINGS + if (m_cachedMapping == NULL) + { + m_cachedMapping = pBlock; + pBlock->refCount++; + } + else if (m_cachedMapping != pBlock) + { + void* unmapAddress = NULL; + size_t unmapSize; + + if (!RemoveRWBlock(m_cachedMapping->baseRW, &unmapAddress, &unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found")); + } + if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed")); + } + m_cachedMapping = pBlock; + pBlock->refCount++; + } +#endif // ENABLE_CACHED_MAPPINGS +} + +void* ExecutableAllocator::FindRWBlock(void* baseRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next) + { + if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + pBlock->refCount++; + UpdateCachedMapping(pBlock); + + return (BYTE*)pBlock->baseRW + ((size_t)baseRX - (size_t)pBlock->baseRX); + } + } + + return NULL; +} + +bool ExecutableAllocator::AddRWBlock(void* baseRW, void* baseRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next) + { + if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + break; + } + } + + // The new "nothrow" below failure is handled as fail fast since it is not recoverable + PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure); + + BlockRW* pBlockRW = new (nothrow) BlockRW(); + if (pBlockRW == NULL) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block metadata cannot be allocated")); + return false; + } + + pBlockRW->baseRW = baseRW; + pBlockRW->baseRX = baseRX; + pBlockRW->size = size; + pBlockRW->next = m_pFirstBlockRW; + pBlockRW->refCount = 1; + m_pFirstBlockRW = pBlockRW; + + UpdateCachedMapping(pBlockRW); + + return true; +} + +bool ExecutableAllocator::RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize) +{ + LIMITED_METHOD_CONTRACT; + + BlockRW* pPrevBlockRW = NULL; + for (BlockRW* pBlockRW = m_pFirstBlockRW; pBlockRW != NULL; pBlockRW = pBlockRW->next) + { + if (pBlockRW->baseRW <= pRW && (size_t)pRW < ((size_t)pBlockRW->baseRW + pBlockRW->size)) + { + // found + pBlockRW->refCount--; + if (pBlockRW->refCount != 0) + { + *pUnmapAddress = NULL; + return true; + } + + if (pPrevBlockRW == NULL) + { + m_pFirstBlockRW = pBlockRW->next; + } + else + { + pPrevBlockRW->next = pBlockRW->next; + } + + *pUnmapAddress = pBlockRW->baseRW; + *pUnmapSize = pBlockRW->size; + + delete pBlockRW; + return true; + } + + pPrevBlockRW = pBlockRW; + } + + return false; +} + +bool ExecutableAllocator::AllocateOffset(size_t* pOffset, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + size_t offset = m_freeOffset; + size_t newFreeOffset = offset + size; + + if (newFreeOffset > m_maxExecutableCodeSize) + { + return false; + } + + m_freeOffset = newFreeOffset; + + *pOffset = offset; + + return true; +} + +void ExecutableAllocator::AddRXBlock(BlockRX* pBlock) +{ + LIMITED_METHOD_CONTRACT; + + pBlock->next = m_pFirstBlockRX; + m_pFirstBlockRX = pBlock; +} + +void* ExecutableAllocator::Commit(void* pStart, size_t size, bool isExecutable) +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + return VMToOSInterface::CommitDoubleMappedMemory(pStart, size, isExecutable); + } + else + { + return ClrVirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + } +} + +void ExecutableAllocator::Release(void* pRX) +{ + LIMITED_METHOD_CONTRACT; + + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + // Locate the RX block corresponding to the pRX and remove it from the linked list + BlockRX* pBlock; + BlockRX* pPrevBlock = NULL; + + for (pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next) + { + if (pRX == pBlock->baseRX) + { + if (pPrevBlock == NULL) + { + m_pFirstBlockRX = pBlock->next; + } + else + { + pPrevBlock->next = pBlock->next; + } + + break; + } + pPrevBlock = pBlock; + } + + if (pBlock != NULL) + { + VMToOSInterface::ReleaseDoubleMappedMemory(m_doubleMemoryMapperHandle, pRX, pBlock->offset, pBlock->size); + // Put the released block into the free block list + pBlock->baseRX = NULL; + pBlock->next = m_pFirstFreeBlockRX; + m_pFirstFreeBlockRX = pBlock; + } + else + { + // The block was not found, which should never happen. + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to release was not found")); + } + } + else + { + ClrVirtualFree(pRX, 0, MEM_RELEASE); + } +} + +// Find a free block with the closest size >= the requested size. +// Returns NULL if no such block exists. +ExecutableAllocator::BlockRX* ExecutableAllocator::FindBestFreeBlock(size_t size) +{ + LIMITED_METHOD_CONTRACT; + + BlockRX* pPrevBlock = NULL; + BlockRX* pPrevBestBlock = NULL; + BlockRX* pBestBlock = NULL; + BlockRX* pBlock = m_pFirstFreeBlockRX; + + while (pBlock != NULL) + { + if (pBlock->size >= size) + { + if (pBestBlock != NULL) + { + if (pBlock->size < pBestBlock->size) + { + pPrevBestBlock = pPrevBlock; + pBestBlock = pBlock; + } + } + else + { + pPrevBestBlock = pPrevBlock; + pBestBlock = pBlock; + } + } + pPrevBlock = pBlock; + pBlock = pBlock->next; + } + + if (pBestBlock != NULL) + { + if (pPrevBestBlock != NULL) + { + pPrevBestBlock->next = pBestBlock->next; + } + else + { + m_pFirstFreeBlockRX = pBestBlock->next; + } + + pBestBlock->next = NULL; + } + + return pBestBlock; +} + +// Allocate a new block of executable memory and the related descriptor structure. +// First try to get it from the free blocks and if there is no suitable free block, +// allocate a new one. +ExecutableAllocator::BlockRX* ExecutableAllocator::AllocateBlock(size_t size, bool* pIsFreeBlock) +{ + LIMITED_METHOD_CONTRACT; + + size_t offset; + BlockRX* block = FindBestFreeBlock(size); + *pIsFreeBlock = (block != NULL); + + if (block == NULL) + { + if (!AllocateOffset(&offset, size)) + { + return NULL; + } + + block = new (nothrow) BlockRX(); + if (block == NULL) + { + return NULL; + } + + block->offset = offset; + block->size = size; + } + + return block; +} + +// Backout a previously allocated block. The block is added to the free blocks list and +// reused for later allocation requests. +void ExecutableAllocator::BackoutBlock(BlockRX* pBlock, bool isFreeBlock) +{ + LIMITED_METHOD_CONTRACT; + + if (!isFreeBlock) + { + m_freeOffset -= pBlock->size; + delete pBlock; + } + else + { + pBlock->next = m_pFirstFreeBlockRX; + m_pFirstFreeBlockRX = pBlock; + } +} + +// Reserve executable memory within the specified virtual address space range. If it is not possible to +// reserve memory in that range, the method returns NULL and nothing is allocated. +void* ExecutableAllocator::ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + void *result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, loAddress, hiAddress); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + + return result; + } + else + { + DWORD allocationType = MEM_RESERVE; +#ifdef HOST_UNIX + // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. + // This will allow us to place JIT'ed code close to the coreclr library + // and thus improve performance by avoiding jump stubs in managed code. + allocationType |= MEM_RESERVE_EXECUTABLE; +#endif + return ClrVirtualAllocWithinRange((const BYTE*)loAddress, (const BYTE*)hiAddress, size, allocationType, PAGE_NOACCESS); + } +} + +// Reserve executable memory. On Windows it tries to use the allocation hints to +// allocate memory close to the previously allocated executable memory and loaded +// executable files. +void* ExecutableAllocator::Reserve(size_t size) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + + BYTE *result = NULL; + +#if USE_UPPER_ADDRESS + // + // If we are using the UPPER_ADDRESS space (on Win64) + // then for any heap that will contain executable code + // we will place it in the upper address space + // + // This enables us to avoid having to use JumpStubs + // to reach the code for our ngen-ed images on x64, + // since they are also placed in the UPPER_ADDRESS space. + // + BYTE * pHint = g_codeAllocHint; + + if (size <= (SIZE_T)(g_codeMaxAddr - g_codeMinAddr) && pHint != NULL) + { + // Try to allocate in the preferred region after the hint + result = (BYTE*)ReserveWithinRange(size, pHint, g_codeMaxAddr); + if (result != NULL) + { + g_codeAllocHint = result + size; + } + else + { + // Try to allocate in the preferred region before the hint + result = (BYTE*)ReserveWithinRange(size, g_codeMinAddr, pHint + size); + + if (result != NULL) + { + g_codeAllocHint = result + size; + } + + g_codeAllocHint = NULL; + } + } + + // Fall through to +#endif // USE_UPPER_ADDRESS + + if (result == NULL) + { + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + result = (BYTE*)VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, 0, 0); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + } + else + { + DWORD allocationType = MEM_RESERVE; +#ifdef HOST_UNIX + // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. + // This will allow us to place JIT'ed code close to the coreclr library + // and thus improve performance by avoiding jump stubs in managed code. + allocationType |= MEM_RESERVE_EXECUTABLE; +#endif + result = (BYTE*)ClrVirtualAlloc(NULL, size, allocationType, PAGE_NOACCESS); + } + } + + return result; +} + +// Reserve a block of executable memory at the specified virtual address. If it is not +// possible, the method returns NULL. +void* ExecutableAllocator::ReserveAt(void* baseAddressRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((size & (Granularity() - 1)) == 0); + + if (IsDoubleMappingEnabled()) + { + CRITSEC_Holder csh(m_CriticalSection); + + bool isFreeBlock; + BlockRX* block = AllocateBlock(size, &isFreeBlock); + if (block == NULL) + { + return NULL; + } + + void* result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, baseAddressRX, baseAddressRX); + + if (result != NULL) + { + block->baseRX = result; + AddRXBlock(block); + } + else + { + BackoutBlock(block, isFreeBlock); + } + + return result; + } + else + { + return VirtualAlloc(baseAddressRX, size, MEM_RESERVE, PAGE_NOACCESS); + } +} + +// Map an executable memory block as writeable. If there is already a mapping +// covering the specified block, return that mapping instead of creating a new one. +// Return starting address of the writeable mapping. +void* ExecutableAllocator::MapRW(void* pRX, size_t size) +{ + LIMITED_METHOD_CONTRACT; + + if (!IsDoubleMappingEnabled()) + { + return pRX; + } + + CRITSEC_Holder csh(m_CriticalSection); + + void* result = FindRWBlock(pRX, size); + if (result != NULL) + { + return result; + } + + for (BlockRX* pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next) + { + if (pRX >= pBlock->baseRX && ((size_t)pRX + size) <= ((size_t)pBlock->baseRX + pBlock->size)) + { + // Offset of the RX address in the originally allocated block + size_t offset = (size_t)pRX - (size_t)pBlock->baseRX; + // Offset of the RX address that will start the newly mapped block + size_t mapOffset = ALIGN_DOWN(offset, Granularity()); + // Size of the block we will map + size_t mapSize = ALIGN_UP(offset - mapOffset + size, Granularity()); + void* pRW = VMToOSInterface::GetRWMapping(m_doubleMemoryMapperHandle, (BYTE*)pBlock->baseRX + mapOffset, pBlock->offset + mapOffset, mapSize); + + if (pRW == NULL) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Failed to create RW mapping for RX memory")); + } + + AddRWBlock(pRW, (BYTE*)pBlock->baseRX + mapOffset, mapSize); + + return (void*)((size_t)pRW + (offset - mapOffset)); + } + else if (pRX >= pBlock->baseRX && pRX < (void*)((size_t)pBlock->baseRX + pBlock->size)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to RW map a block that crosses the end of the allocated RX range")); + } + else if (pRX < pBlock->baseRX && (void*)((size_t)pRX + size) > pBlock->baseRX) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to map a block that crosses the beginning of the allocated range")); + } + } + + // The executable memory block was not found, so we cannot provide the writeable mapping. + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to map as RW was not found")); + return NULL; +} + +// Unmap writeable mapping at the specified address. The address must be an address +// returned by the MapRW method. +void ExecutableAllocator::UnmapRW(void* pRW) +{ + LIMITED_METHOD_CONTRACT; + + if (!IsDoubleMappingEnabled()) + { + return; + } + + CRITSEC_Holder csh(m_CriticalSection); + _ASSERTE(pRW != NULL); + + void* unmapAddress = NULL; + size_t unmapSize; + + if (!RemoveRWBlock(pRW, &unmapAddress, &unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found")); + } + + if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize)) + { + g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed")); + } +} diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp index adaf07d8f58..b3b381b2f9b 100644 --- a/src/coreclr/utilcode/loaderheap.cpp +++ b/src/coreclr/utilcode/loaderheap.cpp @@ -695,15 +695,21 @@ size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap); struct LoaderHeapFreeBlock { public: - LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list - size_t m_dwSize; // Total size of this block (including this header) -//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations. + LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list + size_t m_dwSize; // Total size of this block + void *m_pBlockAddress; // Virtual address of the block +#ifndef DACCESS_COMPILE static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; + // The new "nothrow" below failure is handled in a non-fault way, so + // make sure that callers with FORBID_FAULT can call this method without + // firing the contract violation assert. + PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure); + LOADER_HEAP_BEGIN_TRAP_FAULT // It's illegal to insert a free block that's smaller than the minimum sized allocation - @@ -722,19 +728,30 @@ struct LoaderHeapFreeBlock } #endif - INDEBUG(memset(pMem, 0xcc, dwTotalSize);) - LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem; - pNewBlock->m_pNext = *ppHead; - pNewBlock->m_dwSize = dwTotalSize; - *ppHead = pNewBlock; + void* pMemRW = pMem; + ExecutableWriterHolder memWriterHolder; + if (pHeap->IsExecutable()) + { + memWriterHolder = ExecutableWriterHolder(pMem, dwTotalSize); + pMemRW = memWriterHolder.GetRW(); + } - MergeBlock(pNewBlock, pHeap); + INDEBUG(memset(pMemRW, 0xcc, dwTotalSize);) + LoaderHeapFreeBlock *pNewBlock = new (nothrow) LoaderHeapFreeBlock; + // If we fail allocating the LoaderHeapFreeBlock, ignore the failure and don't insert the free block at all. + if (pNewBlock != NULL) + { + pNewBlock->m_pNext = *ppHead; + pNewBlock->m_dwSize = dwTotalSize; + pNewBlock->m_pBlockAddress = pMem; + *ppHead = pNewBlock; + MergeBlock(pNewBlock, pHeap); + } LOADER_HEAP_END_TRAP_FAULT } - - static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap) + static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, UnlockedLoaderHeap *pHeap) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; @@ -751,23 +768,19 @@ struct LoaderHeapFreeBlock size_t dwCurSize = pCur->m_dwSize; if (dwCurSize == dwSize) { - pResult = pCur; + pResult = pCur->m_pBlockAddress; // Exact match. Hooray! - if (fRemoveFromFreeList) - { - *ppWalk = pCur->m_pNext; - } + *ppWalk = pCur->m_pNext; + delete pCur; break; } else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap)) { // Partial match. Ok... - pResult = pCur; - if (fRemoveFromFreeList) - { - *ppWalk = pCur->m_pNext; - InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap ); - } + pResult = pCur->m_pBlockAddress; + *ppWalk = pCur->m_pNext; + InsertFreeBlock(ppWalk, ((BYTE*)pCur->m_pBlockAddress) + dwSize, dwCurSize - dwSize, pHeap ); + delete pCur; break; } @@ -777,19 +790,22 @@ struct LoaderHeapFreeBlock ppWalk = &( pCur->m_pNext ); } - if (pResult && fRemoveFromFreeList) + if (pResult) { + void *pResultRW = pResult; + ExecutableWriterHolder resultWriterHolder; + if (pHeap->IsExecutable()) + { + resultWriterHolder = ExecutableWriterHolder(pResult, dwSize); + pResultRW = resultWriterHolder.GetRW(); + } // Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant! - memset(pResult, 0, dwSize); + memset(pResultRW, 0, dwSize); } LOADER_HEAP_END_TRAP_FAULT return pResult; - - - } - private: // Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened. static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap) @@ -803,7 +819,7 @@ struct LoaderHeapFreeBlock LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext; size_t dwSize = pFreeBlock->m_dwSize; - if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize)) + if (pNextBlock == NULL || ((BYTE*)pNextBlock->m_pBlockAddress) != (((BYTE*)pFreeBlock->m_pBlockAddress) + dwSize)) { result = FALSE; } @@ -811,9 +827,17 @@ struct LoaderHeapFreeBlock { size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize; LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext; - INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);) + void *pMemRW = pFreeBlock->m_pBlockAddress; + ExecutableWriterHolder memWriterHolder; + if (pHeap->IsExecutable()) + { + memWriterHolder = ExecutableWriterHolder(pFreeBlock->m_pBlockAddress, dwCombinedSize); + pMemRW = memWriterHolder.GetRW(); + } + INDEBUG(memset(pMemRW, 0xcc, dwCombinedSize);) pFreeBlock->m_pNext = pNextNextBlock; pFreeBlock->m_dwSize = dwCombinedSize; + delete pNextBlock; result = TRUE; } @@ -822,7 +846,7 @@ struct LoaderHeapFreeBlock return result; } - +#endif // DACCESS_COMPILE }; @@ -840,8 +864,7 @@ struct LoaderHeapFreeBlock // - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte) // - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag) // -// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock) -// - c bytes of pad (where "c" is just enough to pointer-align the following byte) +// - b bytes of pad (where "b" is just enough to pointer-align the following byte) // // ==> Following address is always pointer-aligned //===================================================================================== @@ -862,10 +885,6 @@ inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHe #ifdef _DEBUG dwSize += sizeof(LoaderHeapValidationTag); #endif - if (dwSize < sizeof(LoaderHeapFreeBlock)) - { - dwSize = sizeof(LoaderHeapFreeBlock); - } } dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); @@ -977,9 +996,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap() if (fReleaseMemory) { - BOOL fSuccess; - fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE); - _ASSERTE(fSuccess); + ExecutableAllocator::Instance()->Release(pVirtualAddress); } delete pSearch; @@ -987,9 +1004,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap() if (m_reservedBlock.m_fReleaseMemory) { - BOOL fSuccess; - fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE); - _ASSERTE(fSuccess); + ExecutableAllocator::Instance()->Release(m_reservedBlock.pVirtualAddress); } INDEBUG(s_dwNumInstancesOfLoaderHeaps --;) @@ -1058,7 +1073,7 @@ void ReleaseReservedMemory(BYTE* value) { if (value) { - ClrVirtualFree(value, 0, MEM_RELEASE); + ExecutableAllocator::Instance()->Release(value); } } @@ -1114,7 +1129,9 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit) // Reserve pages // - pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS); + // Reserve the memory for even non-executable stuff close to the executable code, as it has profound effect + // on e.g. a static variable access performance. + pData = (BYTE *)ExecutableAllocator::Instance()->Reserve(dwSizeToReserve); if (pData == NULL) { return FALSE; @@ -1140,7 +1157,7 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit) } // Commit first set of pages, since it will contain the LoaderHeapBlock - void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + void *pTemp = ExecutableAllocator::Instance()->Commit(pData, dwSizeToCommit, (m_Options & LHF_EXECUTABLE)); if (pTemp == NULL) { //_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap"); @@ -1213,7 +1230,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize) dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize()); // Yes, so commit the desired number of reserved pages - void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + void *pData = ExecutableAllocator::Instance()->Commit(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, (m_Options & LHF_EXECUTABLE)); if (pData == NULL) return FALSE; @@ -1316,7 +1333,7 @@ again: { // Any memory available on the free list? - void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this); + void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, this); if (!pData) { // Enough bytes available in committed region? @@ -1518,8 +1535,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize )) { - // Cool. This was the last block allocated. We can just undo the allocation instead - // of going to the freelist. void *pMemRW = pMem; ExecutableWriterHolder memWriterHolder; if (m_Options & LHF_EXECUTABLE) @@ -1527,6 +1542,9 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, memWriterHolder = ExecutableWriterHolder(pMem, dwSize); pMemRW = memWriterHolder.GetRW(); } + + // Cool. This was the last block allocated. We can just undo the allocation instead + // of going to the freelist. memset(pMemRW, 0x00, dwSize); // Fill freed region with 0 m_pAllocPtr = (BYTE*)pMem; } @@ -1534,7 +1552,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, { LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this); } - } diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 0026d1f619f..e7b1755b2b1 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -352,168 +352,6 @@ HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid, return hr; } -#if USE_UPPER_ADDRESS -static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in. -static BYTE * s_CodeMaxAddr; -static BYTE * s_CodeAllocStart; -static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region. -#endif - -// -// Use this function to initialize the s_CodeAllocHint -// during startup. base is runtime .dll base address, -// size is runtime .dll virtual size. -// -void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset) -{ -#if USE_UPPER_ADDRESS - -#ifdef _DEBUG - // If GetForceRelocs is enabled we don't constrain the pMinAddr - if (PEDecoder::GetForceRelocs()) - return; -#endif - -// - // If we are using the UPPER_ADDRESS space (on Win64) - // then for any code heap that doesn't specify an address - // range using [pMinAddr..pMaxAddr] we place it in the - // upper address space - // This enables us to avoid having to use long JumpStubs - // to reach the code for our ngen-ed images. - // Which are also placed in the UPPER_ADDRESS space. - // - SIZE_T reach = 0x7FFF0000u; - - // We will choose the preferred code region based on the address of clr.dll. The JIT helpers - // in clr.dll are the most heavily called functions. - s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0; - s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1; - - BYTE * pStart; - - if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS && - (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr) - { - // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista) - // Use the code head start address that does not cause collisions with NGen images. - // This logic is coupled with scripts that we use to assign base addresses. - pStart = (BYTE *)CODEHEAP_START_ADDRESS; - } - else - if (base > UINT32_MAX) - { - // clr.dll got address assigned by ASLR? - // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned - // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images - // that can be placed at higher addresses than clr.dll. - pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8; - } - else - { - // clr.dll missed the base address? - // Try to occupy the space right after it. - pStart = (BYTE *)(base + size); - } - - // Randomize the address space - pStart += GetOsPageSize() * randomPageOffset; - - s_CodeAllocStart = pStart; - s_CodeAllocHint = pStart; -#endif -} - -// -// Use this function to reset the s_CodeAllocHint -// after unloading an AppDomain -// -void ResetCodeAllocHint() -{ - LIMITED_METHOD_CONTRACT; -#if USE_UPPER_ADDRESS - s_CodeAllocHint = s_CodeAllocStart; -#endif -} - -// -// Returns TRUE if p is located in near clr.dll that allows us -// to use rel32 IP-relative addressing modes. -// -BOOL IsPreferredExecutableRange(void * p) -{ - LIMITED_METHOD_CONTRACT; -#if USE_UPPER_ADDRESS - if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr) - return TRUE; -#endif - return FALSE; -} - -// -// Allocate free memory that will be used for executable code -// Handles the special requirements that we have on 64-bit platforms -// where we want the executable memory to be located near clr.dll -// -BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize, - DWORD flAllocationType, - DWORD flProtect) -{ - CONTRACTL - { - NOTHROW; - } - CONTRACTL_END; - -#if USE_UPPER_ADDRESS - // - // If we are using the UPPER_ADDRESS space (on Win64) - // then for any heap that will contain executable code - // we will place it in the upper address space - // - // This enables us to avoid having to use JumpStubs - // to reach the code for our ngen-ed images on x64, - // since they are also placed in the UPPER_ADDRESS space. - // - BYTE * pHint = s_CodeAllocHint; - - if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL) - { - // Try to allocate in the preferred region after the hint - BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect); - - if (pResult != NULL) - { - s_CodeAllocHint = pResult + dwSize; - return pResult; - } - - // Try to allocate in the preferred region before the hint - pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect); - - if (pResult != NULL) - { - s_CodeAllocHint = pResult + dwSize; - return pResult; - } - - s_CodeAllocHint = NULL; - } - - // Fall through to -#endif // USE_UPPER_ADDRESS - -#ifdef HOST_UNIX - // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory. - // This will allow us to place JIT'ed code close to the coreclr library - // and thus improve performance by avoiding jump stubs in managed code. - flAllocationType |= MEM_RESERVE_EXECUTABLE; -#endif // HOST_UNIX - - return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect); - -} - // // Allocate free memory with specific alignment. // diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 1d682d2a428..9c2cb3df0b7 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -833,7 +833,6 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) set(VM_SOURCES_DAC_AND_WKS_ARCH ${ARCH_SOURCES_DIR}/exceparm.cpp ${ARCH_SOURCES_DIR}/stubs.cpp - ${ARCH_SOURCES_DIR}/armsinglestepper.cpp ) set(VM_HEADERS_DAC_AND_WKS_ARCH @@ -844,6 +843,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) set(VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/profiler.cpp + ${ARCH_SOURCES_DIR}/armsinglestepper.cpp exceptionhandling.cpp gcinfodecoder.cpp ) @@ -868,7 +868,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64) ) if(CLR_CMAKE_HOST_UNIX) - list(APPEND VM_SOURCES_DAC_AND_WKS_ARCH + list(APPEND VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/arm64singlestepper.cpp ) endif(CLR_CMAKE_HOST_UNIX) diff --git a/src/coreclr/vm/amd64/JitHelpers_Fast.asm b/src/coreclr/vm/amd64/JitHelpers_Fast.asm index 82a301bb0cb..219597eb350 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Fast.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Fast.asm @@ -51,37 +51,6 @@ endif extern JIT_InternalThrow:proc -; There is an even more optimized version of these helpers possible which takes -; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 -; that check (this is more significant in the JIT_WriteBarrier case). -; -; Additionally we can look into providing helpers which will take the src/dest from -; specific registers (like x86) which _could_ (??) make for easier register allocation -; for the JIT64, however it might lead to having to have some nasty code that treats -; these guys really special like... :(. -; -; Version that does the move, checks whether or not it's in the GC and whether or not -; it needs to have it's card updated -; -; void JIT_CheckedWriteBarrier(Object** dst, Object* src) -LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT - - ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference - ; but if it isn't then it will just return. - ; - ; See if this is in GCHeap - cmp rcx, [g_lowest_address] - jb NotInHeap - cmp rcx, [g_highest_address] - jnb NotInHeap - - jmp JIT_WriteBarrier - - NotInHeap: - ; See comment above about possible AV - mov [rcx], rdx - ret -LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT ; Mark start of the code region that we patch at runtime LEAF_ENTRY JIT_PatchedCodeStart, _TEXT @@ -99,7 +68,8 @@ LEAF_ENTRY JIT_WriteBarrier, _TEXT ifdef _DEBUG ; In debug builds, this just contains jump to the debug version of the write barrier by default - jmp JIT_WriteBarrier_Debug + mov rax, JIT_WriteBarrier_Debug + jmp rax endif ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP @@ -388,6 +358,51 @@ endif ret LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT +Section segment para 'DATA' + + align 16 + + public JIT_WriteBarrier_Loc +JIT_WriteBarrier_Loc: + dq 0 + +LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT + ; JIT_WriteBarrier(Object** dst, Object* src) + jmp QWORD PTR [JIT_WriteBarrier_Loc] +LEAF_END JIT_WriteBarrier_Callable, _TEXT + +; There is an even more optimized version of these helpers possible which takes +; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 +; that check (this is more significant in the JIT_WriteBarrier case). +; +; Additionally we can look into providing helpers which will take the src/dest from +; specific registers (like x86) which _could_ (??) make for easier register allocation +; for the JIT64, however it might lead to having to have some nasty code that treats +; these guys really special like... :(. +; +; Version that does the move, checks whether or not it's in the GC and whether or not +; it needs to have it's card updated +; +; void JIT_CheckedWriteBarrier(Object** dst, Object* src) +LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT + + ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference + ; but if it isn't then it will just return. + ; + ; See if this is in GCHeap + cmp rcx, [g_lowest_address] + jb NotInHeap + cmp rcx, [g_highest_address] + jnb NotInHeap + + jmp QWORD PTR [JIT_WriteBarrier_Loc] + + NotInHeap: + ; See comment above about possible AV + mov [rcx], rdx + ret +LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT + ; The following helper will access ("probe") a word on each page of the stack ; starting with the page right beneath rsp down to the one pointed to by r11. ; The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. diff --git a/src/coreclr/vm/amd64/jithelpers_fast.S b/src/coreclr/vm/amd64/jithelpers_fast.S index a13afb48785..8109886d0c9 100644 --- a/src/coreclr/vm/amd64/jithelpers_fast.S +++ b/src/coreclr/vm/amd64/jithelpers_fast.S @@ -32,26 +32,14 @@ LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT // See if this is in GCHeap PREPARE_EXTERNAL_VAR g_lowest_address, rax cmp rdi, [rax] -#ifdef FEATURE_WRITEBARRIER_COPY // jb NotInHeap .byte 0x72, 0x12 -#else - // jb NotInHeap - .byte 0x72, 0x0e -#endif PREPARE_EXTERNAL_VAR g_highest_address, rax cmp rdi, [rax] -#ifdef FEATURE_WRITEBARRIER_COPY // jnb NotInHeap .byte 0x73, 0x06 jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)] -#else - // jnb NotInHeap - .byte 0x73, 0x02 - // jmp C_FUNC(JIT_WriteBarrier) - .byte 0xeb, 0x05 -#endif NotInHeap: // See comment above about possible AV @@ -398,11 +386,17 @@ LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT ret LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT -#ifdef FEATURE_WRITEBARRIER_COPY // When JIT_WriteBarrier is copied into an allocated page, // helpers use this global variable to jump to it. This variable is set in InitThreadManager. - .global _JIT_WriteBarrier_Loc - .zerofill __DATA,__common,_JIT_WriteBarrier_Loc,8,3 + .global C_FUNC(JIT_WriteBarrier_Loc) +#ifdef TARGET_OSX + .zerofill __DATA,__common,C_FUNC(JIT_WriteBarrier_Loc),8,3 +#else + .data + C_FUNC(JIT_WriteBarrier_Loc): + .quad 0 + .text +#endif // ------------------------------------------------------------------ // __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) @@ -412,8 +406,6 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)] LEAF_END JIT_WriteBarrier_Callable, _TEXT -#endif // FEATURE_WRITEBARRIER_COPY - // The following helper will access ("probe") a word on each page of the stack // starting with the page right beneath rsp down to the one pointed to by r11. diff --git a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp index 38bff78a54c..02b023777b8 100644 --- a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp +++ b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp @@ -293,7 +293,10 @@ int WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier, // the memcpy must come before the switch statment because the asserts inside the switch // are actually looking into the JIT_WriteBarrier buffer - memcpy(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize()); + { + ExecutableWriterHolder writeBarrierWriterHolder(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), GetCurrentWriteBarrierSize()); + memcpy(writeBarrierWriterHolder.GetRW(), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize()); + } switch (newWriteBarrier) { @@ -544,7 +547,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended) // Change immediate if different from new g_ephermeral_high. if (*(UINT64*)m_pUpperBoundImmediate != (size_t)g_ephemeral_high) { - *(UINT64*)m_pUpperBoundImmediate = (size_t)g_ephemeral_high; + ExecutableWriterHolder upperBoundWriterHolder((UINT64*)m_pUpperBoundImmediate, sizeof(UINT64)); + *upperBoundWriterHolder.GetRW() = (size_t)g_ephemeral_high; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } } @@ -557,7 +561,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended) // Change immediate if different from new g_ephermeral_low. if (*(UINT64*)m_pLowerBoundImmediate != (size_t)g_ephemeral_low) { - *(UINT64*)m_pLowerBoundImmediate = (size_t)g_ephemeral_low; + ExecutableWriterHolder lowerBoundImmediateWriterHolder((UINT64*)m_pLowerBoundImmediate, sizeof(UINT64)); + *lowerBoundImmediateWriterHolder.GetRW() = (size_t)g_ephemeral_low; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } break; @@ -609,7 +614,8 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus #endif // FEATURE_SVR_GC if (*(UINT64*)m_pWriteWatchTableImmediate != (size_t)g_sw_ww_table) { - *(UINT64*)m_pWriteWatchTableImmediate = (size_t)g_sw_ww_table; + ExecutableWriterHolder writeWatchTableImmediateWriterHolder((UINT64*)m_pWriteWatchTableImmediate, sizeof(UINT64)); + *writeWatchTableImmediateWriterHolder.GetRW() = (size_t)g_sw_ww_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } break; @@ -621,14 +627,16 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus if (*(UINT64*)m_pCardTableImmediate != (size_t)g_card_table) { - *(UINT64*)m_pCardTableImmediate = (size_t)g_card_table; + ExecutableWriterHolder cardTableImmediateWriterHolder((UINT64*)m_pCardTableImmediate, sizeof(UINT64)); + *cardTableImmediateWriterHolder.GetRW() = (size_t)g_card_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES if (*(UINT64*)m_pCardBundleTableImmediate != (size_t)g_card_bundle_table) { - *(UINT64*)m_pCardBundleTableImmediate = (size_t)g_card_bundle_table; + ExecutableWriterHolder cardBundleTableImmediateWriterHolder((UINT64*)m_pCardBundleTableImmediate, sizeof(UINT64)); + *cardBundleTableImmediateWriterHolder.GetRW() = (size_t)g_card_bundle_table; stompWBCompleteActions |= SWB_ICACHE_FLUSH; } #endif diff --git a/src/coreclr/vm/arm/armsinglestepper.cpp b/src/coreclr/vm/arm/armsinglestepper.cpp index 79317263b22..f9e718ae542 100644 --- a/src/coreclr/vm/arm/armsinglestepper.cpp +++ b/src/coreclr/vm/arm/armsinglestepper.cpp @@ -97,11 +97,7 @@ ArmSingleStepper::ArmSingleStepper() ArmSingleStepper::~ArmSingleStepper() { #if !defined(DACCESS_COMPILE) -#ifdef TARGET_UNIX SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(WORD)); -#else - DeleteExecutable(m_rgCode); -#endif #endif } @@ -110,11 +106,7 @@ void ArmSingleStepper::Init() #if !defined(DACCESS_COMPILE) if (m_rgCode == NULL) { -#ifdef TARGET_UNIX m_rgCode = (WORD *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(WORD))); -#else - m_rgCode = new (executable) WORD[kMaxCodeBuffer]; -#endif } #endif } @@ -287,6 +279,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) DWORD idxNextInstruction = 0; + ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); + if (m_originalITState.InITBlock() && !ConditionHolds(pCtx, m_originalITState.CurrentCondition())) { LOG((LF_CORDB, LL_INFO100000, "ArmSingleStepper: Case 1: ITState::Clear;\n")); @@ -295,7 +289,7 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) // to execute. We'll put the correct value back during fixup. ITState::Clear(pCtx); m_fSkipIT = true; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } else if (TryEmulate(pCtx, opcode1, opcode2, false)) { @@ -308,8 +302,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) m_fEmulate = true; // Set breakpoints to stop the execution. This will get us right back here. - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } else { @@ -323,24 +317,24 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx) // guarantee one of them will be hit (we don't care which one -- the fixup code will update // the PC and IT state to make it look as though the CPU just executed the current // instruction). - m_rgCode[idxNextInstruction++] = opcode1; + codeWriterHolder.GetRW()[idxNextInstruction++] = opcode1; if (Is32BitInstruction(opcode1)) - m_rgCode[idxNextInstruction++] = opcode2; + codeWriterHolder.GetRW()[idxNextInstruction++] = opcode2; - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; } // Always terminate the redirection buffer with a breakpoint. - m_rgCode[idxNextInstruction++] = kBreakpointOp; + codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp; _ASSERTE(idxNextInstruction <= kMaxCodeBuffer); // Set the thread up so it will redirect to our buffer when execution resumes. pCtx->Pc = ((DWORD)(DWORD_PTR)m_rgCode) | THUMB_CODE; // Make sure the CPU sees the updated contents of the buffer. - FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode)); + FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); // Done, set the state. m_state = Applied; diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 930395b56dc..3faa8fe3684 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -978,6 +978,16 @@ g_rgWriteBarrierDescriptors: .global g_rgWriteBarrierDescriptors +// ------------------------------------------------------------------ +// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) + LEAF_ENTRY JIT_WriteBarrier_Callable + + // Branch to the write barrier + ldr r2, =JIT_WriteBarrier_Loc // or R3? See targetarm.h + ldr pc, [r2] + + LEAF_END JIT_WriteBarrier_Callable + #ifdef FEATURE_READYTORUN NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler diff --git a/src/coreclr/vm/arm/asmhelpers.asm b/src/coreclr/vm/arm/asmhelpers.asm index d20540e6209..82596e66693 100644 --- a/src/coreclr/vm/arm/asmhelpers.asm +++ b/src/coreclr/vm/arm/asmhelpers.asm @@ -1724,6 +1724,18 @@ tempReg SETS "$tmpReg" END_WRITE_BARRIERS + IMPORT JIT_WriteBarrier_Loc + +; ------------------------------------------------------------------ +; __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) + LEAF_ENTRY JIT_WriteBarrier_Callable + + ; Branch to the write barrier + ldr r2, =JIT_WriteBarrier_Loc ; or R3? See targetarm.h + ldr pc, [r2] + + LEAF_END + #ifdef FEATURE_READYTORUN NESTED_ENTRY DelayLoad_MethodCall_FakeProlog diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h index 88d0c6802b6..425c2865584 100644 --- a/src/coreclr/vm/arm/cgencpu.h +++ b/src/coreclr/vm/arm/cgencpu.h @@ -1069,6 +1069,7 @@ struct StubPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -1095,6 +1096,7 @@ struct StubPrecode { return (TADDR)InterlockedCompareExchange( (LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected; } +#endif // !DACCESS_COMPILE #ifdef FEATURE_PREJIT void Fixup(DataImage *image); @@ -1167,6 +1169,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); PCODE GetTarget() @@ -1175,6 +1184,7 @@ struct FixupPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -1201,6 +1211,7 @@ struct FixupPrecode { return (TADDR)InterlockedCompareExchange( (LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected; } +#endif // !DACCESS_COMPILE static BOOL IsFixupPrecodeByASM(PCODE addr) { @@ -1256,6 +1267,7 @@ struct ThisPtrRetBufPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE BOOL SetTargetInterlocked(TADDR target, TADDR expected) { CONTRACTL @@ -1268,6 +1280,7 @@ struct ThisPtrRetBufPrecode { ExecutableWriterHolder precodeWriterHolder(this, sizeof(ThisPtrRetBufPrecode)); return FastInterlockCompareExchange((LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == (LONG)expected; } +#endif // !DACCESS_COMPILE }; typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index aac3e25b181..6e62df23703 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -329,16 +329,28 @@ void ComputeWriteBarrierRange(BYTE ** ppbStart, DWORD * pcbLength) { DWORD size = (PBYTE)JIT_PatchedWriteBarrierLast - (PBYTE)JIT_PatchedWriteBarrierStart; *ppbStart = (PBYTE)JIT_PatchedWriteBarrierStart; + if (IsWriteBarrierCopyEnabled()) + { + *ppbStart = GetWriteBarrierCodeLocation(*ppbStart); + } *pcbLength = size; } void CopyWriteBarrier(PCODE dstCode, PCODE srcCode, PCODE endCode) { - TADDR dst = PCODEToPINSTR(dstCode); + TADDR dst = (TADDR)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation((void*)dstCode)); TADDR src = PCODEToPINSTR(srcCode); TADDR end = PCODEToPINSTR(endCode); size_t size = (PBYTE)end - (PBYTE)src; + + ExecutableWriterHolder writeBarrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + writeBarrierWriterHolder = ExecutableWriterHolder((void*)dst, size); + dst = (TADDR)writeBarrierWriterHolder.GetRW(); + } + memcpy((PVOID)dst, (PVOID)src, size); } @@ -419,7 +431,7 @@ void UpdateGCWriteBarriers(bool postGrow = false) } #define GWB_PATCH_OFFSET(_global) \ if (pDesc->m_dw_##_global##_offset != 0xffff) \ - PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset - 1), (UINT32)(dac_cast(_global))); + PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset), (UINT32)(dac_cast(_global))); // Iterate through the write barrier patch table created in the .clrwb section // (see write barrier asm code) @@ -431,6 +443,13 @@ void UpdateGCWriteBarriers(bool postGrow = false) PBYTE to = FindWBMapping(pDesc->m_pFuncStart); if(to) { + to = (PBYTE)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation(to)); + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(to, pDesc->m_pFuncEnd - pDesc->m_pFuncStart); + to = barrierWriterHolder.GetRW(); + } GWB_PATCH_OFFSET(g_lowest_address); GWB_PATCH_OFFSET(g_highest_address); GWB_PATCH_OFFSET(g_ephemeral_low); diff --git a/src/coreclr/vm/arm64/arm64singlestepper.cpp b/src/coreclr/vm/arm64/arm64singlestepper.cpp index d45925311a3..6c1764647c9 100644 --- a/src/coreclr/vm/arm64/arm64singlestepper.cpp +++ b/src/coreclr/vm/arm64/arm64singlestepper.cpp @@ -46,11 +46,7 @@ Arm64SingleStepper::Arm64SingleStepper() Arm64SingleStepper::~Arm64SingleStepper() { #if !defined(DACCESS_COMPILE) -#ifdef TARGET_UNIX SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(uint32_t)); -#else - DeleteExecutable(m_rgCode); -#endif #endif } @@ -59,11 +55,7 @@ void Arm64SingleStepper::Init() #if !defined(DACCESS_COMPILE) if (m_rgCode == NULL) { -#ifdef TARGET_UNIX m_rgCode = (uint32_t *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(uint32_t))); -#else - m_rgCode = new (executable) uint32_t[kMaxCodeBuffer]; -#endif } #endif } @@ -207,7 +199,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx) unsigned int idxNextInstruction = 0; - ExecutableWriterHolder codeWriterHolder(m_rgCode, sizeof(m_rgCode)); + ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); if (TryEmulate(pCtx, opcode, false)) { @@ -230,7 +222,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx) pCtx->Pc = (uint64_t)m_rgCode; // Make sure the CPU sees the updated contents of the buffer. - FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode)); + FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0])); // Done, set the state. m_state = Applied; diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index e6b47d07b2b..8ef66586cd2 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -270,13 +270,9 @@ LOCAL_LABEL(EphemeralCheckEnabled): ldr x7, [x12] // Update wbs state -#ifdef FEATURE_WRITEBARRIER_COPY PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Table_Loc, x12 ldr x12, [x12] add x12, x12, x9 -#else // FEATURE_WRITEBARRIER_COPY - adr x12, LOCAL_LABEL(wbs_begin) -#endif // FEATURE_WRITEBARRIER_COPY stp x0, x1, [x12], 16 stp x2, x3, [x12], 16 @@ -295,16 +291,10 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT mov x14, x0 // x14 = dst mov x15, x1 // x15 = val -#ifdef FEATURE_WRITEBARRIER_COPY -LOCAL_LABEL(Branch_JIT_WriteBarrier_Copy): // Branch to the write barrier PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Loc, x17 ldr x17, [x17] br x17 -#else // FEATURE_WRITEBARRIER_COPY - // Branch to the write barrier - b C_FUNC(JIT_WriteBarrier) -#endif // FEATURE_WRITEBARRIER_COPY LEAF_END JIT_WriteBarrier_Callable, _TEXT .balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index ffbeb9fd1ac..17d3a676940 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -61,6 +61,10 @@ #ifdef FEATURE_COMINTEROP IMPORT CLRToCOMWorker #endif // FEATURE_COMINTEROP + + IMPORT JIT_WriteBarrier_Table_Loc + IMPORT JIT_WriteBarrier_Loc + TEXTAREA ;; LPVOID __stdcall GetCurrentIP(void); @@ -308,6 +312,7 @@ ThePreStubPatchLabel ; x12 will be used for pointers mov x8, x0 + mov x9, x1 adrp x12, g_card_table ldr x0, [x12, g_card_table] @@ -346,7 +351,9 @@ EphemeralCheckEnabled ldr x7, [x12, g_highest_address] ; Update wbs state - adr x12, wbs_begin + adrp x12, JIT_WriteBarrier_Table_Loc + ldr x12, [x12, JIT_WriteBarrier_Table_Loc] + add x12, x12, x9 stp x0, x1, [x12], 16 stp x2, x3, [x12], 16 stp x4, x5, [x12], 16 @@ -355,9 +362,11 @@ EphemeralCheckEnabled EPILOG_RESTORE_REG_PAIR fp, lr, #16! EPILOG_RETURN + WRITE_BARRIER_END JIT_UpdateWriteBarrierState + ; Begin patchable literal pool ALIGN 64 ; Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line - + WRITE_BARRIER_ENTRY JIT_WriteBarrier_Table wbs_begin wbs_card_table DCQ 0 @@ -375,14 +384,7 @@ wbs_lowest_address DCQ 0 wbs_highest_address DCQ 0 - - WRITE_BARRIER_END JIT_UpdateWriteBarrierState - -; ------------------------------------------------------------------ -; End of the writeable code region - LEAF_ENTRY JIT_PatchedCodeLast - ret lr - LEAF_END + WRITE_BARRIER_END JIT_WriteBarrier_Table ; void JIT_ByRefWriteBarrier ; On entry: @@ -546,6 +548,12 @@ Exit ret lr WRITE_BARRIER_END JIT_WriteBarrier +; ------------------------------------------------------------------ +; End of the writeable code region + LEAF_ENTRY JIT_PatchedCodeLast + ret lr + LEAF_END + #ifdef FEATURE_PREJIT ;------------------------------------------------ ; VirtualMethodFixupStub @@ -1417,9 +1425,10 @@ CallHelper2 mov x14, x0 ; x14 = dst mov x15, x1 ; x15 = val - ; Branch to the write barrier (which is already correctly overwritten with - ; single or multi-proc code based on the current CPU - b JIT_WriteBarrier + ; Branch to the write barrier + adrp x17, JIT_WriteBarrier_Loc + ldr x17, [x17, JIT_WriteBarrier_Loc] + br x17 LEAF_END diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 83e56cfb9f9..0641d89ff1a 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -597,6 +597,7 @@ struct StubPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -623,6 +624,7 @@ struct StubPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE #ifdef FEATURE_PREJIT void Fixup(DataImage *image); @@ -715,6 +717,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); PCODE GetTarget() @@ -723,6 +732,7 @@ struct FixupPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -749,6 +759,7 @@ struct FixupPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE static BOOL IsFixupPrecodeByASM(PCODE addr) { @@ -797,6 +808,7 @@ struct ThisPtrRetBufPrecode { return m_pTarget; } +#ifndef DACCESS_COMPILE BOOL SetTargetInterlocked(TADDR target, TADDR expected) { CONTRACTL @@ -810,6 +822,7 @@ struct ThisPtrRetBufPrecode { return (TADDR)InterlockedCompareExchange64( (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; } +#endif // !DACCESS_COMPILE }; typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 54cf1c49275..12d56ddb986 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -1067,8 +1067,14 @@ extern "C" void STDCALL JIT_PatchedCodeLast(); static void UpdateWriteBarrierState(bool skipEphemeralCheck) { BYTE *writeBarrierCodeStart = GetWriteBarrierCodeLocation((void*)JIT_PatchedCodeStart); - ExecutableWriterHolder writeBarrierWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart); - JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierWriterHolder.GetRW() - writeBarrierCodeStart); + BYTE *writeBarrierCodeStartRW = writeBarrierCodeStart; + ExecutableWriterHolder writeBarrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + writeBarrierWriterHolder = ExecutableWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart); + writeBarrierCodeStartRW = writeBarrierWriterHolder.GetRW(); + } + JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierCodeStartRW - writeBarrierCodeStart); } void InitJITHelpers1() diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index cdc5925234a..b60aac924d2 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -607,6 +607,11 @@ void EESocketCleanupHelper(bool isExecutingOnAltStack) #endif // TARGET_UNIX #endif // CROSSGEN_COMPILE +void FatalErrorHandler(UINT errorCode, LPCWSTR pszMessage) +{ + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(errorCode, pszMessage); +} + void EEStartupHelper() { CONTRACTL @@ -670,6 +675,8 @@ void EEStartupHelper() // This needs to be done before the EE has started InitializeStartupFlags(); + IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); + ThreadpoolMgr::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); @@ -824,7 +831,7 @@ void EEStartupHelper() g_runtimeLoadedBaseAddress = (SIZE_T)pe.GetBase(); g_runtimeVirtualSize = (SIZE_T)pe.GetVirtualSize(); - InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64)); + ExecutableAllocator::InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64)); } #endif // !TARGET_UNIX diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 02feec829a7..5c5004f5686 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -153,7 +153,9 @@ void EEClass::Destruct(MethodTable * pOwningMT) if (pDelegateEEClass->m_pStaticCallStub) { - BOOL fStubDeleted = pDelegateEEClass->m_pStaticCallStub->DecRef(); + ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pStaticCallStub, sizeof(Stub)); + BOOL fStubDeleted = stubWriterHolder.GetRW()->DecRef(); + if (fStubDeleted) { DelegateInvokeStubManager::g_pManager->RemoveStub(pDelegateEEClass->m_pStaticCallStub); @@ -167,7 +169,6 @@ void EEClass::Destruct(MethodTable * pOwningMT) // it is owned by the m_pMulticastStubCache, not by the class // - it is shared across classes. So we don't decrement // its ref count here - delete pDelegateEEClass->m_pUMThunkMarshInfo; } #ifdef FEATURE_COMINTEROP diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 37220786fed..78721292a3e 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -2139,8 +2139,7 @@ VOID EEJitManager::EnsureJumpStubReserve(BYTE * pImageBase, SIZE_T imageSize, SI return; // Unable to allocate the reserve - give up } - pNewReserve->m_ptr = ClrVirtualAllocWithinRange(loAddrCurrent, hiAddrCurrent, - allocChunk, MEM_RESERVE, PAGE_NOACCESS); + pNewReserve->m_ptr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(allocChunk, loAddrCurrent, hiAddrCurrent); if (pNewReserve->m_ptr != NULL) break; @@ -2231,8 +2230,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap if (!pInfo->getThrowOnOutOfMemoryWithinRange() && PEDecoder::GetForceRelocs()) RETURN NULL; #endif - pBaseAddr = ClrVirtualAllocWithinRange(loAddr, hiAddr, - reserveSize, MEM_RESERVE, PAGE_NOACCESS); + pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(reserveSize, loAddr, hiAddr); if (!pBaseAddr) { @@ -2251,7 +2249,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap } else { - pBaseAddr = ClrVirtualAllocExecutable(reserveSize, MEM_RESERVE, PAGE_NOACCESS); + pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(reserveSize); if (!pBaseAddr) ThrowOutOfMemory(); } @@ -2686,15 +2684,14 @@ void EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, size_t reserveFo *pAllocatedSize = sizeof(CodeHeader) + totalSize; -#if defined(HOST_OSX) && defined(HOST_ARM64) -#define FEATURE_WXORX -#endif - -#ifdef FEATURE_WXORX - pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize]; -#else - pCodeHdrRW = pCodeHdr; -#endif + if (ExecutableAllocator::IsWXORXEnabled()) + { + pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize]; + } + else + { + pCodeHdrRW = pCodeHdr; + } #ifdef USE_INDIRECT_CODEHEADER if (requestInfo.IsDynamicDomain()) @@ -3347,7 +3344,7 @@ void EEJitManager::Unload(LoaderAllocator *pAllocator) } } - ResetCodeAllocHint(); + ExecutableAllocator::ResetCodeAllocHint(); } EEJitManager::DomainCodeHeapList::DomainCodeHeapList() diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 8b95dac8cdd..499880dc16d 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -3183,12 +3183,11 @@ void ComMethodTable::Cleanup() if (m_pDispatchInfo) delete m_pDispatchInfo; - if (m_pMDescr) - DeleteExecutable(m_pMDescr); if (m_pITypeInfo && !g_fProcessDetach) SafeRelease(m_pITypeInfo); - DeleteExecutable(this); + // The m_pMDescr and the current instance is allocated from the related LoaderAllocator + // so no cleanup is needed here. } @@ -3214,7 +3213,7 @@ void ComMethodTable::LayOutClassMethodTable() SLOT *pComVtable; unsigned cbPrevSlots = 0; unsigned cbAlloc = 0; - NewExecutableHolder pMDMemoryPtr = NULL; + AllocMemHolder pMDMemoryPtr; BYTE* pMethodDescMemory = NULL; size_t writeableOffset = 0; unsigned cbNumParentVirtualMethods = 0; @@ -3321,7 +3320,7 @@ void ComMethodTable::LayOutClassMethodTable() cbAlloc = cbMethodDescs; if (cbAlloc > 0) { - pMDMemoryPtr = (BYTE*) new (executable) BYTE[cbAlloc + sizeof(UINT_PTR)]; + pMDMemoryPtr = m_pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbAlloc + sizeof(UINT_PTR))); pMethodDescMemory = pMDMemoryPtr; methodDescMemoryWriteableHolder = ExecutableWriterHolder(pMethodDescMemory, cbAlloc + sizeof(UINT_PTR)); @@ -3703,7 +3702,6 @@ BOOL ComMethodTable::LayOutInterfaceMethodTable(MethodTable* pClsMT) // Method descs are at the end of the vtable // m_cbSlots interfaces methods + IUnk methods pMethodDescMemory = (BYTE *)&pComVtable[m_cbSlots]; - for (i = 0; i < cbSlots; i++) { ComCallMethodDesc* pNewMD = (ComCallMethodDesc *) (pMethodDescMemory + COMMETHOD_PREPAD); @@ -4495,13 +4493,12 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForClass(MethodTable if (cbToAlloc.IsOverflow()) ThrowHR(COR_E_OVERFLOW); - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()]; + AllocMemHolder pComMT(pClassMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value()))); _ASSERTE(!cbNewSlots.IsOverflow() && !cbTotalSlots.IsOverflow() && !cbVtable.IsOverflow()); ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc.Value()); ComMethodTable* pComMTRW = comMTWriterHolder.GetRW(); - // set up the header pComMTRW->m_ptReserved = (SLOT)(size_t)0xDEADC0FF; // reserved pComMTRW->m_pMT = pClassMT; // pointer to the class method table @@ -4573,7 +4570,7 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForInterface(MethodT if (cbToAlloc.IsOverflow()) ThrowHR(COR_E_OVERFLOW); - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()]; + AllocMemHolder pComMT(pInterfaceMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value()))); _ASSERTE(!cbVtable.IsOverflow() && !cbMethDescs.IsOverflow()); @@ -4639,7 +4636,8 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForBasic(MethodTable unsigned cbVtable = cbExtraSlots * sizeof(SLOT); unsigned cbToAlloc = sizeof(ComMethodTable) + cbVtable; - NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc]; + AllocMemHolder pComMT(pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc))); + ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc); ComMethodTable* pComMTRW = comMTWriterHolder.GetRW(); diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h index 2581ddf832f..0f1e4b878e4 100644 --- a/src/coreclr/vm/comcallablewrapper.h +++ b/src/coreclr/vm/comcallablewrapper.h @@ -499,6 +499,7 @@ struct ComMethodTable // Accessor for the IDispatch information. DispatchInfo* GetDispatchInfo(); +#ifndef DACCESS_COMPILE LONG AddRef() { LIMITED_METHOD_CONTRACT; @@ -527,6 +528,7 @@ struct ComMethodTable return cbRef; } +#endif // DACCESS_COMPILE CorIfaceAttr GetInterfaceType() { @@ -746,6 +748,7 @@ struct ComMethodTable } +#ifndef DACCESS_COMPILE inline REFIID GetIID() { // Cannot use a normal CONTRACT since the return type is ref type which @@ -768,6 +771,7 @@ struct ComMethodTable return m_IID; } +#endif // DACCESS_COMPILE void CheckParentComVisibility(BOOL fForIDispatch) { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b6c17260a13..1b61e16dec5 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1253,7 +1253,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) { GCX_PREEMP(); - pUMThunkMarshInfo = new UMThunkMarshInfo(); + pUMThunkMarshInfo = (UMThunkMarshInfo*)(void*)pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(sizeof(UMThunkMarshInfo))); ExecutableWriterHolder uMThunkMarshInfoWriterHolder(pUMThunkMarshInfo, sizeof(UMThunkMarshInfo)); uMThunkMarshInfoWriterHolder.GetRW()->LoadTimeInit(pInvokeMeth); diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 4a88f81df52..4f3cf879d10 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -41,7 +41,7 @@ public: { WRAPPER_NO_CONTRACT; - m_crst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE); + m_crst.Init(CrstUMEntryThunkFreeListLock, CRST_UNSAFE_ANYMODE); } UMEntryThunk *GetUMEntryThunk() diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp index 9dae86aca93..541d88dc168 100644 --- a/src/coreclr/vm/dynamicmethod.cpp +++ b/src/coreclr/vm/dynamicmethod.cpp @@ -403,8 +403,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) if (pInfo->m_loAddr != NULL || pInfo->m_hiAddr != NULL) { - m_pBaseAddr = ClrVirtualAllocWithinRange(pInfo->m_loAddr, pInfo->m_hiAddr, - ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); + m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(ReserveBlockSize, pInfo->m_loAddr, pInfo->m_hiAddr); if (!m_pBaseAddr) { if (pInfo->getThrowOnOutOfMemoryWithinRange()) @@ -417,7 +416,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) // top up the ReserveBlockSize to suggested minimum ReserveBlockSize = max(ReserveBlockSize, pInfo->getReserveSize()); - m_pBaseAddr = ClrVirtualAllocExecutable(ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); + m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(ReserveBlockSize); if (!m_pBaseAddr) ThrowOutOfMemory(); } @@ -749,7 +748,7 @@ HostCodeHeap::TrackAllocation* HostCodeHeap::AllocMemory_NoThrow(size_t header, if (m_pLastAvailableCommittedAddr + sizeToCommit <= m_pBaseAddr + m_TotalBytesAvailable) { - if (NULL == ClrVirtualAlloc(m_pLastAvailableCommittedAddr, sizeToCommit, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) + if (NULL == ExecutableAllocator::Instance()->Commit(m_pLastAvailableCommittedAddr, sizeToCommit, true /* isExecutable */)) { LOG((LF_BCL, LL_ERROR, "CodeHeap [0x%p] - VirtualAlloc failed\n", this)); return NULL; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a1fdf255a5c..6bf5efcc802 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6699,14 +6699,12 @@ AdjustContextForJITHelpers( PCODE ip = GetIP(pContext); -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(ip)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame ip = AdjustWriteBarrierIP(ip); SetIP(pContext, ip); } -#endif // FEATURE_WRITEBARRIER_COPY #ifdef FEATURE_DATABREAKPOINT diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 7fff234ca85..4af702fab14 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -4694,14 +4694,12 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT break; } -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame controlPc = AdjustWriteBarrierIP(controlPc); SetIP(frameContext, controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY UINT_PTR sp = GetSP(frameContext); @@ -5174,13 +5172,11 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e { PCODE controlPc = GetIP(contextRecord); -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location controlPc = AdjustWriteBarrierIP(controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY return g_fEEStarted && ( exceptionRecord->ExceptionCode == STATUS_BREAKPOINT || @@ -5259,14 +5255,12 @@ BOOL HandleHardwareException(PAL_SEHException* ex) { GCX_COOP(); // Must be cooperative to modify frame chain. -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(controlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame controlPc = AdjustWriteBarrierIP(controlPc); SetIP(ex->GetContextRecord(), controlPc); } -#endif // FEATURE_WRITEBARRIER_COPY if (IsIPInMarkedJitHelper(controlPc)) { diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index be856dbe1a6..9ce0cc676f7 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1258,9 +1258,9 @@ void RemoveGcCoverageInterrupt(TADDR instrPtr, BYTE * savedInstrPtr, GCCoverageI { ExecutableWriterHolder instrPtrWriterHolder((void*)instrPtr, 4); #ifdef TARGET_ARM - if (GetARMInstructionLength(savedInstrPtr) == 2) + if (GetARMInstructionLength(savedInstrPtr) == 2) *(WORD *)instrPtrWriterHolder.GetRW() = *(WORD *)savedInstrPtr; - else + else *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; #elif defined(TARGET_ARM64) *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; diff --git a/src/coreclr/vm/i386/jithelp.S b/src/coreclr/vm/i386/jithelp.S index facce7cacd3..dc56da1d177 100644 --- a/src/coreclr/vm/i386/jithelp.S +++ b/src/coreclr/vm/i386/jithelp.S @@ -377,10 +377,27 @@ LEAF_ENTRY JIT_WriteBarrierGroup, _TEXT ret LEAF_END JIT_WriteBarrierGroup, _TEXT -#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS -// ******************************************************************************* -// Write barrier wrappers with fcall calling convention -// + .data + .align 4 + .global C_FUNC(JIT_WriteBarrierEAX_Loc) +C_FUNC(JIT_WriteBarrierEAX_Loc): + .word 0 + .text + +LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT + mov eax, edx + mov edx, ecx + push eax + call 1f +1: + pop eax +2: + add eax, offset _GLOBAL_OFFSET_TABLE_+1 // (2b - 1b) + mov eax, dword ptr [eax + C_FUNC(JIT_WriteBarrierEAX_Loc)@GOT] + xchg eax, dword ptr [esp] + ret +LEAF_END JIT_WriteBarrier_Callable, _TEXT + .macro UniversalWriteBarrierHelper name .align 4 @@ -392,6 +409,11 @@ LEAF_END JIT_\name, _TEXT .endm +#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS +// ******************************************************************************* +// Write barrier wrappers with fcall calling convention +// + // Only define these if we're using the ASM GC write barriers; if this flag is not defined, // we'll use C++ versions of these write barriers. UniversalWriteBarrierHelper CheckedWriteBarrier diff --git a/src/coreclr/vm/i386/jithelp.asm b/src/coreclr/vm/i386/jithelp.asm index 3743ac3cbe0..3650b3f2afd 100644 --- a/src/coreclr/vm/i386/jithelp.asm +++ b/src/coreclr/vm/i386/jithelp.asm @@ -411,6 +411,31 @@ ENDM ;******************************************************************************* ; Write barrier wrappers with fcall calling convention ; + + .data + ALIGN 4 + public _JIT_WriteBarrierEAX_Loc +_JIT_WriteBarrierEAX_Loc dd 0 + + .code + +; WriteBarrierStart and WriteBarrierEnd are used to determine bounds of +; WriteBarrier functions so can determine if got AV in them. +; +PUBLIC _JIT_WriteBarrierGroup@0 +_JIT_WriteBarrierGroup@0 PROC +ret +_JIT_WriteBarrierGroup@0 ENDP + + ALIGN 4 +PUBLIC @JIT_WriteBarrier_Callable@8 +@JIT_WriteBarrier_Callable@8 PROC + mov eax,edx + mov edx,ecx + jmp DWORD PTR [_JIT_WriteBarrierEAX_Loc] + +@JIT_WriteBarrier_Callable@8 ENDP + UniversalWriteBarrierHelper MACRO name ALIGN 4 PUBLIC @JIT_&name&@8 @@ -421,14 +446,6 @@ PUBLIC @JIT_&name&@8 @JIT_&name&@8 ENDP ENDM -; WriteBarrierStart and WriteBarrierEnd are used to determine bounds of -; WriteBarrier functions so can determine if got AV in them. -; -PUBLIC _JIT_WriteBarrierGroup@0 -_JIT_WriteBarrierGroup@0 PROC -ret -_JIT_WriteBarrierGroup@0 ENDP - ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS ; Only define these if we're using the ASM GC write barriers; if this flag is not defined, ; we'll use C++ versions of these write barriers. @@ -1233,6 +1250,8 @@ fremloopd: ; PatchedCodeStart and PatchedCodeEnd are used to determine bounds of patched code. ; + ALIGN 4 + _JIT_PatchedCodeStart@0 proc public ret _JIT_PatchedCodeStart@0 endp diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 0e366bdbd1a..0467f347aaa 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -1050,10 +1050,18 @@ void InitJITHelpers1() { BYTE * pfunc = (BYTE *) JIT_WriteBarrierReg_PreGrow; - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); int reg = c_rgWriteBarrierRegs[iBarrier]; - memcpy(pBuf, pfunc, 34); + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 34); + pBufRW = barrierWriterHolder.GetRW(); + } + + memcpy(pBufRW, pfunc, 34); // assert the copied code ends in a ret to make sure we got the right length _ASSERTE(pBuf[33] == 0xC3); @@ -1069,24 +1077,24 @@ void InitJITHelpers1() _ASSERTE(pBuf[0] == 0x89); // Update the reg field (bits 3..5) of the ModR/M byte of this instruction - pBuf[1] &= 0xc7; - pBuf[1] |= reg << 3; + pBufRW[1] &= 0xc7; + pBufRW[1] |= reg << 3; // Second instruction to patch is cmp reg, imm32 (low bound) _ASSERTE(pBuf[2] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[3] &= 0xf8; - pBuf[3] |= reg; + pBufRW[3] &= 0xf8; + pBufRW[3] |= reg; #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization just jump to the old one // Use the slow one from time to time in a debug build because // there are some good asserts in the unoptimized one if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK) || DEBUG_RANDOM_BARRIER_CHECK) { - pfunc = &pBuf[0]; + pfunc = &pBufRW[0]; *pfunc++ = 0xE9; // JMP c_rgDebugWriteBarriers[iBarrier] - *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (pfunc + sizeof(DWORD)); + *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (&pBuf[1] + sizeof(DWORD)); } #endif // WRITE_BARRIER_CHECK } @@ -1132,7 +1140,7 @@ void ValidateWriteBarrierHelpers() #endif // WRITE_BARRIER_CHECK // first validate the PreGrow helper - BYTE* pWriteBarrierFunc = reinterpret_cast(JIT_WriteBarrierEAX); + BYTE* pWriteBarrierFunc = GetWriteBarrierCodeLocation(reinterpret_cast(JIT_WriteBarrierEAX)); // ephemeral region DWORD* pLocation = reinterpret_cast(&pWriteBarrierFunc[AnyGrow_EphemeralLowerBound]); @@ -1170,7 +1178,7 @@ void ValidateWriteBarrierHelpers() #endif //CODECOVERAGE /*********************************************************************/ -#define WriteBarrierIsPreGrow() (((BYTE *)JIT_WriteBarrierEAX)[10] == 0xc1) +#define WriteBarrierIsPreGrow() ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[10] == 0xc1) /*********************************************************************/ @@ -1188,20 +1196,28 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization if we are checking write barrier - if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier + if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier return stompWBCompleteActions; #endif // WRITE_BARRIER_CHECK // Update the lower bound. for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++) { - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); + + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 42); + pBufRW = barrierWriterHolder.GetRW(); + } // assert there is in fact a cmp r/m32, imm32 there _ASSERTE(pBuf[2] == 0x81); // Update the immediate which is the lower bound of the ephemeral generation - size_t *pfunc = (size_t *) &pBuf[AnyGrow_EphemeralLowerBound]; + size_t *pfunc = (size_t *) &pBufRW[AnyGrow_EphemeralLowerBound]; //avoid trivial self modifying code if (*pfunc != (size_t) g_ephemeral_low) { @@ -1214,7 +1230,7 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) _ASSERTE(pBuf[10] == 0x81); // Update the upper bound if we are using the PostGrow thunk. - pfunc = (size_t *) &pBuf[PostGrow_EphemeralUpperBound]; + pfunc = (size_t *) &pBufRW[PostGrow_EphemeralUpperBound]; //avoid trivial self modifying code if (*pfunc != (size_t) g_ephemeral_high) { @@ -1244,7 +1260,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) #ifdef WRITE_BARRIER_CHECK // Don't do the fancy optimization if we are checking write barrier - if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier + if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier return stompWBCompleteActions; #endif // WRITE_BARRIER_CHECK @@ -1253,12 +1269,20 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++) { - BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier]; + BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]); int reg = c_rgWriteBarrierRegs[iBarrier]; size_t *pfunc; - // Check if we are still using the pre-grow version of the write barrier. + BYTE * pBufRW = pBuf; + ExecutableWriterHolder barrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + barrierWriterHolder = ExecutableWriterHolder(pBuf, 42); + pBufRW = barrierWriterHolder.GetRW(); + } + + // Check if we are still using the pre-grow version of the write barrier. if (bWriteBarrierIsPreGrow) { // Check if we need to use the upper bounds checking barrier stub. @@ -1271,7 +1295,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) } pfunc = (size_t *) JIT_WriteBarrierReg_PostGrow; - memcpy(pBuf, pfunc, 42); + memcpy(pBufRW, pfunc, 42); // assert the copied code ends in a ret to make sure we got the right length _ASSERTE(pBuf[41] == 0xC3); @@ -1287,35 +1311,35 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) _ASSERTE(pBuf[0] == 0x89); // Update the reg field (bits 3..5) of the ModR/M byte of this instruction - pBuf[1] &= 0xc7; - pBuf[1] |= reg << 3; + pBufRW[1] &= 0xc7; + pBufRW[1] |= reg << 3; // Second instruction to patch is cmp reg, imm32 (low bound) _ASSERTE(pBuf[2] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[3] &= 0xf8; - pBuf[3] |= reg; + pBufRW[3] &= 0xf8; + pBufRW[3] |= reg; // Third instruction to patch is another cmp reg, imm32 (high bound) _ASSERTE(pBuf[10] == 0x81); // Here the lowest three bits in ModR/M field are the register - pBuf[11] &= 0xf8; - pBuf[11] |= reg; + pBufRW[11] &= 0xf8; + pBufRW[11] |= reg; bStompWriteBarrierEphemeral = true; // What we're trying to update is the offset field of a // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[22] == 0x80); - pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[34] == 0xC6); - pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation]; } else @@ -1324,14 +1348,14 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[14] == 0x80); - pfunc = (size_t *) &pBuf[PreGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PreGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[26] == 0xC6); - pfunc = (size_t *) &pBuf[PreGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PreGrow_CardTableSecondLocation]; } } else @@ -1340,13 +1364,13 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) // cmp offset[edx], 0ffh instruction _ASSERTE(pBuf[22] == 0x80); - pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation]; *pfunc = (size_t) g_card_table; // What we're trying to update is the offset field of a // mov offset[edx], 0ffh instruction _ASSERTE(pBuf[34] == 0xC6); - pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation]; + pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation]; } // Stick in the adjustment value. diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index 61c5dfd90cb..564363053fc 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -4829,7 +4829,7 @@ COPY_VALUE_CLASS: X86EmitOp(0x8d, kEDX, elemBaseReg, elemOfs, elemScaledReg, elemScale); // call JIT_Writeable_Thunks_Buf.WriteBarrierReg[0] (== EAX) - X86EmitCall(NewExternalCodeLabel((LPVOID) &JIT_WriteBarrierEAX), 0); + X86EmitCall(NewExternalCodeLabel((LPVOID) GetWriteBarrierCodeLocation(&JIT_WriteBarrierEAX)), 0); } else #else // TARGET_AMD64 diff --git a/src/coreclr/vm/i386/stublinkerx86.h b/src/coreclr/vm/i386/stublinkerx86.h index af5244d0771..564c999975e 100644 --- a/src/coreclr/vm/i386/stublinkerx86.h +++ b/src/coreclr/vm/i386/stublinkerx86.h @@ -536,7 +536,7 @@ struct StubPrecode { return rel32Decode(PTR_HOST_MEMBER_TADDR(StubPrecode, this, m_rel32)); } - +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { CONTRACTL @@ -562,6 +562,7 @@ struct StubPrecode { ExecutableWriterHolder rel32Holder(&m_rel32, 4); return rel32SetInterlocked(&m_rel32, rel32Holder.GetRW(), target, expected, (MethodDesc*)GetMethodDesc()); } +#endif // !DACCESS_COMPILE }; IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_movR10) == OFFSETOF_PRECODE_TYPE);) IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_type) == OFFSETOF_PRECODE_TYPE_MOV_R10);) @@ -646,6 +647,13 @@ struct FixupPrecode { return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); } + size_t GetSizeRW() + { + LIMITED_METHOD_CONTRACT; + + return GetBase() + sizeof(void*) - dac_cast(this); + } + TADDR GetMethodDesc(); #else // HAS_FIXUP_PRECODE_CHUNKS TADDR GetMethodDesc() diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a1e4d93d881..882e2c29cef 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11875,7 +11875,7 @@ WORD CEEJitInfo::getRelocTypeHint(void * target) if (m_fAllowRel32) { // The JIT calls this method for data addresses only. It always uses REL32s for direct code targets. - if (IsPreferredExecutableRange(target)) + if (ExecutableAllocator::IsPreferredExecutableRange(target)) return IMAGE_REL_BASED_REL32; } #endif // TARGET_AMD64 diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index ca9d03c2141..e071d0717d1 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -238,15 +238,10 @@ extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj); -#if defined(TARGET_ARM64) || defined(FEATURE_WRITEBARRIER_COPY) // ARM64 JIT_WriteBarrier uses speciall ABI and thus is not callable directly // Copied write barriers must be called at a different location extern "C" FCDECL2(VOID, JIT_WriteBarrier_Callable, Object **dst, Object *ref); #define WriteBarrier_Helper JIT_WriteBarrier_Callable -#else -// in other cases the regular JIT helper is callable. -#define WriteBarrier_Helper JIT_WriteBarrier -#endif extern "C" FCDECL1(void, JIT_InternalThrow, unsigned exceptNum); extern "C" FCDECL1(void*, JIT_InternalThrowFromHelper, unsigned exceptNum); @@ -344,28 +339,25 @@ EXTERN_C FCDECL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift); #ifdef TARGET_X86 +#define ENUM_X86_WRITE_BARRIER_REGISTERS() \ + X86_WRITE_BARRIER_REGISTER(EAX) \ + X86_WRITE_BARRIER_REGISTER(ECX) \ + X86_WRITE_BARRIER_REGISTER(EBX) \ + X86_WRITE_BARRIER_REGISTER(ESI) \ + X86_WRITE_BARRIER_REGISTER(EDI) \ + X86_WRITE_BARRIER_REGISTER(EBP) + extern "C" { - void STDCALL JIT_CheckedWriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_CheckedWriteBarrierEBP(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_DebugWriteBarrierEBP(); // JIThelp.asm/JIThelp.s +// JIThelp.asm/JIThelp.s +#define X86_WRITE_BARRIER_REGISTER(reg) \ + void STDCALL JIT_CheckedWriteBarrier##reg(); \ + void STDCALL JIT_DebugWriteBarrier##reg(); \ + void STDCALL JIT_WriteBarrier##reg(); - void STDCALL JIT_WriteBarrierEAX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEBX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierECX(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierESI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEDI(); // JIThelp.asm/JIThelp.s - void STDCALL JIT_WriteBarrierEBP(); // JIThelp.asm/JIThelp.s + ENUM_X86_WRITE_BARRIER_REGISTERS() +#undef X86_WRITE_BARRIER_REGISTER void STDCALL JIT_WriteBarrierGroup(); void STDCALL JIT_WriteBarrierGroup_End(); diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 4f222be4a2c..0a77e4445f0 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1137,7 +1137,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) _ASSERTE(dwTotalReserveMemSize <= VIRTUAL_ALLOC_RESERVE_GRANULARITY); #endif - BYTE * initReservedMem = ClrVirtualAllocExecutable(dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS); + BYTE * initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize); m_InitialReservedMemForLoaderHeaps = initReservedMem; @@ -1672,18 +1672,25 @@ void AssemblyLoaderAllocator::SetCollectible() { CONTRACTL { - THROWS; + NOTHROW; } CONTRACTL_END; m_IsCollectible = true; -#ifndef DACCESS_COMPILE - m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); -#endif } #ifndef DACCESS_COMPILE +void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain) +{ + m_Id.Init(); + LoaderAllocator::Init((BaseDomain *)pAppDomain); + if (IsCollectible()) + { + m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); + } +} + #ifndef CROSSGEN_COMPILE AssemblyLoaderAllocator::~AssemblyLoaderAllocator() diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl index a826675ccc9..993732d4010 100644 --- a/src/coreclr/vm/loaderallocator.inl +++ b/src/coreclr/vm/loaderallocator.inl @@ -21,12 +21,6 @@ inline void GlobalLoaderAllocator::Init(BaseDomain *pDomain) LoaderAllocator::Init(pDomain, m_ExecutableHeapInstance); } -inline void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain) -{ - m_Id.Init(); - LoaderAllocator::Init((BaseDomain *)pAppDomain); -} - inline BOOL LoaderAllocatorID::Equals(LoaderAllocatorID *pId) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index bd3984d8697..db308ab208a 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -4188,46 +4188,6 @@ c_CentralJumpCode = { }; #include -#elif defined(TARGET_AMD64) - -#include -static const struct CentralJumpCode { - BYTE m_movzxRAX[4]; - BYTE m_shlEAX[4]; - BYTE m_movRAX[2]; - MethodDesc* m_pBaseMD; - BYTE m_addR10RAX[3]; - BYTE m_jmp[1]; - INT32 m_rel32; - - inline void Setup(CentralJumpCode* pCodeRX, MethodDesc* pMD, PCODE target, LoaderAllocator *pLoaderAllocator) { - WRAPPER_NO_CONTRACT; - m_pBaseMD = pMD; - m_rel32 = rel32UsingJumpStub(&pCodeRX->m_rel32, target, pMD, pLoaderAllocator); - } - - inline BOOL CheckTarget(TADDR target) { - WRAPPER_NO_CONTRACT; - TADDR addr = rel32Decode(PTR_HOST_MEMBER_TADDR(CentralJumpCode, this, m_rel32)); - if (*PTR_BYTE(addr) == 0x48 && - *PTR_BYTE(addr+1) == 0xB8 && - *PTR_BYTE(addr+10) == 0xFF && - *PTR_BYTE(addr+11) == 0xE0) - { - addr = *PTR_TADDR(addr+2); - } - return (addr == target); - } -} -c_CentralJumpCode = { - { 0x48, 0x0F, 0xB6, 0xC0 }, // movzx rax,al - { 0x48, 0xC1, 0xE0, MethodDesc::ALIGNMENT_SHIFT }, // shl rax, MethodDesc::ALIGNMENT_SHIFT - { 0x49, 0xBA }, NULL, // mov r10, pBaseMD - { 0x4C, 0x03, 0xD0 }, // add r10,rax - { 0xE9 }, 0 // jmp PreStub -}; -#include - #elif defined(TARGET_ARM) #include diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index 80731c191e7..0bd2bd657f9 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -480,7 +480,9 @@ void Precode::Reset() #ifdef HAS_FIXUP_PRECODE_CHUNKS if (t == PRECODE_FIXUP) { - size = sizeof(FixupPrecode) + sizeof(PTR_MethodDesc); + // The writeable size the Init method accesses is dynamic depending on + // the FixupPrecode members. + size = ((FixupPrecode*)this)->GetSizeRW(); } else #endif diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 0971334af4d..e61802b9849 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -713,14 +713,12 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext) // get our caller's PSP, or our caller's caller's SP. while (!ExecutionManager::IsManagedCode(uControlPc)) { -#ifdef FEATURE_WRITEBARRIER_COPY if (IsIPInWriteBarrierCodeCopy(uControlPc)) { // Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame uControlPc = AdjustWriteBarrierIP(uControlPc); SetIP(pContext, uControlPc); } -#endif // FEATURE_WRITEBARRIER_COPY #ifndef TARGET_UNIX uControlPc = VirtualUnwindCallFrame(pContext); diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 04a33e39826..304cb4fb35b 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -846,7 +846,7 @@ Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags) ); ASSERT(pStub != NULL); - bool fSuccess = EmitStub(pStub, globalsize, pHeap); + bool fSuccess = EmitStub(pStub, globalsize, size, pHeap); #ifdef STUBLINKER_GENERATES_UNWIND_INFO if (fSuccess) @@ -1007,13 +1007,13 @@ int StubLinker::CalculateSize(int* pGlobalSize) return globalsize + datasize; } -bool StubLinker::EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap) +bool StubLinker::EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap) { STANDARD_VM_CONTRACT; BYTE *pCode = (BYTE*)(pStub->GetBlob()); - ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub)); + ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub) + totalSize); Stub *pStubRW = stubWriterHolder.GetRW(); BYTE *pCodeRW = (BYTE*)(pStubRW->GetBlob()); @@ -2013,11 +2013,7 @@ VOID Stub::DeleteStub() FillMemory(this+1, m_numCodeBytes, 0xcc); #endif -#ifndef TARGET_UNIX - DeleteExecutable((BYTE*)GetAllocationBase()); -#else delete [] (BYTE*)GetAllocationBase(); -#endif } } @@ -2124,11 +2120,7 @@ Stub* Stub::NewStub(PTR_VOID pCode, DWORD flags) BYTE *pBlock; if (pHeap == NULL) { -#ifndef TARGET_UNIX - pBlock = new (executable) BYTE[totalSize]; -#else pBlock = new BYTE[totalSize]; -#endif } else { diff --git a/src/coreclr/vm/stublink.h b/src/coreclr/vm/stublink.h index 94326f9962e..9613fd48f68 100644 --- a/src/coreclr/vm/stublink.h +++ b/src/coreclr/vm/stublink.h @@ -395,7 +395,7 @@ private: // Writes out the code element into memory following the // stub object. - bool EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap); + bool EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap); CodeRun *GetLastCodeRunIfAny(); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index fa93110399d..2c55f8770b0 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1078,18 +1078,30 @@ DWORD_PTR Thread::OBJREF_HASH = OBJREF_TABSIZE; extern "C" void STDCALL JIT_PatchedCodeStart(); extern "C" void STDCALL JIT_PatchedCodeLast(); -#ifdef FEATURE_WRITEBARRIER_COPY - static void* s_barrierCopy = NULL; BYTE* GetWriteBarrierCodeLocation(VOID* barrier) { - return (BYTE*)s_barrierCopy + ((BYTE*)barrier - (BYTE*)JIT_PatchedCodeStart); + if (IsWriteBarrierCopyEnabled()) + { + return (BYTE*)PINSTRToPCODE((TADDR)s_barrierCopy + ((TADDR)barrier - (TADDR)JIT_PatchedCodeStart)); + } + else + { + return (BYTE*)barrier; + } } BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc) { - return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart))); + if (IsWriteBarrierCopyEnabled()) + { + return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart))); + } + else + { + return FALSE; + } } PCODE AdjustWriteBarrierIP(PCODE controlPc) @@ -1100,14 +1112,21 @@ PCODE AdjustWriteBarrierIP(PCODE controlPc) return (PCODE)JIT_PatchedCodeStart + (controlPc - (PCODE)s_barrierCopy); } +#ifdef TARGET_X86 +extern "C" void *JIT_WriteBarrierEAX_Loc; +#else extern "C" void *JIT_WriteBarrier_Loc; +#endif + #ifdef TARGET_ARM64 extern "C" void (*JIT_WriteBarrier_Table)(); extern "C" void *JIT_WriteBarrier_Loc = 0; extern "C" void *JIT_WriteBarrier_Table_Loc = 0; #endif // TARGET_ARM64 -#endif // FEATURE_WRITEBARRIER_COPY +#ifdef TARGET_ARM +extern "C" void *JIT_WriteBarrier_Loc = 0; +#endif // TARGET_ARM #ifndef TARGET_UNIX // g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out. @@ -1138,50 +1157,80 @@ void InitThreadManager() _ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart > (ptrdiff_t)0); _ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart < (ptrdiff_t)GetOsPageSize()); -#ifdef FEATURE_WRITEBARRIER_COPY - s_barrierCopy = ClrVirtualAlloc(NULL, g_SystemInfo.dwAllocationGranularity, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (s_barrierCopy == NULL) + if (IsWriteBarrierCopyEnabled()) { - _ASSERTE(!"ClrVirtualAlloc of GC barrier code page failed"); - COMPlusThrowWin32(); - } + s_barrierCopy = ExecutableAllocator::Instance()->Reserve(g_SystemInfo.dwAllocationGranularity); + ExecutableAllocator::Instance()->Commit(s_barrierCopy, g_SystemInfo.dwAllocationGranularity, true); + if (s_barrierCopy == NULL) + { + _ASSERTE(!"Allocation of GC barrier code page failed"); + COMPlusThrowWin32(); + } - { - size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; - ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize); - memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize); - } + { + size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; + ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize); + memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize); + } - // Store the JIT_WriteBarrier copy location to a global variable so that helpers - // can jump to it. - JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); + // Store the JIT_WriteBarrier copy location to a global variable so that helpers + // can jump to it. +#ifdef TARGET_X86 + JIT_WriteBarrierEAX_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrierEAX); - SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); +#define X86_WRITE_BARRIER_REGISTER(reg) \ + SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF_##reg, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg)); \ + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("@WriteBarrier" #reg)); + + ENUM_X86_WRITE_BARRIER_REGISTERS() + +#undef X86_WRITE_BARRIER_REGISTER + +#else // TARGET_X86 + JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); +#endif // TARGET_X86 + SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier")); #ifdef TARGET_ARM64 - // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. - JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table); - - SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); - SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); + // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. + JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table); #endif // TARGET_ARM64 -#else // FEATURE_WRITEBARRIER_COPY +#if defined(TARGET_ARM64) || defined(TARGET_ARM) + SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier")); + SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); + ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier")); +#endif // TARGET_ARM64 || TARGET_ARM - // I am using virtual protect to cover the entire range that this code falls in. - // - - // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth, - // so instead we'll leave it writable from here forward. - - DWORD oldProt; - if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart, - PAGE_EXECUTE_READWRITE, &oldProt)) - { - _ASSERTE(!"ClrVirtualProtect of code page failed"); - COMPlusThrowWin32(); } -#endif // FEATURE_WRITEBARRIER_COPY + else + { + // I am using virtual protect to cover the entire range that this code falls in. + // + + // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth, + // so instead we'll leave it writable from here forward. + + DWORD oldProt; + if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart, + PAGE_EXECUTE_READWRITE, &oldProt)) + { + _ASSERTE(!"ClrVirtualProtect of code page failed"); + COMPlusThrowWin32(); + } + +#ifdef TARGET_X86 + JIT_WriteBarrierEAX_Loc = (void*)JIT_WriteBarrierEAX; +#else + JIT_WriteBarrier_Loc = (void*)JIT_WriteBarrier; +#endif +#ifdef TARGET_ARM64 + // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. + JIT_WriteBarrier_Table_Loc = (void*)&JIT_WriteBarrier_Table; +#endif // TARGET_ARM64 + } #ifndef TARGET_UNIX _ASSERTE(GetThreadNULLOk() == NULL); diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index d18b21d58f9..7d600dab5ed 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -6271,18 +6271,23 @@ private: BOOL Debug_IsLockedViaThreadSuspension(); -#ifdef FEATURE_WRITEBARRIER_COPY +inline BOOL IsWriteBarrierCopyEnabled() +{ +#ifdef DACCESS_COMPILE + return FALSE; +#else // DACCESS_COMPILE +#ifdef HOST_OSX + return TRUE; +#else + return ExecutableAllocator::IsWXORXEnabled(); +#endif +#endif // DACCESS_COMPILE +} BYTE* GetWriteBarrierCodeLocation(VOID* barrier); BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc); PCODE AdjustWriteBarrierIP(PCODE controlPc); -#else // FEATURE_WRITEBARRIER_COPY - -#define GetWriteBarrierCodeLocation(barrier) ((BYTE*)(barrier)) - -#endif // FEATURE_WRITEBARRIER_COPY - #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) extern thread_local Thread* t_pStackWalkerWalkingThread; #define SET_THREAD_TYPE_STACKWALKER(pThread) t_pStackWalkerWalkingThread = pThread diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 95d568d641c..3af4c52afc9 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -641,7 +641,7 @@ void VirtualCallStubManager::Init(BaseDomain *pDomain, LoaderAllocator *pLoaderA dwTotalReserveMemSize); } - initReservedMem = ClrVirtualAllocExecutable (dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS); + initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize); m_initialReservedMemForHeaps = (BYTE *) initReservedMem; @@ -2766,11 +2766,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad } #endif - ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder) -#ifdef TARGET_AMD64 - + sizeof(DispatchStubShort) -#endif - ); + ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize); dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode, addrOfFail, (size_t)pMTExpected @@ -2833,9 +2829,9 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE } CONTRACT_END; //allocate from the requisite heap and copy the template over it. - DispatchHolder * holder = (DispatchHolder*) (void*) - dispatch_heap->AllocAlignedMem(DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG), CODE_SIZE_ALIGN); - ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder) + sizeof(DispatchStubLong)); + size_t dispatchHolderSize = DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG); + DispatchHolder * holder = (DispatchHolder*) (void*)dispatch_heap->AllocAlignedMem(dispatchHolderSize, CODE_SIZE_ALIGN); + ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize); dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode, addrOfFail, From bb3982239e11d559f839aba445ea7d698084e623 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 11 Jul 2021 08:24:08 -0700 Subject: [PATCH 424/926] Adding more tests for the generic math feature (#55377) * Fixing some issues in the generic math implementation * Adding generic math tests for integer types --- .../System.Private.CoreLib/src/System/Byte.cs | 12 +- .../System.Private.CoreLib/src/System/Char.cs | 12 +- .../src/System/Decimal.cs | 8 +- .../src/System/Double.cs | 8 +- .../System.Private.CoreLib/src/System/Half.cs | 8 +- .../src/System/IInteger.cs | 4 +- .../src/System/Int16.cs | 18 +- .../src/System/Int32.cs | 8 +- .../src/System/Int64.cs | 16 +- .../src/System/IntPtr.cs | 56 +- .../src/System/SByte.cs | 18 +- .../src/System/Single.cs | 8 +- .../src/System/UInt16.cs | 12 +- .../src/System/UInt32.cs | 16 +- .../src/System/UInt64.cs | 16 +- .../src/System/UIntPtr.cs | 60 +- .../System.Runtime/ref/System.Runtime.cs | 44 +- .../tests/System.Runtime.Tests.csproj | 22 +- .../tests/System/ByteTests.GenericMath.cs | 1175 +++++++++++ .../tests/System/CharTests.GenericMath.cs | 1057 ++++++++++ .../tests/System/GenericMathHelpers.cs | 228 +++ .../tests/System/GenericMathTests.cs | 73 - .../tests/System/Int16Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/Int32GenericMathTests.cs | 35 - .../tests/System/Int32Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/Int64Tests.GenericMath.cs | 1181 +++++++++++ .../tests/System/IntPtrTests.GenericMath.cs | 1746 +++++++++++++++++ .../tests/System/SByteTests.GenericMath.cs | 1181 +++++++++++ .../tests/System/UInt16Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UInt32Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UInt64Tests.GenericMath.cs | 1175 +++++++++++ .../tests/System/UIntPtrTests.GenericMath.cs | 1695 ++++++++++++++++ 32 files changed, 14331 insertions(+), 273 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs delete mode 100644 src/libraries/System.Runtime/tests/System/GenericMathTests.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs delete mode 100644 src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs create mode 100644 src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 39bfeae2a7e..3d5b448159a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -313,11 +313,11 @@ namespace System => (byte)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) + static byte IBinaryInteger.RotateLeft(byte value, int rotateAmount) => (byte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] - static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) + static byte IBinaryInteger.RotateRight(byte value, int rotateAmount) => (byte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] @@ -382,11 +382,11 @@ namespace System [RequiresPreviewFeatures] static byte IDecrementOperators.operator --(byte value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked byte IDecrementOperators.operator --(byte value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -418,11 +418,11 @@ namespace System [RequiresPreviewFeatures] static byte IIncrementOperators.operator ++(byte value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked byte IIncrementOperators.operator ++(byte value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 2f58b54b746..26ff3399fe1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1093,11 +1093,11 @@ namespace System => (char)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static char IBinaryInteger.RotateLeft(char value, char rotateAmount) + static char IBinaryInteger.RotateLeft(char value, int rotateAmount) => (char)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static char IBinaryInteger.RotateRight(char value, char rotateAmount) + static char IBinaryInteger.RotateRight(char value, int rotateAmount) => (char)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] @@ -1162,11 +1162,11 @@ namespace System [RequiresPreviewFeatures] static char IDecrementOperators.operator --(char value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked char IDecrementOperators.operator --(char value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -1198,11 +1198,11 @@ namespace System [RequiresPreviewFeatures] static char IIncrementOperators.operator ++(char value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked char IIncrementOperators.operator ++(char value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index e5f26f2ff01..5c4f30795cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -1126,11 +1126,11 @@ namespace System [RequiresPreviewFeatures] static decimal IDecrementOperators.operator --(decimal value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked decimal IDecrementOperators.operator --(decimal value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -1162,11 +1162,11 @@ namespace System [RequiresPreviewFeatures] static decimal IIncrementOperators.operator ++(decimal value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked decimal IIncrementOperators.operator ++(decimal value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 85d5f8c36f1..10178510f63 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -550,11 +550,11 @@ namespace System [RequiresPreviewFeatures] static double IDecrementOperators.operator --(double value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked double IDecrementOperators.operator --(double value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -828,11 +828,11 @@ namespace System [RequiresPreviewFeatures] static double IIncrementOperators.operator ++(double value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked double IIncrementOperators.operator ++(double value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index ea64c10f835..c70cb712bfe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -802,7 +802,7 @@ namespace System static Half IDecrementOperators.operator --(Half value) { var tmp = (float)value; - tmp--; + --tmp; return (Half)tmp; } @@ -810,7 +810,7 @@ namespace System // static checked Half IDecrementOperators.operator --(Half value) // { // var tmp = (float)value; - // tmp--; + // --tmp; // return (Half)tmp; // } @@ -1132,7 +1132,7 @@ namespace System static Half IIncrementOperators.operator ++(Half value) { var tmp = (float)value; - tmp++; + ++tmp; return (Half)tmp; } @@ -1140,7 +1140,7 @@ namespace System // static checked Half IIncrementOperators.operator ++(Half value) // { // var tmp = (float)value; - // tmp++; + // ++tmp; // return (Half)tmp; // } diff --git a/src/libraries/System.Private.CoreLib/src/System/IInteger.cs b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs index a93b439625e..89ec036bb14 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IInteger.cs @@ -31,13 +31,13 @@ namespace System /// The value which is rotated left by . /// The amount by which is rotated left. /// The result of rotating left by . - static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateLeft(TSelf value, int rotateAmount); /// Rotates a value right by a given amount. /// The value which is rotated right by . /// The amount by which is rotated right. /// The result of rotating right by . - static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateRight(TSelf value, int rotateAmount); /// Computes the number of trailing zeros in a value. /// The value whose trailing zeroes are to be counted. diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index f42c84de2bd..909b37f77b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -316,12 +316,12 @@ namespace System => (short)BitOperations.PopCount((ushort)value); [RequiresPreviewFeatures] - static short IBinaryInteger.RotateLeft(short value, short rotateAmount) - => (short)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); + static short IBinaryInteger.RotateLeft(short value, int rotateAmount) + => (short)((value << (rotateAmount & 15)) | ((ushort)value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static short IBinaryInteger.RotateRight(short value, short rotateAmount) - => (short)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); + static short IBinaryInteger.RotateRight(short value, int rotateAmount) + => (short)(((ushort)value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] static short IBinaryInteger.TrailingZeroCount(short value) @@ -391,11 +391,11 @@ namespace System [RequiresPreviewFeatures] static short IDecrementOperators.operator --(short value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked short IDecrementOperators.operator --(short value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -427,11 +427,11 @@ namespace System [RequiresPreviewFeatures] static short IIncrementOperators.operator ++(short value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked short IIncrementOperators.operator ++(short value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -565,7 +565,7 @@ namespace System { if (typeof(TOther) == typeof(byte)) { - return (short)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index d8514bc89f7..68c6a70cdb0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -383,11 +383,11 @@ namespace System [RequiresPreviewFeatures] static int IDecrementOperators.operator --(int value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked int IDecrementOperators.operator --(int value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -419,11 +419,11 @@ namespace System [RequiresPreviewFeatures] static int IIncrementOperators.operator ++(int value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked int IIncrementOperators.operator ++(int value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index c0420f5052e..1646cf96457 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -295,12 +295,12 @@ namespace System => BitOperations.PopCount((ulong)value); [RequiresPreviewFeatures] - static long IBinaryInteger.RotateLeft(long value, long rotateAmount) - => (long)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + static long IBinaryInteger.RotateLeft(long value, int rotateAmount) + => (long)BitOperations.RotateLeft((ulong)value, rotateAmount); [RequiresPreviewFeatures] - static long IBinaryInteger.RotateRight(long value, long rotateAmount) - => (long)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + static long IBinaryInteger.RotateRight(long value, int rotateAmount) + => (long)BitOperations.RotateRight((ulong)value, rotateAmount); [RequiresPreviewFeatures] static long IBinaryInteger.TrailingZeroCount(long value) @@ -370,11 +370,11 @@ namespace System [RequiresPreviewFeatures] static long IDecrementOperators.operator --(long value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked long IDecrementOperators.operator --(long value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -406,11 +406,11 @@ namespace System [RequiresPreviewFeatures] static long IIncrementOperators.operator ++(long value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked long IIncrementOperators.operator ++(long value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index f64effb167a..a9592617b2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -278,11 +278,11 @@ namespace System { if (Environment.Is64BitProcess) { - return BitOperations.LeadingZeroCount((uint)value); + return BitOperations.LeadingZeroCount((ulong)value); } else { - return BitOperations.LeadingZeroCount((ulong)value); + return BitOperations.LeadingZeroCount((uint)value); } } @@ -290,39 +290,39 @@ namespace System static nint IBinaryInteger.PopCount(nint value) { if (Environment.Is64BitProcess) - { - return BitOperations.PopCount((uint)value); - } - else { return BitOperations.PopCount((ulong)value); } - } - - [RequiresPreviewFeatures] - static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) - { - if (Environment.Is64BitProcess) - { - return (nint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); - } else { - return (nint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + return BitOperations.PopCount((uint)value); } } [RequiresPreviewFeatures] - static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) + static nint IBinaryInteger.RotateLeft(nint value, int rotateAmount) + { + if (Environment.Is64BitProcess) + { + return (nint)BitOperations.RotateLeft((ulong)value, rotateAmount); + } + else + { + return (nint)BitOperations.RotateLeft((uint)value, rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nint IBinaryInteger.RotateRight(nint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + return (nint)BitOperations.RotateRight((ulong)value, rotateAmount); } else { - return (nint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + return (nint)BitOperations.RotateRight((uint)value, rotateAmount); } } @@ -331,11 +331,11 @@ namespace System { if (Environment.Is64BitProcess) { - return BitOperations.TrailingZeroCount((uint)value); + return BitOperations.TrailingZeroCount((ulong)value); } else { - return BitOperations.TrailingZeroCount((ulong)value); + return BitOperations.TrailingZeroCount((uint)value); } } @@ -357,11 +357,11 @@ namespace System if (Environment.Is64BitProcess) { - return BitOperations.Log2((uint)value); + return BitOperations.Log2((ulong)value); } else { - return BitOperations.Log2((ulong)value); + return BitOperations.Log2((uint)value); } } @@ -411,11 +411,11 @@ namespace System [RequiresPreviewFeatures] static nint IDecrementOperators.operator --(nint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked nint IDecrementOperators.operator --(nint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -447,11 +447,11 @@ namespace System [RequiresPreviewFeatures] static nint IIncrementOperators.operator ++(nint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked nint IIncrementOperators.operator ++(nint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -585,7 +585,7 @@ namespace System { if (typeof(TOther) == typeof(byte)) { - return (nint)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index cec0716214f..ce811d0cffd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -318,19 +318,19 @@ namespace System [RequiresPreviewFeatures] static sbyte IBinaryInteger.LeadingZeroCount(sbyte value) - => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 16); + => (sbyte)(BitOperations.LeadingZeroCount((byte)value) - 24); [RequiresPreviewFeatures] static sbyte IBinaryInteger.PopCount(sbyte value) => (sbyte)BitOperations.PopCount((byte)value); [RequiresPreviewFeatures] - static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) - => (sbyte)((value << (rotateAmount & 7)) | (value >> ((8 - rotateAmount) & 7))); + static sbyte IBinaryInteger.RotateLeft(sbyte value, int rotateAmount) + => (sbyte)((value << (rotateAmount & 7)) | ((byte)value >> ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] - static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) - => (sbyte)((value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); + static sbyte IBinaryInteger.RotateRight(sbyte value, int rotateAmount) + => (sbyte)(((byte)value >> (rotateAmount & 7)) | (value << ((8 - rotateAmount) & 7))); [RequiresPreviewFeatures] static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) @@ -400,11 +400,11 @@ namespace System [RequiresPreviewFeatures] static sbyte IDecrementOperators.operator --(sbyte value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked sbyte IDecrementOperators.operator --(sbyte value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -436,11 +436,11 @@ namespace System [RequiresPreviewFeatures] static sbyte IIncrementOperators.operator ++(sbyte value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked sbyte IIncrementOperators.operator ++(sbyte value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 92dc3a9b6df..59dbddd253c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -542,11 +542,11 @@ namespace System [RequiresPreviewFeatures] static float IDecrementOperators.operator --(float value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked float IDecrementOperators.operator --(float value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -820,11 +820,11 @@ namespace System [RequiresPreviewFeatures] static float IIncrementOperators.operator ++(float value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked float IIncrementOperators.operator ++(float value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 218c092ed19..49c08cc99fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -307,11 +307,11 @@ namespace System => (ushort)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) + static ushort IBinaryInteger.RotateLeft(ushort value, int rotateAmount) => (ushort)((value << (rotateAmount & 15)) | (value >> ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] - static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) + static ushort IBinaryInteger.RotateRight(ushort value, int rotateAmount) => (ushort)((value >> (rotateAmount & 15)) | (value << ((16 - rotateAmount) & 15))); [RequiresPreviewFeatures] @@ -376,11 +376,11 @@ namespace System [RequiresPreviewFeatures] static ushort IDecrementOperators.operator --(ushort value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked ushort IDecrementOperators.operator --(ushort value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -412,11 +412,11 @@ namespace System [RequiresPreviewFeatures] static ushort IIncrementOperators.operator ++(ushort value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked ushort IIncrementOperators.operator ++(ushort value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 14d06e9b0ef..70f073b1e15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -293,12 +293,12 @@ namespace System => (uint)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) - => BitOperations.RotateLeft(value, (int)rotateAmount); + static uint IBinaryInteger.RotateLeft(uint value, int rotateAmount) + => BitOperations.RotateLeft(value, rotateAmount); [RequiresPreviewFeatures] - static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) - => BitOperations.RotateRight(value, (int)rotateAmount); + static uint IBinaryInteger.RotateRight(uint value, int rotateAmount) + => BitOperations.RotateRight(value, rotateAmount); [RequiresPreviewFeatures] static uint IBinaryInteger.TrailingZeroCount(uint value) @@ -362,11 +362,11 @@ namespace System [RequiresPreviewFeatures] static uint IDecrementOperators.operator --(uint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked uint IDecrementOperators.operator --(uint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -398,11 +398,11 @@ namespace System [RequiresPreviewFeatures] static uint IIncrementOperators.operator ++(uint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked uint IIncrementOperators.operator ++(uint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index e341b0bee93..6b2a45f1a8e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -292,12 +292,12 @@ namespace System => (ulong)BitOperations.PopCount(value); [RequiresPreviewFeatures] - static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) - => BitOperations.RotateLeft(value, (int)rotateAmount); + static ulong IBinaryInteger.RotateLeft(ulong value, int rotateAmount) + => BitOperations.RotateLeft(value, rotateAmount); [RequiresPreviewFeatures] - static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) - => BitOperations.RotateRight(value, (int)rotateAmount); + static ulong IBinaryInteger.RotateRight(ulong value, int rotateAmount) + => BitOperations.RotateRight(value, rotateAmount); [RequiresPreviewFeatures] static ulong IBinaryInteger.TrailingZeroCount(ulong value) @@ -361,11 +361,11 @@ namespace System [RequiresPreviewFeatures] static ulong IDecrementOperators.operator --(ulong value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked ulong IDecrementOperators.operator --(ulong value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -397,11 +397,11 @@ namespace System [RequiresPreviewFeatures] static ulong IIncrementOperators.operator ++(ulong value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked ulong IIncrementOperators.operator ++(ulong value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 43c549fe94e..b413125474f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -270,11 +270,11 @@ namespace System { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.LeadingZeroCount((uint)value); + return (nuint)BitOperations.LeadingZeroCount((ulong)value); } else { - return (nuint)BitOperations.LeadingZeroCount((ulong)value); + return (nuint)BitOperations.LeadingZeroCount((uint)value); } } @@ -282,38 +282,38 @@ namespace System static nuint IBinaryInteger.PopCount(nuint value) { if (Environment.Is64BitProcess) - { - return (nuint)BitOperations.PopCount((uint)value); - } - else { return (nuint)BitOperations.PopCount((ulong)value); } - } - - [RequiresPreviewFeatures] - static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) - { - if (Environment.Is64BitProcess) - { - return (nuint)BitOperations.RotateLeft((uint)value, (int)rotateAmount); - } else { - return (nuint)BitOperations.RotateLeft((ulong)value, (int)rotateAmount); + return (nuint)BitOperations.PopCount((uint)value); } } [RequiresPreviewFeatures] - static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) + static nuint IBinaryInteger.RotateLeft(nuint value, int rotateAmount) { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.RotateRight((uint)value, (int)rotateAmount); + return (nuint)BitOperations.RotateLeft((ulong)value, rotateAmount); } else { - return (nuint)BitOperations.RotateRight((ulong)value, (int)rotateAmount); + return (nuint)BitOperations.RotateLeft((uint)value, rotateAmount); + } + } + + [RequiresPreviewFeatures] + static nuint IBinaryInteger.RotateRight(nuint value, int rotateAmount) + { + if (Environment.Is64BitProcess) + { + return (nuint)BitOperations.RotateRight((ulong)value, rotateAmount); + } + else + { + return (nuint)BitOperations.RotateRight((uint)value, rotateAmount); } } @@ -322,11 +322,11 @@ namespace System { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.TrailingZeroCount((uint)value); + return (nuint)BitOperations.TrailingZeroCount((ulong)value); } else { - return (nuint)BitOperations.TrailingZeroCount((ulong)value); + return (nuint)BitOperations.TrailingZeroCount((uint)value); } } @@ -339,11 +339,11 @@ namespace System { if (Environment.Is64BitProcess) { - return BitOperations.IsPow2((uint)value); + return BitOperations.IsPow2((ulong)value); } else { - return BitOperations.IsPow2((ulong)value); + return BitOperations.IsPow2((uint)value); } } @@ -352,11 +352,11 @@ namespace System { if (Environment.Is64BitProcess) { - return (nuint)BitOperations.Log2((uint)value); + return (nuint)BitOperations.Log2((ulong)value); } else { - return (nuint)BitOperations.Log2((ulong)value); + return (nuint)BitOperations.Log2((uint)value); } } @@ -406,11 +406,11 @@ namespace System [RequiresPreviewFeatures] static nuint IDecrementOperators.operator --(nuint value) - => value--; + => --value; // [RequiresPreviewFeatures] // static checked nuint IDecrementOperators.operator --(nuint value) - // => checked(value--); + // => checked(--value); // // IDivisionOperators @@ -442,11 +442,11 @@ namespace System [RequiresPreviewFeatures] static nuint IIncrementOperators.operator ++(nuint value) - => value++; + => ++value; // [RequiresPreviewFeatures] // static checked nuint IIncrementOperators.operator ++(nuint value) - // => checked(value++); + // => checked(++value); // // IMinMaxValue @@ -661,7 +661,7 @@ namespace System { if (typeof(TOther) == typeof(byte)) { - return (nuint)(object)value; + return (byte)(object)value; } else if (typeof(TOther) == typeof(char)) { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 1b9b1d241b8..67e80443421 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -777,9 +777,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static byte IBinaryInteger.PopCount(byte value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static byte IBinaryInteger.RotateLeft(byte value, byte rotateAmount) { throw null; } + static byte IBinaryInteger.RotateLeft(byte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static byte IBinaryInteger.RotateRight(byte value, byte rotateAmount) { throw null; } + static byte IBinaryInteger.RotateRight(byte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static byte IBinaryInteger.TrailingZeroCount(byte value) { throw null; } @@ -997,9 +997,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static char IBinaryInteger.PopCount(char value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static char IBinaryInteger.RotateLeft(char value, char rotateAmount) { throw null; } + static char IBinaryInteger.RotateLeft(char value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static char IBinaryInteger.RotateRight(char value, char rotateAmount) { throw null; } + static char IBinaryInteger.RotateRight(char value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static char IBinaryInteger.TrailingZeroCount(char value) { throw null; } @@ -3379,8 +3379,8 @@ namespace System { static abstract TSelf LeadingZeroCount(TSelf value); static abstract TSelf PopCount(TSelf value); - static abstract TSelf RotateLeft(TSelf value, TSelf rotateAmount); - static abstract TSelf RotateRight(TSelf value, TSelf rotateAmount); + static abstract TSelf RotateLeft(TSelf value, int rotateAmount); + static abstract TSelf RotateRight(TSelf value, int rotateAmount); static abstract TSelf TrailingZeroCount(TSelf value); } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] @@ -3760,9 +3760,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static short IBinaryInteger.PopCount(short value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static short IBinaryInteger.RotateLeft(short value, short rotateAmount) { throw null; } + static short IBinaryInteger.RotateLeft(short value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static short IBinaryInteger.RotateRight(short value, short rotateAmount) { throw null; } + static short IBinaryInteger.RotateRight(short value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static short IBinaryInteger.TrailingZeroCount(short value) { throw null; } @@ -4118,9 +4118,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static long IBinaryInteger.PopCount(long value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static long IBinaryInteger.RotateLeft(long value, long rotateAmount) { throw null; } + static long IBinaryInteger.RotateLeft(long value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static long IBinaryInteger.RotateRight(long value, long rotateAmount) { throw null; } + static long IBinaryInteger.RotateRight(long value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static long IBinaryInteger.TrailingZeroCount(long value) { throw null; } @@ -4306,9 +4306,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nint IBinaryInteger.PopCount(nint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nint IBinaryInteger.RotateLeft(nint value, nint rotateAmount) { throw null; } + static nint IBinaryInteger.RotateLeft(nint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nint IBinaryInteger.RotateRight(nint value, nint rotateAmount) { throw null; } + static nint IBinaryInteger.RotateRight(nint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nint IBinaryInteger.TrailingZeroCount(nint value) { throw null; } @@ -5269,9 +5269,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static sbyte IBinaryInteger.PopCount(sbyte value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static sbyte IBinaryInteger.RotateLeft(sbyte value, sbyte rotateAmount) { throw null; } + static sbyte IBinaryInteger.RotateLeft(sbyte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static sbyte IBinaryInteger.RotateRight(sbyte value, sbyte rotateAmount) { throw null; } + static sbyte IBinaryInteger.RotateRight(sbyte value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static sbyte IBinaryInteger.TrailingZeroCount(sbyte value) { throw null; } @@ -6979,9 +6979,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ushort IBinaryInteger.PopCount(ushort value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ushort IBinaryInteger.RotateLeft(ushort value, ushort rotateAmount) { throw null; } + static ushort IBinaryInteger.RotateLeft(ushort value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ushort IBinaryInteger.RotateRight(ushort value, ushort rotateAmount) { throw null; } + static ushort IBinaryInteger.RotateRight(ushort value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ushort IBinaryInteger.TrailingZeroCount(ushort value) { throw null; } @@ -7156,9 +7156,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static uint IBinaryInteger.PopCount(uint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static uint IBinaryInteger.RotateLeft(uint value, uint rotateAmount) { throw null; } + static uint IBinaryInteger.RotateLeft(uint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static uint IBinaryInteger.RotateRight(uint value, uint rotateAmount) { throw null; } + static uint IBinaryInteger.RotateRight(uint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static uint IBinaryInteger.TrailingZeroCount(uint value) { throw null; } @@ -7333,9 +7333,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ulong IBinaryInteger.PopCount(ulong value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ulong IBinaryInteger.RotateLeft(ulong value, ulong rotateAmount) { throw null; } + static ulong IBinaryInteger.RotateLeft(ulong value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static ulong IBinaryInteger.RotateRight(ulong value, ulong rotateAmount) { throw null; } + static ulong IBinaryInteger.RotateRight(ulong value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static ulong IBinaryInteger.TrailingZeroCount(ulong value) { throw null; } @@ -7515,9 +7515,9 @@ namespace System [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nuint IBinaryInteger.PopCount(nuint value) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nuint IBinaryInteger.RotateLeft(nuint value, nuint rotateAmount) { throw null; } + static nuint IBinaryInteger.RotateLeft(nuint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] - static nuint IBinaryInteger.RotateRight(nuint value, nuint rotateAmount) { throw null; } + static nuint IBinaryInteger.RotateRight(nuint value, int rotateAmount) { throw null; } [System.Runtime.Versioning.RequiresPreviewFeaturesAttribute] static nuint IBinaryInteger.TrailingZeroCount(nuint value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 32bbac1e993..0f4092248ae 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -1,12 +1,16 @@ - + true + true $(NoWarn),1718,SYSLIB0013 true true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser disable + + $(DefineConstants);FEATURE_GENERIC_MATH + @@ -81,7 +85,6 @@ - @@ -89,7 +92,6 @@ - @@ -247,6 +249,20 @@ + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs new file mode 100644 index 00000000000..140279d54f2 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/ByteTests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class ByteTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((byte)0x00, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((byte)0x00, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((byte)0xFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((byte)0x01, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((byte)0x01, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((byte)0x00, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((byte)0x01, AdditionOperatorsHelper.op_Addition((byte)0x00, (byte)1)); + Assert.Equal((byte)0x02, AdditionOperatorsHelper.op_Addition((byte)0x01, (byte)1)); + Assert.Equal((byte)0x80, AdditionOperatorsHelper.op_Addition((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, AdditionOperatorsHelper.op_Addition((byte)0x80, (byte)1)); + Assert.Equal((byte)0x00, AdditionOperatorsHelper.op_Addition((byte)0xFF, (byte)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((byte)0x08, BinaryIntegerHelper.LeadingZeroCount((byte)0x00)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.LeadingZeroCount((byte)0x01)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.LeadingZeroCount((byte)0x7F)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.LeadingZeroCount((byte)0x80)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.LeadingZeroCount((byte)0xFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.PopCount((byte)0x00)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.PopCount((byte)0x01)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.PopCount((byte)0x7F)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.PopCount((byte)0x80)); + Assert.Equal((byte)0x08, BinaryIntegerHelper.PopCount((byte)0xFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.RotateLeft((byte)0x00, 1)); + Assert.Equal((byte)0x02, BinaryIntegerHelper.RotateLeft((byte)0x01, 1)); + Assert.Equal((byte)0xFE, BinaryIntegerHelper.RotateLeft((byte)0x7F, 1)); + Assert.Equal((byte)0x01, BinaryIntegerHelper.RotateLeft((byte)0x80, 1)); + Assert.Equal((byte)0xFF, BinaryIntegerHelper.RotateLeft((byte)0xFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((byte)0x00, BinaryIntegerHelper.RotateRight((byte)0x00, 1)); + Assert.Equal((byte)0x80, BinaryIntegerHelper.RotateRight((byte)0x01, 1)); + Assert.Equal((byte)0xBF, BinaryIntegerHelper.RotateRight((byte)0x7F, 1)); + Assert.Equal((byte)0x40, BinaryIntegerHelper.RotateRight((byte)0x80, 1)); + Assert.Equal((byte)0xFF, BinaryIntegerHelper.RotateRight((byte)0xFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((byte)0x08, BinaryIntegerHelper.TrailingZeroCount((byte)0x00)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0x01)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0x7F)); + Assert.Equal((byte)0x07, BinaryIntegerHelper.TrailingZeroCount((byte)0x80)); + Assert.Equal((byte)0x00, BinaryIntegerHelper.TrailingZeroCount((byte)0xFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((byte)0x00)); + Assert.True(BinaryNumberHelper.IsPow2((byte)0x01)); + Assert.False(BinaryNumberHelper.IsPow2((byte)0x7F)); + Assert.True(BinaryNumberHelper.IsPow2((byte)0x80)); + Assert.False(BinaryNumberHelper.IsPow2((byte)0xFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((byte)0x00, BinaryNumberHelper.Log2((byte)0x00)); + Assert.Equal((byte)0x00, BinaryNumberHelper.Log2((byte)0x01)); + Assert.Equal((byte)0x06, BinaryNumberHelper.Log2((byte)0x7F)); + Assert.Equal((byte)0x07, BinaryNumberHelper.Log2((byte)0x80)); + Assert.Equal((byte)0x07, BinaryNumberHelper.Log2((byte)0xFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x01, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0x80, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7F, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, BitwiseOperatorsHelper.op_BitwiseOr((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFF, BitwiseOperatorsHelper.op_BitwiseOr((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((byte)0x01, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x00, (byte)1)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7E, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x81, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFE, BitwiseOperatorsHelper.op_ExclusiveOr((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((byte)0xFF, BitwiseOperatorsHelper.op_OnesComplement((byte)0x00)); + Assert.Equal((byte)0xFE, BitwiseOperatorsHelper.op_OnesComplement((byte)0x01)); + Assert.Equal((byte)0x80, BitwiseOperatorsHelper.op_OnesComplement((byte)0x7F)); + Assert.Equal((byte)0x7F, BitwiseOperatorsHelper.op_OnesComplement((byte)0x80)); + Assert.Equal((byte)0x00, BitwiseOperatorsHelper.op_OnesComplement((byte)0xFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((byte)0x00, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x01, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x7F, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0x80, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x00, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x01, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x7F, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0x80, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((byte)0x00, (byte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((byte)0x01, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0x7F, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0x80, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x00, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x01, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x7F, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0x80, (byte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((byte)0xFF, DecrementOperatorsHelper.op_Decrement((byte)0x00)); + Assert.Equal((byte)0x00, DecrementOperatorsHelper.op_Decrement((byte)0x01)); + Assert.Equal((byte)0x7E, DecrementOperatorsHelper.op_Decrement((byte)0x7F)); + Assert.Equal((byte)0x7F, DecrementOperatorsHelper.op_Decrement((byte)0x80)); + Assert.Equal((byte)0xFE, DecrementOperatorsHelper.op_Decrement((byte)0xFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((byte)0x00, DivisionOperatorsHelper.op_Division((byte)0x00, (byte)2)); + Assert.Equal((byte)0x00, DivisionOperatorsHelper.op_Division((byte)0x01, (byte)2)); + Assert.Equal((byte)0x3F, DivisionOperatorsHelper.op_Division((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x40, DivisionOperatorsHelper.op_Division((byte)0x80, (byte)2)); + Assert.Equal((byte)0x7F, DivisionOperatorsHelper.op_Division((byte)0xFF, (byte)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x00, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((byte)0x01, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x7F, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0x80, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x00, (byte)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((byte)0x01, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x7F, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0x80, (byte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((byte)0x01, IncrementOperatorsHelper.op_Increment((byte)0x00)); + Assert.Equal((byte)0x02, IncrementOperatorsHelper.op_Increment((byte)0x01)); + Assert.Equal((byte)0x80, IncrementOperatorsHelper.op_Increment((byte)0x7F)); + Assert.Equal((byte)0x81, IncrementOperatorsHelper.op_Increment((byte)0x80)); + Assert.Equal((byte)0x00, IncrementOperatorsHelper.op_Increment((byte)0xFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((byte)0x00, ModulusOperatorsHelper.op_Modulus((byte)0x00, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0x01, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x00, ModulusOperatorsHelper.op_Modulus((byte)0x80, (byte)2)); + Assert.Equal((byte)0x01, ModulusOperatorsHelper.op_Modulus((byte)0xFF, (byte)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((byte)0x00, MultiplyOperatorsHelper.op_Multiply((byte)0x00, (byte)2)); + Assert.Equal((byte)0x02, MultiplyOperatorsHelper.op_Multiply((byte)0x01, (byte)2)); + Assert.Equal((byte)0xFE, MultiplyOperatorsHelper.op_Multiply((byte)0x7F, (byte)2)); + Assert.Equal((byte)0x00, MultiplyOperatorsHelper.op_Multiply((byte)0x80, (byte)2)); + Assert.Equal((byte)0xFE, MultiplyOperatorsHelper.op_Multiply((byte)0xFF, (byte)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((byte)0x00, NumberHelper.Abs((byte)0x00)); + Assert.Equal((byte)0x01, NumberHelper.Abs((byte)0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Abs((byte)0x7F)); + Assert.Equal((byte)0x80, NumberHelper.Abs((byte)0x80)); + Assert.Equal((byte)0xFF, NumberHelper.Abs((byte)0xFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((byte)0x01, NumberHelper.Clamp((byte)0x00, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x01, NumberHelper.Clamp((byte)0x01, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0x7F, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0x80, (byte)0x01, (byte)0x3F)); + Assert.Equal((byte)0x3F, NumberHelper.Clamp((byte)0xFF, (byte)0x01, (byte)0x3F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Create(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.Create(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create((char)0x0001)); + Assert.Throws(() => NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((byte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((byte)0x80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((byte)0x01, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((byte)0x00, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((byte)0xFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((byte)0x00, (byte)0x00), NumberHelper.DivRem((byte)0x00, (byte)2)); + Assert.Equal(((byte)0x00, (byte)0x01), NumberHelper.DivRem((byte)0x01, (byte)2)); + Assert.Equal(((byte)0x3F, (byte)0x01), NumberHelper.DivRem((byte)0x7F, (byte)2)); + Assert.Equal(((byte)0x40, (byte)0x00), NumberHelper.DivRem((byte)0x80, (byte)2)); + Assert.Equal(((byte)0x7F, (byte)0x01), NumberHelper.DivRem((byte)0xFF, (byte)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((byte)0x01, NumberHelper.Max((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Max((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7F, NumberHelper.Max((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x80, NumberHelper.Max((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFF, NumberHelper.Max((byte)0xFF, (byte)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((byte)0x00, NumberHelper.Min((byte)0x00, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x01, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0x80, (byte)1)); + Assert.Equal((byte)0x01, NumberHelper.Min((byte)0xFF, (byte)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((byte)0x00, NumberHelper.Sign((byte)0x00)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x01)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x7F)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0x80)); + Assert.Equal((byte)0x01, NumberHelper.Sign((byte)0xFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((byte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((byte)0x7F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((byte)0x80, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((byte)0xFF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + byte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((byte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((byte)0x7F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + byte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + byte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((byte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((byte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((byte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((byte)0x00, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_LeftShift((byte)0x00, 1)); + Assert.Equal((byte)0x02, ShiftOperatorsHelper.op_LeftShift((byte)0x01, 1)); + Assert.Equal((byte)0xFE, ShiftOperatorsHelper.op_LeftShift((byte)0x7F, 1)); + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_LeftShift((byte)0x80, 1)); + Assert.Equal((byte)0xFE, ShiftOperatorsHelper.op_LeftShift((byte)0xFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_RightShift((byte)0x00, 1)); + Assert.Equal((byte)0x00, ShiftOperatorsHelper.op_RightShift((byte)0x01, 1)); + Assert.Equal((byte)0x3F, ShiftOperatorsHelper.op_RightShift((byte)0x7F, 1)); + Assert.Equal((byte)0x40, ShiftOperatorsHelper.op_RightShift((byte)0x80, 1)); + Assert.Equal((byte)0x7F, ShiftOperatorsHelper.op_RightShift((byte)0xFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((byte)0xFF, SubtractionOperatorsHelper.op_Subtraction((byte)0x00, (byte)1)); + Assert.Equal((byte)0x00, SubtractionOperatorsHelper.op_Subtraction((byte)0x01, (byte)1)); + Assert.Equal((byte)0x7E, SubtractionOperatorsHelper.op_Subtraction((byte)0x7F, (byte)1)); + Assert.Equal((byte)0x7F, SubtractionOperatorsHelper.op_Subtraction((byte)0x80, (byte)1)); + Assert.Equal((byte)0xFE, SubtractionOperatorsHelper.op_Subtraction((byte)0xFF, (byte)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((byte)0x00, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x00)); + Assert.Equal((byte)0xFF, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x01)); + Assert.Equal((byte)0x81, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x7F)); + Assert.Equal((byte)0x80, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0x80)); + Assert.Equal((byte)0x01, UnaryNegationOperatorsHelper.op_UnaryNegation((byte)0xFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((byte)0x00, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x00)); + Assert.Equal((byte)0x01, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x01)); + Assert.Equal((byte)0x7F, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x7F)); + Assert.Equal((byte)0x80, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0x80)); + Assert.Equal((byte)0xFF, UnaryPlusOperatorsHelper.op_UnaryPlus((byte)0xFF)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Valid_TestData), MemberType = typeof(ByteTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, byte expected) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Invalid_TestData), MemberType = typeof(ByteTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(byte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(ByteTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, byte expected) + { + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(ByteTests.Parse_Invalid_TestData), MemberType = typeof(ByteTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + byte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(byte), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(byte), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs new file mode 100644 index 00000000000..7dd512f9871 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/CharTests.GenericMath.cs @@ -0,0 +1,1057 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class CharTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((char)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((char)0x0000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((char)0xFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((char)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((char)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((char)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((char)0x0001, AdditionOperatorsHelper.op_Addition((char)0x0000, (char)1)); + Assert.Equal((char)0x0002, AdditionOperatorsHelper.op_Addition((char)0x0001, (char)1)); + Assert.Equal((char)0x8000, AdditionOperatorsHelper.op_Addition((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, AdditionOperatorsHelper.op_Addition((char)0x8000, (char)1)); + Assert.Equal((char)0x0000, AdditionOperatorsHelper.op_Addition((char)0xFFFF, (char)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((char)0x0010, BinaryIntegerHelper.LeadingZeroCount((char)0x0000)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.LeadingZeroCount((char)0x0001)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.LeadingZeroCount((char)0x7FFF)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.LeadingZeroCount((char)0x8000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.LeadingZeroCount((char)0xFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.PopCount((char)0x0000)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.PopCount((char)0x0001)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.PopCount((char)0x7FFF)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.PopCount((char)0x8000)); + Assert.Equal((char)0x0010, BinaryIntegerHelper.PopCount((char)0xFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.RotateLeft((char)0x0000, 1)); + Assert.Equal((char)0x0002, BinaryIntegerHelper.RotateLeft((char)0x0001, 1)); + Assert.Equal((char)0xFFFE, BinaryIntegerHelper.RotateLeft((char)0x7FFF, 1)); + Assert.Equal((char)0x0001, BinaryIntegerHelper.RotateLeft((char)0x8000, 1)); + Assert.Equal((char)0xFFFF, BinaryIntegerHelper.RotateLeft((char)0xFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((char)0x0000, BinaryIntegerHelper.RotateRight((char)0x0000, 1)); + Assert.Equal((char)0x8000, BinaryIntegerHelper.RotateRight((char)0x0001, 1)); + Assert.Equal((char)0xBFFF, BinaryIntegerHelper.RotateRight((char)0x7FFF, 1)); + Assert.Equal((char)0x4000, BinaryIntegerHelper.RotateRight((char)0x8000, 1)); + Assert.Equal((char)0xFFFF, BinaryIntegerHelper.RotateRight((char)0xFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((char)0x0010, BinaryIntegerHelper.TrailingZeroCount((char)0x0000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0x0001)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0x7FFF)); + Assert.Equal((char)0x000F, BinaryIntegerHelper.TrailingZeroCount((char)0x8000)); + Assert.Equal((char)0x0000, BinaryIntegerHelper.TrailingZeroCount((char)0xFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((char)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((char)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((char)0x7FFF)); + Assert.True(BinaryNumberHelper.IsPow2((char)0x8000)); + Assert.False(BinaryNumberHelper.IsPow2((char)0xFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((char)0x0000, BinaryNumberHelper.Log2((char)0x0000)); + Assert.Equal((char)0x0000, BinaryNumberHelper.Log2((char)0x0001)); + Assert.Equal((char)0x000E, BinaryNumberHelper.Log2((char)0x7FFF)); + Assert.Equal((char)0x000F, BinaryNumberHelper.Log2((char)0x8000)); + Assert.Equal((char)0x000F, BinaryNumberHelper.Log2((char)0xFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x0001, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x7FFF, (char)1)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((char)0x8000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, BitwiseOperatorsHelper.op_BitwiseOr((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFF, BitwiseOperatorsHelper.op_BitwiseOr((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((char)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x0000, (char)1)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8001, BitwiseOperatorsHelper.op_ExclusiveOr((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((char)0xFFFF, BitwiseOperatorsHelper.op_OnesComplement((char)0x0000)); + Assert.Equal((char)0xFFFE, BitwiseOperatorsHelper.op_OnesComplement((char)0x0001)); + Assert.Equal((char)0x8000, BitwiseOperatorsHelper.op_OnesComplement((char)0x7FFF)); + Assert.Equal((char)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement((char)0x8000)); + Assert.Equal((char)0x0000, BitwiseOperatorsHelper.op_OnesComplement((char)0xFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((char)0x0000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x0001, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x7FFF, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0x8000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x0000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x0001, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x7FFF, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0x8000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((char)0x0000, (char)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((char)0x0001, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0x7FFF, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0x8000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x0000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x0001, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x7FFF, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0x8000, (char)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((char)0xFFFF, DecrementOperatorsHelper.op_Decrement((char)0x0000)); + Assert.Equal((char)0x0000, DecrementOperatorsHelper.op_Decrement((char)0x0001)); + Assert.Equal((char)0x7FFE, DecrementOperatorsHelper.op_Decrement((char)0x7FFF)); + Assert.Equal((char)0x7FFF, DecrementOperatorsHelper.op_Decrement((char)0x8000)); + Assert.Equal((char)0xFFFE, DecrementOperatorsHelper.op_Decrement((char)0xFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((char)0x0000, DivisionOperatorsHelper.op_Division((char)0x0000, (char)2)); + Assert.Equal((char)0x0000, DivisionOperatorsHelper.op_Division((char)0x0001, (char)2)); + Assert.Equal((char)0x3FFF, DivisionOperatorsHelper.op_Division((char)0x7FFF, (char)2)); + Assert.Equal((char)0x4000, DivisionOperatorsHelper.op_Division((char)0x8000, (char)2)); + Assert.Equal((char)0x7FFF, DivisionOperatorsHelper.op_Division((char)0xFFFF, (char)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x0000, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((char)0x0001, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x7FFF, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0x8000, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x0000, (char)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((char)0x0001, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x7FFF, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0x8000, (char)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((char)0x0001, IncrementOperatorsHelper.op_Increment((char)0x0000)); + Assert.Equal((char)0x0002, IncrementOperatorsHelper.op_Increment((char)0x0001)); + Assert.Equal((char)0x8000, IncrementOperatorsHelper.op_Increment((char)0x7FFF)); + Assert.Equal((char)0x8001, IncrementOperatorsHelper.op_Increment((char)0x8000)); + Assert.Equal((char)0x0000, IncrementOperatorsHelper.op_Increment((char)0xFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((char)0x0000, ModulusOperatorsHelper.op_Modulus((char)0x0000, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0x0001, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0x7FFF, (char)2)); + Assert.Equal((char)0x0000, ModulusOperatorsHelper.op_Modulus((char)0x8000, (char)2)); + Assert.Equal((char)0x0001, ModulusOperatorsHelper.op_Modulus((char)0xFFFF, (char)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((char)0x0000, MultiplyOperatorsHelper.op_Multiply((char)0x0000, (char)2)); + Assert.Equal((char)0x0002, MultiplyOperatorsHelper.op_Multiply((char)0x0001, (char)2)); + Assert.Equal((char)0xFFFE, MultiplyOperatorsHelper.op_Multiply((char)0x7FFF, (char)2)); + Assert.Equal((char)0x0000, MultiplyOperatorsHelper.op_Multiply((char)0x8000, (char)2)); + Assert.Equal((char)0xFFFE, MultiplyOperatorsHelper.op_Multiply((char)0xFFFF, (char)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((char)0x0000, NumberHelper.Abs((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Abs((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Abs((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Abs((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Abs((char)0xFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((char)0x0001, NumberHelper.Clamp((char)0x0000, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x0001, NumberHelper.Clamp((char)0x0001, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0x7FFF, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0x8000, (char)0x0001, (char)0x003F)); + Assert.Equal((char)0x003F, NumberHelper.Clamp((char)0xFFFF, (char)0x0001, (char)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((char)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Create((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((char)0x007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.Create(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((char)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((char)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((char)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((char)0xFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((char)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((char)0x8000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((char)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((char)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((char)0xFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((char)0x0000, (char)0x0000), NumberHelper.DivRem((char)0x0000, (char)2)); + Assert.Equal(((char)0x0000, (char)0x0001), NumberHelper.DivRem((char)0x0001, (char)2)); + Assert.Equal(((char)0x3FFF, (char)0x0001), NumberHelper.DivRem((char)0x7FFF, (char)2)); + Assert.Equal(((char)0x4000, (char)0x0000), NumberHelper.DivRem((char)0x8000, (char)2)); + Assert.Equal(((char)0x7FFF, (char)0x0001), NumberHelper.DivRem((char)0xFFFF, (char)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((char)0x0001, NumberHelper.Max((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Max((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFF, NumberHelper.Max((char)0x7FFF, (char)1)); + Assert.Equal((char)0x8000, NumberHelper.Max((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFF, NumberHelper.Max((char)0xFFFF, (char)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((char)0x0000, NumberHelper.Min((char)0x0000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x0001, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x7FFF, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0x8000, (char)1)); + Assert.Equal((char)0x0001, NumberHelper.Min((char)0xFFFF, (char)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((char)0x0000, NumberHelper.Sign((char)0x0000)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x0001)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x7FFF)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0x8000)); + Assert.Equal((char)0x0001, NumberHelper.Sign((char)0xFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((char)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((char)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((char)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + char result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((char)0x8000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((char)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + char result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((char)0x007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((char)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((char)0x8000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((char)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + char result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + char result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((char)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((char)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((char)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((char)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_LeftShift((char)0x0000, 1)); + Assert.Equal((char)0x0002, ShiftOperatorsHelper.op_LeftShift((char)0x0001, 1)); + Assert.Equal((char)0xFFFE, ShiftOperatorsHelper.op_LeftShift((char)0x7FFF, 1)); + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_LeftShift((char)0x8000, 1)); + Assert.Equal((char)0xFFFE, ShiftOperatorsHelper.op_LeftShift((char)0xFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_RightShift((char)0x0000, 1)); + Assert.Equal((char)0x0000, ShiftOperatorsHelper.op_RightShift((char)0x0001, 1)); + Assert.Equal((char)0x3FFF, ShiftOperatorsHelper.op_RightShift((char)0x7FFF, 1)); + Assert.Equal((char)0x4000, ShiftOperatorsHelper.op_RightShift((char)0x8000, 1)); + Assert.Equal((char)0x7FFF, ShiftOperatorsHelper.op_RightShift((char)0xFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((char)0xFFFF, SubtractionOperatorsHelper.op_Subtraction((char)0x0000, (char)1)); + Assert.Equal((char)0x0000, SubtractionOperatorsHelper.op_Subtraction((char)0x0001, (char)1)); + Assert.Equal((char)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((char)0x7FFF, (char)1)); + Assert.Equal((char)0x7FFF, SubtractionOperatorsHelper.op_Subtraction((char)0x8000, (char)1)); + Assert.Equal((char)0xFFFE, SubtractionOperatorsHelper.op_Subtraction((char)0xFFFF, (char)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((char)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x0000)); + Assert.Equal((char)0xFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x0001)); + Assert.Equal((char)0x8001, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x7FFF)); + Assert.Equal((char)0x8000, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0x8000)); + Assert.Equal((char)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation((char)0xFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((char)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x0000)); + Assert.Equal((char)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x0001)); + Assert.Equal((char)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x7FFF)); + Assert.Equal((char)0x8000, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0x8000)); + Assert.Equal((char)0xFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((char)0xFFFF)); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs b/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs new file mode 100644 index 00000000000..2895dd88419 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/GenericMathHelpers.cs @@ -0,0 +1,228 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; + +namespace System.Tests +{ + [RequiresPreviewFeatures] + public static class AdditionOperatorsHelper + where TSelf : IAdditionOperators + { + public static TResult op_Addition(TSelf left, TOther right) => left + right; + } + + [RequiresPreviewFeatures] + public static class AdditiveIdentityHelper + where TSelf : IAdditiveIdentity + { + public static TResult AdditiveIdentity => TSelf.AdditiveIdentity; + } + + [RequiresPreviewFeatures] + public static class BinaryIntegerHelper + where TSelf : IBinaryInteger + { + public static TSelf LeadingZeroCount(TSelf value) => TSelf.LeadingZeroCount(value); + + public static TSelf PopCount(TSelf value) => TSelf.PopCount(value); + + public static TSelf RotateLeft(TSelf value, int rotateAmount) => TSelf.RotateLeft(value, rotateAmount); + + public static TSelf RotateRight(TSelf value, int rotateAmount) => TSelf.RotateRight(value, rotateAmount); + + public static TSelf TrailingZeroCount(TSelf value) => TSelf.TrailingZeroCount(value); + } + + [RequiresPreviewFeatures] + public static class BinaryNumberHelper + where TSelf : IBinaryNumber + { + public static bool IsPow2(TSelf value) => TSelf.IsPow2(value); + + public static TSelf Log2(TSelf value) => TSelf.Log2(value); + } + + [RequiresPreviewFeatures] + public static class BitwiseOperatorsHelper + where TSelf : IBitwiseOperators + { + public static TResult op_BitwiseAnd(TSelf left, TOther right) => left & right; + + public static TResult op_BitwiseOr(TSelf left, TOther right) => left | right; + + public static TResult op_ExclusiveOr(TSelf left, TOther right) => left ^ right; + + public static TResult op_OnesComplement(TSelf value) => ~value; + } + + [RequiresPreviewFeatures] + public static class ComparisonOperatorsHelper + where TSelf : IComparisonOperators + { + public static bool op_GreaterThan(TSelf left, TOther right) => left > right; + + public static bool op_GreaterThanOrEqual(TSelf left, TOther right) => left >= right; + + public static bool op_LessThan(TSelf left, TOther right) => left < right; + + public static bool op_LessThanOrEqual(TSelf left, TOther right) => left <= right; + } + + [RequiresPreviewFeatures] + public static class DecrementOperatorsHelper + where TSelf : IDecrementOperators +{ + public static TSelf op_Decrement(TSelf value) => --value; + } + + [RequiresPreviewFeatures] + public static class DivisionOperatorsHelper + where TSelf : IDivisionOperators + { + public static TResult op_Division(TSelf left, TOther right) => left / right; + } + + [RequiresPreviewFeatures] + public static class EqualityOperatorsHelper + where TSelf : IEqualityOperators + { + public static bool op_Equality(TSelf left, TOther right) => left == right; + + public static bool op_Inequality(TSelf left, TOther right) => left != right; + } + + [RequiresPreviewFeatures] + public static class IncrementOperatorsHelper + where TSelf : IIncrementOperators + { + public static TSelf op_Increment(TSelf value) => ++value; + } + + [RequiresPreviewFeatures] + public static class ModulusOperatorsHelper + where TSelf : IModulusOperators + { + public static TResult op_Modulus(TSelf left, TOther right) => left % right; + } + + [RequiresPreviewFeatures] + public static class MultiplyOperatorsHelper + where TSelf : IMultiplyOperators + { + public static TResult op_Multiply(TSelf left, TOther right) => left * right; + } + + [RequiresPreviewFeatures] + public static class MinMaxValueHelper + where TSelf : IMinMaxValue + { + public static TSelf MaxValue => TSelf.MaxValue; + + public static TSelf MinValue => TSelf.MinValue; + } + + [RequiresPreviewFeatures] + public static class MultiplicativeIdentityHelper + where TSelf : IMultiplicativeIdentity + { + public static TResult MultiplicativeIdentity => TSelf.MultiplicativeIdentity; + } + + [RequiresPreviewFeatures] + public static class NumberHelper + where TSelf : INumber + { + public static TSelf One => TSelf.One; + + public static TSelf Zero => TSelf.Zero; + + public static TSelf Abs(TSelf value) => TSelf.Abs(value); + + public static TSelf Clamp(TSelf value, TSelf min, TSelf max) => TSelf.Clamp(value, min, max); + + public static TSelf Create(TOther value) + where TOther : INumber => TSelf.Create(value); + + public static TSelf CreateSaturating(TOther value) + where TOther : INumber => TSelf.CreateSaturating(value); + + public static TSelf CreateTruncating(TOther value) + where TOther : INumber => TSelf.CreateTruncating(value); + + public static (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) => TSelf.DivRem(left, right); + + public static TSelf Max(TSelf x, TSelf y) => TSelf.Max(x, y); + + public static TSelf Min(TSelf x, TSelf y) => TSelf.Min(x, y); + + public static TSelf Parse(string s, NumberStyles style, IFormatProvider provider) => TSelf.Parse(s, style, provider); + + public static TSelf Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => TSelf.Parse(s, style, provider); + + public static TSelf Sign(TSelf value) => TSelf.Sign(value); + + public static bool TryCreate(TOther value, out TSelf result) + where TOther : INumber => TSelf.TryCreate(value, out result); + + public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, style, provider, out result); + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, style, provider, out result); + } + + [RequiresPreviewFeatures] + public static class ParseableHelper + where TSelf : IParseable + { + public static TSelf Parse(string s, IFormatProvider provider) => TSelf.Parse(s, provider); + + public static bool TryParse(string s, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, provider, out result); + } + + [RequiresPreviewFeatures] + public static class ShiftOperatorsHelper + where TSelf : IShiftOperators + { + public static TResult op_LeftShift(TSelf value, int shiftAmount) => value << shiftAmount; + + public static TResult op_RightShift(TSelf value, int shiftAmount) => value >> shiftAmount; + } + + [RequiresPreviewFeatures] + public static class SignedNumberHelper + where TSelf : ISignedNumber + { + public static TSelf NegativeOne => TSelf.NegativeOne; + } + + [RequiresPreviewFeatures] + public static class SpanParseableHelper + where TSelf : ISpanParseable + { + public static TSelf Parse(ReadOnlySpan s, IFormatProvider provider) => TSelf.Parse(s, provider); + + public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out TSelf result) => TSelf.TryParse(s, provider, out result); + } + + [RequiresPreviewFeatures] + public static class SubtractionOperatorsHelper + where TSelf : ISubtractionOperators + { + public static TResult op_Subtraction(TSelf left, TOther right) => left - right; + } + + [RequiresPreviewFeatures] + public static class UnaryNegationOperatorsHelper + where TSelf : IUnaryNegationOperators + { + public static TResult op_UnaryNegation(TSelf value) => -value; + } + + [RequiresPreviewFeatures] + public static class UnaryPlusOperatorsHelper + where TSelf : IUnaryPlusOperators + { + public static TResult op_UnaryPlus(TSelf value) => +value; + } +} diff --git a/src/libraries/System.Runtime/tests/System/GenericMathTests.cs b/src/libraries/System.Runtime/tests/System/GenericMathTests.cs deleted file mode 100644 index ccd3b8a269f..00000000000 --- a/src/libraries/System.Runtime/tests/System/GenericMathTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Linq; -using Xunit; - -namespace System.Tests -{ - public static class GenericMath - { - public static TResult Average(IEnumerable values) - where TSelf : INumber - where TResult : INumber - { - TResult sum = Sum(values); - return TResult.Create(sum) / TResult.Create(values.Count()); - } - - public static TResult StandardDeviation(IEnumerable values) - where TSelf : INumber - where TResult : IFloatingPoint - { - TResult standardDeviation = TResult.Zero; - - if (values.Any()) - { - TResult average = Average(values); - TResult sum = Sum(values.Select((value) => { - var deviation = TResult.Create(value) - average; - return deviation * deviation; - })); - standardDeviation = TResult.Sqrt(sum / TResult.Create(values.Count() - 1)); - } - - return standardDeviation; - } - - public static TResult Sum(IEnumerable values) - where TSelf : INumber - where TResult : INumber - { - TResult result = TResult.Zero; - - foreach (var value in values) - { - result += TResult.Create(value); - } - - return result; - } - } - - public abstract class GenericMathTests - where TSelf : INumber - { - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void AverageTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void StandardDeviationTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void SumTest(); - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] - public abstract void SumInt32Test(); - } -} diff --git a/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs new file mode 100644 index 00000000000..667a5f55ac2 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int16Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int16Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((short)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((short)0x8000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((short)0x7FFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((short)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((short)0xFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((short)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((short)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((short)0x0001, AdditionOperatorsHelper.op_Addition((short)0x0000, (short)1)); + Assert.Equal((short)0x0002, AdditionOperatorsHelper.op_Addition((short)0x0001, (short)1)); + Assert.Equal(unchecked((short)0x8000), AdditionOperatorsHelper.op_Addition((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), AdditionOperatorsHelper.op_Addition(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0000, AdditionOperatorsHelper.op_Addition(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((short)0x0010, BinaryIntegerHelper.LeadingZeroCount((short)0x0000)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.LeadingZeroCount((short)0x0001)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.LeadingZeroCount((short)0x7FFF)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.LeadingZeroCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BinaryIntegerHelper.LeadingZeroCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.PopCount((short)0x0000)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.PopCount((short)0x0001)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.PopCount((short)0x7FFF)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.PopCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0010, BinaryIntegerHelper.PopCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.RotateLeft((short)0x0000, 1)); + Assert.Equal((short)0x0002, BinaryIntegerHelper.RotateLeft((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xFFFE), BinaryIntegerHelper.RotateLeft((short)0x7FFF, 1)); + Assert.Equal((short)0x0001, BinaryIntegerHelper.RotateLeft(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), BinaryIntegerHelper.RotateLeft(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((short)0x0000, BinaryIntegerHelper.RotateRight((short)0x0000, 1)); + Assert.Equal(unchecked((short)0x8000), BinaryIntegerHelper.RotateRight((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xBFFF), BinaryIntegerHelper.RotateRight((short)0x7FFF, 1)); + Assert.Equal((short)0x4000, BinaryIntegerHelper.RotateRight(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), BinaryIntegerHelper.RotateRight(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((short)0x0010, BinaryIntegerHelper.TrailingZeroCount((short)0x0000)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount((short)0x0001)); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount((short)0x7FFF)); + Assert.Equal((short)0x000F, BinaryIntegerHelper.TrailingZeroCount(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BinaryIntegerHelper.TrailingZeroCount(unchecked((short)0xFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((short)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((short)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((short)0x7FFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((short)0x8000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((short)0xFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((short)0x0000, BinaryNumberHelper.Log2((short)0x0000)); + Assert.Equal((short)0x0000, BinaryNumberHelper.Log2((short)0x0001)); + Assert.Equal((short)0x000E, BinaryNumberHelper.Log2((short)0x7FFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((short)0x8000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x0001, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((short)0x7FFF, (short)1)); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((short)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x0000, (short)1)); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((short)0xFFFF), BitwiseOperatorsHelper.op_OnesComplement((short)0x0000)); + Assert.Equal(unchecked((short)0xFFFE), BitwiseOperatorsHelper.op_OnesComplement((short)0x0001)); + Assert.Equal(unchecked((short)0x8000), BitwiseOperatorsHelper.op_OnesComplement((short)0x7FFF)); + Assert.Equal((short)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((short)0x0000, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((short)0x0001, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((short)0x7FFF, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((short)0x8000), (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x0000, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x0001, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((short)0x7FFF, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((short)0x8000), (short)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((short)0x0000, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((short)0x0001, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((short)0x7FFF, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((short)0x8000), (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x0000, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x0001, (short)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((short)0x7FFF, (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((short)0x8000), (short)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((short)0xFFFF), DecrementOperatorsHelper.op_Decrement((short)0x0000)); + Assert.Equal((short)0x0000, DecrementOperatorsHelper.op_Decrement((short)0x0001)); + Assert.Equal((short)0x7FFE, DecrementOperatorsHelper.op_Decrement((short)0x7FFF)); + Assert.Equal((short)0x7FFF, DecrementOperatorsHelper.op_Decrement(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division((short)0x0000, (short)2)); + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division((short)0x0001, (short)2)); + Assert.Equal((short)0x3FFF, DivisionOperatorsHelper.op_Division((short)0x7FFF, (short)2)); + Assert.Equal(unchecked((short)0xC000), DivisionOperatorsHelper.op_Division(unchecked((short)0x8000), (short)2)); + Assert.Equal((short)0x0000, DivisionOperatorsHelper.op_Division(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((short)0x0000, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((short)0x0001, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((short)0x7FFF, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((short)0x8000), (short)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((short)0x0000, (short)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((short)0x0001, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((short)0x7FFF, (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((short)0x8000), (short)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((short)0x0001, IncrementOperatorsHelper.op_Increment((short)0x0000)); + Assert.Equal((short)0x0002, IncrementOperatorsHelper.op_Increment((short)0x0001)); + Assert.Equal(unchecked((short)0x8000), IncrementOperatorsHelper.op_Increment((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8001), IncrementOperatorsHelper.op_Increment(unchecked((short)0x8000))); + Assert.Equal((short)0x0000, IncrementOperatorsHelper.op_Increment(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((short)0x0000, ModulusOperatorsHelper.op_Modulus((short)0x0000, (short)2)); + Assert.Equal((short)0x0001, ModulusOperatorsHelper.op_Modulus((short)0x0001, (short)2)); + Assert.Equal((short)0x0001, ModulusOperatorsHelper.op_Modulus((short)0x7FFF, (short)2)); + Assert.Equal((short)0x0000, ModulusOperatorsHelper.op_Modulus(unchecked((short)0x8000), (short)2)); + Assert.Equal(unchecked((short)0xFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((short)0x0000, MultiplyOperatorsHelper.op_Multiply((short)0x0000, (short)2)); + Assert.Equal((short)0x0002, MultiplyOperatorsHelper.op_Multiply((short)0x0001, (short)2)); + Assert.Equal(unchecked((short)0xFFFE), MultiplyOperatorsHelper.op_Multiply((short)0x7FFF, (short)2)); + Assert.Equal((short)0x0000, MultiplyOperatorsHelper.op_Multiply(unchecked((short)0x8000), (short)2)); + Assert.Equal(unchecked((short)0xFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((short)0x0000, NumberHelper.Abs((short)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Abs((short)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Abs((short)0x7FFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((short)0x8000))); + Assert.Equal((short)0x0001, NumberHelper.Abs(unchecked((short)0xFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((short)0x0000, NumberHelper.Clamp((short)0x0000, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal((short)0x0001, NumberHelper.Clamp((short)0x0001, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal((short)0x003F, NumberHelper.Clamp((short)0x7FFF, unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal(unchecked((short)0xFFC0), NumberHelper.Clamp(unchecked((short)0x8000), unchecked((short)0xFFC0), (short)0x003F)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Clamp(unchecked((short)0xFFFF), unchecked((short)0xFFC0), (short)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((short)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((short)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((short)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((short)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((short)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((short)0xFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((short)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((short)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((short)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((short)0x0000, (short)0x0000), NumberHelper.DivRem((short)0x0000, (short)2)); + Assert.Equal(((short)0x0000, (short)0x0001), NumberHelper.DivRem((short)0x0001, (short)2)); + Assert.Equal(((short)0x3FFF, (short)0x0001), NumberHelper.DivRem((short)0x7FFF, (short)2)); + Assert.Equal((unchecked((short)0xC000), (short)0x0000), NumberHelper.DivRem(unchecked((short)0x8000), (short)2)); + Assert.Equal(((short)0x0000, unchecked((short)0xFFFF)), NumberHelper.DivRem(unchecked((short)0xFFFF), (short)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((short)0x0001, NumberHelper.Max((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFF, NumberHelper.Max((short)0x7FFF, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max(unchecked((short)0x8000), (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Max(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((short)0x0000, NumberHelper.Min((short)0x0000, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Min((short)0x0001, (short)1)); + Assert.Equal((short)0x0001, NumberHelper.Min((short)0x7FFF, (short)1)); + Assert.Equal(unchecked((short)0x8000), NumberHelper.Min(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Min(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((short)0x0000, NumberHelper.Sign((short)0x0000)); + Assert.Equal((short)0x0001, NumberHelper.Sign((short)0x0001)); + Assert.Equal((short)0x0001, NumberHelper.Sign((short)0x7FFF)); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Sign(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), NumberHelper.Sign(unchecked((short)0xFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((short)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((short)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((short)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + short result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((short)0x8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + short result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((short)0x007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((short)0xFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((short)0xFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((short)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + short result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + short result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((short)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((short)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((short)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((short)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_LeftShift((short)0x0000, 1)); + Assert.Equal((short)0x0002, ShiftOperatorsHelper.op_LeftShift((short)0x0001, 1)); + Assert.Equal(unchecked((short)0xFFFE), ShiftOperatorsHelper.op_LeftShift((short)0x7FFF, 1)); + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_LeftShift(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_RightShift((short)0x0000, 1)); + Assert.Equal((short)0x0000, ShiftOperatorsHelper.op_RightShift((short)0x0001, 1)); + Assert.Equal((short)0x3FFF, ShiftOperatorsHelper.op_RightShift((short)0x7FFF, 1)); + Assert.Equal(unchecked((short)0xC000), ShiftOperatorsHelper.op_RightShift(unchecked((short)0x8000), 1)); + Assert.Equal(unchecked((short)0xFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((short)0xFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((short)0xFFFF), SubtractionOperatorsHelper.op_Subtraction((short)0x0000, (short)1)); + Assert.Equal((short)0x0000, SubtractionOperatorsHelper.op_Subtraction((short)0x0001, (short)1)); + Assert.Equal((short)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((short)0x7FFF, (short)1)); + Assert.Equal((short)0x7FFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((short)0x8000), (short)1)); + Assert.Equal(unchecked((short)0xFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((short)0xFFFF), (short)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((short)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x0000)); + Assert.Equal(unchecked((short)0xFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x0001)); + Assert.Equal(unchecked((short)0x8001), UnaryNegationOperatorsHelper.op_UnaryNegation((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((short)0x8000))); + Assert.Equal((short)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((short)0xFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((short)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x0000)); + Assert.Equal((short)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x0001)); + Assert.Equal((short)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((short)0x7FFF)); + Assert.Equal(unchecked((short)0x8000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((short)0x8000))); + Assert.Equal(unchecked((short)0xFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((short)0xFFFF))); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Valid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, short expected) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Invalid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(short), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int16Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, short expected) + { + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int16Tests.Parse_Invalid_TestData), MemberType = typeof(Int16Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + short result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(short), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(short), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs b/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs deleted file mode 100644 index c8f469d6eb3..00000000000 --- a/src/libraries/System.Runtime/tests/System/Int32GenericMathTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Linq; -using Xunit; - -namespace System.Tests -{ - public sealed class Int32GenericMathTests : GenericMathTests - { - public override void AverageTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 16383.5, actual: GenericMath.Average(values)); - } - - public override void StandardDeviationTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 9459.451146868934, actual: GenericMath.StandardDeviation(values)); - } - - public override void SumTest() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 536854528, actual: GenericMath.Sum(values)); - } - - public override void SumInt32Test() - { - var values = Enumerable.Range(0, 32768); - Assert.Equal(expected: 536854528, actual: GenericMath.Sum(values)); - } - } -} diff --git a/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs new file mode 100644 index 00000000000..28f3c06c403 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int32Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int32Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((int)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((int)0x80000000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((int)0x7FFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((int)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((int)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((int)0x00000001, AdditionOperatorsHelper.op_Addition((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, AdditionOperatorsHelper.op_Addition((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0x80000000), AdditionOperatorsHelper.op_Addition((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), AdditionOperatorsHelper.op_Addition(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000000, AdditionOperatorsHelper.op_Addition(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((int)0x00000020, BinaryIntegerHelper.LeadingZeroCount((int)0x00000000)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.LeadingZeroCount((int)0x00000001)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.LeadingZeroCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.PopCount((int)0x00000000)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.PopCount((int)0x00000001)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.PopCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.PopCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000020, BinaryIntegerHelper.PopCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.RotateLeft((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, BinaryIntegerHelper.RotateLeft((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BinaryIntegerHelper.RotateLeft((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000001, BinaryIntegerHelper.RotateLeft(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((int)0x00000000, BinaryIntegerHelper.RotateRight((int)0x00000000, 1)); + Assert.Equal(unchecked((int)0x80000000), BinaryIntegerHelper.RotateRight((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xBFFFFFFF), BinaryIntegerHelper.RotateRight((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x40000000, BinaryIntegerHelper.RotateRight(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((int)0x00000020, BinaryIntegerHelper.TrailingZeroCount((int)0x00000000)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount((int)0x00000001)); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount((int)0x7FFFFFFF)); + Assert.Equal((int)0x0000001F, BinaryIntegerHelper.TrailingZeroCount(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((int)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((int)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((int)0x7FFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((int)0x80000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((int)0x00000000, BinaryNumberHelper.Log2((int)0x00000000)); + Assert.Equal((int)0x00000000, BinaryNumberHelper.Log2((int)0x00000001)); + Assert.Equal((int)0x0000001E, BinaryNumberHelper.Log2((int)0x7FFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((int)0x80000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x00000001, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((int)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((int)0x00000000)); + Assert.Equal(unchecked((int)0xFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000000), BitwiseOperatorsHelper.op_OnesComplement((int)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((int)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((int)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((int)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((int)0x80000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((int)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((int)0x80000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((int)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((int)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((int)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((int)0x80000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((int)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((int)0x80000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), DecrementOperatorsHelper.op_Decrement((int)0x00000000)); + Assert.Equal((int)0x00000000, DecrementOperatorsHelper.op_Decrement((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((int)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division((int)0x00000000, 2)); + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division((int)0x00000001, 2)); + Assert.Equal((int)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((int)0x7FFFFFFF, 2)); + Assert.Equal(unchecked((int)0xC0000000), DivisionOperatorsHelper.op_Division(unchecked((int)0x80000000), 2)); + Assert.Equal((int)0x00000000, DivisionOperatorsHelper.op_Division(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((int)0x00000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((int)0x00000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((int)0x7FFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((int)0x80000000), 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((int)0x00000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((int)0x00000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((int)0x7FFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((int)0x80000000), 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((int)0x00000001, IncrementOperatorsHelper.op_Increment((int)0x00000000)); + Assert.Equal((int)0x00000002, IncrementOperatorsHelper.op_Increment((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000000), IncrementOperatorsHelper.op_Increment((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000001), IncrementOperatorsHelper.op_Increment(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000000, IncrementOperatorsHelper.op_Increment(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((int)0x00000000, ModulusOperatorsHelper.op_Modulus((int)0x00000000, 2)); + Assert.Equal((int)0x00000001, ModulusOperatorsHelper.op_Modulus((int)0x00000001, 2)); + Assert.Equal((int)0x00000001, ModulusOperatorsHelper.op_Modulus((int)0x7FFFFFFF, 2)); + Assert.Equal((int)0x00000000, ModulusOperatorsHelper.op_Modulus(unchecked((int)0x80000000), 2)); + Assert.Equal(unchecked((int)0xFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((int)0x00000000, MultiplyOperatorsHelper.op_Multiply((int)0x00000000, 2)); + Assert.Equal((int)0x00000002, MultiplyOperatorsHelper.op_Multiply((int)0x00000001, 2)); + Assert.Equal(unchecked((int)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((int)0x7FFFFFFF, 2)); + Assert.Equal((int)0x00000000, MultiplyOperatorsHelper.op_Multiply(unchecked((int)0x80000000), 2)); + Assert.Equal(unchecked((int)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Abs((int)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Abs((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Abs((int)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000001, NumberHelper.Abs(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Clamp((int)0x00000000, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal((int)0x00000001, NumberHelper.Clamp((int)0x00000001, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal((int)0x0000003F, NumberHelper.Clamp((int)0x7FFFFFFF, unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((int)0xFFFFFFC0), NumberHelper.Clamp(unchecked((int)0x80000000), unchecked((int)0xFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Clamp(unchecked((int)0xFFFFFFFF), unchecked((int)0xFFFFFFC0), 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Create(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((int)0x7FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((int)0x7FFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((int)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((int)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((int)0xFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(unchecked(unchecked((int)0x80000000)))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((int)0xFFFFFFFF)))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((int)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((int)0xFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((int)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((int)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((int)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((int)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((int)0x00000000, (int)0x00000000), NumberHelper.DivRem((int)0x00000000, 2)); + Assert.Equal(((int)0x00000000, (int)0x00000001), NumberHelper.DivRem((int)0x00000001, 2)); + Assert.Equal(((int)0x3FFFFFFF, (int)0x00000001), NumberHelper.DivRem((int)0x7FFFFFFF, 2)); + Assert.Equal((unchecked((int)0xC0000000), (int)0x00000000), NumberHelper.DivRem(unchecked((int)0x80000000), 2)); + Assert.Equal(((int)0x00000000, unchecked((int)0xFFFFFFFF)), NumberHelper.DivRem(unchecked((int)0xFFFFFFFF), 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((int)0x00000001, NumberHelper.Max((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFF, NumberHelper.Max((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max(unchecked((int)0x80000000), 1)); + Assert.Equal((int)0x00000001, NumberHelper.Max(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Min((int)0x00000000, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Min((int)0x00000001, 1)); + Assert.Equal((int)0x00000001, NumberHelper.Min((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0x80000000), NumberHelper.Min(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Min(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((int)0x00000000, NumberHelper.Sign((int)0x00000000)); + Assert.Equal((int)0x00000001, NumberHelper.Sign((int)0x00000001)); + Assert.Equal((int)0x00000001, NumberHelper.Sign((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Sign(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), NumberHelper.Sign(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((int)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((int)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((int)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + int result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((int)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((int)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((int)0xFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((int)0x80000000)), out result)); + Assert.Equal(unchecked((int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((int)0xFFFFFFFF)), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + int result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(unchecked((int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((int)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((int)0xFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((int)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((int)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((int)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(unchecked((int)0x00000000), result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(unchecked((int)0x00000000), result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + int result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((int)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + int result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((int)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((int)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((int)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((int)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((int)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((int)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal(unchecked((int)0x00000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((int)0x00000000), result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_LeftShift((int)0x00000000, 1)); + Assert.Equal((int)0x00000002, ShiftOperatorsHelper.op_LeftShift((int)0x00000001, 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_LeftShift(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_RightShift((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, ShiftOperatorsHelper.op_RightShift((int)0x00000001, 1)); + Assert.Equal((int)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((int)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((int)0xC0000000), ShiftOperatorsHelper.op_RightShift(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((int)0xFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((int)0x00000000, 1)); + Assert.Equal((int)0x00000000, SubtractionOperatorsHelper.op_Subtraction((int)0x00000001, 1)); + Assert.Equal((int)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((int)0x7FFFFFFF, 1)); + Assert.Equal((int)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((int)0x80000000), 1)); + Assert.Equal(unchecked((int)0xFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((int)0xFFFFFFFF), 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((int)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x00000000)); + Assert.Equal(unchecked((int)0xFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x00000001)); + Assert.Equal(unchecked((int)0x80000001), UnaryNegationOperatorsHelper.op_UnaryNegation((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((int)0x80000000))); + Assert.Equal((int)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((int)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x00000000)); + Assert.Equal((int)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x00000001)); + Assert.Equal((int)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((int)0x7FFFFFFF)); + Assert.Equal(unchecked((int)0x80000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((int)0x80000000))); + Assert.Equal(unchecked((int)0xFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((int)0xFFFFFFFF))); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Valid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, int expected) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Invalid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(int), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int32Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, int expected) + { + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int32Tests.Parse_Invalid_TestData), MemberType = typeof(Int32Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + int result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(int), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(int), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs new file mode 100644 index 00000000000..ddc380cc36e --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int64Tests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class Int64Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((long)0x0000000000000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((long)0x8000000000000000), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((long)0x0000000000000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((long)0x0000000000000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((long)0x0000000000000001, AdditionOperatorsHelper.op_Addition((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, AdditionOperatorsHelper.op_Addition((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), AdditionOperatorsHelper.op_Addition((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000000, AdditionOperatorsHelper.op_Addition(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.LeadingZeroCount((long)0x0000000000000000)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.LeadingZeroCount((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.PopCount((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.PopCount((long)0x0000000000000001)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.PopCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.PopCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.PopCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.RotateLeft((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, BinaryIntegerHelper.RotateLeft((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000001, BinaryIntegerHelper.RotateLeft(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.RotateRight((long)0x0000000000000000, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), BinaryIntegerHelper.RotateRight((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x4000000000000000, BinaryIntegerHelper.RotateRight(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((long)0x0000000000000040, BinaryIntegerHelper.TrailingZeroCount((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x000000000000003F, BinaryIntegerHelper.TrailingZeroCount(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((long)0x0000000000000000)); + Assert.True(BinaryNumberHelper.IsPow2((long)0x0000000000000001)); + Assert.False(BinaryNumberHelper.IsPow2((long)0x7FFFFFFFFFFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((long)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((long)0x0000000000000000, BinaryNumberHelper.Log2((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, BinaryNumberHelper.Log2((long)0x0000000000000001)); + Assert.Equal((long)0x000000000000003E, BinaryNumberHelper.Log2((long)0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((long)0x8000000000000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x0000000000000001, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((long)0x0000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((long)0x0000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((long)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((long)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((long)0x8000000000000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((long)0x8000000000000000), 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((long)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((long)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((long)0x8000000000000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((long)0x8000000000000000), 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000000, DecrementOperatorsHelper.op_Decrement((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division((long)0x0000000000000001, 2)); + Assert.Equal((long)0x3FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal(unchecked((long)0xC000000000000000), DivisionOperatorsHelper.op_Division(unchecked((long)0x8000000000000000), 2)); + Assert.Equal((long)0x0000000000000000, DivisionOperatorsHelper.op_Division(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((long)0x0000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((long)0x0000000000000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((long)0x8000000000000000), 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((long)0x0000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((long)0x0000000000000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((long)0x8000000000000000), 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((long)0x0000000000000001, IncrementOperatorsHelper.op_Increment((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000002, IncrementOperatorsHelper.op_Increment((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000000), IncrementOperatorsHelper.op_Increment((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000000, IncrementOperatorsHelper.op_Increment(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((long)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((long)0x0000000000000001, 2)); + Assert.Equal((long)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((long)0x0000000000000000, ModulusOperatorsHelper.op_Modulus(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((long)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((long)0x0000000000000000, 2)); + Assert.Equal((long)0x0000000000000002, MultiplyOperatorsHelper.op_Multiply((long)0x0000000000000001, 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((long)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Abs((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Abs((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Abs((long)0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Abs(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Clamp((long)0x0000000000000000, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Clamp((long)0x0000000000000001, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal((long)0x000000000000003F, NumberHelper.Clamp((long)0x7FFFFFFFFFFFFFFF, unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFC0), NumberHelper.Clamp(unchecked((long)0x8000000000000000), unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Clamp(unchecked((long)0xFFFFFFFFFFFFFFFF), unchecked((long)0xFFFFFFFFFFFFFFC0), 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.Create(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Create(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.Create(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.Create(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateSaturating(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((long)0x0000000000000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((long)0x00000000000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked(unchecked((long)0x8000000000000000)))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((long)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((long)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((long)0x0000000000008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((long)0x000000000000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((long)0x0000000000000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((long)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((long)0x0000000080000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((long)0x00000000FFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((long)0x0000000000000000, (long)0x0000000000000000), NumberHelper.DivRem((long)0x0000000000000000, 2)); + Assert.Equal(((long)0x0000000000000000, (long)0x0000000000000001), NumberHelper.DivRem((long)0x0000000000000001, 2)); + Assert.Equal(((long)0x3FFFFFFFFFFFFFFF, (long)0x0000000000000001), NumberHelper.DivRem((long)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((unchecked((long)0xC000000000000000), (long)0x0000000000000000), NumberHelper.DivRem(unchecked((long)0x8000000000000000), 2)); + Assert.Equal(((long)0x0000000000000000, unchecked((long)0xFFFFFFFFFFFFFFFF)), NumberHelper.DivRem(unchecked((long)0xFFFFFFFFFFFFFFFF), 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((long)0x0000000000000001, NumberHelper.Max((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, NumberHelper.Max((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max(unchecked((long)0x8000000000000000), 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Max(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Min((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Min((long)0x0000000000000001, 1)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Min((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0x8000000000000000), NumberHelper.Min(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Min(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((long)0x0000000000000000, NumberHelper.Sign((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Sign((long)0x0000000000000001)); + Assert.Equal((long)0x0000000000000001, NumberHelper.Sign((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((long)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((long)0x0000000000000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((long)0x00000000000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + long result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((long)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((long)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((long)0x8000000000000000)), out result)); + Assert.Equal(unchecked((long)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + long result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((long)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFF80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((long)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((long)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((long)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((long)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((long)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((long)0x00000000FFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + long result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((long)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + long result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((long)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((long)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((long)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((long)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((long)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((long)0x00000000FFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000002, ShiftOperatorsHelper.op_LeftShift((long)0x0000000000000001, 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((long)0x0000000000000001, 1)); + Assert.Equal((long)0x3FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal(unchecked((long)0xC000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((long)0x0000000000000000, 1)); + Assert.Equal((long)0x0000000000000000, SubtractionOperatorsHelper.op_Subtraction((long)0x0000000000000001, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((long)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((long)0x8000000000000000), 1)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((long)0xFFFFFFFFFFFFFFFF), 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((long)0x0000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x0000000000000000)); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x0000000000000001)); + Assert.Equal(unchecked((long)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((long)0x8000000000000000))); + Assert.Equal((long)0x0000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((long)0x0000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x0000000000000000)); + Assert.Equal((long)0x0000000000000001, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x0000000000000001)); + Assert.Equal((long)0x7FFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((long)0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((long)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((long)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Valid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, long expected) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Invalid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(long), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(Int64Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, long expected) + { + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Int64Tests.Parse_Invalid_TestData), MemberType = typeof(Int64Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + long result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(long), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(long), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs new file mode 100644 index 00000000000..1f5b4144f94 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.GenericMath.cs @@ -0,0 +1,1746 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class IntPtrTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((nint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x8000000000000000), MinMaxValueHelper.MinValue); + } + else + { + Assert.Equal(unchecked((nint)0x80000000), MinMaxValueHelper.MinValue); + } + } + + [Fact] + public static void MaxValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), MinMaxValueHelper.MaxValue); + } + else + { + Assert.Equal((nint)0x7FFFFFFF, MinMaxValueHelper.MaxValue); + } + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((nint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), SignedNumberHelper.NegativeOne); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), SignedNumberHelper.NegativeOne); + } + } + + [Fact] + public static void OneTest() + { + Assert.Equal((nint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000002), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, AdditionOperatorsHelper.op_Addition((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000002, AdditionOperatorsHelper.op_Addition((nint)0x00000001, (nint)1)); + Assert.Equal(unchecked((nint)0x80000000), AdditionOperatorsHelper.op_Addition((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), AdditionOperatorsHelper.op_Addition(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000000, AdditionOperatorsHelper.op_Addition(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void LeadingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x0000000000000020, BinaryIntegerHelper.LeadingZeroCount((nint)0x00000000)); + Assert.Equal((nint)0x000000000000001F, BinaryIntegerHelper.LeadingZeroCount((nint)0x00000001)); + Assert.Equal((nint)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void PopCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.PopCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.PopCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.PopCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.PopCount((nint)0x00000000)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.PopCount((nint)0x00000001)); + Assert.Equal((nint)0x0000001F, BinaryIntegerHelper.PopCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.PopCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000020, BinaryIntegerHelper.PopCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void RotateLeftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000002), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BinaryIntegerHelper.RotateLeft(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.RotateLeft((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000002, BinaryIntegerHelper.RotateLeft((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BinaryIntegerHelper.RotateLeft((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x00000001, BinaryIntegerHelper.RotateLeft(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void RotateRightTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x8000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x4000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.RotateRight((nint)0x00000000, 1)); + Assert.Equal(unchecked((nint)0x80000000), BinaryIntegerHelper.RotateRight((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xBFFFFFFF), BinaryIntegerHelper.RotateRight((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x40000000, BinaryIntegerHelper.RotateRight(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void TrailingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000040), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x000000000000003F), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((nint)0x00000000)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nint)0x00000001)); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, BinaryIntegerHelper.TrailingZeroCount(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void IsPow2Test() + { + if (Environment.Is64BitProcess) + { + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x0000000000000000))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nint)0x0000000000000001))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.False(BinaryNumberHelper.IsPow2((nint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((nint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((nint)0x7FFFFFFF)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0x80000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void Log2Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x000000000000003E), BinaryNumberHelper.Log2(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, BinaryNumberHelper.Log2((nint)0x00000000)); + Assert.Equal((nint)0x00000000, BinaryNumberHelper.Log2((nint)0x00000001)); + Assert.Equal((nint)0x0000001E, BinaryNumberHelper.Log2((nint)0x7FFFFFFF)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0x80000000))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_BitwiseAndTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_BitwiseOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_ExclusiveOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_OnesComplementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement((nint)0x00000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000000), BitwiseOperatorsHelper.op_OnesComplement((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_LessThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((nint)0x00000000, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nint)0x00000001, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nint)0x7FFFFFFF, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0x80000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x00000000, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x00000001, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nint)0x7FFFFFFF, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0x80000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_GreaterThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nint)0x00000000, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nint)0x00000001, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nint)0x7FFFFFFF, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0x80000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x00000000, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x00000001, (nint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nint)0x7FFFFFFF, (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0x80000000), (nint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_DecrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), DecrementOperatorsHelper.op_Decrement((nint)0x00000000)); + Assert.Equal((nint)0x00000000, DecrementOperatorsHelper.op_Decrement((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((nint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_DivisionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0x3FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0xC000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division((nint)0x00000001, (nint)2)); + Assert.Equal((nint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal(unchecked((nint)0xC0000000), DivisionOperatorsHelper.op_Division(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal((nint)0x00000000, DivisionOperatorsHelper.op_Division(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void op_EqualityTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.False(EqualityOperatorsHelper.op_Equality((nint)0x00000000, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((nint)0x00000001, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nint)0x7FFFFFFF, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0x80000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_InequalityTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.True(EqualityOperatorsHelper.op_Inequality((nint)0x00000000, (nint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((nint)0x00000001, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nint)0x7FFFFFFF, (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0x80000000), (nint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_IncrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000002), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000001, IncrementOperatorsHelper.op_Increment((nint)0x00000000)); + Assert.Equal((nint)0x00000002, IncrementOperatorsHelper.op_Increment((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000000), IncrementOperatorsHelper.op_Increment((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000001), IncrementOperatorsHelper.op_Increment(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000000, IncrementOperatorsHelper.op_Increment(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_ModulusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, ModulusOperatorsHelper.op_Modulus((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000001, ModulusOperatorsHelper.op_Modulus((nint)0x00000001, (nint)2)); + Assert.Equal((nint)0x00000001, ModulusOperatorsHelper.op_Modulus((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((nint)0x00000000, ModulusOperatorsHelper.op_Modulus(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), ModulusOperatorsHelper.op_Modulus(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void op_MultiplyTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000002), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal(unchecked((nint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal((nint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nint)0x00000000, (nint)2)); + Assert.Equal((nint)0x00000002, MultiplyOperatorsHelper.op_Multiply((nint)0x00000001, (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((nint)0x00000000, MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void AbsTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Abs(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Abs(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Abs(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Abs(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Abs((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Abs((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Abs((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Abs(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000001, NumberHelper.Abs(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void ClampTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Clamp(unchecked((nint)0x0000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Clamp(unchecked((nint)0x0000000000000001), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0x000000000000003F), NumberHelper.Clamp(unchecked((nint)0x7FFFFFFFFFFFFFFF), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFC0), NumberHelper.Clamp(unchecked((nint)0x8000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Clamp(unchecked((nint)0xFFFFFFFFFFFFFFFF), unchecked((nint)0xFFFFFFFFFFFFFFC0), unchecked((nint)0x000000000000003F))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Clamp((nint)0x00000000, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal((nint)0x00000001, NumberHelper.Clamp((nint)0x00000001, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal((nint)0x0000003F, NumberHelper.Clamp((nint)0x7FFFFFFF, unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal(unchecked((nint)0xFFFFFFC0), NumberHelper.Clamp(unchecked((nint)0x80000000), unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Clamp(unchecked((nint)0xFFFFFFFF), unchecked((nint)0xFFFFFFC0), (nint)0x0000003F)); + } + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.Create(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Create(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x00000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x00000001)); + Assert.Equal(unchecked((nint)0x000000007FFFFFFF), NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x0000000080000000), NumberHelper.Create(0x80000000)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), NumberHelper.Create(0xFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateSaturating(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((nint)0x000000007FFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x0000000080000000), NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x80000000))); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((nint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((nint)0x0000000000007FFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nint)0xFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)(int)0x80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(unchecked(unchecked((nint)0x80000000)))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked(unchecked((nint)0xFFFFFFFF)))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x00)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x01)); + Assert.Equal(unchecked((nint)0x000000000000007F), NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nint)0xFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((nint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.CreateTruncating(unchecked((nuint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFF))); + } + } + + [Fact] + public static void DivRemTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nint)0x0000000000000000), (nint)2)); + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nint)0x0000000000000001), (nint)2)); + Assert.Equal((unchecked((nint)0x3FFFFFFFFFFFFFFF), unchecked((nint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)2)); + Assert.Equal((unchecked((nint)0xC000000000000000), unchecked((nint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nint)0x8000000000000000), (nint)2)); + Assert.Equal((unchecked((nint)0x0000000000000000), unchecked((nint)0xFFFFFFFFFFFFFFFF)), NumberHelper.DivRem(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)2)); + } + else + { + Assert.Equal(((nint)0x00000000, (nint)0x00000000), NumberHelper.DivRem((nint)0x00000000, (nint)2)); + Assert.Equal(((nint)0x00000000, (nint)0x00000001), NumberHelper.DivRem((nint)0x00000001, (nint)2)); + Assert.Equal(((nint)0x3FFFFFFF, (nint)0x00000001), NumberHelper.DivRem((nint)0x7FFFFFFF, (nint)2)); + Assert.Equal((unchecked((nint)0xC0000000), (nint)0x00000000), NumberHelper.DivRem(unchecked((nint)0x80000000), (nint)2)); + Assert.Equal(((nint)0x00000000, unchecked((nint)0xFFFFFFFF)), NumberHelper.DivRem(unchecked((nint)0xFFFFFFFF), (nint)2)); + } + } + + [Fact] + public static void MaxTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Max(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000001, NumberHelper.Max((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, NumberHelper.Max((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Max(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void MinTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Min(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Min(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Min(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x8000000000000000), NumberHelper.Min(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Min(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Min((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Min((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x00000001, NumberHelper.Min((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal(unchecked((nint)0x80000000), NumberHelper.Min(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Min(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void SignTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), NumberHelper.Sign(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Sign(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x0000000000000001), NumberHelper.Sign(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), NumberHelper.Sign(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, NumberHelper.Sign((nint)0x00000000)); + Assert.Equal((nint)0x00000001, NumberHelper.Sign((nint)0x00000001)); + Assert.Equal((nint)0x00000001, NumberHelper.Sign((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Sign(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), NumberHelper.Sign(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((nint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((nint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((nint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((nint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFF8000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(unchecked((nint)(int)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(unchecked((nint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((nint)0x80000000)), out result)); + Assert.Equal(unchecked((nint)0x80000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked(unchecked((nint)0xFFFFFFFF)), out result)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFF80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((nint)(int)0xFFFFFFFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + nint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((nint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((nint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(unchecked((nint)0x0000000080000000), result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x00000000FFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nint)0x00000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nint)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + nint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((nint)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((nint)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((nint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((nint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked(unchecked((nuint)0x80000000)), out result)); + Assert.Equal((nint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked(unchecked((nuint)0xFFFFFFFF)), out result)); + Assert.Equal((nint)0x00000000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000002), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000002, ShiftOperatorsHelper.op_LeftShift((nint)0x00000001, 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift((nint)0x7FFFFFFF, 1)); + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void op_RightShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nint)0x3FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nint)0xC000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_RightShift((nint)0x00000000, 1)); + Assert.Equal((nint)0x00000000, ShiftOperatorsHelper.op_RightShift((nint)0x00000001, 1)); + Assert.Equal((nint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((nint)0x7FFFFFFF, 1)); + Assert.Equal(unchecked((nint)0xC0000000), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0x80000000), 1)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nint)0xFFFFFFFF), 1)); + } + } + + [Fact] + public static void op_SubtractionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x0000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0x0000000000000000), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x0000000000000001), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x7FFFFFFFFFFFFFFF), (nint)1)); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x8000000000000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0xFFFFFFFFFFFFFFFF), (nint)1)); + } + else + { + Assert.Equal(unchecked((nint)0xFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction((nint)0x00000000, (nint)1)); + Assert.Equal((nint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((nint)0x00000001, (nint)1)); + Assert.Equal((nint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nint)0x7FFFFFFF, (nint)1)); + Assert.Equal((nint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0x80000000), (nint)1)); + Assert.Equal(unchecked((nint)0xFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nint)0xFFFFFFFF), (nint)1)); + } + } + + [Fact] + public static void op_UnaryNegationTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x00000000)); + Assert.Equal(unchecked((nint)0xFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x00000001)); + Assert.Equal(unchecked((nint)0x80000001), UnaryNegationOperatorsHelper.op_UnaryNegation((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0x80000000))); + Assert.Equal((nint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void op_UnaryPlusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nint)0x0000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nint)0x0000000000000001), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nint)0x7FFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nint)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x00000000)); + Assert.Equal((nint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x00000001)); + Assert.Equal((nint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((nint)0x80000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((nint)0xFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nint)0xFFFFFFFF))); + } + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Valid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, nint expected) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Invalid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(nint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, nint expected) + { + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(IntPtrTests.Parse_Invalid_TestData), MemberType = typeof(IntPtrTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + nint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(nint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(nint), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs new file mode 100644 index 00000000000..82877a1e2d6 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/SByteTests.GenericMath.cs @@ -0,0 +1,1181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class SByteTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((sbyte)0x00, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(unchecked((sbyte)0x80), MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((sbyte)0x7F, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((sbyte)0x01, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(unchecked((sbyte)0xFF), SignedNumberHelper.NegativeOne); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((sbyte)0x01, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((sbyte)0x01, AdditionOperatorsHelper.op_Addition((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x02, AdditionOperatorsHelper.op_Addition((sbyte)0x01, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x80), AdditionOperatorsHelper.op_Addition((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), AdditionOperatorsHelper.op_Addition(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x00, AdditionOperatorsHelper.op_Addition(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x00)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x01)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.LeadingZeroCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.LeadingZeroCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.LeadingZeroCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.PopCount((sbyte)0x00)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.PopCount((sbyte)0x01)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.PopCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.PopCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.PopCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.RotateLeft((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x02, BinaryIntegerHelper.RotateLeft((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xFE), BinaryIntegerHelper.RotateLeft((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x01, BinaryIntegerHelper.RotateLeft(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), BinaryIntegerHelper.RotateLeft(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.RotateRight((sbyte)0x00, 1)); + Assert.Equal(unchecked((sbyte)0x80), BinaryIntegerHelper.RotateRight((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xBF), BinaryIntegerHelper.RotateRight((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x40, BinaryIntegerHelper.RotateRight(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), BinaryIntegerHelper.RotateRight(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((sbyte)0x08, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x00)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x01)); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount((sbyte)0x7F)); + Assert.Equal((sbyte)0x07, BinaryIntegerHelper.TrailingZeroCount(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BinaryIntegerHelper.TrailingZeroCount(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((sbyte)0x00)); + Assert.True(BinaryNumberHelper.IsPow2((sbyte)0x01)); + Assert.False(BinaryNumberHelper.IsPow2((sbyte)0x7F)); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((sbyte)0x80))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((sbyte)0x00, BinaryNumberHelper.Log2((sbyte)0x00)); + Assert.Equal((sbyte)0x00, BinaryNumberHelper.Log2((sbyte)0x01)); + Assert.Equal((sbyte)0x06, BinaryNumberHelper.Log2((sbyte)0x7F)); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((sbyte)0x80))); + Assert.Throws(() => BinaryNumberHelper.Log2(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7F, BitwiseOperatorsHelper.op_BitwiseOr((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((sbyte)0x01, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7E, BitwiseOperatorsHelper.op_ExclusiveOr((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x81), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(unchecked((sbyte)0xFF), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x00)); + Assert.Equal(unchecked((sbyte)0xFE), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x80), BitwiseOperatorsHelper.op_OnesComplement((sbyte)0x7F)); + Assert.Equal((sbyte)0x7F, BitwiseOperatorsHelper.op_OnesComplement(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, BitwiseOperatorsHelper.op_OnesComplement(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((sbyte)0x00, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((sbyte)0x01, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((sbyte)0x7F, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x00, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x01, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((sbyte)0x7F, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x00, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x01, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((sbyte)0x7F, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x00, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x01, (sbyte)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((sbyte)0x7F, (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(unchecked((sbyte)0xFF), DecrementOperatorsHelper.op_Decrement((sbyte)0x00)); + Assert.Equal((sbyte)0x00, DecrementOperatorsHelper.op_Decrement((sbyte)0x01)); + Assert.Equal((sbyte)0x7E, DecrementOperatorsHelper.op_Decrement((sbyte)0x7F)); + Assert.Equal((sbyte)0x7F, DecrementOperatorsHelper.op_Decrement(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFE), DecrementOperatorsHelper.op_Decrement(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division((sbyte)0x01, (sbyte)2)); + Assert.Equal((sbyte)0x3F, DivisionOperatorsHelper.op_Division((sbyte)0x7F, (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xC0), DivisionOperatorsHelper.op_Division(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal((sbyte)0x00, DivisionOperatorsHelper.op_Division(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((sbyte)0x00, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((sbyte)0x01, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((sbyte)0x7F, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((sbyte)0x80), (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((sbyte)0x00, (sbyte)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((sbyte)0x01, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((sbyte)0x7F, (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((sbyte)0x80), (sbyte)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((sbyte)0x01, IncrementOperatorsHelper.op_Increment((sbyte)0x00)); + Assert.Equal((sbyte)0x02, IncrementOperatorsHelper.op_Increment((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x80), IncrementOperatorsHelper.op_Increment((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x81), IncrementOperatorsHelper.op_Increment(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x00, IncrementOperatorsHelper.op_Increment(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((sbyte)0x00, ModulusOperatorsHelper.op_Modulus((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x01, ModulusOperatorsHelper.op_Modulus((sbyte)0x01, (sbyte)2)); + Assert.Equal((sbyte)0x01, ModulusOperatorsHelper.op_Modulus((sbyte)0x7F, (sbyte)2)); + Assert.Equal((sbyte)0x00, ModulusOperatorsHelper.op_Modulus(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFF), ModulusOperatorsHelper.op_Modulus(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((sbyte)0x00, MultiplyOperatorsHelper.op_Multiply((sbyte)0x00, (sbyte)2)); + Assert.Equal((sbyte)0x02, MultiplyOperatorsHelper.op_Multiply((sbyte)0x01, (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFE), MultiplyOperatorsHelper.op_Multiply((sbyte)0x7F, (sbyte)2)); + Assert.Equal((sbyte)0x00, MultiplyOperatorsHelper.op_Multiply(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(unchecked((sbyte)0xFE), MultiplyOperatorsHelper.op_Multiply(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Abs((sbyte)0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Abs((sbyte)0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Abs((sbyte)0x7F)); + Assert.Throws(() => NumberHelper.Abs(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x01, NumberHelper.Abs(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Clamp((sbyte)0x00, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal((sbyte)0x01, NumberHelper.Clamp((sbyte)0x01, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal((sbyte)0x3F, NumberHelper.Clamp((sbyte)0x7F, unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal(unchecked((sbyte)0xC0), NumberHelper.Clamp(unchecked((sbyte)0x80), unchecked((sbyte)0xC0), (sbyte)0x3F)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Clamp(unchecked((sbyte)0xFF), unchecked((sbyte)0xC0), (sbyte)0x3F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(0x80)); + Assert.Throws(() => NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((char)0x0001)); + Assert.Throws(() => NumberHelper.Create((char)0x7FFF)); + Assert.Throws(() => NumberHelper.Create((char)0x8000)); + Assert.Throws(() => NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.Create(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0001)); + Assert.Throws(() => NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(0x8000)); + Assert.Throws(() => NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x8000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal(unchecked((sbyte)0x7F), NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateTruncating(0x80)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((sbyte)0x7F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((sbyte)0x01, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((sbyte)0x00, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((sbyte)0x00, (sbyte)0x00), NumberHelper.DivRem((sbyte)0x00, (sbyte)2)); + Assert.Equal(((sbyte)0x00, (sbyte)0x01), NumberHelper.DivRem((sbyte)0x01, (sbyte)2)); + Assert.Equal(((sbyte)0x3F, (sbyte)0x01), NumberHelper.DivRem((sbyte)0x7F, (sbyte)2)); + Assert.Equal((unchecked((sbyte)0xC0), (sbyte)0x00), NumberHelper.DivRem(unchecked((sbyte)0x80), (sbyte)2)); + Assert.Equal(((sbyte)0x00, unchecked((sbyte)0xFF)), NumberHelper.DivRem(unchecked((sbyte)0xFF), (sbyte)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((sbyte)0x01, NumberHelper.Max((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7F, NumberHelper.Max((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Max(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Min((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Min((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x01, NumberHelper.Min((sbyte)0x7F, (sbyte)1)); + Assert.Equal(unchecked((sbyte)0x80), NumberHelper.Min(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Min(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((sbyte)0x00, NumberHelper.Sign((sbyte)0x00)); + Assert.Equal((sbyte)0x01, NumberHelper.Sign((sbyte)0x01)); + Assert.Equal((sbyte)0x01, NumberHelper.Sign((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Sign(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), NumberHelper.Sign(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void TryCreateFromByteTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((sbyte)0x7F, result); + + Assert.False(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + sbyte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((sbyte)0x7F, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(unchecked((sbyte)0x80), result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(unchecked((sbyte)0xFF), result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + sbyte result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + sbyte result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((sbyte)0x01, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((sbyte)0x00, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((sbyte)0x00, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_LeftShift((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x02, ShiftOperatorsHelper.op_LeftShift((sbyte)0x01, 1)); + Assert.Equal(unchecked((sbyte)0xFE), ShiftOperatorsHelper.op_LeftShift((sbyte)0x7F, 1)); + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_LeftShift(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFE), ShiftOperatorsHelper.op_LeftShift(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_RightShift((sbyte)0x00, 1)); + Assert.Equal((sbyte)0x00, ShiftOperatorsHelper.op_RightShift((sbyte)0x01, 1)); + Assert.Equal((sbyte)0x3F, ShiftOperatorsHelper.op_RightShift((sbyte)0x7F, 1)); + Assert.Equal(unchecked((sbyte)0xC0), ShiftOperatorsHelper.op_RightShift(unchecked((sbyte)0x80), 1)); + Assert.Equal(unchecked((sbyte)0xFF), ShiftOperatorsHelper.op_RightShift(unchecked((sbyte)0xFF), 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(unchecked((sbyte)0xFF), SubtractionOperatorsHelper.op_Subtraction((sbyte)0x00, (sbyte)1)); + Assert.Equal((sbyte)0x00, SubtractionOperatorsHelper.op_Subtraction((sbyte)0x01, (sbyte)1)); + Assert.Equal((sbyte)0x7E, SubtractionOperatorsHelper.op_Subtraction((sbyte)0x7F, (sbyte)1)); + Assert.Equal((sbyte)0x7F, SubtractionOperatorsHelper.op_Subtraction(unchecked((sbyte)0x80), (sbyte)1)); + Assert.Equal(unchecked((sbyte)0xFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((sbyte)0xFF), (sbyte)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((sbyte)0x00, UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x00)); + Assert.Equal(unchecked((sbyte)0xFF), UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x01)); + Assert.Equal(unchecked((sbyte)0x81), UnaryNegationOperatorsHelper.op_UnaryNegation((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x80), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((sbyte)0x80))); + Assert.Equal((sbyte)0x01, UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((sbyte)0x00, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x00)); + Assert.Equal((sbyte)0x01, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x01)); + Assert.Equal((sbyte)0x7F, UnaryPlusOperatorsHelper.op_UnaryPlus((sbyte)0x7F)); + Assert.Equal(unchecked((sbyte)0x80), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((sbyte)0xFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((sbyte)0xFF))); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Valid_TestData), MemberType = typeof(SByteTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, sbyte expected) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Invalid_TestData), MemberType = typeof(SByteTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(sbyte), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(SByteTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, sbyte expected) + { + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(SByteTests.Parse_Invalid_TestData), MemberType = typeof(SByteTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + sbyte result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(sbyte), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(sbyte), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs new file mode 100644 index 00000000000..982740495bb --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt16Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt16Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((ushort)0x0000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((ushort)0x0000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((ushort)0xFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((ushort)0x0001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((ushort)0x0001, AdditionOperatorsHelper.op_Addition((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0002, AdditionOperatorsHelper.op_Addition((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x8000, AdditionOperatorsHelper.op_Addition((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, AdditionOperatorsHelper.op_Addition((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0000, AdditionOperatorsHelper.op_Addition((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.LeadingZeroCount((ushort)0x0000)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.LeadingZeroCount((ushort)0x0001)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.LeadingZeroCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.LeadingZeroCount((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.LeadingZeroCount((ushort)0xFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.PopCount((ushort)0x0000)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.PopCount((ushort)0x0001)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.PopCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.PopCount((ushort)0x8000)); + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.PopCount((ushort)0xFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.RotateLeft((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0002, BinaryIntegerHelper.RotateLeft((ushort)0x0001, 1)); + Assert.Equal((ushort)0xFFFE, BinaryIntegerHelper.RotateLeft((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x0001, BinaryIntegerHelper.RotateLeft((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFF, BinaryIntegerHelper.RotateLeft((ushort)0xFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.RotateRight((ushort)0x0000, 1)); + Assert.Equal((ushort)0x8000, BinaryIntegerHelper.RotateRight((ushort)0x0001, 1)); + Assert.Equal((ushort)0xBFFF, BinaryIntegerHelper.RotateRight((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x4000, BinaryIntegerHelper.RotateRight((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFF, BinaryIntegerHelper.RotateRight((ushort)0xFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((ushort)0x0010, BinaryIntegerHelper.TrailingZeroCount((ushort)0x0000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0x0001)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0x7FFF)); + Assert.Equal((ushort)0x000F, BinaryIntegerHelper.TrailingZeroCount((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BinaryIntegerHelper.TrailingZeroCount((ushort)0xFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((ushort)0x0000)); + Assert.True(BinaryNumberHelper.IsPow2((ushort)0x0001)); + Assert.False(BinaryNumberHelper.IsPow2((ushort)0x7FFF)); + Assert.True(BinaryNumberHelper.IsPow2((ushort)0x8000)); + Assert.False(BinaryNumberHelper.IsPow2((ushort)0xFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((ushort)0x0000, BinaryNumberHelper.Log2((ushort)0x0000)); + Assert.Equal((ushort)0x0000, BinaryNumberHelper.Log2((ushort)0x0001)); + Assert.Equal((ushort)0x000E, BinaryNumberHelper.Log2((ushort)0x7FFF)); + Assert.Equal((ushort)0x000F, BinaryNumberHelper.Log2((ushort)0x8000)); + Assert.Equal((ushort)0x000F, BinaryNumberHelper.Log2((ushort)0xFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseAnd((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFF, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((ushort)0x0001, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFE, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8001, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((ushort)0xFFFF, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x0000)); + Assert.Equal((ushort)0xFFFE, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x0001)); + Assert.Equal((ushort)0x8000, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x7FFF)); + Assert.Equal((ushort)0x7FFF, BitwiseOperatorsHelper.op_OnesComplement((ushort)0x8000)); + Assert.Equal((ushort)0x0000, BitwiseOperatorsHelper.op_OnesComplement((ushort)0xFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((ushort)0x0000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x0001, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x7FFF, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0x8000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x0000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x0001, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x7FFF, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0x8000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x0000, (ushort)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x0001, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x7FFF, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0x8000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x0000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x0001, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x7FFF, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0x8000, (ushort)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((ushort)0xFFFF, DecrementOperatorsHelper.op_Decrement((ushort)0x0000)); + Assert.Equal((ushort)0x0000, DecrementOperatorsHelper.op_Decrement((ushort)0x0001)); + Assert.Equal((ushort)0x7FFE, DecrementOperatorsHelper.op_Decrement((ushort)0x7FFF)); + Assert.Equal((ushort)0x7FFF, DecrementOperatorsHelper.op_Decrement((ushort)0x8000)); + Assert.Equal((ushort)0xFFFE, DecrementOperatorsHelper.op_Decrement((ushort)0xFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((ushort)0x0000, DivisionOperatorsHelper.op_Division((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0000, DivisionOperatorsHelper.op_Division((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0x3FFF, DivisionOperatorsHelper.op_Division((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x4000, DivisionOperatorsHelper.op_Division((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0x7FFF, DivisionOperatorsHelper.op_Division((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x0000, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((ushort)0x0001, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x7FFF, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0x8000, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x0000, (ushort)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((ushort)0x0001, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x7FFF, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0x8000, (ushort)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((ushort)0x0001, IncrementOperatorsHelper.op_Increment((ushort)0x0000)); + Assert.Equal((ushort)0x0002, IncrementOperatorsHelper.op_Increment((ushort)0x0001)); + Assert.Equal((ushort)0x8000, IncrementOperatorsHelper.op_Increment((ushort)0x7FFF)); + Assert.Equal((ushort)0x8001, IncrementOperatorsHelper.op_Increment((ushort)0x8000)); + Assert.Equal((ushort)0x0000, IncrementOperatorsHelper.op_Increment((ushort)0xFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((ushort)0x0000, ModulusOperatorsHelper.op_Modulus((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x0000, ModulusOperatorsHelper.op_Modulus((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0x0001, ModulusOperatorsHelper.op_Modulus((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((ushort)0x0000, MultiplyOperatorsHelper.op_Multiply((ushort)0x0000, (ushort)2)); + Assert.Equal((ushort)0x0002, MultiplyOperatorsHelper.op_Multiply((ushort)0x0001, (ushort)2)); + Assert.Equal((ushort)0xFFFE, MultiplyOperatorsHelper.op_Multiply((ushort)0x7FFF, (ushort)2)); + Assert.Equal((ushort)0x0000, MultiplyOperatorsHelper.op_Multiply((ushort)0x8000, (ushort)2)); + Assert.Equal((ushort)0xFFFE, MultiplyOperatorsHelper.op_Multiply((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Abs((ushort)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Abs((ushort)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Abs((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Abs((ushort)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Abs((ushort)0xFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.Clamp((ushort)0x0000, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x0001, NumberHelper.Clamp((ushort)0x0001, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0x7FFF, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0x8000, (ushort)0x0001, (ushort)0x003F)); + Assert.Equal((ushort)0x003F, NumberHelper.Clamp((ushort)0xFFFF, (ushort)0x0001, (ushort)0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.Create(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.Create(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Create((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((nint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.Create(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x00000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x80000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.Create((nuint)0x00000001)); + Assert.Throws(() => NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create((nuint)0x80000000)); + Assert.Throws(() => NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ushort)0x0080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((ushort)0x00FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ushort)0x007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ushort)0xFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ushort)0x7FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ushort)0x8000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((ushort)0x0001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((ushort)0x0000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((ushort)0xFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((ushort)0x0000, (ushort)0x0000), NumberHelper.DivRem((ushort)0x0000, (ushort)2)); + Assert.Equal(((ushort)0x0000, (ushort)0x0001), NumberHelper.DivRem((ushort)0x0001, (ushort)2)); + Assert.Equal(((ushort)0x3FFF, (ushort)0x0001), NumberHelper.DivRem((ushort)0x7FFF, (ushort)2)); + Assert.Equal(((ushort)0x4000, (ushort)0x0000), NumberHelper.DivRem((ushort)0x8000, (ushort)2)); + Assert.Equal(((ushort)0x7FFF, (ushort)0x0001), NumberHelper.DivRem((ushort)0xFFFF, (ushort)2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((ushort)0x0001, NumberHelper.Max((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Max((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFF, NumberHelper.Max((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x8000, NumberHelper.Max((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFF, NumberHelper.Max((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Min((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0x0001, NumberHelper.Min((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((ushort)0x0000, NumberHelper.Sign((ushort)0x0000)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x0001)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x7FFF)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0x8000)); + Assert.Equal((ushort)0x0001, NumberHelper.Sign((ushort)0xFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ushort)0x007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((ushort)0x0080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((ushort)0x00FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((ushort)0x8000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((ushort)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + ushort result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ushort)0x007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ushort)0x7FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((ushort)0x8000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((ushort)0xFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + ushort result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + ushort result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((ushort)0x0001, result); + + Assert.False(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((ushort)0x0000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((ushort)0x0000, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_LeftShift((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0002, ShiftOperatorsHelper.op_LeftShift((ushort)0x0001, 1)); + Assert.Equal((ushort)0xFFFE, ShiftOperatorsHelper.op_LeftShift((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_LeftShift((ushort)0x8000, 1)); + Assert.Equal((ushort)0xFFFE, ShiftOperatorsHelper.op_LeftShift((ushort)0xFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_RightShift((ushort)0x0000, 1)); + Assert.Equal((ushort)0x0000, ShiftOperatorsHelper.op_RightShift((ushort)0x0001, 1)); + Assert.Equal((ushort)0x3FFF, ShiftOperatorsHelper.op_RightShift((ushort)0x7FFF, 1)); + Assert.Equal((ushort)0x4000, ShiftOperatorsHelper.op_RightShift((ushort)0x8000, 1)); + Assert.Equal((ushort)0x7FFF, ShiftOperatorsHelper.op_RightShift((ushort)0xFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((ushort)0xFFFF, SubtractionOperatorsHelper.op_Subtraction((ushort)0x0000, (ushort)1)); + Assert.Equal((ushort)0x0000, SubtractionOperatorsHelper.op_Subtraction((ushort)0x0001, (ushort)1)); + Assert.Equal((ushort)0x7FFE, SubtractionOperatorsHelper.op_Subtraction((ushort)0x7FFF, (ushort)1)); + Assert.Equal((ushort)0x7FFF, SubtractionOperatorsHelper.op_Subtraction((ushort)0x8000, (ushort)1)); + Assert.Equal((ushort)0xFFFE, SubtractionOperatorsHelper.op_Subtraction((ushort)0xFFFF, (ushort)1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((ushort)0x0000, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x0000)); + Assert.Equal((ushort)0xFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x0001)); + Assert.Equal((ushort)0x8001, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0x8000)); + Assert.Equal((ushort)0x0001, UnaryNegationOperatorsHelper.op_UnaryNegation((ushort)0xFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((ushort)0x0000, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x0000)); + Assert.Equal((ushort)0x0001, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x0001)); + Assert.Equal((ushort)0x7FFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x7FFF)); + Assert.Equal((ushort)0x8000, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0x8000)); + Assert.Equal((ushort)0xFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ushort)0xFFFF)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Valid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, ushort expected) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Invalid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(ushort), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, ushort expected) + { + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt16Tests.Parse_Invalid_TestData), MemberType = typeof(UInt16Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + ushort result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(ushort), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(ushort), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs new file mode 100644 index 00000000000..ab6a6e02471 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt32Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt32Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((uint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((uint)0x00000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((uint)0xFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((uint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((uint)0x00000001, AdditionOperatorsHelper.op_Addition((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, AdditionOperatorsHelper.op_Addition((uint)0x00000001, 1)); + Assert.Equal((uint)0x80000000, AdditionOperatorsHelper.op_Addition((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, AdditionOperatorsHelper.op_Addition((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000000, AdditionOperatorsHelper.op_Addition((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.LeadingZeroCount((uint)0x00000000)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.LeadingZeroCount((uint)0x00000001)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.LeadingZeroCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.LeadingZeroCount((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.LeadingZeroCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.PopCount((uint)0x00000000)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.PopCount((uint)0x00000001)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.PopCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.PopCount((uint)0x80000000)); + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.PopCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.RotateLeft((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, BinaryIntegerHelper.RotateLeft((uint)0x00000001, 1)); + Assert.Equal((uint)0xFFFFFFFE, BinaryIntegerHelper.RotateLeft((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000001, BinaryIntegerHelper.RotateLeft((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BinaryIntegerHelper.RotateLeft((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.RotateRight((uint)0x00000000, 1)); + Assert.Equal((uint)0x80000000, BinaryIntegerHelper.RotateRight((uint)0x00000001, 1)); + Assert.Equal((uint)0xBFFFFFFF, BinaryIntegerHelper.RotateRight((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x40000000, BinaryIntegerHelper.RotateRight((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BinaryIntegerHelper.RotateRight((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((uint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((uint)0x00000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0x00000001)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((uint)0xFFFFFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((uint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((uint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((uint)0x7FFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((uint)0x80000000)); + Assert.False(BinaryNumberHelper.IsPow2((uint)0xFFFFFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((uint)0x00000000, BinaryNumberHelper.Log2((uint)0x00000000)); + Assert.Equal((uint)0x00000000, BinaryNumberHelper.Log2((uint)0x00000001)); + Assert.Equal((uint)0x0000001E, BinaryNumberHelper.Log2((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x0000001F, BinaryNumberHelper.Log2((uint)0x80000000)); + Assert.Equal((uint)0x0000001F, BinaryNumberHelper.Log2((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x00000001, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, BitwiseOperatorsHelper.op_BitwiseOr((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((uint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000001, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((uint)0xFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((uint)0x00000000)); + Assert.Equal((uint)0xFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((uint)0x00000001)); + Assert.Equal((uint)0x80000000, BitwiseOperatorsHelper.op_OnesComplement((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((uint)0x80000000)); + Assert.Equal((uint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((uint)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0x80000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x00000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x7FFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0x80000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((uint)0x00000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((uint)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0x80000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x00000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x00000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x7FFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0x80000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((uint)0xFFFFFFFF, DecrementOperatorsHelper.op_Decrement((uint)0x00000000)); + Assert.Equal((uint)0x00000000, DecrementOperatorsHelper.op_Decrement((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFE, DecrementOperatorsHelper.op_Decrement((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((uint)0x00000000, DivisionOperatorsHelper.op_Division((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000000, DivisionOperatorsHelper.op_Division((uint)0x00000001, 2)); + Assert.Equal((uint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x40000000, DivisionOperatorsHelper.op_Division((uint)0x80000000, 2)); + Assert.Equal((uint)0x7FFFFFFF, DivisionOperatorsHelper.op_Division((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x00000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((uint)0x00000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x7FFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0x80000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x00000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((uint)0x00000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x7FFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0x80000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((uint)0x00000001, IncrementOperatorsHelper.op_Increment((uint)0x00000000)); + Assert.Equal((uint)0x00000002, IncrementOperatorsHelper.op_Increment((uint)0x00000001)); + Assert.Equal((uint)0x80000000, IncrementOperatorsHelper.op_Increment((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000001, IncrementOperatorsHelper.op_Increment((uint)0x80000000)); + Assert.Equal((uint)0x00000000, IncrementOperatorsHelper.op_Increment((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((uint)0x00000000, ModulusOperatorsHelper.op_Modulus((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0x00000001, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x00000000, ModulusOperatorsHelper.op_Modulus((uint)0x80000000, 2)); + Assert.Equal((uint)0x00000001, ModulusOperatorsHelper.op_Modulus((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((uint)0x00000000, MultiplyOperatorsHelper.op_Multiply((uint)0x00000000, 2)); + Assert.Equal((uint)0x00000002, MultiplyOperatorsHelper.op_Multiply((uint)0x00000001, 2)); + Assert.Equal((uint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((uint)0x7FFFFFFF, 2)); + Assert.Equal((uint)0x00000000, MultiplyOperatorsHelper.op_Multiply((uint)0x80000000, 2)); + Assert.Equal((uint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Abs((uint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Abs((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Abs((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Abs((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Abs((uint)0xFFFFFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.Clamp((uint)0x00000000, 0x0001, 0x003F)); + Assert.Equal((uint)0x00000001, NumberHelper.Clamp((uint)0x00000001, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0x7FFFFFFF, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0x80000000, 0x0001, 0x003F)); + Assert.Equal((uint)0x0000003F, NumberHelper.Clamp((uint)0xFFFFFFFF, 0x0001, 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Create(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((uint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((uint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((uint)0xFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((uint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((uint)0xFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((uint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((uint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((uint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((uint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((uint)0x00000000, (uint)0x00000000), NumberHelper.DivRem((uint)0x00000000, 2)); + Assert.Equal(((uint)0x00000000, (uint)0x00000001), NumberHelper.DivRem((uint)0x00000001, 2)); + Assert.Equal(((uint)0x3FFFFFFF, (uint)0x00000001), NumberHelper.DivRem((uint)0x7FFFFFFF, 2)); + Assert.Equal(((uint)0x40000000, (uint)0x00000000), NumberHelper.DivRem((uint)0x80000000, 2)); + Assert.Equal(((uint)0x7FFFFFFF, (uint)0x00000001), NumberHelper.DivRem((uint)0xFFFFFFFF, 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((uint)0x00000001, NumberHelper.Max((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Max((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFF, NumberHelper.Max((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x80000000, NumberHelper.Max((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFF, NumberHelper.Max((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Min((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x00000001, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0x80000000, 1)); + Assert.Equal((uint)0x00000001, NumberHelper.Min((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((uint)0x00000000, NumberHelper.Sign((uint)0x00000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x00000001)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0x80000000)); + Assert.Equal((uint)0x00000001, NumberHelper.Sign((uint)0xFFFFFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((uint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((uint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((uint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((uint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((uint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + uint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((uint)0x0000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((uint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((uint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((uint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((uint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((uint)0xFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + uint result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((uint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + uint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((uint)0x00000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((uint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((uint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((uint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((uint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((uint)0xFFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_LeftShift((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000002, ShiftOperatorsHelper.op_LeftShift((uint)0x00000001, 1)); + Assert.Equal((uint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_LeftShift((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_RightShift((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, ShiftOperatorsHelper.op_RightShift((uint)0x00000001, 1)); + Assert.Equal((uint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x40000000, ShiftOperatorsHelper.op_RightShift((uint)0x80000000, 1)); + Assert.Equal((uint)0x7FFFFFFF, ShiftOperatorsHelper.op_RightShift((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((uint)0xFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((uint)0x00000000, 1)); + Assert.Equal((uint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((uint)0x00000001, 1)); + Assert.Equal((uint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((uint)0x7FFFFFFF, 1)); + Assert.Equal((uint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction((uint)0x80000000, 1)); + Assert.Equal((uint)0xFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((uint)0xFFFFFFFF, 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((uint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x00000000)); + Assert.Equal((uint)0xFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x00000001)); + Assert.Equal((uint)0x80000001, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0x80000000)); + Assert.Equal((uint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation((uint)0xFFFFFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((uint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x00000000)); + Assert.Equal((uint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x00000001)); + Assert.Equal((uint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x7FFFFFFF)); + Assert.Equal((uint)0x80000000, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0x80000000)); + Assert.Equal((uint)0xFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((uint)0xFFFFFFFF)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Valid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, uint expected) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Invalid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(uint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, uint expected) + { + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt32Tests.Parse_Invalid_TestData), MemberType = typeof(UInt32Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + uint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(uint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(uint), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs new file mode 100644 index 00000000000..1b7ee209a9f --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt64Tests.GenericMath.cs @@ -0,0 +1,1175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UInt64Tests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((ulong)0x0000000000000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((ulong)0x0000000000000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((ulong)0x0000000000000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + Assert.Equal((ulong)0x0000000000000001, AdditionOperatorsHelper.op_Addition((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, AdditionOperatorsHelper.op_Addition((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x8000000000000000, AdditionOperatorsHelper.op_Addition((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, AdditionOperatorsHelper.op_Addition((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, AdditionOperatorsHelper.op_Addition((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.LeadingZeroCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.LeadingZeroCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.PopCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.PopCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.PopCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.PopCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.PopCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.RotateLeft((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, BinaryIntegerHelper.RotateLeft((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BinaryIntegerHelper.RotateLeft((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000001, BinaryIntegerHelper.RotateLeft((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateLeft((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x8000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xBFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateRight((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x4000000000000000, BinaryIntegerHelper.RotateRight((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BinaryIntegerHelper.RotateRight((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal((ulong)0x0000000000000040, BinaryIntegerHelper.TrailingZeroCount((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x000000000000003F, BinaryIntegerHelper.TrailingZeroCount((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryIntegerHelper.TrailingZeroCount((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void IsPow2Test() + { + Assert.False(BinaryNumberHelper.IsPow2((ulong)0x0000000000000000)); + Assert.True(BinaryNumberHelper.IsPow2((ulong)0x0000000000000001)); + Assert.False(BinaryNumberHelper.IsPow2((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((ulong)0x8000000000000000)); + Assert.False(BinaryNumberHelper.IsPow2((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal((ulong)0x0000000000000000, BinaryNumberHelper.Log2((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BinaryNumberHelper.Log2((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x000000000000003E, BinaryNumberHelper.Log2((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x000000000000003F, BinaryNumberHelper.Log2((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x000000000000003F, BinaryNumberHelper.Log2((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseAnd((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal((ulong)0x0000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000001, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x0000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000000, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, BitwiseOperatorsHelper.op_OnesComplement((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((ulong)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0x8000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x0000000000000001, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0x8000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x0000000000000000, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0x8000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x0000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x0000000000000001, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0x8000000000000000, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000000, DecrementOperatorsHelper.op_Decrement((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, DecrementOperatorsHelper.op_Decrement((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, DecrementOperatorsHelper.op_Decrement((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal((ulong)0x0000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0x3FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x4000000000000000, DivisionOperatorsHelper.op_Division((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, DivisionOperatorsHelper.op_Division((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x0000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality((ulong)0x0000000000000001, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0x8000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x0000000000000000, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((ulong)0x0000000000000001, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0x8000000000000000, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal((ulong)0x0000000000000001, IncrementOperatorsHelper.op_Increment((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000002, IncrementOperatorsHelper.op_Increment((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000000, IncrementOperatorsHelper.op_Increment((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000001, IncrementOperatorsHelper.op_Increment((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000000, IncrementOperatorsHelper.op_Increment((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal((ulong)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x0000000000000000, ModulusOperatorsHelper.op_Modulus((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000001, ModulusOperatorsHelper.op_Modulus((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal((ulong)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((ulong)0x0000000000000000, 2)); + Assert.Equal((ulong)0x0000000000000002, MultiplyOperatorsHelper.op_Multiply((ulong)0x0000000000000001, 2)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal((ulong)0x0000000000000000, MultiplyOperatorsHelper.op_Multiply((ulong)0x8000000000000000, 2)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Abs((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Abs((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Abs((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Abs((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Abs((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void ClampTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Clamp((ulong)0x0000000000000000, 0x0001, 0x003F)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Clamp((ulong)0x0000000000000001, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0x7FFFFFFFFFFFFFFF, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0x8000000000000000, 0x0001, 0x003F)); + Assert.Equal((ulong)0x000000000000003F, NumberHelper.Clamp((ulong)0xFFFFFFFFFFFFFFFF, 0x0001, 0x003F)); + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.Create(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.Create(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.Create(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Create(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ulong)0x0000000000000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((ulong)0x00000000000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ulong)0xFFFFFFFFFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ulong)0xFFFFFFFF80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((ulong)0xFFFFFFFF80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((ulong)0x000000000000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((ulong)0x0000000000007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((ulong)0x0000000000008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((ulong)0x000000000000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((ulong)0x000000007FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((ulong)0x0000000080000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((ulong)0x00000000FFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + Assert.Equal(((ulong)0x0000000000000000, (ulong)0x0000000000000000), NumberHelper.DivRem((ulong)0x0000000000000000, 2)); + Assert.Equal(((ulong)0x0000000000000000, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0x0000000000000001, 2)); + Assert.Equal(((ulong)0x3FFFFFFFFFFFFFFF, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0x7FFFFFFFFFFFFFFF, 2)); + Assert.Equal(((ulong)0x4000000000000000, (ulong)0x0000000000000000), NumberHelper.DivRem((ulong)0x8000000000000000, 2)); + Assert.Equal(((ulong)0x7FFFFFFFFFFFFFFF, (ulong)0x0000000000000001), NumberHelper.DivRem((ulong)0xFFFFFFFFFFFFFFFF, 2)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Max((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Max((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, NumberHelper.Max((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x8000000000000000, NumberHelper.Max((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, NumberHelper.Max((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Min((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Min((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal((ulong)0x0000000000000000, NumberHelper.Sign((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000001, NumberHelper.Sign((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void TryCreateFromByteTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ulong)0x000000000000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((ulong)0x0000000000000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((ulong)0x00000000000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((ulong)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((ulong)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + ulong result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((ulong)0x000000000000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((ulong)0x0000000000007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((ulong)0x0000000000008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((ulong)0x000000000000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((ulong)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((ulong)0x00000000FFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + ulong result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((ulong)0x8000000000000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + ulong result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal((ulong)0x8000000000000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((ulong)0x0000000000000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((ulong)0x0000000000000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((ulong)0x000000007FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((ulong)0x0000000080000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((ulong)0x00000000FFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000002, ShiftOperatorsHelper.op_LeftShift((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_LeftShift((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x3FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x4000000000000000, ShiftOperatorsHelper.op_RightShift((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, ShiftOperatorsHelper.op_RightShift((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((ulong)0x0000000000000000, 1)); + Assert.Equal((ulong)0x0000000000000000, SubtractionOperatorsHelper.op_Subtraction((ulong)0x0000000000000001, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((ulong)0x7FFFFFFFFFFFFFFF, 1)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((ulong)0x8000000000000000, 1)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((ulong)0xFFFFFFFFFFFFFFFF, 1)); + } + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal((ulong)0x0000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x0000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x8000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0x8000000000000000)); + Assert.Equal((ulong)0x0000000000000001, UnaryNegationOperatorsHelper.op_UnaryNegation((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal((ulong)0x0000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x0000000000000000)); + Assert.Equal((ulong)0x0000000000000001, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x0000000000000001)); + Assert.Equal((ulong)0x7FFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x7FFFFFFFFFFFFFFF)); + Assert.Equal((ulong)0x8000000000000000, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0x8000000000000000)); + Assert.Equal((ulong)0xFFFFFFFFFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((ulong)0xFFFFFFFFFFFFFFFF)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Valid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, ulong expected) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Invalid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(ulong), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, ulong expected) + { + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UInt64Tests.Parse_Invalid_TestData), MemberType = typeof(UInt64Tests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + ulong result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(ulong), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(ulong), result); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs new file mode 100644 index 00000000000..8e0c92c2a82 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UIntPtrTests.GenericMath.cs @@ -0,0 +1,1695 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.Versioning; +using Xunit; + +namespace System.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/54910", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [RequiresPreviewFeaturesAttribute] + public class UIntPtrTests_GenericMath + { + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal((nuint)0x00000000, AdditiveIdentityHelper.AdditiveIdentity); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal((nuint)0x00000000, MinMaxValueHelper.MinValue); + } + + [Fact] + public static void MaxValueTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), MinMaxValueHelper.MaxValue); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, MinMaxValueHelper.MaxValue); + } + } + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal((nuint)0x00000001, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + [Fact] + public static void OneTest() + { + Assert.Equal((nuint)0x00000001, NumberHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Zero); + } + + [Fact] + public static void op_AdditionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), AdditionOperatorsHelper.op_Addition(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, AdditionOperatorsHelper.op_Addition((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000002, AdditionOperatorsHelper.op_Addition((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x80000000, AdditionOperatorsHelper.op_Addition((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, AdditionOperatorsHelper.op_Addition((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, AdditionOperatorsHelper.op_Addition((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void LeadingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.LeadingZeroCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x0000000000000020, BinaryIntegerHelper.LeadingZeroCount((nuint)0x00000000)); + Assert.Equal((nuint)0x000000000000001F, BinaryIntegerHelper.LeadingZeroCount((nuint)0x00000001)); + Assert.Equal((nuint)0x0000000000000001, BinaryIntegerHelper.LeadingZeroCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((nuint)0x80000000)); + Assert.Equal((nuint)0x0000000000000000, BinaryIntegerHelper.LeadingZeroCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void PopCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.PopCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.PopCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.PopCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.PopCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.PopCount((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.PopCount((nuint)0x00000001)); + Assert.Equal((nuint)0x0000001F, BinaryIntegerHelper.PopCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.PopCount((nuint)0x80000000)); + Assert.Equal((nuint)0x00000020, BinaryIntegerHelper.PopCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void RotateLeftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateLeft(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.RotateLeft((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000002, BinaryIntegerHelper.RotateLeft((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xFFFFFFFE, BinaryIntegerHelper.RotateLeft((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x00000001, BinaryIntegerHelper.RotateLeft((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFF, BinaryIntegerHelper.RotateLeft((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void RotateRightTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xBFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x4000000000000000), BinaryIntegerHelper.RotateRight(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BinaryIntegerHelper.RotateRight(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.RotateRight((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x80000000, BinaryIntegerHelper.RotateRight((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xBFFFFFFF, BinaryIntegerHelper.RotateRight((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x40000000, BinaryIntegerHelper.RotateRight((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFF, BinaryIntegerHelper.RotateRight((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void TrailingZeroCountTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000040), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryIntegerHelper.TrailingZeroCount(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000020, BinaryIntegerHelper.TrailingZeroCount((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0x00000001)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000001F, BinaryIntegerHelper.TrailingZeroCount((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, BinaryIntegerHelper.TrailingZeroCount((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void IsPow2Test() + { + if (Environment.Is64BitProcess) + { + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0x0000000000000000))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nuint)0x0000000000000001))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.True(BinaryNumberHelper.IsPow2(unchecked((nuint)0x8000000000000000))); + Assert.False(BinaryNumberHelper.IsPow2(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.False(BinaryNumberHelper.IsPow2((nuint)0x00000000)); + Assert.True(BinaryNumberHelper.IsPow2((nuint)0x00000001)); + Assert.False(BinaryNumberHelper.IsPow2((nuint)0x7FFFFFFF)); + Assert.True(BinaryNumberHelper.IsPow2((nuint)0x80000000)); + Assert.False(BinaryNumberHelper.IsPow2((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void Log2Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BinaryNumberHelper.Log2(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x000000000000003E), BinaryNumberHelper.Log2(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryNumberHelper.Log2(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x000000000000003F), BinaryNumberHelper.Log2(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, BinaryNumberHelper.Log2((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, BinaryNumberHelper.Log2((nuint)0x00000001)); + Assert.Equal((nuint)0x0000001E, BinaryNumberHelper.Log2((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x0000001F, BinaryNumberHelper.Log2((nuint)0x80000000)); + Assert.Equal((nuint)0x0000001F, BinaryNumberHelper.Log2((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_BitwiseAndTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseAnd(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseAnd((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_BitwiseOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_BitwiseOr(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFF, BitwiseOperatorsHelper.op_BitwiseOr((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_ExclusiveOrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000001), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_ExclusiveOr(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000001, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFE, BitwiseOperatorsHelper.op_ExclusiveOr((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_OnesComplementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), BitwiseOperatorsHelper.op_OnesComplement(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x00000000)); + Assert.Equal((nuint)0xFFFFFFFE, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x00000001)); + Assert.Equal((nuint)0x80000000, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x7FFFFFFF, BitwiseOperatorsHelper.op_OnesComplement((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, BitwiseOperatorsHelper.op_OnesComplement((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_LessThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThan((nuint)0x00000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x00000001, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0x80000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x00000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x00000001, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0x80000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_GreaterThanTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x00000000, (nuint)1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x00000001, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0x80000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x00000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x00000001, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0x80000000, (nuint)1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_DecrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), DecrementOperatorsHelper.op_Decrement(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, DecrementOperatorsHelper.op_Decrement((nuint)0x00000000)); + Assert.Equal((nuint)0x00000000, DecrementOperatorsHelper.op_Decrement((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFE, DecrementOperatorsHelper.op_Decrement((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x7FFFFFFF, DecrementOperatorsHelper.op_Decrement((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFE, DecrementOperatorsHelper.op_Decrement((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_DivisionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0x3FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x4000000000000000), DivisionOperatorsHelper.op_Division(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), DivisionOperatorsHelper.op_Division(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, DivisionOperatorsHelper.op_Division((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000000, DivisionOperatorsHelper.op_Division((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0x3FFFFFFF, DivisionOperatorsHelper.op_Division((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x40000000, DivisionOperatorsHelper.op_Division((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0x7FFFFFFF, DivisionOperatorsHelper.op_Division((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void op_EqualityTest() + { + if (Environment.Is64BitProcess) + { + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x00000000, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Equality((nuint)0x00000001, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x7FFFFFFF, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0x80000000, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Equality((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_InequalityTest() + { + if (Environment.Is64BitProcess) + { + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x00000000, (nuint)1)); + Assert.False(EqualityOperatorsHelper.op_Inequality((nuint)0x00000001, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x7FFFFFFF, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0x80000000, (nuint)1)); + Assert.True(EqualityOperatorsHelper.op_Inequality((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_IncrementTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000002), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000001), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), IncrementOperatorsHelper.op_Increment(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000001, IncrementOperatorsHelper.op_Increment((nuint)0x00000000)); + Assert.Equal((nuint)0x00000002, IncrementOperatorsHelper.op_Increment((nuint)0x00000001)); + Assert.Equal((nuint)0x80000000, IncrementOperatorsHelper.op_Increment((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000001, IncrementOperatorsHelper.op_Increment((nuint)0x80000000)); + Assert.Equal((nuint)0x00000000, IncrementOperatorsHelper.op_Increment((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_ModulusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000001), ModulusOperatorsHelper.op_Modulus(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, ModulusOperatorsHelper.op_Modulus((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x00000000, ModulusOperatorsHelper.op_Modulus((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0x00000001, ModulusOperatorsHelper.op_Modulus((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void op_MultiplyTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000002), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal(unchecked((nuint)0x0000000000000000), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), MultiplyOperatorsHelper.op_Multiply(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal((nuint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nuint)0x00000000, (nuint)2)); + Assert.Equal((nuint)0x00000002, MultiplyOperatorsHelper.op_Multiply((nuint)0x00000001, (nuint)2)); + Assert.Equal((nuint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal((nuint)0x00000000, MultiplyOperatorsHelper.op_Multiply((nuint)0x80000000, (nuint)2)); + Assert.Equal((nuint)0xFFFFFFFE, MultiplyOperatorsHelper.op_Multiply((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void AbsTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Abs(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Abs(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Abs(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Abs(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Abs((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Abs((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Abs((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Abs((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Abs((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void ClampTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Clamp(unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Clamp(unchecked((nuint)0x0000000000000001), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0x7FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0x8000000000000000), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + Assert.Equal(unchecked((nuint)0x000000000000003F), NumberHelper.Clamp(unchecked((nuint)0xFFFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001), unchecked((nuint)0x000000000000003F))); + } + else + { + Assert.Equal((nuint)0x00000001, NumberHelper.Clamp((nuint)0x00000000, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x00000001, NumberHelper.Clamp((nuint)0x00000001, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0x7FFFFFFF, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0x80000000, (nuint)0x00000001, (nuint)0x0000003F)); + Assert.Equal((nuint)0x0000003F, NumberHelper.Clamp((nuint)0xFFFFFFFF, (nuint)0x00000001, (nuint)0x0000003F)); + } + } + + [Fact] + public static void CreateFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.Create(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.Create(0xFF)); + } + + [Fact] + public static void CreateFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.Create((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.Create((char)0xFFFF)); + } + + [Fact] + public static void CreateFromInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.Create(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateFromInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.Create(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateFromSByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.Create(0x7F)); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.Create(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.Create(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.Create(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.Create(0xFFFF)); + } + + [Fact] + public static void CreateFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Create(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Create(0xFFFFFFFF)); + } + + [Fact] + public static void CreateFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Create(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create(0x0000000000000001)); + Assert.Throws(() => NumberHelper.Create(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.Create(0x8000000000000000)); + Assert.Throws(() => NumberHelper.Create(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Create(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Create(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Create(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Create(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Create((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Create((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Create((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.Create((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Create((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.CreateSaturating(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nuint)0x00000080, NumberHelper.CreateTruncating(0x80)); + Assert.Equal((nuint)0x000000FF, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(unchecked((nuint)0x0000000000007FFF), NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFF8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nuint)0xFFFF8000, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(unchecked((nuint)0x000000007FFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(unchecked((nuint)0xFFFFFFFF80000000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x00)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x01)); + Assert.Equal(unchecked((nuint)0x000000000000007F), NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x01)); + Assert.Equal((nuint)0x0000007F, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal((nuint)0xFFFFFF80, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal((nuint)0x00007FFF, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal((nuint)0x00008000, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal((nuint)0x0000FFFF, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void DivRemTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal((unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nuint)0x0000000000000000), (nuint)2)); + Assert.Equal((unchecked((nuint)0x0000000000000000), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0x0000000000000001), (nuint)2)); + Assert.Equal((unchecked((nuint)0x3FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)2)); + Assert.Equal((unchecked((nuint)0x4000000000000000), unchecked((nuint)0x0000000000000000)), NumberHelper.DivRem(unchecked((nuint)0x8000000000000000), (nuint)2)); + Assert.Equal((unchecked((nuint)0x7FFFFFFFFFFFFFFF), unchecked((nuint)0x0000000000000001)), NumberHelper.DivRem(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)2)); + } + else + { + Assert.Equal(((nuint)0x00000000, (nuint)0x00000000), NumberHelper.DivRem((nuint)0x00000000, (nuint)2)); + Assert.Equal(((nuint)0x00000000, (nuint)0x00000001), NumberHelper.DivRem((nuint)0x00000001, (nuint)2)); + Assert.Equal(((nuint)0x3FFFFFFF, (nuint)0x00000001), NumberHelper.DivRem((nuint)0x7FFFFFFF, (nuint)2)); + Assert.Equal(((nuint)0x40000000, (nuint)0x00000000), NumberHelper.DivRem((nuint)0x80000000, (nuint)2)); + Assert.Equal(((nuint)0x7FFFFFFF, (nuint)0x00000001), NumberHelper.DivRem((nuint)0xFFFFFFFF, (nuint)2)); + } + } + + [Fact] + public static void MaxTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Max(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Max(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x8000000000000000), NumberHelper.Max(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), NumberHelper.Max(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000001, NumberHelper.Max((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Max((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, NumberHelper.Max((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x80000000, NumberHelper.Max((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFF, NumberHelper.Max((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void MinTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Min(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Min(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Min((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0x00000001, NumberHelper.Min((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void SignTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), NumberHelper.Sign(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), NumberHelper.Sign(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, NumberHelper.Sign((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x00000001)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0x80000000)); + Assert.Equal((nuint)0x00000001, NumberHelper.Sign((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nuint)0x0000007F, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal((nuint)0x00000080, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal((nuint)0x000000FF, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal((nuint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal((nuint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal((nuint)0x0000007F, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal((nuint)0x00000000, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal((nuint)0x00007FFF, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal((nuint)0x00008000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal((nuint)0x0000FFFF, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + nuint result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal((nuint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal((nuint)0xFFFFFFFF, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(unchecked((nuint)0x00000000000000001), result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal(unchecked((nuint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.False(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.False(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal((nuint)0x00000000, result); + } + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + nuint result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(unchecked((nuint)0x0000000000000001), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal(unchecked((nuint)0x8000000000000000), result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal((nuint)0x00000000, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal((nuint)0x00000001, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal((nuint)0x7FFFFFFF, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal((nuint)0x80000000, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal((nuint)0xFFFFFFFF, result); + } + } + + [Fact] + + public static void op_LeftShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000002), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), ShiftOperatorsHelper.op_LeftShift(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000002, ShiftOperatorsHelper.op_LeftShift((nuint)0x00000001, 1)); + Assert.Equal((nuint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_LeftShift((nuint)0x80000000, 1)); + Assert.Equal((nuint)0xFFFFFFFE, ShiftOperatorsHelper.op_LeftShift((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void op_RightShiftTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x0000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x0000000000000001), 1)); + Assert.Equal(unchecked((nuint)0x3FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x7FFFFFFFFFFFFFFF), 1)); + Assert.Equal(unchecked((nuint)0x4000000000000000), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0x8000000000000000), 1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), ShiftOperatorsHelper.op_RightShift(unchecked((nuint)0xFFFFFFFFFFFFFFFF), 1)); + } + else + { + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_RightShift((nuint)0x00000000, 1)); + Assert.Equal((nuint)0x00000000, ShiftOperatorsHelper.op_RightShift((nuint)0x00000001, 1)); + Assert.Equal((nuint)0x3FFFFFFF, ShiftOperatorsHelper.op_RightShift((nuint)0x7FFFFFFF, 1)); + Assert.Equal((nuint)0x40000000, ShiftOperatorsHelper.op_RightShift((nuint)0x80000000, 1)); + Assert.Equal((nuint)0x7FFFFFFF, ShiftOperatorsHelper.op_RightShift((nuint)0xFFFFFFFF, 1)); + } + } + + [Fact] + public static void op_SubtractionTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x0000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0x0000000000000000), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x0000000000000001), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x7FFFFFFFFFFFFFFF), (nuint)1)); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0x8000000000000000), (nuint)1)); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFE), SubtractionOperatorsHelper.op_Subtraction(unchecked((nuint)0xFFFFFFFFFFFFFFFF), (nuint)1)); + } + else + { + Assert.Equal((nuint)0xFFFFFFFF, SubtractionOperatorsHelper.op_Subtraction((nuint)0x00000000, (nuint)1)); + Assert.Equal((nuint)0x00000000, SubtractionOperatorsHelper.op_Subtraction((nuint)0x00000001, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nuint)0x7FFFFFFF, (nuint)1)); + Assert.Equal((nuint)0x7FFFFFFF, SubtractionOperatorsHelper.op_Subtraction((nuint)0x80000000, (nuint)1)); + Assert.Equal((nuint)0xFFFFFFFE, SubtractionOperatorsHelper.op_Subtraction((nuint)0xFFFFFFFF, (nuint)1)); + } + } + + [Fact] + public static void op_UnaryNegationTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x8000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), UnaryNegationOperatorsHelper.op_UnaryNegation(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x00000000)); + Assert.Equal((nuint)0xFFFFFFFF, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x00000001)); + Assert.Equal((nuint)0x80000001, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0x80000000)); + Assert.Equal((nuint)0x00000001, UnaryNegationOperatorsHelper.op_UnaryNegation((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void op_UnaryPlusTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(unchecked((nuint)0x0000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x0000000000000000))); + Assert.Equal(unchecked((nuint)0x0000000000000001), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x0000000000000001))); + Assert.Equal(unchecked((nuint)0x7FFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(unchecked((nuint)0x8000000000000000), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0x8000000000000000))); + Assert.Equal(unchecked((nuint)0xFFFFFFFFFFFFFFFF), UnaryPlusOperatorsHelper.op_UnaryPlus(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal((nuint)0x00000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x00000000)); + Assert.Equal((nuint)0x00000001, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x00000001)); + Assert.Equal((nuint)0x7FFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x7FFFFFFF)); + Assert.Equal((nuint)0x80000000, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0x80000000)); + Assert.Equal((nuint)0xFFFFFFFF, UnaryPlusOperatorsHelper.op_UnaryPlus((nuint)0xFFFFFFFF)); + } + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Valid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseValidStringTest(string value, NumberStyles style, IFormatProvider provider, nuint expected) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.True(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.True(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Invalid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseInvalidStringTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(ParseableHelper.TryParse(value, provider, out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Default provider + if (provider is null) + { + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + + // Substitute default NumberFormatInfo + Assert.False(NumberHelper.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => ParseableHelper.Parse(value, provider)); + } + + // Full overloads + Assert.False(NumberHelper.TryParse(value, style, provider, out result)); + Assert.Equal(default(nuint), result); + Assert.Throws(exceptionType, () => NumberHelper.Parse(value, style, provider)); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_ValidWithOffsetCount_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseValidSpanTest(string value, int offset, int count, NumberStyles style, IFormatProvider provider, nuint expected) + { + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.True(SpanParseableHelper.TryParse(value.AsSpan(offset, count), provider, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, NumberHelper.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(NumberHelper.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(UIntPtrTests.Parse_Invalid_TestData), MemberType = typeof(UIntPtrTests))] + public static void ParseInvalidSpanTest(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value is null) + { + return; + } + + nuint result; + + // Default style and provider + if ((style == NumberStyles.Integer) && (provider is null)) + { + Assert.False(SpanParseableHelper.TryParse(value.AsSpan(), provider, out result)); + Assert.Equal(default(nuint), result); + } + + Assert.Throws(exceptionType, () => NumberHelper.Parse(value.AsSpan(), style, provider)); + + Assert.False(NumberHelper.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default(nuint), result); + } + } +} From a31d0d57eff985a68828c37775d103b91032f786 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 11 Jul 2021 14:13:35 -0400 Subject: [PATCH 425/926] Move PosixSignalRegistration to corelib (#55433) * Move PosixSignalRegistration to corelib * Fix trimming issue on mobile targets --- .../TestUtilities/System/PlatformDetection.cs | 3 +- .../System.Private.CoreLib.Shared.projitems | 15 +++++++++ .../Runtime/InteropServices/PosixSignal.cs | 0 .../InteropServices/PosixSignalContext.cs | 0 ...ignalRegistration.PlatformNotSupported.cs} | 3 ++ .../PosixSignalRegistration.Unix.cs | 0 .../PosixSignalRegistration.Windows.cs | 0 .../PosixSignalRegistration.cs | 0 .../src/Resources/Strings.resx | 3 -- .../src/System.Runtime.InteropServices.csproj | 27 ++-------------- ...ystem.Runtime.InteropServices.Tests.csproj | 5 +-- .../PosixSignalRegistrationTests.Browser.cs | 29 ----------------- .../PosixSignalRegistrationTests.Unix.cs | 32 +++++++++++++------ 13 files changed, 46 insertions(+), 71 deletions(-) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignal.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalContext.cs (100%) rename src/libraries/{System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs => System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs} (80%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs (100%) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Runtime/InteropServices/PosixSignalRegistration.cs (100%) delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index e11da7e8d4d..90f21d02f5c 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -37,7 +37,8 @@ namespace System public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS")); public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); public static bool IsNotBrowser => !IsBrowser; - public static bool IsNotMobile => IsNotBrowser && !IsMacCatalyst && !IsiOS && !IstvOS && !IsAndroid; + public static bool IsMobile => IsBrowser || IsMacCatalyst || IsiOS || IstvOS || IsAndroid; + public static bool IsNotMobile => !IsMobile; public static bool IsNotNetFramework => !IsNetFramework; public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a1e270dbde9..3dcfc2c7313 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -837,6 +837,9 @@ + + + @@ -1235,6 +1238,7 @@ + @@ -1651,6 +1655,9 @@ Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs + + Common\Interop\Windows\Interop.SetConsoleCtrlHandler.cs + Common\Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs @@ -1833,6 +1840,7 @@ + @@ -2129,6 +2137,10 @@ Common\Interop\Unix\System.Native\Interop.GetPid.cs + + + + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignal.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignal.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignal.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalContext.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalContext.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalContext.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs similarity index 80% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs index 4bfd2eae0be..3ccc169b1a8 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unsupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs @@ -1,12 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.Runtime.InteropServices { public sealed partial class PosixSignalRegistration { private PosixSignalRegistration() { } + [DynamicDependency("#ctor")] // Prevent the private ctor and the IDisposable implementation from getting linked away public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) { if (handler is null) diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/PosixSignalRegistration.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index d7644789f28..08d7e045a58 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -111,7 +111,4 @@ Specified file length was too large for the file system. - - Cannot create '{0}' because a file or directory with the same name already exists. - diff --git a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index 7137155589b..3bc59997f7f 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/src/libraries/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -2,7 +2,7 @@ true enable - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent) @@ -33,9 +33,6 @@ - - - @@ -51,27 +48,7 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 33204d0ee7c..f0ffe70d47c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -1,7 +1,7 @@ true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix true true @@ -192,7 +192,4 @@ - - - diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs deleted file mode 100644 index 2202ed25744..00000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Browser.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Linq; -using System.Runtime.InteropServices; -using System.Collections.Generic; - -namespace System.Tests -{ - public partial class PosixSignalRegistrationTests - { - public static IEnumerable UninstallableSignals() => Enumerable.Empty(); - - public static IEnumerable SupportedSignals() => Enumerable.Empty(); - - public static IEnumerable UnsupportedSignals() - { - foreach (PosixSignal signal in Enum.GetValues()) - { - yield return new object[] { signal }; - } - - yield return new object[] { 0 }; - yield return new object[] { 3 }; - yield return new object[] { -1000 }; - yield return new object[] { 1000 }; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs index 1716a3ba24f..e08d7756551 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -14,23 +14,37 @@ namespace System.Tests { public static IEnumerable UninstallableSignals() { - yield return new object[] { (PosixSignal)9 }; + if (PlatformDetection.IsNotMobile) + { + yield return new object[] { (PosixSignal)9 }; + } } public static IEnumerable SupportedSignals() { - foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) - yield return new object[] { value }; + if (PlatformDetection.IsNotMobile) + { + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; + } } public static IEnumerable UnsupportedSignals() { + if (PlatformDetection.IsMobile) + { + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; + } + yield return new object[] { 0 }; yield return new object[] { -1000 }; yield return new object[] { 1000 }; } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static bool NotMobileAndRemoteExecutable => PlatformDetection.IsNotMobile && RemoteExecutor.IsSupported; + + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [MemberData(nameof(SupportedSignals))] public void SignalHandlerCalledForKnownSignals(PosixSignal s) { @@ -55,7 +69,7 @@ namespace System.Tests }, s.ToString()).Dispose(); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [MemberData(nameof(PosixSignalAsRawValues))] public void SignalHandlerCalledForRawSignals(PosixSignal s) { @@ -80,7 +94,7 @@ namespace System.Tests }, s.ToString()).Dispose(); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerWorksForSecondRegistration() { PosixSignal signal = PosixSignal.SIGCONT; @@ -104,7 +118,7 @@ namespace System.Tests } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerNotCalledWhenDisposed() { PosixSignal signal = PosixSignal.SIGCONT; @@ -118,7 +132,7 @@ namespace System.Tests Thread.Sleep(100); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void SignalHandlerNotCalledWhenFinalized() { PosixSignal signal = PosixSignal.SIGCONT; @@ -140,7 +154,7 @@ namespace System.Tests } } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))] [InlineData(PosixSignal.SIGINT, true, 0)] [InlineData(PosixSignal.SIGINT, false, 130)] [InlineData(PosixSignal.SIGTERM, true, 0)] From 7d518fe3d0963f9f2cbe7aa971df200d2f8e67c7 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sun, 11 Jul 2021 15:59:37 -0400 Subject: [PATCH 426/926] [mono] Improve aot code generation for isinst for sealed classes. (#55450) Generate the same code as in the non-aot case, i.e. compare the vtable with a constant. --- src/mono/mono/mini/type-checking.c | 46 ++++++++++++------------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/mono/mono/mini/type-checking.c b/src/mono/mono/mini/type-checking.c index 8f6f93e1f35..b623c603741 100644 --- a/src/mono/mono/mini/type-checking.c +++ b/src/mono/mono/mini/type-checking.c @@ -429,15 +429,11 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - if (!m_class_get_rank (klass) && !cfg->compile_aot && mono_class_is_sealed (klass)) { - /* the remoting code is broken, access the class for now */ - if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/ - MonoVTable *vt = mono_class_vtable_checked (klass, cfg->error); - if (!is_ok (cfg->error)) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt); + if (!m_class_get_rank (klass) && mono_class_is_sealed (klass)) { + if (cfg->compile_aot) { + MonoInst *vtable_ins; + EMIT_NEW_AOTCONST (cfg, vtable_ins, MONO_PATCH_INFO_VTABLE, klass); + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg); } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); @@ -584,28 +580,22 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_us MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); /* the is_null_bb target simply copies the input register to the output */ mini_emit_isninst_cast (cfg, klass_reg, m_class_get_cast_class (klass), false_bb, is_null_bb); - } else { - if (!cfg->compile_aot && mono_class_is_sealed (klass)) { - g_assert (!context_used); - /* the remoting code is broken, access the class for now */ - if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/ - MonoVTable *vt = mono_class_vtable_checked (klass, cfg->error); - if (!is_ok (cfg->error)) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); - } - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb); + } else if (mono_class_is_sealed (klass)) { + g_assert (!context_used); + if (cfg->compile_aot) { + MonoInst *vtable_ins; + EMIT_NEW_AOTCONST (cfg, vtable_ins, MONO_PATCH_INFO_VTABLE, klass); + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg); } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - /* the is_null_bb target simply copies the input register to the output */ - mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass); } + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb); + } else { + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); + /* the is_null_bb target simply copies the input register to the output */ + mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); } } From c721c87704434363ee2310ab04b97407c0a12eac Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Sun, 11 Jul 2021 17:30:04 -0700 Subject: [PATCH 427/926] Improve MetricsEventSource error handling (#55466) Previously exceptions that escaped user provided callbacks for observable instruments would have terminated all further metric collection. Now those exceptions are caught and reported, only potentially interfering with other observable instruments in the same collection interval. --- .../Diagnostics/Metrics/AggregationManager.cs | 14 ++++++- .../Diagnostics/Metrics/MetricsEventSource.cs | 9 ++++- .../tests/MetricEventSourceTests.cs | 38 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs index 41bb991cda7..d181fbdf9a6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs @@ -41,6 +41,7 @@ namespace System.Diagnostics.Metrics private readonly Action _collectionError; private readonly Action _timeSeriesLimitReached; private readonly Action _histogramLimitReached; + private readonly Action _observableInstrumentCallbackError; public AggregationManager( int maxTimeSeries, @@ -54,7 +55,8 @@ namespace System.Diagnostics.Metrics Action initialInstrumentEnumerationComplete, Action collectionError, Action timeSeriesLimitReached, - Action histogramLimitReached) + Action histogramLimitReached, + Action observableInstrumentCallbackError) { _maxTimeSeries = maxTimeSeries; _maxHistograms = maxHistograms; @@ -68,6 +70,7 @@ namespace System.Diagnostics.Metrics _collectionError = collectionError; _timeSeriesLimitReached = timeSeriesLimitReached; _histogramLimitReached = histogramLimitReached; + _observableInstrumentCallbackError = observableInstrumentCallbackError; _listener = new MeterListener() { @@ -351,7 +354,14 @@ namespace System.Diagnostics.Metrics internal void Collect() { - _listener.RecordObservableInstruments(); + try + { + _listener.RecordObservableInstruments(); + } + catch (Exception e) + { + _observableInstrumentCallbackError(e); + } foreach (KeyValuePair kv in _instrumentStates) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs index 80baa185a09..38c4216bdcd 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -169,6 +169,12 @@ namespace System.Diagnostics.Metrics WriteEvent(13, sessionId); } + [Event(14, Keywords = Keywords.TimeSeriesValues)] + public void ObservableInstrumentCallbackError(string sessionId, string errorMessage) + { + WriteEvent(14, sessionId, errorMessage); + } + /// /// Called when the EventSource gets a command from a EventListener or ETW. /// @@ -303,7 +309,8 @@ namespace System.Diagnostics.Metrics () => Log.InitialInstrumentEnumerationComplete(sessionId), e => Log.Error(sessionId, e.ToString()), () => Log.TimeSeriesLimitReached(sessionId), - () => Log.HistogramLimitReached(sessionId)); + () => Log.HistogramLimitReached(sessionId), + e => Log.ObservableInstrumentCallbackError(sessionId, e.ToString())); _aggregationManager.SetCollectionPeriod(TimeSpan.FromSeconds(refreshIntervalSecs)); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs index 9cd9501d8c5..ce68dcf98f8 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -619,6 +619,33 @@ namespace System.Diagnostics.Metrics.Tests AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] + public void EventSourceHandlesObservableCallbackException() + { + using Meter meter = new Meter("TestMeter15"); + Counter c = meter.CreateCounter("counter1"); + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", + (Func)(() => { throw new Exception("Example user exception"); })); + + EventWrittenEventArgs[] events; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter15")) + { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + c.Add(12); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertObservableCallbackErrorPresent(events); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); + } + private void AssertBeginInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) { var beginReportEvents = events.Where(e => e.EventName == "BeginInstrumentReporting").Select(e => @@ -833,6 +860,17 @@ namespace System.Diagnostics.Metrics.Tests Assert.Equal(expectedPairs, startEventsSeen); Assert.Equal(expectedPairs, stopEventsSeen); } + + private void AssertObservableCallbackErrorPresent(EventWrittenEventArgs[] events) + { + var errorEvents = events.Where(e => e.EventName == "ObservableInstrumentCallbackError").Select(e => + new + { + ErrorText = e.Payload[1].ToString(), + }).ToArray(); + Assert.NotEmpty(errorEvents); + Assert.Contains("Example user exception", errorEvents[0].ErrorText); + } } class MetricsEventListener : EventListener From a4590197c1011384ba8a8469926ade0e72fa9e97 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 12 Jul 2021 04:07:13 +0300 Subject: [PATCH 428/926] Use init rather than ms extension syntax (#55475) --- src/coreclr/jit/emit.cpp | 4 ++-- src/coreclr/jit/jitstd/list.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index ca6cd0c4a71..21e37879eed 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -8130,8 +8130,8 @@ void emitter::emitInitIG(insGroup* ig) #ifdef DEBUG ig->lastGeneratedBlock = nullptr; - // Explicitly call the constructor, since IGs don't actually have a constructor. - ig->igBlocks.jitstd::list::list(emitComp->getAllocator(CMK_LoopOpt)); + // Explicitly call init, since IGs don't actually have a constructor. + ig->igBlocks.jitstd::list::init(emitComp->getAllocator(CMK_LoopOpt)); #endif } diff --git a/src/coreclr/jit/jitstd/list.h b/src/coreclr/jit/jitstd/list.h index 070d94361f2..b573a3952fb 100644 --- a/src/coreclr/jit/jitstd/list.h +++ b/src/coreclr/jit/jitstd/list.h @@ -160,6 +160,17 @@ public: Node* m_pNode; }; +#ifdef DEBUG + void init(const Allocator& a) + { + m_pHead = nullptr; + m_pTail = nullptr; + m_nSize = 0; + m_allocator = a; + m_nodeAllocator = a; + } +#endif + explicit list(const Allocator&); list(size_type n, const T& value, const Allocator&); From 9f98044e3e0c38ed739e90db1eaf998e35b8351e Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Sun, 11 Jul 2021 18:59:57 -0700 Subject: [PATCH 429/926] Fix mono CORDBI build (#55482) --- src/mono/dlls/mscordbi/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/dlls/mscordbi/CMakeLists.txt b/src/mono/dlls/mscordbi/CMakeLists.txt index 32e61b129f3..47924e8b9a7 100644 --- a/src/mono/dlls/mscordbi/CMakeLists.txt +++ b/src/mono/dlls/mscordbi/CMakeLists.txt @@ -123,6 +123,8 @@ add_subdirectory(${CLR_DIR}/md/compiler md/compiler) include(${CLR_DIR}/clrdefinitions.cmake) include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../) include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../inc/) +include_directories(${CLR_DIR}/minipal) + add_subdirectory(${CLR_DIR}/md/enc md/enc) add_subdirectory(${CLR_DIR}/utilcode utilcode) if (CLR_CMAKE_HOST_UNIX) From 3b724ff05ef419ef18c65b937e35b4a0bf8c3889 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 11 Jul 2021 19:27:46 -0700 Subject: [PATCH 430/926] disable Quic AcceptStream_ConnectionAborted_ByClient_Throws on Linux (#55481) --- .../System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index 8c81ce800dc..41b8d549ed7 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -36,6 +36,7 @@ namespace System.Net.Quic.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55242", TestPlatforms.Linux)] public async Task AcceptStream_ConnectionAborted_ByClient_Throws() { const int ExpectedErrorCode = 1234; From 6ece5d3c70052b01a62bd19d1bba72839cf31aed Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 11 Jul 2021 20:17:19 -0700 Subject: [PATCH 431/926] use updated fedora image with msquic (#55424) * use updated fedora image with msquic * fix ping --- eng/pipelines/libraries/helix-queues-setup.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 7d33d26edb0..75d6cbd2122 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -57,7 +57,7 @@ jobs: - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - RedHat.7.Amd64.Open - SLES.15.Amd64.Open - - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20200512010618-efb9f14 + - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408 - (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623 - (Debian.10.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006 - ${{ if or(ne(parameters.jobParameters.testScope, 'outerloop'), ne(parameters.jobParameters.runtimeFlavor, 'mono')) }}: @@ -70,7 +70,7 @@ jobs: - SLES.12.Amd64.Open - SLES.15.Amd64.Open - (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7 - - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20200512010618-efb9f14 + - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408 - (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623 - (Debian.10.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006 - ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}: @@ -80,7 +80,7 @@ jobs: - Ubuntu.1604.Amd64.Open - Ubuntu.1804.Amd64.Open - SLES.15.Amd64.Open - - (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7 + - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408 - ${{ if or(eq(parameters.jobParameters.interpreter, 'true'), eq(parameters.jobParameters.isSingleFile, true)) }}: # Limiting interp runs as we don't need as much coverage. - Debian.9.Amd64.Open From d766727db87d01c9bdf321565a99d7cfe65d5f6b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 11 Jul 2021 20:32:52 -0700 Subject: [PATCH 432/926] make quic tracing easir to correlate with MsQuic (#55483) --- .../MsQuic/MsQuicConnection.cs | 18 ++++++++++--- .../Implementations/MsQuic/MsQuicStream.cs | 27 ++++++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index dd651ad0ab5..c5df9a21b46 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -83,6 +83,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (releaseHandles) { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId()} releasing handle after last stream."); Handle?.Dispose(); } } @@ -123,8 +124,15 @@ namespace System.Net.Quic.Implementations.MsQuic _closing = true; } } + + internal string TraceId() + { + return $"[MsQuicConnection#{this.GetHashCode()}/{Handle?.DangerousGetHandle():x}]"; + } } + internal string TraceId() => _state.TraceId(); + // constructor for inbound connections public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle, bool remoteCertificateRequired = false, X509RevocationMode revocationMode = X509RevocationMode.Offline, RemoteCertificateValidationCallback? remoteCertificateValidationCallback = null) { @@ -162,7 +170,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(_state, $"[Connection#{_state.GetHashCode()}] inbound connection created"); + NetEventSource.Info(_state, $"{TraceId()} inbound connection created"); } } @@ -201,7 +209,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(_state, $"[Connection#{_state.GetHashCode()}] outbound connection created"); + NetEventSource.Info(_state, $"{TraceId()} outbound connection created"); } } @@ -621,7 +629,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(state, $"[Connection#{state.GetHashCode()}] received event {connectionEvent.Type}"); + NetEventSource.Info(state, $"{state.TraceId()} received event {connectionEvent.Type}"); } try @@ -697,6 +705,8 @@ namespace System.Net.Quic.Implementations.MsQuic return; } + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposing {disposing}"); + bool releaseHandles = false; lock (_state) { @@ -716,6 +726,8 @@ namespace System.Net.Quic.Implementations.MsQuic _configuration?.Dispose(); if (releaseHandles) { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} releasing handle"); + // We may not be fully initialized if constructor fails. _state.Handle?.Dispose(); } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index fed61e11e56..f962dbc81fd 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -69,6 +69,8 @@ namespace System.Net.Quic.Implementations.MsQuic public void Cleanup() { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId()} releasing handles."); + ShutdownState = ShutdownState.Finished; CleanupSendState(this); Handle?.Dispose(); @@ -77,8 +79,15 @@ namespace System.Net.Quic.Implementations.MsQuic if (StateGCHandle.IsAllocated) StateGCHandle.Free(); ConnectionState?.RemoveStream(null); } + + internal string TraceId() + { + return $"[MsQuicStream#{this.GetHashCode()}/{Handle?.DangerousGetHandle():x}]"; + } } + internal string TraceId() => _state.TraceId(); + // inbound. internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { @@ -117,8 +126,8 @@ namespace System.Net.Quic.Implementations.MsQuic { NetEventSource.Info( _state, - $"[Stream#{_state.GetHashCode()}] inbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + - $"in Connection#{_state.ConnectionState.GetHashCode()}."); + $"{TraceId()} inbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + + $"in {_state.ConnectionState.TraceId()}."); } } @@ -170,8 +179,8 @@ namespace System.Net.Quic.Implementations.MsQuic { NetEventSource.Info( _state, - $"[Stream#{_state.GetHashCode()}] outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + - $"in Connection#{_state.ConnectionState.GetHashCode()}."); + $"{TraceId()} outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + + $"in {_state.ConnectionState.TraceId()}."); } } @@ -647,6 +656,9 @@ namespace System.Net.Quic.Implementations.MsQuic return; } + + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposing {disposing}"); + bool callShutdown = false; bool abortRead = false; bool releaseHandles = false; @@ -698,10 +710,7 @@ namespace System.Net.Quic.Implementations.MsQuic _state.Cleanup(); } - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(_state, $"[Stream#{_state.GetHashCode()}] disposed"); - } + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposed"); } private void EnableReceive() @@ -726,7 +735,7 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(state, $"[Stream#{state.GetHashCode()}] received event {evt.Type}"); + NetEventSource.Info(state, $"{state.TraceId()} received event {evt.Type}"); } try From 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Sun, 11 Jul 2021 22:29:04 -0700 Subject: [PATCH 433/926] remove waiting on start event (#55442) Co-authored-by: Geoffrey Kizer --- .../Implementations/MsQuic/MsQuicStream.cs | 65 ++++++------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index f962dbc81fd..f66f05fdb1c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -26,9 +26,6 @@ namespace System.Net.Quic.Implementations.MsQuic // Backing for StreamId private long _streamId = -1; - // Used to check if StartAsync has been called. - private bool _started; - private int _disposed; private sealed class State @@ -53,7 +50,7 @@ namespace System.Net.Quic.Implementations.MsQuic public int SendBufferMaxCount; public int SendBufferCount; - // Resettable completions to be used for multiple calls to send, start, and shutdown. + // Resettable completions to be used for multiple calls to send. public readonly ResettableCompletionSource SendResettableCompletionSource = new ResettableCompletionSource(); public ShutdownWriteState ShutdownWriteState; @@ -94,7 +91,6 @@ namespace System.Net.Quic.Implementations.MsQuic _state.Handle = streamHandle; _canRead = true; _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); - _started = true; if (!_canWrite) { _state.SendState = SendState.Closed; @@ -217,7 +213,7 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); - using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); + using CancellationTokenRegistration registration = HandleWriteStartState(cancellationToken); await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); @@ -233,7 +229,7 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); - using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); + using CancellationTokenRegistration registration = HandleWriteStartState(cancellationToken); await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); @@ -244,14 +240,14 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); - using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); + using CancellationTokenRegistration registration = HandleWriteStartState(cancellationToken); await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } - private async ValueTask HandleWriteStartState(CancellationToken cancellationToken) + private CancellationTokenRegistration HandleWriteStartState(CancellationToken cancellationToken) { if (_state.SendState == SendState.Closed) { @@ -277,14 +273,7 @@ namespace System.Net.Quic.Implementations.MsQuic } } - throw new System.OperationCanceledException(cancellationToken); - } - - // Make sure start has completed - if (!_started) - { - await _state.SendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); - _started = true; + throw new OperationCanceledException(cancellationToken); } // if token was already cancelled, this would execute synchronously @@ -372,7 +361,7 @@ namespace System.Net.Quic.Implementations.MsQuic } } - throw new System.OperationCanceledException(cancellationToken); + throw new OperationCanceledException(cancellationToken); } if (NetEventSource.Log.IsEnabled()) @@ -740,12 +729,12 @@ namespace System.Net.Quic.Implementations.MsQuic try { - switch ((QUIC_STREAM_EVENT_TYPE)evt.Type) + switch (evt.Type) { // Stream has started. // Will only be done for outbound streams (inbound streams have already started) case QUIC_STREAM_EVENT_TYPE.START_COMPLETE: - return HandleEventStartComplete(state); + return HandleEventStartComplete(state, ref evt); // Received data on the stream case QUIC_STREAM_EVENT_TYPE.RECEIVE: return HandleEventRecv(state, ref evt); @@ -778,12 +767,10 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex}"); + NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {evt.Type} event: {ex}"); } - // This is getting hit currently - // See https://github.com/dotnet/runtime/issues/55302 - //Debug.Fail($"[Stream#{state.GetHashCode()}] Exception occurred during handling {(QUIC_STREAM_EVENT_TYPE)evt.Type} event: {ex}"); + Debug.Fail($"[Stream#{state.GetHashCode()}] Exception occurred during handling {evt.Type} event: {ex}"); return MsQuicStatusCodes.InternalError; } @@ -840,22 +827,10 @@ namespace System.Net.Quic.Implementations.MsQuic return MsQuicStatusCodes.Success; } - private static uint HandleEventStartComplete(State state) + private static uint HandleEventStartComplete(State state, ref StreamEvent evt) { - bool shouldComplete = false; - lock (state) - { - // Check send state before completing as send cancellation is shared between start and send. - if (state.SendState == SendState.None) - { - shouldComplete = true; - } - } - - if (shouldComplete) - { - state.SendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); - } + // TODO: We should probably check for a failure as indicated by the event data (or at least assert no failure if we aren't expecting it). + // However, since there is no definition for START_COMPLETE event data currently, we can't do this right now. return MsQuicStatusCodes.Success; } @@ -1336,7 +1311,7 @@ namespace System.Net.Quic.Implementations.MsQuic /// /// The stream is open, but there is no data available. /// - None, + None = 0, /// /// Data is available in . @@ -1366,7 +1341,7 @@ namespace System.Net.Quic.Implementations.MsQuic private enum ShutdownWriteState { - None, + None = 0, Canceled, Finished, ConnectionClosed @@ -1374,7 +1349,7 @@ namespace System.Net.Quic.Implementations.MsQuic private enum ShutdownState { - None, + None = 0, Canceled, Pending, Finished, @@ -1383,10 +1358,12 @@ namespace System.Net.Quic.Implementations.MsQuic private enum SendState { - None, + None = 0, Pending, - Aborted, Finished, + + // Terminal states + Aborted, ConnectionClosed, Closed } From f463d8d644f1f2f70028a45a864b93e8c04f2cf4 Mon Sep 17 00:00:00 2001 From: Matt Thalman Date: Mon, 12 Jul 2021 01:30:10 -0700 Subject: [PATCH 434/926] Update tag references to dotnet-buildtools/prereqs (#55426) Updating Docker image tag references that are obsolete and replaced by references to mcr.microsoft.com/dotnet-buildtools/prereqs. See dotnet/dotnet-docker#2848. --- .../System.Data.Odbc/src/DatabaseSetupInstructions.md | 4 ++-- src/tests/Common/scripts/arm32_ci_script.sh | 8 ++++---- src/tests/Common/scripts/run-pmi-diffs.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Data.Odbc/src/DatabaseSetupInstructions.md b/src/libraries/System.Data.Odbc/src/DatabaseSetupInstructions.md index 4c3b16f43fc..25bcc722576 100644 --- a/src/libraries/System.Data.Odbc/src/DatabaseSetupInstructions.md +++ b/src/libraries/System.Data.Odbc/src/DatabaseSetupInstructions.md @@ -17,8 +17,8 @@ - followed [dockerfile](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/blob/master/src/debian/8.2/Dockerfile) instructions for debian 8.2 - dependencies: libkrb5-dev, cmake -Get the tag name from https://hub.docker.com/r/microsoft/dotnet-buildtools-prereqs/tags/ and use in docker run below -- `docker run -it microsoft/dotnet-buildtools-prereqs:debian-8.2-SHA-YMD.. /bin/sh` +Get the tag name from https://mcr.microsoft.com/v2/dotnet-buildtools/prereqs/tags/list and use in docker run below +- `docker run -it mcr.microsoft.com/dotnet-buildtools/prereqs:debian-8.2-SHA-YMD.. /bin/sh` - `docker images` shows _id for Debian 8.2 to use in command below - `docker exec -it _id /bin/sh` diff --git a/src/tests/Common/scripts/arm32_ci_script.sh b/src/tests/Common/scripts/arm32_ci_script.sh index 8866d49536d..33d884f4f99 100755 --- a/src/tests/Common/scripts/arm32_ci_script.sh +++ b/src/tests/Common/scripts/arm32_ci_script.sh @@ -246,13 +246,13 @@ function cross_build_coreclr_with_docker { # TODO: For arm, we are going to embed RootFS inside Docker image. case $__linuxCodeName in trusty) - __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu-14.04-cross-0cd4667-20172211042239" + __dockerImage=" mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-14.04-cross-0cd4667-20172211042239" __skipRootFS=1 __dockerEnvironmentVariables+=" -e ROOTFS_DIR=/crossrootfs/arm" __runtimeOS="ubuntu.14.04" ;; xenial) - __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu-16.04-cross-ef0ac75-20175511035548" + __dockerImage=" mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-16.04-cross-ef0ac75-20175511035548" __skipRootFS=1 __dockerEnvironmentVariables+=" -e ROOTFS_DIR=/crossrootfs/arm" __runtimeOS="ubuntu.16.04" @@ -372,12 +372,12 @@ function run_tests_using_docker { if [ "$__buildArch" == "arm" ]; then case $__linuxCodeName in trusty) - __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu1404_cross_prereqs_v3" + __dockerImage=" mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu1404_cross_prereqs_v3" __skipRootFS=1 __dockerEnvironmentVariables=" -e ROOTFS_DIR=/crossrootfs/arm" ;; xenial) - __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu1604_cross_prereqs_v3" + __dockerImage=" mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu1604_cross_prereqs_v3" __skipRootFS=1 __dockerEnvironmentVariables=" -e ROOTFS_DIR=/crossrootfs/arm" ;; diff --git a/src/tests/Common/scripts/run-pmi-diffs.py b/src/tests/Common/scripts/run-pmi-diffs.py index 106137eaeae..f711030e9cb 100755 --- a/src/tests/Common/scripts/run-pmi-diffs.py +++ b/src/tests/Common/scripts/run-pmi-diffs.py @@ -48,10 +48,10 @@ Jitutils_url = 'https://github.com/dotnet/jitutils.git' # The Docker file and possibly options should be hoisted out to a text file to be shared between scripts. -Docker_name_arm32 = 'microsoft/dotnet-buildtools-prereqs:ubuntu-14.04-cross-e435274-20180426002420' +Docker_name_arm32 = 'mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-14.04-cross-e435274-20180426002420' Docker_opts_arm32 = '-e ROOTFS_DIR=/crossrootfs/arm' -Docker_name_arm64 = 'microsoft/dotnet-buildtools-prereqs:ubuntu-16.04-cross-arm64-a3ae44b-20180315221921' +Docker_name_arm64 = 'mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-16.04-cross-arm64-a3ae44b-20180315221921' Docker_opts_arm64 = '-e ROOTFS_DIR=/crossrootfs/arm64' Is_illumos = ('illumos' in subprocess.Popen(["uname", "-o"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8')) From e4cc8f9d37eb6a893d391b458b900f5a20eb9cca Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Mon, 12 Jul 2021 02:06:32 -0700 Subject: [PATCH 435/926] Minor QUIC test improvements (#55494) * reduce timeout in SetListenerTimeoutWorksWithSmallTimeout * replace AssertArrayEqual with AssertExtensions.SequenceEqual Co-authored-by: Geoffrey Kizer --- .../tests/FunctionalTests/MsQuicTests.cs | 6 ++-- .../tests/FunctionalTests/QuicStreamTests.cs | 6 ++-- .../tests/FunctionalTests/QuicTestBase.cs | 34 ------------------- 3 files changed, 6 insertions(+), 40 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index b8097a43a6c..6ce1540fba5 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -216,7 +216,7 @@ namespace System.Net.Quic.Tests public async Task SetListenerTimeoutWorksWithSmallTimeout() { var quicOptions = new QuicListenerOptions(); - quicOptions.IdleTimeout = TimeSpan.FromSeconds(10); + quicOptions.IdleTimeout = TimeSpan.FromSeconds(1); quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions(); quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0); @@ -476,7 +476,7 @@ namespace System.Net.Quic.Tests byte[] buffer = new byte[data.Length]; int bytesRead = await ReadAll(stream, buffer); Assert.Equal(data.Length, bytesRead); - AssertArrayEqual(data, buffer); + AssertExtensions.SequenceEqual(data, buffer); for (int pos = 0; pos < data.Length; pos += writeSize) { @@ -499,7 +499,7 @@ namespace System.Net.Quic.Tests byte[] buffer = new byte[data.Length]; int bytesRead = await ReadAll(stream, buffer); Assert.Equal(data.Length, bytesRead); - AssertArrayEqual(data, buffer); + AssertExtensions.SequenceEqual(data, buffer); await stream.ShutdownCompleted(); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 7b409036518..b21135000ac 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -190,7 +190,7 @@ namespace System.Net.Quic.Tests byte[] buffer = new byte[data.Length]; int bytesRead = await ReadAll(stream, buffer); Assert.Equal(data.Length, bytesRead); - AssertArrayEqual(data, buffer); + AssertExtensions.SequenceEqual(data, buffer); for (int pos = 0; pos < data.Length; pos += writeSize) { @@ -213,7 +213,7 @@ namespace System.Net.Quic.Tests byte[] buffer = new byte[data.Length]; int bytesRead = await ReadAll(stream, buffer); Assert.Equal(data.Length, bytesRead); - AssertArrayEqual(data, buffer); + AssertExtensions.SequenceEqual(data, buffer); await stream.ShutdownCompleted(); } @@ -391,7 +391,7 @@ namespace System.Net.Quic.Tests } Assert.Equal(testBuffer.Length, totalBytesRead); - AssertArrayEqual(testBuffer, receiveBuffer); + AssertExtensions.SequenceEqual(testBuffer, receiveBuffer); await serverStream.ShutdownCompleted(); }); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index 4d9ab4de6a3..803b2d40705 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -171,40 +171,6 @@ namespace System.Net.Quic.Tests await stream.WriteAsync(buffer); } } - - internal static void AssertArrayEqual(byte[] expected, byte[] actual) - { - for (int i = 0; i < expected.Length; ++i) - { - if (expected[i] == actual[i]) - { - continue; - } - - var message = $"Wrong data starting from idx={i}\n" + - $"Expected: {ToStringAroundIndex(expected, i)}\n" + - $"Actual: {ToStringAroundIndex(actual, i)}"; - - Assert.True(expected[i] == actual[i], message); - } - } - - private static string ToStringAroundIndex(byte[] arr, int idx, int dl = 3, int dr = 7) - { - var sb = new StringBuilder(idx - (dl+1) >= 0 ? "[..., " : "["); - - for (int i = idx - dl; i <= idx + dr; ++i) - { - if (i >= 0 && i < arr.Length) - { - sb.Append($"{arr[i]}, "); - } - } - - sb.Append(idx + (dr+1) < arr.Length ? "...]" : "]"); - - return sb.ToString(); - } } public interface IQuicImplProviderFactory From 067aa165d97372f3e281786801d014fccea8b12c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 12 Jul 2021 14:24:54 +0200 Subject: [PATCH 436/926] Expose a global switch to disable IPV6 (#55012) Fixes #47583. Resolves #52287, #54807 and similar issues, without changing customer application code. Introduces an AppContext switch `System.Net.DisableIPv6` and environment variable `DOTNET_SYSTEM_NET_DISABLEIPV6` to emulate the lack of OS-level IPv6 support. This is useful to workaround dual-stack socket issues when the OS reports support of IPv6, but some other underlying infrastructure element (typically VPN) doesn't function well with IPv6 request. For consistency, this switch also impacts NameResolution and Ping, not only Sockets. --- .../Net/SocketProtocolSupportPal.Unix.cs | 6 +- .../Net/SocketProtocolSupportPal.Windows.cs | 6 +- .../System/Net/SocketProtocolSupportPal.cs | 36 +++++++++++ .../src/System.Net.NameResolution.csproj | 2 + .../src/System/Net/NameResolutionPal.Unix.cs | 6 +- .../tests/FunctionalTests/GetHostEntryTest.cs | 59 ++++++++++++++++--- ...System.Net.NameResolution.Pal.Tests.csproj | 2 + .../src/System.Net.Ping.csproj | 2 + .../src/System.Net.Sockets.csproj | 2 + .../tests/FunctionalTests/OSSupport.cs | 32 ++++++++++ 10 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs index d06ce8117fc..8de1e2a0f4c 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs @@ -7,12 +7,8 @@ using System.Runtime.InteropServices; namespace System.Net { - internal static class SocketProtocolSupportPal + internal static partial class SocketProtocolSupportPal { - public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6); - public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork); - public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix); - private static unsafe bool IsSupported(AddressFamily af) { IntPtr invalid = (IntPtr)(-1); diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs index de0465a51c6..50e7db176e2 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs @@ -9,12 +9,8 @@ using SocketType = System.Net.Internals.SocketType; namespace System.Net { - internal static class SocketProtocolSupportPal + internal static partial class SocketProtocolSupportPal { - public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6); - public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork); - public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix); - private static bool IsSupported(AddressFamily af) { Interop.Winsock.EnsureInitialized(); diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs new file mode 100644 index 00000000000..a61f47a0fa4 --- /dev/null +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.Sockets; + +namespace System.Net +{ + internal static partial class SocketProtocolSupportPal + { + private const string DisableIPv6AppCtxSwitch = "System.Net.DisableIPv6"; + private const string DisableIPv6EnvironmentVariable = "DOTNET_SYSTEM_NET_DISABLEIPV6"; + + public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6) && !IsIPv6Disabled(); + public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork); + public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix); + + private static bool IsIPv6Disabled() + { + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(DisableIPv6AppCtxSwitch, out bool disabled)) + { + return disabled; + } + + // AppContext switch wasn't used. Check the environment variable. + string? envVar = Environment.GetEnvironmentVariable(DisableIPv6EnvironmentVariable); + + if (envVar is not null) + { + return envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + return false; + } + } +} diff --git a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj index d2d9b9b4691..1642f1fff33 100644 --- a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj +++ b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj @@ -31,6 +31,8 @@ Link="Common\System\Net\IPAddressParserStatics.cs" /> + diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 46858edea5d..74c62684a82 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -76,9 +76,11 @@ namespace System.Net Interop.Sys.IPAddress* addressHandle = hostEntry.IPAddressList; for (int i = 0; i < hostEntry.IPAddressCount; i++) { - if (Array.IndexOf(nativeAddresses, addressHandle[i], 0, nativeAddressCount) == -1) + Interop.Sys.IPAddress nativeAddr = addressHandle[i]; + if (Array.IndexOf(nativeAddresses, nativeAddr, 0, nativeAddressCount) == -1 && + (!nativeAddr.IsIPv6 || SocketProtocolSupportPal.OSSupportsIPv6)) // Do not include IPv6 addresses if IPV6 support is force-disabled { - nativeAddresses[nativeAddressCount++] = addressHandle[i]; + nativeAddresses[nativeAddressCount++] = nativeAddr; } } diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index f860a2274e8..7a5d4c899e9 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -6,7 +6,7 @@ using System.IO; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; - +using Microsoft.DotNet.RemoteExecutor; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -21,9 +21,16 @@ namespace System.Net.NameResolution.Tests await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(localIPAddress)); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] + + public static bool GetHostEntryWorks = + // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] + PlatformDetection.IsNotArmNorArm64Process && + // [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] + PlatformDetection.IsNotOSX && + // [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst; + + [ConditionalTheory(nameof(GetHostEntryWorks))] [InlineData("")] [InlineData(TestSettings.LocalHost)] public async Task Dns_GetHostEntry_HostString_Ok(string hostName) @@ -77,12 +84,10 @@ namespace System.Net.NameResolution.Tests } } - [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] + [ConditionalTheory(nameof(GetHostEntryWorks))] [InlineData("")] [InlineData(TestSettings.LocalHost)] - public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) + public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) { if (PlatformDetection.IsSLES) { @@ -112,6 +117,44 @@ namespace System.Net.NameResolution.Tests Assert.Equal(list1, list2); } + public static bool GetHostEntry_DisableIPv6_Condition = GetHostEntryWorks && RemoteExecutor.IsSupported; + + [ConditionalTheory(nameof(GetHostEntry_DisableIPv6_Condition))] + [InlineData("")] + [InlineData(TestSettings.LocalHost)] + public void Dns_GetHostEntry_DisableIPv6_ExcludesIPv6Addresses(string hostnameOuter) + { + RemoteExecutor.Invoke(RunTest, hostnameOuter).Dispose(); + + static void RunTest(string hostnameInner) + { + AppContext.SetSwitch("System.Net.DisableIPv6", true); + IPHostEntry entry = Dns.GetHostEntry(hostnameInner); + foreach (IPAddress address in entry.AddressList) + { + Assert.NotEqual(AddressFamily.InterNetworkV6, address.AddressFamily); + } + } + } + + [ConditionalTheory(nameof(GetHostEntry_DisableIPv6_Condition))] + [InlineData("")] + [InlineData(TestSettings.LocalHost)] + public void Dns_GetHostEntryAsync_DisableIPv6_ExcludesIPv6Addresses(string hostnameOuter) + { + RemoteExecutor.Invoke(RunTest, hostnameOuter).Dispose(); + + static async Task RunTest(string hostnameInner) + { + AppContext.SetSwitch("System.Net.DisableIPv6", true); + IPHostEntry entry = await Dns.GetHostEntryAsync(hostnameInner); + foreach (IPAddress address in entry.AddressList) + { + Assert.NotEqual(AddressFamily.InterNetworkV6, address.AddressFamily); + } + } + } + [Fact] public async Task Dns_GetHostEntry_NullStringHost_Fail() { diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj index 7b2b9ee2c9f..3b9a81c4ee5 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj @@ -28,6 +28,8 @@ Link="Common\System\Net\IPEndPointStatics.cs" /> + + diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 35d10bb679f..496c628956f 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -65,6 +65,8 @@ Link="Common\System\Net\SocketAddress.cs" /> + diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs index 3e2886baf52..9ed625aecd0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs @@ -3,6 +3,7 @@ using System.Threading; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Net.Sockets.Tests @@ -25,6 +26,37 @@ namespace System.Net.Sockets.Tests #pragma warning restore } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void DisableIPv6_OSSupportsIPv6_False() + { + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_DISABLEIPV6"] = "1"; + RemoteExecutor.Invoke(RunTest, options).Dispose(); + + static void RunTest() + { + Assert.False(Socket.OSSupportsIPv6); + } + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void DisableIPv6_SocketConstructor_CreatesIPv4Socket() + { + RemoteExecutor.Invoke(RunTest).Dispose(); + + static void RunTest() + { + AppContext.SetSwitch("System.Net.DisableIPv6", true); + using Socket socket1 = new Socket(SocketType.Stream, ProtocolType.Tcp); + using Socket socket2 = new Socket(SocketType.Dgram, ProtocolType.Udp); + + Assert.Equal(AddressFamily.InterNetwork, socket1.AddressFamily); + Assert.Equal(AddressFamily.InterNetwork, socket2.AddressFamily); + Assert.False(socket1.DualMode); + Assert.False(socket2.DualMode); + } + } + [Fact] public void IOControl_FIONREAD_Success() { From 375ca44e80fb60c7183b3a3cc8ea82b77dd84430 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Mon, 12 Jul 2021 09:24:55 -0400 Subject: [PATCH 437/926] Fix android uninstall command and add validations (#55431) --- .../Coreclr.TestWrapper/MobileAppHandler.cs | 160 +++++++++++------- src/tests/run.proj | 9 +- 2 files changed, 105 insertions(+), 64 deletions(-) diff --git a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs index b594455f107..f4d179ce4ce 100644 --- a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs +++ b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs @@ -8,43 +8,24 @@ namespace CoreclrTestLib { public class MobileAppHandler { - public void InstallMobileApp(string platform, string category, string testBinaryBase, string reportBase) + public int InstallMobileApp(string platform, string category, string testBinaryBase, string reportBase) { - HandleMobileApp("install", platform, category, testBinaryBase, reportBase); + return HandleMobileApp("install", platform, category, testBinaryBase, reportBase); } - public void UninstallMobileApp(string platform, string category, string testBinaryBase, string reportBase) + public int UninstallMobileApp(string platform, string category, string testBinaryBase, string reportBase) { - HandleMobileApp("uninstall", platform, category, testBinaryBase, reportBase); + return HandleMobileApp("uninstall", platform, category, testBinaryBase, reportBase); } - private static void HandleMobileApp(string action, string platform, string category, string testBinaryBase, string reportBase) + private static int HandleMobileApp(string action, string platform, string category, string testBinaryBase, string reportBase) { //install or uninstall mobile app + int exitCode = -100; string outputFile = Path.Combine(reportBase, action, $"{category}_{action}.output.txt"); string errorFile = Path.Combine(reportBase, action, $"{category}_{action}.error.txt"); - string dotnetCmd_raw = System.Environment.GetEnvironmentVariable("__TestDotNetCmd"); - string xharnessCmd_raw = System.Environment.GetEnvironmentVariable("XHARNESS_CLI_PATH"); - int timeout = 600000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI - - string dotnetCmd = string.IsNullOrEmpty(dotnetCmd_raw) ? "dotnet" : dotnetCmd_raw; - string xharnessCmd = string.IsNullOrEmpty(xharnessCmd_raw) ? "xharness" : $"exec {xharnessCmd_raw}"; - string appExtension = platform == "android" ? "apk" : "app"; - string cmdStr = $"{dotnetCmd} {xharnessCmd} {platform} {action} --output-directory={reportBase}/{action}"; - - if (action == "install") - { - cmdStr += $" --app={testBinaryBase}/{category}.{appExtension}"; - } - else if (platform != "android") - { - cmdStr += $" --app=net.dot.{category}"; - } - - if (platform == "android") - { - cmdStr += $" --package-name=net.dot.{category}"; - } + bool platformValueFlag = true; + bool actionValueFlag = true; Directory.CreateDirectory(Path.Combine(reportBase, action)); var outputStream = new FileStream(outputFile, FileMode.Create); @@ -52,55 +33,110 @@ namespace CoreclrTestLib using (var outputWriter = new StreamWriter(outputStream)) using (var errorWriter = new StreamWriter(errorStream)) - using (Process process = new Process()) { - if (OperatingSystem.IsWindows()) + //Validate inputs + if ((platform != "android") && (platform != "apple")) { - process.StartInfo.FileName = "cmd.exe"; - } - else - { - process.StartInfo.FileName = "/bin/bash"; + outputWriter.WriteLine($"Incorrect value of platform. Provided {platform}. Valid strings are android and apple."); + platformValueFlag = false; } - process.StartInfo.Arguments = ConvertCmd2Arg(cmdStr); - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - - DateTime startTime = DateTime.Now; - process.Start(); - - var cts = new CancellationTokenSource(); - Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream, 4096, cts.Token); - Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream, 4096, cts.Token); - - if (process.WaitForExit(timeout)) + if ((action != "install") && (action != "uninstall")) { - Task.WaitAll(copyOutput, copyError); + outputWriter.WriteLine($"Incorrect value of action. Provided {action}. Valid strings are install and uninstall."); + actionValueFlag = false; } - else - { - //Time out - DateTime endTime = DateTime.Now; - try + if (platformValueFlag && actionValueFlag) + { + int timeout = 240000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI + string dotnetCmd_raw = System.Environment.GetEnvironmentVariable("__TestDotNetCmd"); + string xharnessCmd_raw = System.Environment.GetEnvironmentVariable("XHARNESS_CLI_PATH"); + string dotnetCmd = string.IsNullOrEmpty(dotnetCmd_raw) ? "dotnet" : dotnetCmd_raw; + string xharnessCmd = string.IsNullOrEmpty(xharnessCmd_raw) ? "xharness" : $"exec {xharnessCmd_raw}"; + string appExtension = platform == "android" ? "apk" : "app"; + + string cmdStr = $"{dotnetCmd} {xharnessCmd} {platform} {action}"; + + if (platform == "android") { - cts.Cancel(); - } - catch {} + cmdStr += $" --package-name=net.dot.{category}"; - outputWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})", - cmdStr, timeout, startTime.ToString(), endTime.ToString()); - errorWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})", - cmdStr, timeout, startTime.ToString(), endTime.ToString()); - - process.Kill(entireProcessTree: true); + if (action == "install") + { + cmdStr += $" --app={testBinaryBase}/{category}.{appExtension} --output-directory={reportBase}/{action}"; + } + } + else // platform is apple + { + cmdStr += $" --output-directory={reportBase}/{action} --target=ios-simulator-64"; //To Do: target should be either emulator or device + + if (action == "install") + { + cmdStr += $" --app={testBinaryBase}/{category}.{appExtension}"; + } + else // action is uninstall + { + cmdStr += $" --app=net.dot.{category}"; + } + } + + using (Process process = new Process()) + { + if (OperatingSystem.IsWindows()) + { + process.StartInfo.FileName = "cmd.exe"; + } + else + { + process.StartInfo.FileName = "/bin/bash"; + } + + process.StartInfo.Arguments = ConvertCmd2Arg(cmdStr); + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + DateTime startTime = DateTime.Now; + process.Start(); + + var cts = new CancellationTokenSource(); + Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream, 4096, cts.Token); + Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream, 4096, cts.Token); + + if (process.WaitForExit(timeout)) + { + // Process completed. + exitCode = process.ExitCode; + Task.WaitAll(copyOutput, copyError); + } + else + { + //Time out. + DateTime endTime = DateTime.Now; + + try + { + cts.Cancel(); + } + catch {} + + outputWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})", + cmdStr, timeout, startTime.ToString(), endTime.ToString()); + errorWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})", + cmdStr, timeout, startTime.ToString(), endTime.ToString()); + + process.Kill(entireProcessTree: true); + } + } } + outputWriter.WriteLine("xharness exitcode is : " + exitCode.ToString()); outputWriter.Flush(); errorWriter.Flush(); } + + return exitCode; } private static string ConvertCmd2Arg(string cmd) diff --git a/src/tests/run.proj b/src/tests/run.proj index a64233555d2..3289e9c9ee6 100644 --- a/src/tests/run.proj +++ b/src/tests/run.proj @@ -272,6 +272,8 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). coreRoot = Environment.GetEnvironmentVariable(%22CORE_ROOT%22)%3B category = "$([System.String]::Copy($(Category)).Replace(".","_").Replace("\","").Replace("-","_"))"%3B helixUploadRoot = Environment.GetEnvironmentVariable(%22HELIX_WORKITEM_UPLOAD_ROOT%22)%3B + int retCode = -100%3B + if (!String.IsNullOrEmpty(helixUploadRoot)) { reportBase = Path.Combine(Path.GetFullPath(helixUploadRoot), "Reports")%3B } @@ -293,12 +295,15 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). string operatingSystem = Environment.GetEnvironmentVariable("OS")%3B runningInWindows = (operatingSystem != null && operatingSystem.StartsWith("Windows"))%3B - handler.InstallMobileApp(%22$(MobilePlatform)%22, category, testBinaryBase, reportBase)%3B + retCode = handler.InstallMobileApp(%22$(MobilePlatform)%22, category, testBinaryBase, reportBase)%3B + Assert.True(retCode == 0, "Failed to install mobile app.")%3B } public void Dispose() { - handler.UninstallMobileApp(%22$(MobilePlatform)%22, category, testBinaryBase, reportBase)%3B + int retCode = -100%3B + retCode = handler.UninstallMobileApp(%22$(MobilePlatform)%22, category, testBinaryBase, reportBase)%3B + Assert.True(retCode == 0, "Failed to uninstall mobile app.")%3B } } From 633e04481f21a23d567cf49177f76fbef3a34cfe Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 10:18:04 -0400 Subject: [PATCH 438/926] Enable CA1419 (SafeHandle public parameterless ctor) (#55460) --- eng/CodeAnalysis.ruleset | 1 + .../Interop.Keychain.macOS.cs | 2 +- .../src/Interop/Windows/HttpApi/Interop.HttpApi.cs | 2 +- .../src/Interop/Windows/SspiCli/SecuritySafeHandles.cs | 2 +- .../Win32/SafeHandles/Asn1SafeHandles.Unix.cs | 4 ++-- .../SafeHandles/GssSafeHandles.PlatformNotSupported.cs | 7 ++++--- .../Win32/SafeHandles/SafeUnicodeStringHandle.cs | 2 ++ .../System/Net/Security/SecurityContextTokenHandle.cs | 2 +- .../System/Net/Security/Unix/SafeDeleteNegoContext.cs | 2 ++ .../System/Net/Security/Unix/SafeDeleteSslContext.cs | 2 ++ .../System/Net/Security/Unix/SafeFreeCertContext.cs | 2 ++ .../Net/Security/Unix/SafeFreeNegoCredentials.cs | 2 ++ .../System/Net/Security/Unix/SafeFreeSslCredentials.cs | 2 ++ .../src/System/Data/Odbc/OdbcConnectionHandle.cs | 2 ++ .../src/System/Data/Odbc/OdbcHandle.cs | 2 ++ .../src/System/Data/Odbc/OdbcStatementHandle.cs | 2 ++ .../System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs | 2 ++ .../System.Data.OleDb/src/OleDbTransaction.cs | 2 ++ src/libraries/System.Data.OleDb/src/OleDbWrapper.cs | 6 ++++-- src/libraries/System.Data.OleDb/src/PropertyIDSet.cs | 2 ++ src/libraries/System.Data.OleDb/src/PropertyInfoSet.cs | 2 ++ src/libraries/System.Data.OleDb/src/RowBinding.cs | 2 ++ src/libraries/System.Data.OleDb/src/SafeHandles.cs | 2 ++ .../src/System/Data/ProviderBase/DbConnectionPool.cs | 2 +- .../DirectoryServices/AccountManagement/AuthZSet.cs | 5 +++-- .../DirectoryServices/Protocols/Interop/SafeHandles.cs | 2 ++ .../Drawing/Drawing2D/SafeCustomLineCapHandle.cs | 4 ++++ .../src/System/Net/Http/WinHttpChannelBinding.cs | 3 ++- .../src/System/Net/Windows/HttpServerSessionHandle.cs | 2 ++ .../Quic/Implementations/MsQuic/Internal/MsQuicApi.cs | 10 +++++----- .../MsQuic/Interop/SafeMsQuicConfigurationHandle.cs | 2 +- .../MsQuic/Interop/SafeMsQuicConnectionHandle.cs | 2 +- .../MsQuic/Interop/SafeMsQuicListenerHandle.cs | 2 +- .../MsQuic/Interop/SafeMsQuicRegistrationHandle.cs | 2 +- .../MsQuic/Interop/SafeMsQuicStreamHandle.cs | 2 +- .../Net/Security/Pal.Android/SafeDeleteSslContext.cs | 2 ++ .../Security/Pal.Managed/SafeChannelBindingHandle.cs | 2 ++ .../Net/Security/Pal.Managed/SafeFreeSslCredentials.cs | 2 ++ .../Net/Security/Pal.OSX/SafeDeleteSslContext.cs | 2 ++ .../src/System/Security/SecureString.cs | 2 ++ ...m.Private.Runtime.InteropServices.JavaScript.csproj | 1 + .../src/System/Security/Cryptography/CngKeyLite.cs | 2 ++ .../Win32/SafeHandles/SafeCertContextHandle.cs | 2 ++ .../Win32/SafeHandles/SafeProvOrNCryptKeyHandleUwp.cs | 2 ++ .../Cryptography/Pal.Windows/Native/SafeHandles.cs | 2 ++ .../Microsoft/Win32/SafeHandles/SafePasswordHandle.cs | 2 ++ .../Microsoft/Win32/SafeHandles/SafeServiceHandle.cs | 4 ++++ 47 files changed, 94 insertions(+), 25 deletions(-) diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index 8faa50e9a6f..133252bae8f 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -68,6 +68,7 @@ + diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs index 862760aa2c8..c93319994bb 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs @@ -401,7 +401,7 @@ namespace System.Security.Cryptography.Apple private static readonly Dictionary s_lookup = new Dictionary(); - internal SafeTemporaryKeychainHandle() + public SafeTemporaryKeychainHandle() { } diff --git a/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs b/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs index e0982d907c6..27c22800a42 100644 --- a/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs +++ b/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs @@ -508,7 +508,7 @@ internal static partial class Interop { private int _size; - private SafeLocalFreeChannelBinding() { } + public SafeLocalFreeChannelBinding() { } public override int Size { diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs index 0f2cb05603a..43b718524d1 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs @@ -128,7 +128,7 @@ namespace System.Net.Security internal sealed class SafeFreeContextBuffer_SECURITY : SafeFreeContextBuffer { - internal SafeFreeContextBuffer_SECURITY() : base() { } + public SafeFreeContextBuffer_SECURITY() : base() { } protected override bool ReleaseHandle() { diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs index 193533551bc..f8b26951cf2 100644 --- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs @@ -68,7 +68,7 @@ namespace Microsoft.Win32.SafeHandles internal sealed class SafeSharedAsn1IntegerHandle : SafeInteriorHandle { - private SafeSharedAsn1IntegerHandle() : + public SafeSharedAsn1IntegerHandle() : base(IntPtr.Zero, ownsHandle: true) { } @@ -76,7 +76,7 @@ namespace Microsoft.Win32.SafeHandles internal sealed class SafeSharedAsn1OctetStringHandle : SafeInteriorHandle { - private SafeSharedAsn1OctetStringHandle() : + public SafeSharedAsn1OctetStringHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs index 0c6a8304620..20eeb6dfbf8 100644 --- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs @@ -18,7 +18,8 @@ namespace Microsoft.Win32.SafeHandles } protected override bool ReleaseHandle() => throw new PlatformNotSupportedException(); - private SafeGssNameHandle() + + public SafeGssNameHandle() : base(IntPtr.Zero, true) { } @@ -27,7 +28,7 @@ namespace Microsoft.Win32.SafeHandles [UnsupportedOSPlatform("tvos")] internal sealed class SafeGssCredHandle : SafeHandle { - private SafeGssCredHandle() + public SafeGssCredHandle() : base(IntPtr.Zero, true) { } @@ -43,7 +44,7 @@ namespace Microsoft.Win32.SafeHandles [UnsupportedOSPlatform("tvos")] internal sealed class SafeGssContextHandle : SafeHandle { - private SafeGssContextHandle() + public SafeGssContextHandle() : base(IntPtr.Zero, true) { } diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs index e58bfd19695..0c27a200495 100644 --- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs @@ -4,6 +4,8 @@ using System; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace Microsoft.Win32.SafeHandles { /// diff --git a/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs b/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs index 8e889f852af..4f4c9951a72 100644 --- a/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs +++ b/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs @@ -16,7 +16,7 @@ namespace System.Net.Security #endif private int _disposed; - private SecurityContextTokenHandle() : base() + public SecurityContextTokenHandle() : base() { } diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs index 123a28ace0f..2e5a0b85600 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { internal sealed class SafeDeleteNegoContext : SafeDeleteContext diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs index 9b27cc0d969..3f550d36d7f 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs @@ -11,6 +11,8 @@ using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { internal sealed class SafeDeleteSslContext : SafeDeleteContext diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs index c5fa820ab92..ca7438a33b4 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs @@ -8,6 +8,8 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { #if DEBUG diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs index 58d1942829e..fef571d5f7d 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs index 8867027d070..ff378ab4251 100644 --- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs +++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs @@ -10,6 +10,8 @@ using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { internal sealed class SafeFreeSslCredentials : SafeFreeCredentials diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs index 6cd776f00fa..b2c7ee7cb33 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.Odbc { internal sealed class OdbcConnectionHandle : OdbcHandle diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs index 77900ae013f..a5432b82405 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs @@ -7,6 +7,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.Odbc { internal abstract class OdbcHandle : SafeHandle diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs index 871478cae50..f8384b7a20b 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs @@ -4,6 +4,8 @@ using System.Data.Common; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.Odbc { internal readonly struct SQLLEN diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs index 370bb9771dc..6df1b3c9ec9 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs @@ -6,6 +6,8 @@ using System.Diagnostics; // Debug services using System.Runtime.InteropServices; using System.Text; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.Odbc { internal sealed class CNativeBuffer : System.Data.ProviderBase.DbBuffer diff --git a/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs b/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs index 2181a6cbe75..4a169181f2b 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbTransaction.cs @@ -7,6 +7,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { public sealed class OleDbTransaction : DbTransaction diff --git a/src/libraries/System.Data.OleDb/src/OleDbWrapper.cs b/src/libraries/System.Data.OleDb/src/OleDbWrapper.cs index 23ef6c6b0d4..bc458f68223 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbWrapper.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbWrapper.cs @@ -7,6 +7,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { // SafeHandle wrapper around 'DataLinks' object which pools the native OLE DB providers. @@ -92,7 +94,7 @@ namespace System.Data.OleDb // we expect to store IDBInitialize instance pointer in base.handle // construct a DataSourceWrapper and used as a ref parameter to GetDataSource - internal DataSourceWrapper() : base() + public DataSourceWrapper() : base() { } @@ -229,7 +231,7 @@ namespace System.Data.OleDb // since we maintain an AddRef on IDBCreateCommand it is safe to use the delegate without rechecking its function pointer private UnsafeNativeMethods.IDBCreateCommandCreateCommand? DangerousIDBCreateCommandCreateCommand; - internal SessionWrapper() : base() + public SessionWrapper() : base() { } diff --git a/src/libraries/System.Data.OleDb/src/PropertyIDSet.cs b/src/libraries/System.Data.OleDb/src/PropertyIDSet.cs index 52b854cd0dc..5aca3735e55 100644 --- a/src/libraries/System.Data.OleDb/src/PropertyIDSet.cs +++ b/src/libraries/System.Data.OleDb/src/PropertyIDSet.cs @@ -5,6 +5,8 @@ using System.Data.Common; using System.Data.ProviderBase; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { internal sealed class PropertyIDSet : DbBuffer diff --git a/src/libraries/System.Data.OleDb/src/PropertyInfoSet.cs b/src/libraries/System.Data.OleDb/src/PropertyInfoSet.cs index 60b44bab7de..67f95789eb0 100644 --- a/src/libraries/System.Data.OleDb/src/PropertyInfoSet.cs +++ b/src/libraries/System.Data.OleDb/src/PropertyInfoSet.cs @@ -7,6 +7,8 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { internal sealed class OleDbPropertyInfo diff --git a/src/libraries/System.Data.OleDb/src/RowBinding.cs b/src/libraries/System.Data.OleDb/src/RowBinding.cs index 7f94bf563d5..75312475fcd 100644 --- a/src/libraries/System.Data.OleDb/src/RowBinding.cs +++ b/src/libraries/System.Data.OleDb/src/RowBinding.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { internal sealed class RowBinding : System.Data.ProviderBase.DbBuffer diff --git a/src/libraries/System.Data.OleDb/src/SafeHandles.cs b/src/libraries/System.Data.OleDb/src/SafeHandles.cs index d435f7f45bc..edd87806063 100644 --- a/src/libraries/System.Data.OleDb/src/SafeHandles.cs +++ b/src/libraries/System.Data.OleDb/src/SafeHandles.cs @@ -9,6 +9,8 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using static System.Data.Common.UnsafeNativeMethods; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Data.OleDb { internal sealed class DualCoTaskMem : SafeHandle diff --git a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPool.cs b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPool.cs index e411fc55010..521ea909f11 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPool.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPool.cs @@ -233,7 +233,7 @@ namespace System.Data.ProviderBase private readonly int _releaseFlags; - internal PoolWaitHandles() : base(3 * IntPtr.Size) + public PoolWaitHandles() : base(3 * IntPtr.Size) { bool mustRelease1 = false, mustRelease2 = false, mustRelease3 = false; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs index 4c6491929ad..56edd76877c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs @@ -566,8 +566,9 @@ namespace System.DirectoryServices.AccountManagement // private sealed class SafeMemoryPtr : SafeHandle { - private SafeMemoryPtr() : base(IntPtr.Zero, true) - { } + public SafeMemoryPtr() : base(IntPtr.Zero, true) + { + } internal SafeMemoryPtr(IntPtr handle) : base(IntPtr.Zero, true) { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.cs index 3dc2a589a8d..4f2ac1c3086 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.cs @@ -4,6 +4,8 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.DirectoryServices.Protocols { internal sealed class HGlobalMemHandle : SafeHandleZeroOrMinusOneIsInvalid diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs index beb8d42bb6f..b01b88392b9 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs @@ -11,6 +11,10 @@ namespace System.Drawing.Drawing2D { internal sealed class SafeCustomLineCapHandle : SafeHandle { + public SafeCustomLineCapHandle() : base(IntPtr.Zero, true) + { + } + // Create a SafeHandle, informing the base class // that this SafeHandle instance "owns" the handle, // and therefore SafeHandle should call diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs index 5e862724d0e..8011f2859d0 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs @@ -5,9 +5,10 @@ using System; using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; using System.Text; - using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Http { internal sealed class WinHttpChannelBinding : ChannelBinding diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpServerSessionHandle.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpServerSessionHandle.cs index 671eae6386b..f180fefecd1 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpServerSessionHandle.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpServerSessionHandle.cs @@ -4,6 +4,8 @@ using Microsoft.Win32.SafeHandles; using System.Threading; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net { // diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index 2bf1b9b1955..cba34f6dbba 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -16,11 +16,11 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal // This is workaround for a bug in ILTrimmer. // Without these DynamicDependency attributes, .ctor() will be removed from the safe handles. // Remove once fixed: https://github.com/mono/linker/issues/1660 - [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicRegistrationHandle))] - [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConfigurationHandle))] - [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicListenerHandle))] - [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConnectionHandle))] - [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicStreamHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicRegistrationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicConfigurationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicListenerHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicConnectionHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicStreamHandle))] private MsQuicApi(NativeApi* vtable) { uint status; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index f80f33f53ae..df48e0db377 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -21,7 +21,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicConfigurationHandle() + public SafeMsQuicConfigurationHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs index 74f806c9818..9354ce04a18 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -9,7 +9,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicConnectionHandle() + public SafeMsQuicConnectionHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs index 87e59b69a22..f0f75556921 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs @@ -9,7 +9,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicListenerHandle() + public SafeMsQuicListenerHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs index d9c6e7c70ad..798636bd7e4 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs @@ -9,7 +9,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicRegistrationHandle() + public SafeMsQuicRegistrationHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs index 3037f60956a..179bdd3d15e 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs @@ -9,7 +9,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicStreamHandle() + public SafeMsQuicStreamHandle() : base(IntPtr.Zero, ownsHandle: true) { } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs index b0177cbc432..99cb5fa68cf 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs @@ -13,6 +13,8 @@ using Microsoft.Win32.SafeHandles; using PAL_KeyAlgorithm = Interop.AndroidCrypto.PAL_KeyAlgorithm; using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs index 0308355c889..e65b0aacde2 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs @@ -6,6 +6,8 @@ using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; using System.Text; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net.Security { internal sealed class SafeChannelBindingHandle : ChannelBinding diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeFreeSslCredentials.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeFreeSslCredentials.cs index 24a4e7be784..c047b88e5c5 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeFreeSslCredentials.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeFreeSslCredentials.cs @@ -6,6 +6,8 @@ using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net { internal sealed class SafeFreeSslCredentials : SafeFreeCredentials diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 44c8757c51e..9b4cef4873d 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -9,6 +9,8 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs index 3f1cf733cb6..721c74149ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs @@ -429,7 +429,9 @@ namespace System.Security // A local copy of byte length to be able to access it in ReleaseHandle without the risk of throwing exceptions private int _byteLength; +#pragma warning disable CA1419 // not intended for use with P/Invoke private UnmanagedBuffer() : base(true) { } +#pragma warning restore CA1419 public static UnmanagedBuffer Allocate(int byteLength) { diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj index 08f018bc714..9d779477a88 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj @@ -3,6 +3,7 @@ true enable $(NetCoreAppCurrent)-Browser + $(NoWarn);CA1419 diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CngKeyLite.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CngKeyLite.cs index c7c38e7bcc4..3670b98ffc0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CngKeyLite.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CngKeyLite.cs @@ -724,6 +724,8 @@ namespace Microsoft.Win32.SafeHandles { } +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + internal sealed class DuplicateSafeNCryptKeyHandle : SafeNCryptKeyHandle { public DuplicateSafeNCryptKeyHandle(SafeNCryptKeyHandle original) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeCertContextHandle.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeCertContextHandle.cs index 50293c45860..5496b23d1bb 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeCertContextHandle.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeCertContextHandle.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using static Interop.Crypt32; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace Microsoft.Win32.SafeHandles { internal sealed class SafeCertContextHandle : SafeHandle diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeProvOrNCryptKeyHandleUwp.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeProvOrNCryptKeyHandleUwp.cs index a1ac99fff12..884bf569481 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeProvOrNCryptKeyHandleUwp.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Microsoft/Win32/SafeHandles/SafeProvOrNCryptKeyHandleUwp.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using ErrorCode = Interop.NCrypt.ErrorCode; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace Microsoft.Win32.SafeHandles { // diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/SafeHandles.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/SafeHandles.cs index 4c3ba2011b6..0f67d4244f6 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/SafeHandles.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/SafeHandles.cs @@ -7,6 +7,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace Internal.Cryptography.Pal.Native { /// diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs index 6ebaa978de9..f528eae37b0 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs @@ -5,6 +5,8 @@ using System; using System.Runtime.InteropServices; using System.Security; +#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke + namespace Microsoft.Win32.SafeHandles { /// diff --git a/src/libraries/System.ServiceProcess.ServiceController/src/Microsoft/Win32/SafeHandles/SafeServiceHandle.cs b/src/libraries/System.ServiceProcess.ServiceController/src/Microsoft/Win32/SafeHandles/SafeServiceHandle.cs index 48f03927896..77b1ea855ba 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/src/Microsoft/Win32/SafeHandles/SafeServiceHandle.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/src/Microsoft/Win32/SafeHandles/SafeServiceHandle.cs @@ -11,6 +11,10 @@ namespace Microsoft.Win32.SafeHandles /// internal sealed class SafeServiceHandle : SafeHandle { + public SafeServiceHandle() : base(IntPtr.Zero, true) + { + } + internal SafeServiceHandle(IntPtr handle) : base(IntPtr.Zero, true) { SetHandle(handle); From 102fc35e821a983ab6cab87e25322eb950b07d6b Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Mon, 12 Jul 2021 17:22:18 +0200 Subject: [PATCH 439/926] QUIC logging update (#55500) Together with #55483 this updates all logging to use msquic handles instead of hash code - for listeners, connections and streams. The format of handle is the same as msquic one. --- .../src/System.Net.Quic.csproj | 1 + .../MsQuic/Interop/MsQuicTraceHelper.cs | 30 +++++++++++++++ .../MsQuic/MsQuicConnection.cs | 35 +++++++++-------- .../Implementations/MsQuic/MsQuicListener.cs | 12 ++++++ .../Implementations/MsQuic/MsQuicStream.cs | 38 +++++++++---------- 5 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicTraceHelper.cs diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 6d8de7f5d6c..1cd17da1d87 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -20,6 +20,7 @@ + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicTraceHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicTraceHelper.cs new file mode 100644 index 00000000000..33ef7b948c9 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicTraceHelper.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicTraceHelper + { + internal static string GetTraceId(SafeMsQuicStreamHandle handle) + { + return $"[strm][0x{GetIntPtrHex(handle)}]"; + } + + internal static string GetTraceId(SafeMsQuicConnectionHandle handle) + { + return $"[conn][0x{GetIntPtrHex(handle)}]"; + } + + internal static string GetTraceId(SafeMsQuicListenerHandle handle) + { + return $"[list][0x{GetIntPtrHex(handle)}]"; + } + + private static string GetIntPtrHex(SafeHandle handle) + { + return handle.DangerousGetHandle().ToString("X11"); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index c5df9a21b46..ec1710316c1 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -43,6 +43,8 @@ namespace System.Net.Quic.Implementations.MsQuic internal sealed class State { public SafeMsQuicConnectionHandle Handle = null!; // set inside of MsQuicConnection ctor. + public string TraceId = null!; // set inside of MsQuicConnection ctor. + public GCHandle StateGCHandle; // These exists to prevent GC of the MsQuicConnection in the middle of an async op (Connect or Shutdown). @@ -83,7 +85,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (releaseHandles) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId()} releasing handle after last stream."); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId} releasing handle after last stream."); Handle?.Dispose(); } } @@ -124,14 +126,9 @@ namespace System.Net.Quic.Implementations.MsQuic _closing = true; } } - - internal string TraceId() - { - return $"[MsQuicConnection#{this.GetHashCode()}/{Handle?.DangerousGetHandle():x}]"; - } } - internal string TraceId() => _state.TraceId(); + internal string TraceId() => _state.TraceId; // constructor for inbound connections public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle, bool remoteCertificateRequired = false, X509RevocationMode revocationMode = X509RevocationMode.Offline, RemoteCertificateValidationCallback? remoteCertificateValidationCallback = null) @@ -168,9 +165,10 @@ namespace System.Net.Quic.Implementations.MsQuic throw; } + _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(_state, $"{TraceId()} inbound connection created"); + NetEventSource.Info(_state, $"{TraceId()} Inbound connection created"); } } @@ -207,9 +205,10 @@ namespace System.Net.Quic.Implementations.MsQuic throw; } + _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(_state, $"{TraceId()} outbound connection created"); + NetEventSource.Info(_state, $"{TraceId()} Outbound connection created"); } } @@ -390,7 +389,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (certificate == null) { - if (NetEventSource.Log.IsEnabled() && connection._remoteCertificateRequired) NetEventSource.Error(state.Connection, "Remote certificate required, but no remote certificate received"); + if (NetEventSource.Log.IsEnabled() && connection._remoteCertificateRequired) NetEventSource.Error(state, $"{state.TraceId} Remote certificate required, but no remote certificate received"); sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; } else @@ -420,18 +419,18 @@ namespace System.Net.Quic.Implementations.MsQuic { bool success = connection._remoteCertificateValidationCallback(connection, certificate, chain, sslPolicyErrors); if (!success && NetEventSource.Log.IsEnabled()) - NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] remote certificate rejected by verification callback"); + NetEventSource.Error(state, $"{state.TraceId} Remote certificate rejected by verification callback"); return success ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; } if (NetEventSource.Log.IsEnabled()) - NetEventSource.Info(state, $"[Connection#{state.GetHashCode()}] certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}"); + NetEventSource.Info(state, $"{state.TraceId} Certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}"); return (sslPolicyErrors == SslPolicyErrors.None) ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; } catch (Exception ex) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] certificate validation failed ${ex.Message}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(state, $"{state.TraceId} Certificate validation failed ${ex.Message}"); } return MsQuicStatusCodes.InternalError; @@ -629,7 +628,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(state, $"{state.TraceId()} received event {connectionEvent.Type}"); + NetEventSource.Info(state, $"{state.TraceId} Connection received event {connectionEvent.Type}"); } try @@ -658,10 +657,10 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); + NetEventSource.Error(state, $"{state.TraceId} Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); } - Debug.Fail($"[Connection#{state.GetHashCode()}] Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); + Debug.Fail($"{state.TraceId} Exception occurred during handling {connectionEvent.Type} connection callback: {ex}"); // TODO: trigger an exception on any outstanding async calls. @@ -705,7 +704,7 @@ namespace System.Net.Quic.Implementations.MsQuic return; } - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposing {disposing}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Stream disposing {disposing}"); bool releaseHandles = false; lock (_state) @@ -726,7 +725,7 @@ namespace System.Net.Quic.Implementations.MsQuic _configuration?.Dispose(); if (releaseHandles) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} releasing handle"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Connection releasing handle"); // We may not be fully initialized if constructor fails. _state.Handle?.Dispose(); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs index c5ef5d3fdce..391d26a93dd 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -29,6 +29,7 @@ namespace System.Net.Quic.Implementations.MsQuic { // set immediately in ctor, but we need a GCHandle to State in order to create the handle. public SafeMsQuicListenerHandle Handle = null!; + public string TraceId = null!; // set in ctor. public readonly SafeMsQuicConfigurationHandle ConnectionConfiguration; public readonly Channel AcceptConnectionQueue; @@ -75,7 +76,18 @@ namespace System.Net.Quic.Implementations.MsQuic throw; } + _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(_state, $"{_state.TraceId} Listener created"); + } + _listenEndPoint = Start(options); + + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(_state, $"{_state.TraceId} Listener started"); + } } internal override IPEndPoint ListenEndPoint diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index f66f05fdb1c..437f81ce775 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -33,6 +33,7 @@ namespace System.Net.Quic.Implementations.MsQuic public SafeMsQuicStreamHandle Handle = null!; // set in ctor. public GCHandle StateGCHandle; public MsQuicConnection.State ConnectionState = null!; // set in ctor. + public string TraceId = null!; // set in ctor. public ReadState ReadState; public long ReadErrorCode = -1; @@ -66,7 +67,7 @@ namespace System.Net.Quic.Implementations.MsQuic public void Cleanup() { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId()} releasing handles."); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{TraceId} releasing handles."); ShutdownState = ShutdownState.Finished; CleanupSendState(this); @@ -76,14 +77,9 @@ namespace System.Net.Quic.Implementations.MsQuic if (StateGCHandle.IsAllocated) StateGCHandle.Free(); ConnectionState?.RemoveStream(null); } - - internal string TraceId() - { - return $"[MsQuicStream#{this.GetHashCode()}/{Handle?.DangerousGetHandle():x}]"; - } } - internal string TraceId() => _state.TraceId(); + internal string TraceId() => _state.TraceId; // inbound. internal MsQuicStream(MsQuicConnection.State connectionState, SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) @@ -118,12 +114,13 @@ namespace System.Net.Quic.Implementations.MsQuic _state.ConnectionState = connectionState; + _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info( _state, - $"{TraceId()} inbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + - $"in {_state.ConnectionState.TraceId()}."); + $"{TraceId()} Inbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + + $"in connection {_state.ConnectionState.TraceId}."); } } @@ -171,12 +168,13 @@ namespace System.Net.Quic.Implementations.MsQuic _state.ConnectionState = connectionState; + _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info( _state, - $"{TraceId()} outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + - $"in {_state.ConnectionState.TraceId()}."); + $"{_state.TraceId} Outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + + $"in connection {_state.ConnectionState.TraceId}."); } } @@ -366,7 +364,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(_state, $"[Stream#{_state.GetHashCode()}] reading into Memory of '{destination.Length}' bytes."); + NetEventSource.Info(_state, $"{TraceId()} Stream reading into Memory of '{destination.Length}' bytes."); } lock (_state) @@ -646,7 +644,7 @@ namespace System.Net.Quic.Implementations.MsQuic } - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposing {disposing}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Stream disposing {disposing}"); bool callShutdown = false; bool abortRead = false; @@ -699,7 +697,7 @@ namespace System.Net.Quic.Implementations.MsQuic _state.Cleanup(); } - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} disposed"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Stream disposed"); } private void EnableReceive() @@ -724,7 +722,7 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(state, $"{state.TraceId()} received event {evt.Type}"); + NetEventSource.Info(state, $"{state.TraceId} Stream received event {evt.Type}"); } try @@ -767,10 +765,10 @@ namespace System.Net.Quic.Implementations.MsQuic { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Stream#{state.GetHashCode()}] Exception occurred during handling {evt.Type} event: {ex}"); + NetEventSource.Error(state, $"{state.TraceId} Exception occurred during handling Stream {evt.Type} event: {ex}"); } - Debug.Fail($"[Stream#{state.GetHashCode()}] Exception occurred during handling {evt.Type} event: {ex}"); + Debug.Fail($"{state.TraceId} Exception occurred during handling Stream {evt.Type} event: {ex}"); return MsQuicStatusCodes.InternalError; } @@ -871,7 +869,7 @@ namespace System.Net.Quic.Implementations.MsQuic lock (state) { // This event won't occur within the middle of a receive. - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"[Stream#{state.GetHashCode()}] completing resettable event source."); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"{state.TraceId} Stream completing resettable event source."); if (state.ReadState == ReadState.None) { @@ -950,7 +948,7 @@ namespace System.Net.Quic.Implementations.MsQuic lock (state) { // This event won't occur within the middle of a receive. - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"[Stream#{state.GetHashCode()}] completing resettable event source."); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"{state.TraceId} Stream completing resettable event source."); if (state.ReadState == ReadState.None) { @@ -1240,7 +1238,7 @@ namespace System.Net.Quic.Implementations.MsQuic long errorCode = state.ConnectionState.AbortErrorCode; if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Info(state, $"[Stream#{state.GetHashCode()}] handling Connection#{state.ConnectionState.GetHashCode()} close" + + NetEventSource.Info(state, $"{state.TraceId} Stream handling connection {state.ConnectionState.TraceId} close" + (errorCode != -1 ? $" with code {errorCode}" : "")); } From f28318bf770f49aeaf8b2b67b440250732b7fbf3 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 12 Jul 2021 17:43:53 +0200 Subject: [PATCH 440/926] [wasm] WebSocket is already disposed by after abort. (#55075) _innerWebSocket is already disposed by after abort. --- .../BrowserWebSockets/BrowserWebSocket.cs | 19 +++++++++++++++++-- .../tests/AbortTest.cs | 10 +++++----- .../tests/ConnectTest.cs | 1 - 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs index 0eae170576a..b052020b11e 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs +++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/BrowserWebSockets/BrowserWebSocket.cs @@ -574,7 +574,15 @@ namespace System.Net.WebSockets _innerWebSocketCloseStatus = closeStatus; _innerWebSocketCloseStatusDescription = statusDescription; _innerWebSocket!.Invoke("close", (int)closeStatus, statusDescription); - _closeStatus = (int)_innerWebSocket.GetObjectProperty("readyState"); + if (_innerWebSocket != null && !_innerWebSocket.IsDisposed && _state != (int)InternalState.Aborted) + { + _closeStatus = (int)_innerWebSocket.GetObjectProperty("readyState"); + } + else + { + _closeStatus = 3; // (CLOSED) + } + return _tcsClose.Task; } catch (Exception exc) @@ -612,7 +620,14 @@ namespace System.Net.WebSockets _innerWebSocketCloseStatus = closeStatus; _innerWebSocketCloseStatusDescription = statusDescription; _innerWebSocket!.Invoke("close", (int)closeStatus, statusDescription); - _closeStatus = (int)_innerWebSocket.GetObjectProperty("readyState"); + if (_innerWebSocket != null && !_innerWebSocket.IsDisposed && _state != (int)InternalState.Aborted) + { + _closeStatus = (int)_innerWebSocket.GetObjectProperty("readyState"); + } + else + { + _closeStatus = 3; // (CLOSED) + } OnCloseCallback(null, cancellationToken); return Task.CompletedTask; } diff --git a/src/libraries/System.Net.WebSockets.Client/tests/AbortTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/AbortTest.cs index 6e9a60f0647..209a6f10ff2 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/AbortTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/AbortTest.cs @@ -15,7 +15,7 @@ namespace System.Net.WebSockets.Client.Tests { public AbortTest(ITestOutputHelper output) : base(output) { } - [OuterLoop] + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task Abort_ConnectAndAbort_ThrowsWebSocketExceptionWithmessage(Uri server) { @@ -40,7 +40,7 @@ namespace System.Net.WebSockets.Client.Tests } } - [OuterLoop] + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task Abort_SendAndAbort_Success(Uri server) { @@ -60,7 +60,7 @@ namespace System.Net.WebSockets.Client.Tests }, server); } - [OuterLoop] + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task Abort_ReceiveAndAbort_Success(Uri server) { @@ -84,7 +84,7 @@ namespace System.Net.WebSockets.Client.Tests }, server); } - [OuterLoop] + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task Abort_CloseAndAbort_Success(Uri server) { @@ -108,7 +108,7 @@ namespace System.Net.WebSockets.Client.Tests }, server); } - [OuterLoop] + [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task ClientWebSocket_Abort_CloseOutputAsync(Uri server) { diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index 0fb1314a606..296bffbc620 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -282,7 +282,6 @@ namespace System.Net.WebSockets.Client.Tests [ConditionalFact(nameof(WebSocketsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54152", TestPlatforms.Browser)] public async Task ConnectAsync_CancellationRequestedAfterConnect_ThrowsOperationCanceledException() { var releaseServer = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); From f57b6e7308e9db32e63488aa5e8f938934f54cf0 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 12 Jul 2021 08:55:14 -0700 Subject: [PATCH 441/926] Add crash report to createdump for Linux Watson (#55438) Add crash report to createdump for Linux Watson For Linux Watson the crash report will contains the .NET Core version, the faulting process name (the module/assembly containing Main) and the managed exception info (including the exception HRESULT) and thread stack trace of the thread the caused the crash. Add the CLRDATA_MODULE_IS_MAIN_MODULE flag to the DAC's IXCLRDataModule::GetFlags API. Add code to get the managed method name and write it out as "method_name" (even on MacOS). Add native frame symbolization (unmanaged_name) using dladdr. Only get the image info once and save in a global Demangle the stack frame symbols Only write PH_HDR_CANARY section if neccessary --- src/coreclr/debug/createdump/CMakeLists.txt | 1 + src/coreclr/debug/createdump/crashinfo.cpp | 87 ++++++++-- src/coreclr/debug/createdump/crashinfo.h | 11 +- src/coreclr/debug/createdump/crashinfomac.cpp | 36 ++++ .../debug/createdump/crashinfounix.cpp | 10 ++ .../debug/createdump/crashreportwriter.cpp | 164 +++++++++++------- .../debug/createdump/crashreportwriter.h | 6 +- src/coreclr/debug/createdump/createdump.h | 2 + src/coreclr/debug/createdump/datatarget.h | 4 + .../debug/createdump/dumpwriterelf.cpp | 35 ++-- src/coreclr/debug/createdump/dumpwriterelf.h | 4 + .../debug/createdump/dumpwritermacho.h | 4 + src/coreclr/debug/createdump/moduleinfo.h | 49 +++--- src/coreclr/debug/createdump/stackframe.h | 42 ++++- src/coreclr/debug/createdump/threadinfo.cpp | 18 +- src/coreclr/debug/createdump/threadinfo.h | 25 ++- .../debug/createdump/threadinfomac.cpp | 1 + .../debug/createdump/threadinfounix.cpp | 3 +- src/coreclr/debug/daccess/task.cpp | 11 +- src/coreclr/inc/xclrdata.idl | 1 + src/coreclr/pal/prebuilt/inc/xclrdata.h | 3 +- 21 files changed, 373 insertions(+), 144 deletions(-) diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 5d766d53da1..f0093b7cb66 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -86,6 +86,7 @@ endif(CLR_CMAKE_HOST_OSX) dbgutil # share the PAL in the dac module mscordaccore + dl ) add_dependencies(createdump mscordaccore) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index bc444815415..807036fd200 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -6,13 +6,17 @@ // This is for the PAL_VirtualUnwindOutOfProc read memory adapter. CrashInfo* g_crashInfo; +static bool ModuleInfoCompare(const ModuleInfo* lhs, const ModuleInfo* rhs) { return lhs->BaseAddress() < rhs->BaseAddress(); } + CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal) : m_ref(1), m_pid(pid), m_ppid(-1), + m_hdac(nullptr), m_gatherFrames(gatherFrames), m_crashThread(crashThread), - m_signal(signal) + m_signal(signal), + m_moduleInfos(&ModuleInfoCompare) { g_crashInfo = this; #ifdef __APPLE__ @@ -31,6 +35,20 @@ CrashInfo::~CrashInfo() delete thread; } m_threads.clear(); + + // Clean up the modules + for (ModuleInfo* module : m_moduleInfos) + { + delete module; + } + m_moduleInfos.clear(); + + // Unload DAC module + if (m_hdac != nullptr) + { + FreeLibrary(m_hdac); + m_hdac = nullptr; + } #ifdef __APPLE__ if (m_task != 0) { @@ -191,7 +209,6 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr; IXCLRDataProcess* pClrDataProcess = nullptr; - HMODULE hdac = nullptr; HRESULT hr = S_OK; bool result = false; @@ -205,13 +222,13 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) dacPath.append(MAKEDLLNAME_A("mscordaccore")); // Load and initialize the DAC - hdac = LoadLibraryA(dacPath.c_str()); - if (hdac == nullptr) + m_hdac = LoadLibraryA(dacPath.c_str()); + if (m_hdac == nullptr) { fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n", dacPath.c_str(), GetLastError()); goto exit; } - pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance"); + pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(m_hdac, "CLRDataCreateInstance"); if (pfnCLRDataCreateInstance == nullptr) { fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError()); @@ -262,10 +279,6 @@ exit: { pClrDataProcess->Release(); } - if (hdac != nullptr) - { - FreeLibrary(hdac); - } return result; } @@ -347,10 +360,13 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) bool CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) { + ReleaseHolder pSos = nullptr; + pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); + // For each native and managed thread for (ThreadInfo* thread : m_threads) { - if (!thread->UnwindThread(pClrDataProcess)) { + if (!thread->UnwindThread(pClrDataProcess, pSos)) { return false; } } @@ -426,9 +442,9 @@ CrashInfo::GetBaseAddressFromAddress(uint64_t address) uint64_t CrashInfo::GetBaseAddressFromName(const char* moduleName) { - for (const ModuleInfo& moduleInfo : m_moduleInfos) + for (const ModuleInfo* moduleInfo : m_moduleInfos) { - std::string name = GetFileName(moduleInfo.ModuleName()); + std::string name = GetFileName(moduleInfo->ModuleName()); #ifdef __APPLE__ // Module names are case insenstive on MacOS if (strcasecmp(name.c_str(), moduleName) == 0) @@ -436,7 +452,7 @@ CrashInfo::GetBaseAddressFromName(const char* moduleName) if (name.compare(moduleName) == 0) #endif { - return moduleInfo.BaseAddress(); + return moduleInfo->BaseAddress(); } } return 0; @@ -445,14 +461,14 @@ CrashInfo::GetBaseAddressFromName(const char* moduleName) // // Return the module info for the base address // -const ModuleInfo* +ModuleInfo* CrashInfo::GetModuleInfoFromBaseAddress(uint64_t baseAddress) { ModuleInfo search(baseAddress); - const auto& found = m_moduleInfos.find(search); + const auto& found = m_moduleInfos.find(&search); if (found != m_moduleInfos.end()) { - return &*found; + return *found; } return nullptr; } @@ -475,11 +491,12 @@ void CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName) { ModuleInfo moduleInfo(baseAddress); - const auto& found = m_moduleInfos.find(moduleInfo); + const auto& found = m_moduleInfos.find(&moduleInfo); if (found == m_moduleInfos.end()) { uint32_t timeStamp = 0; uint32_t imageSize = 0; + bool isMainModule = false; GUID mvid; if (isManaged) { @@ -511,11 +528,18 @@ CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* } if (pClrDataModule != nullptr) { + ULONG32 flags = 0; + pClrDataModule->GetFlags(&flags); + isMainModule = (flags & CLRDATA_MODULE_IS_MAIN_MODULE) != 0; pClrDataModule->GetVersionId(&mvid); } - TRACE("MODULE: timestamp %08x size %08x %s %s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), moduleName.c_str()); + TRACE("MODULE: timestamp %08x size %08x %s %s%s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), isMainModule ? "*" : "", moduleName.c_str()); } - m_moduleInfos.insert(ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName)); + ModuleInfo* moduleInfo = new ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName); + if (isMainModule) { + m_mainModule = moduleInfo; + } + m_moduleInfos.insert(moduleInfo); } } @@ -737,6 +761,31 @@ CrashInfo::TraceVerbose(const char* format, ...) } } +// +// Lookup a symbol in a module. The caller needs to call "free()" on symbol returned. +// +const char* +ModuleInfo::GetSymbolName(uint64_t address) +{ + LoadModule(); + + if (m_localBaseAddress != 0) + { + uint64_t localAddress = m_localBaseAddress + (address - m_baseAddress); + Dl_info info; + if (dladdr((void*)localAddress, &info) != 0) + { + if (info.dli_sname != nullptr) + { + int status = -1; + char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); + return status == 0 ? demangled : strdup(info.dli_sname); + } + } + } + return nullptr; +} + // // Returns just the file name portion of a file path // diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index f315b98dd28..199144e1754 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -46,6 +46,7 @@ private: pid_t m_pid; // pid pid_t m_ppid; // parent pid pid_t m_tgid; // process group + HMODULE m_hdac; // dac module handle when loaded bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info pid_t m_crashThread; // crashing thread id or 0 if none uint32_t m_signal; // crash signal code or 0 if none @@ -68,7 +69,12 @@ private: std::set m_otherMappings; // other memory mappings std::set m_memoryRegions; // memory regions from DAC, etc. std::set m_moduleAddresses; // memory region to module base address - std::set m_moduleInfos; // module infos (base address and module name) + std::set m_moduleInfos; // module infos (base address and module name) + ModuleInfo* m_mainModule; // the module containing "Main" + + // no public copy constructor + CrashInfo(const CrashInfo&) = delete; + void operator=(const CrashInfo&) = delete; public: CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal); @@ -82,7 +88,7 @@ public: bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory uint64_t GetBaseAddressFromAddress(uint64_t address); uint64_t GetBaseAddressFromName(const char* moduleName); - const ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress); + ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress); void AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress); void AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName); void InsertMemoryRegion(uint64_t address, size_t size); @@ -98,6 +104,7 @@ public: inline const pid_t CrashThread() const { return m_crashThread; } inline const uint32_t Signal() const { return m_signal; } inline const std::string& Name() const { return m_name; } + inline const ModuleInfo* MainModule() const { return m_mainModule; } inline const std::vector Threads() const { return m_threads; } inline const std::set ModuleMappings() const { return m_moduleMappings; } diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 4461d2a8c96..ad9c247e37d 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -380,3 +380,39 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r *read = numberOfBytesRead; return size == 0 || numberOfBytesRead > 0; } + +const struct dyld_all_image_infos* g_image_infos = nullptr; + +void +ModuleInfo::LoadModule() +{ + if (m_module == nullptr) + { + m_module = dlopen(m_moduleName.c_str(), RTLD_LAZY); + if (m_module != nullptr) + { + if (g_image_infos == nullptr) + { + struct task_dyld_info dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t result = task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); + if (result == KERN_SUCCESS) + { + g_image_infos = (const struct dyld_all_image_infos*)dyld_info.all_image_info_addr; + } + } + if (g_image_infos != nullptr) + { + for (int i = 0; i < g_image_infos->infoArrayCount; ++i) + { + const struct dyld_image_info* image = g_image_infos->infoArray + i; + if (strcasecmp(image->imageFilePath, m_moduleName.c_str()) == 0) + { + m_localBaseAddress = (uint64_t)image->imageLoadAddress; + break; + } + } + } + } + } +} diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 2cc1eb6c688..2f9605554ad 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -414,3 +414,13 @@ GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name) fclose(statusFile); return true; } + +void +ModuleInfo::LoadModule() +{ + if (m_module == nullptr) + { + m_module = dlopen(m_moduleName.c_str(), RTLD_LAZY); + m_localBaseAddress = ((struct link_map*)m_module)->l_addr; + } +} diff --git a/src/coreclr/debug/createdump/crashreportwriter.cpp b/src/coreclr/debug/createdump/crashreportwriter.cpp index 9cac899968f..40d8dbdba76 100644 --- a/src/coreclr/debug/createdump/crashreportwriter.cpp +++ b/src/coreclr/debug/createdump/crashreportwriter.cpp @@ -51,20 +51,19 @@ CrashReportWriter::WriteCrashReport(const std::string& dumpFileName) } } -#ifdef __APPLE__ - void CrashReportWriter::WriteCrashReport() { - const char* exceptionType = nullptr; OpenObject("payload"); - WriteValue("protocol_version", "0.0.7"); + WriteValue("protocol_version", "1.0.0"); OpenObject("configuration"); #if defined(__x86_64__) WriteValue("architecture", "amd64"); #elif defined(__aarch64__) WriteValue("architecture", "arm64"); +#elif defined(__arm__) + WriteValue("architecture", "arm"); #endif std::string version; assert(strncmp(sccsid, "@(#)Version ", 12) == 0); @@ -73,6 +72,13 @@ CrashReportWriter::WriteCrashReport() WriteValue("version", version.c_str()); CloseObject(); // configuration + // The main module was saved away in the crash info + if (m_crashInfo.MainModule()->BaseAddress() != 0) + { + WriteValue("process_name", GetFileName(m_crashInfo.MainModule()->ModuleName()).c_str()); + } + + const char* exceptionType = nullptr; OpenArray("threads"); for (const ThreadInfo* thread : m_crashInfo.Threads()) { @@ -131,6 +137,10 @@ CrashReportWriter::WriteCrashReport() { WriteValue("managed_exception_type", thread->ManagedExceptionType().c_str()); } + if (thread->ManagedExceptionHResult() != 0) + { + WriteValue32("managed_exception_hresult", thread->ManagedExceptionHResult()); + } WriteValue64("native_thread_id", thread->Tid()); OpenObject("ctx"); WriteValue64("IP", thread->GetInstructionPointer()); @@ -148,7 +158,7 @@ CrashReportWriter::WriteCrashReport() } CloseArray(); // threads CloseObject(); // payload - +#ifdef __APPLE__ OpenObject("parameters"); if (exceptionType != nullptr) { @@ -158,43 +168,10 @@ CrashReportWriter::WriteCrashReport() WriteSysctl("hw.model", "SystemModel"); WriteValue("SystemManufacturer", "apple"); CloseObject(); // parameters +#endif // __APPLE__ } -void -CrashReportWriter::WriteStackFrame(const StackFrame& frame) -{ - OpenObject(); - WriteValueBool("is_managed", frame.IsManaged()); - WriteValue64("module_address", frame.ModuleAddress()); - WriteValue64("stack_pointer", frame.StackPointer()); - WriteValue64("native_address", frame.ReturnAddress()); - WriteValue64("native_offset", frame.NativeOffset()); - if (frame.IsManaged()) - { - WriteValue32("token", frame.Token()); - WriteValue32("il_offset", frame.ILOffset()); - } - if (frame.ModuleAddress() != 0) - { - const ModuleInfo* moduleInfo = m_crashInfo.GetModuleInfoFromBaseAddress(frame.ModuleAddress()); - if (moduleInfo != nullptr) - { - std::string moduleName = GetFileName(moduleInfo->ModuleName()); - if (frame.IsManaged()) - { - WriteValue32("timestamp", moduleInfo->TimeStamp()); - WriteValue32("sizeofimage", moduleInfo->ImageSize()); - WriteValue("filename", moduleName.c_str()); - WriteValue("guid", FormatGuid(moduleInfo->Mvid()).c_str()); - } - else - { - WriteValue("native_module", moduleName.c_str()); - } - } - } - CloseObject(); -} +#ifdef __APPLE__ void CrashReportWriter::WriteSysctl(const char* sysctlname, const char* valueName) @@ -218,16 +195,62 @@ CrashReportWriter::WriteSysctl(const char* sysctlname, const char* valueName) } } -#else // __APPLE__ - -void -CrashReportWriter::WriteCrashReport() -{ -} - #endif // __APPLE__ -bool CrashReportWriter::OpenWriter(const char* fileName) +void +CrashReportWriter::WriteStackFrame(const StackFrame& frame) +{ + OpenObject(); + WriteValueBool("is_managed", frame.IsManaged()); + WriteValue64("module_address", frame.ModuleAddress()); + WriteValue64("stack_pointer", frame.StackPointer()); + WriteValue64("native_address", frame.InstructionPointer()); + WriteValue64("native_offset", frame.NativeOffset()); + if (frame.IsManaged()) + { + WriteValue32("token", frame.Token()); + WriteValue32("il_offset", frame.ILOffset()); + } + IXCLRDataMethodInstance* pMethod = frame.GetMethod(); + if (pMethod != nullptr) + { + ArrayHolder wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; + if (SUCCEEDED(pMethod->GetName(0, MAX_LONGPATH, nullptr, wszUnicodeName))) + { + std::string methodName = FormatString("%S", wszUnicodeName.GetPtr()); + WriteValue("method_name", methodName.c_str()); + } + } + if (frame.ModuleAddress() != 0) + { + ModuleInfo* moduleInfo = m_crashInfo.GetModuleInfoFromBaseAddress(frame.ModuleAddress()); + if (moduleInfo != nullptr) + { + std::string moduleName = GetFileName(moduleInfo->ModuleName()); + if (frame.IsManaged()) + { + WriteValue32("timestamp", moduleInfo->TimeStamp()); + WriteValue32("sizeofimage", moduleInfo->ImageSize()); + WriteValue("filename", moduleName.c_str()); + WriteValue("guid", FormatGuid(moduleInfo->Mvid()).c_str()); + } + else + { + const char* symbol = moduleInfo->GetSymbolName(frame.InstructionPointer()); + if (symbol != nullptr) + { + WriteValue("unmanaged_name", symbol); + free((void*)symbol); + } + WriteValue("native_module", moduleName.c_str()); + } + } + } + CloseObject(); +} + +bool +CrashReportWriter::OpenWriter(const char* fileName) { m_fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0664); if (m_fd == -1) @@ -239,13 +262,15 @@ bool CrashReportWriter::OpenWriter(const char* fileName) return true; } -void CrashReportWriter::CloseWriter() +void +CrashReportWriter::CloseWriter() { assert(m_indent == JSON_INDENT_VALUE); Write("\n}\n"); } -void CrashReportWriter::Write(const std::string& text) +void +CrashReportWriter::Write(const std::string& text) { if (!DumpWriter::WriteData(m_fd, (void*)text.c_str(), text.length())) { @@ -253,19 +278,22 @@ void CrashReportWriter::Write(const std::string& text) } } -void CrashReportWriter::Write(const char* buffer) +void +CrashReportWriter::Write(const char* buffer) { std::string text(buffer); Write(text); } -void CrashReportWriter::Indent(std::string& text) +void +CrashReportWriter::Indent(std::string& text) { assert(m_indent >= 0); text.append(m_indent, ' '); } -void CrashReportWriter::WriteSeperator(std::string& text) +void +CrashReportWriter::WriteSeperator(std::string& text) { if (m_comma) { @@ -275,7 +303,8 @@ void CrashReportWriter::WriteSeperator(std::string& text) Indent(text); } -void CrashReportWriter::OpenValue(const char* key, char marker) +void +CrashReportWriter::OpenValue(const char* key, char marker) { std::string text; WriteSeperator(text); @@ -292,7 +321,8 @@ void CrashReportWriter::OpenValue(const char* key, char marker) Write(text); } -void CrashReportWriter::CloseValue(char marker) +void +CrashReportWriter::CloseValue(char marker) { std::string text; text.append(1, '\n'); @@ -304,7 +334,8 @@ void CrashReportWriter::CloseValue(char marker) Write(text); } -void CrashReportWriter::WriteValue(const char* key, const char* value) +void +CrashReportWriter::WriteValue(const char* key, const char* value) { std::string text; WriteSeperator(text); @@ -317,41 +348,48 @@ void CrashReportWriter::WriteValue(const char* key, const char* value) Write(text); } -void CrashReportWriter::WriteValueBool(const char* key, bool value) +void +CrashReportWriter::WriteValueBool(const char* key, bool value) { WriteValue(key, value ? "true" : "false"); } -void CrashReportWriter::WriteValue32(const char* key, uint32_t value) +void +CrashReportWriter::WriteValue32(const char* key, uint32_t value) { char buffer[16]; snprintf(buffer, sizeof(buffer), "0x%x", value); WriteValue(key, buffer); } -void CrashReportWriter::WriteValue64(const char* key, uint64_t value) +void +CrashReportWriter::WriteValue64(const char* key, uint64_t value) { char buffer[32]; snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value); WriteValue(key, buffer); } -void CrashReportWriter::OpenObject(const char* key) +void +CrashReportWriter::OpenObject(const char* key) { OpenValue(key, '{'); } -void CrashReportWriter::CloseObject() +void +CrashReportWriter::CloseObject() { CloseValue('}'); } -void CrashReportWriter::OpenArray(const char* key) +void +CrashReportWriter::OpenArray(const char* key) { OpenValue(key, '['); } -void CrashReportWriter::CloseArray() +void +CrashReportWriter::CloseArray() { CloseValue(']'); } diff --git a/src/coreclr/debug/createdump/crashreportwriter.h b/src/coreclr/debug/createdump/crashreportwriter.h index ef77bfcac55..e5f0f618d94 100644 --- a/src/coreclr/debug/createdump/crashreportwriter.h +++ b/src/coreclr/debug/createdump/crashreportwriter.h @@ -13,6 +13,10 @@ private: bool m_comma; CrashInfo& m_crashInfo; + // no public copy constructor + CrashReportWriter(const CrashReportWriter&) = delete; + void operator=(const CrashReportWriter&) = delete; + public: CrashReportWriter(CrashInfo& crashInfo); virtual ~CrashReportWriter(); @@ -21,9 +25,9 @@ public: private: void WriteCrashReport(); #ifdef __APPLE__ - void WriteStackFrame(const StackFrame& frame); void WriteSysctl(const char* sysctlname, const char* valueName); #endif + void WriteStackFrame(const StackFrame& frame); void Write(const std::string& text); void Write(const char* buffer); void Indent(std::string& text); diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index 95f63f460e4..f588867c792 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -71,6 +71,8 @@ typedef int T_CONTEXT; #endif #include #include +#include +#include #ifdef __APPLE__ #include #else diff --git a/src/coreclr/debug/createdump/datatarget.h b/src/coreclr/debug/createdump/datatarget.h index 954ff5328b4..792947bafe2 100644 --- a/src/coreclr/debug/createdump/datatarget.h +++ b/src/coreclr/debug/createdump/datatarget.h @@ -9,6 +9,10 @@ private: LONG m_ref; // reference count CrashInfo& m_crashInfo; + // no public copy constructor + DumpDataTarget(const DumpDataTarget&) = delete; + void operator=(const DumpDataTarget&) = delete; + public: DumpDataTarget(CrashInfo& crashInfo); virtual ~DumpDataTarget(); diff --git a/src/coreclr/debug/createdump/dumpwriterelf.cpp b/src/coreclr/debug/createdump/dumpwriterelf.cpp index 57249d83f7e..afd403212b2 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.cpp +++ b/src/coreclr/debug/createdump/dumpwriterelf.cpp @@ -32,12 +32,10 @@ DumpWriter::WriteDump() ehdr.e_type = ET_CORE; ehdr.e_machine = ELF_ARCH; ehdr.e_version = EV_CURRENT; - ehdr.e_shoff = sizeof(Ehdr); - ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr); + ehdr.e_phoff = sizeof(Ehdr); ehdr.e_ehsize = sizeof(Ehdr); ehdr.e_phentsize = sizeof(Phdr); - ehdr.e_shentsize = sizeof(Shdr); // The ELF header only allows UINT16 for the number of program // headers. In a core dump this equates to PT_NODE and PT_LOAD. @@ -60,26 +58,33 @@ DumpWriter::WriteDump() } else { ehdr.e_phnum = PH_HDR_CANARY; + ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr); + ehdr.e_shnum = 1; + ehdr.e_shoff = sizeof(Ehdr); + ehdr.e_shentsize = sizeof(Shdr); } if (!WriteData(&ehdr, sizeof(Ehdr))) { return false; } - size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr)); + size_t offset = sizeof(Ehdr) + (phnum * sizeof(Phdr)); size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize(); - // Add single section containing the actual count - // of the program headers to be written. - Shdr shdr; - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_info = phnum; - // When section header offset is present but ehdr section num = 0 - // then is is expected that the sh_size indicates the size of the - // section array or 1 in our case. - shdr.sh_size = 1; - if (!WriteData(&shdr, sizeof(shdr))) { - return false; + if (ehdr.e_phnum == PH_HDR_CANARY) + { + // Add single section containing the actual count of the program headers to be written. + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_info = phnum; + shdr.sh_size = 1; + offset += sizeof(Shdr); + + // When section header offset is present but ehdr section num = 0 then is is expected that + // the sh_size indicates the size of the section array or 1 in our case. + if (!WriteData(&shdr, sizeof(shdr))) { + return false; + } } // PT_NOTE header diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h index ac4dc10fd2f..6da55da2f13 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.h +++ b/src/coreclr/debug/createdump/dumpwriterelf.h @@ -36,6 +36,10 @@ private: CrashInfo& m_crashInfo; BYTE m_tempBuffer[0x4000]; + // no public copy constructor + DumpWriter(const DumpWriter&) = delete; + void operator=(const DumpWriter&) = delete; + public: DumpWriter(CrashInfo& crashInfo); virtual ~DumpWriter(); diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h index 6be2aa7742b..704ea084882 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.h +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -30,6 +30,10 @@ private: std::vector m_threadLoadCommands; BYTE m_tempBuffer[0x4000]; + // no public copy constructor + DumpWriter(const DumpWriter&) = delete; + void operator=(const DumpWriter&) = delete; + public: DumpWriter(CrashInfo& crashInfo); virtual ~DumpWriter(); diff --git a/src/coreclr/debug/createdump/moduleinfo.h b/src/coreclr/debug/createdump/moduleinfo.h index f07e50d592f..2876562fba2 100644 --- a/src/coreclr/debug/createdump/moduleinfo.h +++ b/src/coreclr/debug/createdump/moduleinfo.h @@ -10,10 +10,27 @@ private: GUID m_mvid; std::string m_moduleName; bool m_isManaged; + void* m_module; + uint64_t m_localBaseAddress; + + // no public copy constructor + ModuleInfo(const ModuleInfo&) = delete; + void operator=(const ModuleInfo&) = delete; + + void LoadModule(); public: + ModuleInfo() : + m_baseAddress(0), + m_module(nullptr), + m_localBaseAddress(0) + { + } + ModuleInfo(uint64_t baseAddress) : - m_baseAddress(baseAddress) + m_baseAddress(baseAddress), + m_module(nullptr), + m_localBaseAddress(0) { } @@ -23,23 +40,19 @@ public: m_imageSize(imageSize), m_mvid(*mvid), m_moduleName(moduleName), - m_isManaged(isManaged) - { - } - - // copy constructor - ModuleInfo(const ModuleInfo& moduleInfo) : - m_baseAddress(moduleInfo.m_baseAddress), - m_timeStamp(moduleInfo.m_timeStamp), - m_imageSize(moduleInfo.m_imageSize), - m_mvid(moduleInfo.m_mvid), - m_moduleName(moduleInfo.m_moduleName), - m_isManaged(moduleInfo.m_isManaged) + m_isManaged(isManaged), + m_module(nullptr), + m_localBaseAddress(0) { } ~ModuleInfo() { + if (m_module != nullptr) + { + dlclose(m_module); + m_module = nullptr; + } } inline bool IsManaged() const { return m_isManaged; } @@ -49,13 +62,5 @@ public: inline const GUID* Mvid() const { return &m_mvid; } inline const std::string& ModuleName() const { return m_moduleName; } - bool operator<(const ModuleInfo& rhs) const - { - return m_baseAddress < rhs.m_baseAddress; - } - - void Trace() const - { - TRACE("%" PRIA PRIx64 " %s\n", m_baseAddress, m_moduleName.c_str()); - } + const char* GetSymbolName(uint64_t address); }; diff --git a/src/coreclr/debug/createdump/stackframe.h b/src/coreclr/debug/createdump/stackframe.h index 90db2697b81..75e20d93120 100644 --- a/src/coreclr/debug/createdump/stackframe.h +++ b/src/coreclr/debug/createdump/stackframe.h @@ -5,45 +5,75 @@ struct StackFrame { private: uint64_t m_moduleAddress; - uint64_t m_returnAddress; + uint64_t m_instructionPointer; uint64_t m_stackPointer; uint32_t m_nativeOffset; uint32_t m_token; uint32_t m_ilOffset; + IXCLRDataMethodInstance* m_pMethod; bool m_isManaged; public: // Create native stack frame - StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset) : + StackFrame(uint64_t moduleAddress, uint64_t instructionPointer, uint64_t stackPointer, uint32_t nativeOffset) : m_moduleAddress(moduleAddress), - m_returnAddress(returnAddress), + m_instructionPointer(instructionPointer), m_stackPointer(stackPointer), m_nativeOffset(nativeOffset), m_token(0), m_ilOffset(0), + m_pMethod(nullptr), m_isManaged(false) { } // Create managed stack frame - StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset, uint64_t token, uint32_t ilOffset) : + StackFrame(uint64_t moduleAddress, uint64_t instructionPointer, uint64_t stackPointer, IXCLRDataMethodInstance* pMethod, uint32_t nativeOffset, uint64_t token, uint32_t ilOffset) : m_moduleAddress(moduleAddress), - m_returnAddress(returnAddress), + m_instructionPointer(instructionPointer), m_stackPointer(stackPointer), m_nativeOffset(nativeOffset), m_token(token), m_ilOffset(ilOffset), + m_pMethod(pMethod), m_isManaged(true) { } + // copy constructor + StackFrame(const StackFrame& frame) : + m_moduleAddress(frame.m_moduleAddress), + m_instructionPointer(frame.m_instructionPointer), + m_stackPointer(frame.m_stackPointer), + m_nativeOffset(frame.m_nativeOffset), + m_token(frame.m_token), + m_ilOffset(frame.m_ilOffset), + m_pMethod(frame.m_pMethod), + m_isManaged(frame.m_isManaged) + { + if (m_pMethod != nullptr) + { + m_pMethod->AddRef(); + } + } + + ~StackFrame() + { + if (m_pMethod != nullptr) + { + m_pMethod->Release(); + m_pMethod = nullptr; + } + } + inline uint64_t ModuleAddress() const { return m_moduleAddress; } - inline uint64_t ReturnAddress() const { return m_returnAddress; } + inline uint64_t InstructionPointer() const { return m_instructionPointer; } inline uint64_t StackPointer() const { return m_stackPointer; } inline uint32_t NativeOffset() const { return m_nativeOffset; } inline uint32_t Token() const { return m_token; } inline uint32_t ILOffset() const { return m_ilOffset; } inline bool IsManaged() const { return m_isManaged; } + inline IXCLRDataMethodInstance* GetMethod() const { return m_pMethod; } bool operator<(const StackFrame& rhs) const { diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 99284ed0402..2107c6c1baf 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -107,7 +107,7 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) } bool -ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) +ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos) { TRACE("Unwind: thread %04x\n", Tid()); @@ -152,7 +152,15 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess) if (SUCCEEDED(pExceptionValue->GetAddress(&exceptionObject))) { m_exceptionObject = exceptionObject; - TRACE("Unwind: exception object %p\n", (void*)exceptionObject); + if (pSos != nullptr) + { + DacpExceptionObjectData exceptionData; + if (SUCCEEDED(exceptionData.Request(pSos, exceptionObject))) + { + m_exceptionHResult = exceptionData.HResult; + } + } + TRACE("Unwind: exception object %p exception hresult %08x\n", (void*)m_exceptionObject, m_exceptionHResult); } ReleaseHolder pExceptionType; if (SUCCEEDED(pExceptionValue->GetType(&pExceptionType))) @@ -202,6 +210,7 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk) mdMethodDef token = 0; uint32_t nativeOffset = 0; uint32_t ilOffset = 0; + ReleaseHolder pMethod; ReleaseHolder pFrame; if (SUCCEEDED(pStackwalk->GetFrame(&pFrame))) @@ -212,7 +221,6 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk) if ((simpleType & (CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE)) != 0) { - ReleaseHolder pMethod; if (SUCCEEDED(pFrame->GetMethodInstance(&pMethod))) { ReleaseHolder pModule; @@ -258,7 +266,7 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk) } // Add managed stack frame for the crash info notes - StackFrame frame(moduleAddress, ip, sp, nativeOffset, token, ilOffset); + StackFrame frame(moduleAddress, ip, sp, pMethod.Detach(), nativeOffset, token, ilOffset); AddStackFrame(frame); } @@ -270,7 +278,7 @@ ThreadInfo::AddStackFrame(const StackFrame& frame) { TRACE("Unwind: sp %p ip %p off %08x mod %p%c\n", (void*)frame.StackPointer(), - (void*)frame.ReturnAddress(), + (void*)frame.InstructionPointer(), frame.NativeOffset(), (void*)frame.ModuleAddress(), frame.IsManaged() ? '*' : ' '); diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h index 1a690157908..7ce0df5f1ec 100644 --- a/src/coreclr/debug/createdump/threadinfo.h +++ b/src/coreclr/debug/createdump/threadinfo.h @@ -42,7 +42,8 @@ private: pid_t m_tgid; // thread group bool m_managed; // if true, thread has managed code running uint64_t m_exceptionObject; // exception object address - std::string m_exceptionType; // exception type + std::string m_exceptionType; // exception type + int32_t m_exceptionHResult; // exception HRESULT std::set m_frames; // stack frames #ifdef __APPLE__ @@ -64,6 +65,10 @@ private: #endif #endif // __APPLE__ + // no public copy constructor + ThreadInfo(const ThreadInfo&) = delete; + void operator=(const ThreadInfo&) = delete; + public: #ifdef __APPLE__ ThreadInfo(CrashInfo& crashInfo, pid_t tid, mach_port_t port); @@ -73,7 +78,7 @@ public: #endif ~ThreadInfo(); bool Initialize(); - bool UnwindThread(IXCLRDataProcess* pClrDataProcess); + bool UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos); void GetThreadStack(); void GetThreadContext(uint32_t flags, CONTEXT* context) const; @@ -83,6 +88,7 @@ public: inline bool IsManaged() const { return m_managed; } inline uint64_t ManagedExceptionObject() const { return m_exceptionObject; } + inline int32_t ManagedExceptionHResult() const { return m_exceptionHResult; } inline std::string ManagedExceptionType() const { return m_exceptionType; } inline const std::set StackFrames() const { return m_frames; } @@ -108,16 +114,19 @@ public: #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__) inline const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; } #endif - inline const uint64_t GetStackPointer() const - { #if defined(__x86_64__) - return m_gpRegisters.rsp; + inline const uint64_t GetInstructionPointer() const { return m_gpRegisters.rip; } + inline const uint64_t GetStackPointer() const { return m_gpRegisters.rsp; } + inline const uint64_t GetFramePointer() const { return m_gpRegisters.rbp; } #elif defined(__aarch64__) - return MCREG_Sp(m_gpRegisters); + inline const uint64_t GetInstructionPointer() const { return MCREG_Pc(m_gpRegisters); } + inline const uint64_t GetStackPointer() const { return MCREG_Sp(m_gpRegisters); } + inline const uint64_t GetFramePointer() const { return MCREG_Fp(m_gpRegisters); } #elif defined(__arm__) - return m_gpRegisters.ARM_sp; + inline const uint64_t GetInstructionPointer() const { return m_gpRegisters.ARM_pc; } + inline const uint64_t GetStackPointer() const { return m_gpRegisters.ARM_sp; } + inline const uint64_t GetFramePointer() const { return m_gpRegisters.ARM_fp; } #endif - } #endif // __APPLE__ private: diff --git a/src/coreclr/debug/createdump/threadinfomac.cpp b/src/coreclr/debug/createdump/threadinfomac.cpp index 3ea9151a649..92b9339088f 100644 --- a/src/coreclr/debug/createdump/threadinfomac.cpp +++ b/src/coreclr/debug/createdump/threadinfomac.cpp @@ -8,6 +8,7 @@ ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid, mach_port_t port) : m_tid(tid), m_managed(false), m_exceptionObject(0), + m_exceptionHResult(0), m_port(port) { } diff --git a/src/coreclr/debug/createdump/threadinfounix.cpp b/src/coreclr/debug/createdump/threadinfounix.cpp index c1e5ca1154c..90a4c8ab9ff 100644 --- a/src/coreclr/debug/createdump/threadinfounix.cpp +++ b/src/coreclr/debug/createdump/threadinfounix.cpp @@ -26,7 +26,8 @@ ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid) : m_crashInfo(crashInfo), m_tid(tid), m_managed(false), - m_exceptionObject(0) + m_exceptionObject(0), + m_exceptionHResult(0) { } diff --git a/src/coreclr/debug/daccess/task.cpp b/src/coreclr/debug/daccess/task.cpp index b16f85d8773..c3143bf243b 100644 --- a/src/coreclr/debug/daccess/task.cpp +++ b/src/coreclr/debug/daccess/task.cpp @@ -2503,7 +2503,16 @@ ClrDataModule::GetFlags( { (*flags) |= CLRDATA_MODULE_IS_MEMORY_STREAM; } - + PTR_Assembly pAssembly = m_module->GetAssembly(); + PTR_BaseDomain pBaseDomain = pAssembly->GetDomain(); + if (pBaseDomain->IsAppDomain()) + { + AppDomain* pAppDomain = pBaseDomain->AsAppDomain(); + if (pAssembly == pAppDomain->GetRootAssembly()) + { + (*flags) |= CLRDATA_MODULE_IS_MAIN_MODULE; + } + } status = S_OK; } EX_CATCH diff --git a/src/coreclr/inc/xclrdata.idl b/src/coreclr/inc/xclrdata.idl index 818915a29cc..aeddf9529be 100644 --- a/src/coreclr/inc/xclrdata.idl +++ b/src/coreclr/inc/xclrdata.idl @@ -1014,6 +1014,7 @@ typedef enum CLRDATA_MODULE_DEFAULT = 0x00000000, CLRDATA_MODULE_IS_DYNAMIC = 0x00000001, CLRDATA_MODULE_IS_MEMORY_STREAM = 0x00000002, + CLRDATA_MODULE_IS_MAIN_MODULE = 0x00000004, } CLRDataModuleFlag; typedef enum diff --git a/src/coreclr/pal/prebuilt/inc/xclrdata.h b/src/coreclr/pal/prebuilt/inc/xclrdata.h index dbee62b9b12..b28463dc86c 100644 --- a/src/coreclr/pal/prebuilt/inc/xclrdata.h +++ b/src/coreclr/pal/prebuilt/inc/xclrdata.h @@ -3300,7 +3300,8 @@ enum __MIDL___MIDL_itf_xclrdata_0000_0008_0001 { CLRDATA_MODULE_DEFAULT = 0, CLRDATA_MODULE_IS_DYNAMIC = 0x1, - CLRDATA_MODULE_IS_MEMORY_STREAM = 0x2 + CLRDATA_MODULE_IS_MEMORY_STREAM = 0x2, + CLRDATA_MODULE_IS_MAIN_MODULE = 0x4 } CLRDataModuleFlag; typedef /* [public][public][public] */ From 9fb28c806a40bafb5a579dd2218558ea0b9bf569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 12 Jul 2021 12:40:16 -0400 Subject: [PATCH 442/926] [hot_reload] Add support for row modifications; CustomAttribute updates (#55445) This fixes https://github.com/dotnet/runtime/issues/55097 - which allows us to support C# nullability analysis once again in hot reload deltas. Specifically we allow EnC deltas to include modifications of existing rows in the CustomAttribute table as long as the Parent and Type columns stay the same (that is: a custom attribute modification that still applies to the same element - and uses the same custom attribute constructor, but may have a different value). To support this, we add a new BaselineInfo:any_modified_rows array that keeps track for each table whether any rows have been modified (as opposed to added) by any EnC delta. When the runtime calls mono_metadata_decode_row, if there have been any deltas that modified a row in the requested table, we call hot_reload_effective_table_slow which find the latest delta that modified that row. If there have only been additions, we stop at the first delta that added the row we're looking for, if there are modifications, we look through all the deltas and return the latest one. * [hot_reload] Add test for updating custom attribute ctor values Just changing the arguments of a custom attribute constructor should generate an update to the CustomAttributes table with the same parent and .ctor. That kind of change should be allowed by Mono and CoreCLR * [hot_reload] Allow custom attribute row modifications if Parent and Type unchanged. Allows updates to the constructor arguments (or property values) * [hot_reload] Implement table lookup of modified rows Add a bool array on the base image to keep track of whether each table had any row modifications (as opposed to row additions) in any generation. If there was a modification, take the slow path in mono_image_effective_table even if the index we're looking at is in the base image. Update hot_reload_effective_table_slow so that if there was a modified row, we look at all the deltas to see if there's an even later update to that row. (When there are only additions, keep same behavior as before - only look as far as the generation that added the row we wanted to find). Also refine the assertion in hot_reload_relative_delta_index to properly account for EnCMap entries that correspond to modifications - in that case we might stop searching a metadata delta before we hit the end of the table if the EnCmap entries start pointing to rows that are past the one we wanted to look up. * Update the CustomAttributeUpdates test to check attribute value Check that we get the updated custom attribute string property value. * Re-enable nullability for hot reload tests Mono can now deal with the custom attributes modifications that Roslyn emits --- .../CustomAttributeUpdate.cs | 35 ++++ .../CustomAttributeUpdate_v1.cs | 35 ++++ ...lyUpdate.Test.CustomAttributeUpdate.csproj | 11 ++ .../deltascript.json | 6 + ...tadata.ApplyUpdate.Test.MethodBody1.csproj | 1 - .../tests/ApplyUpdateTest.cs | 29 +++ .../tests/System.Runtime.Loader.Tests.csproj | 4 + src/mono/mono/component/hot_reload-stub.c | 9 + src/mono/mono/component/hot_reload.c | 173 +++++++++++++----- src/mono/mono/component/hot_reload.h | 2 + src/mono/mono/metadata/metadata-internals.h | 5 +- src/mono/mono/metadata/metadata-update.c | 5 + .../ApplyUpdateReferencedAssembly.csproj | 1 - 13 files changed, 270 insertions(+), 46 deletions(-) create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate.cs create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate_v1.cs create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.csproj create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/deltascript.json diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate.cs new file mode 100644 index 00000000000..f1590848bea --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + [AttributeUsage (AttributeTargets.Method, AllowMultiple=true)] + public class MyAttribute : Attribute + { + public MyAttribute (string stringValue) { StringValue = stringValue; } + + public MyAttribute (Type typeValue) { TypeValue = typeValue; } + + public MyAttribute (int x) { IntValue = x; } + + public string StringValue { get; set; } + public Type TypeValue {get; set; } + public int IntValue {get; set; } + } + + public class ClassWithCustomAttributeUpdates + { + [MyAttribute ("abcd")] + public static string Method1 () => null; + + [MyAttribute (typeof(Exception))] + public static string Method2 () => null; + + [MyAttribute (42, StringValue = "hijkl", TypeValue = typeof(Type))] + [MyAttribute (17, StringValue = "", TypeValue = typeof(object))] + public static string Method3 () => null; + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate_v1.cs new file mode 100644 index 00000000000..516914d76d8 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/CustomAttributeUpdate_v1.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + [AttributeUsage (AttributeTargets.Method, AllowMultiple=true)] + public class MyAttribute : Attribute + { + public MyAttribute (string stringValue) { StringValue = stringValue; } + + public MyAttribute (Type typeValue) { TypeValue = typeValue; } + + public MyAttribute (int x) { IntValue = x; } + + public string StringValue { get; set; } + public Type TypeValue {get; set; } + public int IntValue {get; set; } + } + + public class ClassWithCustomAttributeUpdates + { + [MyAttribute ("rstuv")] + public static string Method1 () => null; + + [MyAttribute (typeof(ArgumentException))] + public static string Method2 () => null; + + [MyAttribute (2042, StringValue = "qwerty", TypeValue = typeof(byte[]))] + [MyAttribute (17, StringValue = "", TypeValue = typeof(object))] + public static string Method3 () => null; + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.csproj new file mode 100644 index 00000000000..985424ab348 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.csproj @@ -0,0 +1,11 @@ + + + System.Runtime.Loader.Tests + $(NetCoreAppCurrent) + true + deltascript.json + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/deltascript.json new file mode 100644 index 00000000000..3dcb95912db --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "CustomAttributeUpdate.cs", "update": "CustomAttributeUpdate_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj index 87a4a28d000..57ba4f3ec52 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj @@ -5,7 +5,6 @@ true deltascript.json true - disable diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index c2f06b86b93..6976f8a6f77 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -83,6 +83,35 @@ namespace System.Reflection.Metadata }); } + [ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))] + public void CustomAttributeUpdates() + { + // Test that _modifying_ custom attribute constructor/property argumments works as expected. + // For this test, we don't change which constructor is called, or how many custom attributes there are. + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.ClassWithCustomAttributeUpdates).Assembly; + + ApplyUpdateUtil.ApplyUpdate(assm); + ApplyUpdateUtil.ClearAllReflectionCaches(); + + // Just check the updated value on one method + + Type attrType = typeof(System.Reflection.Metadata.ApplyUpdate.Test.MyAttribute); + Type ty = assm.GetType("System.Reflection.Metadata.ApplyUpdate.Test.ClassWithCustomAttributeUpdates"); + Assert.NotNull(ty); + MethodInfo mi = ty.GetMethod(nameof(System.Reflection.Metadata.ApplyUpdate.Test.ClassWithCustomAttributeUpdates.Method1), BindingFlags.Public | BindingFlags.Static); + Assert.NotNull(mi); + var cattrs = Attribute.GetCustomAttributes(mi, attrType); + Assert.NotNull(cattrs); + Assert.Equal(1, cattrs.Length); + Assert.NotNull(cattrs[0]); + Assert.Equal(attrType, cattrs[0].GetType()); + string p = (cattrs[0] as System.Reflection.Metadata.ApplyUpdate.Test.MyAttribute).StringValue; + Assert.Equal("rstuv", p); + }); + } + class NonRuntimeAssembly : Assembly { } diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index eeae81e75cc..a5f2b3b0cce 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -39,6 +39,7 @@ + @@ -58,6 +59,9 @@ + diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 718ce3e453a..48033c1c7a0 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -59,6 +59,9 @@ hot_reload_stub_table_bounds_check (MonoImage *base_image, int table_index, int static gboolean hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gboolean +hot_reload_stub_has_modified_rows (const MonoTableInfo *table); + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_stub_available }, &hot_reload_stub_set_fastpath_data, @@ -75,6 +78,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_updated_method_rva, &hot_reload_stub_table_bounds_check, &hot_reload_stub_delta_heap_lookup, + &hot_reload_stub_has_modified_rows, }; static bool @@ -172,6 +176,11 @@ hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc g_assert_not_reached (); } +static gboolean +hot_reload_stub_has_modified_rows (const MonoTableInfo *table){ + return FALSE; +} + MONO_COMPONENT_EXPORT_ENTRYPOINT MonoComponentHotReload * mono_component_hot_reload_init (void) diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 0997e8f859c..76861056eda 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -73,6 +73,9 @@ hot_reload_table_bounds_check (MonoImage *base_image, int table_index, int token static gboolean hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gboolean +hot_reload_has_modified_rows (const MonoTableInfo *table); + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_available }, &hot_reload_set_fastpath_data, @@ -89,6 +92,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_updated_method_rva, &hot_reload_table_bounds_check, &hot_reload_delta_heap_lookup, + &hot_reload_has_modified_rows, }; MonoComponentHotReload * @@ -161,6 +165,9 @@ typedef struct _BaselineInfo { /* Maps MethodDef token indices to a boolean flag that there's an update for the method */ GHashTable *method_table_update; + + /* TRUE if any published update modified an existing row */ + gboolean any_modified_rows [MONO_TABLE_NUM]; } BaselineInfo; #define DOTNET_MODIFIABLE_ASSEMBLIES "DOTNET_MODIFIABLE_ASSEMBLIES" @@ -419,6 +426,28 @@ table_info_get_base_image (const MonoTableInfo *t) return image; } +/* Given a table, find the base image that it came from and its table index */ +static gboolean +table_info_find_in_base (const MonoTableInfo *table, MonoImage **base_out, int *tbl_index) +{ + g_assert (base_out); + *base_out = NULL; + MonoImage *base = table_info_get_base_image (table); + if (!base) + return FALSE; + + *base_out = base; + + /* Invariant: `table` must be a `MonoTableInfo` of the base image. */ + g_assert (base->tables < table && table < &base->tables [MONO_TABLE_LAST]); + + if (tbl_index) { + size_t s = ALIGN_TO (sizeof (MonoTableInfo), sizeof (gpointer)); + *tbl_index = ((intptr_t) table - (intptr_t) base->tables) / s; + } + return TRUE; +} + static MonoImage* image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gconstpointer dmeta_bytes, uint32_t dmeta_length); @@ -724,67 +753,66 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) void hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx) { - if (G_LIKELY (*idx < table_info_get_rows (*t))) - return; - /* FIXME: don't let any thread other than the updater thread see values from a delta image * with a generation past update_published */ - MonoImage *base = table_info_get_base_image (*t); - if (!base) + MonoImage *base; + int tbl_index; + if (!table_info_find_in_base (*t, &base, &tbl_index)) return; BaselineInfo *info = baseline_info_lookup (base); if (!info) return; + gboolean any_modified = info->any_modified_rows[tbl_index]; + + if (G_LIKELY (*idx < table_info_get_rows (*t) && !any_modified)) + return; + GList *list = info->delta_image; MonoImage *dmeta; int ridx; MonoTableInfo *table; - - /* Invariant: `*t` must be a `MonoTableInfo` of the base image. */ - g_assert (base->tables < *t && *t < &base->tables [MONO_TABLE_LAST]); - - size_t s = ALIGN_TO (sizeof (MonoTableInfo), sizeof (gpointer)); - int tbl_index = ((intptr_t) *t - (intptr_t) base->tables) / s; - - /* FIXME: I don't understand how ReplaceMethodOften works - it always has a - * EnCMap entry 2: 0x06000002 (MethodDef) for every revision. Shouldn't the number of methodDef rows be going up? - - * Apparently not - because conceptually the EnC log is saying to overwrite the existing rows. - */ - - /* FIXME: so if the tables are conceptually mutated by each delta, we can't just stop at the - * first lookup that gets a relative index in the right range, can we? that will always be - * the oldest delta. - */ - - /* FIXME: the other problem is that the EnClog is a sequence of actions to MUTATE rows. So when looking up an existing row we have to be able to make it so that naive callers decoding that row see the updated data. - * - * That's the main thing that PAss1 should eb doing for us. - * - * I think we can't get away from mutating. The format is just too geared toward it. - * - * We should make the mutations atomic, though. (And I guess the heap extension is probably unavoidable) - * - * 1. Keep a table of inv - */ int g = 0; + /* Candidate: the last delta that had updates for the requested row */ + MonoImage *cand_dmeta = NULL; + MonoTableInfo *cand_table = NULL; + int cand_ridx = -1; + int cand_g = 0; + + gboolean cont; do { g_assertf (list, "couldn't find idx=0x%08x in assembly=%s", *idx, dmeta && dmeta->name ? dmeta->name : "unknown image"); dmeta = (MonoImage*)list->data; list = list->next; table = &dmeta->tables [tbl_index]; - ridx = hot_reload_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)) - 1; + int rel_row = hot_reload_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)); + g_assert (rel_row == -1 || (rel_row > 0 && rel_row <= table_info_get_rows (table))); g++; - } while (ridx < 0 || ridx >= table_info_get_rows (table)); + if (rel_row != -1) { + cand_dmeta = dmeta; + cand_table = table; + cand_ridx = rel_row - 1; + cand_g = g; + } + ridx = rel_row - 1; + if (!any_modified) { + /* if the table only got additions, not modifications, don't continue after we find the first image that has the right number of rows */ + cont = ridx < 0 || ridx >= table_info_get_rows (table); + } else { + /* otherwise, keep going in case a later generation modified the row again */ + cont = list != NULL; + } + } while (cont); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "effective table for %s: 0x%08x -> 0x%08x (rows = 0x%08x) (gen %d, g %d)", mono_meta_table_name (tbl_index), *idx, ridx, table_info_get_rows (table), metadata_update_local_generation (base, info, dmeta), g); + if (cand_ridx != -1) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "effective table for %s: 0x%08x -> 0x%08x (rows = 0x%08x) (gen %d, g %d)", mono_meta_table_name (tbl_index), *idx, cand_ridx, table_info_get_rows (cand_table), metadata_update_local_generation (base, info, cand_dmeta), cand_g); - *t = table; - *idx = ridx; + *t = cand_table; + *idx = cand_ridx; + } } /* @@ -835,6 +863,11 @@ hot_reload_relative_delta_index (MonoImage *image_dmeta, int token) mono_metadata_decode_row (encmap, index_map - 1, cols, MONO_ENCMAP_SIZE); int map_entry = cols [MONO_ENCMAP_TOKEN]; + /* we're looking at the beginning of a sequence of encmap rows that are all the + * modifications+additions for the table we are looking for (or we're looking at an entry + * for the next table after the one we wanted). the map entries will have tokens in + * increasing order. skip over the rows where the tokens are not the one we want, until we + * hit the rows for the next table or we hit the end of the encmap */ while (mono_metadata_token_table (map_entry) == table && mono_metadata_token_index (map_entry) < index && index_map < encmap_rows) { mono_metadata_decode_row (encmap, ++index_map - 1, cols, MONO_ENCMAP_SIZE); map_entry = cols [MONO_ENCMAP_TOKEN]; @@ -848,12 +881,18 @@ hot_reload_relative_delta_index (MonoImage *image_dmeta, int token) mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "relative index for token 0x%08x -> table 0x%02x row 0x%08x", token, table, return_val); return return_val; } else { - /* otherwise the last entry in the encmap is for this table, but is still less than the index - the index is in the next generation */ - g_assert (mono_metadata_token_index (map_entry) < index && index_map == encmap_rows); + /* Otherwise we stopped either: because we saw an an entry for a row after + * the one we wanted - we were looking for a modification, but the encmap + * has an addition; or, because we saw the last entry in the encmap and it + * still wasn't for a row as high as the one we wanted. either way, the + * update we want is not in the delta we're looking at. + */ + g_assert ((mono_metadata_token_index (map_entry) > index) || (mono_metadata_token_index (map_entry) < index && index_map == encmap_rows)); return -1; } } else { - /* otherwise there are no more encmap entries for this table, and we didn't see the index, so there index is in the next generation */ + /* otherwise there are no more encmap entries for this table, and we didn't see the + * index, so there was no modification/addition for that index in this delta. */ g_assert (mono_metadata_token_table (map_entry) > table); return -1; } @@ -925,9 +964,10 @@ delta_info_compute_table_records (MonoImage *image_dmeta, MonoImage *image_base, g_assert (table != MONO_TABLE_ENCMAP); g_assert (table >= prev_table); /* FIXME: check bounds - is it < or <=. */ - if (rid < delta_info->count[table].prev_gen_rows) + if (rid < delta_info->count[table].prev_gen_rows) { + base_info->any_modified_rows[table] = TRUE; delta_info->count[table].modified_rows++; - else + } else delta_info->count[table].inserted_rows++; if (table == prev_table) continue; @@ -1041,6 +1081,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer continue; } case MONO_TABLE_METHODSEMANTICS: { + /* FIXME: this should get the current table size, not the base stable size */ if (token_index > table_info_get_rows (&image_base->tables [token_table])) { /* new rows are fine, as long as they point at existing methods */ guint32 sema_cols [MONO_METHOD_SEMA_SIZE]; @@ -1073,6 +1114,35 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer continue; } } + case MONO_TABLE_CUSTOMATTRIBUTE: { + /* FIXME: this should get the current table size, not the base stable size */ + if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { + /* modifying existing rows is ok, as long as the parent and ctor are the same */ + guint32 ca_upd_cols [MONO_CUSTOM_ATTR_SIZE]; + guint32 ca_base_cols [MONO_CUSTOM_ATTR_SIZE]; + int mapped_token = hot_reload_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); + g_assert (mapped_token != -1); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. mapped index = 0x%08x\n", i, log_token, mapped_token); + + mono_metadata_decode_row (&image_dmeta->tables [MONO_TABLE_CUSTOMATTRIBUTE], mapped_token - 1, ca_upd_cols, MONO_CUSTOM_ATTR_SIZE); + mono_metadata_decode_row (&image_base->tables [MONO_TABLE_CUSTOMATTRIBUTE], token_index - 1, ca_base_cols, MONO_CUSTOM_ATTR_SIZE); + + /* compare the ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] to ca_base_cols [MONO_CUSTOM_ATTR_PARENT]. */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. Old Parent 0x%08x New Parent 0x%08x\n", i, log_token, ca_base_cols [MONO_CUSTOM_ATTR_PARENT], ca_upd_cols [MONO_CUSTOM_ATTR_PARENT]); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. Old ctor 0x%08x New ctor 0x%08x\n", i, log_token, ca_base_cols [MONO_CUSTOM_ATTR_TYPE], ca_upd_cols [MONO_CUSTOM_ATTR_TYPE]); + + if (ca_base_cols [MONO_CUSTOM_ATTR_PARENT] != ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] || + ca_base_cols [MONO_CUSTOM_ATTR_TYPE] != ca_upd_cols [MONO_CUSTOM_ATTR_TYPE]) { + mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing CA table cols with a different Parent or Type. token=0x%08x", log_token); + unsupported_edits = TRUE; + continue; + } + break; + } else { + /* Added a row. ok */ + break; + } + } default: /* FIXME: this bounds check is wrong for cumulative updates - need to look at the DeltaInfo:count.prev_gen_rows */ if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { @@ -1211,6 +1281,10 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen /* assuming that property attributes and type haven't changed. */ break; } + case MONO_TABLE_CUSTOMATTRIBUTE: { + /* ok, pass1 checked for disallowed modifications */ + break; + } default: { g_assert (token_index > table_info_get_rows (&image_base->tables [token_table])); if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) @@ -1478,3 +1552,16 @@ hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_ return (cur != NULL); } +static gboolean +hot_reload_has_modified_rows (const MonoTableInfo *table) +{ + MonoImage *base; + int tbl_index; + if (!table_info_find_in_base (table, &base, &tbl_index)) + return FALSE; + BaselineInfo *info = baseline_info_lookup (base); + if (!info) + return FALSE; + return info->any_modified_rows[tbl_index]; +} + diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 4994664103c..0bdb63d121d 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -29,6 +29,8 @@ typedef struct _MonoComponentHotReload { gpointer (*get_updated_method_rva) (MonoImage *base_image, uint32_t idx); gboolean (*table_bounds_check) (MonoImage *base_image, int table_index, int token_index); gboolean (*delta_heap_lookup) (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); + gboolean (*has_modified_rows) (const MonoTableInfo *table); + } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 545693ef12a..20f36c60e8f 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -805,11 +805,14 @@ mono_metadata_has_updates (void) void mono_image_effective_table_slow (const MonoTableInfo **t, int *idx); +gboolean +mono_metadata_update_has_modified_rows (const MonoTableInfo *t); + static inline void mono_image_effective_table (const MonoTableInfo **t, int *idx) { if (G_UNLIKELY (mono_metadata_has_updates ())) { - if (G_UNLIKELY (*idx >= table_info_get_rows ((*t)))) { + if (G_UNLIKELY (*idx >= table_info_get_rows ((*t)) || mono_metadata_update_has_modified_rows (*t))) { mono_image_effective_table_slow (t, idx); } } diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index d1d646c49ba..8787b28bca6 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -121,3 +121,8 @@ mono_metadata_update_delta_heap_lookup (MonoImage *base_image, MetadataHeapGette } +gboolean +mono_metadata_update_has_modified_rows (const MonoTableInfo *table) +{ + return mono_component_hot_reload ()->has_modified_rows (table); +} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj index 06190d7b86f..46e260f24e1 100644 --- a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj +++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -11,7 +11,6 @@ false false - disable From 738b09bee351a60f1a7c037d5968b1e4145f6c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 12 Jul 2021 18:53:50 +0200 Subject: [PATCH 443/926] Use inline Vector{128|256}.Create for constants (#54827) * Used VectorXYZ.Create for constants in Base64 * Used VectorXYZ.Create for constants in BitArray * Remove conditional compilation It's only built for NetCoreAppCurrent, so no need to special case older runtimes. --- .../src/System/Collections/BitArray.cs | 45 +++-- .../src/System/Buffers/Text/Base64.cs | 8 - .../src/System/Buffers/Text/Base64Decoder.cs | 172 ++++++++---------- .../src/System/Buffers/Text/Base64Encoder.cs | 99 +++++----- 4 files changed, 147 insertions(+), 177 deletions(-) diff --git a/src/libraries/System.Collections/src/System/Collections/BitArray.cs b/src/libraries/System.Collections/src/System/Collections/BitArray.cs index 6921b13347b..9f8531a9e81 100644 --- a/src/libraries/System.Collections/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Collections/src/System/Collections/BitArray.cs @@ -120,10 +120,6 @@ namespace System.Collections _version = 0; } - private static readonly Vector128 s_bitMask128 = BitConverter.IsLittleEndian ? - Vector128.Create(0x80402010_08040201).AsByte() : - Vector128.Create(0x01020408_10204080).AsByte(); - private const uint Vector128ByteCount = 16; private const uint Vector128IntCount = 4; private const uint Vector256ByteCount = 32; @@ -190,6 +186,10 @@ namespace System.Collections // However comparison against zero can be replaced to cmeq against zero (vceqzq_s8) // See dotnet/runtime#33972 for details Vector128 zero = Vector128.Zero; + Vector128 bitMask128 = BitConverter.IsLittleEndian ? + Vector128.Create(0x80402010_08040201).AsByte() : + Vector128.Create(0x01020408_10204080).AsByte(); + fixed (bool* ptr = values) { for (; (i + Vector128ByteCount * 2u) <= (uint)values.Length; i += Vector128ByteCount * 2u) @@ -199,7 +199,7 @@ namespace System.Collections // and combine by ORing all of them together (In this case, adding all of them does the same thing) Vector128 lowerVector = AdvSimd.LoadVector128((byte*)ptr + i); Vector128 lowerIsFalse = AdvSimd.CompareEqual(lowerVector, zero); - Vector128 bitsExtracted1 = AdvSimd.And(lowerIsFalse, s_bitMask128); + Vector128 bitsExtracted1 = AdvSimd.And(lowerIsFalse, bitMask128); bitsExtracted1 = AdvSimd.Arm64.AddPairwise(bitsExtracted1, bitsExtracted1); bitsExtracted1 = AdvSimd.Arm64.AddPairwise(bitsExtracted1, bitsExtracted1); bitsExtracted1 = AdvSimd.Arm64.AddPairwise(bitsExtracted1, bitsExtracted1); @@ -207,7 +207,7 @@ namespace System.Collections Vector128 upperVector = AdvSimd.LoadVector128((byte*)ptr + i + Vector128.Count); Vector128 upperIsFalse = AdvSimd.CompareEqual(upperVector, zero); - Vector128 bitsExtracted2 = AdvSimd.And(upperIsFalse, s_bitMask128); + Vector128 bitsExtracted2 = AdvSimd.And(upperIsFalse, bitMask128); bitsExtracted2 = AdvSimd.Arm64.AddPairwise(bitsExtracted2, bitsExtracted2); bitsExtracted2 = AdvSimd.Arm64.AddPairwise(bitsExtracted2, bitsExtracted2); bitsExtracted2 = AdvSimd.Arm64.AddPairwise(bitsExtracted2, bitsExtracted2); @@ -857,12 +857,6 @@ namespace System.Collections } } - // The mask used when shuffling a single int into Vector128/256. - // On little endian machines, the lower 8 bits of int belong in the first byte, next lower 8 in the second and so on. - // We place the bytes that contain the bits to its respective byte so that we can mask out only the relevant bits later. - private static readonly Vector128 s_lowerShuffleMask_CopyToBoolArray = Vector128.Create(0, 0x01010101_01010101).AsByte(); - private static readonly Vector128 s_upperShuffleMask_CopyToBoolArray = Vector128.Create(0x02020202_02020202, 0x03030303_03030303).AsByte(); - public unsafe void CopyTo(Array array, int index) { if (array == null) @@ -953,9 +947,15 @@ namespace System.Collections if (m_length < BitsPerInt32) goto LessThan32; + // The mask used when shuffling a single int into Vector128/256. + // On little endian machines, the lower 8 bits of int belong in the first byte, next lower 8 in the second and so on. + // We place the bytes that contain the bits to its respective byte so that we can mask out only the relevant bits later. + Vector128 lowerShuffleMask_CopyToBoolArray = Vector128.Create(0, 0x01010101_01010101).AsByte(); + Vector128 upperShuffleMask_CopyToBoolArray = Vector128.Create(0x02020202_02020202, 0x03030303_03030303).AsByte(); + if (Avx2.IsSupported) { - Vector256 shuffleMask = Vector256.Create(s_lowerShuffleMask_CopyToBoolArray, s_upperShuffleMask_CopyToBoolArray); + Vector256 shuffleMask = Vector256.Create(lowerShuffleMask_CopyToBoolArray, upperShuffleMask_CopyToBoolArray); Vector256 bitMask = Vector256.Create(0x80402010_08040201).AsByte(); Vector256 ones = Vector256.Create((byte)1); @@ -977,9 +977,12 @@ namespace System.Collections } else if (Ssse3.IsSupported) { - Vector128 lowerShuffleMask = s_lowerShuffleMask_CopyToBoolArray; - Vector128 upperShuffleMask = s_upperShuffleMask_CopyToBoolArray; + Vector128 lowerShuffleMask = lowerShuffleMask_CopyToBoolArray; + Vector128 upperShuffleMask = upperShuffleMask_CopyToBoolArray; Vector128 ones = Vector128.Create((byte)1); + Vector128 bitMask128 = BitConverter.IsLittleEndian ? + Vector128.Create(0x80402010_08040201).AsByte() : + Vector128.Create(0x01020408_10204080).AsByte(); fixed (bool* destination = &boolArray[index]) { @@ -989,12 +992,12 @@ namespace System.Collections Vector128 scalar = Vector128.CreateScalarUnsafe(bits); Vector128 shuffledLower = Ssse3.Shuffle(scalar.AsByte(), lowerShuffleMask); - Vector128 extractedLower = Sse2.And(shuffledLower, s_bitMask128); + Vector128 extractedLower = Sse2.And(shuffledLower, bitMask128); Vector128 normalizedLower = Sse2.Min(extractedLower, ones); Sse2.Store((byte*)destination + i, normalizedLower); Vector128 shuffledHigher = Ssse3.Shuffle(scalar.AsByte(), upperShuffleMask); - Vector128 extractedHigher = Sse2.And(shuffledHigher, s_bitMask128); + Vector128 extractedHigher = Sse2.And(shuffledHigher, bitMask128); Vector128 normalizedHigher = Sse2.Min(extractedHigher, ones); Sse2.Store((byte*)destination + i + Vector128.Count, normalizedHigher); } @@ -1003,6 +1006,10 @@ namespace System.Collections else if (AdvSimd.IsSupported) { Vector128 ones = Vector128.Create((byte)1); + Vector128 bitMask128 = BitConverter.IsLittleEndian ? + Vector128.Create(0x80402010_08040201).AsByte() : + Vector128.Create(0x01020408_10204080).AsByte(); + fixed (bool* destination = &boolArray[index]) { for (; (i + Vector128ByteCount * 2u) <= (uint)m_length; i += Vector128ByteCount * 2u) @@ -1028,12 +1035,12 @@ namespace System.Collections vector = AdvSimd.Arm64.ZipLow(vector, vector); Vector128 shuffledLower = AdvSimd.Arm64.ZipLow(vector, vector); - Vector128 extractedLower = AdvSimd.And(shuffledLower, s_bitMask128); + Vector128 extractedLower = AdvSimd.And(shuffledLower, bitMask128); Vector128 normalizedLower = AdvSimd.Min(extractedLower, ones); AdvSimd.Store((byte*)destination + i, normalizedLower); Vector128 shuffledHigher = AdvSimd.Arm64.ZipHigh(vector, vector); - Vector128 extractedHigher = AdvSimd.And(shuffledHigher, s_bitMask128); + Vector128 extractedHigher = AdvSimd.And(shuffledHigher, bitMask128); Vector128 normalizedHigher = AdvSimd.Min(extractedHigher, ones); AdvSimd.Store((byte*)destination + i + Vector128.Count, normalizedHigher); } diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs b/src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs index 60d1558a50f..7506cbe1eb0 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs +++ b/src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs @@ -2,20 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; namespace System.Buffers.Text { public static partial class Base64 { - private static TVector ReadVector(ReadOnlySpan data) - { - ref sbyte tmp = ref MemoryMarshal.GetReference(data); - return Unsafe.As(ref tmp); - } - [Conditional("DEBUG")] private static unsafe void AssertRead(byte* src, byte* srcStart, int srcLength) { diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs index b6c639781c2..e71b3b7f2d2 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs @@ -100,7 +100,7 @@ namespace System.Buffers.Text maxSrcLength = (destLength / 3) * 4; } - ref sbyte decodingMap = ref MemoryMarshal.GetReference(s_decodingMap); + ref sbyte decodingMap = ref MemoryMarshal.GetReference(DecodingMap); srcMax = srcBytes + (uint)maxSrcLength; while (src < srcMax) @@ -275,7 +275,7 @@ namespace System.Buffers.Text if (bufferLength == 0) goto DoneExit; - ref sbyte decodingMap = ref MemoryMarshal.GetReference(s_decodingMap); + ref sbyte decodingMap = ref MemoryMarshal.GetReference(DecodingMap); while (sourceIndex < bufferLength - 4) { @@ -362,14 +362,59 @@ namespace System.Buffers.Text // See SSSE3-version below for an explanation of how the code works. // The JIT won't hoist these "constants", so help it - Vector256 lutHi = ReadVector>(s_avxDecodeLutHi); - Vector256 lutLo = ReadVector>(s_avxDecodeLutLo); - Vector256 lutShift = ReadVector>(s_avxDecodeLutShift); + Vector256 lutHi = Vector256.Create( + 0x10, 0x10, 0x01, 0x02, + 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x01, 0x02, + 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10); + + Vector256 lutLo = Vector256.Create( + 0x15, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, + 0x1B, 0x1B, 0x1B, 0x1A, + 0x15, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, + 0x1B, 0x1B, 0x1B, 0x1A); + + Vector256 lutShift = Vector256.Create( + 0, 16, 19, 4, + -65, -65, -71, -71, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 16, 19, 4, + -65, -65, -71, -71, + 0, 0, 0, 0, + 0, 0, 0, 0); + + Vector256 packBytesInLaneMask = Vector256.Create( + 2, 1, 0, 6, + 5, 4, 10, 9, + 8, 14, 13, 12, + -1, -1, -1, -1, + 2, 1, 0, 6, + 5, 4, 10, 9, + 8, 14, 13, 12, + -1, -1, -1, -1); + + Vector256 packLanesControl = Vector256.Create( + 0, 0, 0, 0, + 1, 0, 0, 0, + 2, 0, 0, 0, + 4, 0, 0, 0, + 5, 0, 0, 0, + 6, 0, 0, 0, + -1, -1, -1, -1, + -1, -1, -1, -1).AsInt32(); + Vector256 mask2F = Vector256.Create((sbyte)'/'); Vector256 mergeConstant0 = Vector256.Create(0x01400140).AsSByte(); Vector256 mergeConstant1 = Vector256.Create(0x00011000).AsInt16(); - Vector256 packBytesInLaneMask = ReadVector>(s_avxDecodePackBytesInLaneMask); - Vector256 packLanesControl = ReadVector>(s_avxDecodePackLanesControl).AsInt32(); byte* src = srcBytes; byte* dest = destBytes; @@ -508,13 +553,33 @@ namespace System.Buffers.Text // 1111 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 // The JIT won't hoist these "constants", so help it - Vector128 lutHi = ReadVector>(s_sseDecodeLutHi); - Vector128 lutLo = ReadVector>(s_sseDecodeLutLo); - Vector128 lutShift = ReadVector>(s_sseDecodeLutShift); + Vector128 lutHi = Vector128.Create( + 0x10, 0x10, 0x01, 0x02, + 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10); + + Vector128 lutLo = Vector128.Create( + 0x15, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, + 0x1B, 0x1B, 0x1B, 0x1A); + + Vector128 lutShift = Vector128.Create( + 0, 16, 19, 4, + -65, -65, -71, -71, + 0, 0, 0, 0, + 0, 0, 0, 0); + + Vector128 packBytesMask = Vector128.Create( + 2, 1, 0, 6, + 5, 4, 10, 9, + 8, 14, 13, 12, + -1, -1, -1, -1); + Vector128 mask2F = Vector128.Create((sbyte)'/'); Vector128 mergeConstant0 = Vector128.Create(0x01400140).AsSByte(); Vector128 mergeConstant1 = Vector128.Create(0x00011000).AsInt16(); - Vector128 packBytesMask = ReadVector>(s_sseDecodePackBytesMask); Vector128 zero = Vector128.Zero; byte* src = srcBytes; @@ -613,7 +678,7 @@ namespace System.Buffers.Text } // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in tests) - private static ReadOnlySpan s_decodingMap => new sbyte[] { + private static ReadOnlySpan DecodingMap => new sbyte[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, //62 is placed at index 43 (for +), 63 at index 47 (for /) @@ -631,88 +696,5 @@ namespace System.Buffers.Text -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - - private static ReadOnlySpan s_sseDecodePackBytesMask => new sbyte[] { - 2, 1, 0, 6, - 5, 4, 10, 9, - 8, 14, 13, 12, - -1, -1, -1, -1 - }; - - private static ReadOnlySpan s_sseDecodeLutLo => new sbyte[] { - 0x15, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x13, 0x1A, - 0x1B, 0x1B, 0x1B, 0x1A - }; - - private static ReadOnlySpan s_sseDecodeLutHi => new sbyte[] { - 0x10, 0x10, 0x01, 0x02, - 0x04, 0x08, 0x04, 0x08, - 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10 - }; - - private static ReadOnlySpan s_sseDecodeLutShift => new sbyte[] { - 0, 16, 19, 4, - -65, -65, -71, -71, - 0, 0, 0, 0, - 0, 0, 0, 0 - }; - - private static ReadOnlySpan s_avxDecodePackBytesInLaneMask => new sbyte[] { - 2, 1, 0, 6, - 5, 4, 10, 9, - 8, 14, 13, 12, - -1, -1, -1, -1, - 2, 1, 0, 6, - 5, 4, 10, 9, - 8, 14, 13, 12, - -1, -1, -1, -1 - }; - - private static ReadOnlySpan s_avxDecodePackLanesControl => new sbyte[] { - 0, 0, 0, 0, - 1, 0, 0, 0, - 2, 0, 0, 0, - 4, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, 0, 0, - -1, -1, -1, -1, - -1, -1, -1, -1 - }; - - private static ReadOnlySpan s_avxDecodeLutLo => new sbyte[] { - 0x15, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x13, 0x1A, - 0x1B, 0x1B, 0x1B, 0x1A, - 0x15, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x13, 0x1A, - 0x1B, 0x1B, 0x1B, 0x1A - }; - - private static ReadOnlySpan s_avxDecodeLutHi => new sbyte[] { - 0x10, 0x10, 0x01, 0x02, - 0x04, 0x08, 0x04, 0x08, - 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x01, 0x02, - 0x04, 0x08, 0x04, 0x08, - 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10 - }; - - private static ReadOnlySpan s_avxDecodeLutShift => new sbyte[] { - 0, 16, 19, 4, - -65, -65, -71, -71, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 16, 19, 4, - -65, -65, -71, -71, - 0, 0, 0, 0, - 0, 0, 0, 0 - }; } } diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs index 0ee99479128..99add7b72e8 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs @@ -85,7 +85,7 @@ namespace System.Buffers.Text } } - ref byte encodingMap = ref MemoryMarshal.GetReference(s_encodingMap); + ref byte encodingMap = ref MemoryMarshal.GetReference(EncodingMap); uint result = 0; srcMax -= 2; @@ -189,7 +189,7 @@ namespace System.Buffers.Text uint destinationIndex = (uint)(encodedLength - 4); uint sourceIndex = (uint)(dataLength - leftover); uint result = 0; - ref byte encodingMap = ref MemoryMarshal.GetReference(s_encodingMap); + ref byte encodingMap = ref MemoryMarshal.GetReference(EncodingMap); // encode last pack to avoid conditional in the main loop if (leftover != 0) @@ -241,14 +241,32 @@ namespace System.Buffers.Text // l k j i h g f e d c b a 0 0 0 0 // The JIT won't hoist these "constants", so help it - Vector256 shuffleVec = ReadVector>(s_avxEncodeShuffleVec); + Vector256 shuffleVec = Vector256.Create( + 5, 4, 6, 5, + 8, 7, 9, 8, + 11, 10, 12, 11, + 14, 13, 15, 14, + 1, 0, 2, 1, + 4, 3, 5, 4, + 7, 6, 8, 7, + 10, 9, 11, 10); + + Vector256 lut = Vector256.Create( + 65, 71, -4, -4, + -4, -4, -4, -4, + -4, -4, -4, -4, + -19, -16, 0, 0, + 65, 71, -4, -4, + -4, -4, -4, -4, + -4, -4, -4, -4, + -19, -16, 0, 0); + Vector256 maskAC = Vector256.Create(0x0fc0fc00).AsSByte(); Vector256 maskBB = Vector256.Create(0x003f03f0).AsSByte(); Vector256 shiftAC = Vector256.Create(0x04000040).AsUInt16(); Vector256 shiftBB = Vector256.Create(0x01000010).AsInt16(); Vector256 const51 = Vector256.Create((byte)51); Vector256 const25 = Vector256.Create((sbyte)25); - Vector256 lut = ReadVector>(s_avxEncodeLut); byte* src = srcBytes; byte* dest = destBytes; @@ -258,7 +276,15 @@ namespace System.Buffers.Text Vector256 str = Avx.LoadVector256(src).AsSByte(); // shift by 4 bytes, as required by Reshuffle - str = Avx2.PermuteVar8x32(str.AsInt32(), ReadVector>(s_avxEncodePermuteVec).AsInt32()).AsSByte(); + str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create( + 0, 0, 0, 0, + 0, 0, 0, 0, + 1, 0, 0, 0, + 2, 0, 0, 0, + 3, 0, 0, 0, + 4, 0, 0, 0, + 5, 0, 0, 0, + 6, 0, 0, 0).AsInt32()).AsSByte(); // Next loads are done at src-4, as required by Reshuffle, so shift it once src -= 4; @@ -380,14 +406,24 @@ namespace System.Buffers.Text // 0 0 0 0 l k j i h g f e d c b a // The JIT won't hoist these "constants", so help it - Vector128 shuffleVec = ReadVector>(s_sseEncodeShuffleVec); + Vector128 shuffleVec = Vector128.Create( + 1, 0, 2, 1, + 4, 3, 5, 4, + 7, 6, 8, 7, + 10, 9, 11, 10); + + Vector128 lut = Vector128.Create( + 65, 71, -4, -4, + -4, -4, -4, -4, + -4, -4, -4, -4, + -19, -16, 0, 0); + Vector128 maskAC = Vector128.Create(0x0fc0fc00).AsSByte(); Vector128 maskBB = Vector128.Create(0x003f03f0).AsSByte(); Vector128 shiftAC = Vector128.Create(0x04000040).AsUInt16(); Vector128 shiftBB = Vector128.Create(0x01000010).AsInt16(); Vector128 const51 = Vector128.Create((byte)51); Vector128 const25 = Vector128.Create((sbyte)25); - Vector128 lut = ReadVector>(s_sseEncodeLut); byte* src = srcBytes; byte* dest = destBytes; @@ -543,7 +579,7 @@ namespace System.Buffers.Text private const int MaximumEncodeLength = (int.MaxValue / 4) * 3; // 1610612733 // Pre-computing this table using a custom string(s_characters) and GenerateEncodingMapAndVerify (found in tests) - private static ReadOnlySpan s_encodingMap => new byte[] { + private static ReadOnlySpan EncodingMap => new byte[] { 65, 66, 67, 68, 69, 70, 71, 72, //A..H 73, 74, 75, 76, 77, 78, 79, 80, //I..P 81, 82, 83, 84, 85, 86, 87, 88, //Q..X @@ -553,52 +589,5 @@ namespace System.Buffers.Text 119, 120, 121, 122, 48, 49, 50, 51, //w..z, 0..3 52, 53, 54, 55, 56, 57, 43, 47 //4..9, +, / }; - - private static ReadOnlySpan s_sseEncodeShuffleVec => new sbyte[] { - 1, 0, 2, 1, - 4, 3, 5, 4, - 7, 6, 8, 7, - 10, 9, 11, 10 - }; - - private static ReadOnlySpan s_sseEncodeLut => new sbyte[] { - 65, 71, -4, -4, - -4, -4, -4, -4, - -4, -4, -4, -4, - -19, -16, 0, 0 - }; - - private static ReadOnlySpan s_avxEncodePermuteVec => new sbyte[] { - 0, 0, 0, 0, - 0, 0, 0, 0, - 1, 0, 0, 0, - 2, 0, 0, 0, - 3, 0, 0, 0, - 4, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, 0, 0 - }; - - private static ReadOnlySpan s_avxEncodeShuffleVec => new sbyte[] { - 5, 4, 6, 5, - 8, 7, 9, 8, - 11, 10, 12, 11, - 14, 13, 15, 14, - 1, 0, 2, 1, - 4, 3, 5, 4, - 7, 6, 8, 7, - 10, 9, 11, 10 - }; - - private static ReadOnlySpan s_avxEncodeLut => new sbyte[] { - 65, 71, -4, -4, - -4, -4, -4, -4, - -4, -4, -4, -4, - -19, -16, 0, 0, - 65, 71, -4, -4, - -4, -4, -4, -4, - -4, -4, -4, -4, - -19, -16, 0, 0 - }; } } From 4156116824c18be1cf1d29a42278b5586d16ffcf Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 12 Jul 2021 13:59:13 -0400 Subject: [PATCH 444/926] [mono] Add some WASM AOT documention. (#55498) --- docs/design/mono/wasm-aot.md | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/design/mono/wasm-aot.md diff --git a/docs/design/mono/wasm-aot.md b/docs/design/mono/wasm-aot.md new file mode 100644 index 00000000000..667d124ed9c --- /dev/null +++ b/docs/design/mono/wasm-aot.md @@ -0,0 +1,66 @@ +# WebAssembly AOT code generation + +## Basic operation + +The LLVM backend of the Mono JIT is used to generate an llvm .bc file for each assembly, then the .bc files are +compiled to webassembly using emscripten, then the resulting wasm files are linked into the final app. The 'bitcode'/'llvmonly' +variant of the LLVM backend is used since webassembly doesn't support inline assembly etc. + +## GC Support + +On wasm, the execution stack is not stored in linear memory, so its not possible to scan it for GC references. However, there +is an additional C stack which stores variables whose addresses are taken. Variables which hold GC references are marked as +'volatile' in the llvm backend, forcing llvm to spill those to the C stack so they can be scanned. + +## Interpreter support + +Its possible for AOTed and interpreted code to interop, this is called mixed mode. +For the AOT -> interpreter case, every call from AOTed code which might end up in the interpreter is +emitted as an indirect call. When the callee is not found, a wrapper function is used which +packages up the arguments into an array and passes control to the interpreter. +For the interpreter -> AOT case, and similar wrapper function is used which receives the +arguments and a return value pointer from the interpreter in an array, and calls the +AOTed code. There is usually one aot->interp and interp->aot wrapper for each signature, with +some sharing. These wrappers are generated by the AOT compiler when the 'interp' aot option +is used. + +## Null checks + +Since wasm has no signal support, we generate explicit null checks. + +## Issues + +The generated code is in general much bigger than the code generated on ios etc. Some of the +current issues are described below. + +### Function pointers + +The runtime needs to be able to do a IL method -> wasm function lookup. To do this, every +AOT image includes a table mapping from a method index to wasm functions. This means that +every generated AOT method has its address taken, which severely limits the interprocedural +optimizations that LLVM can do, since it cannot determine the set of callers for a function. +This means that it cannot remove functions corresponding to unused IL methods, cannot +specialize functions for constant/nonnull arguments, etc. +The dotnet linker includes some support for adding a [DisablePrivateReflection] attribute to +methods which cannot be called using reflection, and the AOT compiler could use this +to avoid generating function pointers for methods which are not called from outside the +AOT image. This is not enabled right now because the linker support is not complete. + +### Null checks + +The explicit null checking code adds a lot of size overhead since null checks are very common. + +### Virtual calls + +Vtable slots are lazily initialized on the first call, i.e. every virtual call looks like this: +```C +vt_entry = vtable [slot]; +if (vt_entry == null) + vt_entry = init_vt_entry (); +``` + +### GC overhead + +Since GC variables are marked as volatile and stored on the C stack, they are loaded/stored on every access, +even if there is no GC safe point between the accesses. Instead, they should only be loaded/stored around +GC safe points. From efd0bb58bdc2d7591d77e604b810c096a7a5d58e Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Mon, 12 Jul 2021 12:24:17 -0700 Subject: [PATCH 445/926] Remove NuGet config in HTTP stress (#55519) --- .../tests/StressTests/HttpStress/NuGet.config | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config deleted file mode 100644 index 0992c432038..00000000000 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/NuGet.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From cd4df7dac9efed87702b69c8fbf86b265372a533 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Mon, 12 Jul 2021 12:57:21 -0700 Subject: [PATCH 446/926] Implement part of SslCertificateTrust (#55104) * snatpshot * snapshot * add csproj' * update * more ckeanup * more cleanup * feedback from review * fix mail tests * feedabck from review * feedback from review * make constructor private --- .../Interop/Windows/SspiCli/Interop.SSPI.cs | 22 +++++++ .../src/System.Net.Http.csproj | 4 +- .../src/System.Net.Mail.csproj | 2 + .../Unit/System.Net.Mail.Unit.Tests.csproj | 2 + .../ref/System.Net.Security.cs | 17 ++++- .../src/Resources/Strings.resx | 6 ++ .../src/System.Net.Security.csproj | 4 ++ .../src/System/Net/Security/SecureChannel.cs | 15 ++++- .../Net/Security/SslCertificateTrust.cs | 64 +++++++++++++++++++ .../Net/Security/SslStream.Implementation.cs | 2 +- .../SslStreamCertificateContext.Linux.cs | 3 +- .../SslStreamCertificateContext.OSX.cs | 5 +- .../SslStreamCertificateContext.Windows.cs | 5 +- .../Security/SslStreamCertificateContext.cs | 15 +++-- .../Net/Security/SslStreamPal.Windows.cs | 37 +++++++++-- .../SslStreamCredentialCacheTest.cs | 8 +-- 16 files changed, 188 insertions(+), 23 deletions(-) create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/SslCertificateTrust.cs diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs index b675f7f0224..1694ca539a8 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs @@ -65,6 +65,7 @@ internal static partial class Interop SECPKG_ATTR_LOCAL_CERT_CONTEXT = 0x54, // returns PCCERT_CONTEXT SECPKG_ATTR_ROOT_STORE = 0x55, // returns HCERTCONTEXT to the root store SECPKG_ATTR_ISSUER_LIST_EX = 0x59, // returns SecPkgContext_IssuerListInfoEx + SECPKG_ATTR_CLIENT_CERT_POLICY = 0x60, // sets SecPkgCred_ClientCertCtlPolicy SECPKG_ATTR_CONNECTION_INFO = 0x5A, // returns SecPkgContext_ConnectionInfo SECPKG_ATTR_CIPHER_INFO = 0x64, // returns SecPkgContext_CipherInfo SECPKG_ATTR_UI_INFO = 0x68, // sets SEcPkgContext_UiInfo @@ -315,6 +316,20 @@ internal static partial class Interop } } + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct SecPkgCred_ClientCertPolicy + { + public uint dwFlags; + public Guid guidPolicyId; + public uint dwCertFlags; + public uint dwUrlRetrievalTimeout; + public BOOL fCheckRevocationFreshnessTime; + public uint dwRevocationFreshnessTime; + public BOOL fOmitUsageCheck; + public char* pwszSslCtlStoreName; + public char* pwszSslCtlIdentifier; + } + [DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, SetLastError = true)] internal static extern int EncryptMessage( ref CredHandle contextHandle, @@ -472,5 +487,12 @@ internal static partial class Interop [In] string domainName, [In] string password, [Out] out SafeSspiAuthDataHandle authData); + + [DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SECURITY_STATUS SetCredentialsAttributesW( + [In] ref CredHandle handlePtr, + [In] long ulAttribute, + [In] ref SecPkgCred_ClientCertPolicy pBuffer, + [In] long cbBuffer); } } diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 49bc41d756e..0bbe56c0d87 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -396,10 +396,12 @@ + + Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" /> + + Setting an SNI hostname is not supported on this API level. + + Only LocalMachine stores are supported on Windows. + + + Sending trust from collection is not supported on Windows. + diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 100115a0920..ca89fca29fa 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent) $(DefineConstants);PRODUCT + $(DefineConstants);TARGET_WINDOWS enable @@ -29,6 +30,7 @@ + @@ -156,6 +158,8 @@ + Create(target, null); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.OSX.cs index 4579f15407f..9cef66806ac 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.OSX.cs @@ -10,16 +10,17 @@ namespace System.Net.Security // No leaf, no root. private const bool TrimRootCertificate = true; - private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates) + private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust) { Certificate = target; IntermediateCertificates = intermediates; + Trust = trust; } internal static SslStreamCertificateContext Create(X509Certificate2 target) { // On OSX we do not need to build chain unless we are asked for it. - return new SslStreamCertificateContext(target, Array.Empty()); + return new SslStreamCertificateContext(target, Array.Empty(), null); } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs index fcb84b2dda6..699a5c8c012 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs @@ -13,10 +13,10 @@ namespace System.Net.Security internal static SslStreamCertificateContext Create(X509Certificate2 target) { // On Windows we do not need to build chain unless we are asked for it. - return new SslStreamCertificateContext(target, Array.Empty()); + return new SslStreamCertificateContext(target, Array.Empty(), null); } - private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates) + private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust) { if (intermediates.Length > 0) { @@ -103,6 +103,7 @@ namespace System.Net.Security Certificate = target; IntermediateCertificates = intermediates; + Trust = trust; } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs index 4ae127aadcc..c585d7e033d 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Security.Cryptography.X509Certificates; namespace System.Net.Security @@ -9,8 +10,15 @@ namespace System.Net.Security { internal readonly X509Certificate2 Certificate; internal readonly X509Certificate2[] IntermediateCertificates; + internal readonly SslCertificateTrust? Trust; - public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline = false) + [EditorBrowsable(EditorBrowsableState.Never)] + public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline) + { + return Create(target, additionalCertificates, offline, null); + } + + public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline = false, SslCertificateTrust? trust = null) { if (!target.HasPrivateKey) { @@ -81,13 +89,12 @@ namespace System.Net.Security } } - return new SslStreamCertificateContext(target, intermediates); + return new SslStreamCertificateContext(target, intermediates, trust); } internal SslStreamCertificateContext Duplicate() { - return new SslStreamCertificateContext(new X509Certificate2(Certificate), IntermediateCertificates); - + return new SslStreamCertificateContext(new X509Certificate2(Certificate), IntermediateCertificates, Trust); } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 4060296c4ed..29ab3bed0ee 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; +using System.Text; using Microsoft.Win32.SafeHandles; namespace System.Net.Security @@ -121,14 +123,41 @@ namespace System.Net.Security public static SafeFreeCredentials AcquireCredentialsHandle(SslStreamCertificateContext? certificateContext, SslProtocols protocols, EncryptionPolicy policy, bool isServer) { // New crypto API supports TLS1.3 but it does not allow to force NULL encryption. - return !UseNewCryptoApi || policy == EncryptionPolicy.NoEncryption ? + SafeFreeCredentials cred = !UseNewCryptoApi || policy == EncryptionPolicy.NoEncryption ? AcquireCredentialsHandleSchannelCred(certificateContext?.Certificate, protocols, policy, isServer) : AcquireCredentialsHandleSchCredentials(certificateContext?.Certificate, protocols, policy, isServer); + if (certificateContext != null && certificateContext.Trust != null && certificateContext.Trust._sendTrustInHandshake) + { + AttachCertificateStore(cred, certificateContext.Trust._store!); + } + + return cred; + } + + private static unsafe void AttachCertificateStore(SafeFreeCredentials cred, X509Store store) + { + Interop.SspiCli.SecPkgCred_ClientCertPolicy clientCertPolicy = default; + fixed (char* ptr = store.Name) + { + clientCertPolicy.pwszSslCtlStoreName = ptr; + Interop.SECURITY_STATUS errorCode = Interop.SspiCli.SetCredentialsAttributesW( + ref cred._handle, + (long)Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_CERT_POLICY, + ref clientCertPolicy, + sizeof(Interop.SspiCli.SecPkgCred_ClientCertPolicy)); + + if (errorCode != Interop.SECURITY_STATUS.OK) + { + throw new Win32Exception((int)errorCode); + } + } + + return; } // This is legacy crypto API used on .NET Framework and older Windows versions. // It only supports TLS up to 1.2 - public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchannelCred(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) + public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchannelCred(X509Certificate2? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) { int protocolFlags = GetProtocolFlagsFromSslProtocols(protocols, isServer); Interop.SspiCli.SCHANNEL_CRED.Flags flags; @@ -174,12 +203,11 @@ namespace System.Net.Security } // This function uses new crypto API to support TLS 1.3 and beyond. - public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) + public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials(X509Certificate2? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) { int protocolFlags = GetProtocolFlagsFromSslProtocols(protocols, isServer); Interop.SspiCli.SCH_CREDENTIALS.Flags flags; Interop.SspiCli.CredentialUse direction; - if (isServer) { direction = Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND; @@ -215,7 +243,6 @@ namespace System.Net.Security Interop.SspiCli.SCH_CREDENTIALS credential = default; credential.dwVersion = Interop.SspiCli.SCH_CREDENTIALS.CurrentVersion; credential.dwFlags = flags; - Interop.Crypt32.CERT_CONTEXT *certificateHandle = null; if (certificate != null) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs index e2e8314249d..f1350954763 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs @@ -28,15 +28,13 @@ namespace System.Net.Security.Tests X509Certificate2Collection clientCertificateCollection = new X509Certificate2Collection(certificate); - var tasks = new Task[2]; - - tasks[0] = server.AuthenticateAsServerAsync(certificate, true, false); - tasks[1] = client.AuthenticateAsClientAsync( + Task t1 = server.AuthenticateAsServerAsync(certificate, true, false); + Task t2 = client.AuthenticateAsClientAsync( certificate.GetNameInfo(X509NameType.SimpleName, false), clientCertificateCollection, false); - await Task.WhenAll(tasks).WaitAsync(TestConfiguration.PassingTestTimeout); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); if (!PlatformDetection.IsWindows7 || Capability.IsTrustedRootCertificateInstalled()) From e544ccffbd6f7018372923871dd39129810e4b11 Mon Sep 17 00:00:00 2001 From: John Salem Date: Mon, 12 Jul 2021 13:02:28 -0700 Subject: [PATCH 447/926] Prevent AV in processinfo2 while suspended (#55379) --- .../vm/eventing/eventpipe/ep-rt-coreclr.h | 17 ++++++++- .../diagnosticport/diagnosticport.cs | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h index 607286d048b..7ee83ae8c5f 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h @@ -1169,7 +1169,22 @@ ep_rt_entrypoint_assembly_name_get_utf8 (void) { STATIC_CONTRACT_NOTHROW; - return reinterpret_cast(GetAppDomain ()->GetRootAssembly ()->GetSimpleName ()); + AppDomain *app_domain_ref = nullptr; + Assembly *assembly_ref = nullptr; + + app_domain_ref = GetAppDomain (); + if (app_domain_ref != nullptr) + { + assembly_ref = app_domain_ref->GetRootAssembly (); + if (assembly_ref != nullptr) + { + return reinterpret_cast(assembly_ref->GetSimpleName ()); + } + } + + // fallback to the empty string if we can't get assembly info, e.g., if the runtime is + // suspended before an assembly is loaded. + return reinterpret_cast(""); } static diff --git a/src/tests/tracing/eventpipe/diagnosticport/diagnosticport.cs b/src/tests/tracing/eventpipe/diagnosticport/diagnosticport.cs index a4e6bd64181..b1455d01e21 100644 --- a/src/tests/tracing/eventpipe/diagnosticport/diagnosticport.cs +++ b/src/tests/tracing/eventpipe/diagnosticport/diagnosticport.cs @@ -388,6 +388,42 @@ namespace Tracing.Tests.DiagnosticPortValidation return fSuccess; } + public static async Task TEST_CanGetProcessInfo2WhileSuspended() + { + bool fSuccess = true; + Task subprocessTask = Utils.RunSubprocess( + currentAssembly: Assembly.GetExecutingAssembly(), + environment: new Dictionary + { + { Utils.DiagnosticPortSuspend, "1" } + }, + duringExecution: (int pid) => + { + Stream stream = ConnectionHelper.GetStandardTransport(pid); + + // 0x04 = ProcessCommandSet, 0x04 = ProcessInfo2 + var processInfoMessage = new IpcMessage(0x04, 0x04); + Logger.logger.Log($"Wrote: {processInfoMessage}"); + IpcMessage response = IpcClient.SendMessage(stream, processInfoMessage); + Logger.logger.Log($"Received: [{response.Payload.Select(b => b.ToString("X2") + " ").Aggregate(string.Concat)}]"); + ProcessInfo2 processInfo2 = ProcessInfo2.TryParse(response.Payload); + Utils.Assert(String.IsNullOrEmpty(processInfo2.ManagedEntrypointAssemblyName)); + + // send resume command on this connection + var message = new IpcMessage(0x04,0x01); + Logger.logger.Log($"Sent: {message.ToString()}"); + response = IpcClient.SendMessage(ConnectionHelper.GetStandardTransport(pid), message); + Logger.logger.Log($"Received: {response.ToString()}"); + + return Task.FromResult(true); + } + ); + + fSuccess &= await subprocessTask; + + return fSuccess; + } + public static async Task Main(string[] args) { if (args.Length >= 1) From f280419a07a9445e1c6724e5717b3da7bdc5be7d Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 12 Jul 2021 15:37:40 -0500 Subject: [PATCH 448/926] Expose the platform directly for apple products (#55381) Co-authored-by: Steve Pfister --- ...T.Workload.Mono.Toolchain.Manifest.pkgproj | 7 +- .../WorkloadManifest.json.in | 110 +++++++++++++----- ...st.targets => WorkloadManifest.targets.in} | 86 +++++++++++--- 3 files changed, 161 insertions(+), 42 deletions(-) rename src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/{WorkloadManifest.targets => WorkloadManifest.targets.in} (55%) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj index 0f6c403f410..f89c42b2884 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj @@ -13,11 +13,12 @@ $(IntermediateOutputPath)WorkloadManifest.json + $(IntermediateOutputPath)WorkloadManifest.targets - + @@ -37,6 +38,10 @@ TemplateFile="WorkloadManifest.json.in" Properties="@(_WorkloadManifestValues)" OutputPath="$(WorkloadManifestPath)" /> + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index 05d94220cb8..d7fec86e3a1 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -44,11 +44,13 @@ "packs": [ "Microsoft.NETCore.App.Runtime.Mono.ios-arm", "Microsoft.NETCore.App.Runtime.Mono.ios-arm64", - "Microsoft.NETCore.App.Runtime.Mono.iossimulator", + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64", + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64", "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86", "Microsoft.NETCore.App.Runtime.AOT.Cross.ios-arm", "Microsoft.NETCore.App.Runtime.AOT.Cross.ios-arm64", - "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator", + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-arm64", + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x64", "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x86" ], "extends": [ "microsoft-net-runtime-mono-tooling" ], @@ -58,8 +60,22 @@ "abstract": true, "description": "MacCatalyst Mono Runtime and AOT Workload", "packs": [ - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst", - "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst" + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64", + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64", + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-arm64", + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-x64" + ], + "extends": [ "microsoft-net-runtime-mono-tooling" ], + "platforms": [ "osx-arm64", "osx-x64" ] + }, + "microsoft-net-runtime-macos": { + "abstract": true, + "description": "MacOS CoreCLR and Mono Runtime Workload", + "packs": [ + "Microsoft.NETCore.App.Runtime.Mono.osx-arm64", + "Microsoft.NETCore.App.Runtime.Mono.osx-x64", + "Microsoft.NETCore.App.Runtime.osx-arm64", + "Microsoft.NETCore.App.Runtime.osx-x64" ], "extends": [ "microsoft-net-runtime-mono-tooling" ], "platforms": [ "osx-arm64", "osx-x64" ] @@ -69,9 +85,11 @@ "description": "tvOS Mono Runtime and AOT Workload", "packs": [ "Microsoft.NETCore.App.Runtime.Mono.tvos-arm64", - "Microsoft.NETCore.App.Runtime.Mono.tvossimulator", + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64", + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64", "Microsoft.NETCore.App.Runtime.AOT.Cross.tvos-arm64", - "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator" + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-arm64", + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-x64" ], "extends": [ "microsoft-net-runtime-mono-tooling" ], "platforms": [ "osx-arm64", "osx-x64" ] @@ -154,13 +172,29 @@ "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.android-arm64" } }, - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst": { + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.osx-arm64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.osx-x64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.osx-arm64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.osx-x64": { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64" - } }, "Microsoft.NETCore.App.Runtime.Mono.ios-arm" : { "kind": "framework", @@ -170,20 +204,17 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NETCore.App.Runtime.Mono.iossimulator" : { + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64" : { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64" : { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64" - } }, "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86" : { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86" - } }, "Microsoft.NETCore.App.Runtime.AOT.Cross.tvos-arm64": { "kind": "Sdk", @@ -197,27 +228,43 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NETCore.App.Runtime.Mono.tvossimulator" : { + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64" : { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64" - } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst": { + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64" : { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-arm64" + } + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-x64": { + "kind": "Sdk", + "version": "${PackageVersion}", + "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-x64" } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator": { + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-arm64" + } + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-x64": { + "kind": "Sdk", + "version": "${PackageVersion}", + "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-x64" } }, @@ -237,11 +284,19 @@ "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.ios-arm64", } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator": { + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-arm64" + } + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x64": { + "kind": "Sdk", + "version": "${PackageVersion}", + "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x64" } }, @@ -249,6 +304,7 @@ "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x86", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x86" } }, diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in similarity index 55% rename from src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets rename to src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in index c93e50175e9..26766076edc 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in @@ -1,4 +1,7 @@ + + ${PackageVersion} + true $(WasmNativeWorkload) @@ -21,30 +24,46 @@ - + + + + + + + + + + - - + + + + + + + - - - + + + + + + - - - - - - - - + + @@ -54,4 +73,43 @@ + + <_MonoWorkloadTargetsMobile>true + <_MonoWorkloadRuntimePackPackageVersion>$(RuntimePackInWorkloadVersion) + + + + + + + + + From 67ecbe844f5fd69b35024f40faa615fb7557b82a Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 12 Jul 2021 23:22:17 +0200 Subject: [PATCH 449/926] File.Unix: Replace: increase Windows compatibility (#50234) * File.Unix: Replace: increase Windows compatibility - When source and destination are not a file throw UnauthorizedAccessException. - When source and destination are the same file throw IOException. * Include paths in exception message Co-authored-by: Jeff Handley --- .../src/Resources/Strings.resx | 290 ++++++++++++++++++ .../tests/File/Replace.cs | 31 ++ .../src/Resources/Strings.resx | 6 + .../src/System/IO/FileSystem.Unix.cs | 30 ++ 4 files changed, 357 insertions(+) create mode 100644 src/libraries/System.IO.FileSystem/src/Resources/Strings.resx diff --git a/src/libraries/System.IO.FileSystem/src/Resources/Strings.resx b/src/libraries/System.IO.FileSystem/src/Resources/Strings.resx new file mode 100644 index 00000000000..3c391ca6bcd --- /dev/null +++ b/src/libraries/System.IO.FileSystem/src/Resources/Strings.resx @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The target file '{0}' is a directory, not a file. + + + Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O). + + + Handle does not support synchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened asynchronously (that is, it was opened explicitly for overlapped I/O). + + + Invalid File or Directory attributes value. + + + Invalid handle. + + + Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + + + Second path fragment must not be a drive or UNC name. + + + Path must not be a drive. + + + Buffer cannot be null. + + + File name cannot be null. + + + Path cannot be null. + + + Enum value was out of legal range. + + + Specified file length was too large for the file system. + + + Non-negative number required. + + + Positive number required. + + + Empty file name is not legal. + + + Empty path name is not legal. + + + The stream's length cannot be changed. + + + Append access can be requested only in write-only mode. + + + Combining FileMode: {0} with FileAccess: {1} is invalid. + + + Illegal characters in path '{0}'. + + + Invalid seek origin. + + + The directory specified, '{0}', is not a subdirectory of '{1}'. + + + Path cannot be the empty string or all whitespace. + + + Cannot create '{0}' because a file or directory with the same name already exists. + + + BindHandle for ThreadPool failed on this handle. + + + The specified directory '{0}' cannot be created. + + + The source '{0}' and destination '{1}' are the same file. + + + Unable to read beyond the end of the stream. + + + The file '{0}' already exists. + + + Unable to find the specified file. + + + Could not find file '{0}'. + + + The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss. + + + The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. + + + IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations. + + + The specified path '{0}' is not a file. + + + Could not find a part of the path. + + + Could not find a part of the path '{0}'. + + + The specified file name or path is too long, or a component of the specified path is too long. + + + Unable seek backward to overwrite data that previously existed in a file opened in Append mode. + + + Unable to truncate data that previously existed in a file opened in Append mode. + + + The process cannot access the file '{0}' because it is being used by another process. + + + The process cannot access the file because it is being used by another process. + + + Source and destination path must be different. + + + Source and destination path must have identical roots. Move will not work across volumes. + + + Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run. + + + [Unknown] + + + Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader. + + + Stream does not support reading. + + + Stream does not support seeking. + + + Stream does not support writing. + + + Cannot access a closed file. + + + Access to the path is denied. + + + Access to the path '{0}' is denied. + + + Cannot access a closed stream. + + + File encryption is not supported on this platform. + + + The path '{0}' is too long, or a component of the specified path is too long. + + diff --git a/src/libraries/System.IO.FileSystem/tests/File/Replace.cs b/src/libraries/System.IO.FileSystem/tests/File/Replace.cs index 2cd2a3011a3..8bebeef1bb0 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Replace.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Replace.cs @@ -109,6 +109,37 @@ namespace System.IO.Tests Assert.Throws(() => Replace(testFile, testFile2, "*\0*")); } + [Fact] + public void SourceCannotBeADirectory() + { + string testFile = GetTestFilePath(); + File.Create(testFile).Dispose(); + string testDir = GetTestFilePath(); + Directory.CreateDirectory(testDir); + + Assert.Throws(() => File.Replace(testDir, testFile, null)); + } + + [Fact] + public void DestinationCannotBeADirectory() + { + string testFile = GetTestFilePath(); + File.Create(testFile).Dispose(); + string testDir = GetTestFilePath(); + Directory.CreateDirectory(testDir); + + Assert.Throws(() => File.Replace(testFile, testDir, null)); + } + + [Fact] + public void SourceAndDestinationCannotBeTheSame() + { + string testFile = GetTestFilePath(); + File.Create(testFile).Dispose(); + + Assert.Throws(() => File.Replace(testFile, testFile, null)); + } + #endregion } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 23a96351e04..a9910865f00 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3778,6 +3778,12 @@ The specified directory '{0}' cannot be created. + + The source '{0}' and destination '{1}' are the same file. + + + The specified path '{0}' is not a file. + Source and destination path must be different. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index c80e585eda8..0151499893a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -103,6 +103,36 @@ namespace System.IO public static void ReplaceFile(string sourceFullPath, string destFullPath, string? destBackupFullPath, bool ignoreMetadataErrors) { + // Unix rename works in more cases, we limit to what is allowed by Windows File.Replace. + // These checks are not atomic, the file could change after a check was performed and before it is renamed. + Interop.Sys.FileStatus sourceStat; + if (Interop.Sys.LStat(sourceFullPath, out sourceStat) != 0) + { + Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo(); + throw Interop.GetExceptionForIoErrno(errno, sourceFullPath); + } + // Check source is not a directory. + if ((sourceStat.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) + { + throw new UnauthorizedAccessException(SR.Format(SR.IO_NotAFile, sourceFullPath)); + } + + Interop.Sys.FileStatus destStat; + if (Interop.Sys.LStat(destFullPath, out destStat) == 0) + { + // Check destination is not a directory. + if ((destStat.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) + { + throw new UnauthorizedAccessException(SR.Format(SR.IO_NotAFile, destFullPath)); + } + // Check source and destination are not the same. + if (sourceStat.Dev == destStat.Dev && + sourceStat.Ino == destStat.Ino) + { + throw new IOException(SR.Format(SR.IO_CannotReplaceSameFile, sourceFullPath, destFullPath)); + } + } + if (destBackupFullPath != null) { // We're backing up the destination file to the backup file, so we need to first delete the backup From d91e11a2bc2f21f3f9df6e16603aa097bea1bd16 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 12 Jul 2021 14:47:52 -0700 Subject: [PATCH 450/926] Look up the ICustomMarshaler implementation methods based on runtime type (#55439) --- src/coreclr/vm/custommarshalerinfo.cpp | 20 ++++--- .../Primitives/ICustomMarshaler.cs | 56 ++++++++++++++++++- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/custommarshalerinfo.cpp b/src/coreclr/vm/custommarshalerinfo.cpp index 67acbff136e..0af48f8e715 100644 --- a/src/coreclr/vm/custommarshalerinfo.cpp +++ b/src/coreclr/vm/custommarshalerinfo.cpp @@ -67,13 +67,6 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type STRINGREF CookieStringObj = StringObject::NewString(strCookie, cCookieStrBytes); GCPROTECT_BEGIN(CookieStringObj); #endif - - // Load the method desc's for all the methods in the ICustomMarshaler interface. - m_pMarshalNativeToManagedMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalNativeToManaged, hndCustomMarshalerType); - m_pMarshalManagedToNativeMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalManagedToNative, hndCustomMarshalerType); - m_pCleanUpNativeDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpNativeData, hndCustomMarshalerType); - m_pCleanUpManagedDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpManagedData, hndCustomMarshalerType); - // Load the method desc for the static method to retrieve the instance. MethodDesc *pGetCustomMarshalerMD = GetCustomMarshalerMD(CustomMarshalerMethods_GetInstance, hndCustomMarshalerType); @@ -103,7 +96,9 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type }; // Call the GetCustomMarshaler method to retrieve the custom marshaler to use. - OBJECTREF CustomMarshalerObj = getCustomMarshaler.Call_RetOBJECTREF(GetCustomMarshalerArgs); + OBJECTREF CustomMarshalerObj = NULL; + GCPROTECT_BEGIN(CustomMarshalerObj); + CustomMarshalerObj = getCustomMarshaler.Call_RetOBJECTREF(GetCustomMarshalerArgs); if (!CustomMarshalerObj) { DefineFullyQualifiedNameForClassW() @@ -111,7 +106,16 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type IDS_EE_NOCUSTOMMARSHALER, GetFullyQualifiedNameForClassW(hndCustomMarshalerType.GetMethodTable())); } + // Load the method desc's for all the methods in the ICustomMarshaler interface based on the type of the marshaler object. + TypeHandle customMarshalerObjType = CustomMarshalerObj->GetMethodTable(); + + m_pMarshalNativeToManagedMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalNativeToManaged, customMarshalerObjType); + m_pMarshalManagedToNativeMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalManagedToNative, customMarshalerObjType); + m_pCleanUpNativeDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpNativeData, customMarshalerObjType); + m_pCleanUpManagedDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpManagedData, customMarshalerObjType); + m_hndCustomMarshaler = pLoaderAllocator->AllocateHandle(CustomMarshalerObj); + GCPROTECT_END(); // Retrieve the size of the native data. if (m_bDataIsByValue) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs index 271c7485d19..78d7d541872 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs @@ -365,7 +365,7 @@ namespace System.Runtime.InteropServices.Tests { Assert.Throws(() => NonICustomMarshalerMethod("")); } - + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] public static extern int NonICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(string))] string str); @@ -509,7 +509,7 @@ namespace System.Runtime.InteropServices.Tests [Fact] public void Parameter_GetInstanceMethodThrows_ThrowsActualException() - { + { Assert.Throws(() => ThrowingGetInstanceMethod("")); } @@ -588,6 +588,58 @@ namespace System.Runtime.InteropServices.Tests [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] public static extern int StructWithCustomMarshalerFieldMethod(StructWithCustomMarshalerField c); + + [Fact] + public void Parameter_DifferentCustomMarshalerType_MarshalsCorrectly() + { + Assert.Equal(234, DifferentCustomMarshalerType("5678")); + } + + public class OuterCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) => throw new NotImplementedException(); + public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException(); + + public int GetNativeDataSize() => throw new NotImplementedException(); + + public IntPtr MarshalManagedToNative(object ManagedObj) => throw new NotImplementedException(); + public object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException(); + + public static ICustomMarshaler GetInstance(string cookie) => new InnerCustomMarshaler(); + + private interface ILargeInterface + { + void Method1(); + void Method2(); + void Method3(); + void Method4(); + void Method5(); + void Method6(); + } + + private class InnerCustomMarshaler : ILargeInterface, ICustomMarshaler + { + public void Method1() => throw new InvalidOperationException(); + public void Method2() => throw new InvalidOperationException(); + public void Method3() => throw new InvalidOperationException(); + public void Method4() => throw new InvalidOperationException(); + public void Method5() => throw new InvalidOperationException(); + public void Method6() => throw new InvalidOperationException(); + + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData); + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int DifferentCustomMarshalerType([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OuterCustomMarshaler))] string str); + + public static int Main(String[] args) { return new ICustomMarshalerTests().RunTests(); From 1509b1a0e66aae1504137488fb25c00bf822e3e6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 12 Jul 2021 15:55:26 -0700 Subject: [PATCH 451/926] Fix more alloc-dealloc mismatches and use-after-scopes (#55420) * Fix another dynamically-sized allocation to use new/delete instead of the mismatched new[]/delete. * Fix use-after-scope * Fix another alloc-dealloc mismatch * Update src/coreclr/vm/threadstatics.cpp Co-authored-by: Jan Kotas * Use standard size_t instead of custom SIZE_T typedef. * Fix formatting. Co-authored-by: Jan Kotas --- src/coreclr/jit/lsrabuild.cpp | 12 ++++++------ src/coreclr/vm/threadstatics.cpp | 8 +++----- src/coreclr/vm/threadstatics.h | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index f5f4d781d75..da7cb15b7cd 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -3559,6 +3559,7 @@ int LinearScan::BuildReturn(GenTree* tree) noway_assert(op1->IsMultiRegCall() || op1->IsMultiRegLclVar()); int srcCount; + ReturnTypeDesc nonCallRetTypeDesc; const ReturnTypeDesc* pRetTypeDesc; if (op1->OperIs(GT_CALL)) { @@ -3567,13 +3568,12 @@ int LinearScan::BuildReturn(GenTree* tree) else { assert(compiler->lvaEnregMultiRegVars); - LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); - ReturnTypeDesc retTypeDesc; - retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), - compiler->info.compCallConv); - pRetTypeDesc = &retTypeDesc; + LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum()); + nonCallRetTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), + compiler->info.compCallConv); + pRetTypeDesc = &nonCallRetTypeDesc; assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt == - retTypeDesc.GetReturnRegCount()); + nonCallRetTypeDesc.GetReturnRegCount()); } srcCount = pRetTypeDesc->GetReturnRegCount(); // For any source that's coming from a different register file, we need to ensure that diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 2644d7ad5fc..bac0f082fb8 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -97,7 +97,7 @@ void ThreadLocalBlock::FreeTable() SpinLock::Holder lock(&m_TLMTableLock); // Free the table itself - delete m_pTLMTable; + delete[] m_pTLMTable; m_pTLMTable = NULL; } @@ -136,7 +136,7 @@ void ThreadLocalBlock::EnsureModuleIndex(ModuleIndex index) // If this allocation fails, we will throw. If it succeeds, // then we are good to go - PTR_TLMTableEntry pNewModuleSlots = (PTR_TLMTableEntry) (void*) new BYTE[sizeof(TLMTableEntry) * aModuleIndices]; + PTR_TLMTableEntry pNewModuleSlots = new TLMTableEntry[aModuleIndices]; // Zero out the new TLM table memset(pNewModuleSlots, 0 , sizeof(TLMTableEntry) * aModuleIndices); @@ -704,9 +704,7 @@ PTR_ThreadLocalModule ThreadStatics::AllocateTLM(Module * pModule) SIZE_T size = pModule->GetThreadLocalModuleSize(); - _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob()); - - PTR_ThreadLocalModule pThreadLocalModule = (ThreadLocalModule*)new BYTE[size]; + PTR_ThreadLocalModule pThreadLocalModule = new({ pModule }) ThreadLocalModule; // We guarantee alignment for 64-bit regular thread statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons. diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 1755bf7230d..ddb59b5cbc2 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -450,6 +450,20 @@ public: return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG; } + void* operator new(size_t) = delete; + + struct ParentModule { PTR_Module pModule; }; + + void* operator new(size_t baseSize, ParentModule parentModule) + { + size_t size = parentModule.pModule->GetThreadLocalModuleSize(); + + _ASSERTE(size >= baseSize); + _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob()); + + return ::operator new(size); + } + #ifndef DACCESS_COMPILE FORCEINLINE void EnsureClassAllocated(MethodTable * pMT) From f787f38f4b75982baf9091c08eb6e71ed7e3f0c1 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Mon, 12 Jul 2021 16:14:26 -0700 Subject: [PATCH 452/926] Improve perf of Utf8Parser.TryParse(out [u]long, default) (#52423) --- .../Utf8Parser/Utf8Parser.Integer.Signed.D.cs | 162 ++++++++++-------- .../Utf8Parser.Integer.Unsigned.D.cs | 104 +++++------ 2 files changed, 143 insertions(+), 123 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs index ee2e046901f..07723d04810 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs @@ -336,107 +336,121 @@ namespace System.Buffers.Text private static bool TryParseInt64D(ReadOnlySpan source, out long value, out int bytesConsumed) { - if (source.Length < 1) - { - bytesConsumed = 0; - value = default; - return false; - } + long sign = 0; // 0 if the value is positive, -1 if the value is negative + int idx = 0; - int indexOfFirstDigit = 0; - int sign = 1; - if (source[0] == '-') - { - indexOfFirstDigit = 1; - sign = -1; + // We use 'nuint' for the firstChar and nextChar data types in this method because + // it gives us a free early zero-extension to 64 bits when running on a 64-bit platform. - if (source.Length <= indexOfFirstDigit) + nuint firstChar; + while (true) + { + if ((uint)idx >= (uint)source.Length) { goto FalseExit; } + firstChar = (uint)source[idx] - '0'; + if ((uint)firstChar <= 9) { break; } + + // We saw something that wasn't a digit. If it's a '+' or a '-', + // we'll set the 'sign' value appropriately and resume the "read + // first char" loop from the next index. If this loops more than + // once (idx != 0), it means we saw a sign character followed by + // a non-digit character, which should be considered an error. + + if (idx != 0) { - bytesConsumed = 0; - value = default; - return false; + goto FalseExit; } - } - else if (source[0] == '+') - { - indexOfFirstDigit = 1; - if (source.Length <= indexOfFirstDigit) + idx++; + + if ((uint)firstChar == unchecked((uint)('-' - '0'))) { - bytesConsumed = 0; - value = default; - return false; + sign--; // set to -1 + } + else if ((uint)firstChar != unchecked((uint)('+' - '0'))) + { + goto FalseExit; // not a digit, not '-', and not '+'; fail } } - int overflowLength = ParserHelpers.Int64OverflowLength + indexOfFirstDigit; + ulong parsedValue = firstChar; + int overflowLength = ParserHelpers.Int64OverflowLength + idx; // +idx to account for any sign char we read + idx++; - // Parse the first digit separately. If invalid here, we need to return false. - long firstDigit = source[indexOfFirstDigit] - 48; // '0' - if (firstDigit < 0 || firstDigit > 9) - { - bytesConsumed = 0; - value = default; - return false; - } - ulong parsedValue = (ulong)firstDigit; + // At this point, we successfully read a single digit character. + // The only failure condition from here on out is integer overflow. if (source.Length < overflowLength) { - // Length is less than Parsers.Int64OverflowLength; overflow is not possible - for (int index = indexOfFirstDigit + 1; index < source.Length; index++) + // If the input span is short enough such that integer overflow isn't an issue, + // don't bother performing overflow checks. Just keep shifting in new digits + // until we see a non-digit character or until we've exhausted our input buffer. + + while (true) { - long nextDigit = source[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) - { - bytesConsumed = index; - value = ((long)parsedValue) * sign; - return true; - } - parsedValue = parsedValue * 10 + (ulong)nextDigit; + if ((uint)idx >= (uint)source.Length) { break; } // EOF + nuint nextChar = (uint)source[idx] - '0'; + if ((uint)nextChar > 9) { break; } // not a digit + parsedValue = parsedValue * 10 + nextChar; + idx++; } } else { - // Length is greater than Parsers.Int64OverflowLength; overflow is only possible after Parsers.Int64OverflowLength - // digits. There may be no overflow after Parsers.Int64OverflowLength if there are leading zeroes. - for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++) + while (true) { - long nextDigit = source[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) + if ((uint)idx >= (uint)source.Length) { break; } // EOF + nuint nextChar = (uint)source[idx] - '0'; + if ((uint)nextChar > 9) { break; } // not a digit + idx++; + + // The const below is the smallest unsigned x for which "x * 10 + 9" + // might overflow long.MaxValue. If the current accumulator is below + // this const, there's no risk of overflowing. + + const ulong OverflowRisk = 0x0CCC_CCCC_CCCC_CCCCul; + + if (parsedValue < OverflowRisk) { - bytesConsumed = index; - value = ((long)parsedValue) * sign; - return true; + parsedValue = parsedValue * 10 + nextChar; + continue; } - parsedValue = parsedValue * 10 + (ulong)nextDigit; - } - for (int index = overflowLength - 1; index < source.Length; index++) - { - long nextDigit = source[index] - 48; // '0' - if (nextDigit < 0 || nextDigit > 9) + + // If the current accumulator is exactly equal to the const above, + // then "accumulator * 10 + 7" is the highest we can go without overflowing + // long.MaxValue. (If we know the value is negative, we can instead allow + // +8, since the range of negative numbers is one higher than the range of + // positive numbers.) This also implies that if the current accumulator + // is higher than the const above, there's no hope that we'll succeed, + // so we may as well just fail now. + // + // The (nextChar + sign) trick below works because sign is 0 or -1, + // so if sign is -1 then this actually checks that nextChar > 8. + // n.b. signed arithmetic below because nextChar may be 0. + + if (parsedValue != OverflowRisk || (int)nextChar + (int)sign > 7) { - bytesConsumed = index; - value = ((long)parsedValue) * sign; - return true; + goto FalseExit; } - // If parsedValue > (long.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (long.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. - bool positive = sign > 0; - bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7); - if (parsedValue > long.MaxValue / 10 || parsedValue == long.MaxValue / 10 && nextDigitTooLarge) - { - bytesConsumed = 0; - value = default; - return false; - } - parsedValue = parsedValue * 10 + (ulong)nextDigit; + + parsedValue = OverflowRisk * 10 + nextChar; } } - bytesConsumed = source.Length; - value = ((long)parsedValue) * sign; + // 'sign' is 0 for non-negative and -1 for negative. This allows us to perform + // cheap arithmetic + bitwise operations to mimic a multiplication by 1 or -1 + // without incurring the cost of an actual multiplication operation. + // + // If sign = 0, this becomes value = (parsedValue ^ 0) - 0 = parsedValue + // If sign = -1, this becomes value = (parsedValue ^ -1) - (-1) = ~parsedValue + 1 = -parsedValue + + bytesConsumed = idx; + value = ((long)parsedValue ^ sign) - sign; return true; + + FalseExit: + bytesConsumed = 0; + value = default; + return false; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs index 0b9cca720b4..dd4572afaf7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs @@ -277,77 +277,83 @@ namespace System.Buffers.Text private static bool TryParseUInt64D(ReadOnlySpan source, out ulong value, out int bytesConsumed) { - if (source.Length < 1) + if (source.IsEmpty) { - bytesConsumed = 0; - value = default; - return false; + goto FalseExit; } + // We use 'nuint' for the firstDigit and nextChar data types in this method because + // it gives us a free early zero-extension to 64 bits when running on a 64-bit platform. + // // Parse the first digit separately. If invalid here, we need to return false. - ulong firstDigit = source[0] - 48u; // '0' - if (firstDigit > 9) - { - bytesConsumed = 0; - value = default; - return false; - } + + nuint firstDigit = (uint)source[0] - '0'; + if ((uint)firstDigit > 9) { goto FalseExit; } ulong parsedValue = firstDigit; - if (source.Length < ParserHelpers.Int64OverflowLength) + // At this point, we successfully read a single digit character. + // The only failure condition from here on out is integer overflow. + + int idx = 1; + if (source.Length < ParserHelpers.UInt64OverflowLength) { - // Length is less than Parsers.Int64OverflowLength; overflow is not possible - for (int index = 1; index < source.Length; index++) + // If the input span is short enough such that integer overflow isn't an issue, + // don't bother performing overflow checks. Just keep shifting in new digits + // until we see a non-digit character or until we've exhausted our input buffer. + + while (true) { - ulong nextDigit = source[index] - 48u; // '0' - if (nextDigit > 9) - { - bytesConsumed = index; - value = parsedValue; - return true; - } - parsedValue = parsedValue * 10 + nextDigit; + if ((uint)idx >= (uint)source.Length) { break; } // EOF + nuint nextChar = (uint)source[idx] - '0'; + if ((uint)nextChar > 9) { break; } // not a digit + parsedValue = parsedValue * 10 + nextChar; + idx++; } } else { - // Length is greater than Parsers.Int64OverflowLength; overflow is only possible after Parsers.Int64OverflowLength - // digits. There may be no overflow after Parsers.Int64OverflowLength if there are leading zeroes. - for (int index = 1; index < ParserHelpers.Int64OverflowLength - 1; index++) + while (true) { - ulong nextDigit = source[index] - 48u; // '0' - if (nextDigit > 9) + if ((uint)idx >= (uint)source.Length) { break; } // EOF + nuint nextChar = (uint)source[idx] - '0'; + if ((uint)nextChar > 9) { break; } // not a digit + idx++; + + // The const below is the smallest unsigned x for which "x * 10 + 9" + // might overflow ulong.MaxValue. If the current accumulator is below + // this const, there's no risk of overflowing. + + const ulong OverflowRisk = 0x1999_9999_9999_9999ul; + + if (parsedValue < OverflowRisk) { - bytesConsumed = index; - value = parsedValue; - return true; + parsedValue = parsedValue * 10 + nextChar; + continue; } - parsedValue = parsedValue * 10 + nextDigit; - } - for (int index = ParserHelpers.Int64OverflowLength - 1; index < source.Length; index++) - { - ulong nextDigit = source[index] - 48u; // '0' - if (nextDigit > 9) + + // If the current accumulator is exactly equal to the const above, + // then "accumulator * 10 + 5" is the highest we can go without overflowing + // ulong.MaxValue. This also implies that if the current accumulator + // is higher than the const above, there's no hope that we'll succeed, + // so we may as well just fail now. + + if (parsedValue != OverflowRisk || (uint)nextChar > 5) { - bytesConsumed = index; - value = parsedValue; - return true; + goto FalseExit; } - // If parsedValue > (ulong.MaxValue / 10), any more appended digits will cause overflow. - // if parsedValue == (ulong.MaxValue / 10), any nextDigit greater than 5 implies overflow. - if (parsedValue > ulong.MaxValue / 10 || (parsedValue == ulong.MaxValue / 10 && nextDigit > 5)) - { - bytesConsumed = 0; - value = default; - return false; - } - parsedValue = parsedValue * 10 + nextDigit; + + parsedValue = OverflowRisk * 10 + nextChar; } } - bytesConsumed = source.Length; + bytesConsumed = idx; value = parsedValue; return true; + + FalseExit: + bytesConsumed = 0; + value = default; + return false; } } } From da7bce76053ebd5c5860b838c6b2b9d83471d6ca Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 19:39:21 -0400 Subject: [PATCH 453/926] Change RandomAccess.Write* methods to be void-returning (#55490) --- .../tests/RandomAccess/Mixed.Windows.cs | 22 +- .../tests/RandomAccess/NoBuffering.Windows.cs | 26 +- .../tests/RandomAccess/Write.cs | 18 +- .../tests/RandomAccess/WriteAsync.cs | 15 +- .../tests/RandomAccess/WriteGather.cs | 18 +- .../tests/RandomAccess/WriteGatherAsync.cs | 17 +- ...andle.OverlappedValueTaskSource.Windows.cs | 6 +- ...afeFileHandle.ThreadPoolValueTaskSource.cs | 29 +-- .../src/System/IO/RandomAccess.Unix.cs | 131 +++++++--- .../src/System/IO/RandomAccess.Windows.cs | 228 ++++++++++-------- .../src/System/IO/RandomAccess.cs | 27 +-- .../IO/Strategies/OSFileStreamStrategy.cs | 13 +- .../IO/Strategies/UnixFileStreamStrategy.cs | 9 +- .../src/System/Threading/Tasks/ValueTask.cs | 25 -- .../System.Runtime/ref/System.Runtime.cs | 8 +- 15 files changed, 347 insertions(+), 245 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs index dcc7d47924f..cee09fe2d62 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Mixed.Windows.cs @@ -62,8 +62,17 @@ namespace System.IO.Tests { writeBuffer[0] = (byte)fileOffset; - Assert.Equal(writeBuffer.Length, syncWrite ? RandomAccess.Write(handle, writeBuffer, fileOffset) : await RandomAccess.WriteAsync(handle, writeBuffer, fileOffset)); + if (syncWrite) + { + RandomAccess.Write(handle, writeBuffer, fileOffset); + } + else + { + await RandomAccess.WriteAsync(handle, writeBuffer, fileOffset); + } + Assert.Equal(writeBuffer.Length, syncRead ? RandomAccess.Read(handle, readBuffer, fileOffset) : await RandomAccess.ReadAsync(handle, readBuffer, fileOffset)); + Assert.Equal(writeBuffer[0], readBuffer[0]); fileOffset += 1; @@ -116,8 +125,17 @@ namespace System.IO.Tests writeBuffer_1[0] = (byte)fileOffset; writeBuffer_2[0] = (byte)(fileOffset+1); - Assert.Equal(writeBuffer_1.Length + writeBuffer_2.Length, syncWrite ? RandomAccess.Write(handle, writeBuffers, fileOffset) : await RandomAccess.WriteAsync(handle, writeBuffers, fileOffset)); + if (syncWrite) + { + RandomAccess.Write(handle, writeBuffers, fileOffset); + } + else + { + await RandomAccess.WriteAsync(handle, writeBuffers, fileOffset); + } + Assert.Equal(writeBuffer_1.Length + writeBuffer_2.Length, syncRead ? RandomAccess.Read(handle, readBuffers, fileOffset) : await RandomAccess.ReadAsync(handle, readBuffers, fileOffset)); + Assert.Equal(writeBuffer_1[0], readBuffer_1[0]); Assert.Equal(writeBuffer_2[0], readBuffer_2[0]); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs index 585335c4cdc..d897cc7b8be 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/NoBuffering.Windows.cs @@ -118,9 +118,16 @@ namespace System.IO.Tests int take = Math.Min(content.Length - total, bufferSize); content.AsSpan(total, take).CopyTo(buffer.GetSpan()); - total += async - ? await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset: total) - : RandomAccess.Write(handle, buffer.GetSpan(), fileOffset: total); + if (async) + { + await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset: total); + } + else + { + RandomAccess.Write(handle, buffer.GetSpan(), fileOffset: total); + } + + total += buffer.Memory.Length; } } @@ -154,9 +161,16 @@ namespace System.IO.Tests content.AsSpan((int)total, bufferSize).CopyTo(buffer_1.GetSpan()); content.AsSpan((int)total + bufferSize, bufferSize).CopyTo(buffer_2.GetSpan()); - total += async - ? await RandomAccess.WriteAsync(handle, buffers, fileOffset: total) - : RandomAccess.Write(handle, buffers, fileOffset: total); + if (async) + { + await RandomAccess.WriteAsync(handle, buffers, fileOffset: total); + } + else + { + RandomAccess.Write(handle, buffers, fileOffset: total); + } + + total += buffer_1.Memory.Length + buffer_2.Memory.Length; } } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs index 3cac633f53f..8a687dc6012 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs @@ -10,7 +10,10 @@ namespace System.IO.Tests public class RandomAccess_Write : RandomAccess_Base { protected override int MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) - => RandomAccess.Write(handle, bytes, fileOffset); + { + RandomAccess.Write(handle, bytes, fileOffset); + return bytes?.Length ?? 0; + } [Theory] [MemberData(nameof(GetSyncAsyncOptions))] @@ -24,11 +27,11 @@ namespace System.IO.Tests [Theory] [MemberData(nameof(GetSyncAsyncOptions))] - public void WriteUsingEmptyBufferReturnsZero(FileOptions options) + public void WriteUsingEmptyBufferReturns(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(0, RandomAccess.Write(handle, Array.Empty(), fileOffset: 0)); + RandomAccess.Write(handle, Array.Empty(), fileOffset: 0); } } @@ -41,7 +44,7 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(stackAllocated.Length, RandomAccess.Write(handle, stackAllocated, fileOffset: 0)); + RandomAccess.Write(handle, stackAllocated, fileOffset: 0); } Assert.Equal(stackAllocated.ToArray(), File.ReadAllBytes(filePath)); @@ -58,17 +61,14 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { int total = 0; - int current = 0; while (total != fileSize) { Span buffer = content.AsSpan(total, Math.Min(content.Length - total, fileSize / 4)); - current = RandomAccess.Write(handle, buffer, fileOffset: total); + RandomAccess.Write(handle, buffer, fileOffset: total); - Assert.InRange(current, 0, buffer.Length); - - total += current; + total += buffer.Length; } } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs index b5c2399f964..37003283982 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs @@ -11,9 +11,9 @@ namespace System.IO.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [SkipOnPlatform(TestPlatforms.Browser, "async file IO is not supported on browser")] - public class RandomAccess_WriteAsync : RandomAccess_Base> + public class RandomAccess_WriteAsync : RandomAccess_Base { - protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) + protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, bytes, fileOffset); [Theory] @@ -44,11 +44,11 @@ namespace System.IO.Tests [Theory] [MemberData(nameof(GetSyncAsyncOptions))] - public async Task WriteUsingEmptyBufferReturnsZeroAsync(FileOptions options) + public async Task WriteUsingEmptyBufferReturnsAsync(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(0, await RandomAccess.WriteAsync(handle, Array.Empty(), fileOffset: 0)); + await RandomAccess.WriteAsync(handle, Array.Empty(), fileOffset: 0); } } @@ -63,17 +63,14 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { int total = 0; - int current = 0; while (total != fileSize) { Memory buffer = content.AsMemory(total, Math.Min(content.Length - total, fileSize / 4)); - current = await RandomAccess.WriteAsync(handle, buffer, fileOffset: total); + await RandomAccess.WriteAsync(handle, buffer, fileOffset: total); - Assert.InRange(current, 0, buffer.Length); - - total += current; + total += buffer.Length; } } diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs index 5af52f0c61a..0f28b7c9c68 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs @@ -12,7 +12,10 @@ namespace System.IO.Tests public class RandomAccess_WriteGather : RandomAccess_Base { protected override long MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) - => RandomAccess.Write(handle, new ReadOnlyMemory[] { bytes }, fileOffset); + { + RandomAccess.Write(handle, new ReadOnlyMemory[] { bytes }, fileOffset); + return bytes?.Length ?? 0; + } [Theory] [MemberData(nameof(GetSyncAsyncOptions))] @@ -36,11 +39,11 @@ namespace System.IO.Tests [Theory] [MemberData(nameof(GetSyncAsyncOptions))] - public void WriteUsingEmptyBufferReturnsZero(FileOptions options) + public void WriteUsingEmptyBufferReturns(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(0, RandomAccess.Write(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0)); + RandomAccess.Write(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0); } } @@ -55,7 +58,6 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { long total = 0; - long current = 0; while (total != fileSize) { @@ -63,7 +65,7 @@ namespace System.IO.Tests Memory buffer_1 = content.AsMemory((int)total, firstBufferLength); Memory buffer_2 = content.AsMemory((int)total + firstBufferLength); - current = RandomAccess.Write( + RandomAccess.Write( handle, new ReadOnlyMemory[] { @@ -73,9 +75,7 @@ namespace System.IO.Tests }, fileOffset: total); - Assert.InRange(current, 0, buffer_1.Length + buffer_2.Length); - - total += current; + total += buffer_1.Length + buffer_2.Length; } } @@ -94,7 +94,7 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(repeatCount, RandomAccess.Write(handle, buffers, fileOffset: 0)); + RandomAccess.Write(handle, buffers, fileOffset: 0); } byte[] actualContent = File.ReadAllBytes(filePath); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs index d6bd235efb7..f89cfd3b4fc 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs @@ -13,9 +13,9 @@ namespace System.IO.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [SkipOnPlatform(TestPlatforms.Browser, "async file IO is not supported on browser")] - public class RandomAccess_WriteGatherAsync : RandomAccess_Base> + public class RandomAccess_WriteGatherAsync : RandomAccess_Base { - protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) + protected override ValueTask MethodUnderTest(SafeFileHandle handle, byte[] bytes, long fileOffset) => RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { bytes }, fileOffset); [Theory] @@ -56,11 +56,11 @@ namespace System.IO.Tests [Theory] [MemberData(nameof(GetSyncAsyncOptions))] - public async Task WriteUsingEmptyBufferReturnsZeroAsync(FileOptions options) + public async Task WriteUsingEmptyBufferReturnsAsync(FileOptions options) { using (SafeFileHandle handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(0, await RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0)); + await RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { Array.Empty() }, fileOffset: 0); } } @@ -75,7 +75,6 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, options)) { long total = 0; - long current = 0; while (total != fileSize) { @@ -83,7 +82,7 @@ namespace System.IO.Tests Memory buffer_1 = content.AsMemory((int)total, firstBufferLength); Memory buffer_2 = content.AsMemory((int)total + firstBufferLength); - current = await RandomAccess.WriteAsync( + await RandomAccess.WriteAsync( handle, new ReadOnlyMemory[] { @@ -93,9 +92,7 @@ namespace System.IO.Tests }, fileOffset: total); - Assert.InRange(current, 0, buffer_1.Length + buffer_2.Length); - - total += current; + total += buffer_1.Length + buffer_2.Length; } } @@ -114,7 +111,7 @@ namespace System.IO.Tests using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal(repeatCount, await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0)); + await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); } byte[] actualContent = File.ReadAllBytes(filePath); diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs index cd4e18e7486..367184e9a45 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs @@ -86,10 +86,8 @@ namespace Microsoft.Win32.SafeHandles public ValueTaskSourceStatus GetStatus(short token) => _source.GetStatus(token); public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _source.OnCompleted(continuation, state, token, flags); - void IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); - int IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); - - private int GetResultAndRelease(short token) + void IValueTaskSource.GetResult(short token) => GetResult(token); + public int GetResult(short token) { try { diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs index 49638af80f3..abb6238b521 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs @@ -54,7 +54,15 @@ namespace Microsoft.Win32.SafeHandles Debug.Assert(op == Operation.None, $"An operation was queued before the previous {op}'s completion."); } - private long GetResultAndRelease(short token) + public ValueTaskSourceStatus GetStatus(short token) => + _source.GetStatus(token); + + public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _source.OnCompleted(continuation, state, token, flags); + + void IValueTaskSource.GetResult(short token) => GetResult(token); + int IValueTaskSource.GetResult(short token) => (int)GetResult(token); + public long GetResult(short token) { try { @@ -67,13 +75,6 @@ namespace Microsoft.Win32.SafeHandles } } - public ValueTaskSourceStatus GetStatus(short token) => _source.GetStatus(token); - public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => - _source.OnCompleted(continuation, state, token, flags); - int IValueTaskSource.GetResult(short token) => (int) GetResultAndRelease(token); - long IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); - void IValueTaskSource.GetResult(short token) => GetResultAndRelease(token); - private void ExecuteInternal() { Debug.Assert(_operation >= Operation.Read && _operation <= Operation.WriteGather); @@ -96,7 +97,7 @@ namespace Microsoft.Win32.SafeHandles result = RandomAccess.ReadAtOffset(_fileHandle, writableSingleSegment.Span, _fileOffset); break; case Operation.Write: - result = RandomAccess.WriteAtOffset(_fileHandle, _singleSegment.Span, _fileOffset); + RandomAccess.WriteAtOffset(_fileHandle, _singleSegment.Span, _fileOffset); break; case Operation.ReadScatter: Debug.Assert(_readScatterBuffers != null); @@ -104,7 +105,7 @@ namespace Microsoft.Win32.SafeHandles break; case Operation.WriteGather: Debug.Assert(_writeGatherBuffers != null); - result = RandomAccess.WriteGatherAtOffset(_fileHandle, _writeGatherBuffers, _fileOffset); + RandomAccess.WriteGatherAtOffset(_fileHandle, _writeGatherBuffers, _fileOffset); break; } } @@ -164,7 +165,7 @@ namespace Microsoft.Win32.SafeHandles return new ValueTask(this, _source.Version); } - public ValueTask QueueWrite(ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + public ValueTask QueueWrite(ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) { ValidateInvariants(); @@ -174,7 +175,7 @@ namespace Microsoft.Win32.SafeHandles _cancellationToken = cancellationToken; QueueToThreadPool(); - return new ValueTask(this, _source.Version); + return new ValueTask(this, _source.Version); } public ValueTask QueueReadScatter(IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) @@ -190,7 +191,7 @@ namespace Microsoft.Win32.SafeHandles return new ValueTask(this, _source.Version); } - public ValueTask QueueWriteGather(IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) + public ValueTask QueueWriteGather(IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { ValidateInvariants(); @@ -200,7 +201,7 @@ namespace Microsoft.Win32.SafeHandles _cancellationToken = cancellationToken; QueueToThreadPool(); - return new ValueTask(this, _source.Version); + return new ValueTask(this, _source.Version); } private enum Operation : byte diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index 41de56fcbea..b82627112f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -3,7 +3,9 @@ using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; using System.IO.Strategies; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -79,41 +81,116 @@ namespace System.IO long fileOffset, CancellationToken cancellationToken) => ScheduleSyncReadScatterAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); - internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) + internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { - fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) + while (!buffer.IsEmpty) { - // The Windows implementation uses WriteFile, which ignores the offset if the handle - // isn't seekable. We do the same manually with PWrite vs Write, in order to enable - // the function to be used by FileStream for all the same situations. - int result = handle.CanSeek ? - Interop.Sys.PWrite(handle, bufPtr, buffer.Length, fileOffset) : - Interop.Sys.Write(handle, bufPtr, buffer.Length); - FileStreamHelpers.CheckFileCall(result, handle.Path); - return result; + fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) + { + // The Windows implementation uses WriteFile, which ignores the offset if the handle + // isn't seekable. We do the same manually with PWrite vs Write, in order to enable + // the function to be used by FileStream for all the same situations. + int bytesWritten = handle.CanSeek ? + Interop.Sys.PWrite(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length), fileOffset) : + Interop.Sys.Write(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length)); + + FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path); + if (bytesWritten == buffer.Length) + { + break; + } + + // The write completed successfully but for fewer bytes than requested. + // We need to try again for the remainder. + buffer = buffer.Slice(bytesWritten); + fileOffset += bytesWritten; + } } } - internal static unsafe long WriteGatherAtOffset(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetNumberOfBytesToWrite(int byteCount) { - MemoryHandle[] handles = new MemoryHandle[buffers.Count]; - Span vectors = buffers.Count <= IovStackThreshold ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffers.Count ]; +#if DEBUG + // In debug only, to assist with testing, simulate writing fewer than the requested number of bytes. + if (byteCount > 1 && // ensure we don't turn the read into a zero-byte read + byteCount < 512) // avoid on larger buffers that might have a length used to meet an alignment requirement + { + byteCount /= 2; + } +#endif + return byteCount; + } + + internal static unsafe void WriteGatherAtOffset(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) + { + int buffersCount = buffers.Count; + if (buffersCount == 0) + { + return; + } + + var handles = new MemoryHandle[buffersCount]; + Span vectors = buffersCount <= IovStackThreshold ? + stackalloc Interop.Sys.IOVector[IovStackThreshold] : + new Interop.Sys.IOVector[buffersCount]; - long result; try { - int buffersCount = buffers.Count; - for (int i = 0; i < buffersCount; i++) + int buffersOffset = 0, firstBufferOffset = 0; + while (true) { - ReadOnlyMemory buffer = buffers[i]; - MemoryHandle memoryHandle = buffer.Pin(); - vectors[i] = new Interop.Sys.IOVector { Base = (byte*)memoryHandle.Pointer, Count = (UIntPtr)buffer.Length }; - handles[i] = memoryHandle; - } + long totalBytesToWrite = 0; - fixed (Interop.Sys.IOVector* pinnedVectors = &MemoryMarshal.GetReference(vectors)) - { - result = Interop.Sys.PWriteV(handle, pinnedVectors, buffers.Count, fileOffset); + for (int i = buffersOffset; i < buffersCount; i++) + { + ReadOnlyMemory buffer = buffers[i]; + totalBytesToWrite += buffer.Length; + + MemoryHandle memoryHandle = buffer.Pin(); + vectors[i] = new Interop.Sys.IOVector { Base = firstBufferOffset + (byte*)memoryHandle.Pointer, Count = (UIntPtr)buffer.Length }; + handles[i] = memoryHandle; + + firstBufferOffset = 0; + } + + if (totalBytesToWrite == 0) + { + break; + } + + long bytesWritten; + fixed (Interop.Sys.IOVector* pinnedVectors = &MemoryMarshal.GetReference(vectors)) + { + bytesWritten = Interop.Sys.PWriteV(handle, pinnedVectors, buffersCount, fileOffset); + } + + FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path); + if (bytesWritten == totalBytesToWrite) + { + break; + } + + // The write completed successfully but for fewer bytes than requested. + // We need to try again for the remainder. + for (int i = 0; i < buffersCount; i++) + { + int n = buffers[i].Length; + if (n <= bytesWritten) + { + buffersOffset++; + bytesWritten -= n; + if (bytesWritten == 0) + { + break; + } + } + else + { + firstBufferOffset = (int)(bytesWritten - n); + break; + } + } } } finally @@ -123,14 +200,12 @@ namespace System.IO memoryHandle.Dispose(); } } - - return FileStreamHelpers.CheckFileCall(result, handle.Path); } - internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) => ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); - private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, + private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) => ScheduleSyncWriteGatherAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index 63639cc8503..8711ce23e9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -120,11 +120,17 @@ namespace System.IO } } - internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) + internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { + if (buffer.IsEmpty) + { + return; + } + if (handle.IsAsync) { - return WriteSyncUsingAsyncHandle(handle, buffer, fileOffset); + WriteSyncUsingAsyncHandle(handle, buffer, fileOffset); + return; } NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); @@ -132,22 +138,28 @@ namespace System.IO { if (Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, out int numBytesWritten, &overlapped) != 0) { - return numBytesWritten; + Debug.Assert(numBytesWritten == buffer.Length); + return; } int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_NO_DATA: // EOF on a pipe - return 0; + return; default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } - private static unsafe int WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) + private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { + if (buffer.IsEmpty) + { + return; + } + handle.EnsureThreadPoolBindingInitialized(); CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding!); @@ -173,8 +185,8 @@ namespace System.IO int result = 0; if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) { - Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); - return result; + Debug.Assert(result == buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); + return; } errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); @@ -184,7 +196,7 @@ namespace System.IO { case Interop.Errors.ERROR_NO_DATA: // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. - return 0; + return; case Interop.Errors.ERROR_INVALID_PARAMETER: // ERROR_INVALID_PARAMETER may be returned for writes @@ -209,17 +221,28 @@ namespace System.IO } internal static ValueTask ReadAtOffsetAsync(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) - => handle.IsAsync - ? Map(QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken)) - : ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); + { + if (handle.IsAsync) + { + (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = QueueAsyncReadFile(handle, buffer, fileOffset, cancellationToken); - private static ValueTask Map((SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) tuple) - => tuple.vts != null - ? new ValueTask(tuple.vts, tuple.vts.Version) - : tuple.errorCode == 0 ? ValueTask.FromResult(0) : ValueTask.FromException(Win32Marshal.GetExceptionForWin32Error(tuple.errorCode)); + if (vts is not null) + { + return new ValueTask(vts, vts.Version); + } - internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) QueueAsyncReadFile( - SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) + if (errorCode == 0) + { + return ValueTask.FromResult(0); + } + + return ValueTask.FromException(Win32Marshal.GetExceptionForWin32Error(errorCode)); + } + + return ScheduleSyncReadAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); + } + + internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) QueueAsyncReadFile(SafeFileHandle handle, Memory buffer, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); @@ -269,13 +292,29 @@ namespace System.IO return (vts, -1); } - internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) - => handle.IsAsync - ? Map(QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken)) - : ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); + internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + { + if (handle.IsAsync) + { + (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = QueueAsyncWriteFile(handle, buffer, fileOffset, cancellationToken); - internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) QueueAsyncWriteFile( - SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) + if (vts is not null) + { + return new ValueTask(vts, vts.Version); + } + + if (errorCode == 0) + { + return ValueTask.CompletedTask; + } + + return ValueTask.FromException(Win32Marshal.GetExceptionForWin32Error(errorCode)); + } + + return ScheduleSyncWriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); + } + + internal static unsafe (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) QueueAsyncWriteFile(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); @@ -323,7 +362,8 @@ namespace System.IO long total = 0; // ReadFileScatter does not support sync handles, so we just call ReadFile in a loop - for (int i = 0; i < buffers.Count; i++) + int buffersCount = buffers.Count; + for (int i = 0; i < buffersCount; i++) { Span span = buffers[i].Span; int read = ReadAtOffset(handle, span, fileOffset + total); @@ -340,26 +380,17 @@ namespace System.IO return total; } - internal static long WriteGatherAtOffset(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) + internal static void WriteGatherAtOffset(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) { - long total = 0; - // WriteFileGather does not support sync handles, so we just call WriteFile in a loop - for (int i = 0; i < buffers.Count; i++) + int bytesWritten = 0; + int buffersCount = buffers.Count; + for (int i = 0; i < buffersCount; i++) { ReadOnlySpan span = buffers[i].Span; - int written = WriteAtOffset(handle, span, fileOffset + total); - total += written; - - // We stop on the first incomplete write. - // Most probably the disk became full and the next write is going to throw. - if (written != span.Length) - { - break; - } + WriteAtOffset(handle, span, fileOffset + bytesWritten); + bytesWritten += span.Length; } - - return total; } private static ValueTask ReadScatterAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, @@ -373,7 +404,8 @@ namespace System.IO if (CanUseScatterGatherWindowsAPIs(handle)) { long totalBytes = 0; - for (int i = 0; i < buffers.Count; i++) + int buffersCount = buffers.Count; + for (int i = 0; i < buffersCount; i++) { totalBytes += buffers[i].Length; } @@ -392,25 +424,25 @@ namespace System.IO private static bool CanUseScatterGatherWindowsAPIs(SafeFileHandle handle) => handle.IsAsync && ((handle.GetFileOptions() & SafeFileHandle.NoBuffering) != 0); - private static async ValueTask ReadScatterAtOffsetSingleSyscallAsync(SafeFileHandle handle, - IReadOnlyList> buffers, long fileOffset, int totalBytes, CancellationToken cancellationToken) + private static async ValueTask ReadScatterAtOffsetSingleSyscallAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, int totalBytes, CancellationToken cancellationToken) { - if (buffers.Count == 1) + int buffersCount = buffers.Count; + if (buffersCount == 1) { // we have to await it because we can't cast a VT to VT return await ReadAtOffsetAsync(handle, buffers[0], fileOffset, cancellationToken).ConfigureAwait(false); } // "The array must contain enough elements to store nNumberOfBytesToWrite bytes of data, and one element for the terminating NULL. " - long[] fileSegments = new long[buffers.Count + 1]; - fileSegments[buffers.Count] = 0; + long[] fileSegments = new long[buffersCount + 1]; + fileSegments[buffersCount] = 0; - MemoryHandle[] memoryHandles = new MemoryHandle[buffers.Count]; + MemoryHandle[] memoryHandles = new MemoryHandle[buffersCount]; MemoryHandle pinnedSegments = fileSegments.AsMemory().Pin(); try { - for (int i = 0; i < buffers.Count; i++) + for (int i = 0; i < buffersCount; i++) { Memory buffer = buffers[i]; MemoryHandle memoryHandle = buffer.Pin(); @@ -434,8 +466,7 @@ namespace System.IO } } - private static unsafe ValueTask ReadFileScatterAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, - int bytesToRead, long fileOffset, CancellationToken cancellationToken) + private static unsafe ValueTask ReadFileScatterAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToRead, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); @@ -484,12 +515,12 @@ namespace System.IO return new ValueTask(vts, vts.Version); } - private static async ValueTask ReadScatterAtOffsetMultipleSyscallsAsync(SafeFileHandle handle, IReadOnlyList> buffers, - long fileOffset, CancellationToken cancellationToken) + private static async ValueTask ReadScatterAtOffsetMultipleSyscallsAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { long total = 0; - for (int i = 0; i < buffers.Count; i++) + int buffersCount = buffers.Count; + for (int i = 0; i < buffersCount; i++) { Memory buffer = buffers[i]; int read = await ReadAtOffsetAsync(handle, buffer, fileOffset + total, cancellationToken).ConfigureAwait(false); @@ -504,8 +535,7 @@ namespace System.IO return total; } - private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, - long fileOffset, CancellationToken cancellationToken) + private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { if (!handle.IsAsync) { @@ -529,69 +559,65 @@ namespace System.IO return WriteGatherAtOffsetMultipleSyscallsAsync(handle, buffers, fileOffset, cancellationToken); } - private static async ValueTask WriteGatherAtOffsetMultipleSyscallsAsync(SafeFileHandle handle, IReadOnlyList> buffers, - long fileOffset, CancellationToken cancellationToken) + private static async ValueTask WriteGatherAtOffsetMultipleSyscallsAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { - long total = 0; - - for (int i = 0; i < buffers.Count; i++) + long bytesWritten = 0; + int buffersCount = buffers.Count; + for (int i = 0; i < buffersCount; i++) { - ReadOnlyMemory buffer = buffers[i]; - int written = await WriteAtOffsetAsync(handle, buffer, fileOffset + total, cancellationToken).ConfigureAwait(false); - total += written; - - if (written != buffer.Length) - { - break; - } + ReadOnlyMemory rom = buffers[i]; + await WriteAtOffsetAsync(handle, rom, fileOffset + bytesWritten, cancellationToken).ConfigureAwait(false); + bytesWritten += rom.Length; } - - return total; } - private static async ValueTask WriteGatherAtOffsetSingleSyscallAsync(SafeFileHandle handle, IReadOnlyList> buffers, - long fileOffset, int totalBytes, CancellationToken cancellationToken) + private static ValueTask WriteGatherAtOffsetSingleSyscallAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, int totalBytes, CancellationToken cancellationToken) { if (buffers.Count == 1) { - return await WriteAtOffsetAsync(handle, buffers[0], fileOffset, cancellationToken).ConfigureAwait(false); + return WriteAtOffsetAsync(handle, buffers[0], fileOffset, cancellationToken); } - // "The array must contain enough elements to store nNumberOfBytesToWrite bytes of data, and one element for the terminating NULL. " - long[] fileSegments = new long[buffers.Count + 1]; - fileSegments[buffers.Count] = 0; + return Core(handle, buffers, fileOffset, totalBytes, cancellationToken); - MemoryHandle[] memoryHandles = new MemoryHandle[buffers.Count]; - MemoryHandle pinnedSegments = fileSegments.AsMemory().Pin(); - - try + static async ValueTask Core(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, int totalBytes, CancellationToken cancellationToken) { - for (int i = 0; i < buffers.Count; i++) - { - ReadOnlyMemory buffer = buffers[i]; - MemoryHandle memoryHandle = buffer.Pin(); - memoryHandles[i] = memoryHandle; + // "The array must contain enough elements to store nNumberOfBytesToWrite bytes of data, and one element for the terminating NULL. " + int buffersCount = buffers.Count; + long[] fileSegments = new long[buffersCount + 1]; + fileSegments[buffersCount] = 0; - unsafe // awaits can't be in an unsafe context + MemoryHandle[] memoryHandles = new MemoryHandle[buffersCount]; + MemoryHandle pinnedSegments = fileSegments.AsMemory().Pin(); + + try + { + for (int i = 0; i < buffersCount; i++) { - fileSegments[i] = new IntPtr(memoryHandle.Pointer).ToInt64(); - } - } + ReadOnlyMemory buffer = buffers[i]; + MemoryHandle memoryHandle = buffer.Pin(); + memoryHandles[i] = memoryHandle; - return await WriteFileGatherAsync(handle, pinnedSegments, totalBytes, fileOffset, cancellationToken).ConfigureAwait(false); - } - finally - { - foreach (MemoryHandle memoryHandle in memoryHandles) - { - memoryHandle.Dispose(); + unsafe // awaits can't be in an unsafe context + { + fileSegments[i] = new IntPtr(memoryHandle.Pointer).ToInt64(); + } + } + + await WriteFileGatherAsync(handle, pinnedSegments, totalBytes, fileOffset, cancellationToken).ConfigureAwait(false); + } + finally + { + foreach (MemoryHandle memoryHandle in memoryHandles) + { + memoryHandle.Dispose(); + } + pinnedSegments.Dispose(); } - pinnedSegments.Dispose(); } } - private static unsafe ValueTask WriteFileGatherAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, - int bytesToWrite, long fileOffset, CancellationToken cancellationToken) + private static unsafe ValueTask WriteFileGatherAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToWrite, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); @@ -617,8 +643,8 @@ namespace System.IO // Error. Callback will not be invoked. vts.Dispose(); return errorCode == Interop.Errors.ERROR_NO_DATA // EOF on a pipe. IO callback will not be called. - ? ValueTask.FromResult(0) - : ValueTask.FromException(SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, path: null)); + ? ValueTask.CompletedTask + : ValueTask.FromException(SafeFileHandle.OverlappedValueTaskSource.GetIOError(errorCode, path: null)); } } } @@ -630,7 +656,7 @@ namespace System.IO // Completion handled by callback. vts.FinishedScheduling(); - return new ValueTask(vts, vts.Version); + return new ValueTask(vts, vts.Version); } private static unsafe NativeOverlapped* GetNativeOverlappedForAsyncHandle(ThreadPoolBoundHandle threadPoolBinding, long fileOffset, CallbackResetEvent resetEvent) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs index 4d768cad53e..ad190fe2f75 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; @@ -134,7 +135,6 @@ namespace System.IO /// The file handle. /// A region of memory. This method copies the contents of this region to the file. /// The file position to write to. - /// The total number of bytes written into the file. This can be less than the number of bytes provided in the buffer and it's not an error. /// is . /// is invalid. /// The file is closed. @@ -143,11 +143,11 @@ namespace System.IO /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. - public static int Write(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) + public static void Write(SafeFileHandle handle, ReadOnlySpan buffer, long fileOffset) { ValidateInput(handle, fileOffset); - return WriteAtOffset(handle, buffer, fileOffset); + WriteAtOffset(handle, buffer, fileOffset); } /// @@ -156,7 +156,6 @@ namespace System.IO /// The file handle. /// A list of memory buffers. This method copies the contents of these buffers to the file. /// The file position to write to. - /// The total number of bytes written into the file. This can be less than the number of bytes provided in the buffers and it's not an error. /// or is . /// is invalid. /// The file is closed. @@ -165,12 +164,12 @@ namespace System.IO /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. - public static long Write(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) + public static void Write(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset) { ValidateInput(handle, fileOffset); ValidateBuffers(buffers); - return WriteGatherAtOffset(handle, buffers, fileOffset); + WriteGatherAtOffset(handle, buffers, fileOffset); } /// @@ -180,7 +179,7 @@ namespace System.IO /// A region of memory. This method copies the contents of this region to the file. /// The file position to write to. /// The token to monitor for cancellation requests. The default value is . - /// The total number of bytes written into the file. This can be less than the number of bytes provided in the buffer and it's not an error. + /// A task representing the asynchronous completion of the write operation. /// is . /// is invalid. /// The file is closed. @@ -189,13 +188,13 @@ namespace System.IO /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. - public static ValueTask WriteAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken = default) + public static ValueTask WriteAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken = default) { ValidateInput(handle, fileOffset); if (cancellationToken.IsCancellationRequested) { - return ValueTask.FromCanceled(cancellationToken); + return ValueTask.FromCanceled(cancellationToken); } return WriteAtOffsetAsync(handle, buffer, fileOffset, cancellationToken); @@ -208,7 +207,7 @@ namespace System.IO /// A list of memory buffers. This method copies the contents of these buffers to the file. /// The file position to write to. /// The token to monitor for cancellation requests. The default value is . - /// The total number of bytes written into the file. This can be less than the number of bytes provided in the buffers and it's not an error. + /// A task representing the asynchronous completion of the write operation. /// or is . /// is invalid. /// The file is closed. @@ -217,14 +216,14 @@ namespace System.IO /// was not opened for writing. /// An I/O error occurred. /// Position of the file is not advanced. - public static ValueTask WriteAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken = default) + public static ValueTask WriteAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken = default) { ValidateInput(handle, fileOffset); ValidateBuffers(buffers); if (cancellationToken.IsCancellationRequested) { - return ValueTask.FromCanceled(cancellationToken); + return ValueTask.FromCanceled(cancellationToken); } return WriteGatherAtOffsetAsync(handle, buffers, fileOffset, cancellationToken); @@ -276,13 +275,13 @@ namespace System.IO return handle.GetThreadPoolValueTaskSource().QueueReadScatter(buffers, fileOffset, cancellationToken); } - private static ValueTask ScheduleSyncWriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, + private static ValueTask ScheduleSyncWriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemory buffer, long fileOffset, CancellationToken cancellationToken) { return handle.GetThreadPoolValueTaskSource().QueueWrite(buffer, fileOffset, cancellationToken); } - private static ValueTask ScheduleSyncWriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, + private static ValueTask ScheduleSyncWriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken) { return handle.GetThreadPoolValueTaskSource().QueueWriteGather(buffers, fileOffset, cancellationToken); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs index b49b8ce2f33..7652fc768c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs @@ -290,10 +290,17 @@ namespace System.IO.Strategies ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - int r = RandomAccess.WriteAtOffset(_fileHandle, buffer, _filePosition); - Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); - _filePosition += r; + try + { + RandomAccess.WriteAtOffset(_fileHandle, buffer, _filePosition); + } + catch + { + _length = -1; // invalidate cached length + throw; + } + _filePosition += buffer.Length; UpdateLengthOnChangePosition(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs index c88665bf0b8..6f0f9f9562d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs @@ -58,14 +58,9 @@ namespace System.IO.Strategies TaskToApm.End(asyncResult); public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => - WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); - public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) => -#pragma warning disable CA2012 // The analyzer doesn't know the internal AsValueTask is safe. - WriteAsyncCore(source, cancellationToken).AsValueTask(); -#pragma warning restore CA2012 - - private ValueTask WriteAsyncCore(ReadOnlyMemory source, CancellationToken cancellationToken) + public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) { if (!CanWrite) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs index 1b74aa3a236..3118072c757 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs @@ -571,31 +571,6 @@ namespace System.Threading.Tasks /// Gets a that may be used at any point in the future. public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask()); - /// Casts a to . - internal ValueTask AsValueTask() - { - object? obj = _obj; - Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource); - - if (obj is null) - { - return default; - } - - if (obj is Task t) - { - return new ValueTask(t); - } - - if (obj is IValueTaskSource vts) - { - // This assumes the token used with IVTS is the same as used with IVTS. - return new ValueTask(vts, _token); - } - - return new ValueTask(GetTaskForValueTaskSource(Unsafe.As>(obj))); - } - /// Creates a to represent the . /// /// The is passed in rather than reading and casting diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 67e80443421..4f0c63dd430 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10873,10 +10873,10 @@ namespace System.IO public static long Read(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset) { throw null; } public static System.Threading.Tasks.ValueTask ReadAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Memory buffer, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.ValueTask ReadAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public static int Write(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.ReadOnlySpan buffer, long fileOffset) { throw null; } - public static long Write(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset) { throw null; } - public static System.Threading.Tasks.ValueTask WriteAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.ReadOnlyMemory buffer, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public static System.Threading.Tasks.ValueTask WriteAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static void Write(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.ReadOnlySpan buffer, long fileOffset) { throw null; } + public static void Write(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset) { throw null; } + public static System.Threading.Tasks.ValueTask WriteAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.ReadOnlyMemory buffer, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Threading.Tasks.ValueTask WriteAsync(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.Collections.Generic.IReadOnlyList> buffers, long fileOffset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } } namespace System.IO.Enumeration From c786e4f4f982b79410f8f6937c8069e2829d220a Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Mon, 12 Jul 2021 21:27:30 -0300 Subject: [PATCH 454/926] [mono][debugger] Support debug after hot-reload (#55220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add test case for debug after hot reload. * Implementing debug after hot reload, on runtime side, and on client side for wasm. * Reuse some code. * Remove unrelated comment. * Reuse code. * Refactor new code. * Fix log. * Apply suggestions from code review Co-authored-by: Aleksey Kliger (λgeek) * Fixing hot reload without debug information. * replacing spaces with tabs. * Fixing CI. Co-authored-by: Aleksey Kliger (λgeek) --- src/mono/mono/component/debugger-agent.c | 58 +++++- src/mono/mono/component/debugger-agent.h | 2 +- src/mono/mono/component/debugger-protocol.h | 4 +- src/mono/mono/component/debugger-stub.c | 13 +- src/mono/mono/component/debugger.h | 3 + src/mono/mono/component/hot_reload-stub.c | 17 +- src/mono/mono/component/hot_reload.c | 99 ++++++++- src/mono/mono/component/hot_reload.h | 4 +- src/mono/mono/component/mini-wasm-debugger.c | 11 +- src/mono/mono/metadata/debug-mono-ppdb.c | 190 ++++++++++-------- src/mono/mono/metadata/debug-mono-ppdb.h | 3 + src/mono/mono/metadata/icall.c | 4 +- src/mono/mono/metadata/metadata-internals.h | 2 +- src/mono/mono/metadata/metadata-update.c | 13 +- src/mono/mono/metadata/metadata-update.h | 3 + src/mono/mono/metadata/mono-debug.c | 8 +- src/mono/mono/mini/interp/transform.c | 3 +- .../debugger/BrowserDebugProxy/DebugStore.cs | 103 ++++++++-- .../debugger/BrowserDebugProxy/MonoProxy.cs | 95 ++++++++- .../BrowserDebugProxy/MonoSDBHelper.cs | 21 +- .../DebuggerTestSuite/BreakpointTests.cs | 58 ++++++ .../DebuggerTestSuite/DebuggerTestBase.cs | 49 +++++ .../ApplyUpdateReferencedAssembly.csproj | 31 +++ .../MethodBody1.cs | 34 ++++ .../MethodBody1_v1.cs | 34 ++++ .../MethodBody1_v2.cs | 34 ++++ .../deltascript.json | 7 + .../tests/debugger-test/debugger-test.cs | 75 +++++++ .../tests/debugger-test/debugger-test.csproj | 28 ++- .../tests/debugger-test/runtime-debugger.js | 6 +- 30 files changed, 866 insertions(+), 146 deletions(-) create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index f72c95392e9..bec98edb3c2 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -307,6 +307,14 @@ typedef struct { MonoStackHash *hashes; } EventInfo; +typedef struct { + MonoImage *image; + gconstpointer meta_bytes; + int meta_len; + gconstpointer pdb_bytes; + int pdb_len; +} EnCInfo; + #ifdef HOST_WIN32 #define get_last_sock_error() WSAGetLastError() #define MONO_EWOULDBLOCK WSAEWOULDBLOCK @@ -1604,7 +1612,7 @@ static MonoGHashTable *suspended_objs; #ifdef TARGET_WASM void -mono_init_debugger_agent_for_wasm (int log_level_parm) +mono_init_debugger_agent_for_wasm (int log_level_parm, MonoProfilerHandle *prof) { if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1) return; @@ -1615,6 +1623,7 @@ mono_init_debugger_agent_for_wasm (int log_level_parm) ids_init(); objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); obj_to_objref = g_hash_table_new (NULL, NULL); + pending_assembly_loads = g_ptr_array_new (); log_level = log_level_parm; event_requests = g_ptr_array_new (); @@ -1622,6 +1631,8 @@ mono_init_debugger_agent_for_wasm (int log_level_parm) transport = &transports [0]; memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData)); agent_config.enabled = TRUE; + + mono_profiler_set_jit_done_callback (*prof, jit_done); } #endif @@ -3600,6 +3611,9 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_TYPE_LOAD: buffer_add_typeid (&buf, domain, (MonoClass *)arg); break; + case MDBGPROT_EVENT_KIND_METHOD_UPDATE: + buffer_add_methodid (&buf, domain, (MonoMethod *)arg); + break; case EVENT_KIND_BREAKPOINT: case EVENT_KIND_STEP: { GET_DEBUGGER_TLS(); @@ -3655,6 +3669,15 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_KEEPALIVE: suspend_policy = SUSPEND_POLICY_NONE; break; + + case MDBGPROT_EVENT_KIND_ENC_UPDATE: { + EnCInfo *ei = (EnCInfo *)arg; + buffer_add_moduleid (&buf, mono_domain_get (), ei->image); + m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) ei->meta_bytes, ei->meta_len); + m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) ei->pdb_bytes, ei->pdb_len); + break; + } + default: g_assert_not_reached (); } @@ -4060,6 +4083,9 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) send_type_load (method->klass); + if (m_class_get_image(method->klass)->has_updates) { + process_profiler_event (MDBGPROT_EVENT_KIND_METHOD_UPDATE, method); + } if (jinfo) mono_de_add_pending_breakpoints (method, jinfo); } @@ -6517,6 +6543,28 @@ get_types_for_source_file (gpointer key, gpointer value, gpointer user_data) } } +static void +send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len) +{ + //TODO: if it came from debugger we don't need to pass the parameters back, they are already on debugger client side. + if (agent_config.enabled) { + int suspend_policy; + GSList *events; + mono_loader_lock (); + events = create_event_list (MDBGPROT_EVENT_KIND_ENC_UPDATE, NULL, NULL, NULL, &suspend_policy); + mono_loader_unlock (); + + EnCInfo info; + info.image = image; + info.meta_bytes = dpdb_bytes; + info.meta_len = dpdb_len; + info.pdb_bytes = dpdb_bytes; + info.pdb_len = dpdb_len; + + process_event (MDBGPROT_EVENT_KIND_ENC_UPDATE, &info, 0, NULL, events, suspend_policy); + } +} + static gboolean module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoArray *dpdb, MonoError *error) { @@ -6525,9 +6573,9 @@ module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoAr int32_t dmeta_len = mono_array_length_internal (dmeta); gpointer dil_bytes = (gpointer)mono_array_addr_internal (dil, char, 0); int32_t dil_len = mono_array_length_internal (dil); - gpointer dpdb_bytes G_GNUC_UNUSED = !dpdb ? NULL : (gpointer)mono_array_addr_internal (dpdb, char, 0); - int32_t dpdb_len G_GNUC_UNUSED = !dpdb ? 0 : mono_array_length_internal (dpdb); - mono_image_load_enc_delta (image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); + gpointer dpdb_bytes = !dpdb ? NULL : (gpointer)mono_array_addr_internal (dpdb, char, 0); + int32_t dpdb_len = !dpdb ? 0 : mono_array_length_internal (dpdb); + mono_image_load_enc_delta (image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); return is_ok (error); } @@ -7239,6 +7287,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) req->info = mono_de_set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req, NULL); } else if (req->event_kind == EVENT_KIND_EXCEPTION) { } else if (req->event_kind == EVENT_KIND_TYPE_LOAD) { + } else if (req->event_kind == MDBGPROT_EVENT_KIND_METHOD_UPDATE) { } else { if (req->nmodifiers) { g_free (req); @@ -10278,6 +10327,7 @@ debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table) fn_table->debug_log_is_enabled = debugger_agent_debug_log_is_enabled; fn_table->send_crash = mono_debugger_agent_send_crash; fn_table->transport_handshake = debugger_agent_transport_handshake; + fn_table->send_enc_delta = send_enc_delta; } diff --git a/src/mono/mono/component/debugger-agent.h b/src/mono/mono/component/debugger-agent.h index 60f42448202..ad5a0cb1d0b 100644 --- a/src/mono/mono/component/debugger-agent.h +++ b/src/mono/mono/component/debugger-agent.h @@ -23,7 +23,7 @@ DebuggerTlsData* mono_wasm_get_tls (void); void -mono_init_debugger_agent_for_wasm (int log_level); +mono_init_debugger_agent_for_wasm (int log_level, MonoProfilerHandle *prof); void mono_wasm_save_thread_context (void); diff --git a/src/mono/mono/component/debugger-protocol.h b/src/mono/mono/component/debugger-protocol.h index 24e8a2e42c2..292b723cc35 100644 --- a/src/mono/mono/component/debugger-protocol.h +++ b/src/mono/mono/component/debugger-protocol.h @@ -294,7 +294,9 @@ typedef enum { MDBGPROT_EVENT_KIND_KEEPALIVE = 14, MDBGPROT_EVENT_KIND_USER_BREAK = 15, MDBGPROT_EVENT_KIND_USER_LOG = 16, - MDBGPROT_EVENT_KIND_CRASH = 17 + MDBGPROT_EVENT_KIND_CRASH = 17, + MDBGPROT_EVENT_KIND_ENC_UPDATE = 18, + MDBGPROT_EVENT_KIND_METHOD_UPDATE = 19, } MdbgProtEventKind; typedef enum { diff --git a/src/mono/mono/component/debugger-stub.c b/src/mono/mono/component/debugger-stub.c index 1c5467adadd..3628c0fbc53 100644 --- a/src/mono/mono/component/debugger-stub.c +++ b/src/mono/mono/component/debugger-stub.c @@ -66,6 +66,9 @@ stub_mono_wasm_breakpoint_hit (void); static void stub_mono_wasm_single_step_hit (void); +static void +stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); + static MonoComponentDebugger fn_table = { { MONO_COMPONENT_ITF_VERSION, &debugger_avaliable }, &stub_debugger_init, @@ -87,7 +90,10 @@ static MonoComponentDebugger fn_table = { //wasm &stub_mono_wasm_breakpoint_hit, - &stub_mono_wasm_single_step_hit + &stub_mono_wasm_single_step_hit, + + //HotReload + &stub_send_enc_delta, }; static bool @@ -201,3 +207,8 @@ static void stub_mono_wasm_single_step_hit (void) { } + +static void +stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len) +{ +} diff --git a/src/mono/mono/component/debugger.h b/src/mono/mono/component/debugger.h index de50c564113..81ef259f3eb 100644 --- a/src/mono/mono/component/debugger.h +++ b/src/mono/mono/component/debugger.h @@ -194,6 +194,9 @@ typedef struct MonoComponentDebugger { void (*mono_wasm_breakpoint_hit) (void); void (*mono_wasm_single_step_hit) (void); + //HotReload + void (*send_enc_delta) (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); + } MonoComponentDebugger; diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 48033c1c7a0..0ebf79a50c8 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -15,7 +15,7 @@ static bool hot_reload_stub_available (void); static void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static MonoComponentHotReload * component_hot_reload_stub_init (void); @@ -59,6 +59,9 @@ hot_reload_stub_table_bounds_check (MonoImage *base_image, int table_index, int static gboolean hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gpointer +hot_reload_stub_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + static gboolean hot_reload_stub_has_modified_rows (const MonoTableInfo *table); @@ -78,6 +81,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_updated_method_rva, &hot_reload_stub_table_bounds_check, &hot_reload_stub_delta_heap_lookup, + &hot_reload_stub_get_updated_method_ppdb, &hot_reload_stub_has_modified_rows, }; @@ -143,7 +147,7 @@ hot_reload_stub_relative_delta_index (MonoImage *image_dmeta, int token) } void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error) +hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { mono_error_set_not_supported (error, "Hot reload not supported in this runtime."); } @@ -176,8 +180,15 @@ hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc g_assert_not_reached (); } +static gpointer +hot_reload_stub_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + g_assert_not_reached (); +} + static gboolean -hot_reload_stub_has_modified_rows (const MonoTableInfo *table){ +hot_reload_stub_has_modified_rows (const MonoTableInfo *table) +{ return FALSE; } diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 76861056eda..32820888b26 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -53,7 +53,7 @@ static void hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx); static void -hot_reload_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +hot_reload_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static int hot_reload_relative_delta_index (MonoImage *image_dmeta, int token); @@ -73,6 +73,9 @@ hot_reload_table_bounds_check (MonoImage *base_image, int table_index, int token static gboolean hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gpointer +hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + static gboolean hot_reload_has_modified_rows (const MonoTableInfo *table); @@ -92,6 +95,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_updated_method_rva, &hot_reload_table_bounds_check, &hot_reload_delta_heap_lookup, + &hot_reload_get_updated_method_ppdb, &hot_reload_has_modified_rows, }; @@ -150,6 +154,9 @@ typedef struct _DeltaInfo { /* Maps MethodDef token indices to a pointer into the RVA of the delta IL */ GHashTable *method_table_update; + /* Maps MethodDef token indices to a pointer into the RVA of the delta PPDB */ + GHashTable *method_ppdb_table_update; + // for each table, the row in the EncMap table that has the first token for remapping it? uint32_t enc_recs [MONO_TABLE_NUM]; delta_row_count count [MONO_TABLE_NUM]; @@ -319,6 +326,8 @@ delta_info_destroy (DeltaInfo *dinfo) { if (dinfo->method_table_update) g_hash_table_destroy (dinfo->method_table_update); + if (dinfo->method_ppdb_table_update) + g_hash_table_destroy (dinfo->method_ppdb_table_update); g_free (dinfo); } @@ -1173,17 +1182,50 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer } static void -set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address) +set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address, const char* pdb_address) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "setting method 0x%08x in g=%d IL=%p", token_index, generation, (void*)il_address); /* FIXME: this is a race if other threads are doing a lookup. */ g_hash_table_insert (base_info->method_table_update, GUINT_TO_POINTER (token_index), GUINT_TO_POINTER (generation)); g_hash_table_insert (delta_info->method_table_update, GUINT_TO_POINTER (token_index), (gpointer) il_address); + g_hash_table_insert (delta_info->method_ppdb_table_update, GUINT_TO_POINTER (token_index), (gpointer) pdb_address); +} + +static const char * +hot_reload_get_method_debug_information (MonoImage *image_dppdb, int idx) +{ + if (!image_dppdb) + return NULL; + + MonoTableInfo *table_encmap = &image_dppdb->tables [MONO_TABLE_ENCMAP]; + int rows = table_info_get_rows (table_encmap); + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (table_encmap, i, cols, MONO_ENCMAP_SIZE); + int map_token = cols [MONO_ENCMAP_TOKEN]; + int token_table = mono_metadata_token_table (map_token); + if (token_table != MONO_TABLE_METHODBODY) + continue; + int token_index = mono_metadata_token_index (map_token); + if (token_index == idx) + { + guint32 cols [MONO_METHODBODY_SIZE]; + MonoTableInfo *methodbody_table = &image_dppdb->tables [MONO_TABLE_METHODBODY]; + mono_metadata_decode_row (methodbody_table, i, cols, MONO_METHODBODY_SIZE); + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return NULL; + + const char *ptr = mono_metadata_blob_heap (image_dppdb, cols [MONO_METHODBODY_SEQ_POINTS]); + return ptr; + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb encmap i=%d: token=0x%08x (table=%s)", i, map_token, mono_meta_table_name (token_table)); + } + return NULL; } /* do actuall enclog application */ static gboolean -apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, MonoImage *image_dppdb, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) { MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; int rows = table_info_get_rows (table_enclog); @@ -1254,12 +1296,16 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen base_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); if (!delta_info->method_table_update) delta_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); + if (!delta_info->method_ppdb_table_update) + + delta_info->method_ppdb_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); int mapped_token = hot_reload_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); if (rva < dil_length) { char *il_address = ((char *) dil_data) + rva; - set_update_method (image_base, base_info, generation, image_dmeta, delta_info, token_index, il_address); + const char *method_debug_information = hot_reload_get_method_debug_information (image_dppdb, token_index); + set_update_method (image_base, base_info, generation, image_dmeta, delta_info, token_index, il_address, method_debug_information); } else { /* rva points probably into image_base IL stream. can this ever happen? */ g_print ("TODO: this case is still a bit contrived. token=0x%08x with rva=0x%04x\n", log_token, rva); @@ -1300,7 +1346,7 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * LOCKING: Takes the publish_lock */ void -hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, MonoError *error) +hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { if (!assembly_update_supported (image_base->assembly)) { mono_error_set_invalid_operation (error, "The assembly can not be edited or changed."); @@ -1335,7 +1381,16 @@ hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint /* makes a copy of dil_bytes_orig */ gpointer dil_bytes = open_dil_data (image_base, dil_bytes_orig, dil_length); - /* TODO: make a copy of the dpdb bytes, once we consume them */ + + MonoImage *image_dpdb = NULL; + if (dpdb_length > 0) + { + MonoImage *image_dpdb = image_open_dmeta_from_data (image_base, generation, dpdb_bytes_orig, dpdb_length); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image string size: 0x%08x", image_dpdb->heap_strings.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image user string size: 0x%08x", image_dpdb->heap_us.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image blob heap addr: %p", image_dpdb->heap_blob.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image blob heap size: 0x%08x", image_dpdb->heap_blob.size); + } BaselineInfo *base_info = baseline_info_lookup_or_add (image_base); @@ -1391,7 +1446,7 @@ hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) dump_update_summary (image_base, image_dmeta); - if (!apply_enclog_pass2 (image_base, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, error)) { + if (!apply_enclog_pass2 (image_base, base_info, generation, image_dmeta, image_dpdb, delta_info, dil_bytes, dil_length, error)) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error applying delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); hot_reload_update_cancel (generation); return; @@ -1434,7 +1489,7 @@ metadata_update_count_updates (MonoImage *base) } static gpointer -get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t idx) +get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t idx, gboolean is_pdb) { gpointer loc = NULL; uint32_t cur = hot_reload_get_thread_generation (); @@ -1448,8 +1503,13 @@ get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t g_assert (delta_info); if (delta_info->generation > cur) break; - if (delta_info->method_table_update) { - gpointer result = g_hash_table_lookup (delta_info->method_table_update, GUINT_TO_POINTER (idx)); + GHashTable *table = NULL; + if (is_pdb) + table = delta_info->method_ppdb_table_update; + else + table = delta_info->method_table_update; + if (table) { + gpointer result = g_hash_table_lookup (table, GUINT_TO_POINTER (idx)); /* if it's not in the table of a later generation, the * later generation didn't modify the method */ @@ -1463,6 +1523,23 @@ get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t return loc; } +gpointer +hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + BaselineInfo *info = baseline_info_lookup (base_image); + if (!info) + return NULL; + gpointer loc = NULL; + /* EnC case */ + if (G_UNLIKELY (info->method_table_update)) { + uint32_t gen = GPOINTER_TO_UINT (g_hash_table_lookup (info->method_table_update, GUINT_TO_POINTER (idx))); + if (G_UNLIKELY (gen > 0)) { + loc = get_method_update_rva (base_image, info, idx, TRUE); + } + } + return loc; +} + gpointer hot_reload_get_updated_method_rva (MonoImage *base_image, uint32_t idx) { @@ -1474,7 +1551,7 @@ hot_reload_get_updated_method_rva (MonoImage *base_image, uint32_t idx) if (G_UNLIKELY (info->method_table_update)) { uint32_t gen = GPOINTER_TO_UINT (g_hash_table_lookup (info->method_table_update, GUINT_TO_POINTER (idx))); if (G_UNLIKELY (gen > 0)) { - loc = get_method_update_rva (base_image, info, idx); + loc = get_method_update_rva (base_image, info, idx, FALSE); } } return loc; diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 0bdb63d121d..26ca0089a69 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -23,14 +23,14 @@ typedef struct _MonoComponentHotReload { void (*cleanup_on_close) (MonoImage *image); void (*effective_table_slow) (const MonoTableInfo **t, int *idx); int (*relative_delta_index) (MonoImage *image_dmeta, int token); - void (*apply_changes) (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); + void (*apply_changes) (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); void (*image_close_except_pools_all) (MonoImage *base_image); void (*image_close_all) (MonoImage *base_image); gpointer (*get_updated_method_rva) (MonoImage *base_image, uint32_t idx); gboolean (*table_bounds_check) (MonoImage *base_image, int table_index, int token_index); gboolean (*delta_heap_lookup) (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); + gpointer (*get_updated_method_ppdb) (MonoImage *base_image, uint32_t idx); gboolean (*has_modified_rows) (const MonoTableInfo *table); - } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/component/mini-wasm-debugger.c b/src/mono/mono/component/mini-wasm-debugger.c index 4df2172841e..5dd9d8ce1d4 100644 --- a/src/mono/mono/component/mini-wasm-debugger.c +++ b/src/mono/mono/component/mini-wasm-debugger.c @@ -78,12 +78,6 @@ void wasm_debugger_log (int level, const gchar *format, ...) g_free (mesg); } -static void -jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) -{ - mono_de_add_pending_breakpoints (method, jinfo); -} - static void appdomain_load (MonoProfiler *prof, MonoDomain *domain) { @@ -149,9 +143,9 @@ handle_multiple_ss_requests (void) { static void mono_wasm_enable_debugging_internal (int debug_level) { + log_level = debug_level; PRINT_DEBUG_MSG (1, "DEBUGGING ENABLED\n"); debugger_enabled = TRUE; - log_level = debug_level; } static void @@ -186,7 +180,6 @@ mono_wasm_debugger_init (MonoDefaults *mono_defaults) get_mini_debug_options ()->load_aot_jit_info_eagerly = TRUE; MonoProfilerHandle prof = mono_profiler_create (NULL); - mono_profiler_set_jit_done_callback (prof, jit_done); //FIXME support multiple appdomains mono_profiler_set_domain_loaded_callback (prof, appdomain_load); mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded); @@ -197,7 +190,7 @@ mono_wasm_debugger_init (MonoDefaults *mono_defaults) trans.send = receive_debugger_agent_message; mono_debugger_agent_register_transport (&trans); - mono_init_debugger_agent_for_wasm (log_level); + mono_init_debugger_agent_for_wasm (log_level, &prof); } static void diff --git a/src/mono/mono/metadata/debug-mono-ppdb.c b/src/mono/mono/metadata/debug-mono-ppdb.c index 070f9b8b0cf..67bd4715d33 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.c +++ b/src/mono/mono/metadata/debug-mono-ppdb.c @@ -456,6 +456,108 @@ mono_ppdb_is_embedded (MonoPPDBFile *ppdb) return ppdb->is_embedded; } +static int +mono_ppdb_get_seq_points_internal (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points, int docidx, MonoImage *image, MonoPPDBFile *ppdb, GPtrArray **sfiles, char **source_file, int **source_files, GPtrArray **sindexes, gboolean read_doc_value) +{ + GArray *sps; + MonoSymSeqPoint sp; + int iloffset = 0; + int start_line = 0; + int start_col = 0; + int delta_cols = 0; + gboolean first_non_hidden = TRUE; + int adv_line, adv_col; + int size = mono_metadata_decode_blob_size (ptr, &ptr); + const char* end = ptr + size; + MonoDebugSourceInfo *docinfo; + gboolean first = TRUE; + + sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint)); + + /* Header */ + /* LocalSignature */ + mono_metadata_decode_value (ptr, &ptr); + if (docidx == 0 && read_doc_value) + docidx = mono_metadata_decode_value (ptr, &ptr); + if (sfiles && *sfiles) + { + docinfo = get_docinfo (ppdb, image, docidx); + g_ptr_array_add (*sfiles, docinfo); + } + + if (source_file && *source_file) + *source_file = g_strdup (docinfo->source_file); + + iloffset = 0; + start_line = 0; + start_col = 0; + while (ptr < end) { + int delta_il = mono_metadata_decode_value (ptr, &ptr); + if (!first && delta_il == 0 && read_doc_value) { + /* subsequent-document-record */ + docidx = mono_metadata_decode_value (ptr, &ptr); + docinfo = get_docinfo (ppdb, image, docidx); + if (sfiles && *sfiles) + { + g_ptr_array_add (*sfiles, docinfo); + } + continue; + } + iloffset += delta_il; + first = FALSE; + + int delta_lines = mono_metadata_decode_value (ptr, &ptr); + if (delta_lines == 0) + delta_cols = mono_metadata_decode_value (ptr, &ptr); + else + delta_cols = mono_metadata_decode_signed_value (ptr, &ptr); + + if (delta_lines == 0 && delta_cols == 0) { + /* Hidden sequence point */ + continue; + } + + if (first_non_hidden) { + start_line = mono_metadata_decode_value (ptr, &ptr); + start_col = mono_metadata_decode_value (ptr, &ptr); + } else { + adv_line = mono_metadata_decode_signed_value (ptr, &ptr); + adv_col = mono_metadata_decode_signed_value (ptr, &ptr); + start_line += adv_line; + start_col += adv_col; + } + first_non_hidden = FALSE; + + memset (&sp, 0, sizeof (sp)); + sp.il_offset = iloffset; + sp.line = start_line; + sp.column = start_col; + sp.end_line = start_line + delta_lines; + sp.end_column = start_col + delta_cols; + + g_array_append_val (sps, sp); + if (sindexes && *sindexes) { + g_ptr_array_add (*sindexes, GUINT_TO_POINTER ((*sfiles)->len - 1)); + } + } + + if (n_seq_points) { + *n_seq_points = sps->len; + g_assert (seq_points); + *seq_points = g_new (MonoSymSeqPoint, sps->len); + memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); + } + int sps_len = sps->len; + g_array_free (sps, TRUE); + return sps_len; +} + +void +mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + mono_ppdb_get_seq_points_internal (ptr, seq_points, n_seq_points, 0, NULL, NULL, NULL, NULL, NULL, NULL, FALSE); +} + void mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) { @@ -465,12 +567,7 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr MonoTableInfo *tables = image->tables; guint32 cols [MONO_METHODBODY_SIZE]; const char *ptr; - const char *end; - MonoDebugSourceInfo *docinfo; - int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col; - gboolean first = TRUE, first_non_hidden = TRUE; - GArray *sps; - MonoSymSeqPoint sp; + int i, method_idx, docidx; GPtrArray *sfiles = NULL; GPtrArray *sindexes = NULL; @@ -510,89 +607,16 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr return; ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); - size = mono_metadata_decode_blob_size (ptr, &ptr); - end = ptr + size; - - sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint)); - - /* Header */ - /* LocalSignature */ - mono_metadata_decode_value (ptr, &ptr); - if (docidx == 0) - docidx = mono_metadata_decode_value (ptr, &ptr); - docinfo = get_docinfo (ppdb, image, docidx); - - if (sfiles) - g_ptr_array_add (sfiles, docinfo); - - if (source_file) - *source_file = g_strdup (docinfo->source_file); - - iloffset = 0; - start_line = 0; - start_col = 0; - while (ptr < end) { - delta_il = mono_metadata_decode_value (ptr, &ptr); - if (!first && delta_il == 0) { - /* subsequent-document-record */ - docidx = mono_metadata_decode_value (ptr, &ptr); - docinfo = get_docinfo (ppdb, image, docidx); - if (sfiles) - g_ptr_array_add (sfiles, docinfo); - continue; - } - iloffset += delta_il; - first = FALSE; - - delta_lines = mono_metadata_decode_value (ptr, &ptr); - if (delta_lines == 0) - delta_cols = mono_metadata_decode_value (ptr, &ptr); - else - delta_cols = mono_metadata_decode_signed_value (ptr, &ptr); - - if (delta_lines == 0 && delta_cols == 0) { - /* Hidden sequence point */ - continue; - } - - if (first_non_hidden) { - start_line = mono_metadata_decode_value (ptr, &ptr); - start_col = mono_metadata_decode_value (ptr, &ptr); - } else { - adv_line = mono_metadata_decode_signed_value (ptr, &ptr); - adv_col = mono_metadata_decode_signed_value (ptr, &ptr); - start_line += adv_line; - start_col += adv_col; - } - first_non_hidden = FALSE; - - memset (&sp, 0, sizeof (sp)); - sp.il_offset = iloffset; - sp.line = start_line; - sp.column = start_col; - sp.end_line = start_line + delta_lines; - sp.end_column = start_col + delta_cols; - - g_array_append_val (sps, sp); - if (source_files) - g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1)); - } - - if (n_seq_points) { - *n_seq_points = sps->len; - g_assert (seq_points); - *seq_points = g_new (MonoSymSeqPoint, sps->len); - memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); - } + + int sps_len = mono_ppdb_get_seq_points_internal (ptr, seq_points, n_seq_points, docidx, image, ppdb, &sfiles, source_file, source_files, &sindexes, TRUE); if (source_files) { - *source_files = g_new (int, sps->len); - for (i = 0; i < sps->len; ++i) + *source_files = g_new (int, sps_len); + for (i = 0; i < sps_len; ++i) (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i)); g_ptr_array_free (sindexes, TRUE); } - g_array_free (sps, TRUE); } MonoDebugLocalsInfo* diff --git a/src/mono/mono/metadata/debug-mono-ppdb.h b/src/mono/mono/metadata/debug-mono-ppdb.h index c27a97069c9..e16317f9fc7 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.h +++ b/src/mono/mono/metadata/debug-mono-ppdb.h @@ -32,6 +32,9 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset); void mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); +void +mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points); + MonoDebugLocalsInfo* mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 7c3f33fc792..240479fb90a 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5782,9 +5782,9 @@ ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, g_assert (dmeta_len >= 0); MonoImage *image_base = assm->image; g_assert (image_base); - // TODO: use dpdb_bytes - mono_image_load_enc_delta (image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); + mono_image_load_enc_delta (image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); + mono_error_set_pending_exception (error); } diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 20f36c60e8f..9a0d030a9f1 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -822,7 +822,7 @@ int mono_image_relative_delta_index (MonoImage *image_dmeta, int token); MONO_COMPONENT_API void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error); gboolean mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 8787b28bca6..71520b8aa1e 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -73,9 +73,12 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token) } void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error) +mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error) { - mono_component_hot_reload ()->apply_changes (base_image, dmeta, dmeta_len, dil, dil_len, error); + mono_component_hot_reload ()->apply_changes (base_image, dmeta, dmeta_len, dil, dil_len, dpdb, dpdb_len, error); + if (is_ok (error)) { + mono_component_debugger ()->send_enc_delta (base_image, dmeta, dmeta_len, dpdb, dpdb_len); + } } static void @@ -108,6 +111,12 @@ mono_metadata_update_get_updated_method_rva (MonoImage *base_image, uint32_t idx return mono_component_hot_reload ()->get_updated_method_rva (base_image, idx); } +gpointer +mono_metadata_update_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + return mono_component_hot_reload ()->get_updated_method_ppdb (base_image, idx); +} + gboolean mono_metadata_update_table_bounds_check (MonoImage *base_image, int table_index, int token_index) { diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index 52bfa701e57..15d0d51e10f 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -48,6 +48,9 @@ mono_metadata_update_image_close_all (MonoImage *base_image); gpointer mono_metadata_update_get_updated_method_rva (MonoImage *base_image, uint32_t idx); +gpointer +mono_metadata_update_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + gboolean mono_metadata_update_table_bounds_check (MonoImage *base_image, int table_index, int token_index); diff --git a/src/mono/mono/metadata/mono-debug.c b/src/mono/mono/metadata/mono-debug.c index bdef02c84f0..58ecee24d45 100644 --- a/src/mono/mono/metadata/mono-debug.c +++ b/src/mono/mono/metadata/mono-debug.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #if NO_UNALIGNED_ACCESS @@ -1115,7 +1116,12 @@ mono_debug_enabled (void) void mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) { - if (minfo->handle->ppdb) + MonoImage* img = m_class_get_image (minfo->method->klass); + if (img->has_updates) { + int idx = mono_metadata_token_index (minfo->method->token); + gpointer ptr = mono_metadata_update_get_updated_method_ppdb (img, idx); + mono_ppdb_get_seq_points_enc (ptr, seq_points, n_seq_points); + } else if (minfo->handle->ppdb) mono_ppdb_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); else mono_debug_symfile_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0eed6c7146e..e75fefe393c 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9758,8 +9758,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); - if (!g_hash_table_lookup (jit_mm->seq_points, imethod->method)) - g_hash_table_insert (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); + g_hash_table_replace (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); jit_mm_unlock (jit_mm); // FIXME: Add a different callback ? diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 81c419e4c58..c1e8ce60bda 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -62,12 +62,12 @@ namespace Microsoft.WebAssembly.Diagnostics public int Line { get; private set; } public int Column { get; private set; } public string Condition { get; private set; } - public MethodInfo Method { get; private set; } + public MethodInfo Method { get; set; } private JObject request; public bool IsResolved => Assembly != null; - public List Locations { get; } = new List(); + public List Locations { get; set; } = new List(); public override string ToString() => $"BreakpointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}"; @@ -321,21 +321,26 @@ namespace Microsoft.WebAssembly.Diagnostics public string Name { get; } public MethodDebugInformation DebugInformation; public MethodDefinitionHandle methodDefHandle; + private MetadataReader pdbMetadataReader; - public SourceLocation StartLocation { get; } - public SourceLocation EndLocation { get; } + public SourceLocation StartLocation { get; set;} + public SourceLocation EndLocation { get; set;} public AssemblyInfo Assembly { get; } public int Token { get; } + internal bool IsEnCMethod; + internal LocalScopeHandleCollection localScopes; public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; - public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type) + public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) { this.Assembly = assembly; - this.methodDef = Assembly.asmMetadataReader.GetMethodDefinition(methodDefHandle); - this.DebugInformation = Assembly.pdbMetadataReader.GetMethodDebugInformation(methodDefHandle.ToDebugInformationHandle()); + this.methodDef = asmMetadataReader.GetMethodDefinition(methodDefHandle); + this.DebugInformation = pdbMetadataReader.GetMethodDebugInformation(methodDefHandle.ToDebugInformationHandle()); this.source = source; this.Token = token; this.methodDefHandle = methodDefHandle; - this.Name = Assembly.asmMetadataReader.GetString(methodDef.Name); + this.Name = asmMetadataReader.GetString(methodDef.Name); + this.pdbMetadataReader = pdbMetadataReader; + this.IsEnCMethod = false; if (!DebugInformation.SequencePointsBlob.IsNil) { var sps = DebugInformation.GetSequencePoints(); @@ -358,6 +363,37 @@ namespace Microsoft.WebAssembly.Diagnostics StartLocation = new SourceLocation(this, start); EndLocation = new SourceLocation(this, end); } + localScopes = pdbMetadataReader.GetLocalScopes(methodDefHandle); + } + + public void UpdateEnC(MetadataReader asmMetadataReader, MetadataReader pdbMetadataReaderParm, int method_idx) + { + this.DebugInformation = pdbMetadataReaderParm.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method_idx)); + this.pdbMetadataReader = pdbMetadataReaderParm; + this.IsEnCMethod = true; + if (!DebugInformation.SequencePointsBlob.IsNil) + { + var sps = DebugInformation.GetSequencePoints(); + SequencePoint start = sps.First(); + SequencePoint end = sps.First(); + + foreach (SequencePoint sp in sps) + { + if (sp.StartLine < start.StartLine) + start = sp; + else if (sp.StartLine == start.StartLine && sp.StartColumn < start.StartColumn) + start = sp; + + if (sp.EndLine > end.EndLine) + end = sp; + else if (sp.EndLine == end.EndLine && sp.EndColumn > end.EndColumn) + end = sp; + } + + StartLocation = new SourceLocation(this, start); + EndLocation = new SourceLocation(this, end); + } + localScopes = pdbMetadataReader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method_idx)); } public SourceLocation GetLocationByIl(int pos) @@ -394,18 +430,18 @@ namespace Microsoft.WebAssembly.Diagnostics res.Add(new VarInfo(parameter, Assembly.asmMetadataReader)); } - var localScopes = Assembly.pdbMetadataReader.GetLocalScopes(methodDefHandle); + foreach (var localScopeHandle in localScopes) { - var localScope = Assembly.pdbMetadataReader.GetLocalScope(localScopeHandle); + var localScope = pdbMetadataReader.GetLocalScope(localScopeHandle); if (localScope.StartOffset <= offset && localScope.EndOffset > offset) { var localVariables = localScope.GetLocalVariables(); foreach (var localVariableHandle in localVariables) { - var localVariable = Assembly.pdbMetadataReader.GetLocalVariable(localVariableHandle); + var localVariable = pdbMetadataReader.GetLocalVariable(localVariableHandle); if (localVariable.Attributes != LocalVariableAttributes.DebuggerHidden) - res.Add(new VarInfo(localVariable, Assembly.pdbMetadataReader)); + res.Add(new VarInfo(localVariable, pdbMetadataReader)); } } } @@ -465,6 +501,8 @@ namespace Microsoft.WebAssembly.Diagnostics internal string Url { get; } internal MetadataReader asmMetadataReader { get; } internal MetadataReader pdbMetadataReader { get; set; } + internal List enCMemoryStream = new List(); + internal List enCMetadataReader = new List(); internal PEReader peReader; internal MemoryStream asmStream; internal MemoryStream pdbStream; @@ -496,11 +534,39 @@ namespace Microsoft.WebAssembly.Diagnostics Populate(); } + public bool EnC(byte[] meta, byte[] pdb) + { + var asmStream = new MemoryStream(meta); + MetadataReader asmMetadataReader = MetadataReaderProvider.FromMetadataStream(asmStream).GetMetadataReader(); + var pdbStream = new MemoryStream(pdb); + MetadataReader pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + enCMemoryStream.Add(asmStream); + enCMemoryStream.Add(pdbStream); + enCMetadataReader.Add(asmMetadataReader); + enCMetadataReader.Add(pdbMetadataReader); + PopulateEnC(asmMetadataReader, pdbMetadataReader); + return true; + } + public AssemblyInfo(ILogger logger) { this.logger = logger; } + private void PopulateEnC(MetadataReader asmMetadataReaderParm, MetadataReader pdbMetadataReaderParm) + { + int i = 1; + foreach (EntityHandle encMapHandle in asmMetadataReaderParm.GetEditAndContinueMapEntries()) + { + if (encMapHandle.Kind == HandleKind.MethodDebugInformation) + { + var method = methods[asmMetadataReader.GetRowNumber(encMapHandle)]; + method.UpdateEnC(asmMetadataReaderParm, pdbMetadataReaderParm, i); + i++; + } + } + } + private void Populate() { var d2s = new Dictionary(); @@ -543,7 +609,7 @@ namespace Microsoft.WebAssembly.Diagnostics var document = pdbMetadataReader.GetDocument(methodDebugInformation.Document); var documentName = pdbMetadataReader.GetString(document.Name); SourceFile source = FindSource(methodDebugInformation.Document, asmMetadataReader.GetRowNumber(methodDebugInformation.Document), documentName); - var methodInfo = new MethodInfo(this, method, asmMetadataReader.GetRowNumber(method), source, typeInfo); + var methodInfo = new MethodInfo(this, method, asmMetadataReader.GetRowNumber(method), source, typeInfo, asmMetadataReader, pdbMetadataReader); methods[asmMetadataReader.GetRowNumber(method)] = methodInfo; if (source != null) @@ -605,6 +671,7 @@ namespace Microsoft.WebAssembly.Diagnostics } public IEnumerable Sources => this.sources; + public Dictionary Methods => this.methods; public Dictionary TypesByName => this.typesByName; public int Id => id; @@ -828,6 +895,16 @@ namespace Microsoft.WebAssembly.Diagnostics public Task Data { get; set; } } + public IEnumerable EnC(SessionId sessionId, AssemblyInfo asm, byte[] meta_data, byte[] pdb_data) + { + asm.EnC(meta_data, pdb_data); + foreach (var method in asm.Methods) + { + if (method.Value.IsEnCMethod) + yield return method.Value; + } + } + public IEnumerable Add(SessionId sessionId, byte[] assembly_data, byte[] pdb_data) { AssemblyInfo assembly = null; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index f45d31ca1c8..f14bc1e16a9 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -349,7 +349,7 @@ namespace Microsoft.WebAssembly.Diagnostics case "Debugger.removeBreakpoint": { - await RemoveBreakpoint(id, args, token); + await RemoveBreakpoint(id, args, false, token); break; } @@ -689,6 +689,56 @@ namespace Microsoft.WebAssembly.Diagnostics } return false; } + + private async Task ProcessEnC(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + int moduleId = ret_debugger_cmd_reader.ReadInt32(); + int meta_size = ret_debugger_cmd_reader.ReadInt32(); + byte[] meta_buf = ret_debugger_cmd_reader.ReadBytes(meta_size); + int pdb_size = ret_debugger_cmd_reader.ReadInt32(); + byte[] pdb_buf = ret_debugger_cmd_reader.ReadBytes(pdb_size); + + var assembly_name = await sdbHelper.GetAssemblyNameFromModule(sessionId, moduleId, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + foreach (var method in store.EnC(sessionId, asm, meta_buf, pdb_buf)) + await ResetBreakpoint(sessionId, method, token); + return true; + } + + private async Task SendBreakpointsOfMethodUpdated(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var method_id = ret_debugger_cmd_reader.ReadInt32(); + var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); + var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); + var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + if (asm == null) + { + assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); + asm = store.GetAssemblyByName(assembly_name); + if (asm == null) + { + return true; + } + } + MethodInfo method = asm.GetMethodByToken(method_token); + if (method == null) + { + return true; + } + foreach (var req in context.BreakpointRequests.Values) + { + if (req.Method != null && req.Method.Assembly.Id == method.Assembly.Id && req.Method.Token == method.Token) + { + await SetBreakpoint(sessionId, context.store, req, true, token); + } + } + return true; + } + private async Task SendCallStack(SessionId sessionId, ExecutionContext context, string reason, int thread_id, Breakpoint bp, JObject data, IEnumerable orig_callframes, CancellationToken token) { var callFrames = new List(); @@ -844,6 +894,18 @@ namespace Microsoft.WebAssembly.Diagnostics int thread_id = ret_debugger_cmd_reader.ReadInt32(); switch (event_kind) { + case EventKind.MethodUpdate: + { + var ret = await SendBreakpointsOfMethodUpdated(sessionId, context, ret_debugger_cmd_reader, token); + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return ret; + } + case EventKind.EnC: + { + var ret = await ProcessEnC(sessionId, context, ret_debugger_cmd_reader, token); + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return ret; + } case EventKind.Exception: { string reason = "exception"; @@ -1209,7 +1271,9 @@ namespace Microsoft.WebAssembly.Diagnostics await sdbHelper.EnableExceptions(sessionId, "uncaught", token); await sdbHelper.SetProtocolVersion(sessionId, token); - await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.EnC, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.MethodUpdate, token); DebugStore store = await LoadStore(sessionId, token); @@ -1218,7 +1282,21 @@ namespace Microsoft.WebAssembly.Diagnostics return store; } - private async Task RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) + private async Task ResetBreakpoint(SessionId msg_id, MethodInfo method, CancellationToken token) + { + ExecutionContext context = GetContext(msg_id); + foreach (var req in context.BreakpointRequests.Values) + { + if (req.Method != null) + { + if (req.Method.Assembly.Id == method.Assembly.Id && req.Method.Token == method.Token) { + await RemoveBreakpoint(msg_id, JObject.FromObject(new {breakpointId = req.Id}), true, token); + } + } + } + } + + private async Task RemoveBreakpoint(SessionId msg_id, JObject args, bool isEnCReset, CancellationToken token) { string bpid = args?["breakpointId"]?.Value(); @@ -1232,10 +1310,16 @@ namespace Microsoft.WebAssembly.Diagnostics if (breakpoint_removed) { bp.RemoteId = -1; - bp.State = BreakpointState.Disabled; + if (isEnCReset) + bp.State = BreakpointState.Pending; + else + bp.State = BreakpointState.Disabled; } } - context.BreakpointRequests.Remove(bpid); + if (!isEnCReset) + context.BreakpointRequests.Remove(bpid); + else + breakpointRequest.Locations = new List(); } private async Task SetBreakpoint(SessionId sessionId, DebugStore store, BreakpointRequest req, bool sendResolvedEvent, CancellationToken token) @@ -1263,6 +1347,7 @@ namespace Microsoft.WebAssembly.Diagnostics foreach (IGrouping sourceId in locations) { SourceLocation loc = sourceId.First(); + req.Method = loc.CliLocation.Method; Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, req.Condition, token); // If we didn't successfully enable the breakpoint diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 48ed3a9992b..42ad54e6e2c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -83,7 +83,9 @@ namespace Microsoft.WebAssembly.Diagnostics KeepAlive = 14, UserBreak = 15, UserLog = 16, - Crash = 17 + Crash = 17, + EnC = 18, + MethodUpdate = 19 } internal enum ModifierKind { @@ -552,16 +554,18 @@ namespace Microsoft.WebAssembly.Diagnostics var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); return true; } - public async Task EnableReceiveUserBreakRequest(SessionId sessionId, CancellationToken token) + + public async Task EnableReceiveRequests(SessionId sessionId, EventKind event_kind, CancellationToken token) { var command_params = new MemoryStream(); var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.UserBreak); + command_params_writer.Write((byte)event_kind); command_params_writer.Write((byte)SuspendPolicy.None); command_params_writer.Write((byte)0); var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); return true; } + internal async Task SendDebuggerAgentCommandInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, CancellationToken token) { Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommand(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray())), token); @@ -652,6 +656,17 @@ namespace Microsoft.WebAssembly.Diagnostics return ret_debugger_cmd_reader.ReadInt32(); } + public async Task GetAssemblyNameFromModule(SessionId sessionId, int moduleId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(moduleId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdModule.GetInfo, command_params, token); + ret_debugger_cmd_reader.ReadString(); + return ret_debugger_cmd_reader.ReadString(); + } + public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token) { var command_params = new MemoryStream(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index a4f53aac02b..77a8cb6c12e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -287,5 +287,63 @@ namespace DebuggerTests CheckNumber(locals, "b", 10); } + [Fact] + public async Task DebugHotReloadMethodChangedUserBreak() + { + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody1", "StaticMethod1"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 14, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "b", 15); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 14, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckBool(locals, "c", true); + } + + [Fact] + public async Task DebugHotReloadMethodUnchanged() + { + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody2", "StaticMethod1"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 23, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 23, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + } + + [Fact] + public async Task DebugHotReloadMethodAddBreakpoint() + { + int line = 30; + await SetBreakpoint(".*/MethodBody1.cs$", line, 12, use_regex: true); + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody3", "StaticMethod3"); + + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "b", 15); + + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckBool(locals, "c", true); + } + } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index a6af28d8e45..b2662480715 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -1040,6 +1040,55 @@ namespace DebuggerTests await cli.SendCommand("Runtime.evaluate", run_method, token); return await insp.WaitFor(Inspector.PAUSE); } + + internal async Task LoadAssemblyAndTestHotReload(string asm_file, string pdb_file, string asm_file_hot_reload, string class_name, string method_name) + { + // Simulate loading an assembly into the framework + byte[] bytes = File.ReadAllBytes(asm_file); + string asm_base64 = Convert.ToBase64String(bytes); + bytes = File.ReadAllBytes(pdb_file); + string pdb_base64 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dmeta"); + string dmeta1 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dil"); + string dil1 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dpdb"); + string dpdb1 = Convert.ToBase64String(bytes); + + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dmeta"); + string dmeta2 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dil"); + string dil2 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dpdb"); + string dpdb2 = Convert.ToBase64String(bytes); + + string expression = $"let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}';"; + expression = $"{expression} let dmeta1 = '{dmeta1}'; let dil1 = '{dil1}'; let dpdb1 = '{dpdb1}';"; + expression = $"{expression} let dmeta2 = '{dmeta2}'; let dil2 = '{dil2}'; let dpdb2 = '{dpdb2}';"; + expression = $"{{ {expression} invoke_static_method('[debugger-test] TestHotReload:LoadLazyHotReload', asm_b64, pdb_b64, dmeta1, dil1, dpdb1, dmeta2, dil2, dpdb2); }}"; + var load_assemblies = JObject.FromObject(new + { + expression + }); + + Result load_assemblies_res = await cli.SendCommand("Runtime.evaluate", load_assemblies, token); + + Assert.True(load_assemblies_res.IsOk); + Thread.Sleep(1000); + var run_method = JObject.FromObject(new + { + expression = "window.setTimeout(function() { invoke_static_method('[debugger-test] TestHotReload:RunMethod', '" + class_name + "', '" + method_name + "'); }, 1);" + }); + + await cli.SendCommand("Runtime.evaluate", run_method, token); + return await insp.WaitFor(Inspector.PAUSE); + } } class DotnetObjectId diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj new file mode 100644 index 00000000000..550971c8b6f --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -0,0 +1,31 @@ + + + true + deltascript.json + library + false + true + + false + true + + false + false + false + + + + + + + + + + + diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs new file mode 100644 index 00000000000..b2b8f4d364c --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + int a = 10; + Console.WriteLine("original"); + return "OLD STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs new file mode 100644 index 00000000000..03a4d33b064 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + Console.WriteLine("v1"); + double b = 15; + Debugger.Break(); + return "NEW STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + float b = 15; + Console.WriteLine("v1"); + return "NEW STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs new file mode 100644 index 00000000000..4cd88d660a3 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () + { + Console.WriteLine("v2"); + bool c = true; + Debugger.Break(); + return "NEWEST STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + bool c = true; + Console.WriteLine("v2"); + return "NEWEST STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json new file mode 100644 index 00000000000..8e738364bc7 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json @@ -0,0 +1,7 @@ +{ + "changes": [ + {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"}, + {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"}, + ] +} + diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 32177bbc0c5..228d9d1ff62 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -578,3 +578,78 @@ public class LoadDebuggerTestALC { } } + public class TestHotReload { + static System.Reflection.Assembly loadedAssembly; + static byte[] dmeta_data1_bytes; + static byte[] dil_data1_bytes; + static byte[] dpdb_data1_bytes; + static byte[] dmeta_data2_bytes; + static byte[] dil_data2_bytes; + static byte[] dpdb_data2_bytes; + public static void LoadLazyHotReload(string asm_base64, string pdb_base64, string dmeta_data1, string dil_data1, string dpdb_data1, string dmeta_data2, string dil_data2, string dpdb_data2) + { + byte[] asm_bytes = Convert.FromBase64String(asm_base64); + byte[] pdb_bytes = Convert.FromBase64String(pdb_base64); + + dmeta_data1_bytes = Convert.FromBase64String(dmeta_data1); + dil_data1_bytes = Convert.FromBase64String(dil_data1); + dpdb_data1_bytes = Convert.FromBase64String(dpdb_data1); + + dmeta_data2_bytes = Convert.FromBase64String(dmeta_data2); + dil_data2_bytes = Convert.FromBase64String(dil_data2); + dpdb_data2_bytes = Convert.FromBase64String(dpdb_data2); + + + loadedAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(new System.IO.MemoryStream(asm_bytes), new System.IO.MemoryStream(pdb_bytes)); + Console.WriteLine($"Loaded - {loadedAssembly}"); + + } + public static void RunMethod(string className, string methodName) + { + var ty = typeof(System.Reflection.Metadata.AssemblyExtensions); + var mi = ty.GetMethod("GetApplyUpdateCapabilities", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static, Array.Empty()); + + if (mi == null) + return; + + var caps = mi.Invoke(null, null) as string; + + if (String.IsNullOrEmpty(caps)) + return; + + var myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + var myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + + ApplyUpdate(loadedAssembly, 1); + + myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + + ApplyUpdate(loadedAssembly, 2); + + myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + } + + internal static void ApplyUpdate (System.Reflection.Assembly assm, int version) + { + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {version}"); + + if (version == 1) + { + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data1_bytes, dil_data1_bytes, dpdb_data1_bytes); + } + else if (version == 2) + { + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data2_bytes, dil_data2_bytes, dpdb_data2_bytes); + } + + } + } + diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index 648dbcc64fc..797fa167516 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -18,13 +18,15 @@ + $(AppDir) $(MonoProjectRoot)wasm\runtime-test.js - 1 + + -1 true @@ -38,6 +40,30 @@ + + + + + + + + + + + + + + + diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 8fb1e86211a..2c24917f620 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -var Module = { +var Module = { config: null, preInit: async function() { @@ -27,6 +27,10 @@ var Module = { MONO.mono_wasm_setenv ("MONO_LOG_LEVEL", "debug"); MONO.mono_wasm_setenv ("MONO_LOG_MASK", "all"); */ + + Module.config.environment_variables = { + "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" + }; MONO.mono_load_runtime_and_bcl_args (Module.config) }, }; From d021b563e811b0685149c5a5ca7628569bef9f7f Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 13 Jul 2021 03:38:46 +0300 Subject: [PATCH 455/926] Move the "do not zero-extend setcc" optimization to lower (#53778) * Strongly type StoreInd lowering * Improve clarity of code through the use of helpers * Move the "do not zero-extend setcc" to lowering It is XARCH-specific and moving it eliminates questionable code that is trying to compensate for CSE changing the store. * Delete now unnecessary copying of the relop type --- src/coreclr/jit/lower.cpp | 23 +++++++++++------------ src/coreclr/jit/lower.h | 6 +++--- src/coreclr/jit/lowerarmarch.cpp | 8 ++++---- src/coreclr/jit/lowerxarch.cpp | 22 +++++++++++++++------- src/coreclr/jit/morph.cpp | 18 ------------------ 5 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 9499ac5d817..a5b0ba16fdc 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -116,7 +116,7 @@ GenTree* Lowering::LowerNode(GenTree* node) break; case GT_STOREIND: - LowerStoreIndirCommon(node->AsIndir()); + LowerStoreIndirCommon(node->AsStoreInd()); break; case GT_ADD: @@ -3558,7 +3558,7 @@ void Lowering::LowerStoreSingleRegCallStruct(GenTreeBlk* store) { store->ChangeType(regType); store->SetOper(GT_STOREIND); - LowerStoreIndirCommon(store); + LowerStoreIndirCommon(store->AsStoreInd()); return; } else @@ -4100,7 +4100,7 @@ void Lowering::InsertPInvokeMethodProlog() // The init routine sets InlinedCallFrame's m_pNext, so we just set the thead's top-of-stack GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame); firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd)); - ContainCheckStoreIndir(frameUpd->AsIndir()); + ContainCheckStoreIndir(frameUpd->AsStoreInd()); DISPTREERANGE(firstBlockRange, frameUpd); } #endif // TARGET_64BIT @@ -4163,7 +4163,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree* { GenTree* frameUpd = CreateFrameLinkUpdate(PopFrame); returnBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd)); - ContainCheckStoreIndir(frameUpd->AsIndir()); + ContainCheckStoreIndir(frameUpd->AsStoreInd()); } } @@ -4325,7 +4325,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) // Stubs do this once per stub, not once per call. GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame); BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, frameUpd)); - ContainCheckStoreIndir(frameUpd->AsIndir()); + ContainCheckStoreIndir(frameUpd->AsStoreInd()); } #endif // TARGET_64BIT @@ -4335,7 +4335,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) // [tcb + offsetOfGcState] = 0 GenTree* storeGCState = SetGCState(0); BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, storeGCState)); - ContainCheckStoreIndir(storeGCState->AsIndir()); + ContainCheckStoreIndir(storeGCState->AsStoreInd()); // Indicate that codegen has switched this thread to preemptive GC. // This tree node doesn't generate any code, but impacts LSRA and gc reporting. @@ -4381,7 +4381,7 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call) GenTree* tree = SetGCState(1); BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree)); - ContainCheckStoreIndir(tree->AsIndir()); + ContainCheckStoreIndir(tree->AsStoreInd()); tree = CreateReturnTrapSeq(); BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree)); @@ -4396,7 +4396,7 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call) { tree = CreateFrameLinkUpdate(PopFrame); BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree)); - ContainCheckStoreIndir(tree->AsIndir()); + ContainCheckStoreIndir(tree->AsStoreInd()); } #else const CORINFO_EE_INFO::InlinedCallFrameInfo& callFrameInfo = comp->eeGetEEInfo()->inlinedCallFrameInfo; @@ -6421,7 +6421,7 @@ void Lowering::ContainCheckNode(GenTree* node) ContainCheckReturnTrap(node->AsOp()); break; case GT_STOREIND: - ContainCheckStoreIndir(node->AsIndir()); + ContainCheckStoreIndir(node->AsStoreInd()); break; case GT_IND: ContainCheckIndir(node->AsIndir()); @@ -6604,9 +6604,8 @@ void Lowering::ContainCheckBitCast(GenTree* node) // Arguments: // ind - the store indirection node we are lowering. // -void Lowering::LowerStoreIndirCommon(GenTreeIndir* ind) +void Lowering::LowerStoreIndirCommon(GenTreeStoreInd* ind) { - assert(ind->OperIs(GT_STOREIND)); assert(ind->TypeGet() != TYP_STRUCT); TryCreateAddrMode(ind->Addr(), true); if (!comp->codeGen->gcInfo.gcIsWriteBarrierStoreIndNode(ind)) @@ -6806,6 +6805,6 @@ bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode) { assert(src->TypeIs(regType) || src->IsCnsIntOrI() || src->IsCall()); } - LowerStoreIndirCommon(blkNode); + LowerStoreIndirCommon(blkNode->AsStoreInd()); return true; } diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 7f7c9d3760a..a0d897e74da 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -89,7 +89,7 @@ private: void ContainCheckBitCast(GenTree* node); void ContainCheckCallOperands(GenTreeCall* call); void ContainCheckIndir(GenTreeIndir* indirNode); - void ContainCheckStoreIndir(GenTreeIndir* indirNode); + void ContainCheckStoreIndir(GenTreeStoreInd* indirNode); void ContainCheckMul(GenTreeOp* node); void ContainCheckShiftRotate(GenTreeOp* node); void ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const; @@ -292,9 +292,9 @@ private: #endif // defined(TARGET_XARCH) // Per tree node member functions - void LowerStoreIndirCommon(GenTreeIndir* ind); + void LowerStoreIndirCommon(GenTreeStoreInd* ind); void LowerIndir(GenTreeIndir* ind); - void LowerStoreIndir(GenTreeIndir* node); + void LowerStoreIndir(GenTreeStoreInd* node); GenTree* LowerAdd(GenTreeOp* node); bool LowerUnsignedDivOrMod(GenTreeOp* divMod); GenTree* LowerConstIntDivOrMod(GenTree* node); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 7edf8c7103f..134b77281f6 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -218,7 +218,7 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc) // Return Value: // None. // -void Lowering::LowerStoreIndir(GenTreeIndir* node) +void Lowering::LowerStoreIndir(GenTreeStoreInd* node) { ContainCheckStoreIndir(node); } @@ -1376,11 +1376,11 @@ void Lowering::ContainCheckCallOperands(GenTreeCall* call) // Arguments: // node - pointer to the node // -void Lowering::ContainCheckStoreIndir(GenTreeIndir* node) +void Lowering::ContainCheckStoreIndir(GenTreeStoreInd* node) { #ifdef TARGET_ARM64 - GenTree* src = node->AsOp()->gtOp2; - if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0)) + GenTree* src = node->Data(); + if (src->IsIntegralConst(0)) { // an integer zero for 'src' can be contained. MakeSrcContained(node, src); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index ed889f7f383..43c0df62042 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -110,11 +110,11 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc) // Return Value: // None. // -void Lowering::LowerStoreIndir(GenTreeIndir* node) +void Lowering::LowerStoreIndir(GenTreeStoreInd* node) { // Mark all GT_STOREIND nodes to indicate that it is not known // whether it represents a RMW memory op. - node->AsStoreInd()->SetRMWStatusDefault(); + node->SetRMWStatusDefault(); if (!varTypeIsFloating(node)) { @@ -130,10 +130,10 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node) return; } } - else if (node->AsStoreInd()->Data()->OperIs(GT_CNS_DBL)) + else if (node->Data()->IsCnsFltOrDbl()) { // Optimize *x = DCON to *x = ICON which is slightly faster on xarch - GenTree* data = node->AsStoreInd()->Data(); + GenTree* data = node->Data(); double dblCns = data->AsDblCon()->gtDconVal; ssize_t intCns = 0; var_types type = TYP_UNKNOWN; @@ -162,6 +162,13 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node) node->ChangeType(type); } } + + // Optimization: do not unnecessarily zero-extend the result of setcc. + if (varTypeIsByte(node) && (node->Data()->OperIsCompare() || node->Data()->OperIs(GT_SETCC))) + { + node->Data()->ChangeType(TYP_BYTE); + } + ContainCheckStoreIndir(node); } @@ -4588,17 +4595,18 @@ void Lowering::ContainCheckIndir(GenTreeIndir* node) // Arguments: // node - pointer to the node // -void Lowering::ContainCheckStoreIndir(GenTreeIndir* node) +void Lowering::ContainCheckStoreIndir(GenTreeStoreInd* node) { // If the source is a containable immediate, make it contained, unless it is // an int-size or larger store of zero to memory, because we can generate smaller code // by zeroing a register and then storing it. - GenTree* src = node->AsOp()->gtOp2; + GenTree* src = node->Data(); if (IsContainableImmed(node, src) && - (!src->IsIntegralConst(0) || varTypeIsSmall(node) || node->gtGetOp1()->OperGet() == GT_CLS_VAR_ADDR)) + (!src->IsIntegralConst(0) || varTypeIsSmall(node) || node->Addr()->OperIs(GT_CLS_VAR_ADDR))) { MakeSrcContained(node, src); } + ContainCheckIndir(node); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 68f31af7636..b6ee82d3b80 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -12087,23 +12087,8 @@ DONE_MORPHING_CHILDREN: tree->AsOp()->gtOp2 = op2 = op2->AsCast()->CastOp(); } } - else if (op2->OperIsCompare() && varTypeIsByte(effectiveOp1->TypeGet())) - { - /* We don't need to zero extend the setcc instruction */ - op2->gtType = TYP_BYTE; - } } - // If we introduced a CSE we may need to undo the optimization above - // (i.e. " op2->gtType = TYP_BYTE;" which depends upon op1 being a GT_IND of a byte type) - // When we introduce the CSE we remove the GT_IND and subsitute a GT_LCL_VAR in it place. - else if (op2->OperIsCompare() && (op2->gtType == TYP_BYTE) && (op1->gtOper == GT_LCL_VAR)) - { - unsigned varNum = op1->AsLclVarCommon()->GetLclNum(); - LclVarDsc* varDsc = &lvaTable[varNum]; - /* We again need to zero extend the setcc instruction */ - op2->gtType = varDsc->TypeGet(); - } fgAssignSetVarDef(tree); /* We can't CSE the LHS of an assignment */ @@ -12345,9 +12330,6 @@ DONE_MORPHING_CHILDREN: gtReverseCond(op1); } - /* Propagate gtType of tree into op1 in case it is TYP_BYTE for setcc optimization */ - op1->gtType = tree->gtType; - noway_assert((op1->gtFlags & GTF_RELOP_JMP_USED) == 0); op1->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE); From 2d5415894cea63bfb3da1f756b8d40105622f8ac Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 21:40:24 -0500 Subject: [PATCH 456/926] [main] Update dependencies from 6 repositories (#55395) * Update dependencies from https://github.com/dotnet/runtime-assets build 20210708.1 System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.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.Windows.Extensions.TestData From Version 6.0.0-beta.21356.1 -> To Version 6.0.0-beta.21358.1 * Update dependencies from https://github.com/mono/linker build 20210708.3 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21357.1 -> To Version 6.0.100-preview.6.21358.3 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210709.4 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21357.3 -> To Version 1.0.0-prerelease.21359.4 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210709.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21355.1 -> To Version 1.0.1-alpha.0.21359.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210710.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21355.1 -> To Version 1.0.1-alpha.0.21360.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210711.3 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21357.3 -> To Version 1.0.0-prerelease.21361.3 * Update dependencies from https://github.com/dotnet/runtime build 20210711.10 Microsoft.NETCore.DotNetHost , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , Microsoft.NET.Sdk.IL , System.Runtime.CompilerServices.Unsafe , System.Text.Json From Version 6.0.0-preview.7.21355.1 -> To Version 6.0.0-preview.7.21361.10 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210711.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21355.1 -> To Version 1.0.1-alpha.0.21361.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210712.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21358.1 -> To Version 6.0.0-preview.7.21362.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210712.2 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21358.1 -> To Version 6.0.0-preview.7.21362.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 96 ++++++++++++++++++++--------------------- eng/Versions.props | 44 +++++++++---------- global.json | 2 +- 3 files changed, 71 insertions(+), 71 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2389066e4b3..73444fdb70f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - 5c9145289bd4d4e14b18a544dda60a185f66f688 + 7e218d66bf9ec3ca4fc70c0b63e9a162f2e33451 @@ -82,41 +82,41 @@ https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da https://github.com/dotnet/llvm-project @@ -154,37 +154,37 @@ https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/dotnet/runtime - 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6 + 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/mono/linker - 35a1c74d6a0dbd115bf079dc986cea59cdb01430 + b9501922637806f4135df09a9922d5540e203858 https://github.com/dotnet/xharness @@ -198,29 +198,29 @@ https://github.com/dotnet/arcade 286d98094b830b8dad769542b2669cb1b75f7097 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a89f052e97fec59a2d0148c08d3b4801567ec200 + ae45cbdfa6d15fce7e3cf089462f0d2b55727273 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a89f052e97fec59a2d0148c08d3b4801567ec200 + ae45cbdfa6d15fce7e3cf089462f0d2b55727273 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a89f052e97fec59a2d0148c08d3b4801567ec200 + ae45cbdfa6d15fce7e3cf089462f0d2b55727273 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a89f052e97fec59a2d0148c08d3b4801567ec200 + ae45cbdfa6d15fce7e3cf089462f0d2b55727273 - + https://github.com/dotnet/hotreload-utils - 3960ef9a8980181e840b5c1d64ed0b234711e850 + 640e908a67b5bc63fa615d31c7877e62c2b15062 - + https://github.com/dotnet/runtime-assets - c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334 + 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 7be0ee173f2..24788db6f92 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -69,11 +69,11 @@ 6.0.0-preview.1.102 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21355.1 - 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21361.10 + 6.0.0-preview.7.21361.10 3.1.0 - 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21361.10 5.0.0 4.3.0 @@ -107,27 +107,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21355.1 - 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21361.10 + 6.0.0-preview.7.21361.10 4.5.4 4.5.0 - 6.0.0-preview.7.21355.1 + 6.0.0-preview.7.21361.10 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 - 6.0.0-beta.21356.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 + 6.0.0-beta.21358.1 - 1.0.0-prerelease.21357.3 - 1.0.0-prerelease.21357.3 - 1.0.0-prerelease.21357.3 - 1.0.0-prerelease.21357.3 + 1.0.0-prerelease.21361.3 + 1.0.0-prerelease.21361.3 + 1.0.0-prerelease.21361.3 + 1.0.0-prerelease.21361.3 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -153,7 +153,7 @@ 16.9.0-preview-20201201-01 1.0.0-prerelease.21357.4 1.0.0-prerelease.21357.4 - 1.0.1-alpha.0.21355.1 + 1.0.1-alpha.0.21361.1 2.4.1 2.4.2 1.3.0 @@ -164,7 +164,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21357.1 + 6.0.100-preview.6.21358.3 $(MicrosoftNETILLinkTasksVersion) 6.0.0-preview.7.21328.1 diff --git a/global.json b/global.json index 99807111962..e9516bcfa8f 100644 --- a/global.json +++ b/global.json @@ -19,6 +19,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21357.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21355.1" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10" } } From c7ebb65ac7bfd5958836073b52293a2b3dda7737 Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Mon, 12 Jul 2021 19:48:55 -0700 Subject: [PATCH 457/926] switch to using CSTRMarshaller instead of AnsiBSTR (#55032) * switch to using CSTRMarshaller instead of AnsiBSTR * cleanup * Add CleanupManaged back Updated the cleanup logic to the one implemented in ILOptimizedAllocMarshaler::EmitClearNative * update MAX_LOCAL_BUFFER_LENGTH * CR feedback --- .../TypeSystem/Interop/IL/Marshaller.cs | 92 ++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index 46c53157b46..f78e4df8a29 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -1581,6 +1581,12 @@ namespace Internal.TypeSystem.Interop class AnsiStringMarshaller : Marshaller { +#if READYTORUN + const int MAX_LOCAL_BUFFER_LENGTH = 260 + 1; // MAX_PATH + 1 + + private ILLocalVariable? _localBuffer = null; +#endif + internal override bool CleanupRequired { get @@ -1605,12 +1611,75 @@ namespace Internal.TypeSystem.Interop #if READYTORUN var stringToAnsi = - Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler") + Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler") .GetKnownMethod("ConvertToNative", null); + + bool bPassByValueInOnly = In && !Out && !IsManagedByRef; + + if (bPassByValueInOnly) + { + var bufSize = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + _localBuffer = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr)); + + // LocalBuffer = 0 + codeStream.Emit(ILOpcode.ldnull); + codeStream.EmitStLoc((ILLocalVariable)_localBuffer); + + var noOptimize = emitter.NewCodeLabel(); + + // if == NULL, goto NoOptimize + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, noOptimize); + + // String.Length + 2 + LoadManagedValue(codeStream); + var stringLen = + Context.GetWellKnownType(WellKnownType.String) + .GetKnownMethod("get_Length", null); + codeStream.Emit(ILOpcode.call, emitter.NewToken(stringLen)); + codeStream.EmitLdc(2); + codeStream.Emit(ILOpcode.add); + + // (String.Length + 2) * GetMaxDBCSCharByteSize() + codeStream.Emit(ILOpcode.ldsfld, emitter.NewToken(Context.SystemModule.GetKnownType( + "System.Runtime.InteropServices","Marshal") + .GetKnownField("SystemMaxDBCSCharSize"))); + codeStream.Emit(ILOpcode.mul_ovf); + + // BufSize = (String.Length + 2) * GetMaxDBCSCharByteSize() + codeStream.EmitStLoc(bufSize); + + // if (MAX_LOCAL_BUFFER_LENGTH < BufSize ) goto NoOptimize + codeStream.EmitLdc(MAX_LOCAL_BUFFER_LENGTH + 1); + codeStream.EmitLdLoc(bufSize); + codeStream.Emit(ILOpcode.clt); + codeStream.Emit(ILOpcode.brtrue, noOptimize); + + // LocalBuffer = localloc(BufSize); + codeStream.EmitLdLoc(bufSize); + codeStream.Emit(ILOpcode.localloc); + codeStream.EmitStLoc((ILLocalVariable)_localBuffer); + + // NoOptimize: + codeStream.EmitLabel(noOptimize); + } + int flags = (PInvokeFlags.BestFitMapping ? 0x1 : 0) | (PInvokeFlags.ThrowOnUnmappableChar ? 0x100 : 0); + + // CSTRMarshaler.ConvertToNative pManaged, dwAnsiMarshalFlags, pLocalBuffer codeStream.EmitLdc(flags); LoadManagedValue(codeStream); + + if (_localBuffer.HasValue) + { + codeStream.EmitLdLoc((ILLocalVariable)_localBuffer); + } + else + { + codeStream.Emit(ILOpcode.ldnull); + } + codeStream.Emit(ILOpcode.call, emitter.NewToken(stringToAnsi)); #else LoadManagedValue(codeStream); @@ -1631,7 +1700,7 @@ namespace Internal.TypeSystem.Interop #if READYTORUN var ansiToString = - Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler") + Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler") .GetKnownMethod("ConvertToManaged", null); #else var ansiToString = Context.GetHelperEntryPoint("InteropHelpers", "AnsiStringToString"); @@ -1645,11 +1714,28 @@ namespace Internal.TypeSystem.Interop { var emitter = _ilCodeStreams.Emitter; #if READYTORUN + var optimize = emitter.NewCodeLabel(); + MethodDesc clearNative = - Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler") + Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler") .GetKnownMethod("ClearNative", null); + + if (_localBuffer.HasValue) + { + // if (m_dwLocalBuffer) goto Optimize + codeStream.EmitLdLoc((ILLocalVariable)_localBuffer); + codeStream.Emit(ILOpcode.brtrue, optimize); + } + LoadNativeValue(codeStream); + // static void m_idClearNative(IntPtr ptr) codeStream.Emit(ILOpcode.call, emitter.NewToken(clearNative)); + + // Optimize: + if (_localBuffer != default) + { + codeStream.EmitLabel(optimize); + } #else var lNullCheck = emitter.NewCodeLabel(); From 66967685e97c48c416f1887530735925b2500c73 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Mon, 12 Jul 2021 20:05:15 -0700 Subject: [PATCH 458/926] Disable GetHostEntry tests on SLES. (#55543) --- .../tests/FunctionalTests/GetHostEntryTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index 7a5d4c899e9..bfb8b48cc4e 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -28,7 +28,9 @@ namespace System.Net.NameResolution.Tests // [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] PlatformDetection.IsNotOSX && // [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst; + !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst && + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55271")] + !PlatformDetection.IsSLES; [ConditionalTheory(nameof(GetHostEntryWorks))] [InlineData("")] From 7b19ccefccb4d116a64bf09c9bb1db3dd1df35e8 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Mon, 12 Jul 2021 21:11:25 -0700 Subject: [PATCH 459/926] Enable property visibility tests in source-gen mode (#54526) * Enable property visibility tests in source-gen mode * Address feedback * Fix S.N.H.Json tests * Fix S.T.J tests * Fix S.N.H.J tests ii --- .../FunctionalTests/JsonContext/Person.cs | 12 + .../System.Text.Json/Common/JsonHelpers.cs | 28 + .../Common/ReflectionExtensions.cs | 17 + .../gen/ContextGenerationSpec.cs | 4 +- .../gen/JsonSourceGenerator.Emitter.cs | 421 ++- .../gen/JsonSourceGenerator.Parser.cs | 201 +- .../gen/JsonSourceGenerator.cs | 20 +- .../gen/PropertyGenerationSpec.cs | 14 +- .../gen/Reflection/AssemblyWrapper.cs | 2 +- .../gen/Reflection/ConstructorInfoWrapper.cs | 2 +- .../Reflection/CustomAttributeDataWrapper.cs | 2 +- .../gen/Reflection/FieldInfoWrapper.cs | 10 +- .../gen/Reflection/MemberInfoWrapper.cs | 2 +- .../Reflection/MetadataLoadContextInternal.cs | 2 +- .../gen/Reflection/MethodInfoWrapper.cs | 7 +- .../gen/Reflection/ParameterInfoWrapper.cs | 2 +- .../gen/Reflection/PropertyInfoWrapper.cs | 2 +- .../gen/Reflection/ReflectionExtensions.cs | 4 +- .../gen/Reflection/RoslynExtensions.cs | 2 +- .../gen/Reflection/TypeExtensions.cs | 2 +- .../gen/Reflection/TypeWrapper.cs | 46 +- .../System.Text.Json.SourceGeneration.csproj | 3 + .../gen/TypeGenerationSpec.cs | 89 +- .../System.Text.Json/ref/System.Text.Json.cs | 2 +- .../src/Resources/Strings.resx | 3 + .../src/System.Text.Json.csproj | 2 + .../src/System/Text/Json/JsonHelpers.cs | 19 - .../Metadata/JsonMetadataServices.cs | 17 +- .../Metadata/JsonPropertyInfo.cs | 25 +- .../Metadata/JsonPropertyInfoOfT.cs | 11 +- .../Metadata/JsonTypeInfo.Cache.cs | 41 +- .../Serialization/Metadata/JsonTypeInfo.cs | 115 +- .../Text/Json/ThrowHelper.Serialization.cs | 4 +- .../Common/JsonSerializerWrapperForString.cs | 27 + .../PropertyVisibilityTests.InitOnly.cs} | 21 +- ...pertyVisibilityTests.NonPublicAccessors.cs | 107 +- .../tests/Common/PropertyVisibilityTests.cs | 2632 +++++++++++++++++ .../tests/Common/SerializerTests.cs | 12 + .../TestClasses.ConcurrentCollections.cs | 0 .../TestClasses/TestClasses.Constructor.cs | 0 .../TestClasses.GenericCollections.cs | 0 .../TestClasses.ImmutableCollections.cs | 0 .../TestClasses.NonGenericCollections.cs | 0 .../TestClasses/TestClasses.Polymorphic.cs | 0 .../TestClasses.SimpleTestClass.cs | 0 .../TestClasses.SimpleTestClassWithFields.cs | 0 ...estClasses.SimpleTestClassWithNullables.cs | 0 .../TestClasses.SimpleTestClassWithObject.cs | 0 ...Classes.SimpleTestClassWithObjectArrays.cs | 0 ...Classes.SimpleTestClassWithSimpleObject.cs | 0 .../TestClasses.SimpleTestStruct.cs | 0 .../TestClasses.SimpleTestStructWithFields.cs | 0 .../TestClasses.ValueTypedMember.cs | 0 .../TestClasses/TestClasses.cs | 0 ...sonSerializerWrapperForString_SourceGen.cs | 102 + .../Serialization/PropertyVisibilityTests.cs | 424 +++ ...em.Text.Json.SourceGeneration.Tests.csproj | 29 + .../JsonSourceGeneratorTests.cs | 7 +- .../TypeWrapperTests.cs | 6 +- .../JsonSerializerWrapperForString.cs | 18 +- .../JsonSerializerWrapperForString_Dynamic.cs | 32 + .../MetadataTests/JsonContext/HighLowTemps.cs | 6 + .../JsonContext/WeatherForecastWithPOCOs.cs | 21 + .../MetadataTests.JsonMetadataServices.cs | 33 + .../MetadataTests.JsonSerializer.cs | 1 + .../Serialization/PropertyVisibilityTests.cs | 2547 +--------------- .../System.Text.Json.Tests.csproj | 42 +- 67 files changed, 4158 insertions(+), 3042 deletions(-) create mode 100644 src/libraries/System.Text.Json/Common/JsonHelpers.cs create mode 100644 src/libraries/System.Text.Json/Common/ReflectionExtensions.cs create mode 100644 src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization/PropertyVisiblityTests.InitOnly.cs => Common/PropertyVisibilityTests.InitOnly.cs} (73%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/PropertyVisibilityTests.NonPublicAccessors.cs (66%) create mode 100644 src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs create mode 100644 src/libraries/System.Text.Json/tests/Common/SerializerTests.cs rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.ConcurrentCollections.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.Constructor.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.GenericCollections.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.ImmutableCollections.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.NonGenericCollections.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.Polymorphic.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClass.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClassWithFields.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClassWithNullables.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClassWithObject.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClassWithObjectArrays.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestClassWithSimpleObject.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestStruct.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.SimpleTestStructWithFields.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.ValueTypedMember.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/TestClasses/TestClasses.cs (100%) create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs index 36785e95bd0..8049845e1a2 100644 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs +++ b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs @@ -47,12 +47,15 @@ namespace System.Net.Http.Json.Functional.Tests properties[0] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(Person), propertyTypeInfo: jsonContext.Int32, converter: null, getter: static (obj) => { return ((Person)obj).Age; }, setter: static (obj, value) => { ((Person)obj).Age = value; }, ignoreCondition: default, + hasJsonInclude: false, numberHandling: default, propertyName: nameof(Tests.Person.Age), jsonPropertyName: null); @@ -60,12 +63,15 @@ namespace System.Net.Http.Json.Functional.Tests properties[1] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(Person), propertyTypeInfo: jsonContext.String, converter: null, getter: static (obj) => { return ((Person)obj).Name; }, setter: static (obj, value) => { ((Person)obj).Name = value; }, ignoreCondition: default, + hasJsonInclude: false, numberHandling: default, propertyName: nameof(Tests.Person.Name), jsonPropertyName: null); @@ -73,12 +79,15 @@ namespace System.Net.Http.Json.Functional.Tests properties[2] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(Person), propertyTypeInfo: jsonContext.Person, converter: null, getter: static (obj) => { return ((Person)obj).Parent; }, setter: static (obj, value) => { ((Person)obj).Parent = value; }, ignoreCondition: default, + hasJsonInclude: false, numberHandling: default, propertyName: nameof(Tests.Person.Parent), jsonPropertyName: null); @@ -86,12 +95,15 @@ namespace System.Net.Http.Json.Functional.Tests properties[3] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(Person), propertyTypeInfo: jsonContext.String, converter: null, getter: static (obj) => { return ((Person)obj).PlaceOfBirth; }, setter: static (obj, value) => { ((Person)obj).PlaceOfBirth = value; }, ignoreCondition: default, + hasJsonInclude: false, numberHandling: default, propertyName: nameof(Tests.Person.PlaceOfBirth), jsonPropertyName: null); diff --git a/src/libraries/System.Text.Json/Common/JsonHelpers.cs b/src/libraries/System.Text.Json/Common/JsonHelpers.cs new file mode 100644 index 00000000000..96a2872621f --- /dev/null +++ b/src/libraries/System.Text.Json/Common/JsonHelpers.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Text.Json +{ + internal static partial class JsonHelpers + { + /// + /// Emulates Dictionary.TryAdd on netstandard. + /// + public static bool TryAdd(this Dictionary dictionary, in TKey key, in TValue value) where TKey : notnull + { +#if NETSTANDARD2_0 || NETFRAMEWORK + if (!dictionary.ContainsKey(key)) + { + dictionary[key] = value; + return true; + } + + return false; +#else + return dictionary.TryAdd(key, value); +#endif + } + } +} diff --git a/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs new file mode 100644 index 00000000000..5d9ef545b8d --- /dev/null +++ b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Reflection; + +namespace System.Text.Json.Reflection +{ + internal static partial class ReflectionExtensions + { + public static bool IsVirtual(this PropertyInfo? propertyInfo) + { + Debug.Assert(propertyInfo != null); + return propertyInfo != null && (propertyInfo.GetMethod?.IsVirtual == true || propertyInfo.SetMethod?.IsVirtual == true); + } + } +} diff --git a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs index f4b27c2db11..8020f798086 100644 --- a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -using System.Text.Json.SourceGeneration.Reflection; +using System.Text.Json.Reflection; namespace System.Text.Json.SourceGeneration { @@ -19,6 +19,8 @@ namespace System.Text.Json.SourceGeneration public List RootSerializableTypes { get; } = new(); + public HashSet? NullableUnderlyingTypes { get; } = new(); + public List ContextClassDeclarationList { get; init; } /// diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 811a4ea8365..cbab663971e 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -35,8 +37,9 @@ namespace System.Text.Json.SourceGeneration private const string ArrayTypeRef = "global::System.Array"; private const string InvalidOperationExceptionTypeRef = "global::System.InvalidOperationException"; private const string TypeTypeRef = "global::System.Type"; - private const string UnsafeTypeRef = "global::System.CompilerServices.Unsafe"; + private const string UnsafeTypeRef = "global::System.Runtime.CompilerServices.Unsafe"; private const string NullableTypeRef = "global::System.Nullable"; + private const string EqualityComparerTypeRef = "global::System.Collections.Generic.EqualityComparer"; private const string IListTypeRef = "global::System.Collections.Generic.IList"; private const string KeyValuePairTypeRef = "global::System.Collections.Generic.KeyValuePair"; private const string ListTypeRef = "global::System.Collections.Generic.List"; @@ -210,9 +213,9 @@ namespace {_currentContext.ContextType.Namespace} { source = GenerateForObject(typeGenerationSpec); - if (typeGenerationSpec.PropertiesMetadata != null) + if (typeGenerationSpec.PropertyGenSpecList != null) { - foreach (PropertyGenerationSpec metadata in typeGenerationSpec.PropertiesMetadata) + foreach (PropertyGenerationSpec metadata in typeGenerationSpec.PropertyGenSpecList) { GenerateTypeInfo(metadata.TypeGenerationSpec); } @@ -279,7 +282,7 @@ namespace {_currentContext.ContextType.Namespace} }} // Allow nullable handling to forward to the underlying type's converter. - converter = {JsonMetadataServicesTypeRef}.GetNullableConverter<{typeCompilableName}>(({JsonConverterTypeRef}<{typeCompilableName}>)actualConverter); + converter = {JsonMetadataServicesTypeRef}.GetNullableConverter<{typeCompilableName}>(this.{typeFriendlyName}); }} else {{ @@ -471,7 +474,6 @@ namespace {_currentContext.ContextType.Namespace} private string GenerateForObject(TypeGenerationSpec typeMetadata) { - string typeCompilableName = typeMetadata.TypeRef; string typeFriendlyName = typeMetadata.TypeInfoPropertyName; string createObjectFuncTypeArg = typeMetadata.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor @@ -486,11 +488,9 @@ namespace {_currentContext.ContextType.Namespace} string? serializeFuncSource = null; string serializeFuncNamedArg; - List? properties = typeMetadata.PropertiesMetadata; - if (typeMetadata.GenerateMetadata) { - propMetadataInitFuncSource = GeneratePropMetadataInitFunc(typeMetadata.IsValueType, propInitMethodName, properties); + propMetadataInitFuncSource = GeneratePropMetadataInitFunc(typeMetadata.IsValueType, propInitMethodName, typeMetadata.PropertyGenSpecList!); propMetadataInitFuncNamedArg = $@"propInitFunc: {propInitMethodName}"; } else @@ -500,13 +500,7 @@ namespace {_currentContext.ContextType.Namespace} if (typeMetadata.GenerateSerializationLogic) { - serializeFuncSource = GenerateFastPathFuncForObject( - typeCompilableName, - serializeMethodName, - typeMetadata.CanBeNull, - typeMetadata.ImplementsIJsonOnSerialized, - typeMetadata.ImplementsIJsonOnSerializing, - properties); + serializeFuncSource = GenerateFastPathFuncForObject(typeMetadata, serializeMethodName); serializeFuncNamedArg = $@"serializeFunc: {serializeMethodName}"; } else @@ -514,14 +508,12 @@ namespace {_currentContext.ContextType.Namespace} serializeFuncNamedArg = @"serializeFunc: null"; } - string objectInfoInitSource = $@"{JsonTypeInfoTypeRef}<{typeCompilableName}> objectInfo = {JsonMetadataServicesTypeRef}.CreateObjectInfo<{typeCompilableName}>( + string objectInfoInitSource = $@"_{typeFriendlyName} = {JsonMetadataServicesTypeRef}.CreateObjectInfo<{typeMetadata.TypeRef}>( {OptionsInstanceVariableName}, {createObjectFuncTypeArg}, {propMetadataInitFuncNamedArg}, {GetNumberHandlingAsStr(typeMetadata.NumberHandling)}, - {serializeFuncNamedArg}); - - _{typeFriendlyName} = objectInfo;"; + {serializeFuncNamedArg});"; string additionalSource; if (propMetadataInitFuncSource == null || serializeFuncSource == null) @@ -539,14 +531,16 @@ namespace {_currentContext.ContextType.Namespace} private string GeneratePropMetadataInitFunc( bool declaringTypeIsValueType, string propInitMethodName, - List? properties) + List properties) { const string PropVarName = "properties"; const string JsonContextVarName = "jsonContext"; + int propCount = properties.Count; + string propertyArrayInstantiationValue = properties == null ? $"{ArrayTypeRef}.Empty<{JsonPropertyInfoTypeRef}>()" - : $"new {JsonPropertyInfoTypeRef}[{properties.Count}]"; + : $"new {JsonPropertyInfoTypeRef}[{propCount}]"; string contextTypeRef = _currentContext.ContextTypeRef; @@ -562,72 +556,72 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC {JsonPropertyInfoTypeRef}[] {PropVarName} = {propertyArrayInstantiationValue}; "); - if (properties != null) + for (int i = 0; i < propCount; i++) { - for (int i = 0; i < properties.Count; i++) + PropertyGenerationSpec memberMetadata = properties[i]; + + TypeGenerationSpec memberTypeMetadata = memberMetadata.TypeGenerationSpec; + + string clrPropertyName = memberMetadata.ClrName; + + string declaringTypeCompilableName = memberMetadata.DeclaringTypeRef; + + string memberTypeFriendlyName = memberTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen + ? "null" + : $"{JsonContextVarName}.{memberTypeMetadata.TypeInfoPropertyName}"; + + string typeTypeInfoNamedArg = $"propertyTypeInfo: {memberTypeFriendlyName}"; + + string jsonPropertyNameNamedArg = memberMetadata.JsonPropertyName != null + ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}""" + : "jsonPropertyName: null"; + + string getterNamedArg = memberMetadata.CanUseGetter + ? $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}" + : "getter: null"; + + string setterNamedArg; + if (memberMetadata.CanUseSetter) { - PropertyGenerationSpec memberMetadata = properties[i]; + string propMutation = declaringTypeIsValueType + ? @$"{UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value" + : $@"(({declaringTypeCompilableName})obj).{clrPropertyName} = value"; - TypeGenerationSpec memberTypeMetadata = memberMetadata.TypeGenerationSpec; + setterNamedArg = $"setter: static (obj, value) => {propMutation}"; + } + else + { + setterNamedArg = "setter: null"; + } - string clrPropertyName = memberMetadata.ClrName; + JsonIgnoreCondition? ignoreCondition = memberMetadata.DefaultIgnoreCondition; + string ignoreConditionNamedArg = ignoreCondition.HasValue + ? $"ignoreCondition: {JsonIgnoreConditionTypeRef}.{ignoreCondition.Value}" + : "ignoreCondition: null"; - string declaringTypeCompilableName = memberMetadata.DeclaringTypeRef; + string converterNamedArg = memberMetadata.ConverterInstantiationLogic == null + ? "converter: null" + : $"converter: {memberMetadata.ConverterInstantiationLogic}"; - string memberTypeFriendlyName = memberTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen - ? "null" - : $"{JsonContextVarName}.{memberTypeMetadata.TypeInfoPropertyName}"; + string memberTypeCompilableName = memberTypeMetadata.TypeRef; - string typeTypeInfoNamedArg = $"propertyTypeInfo: {memberTypeFriendlyName}"; - - string jsonPropertyNameNamedArg = memberMetadata.JsonPropertyName != null - ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}""" - : "jsonPropertyName: null"; - - string getterNamedArg = memberMetadata.CanUseGetter - ? $"getter: static (obj) => {{ return (({declaringTypeCompilableName})obj).{clrPropertyName}; }}" - : "getter: null"; - - string setterNamedArg; - if (memberMetadata.CanUseSetter) - { - string propMutation = declaringTypeIsValueType - ? @$"{{ {UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value; }}" - : $@"{{ (({declaringTypeCompilableName})obj).{clrPropertyName} = value; }}"; - - setterNamedArg = $"setter: static (obj, value) => {propMutation}"; - } - else - { - setterNamedArg = "setter: null"; - } - - JsonIgnoreCondition? ignoreCondition = memberMetadata.DefaultIgnoreCondition; - string ignoreConditionNamedArg = ignoreCondition.HasValue - ? $"ignoreCondition: JsonIgnoreCondition.{ignoreCondition.Value}" - : "ignoreCondition: default"; - - string converterNamedArg = memberMetadata.ConverterInstantiationLogic == null - ? "converter: null" - : $"converter: {memberMetadata.ConverterInstantiationLogic}"; - - string memberTypeCompilableName = memberTypeMetadata.TypeRef; - - sb.Append($@" + sb.Append($@" {PropVarName}[{i}] = {JsonMetadataServicesTypeRef}.CreatePropertyInfo<{memberTypeCompilableName}>( options, - isProperty: {memberMetadata.IsProperty.ToString().ToLowerInvariant()}, + isProperty: {ToCSharpKeyword(memberMetadata.IsProperty)}, + isPublic: {ToCSharpKeyword(memberMetadata.IsPublic)}, + isVirtual: {ToCSharpKeyword(memberMetadata.IsVirtual)}, declaringType: typeof({memberMetadata.DeclaringTypeRef}), {typeTypeInfoNamedArg}, {converterNamedArg}, {getterNamedArg}, {setterNamedArg}, {ignoreConditionNamedArg}, + hasJsonInclude: {ToCSharpKeyword(memberMetadata.HasJsonInclude)}, numberHandling: {GetNumberHandlingAsStr(memberMetadata.NumberHandling)}, propertyName: ""{clrPropertyName}"", {jsonPropertyNameNamedArg}); "); - } } sb.Append(@$" @@ -637,24 +631,29 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC return sb.ToString(); } - private string GenerateFastPathFuncForObject( - string typeInfoTypeRef, - string serializeMethodName, - bool canBeNull, - bool implementsIJsonOnSerialized, - bool implementsIJsonOnSerializing, - List? properties) + private string GenerateFastPathFuncForObject(TypeGenerationSpec typeGenSpec, string serializeMethodName) { JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions; + string typeRef = typeGenSpec.TypeRef; - // Add the property names to the context-wide cache; we'll generate the source to initialize them at the end of generation. - string[] runtimePropNames = GetRuntimePropNames(properties, options.PropertyNamingPolicy); - _currentContext.RuntimePropertyNames.UnionWith(runtimePropNames); + if (!typeGenSpec.TryFilterSerializableProps( + options, + out Dictionary? serializableProperties, + out bool castingRequiredForProps)) + { + string exceptionMessage = @$"""Invalid serializable-property configuration specified for type '{typeRef}'. For more information, use 'JsonSourceGenerationMode.Serialization'."""; + + return GenerateFastPathFuncForType( + serializeMethodName, + typeRef, + $@"throw new {InvalidOperationExceptionTypeRef}({exceptionMessage});", + canBeNull: false); // Skip null check since we want to throw an exception straightaway. + } StringBuilder sb = new(); - // Begin method definition - if (implementsIJsonOnSerializing) + // Begin method logic. + if (typeGenSpec.ImplementsIJsonOnSerializing) { sb.Append($@"(({IJsonOnSerializingFullName}){ValueVarName}).OnSerializing();"); sb.Append($@"{Environment.NewLine} "); @@ -662,98 +661,119 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC sb.Append($@"{WriterVarName}.WriteStartObject();"); - if (properties != null) + // Provide generation logic for each prop. + Debug.Assert(serializableProperties != null); + + foreach (PropertyGenerationSpec propertyGenSpec in serializableProperties.Values) { - // Provide generation logic for each prop. - for (int i = 0; i < properties.Count; i++) + if (!ShouldIncludePropertyForFastPath(propertyGenSpec, options)) { - PropertyGenerationSpec propertySpec = properties[i]; - TypeGenerationSpec propertyTypeSpec = propertySpec.TypeGenerationSpec; - - if (propertyTypeSpec.ClassType == ClassType.TypeUnsupportedBySourceGen) - { - continue; - } - - if (propertySpec.IsReadOnly) - { - if (propertySpec.IsProperty) - { - if (options.IgnoreReadOnlyProperties) - { - continue; - } - } - else if (options.IgnoreReadOnlyFields) - { - continue; - } - } - - if (!propertySpec.IsProperty && !propertySpec.HasJsonInclude && !options.IncludeFields) - { - continue; - } - - Type propertyType = propertyTypeSpec.Type; - string propName = $"{runtimePropNames[i]}PropName"; - string propValue = $"{ValueVarName}.{propertySpec.ClrName}"; - string methodArgs = $"{propName}, {propValue}"; - - string? methodToCall = GetWriterMethod(propertyType); - - if (propertyType == _generationSpec.CharType) - { - methodArgs = $"{methodArgs}.ToString()"; - } - - string serializationLogic; - - if (methodToCall != null) - { - serializationLogic = $@" - {methodToCall}({methodArgs});"; - } - else - { - serializationLogic = $@" - {WriterVarName}.WritePropertyName({propName}); - {GetSerializeLogicForNonPrimitiveType(propertyTypeSpec.TypeInfoPropertyName, propValue, propertyTypeSpec.GenerateSerializationLogic)}"; - } - - JsonIgnoreCondition ignoreCondition = propertySpec.DefaultIgnoreCondition ?? options.DefaultIgnoreCondition; - DefaultCheckType defaultCheckType; - bool typeCanBeNull = propertyTypeSpec.CanBeNull; - - switch (ignoreCondition) - { - case JsonIgnoreCondition.WhenWritingNull: - defaultCheckType = typeCanBeNull ? DefaultCheckType.Null : DefaultCheckType.None; - break; - case JsonIgnoreCondition.WhenWritingDefault: - defaultCheckType = typeCanBeNull ? DefaultCheckType.Null : DefaultCheckType.Default; - break; - default: - defaultCheckType = DefaultCheckType.None; - break; - } - - sb.Append(WrapSerializationLogicInDefaultCheckIfRequired(serializationLogic, propValue, defaultCheckType)); + continue; } + + TypeGenerationSpec propertyTypeSpec = propertyGenSpec.TypeGenerationSpec; + + string runtimePropName = propertyGenSpec.RuntimePropertyName; + + // Add the property names to the context-wide cache; we'll generate the source to initialize them at the end of generation. + _currentContext.RuntimePropertyNames.Add(runtimePropName); + + Type propertyType = propertyTypeSpec.Type; + string propName = $"{runtimePropName}PropName"; + string? objectRef = castingRequiredForProps ? $"(({propertyGenSpec.DeclaringTypeRef}){ValueVarName})" : ValueVarName; + string propValue = $"{objectRef}.{propertyGenSpec.ClrName}"; + string methodArgs = $"{propName}, {propValue}"; + + string? methodToCall = GetWriterMethod(propertyType); + + if (propertyType == _generationSpec.CharType) + { + methodArgs = $"{methodArgs}.ToString()"; + } + + string serializationLogic; + + if (methodToCall != null) + { + serializationLogic = $@" + {methodToCall}({methodArgs});"; + } + else + { + serializationLogic = $@" + {WriterVarName}.WritePropertyName({propName}); + {GetSerializeLogicForNonPrimitiveType(propertyTypeSpec.TypeInfoPropertyName, propValue, propertyTypeSpec.GenerateSerializationLogic)}"; + } + + JsonIgnoreCondition ignoreCondition = propertyGenSpec.DefaultIgnoreCondition ?? options.DefaultIgnoreCondition; + DefaultCheckType defaultCheckType; + bool typeCanBeNull = propertyTypeSpec.CanBeNull; + + switch (ignoreCondition) + { + case JsonIgnoreCondition.WhenWritingNull: + defaultCheckType = typeCanBeNull ? DefaultCheckType.Null : DefaultCheckType.None; + break; + case JsonIgnoreCondition.WhenWritingDefault: + defaultCheckType = typeCanBeNull ? DefaultCheckType.Null : DefaultCheckType.Default; + break; + default: + defaultCheckType = DefaultCheckType.None; + break; + } + + sb.Append(WrapSerializationLogicInDefaultCheckIfRequired(serializationLogic, propValue, propertyTypeSpec.TypeRef, defaultCheckType)); } - // End method definition + // End method logic. sb.Append($@" - {WriterVarName}.WriteEndObject();"); + {WriterVarName}.WriteEndObject();"); - if (implementsIJsonOnSerialized) + if (typeGenSpec.ImplementsIJsonOnSerialized) { sb.Append($@"{Environment.NewLine} "); sb.Append($@"(({IJsonOnSerializedFullName}){ValueVarName}).OnSerialized();"); }; - return GenerateFastPathFuncForType(serializeMethodName, typeInfoTypeRef, sb.ToString(), canBeNull); + return GenerateFastPathFuncForType(serializeMethodName, typeRef, sb.ToString(), typeGenSpec.CanBeNull); + } + + private static bool ShouldIncludePropertyForFastPath(PropertyGenerationSpec propertyGenSpec, JsonSourceGenerationOptionsAttribute options) + { + TypeGenerationSpec propertyTypeSpec = propertyGenSpec.TypeGenerationSpec; + + if (propertyTypeSpec.ClassType == ClassType.TypeUnsupportedBySourceGen || !propertyGenSpec.CanUseGetter) + { + return false; + } + + if (!propertyGenSpec.IsProperty && !propertyGenSpec.HasJsonInclude && !options.IncludeFields) + { + return false; + } + + if (propertyGenSpec.DefaultIgnoreCondition == JsonIgnoreCondition.Always) + { + return false; + } + + if (propertyGenSpec.IsReadOnly) + { + if (propertyGenSpec.IsProperty) + { + if (options.IgnoreReadOnlyProperties) + { + return false; + } + } + else if (options.IgnoreReadOnlyFields) + { + return false; + } + } + + return true; } private string? GetWriterMethod(Type type) @@ -829,62 +849,28 @@ private static void {serializeMethodName}({Utf8JsonWriterTypeRef} {WriterVarName Default, } - private string WrapSerializationLogicInDefaultCheckIfRequired(string serializationLogic, string propValue, DefaultCheckType defaultCheckType) + private string WrapSerializationLogicInDefaultCheckIfRequired(string serializationLogic, string propValue, string propTypeRef, DefaultCheckType defaultCheckType) { - if (defaultCheckType == DefaultCheckType.None) + string comparisonLogic; + + switch (defaultCheckType) { - return serializationLogic; + case DefaultCheckType.None: + return serializationLogic; + case DefaultCheckType.Null: + comparisonLogic = $"{propValue} != null"; + break; + case DefaultCheckType.Default: + comparisonLogic = $"!{EqualityComparerTypeRef}<{propTypeRef}>.Default.Equals(default, {propValue})"; + break; + default: + throw new InvalidOperationException(); } - string defaultLiteral = defaultCheckType == DefaultCheckType.Null ? "null" : "default"; return $@" - if ({propValue} != {defaultLiteral}) - {{{serializationLogic} - }}"; - } - - private string[] GetRuntimePropNames(List? properties, JsonKnownNamingPolicy namingPolicy) - { - if (properties == null) - { - return Array.Empty(); - } - - int propCount = properties.Count; - string[] runtimePropNames = new string[propCount]; - - // Compute JsonEncodedText values to represent each property name. This gives the best throughput performance - for (int i = 0; i < propCount; i++) - { - PropertyGenerationSpec propertySpec = properties[i]; - - string propName = DetermineRuntimePropName(propertySpec.ClrName, propertySpec.JsonPropertyName, namingPolicy); - Debug.Assert(propName != null); - - runtimePropNames[i] = propName; - } - - return runtimePropNames; - } - - private string DetermineRuntimePropName(string clrPropName, string? jsonPropName, JsonKnownNamingPolicy namingPolicy) - { - string runtimePropName; - - if (jsonPropName != null) - { - runtimePropName = jsonPropName; - } - else if (namingPolicy == JsonKnownNamingPolicy.CamelCase) - { - runtimePropName = JsonNamingPolicy.CamelCase.ConvertName(clrPropName); - } - else - { - runtimePropName = clrPropName; - } - - return runtimePropName; + if ({comparisonLogic}) + {{{IndentSource(serializationLogic, numIndentations: 1)} + }}"; } private string GenerateForType(TypeGenerationSpec typeMetadata, string metadataInitSource, string? additionalSource = null) @@ -964,10 +950,10 @@ public {contextTypeName}({JsonSerializerOptionsTypeRef} options) : base(options, private static {JsonSerializerOptionsTypeRef} {DefaultOptionsStaticVarName} {{ get; }} = new {JsonSerializerOptionsTypeRef}() {{ DefaultIgnoreCondition = {JsonIgnoreConditionTypeRef}.{options.DefaultIgnoreCondition}, - IgnoreReadOnlyFields = {options.IgnoreReadOnlyFields.ToString().ToLowerInvariant()}, - IgnoreReadOnlyProperties = {options.IgnoreReadOnlyProperties.ToString().ToLowerInvariant()}, - IncludeFields = {options.IncludeFields.ToString().ToLowerInvariant()}, - WriteIndented = {options.WriteIndented.ToString().ToLowerInvariant()},{namingPolicyInit} + IgnoreReadOnlyFields = {ToCSharpKeyword(options.IgnoreReadOnlyFields)}, + IgnoreReadOnlyProperties = {ToCSharpKeyword(options.IgnoreReadOnlyProperties)}, + IncludeFields = {ToCSharpKeyword(options.IncludeFields)}, + WriteIndented = {ToCSharpKeyword(options.WriteIndented)},{namingPolicyInit} }};"; } @@ -1013,8 +999,11 @@ private static {JsonSerializerOptionsTypeRef} {DefaultOptionsStaticVarName} {{ g sb.Append(@$"public override {JsonTypeInfoTypeRef} GetTypeInfo({TypeTypeRef} type) {{"); + HashSet types = new(_currentContext.RootSerializableTypes); + types.UnionWith(_currentContext.NullableUnderlyingTypes); + // TODO (https://github.com/dotnet/runtime/issues/52218): Make this Dictionary-lookup-based if root-serializable type count > 64. - foreach (TypeGenerationSpec metadata in _currentContext.RootSerializableTypes) + foreach (TypeGenerationSpec metadata in types) { if (metadata.ClassType != ClassType.TypeUnsupportedBySourceGen) { @@ -1065,5 +1054,7 @@ private static {JsonEncodedTextTypeRef} {propName}PropName = {JsonEncodedTextTyp private static string GetCreateValueInfoMethodRef(string typeCompilableName) => $"{CreateValueInfoMethodName}<{typeCompilableName}>"; } + + private static string ToCSharpKeyword(bool value) => value.ToString().ToLowerInvariant(); } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 70c8a412b6e..cefb9de2d71 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -4,15 +4,15 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; -using System.Text.Json.Serialization; -using System.Text.Json.SourceGeneration.Reflection; -using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Text.Json.Reflection; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace System.Text.Json.SourceGeneration { @@ -62,6 +62,10 @@ namespace System.Text.Json.SourceGeneration /// private readonly Dictionary _typeGenerationSpecCache = new(); + private readonly HashSet _nullableTypeGenerationSpecCache = new(); + + private JsonKnownNamingPolicy _currentContextNamingPolicy; + private static DiagnosticDescriptor ContextClassesMustBePartial { get; } = new DiagnosticDescriptor( id: "SYSLIB1032", title: new LocalizableResourceString(nameof(SR.ContextClassesMustBePartialTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)), @@ -164,7 +168,10 @@ namespace System.Text.Json.SourceGeneration ContextClassDeclarationList = classDeclarationList }; - foreach(AttributeSyntax attribute in serializableAttributeList) + // Set the naming policy for the current context. + _currentContextNamingPolicy = contextGenSpec.GenerationOptions.PropertyNamingPolicy; + + foreach (AttributeSyntax attribute in serializableAttributeList) { TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attribute, contextGenSpec.GenerationOptions.GenerationMode); if (metadata != null) @@ -178,11 +185,14 @@ namespace System.Text.Json.SourceGeneration continue; } + contextGenSpec.NullableUnderlyingTypes.UnionWith(_nullableTypeGenerationSpecCache); + contextGenSpecList ??= new List(); contextGenSpecList.Add(contextGenSpec); // Clear the cache of generated metadata between the processing of context classes. _typeGenerationSpecCache.Clear(); + _nullableTypeGenerationSpecCache.Clear(); } if (contextGenSpecList == null) @@ -231,7 +241,7 @@ namespace System.Text.Json.SourceGeneration return match != null; } - private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List? classDeclarationList) + private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out List? classDeclarationList) { INamedTypeSymbol currentSymbol = typeSymbol; classDeclarationList = null; @@ -479,14 +489,14 @@ namespace System.Text.Json.SourceGeneration } // Add metadata to cache now to prevent stack overflow when the same type is found somewhere else in the object graph. - typeMetadata = new(); + typeMetadata = new TypeGenerationSpec(); _typeGenerationSpecCache[type] = typeMetadata; ClassType classType; Type? collectionKeyType = null; Type? collectionValueType = null; - Type? nullableUnderlyingType = null; - List? propertiesMetadata = null; + TypeGenerationSpec? nullableUnderlyingTypeGenSpec = null; + List? propGenSpecList = null; CollectionType collectionType = CollectionType.NotApplicable; ObjectConstructionStrategy constructionStrategy = default; JsonNumberHandling? numberHandling = null; @@ -524,10 +534,12 @@ namespace System.Text.Json.SourceGeneration { classType = ClassType.KnownType; } - else if (type.IsNullableValueType(_nullableOfTType, out nullableUnderlyingType)) + else if (type.IsNullableValueType(_nullableOfTType, out Type? nullableUnderlyingType)) { Debug.Assert(nullableUnderlyingType != null); classType = ClassType.Nullable; + nullableUnderlyingTypeGenSpec = GetOrAddTypeGenerationSpec(nullableUnderlyingType, generationMode); + _nullableTypeGenerationSpecCache.Add(nullableUnderlyingTypeGenSpec); } else if (type.IsEnum) { @@ -585,38 +597,40 @@ namespace System.Text.Json.SourceGeneration implementsIJsonOnSerialized = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializedFullName) != null; implementsIJsonOnSerializing = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializingFullName) != null; + propGenSpecList = new List(); + Dictionary? ignoredMembers = null; + + const BindingFlags bindingFlags = + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.DeclaredOnly; + for (Type? currentType = type; currentType != null; currentType = currentType.BaseType) { - const BindingFlags bindingFlags = - BindingFlags.Instance | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.DeclaredOnly; - foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags)) { - PropertyGenerationSpec metadata = GetPropertyGenerationSpec(propertyInfo, generationMode); + bool isVirtual = propertyInfo.IsVirtual(); - // Ignore indexers. - if (propertyInfo.GetIndexParameters().Length > 0) + if (propertyInfo.GetIndexParameters().Length > 0 || + PropertyIsOverridenAndIgnored(propertyInfo.Name, propertyInfo.PropertyType, isVirtual, ignoredMembers)) { continue; } - if (metadata.CanUseGetter || metadata.CanUseSetter) - { - (propertiesMetadata ??= new()).Add(metadata); - } + PropertyGenerationSpec spec = GetPropertyGenerationSpec(propertyInfo, isVirtual, generationMode); + CacheMember(spec, ref propGenSpecList, ref ignoredMembers); } foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags)) { - PropertyGenerationSpec metadata = GetPropertyGenerationSpec(fieldInfo, generationMode); - - if (metadata.CanUseGetter || metadata.CanUseSetter) + if (PropertyIsOverridenAndIgnored(fieldInfo.Name, fieldInfo.FieldType, currentMemberIsVirtual: false, ignoredMembers)) { - (propertiesMetadata ??= new()).Add(metadata); + continue; } + + PropertyGenerationSpec spec = GetPropertyGenerationSpec(fieldInfo, isVirtual: false, generationMode); + CacheMember(spec, ref propGenSpecList, ref ignoredMembers); } } } @@ -629,12 +643,12 @@ namespace System.Text.Json.SourceGeneration classType, isValueType: type.IsValueType, numberHandling, - propertiesMetadata, + propGenSpecList, collectionType, collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeGenerationSpec(collectionKeyType, generationMode) : null, collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeGenerationSpec(collectionValueType, generationMode) : null, constructionStrategy, - nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeGenerationSpec(nullableUnderlyingType, generationMode) : null, + nullableUnderlyingTypeMetadata: nullableUnderlyingTypeGenSpec, converterInstatiationLogic, implementsIJsonOnSerialized, implementsIJsonOnSerializing); @@ -642,7 +656,37 @@ namespace System.Text.Json.SourceGeneration return typeMetadata; } - private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo, JsonSourceGenerationMode generationMode) + private void CacheMember( + PropertyGenerationSpec propGenSpec, + ref List propGenSpecList, + ref Dictionary ignoredMembers) + { + propGenSpecList.Add(propGenSpec); + + if (propGenSpec.DefaultIgnoreCondition == JsonIgnoreCondition.Always) + { + ignoredMembers ??= new Dictionary(); + ignoredMembers.Add(propGenSpec.ClrName, propGenSpec); + } + } + + private static bool PropertyIsOverridenAndIgnored( + string currentMemberName, + Type currentMemberType, + bool currentMemberIsVirtual, + Dictionary? ignoredMembers) + { + if (ignoredMembers == null || !ignoredMembers.TryGetValue(currentMemberName, out PropertyGenerationSpec? ignoredMember)) + { + return false; + } + + return currentMemberType == ignoredMember.TypeGenerationSpec.Type && + currentMemberIsVirtual && + ignoredMember.IsVirtual; + } + + private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo, bool isVirtual, JsonSourceGenerationMode generationMode) { IList attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo); @@ -709,8 +753,9 @@ namespace System.Text.Json.SourceGeneration Type memberCLRType; bool isReadOnly; - bool canUseGetter; - bool canUseSetter; + bool isPublic = false; + bool canUseGetter = false; + bool canUseSetter = false; bool getterIsVirtual = false; bool setterIsVirtual = false; @@ -718,33 +763,75 @@ namespace System.Text.Json.SourceGeneration { case PropertyInfo propertyInfo: { - MethodInfo setMethod = propertyInfo.SetMethod; memberCLRType = propertyInfo.PropertyType; - isReadOnly = setMethod == null; - canUseGetter = PropertyAccessorCanBeReferenced(propertyInfo.GetMethod, hasJsonInclude); - canUseSetter = PropertyAccessorCanBeReferenced(setMethod, hasJsonInclude) && !setMethod.IsInitOnly(); - getterIsVirtual = propertyInfo.GetMethod?.IsVirtual == true; - setterIsVirtual = propertyInfo.SetMethod?.IsVirtual == true; + + MethodInfo? getMethod = propertyInfo.GetMethod; + MethodInfo? setMethod = propertyInfo.SetMethod; + + if (getMethod != null) + { + if (getMethod.IsPublic) + { + isPublic = true; + canUseGetter = true; + } + else if (getMethod.IsAssembly) + { + canUseGetter = hasJsonInclude; + } + + getterIsVirtual = getMethod.IsVirtual; + } + + if (setMethod != null) + { + isReadOnly = false; + + if (setMethod.IsPublic) + { + isPublic = true; + canUseSetter = !setMethod.IsInitOnly(); + } + else if (setMethod.IsAssembly) + { + canUseSetter = hasJsonInclude && !setMethod.IsInitOnly(); + } + + setterIsVirtual = setMethod.IsVirtual; + } + else + { + isReadOnly = true; + } } break; case FieldInfo fieldInfo: { - Debug.Assert(fieldInfo.IsPublic); memberCLRType = fieldInfo.FieldType; + isPublic = fieldInfo.IsPublic; isReadOnly = fieldInfo.IsInitOnly; - canUseGetter = true; - canUseSetter = !isReadOnly; + + if (!fieldInfo.IsPrivate && !fieldInfo.IsFamily) + { + canUseGetter = true; + canUseSetter = !isReadOnly; + } } break; default: throw new InvalidOperationException(); } + string clrName = memberInfo.Name; + return new PropertyGenerationSpec { - ClrName = memberInfo.Name, + ClrName = clrName, IsProperty = memberInfo.MemberType == MemberTypes.Property, + IsPublic = isPublic, + IsVirtual = isVirtual, JsonPropertyName = jsonPropertyName, + RuntimePropertyName = DetermineRuntimePropName(clrName, jsonPropertyName, _currentContextNamingPolicy), IsReadOnly = isReadOnly, CanUseGetter = canUseGetter, CanUseSetter = canUseSetter, @@ -759,8 +846,8 @@ namespace System.Text.Json.SourceGeneration }; } - private static bool PropertyAccessorCanBeReferenced(MethodInfo? memberAccessor, bool hasJsonInclude) => - (memberAccessor != null && !memberAccessor.IsPrivate) && (memberAccessor.IsPublic || hasJsonInclude); + private static bool PropertyAccessorCanBeReferenced(MethodInfo? accessor) + => accessor != null && (accessor.IsPublic || accessor.IsAssembly); private string? GetConverterInstantiationLogic(CustomAttributeData attributeData) { @@ -780,6 +867,26 @@ namespace System.Text.Json.SourceGeneration return $"new {converterType.GetUniqueCompilableTypeName()}()"; } + private static string DetermineRuntimePropName(string clrPropName, string? jsonPropName, JsonKnownNamingPolicy namingPolicy) + { + string runtimePropName; + + if (jsonPropName != null) + { + runtimePropName = jsonPropName; + } + else if (namingPolicy == JsonKnownNamingPolicy.CamelCase) + { + runtimePropName = JsonNamingPolicy.CamelCase.ConvertName(clrPropName); + } + else + { + runtimePropName = clrPropName; + } + + return runtimePropName; + } + private void PopulateNumberTypes() { Debug.Assert(_numberTypes != null); diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs index 7e8c45ff429..399b893e8e8 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Text.Json.SourceGeneration.Reflection; +using System.Text.Json.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -37,6 +37,12 @@ namespace System.Text.Json.SourceGeneration /// public void Execute(GeneratorExecutionContext executionContext) { +#if LAUNCH_DEBUGGER + if (!Diagnostics.Debugger.IsAttached) + { + Diagnostics.Debugger.Launch(); + } +#endif SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver; List? contextClasses = receiver.ClassDeclarationSyntaxList; if (contextClasses == null) @@ -55,12 +61,6 @@ namespace System.Text.Json.SourceGeneration } } - /// - /// Helper for unit tests. - /// - public Dictionary? GetSerializableTypes() => _rootTypes?.ToDictionary(p => p.Type.FullName, p => p.Type); - private List? _rootTypes; - private sealed class SyntaxReceiver : ISyntaxReceiver { public List? ClassDeclarationSyntaxList { get; private set; } @@ -73,5 +73,11 @@ namespace System.Text.Json.SourceGeneration } } } + + /// + /// Helper for unit tests. + /// + public Dictionary? GetSerializableTypes() => _rootTypes?.ToDictionary(p => p.Type.FullName, p => p.Type); + private List? _rootTypes; } } diff --git a/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs b/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs index 25ae73692ec..5d52ae2e355 100644 --- a/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs @@ -9,9 +9,6 @@ namespace System.Text.Json.SourceGeneration [DebuggerDisplay("Name={Name}, Type={TypeMetadata}")] internal sealed class PropertyGenerationSpec { - /// - /// The CLR name of the property. - /// public string ClrName { get; init; } /// @@ -19,11 +16,22 @@ namespace System.Text.Json.SourceGeneration /// public bool IsProperty { get; init; } + public bool IsPublic { get; init; } + + public bool IsVirtual { get; init; } + /// /// The property name specified via JsonPropertyNameAttribute, if available. /// public string? JsonPropertyName { get; init; } + /// + /// The pre-determined JSON property name, accounting for + /// specified ahead-of-time via . + /// Only used in fast-path serialization logic. + /// + public string RuntimePropertyName { get; init; } + /// /// Whether the property has a set method. /// diff --git a/src/libraries/System.Text.Json/gen/Reflection/AssemblyWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/AssemblyWrapper.cs index d5ed28e4800..a3282b69844 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/AssemblyWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/AssemblyWrapper.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class AssemblyWrapper : Assembly { diff --git a/src/libraries/System.Text.Json/gen/Reflection/ConstructorInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/ConstructorInfoWrapper.cs index 979d32c8f7c..42d167a4622 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/ConstructorInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/ConstructorInfoWrapper.cs @@ -6,7 +6,7 @@ using System.Globalization; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class ConstructorInfoWrapper : ConstructorInfo { diff --git a/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs index 75b31db587a..e518212ce65 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class CustomAttributeDataWrapper : CustomAttributeData { diff --git a/src/libraries/System.Text.Json/gen/Reflection/FieldInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/FieldInfoWrapper.cs index cf36ceacbee..66e00c0de20 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/FieldInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/FieldInfoWrapper.cs @@ -6,7 +6,7 @@ using System.Reflection; using Microsoft.CodeAnalysis; using System.Globalization; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class FieldInfoWrapper : FieldInfo { @@ -33,6 +33,11 @@ namespace System.Text.Json.SourceGeneration.Reflection _attributes |= FieldAttributes.Static; } + if (_field.IsReadOnly) + { + _attributes |= FieldAttributes.InitOnly; + } + switch (_field.DeclaredAccessibility) { case Accessibility.Public: @@ -41,6 +46,9 @@ namespace System.Text.Json.SourceGeneration.Reflection case Accessibility.Private: _attributes |= FieldAttributes.Private; break; + case Accessibility.Protected: + _attributes |= FieldAttributes.Family; + break; } } diff --git a/src/libraries/System.Text.Json/gen/Reflection/MemberInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/MemberInfoWrapper.cs index 1b4de7811fd..ccfd0fb7c6e 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/MemberInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/MemberInfoWrapper.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class MemberInfoWrapper : MemberInfo { diff --git a/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs b/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs index 885885325fe..d12f3f8ed60 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs @@ -8,7 +8,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class MetadataLoadContextInternal { diff --git a/src/libraries/System.Text.Json/gen/Reflection/MethodInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/MethodInfoWrapper.cs index 44eecf0b591..637483847e5 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/MethodInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/MethodInfoWrapper.cs @@ -6,7 +6,7 @@ using System.Globalization; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class MethodInfoWrapper : MethodInfo { @@ -41,7 +41,7 @@ namespace System.Text.Json.SourceGeneration.Reflection _attributes |= MethodAttributes.Static; } - if (_method.IsVirtual) + if (_method.IsVirtual || _method.IsOverride) { _attributes |= MethodAttributes.Virtual; } @@ -54,6 +54,9 @@ namespace System.Text.Json.SourceGeneration.Reflection case Accessibility.Private: _attributes |= MethodAttributes.Private; break; + case Accessibility.Internal: + _attributes |= MethodAttributes.Assembly; + break; } } diff --git a/src/libraries/System.Text.Json/gen/Reflection/ParameterInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/ParameterInfoWrapper.cs index b8891251c7e..ad909101772 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/ParameterInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/ParameterInfoWrapper.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class ParameterInfoWrapper : ParameterInfo { diff --git a/src/libraries/System.Text.Json/gen/Reflection/PropertyInfoWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/PropertyInfoWrapper.cs index 294c6dd1059..bfb593f992f 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/PropertyInfoWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/PropertyInfoWrapper.cs @@ -6,7 +6,7 @@ using System.Globalization; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class PropertyInfoWrapper : PropertyInfo { diff --git a/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs b/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs index 1c0619ddda5..599cae49aeb 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Linq; using System.Reflection; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { - internal static class ReflectionExtensions + internal static partial class ReflectionExtensions { public static CustomAttributeData GetCustomAttributeData(this MemberInfo memberInfo, Type type) { diff --git a/src/libraries/System.Text.Json/gen/Reflection/RoslynExtensions.cs b/src/libraries/System.Text.Json/gen/Reflection/RoslynExtensions.cs index 610e7fe4eef..3dd1e925e2f 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/RoslynExtensions.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/RoslynExtensions.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal static class RoslynExtensions { diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs index 8ff487ab0c6..6ad76f42b7b 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Linq; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal static class TypeExtensions { diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs index 2ffc4725922..abeed3dfaaf 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; -namespace System.Text.Json.SourceGeneration.Reflection +namespace System.Text.Json.Reflection { internal class TypeWrapper : Type { @@ -231,18 +231,32 @@ namespace System.Text.Json.SourceGeneration.Reflection public override FieldInfo[] GetFields(BindingFlags bindingAttr) { - var fields = new List(); + List fields = new(); + foreach (ISymbol item in _typeSymbol.GetMembers()) { - // Associated Symbol checks the field is not a backingfield. - if (item is IFieldSymbol field && field.AssociatedSymbol == null && !field.IsReadOnly) + if (item is IFieldSymbol fieldSymbol) { - if ((item.DeclaredAccessibility & Accessibility.Public) == Accessibility.Public) + // Skip if: + if ( + // this is a backing field + fieldSymbol.AssociatedSymbol != null || + // we want a static field and this is not static + (BindingFlags.Static & bindingAttr) != 0 && !fieldSymbol.IsStatic || + // we want an instance field and this is static or a constant + (BindingFlags.Instance & bindingAttr) != 0 && (fieldSymbol.IsStatic || fieldSymbol.IsConst)) { - fields.Add(new FieldInfoWrapper(field, _metadataLoadContext)); + continue; + } + + if ((BindingFlags.Public & bindingAttr) != 0 && item.DeclaredAccessibility == Accessibility.Public || + (BindingFlags.NonPublic & bindingAttr) != 0) + { + fields.Add(new FieldInfoWrapper(fieldSymbol, _metadataLoadContext)); } } } + return fields.ToArray(); } @@ -300,18 +314,28 @@ namespace System.Text.Json.SourceGeneration.Reflection return nestedTypes.ToArray(); } - // TODO: make sure to use bindingAttr for correctness. Current implementation assumes public and non-static. public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { - var properties = new List(); + List properties = new(); foreach (ISymbol item in _typeSymbol.GetMembers()) { - if (item is IPropertySymbol property) + if (item is IPropertySymbol propertySymbol) { - if ((item.DeclaredAccessibility & Accessibility.Public) == Accessibility.Public) + // Skip if: + if ( + // we want a static property and this is not static + (BindingFlags.Static & bindingAttr) != 0 && !propertySymbol.IsStatic || + // we want an instance property and this is static + (BindingFlags.Instance & bindingAttr) != 0 && propertySymbol.IsStatic) { - properties.Add(new PropertyInfoWrapper(property, _metadataLoadContext)); + continue; + } + + if ((BindingFlags.Public & bindingAttr) != 0 && item.DeclaredAccessibility == Accessibility.Public || + (BindingFlags.NonPublic & bindingAttr) != 0) + { + properties.Add(new PropertyInfoWrapper(propertySymbol, _metadataLoadContext)); } } } diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj index 3e449e71aab..008a5b22189 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj @@ -13,6 +13,7 @@ $(DefineConstants);BUILDING_SOURCE_GENERATOR + $(DefineConstants);LAUNCH_DEBUGGER @@ -27,12 +28,14 @@ + + diff --git a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs index 36a0c58a17a..865134823f9 100644 --- a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs @@ -3,8 +3,9 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Reflection; using System.Text.Json.Serialization; -using System.Text.Json.SourceGeneration.Reflection; namespace System.Text.Json.SourceGeneration { @@ -42,7 +43,7 @@ namespace System.Text.Json.SourceGeneration public JsonNumberHandling? NumberHandling { get; private set; } - public List? PropertiesMetadata { get; private set; } + public List? PropertyGenSpecList { get; private set; } public CollectionType CollectionType { get; private set; } @@ -64,7 +65,7 @@ namespace System.Text.Json.SourceGeneration ClassType classType, bool isValueType, JsonNumberHandling? numberHandling, - List? propertiesMetadata, + List? propertyGenSpecList, CollectionType collectionType, TypeGenerationSpec? collectionKeyTypeMetadata, TypeGenerationSpec? collectionValueTypeMetadata, @@ -82,7 +83,7 @@ namespace System.Text.Json.SourceGeneration IsValueType = isValueType; CanBeNull = !isValueType || nullableUnderlyingTypeMetadata != null; NumberHandling = numberHandling; - PropertiesMetadata = propertiesMetadata; + PropertyGenSpecList = propertyGenSpecList; CollectionType = collectionType; CollectionKeyTypeMetadata = collectionKeyTypeMetadata; CollectionValueTypeMetadata = collectionValueTypeMetadata; @@ -93,6 +94,86 @@ namespace System.Text.Json.SourceGeneration ImplementsIJsonOnSerializing = implementsIJsonOnSerializing; } + public bool TryFilterSerializableProps( + JsonSourceGenerationOptionsAttribute options, + [NotNullWhen(true)] out Dictionary? serializableProperties, + out bool castingRequiredForProps) + { + serializableProperties = new Dictionary(); + Dictionary? ignoredMembers = null; + + for (int i = 0; i < PropertyGenSpecList.Count; i++) + { + PropertyGenerationSpec propGenSpec = PropertyGenSpecList[i]; + bool hasJsonInclude = propGenSpec.HasJsonInclude; + JsonIgnoreCondition? ignoreCondition = propGenSpec.DefaultIgnoreCondition; + + if (ignoreCondition == JsonIgnoreCondition.WhenWritingNull && !propGenSpec.TypeGenerationSpec.CanBeNull) + { + goto ReturnFalse; + } + + if (!propGenSpec.IsPublic) + { + if (hasJsonInclude) + { + goto ReturnFalse; + } + + continue; + } + + if (!propGenSpec.IsProperty && !hasJsonInclude && !options.IncludeFields) + { + continue; + } + + string memberName = propGenSpec.ClrName!; + + // The JsonPropertyNameAttribute or naming policy resulted in a collision. + if (!serializableProperties.TryAdd(propGenSpec.RuntimePropertyName, propGenSpec)) + { + PropertyGenerationSpec other = serializableProperties[propGenSpec.RuntimePropertyName]!; + + if (other.DefaultIgnoreCondition == JsonIgnoreCondition.Always) + { + // Overwrite previously cached property since it has [JsonIgnore]. + serializableProperties[propGenSpec.RuntimePropertyName] = propGenSpec; + } + else if ( + // Does the current property have `JsonIgnoreAttribute`? + propGenSpec.DefaultIgnoreCondition != JsonIgnoreCondition.Always && + // Is the current property hidden by the previously cached property + // (with `new` keyword, or by overriding)? + other.ClrName != memberName && + // Was a property with the same CLR name was ignored? That property hid the current property, + // thus, if it was ignored, the current property should be ignored too. + ignoredMembers?.ContainsKey(memberName) != true) + { + // We throw if we have two public properties that have the same JSON property name, and neither have been ignored. + serializableProperties = null; + castingRequiredForProps = false; + return false; + } + // Ignore the current property. + } + + if (propGenSpec.DefaultIgnoreCondition == JsonIgnoreCondition.Always) + { + (ignoredMembers ??= new Dictionary()).Add(memberName, propGenSpec); + } + } + + Debug.Assert(PropertyGenSpecList.Count >= serializableProperties.Count); + castingRequiredForProps = PropertyGenSpecList.Count > serializableProperties.Count; + return true; + +ReturnFalse: + serializableProperties = null; + castingRequiredForProps = false; + return false; + } + private bool FastPathIsSupported() { if (ClassType == ClassType.Object) diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 2d74d3535c3..8e87154e803 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -932,7 +932,7 @@ namespace System.Text.Json.Serialization.Metadata public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo keyInfo, System.Text.Json.Serialization.Metadata.JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Dictionary where TKey : notnull { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.List { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateObjectInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Func? propInitFunc, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where T : notnull { throw null; } - public static System.Text.Json.Serialization.Metadata.JsonPropertyInfo CreatePropertyInfo(System.Text.Json.JsonSerializerOptions options, bool isProperty, System.Type declaringType, System.Text.Json.Serialization.Metadata.JsonTypeInfo propertyTypeInfo, System.Text.Json.Serialization.JsonConverter? converter, System.Func? getter, System.Action? setter, System.Text.Json.Serialization.JsonIgnoreCondition ignoreCondition, System.Text.Json.Serialization.JsonNumberHandling numberHandling, string propertyName, string? jsonPropertyName) { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonPropertyInfo CreatePropertyInfo(System.Text.Json.JsonSerializerOptions options, bool isProperty, bool isPublic, bool isVirtual, System.Type declaringType, System.Text.Json.Serialization.Metadata.JsonTypeInfo propertyTypeInfo, System.Text.Json.Serialization.JsonConverter? converter, System.Func? getter, System.Action? setter, System.Text.Json.Serialization.JsonIgnoreCondition? ignoreCondition, bool hasJsonInclude, System.Text.Json.Serialization.JsonNumberHandling? numberHandling, string propertyName, string? jsonPropertyName) { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateValueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.JsonConverter converter) { throw null; } public static System.Text.Json.Serialization.JsonConverter GetEnumConverter(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; } public static System.Text.Json.Serialization.JsonConverter GetNullableConverter(System.Text.Json.Serialization.Metadata.JsonTypeInfo underlyingTypeInfo) where T : struct { throw null; } diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index c650f6ebad8..e289dfbb1ea 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -608,4 +608,7 @@ To specify a serialization implementation for type '{0}'', context '{0}' must specify default options. + + A 'field' member cannot be 'virtual'. See arguments for the '{0}' and '{1}' parameters. + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 9d94ae9bdd4..195c640c85a 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -22,12 +22,14 @@ + + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index 8dce0c594e1..0bdd85880bd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace System.Text.Json @@ -100,24 +99,6 @@ namespace System.Text.Json ); } - /// - /// Emulates Dictionary.TryAdd on netstandard. - /// - public static bool TryAdd(Dictionary dictionary, in TKey key, in TValue value) where TKey : notnull - { -#if NETSTANDARD2_0 || NETFRAMEWORK - if (!dictionary.ContainsKey(key)) - { - dictionary[key] = value; - return true; - } - - return false; -#else - return dictionary.TryAdd(key, value); -#endif - } - /// /// Emulates Dictionary(IEnumerable{KeyValuePair}) on netstandard. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs index 5268573219d..731869ec4a9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs @@ -18,6 +18,8 @@ namespace System.Text.Json.Serialization.Metadata /// The type that the converter for the property returns or accepts when converting JSON data. /// The to initialize the metadata with. /// Whether the CLR member is a property or field. + /// Whether the CLR member is public. + /// Whether the CLR member is a virtual property. /// The declaring type of the property or field. /// The info for the property or field's type. /// A for the property or field, specified by . @@ -25,19 +27,23 @@ namespace System.Text.Json.Serialization.Metadata /// Provides a mechanism to set the property or field's value. /// Specifies a condition for the property to be ignored. /// If the property or field is a number, specifies how it should processed when serializing and deserializing. + /// Whether the property was annotated with . /// The CLR name of the property or field. /// The name to be used when processing the property or field, specified by . /// A instance intialized with the provided metadata. public static JsonPropertyInfo CreatePropertyInfo( JsonSerializerOptions options, bool isProperty, + bool isPublic, + bool isVirtual, Type declaringType, JsonTypeInfo propertyTypeInfo, JsonConverter? converter, Func? getter, Action? setter, - JsonIgnoreCondition ignoreCondition, - JsonNumberHandling numberHandling, + JsonIgnoreCondition? ignoreCondition, + bool hasJsonInclude, + JsonNumberHandling? numberHandling, string propertyName, string? jsonPropertyName) { @@ -70,16 +76,23 @@ namespace System.Text.Json.Serialization.Metadata } } + if (!isProperty && isVirtual) + { + throw new InvalidOperationException(SR.Format(SR.FieldCannotBeVirtual, nameof(isProperty), nameof(isVirtual))); + } + JsonPropertyInfo jsonPropertyInfo = new JsonPropertyInfo(); jsonPropertyInfo.InitializeForSourceGen( options, isProperty, + isPublic, declaringType, propertyTypeInfo, converter, getter, setter, ignoreCondition, + hasJsonInclude, numberHandling, propertyName, jsonPropertyName); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs index 2a97240d67b..95081b8519d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs @@ -42,17 +42,19 @@ namespace System.Text.Json.Serialization.Metadata // Create a property that is ignored at run-time. It uses the same type (typeof(sbyte)) to help // prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it. - internal static JsonPropertyInfo CreateIgnoredPropertyPlaceholder(MemberInfo memberInfo, JsonSerializerOptions options) + internal static JsonPropertyInfo CreateIgnoredPropertyPlaceholder(MemberInfo memberInfo, Type memberType, bool isVirtual, JsonSerializerOptions options) { JsonPropertyInfo jsonPropertyInfo = new JsonPropertyInfo(); + jsonPropertyInfo.Options = options; jsonPropertyInfo.MemberInfo = memberInfo; - jsonPropertyInfo.DeterminePropertyName(); jsonPropertyInfo.IsIgnored = true; + jsonPropertyInfo.DeclaredPropertyType = memberType; + jsonPropertyInfo.IsVirtual = isVirtual; + jsonPropertyInfo.DeterminePropertyName(); Debug.Assert(!jsonPropertyInfo.ShouldDeserialize); Debug.Assert(!jsonPropertyInfo.ShouldSerialize); - return jsonPropertyInfo; } @@ -81,6 +83,8 @@ namespace System.Text.Json.Serialization.Metadata { Debug.Assert(MemberInfo != null); + ClrName = MemberInfo.Name; + JsonPropertyNameAttribute? nameAttribute = GetAttribute(MemberInfo); if (nameAttribute != null) { @@ -305,6 +309,7 @@ namespace System.Text.Json.Serialization.Metadata Type? runtimePropertyType, ConverterStrategy runtimeClassType, MemberInfo? memberInfo, + bool isVirtual, JsonConverter converter, JsonIgnoreCondition? ignoreCondition, JsonNumberHandling? parentTypeNumberHandling, @@ -312,12 +317,12 @@ namespace System.Text.Json.Serialization.Metadata { Debug.Assert(converter != null); - ClrName = memberInfo?.Name; DeclaringType = parentClassType; DeclaredPropertyType = declaredPropertyType; RuntimePropertyType = runtimePropertyType; ConverterStrategy = runtimeClassType; MemberInfo = memberInfo; + IsVirtual = isVirtual; ConverterBase = converter; Options = options; } @@ -479,6 +484,16 @@ namespace System.Text.Json.Serialization.Metadata internal bool IsIgnored { get; set; } + /// + /// Relevant to source generated metadata: did the property have the ? + /// + internal bool SrcGen_HasJsonInclude { get; set; } + + /// + /// Relevant to source generated metadata: is the property public? + /// + internal bool SrcGen_IsPublic { get; set; } + internal JsonNumberHandling? NumberHandling { get; set; } // Whether the property type can be null. @@ -489,5 +504,7 @@ namespace System.Text.Json.Serialization.Metadata internal MemberTypes MemberType { get; set; } // TODO: with some refactoring, we should be able to remove this. internal string? ClrName { get; set; } + + internal bool IsVirtual { get; set; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index 8e268c25099..dda0e7f12dd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -37,6 +37,7 @@ namespace System.Text.Json.Serialization.Metadata Type? runtimePropertyType, ConverterStrategy runtimeClassType, MemberInfo? memberInfo, + bool isVirtual, JsonConverter converter, JsonIgnoreCondition? ignoreCondition, JsonNumberHandling? parentTypeNumberHandling, @@ -48,6 +49,7 @@ namespace System.Text.Json.Serialization.Metadata runtimePropertyType, runtimeClassType, memberInfo, + isVirtual, converter, ignoreCondition, parentTypeNumberHandling, @@ -116,13 +118,15 @@ namespace System.Text.Json.Serialization.Metadata internal void InitializeForSourceGen( JsonSerializerOptions options, bool isProperty, + bool isPublic, Type declaringType, JsonTypeInfo typeInfo, JsonConverter converter, Func? getter, Action? setter, - JsonIgnoreCondition ignoreCondition, - JsonNumberHandling numberHandling, + JsonIgnoreCondition? ignoreCondition, + bool hasJsonInclude, + JsonNumberHandling? numberHandling, string propertyName, string? jsonPropertyName) { @@ -150,6 +154,9 @@ namespace System.Text.Json.Serialization.Metadata NameAsUtf8Bytes ??= Encoding.UTF8.GetBytes(NameAsString!); EscapedNameSection ??= JsonHelpers.GetEscapedPropertyNameSection(NameAsUtf8Bytes, Options.Encoder); + SrcGen_IsPublic = isPublic; + SrcGen_HasJsonInclude = hasJsonInclude; + if (ignoreCondition == JsonIgnoreCondition.Always) { IsIgnored = true; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs index 7b3369a90ee..cb2545c0cc1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs @@ -52,13 +52,14 @@ namespace System.Text.Json.Serialization.Metadata MemberInfo memberInfo, Type memberType, Type parentClassType, + bool isVirtual, JsonNumberHandling? parentTypeNumberHandling, JsonSerializerOptions options) { JsonIgnoreCondition? ignoreCondition = JsonPropertyInfo.GetAttribute(memberInfo)?.Condition; if (ignoreCondition == JsonIgnoreCondition.Always) { - return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(memberInfo, options); + return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(memberInfo, memberType, isVirtual, options); } JsonConverter converter = GetConverter( @@ -73,6 +74,7 @@ namespace System.Text.Json.Serialization.Metadata runtimePropertyType: runtimeType, memberInfo, parentClassType, + isVirtual, converter, options, parentTypeNumberHandling, @@ -84,6 +86,7 @@ namespace System.Text.Json.Serialization.Metadata Type? runtimePropertyType, MemberInfo? memberInfo, Type parentClassType, + bool isVirtual, JsonConverter converter, JsonSerializerOptions options, JsonNumberHandling? parentTypeNumberHandling = null, @@ -98,6 +101,7 @@ namespace System.Text.Json.Serialization.Metadata runtimePropertyType, runtimeClassType: converter.ConverterStrategy, memberInfo, + isVirtual, converter, ignoreCondition, parentTypeNumberHandling, @@ -108,7 +112,7 @@ namespace System.Text.Json.Serialization.Metadata /// /// Create a for a given Type. - /// See . + /// See . /// internal static JsonPropertyInfo CreatePropertyInfoForTypeInfo( Type declaredPropertyType, @@ -121,8 +125,9 @@ namespace System.Text.Json.Serialization.Metadata declaredPropertyType: declaredPropertyType, runtimePropertyType: runtimePropertyType, memberInfo: null, // Not a real property so this is null. - parentClassType: JsonTypeInfo.ObjectType, // a dummy value (not used) - converter: converter, + parentClassType: ObjectType, // a dummy value (not used) + isVirtual: false, + converter, options, parentTypeNumberHandling: numberHandling); @@ -582,18 +587,34 @@ namespace System.Text.Json.Serialization.Metadata } JsonPropertyInfo[] array = PropInitFunc(context); - var properties = new JsonPropertyDictionary(Options.PropertyNameCaseInsensitive, array.Length); + Dictionary? ignoredMembers = null; + JsonPropertyDictionary propertyCache = new(Options.PropertyNameCaseInsensitive, array.Length); + for (int i = 0; i < array.Length; i++) { - JsonPropertyInfo property = array[i]; - if (!properties.TryAdd(property.NameAsString, property)) + JsonPropertyInfo jsonPropertyInfo = array[i]; + bool hasJsonInclude = jsonPropertyInfo.SrcGen_HasJsonInclude; + + if (!jsonPropertyInfo.SrcGen_IsPublic) { - ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, property); + if (hasJsonInclude) + { + ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(jsonPropertyInfo.ClrName!, jsonPropertyInfo.DeclaringType); + } + + continue; } + + if (jsonPropertyInfo.MemberType == MemberTypes.Field && !hasJsonInclude && !Options.IncludeFields) + { + continue; + } + + CacheMember(jsonPropertyInfo, propertyCache, ref ignoredMembers); } - // Avoid threading issues by populating a local cache, and assigning it to the global cache after completion. - PropertyCache = properties; + // Avoid threading issues by populating a local cache and assigning it to the global cache after completion. + PropertyCache = propertyCache; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 53ed06d9c36..ba90400e246 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text.Json.Reflection; namespace System.Text.Json.Serialization.Metadata { @@ -192,7 +193,7 @@ namespace System.Text.Json.Serialization.Metadata CreateObject = Options.MemberAccessorStrategy.CreateConstructor(type); - Dictionary? ignoredMembers = null; + Dictionary? ignoredMembers = null; PropertyInfo[] properties = type.GetProperties(bindingFlags); @@ -208,8 +209,12 @@ namespace System.Text.Json.Serialization.Metadata { foreach (PropertyInfo propertyInfo in properties) { + bool isVirtual = propertyInfo.IsVirtual(); + string propertyName = propertyInfo.Name; + // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d. - if (propertyInfo.GetIndexParameters().Length > 0 || PropertyIsOverridenAndIgnored(propertyInfo, ignoredMembers)) + if (propertyInfo.GetIndexParameters().Length > 0 || + PropertyIsOverridenAndIgnored(propertyName, propertyInfo.PropertyType, isVirtual, ignoredMembers)) { continue; } @@ -222,6 +227,7 @@ namespace System.Text.Json.Serialization.Metadata currentType, propertyInfo.PropertyType, propertyInfo, + isVirtual, typeNumberHandling, ref ignoredMembers); } @@ -229,7 +235,7 @@ namespace System.Text.Json.Serialization.Metadata { if (JsonPropertyInfo.GetAttribute(propertyInfo) != null) { - ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyInfo, currentType); + ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(propertyName, currentType); } // Non-public properties should not be included for (de)serialization. @@ -238,7 +244,9 @@ namespace System.Text.Json.Serialization.Metadata foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags)) { - if (PropertyIsOverridenAndIgnored(fieldInfo, ignoredMembers)) + string fieldName = fieldInfo.Name; + + if (PropertyIsOverridenAndIgnored(fieldName, fieldInfo.FieldType, currentMemberIsVirtual: false, ignoredMembers)) { continue; } @@ -253,6 +261,7 @@ namespace System.Text.Json.Serialization.Metadata currentType, fieldInfo.FieldType, fieldInfo, + isVirtual: false, typeNumberHandling, ref ignoredMembers); } @@ -261,7 +270,7 @@ namespace System.Text.Json.Serialization.Metadata { if (hasJsonInclude) { - ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(fieldInfo, currentType); + ThrowHelper.ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(fieldName, currentType); } // Non-public fields should not be included for (de)serialization. @@ -316,8 +325,9 @@ namespace System.Text.Json.Serialization.Metadata Type declaringType, Type memberType, MemberInfo memberInfo, + bool isVirtual, JsonNumberHandling? typeNumberHandling, - ref Dictionary? ignoredMembers) + ref Dictionary? ignoredMembers) { bool hasExtensionAttribute = memberInfo.GetCustomAttribute(typeof(JsonExtensionDataAttribute)) != null; if (hasExtensionAttribute && DataExtensionProperty != null) @@ -325,7 +335,7 @@ namespace System.Text.Json.Serialization.Metadata ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateTypeAttribute(Type, typeof(JsonExtensionDataAttribute)); } - JsonPropertyInfo jsonPropertyInfo = AddProperty(memberInfo, memberType, declaringType, typeNumberHandling, Options); + JsonPropertyInfo jsonPropertyInfo = AddProperty(memberInfo, memberType, declaringType, isVirtual, typeNumberHandling, Options); Debug.Assert(jsonPropertyInfo.NameAsString != null); if (hasExtensionAttribute) @@ -336,38 +346,43 @@ namespace System.Text.Json.Serialization.Metadata } else { - string memberName = memberInfo.Name; + CacheMember(jsonPropertyInfo, PropertyCache, ref ignoredMembers); + } + } - // The JsonPropertyNameAttribute or naming policy resulted in a collision. - if (!PropertyCache!.TryAdd(jsonPropertyInfo.NameAsString, jsonPropertyInfo)) + private void CacheMember(JsonPropertyInfo jsonPropertyInfo, JsonPropertyDictionary? propertyCache, ref Dictionary? ignoredMembers) + { + string memberName = jsonPropertyInfo.ClrName!; + + // The JsonPropertyNameAttribute or naming policy resulted in a collision. + if (!propertyCache!.TryAdd(jsonPropertyInfo.NameAsString, jsonPropertyInfo)) + { + JsonPropertyInfo other = propertyCache[jsonPropertyInfo.NameAsString]!; + + if (other.IsIgnored) { - JsonPropertyInfo other = PropertyCache[jsonPropertyInfo.NameAsString]!; - - if (other.IsIgnored) - { - // Overwrite previously cached property since it has [JsonIgnore]. - PropertyCache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; - } - else if ( - // Does the current property have `JsonIgnoreAttribute`? - !jsonPropertyInfo.IsIgnored && - // Is the current property hidden by the previously cached property - // (with `new` keyword, or by overriding)? - other.MemberInfo!.Name != memberName && - // Was a property with the same CLR name was ignored? That property hid the current property, - // thus, if it was ignored, the current property should be ignored too. - ignoredMembers?.ContainsKey(memberName) != true) - { - // We throw if we have two public properties that have the same JSON property name, and neither have been ignored. - ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); - } - // Ignore the current property. + // Overwrite previously cached property since it has [JsonIgnore]. + propertyCache[jsonPropertyInfo.NameAsString] = jsonPropertyInfo; } - - if (jsonPropertyInfo.IsIgnored) + else if ( + // Does the current property have `JsonIgnoreAttribute`? + !jsonPropertyInfo.IsIgnored && + // Is the current property hidden by the previously cached property + // (with `new` keyword, or by overriding)? + other.ClrName != memberName && + // Was a property with the same CLR name was ignored? That property hid the current property, + // thus, if it was ignored, the current property should be ignored too. + ignoredMembers?.ContainsKey(memberName) != true) { - (ignoredMembers ??= new Dictionary()).Add(memberName, memberInfo); + // We throw if we have two public properties that have the same JSON property name, and neither have been ignored. + ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo); } + // Ignore the current property. + } + + if (jsonPropertyInfo.IsIgnored) + { + (ignoredMembers ??= new Dictionary()).Add(memberName, jsonPropertyInfo); } } @@ -476,33 +491,21 @@ namespace System.Text.Json.Serialization.Metadata ParameterCache = parameterCache; ParameterCount = parameters.Length; } - private static bool PropertyIsOverridenAndIgnored(MemberInfo currentMember, Dictionary? ignoredMembers) + + private static bool PropertyIsOverridenAndIgnored( + string currentMemberName, + Type currentMemberType, + bool currentMemberIsVirtual, + Dictionary? ignoredMembers) { - if (ignoredMembers == null || !ignoredMembers.TryGetValue(currentMember.Name, out MemberInfo? ignoredProperty)) + if (ignoredMembers == null || !ignoredMembers.TryGetValue(currentMemberName, out JsonPropertyInfo? ignoredMember)) { return false; } - Debug.Assert(currentMember is PropertyInfo || currentMember is FieldInfo); - PropertyInfo? currentPropertyInfo = currentMember as PropertyInfo; - Type currentMemberType = currentPropertyInfo == null - ? Unsafe.As(currentMember).FieldType - : currentPropertyInfo.PropertyType; - - Debug.Assert(ignoredProperty is PropertyInfo || ignoredProperty is FieldInfo); - PropertyInfo? ignoredPropertyInfo = ignoredProperty as PropertyInfo; - Type ignoredPropertyType = ignoredPropertyInfo == null - ? Unsafe.As(ignoredProperty).FieldType - : ignoredPropertyInfo.PropertyType; - - return currentMemberType == ignoredPropertyType && - PropertyIsVirtual(currentPropertyInfo) && - PropertyIsVirtual(ignoredPropertyInfo); - } - - private static bool PropertyIsVirtual(PropertyInfo? propertyInfo) - { - return propertyInfo != null && (propertyInfo.GetMethod?.IsVirtual == true || propertyInfo.SetMethod?.IsVirtual == true); + return currentMemberType == ignoredMember.DeclaredPropertyType && + currentMemberIsVirtual && + ignoredMember.IsVirtual; } private void ValidateAndAssignDataExtensionProperty(JsonPropertyInfo jsonPropertyInfo) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 7beb0612a4e..42af83d147a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -244,9 +244,9 @@ namespace System.Text.Json [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(MemberInfo memberInfo, Type parentType) + public static void ThrowInvalidOperationException_JsonIncludeOnNonPublicInvalid(string memberName, Type declaringType) { - throw new InvalidOperationException(SR.Format(SR.JsonIncludeOnNonPublicInvalid, memberInfo.Name, parentType)); + throw new InvalidOperationException(SR.Format(SR.JsonIncludeOnNonPublicInvalid, memberName, declaringType)); } [DoesNotReturn] diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs new file mode 100644 index 00000000000..eb67440c0d1 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class JsonSerializerWrapperForString + { + protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null); + + protected internal abstract Task SerializeWrapper(T value, JsonSerializerOptions options = null); + + protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context); + + protected internal abstract Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo); + + protected internal abstract Task DeserializeWrapper(string json, JsonSerializerOptions options = null); + + protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null); + + protected internal abstract Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo); + + protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerContext context); + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisiblityTests.InitOnly.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs similarity index 73% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisiblityTests.InitOnly.cs rename to src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs index 48fe1e34c93..eda534737b0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisiblityTests.InitOnly.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs @@ -1,51 +1,52 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class PropertyVisibilityTests + public abstract partial class PropertyVisibilityTests { [Theory] [InlineData(typeof(ClassWithInitOnlyProperty))] [InlineData(typeof(StructWithInitOnlyProperty))] - public static void InitOnlyProperties_Work(Type type) + public virtual async Task InitOnlyProperties(Type type) { // Init-only property included by default. - object obj = JsonSerializer.Deserialize(@"{""MyInt"":1}", type); + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type); Assert.Equal(1, (int)type.GetProperty("MyInt").GetValue(obj)); // Init-only properties can be serialized. - Assert.Equal(@"{""MyInt"":1}", JsonSerializer.Serialize(obj)); + Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } [Theory] [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] - public static void NonPublicInitOnlySetter_Without_JsonInclude_Fails(Type type) + public async Task NonPublicInitOnlySetter_Without_JsonInclude_Fails(Type type) { // Non-public init-only property setter ignored. - object obj = JsonSerializer.Deserialize(@"{""MyInt"":1}", type); + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type); Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj)); // Public getter can be used for serialization. - Assert.Equal(@"{""MyInt"":0}", JsonSerializer.Serialize(obj)); + Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type)); } [Theory] [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] - public static void NonPublicInitOnlySetter_With_JsonInclude_Works(Type type) + public virtual async Task NonPublicInitOnlySetter_With_JsonInclude(Type type) { // Non-public init-only property setter included with [JsonInclude]. - object obj = JsonSerializer.Deserialize(@"{""MyInt"":1}", type); + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type); Assert.Equal(1, (int)type.GetProperty("MyInt").GetValue(obj)); // Init-only properties can be serialized. - Assert.Equal(@"{""MyInt"":1}", JsonSerializer.Serialize(obj)); + Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } public class ClassWithInitOnlyProperty diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.NonPublicAccessors.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs similarity index 66% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.NonPublicAccessors.cs rename to src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs index 0dd3657e6f5..0c8dc6a941c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.NonPublicAccessors.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs @@ -2,14 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class PropertyVisibilityTests + public abstract partial class PropertyVisibilityTests { [Fact] - public static void NonPublic_AccessorsNotSupported_WithoutAttribute() + public async Task NonPublic_AccessorsNotSupported_WithoutAttribute() { string json = @"{ ""MyInt"":1, @@ -18,20 +19,20 @@ namespace System.Text.Json.Serialization.Tests ""MyUri"":""https://microsoft.com"" }"; - var obj = JsonSerializer.Deserialize(json); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(0, obj.MyInt); Assert.Null(obj.MyString); Assert.Equal(2f, obj.GetMyFloat); Assert.Equal(new Uri("https://microsoft.com"), obj.MyUri); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Contains(@"""MyInt"":0", json); Assert.Contains(@"""MyString"":null", json); Assert.DoesNotContain(@"""MyFloat"":", json); Assert.DoesNotContain(@"""MyUri"":", json); } - private class MyClass_WithNonPublicAccessors + public class MyClass_WithNonPublicAccessors { public int MyInt { get; private set; } public string MyString { get; internal set; } @@ -43,7 +44,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void Honor_JsonSerializablePropertyAttribute_OnProperties() + public virtual async Task Honor_JsonSerializablePropertyAttribute_OnProperties() { string json = @"{ ""MyInt"":1, @@ -52,20 +53,20 @@ namespace System.Text.Json.Serialization.Tests ""MyUri"":""https://microsoft.com"" }"; - var obj = JsonSerializer.Deserialize(json); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(1, obj.MyInt); Assert.Equal("Hello", obj.MyString); Assert.Equal(2f, obj.GetMyFloat); Assert.Equal(new Uri("https://microsoft.com"), obj.MyUri); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Contains(@"""MyInt"":1", json); Assert.Contains(@"""MyString"":""Hello""", json); Assert.Contains(@"""MyFloat"":2", json); Assert.Contains(@"""MyUri"":""https://microsoft.com""", json); } - private class MyClass_WithNonPublicAccessors_WithPropertyAttributes + public class MyClass_WithNonPublicAccessors_WithPropertyAttributes { [JsonInclude] public int MyInt { get; private set; } @@ -103,19 +104,23 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ExtensionDataCanHaveNonPublicSetter() +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for extension data. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task ExtensionDataCanHaveNonPublicSetter() { string json = @"{""Key"":""Value""}"; // Baseline - var obj1 = JsonSerializer.Deserialize(json); + var obj1 = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Null(obj1.ExtensionData); - Assert.Equal("{}", JsonSerializer.Serialize(obj1)); + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(obj1)); // With attribute - var obj2 = JsonSerializer.Deserialize(json); + var obj2 = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("Value", obj2.ExtensionData["Key"].GetString()); - Assert.Equal(json, JsonSerializer.Serialize(obj2)); + Assert.Equal(json, await JsonSerializerWrapperForString.SerializeWrapper(obj2)); } private class ClassWithExtensionData_NonPublicSetter @@ -138,7 +143,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void HonorCustomConverter() + public virtual async Task HonorCustomConverter_UsingPrivateSetter() { var options = new JsonSerializerOptions(); options.Converters.Add(new JsonStringEnumConverter()); @@ -146,17 +151,17 @@ namespace System.Text.Json.Serialization.Tests string json = @"{""MyEnum"":""AnotherValue"",""MyInt"":2}"; // Deserialization baseline, without enum converter, we get JsonException. - Assert.Throws(() => JsonSerializer.Deserialize(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); - var obj = JsonSerializer.Deserialize(json, options); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum); Assert.Equal(25, obj.MyInt); // ConverterForInt32 throws this exception. - Assert.Throws(() => JsonSerializer.Serialize(obj, options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); } - private struct StructWithPropertiesWithConverter + public struct StructWithPropertiesWithConverter { [JsonInclude] public MySmallEnum MyEnum { private get; set; } @@ -169,23 +174,23 @@ namespace System.Text.Json.Serialization.Tests internal MySmallEnum GetMyEnum => MyEnum; } - private enum MySmallEnum + public enum MySmallEnum { DefaultValue = 0, AnotherValue = 1 } [Fact] - public static void HonorCaseInsensitivity() + public async Task HonorCaseInsensitivity() { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; string json = @"{""MYSTRING"":""Hello""}"; - Assert.Null(JsonSerializer.Deserialize(json).MyString); - Assert.Equal("Hello", JsonSerializer.Deserialize(json, options).MyString); + Assert.Null((await JsonSerializerWrapperForString.DeserializeWrapper(json)).MyString); + Assert.Equal("Hello", (await JsonSerializerWrapperForString.DeserializeWrapper(json, options)).MyString); } - private struct MyStruct_WithNonPublicAccessors_WithTypeAttribute + public struct MyStruct_WithNonPublicAccessors_WithTypeAttribute { [JsonInclude] public int MyInt { get; private set; } @@ -201,30 +206,30 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void HonorNamingPolicy() + public async Task HonorNamingPolicy() { var options = new JsonSerializerOptions { PropertyNamingPolicy = new SimpleSnakeCasePolicy() }; string json = @"{""my_string"":""Hello""}"; - Assert.Null(JsonSerializer.Deserialize(json).MyString); - Assert.Equal("Hello", JsonSerializer.Deserialize(json, options).MyString); + Assert.Null((await JsonSerializerWrapperForString.DeserializeWrapper(json)).MyString); + Assert.Equal("Hello", (await JsonSerializerWrapperForString.DeserializeWrapper(json, options)).MyString); } [Fact] - public static void HonorJsonPropertyName() + public virtual async Task HonorJsonPropertyName() { string json = @"{""prop1"":1,""prop2"":2}"; - var obj = JsonSerializer.Deserialize(json); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum); Assert.Equal(2, obj.MyInt); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Contains(@"""prop1"":1", json); Assert.Contains(@"""prop2"":2", json); } - private struct StructWithPropertiesWithJsonPropertyName + public struct StructWithPropertiesWithJsonPropertyName { [JsonInclude] [JsonPropertyName("prop1")] @@ -239,9 +244,13 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void Map_JsonSerializableProperties_ToCtorArgs() +#if BUILDING_SOURCE_GENERATOR_TESTS + // Needs support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task Map_JsonSerializableProperties_ToCtorArgs() { - var obj = JsonSerializer.Deserialize(@"{""X"":1,""Y"":2}"); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""X"":1,""Y"":2}"); Assert.Equal(1, obj.X); Assert.Equal(2, obj.GetY); } @@ -260,24 +269,24 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void Public_And_NonPublicPropertyAccessors_PropertyAttributes() + public virtual async Task Public_And_NonPublicPropertyAccessors_PropertyAttributes() { string json = @"{""W"":1,""X"":2,""Y"":3,""Z"":4}"; - var obj = JsonSerializer.Deserialize(json); + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(1, obj.W); Assert.Equal(2, obj.X); Assert.Equal(3, obj.Y); Assert.Equal(4, obj.GetZ); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Contains(@"""W"":1", json); Assert.Contains(@"""X"":2", json); Assert.Contains(@"""Y"":3", json); Assert.Contains(@"""Z"":4", json); } - private class ClassWithMixedPropertyAccessors_PropertyAttributes + public class ClassWithMixedPropertyAccessors_PropertyAttributes { [JsonInclude] public int W { get; set; } @@ -301,40 +310,40 @@ namespace System.Text.Json.Serialization.Tests [InlineData(typeof(ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty))] [InlineData(typeof(ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty))] [InlineData(typeof(ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty))] - public static void NonPublicProperty_WithJsonInclude_Invalid(Type type) + public virtual async Task NonPublicProperty_WithJsonInclude_Invalid(Type type) { - InvalidOperationException ex = Assert.Throws(() => JsonSerializer.Deserialize("", type)); + InvalidOperationException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", type)); string exAsStr = ex.ToString(); Assert.Contains("MyString", exAsStr); Assert.Contains(type.ToString(), exAsStr); Assert.Contains("JsonIncludeAttribute", exAsStr); - ex = Assert.Throws(() => JsonSerializer.Serialize(Activator.CreateInstance(type), type)); + ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); exAsStr = ex.ToString(); Assert.Contains("MyString", exAsStr); Assert.Contains(type.ToString(), exAsStr); Assert.Contains("JsonIncludeAttribute", exAsStr); } - private class ClassWithPrivateProperty_WithJsonIncludeProperty + public class ClassWithPrivateProperty_WithJsonIncludeProperty { [JsonInclude] private string MyString { get; set; } } - private class ClassWithInternalProperty_WithJsonIncludeProperty + public class ClassWithInternalProperty_WithJsonIncludeProperty { [JsonInclude] internal string MyString { get; } } - private class ClassWithProtectedProperty_WithJsonIncludeProperty + public class ClassWithProtectedProperty_WithJsonIncludeProperty { [JsonInclude] protected string MyString { get; private set; } } - private class ClassWithPrivateField_WithJsonIncludeProperty + public class ClassWithPrivateField_WithJsonIncludeProperty { [JsonInclude] private string MyString = null; @@ -342,31 +351,31 @@ namespace System.Text.Json.Serialization.Tests public override string ToString() => MyString; } - private class ClassWithInternalField_WithJsonIncludeProperty + public class ClassWithInternalField_WithJsonIncludeProperty { [JsonInclude] internal string MyString = null; } - private class ClassWithProtectedField_WithJsonIncludeProperty + public class ClassWithProtectedField_WithJsonIncludeProperty { [JsonInclude] protected string MyString = null; } - private class ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty + public class ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty { [JsonInclude] private string MyString { get; init; } } - private class ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty + public class ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty { [JsonInclude] internal string MyString { get; init; } } - private class ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty + public class ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty { [JsonInclude] protected string MyString { get; init; } diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs new file mode 100644 index 00000000000..32f19eb120c --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs @@ -0,0 +1,2632 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Numerics; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class PropertyVisibilityTests : SerializerTests + { + public PropertyVisibilityTests(JsonSerializerWrapperForString serializerWrapper) : base(serializerWrapper) { } + + [Fact] + public async Task Serialize_NewSlotPublicField() + { + // Serialize + var obj = new ClassWithNewSlotField(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyString"":""NewDefaultValue""}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewValue", ((ClassWithNewSlotField)obj).MyString); + Assert.Equal("DefaultValue", ((ClassWithInternalField)obj).MyString); + } + + [Fact] + public async Task Serialize_NewSlotPublicProperty() + { + // Serialize + var obj = new ClassWithNewSlotProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyString"":""NewDefaultValue""}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewValue", ((ClassWithNewSlotProperty)obj).MyString); + Assert.Equal("DefaultValue", ((ClassWithInternalProperty)obj).MyString); + } + + [Fact] + public async Task Serialize_BasePublicProperty_ConflictWithDerivedPrivate() + { + // Serialize + var obj = new ClassWithNewSlotInternalProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyString"":""DefaultValue""}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewValue", ((ClassWithPublicProperty)obj).MyString); + Assert.Equal("NewDefaultValue", ((ClassWithNewSlotInternalProperty)obj).MyString); + } + + [Fact] + public async Task Serialize_PublicProperty_ConflictWithPrivateDueAttributes() + { + // Serialize + var obj = new ClassWithPropertyNamingConflict(); + + // Newtonsoft.Json throws JsonSerializationException here because + // non-public properties are included when [JsonProperty] is placed on them. + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyString"":""DefaultValue""}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + + // Newtonsoft.Json throws JsonSerializationException here because + // non-public properties are included when [JsonProperty] is placed on them. + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewValue", obj.MyString); + Assert.Equal("ConflictingValue", obj.ConflictingString); + } + + [Fact] + public async Task Serialize_PublicProperty_ConflictWithPrivateDuePolicy() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassWithPropertyPolicyConflict(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + Assert.Equal(@"{""myString"":""DefaultValue""}", json); + + // Deserialize + json = @"{""myString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + Assert.Equal("NewValue", obj.MyString); + Assert.Equal("ConflictingValue", obj.myString); + } + + [Fact] + public async Task Serialize_NewSlotPublicProperty_ConflictWithBasePublicProperty() + { + // Serialize + var obj = new ClassWithNewSlotDecimalProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyNumeric"":1.5}", json); + + // Deserialize + json = @"{""MyNumeric"":2.5}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(2.5M, obj.MyNumeric); + } + + [Fact] + public async Task Serialize_NewSlotPublicField_ConflictWithBasePublicProperty() + { + // Serialize + var obj = new ClassWithNewSlotDecimalField(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyNumeric"":1.5}", json); + + // Deserialize + json = @"{""MyNumeric"":2.5}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(2.5M, obj.MyNumeric); + } + + [Fact] + public async Task Serialize_NewSlotPublicField_SpecifiedJsonPropertyName() + { + // Serialize + var obj = new ClassWithNewSlotAttributedDecimalField(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Contains(@"""MyNewNumeric"":1.5", json); + Assert.Contains(@"""MyNumeric"":1", json); + + // Deserialize + json = @"{""MyNewNumeric"":2.5,""MyNumeric"":4}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(4, ((ClassWithHiddenByNewSlotIntProperty)obj).MyNumeric); + Assert.Equal(2.5M, ((ClassWithNewSlotAttributedDecimalField)obj).MyNumeric); + } + + [Fact] + public async Task Serialize_NewSlotPublicProperty_SpecifiedJsonPropertyName() + { + // Serialize + var obj = new ClassWithNewSlotAttributedDecimalProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Contains(@"""MyNewNumeric"":1.5", json); + Assert.Contains(@"""MyNumeric"":1", json); + + // Deserialize + json = @"{""MyNewNumeric"":2.5,""MyNumeric"":4}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(4, ((ClassWithHiddenByNewSlotIntProperty)obj).MyNumeric); + Assert.Equal(2.5M, ((ClassWithNewSlotAttributedDecimalProperty)obj).MyNumeric); + } + + [Fact] + public async Task Ignore_NonPublicProperty() + { + // Serialize + var obj = new ClassWithInternalProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("DefaultValue", obj.MyString); + } + + [Fact] + public async Task Ignore_NewSlotPublicFieldIgnored() + { + // Serialize + var obj = new ClassWithIgnoredNewSlotField(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewDefaultValue", ((ClassWithIgnoredNewSlotField)obj).MyString); + Assert.Equal("DefaultValue", ((ClassWithInternalField)obj).MyString); + } + + [Fact] + public async Task Ignore_NewSlotPublicPropertyIgnored() + { + // Serialize + var obj = new ClassWithIgnoredNewSlotProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("NewDefaultValue", ((ClassWithIgnoredNewSlotProperty)obj).MyString); + Assert.Equal("DefaultValue", ((ClassWithInternalProperty)obj).MyString); + } + + [Fact] + public async Task Ignore_BasePublicPropertyIgnored_ConflictWithDerivedPrivate() + { + // Serialize + var obj = new ClassWithIgnoredPublicPropertyAndNewSlotPrivate(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("DefaultValue", ((ClassWithIgnoredPublicProperty)obj).MyString); + Assert.Equal("NewDefaultValue", ((ClassWithIgnoredPublicPropertyAndNewSlotPrivate)obj).MyString); + } + + [Fact] + public async Task Ignore_PublicProperty_ConflictWithPrivateDueAttributes() + { + // Serialize + var obj = new ClassWithIgnoredPropertyNamingConflictPrivate(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{}", json); + + // Newtonsoft.Json has the following output because non-public properties are included when [JsonProperty] is placed on them. + // {"MyString":"ConflictingValue"} + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal("ConflictingValue", obj.ConflictingString); + + // The output for Newtonsoft.Json is: + // obj.ConflictingString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Ignore_PublicProperty_ConflictWithPrivateDuePolicy() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassWithIgnoredPropertyPolicyConflictPrivate(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + Assert.Equal(@"{}", json); + + // Deserialize + json = @"{""myString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal("ConflictingValue", obj.myString); + } + + [Fact] + public async Task Ignore_PublicProperty_ConflictWithPublicDueAttributes() + { + // Serialize + var obj = new ClassWithIgnoredPropertyNamingConflictPublic(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + + Assert.Equal(@"{""MyString"":""ConflictingValue""}", json); + + // Deserialize + json = @"{""MyString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal("NewValue", obj.ConflictingString); + } + + [Fact] + public async Task Ignore_PublicProperty_ConflictWithPublicDuePolicy() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassWithIgnoredPropertyPolicyConflictPublic(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + Assert.Equal(@"{""myString"":""ConflictingValue""}", json); + + // Deserialize + json = @"{""myString"":""NewValue""}"; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal("NewValue", obj.myString); + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDueAttributes() + { + // Serialize + var obj = new ClassWithPropertyNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDueAttributes() + { + // Serialize + var obj = new ClassWithPropertyFieldNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDueAttributes_SingleInheritance() + { + // Serialize + var obj = new ClassInheritedWithPropertyNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // The output for Newtonsoft.Json is: + // {"MyString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + // The output for Newtonsoft.Json is: + // obj.ConflictingString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDueAttributes_SingleInheritance() + { + // Serialize + var obj = new ClassInheritedWithPropertyFieldNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // The output for Newtonsoft.Json is: + // {"MyString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + // The output for Newtonsoft.Json is: + // obj.ConflictingString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDueAttributes_DoubleInheritance() + { + // Serialize + var obj = new ClassTwiceInheritedWithPropertyNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // The output for Newtonsoft.Json is: + // {"MyString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + // The output for Newtonsoft.Json is: + // obj.ConflictingString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDueAttributes_DoubleInheritance() + { + // Serialize + var obj = new ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // The output for Newtonsoft.Json is: + // {"MyString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + // The output for Newtonsoft.Json is: + // obj.ConflictingString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDuePolicy() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassWithPropertyPolicyConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDuePolicy() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassWithPropertyFieldPolicyConflictWhichThrows(); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDuePolicy_SingleInheritance() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassInheritedWithPropertyPolicyConflictWhichThrows(); + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // The output for Newtonsoft.Json is: + // {"myString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + + // The output for Newtonsoft.Json is: + // obj.myString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDuePolicy_SingleInheritance() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassInheritedWithPropertyFieldPolicyConflictWhichThrows(); + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // The output for Newtonsoft.Json is: + // {"myString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + + // The output for Newtonsoft.Json is: + // obj.myString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicProperty_ConflictDuePolicy_DobuleInheritance() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows(); + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // The output for Newtonsoft.Json is: + // {"myString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + + // The output for Newtonsoft.Json is: + // obj.myString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task Throw_PublicPropertyAndField_ConflictDuePolicy_DobuleInheritance() + { + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + + // Serialize + var obj = new ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows(); + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + + // The output for Newtonsoft.Json is: + // {"myString":"ConflictingValue"} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + + // Deserialize + string json = @"{""MyString"":""NewValue""}"; + + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, options)); + + // The output for Newtonsoft.Json is: + // obj.myString = "NewValue" + // obj.MyString still equals "DefaultValue" + } + + [Fact] + public async Task HiddenPropertiesIgnored_WhenOverridesIgnored() + { + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_IgnoredOverride()); + Assert.Equal(@"{}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName()); + Assert.Equal(@"{""MyProp"":null}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_Ignored_NewProperty()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_WithConflictingNewMember()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_WithConflictingNewMember_Of_DifferentType()); + Assert.Equal(@"{""MyProp"":0}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_Ignored_ConflictingNewMember()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_NewProperty_And_ConflictingPropertyName()); + Assert.Equal(@"{""MyProp"":null}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_Ignored_NewProperty_Of_DifferentType()); + Assert.Equal(@"{""MyProp"":false}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName()); + Assert.Equal(@"{""MyProp"":null}", serialized); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new FurtherDerivedClass_With_ConflictingPropertyName()); + Assert.Equal(@"{""MyProp"":null}", serialized); + + // Here we differ from Newtonsoft.Json, where the output would be + // {"MyProp":null} + // Conflicts at different type-hierarchy levels that are not caused by + // deriving or the new keyword are allowed. Properties on more derived types win. + // This is invalid in System.Text.Json. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new DerivedClass_WithConflictingPropertyName())); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(new FurtherDerivedClass_With_IgnoredOverride()); + Assert.Equal(@"{""MyProp"":null}", serialized); + } + + public class ClassWithInternalField + { + internal string MyString = "DefaultValue"; + } + + public class ClassWithNewSlotField : ClassWithInternalField + { + [JsonInclude] + public new string MyString = "NewDefaultValue"; + } + + public class ClassWithInternalProperty + { + internal string MyString { get; set; } = "DefaultValue"; + } + + public class ClassWithNewSlotProperty : ClassWithInternalProperty + { + public new string MyString { get; set; } = "NewDefaultValue"; + } + + public class ClassWithPublicProperty + { + public string MyString { get; set; } = "DefaultValue"; + } + + public class ClassWithNewSlotInternalProperty : ClassWithPublicProperty + { + internal new string MyString { get; set; } = "NewDefaultValue"; + } + + public class ClassWithPropertyNamingConflict + { + public string MyString { get; set; } = "DefaultValue"; + + [JsonPropertyName(nameof(MyString))] + internal string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassWithPropertyNamingConflictWhichThrows + { + public string MyString { get; set; } = "DefaultValue"; + + [JsonPropertyName(nameof(MyString))] + public string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassWithPropertyFieldNamingConflictWhichThrows + { + public string MyString { get; set; } = "DefaultValue"; + + [JsonInclude] + [JsonPropertyName(nameof(MyString))] + public string ConflictingString = "ConflictingValue"; + } + + public class ClassInheritedWithPropertyNamingConflictWhichThrows : ClassWithPublicProperty + { + [JsonPropertyName(nameof(MyString))] + public string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassInheritedWithPropertyFieldNamingConflictWhichThrows : ClassWithPublicProperty + { + [JsonInclude] + [JsonPropertyName(nameof(MyString))] + public string ConflictingString = "ConflictingValue"; + } + + public class ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy : ClassWithPublicProperty + { + } + + public class ClassTwiceInheritedWithPropertyNamingConflictWhichThrows : ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy + { + [JsonPropertyName(nameof(MyString))] + public string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows : ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy + { + [JsonInclude] + [JsonPropertyName(nameof(MyString))] + public string ConflictingString = "ConflictingValue"; + } + + public class ClassWithPropertyPolicyConflict + { + public string MyString { get; set; } = "DefaultValue"; + + internal string myString { get; set; } = "ConflictingValue"; + } + + public class ClassWithPropertyPolicyConflictWhichThrows + { + public string MyString { get; set; } = "DefaultValue"; + + public string myString { get; set; } = "ConflictingValue"; + } + + public class ClassWithPropertyFieldPolicyConflictWhichThrows + { + public string MyString { get; set; } = "DefaultValue"; + + [JsonInclude] + public string myString = "ConflictingValue"; + } + + public class ClassInheritedWithPropertyPolicyConflictWhichThrows : ClassWithPublicProperty + { + public string myString { get; set; } = "ConflictingValue"; + } + + public class ClassInheritedWithPropertyFieldPolicyConflictWhichThrows : ClassWithPublicProperty + { + [JsonInclude] + public string myString = "ConflictingValue"; + } + + public class ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy : ClassWithPublicProperty + { + } + + public class ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows : ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy + { + public string myString { get; set; } = "ConflictingValue"; + } + + public class ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows : ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy + { + [JsonInclude] + public string myString { get; set; } = "ConflictingValue"; + } + + public class ClassWithIgnoredNewSlotField : ClassWithInternalField + { + [JsonIgnore] + public new string MyString = "NewDefaultValue"; + } + + public class ClassWithIgnoredNewSlotProperty : ClassWithInternalProperty + { + [JsonIgnore] + public new string MyString { get; set; } = "NewDefaultValue"; + } + + public class ClassWithIgnoredPublicProperty + { + [JsonIgnore] + public string MyString { get; set; } = "DefaultValue"; + } + + public class ClassWithIgnoredPublicPropertyAndNewSlotPrivate : ClassWithIgnoredPublicProperty + { + internal new string MyString { get; set; } = "NewDefaultValue"; + } + + public class ClassWithIgnoredPropertyNamingConflictPrivate + { + [JsonIgnore] + public string MyString { get; set; } = "DefaultValue"; + + [JsonPropertyName(nameof(MyString))] + internal string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassWithIgnoredPropertyPolicyConflictPrivate + { + [JsonIgnore] + public string MyString { get; set; } = "DefaultValue"; + + internal string myString { get; set; } = "ConflictingValue"; + } + + public class ClassWithIgnoredPropertyNamingConflictPublic + { + [JsonIgnore] + public string MyString { get; set; } = "DefaultValue"; + + [JsonPropertyName(nameof(MyString))] + public string ConflictingString { get; set; } = "ConflictingValue"; + } + + public class ClassWithIgnoredPropertyPolicyConflictPublic + { + [JsonIgnore] + public string MyString { get; set; } = "DefaultValue"; + + public string myString { get; set; } = "ConflictingValue"; + } + + public class ClassWithHiddenByNewSlotIntProperty + { + public int MyNumeric { get; set; } = 1; + } + + public class ClassWithNewSlotDecimalField : ClassWithHiddenByNewSlotIntProperty + { + [JsonInclude] + public new decimal MyNumeric = 1.5M; + } + + public class ClassWithNewSlotDecimalProperty : ClassWithHiddenByNewSlotIntProperty + { + public new decimal MyNumeric { get; set; } = 1.5M; + } + + public class ClassWithNewSlotAttributedDecimalField : ClassWithHiddenByNewSlotIntProperty + { + [JsonInclude] + [JsonPropertyName("MyNewNumeric")] + public new decimal MyNumeric = 1.5M; + } + + public class ClassWithNewSlotAttributedDecimalProperty : ClassWithHiddenByNewSlotIntProperty + { + [JsonPropertyName("MyNewNumeric")] + public new decimal MyNumeric { get; set; } = 1.5M; + } + + public class Class_With_VirtualProperty + { + public virtual bool MyProp { get; set; } + } + + public class DerivedClass_With_IgnoredOverride : Class_With_VirtualProperty + { + [JsonIgnore] + public override bool MyProp { get; set; } + } + + public class DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride : DerivedClass_With_IgnoredOverride + { + public override bool MyProp { get; set; } + } + + public class DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName : Class_With_VirtualProperty + { + [JsonPropertyName("MyProp")] + public string MyString { get; set; } + + [JsonIgnore] + public override bool MyProp { get; set; } + } + + public class Class_With_Property + { + public bool MyProp { get; set; } + } + + public class DerivedClass_With_Ignored_NewProperty : Class_With_Property + { + [JsonIgnore] + public new bool MyProp { get; set; } + } + + public class DerivedClass_With_NewProperty_And_ConflictingPropertyName : Class_With_Property + { + [JsonPropertyName("MyProp")] + public string MyString { get; set; } + + [JsonIgnore] + public new bool MyProp { get; set; } + } + + public class DerivedClass_With_Ignored_NewProperty_Of_DifferentType : Class_With_Property + { + [JsonIgnore] + public new int MyProp { get; set; } + } + + public class DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName : Class_With_Property + { + [JsonPropertyName("MyProp")] + public string MyString { get; set; } + + [JsonIgnore] + public new int MyProp { get; set; } + } + + public class DerivedClass_WithIgnoredOverride : Class_With_VirtualProperty + { + [JsonIgnore] + public override bool MyProp { get; set; } + } + + public class DerivedClass_WithConflictingNewMember : Class_With_VirtualProperty + { + public new bool MyProp { get; set; } + } + + public class DerivedClass_WithConflictingNewMember_Of_DifferentType : Class_With_VirtualProperty + { + public new int MyProp { get; set; } + } + + public class DerivedClass_With_Ignored_ConflictingNewMember : Class_With_VirtualProperty + { + [JsonIgnore] + public new bool MyProp { get; set; } + } + + public class DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType : Class_With_VirtualProperty + { + [JsonIgnore] + public new int MyProp { get; set; } + } + + public class FurtherDerivedClass_With_ConflictingPropertyName : DerivedClass_WithIgnoredOverride + { + [JsonPropertyName("MyProp")] + public string MyString { get; set; } + } + + public class DerivedClass_WithConflictingPropertyName : Class_With_VirtualProperty + { + [JsonPropertyName("MyProp")] + public string MyString { get; set; } + } + + public class FurtherDerivedClass_With_IgnoredOverride : DerivedClass_WithConflictingPropertyName + { + [JsonIgnore] + public override bool MyProp { get; set; } + } + + [Fact] + public async Task IgnoreReadOnlyProperties() + { + var options = new JsonSerializerOptions(); + options.IgnoreReadOnlyProperties = true; + + var obj = new ClassWithNoSetter(); + + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Collections are always serialized unless they have [JsonIgnore]. + Assert.Equal(@"{""MyInts"":[1,2]}", json); + } + + [Fact] + public async Task IgnoreReadOnlyFields() + { + var options = new JsonSerializerOptions(); + options.IncludeFields = true; + options.IgnoreReadOnlyFields = true; + + var obj = new ClassWithReadOnlyFields(); + + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Collections are always serialized unless they have [JsonIgnore]. + Assert.Equal(@"{""MyInts"":[1,2]}", json); + } + + [Fact] + public async Task NoSetter() + { + var obj = new ClassWithNoSetter(); + + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""MyString"":""DefaultValue""", json); + Assert.Contains(@"""MyInts"":[1,2]", json); + + obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyString"":""IgnoreMe"",""MyInts"":[0]}"); + Assert.Equal("DefaultValue", obj.MyString); + Assert.Equal(2, obj.MyInts.Length); + } + + [Fact] + public async Task NoGetter() + { + ClassWithNoGetter objWithNoGetter = await JsonSerializerWrapperForString.DeserializeWrapper( + @"{""MyString"":""Hello"",""MyIntArray"":[0],""MyIntList"":[0]}"); + + Assert.Equal("Hello", objWithNoGetter.GetMyString()); + + // Currently we don't support setters without getters. + Assert.Equal(0, objWithNoGetter.GetMyIntArray().Length); + Assert.Equal(0, objWithNoGetter.GetMyIntList().Count); + } + + [Fact] + public async Task PrivateGetter() + { + var obj = new ClassWithPrivateSetterAndGetter(); + obj.SetMyString("Hello"); + + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal(@"{}", json); + } + + [Fact] + public async Task PrivateSetter() + { + string json = @"{""MyString"":""Hello""}"; + + ClassWithPrivateSetterAndGetter objCopy = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Null(objCopy.GetMyString()); + } + + [Fact] + public async Task PrivateSetterPublicGetter() + { + // https://github.com/dotnet/runtime/issues/29503 + ClassWithPublicGetterAndPrivateSetter obj + = await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Class"": {} }"); + + Assert.NotNull(obj); + Assert.Null(obj.Class); + } + + [Fact] + public async Task MissingObjectProperty() + { + ClassWithMissingObjectProperty obj + = await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Object"": {} }"); + + Assert.Null(obj.Collection); + } + + [Fact] + public async Task MissingCollectionProperty() + { + ClassWithMissingCollectionProperty obj + = await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Collection"": [] }"); + + Assert.Null(obj.Object); + } + + public class ClassWithPublicGetterAndPrivateSetter + { + public NestedClass Class { get; private set; } + } + + public class NestedClass + { + } + + [Fact] + public async Task JsonIgnoreAttribute() + { + var options = new JsonSerializerOptions { IncludeFields = true }; + + // Verify default state. + var obj = new ClassWithIgnoreAttributeProperty(); + Assert.Equal(@"MyString", obj.MyString); + Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); + Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); + Assert.Equal(3.14M, obj.MyNumeric); + + // Verify serialize. + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Contains(@"""MyString""", json); + Assert.DoesNotContain(@"MyStringWithIgnore", json); + Assert.DoesNotContain(@"MyStringsWithIgnore", json); + Assert.DoesNotContain(@"MyDictionaryWithIgnore", json); + Assert.DoesNotContain(@"MyNumeric", json); + + // Verify deserialize default. + obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{}", options); + Assert.Equal(@"MyString", obj.MyString); + Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); + Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); + Assert.Equal(3.14M, obj.MyNumeric); + + // Verify deserialize ignores the json for MyStringWithIgnore and MyStringsWithIgnore. + obj = await JsonSerializerWrapperForString.DeserializeWrapper( + @"{""MyString"":""Hello"", ""MyStringWithIgnore"":""IgnoreMe"", ""MyStringsWithIgnore"":[""IgnoreMe""], ""MyDictionaryWithIgnore"":{""Key"":9}, ""MyNumeric"": 2.71828}", options); + Assert.Contains(@"Hello", obj.MyString); + Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); + Assert.Equal(2, obj.MyStringsWithIgnore.Length); + Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); + Assert.Equal(3.14M, obj.MyNumeric); + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Needs support for more collections. + [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] +#endif + public async Task JsonIgnoreAttribute_UnsupportedCollection() + { + string json = + @"{ + ""MyConcurrentDict"":{ + ""key"":""value"" + }, + ""MyIDict"":{ + ""key"":""value"" + }, + ""MyDict"":{ + ""key"":""value"" + } + }"; + string wrapperJson = + @"{ + ""MyClass"":{ + ""MyConcurrentDict"":{ + ""key"":""value"" + }, + ""MyIDict"":{ + ""key"":""value"" + }, + ""MyDict"":{ + ""key"":""value"" + } + } + }"; + + // Unsupported collections will throw on deserialize by default. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + // Using new options instance to prevent using previously cached metadata. + JsonSerializerOptions options = new JsonSerializerOptions(); + + // Unsupported collections will throw on serialize by default. + // Only when the collection contains elements. + + var dictionary = new Dictionary(); + // Uri is an unsupported dictionary key. + dictionary.Add(new Uri("http://foo"), "bar"); + + var concurrentDictionary = new ConcurrentDictionary(dictionary); + + var instance = new ClassWithUnsupportedDictionary() + { + MyConcurrentDict = concurrentDictionary, + MyIDict = dictionary + }; + + var instanceWithIgnore = new ClassWithIgnoredUnsupportedDictionary + { + MyConcurrentDict = concurrentDictionary, + MyIDict = dictionary + }; + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(instance, options)); + + // Unsupported collections will throw on deserialize by default if they contain elements. + options = new JsonSerializerOptions(); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(wrapperJson, options)); + + options = new JsonSerializerOptions(); + // Unsupported collections will throw on serialize by default if they contain elements. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(instance, options)); + + // When ignored, we can serialize and deserialize without exceptions. + options = new JsonSerializerOptions(); + + Assert.NotNull(await JsonSerializerWrapperForString.SerializeWrapper(instanceWithIgnore, options)); + + ClassWithIgnoredUnsupportedDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.Null(obj.MyDict); + + options = new JsonSerializerOptions(); + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithIgnoredUnsupportedDictionary())); + + options = new JsonSerializerOptions(); + WrapperForClassWithIgnoredUnsupportedDictionary wrapperObj = await JsonSerializerWrapperForString.DeserializeWrapper(wrapperJson, options); + Assert.Null(wrapperObj.MyClass.MyDict); + + options = new JsonSerializerOptions(); + Assert.Equal(@"{""MyClass"":{}}", await JsonSerializerWrapperForString.SerializeWrapper(new WrapperForClassWithIgnoredUnsupportedDictionary() + { + MyClass = new ClassWithIgnoredUnsupportedDictionary(), + }, options)); + } + + [Fact] + public async Task JsonIgnoreAttribute_UnsupportedBigInteger() + { + string json = @"{""MyBigInteger"":1}"; + string wrapperJson = @"{""MyClass"":{""MyBigInteger"":1}}"; + + // Unsupported types will throw by default. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + // Using new options instance to prevent using previously cached metadata. + JsonSerializerOptions options = new JsonSerializerOptions(); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(wrapperJson, options)); + + // When ignored, we can serialize and deserialize without exceptions. + options = new JsonSerializerOptions(); + ClassWithIgnoredUnsupportedBigInteger obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.Null(obj.MyBigInteger); + + options = new JsonSerializerOptions(); + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithIgnoredUnsupportedBigInteger())); + + options = new JsonSerializerOptions(); + WrapperForClassWithIgnoredUnsupportedBigInteger wrapperObj = await JsonSerializerWrapperForString.DeserializeWrapper(wrapperJson, options); + Assert.Null(wrapperObj.MyClass.MyBigInteger); + + options = new JsonSerializerOptions(); + Assert.Equal(@"{""MyClass"":{}}", await JsonSerializerWrapperForString.SerializeWrapper(new WrapperForClassWithIgnoredUnsupportedBigInteger() + { + MyClass = new ClassWithIgnoredUnsupportedBigInteger(), + }, options)); + } + + public class ObjectDictWrapper : Dictionary { } + + public class ClassWithUnsupportedDictionary + { + public ConcurrentDictionary MyConcurrentDict { get; set; } + public IDictionary MyIDict { get; set; } + public ObjectDictWrapper MyDict { get; set; } + } + + public class WrapperForClassWithUnsupportedDictionary + { + public ClassWithUnsupportedDictionary MyClass { get; set; } = new ClassWithUnsupportedDictionary(); + } + + public class ClassWithIgnoredUnsupportedDictionary + { + [JsonIgnore] + public ConcurrentDictionary MyConcurrentDict { get; set; } + [JsonIgnore] + public IDictionary MyIDict { get; set; } + [JsonIgnore] + public ObjectDictWrapper MyDict { get; set; } + } + + public class WrapperForClassWithIgnoredUnsupportedDictionary + { + public ClassWithIgnoredUnsupportedDictionary MyClass { get; set; } + } + + public class ClassWithUnsupportedBigInteger + { + public BigInteger? MyBigInteger { get; set; } + } + + public class WrapperForClassWithUnsupportedBigInteger + { + public ClassWithUnsupportedBigInteger MyClass { get; set; } = new(); + } + + public class ClassWithIgnoredUnsupportedBigInteger + { + [JsonIgnore] + public BigInteger? MyBigInteger { get; set; } + } + + public class WrapperForClassWithIgnoredUnsupportedBigInteger + { + public ClassWithIgnoredUnsupportedBigInteger MyClass { get; set; } + } + + public class ClassWithMissingObjectProperty + { + public object[] Collection { get; set; } + } + + public class ClassWithMissingCollectionProperty + { + public object Object { get; set; } + } + + public class ClassWithPrivateSetterAndGetter + { + private string MyString { get; set; } + + public string GetMyString() + { + return MyString; + } + + public void SetMyString(string value) + { + MyString = value; + } + } + + public class ClassWithReadOnlyFields + { + public ClassWithReadOnlyFields() + { + MyString = "DefaultValue"; + MyInts = new int[] { 1, 2 }; + } + + public readonly string MyString; + public readonly int[] MyInts; + } + + public class ClassWithNoSetter + { + public ClassWithNoSetter() + { + MyString = "DefaultValue"; + MyInts = new int[] { 1, 2 }; + } + + public string MyString { get; } + public int[] MyInts { get; } + } + + public class ClassWithNoGetter + { + string _myString = ""; + int[] _myIntArray = new int[] { }; + List _myIntList = new List { }; + + public string MyString + { + set + { + _myString = value; + } + } + + public int[] MyIntArray + { + set + { + _myIntArray = value; + } + } + + public List MyList + { + set + { + _myIntList = value; + } + } + + public string GetMyString() + { + return _myString; + } + + public int[] GetMyIntArray() + { + return _myIntArray; + } + + public List GetMyIntList() + { + return _myIntList; + } + } + + public class ClassWithIgnoreAttributeProperty + { + public ClassWithIgnoreAttributeProperty() + { + MyDictionaryWithIgnore = new Dictionary { { "Key", 1 } }; + MyString = "MyString"; + MyStringWithIgnore = "MyStringWithIgnore"; + MyStringsWithIgnore = new string[] { "1", "2" }; + MyNumeric = 3.14M; + } + + [JsonIgnore] + public Dictionary MyDictionaryWithIgnore { get; set; } + + [JsonIgnore] + public string MyStringWithIgnore { get; set; } + + public string MyString { get; set; } + + [JsonIgnore] + public string[] MyStringsWithIgnore { get; set; } + + [JsonIgnore] + public decimal MyNumeric; + } + + public enum MyEnum + { + Case1 = 0, + Case2 = 1, + } + + public struct StructWithOverride + { + [JsonIgnore] + public MyEnum EnumValue { get; set; } + + [JsonPropertyName("EnumValue")] + public string EnumString + { + get => EnumValue.ToString(); + set + { + if (value == "Case1") + { + EnumValue = MyEnum.Case1; + } + else if (value == "Case2") + { + EnumValue = MyEnum.Case2; + } + else + { + throw new Exception("Unknown value!"); + } + } + } + } + + [Fact] + public async Task OverrideJsonIgnorePropertyUsingJsonPropertyName() + { + const string json = @"{""EnumValue"":""Case2""}"; + + StructWithOverride obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(MyEnum.Case2, obj.EnumValue); + Assert.Equal("Case2", obj.EnumString); + + string jsonSerialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal(json, jsonSerialized); + } + + public struct ClassWithOverrideReversed + { + // Same as ClassWithOverride except the order of the properties is different, which should cause different reflection order. + [JsonPropertyName("EnumValue")] + public string EnumString + { + get => EnumValue.ToString(); + set + { + if (value == "Case1") + { + EnumValue = MyEnum.Case1; + } + if (value == "Case2") + { + EnumValue = MyEnum.Case2; + } + else + { + throw new Exception("Unknown value!"); + } + } + } + + [JsonIgnore] + public MyEnum EnumValue { get; set; } + } + + [Fact] + public async Task OverrideJsonIgnorePropertyUsingJsonPropertyNameReversed() + { + const string json = @"{""EnumValue"":""Case2""}"; + + ClassWithOverrideReversed obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + Assert.Equal(MyEnum.Case2, obj.EnumValue); + Assert.Equal("Case2", obj.EnumString); + + string jsonSerialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal(json, jsonSerialized); + } + + [Theory] + [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways))] + [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreConditionSetToAlwaysWorks(Type type) + { + string json = @"{""MyString"":""Random"",""MyDateTime"":""2020-03-23"",""MyInt"":4}"; + + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type); + Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(default, (DateTime)type.GetProperty("MyDateTime").GetValue(obj)); + Assert.Equal(4, (int)type.GetProperty("MyInt").GetValue(obj)); + + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""MyString"":""Random""", serialized); + Assert.Contains(@"""MyInt"":4", serialized); + Assert.DoesNotContain(@"""MyDateTime"":", serialized); + } + + public class ClassWithProperty_IgnoreConditionAlways + { + public string MyString { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public DateTime MyDateTime { get; set; } + public int MyInt { get; set; } + } + + private class ClassWithProperty_IgnoreConditionAlways_Ctor + { + public string MyString { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public DateTime MyDateTime { get; } + public int MyInt { get; } + + public ClassWithProperty_IgnoreConditionAlways_Ctor(DateTime myDateTime, int myInt) + { + MyDateTime = myDateTime; + MyInt = myInt; + } + } + + [Theory] + [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData))] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreConditionWhenWritingDefault_ClassProperty(Type type, JsonSerializerOptions options) + { + // Property shouldn't be ignored if it isn't null. + string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; + + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyString"":""Random""", serialized); + Assert.Contains(@"""Int2"":2", serialized); + + // Property should be ignored when null. + json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; + + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + + if (options.IgnoreNullValues) + { + // Null values can be ignored on deserialization using IgnoreNullValues. + Assert.Equal("DefaultString", (string)type.GetProperty("MyString").GetValue(obj)); + } + else + { + Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); + } + + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + // Set property to be ignored to null. + type.GetProperty("MyString").SetValue(obj, null); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""Int2"":2", serialized); + Assert.DoesNotContain(@"""MyString"":", serialized); + } + + public class ClassWithClassProperty_IgnoreConditionWhenWritingDefault + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string MyString { get; set; } = "DefaultString"; + public int Int2 { get; set; } + } + + private class ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string MyString { get; set; } = "DefaultString"; + public int Int2 { get; set; } + + public ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor(string myString) + { + if (myString != null) + { + MyString = myString; + } + } + } + + public static IEnumerable JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData() + { + yield return new object[] { typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault), new JsonSerializerOptions() }; + yield return new object[] { typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor), new JsonSerializerOptions { IgnoreNullValues = true } }; + } + + [Theory] + [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData))] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreConditionWhenWritingDefault_StructProperty(Type type, JsonSerializerOptions options) + { + // Property shouldn't be ignored if it isn't null. + string json = @"{""Int1"":1,""MyInt"":3,""Int2"":2}"; + + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Equal(3, (int)type.GetProperty("MyInt").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyInt"":3", serialized); + Assert.Contains(@"""Int2"":2", serialized); + + // Null being assigned to non-nullable types is invalid. + json = @"{""Int1"":1,""MyInt"":null,""Int2"":2}"; + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options)); + } + + public class ClassWithStructProperty_IgnoreConditionWhenWritingDefault + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int MyInt { get; set; } + public int Int2 { get; set; } + } + + private struct StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int MyInt { get; } + public int Int2 { get; set; } + + [JsonConstructor] + public StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor(int myInt) + { + Int1 = 0; + MyInt = myInt; + Int2 = 0; + } + } + + public static IEnumerable JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData() + { + yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionWhenWritingDefault), new JsonSerializerOptions() }; + yield return new object[] { typeof(StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor), new JsonSerializerOptions { IgnoreNullValues = true } }; + } + + [Theory] + [MemberData(nameof(JsonIgnoreConditionNever_TestData))] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreConditionNever(Type type) + { + // Property should always be (de)serialized, even when null. + string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; + + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyString"":""Random""", serialized); + Assert.Contains(@"""Int2"":2", serialized); + + // Property should always be (de)serialized, even when null. + json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; + + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyString"":null", serialized); + Assert.Contains(@"""Int2"":2", serialized); + } + + [Theory] + [MemberData(nameof(JsonIgnoreConditionNever_TestData))] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreConditionNever_IgnoreNullValues_True(Type type) + { + // Property should always be (de)serialized. + string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj, type, options); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyString"":""Random""", serialized); + Assert.Contains(@"""Int2"":2", serialized); + + // Property should always be (de)serialized, even when null. + json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; + + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, type, options); + Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); + Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); + Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); + + serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj, type, options); + Assert.Contains(@"""Int1"":1", serialized); + Assert.Contains(@"""MyString"":null", serialized); + Assert.Contains(@"""Int2"":2", serialized); + } + + public class ClassWithStructProperty_IgnoreConditionNever + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public string MyString { get; set; } + public int Int2 { get; set; } + } + + public class ClassWithStructProperty_IgnoreConditionNever_Ctor + { + public int Int1 { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public string MyString { get; } + public int Int2 { get; set; } + + public ClassWithStructProperty_IgnoreConditionNever_Ctor(string myString) + { + MyString = myString; + } + } + + public static IEnumerable JsonIgnoreConditionNever_TestData() + { + yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionNever) }; + yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionNever_Ctor) }; + } + + [Fact] + public async Task JsonIgnoreCondition_LastOneWins() + { + string json = @"{""MyString"":""Random"",""MYSTRING"":null}"; + + var options = new JsonSerializerOptions + { + IgnoreNullValues = true, + PropertyNameCaseInsensitive = true + }; + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + Assert.Null(obj.MyString); + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] +#endif + public async Task ClassWithComplexObjectsUsingIgnoreWhenWritingDefaultAttribute() + { + string json = @"{""Class"":{""MyInt16"":18}, ""Dictionary"":null}"; + + ClassUsingIgnoreWhenWritingDefaultAttribute obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + + // Class is deserialized. + Assert.NotNull(obj.Class); + Assert.Equal(18, obj.Class.MyInt16); + + // Dictionary is deserialized as JsonIgnoreCondition.WhenWritingDefault only applies to deserialization. + Assert.Null(obj.Dictionary); + + obj = new ClassUsingIgnoreWhenWritingDefaultAttribute(); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal(@"{""Dictionary"":{""Key"":""Value""}}", json); + } + + public class ClassUsingIgnoreWhenWritingDefaultAttribute + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public SimpleTestClass Class { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public Dictionary Dictionary { get; set; } = new Dictionary { ["Key"] = "Value" }; + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] +#endif + public async Task ClassWithComplexObjectUsingIgnoreNeverAttribute() + { + string json = @"{""Class"":null, ""Dictionary"":null}"; + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + // Class is not deserialized because it is null in json. + Assert.NotNull(obj.Class); + Assert.Equal(18, obj.Class.MyInt16); + + // Dictionary is deserialized regardless of being null in json. + Assert.Null(obj.Dictionary); + + // Serialize when values are null. + obj = new ClassUsingIgnoreNeverAttribute(); + obj.Class = null; + obj.Dictionary = null; + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Class is not included in json because it was null, Dictionary is included regardless of being null. + Assert.Equal(@"{""Dictionary"":null}", json); + } + + public class ClassUsingIgnoreNeverAttribute + { + public SimpleTestClass Class { get; set; } = new SimpleTestClass { MyInt16 = 18 }; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Dictionary Dictionary { get; set; } = new Dictionary { ["Key"] = "Value" }; + } + + [Fact] + public async Task IgnoreConditionNever_WinsOver_IgnoreReadOnlyProperties() + { + var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; + + // Baseline + string json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty("Hello"), options); + Assert.Equal("{}", json); + + // With condition to never ignore + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty_IgnoreNever("Hello"), options); + Assert.Equal(@"{""MyString"":""Hello""}", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty_IgnoreNever(null), options); + Assert.Equal(@"{""MyString"":null}", json); + } + + [Fact] + public async Task IgnoreConditionWhenWritingDefault_WinsOver_IgnoreReadOnlyProperties() + { + var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; + + // Baseline + string json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty("Hello"), options); + Assert.Equal("{}", json); + + // With condition to ignore when null + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault("Hello"), options); + Assert.Equal(@"{""MyString"":""Hello""}", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault(null), options); + Assert.Equal(@"{}", json); + } + + [Fact] + public async Task IgnoreConditionNever_WinsOver_IgnoreReadOnlyFields() + { + var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; + + // Baseline + string json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField("Hello"), options); + Assert.Equal("{}", json); + + // With condition to never ignore + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField_IgnoreNever("Hello"), options); + Assert.Equal(@"{""MyString"":""Hello""}", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField_IgnoreNever(null), options); + Assert.Equal(@"{""MyString"":null}", json); + } + + [Fact] + public async Task IgnoreConditionWhenWritingDefault_WinsOver_IgnoreReadOnlyFields() + { + var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; + + // Baseline + string json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField("Hello"), options); + Assert.Equal("{}", json); + + // With condition to ignore when null + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField_IgnoreWhenWritingDefault("Hello"), options); + Assert.Equal(@"{""MyString"":""Hello""}", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithReadOnlyStringField_IgnoreWhenWritingDefault(null), options); + Assert.Equal(@"{}", json); + } + + public class ClassWithReadOnlyStringProperty + { + public string MyString { get; } + + public ClassWithReadOnlyStringProperty(string myString) => MyString = myString; + } + + public class ClassWithReadOnlyStringProperty_IgnoreNever + { + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public string MyString { get; } + + public ClassWithReadOnlyStringProperty_IgnoreNever(string myString) => MyString = myString; + } + + public class ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string MyString { get; } + + public ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault(string myString) => MyString = myString; + } + + public class ClassWithReadOnlyStringField + { + public string MyString { get; } + + public ClassWithReadOnlyStringField(string myString) => MyString = myString; + } + + public class ClassWithReadOnlyStringField_IgnoreNever + { + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public string MyString { get; } + + public ClassWithReadOnlyStringField_IgnoreNever(string myString) => MyString = myString; + } + + public class ClassWithReadOnlyStringField_IgnoreWhenWritingDefault + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string MyString { get; } + + public ClassWithReadOnlyStringField_IgnoreWhenWritingDefault(string myString) => MyString = myString; + } + + [Fact] + public async Task NonPublicMembersAreNotIncluded() + { + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithNonPublicProperties())); + + string json = @"{""MyInt"":1,""MyString"":""Hello"",""MyFloat"":2,""MyDouble"":3}"; + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj.MyInt); + Assert.Null(obj.MyString); + Assert.Equal(0, obj.GetMyFloat); + Assert.Equal(0, obj.GetMyDouble); + } + + public class ClassWithNonPublicProperties + { + internal int MyInt { get; set; } + internal string MyString { get; private set; } + internal float MyFloat { private get; set; } + private double MyDouble { get; set; } + + internal float GetMyFloat => MyFloat; + internal double GetMyDouble => MyDouble; + } + + [Fact] + public async Task IgnoreCondition_WhenWritingDefault_Globally_Works() + { + // Baseline - default values written. + string expected = @"{""MyString"":null,""MyInt"":0,""MyPoint"":{""X"":0,""Y"":0}}"; + var obj = new ClassWithProps(); + JsonTestHelper.AssertJsonEqual(expected, await JsonSerializerWrapperForString.SerializeWrapper(obj)); + + // Default values ignored when specified. + Assert.Equal("{}", await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault })); + } + + public class ClassWithProps + { + public string MyString { get; set; } + public int MyInt { get; set; } + public Point_2D_Struct MyPoint { get; set; } + } + + [Fact] + public async Task IgnoreCondition_WhenWritingDefault_PerProperty_Works() + { + // Default values ignored when specified. + Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(new ClassWithPropsAndIgnoreAttributes())); + } + + public class ClassWithPropsAndIgnoreAttributes + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string MyString { get; set; } + public int MyInt { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public Point_2D_Struct MyPoint { get; set; } + } + + [Fact] + public async Task IgnoreCondition_WhenWritingDefault_DoesNotApplyToCollections() + { + var list = new List { false, true }; + + var options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; + Assert.Equal("[false,true]", await JsonSerializerWrapperForString.SerializeWrapper(list, options)); + } + + [Fact] + public async Task IgnoreCondition_WhenWritingDefault_DoesNotApplyToDeserialization() + { + // Baseline - null values are ignored on deserialization when using IgnoreNullValues (for compat with initial support). + string json = @"{""MyString"":null,""MyInt"":0,""MyPoint"":{""X"":0,""Y"":0}}"; + + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + ClassWithInitializedProps obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + Assert.Equal("Default", obj.MyString); + // Value types are not ignored. + Assert.Equal(0, obj.MyInt); + Assert.Equal(0, obj.MyPoint.X); + Assert.Equal(0, obj.MyPoint.X); + + // Test - default values (both null and default for value types) are not ignored when using + // JsonIgnoreCondition.WhenWritingDefault (as the option name implies) + options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.Null(obj.MyString); + Assert.Equal(0, obj.MyInt); + Assert.Equal(0, obj.MyPoint.X); + Assert.Equal(0, obj.MyPoint.X); + } + + public class ClassWithInitializedProps + { + public string MyString { get; set; } = "Default"; + public int MyInt { get; set; } = -1; + public Point_2D_Struct MyPoint { get; set; } = new Point_2D_Struct(-1, -1); + } + + [Fact] + public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_ClassTest() + { + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + + // Deserialization. + string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointClass"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; + + ClassWithValueAndReferenceTypes obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + // Null values ignored for reference types/nullable value types. + Assert.Equal("Default", obj.MyString); + Assert.NotNull(obj.MyPointClass); + Assert.True(obj.MyBool); + + // Default values not ignored for value types. + Assert.Equal(0, obj.MyInt); + Assert.Equal(0, obj.MyPointStruct.X); + Assert.Equal(0, obj.MyPointStruct.Y); + + // Serialization. + + // Make all members their default CLR value. + obj.MyString = null; + obj.MyPointClass = null; + obj.MyBool = null; + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Null values not serialized, default values for value types serialized. + JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_LargeStructTest() + { + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + + // Deserialization. + string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointClass"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; + + LargeStructWithValueAndReferenceTypes obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + // Null values ignored for reference types. + + Assert.Equal("Default", obj.MyString); + // No way to specify a non-constant default before construction with ctor, so this remains null. + Assert.Null(obj.MyPointClass); + Assert.True(obj.MyBool); + + // Default values not ignored for value types. + Assert.Equal(0, obj.MyInt); + Assert.Equal(0, obj.MyPointStruct.X); + Assert.Equal(0, obj.MyPointStruct.Y); + + // Serialization. + + // Make all members their default CLR value. + obj = new LargeStructWithValueAndReferenceTypes(null, new Point_2D_Struct(0, 0), null, 0, null); + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Null values not serialized, default values for value types serialized. + JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_SmallStructTest() + { + var options = new JsonSerializerOptions { IgnoreNullValues = true }; + + // Deserialization. + string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; + + SmallStructWithValueAndReferenceTypes obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + + // Null values ignored for reference types. + Assert.Equal("Default", obj.MyString); + Assert.True(obj.MyBool); + + // Default values not ignored for value types. + Assert.Equal(0, obj.MyInt); + Assert.Equal(0, obj.MyPointStruct.X); + Assert.Equal(0, obj.MyPointStruct.Y); + + // Serialization. + + // Make all members their default CLR value. + obj = new SmallStructWithValueAndReferenceTypes(new Point_2D_Struct(0, 0), null, 0, null); + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + + // Null values not serialized, default values for value types serialized. + JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); + } + + public class ClassWithValueAndReferenceTypes + { + public string MyString { get; set; } = "Default"; + public int MyInt { get; set; } = -1; + public bool? MyBool { get; set; } = true; + public PointClass MyPointClass { get; set; } = new PointClass(); + public Point_2D_Struct MyPointStruct { get; set; } = new Point_2D_Struct(1, 2); + } + + public struct LargeStructWithValueAndReferenceTypes + { + public string MyString { get; } + public int MyInt { get; set; } + public bool? MyBool { get; set; } + public PointClass MyPointClass { get; set; } + public Point_2D_Struct MyPointStruct { get; set; } + + [JsonConstructor] + public LargeStructWithValueAndReferenceTypes( + PointClass myPointClass, + Point_2D_Struct myPointStruct, + string myString = "Default", + int myInt = -1, + bool? myBool = true) + { + MyString = myString; + MyInt = myInt; + MyBool = myBool; + MyPointClass = myPointClass; + MyPointStruct = myPointStruct; + } + } + + private struct SmallStructWithValueAndReferenceTypes + { + public string MyString { get; } + public int MyInt { get; set; } + public bool? MyBool { get; set; } + public Point_2D_Struct MyPointStruct { get; set; } + + [JsonConstructor] + public SmallStructWithValueAndReferenceTypes( + Point_2D_Struct myPointStruct, + string myString = "Default", + int myInt = -1, + bool? myBool = true) + { + MyString = myString; + MyInt = myInt; + MyBool = myBool; + MyPointStruct = myPointStruct; + } + } + + public class PointClass { } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task Ignore_WhenWritingNull_Globally() + { + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true + }; + + string json = @"{ +""MyPointClass2_IgnoredWhenWritingNull"":{}, +""MyString1_IgnoredWhenWritingNull"":""Default"", +""MyNullableBool1_IgnoredWhenWritingNull"":null, +""MyInt2"":0, +""MyPointStruct2"":{""X"":1,""Y"":2}, +""MyInt1"":1, +""MyString2_IgnoredWhenWritingNull"":null, +""MyPointClass1_IgnoredWhenWritingNull"":null, +""MyNullableBool2_IgnoredWhenWritingNull"":true, +""MyPointStruct1"":{""X"":0,""Y"":0} +}"; + + // All members should correspond to JSON contents, as ignore doesn't apply to deserialization. + ClassWithThingsToIgnore obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.NotNull(obj.MyPointClass2_IgnoredWhenWritingNull); + Assert.Equal("Default", obj.MyString1_IgnoredWhenWritingNull); + Assert.Null(obj.MyNullableBool1_IgnoredWhenWritingNull); + Assert.Equal(0, obj.MyInt2); + Assert.Equal(1, obj.MyPointStruct2.X); + Assert.Equal(2, obj.MyPointStruct2.Y); + Assert.Equal(1, obj.MyInt1); + Assert.Null(obj.MyString2_IgnoredWhenWritingNull); + Assert.Null(obj.MyPointClass1_IgnoredWhenWritingNull); + Assert.True(obj.MyNullableBool2_IgnoredWhenWritingNull); + Assert.Equal(0, obj.MyPointStruct1.X); + Assert.Equal(0, obj.MyPointStruct1.Y); + + // Ignore null as appropriate during serialization. + string expectedJson = @"{ +""MyPointClass2_IgnoredWhenWritingNull"":{}, +""MyString1_IgnoredWhenWritingNull"":""Default"", +""MyInt2"":0, +""MyPointStruct2"":{""X"":1,""Y"":2}, +""MyInt1"":1, +""MyNullableBool2_IgnoredWhenWritingNull"":true, +""MyPointStruct1"":{""X"":0,""Y"":0} +}"; + JsonTestHelper.AssertJsonEqual(expectedJson, await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + } + + public class ClassWithThingsToIgnore + { + public string MyString1_IgnoredWhenWritingNull { get; set; } + + public string MyString2_IgnoredWhenWritingNull; + + public int MyInt1; + + public int MyInt2 { get; set; } + + public bool? MyNullableBool1_IgnoredWhenWritingNull { get; set; } + + public bool? MyNullableBool2_IgnoredWhenWritingNull; + + public PointClass MyPointClass1_IgnoredWhenWritingNull; + + public PointClass MyPointClass2_IgnoredWhenWritingNull { get; set; } + + public Point_2D_Struct_WithAttribute MyPointStruct1; + + public Point_2D_Struct_WithAttribute MyPointStruct2 { get; set; } + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Need support for parameterized ctors. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task Ignore_WhenWritingNull_PerProperty() + { + var options = new JsonSerializerOptions + { + IncludeFields = true + }; + + string json = @"{ +""MyPointClass2_IgnoredWhenWritingNull"":{}, +""MyString1_IgnoredWhenWritingNull"":""Default"", +""MyNullableBool1_IgnoredWhenWritingNull"":null, +""MyInt2"":0, +""MyPointStruct2"":{""X"":1,""Y"":2}, +""MyInt1"":1, +""MyString2_IgnoredWhenWritingNull"":null, +""MyPointClass1_IgnoredWhenWritingNull"":null, +""MyNullableBool2_IgnoredWhenWritingNull"":true, +""MyPointStruct1"":{""X"":0,""Y"":0} +}"; + + // All members should correspond to JSON contents, as ignore doesn't apply to deserialization. + ClassWithThingsToIgnore_PerProperty obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.NotNull(obj.MyPointClass2_IgnoredWhenWritingNull); + Assert.Equal("Default", obj.MyString1_IgnoredWhenWritingNull); + Assert.Null(obj.MyNullableBool1_IgnoredWhenWritingNull); + Assert.Equal(0, obj.MyInt2); + Assert.Equal(1, obj.MyPointStruct2.X); + Assert.Equal(2, obj.MyPointStruct2.Y); + Assert.Equal(1, obj.MyInt1); + Assert.Null(obj.MyString2_IgnoredWhenWritingNull); + Assert.Null(obj.MyPointClass1_IgnoredWhenWritingNull); + Assert.True(obj.MyNullableBool2_IgnoredWhenWritingNull); + Assert.Equal(0, obj.MyPointStruct1.X); + Assert.Equal(0, obj.MyPointStruct1.Y); + + // Ignore null as appropriate during serialization. + string expectedJson = @"{ +""MyPointClass2_IgnoredWhenWritingNull"":{}, +""MyString1_IgnoredWhenWritingNull"":""Default"", +""MyInt2"":0, +""MyPointStruct2"":{""X"":1,""Y"":2}, +""MyInt1"":1, +""MyNullableBool2_IgnoredWhenWritingNull"":true, +""MyPointStruct1"":{""X"":0,""Y"":0} +}"; + JsonTestHelper.AssertJsonEqual(expectedJson, await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + } + + public class ClassWithThingsToIgnore_PerProperty + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string MyString1_IgnoredWhenWritingNull { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string MyString2_IgnoredWhenWritingNull; + + public int MyInt1; + + public int MyInt2 { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? MyNullableBool1_IgnoredWhenWritingNull { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? MyNullableBool2_IgnoredWhenWritingNull; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public PointClass MyPointClass1_IgnoredWhenWritingNull; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public PointClass MyPointClass2_IgnoredWhenWritingNull { get; set; } + + public Point_2D_Struct_WithAttribute MyPointStruct1; + + public Point_2D_Struct_WithAttribute MyPointStruct2 { get; set; } + } + + [Theory] + [InlineData(typeof(ClassWithBadIgnoreAttribute))] + [InlineData(typeof(StructWithBadIgnoreAttribute))] + public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(Type type) + { + InvalidOperationException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", type)); + string exAsStr = ex.ToString(); + Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); + Assert.Contains("MyBadMember", exAsStr); + Assert.Contains(type.ToString(), exAsStr); + Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); + + ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); + exAsStr = ex.ToString(); + Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); + Assert.Contains("MyBadMember", exAsStr); + Assert.Contains(type.ToString(), exAsStr); + Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); + } + + [Theory] + [InlineData(typeof(ClassWithBadIgnoreAttribute))] + [InlineData(typeof(StructWithBadIgnoreAttribute))] + public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + { + InvalidOperationException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("", type)); + string exAsStr = ex.ToString(); + Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); + Assert.Contains("MyBadMember", exAsStr); + Assert.Contains(type.ToString(), exAsStr); + Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); + + ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type))); + exAsStr = ex.ToString(); + Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); + Assert.Contains("MyBadMember", exAsStr); + Assert.Contains(type.ToString(), exAsStr); + Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); + } + + public class ClassWithBadIgnoreAttribute + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int MyBadMember { get; set; } + } + + public struct StructWithBadIgnoreAttribute + { + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Point_2D_Struct MyBadMember { get; set; } + } + + public interface IUseCustomConverter { } + + [JsonConverter(typeof(MyCustomConverter))] + public struct MyValueTypeWithProperties : IUseCustomConverter + { + public int PrimitiveValue { get; set; } + public object RefValue { get; set; } + } + + public class MyCustomConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeof(IUseCustomConverter).IsAssignableFrom(typeToConvert); + } + + public override IUseCustomConverter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + throw new NotImplementedException(); + + public override void Write(Utf8JsonWriter writer, IUseCustomConverter value, JsonSerializerOptions options) + { + MyValueTypeWithProperties obj = (MyValueTypeWithProperties)value; + writer.WriteNumberValue(obj.PrimitiveValue + 100); + // Ignore obj.RefValue + } + } + + public class MyClassWithValueType + { + public MyClassWithValueType() { } + + public MyValueTypeWithProperties Value { get; set; } + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Needs bug fixes to custom converter handling. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreCondition_WhenWritingDefault_OnValueTypeWithCustomConverter() + { + var obj = new MyClassWithValueType(); + + // Baseline without custom options. + Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("{\"Value\":100}", json); + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + // Verify ignored. + Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{}", json); + + // Change a primitive value so it's no longer a default value. + obj.Value = new MyValueTypeWithProperties { PrimitiveValue = 1 }; + Assert.False(EqualityComparer.Default.Equals(default, obj.Value)); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{\"Value\":101}", json); + + // Change reference value so it's no longer a default value. + obj.Value = new MyValueTypeWithProperties { RefValue = 1 }; + Assert.False(EqualityComparer.Default.Equals(default, obj.Value)); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{\"Value\":100}", json); + } + + [Fact] + public async Task JsonIgnoreCondition_ConverterCalledOnDeserialize() + { + // Verify converter is called. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}")); + + var options = new JsonSerializerOptions + { + IgnoreNullValues = true + }; + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", options)); + } + + [Fact] +#if BUILDING_SOURCE_GENERATOR_TESTS + // Needs bug fixes to custom converter handling. + [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] +#endif + public async Task JsonIgnoreCondition_WhenWritingNull_OnValueTypeWithCustomConverter() + { + string json; + var obj = new MyClassWithValueType(); + + // Baseline without custom options. + Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("{\"Value\":100}", json); + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + // Verify not ignored; MyValueTypeWithProperties is not null. + Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{\"Value\":100}", json); + } + + [Fact] + public async Task JsonIgnoreCondition_WhenWritingDefault_OnRootTypes() + { + string json; + int i = 0; + object obj = null; + + // Baseline without custom options. + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("null", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(i); + Assert.Equal("0", json); + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + // We don't ignore when applied to root types; only properties. + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("null", json); + + json = await JsonSerializerWrapperForString.SerializeWrapper(i, options); + Assert.Equal("0", json); + } + + public struct MyValueTypeWithBoxedPrimitive + { + public object BoxedPrimitive { get; set; } + } + + [Fact] + public async Task JsonIgnoreCondition_WhenWritingDefault_OnBoxedPrimitive() + { + string json; + + MyValueTypeWithBoxedPrimitive obj = new MyValueTypeWithBoxedPrimitive { BoxedPrimitive = 0 }; + + // Baseline without custom options. + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("{\"BoxedPrimitive\":0}", json); + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + // No check if the boxed object's value type is a default value (0 in this case). + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{\"BoxedPrimitive\":0}", json); + + obj = new MyValueTypeWithBoxedPrimitive(); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); + Assert.Equal("{}", json); + } + + public class MyClassWithValueTypeInterfaceProperty + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IInterface MyProp { get; set; } + + public interface IInterface { } + public struct MyStruct : IInterface { } + } + + [Fact] + public async Task JsonIgnoreCondition_WhenWritingDefault_OnInterface() + { + // MyProp should be ignored due to [JsonIgnore]. + var obj = new MyClassWithValueTypeInterfaceProperty(); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("{}", json); + + // No check if the interface property's value type is a default value. + obj = new MyClassWithValueTypeInterfaceProperty { MyProp = new MyClassWithValueTypeInterfaceProperty.MyStruct() }; + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Equal("{\"MyProp\":{}}", json); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs new file mode 100644 index 00000000000..c7198b1e306 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Tests +{ + public abstract class SerializerTests + { + protected JsonSerializerWrapperForString JsonSerializerWrapperForString { get; } + + protected SerializerTests(JsonSerializerWrapperForString serializerWrapper) => JsonSerializerWrapperForString = serializerWrapper; + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ConcurrentCollections.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ConcurrentCollections.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ConcurrentCollections.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ConcurrentCollections.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.Constructor.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.Constructor.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.GenericCollections.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.GenericCollections.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.GenericCollections.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.GenericCollections.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ImmutableCollections.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ImmutableCollections.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ImmutableCollections.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ImmutableCollections.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.NonGenericCollections.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.NonGenericCollections.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.NonGenericCollections.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.NonGenericCollections.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.Polymorphic.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Polymorphic.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.Polymorphic.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Polymorphic.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClass.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClass.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClass.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClass.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithFields.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithFields.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithFields.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithFields.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithNullables.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithNullables.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithNullables.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithNullables.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithObject.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithObject.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithObject.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithObject.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithObjectArrays.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithObjectArrays.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithObjectArrays.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithObjectArrays.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithSimpleObject.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithSimpleObject.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestClassWithSimpleObject.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestClassWithSimpleObject.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestStruct.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestStruct.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestStruct.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestStruct.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestStructWithFields.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestStructWithFields.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.SimpleTestStructWithFields.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.SimpleTestStructWithFields.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ValueTypedMember.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ValueTypedMember.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.ValueTypedMember.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.ValueTypedMember.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/TestClasses/TestClasses.cs rename to src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs new file mode 100644 index 00000000000..dfb968371a2 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Text.Json.Serialization.Tests; +using System.Threading.Tasks; + +namespace System.Text.Json.SourceGeneration.Tests +{ + internal sealed class JsonSerializerWrapperForString_SourceGen : JsonSerializerWrapperForString + { + private readonly JsonSerializerContext _defaultContext; + private readonly Func _customContextCreator; + + public JsonSerializerWrapperForString_SourceGen(JsonSerializerContext defaultContext, Func customContextCreator) + { + _defaultContext = defaultContext ?? throw new ArgumentNullException(nameof(defaultContext)); + _customContextCreator = customContextCreator ?? throw new ArgumentNullException(nameof(defaultContext)); + } + + protected internal override Task SerializeWrapper(object value, Type type, JsonSerializerOptions? options = null) + { + if (options != null) + { + return Task.FromResult(Serialize(value, type, options)); + } + + return Task.FromResult(JsonSerializer.Serialize(value, type, _defaultContext)); + } + + private string Serialize(object value, Type type, JsonSerializerOptions options) + { + JsonSerializerContext context = _customContextCreator(new JsonSerializerOptions(options)); + return JsonSerializer.Serialize(value, type, context); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions? options = null) + { + if (options != null) + { + return Task.FromResult(Serialize(value, options)); + } + + JsonTypeInfo typeInfo = (JsonTypeInfo)_defaultContext.GetTypeInfo(typeof(T)); + return Task.FromResult(JsonSerializer.Serialize(value, typeInfo)); + } + + private string Serialize(T value, JsonSerializerOptions options) + { + JsonSerializerContext context = _customContextCreator(new JsonSerializerOptions(options)); + JsonTypeInfo typeInfo = (JsonTypeInfo)context.GetTypeInfo(typeof(T)); + return JsonSerializer.Serialize(value, typeInfo); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + => throw new NotImplementedException(); + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + => throw new NotImplementedException(); + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions? options = null) + { + if (options != null) + { + return Task.FromResult(Deserialize(json, options)); + } + + JsonTypeInfo typeInfo = (JsonTypeInfo)_defaultContext.GetTypeInfo(typeof(T)); + return Task.FromResult(JsonSerializer.Deserialize(json, typeInfo)); + } + + private T Deserialize(string json, JsonSerializerOptions options) + { + JsonSerializerContext context = _customContextCreator(new JsonSerializerOptions(options)); + JsonTypeInfo typeInfo = (JsonTypeInfo)context.GetTypeInfo(typeof(T)); + return JsonSerializer.Deserialize(json, typeInfo); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions? options = null) + { + if (options != null) + { + return Task.FromResult(Deserialize(json, type, options)); + } + + return Task.FromResult(JsonSerializer.Deserialize(json, type, _defaultContext)); + } + + private object Deserialize(string json, Type type, JsonSerializerOptions options) + { + JsonSerializerContext context = _customContextCreator(new JsonSerializerOptions(options)); + return JsonSerializer.Deserialize(json, type, context); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + => throw new NotImplementedException(); + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs new file mode 100644 index 00000000000..6ebb90f030c --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs @@ -0,0 +1,424 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Tests; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.SourceGeneration.Tests +{ + public partial class PropertyVisibilityTests_Metadata : PropertyVisibilityTests + { + public PropertyVisibilityTests_Metadata() + : this(new JsonSerializerWrapperForString_SourceGen(PropertyVisibilityTestsContext_Metadata.Default, (options) => new PropertyVisibilityTestsContext_Metadata(options))) + { + } + + protected PropertyVisibilityTests_Metadata(JsonSerializerWrapperForString serializerWrapper) + : base(serializerWrapper) + { + } + + [Theory] + [InlineData(typeof(ClassWithBadIgnoreAttribute))] + [InlineData(typeof(StructWithBadIgnoreAttribute))] + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + { + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("", type)); + + InvalidOperationException ioe = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); + string exAsStr = ioe.ToString(); + Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); + Assert.Contains("MyBadMember", exAsStr); + Assert.Contains(type.ToString(), exAsStr); + Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); + } + + [Fact] + public override async Task Honor_JsonSerializablePropertyAttribute_OnProperties() + { + string json = @"{ + ""MyInt"":1, + ""MyString"":""Hello"", + ""MyFloat"":2, + ""MyUri"":""https://microsoft.com"" + }"; + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(0, obj.MyInt); // Source gen can't use private setter + Assert.Equal("Hello", obj.MyString); + Assert.Equal(2f, obj.GetMyFloat); + Assert.Equal(new Uri("https://microsoft.com"), obj.MyUri); + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""MyInt"":0", json); + Assert.Contains(@"""MyString"":""Hello""", json); + Assert.DoesNotContain(@"""MyFloat"":", json); // Source gen can't use private setter + Assert.Contains(@"""MyUri"":""https://microsoft.com""", json); + } + + [Theory] + [InlineData(typeof(ClassWithInitOnlyProperty))] + [InlineData(typeof(StructWithInitOnlyProperty))] + public override async Task InitOnlyProperties(Type type) + { + // Init-only setters cannot be referenced as get/set helpers in generated code. + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type); + Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj)); + + // Init-only properties can be serialized. + Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type)); + } + + [Theory] + [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] + [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] + [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] + public override async Task NonPublicInitOnlySetter_With_JsonInclude(Type type) + { + // Init-only setters cannot be referenced as get/set helpers in generated code. + object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type); + Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj)); + + // Init-only properties can be serialized. + Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type)); + } + + [Fact] + public override async Task HonorCustomConverter_UsingPrivateSetter() + { + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + + string json = @"{""MyEnum"":""AnotherValue"",""MyInt"":2}"; + + // Deserialization baseline, without enum converter, we get JsonException. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json, options); + Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum); + Assert.Equal(0, obj.MyInt); // Private setter can't be used with source-gen. + + // ConverterForInt32 throws this exception. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options)); + } + + [Fact] + public override async Task Public_And_NonPublicPropertyAccessors_PropertyAttributes() + { + string json = @"{""W"":1,""X"":2,""Y"":3,""Z"":4}"; + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(1, obj.W); + Assert.Equal(2, obj.X); + Assert.Equal(3, obj.Y); + Assert.Equal(4, obj.GetZ); + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.Contains(@"""W"":1", json); + Assert.Contains(@"""X"":2", json); + Assert.Contains(@"""Y"":3", json); + Assert.DoesNotContain(@"""Z"":", json); // Private setter cannot be used with source gen. + } + + [Fact] + public override async Task HonorJsonPropertyName() + { + string json = @"{""prop1"":1,""prop2"":2}"; + + var obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); + Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum); + Assert.Equal(0, obj.MyInt); // Private setter cannot be used with source gen. + + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + Assert.DoesNotContain(@"""prop1"":", json); // Private getter cannot be used with source gen. + Assert.Contains(@"""prop2"":0", json); + } + + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(ClassWithNewSlotField))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(object))] + [JsonSerializable(typeof(ClassWithInternalField))] + [JsonSerializable(typeof(ClassWithNewSlotDecimalField))] + [JsonSerializable(typeof(ClassWithNewSlotAttributedDecimalField))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyPolicyConflictPrivate))] + [JsonSerializable(typeof(ClassWithMissingCollectionProperty))] + [JsonSerializable(typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithNoSetter))] + [JsonSerializable(typeof(ClassWithInternalProperty))] + [JsonSerializable(typeof(ClassWithPropertyNamingConflict))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithMissingObjectProperty))] + [JsonSerializable(typeof(ClassWithInitOnlyProperty))] + [JsonSerializable(typeof(StructWithInitOnlyProperty))] + [JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))] + [JsonSerializable(typeof(ClassWithNonPublicProperties))] + [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways))] + [JsonSerializable(typeof(ClassWithBadIgnoreAttribute))] + [JsonSerializable(typeof(StructWithBadIgnoreAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] + [JsonSerializable(typeof(ClassWithIgnoredPublicPropertyAndNewSlotPrivate))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyPolicyConflictPublic))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyNamingConflictPrivate))] + [JsonSerializable(typeof(ClassWithIgnoredNewSlotProperty))] + [JsonSerializable(typeof(ClassWithPublicGetterAndPrivateSetter))] + [JsonSerializable(typeof(ClassWithInitializedProps))] + [JsonSerializable(typeof(ClassWithNewSlotInternalProperty))] + [JsonSerializable(typeof(ClassWithPropertyPolicyConflict))] + [JsonSerializable(typeof(ClassWithPrivateSetterAndGetter))] + [JsonSerializable(typeof(ClassWithIgnoreAttributeProperty))] + [JsonSerializable(typeof(ClassWithIgnoredNewSlotField))] + [JsonSerializable(typeof(MyStruct_WithNonPublicAccessors_WithTypeAttribute))] + [JsonSerializable(typeof(ClassWithReadOnlyFields))] + [JsonSerializable(typeof(MyValueTypeWithBoxedPrimitive))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(ClassWithNoGetter))] + [JsonSerializable(typeof(ClassWithPropsAndIgnoreAttributes))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(MyValueTypeWithProperties))] + [JsonSerializable(typeof(ClassInheritedWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassInheritedWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithOverrideReversed))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreNever))] + [JsonSerializable(typeof(ClassWithProps))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionNever))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionNever_Ctor))] + [JsonSerializable(typeof(ClassWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithBadIgnoreAttribute))] + [JsonSerializable(typeof(StructWithBadIgnoreAttribute))] + [JsonSerializable(typeof(ClassWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField_IgnoreWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField_IgnoreNever))] + [JsonSerializable(typeof(ClassInheritedWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithPrivateProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternalProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtectedProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPrivateField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternalField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtectedField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPublicProperty))] + [JsonSerializable(typeof(ClassInheritedWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(StructWithOverride))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(MyClass_WithNonPublicAccessors_WithPropertyAttributes))] + [JsonSerializable(typeof(Class_PropertyWith_PrivateInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(DerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty))] + [JsonSerializable(typeof(DerivedClass_WithConflictingNewMember))] + [JsonSerializable(typeof(DerivedClass_WithConflictingNewMember_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_ConflictingNewMember))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_NewProperty_And_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName))] + [JsonSerializable(typeof(FurtherDerivedClass_With_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_WithConflictingPropertyName))] + [JsonSerializable(typeof(FurtherDerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyNamingConflictPublic))] + [JsonSerializable(typeof(MyClassWithValueType))] + [JsonSerializable(typeof(StructWithPropertiesWithConverter))] + [JsonSerializable(typeof(ClassWithNewSlotProperty))] + [JsonSerializable(typeof(ClassWithNewSlotAttributedDecimalProperty))] + [JsonSerializable(typeof(ClassWithNewSlotDecimalProperty))] + [JsonSerializable(typeof(LargeStructWithValueAndReferenceTypes))] + [JsonSerializable(typeof(ClassWithUnsupportedBigInteger))] + [JsonSerializable(typeof(WrapperForClassWithUnsupportedBigInteger))] + [JsonSerializable(typeof(ClassWithIgnoredUnsupportedBigInteger))] + [JsonSerializable(typeof(WrapperForClassWithIgnoredUnsupportedBigInteger))] + [JsonSerializable(typeof(ClassWithThingsToIgnore))] + [JsonSerializable(typeof(ClassWithMixedPropertyAccessors_PropertyAttributes))] + [JsonSerializable(typeof(ClassWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(MyClass_WithNonPublicAccessors))] + [JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))] + [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))] + [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))] + internal sealed partial class PropertyVisibilityTestsContext_Metadata : JsonSerializerContext + { + } + } + + public partial class PropertyVisibilityTests_Default : PropertyVisibilityTests_Metadata + //public partial class PropertyVisibilityTests_Default : PropertyVisibilityTests + { + public PropertyVisibilityTests_Default() + : base(new JsonSerializerWrapperForString_SourceGen(PropertyVisibilityTestsContext_Default.Default, (options) => new PropertyVisibilityTestsContext_Default(options))) + { + } + + [Theory] + [InlineData(typeof(ClassWithPrivateProperty_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithInternalProperty_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithProtectedProperty_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithPrivateField_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithInternalField_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithProtectedField_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty))] + [InlineData(typeof(ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty))] + public override async Task NonPublicProperty_WithJsonInclude_Invalid(Type type) + { + // Exception messages direct users to use JsonSourceGenerationMode.Metadata to see a more detailed error. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", type)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); + } + + [Theory] + [InlineData(typeof(ClassWithBadIgnoreAttribute))] + [InlineData(typeof(StructWithBadIgnoreAttribute))] + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(Type type) + { + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", type)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); + } + + [Theory] + [InlineData(typeof(ClassWithBadIgnoreAttribute))] + [InlineData(typeof(StructWithBadIgnoreAttribute))] + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + { + // Since this code goes down fast-path, there's no warm up and we hit the reader exception about having no tokens. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("", type)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(Activator.CreateInstance(type), type)); + } + + [JsonSerializable(typeof(ClassWithNewSlotField))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(object))] + [JsonSerializable(typeof(ClassWithInternalField))] + [JsonSerializable(typeof(ClassWithNewSlotDecimalField))] + [JsonSerializable(typeof(ClassWithNewSlotAttributedDecimalField))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyPolicyConflictPrivate))] + [JsonSerializable(typeof(ClassWithMissingCollectionProperty))] + [JsonSerializable(typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithNoSetter))] + [JsonSerializable(typeof(ClassWithInternalProperty))] + [JsonSerializable(typeof(ClassWithPropertyNamingConflict))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithMissingObjectProperty))] + [JsonSerializable(typeof(ClassWithInitOnlyProperty))] + [JsonSerializable(typeof(StructWithInitOnlyProperty))] + [JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))] + [JsonSerializable(typeof(ClassWithNonPublicProperties))] + [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways))] + [JsonSerializable(typeof(ClassWithBadIgnoreAttribute))] + [JsonSerializable(typeof(StructWithBadIgnoreAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] + [JsonSerializable(typeof(ClassWithIgnoredPublicPropertyAndNewSlotPrivate))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyPolicyConflictPublic))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyNamingConflictPrivate))] + [JsonSerializable(typeof(ClassWithIgnoredNewSlotProperty))] + [JsonSerializable(typeof(ClassWithPublicGetterAndPrivateSetter))] + [JsonSerializable(typeof(ClassWithInitializedProps))] + [JsonSerializable(typeof(ClassWithNewSlotInternalProperty))] + [JsonSerializable(typeof(ClassWithPropertyPolicyConflict))] + [JsonSerializable(typeof(ClassWithPrivateSetterAndGetter))] + [JsonSerializable(typeof(ClassWithIgnoreAttributeProperty))] + [JsonSerializable(typeof(ClassWithIgnoredNewSlotField))] + [JsonSerializable(typeof(MyStruct_WithNonPublicAccessors_WithTypeAttribute))] + [JsonSerializable(typeof(ClassWithReadOnlyFields))] + [JsonSerializable(typeof(MyValueTypeWithBoxedPrimitive))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(ClassWithNoGetter))] + [JsonSerializable(typeof(ClassWithPropsAndIgnoreAttributes))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(MyValueTypeWithProperties))] + [JsonSerializable(typeof(ClassInheritedWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassInheritedWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithOverrideReversed))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreNever))] + [JsonSerializable(typeof(ClassWithProps))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionNever))] + [JsonSerializable(typeof(ClassWithStructProperty_IgnoreConditionNever_Ctor))] + [JsonSerializable(typeof(ClassWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithBadIgnoreAttribute))] + [JsonSerializable(typeof(StructWithBadIgnoreAttribute))] + [JsonSerializable(typeof(ClassWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField_IgnoreWhenWritingDefault))] + [JsonSerializable(typeof(ClassWithReadOnlyStringField_IgnoreNever))] + [JsonSerializable(typeof(ClassInheritedWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows))] + [JsonSerializable(typeof(ClassWithPrivateProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternalProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtectedProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPrivateField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternalField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtectedField_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty))] + [JsonSerializable(typeof(ClassWithPublicProperty))] + [JsonSerializable(typeof(ClassInheritedWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(StructWithOverride))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyNamingConflictWhichThrows))] + [JsonSerializable(typeof(MyClass_WithNonPublicAccessors_WithPropertyAttributes))] + [JsonSerializable(typeof(Class_PropertyWith_PrivateInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] + [JsonSerializable(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] + [JsonSerializable(typeof(DerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty))] + [JsonSerializable(typeof(DerivedClass_WithConflictingNewMember))] + [JsonSerializable(typeof(DerivedClass_WithConflictingNewMember_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_ConflictingNewMember))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_NewProperty_And_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty_Of_DifferentType))] + [JsonSerializable(typeof(DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName))] + [JsonSerializable(typeof(FurtherDerivedClass_With_ConflictingPropertyName))] + [JsonSerializable(typeof(DerivedClass_WithConflictingPropertyName))] + [JsonSerializable(typeof(FurtherDerivedClass_With_IgnoredOverride))] + [JsonSerializable(typeof(ClassWithIgnoredPropertyNamingConflictPublic))] + [JsonSerializable(typeof(MyClassWithValueType))] + [JsonSerializable(typeof(StructWithPropertiesWithConverter))] + [JsonSerializable(typeof(ClassWithNewSlotProperty))] + [JsonSerializable(typeof(ClassWithNewSlotAttributedDecimalProperty))] + [JsonSerializable(typeof(ClassWithNewSlotDecimalProperty))] + [JsonSerializable(typeof(LargeStructWithValueAndReferenceTypes))] + [JsonSerializable(typeof(ClassWithUnsupportedBigInteger))] + [JsonSerializable(typeof(WrapperForClassWithUnsupportedBigInteger))] + [JsonSerializable(typeof(ClassWithIgnoredUnsupportedBigInteger))] + [JsonSerializable(typeof(WrapperForClassWithIgnoredUnsupportedBigInteger))] + [JsonSerializable(typeof(ClassWithThingsToIgnore))] + [JsonSerializable(typeof(ClassWithMixedPropertyAccessors_PropertyAttributes))] + [JsonSerializable(typeof(ClassWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))] + [JsonSerializable(typeof(MyClass_WithNonPublicAccessors))] + [JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))] + [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))] + [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))] + [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))] + internal sealed partial class PropertyVisibilityTestsContext_Default : JsonSerializerContext + { + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj index a88c018a1e7..2520aa52a8b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj @@ -2,6 +2,12 @@ $(NetCoreAppCurrent);$(NetFrameworkCurrent) true + + $(NoWarn);SYSLIB0020 + + + + $(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS @@ -9,15 +15,38 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs index 654b4131020..d654c01bd67 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Reflection; using Microsoft.CodeAnalysis; using Xunit; @@ -395,8 +396,10 @@ namespace System.Text.Json.Serialization private void CheckFieldsPropertiesMethods(Type type, string[] expectedFields, string[] expectedProperties, string[] expectedMethods) { - string[] receivedFields = type.GetFields().Select(field => field.Name).OrderBy(s => s).ToArray(); - string[] receivedProperties = type.GetProperties().Select(property => property.Name).OrderBy(s => s).ToArray(); + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + + string[] receivedFields = type.GetFields(bindingFlags).Select(field => field.Name).OrderBy(s => s).ToArray(); + string[] receivedProperties = type.GetProperties(bindingFlags).Select(property => property.Name).OrderBy(s => s).ToArray(); string[] receivedMethods = type.GetMethods().Select(method => method.Name).OrderBy(s => s).ToArray(); Assert.Equal(expectedFields, receivedFields); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs index afcd88a5867..24a86137bc1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs @@ -158,8 +158,10 @@ namespace System.Text.Json.SourceGeneration.UnitTests receivedMethodsWithAttributeNames ); + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + // Check for FieldInfoWrapper attribute usage. - (string, string[])[] receivedFieldsWithAttributeNames = foundType.GetFields().Select(field => (field.Name, field.GetCustomAttributesData().Cast().Select(attributeData => attributeData.AttributeType.Name).ToArray())).Where(x => x.Item2.Any()).ToArray(); + (string, string[])[] receivedFieldsWithAttributeNames = foundType.GetFields(bindingFlags).Select(field => (field.Name, field.GetCustomAttributesData().Cast().Select(attributeData => attributeData.AttributeType.Name).ToArray())).Where(x => x.Item2.Any()).ToArray(); Assert.Equal( new (string, string[])[] { ("PublicDouble", new string[] { "JsonIncludeAttribute" }), @@ -169,7 +171,7 @@ namespace System.Text.Json.SourceGeneration.UnitTests ); // Check for PropertyInfoWrapper attribute usage. - (string, string[])[] receivedPropertyWithAttributeNames = foundType.GetProperties().Select(property => (property.Name, property.GetCustomAttributesData().Cast().Select(attributeData => attributeData.AttributeType.Name).ToArray())).Where(x => x.Item2.Any()).ToArray(); + (string, string[])[] receivedPropertyWithAttributeNames = foundType.GetProperties(bindingFlags).Select(property => (property.Name, property.GetCustomAttributesData().Cast().Select(attributeData => attributeData.AttributeType.Name).ToArray())).Where(x => x.Item2.Any()).ToArray(); Assert.Equal( new (string, string[])[] { ("PublicPropertyInt", new string[] { "JsonPropertyNameAttribute" }), diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs index b01738c7a96..6b691004df9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs @@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Tests /// /// Base class for wrapping string-based JsonSerializer methods which allows tests to run under different configurations. /// - public abstract class JsonSerializerWrapperForString + public abstract partial class JsonSerializerWrapperForString { private static readonly JsonSerializerOptions _optionsWithSmallBuffer = new JsonSerializerOptions { DefaultBufferSize = 1 }; @@ -22,22 +22,6 @@ namespace System.Text.Json.Serialization.Tests public static JsonSerializerWrapperForString SyncStreamSerializer => new SyncStreamSerializerWrapper(); public static JsonSerializerWrapperForString ReaderWriterSerializer => new ReaderWriterSerializerWrapper(); - protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null); - - protected internal abstract Task SerializeWrapper(T value, JsonSerializerOptions options = null); - - protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context); - - protected internal abstract Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo); - - protected internal abstract Task DeserializeWrapper(string json, JsonSerializerOptions options = null); - - protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null); - - protected internal abstract Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo); - - protected internal abstract Task DeserializeWrapper(string json, Type type, JsonSerializerContext context); - private class SpanSerializerWrapper : JsonSerializerWrapperForString { protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs new file mode 100644 index 00000000000..aaee9f03d81 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace System.Text.Json.Serialization.Tests +{ + internal sealed class JsonSerializerWrapperForString_Dynamic + : JsonSerializerWrapperForString + { + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + => Task.FromResult(JsonSerializer.Deserialize(json, options)); + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + => Task.FromResult(JsonSerializer.Deserialize(json, type, options)); + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) => throw new NotImplementedException(); + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + => Task.FromResult(JsonSerializer.Serialize(value, inputType, options)); + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + => Task.FromResult(JsonSerializer.Serialize(value, options)); + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) => throw new NotImplementedException(); + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs index 39899a5b91e..c6135602978 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs @@ -47,6 +47,8 @@ namespace System.Text.Json.Tests.Serialization properties[0] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(HighLowTemps), propertyTypeInfo: jsonContext.Int32, converter: null, @@ -54,12 +56,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((HighLowTemps)obj).High = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.HighLowTemps.High), jsonPropertyName: null); properties[1] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(HighLowTemps), propertyTypeInfo: jsonContext.Int32, converter: null, @@ -67,6 +72,7 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((HighLowTemps)obj).Low = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.HighLowTemps.Low), jsonPropertyName: null); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs index f75200bf8b5..7d3f8445100 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs @@ -47,6 +47,8 @@ namespace System.Text.Json.Tests.Serialization properties[0] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.DateTimeOffset, converter: null, @@ -54,12 +56,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).Date = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.Date), jsonPropertyName: null); properties[1] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.Int32, converter: null, @@ -67,12 +72,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).TemperatureCelsius = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.TemperatureCelsius), jsonPropertyName: null); properties[2] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.String, converter: null, @@ -80,12 +88,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).Summary = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.Summary), jsonPropertyName: null); properties[3] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.ListSystemDateTimeOffset, converter: null, @@ -93,12 +104,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).DatesAvailable = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.DatesAvailable), jsonPropertyName: null); properties[4] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.Dictionary, converter: null, @@ -106,12 +120,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).TemperatureRanges = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.TemperatureRanges), jsonPropertyName: null); properties[5] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: true, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.StringArray, converter: null, @@ -119,12 +136,15 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).SummaryWords = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.SummaryWords), jsonPropertyName: null); properties[6] = JsonMetadataServices.CreatePropertyInfo( options, isProperty: false, + isPublic: true, + isVirtual: false, declaringType: typeof(WeatherForecastWithPOCOs), propertyTypeInfo: jsonContext.String, converter: null, @@ -132,6 +152,7 @@ namespace System.Text.Json.Tests.Serialization setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).SummaryField = value; }, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: nameof(Serialization.WeatherForecastWithPOCOs.SummaryField), jsonPropertyName: null); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs index 227aab7bf3a..a583b3a121b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs @@ -21,6 +21,8 @@ namespace System.Text.Json.Tests.Serialization ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( options: null, isProperty: true, + isPublic: false, + isVirtual: false, declaringType: typeof(Point), propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), converter: null, @@ -28,6 +30,7 @@ namespace System.Text.Json.Tests.Serialization setter: null, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: "MyInt", jsonPropertyName: null)); Assert.Contains("options", ane.ToString()); @@ -36,6 +39,8 @@ namespace System.Text.Json.Tests.Serialization ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( options: options, isProperty: true, + isPublic: false, + isVirtual: false, declaringType: null, propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), converter: null, @@ -43,6 +48,7 @@ namespace System.Text.Json.Tests.Serialization setter: null, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: "MyInt", jsonPropertyName: null)); Assert.Contains("declaringType", ane.ToString()); @@ -51,6 +57,8 @@ namespace System.Text.Json.Tests.Serialization ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( options: options, isProperty: true, + isPublic: false, + isVirtual: false, declaringType: typeof(Point), propertyTypeInfo: null, converter: null, @@ -58,6 +66,7 @@ namespace System.Text.Json.Tests.Serialization setter: null, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: "MyInt", jsonPropertyName: null)); Assert.Contains("propertyTypeInfo", ane.ToString()); @@ -66,6 +75,8 @@ namespace System.Text.Json.Tests.Serialization ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( options: options, isProperty: true, + isPublic: false, + isVirtual: false, declaringType: typeof(Point), propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), converter: null, @@ -73,6 +84,7 @@ namespace System.Text.Json.Tests.Serialization setter: null, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: null, jsonPropertyName: null)); Assert.Contains("propertyName", ane.ToString()); @@ -81,6 +93,8 @@ namespace System.Text.Json.Tests.Serialization InvalidOperationException ioe = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( options: options, isProperty: true, + isPublic: false, + isVirtual: false, declaringType: typeof(Point), // Converter invalid because you'd need to create with JsonMetadataServices.CreatePropertyInfo instead. propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, new DerivedClassConverter()), @@ -89,12 +103,31 @@ namespace System.Text.Json.Tests.Serialization setter: null, ignoreCondition: default, numberHandling: default, + hasJsonInclude: false, propertyName: "MyProp", jsonPropertyName: null)); string ioeAsStr = ioe.ToString(); Assert.Contains("Point.MyProp", ioeAsStr); Assert.Contains("MyClass", ioeAsStr); + // Fields cannot be virtual. + ioe = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( + options: options, + isProperty: false, + isPublic: false, + isVirtual: true, + declaringType: typeof(Point), + propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), + converter: null, + getter: null, + setter: null, + ignoreCondition: default, + numberHandling: default, + hasJsonInclude: false, + propertyName: "X", + jsonPropertyName: null)); + Assert.Contains("field", ioe.ToString()); + // Source generator tests verify that generated metadata is actually valid. } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs index b4db06777a3..5e02a3b5d6b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Xunit; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs index f164592f4fb..85a36e92b9b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs @@ -1,2555 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Collections.Concurrent; -using System.Numerics; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class PropertyVisibilityTests + public sealed partial class PropertyVisibilityTestsDynamic : PropertyVisibilityTests { - [Fact] - public static void Serialize_NewSlotPublicField() - { - // Serialize - var obj = new ClassWithNewSlotField(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyString"":""NewDefaultValue""}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewValue", ((ClassWithNewSlotField)obj).MyString); - Assert.Equal("DefaultValue", ((ClassWithInternalField)obj).MyString); - } - - [Fact] - public static void Serialize_NewSlotPublicProperty() - { - // Serialize - var obj = new ClassWithNewSlotProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyString"":""NewDefaultValue""}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewValue", ((ClassWithNewSlotProperty)obj).MyString); - Assert.Equal("DefaultValue", ((ClassWithInternalProperty)obj).MyString); - } - - [Fact] - public static void Serialize_BasePublicProperty_ConflictWithDerivedPrivate() - { - // Serialize - var obj = new ClassWithNewSlotInternalProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyString"":""DefaultValue""}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewValue", ((ClassWithPublicProperty)obj).MyString); - Assert.Equal("NewDefaultValue", ((ClassWithNewSlotInternalProperty)obj).MyString); - } - - [Fact] - public static void Serialize_PublicProperty_ConflictWithPrivateDueAttributes() - { - // Serialize - var obj = new ClassWithPropertyNamingConflict(); - - // Newtonsoft.Json throws JsonSerializationException here because - // non-public properties are included when [JsonProperty] is placed on them. - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyString"":""DefaultValue""}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - - // Newtonsoft.Json throws JsonSerializationException here because - // non-public properties are included when [JsonProperty] is placed on them. - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewValue", obj.MyString); - Assert.Equal("ConflictingValue", obj.ConflictingString); - } - - [Fact] - public static void Serialize_PublicProperty_ConflictWithPrivateDuePolicy() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassWithPropertyPolicyConflict(); - string json = JsonSerializer.Serialize(obj, options); - - Assert.Equal(@"{""myString"":""DefaultValue""}", json); - - // Deserialize - json = @"{""myString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json, options); - - Assert.Equal("NewValue", obj.MyString); - Assert.Equal("ConflictingValue", obj.myString); - } - - [Fact] - public static void Serialize_NewSlotPublicProperty_ConflictWithBasePublicProperty() - { - // Serialize - var obj = new ClassWithNewSlotDecimalProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyNumeric"":1.5}", json); - - // Deserialize - json = @"{""MyNumeric"":2.5}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal(2.5M, obj.MyNumeric); - } - - [Fact] - public static void Serialize_NewSlotPublicField_ConflictWithBasePublicProperty() - { - // Serialize - var obj = new ClassWithNewSlotDecimalField(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyNumeric"":1.5}", json); - - // Deserialize - json = @"{""MyNumeric"":2.5}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal(2.5M, obj.MyNumeric); - } - - [Fact] - public static void Serialize_NewSlotPublicField_SpecifiedJsonPropertyName() - { - // Serialize - var obj = new ClassWithNewSlotAttributedDecimalField(); - string json = JsonSerializer.Serialize(obj); - - Assert.Contains(@"""MyNewNumeric"":1.5", json); - Assert.Contains(@"""MyNumeric"":1", json); - - // Deserialize - json = @"{""MyNewNumeric"":2.5,""MyNumeric"":4}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal(4, ((ClassWithHiddenByNewSlotIntProperty)obj).MyNumeric); - Assert.Equal(2.5M, ((ClassWithNewSlotAttributedDecimalField)obj).MyNumeric); - } - - [Fact] - public static void Serialize_NewSlotPublicProperty_SpecifiedJsonPropertyName() - { - // Serialize - var obj = new ClassWithNewSlotAttributedDecimalProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Contains(@"""MyNewNumeric"":1.5", json); - Assert.Contains(@"""MyNumeric"":1", json); - - // Deserialize - json = @"{""MyNewNumeric"":2.5,""MyNumeric"":4}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal(4, ((ClassWithHiddenByNewSlotIntProperty)obj).MyNumeric); - Assert.Equal(2.5M, ((ClassWithNewSlotAttributedDecimalProperty)obj).MyNumeric); - } - - [Fact] - public static void Ignore_NonPublicProperty() - { - // Serialize - var obj = new ClassWithInternalProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("DefaultValue", obj.MyString); - } - - [Fact] - public static void Ignore_NewSlotPublicFieldIgnored() - { - // Serialize - var obj = new ClassWithIgnoredNewSlotField(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewDefaultValue", ((ClassWithIgnoredNewSlotField)obj).MyString); - Assert.Equal("DefaultValue", ((ClassWithInternalField)obj).MyString); - } - - [Fact] - public static void Ignore_NewSlotPublicPropertyIgnored() - { - // Serialize - var obj = new ClassWithIgnoredNewSlotProperty(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("NewDefaultValue", ((ClassWithIgnoredNewSlotProperty)obj).MyString); - Assert.Equal("DefaultValue", ((ClassWithInternalProperty)obj).MyString); - } - - [Fact] - public static void Ignore_BasePublicPropertyIgnored_ConflictWithDerivedPrivate() - { - // Serialize - var obj = new ClassWithIgnoredPublicPropertyAndNewSlotPrivate(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("DefaultValue", ((ClassWithIgnoredPublicProperty)obj).MyString); - Assert.Equal("NewDefaultValue", ((ClassWithIgnoredPublicPropertyAndNewSlotPrivate)obj).MyString); - } - - [Fact] - public static void Ignore_PublicProperty_ConflictWithPrivateDueAttributes() - { - // Serialize - var obj = new ClassWithIgnoredPropertyNamingConflictPrivate(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{}", json); - - // Newtonsoft.Json has the following output because non-public properties are included when [JsonProperty] is placed on them. - // {"MyString":"ConflictingValue"} - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("DefaultValue", obj.MyString); - Assert.Equal("ConflictingValue", obj.ConflictingString); - - // The output for Newtonsoft.Json is: - // obj.ConflictingString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Ignore_PublicProperty_ConflictWithPrivateDuePolicy() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassWithIgnoredPropertyPolicyConflictPrivate(); - string json = JsonSerializer.Serialize(obj, options); - - Assert.Equal(@"{}", json); - - // Deserialize - json = @"{""myString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json, options); - - Assert.Equal("DefaultValue", obj.MyString); - Assert.Equal("ConflictingValue", obj.myString); - } - - [Fact] - public static void Ignore_PublicProperty_ConflictWithPublicDueAttributes() - { - // Serialize - var obj = new ClassWithIgnoredPropertyNamingConflictPublic(); - string json = JsonSerializer.Serialize(obj); - - Assert.Equal(@"{""MyString"":""ConflictingValue""}", json); - - // Deserialize - json = @"{""MyString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json); - - Assert.Equal("DefaultValue", obj.MyString); - Assert.Equal("NewValue", obj.ConflictingString); - } - - [Fact] - public static void Ignore_PublicProperty_ConflictWithPublicDuePolicy() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassWithIgnoredPropertyPolicyConflictPublic(); - string json = JsonSerializer.Serialize(obj, options); - - Assert.Equal(@"{""myString"":""ConflictingValue""}", json); - - // Deserialize - json = @"{""myString"":""NewValue""}"; - obj = JsonSerializer.Deserialize(json, options); - - Assert.Equal("DefaultValue", obj.MyString); - Assert.Equal("NewValue", obj.myString); - } - - [Fact] - public static void Throw_PublicProperty_ConflictDueAttributes() - { - // Serialize - var obj = new ClassWithPropertyNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDueAttributes() - { - // Serialize - var obj = new ClassWithPropertyFieldNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - } - - [Fact] - public static void Throw_PublicProperty_ConflictDueAttributes_SingleInheritance() - { - // Serialize - var obj = new ClassInheritedWithPropertyNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // The output for Newtonsoft.Json is: - // {"MyString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - - // The output for Newtonsoft.Json is: - // obj.ConflictingString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDueAttributes_SingleInheritance() - { - // Serialize - var obj = new ClassInheritedWithPropertyFieldNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // The output for Newtonsoft.Json is: - // {"MyString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - - // The output for Newtonsoft.Json is: - // obj.ConflictingString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicProperty_ConflictDueAttributes_DoubleInheritance() - { - // Serialize - var obj = new ClassTwiceInheritedWithPropertyNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // The output for Newtonsoft.Json is: - // {"MyString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - - // The output for Newtonsoft.Json is: - // obj.ConflictingString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDueAttributes_DoubleInheritance() - { - // Serialize - var obj = new ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj)); - - // The output for Newtonsoft.Json is: - // {"MyString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - - Assert.Throws( - () => JsonSerializer.Deserialize(json)); - - // The output for Newtonsoft.Json is: - // obj.ConflictingString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicProperty_ConflictDuePolicy() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassWithPropertyPolicyConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDuePolicy() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassWithPropertyFieldPolicyConflictWhichThrows(); - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - } - - [Fact] - public static void Throw_PublicProperty_ConflictDuePolicy_SingleInheritance() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassInheritedWithPropertyPolicyConflictWhichThrows(); - - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // The output for Newtonsoft.Json is: - // {"myString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - - // The output for Newtonsoft.Json is: - // obj.myString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDuePolicy_SingleInheritance() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassInheritedWithPropertyFieldPolicyConflictWhichThrows(); - - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // The output for Newtonsoft.Json is: - // {"myString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - - // The output for Newtonsoft.Json is: - // obj.myString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicProperty_ConflictDuePolicy_DobuleInheritance() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows(); - - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // The output for Newtonsoft.Json is: - // {"myString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - - // The output for Newtonsoft.Json is: - // obj.myString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void Throw_PublicPropertyAndField_ConflictDuePolicy_DobuleInheritance() - { - var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - // Serialize - var obj = new ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows(); - - Assert.Throws( - () => JsonSerializer.Serialize(obj, options)); - - // The output for Newtonsoft.Json is: - // {"myString":"ConflictingValue"} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - - // Deserialize - string json = @"{""MyString"":""NewValue""}"; - - Assert.Throws( - () => JsonSerializer.Deserialize(json, options)); - - // The output for Newtonsoft.Json is: - // obj.myString = "NewValue" - // obj.MyString still equals "DefaultValue" - } - - [Fact] - public static void HiddenPropertiesIgnored_WhenOverridesIgnored() - { - string serialized = JsonSerializer.Serialize(new DerivedClass_With_IgnoredOverride()); - Assert.Equal(@"{}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName()); - Assert.Equal(@"{""MyProp"":null}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_Ignored_NewProperty()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_WithConflictingNewMember()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_WithConflictingNewMember_Of_DifferentType()); - Assert.Equal(@"{""MyProp"":0}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_Ignored_ConflictingNewMember()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_NewProperty_And_ConflictingPropertyName()); - Assert.Equal(@"{""MyProp"":null}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_Ignored_NewProperty_Of_DifferentType()); - Assert.Equal(@"{""MyProp"":false}", serialized); - - serialized = JsonSerializer.Serialize(new DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName()); - Assert.Equal(@"{""MyProp"":null}", serialized); - - serialized = JsonSerializer.Serialize(new FurtherDerivedClass_With_ConflictingPropertyName()); - Assert.Equal(@"{""MyProp"":null}", serialized); - - // Here we differ from Newtonsoft.Json, where the output would be - // {"MyProp":null} - // Conflicts at different type-hierarchy levels that are not caused by - // deriving or the new keyword are allowed. Properties on more derived types win. - // This is invalid in System.Text.Json. - Assert.Throws(() => JsonSerializer.Serialize(new DerivedClass_WithConflictingPropertyName())); - - serialized = JsonSerializer.Serialize(new FurtherDerivedClass_With_IgnoredOverride()); - Assert.Equal(@"{""MyProp"":null}", serialized); - } - - public class ClassWithInternalField - { - internal string MyString = "DefaultValue"; - } - - public class ClassWithNewSlotField : ClassWithInternalField - { - [JsonInclude] - public new string MyString = "NewDefaultValue"; - } - - public class ClassWithInternalProperty - { - internal string MyString { get; set; } = "DefaultValue"; - } - - public class ClassWithNewSlotProperty : ClassWithInternalProperty - { - public new string MyString { get; set; } = "NewDefaultValue"; - } - - public class ClassWithPublicProperty - { - public string MyString { get; set; } = "DefaultValue"; - } - - public class ClassWithNewSlotInternalProperty : ClassWithPublicProperty - { - internal new string MyString { get; set; } = "NewDefaultValue"; - } - - public class ClassWithPropertyNamingConflict - { - public string MyString { get; set; } = "DefaultValue"; - - [JsonPropertyName(nameof(MyString))] - internal string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassWithPropertyNamingConflictWhichThrows - { - public string MyString { get; set; } = "DefaultValue"; - - [JsonPropertyName(nameof(MyString))] - public string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassWithPropertyFieldNamingConflictWhichThrows - { - public string MyString { get; set; } = "DefaultValue"; - - [JsonInclude] - [JsonPropertyName(nameof(MyString))] - public string ConflictingString = "ConflictingValue"; - } - - public class ClassInheritedWithPropertyNamingConflictWhichThrows : ClassWithPublicProperty - { - [JsonPropertyName(nameof(MyString))] - public string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassInheritedWithPropertyFieldNamingConflictWhichThrows : ClassWithPublicProperty - { - [JsonInclude] - [JsonPropertyName(nameof(MyString))] - public string ConflictingString = "ConflictingValue"; - } - - public class ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy : ClassWithPublicProperty - { - } - - public class ClassTwiceInheritedWithPropertyNamingConflictWhichThrows : ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy - { - [JsonPropertyName(nameof(MyString))] - public string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassTwiceInheritedWithPropertyFieldNamingConflictWhichThrows : ClassTwiceInheritedWithPropertyNamingConflictWhichThrowsDummy - { - [JsonInclude] - [JsonPropertyName(nameof(MyString))] - public string ConflictingString = "ConflictingValue"; - } - - public class ClassWithPropertyPolicyConflict - { - public string MyString { get; set; } = "DefaultValue"; - - internal string myString { get; set; } = "ConflictingValue"; - } - - public class ClassWithPropertyPolicyConflictWhichThrows - { - public string MyString { get; set; } = "DefaultValue"; - - public string myString { get; set; } = "ConflictingValue"; - } - - public class ClassWithPropertyFieldPolicyConflictWhichThrows - { - public string MyString { get; set; } = "DefaultValue"; - - [JsonInclude] - public string myString = "ConflictingValue"; - } - - public class ClassInheritedWithPropertyPolicyConflictWhichThrows : ClassWithPublicProperty - { - public string myString { get; set; } = "ConflictingValue"; - } - - public class ClassInheritedWithPropertyFieldPolicyConflictWhichThrows : ClassWithPublicProperty - { - [JsonInclude] - public string myString = "ConflictingValue"; - } - - public class ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy : ClassWithPublicProperty - { - } - - public class ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows : ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy - { - public string myString { get; set; } = "ConflictingValue"; - } - - public class ClassTwiceInheritedWithPropertyFieldPolicyConflictWhichThrows : ClassInheritedWithPropertyPolicyConflictWhichThrowsDummy - { - [JsonInclude] - public string myString { get; set; } = "ConflictingValue"; - } - - public class ClassWithIgnoredNewSlotField : ClassWithInternalField - { - [JsonIgnore] - public new string MyString = "NewDefaultValue"; - } - - public class ClassWithIgnoredNewSlotProperty : ClassWithInternalProperty - { - [JsonIgnore] - public new string MyString { get; set; } = "NewDefaultValue"; - } - - public class ClassWithIgnoredPublicProperty - { - [JsonIgnore] - public string MyString { get; set; } = "DefaultValue"; - } - - public class ClassWithIgnoredPublicPropertyAndNewSlotPrivate : ClassWithIgnoredPublicProperty - { - internal new string MyString { get; set; } = "NewDefaultValue"; - } - - public class ClassWithIgnoredPropertyNamingConflictPrivate - { - [JsonIgnore] - public string MyString { get; set; } = "DefaultValue"; - - [JsonPropertyName(nameof(MyString))] - internal string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassWithIgnoredPropertyPolicyConflictPrivate - { - [JsonIgnore] - public string MyString { get; set; } = "DefaultValue"; - - internal string myString { get; set; } = "ConflictingValue"; - } - - public class ClassWithIgnoredPropertyNamingConflictPublic - { - [JsonIgnore] - public string MyString { get; set; } = "DefaultValue"; - - [JsonPropertyName(nameof(MyString))] - public string ConflictingString { get; set; } = "ConflictingValue"; - } - - public class ClassWithIgnoredPropertyPolicyConflictPublic - { - [JsonIgnore] - public string MyString { get; set; } = "DefaultValue"; - - public string myString { get; set; } = "ConflictingValue"; - } - - public class ClassWithHiddenByNewSlotIntProperty - { - public int MyNumeric { get; set; } = 1; - } - - public class ClassWithNewSlotDecimalField : ClassWithHiddenByNewSlotIntProperty - { - [JsonInclude] - public new decimal MyNumeric = 1.5M; - } - - public class ClassWithNewSlotDecimalProperty : ClassWithHiddenByNewSlotIntProperty - { - public new decimal MyNumeric { get; set; } = 1.5M; - } - - public class ClassWithNewSlotAttributedDecimalField : ClassWithHiddenByNewSlotIntProperty - { - [JsonInclude] - [JsonPropertyName("MyNewNumeric")] - public new decimal MyNumeric = 1.5M; - } - - public class ClassWithNewSlotAttributedDecimalProperty : ClassWithHiddenByNewSlotIntProperty - { - [JsonPropertyName("MyNewNumeric")] - public new decimal MyNumeric { get; set; } = 1.5M; - } - - private class Class_With_VirtualProperty - { - public virtual bool MyProp { get; set; } - } - - private class DerivedClass_With_IgnoredOverride : Class_With_VirtualProperty - { - [JsonIgnore] - public override bool MyProp { get; set; } - } - - private class DerivedClass_WithVisibleProperty_Of_DerivedClass_With_IgnoredOverride : DerivedClass_With_IgnoredOverride - { - public override bool MyProp { get; set; } - } - - private class DerivedClass_With_IgnoredOverride_And_ConflictingPropertyName : Class_With_VirtualProperty - { - [JsonPropertyName("MyProp")] - public string MyString { get; set; } - - [JsonIgnore] - public override bool MyProp { get; set; } - } - - private class Class_With_Property - { - public bool MyProp { get; set; } - } - - private class DerivedClass_With_Ignored_NewProperty : Class_With_Property - { - [JsonIgnore] - public new bool MyProp { get; set; } - } - - private class DerivedClass_With_NewProperty_And_ConflictingPropertyName : Class_With_Property - { - [JsonPropertyName("MyProp")] - public string MyString { get; set; } - - [JsonIgnore] - public new bool MyProp { get; set; } - } - - private class DerivedClass_With_Ignored_NewProperty_Of_DifferentType : Class_With_Property - { - [JsonIgnore] - public new int MyProp { get; set; } - } - - private class DerivedClass_With_Ignored_NewProperty_Of_DifferentType_And_ConflictingPropertyName : Class_With_Property - { - [JsonPropertyName("MyProp")] - public string MyString { get; set; } - - [JsonIgnore] - public new int MyProp { get; set; } - } - - private class DerivedClass_WithIgnoredOverride : Class_With_VirtualProperty - { - [JsonIgnore] - public override bool MyProp { get; set; } - } - - private class DerivedClass_WithConflictingNewMember : Class_With_VirtualProperty - { - public new bool MyProp { get; set; } - } - - private class DerivedClass_WithConflictingNewMember_Of_DifferentType : Class_With_VirtualProperty - { - public new int MyProp { get; set; } - } - - private class DerivedClass_With_Ignored_ConflictingNewMember : Class_With_VirtualProperty - { - [JsonIgnore] - public new bool MyProp { get; set; } - } - - private class DerivedClass_With_Ignored_ConflictingNewMember_Of_DifferentType : Class_With_VirtualProperty - { - [JsonIgnore] - public new int MyProp { get; set; } - } - - private class FurtherDerivedClass_With_ConflictingPropertyName : DerivedClass_WithIgnoredOverride - { - [JsonPropertyName("MyProp")] - public string MyString { get; set; } - } - - private class DerivedClass_WithConflictingPropertyName : Class_With_VirtualProperty - { - [JsonPropertyName("MyProp")] - public string MyString { get; set; } - } - - private class FurtherDerivedClass_With_IgnoredOverride : DerivedClass_WithConflictingPropertyName - { - [JsonIgnore] - public override bool MyProp { get; set; } - } - - [Fact] - public static void IgnoreReadOnlyProperties() - { - var options = new JsonSerializerOptions(); - options.IgnoreReadOnlyProperties = true; - - var obj = new ClassWithNoSetter(); - - string json = JsonSerializer.Serialize(obj, options); - - // Collections are always serialized unless they have [JsonIgnore]. - Assert.Equal(@"{""MyInts"":[1,2]}", json); - } - - [Fact] - public static void IgnoreReadOnlyFields() - { - var options = new JsonSerializerOptions(); - options.IncludeFields = true; - options.IgnoreReadOnlyFields = true; - - var obj = new ClassWithReadOnlyFields(); - - string json = JsonSerializer.Serialize(obj, options); - - // Collections are always serialized unless they have [JsonIgnore]. - Assert.Equal(@"{""MyInts"":[1,2]}", json); - } - - [Fact] - public static void NoSetter() - { - var obj = new ClassWithNoSetter(); - - string json = JsonSerializer.Serialize(obj); - Assert.Contains(@"""MyString"":""DefaultValue""", json); - Assert.Contains(@"""MyInts"":[1,2]", json); - - obj = JsonSerializer.Deserialize(@"{""MyString"":""IgnoreMe"",""MyInts"":[0]}"); - Assert.Equal("DefaultValue", obj.MyString); - Assert.Equal(2, obj.MyInts.Length); - } - - [Fact] - public static void NoGetter() - { - ClassWithNoGetter objWithNoGetter = JsonSerializer.Deserialize( - @"{""MyString"":""Hello"",""MyIntArray"":[0],""MyIntList"":[0]}"); - - Assert.Equal("Hello", objWithNoGetter.GetMyString()); - - // Currently we don't support setters without getters. - Assert.Equal(0, objWithNoGetter.GetMyIntArray().Length); - Assert.Equal(0, objWithNoGetter.GetMyIntList().Count); - } - - [Fact] - public static void PrivateGetter() - { - var obj = new ClassWithPrivateSetterAndGetter(); - obj.SetMyString("Hello"); - - string json = JsonSerializer.Serialize(obj); - Assert.Equal(@"{}", json); - } - - [Fact] - public static void PrivateSetter() - { - string json = @"{""MyString"":""Hello""}"; - - ClassWithPrivateSetterAndGetter objCopy = JsonSerializer.Deserialize(json); - Assert.Null(objCopy.GetMyString()); - } - - [Fact] - public static void PrivateSetterPublicGetter() - { - // https://github.com/dotnet/runtime/issues/29503 - ClassWithPublicGetterAndPrivateSetter obj - = JsonSerializer.Deserialize(@"{ ""Class"": {} }"); - - Assert.NotNull(obj); - Assert.Null(obj.Class); - } - - [Fact] - public static void MissingObjectProperty() - { - ClassWithMissingObjectProperty obj - = JsonSerializer.Deserialize(@"{ ""Object"": {} }"); - - Assert.Null(obj.Collection); - } - - [Fact] - public static void MissingCollectionProperty() - { - ClassWithMissingCollectionProperty obj - = JsonSerializer.Deserialize(@"{ ""Collection"": [] }"); - - Assert.Null(obj.Object); - } - - private class ClassWithPublicGetterAndPrivateSetter - { - public NestedClass Class { get; private set; } - } - - private class NestedClass - { - } - - [Fact] - public static void JsonIgnoreAttribute() - { - var options = new JsonSerializerOptions { IncludeFields = true }; - - // Verify default state. - var obj = new ClassWithIgnoreAttributeProperty(); - Assert.Equal(@"MyString", obj.MyString); - Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); - Assert.Equal(2, obj.MyStringsWithIgnore.Length); - Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); - Assert.Equal(3.14M, obj.MyNumeric); - - // Verify serialize. - string json = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""MyString""", json); - Assert.DoesNotContain(@"MyStringWithIgnore", json); - Assert.DoesNotContain(@"MyStringsWithIgnore", json); - Assert.DoesNotContain(@"MyDictionaryWithIgnore", json); - Assert.DoesNotContain(@"MyNumeric", json); - - // Verify deserialize default. - obj = JsonSerializer.Deserialize(@"{}", options); - Assert.Equal(@"MyString", obj.MyString); - Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); - Assert.Equal(2, obj.MyStringsWithIgnore.Length); - Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); - Assert.Equal(3.14M, obj.MyNumeric); - - // Verify deserialize ignores the json for MyStringWithIgnore and MyStringsWithIgnore. - obj = JsonSerializer.Deserialize( - @"{""MyString"":""Hello"", ""MyStringWithIgnore"":""IgnoreMe"", ""MyStringsWithIgnore"":[""IgnoreMe""], ""MyDictionaryWithIgnore"":{""Key"":9}, ""MyNumeric"": 2.71828}", options); - Assert.Contains(@"Hello", obj.MyString); - Assert.Equal(@"MyStringWithIgnore", obj.MyStringWithIgnore); - Assert.Equal(2, obj.MyStringsWithIgnore.Length); - Assert.Equal(1, obj.MyDictionaryWithIgnore["Key"]); - Assert.Equal(3.14M, obj.MyNumeric); - } - - [Fact] - public static void JsonIgnoreAttribute_UnsupportedCollection() - { - string json = - @"{ - ""MyConcurrentDict"":{ - ""key"":""value"" - }, - ""MyIDict"":{ - ""key"":""value"" - }, - ""MyDict"":{ - ""key"":""value"" - } - }"; - string wrapperJson = - @"{ - ""MyClass"":{ - ""MyConcurrentDict"":{ - ""key"":""value"" - }, - ""MyIDict"":{ - ""key"":""value"" - }, - ""MyDict"":{ - ""key"":""value"" - } - } - }"; - - // Unsupported collections will throw on deserialize by default. - Assert.Throws(() => JsonSerializer.Deserialize(json)); - - // Using new options instance to prevent using previously cached metadata. - JsonSerializerOptions options = new JsonSerializerOptions(); - - // Unsupported collections will throw on serialize by default. - // Only when the collection contains elements. - - var dictionary = new Dictionary(); - // Uri is an unsupported dictionary key. - dictionary.Add(new Uri("http://foo"), "bar"); - - var concurrentDictionary = new ConcurrentDictionary(dictionary); - - var instance = new ClassWithUnsupportedDictionary() - { - MyConcurrentDict = concurrentDictionary, - MyIDict = dictionary - }; - - var instanceWithIgnore = new ClassWithIgnoredUnsupportedDictionary - { - MyConcurrentDict = concurrentDictionary, - MyIDict = dictionary - }; - - Assert.Throws(() => JsonSerializer.Serialize(instance, options)); - - // Unsupported collections will throw on deserialize by default if they contain elements. - options = new JsonSerializerOptions(); - Assert.Throws(() => JsonSerializer.Deserialize(wrapperJson, options)); - - options = new JsonSerializerOptions(); - // Unsupported collections will throw on serialize by default if they contain elements. - Assert.Throws(() => JsonSerializer.Serialize(instance, options)); - - // When ignored, we can serialize and deserialize without exceptions. - options = new JsonSerializerOptions(); - - Assert.NotNull(JsonSerializer.Serialize(instanceWithIgnore, options)); - - ClassWithIgnoredUnsupportedDictionary obj = JsonSerializer.Deserialize(json, options); - Assert.Null(obj.MyDict); - - options = new JsonSerializerOptions(); - Assert.Equal("{}", JsonSerializer.Serialize(new ClassWithIgnoredUnsupportedDictionary())); - - options = new JsonSerializerOptions(); - WrapperForClassWithIgnoredUnsupportedDictionary wrapperObj = JsonSerializer.Deserialize(wrapperJson, options); - Assert.Null(wrapperObj.MyClass.MyDict); - - options = new JsonSerializerOptions(); - Assert.Equal(@"{""MyClass"":{}}", JsonSerializer.Serialize(new WrapperForClassWithIgnoredUnsupportedDictionary() - { - MyClass = new ClassWithIgnoredUnsupportedDictionary(), - }, options)); - } - - [Fact] - public static void JsonIgnoreAttribute_UnsupportedBigInteger() - { - string json = @"{""MyBigInteger"":1}"; - string wrapperJson = @"{""MyClass"":{""MyBigInteger"":1}}"; - - // Unsupported types will throw by default. - Assert.Throws(() => JsonSerializer.Deserialize(json)); - // Using new options instance to prevent using previously cached metadata. - JsonSerializerOptions options = new JsonSerializerOptions(); - Assert.Throws(() => JsonSerializer.Deserialize(wrapperJson, options)); - - // When ignored, we can serialize and deserialize without exceptions. - options = new JsonSerializerOptions(); - ClassWithIgnoredUnsupportedBigInteger obj = JsonSerializer.Deserialize(json, options); - Assert.Null(obj.MyBigInteger); - - options = new JsonSerializerOptions(); - Assert.Equal("{}", JsonSerializer.Serialize(new ClassWithIgnoredUnsupportedBigInteger())); - - options = new JsonSerializerOptions(); - WrapperForClassWithIgnoredUnsupportedBigInteger wrapperObj = JsonSerializer.Deserialize(wrapperJson, options); - Assert.Null(wrapperObj.MyClass.MyBigInteger); - - options = new JsonSerializerOptions(); - Assert.Equal(@"{""MyClass"":{}}", JsonSerializer.Serialize(new WrapperForClassWithIgnoredUnsupportedBigInteger() - { - MyClass = new ClassWithIgnoredUnsupportedBigInteger(), - }, options)); - } - - public class ObjectDictWrapper : Dictionary { } - - public class ClassWithUnsupportedDictionary - { - public ConcurrentDictionary MyConcurrentDict { get; set; } - public IDictionary MyIDict { get; set; } - public ObjectDictWrapper MyDict { get; set; } - } - - public class WrapperForClassWithUnsupportedDictionary - { - public ClassWithUnsupportedDictionary MyClass { get; set; } = new ClassWithUnsupportedDictionary(); - } - - public class ClassWithIgnoredUnsupportedDictionary - { - [JsonIgnore] - public ConcurrentDictionary MyConcurrentDict { get; set; } - [JsonIgnore] - public IDictionary MyIDict { get; set; } - [JsonIgnore] - public ObjectDictWrapper MyDict { get; set; } - } - - public class WrapperForClassWithIgnoredUnsupportedDictionary - { - public ClassWithIgnoredUnsupportedDictionary MyClass { get; set; } - } - - public class ClassWithUnsupportedBigInteger - { - public BigInteger? MyBigInteger { get; set; } - } - - public class WrapperForClassWithUnsupportedBigInteger - { - public ClassWithUnsupportedBigInteger MyClass { get; set; } = new ClassWithUnsupportedBigInteger(); - } - - public class ClassWithIgnoredUnsupportedBigInteger - { - [JsonIgnore] - public BigInteger? MyBigInteger { get; set; } - } - - public class WrapperForClassWithIgnoredUnsupportedBigInteger - { - public ClassWithIgnoredUnsupportedBigInteger MyClass { get; set; } - } - - public class ClassWithMissingObjectProperty - { - public object[] Collection { get; set; } - } - - public class ClassWithMissingCollectionProperty - { - public object Object { get; set; } - } - - public class ClassWithPrivateSetterAndGetter - { - private string MyString { get; set; } - - public string GetMyString() - { - return MyString; - } - - public void SetMyString(string value) - { - MyString = value; - } - } - - public class ClassWithReadOnlyFields - { - public ClassWithReadOnlyFields() - { - MyString = "DefaultValue"; - MyInts = new int[] { 1, 2 }; - } - - public readonly string MyString; - public readonly int[] MyInts; - } - - public class ClassWithNoSetter - { - public ClassWithNoSetter() - { - MyString = "DefaultValue"; - MyInts = new int[] { 1, 2 }; - } - - public string MyString { get; } - public int[] MyInts { get; } - } - - public class ClassWithNoGetter - { - string _myString = ""; - int[] _myIntArray = new int[] { }; - List _myIntList = new List { }; - - public string MyString - { - set - { - _myString = value; - } - } - - public int[] MyIntArray - { - set - { - _myIntArray = value; - } - } - - public List MyList - { - set - { - _myIntList = value; - } - } - - public string GetMyString() - { - return _myString; - } - - public int[] GetMyIntArray() - { - return _myIntArray; - } - - public List GetMyIntList() - { - return _myIntList; - } - } - - public class ClassWithIgnoreAttributeProperty - { - public ClassWithIgnoreAttributeProperty() - { - MyDictionaryWithIgnore = new Dictionary { { "Key", 1 } }; - MyString = "MyString"; - MyStringWithIgnore = "MyStringWithIgnore"; - MyStringsWithIgnore = new string[] { "1", "2" }; - MyNumeric = 3.14M; - } - - [JsonIgnore] - public Dictionary MyDictionaryWithIgnore { get; set; } - - [JsonIgnore] - public string MyStringWithIgnore { get; set; } - - public string MyString { get; set; } - - [JsonIgnore] - public string[] MyStringsWithIgnore { get; set; } - - [JsonIgnore] - public decimal MyNumeric; - } - - private enum MyEnum - { - Case1 = 0, - Case2 = 1, - } - - private struct StructWithOverride - { - [JsonIgnore] - public MyEnum EnumValue { get; set; } - - [JsonPropertyName("EnumValue")] - public string EnumString - { - get => EnumValue.ToString(); - set - { - if (value == "Case1") - { - EnumValue = MyEnum.Case1; - } - else if (value == "Case2") - { - EnumValue = MyEnum.Case2; - } - else - { - throw new Exception("Unknown value!"); - } - } - } - } - - [Fact] - public static void OverrideJsonIgnorePropertyUsingJsonPropertyName() - { - const string json = @"{""EnumValue"":""Case2""}"; - - StructWithOverride obj = JsonSerializer.Deserialize(json); - - Assert.Equal(MyEnum.Case2, obj.EnumValue); - Assert.Equal("Case2", obj.EnumString); - - string jsonSerialized = JsonSerializer.Serialize(obj); - Assert.Equal(json, jsonSerialized); - } - - private struct ClassWithOverrideReversed - { - // Same as ClassWithOverride except the order of the properties is different, which should cause different reflection order. - [JsonPropertyName("EnumValue")] - public string EnumString - { - get => EnumValue.ToString(); - set - { - if (value == "Case1") - { - EnumValue = MyEnum.Case1; - } - if (value == "Case2") - { - EnumValue = MyEnum.Case2; - } - else - { - throw new Exception("Unknown value!"); - } - } - } - - [JsonIgnore] - public MyEnum EnumValue { get; set; } - } - - [Fact] - public static void OverrideJsonIgnorePropertyUsingJsonPropertyNameReversed() - { - const string json = @"{""EnumValue"":""Case2""}"; - - ClassWithOverrideReversed obj = JsonSerializer.Deserialize(json); - - Assert.Equal(MyEnum.Case2, obj.EnumValue); - Assert.Equal("Case2", obj.EnumString); - - string jsonSerialized = JsonSerializer.Serialize(obj); - Assert.Equal(json, jsonSerialized); - } - - [Theory] - [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways))] - [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] - public static void JsonIgnoreConditionSetToAlwaysWorks(Type type) - { - string json = @"{""MyString"":""Random"",""MyDateTime"":""2020-03-23"",""MyInt"":4}"; - - object obj = JsonSerializer.Deserialize(json, type); - Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(default, (DateTime)type.GetProperty("MyDateTime").GetValue(obj)); - Assert.Equal(4, (int)type.GetProperty("MyInt").GetValue(obj)); - - string serialized = JsonSerializer.Serialize(obj); - Assert.Contains(@"""MyString"":""Random""", serialized); - Assert.Contains(@"""MyInt"":4", serialized); - Assert.DoesNotContain(@"""MyDateTime"":", serialized); - } - - private class ClassWithProperty_IgnoreConditionAlways - { - public string MyString { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.Always)] - public DateTime MyDateTime { get; set; } - public int MyInt { get; set; } - } - - private class ClassWithProperty_IgnoreConditionAlways_Ctor - { - public string MyString { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.Always)] - public DateTime MyDateTime { get; } - public int MyInt { get; } - - public ClassWithProperty_IgnoreConditionAlways_Ctor(DateTime myDateTime, int myInt) - { - MyDateTime = myDateTime; - MyInt = myInt; - } - } - - [Theory] - [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData))] - public static void JsonIgnoreConditionWhenWritingDefault_ClassProperty(Type type, JsonSerializerOptions options) - { - // Property shouldn't be ignored if it isn't null. - string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; - - object obj = JsonSerializer.Deserialize(json, type, options); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - string serialized = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyString"":""Random""", serialized); - Assert.Contains(@"""Int2"":2", serialized); - - // Property should be ignored when null. - json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; - - obj = JsonSerializer.Deserialize(json, type, options); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - - if (options.IgnoreNullValues) - { - // Null values can be ignored on deserialization using IgnoreNullValues. - Assert.Equal("DefaultString", (string)type.GetProperty("MyString").GetValue(obj)); - } - else - { - Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); - } - - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - // Set property to be ignored to null. - type.GetProperty("MyString").SetValue(obj, null); - - serialized = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""Int2"":2", serialized); - Assert.DoesNotContain(@"""MyString"":", serialized); - } - - private class ClassWithClassProperty_IgnoreConditionWhenWritingDefault - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string MyString { get; set; } = "DefaultString"; - public int Int2 { get; set; } - } - - private class ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string MyString { get; set; } = "DefaultString"; - public int Int2 { get; set; } - - public ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor(string myString) - { - if (myString != null) - { - MyString = myString; - } - } - } - - public static IEnumerable JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData() - { - yield return new object[] { typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault), new JsonSerializerOptions() }; - yield return new object[] { typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor), new JsonSerializerOptions { IgnoreNullValues = true } }; - } - - [Theory] - [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData))] - public static void JsonIgnoreConditionWhenWritingDefault_StructProperty(Type type, JsonSerializerOptions options) - { - // Property shouldn't be ignored if it isn't null. - string json = @"{""Int1"":1,""MyInt"":3,""Int2"":2}"; - - object obj = JsonSerializer.Deserialize(json, type, options); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Equal(3, (int)type.GetProperty("MyInt").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - string serialized = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyInt"":3", serialized); - Assert.Contains(@"""Int2"":2", serialized); - - // Null being assigned to non-nullable types is invalid. - json = @"{""Int1"":1,""MyInt"":null,""Int2"":2}"; - Assert.Throws(() => JsonSerializer.Deserialize(json, type, options)); - } - - private class ClassWithStructProperty_IgnoreConditionWhenWritingDefault - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int MyInt { get; set; } - public int Int2 { get; set; } - } - - private struct StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int MyInt { get; } - public int Int2 { get; set; } - - [JsonConstructor] - public StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor(int myInt) - { - Int1 = 0; - MyInt = myInt; - Int2 = 0; - } - } - - public static IEnumerable JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData() - { - yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionWhenWritingDefault), new JsonSerializerOptions() }; - yield return new object[] { typeof(StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor), new JsonSerializerOptions { IgnoreNullValues = true } }; - } - - [Theory] - [MemberData(nameof(JsonIgnoreConditionNever_TestData))] - public static void JsonIgnoreConditionNever(Type type) - { - // Property should always be (de)serialized, even when null. - string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; - - object obj = JsonSerializer.Deserialize(json, type); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - string serialized = JsonSerializer.Serialize(obj); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyString"":""Random""", serialized); - Assert.Contains(@"""Int2"":2", serialized); - - // Property should always be (de)serialized, even when null. - json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; - - obj = JsonSerializer.Deserialize(json, type); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - serialized = JsonSerializer.Serialize(obj); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyString"":null", serialized); - Assert.Contains(@"""Int2"":2", serialized); - } - - [Theory] - [MemberData(nameof(JsonIgnoreConditionNever_TestData))] - public static void JsonIgnoreConditionNever_IgnoreNullValues_True(Type type) - { - // Property should always be (de)serialized. - string json = @"{""Int1"":1,""MyString"":""Random"",""Int2"":2}"; - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - - object obj = JsonSerializer.Deserialize(json, type, options); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Equal("Random", (string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - string serialized = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyString"":""Random""", serialized); - Assert.Contains(@"""Int2"":2", serialized); - - // Property should always be (de)serialized, even when null. - json = @"{""Int1"":1,""MyString"":null,""Int2"":2}"; - - obj = JsonSerializer.Deserialize(json, type, options); - Assert.Equal(1, (int)type.GetProperty("Int1").GetValue(obj)); - Assert.Null((string)type.GetProperty("MyString").GetValue(obj)); - Assert.Equal(2, (int)type.GetProperty("Int2").GetValue(obj)); - - serialized = JsonSerializer.Serialize(obj, options); - Assert.Contains(@"""Int1"":1", serialized); - Assert.Contains(@"""MyString"":null", serialized); - Assert.Contains(@"""Int2"":2", serialized); - } - - private class ClassWithStructProperty_IgnoreConditionNever - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public string MyString { get; set; } - public int Int2 { get; set; } - } - - private class ClassWithStructProperty_IgnoreConditionNever_Ctor - { - public int Int1 { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public string MyString { get; } - public int Int2 { get; set; } - - public ClassWithStructProperty_IgnoreConditionNever_Ctor(string myString) - { - MyString = myString; - } - } - - public static IEnumerable JsonIgnoreConditionNever_TestData() - { - yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionNever) }; - yield return new object[] { typeof(ClassWithStructProperty_IgnoreConditionNever_Ctor) }; - } - - [Fact] - public static void JsonIgnoreCondition_LastOneWins() - { - string json = @"{""MyString"":""Random"",""MYSTRING"":null}"; - - var options = new JsonSerializerOptions - { - IgnoreNullValues = true, - PropertyNameCaseInsensitive = true - }; - var obj = JsonSerializer.Deserialize(json, options); - - Assert.Null(obj.MyString); - } - - [Fact] - public static void ClassWithComplexObjectsUsingIgnoreWhenWritingDefaultAttribute() - { - string json = @"{""Class"":{""MyInt16"":18}, ""Dictionary"":null}"; - - ClassUsingIgnoreWhenWritingDefaultAttribute obj = JsonSerializer.Deserialize(json); - - // Class is deserialized. - Assert.NotNull(obj.Class); - Assert.Equal(18, obj.Class.MyInt16); - - // Dictionary is deserialized as JsonIgnoreCondition.WhenWritingDefault only applies to deserialization. - Assert.Null(obj.Dictionary); - - obj = new ClassUsingIgnoreWhenWritingDefaultAttribute(); - json = JsonSerializer.Serialize(obj); - Assert.Equal(@"{""Dictionary"":{""Key"":""Value""}}", json); - } - - public class ClassUsingIgnoreWhenWritingDefaultAttribute - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public SimpleTestClass Class { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public Dictionary Dictionary { get; set; } = new Dictionary { ["Key"] = "Value" }; - } - - [Fact] - public static void ClassWithComplexObjectUsingIgnoreNeverAttribute() - { - string json = @"{""Class"":null, ""Dictionary"":null}"; - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - - var obj = JsonSerializer.Deserialize(json, options); - - // Class is not deserialized because it is null in json. - Assert.NotNull(obj.Class); - Assert.Equal(18, obj.Class.MyInt16); - - // Dictionary is deserialized regardless of being null in json. - Assert.Null(obj.Dictionary); - - // Serialize when values are null. - obj = new ClassUsingIgnoreNeverAttribute(); - obj.Class = null; - obj.Dictionary = null; - - json = JsonSerializer.Serialize(obj, options); - - // Class is not included in json because it was null, Dictionary is included regardless of being null. - Assert.Equal(@"{""Dictionary"":null}", json); - } - - public class ClassUsingIgnoreNeverAttribute - { - public SimpleTestClass Class { get; set; } = new SimpleTestClass { MyInt16 = 18 }; - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Dictionary Dictionary { get; set; } = new Dictionary { ["Key"] = "Value" }; - } - - [Fact] - public static void IgnoreConditionNever_WinsOver_IgnoreReadOnlyProperties() - { - var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; - - // Baseline - string json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty("Hello"), options); - Assert.Equal("{}", json); - - // With condition to never ignore - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty_IgnoreNever("Hello"), options); - Assert.Equal(@"{""MyString"":""Hello""}", json); - - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty_IgnoreNever(null), options); - Assert.Equal(@"{""MyString"":null}", json); - } - - [Fact] - public static void IgnoreConditionWhenWritingDefault_WinsOver_IgnoreReadOnlyProperties() - { - var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; - - // Baseline - string json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty("Hello"), options); - Assert.Equal("{}", json); - - // With condition to ignore when null - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault("Hello"), options); - Assert.Equal(@"{""MyString"":""Hello""}", json); - - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault(null), options); - Assert.Equal(@"{}", json); - } - - [Fact] - public static void IgnoreConditionNever_WinsOver_IgnoreReadOnlyFields() - { - var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; - - // Baseline - string json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField("Hello"), options); - Assert.Equal("{}", json); - - // With condition to never ignore - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField_IgnoreNever("Hello"), options); - Assert.Equal(@"{""MyString"":""Hello""}", json); - - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField_IgnoreNever(null), options); - Assert.Equal(@"{""MyString"":null}", json); - } - - [Fact] - public static void IgnoreConditionWhenWritingDefault_WinsOver_IgnoreReadOnlyFields() - { - var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true }; - - // Baseline - string json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField("Hello"), options); - Assert.Equal("{}", json); - - // With condition to ignore when null - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField_IgnoreWhenWritingDefault("Hello"), options); - Assert.Equal(@"{""MyString"":""Hello""}", json); - - json = JsonSerializer.Serialize(new ClassWithReadOnlyStringField_IgnoreWhenWritingDefault(null), options); - Assert.Equal(@"{}", json); - } - - private class ClassWithReadOnlyStringProperty - { - public string MyString { get; } - - public ClassWithReadOnlyStringProperty(string myString) => MyString = myString; - } - - private class ClassWithReadOnlyStringProperty_IgnoreNever - { - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public string MyString { get; } - - public ClassWithReadOnlyStringProperty_IgnoreNever(string myString) => MyString = myString; - } - - private class ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string MyString { get; } - - public ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault(string myString) => MyString = myString; - } - - private class ClassWithReadOnlyStringField - { - public string MyString { get; } - - public ClassWithReadOnlyStringField(string myString) => MyString = myString; - } - - private class ClassWithReadOnlyStringField_IgnoreNever - { - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public string MyString { get; } - - public ClassWithReadOnlyStringField_IgnoreNever(string myString) => MyString = myString; - } - - private class ClassWithReadOnlyStringField_IgnoreWhenWritingDefault - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string MyString { get; } - - public ClassWithReadOnlyStringField_IgnoreWhenWritingDefault(string myString) => MyString = myString; - } - - [Fact] - public static void NonPublicMembersAreNotIncluded() - { - Assert.Equal("{}", JsonSerializer.Serialize(new ClassWithNonPublicProperties())); - - string json = @"{""MyInt"":1,""MyString"":""Hello"",""MyFloat"":2,""MyDouble"":3}"; - var obj = JsonSerializer.Deserialize(json); - Assert.Equal(0, obj.MyInt); - Assert.Null(obj.MyString); - Assert.Equal(0, obj.GetMyFloat); - Assert.Equal(0, obj.GetMyDouble); - } - - private class ClassWithNonPublicProperties - { - internal int MyInt { get; set; } - internal string MyString { get; private set; } - internal float MyFloat { private get; set; } - private double MyDouble { get; set; } - - internal float GetMyFloat => MyFloat; - internal double GetMyDouble => MyDouble; - } - - [Fact] - public static void IgnoreCondition_WhenWritingDefault_Globally_Works() - { - // Baseline - default values written. - string expected = @"{""MyString"":null,""MyInt"":0,""MyPoint"":{""X"":0,""Y"":0}}"; - var obj = new ClassWithProps(); - JsonTestHelper.AssertJsonEqual(expected, JsonSerializer.Serialize(obj)); - - // Default values ignored when specified. - Assert.Equal("{}", JsonSerializer.Serialize(obj, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault })); - } - - private class ClassWithProps - { - public string MyString { get; set; } - public int MyInt { get; set; } - public Point_2D_Struct MyPoint { get; set; } - } - - [Fact] - public static void IgnoreCondition_WhenWritingDefault_PerProperty_Works() - { - // Default values ignored when specified. - Assert.Equal(@"{""MyInt"":0}", JsonSerializer.Serialize(new ClassWithPropsAndIgnoreAttributes())); - } - - private class ClassWithPropsAndIgnoreAttributes - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public string MyString { get; set; } - public int MyInt { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public Point_2D_Struct MyPoint { get; set; } - } - - [Fact] - public static void IgnoreCondition_WhenWritingDefault_DoesNotApplyToCollections() - { - var list = new List { false, true }; - - var options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; - Assert.Equal("[false,true]", JsonSerializer.Serialize(list, options)); - } - - [Fact] - public static void IgnoreCondition_WhenWritingDefault_DoesNotApplyToDeserialization() - { - // Baseline - null values are ignored on deserialization when using IgnoreNullValues (for compat with initial support). - string json = @"{""MyString"":null,""MyInt"":0,""MyPoint"":{""X"":0,""Y"":0}}"; - - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - ClassWithInitializedProps obj = JsonSerializer.Deserialize(json, options); - - Assert.Equal("Default", obj.MyString); - // Value types are not ignored. - Assert.Equal(0, obj.MyInt); - Assert.Equal(0, obj.MyPoint.X); - Assert.Equal(0, obj.MyPoint.X); - - // Test - default values (both null and default for value types) are not ignored when using - // JsonIgnoreCondition.WhenWritingDefault (as the option name implies) - options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; - obj = JsonSerializer.Deserialize(json, options); - Assert.Null(obj.MyString); - Assert.Equal(0, obj.MyInt); - Assert.Equal(0, obj.MyPoint.X); - Assert.Equal(0, obj.MyPoint.X); - } - - private class ClassWithInitializedProps - { - public string MyString { get; set; } = "Default"; - public int MyInt { get; set; } = -1; - public Point_2D_Struct MyPoint { get; set; } = new Point_2D_Struct(-1, -1); - } - - [Fact] - public static void ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_ClassTest() - { - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - - // Deserialization. - string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointClass"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; - - ClassWithValueAndReferenceTypes obj = JsonSerializer.Deserialize(json, options); - - // Null values ignored for reference types/nullable value types. - Assert.Equal("Default", obj.MyString); - Assert.NotNull(obj.MyPointClass); - Assert.True(obj.MyBool); - - // Default values not ignored for value types. - Assert.Equal(0, obj.MyInt); - Assert.Equal(0, obj.MyPointStruct.X); - Assert.Equal(0, obj.MyPointStruct.Y); - - // Serialization. - - // Make all members their default CLR value. - obj.MyString = null; - obj.MyPointClass = null; - obj.MyBool = null; - - json = JsonSerializer.Serialize(obj, options); - - // Null values not serialized, default values for value types serialized. - JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); - } - - [Fact] - public static void ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_LargeStructTest() - { - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - - // Deserialization. - string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointClass"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; - - LargeStructWithValueAndReferenceTypes obj = JsonSerializer.Deserialize(json, options); - - // Null values ignored for reference types. - - Assert.Equal("Default", obj.MyString); - // No way to specify a non-constant default before construction with ctor, so this remains null. - Assert.Null(obj.MyPointClass); - Assert.True(obj.MyBool); - - // Default values not ignored for value types. - Assert.Equal(0, obj.MyInt); - Assert.Equal(0, obj.MyPointStruct.X); - Assert.Equal(0, obj.MyPointStruct.Y); - - // Serialization. - - // Make all members their default CLR value. - obj = new LargeStructWithValueAndReferenceTypes(null, new Point_2D_Struct(0, 0), null, 0, null); - - json = JsonSerializer.Serialize(obj, options); - - // Null values not serialized, default values for value types serialized. - JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); - } - - [Fact] - public static void ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_SmallStructTest() - { - var options = new JsonSerializerOptions { IgnoreNullValues = true }; - - // Deserialization. - string json = @"{""MyString"":null,""MyInt"":0,""MyBool"":null,""MyPointStruct"":{""X"":0,""Y"":0}}"; - - SmallStructWithValueAndReferenceTypes obj = JsonSerializer.Deserialize(json, options); - - // Null values ignored for reference types. - Assert.Equal("Default", obj.MyString); - Assert.True(obj.MyBool); - - // Default values not ignored for value types. - Assert.Equal(0, obj.MyInt); - Assert.Equal(0, obj.MyPointStruct.X); - Assert.Equal(0, obj.MyPointStruct.Y); - - // Serialization. - - // Make all members their default CLR value. - obj = new SmallStructWithValueAndReferenceTypes(new Point_2D_Struct(0, 0), null, 0, null); - - json = JsonSerializer.Serialize(obj, options); - - // Null values not serialized, default values for value types serialized. - JsonTestHelper.AssertJsonEqual(@"{""MyInt"":0,""MyPointStruct"":{""X"":0,""Y"":0}}", json); - } - - private class ClassWithValueAndReferenceTypes - { - public string MyString { get; set; } = "Default"; - public int MyInt { get; set; } = -1; - public bool? MyBool { get; set; } = true; - public PointClass MyPointClass { get; set; } = new PointClass(); - public Point_2D_Struct MyPointStruct { get; set; } = new Point_2D_Struct(1, 2); - } - - private struct LargeStructWithValueAndReferenceTypes - { - public string MyString { get; } - public int MyInt { get; set; } - public bool? MyBool { get; set; } - public PointClass MyPointClass { get; set; } - public Point_2D_Struct MyPointStruct { get; set; } - - [JsonConstructor] - public LargeStructWithValueAndReferenceTypes( - PointClass myPointClass, - Point_2D_Struct myPointStruct, - string myString = "Default", - int myInt = -1, - bool? myBool = true) - { - MyString = myString; - MyInt = myInt; - MyBool = myBool; - MyPointClass = myPointClass; - MyPointStruct = myPointStruct; - } - } - - private struct SmallStructWithValueAndReferenceTypes - { - public string MyString { get; } - public int MyInt { get; set; } - public bool? MyBool { get; set; } - public Point_2D_Struct MyPointStruct { get; set; } - - [JsonConstructor] - public SmallStructWithValueAndReferenceTypes( - Point_2D_Struct myPointStruct, - string myString = "Default", - int myInt = -1, - bool? myBool = true) - { - MyString = myString; - MyInt = myInt; - MyBool = myBool; - MyPointStruct = myPointStruct; - } - } - - public class PointClass { } - - [Fact] - public static void Ignore_WhenWritingNull_Globally() - { - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IncludeFields = true - }; - - string json = @"{ -""MyPointClass2_IgnoredWhenWritingNull"":{}, -""MyString1_IgnoredWhenWritingNull"":""Default"", -""MyNullableBool1_IgnoredWhenWritingNull"":null, -""MyInt2"":0, -""MyPointStruct2"":{""X"":1,""Y"":2}, -""MyInt1"":1, -""MyString2_IgnoredWhenWritingNull"":null, -""MyPointClass1_IgnoredWhenWritingNull"":null, -""MyNullableBool2_IgnoredWhenWritingNull"":true, -""MyPointStruct1"":{""X"":0,""Y"":0} -}"; - - // All members should correspond to JSON contents, as ignore doesn't apply to deserialization. - ClassWithThingsToIgnore obj = JsonSerializer.Deserialize(json, options); - Assert.NotNull(obj.MyPointClass2_IgnoredWhenWritingNull); - Assert.Equal("Default", obj.MyString1_IgnoredWhenWritingNull); - Assert.Null(obj.MyNullableBool1_IgnoredWhenWritingNull); - Assert.Equal(0, obj.MyInt2); - Assert.Equal(1, obj.MyPointStruct2.X); - Assert.Equal(2, obj.MyPointStruct2.Y); - Assert.Equal(1, obj.MyInt1); - Assert.Null(obj.MyString2_IgnoredWhenWritingNull); - Assert.Null(obj.MyPointClass1_IgnoredWhenWritingNull); - Assert.True(obj.MyNullableBool2_IgnoredWhenWritingNull); - Assert.Equal(0, obj.MyPointStruct1.X); - Assert.Equal(0, obj.MyPointStruct1.Y); - - // Ignore null as appropriate during serialization. - string expectedJson = @"{ -""MyPointClass2_IgnoredWhenWritingNull"":{}, -""MyString1_IgnoredWhenWritingNull"":""Default"", -""MyInt2"":0, -""MyPointStruct2"":{""X"":1,""Y"":2}, -""MyInt1"":1, -""MyNullableBool2_IgnoredWhenWritingNull"":true, -""MyPointStruct1"":{""X"":0,""Y"":0} -}"; - JsonTestHelper.AssertJsonEqual(expectedJson, JsonSerializer.Serialize(obj, options)); - } - - public class ClassWithThingsToIgnore - { - public string MyString1_IgnoredWhenWritingNull { get; set; } - - public string MyString2_IgnoredWhenWritingNull; - - public int MyInt1; - - public int MyInt2 { get; set; } - - public bool? MyNullableBool1_IgnoredWhenWritingNull { get; set; } - - public bool? MyNullableBool2_IgnoredWhenWritingNull; - - public PointClass MyPointClass1_IgnoredWhenWritingNull; - - public PointClass MyPointClass2_IgnoredWhenWritingNull { get; set; } - - public Point_2D_Struct_WithAttribute MyPointStruct1; - - public Point_2D_Struct_WithAttribute MyPointStruct2 { get; set; } - } - - [Fact] - public static void Ignore_WhenWritingNull_PerProperty() - { - var options = new JsonSerializerOptions - { - IncludeFields = true - }; - - string json = @"{ -""MyPointClass2_IgnoredWhenWritingNull"":{}, -""MyString1_IgnoredWhenWritingNull"":""Default"", -""MyNullableBool1_IgnoredWhenWritingNull"":null, -""MyInt2"":0, -""MyPointStruct2"":{""X"":1,""Y"":2}, -""MyInt1"":1, -""MyString2_IgnoredWhenWritingNull"":null, -""MyPointClass1_IgnoredWhenWritingNull"":null, -""MyNullableBool2_IgnoredWhenWritingNull"":true, -""MyPointStruct1"":{""X"":0,""Y"":0} -}"; - - // All members should correspond to JSON contents, as ignore doesn't apply to deserialization. - ClassWithThingsToIgnore_PerProperty obj = JsonSerializer.Deserialize(json, options); - Assert.NotNull(obj.MyPointClass2_IgnoredWhenWritingNull); - Assert.Equal("Default", obj.MyString1_IgnoredWhenWritingNull); - Assert.Null(obj.MyNullableBool1_IgnoredWhenWritingNull); - Assert.Equal(0, obj.MyInt2); - Assert.Equal(1, obj.MyPointStruct2.X); - Assert.Equal(2, obj.MyPointStruct2.Y); - Assert.Equal(1, obj.MyInt1); - Assert.Null(obj.MyString2_IgnoredWhenWritingNull); - Assert.Null(obj.MyPointClass1_IgnoredWhenWritingNull); - Assert.True(obj.MyNullableBool2_IgnoredWhenWritingNull); - Assert.Equal(0, obj.MyPointStruct1.X); - Assert.Equal(0, obj.MyPointStruct1.Y); - - // Ignore null as appropriate during serialization. - string expectedJson = @"{ -""MyPointClass2_IgnoredWhenWritingNull"":{}, -""MyString1_IgnoredWhenWritingNull"":""Default"", -""MyInt2"":0, -""MyPointStruct2"":{""X"":1,""Y"":2}, -""MyInt1"":1, -""MyNullableBool2_IgnoredWhenWritingNull"":true, -""MyPointStruct1"":{""X"":0,""Y"":0} -}"; - JsonTestHelper.AssertJsonEqual(expectedJson, JsonSerializer.Serialize(obj, options)); - } - - public class ClassWithThingsToIgnore_PerProperty - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string MyString1_IgnoredWhenWritingNull { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string MyString2_IgnoredWhenWritingNull; - - public int MyInt1; - - public int MyInt2 { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? MyNullableBool1_IgnoredWhenWritingNull { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? MyNullableBool2_IgnoredWhenWritingNull; - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public PointClass MyPointClass1_IgnoredWhenWritingNull; - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public PointClass MyPointClass2_IgnoredWhenWritingNull { get; set; } - - public Point_2D_Struct_WithAttribute MyPointStruct1; - - public Point_2D_Struct_WithAttribute MyPointStruct2 { get; set; } - } - - [Theory] - [InlineData(typeof(ClassWithBadIgnoreAttribute))] - [InlineData(typeof(StructWithBadIgnoreAttribute))] - public static void JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(Type type) - { - InvalidOperationException ex = Assert.Throws(() => JsonSerializer.Deserialize("", type)); - string exAsStr = ex.ToString(); - Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); - Assert.Contains("MyBadMember", exAsStr); - Assert.Contains(type.ToString(), exAsStr); - Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); - - ex = Assert.Throws(() => JsonSerializer.Serialize(Activator.CreateInstance(type))); - exAsStr = ex.ToString(); - Assert.Contains("JsonIgnoreCondition.WhenWritingNull", exAsStr); - Assert.Contains("MyBadMember", exAsStr); - Assert.Contains(type.ToString(), exAsStr); - Assert.Contains("JsonIgnoreCondition.WhenWritingDefault", exAsStr); - } - - private class ClassWithBadIgnoreAttribute - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int MyBadMember { get; set; } - } - - private struct StructWithBadIgnoreAttribute - { - [JsonInclude] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public Point_2D_Struct MyBadMember { get; set; } - } - - private interface IUseCustomConverter { } - - [JsonConverter(typeof(MyCustomConverter))] - private struct MyValueTypeWithProperties : IUseCustomConverter - { - public int PrimitiveValue { get; set; } - public object RefValue { get; set; } - } - - private class MyCustomConverter : JsonConverter - { - public override bool CanConvert(Type typeToConvert) - { - return typeof(IUseCustomConverter).IsAssignableFrom(typeToConvert); - } - - public override IUseCustomConverter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - throw new NotImplementedException(); - - public override void Write(Utf8JsonWriter writer, IUseCustomConverter value, JsonSerializerOptions options) - { - MyValueTypeWithProperties obj = (MyValueTypeWithProperties)value; - writer.WriteNumberValue(obj.PrimitiveValue + 100); - // Ignore obj.RefValue - } - } - - private class MyClassWithValueType - { - public MyClassWithValueType() { } - - public MyValueTypeWithProperties Value { get; set; } - } - - [Fact] - public static void JsonIgnoreCondition_WhenWritingDefault_OnValueTypeWithCustomConverter() - { - var obj = new MyClassWithValueType(); - - // Baseline without custom options. - Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); - string json = JsonSerializer.Serialize(obj); - Assert.Equal("{\"Value\":100}", json); - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault - }; - - // Verify ignored. - Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{}", json); - - // Change a primitive value so it's no longer a default value. - obj.Value = new MyValueTypeWithProperties { PrimitiveValue = 1 }; - Assert.False(EqualityComparer.Default.Equals(default, obj.Value)); - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{\"Value\":101}", json); - - // Change reference value so it's no longer a default value. - obj.Value = new MyValueTypeWithProperties { RefValue = 1 }; - Assert.False(EqualityComparer.Default.Equals(default, obj.Value)); - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{\"Value\":100}", json); - } - - [Fact] - public static void JsonIgnoreCondition_ConverterCalledOnDeserialize() - { - // Verify converter is called. - Assert.Throws(() => JsonSerializer.Deserialize("{}")); - - var options = new JsonSerializerOptions - { - IgnoreNullValues = true - }; - - Assert.Throws(() => JsonSerializer.Deserialize("{}", options)); - } - - [Fact] - public static void JsonIgnoreCondition_WhenWritingNull_OnValueTypeWithCustomConverter() - { - string json; - var obj = new MyClassWithValueType(); - - // Baseline without custom options. - Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); - json = JsonSerializer.Serialize(obj); - Assert.Equal("{\"Value\":100}", json); - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - - // Verify not ignored; MyValueTypeWithProperties is not null. - Assert.True(EqualityComparer.Default.Equals(default, obj.Value)); - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{\"Value\":100}", json); - } - - [Fact] - public static void JsonIgnoreCondition_WhenWritingDefault_OnRootTypes() - { - string json; - int i = 0; - object obj = null; - - // Baseline without custom options. - json = JsonSerializer.Serialize(obj); - Assert.Equal("null", json); - - json = JsonSerializer.Serialize(i); - Assert.Equal("0", json); - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault - }; - - // We don't ignore when applied to root types; only properties. - - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("null", json); - - json = JsonSerializer.Serialize(i, options); - Assert.Equal("0", json); - } - - private struct MyValueTypeWithBoxedPrimitive - { - public object BoxedPrimitive { get; set; } - } - - [Fact] - public static void JsonIgnoreCondition_WhenWritingDefault_OnBoxedPrimitive() - { - string json; - - MyValueTypeWithBoxedPrimitive obj = new MyValueTypeWithBoxedPrimitive { BoxedPrimitive = 0 }; - - // Baseline without custom options. - json = JsonSerializer.Serialize(obj); - Assert.Equal("{\"BoxedPrimitive\":0}", json); - - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault - }; - - // No check if the boxed object's value type is a default value (0 in this case). - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{\"BoxedPrimitive\":0}", json); - - obj = new MyValueTypeWithBoxedPrimitive(); - json = JsonSerializer.Serialize(obj, options); - Assert.Equal("{}", json); - } - - private class MyClassWithValueTypeInterfaceProperty - { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public IInterface MyProp { get; set; } - - public interface IInterface { } - public struct MyStruct : IInterface { } - } - - [Fact] - public static void JsonIgnoreCondition_WhenWritingDefault_OnInterface() - { - // MyProp should be ignored due to [JsonIgnore]. - var obj = new MyClassWithValueTypeInterfaceProperty(); - string json = JsonSerializer.Serialize(obj); - Assert.Equal("{}", json); - - // No check if the interface property's value type is a default value. - obj = new MyClassWithValueTypeInterfaceProperty { MyProp = new MyClassWithValueTypeInterfaceProperty.MyStruct() }; - json = JsonSerializer.Serialize(obj); - Assert.Equal("{\"MyProp\":{}}", json); - } + public PropertyVisibilityTestsDynamic() : base(new JsonSerializerWrapperForString_Dynamic()) { } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 3ba9831cb6d..62b64663119 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent);net461 true @@ -16,7 +16,28 @@ + + + + + + + + + + + + + + + + + + + + + @@ -118,6 +139,7 @@ + @@ -144,8 +166,6 @@ - - @@ -160,22 +180,6 @@ - - - - - - - - - - - - - - - - From 92ed4005e70b37d6d0093d3e68da51f3749f15be Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Mon, 12 Jul 2021 23:18:32 -0700 Subject: [PATCH 460/926] Enreg structs x86 windows. (#55535) * Mark more cases as DoNotEnreg before CSE. There are CSE metrics that take into account how many potential enreg locals do we have. * enable for x86. --- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/jitconfigvalues.h | 2 +- src/coreclr/jit/lclvars.cpp | 4 ++++ src/coreclr/jit/lsra.cpp | 17 ++++++++++++++--- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 4aeeeb9e486..4b7374573ef 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6801,6 +6801,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp GenTreeCopyOrReload(genTreeOps oper, var_types type, GenTree* op1) : GenTreeUnOp(oper, type, op1) { + assert(type != TYP_STRUCT || op1->IsMultiRegNode()); SetRegNum(REG_NA); ClearOtherRegs(); } diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index b14597a8702..3220193c662 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -555,7 +555,7 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave #endif // defined(TARGET_ARM64) #endif // DEBUG -#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) +#if defined(TARGET_WINDOWS) && defined(TARGET_XARCH) CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 1) // Allow to enregister locals with struct type. #else CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) // Don't allow to enregister locals with struct type diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 33c5abaa30d..75bcbfafbf2 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3497,6 +3497,10 @@ void Compiler::lvaSortByRefCount() { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); } + else if (!varDsc->IsEnregisterableType()) + { + lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); + } } if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 4bbb877fda8..f382b1dcf46 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -6180,10 +6180,21 @@ void LinearScan::insertCopyOrReload(BasicBlock* block, GenTree* tree, unsigned m } else { - // Create the new node, with "tree" as its only child. - var_types treeType = tree->TypeGet(); + var_types regType = tree->TypeGet(); + if ((regType == TYP_STRUCT) && !tree->IsMultiRegNode()) + { + assert(compiler->compEnregStructLocals()); + assert(tree->IsLocal()); + const GenTreeLclVarCommon* lcl = tree->AsLclVarCommon(); + const LclVarDsc* varDsc = compiler->lvaGetDesc(lcl); + // We create struct copies with a primitive type so we don't bother copy node with parsing structHndl. + // Note that for multiReg node we keep each regType in the tree and don't need this. + regType = varDsc->GetRegisterType(lcl); + assert(regType != TYP_UNDEF); + } - GenTreeCopyOrReload* newNode = new (compiler, oper) GenTreeCopyOrReload(oper, treeType, tree); + // Create the new node, with "tree" as its only child. + GenTreeCopyOrReload* newNode = new (compiler, oper) GenTreeCopyOrReload(oper, regType, tree); assert(refPosition->registerAssignment != RBM_NONE); SetLsraAdded(newNode); newNode->SetRegNumByIdx(refPosition->assignedReg(), multiRegIdx); From 88112bcd9792266ea6895a944f90f61d8d037555 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 13 Jul 2021 00:18:13 -0700 Subject: [PATCH 461/926] Handle nullable primitives before passing them to WritePrimitive which expects non-null values. (#54800) --- .../ReflectionXmlSerializationWriter.cs | 11 ++++++++ .../XmlSerializerTests.RuntimeOnly.cs | 26 +++++++++++++++++++ .../tests/SerializationTypes.RuntimeOnly.cs | 7 +++++ 3 files changed, 44 insertions(+) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index 3ed294b0ac6..d138146708e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -397,6 +397,17 @@ namespace System.Xml.Serialization { WriteQualifiedNameElement(name, ns!, element.Default, (XmlQualifiedName)o!, element.IsNullable, mapping.IsSoap, mapping); } + else if (o == null && element.IsNullable) + { + if (mapping.IsSoap) + { + WriteNullTagEncoded(element.Name, ns); + } + else + { + WriteNullTagLiteral(element.Name, ns); + } + } else { WritePrimitiveMethodRequirement suffixNullable = mapping.IsSoap ? WritePrimitiveMethodRequirement.Encoded : WritePrimitiveMethodRequirement.None; diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs index 182fb0a3d71..f83571e7ebf 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -42,6 +42,19 @@ public static partial class XmlSerializerTests Assert.Equal(x, y); } + [Fact] + public static void Xml_ByteArrayNull() + { + Assert.Null(SerializeAndDeserialize(null, +@" +")); + byte[] x = new byte[] { 1, 2 }; + byte[] y = SerializeAndDeserialize(x, +@" +AQI="); + Assert.Equal(x, y); + } + [Fact] public static void Xml_CharAsRoot() { @@ -1279,6 +1292,19 @@ public static partial class XmlSerializerTests Assert.True(Enumerable.SequenceEqual(value.XmlAttributeForms, actual.XmlAttributeForms)); } + [Fact] + public static void XML_TypeWithNullableByteArray() + { + var value = new TypeWithNullableByteArray(); // XmlAttributeForms == null + + var actual = SerializeAndDeserialize(value, + "\r\n\r\n \r\n"); + + Assert.NotNull(actual); + Assert.Null(value.XmlAttributeForms); + Assert.Null(actual.XmlAttributeForms); + } + [Fact] public static void XML_TypeWithByteArrayArrayAsXmlAttribute() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs index 2d414e85702..70e080bae6e 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs @@ -1987,6 +1987,13 @@ namespace SerializationTypes public byte[] XmlAttributeForms; } + [XmlType(TypeName = "MyXmlType")] + public class TypeWithNullableByteArray + { + [XmlElement(DataType = "base64Binary", IsNullable = true)] + public byte[] XmlAttributeForms { get; set; } + } + [XmlType(TypeName = "MyXmlType")] public class TypeWithByteArrayArrayAsXmlAttribute { From 1749deb1cb51412c8f19a0246ac4d9d17c2b1a6b Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 13 Jul 2021 00:18:55 -0700 Subject: [PATCH 462/926] Use scopes to resolve array naming issue. (#55451) * Use scope to avoid using duplicate names for arrays in ILGen serializer. * Remove comment. --- .../XmlSerializationWriterILGen.cs | 6 +++ .../tests/XmlSerializer/XmlSerializerTests.cs | 38 +++++++++++++++++++ .../tests/SerializationTypes.cs | 30 ++++++++++++++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs index 46eda857550..f348fdfa953 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs @@ -1176,6 +1176,7 @@ namespace System.Xml.Serialization string aiVar = "ai" + memberTypeDesc.Name; string iVar = "i"; string fullTypeName = memberTypeDesc.CSharpName; + ilg.EnterScope(); WriteArrayLocalDecl(fullTypeName, aVar, source, memberTypeDesc); if (memberTypeDesc.IsNullable) { @@ -1361,6 +1362,8 @@ namespace System.Xml.Serialization { ilg.EndIf(); } + + ilg.ExitScope(); } else { @@ -1435,6 +1438,7 @@ namespace System.Xml.Serialization if (elements.Length == 0 && text == null) return; string arrayTypeName = arrayTypeDesc.CSharpName; string aName = "a" + arrayTypeDesc.Name; + ilg.EnterScope(); WriteArrayLocalDecl(arrayTypeName, aName, source, arrayTypeDesc); LocalBuilder aLoc = ilg.GetLocal(aName); if (arrayTypeDesc.IsNullable) @@ -1486,6 +1490,8 @@ namespace System.Xml.Serialization { ilg.EndIf(); } + + ilg.ExitScope(); } [RequiresUnreferencedCode("calls WriteElements")] diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 364c4f88008..e648a51d5c6 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -69,6 +69,44 @@ string.Format(@" } } + [Fact] + public static void Xml_NamespaceTypeNameClashTest() + { + var serializer = new XmlSerializer(typeof(NamespaceTypeNameClashContainer)); + + Assert.NotNull(serializer); + + var root = new NamespaceTypeNameClashContainer + { + A = new[] { new SerializationTypes.TypeNameClashA.TypeNameClash { Name = "N1" }, new SerializationTypes.TypeNameClashA.TypeNameClash { Name = "N2" } }, + B = new[] { new SerializationTypes.TypeNameClashB.TypeNameClash { Name = "N3" } } + }; + + var xml = @" + + + N1 + + + N2 + + + N3 + + "; + + var actualRoot = SerializeAndDeserialize(root, xml); + + Assert.NotNull(actualRoot); + Assert.NotNull(actualRoot.A); + Assert.NotNull(actualRoot.B); + Assert.Equal(root.A.Length, actualRoot.A.Length); + Assert.Equal(root.B.Length, actualRoot.B.Length); + Assert.Equal(root.A[0].Name, actualRoot.A[0].Name); + Assert.Equal(root.A[1].Name, actualRoot.A[1].Name); + Assert.Equal(root.B[0].Name, actualRoot.B[0].Name); + } + [Fact] public static void Xml_ArrayAsGetSet() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index 12ba77621b4..6c8da649d56 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -850,6 +850,35 @@ namespace SerializationTypes public object Value2 = new SimpleType[1]; } + + namespace TypeNameClashA + { + [System.Xml.Serialization.XmlType("TypeClashA")] + public class TypeNameClash + { + public string Name { get; set; } + } + } + + namespace TypeNameClashB + { + [System.Xml.Serialization.XmlType("TypeClashB")] + public class TypeNameClash + { + public string Name { get; set; } + } + } + + [System.Xml.Serialization.XmlRootAttribute("Root")] + [System.Xml.Serialization.XmlType("ContainerType")] + public class NamespaceTypeNameClashContainer + { + [System.Xml.Serialization.XmlElementAttribute("A")] + public TypeNameClashA.TypeNameClash[] A { get; set; } + + [System.Xml.Serialization.XmlElementAttribute("B")] + public TypeNameClashB.TypeNameClash[] B { get; set; } + } } public class TypeWithXmlElementProperty @@ -921,7 +950,6 @@ public class TypeWithXmlNodeArrayProperty public XmlNode[] CDATA { get; set; } } - public class Animal { public int Age; From c8b3051399fdf2d6d904be66e859df7135bf9afd Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 13 Jul 2021 00:27:06 -0700 Subject: [PATCH 463/926] Keep looking up derived chain for Property.Get if it doesn't exist. (#52509) --- .../ReflectionXmlSerializationReader.cs | 2 +- .../ReflectionXmlSerializationWriter.cs | 66 ++++++++++++++++++- .../src/System/Xml/Serialization/Types.cs | 5 +- .../XmlSerializerTests.RuntimeOnly.cs | 7 +- .../tests/DataContractSerializer.cs | 2 +- .../tests/SerializationTypes.RuntimeOnly.cs | 4 ++ 6 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 4e7b4e8e6e9..eca311b4d31 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -637,7 +637,7 @@ namespace System.Xml.Serialization (Type, string) typeMemberNameTuple = (o.GetType(), memberName); if (!s_setMemberValueDelegateCache.TryGetValue(typeMemberNameTuple, out ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate? result)) { - MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetMember(o.GetType(), memberName); + MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName); Debug.Assert(memberInfo != null, "memberInfo could not be retrieved"); Type memberType; if (memberInfo is PropertyInfo propInfo) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index d138146708e..c40076aa838 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -660,7 +660,7 @@ namespace System.Xml.Serialization [RequiresUnreferencedCode("Calls GetType on object")] private object? GetMemberValue(object o, string memberName) { - MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetMember(o.GetType(), memberName); + MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveGetInfo(o.GetType(), memberName); object? memberValue = GetMemberValue(o, memberInfo); return memberValue; } @@ -1367,7 +1367,7 @@ namespace System.Xml.Serialization internal static class ReflectionXmlSerializationHelper { [RequiresUnreferencedCode("Reflects over base members")] - public static MemberInfo GetMember(Type declaringType, string memberName) + public static MemberInfo? GetMember(Type declaringType, string memberName, bool throwOnNotFound) { MemberInfo[] memberInfos = declaringType.GetMember(memberName); if (memberInfos == null || memberInfos.Length == 0) @@ -1388,7 +1388,11 @@ namespace System.Xml.Serialization if (!foundMatchedMember) { - throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, $"Could not find member named {memberName} of type {declaringType}")); + if (throwOnNotFound) + { + throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, $"Could not find member named {memberName} of type {declaringType}")); + } + return null; } declaringType = currentType!; @@ -1409,5 +1413,61 @@ namespace System.Xml.Serialization return memberInfo; } + + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] + public static MemberInfo GetEffectiveGetInfo(Type declaringType, string memberName) + { + MemberInfo memberInfo = GetMember(declaringType, memberName, true)!; + + // For properties, we might have a PropertyInfo that does not have a valid + // getter at this level of inheritance. If that's the case, we need to look + // up the chain to find the right PropertyInfo for the getter. + if (memberInfo is PropertyInfo propInfo && propInfo.GetMethod == null) + { + var parent = declaringType.BaseType; + + while (parent != null) + { + var mi = GetMember(parent, memberName, false); + + if (mi is PropertyInfo pi && pi.GetMethod != null && pi.PropertyType == propInfo.PropertyType) + { + return pi; + } + + parent = parent.BaseType; + } + } + + return memberInfo; + } + + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] + public static MemberInfo GetEffectiveSetInfo(Type declaringType, string memberName) + { + MemberInfo memberInfo = GetMember(declaringType, memberName, true)!; + + // For properties, we might have a PropertyInfo that does not have a valid + // setter at this level of inheritance. If that's the case, we need to look + // up the chain to find the right PropertyInfo for the setter. + if (memberInfo is PropertyInfo propInfo && propInfo.SetMethod == null) + { + var parent = declaringType.BaseType; + + while (parent != null) + { + var mi = GetMember(parent, memberName, false); + + if (mi is PropertyInfo pi && pi.SetMethod != null && pi.PropertyType == propInfo.PropertyType) + { + return pi; + } + + parent = parent.BaseType; + } + } + + return memberInfo; + } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs index 84a3d736550..1971544633b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs @@ -1219,7 +1219,10 @@ namespace System.Xml.Serialization replacedInfo = info; if (replacedInfo != memberInfoToBeReplaced) { - if (!info.GetMethod!.IsPublic + // The property name is a match. It might be an override, or + // it might be hiding. Either way, check to see if the derived + // property has a getter that is useable for serialization. + if (info.GetMethod != null && !info.GetMethod!.IsPublic && memberInfoToBeReplaced is PropertyInfo && ((PropertyInfo)memberInfoToBeReplaced).GetMethod!.IsPublic ) diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs index f83571e7ebf..167e812048c 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -2836,13 +2836,14 @@ public static partial class XmlSerializerTests [Fact] public static void DerivedTypeWithDifferentOverrides2() { - DerivedTypeWithDifferentOverrides2 value = new DerivedTypeWithDifferentOverrides2() { Name1 = "Name1", Name2 = "Name2", Name3 = "Name3", Name4 = "Name4", Name5 = "Name5", Name6 = "Name6" }; + DerivedTypeWithDifferentOverrides2 value = new DerivedTypeWithDifferentOverrides2() { Name1 = "Name1", Name2 = "Name2", Name3 = "Name3", Name4 = "Name4", Name5 = "Name5", Name6 = "Name6", Name7 = "Name7" }; ((DerivedTypeWithDifferentOverrides)value).Name5 = "MidLevelName5"; ((DerivedTypeWithDifferentOverrides)value).Name4 = "MidLevelName4"; ((SerializationTypes.BaseType)value).Name4 = "BaseLevelName4"; ((DerivedTypeWithDifferentOverrides)value).Name6 = "MidLevelName6"; ((SerializationTypes.BaseType)value).Name6 = "BaseLevelName6"; - DerivedTypeWithDifferentOverrides2 actual = SerializeAndDeserialize(value, @"Name1Name2Name3BaseLevelName4MidLevelName5BaseLevelName6"); + ((DerivedTypeWithDifferentOverrides)value).Name7 = "MidLevelName7"; + DerivedTypeWithDifferentOverrides2 actual = SerializeAndDeserialize(value, @"Name1Name2Name3BaseLevelName4MidLevelName5BaseLevelName6MidLevelName7"); Assert.Equal(value.Name1, actual.Name1); Assert.Equal(value.Name2, actual.Name2); Assert.Equal(value.Name3, actual.Name3); @@ -2855,6 +2856,8 @@ public static partial class XmlSerializerTests Assert.Null(actual.Name6); Assert.Equal(((DerivedTypeWithDifferentOverrides)actual).Name6, ((SerializationTypes.BaseType)actual).Name6); Assert.Equal(((SerializationTypes.BaseType)actual).Name6, ((SerializationTypes.BaseType)actual).Name6); + Assert.Equal(((DerivedTypeWithDifferentOverrides)actual).Name7, ((SerializationTypes.BaseType)actual).Name7); + Assert.Equal(actual.Name7, ((SerializationTypes.BaseType)actual).Name7); } [Fact] diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index 2a39358aa09..038b9130d4d 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -1084,7 +1084,7 @@ public static partial class DataContractSerializerTests public static void DCS_DerivedTypeWithDifferentOverrides() { var x = new DerivedTypeWithDifferentOverrides() { Name1 = "Name1", Name2 = "Name2", Name3 = "Name3", Name4 = "Name4", Name5 = "Name5" }; - var y = DataContractSerializerHelper.SerializeAndDeserialize(x, @"Name1Name2Name3Name5"); + var y = DataContractSerializerHelper.SerializeAndDeserialize(x, @"Name1Name2Name3Name5"); Assert.Equal(x.Name1, y.Name1); Assert.Equal(x.Name2, y.Name2); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs index 70e080bae6e..de485ec10b5 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs @@ -837,6 +837,8 @@ namespace SerializationTypes public string @Name5 { get; set; } public virtual string Name6 { get; set; } + + public virtual string Name7 { get; set; } } public class DerivedTypeWithDifferentOverrides : BaseType @@ -852,6 +854,8 @@ namespace SerializationTypes public new string Name5 { get; set; } public override string Name6 { get; set; } + + public override string Name7 { set { base.Name7 = value; } } } public class DerivedTypeWithDifferentOverrides2 : DerivedTypeWithDifferentOverrides From 2d596ecea22934ecfc18346a40903cf035a0353b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Tue, 13 Jul 2021 00:50:00 -0700 Subject: [PATCH 464/926] Change server paths to use LOCALHOST (#55523) --- .../tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs index 6f9a1ace400..0682f8ce16e 100644 --- a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.FileSystem.cs @@ -512,7 +512,7 @@ namespace System.IO.Tests // Extended DOS yield return Path.Combine(@"\\?\", Path.GetTempPath(), "foo"); // UNC - yield return @"\\SERVER\share\path"; + yield return @"\\LOCALHOST\share\path"; } else { From fe44d588f241c5b72b3b56768a0dbbb1bea027a7 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 13 Jul 2021 10:05:43 +0200 Subject: [PATCH 465/926] Last set of ILLink annotations related to System.Data.Common (#55507) * Last set of ILLink annotations related to System.Data.Common * apply feedback --- .../ref/System.Data.Common.cs | 2 + .../ILLink.Suppressions.LibraryBuild.xml | 47 +++++++++++++++++++ .../src/ILLink/ILLink.Suppressions.xml | 41 ---------------- .../src/System/Data/DataSet.cs | 24 ++++++++-- .../src/System/Data/DataTable.cs | 30 ++++++++---- 5 files changed, 91 insertions(+), 53 deletions(-) create mode 100644 src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.LibraryBuild.xml delete mode 100644 src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index 38a37217d2f..7bcd618b806 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -753,6 +753,7 @@ namespace System.Data public System.Data.DataRow[] GetErrors() { throw null; } public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } protected virtual System.Type GetRowType() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] protected virtual System.Xml.Schema.XmlSchema? GetSchema() { throw null; } public void ImportRow(System.Data.DataRow? row) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from types used in the expression column to be trimmed if not referenced directly.")] @@ -796,6 +797,7 @@ namespace System.Data public void ReadXmlSchema(string fileName) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] public void ReadXmlSchema(System.Xml.XmlReader? reader) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Members from serialized types may be trimmed if not referenced directly.")] protected virtual void ReadXmlSerializable(System.Xml.XmlReader? reader) { } public void RejectChanges() { } public virtual void Reset() { } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.LibraryBuild.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.LibraryBuild.xml new file mode 100644 index 00000000000..fff5aa12ed9 --- /dev/null +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.LibraryBuild.xml @@ -0,0 +1,47 @@ + + + + + ILLink + IL2026 + member + M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader) + DataSet.ReadXml is not trimming safe but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataSet as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataSet is used in the same app which will reduce chance of seeing false positive warning. + + + ILLink + IL2026 + member + M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) + DataSet.WriteXml is not trimming safe but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataSet as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataSet is used in the same app which will reduce chance of seeing false positive warning. + + + ILLink + IL2026 + member + M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.GetSchema() + DataSet.GetSchema is not trimming safe when used in derived types but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataSet as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataSet is used in the same app which will reduce chance of seeing false positive warning. + + + ILLink + IL2026 + member + M:System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader) + DataTable.ReadXml is not trimming safe but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataTable as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataTable is used in the same app which will reduce chance of seeing false positive warning. + + + ILLink + IL2026 + member + M:System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) + DataTable.WriteXml is not trimming safe but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataTable as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataTable is used in the same app which will reduce chance of seeing false positive warning. + + + ILLink + IL2026 + member + M:System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.GetSchema() + DataTable.GetSchema is not trimming safe when used in derived types but developers cannot use it directly and have to go through the IXmlSerializable interface. Neither marking the interface nor constructors of DataTable as unsafe is a good solution so we rely on the fact that this method will be trimmed and warning will be shown only when both interface and DataTable is used in the same app which will reduce chance of seeing false positive warning. + + + diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 393abfc15c4..00000000000 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.GetSchema() - - - ILLink - IL2026 - member - M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader) - - - ILLink - IL2026 - member - M:System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) - - - ILLink - IL2026 - member - M:System.Data.DataTable.GetSchema() - - - ILLink - IL2026 - member - M:System.Data.DataTable.ReadXml(System.Xml.XmlReader,System.Data.XmlReadMode,System.Boolean) - - - ILLink - IL2026 - member - M:System.Data.DataTable.WriteXmlCore(System.Xml.XmlWriter) - - - diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index 33c265766ce..6974aafce60 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -3444,7 +3444,6 @@ namespace System.Data private static bool PublishLegacyWSDL() => false; -#pragma warning disable 8632 XmlSchema? IXmlSerializable.GetSchema() { if (GetType() == typeof(DataSet)) @@ -3457,12 +3456,18 @@ namespace System.Data XmlWriter writer = new XmlTextWriter(stream, null); if (writer != null) { - (new XmlTreeGen(SchemaFormat.WebService)).Save(this, writer); + WriteXmlSchema(this, writer); } stream.Position = 0; return XmlSchema.Read(new XmlTextReader(stream), null); } + [RequiresUnreferencedCode("DataSet.GetSchema uses TypeDescriptor and XmlSerialization underneath which are not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private static void WriteXmlSchema(DataSet ds, XmlWriter writer) + { + (new XmlTreeGen(SchemaFormat.WebService)).Save(ds, writer); + } + void IXmlSerializable.ReadXml(XmlReader reader) { bool fNormalization = true; @@ -3483,7 +3488,7 @@ namespace System.Data } } - ReadXmlSerializable(reader); + ReadXmlSerializableInternal(reader); if (xmlTextParser != null) { @@ -3495,12 +3500,23 @@ namespace System.Data } } + [RequiresUnreferencedCode("DataSet.ReadXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private void ReadXmlSerializableInternal(XmlReader reader) + { + ReadXmlSerializable(reader); + } + void IXmlSerializable.WriteXml(XmlWriter writer) + { + WriteXmlInternal(writer); + } + + [RequiresUnreferencedCode("DataSet.WriteXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private void WriteXmlInternal(XmlWriter writer) { WriteXmlSchema(writer, SchemaFormat.WebService, null); WriteXml(writer, XmlWriteMode.DiffGram); } -#pragma warning restore 8632 [RequiresUnreferencedCode("Using LoadOption may cause members from types used in the expression column to be trimmed if not referenced directly.")] public virtual void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler? errorHandler, params DataTable[] tables) diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index bd8257122e2..22ace6fce64 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -5955,6 +5955,7 @@ namespace System.Data } } + [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] internal XmlReadMode ReadXml(XmlReader? reader, XmlReadMode mode, bool denyResolving) { IDisposable? restrictedScope = null; @@ -6685,8 +6686,11 @@ namespace System.Data return type; } - XmlSchema? IXmlSerializable.GetSchema() => GetSchema(); + XmlSchema? IXmlSerializable.GetSchema() => GetXmlSchema(); + [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2046:UnrecognizedReflectionPattern", + Justification = "https://github.com/mono/linker/issues/1187 Trimmer thinks this implements IXmlSerializable.GetSchema() and warns about not matching attributes.")] protected virtual XmlSchema? GetSchema() { if (GetType() == typeof(DataTable)) @@ -6704,7 +6708,12 @@ namespace System.Data return XmlSchema.Read(new XmlTextReader(stream), null); } -#pragma warning disable 8632 + [RequiresUnreferencedCode("DataTable.GetSchema uses TypeDescriptor and XmlSerialization underneath which are not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private XmlSchema? GetXmlSchema() + { + return GetSchema(); + } + void IXmlSerializable.ReadXml(XmlReader reader) { IXmlTextParser? textReader = reader as IXmlTextParser; @@ -6714,7 +6723,7 @@ namespace System.Data fNormalization = textReader.Normalized; textReader.Normalized = false; } - ReadXmlSerializable(reader); + ReadXmlSerializableInternal(reader); if (textReader != null) { @@ -6722,20 +6731,25 @@ namespace System.Data } } + [RequiresUnreferencedCode("DataTable.ReadXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private void ReadXmlSerializableInternal(XmlReader reader) + { + ReadXmlSerializable(reader); + } + void IXmlSerializable.WriteXml(XmlWriter writer) { - WriteXmlCore(writer); + WriteXmlInternal(writer); } -#pragma warning restore 8632 - // This method exists so that suppression can be placed on `IXmlSerializable.WriteXml(XmlWriter writer)` - private void WriteXmlCore(XmlWriter writer) + [RequiresUnreferencedCode("DataTable.WriteXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")] + private void WriteXmlInternal(XmlWriter writer) { WriteXmlSchema(writer, false); WriteXml(writer, XmlWriteMode.DiffGram, false); } -#pragma warning restore 8632 + [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] protected virtual void ReadXmlSerializable(XmlReader? reader) => ReadXml(reader, XmlReadMode.DiffGram, true); // RowDiffIdUsageSection & DSRowDiffIdUsageSection Usage: From b744e1f0bdd6aedf9908ff53acf0ff163be6d20f Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 13 Jul 2021 11:35:10 +0300 Subject: [PATCH 466/926] Fix bad folding (#54722) * Always sign-extend from int32 in gtFoldExprConst GT_CNS_INT of TYP_INT on 64 hosts has an implicit contract: the value returned by IconValue() must "fit" into 32 bit signed integer, i. e. "static_cast(IconValue()) == IconValue()". gtFoldExprConst was failing to uphold this contract when the target was 32 bit and the host was 64 bit. Fix this by always truncating before calling SetIconValue(). * Add a simple test that reproduces bad codegen --- src/coreclr/jit/gentree.cpp | 12 +++------ .../folding_extends_int32_on_64_bit_hosts.cs | 27 +++++++++++++++++++ ...lding_extends_int32_on_64_bit_hosts.csproj | 10 +++++++ 3 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.cs create mode 100644 src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.csproj diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 277cd53e1c1..7f16e6cd400 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14856,19 +14856,15 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) JITDUMP("\nFolding operator with constant nodes into a constant:\n"); DISPTREE(tree); -#ifdef TARGET_64BIT - // Some operations are performed as 64 bit instead of 32 bit so the upper 32 bits - // need to be discarded. Since constant values are stored as ssize_t and the node - // has TYP_INT the result needs to be sign extended rather than zero extended. - i1 = INT32(i1); -#endif // TARGET_64BIT - // Also all conditional folding jumps here since the node hanging from // GT_JTRUE has to be a GT_CNS_INT - value 0 or 1. tree->ChangeOperConst(GT_CNS_INT); tree->ChangeType(TYP_INT); - tree->AsIntCon()->SetIconValue(i1); + // Some operations are performed as 64 bit instead of 32 bit so the upper 32 bits + // need to be discarded. Since constant values are stored as ssize_t and the node + // has TYP_INT the result needs to be sign extended rather than zero extended. + tree->AsIntCon()->SetIconValue(static_cast(i1)); tree->AsIntCon()->gtFieldSeq = fieldSeq; if (vnStore != nullptr) { diff --git a/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.cs b/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.cs new file mode 100644 index 00000000000..08d7e7a684f --- /dev/null +++ b/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.cs @@ -0,0 +1,27 @@ +public class FoldingExtendsInt32On64BitHostsTest +{ + // On 64 bit hosts, 32 bit constants are stored as 64 bit signed values. + // gtFoldExpr failed to properly truncate the folded value to 32 bits when + // the host was 64 bit and the target - 32 bit. Thus local assertion prop + // got the "poisoned" value, which lead to silent bad codegen. + + public static int Main() + { + var r1 = 31; + // "Poisoned" value. + var s1 = 0b11 << r1; + + if (s1 == 0b11 << 31) + { + return 100; + } + + // Just so that Roslyn actually uses locals. + Use(s1); + Use(r1); + + return -1; + } + + private static void Use(int a) { } +} diff --git a/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.csproj b/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.csproj new file mode 100644 index 00000000000..edc51be9ca2 --- /dev/null +++ b/src/tests/JIT/Directed/ConstantFolding/folding_extends_int32_on_64_bit_hosts.csproj @@ -0,0 +1,10 @@ + + + Exe + True + None + + + + + From 086079a776c6c4d957efa0449f50d6fa5624d724 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 13 Jul 2021 01:37:09 -0700 Subject: [PATCH 467/926] Allow multiple private key references in Unix PFXes Windows has a complicated state for when a PFX contains two certificates that link to the same private key: * EphemeralKeySet: The PFX load fails. * PersistKeySet: Things probably work. * (normal): "It's complicated". When the Unix PFX loader was written it was based on the EphemeralKeySet behavior, because that's what the tests used (to avoid disk penalties and problems). Trying to maintain a balance between Herculean efforts of bug-for-bug compatibility and OS variability, this change takes a simpler approach: * EphemeralKeySet: The PFX load fails, like it will on Windows. * Otherwise: Let it work (but always with cloned keys, so some of the subtle Windows undesirable states are lost). --- .../Pal.Android/AndroidCertificatePal.cs | 7 +- .../Cryptography/Pal.Android/StorePal.cs | 11 +- .../Pal.OSX/AppleCertificatePal.Pkcs12.cs | 2 +- .../Internal/Cryptography/Pal.OSX/StorePal.cs | 5 +- .../Pal.Unix/OpenSslX509CertificateReader.cs | 5 +- .../Cryptography/Pal.Unix/PkcsFormatReader.cs | 38 ++- .../Cryptography/Pal.Unix/StorePal.cs | 15 +- .../Cryptography/Pal.Unix/UnixPkcs12Reader.cs | 28 +- .../AppleCertificatePal.ImportExport.cs | 4 +- .../Pal.iOS/AppleCertificatePal.Pkcs12.cs | 5 +- .../Internal/Cryptography/Pal.iOS/StorePal.cs | 3 +- .../tests/PfxFormatTests.cs | 253 ++++++++++++++++-- .../tests/PfxFormatTests_Collection.cs | 15 +- .../tests/PfxFormatTests_SingleCert.cs | 50 +++- 14 files changed, 377 insertions(+), 64 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs index 658c2da9e1a..2a987506f12 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs @@ -52,6 +52,7 @@ namespace Internal.Cryptography.Pal { Debug.Assert(password != null); + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); switch (contentType) @@ -62,7 +63,7 @@ namespace Internal.Cryptography.Pal // We don't support determining this on Android right now, so we throw. throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner); case X509ContentType.Pkcs12: - return ReadPkcs12(rawData, password); + return ReadPkcs12(rawData, password, ephemeralSpecified); case X509ContentType.Cert: default: { @@ -104,11 +105,11 @@ namespace Internal.Cryptography.Pal return true; } - private static ICertificatePal ReadPkcs12(ReadOnlySpan rawData, SafePasswordHandle password) + private static ICertificatePal ReadPkcs12(ReadOnlySpan rawData, SafePasswordHandle password, bool ephemeralSpecified) { using (var reader = new AndroidPkcs12Reader(rawData)) { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified); UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); AndroidCertificatePal pal = (AndroidCertificatePal)certAndKey.Cert!; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs index 09ec3ce5389..6187979d905 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs @@ -23,9 +23,11 @@ namespace Internal.Cryptography.Pal Debug.Assert(password != null); X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); + if (contentType == X509ContentType.Pkcs12) { - ICertificatePal[] certPals = ReadPkcs12Collection(rawData, password); + ICertificatePal[] certPals = ReadPkcs12Collection(rawData, password, ephemeralSpecified); return new AndroidCertLoader(certPals); } else @@ -108,11 +110,14 @@ namespace Internal.Cryptography.Pal throw new CryptographicException(message, new PlatformNotSupportedException(message)); } - private static ICertificatePal[] ReadPkcs12Collection(ReadOnlySpan rawData, SafePasswordHandle password) + private static ICertificatePal[] ReadPkcs12Collection( + ReadOnlySpan rawData, + SafePasswordHandle password, + bool ephemeralSpecified) { using (var reader = new AndroidPkcs12Reader(rawData)) { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified); ICertificatePal[] certs = new ICertificatePal[reader.GetCertCount()]; int idx = 0; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs index 4febd7080f0..1eaa3f8323a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs @@ -19,7 +19,7 @@ namespace Internal.Cryptography.Pal { using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData)) { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified: false); UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs index 86bbebc594d..e7cbefeb5b4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs @@ -47,7 +47,7 @@ namespace Internal.Cryptography.Pal ? Interop.AppleCrypto.SecKeychainCopyDefault() : Interop.AppleCrypto.CreateTemporaryKeychain(); - return ImportPkcs12(rawData, password, exportable, keychain); + return ImportPkcs12(rawData, password, exportable, ephemeralSpecified: false, keychain); } SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection( @@ -64,13 +64,14 @@ namespace Internal.Cryptography.Pal ReadOnlySpan rawData, SafePasswordHandle password, bool exportable, + bool ephemeralSpecified, SafeKeychainHandle keychain) { ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData); try { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified); return new ApplePkcs12CertLoader(reader, keychain, password, exportable); } catch diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs index e5561d9279c..942c6251f6c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs @@ -49,12 +49,13 @@ namespace Internal.Cryptography.Pal ICertificatePal? cert; Exception? openSslException; + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); if (TryReadX509Der(rawData, out cert) || TryReadX509Pem(rawData, out cert) || PkcsFormatReader.TryReadPkcs7Der(rawData, out cert) || PkcsFormatReader.TryReadPkcs7Pem(rawData, out cert) || - PkcsFormatReader.TryReadPkcs12(rawData, password, out cert, out openSslException)) + PkcsFormatReader.TryReadPkcs12(rawData, password, ephemeralSpecified, out cert, out openSslException)) { if (cert == null) { @@ -73,6 +74,7 @@ namespace Internal.Cryptography.Pal public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { ICertificatePal? pal; + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); // If we can't open the file, fail right away. using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(fileName, "rb")) @@ -87,6 +89,7 @@ namespace Internal.Cryptography.Pal PkcsFormatReader.TryReadPkcs12( File.ReadAllBytes(fileName), password, + ephemeralSpecified, out pal, out Exception? exception); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs index 31a65038d59..f73b5937323 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs @@ -253,24 +253,49 @@ namespace Internal.Cryptography.Pal return true; } - internal static bool TryReadPkcs12(ReadOnlySpan rawData, SafePasswordHandle password, [NotNullWhen(true)] out ICertificatePal? certPal, out Exception? openSslException) + internal static bool TryReadPkcs12( + ReadOnlySpan rawData, + SafePasswordHandle password, + bool ephemeralSpecified, + [NotNullWhen(true)] out ICertificatePal? certPal, + out Exception? openSslException) { List? ignored; - return TryReadPkcs12(rawData, password, true, out certPal!, out ignored, out openSslException); + return TryReadPkcs12( + rawData, + password, + single: true, + ephemeralSpecified, + out certPal!, + out ignored, + out openSslException); } - internal static bool TryReadPkcs12(ReadOnlySpan rawData, SafePasswordHandle password, [NotNullWhen(true)] out List? certPals, out Exception? openSslException) + internal static bool TryReadPkcs12( + ReadOnlySpan rawData, + SafePasswordHandle password, + bool ephemeralSpecified, + [NotNullWhen(true)] out List? certPals, + out Exception? openSslException) { ICertificatePal? ignored; - return TryReadPkcs12(rawData, password, false, out ignored, out certPals!, out openSslException); + return TryReadPkcs12( + rawData, + password, + single: false, + ephemeralSpecified, + out ignored, + out certPals!, + out openSslException); } private static bool TryReadPkcs12( ReadOnlySpan rawData, SafePasswordHandle password, bool single, + bool ephemeralSpecified, out ICertificatePal? readPal, out List? readCerts, out Exception? openSslException) @@ -287,7 +312,7 @@ namespace Internal.Cryptography.Pal using (pfx) { - return TryReadPkcs12(pfx, password, single, out readPal, out readCerts); + return TryReadPkcs12(pfx, password, single, ephemeralSpecified, out readPal, out readCerts); } } @@ -295,10 +320,11 @@ namespace Internal.Cryptography.Pal OpenSslPkcs12Reader pfx, SafePasswordHandle password, bool single, + bool ephemeralSpecified, out ICertificatePal? readPal, out List? readCerts) { - pfx.Decrypt(password); + pfx.Decrypt(password, ephemeralSpecified); if (single) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs index 749beed10ed..bef72007c38 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs @@ -23,6 +23,7 @@ namespace Internal.Cryptography.Pal Debug.Assert(password != null); ICertificatePal? singleCert; + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); if (OpenSslX509CertificateReader.TryReadX509Der(rawData, out singleCert) || OpenSslX509CertificateReader.TryReadX509Pem(rawData, out singleCert)) @@ -39,7 +40,7 @@ namespace Internal.Cryptography.Pal if (PkcsFormatReader.TryReadPkcs7Der(rawData, out certPals) || PkcsFormatReader.TryReadPkcs7Pem(rawData, out certPals) || - PkcsFormatReader.TryReadPkcs12(rawData, password, out certPals, out openSslException)) + PkcsFormatReader.TryReadPkcs12(rawData, password, ephemeralSpecified, out certPals, out openSslException)) { Debug.Assert(certPals != null); @@ -52,15 +53,21 @@ namespace Internal.Cryptography.Pal public static ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); + using (SafeBioHandle bio = Interop.Crypto.BioNewFile(fileName, "rb")) { Interop.Crypto.CheckValidOpenSslHandle(bio); - return FromBio(fileName, bio, password); + return FromBio(fileName, bio, password, ephemeralSpecified); } } - private static ILoaderPal FromBio(string fileName, SafeBioHandle bio, SafePasswordHandle password) + private static ILoaderPal FromBio( + string fileName, + SafeBioHandle bio, + SafePasswordHandle password, + bool ephemeralSpecified) { int bioPosition = Interop.Crypto.BioTell(bio); Debug.Assert(bioPosition >= 0); @@ -104,7 +111,7 @@ namespace Internal.Cryptography.Pal // Capture the exception so in case of failure, the call to BioSeek does not override it. Exception? openSslException; byte[] data = File.ReadAllBytes(fileName); - if (PkcsFormatReader.TryReadPkcs12(data, password, out certPals, out openSslException)) + if (PkcsFormatReader.TryReadPkcs12(data, password, ephemeralSpecified, out certPals, out openSslException)) { return ListToLoaderPal(certPals); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs index 5d4783dbc29..bf6cb9ce630 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs @@ -26,6 +26,7 @@ namespace Internal.Cryptography.Pal private CertAndKey[]? _certs; private int _certCount; private PointerMemoryManager? _tmpManager; + private bool _allowDoubleBind; protected abstract ICertificatePalCore ReadX509Der(ReadOnlyMemory data); protected abstract AsymmetricAlgorithm LoadKey(ReadOnlyMemory safeBagBagValue); @@ -180,11 +181,13 @@ namespace Internal.Cryptography.Pal ArrayPool.Shared.Return(rentedContents, clearArray: true); } - public void Decrypt(SafePasswordHandle password) + public void Decrypt(SafePasswordHandle password, bool ephemeralSpecified) { ReadOnlyMemory authSafeContents = Helpers.DecodeOctetStringAsMemory(_pfxAsn.AuthSafe.Content); + _allowDoubleBind = !ephemeralSpecified; + bool hasRef = false; password.DangerousAddRef(ref hasRef); @@ -314,6 +317,7 @@ namespace Internal.Cryptography.Pal ExtractPrivateKeys(password, keyBags, keyBagIdx, keys, publicKeyInfos); BuildCertsWithKeys( + password, certBags, certBagAttrs, certs, @@ -497,6 +501,7 @@ namespace Internal.Cryptography.Pal } private void BuildCertsWithKeys( + ReadOnlySpan password, CertBagAsn[] certBags, AttributeAsn[]?[] certBagAttrs, CertAndKey[] certs, @@ -550,13 +555,26 @@ namespace Internal.Cryptography.Pal if (matchingKeyIdx != -1) { + // Windows compat: + // If the PFX is loaded with EphemeralKeySet, don't allow double-bind. + // Otherwise, reload the key so a second instance is bound (avoiding one + // cert Dispose removing the key of another). if (keys[matchingKeyIdx] == null) { - throw new CryptographicException(SR.Cryptography_Pfx_BadKeyReference); + if (_allowDoubleBind) + { + certs[certBagIdx].Key = LoadKey(keyBags[matchingKeyIdx], password); + } + else + { + throw new CryptographicException(SR.Cryptography_Pfx_BadKeyReference); + } + } + else + { + certs[certBagIdx].Key = keys[matchingKeyIdx]; + keys[matchingKeyIdx] = null; } - - certs[certBagIdx].Key = keys[matchingKeyIdx]; - keys[matchingKeyIdx] = null; } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs index dfdb5b554f4..ad4baa38cc3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs @@ -103,6 +103,8 @@ namespace Internal.Cryptography.Pal { Debug.Assert(password != null); + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); + if (contentType == X509ContentType.Pkcs7) { throw new CryptographicException( @@ -116,7 +118,7 @@ namespace Internal.Cryptography.Pal // We ignore keyStorageFlags which is tracked in https://github.com/dotnet/runtime/issues/52434. // The keys are always imported as ephemeral and never persisted. Exportability is ignored for // the moment and it needs to be investigated how to map it to iOS keychain primitives. - return ImportPkcs12(rawData, password); + return ImportPkcs12(rawData, password, ephemeralSpecified); } SafeSecIdentityHandle identityHandle; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs index 87e45b117e8..8ff5988f557 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs @@ -22,11 +22,12 @@ namespace Internal.Cryptography.Pal private static AppleCertificatePal ImportPkcs12( ReadOnlySpan rawData, - SafePasswordHandle password) + SafePasswordHandle password, + bool ephemeralSpecified) { using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData)) { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified); return ImportPkcs12(reader.GetSingleCert()); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs index efb7d68349a..47d7485c0f9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs @@ -37,6 +37,7 @@ namespace Internal.Cryptography.Pal return new CertCollectionLoader(certificateList); } + bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); X509ContentType contentType = AppleCertificatePal.GetDerCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) @@ -52,7 +53,7 @@ namespace Internal.Cryptography.Pal try { - reader.Decrypt(password); + reader.Decrypt(password, ephemeralSpecified); return new ApplePkcs12CertLoader(reader, password); } catch diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests.cs index d9a40d068f0..240c0209d21 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests.cs @@ -29,7 +29,11 @@ namespace System.Security.Cryptography.X509Certificates.Tests protected static readonly X509KeyStorageFlags s_exportableImportFlags = s_importFlags | X509KeyStorageFlags.Exportable; + protected static readonly X509KeyStorageFlags s_perphemeralImportFlags = + X509KeyStorageFlags.UserKeySet; + private static readonly Pkcs9LocalKeyId s_keyIdOne = new Pkcs9LocalKeyId(new byte[] { 1 }); + private static readonly Pkcs9LocalKeyId s_keyIdTwo = new Pkcs9LocalKeyId(new byte[] { 2 }); // Windows 7 and 8.1 both fail the PFX load if any key is unloadable (even if not referenced). // Windows 10 1903, 1809, and 1607 all only fail when an unloadable key was actually needed. @@ -41,10 +45,36 @@ namespace System.Security.Cryptography.X509Certificates.Tests OperatingSystem.IsWindows() && !PlatformDetection.IsWindows10Version1607OrGreater; + private void ReadPfx( + byte[] pfxBytes, + string correctPassword, + X509Certificate2 expectedCert, + Action otherWork = null) + { + ReadPfx(pfxBytes, correctPassword, expectedCert, s_importFlags, otherWork); + } + + private void ReadMultiPfx( + byte[] pfxBytes, + string correctPassword, + X509Certificate2 expectedSingleCert, + X509Certificate2[] expectedOrder, + Action perCertOtherWork = null) + { + ReadMultiPfx( + pfxBytes, + correctPassword, + expectedSingleCert, + expectedOrder, + s_importFlags, + perCertOtherWork); + } + protected abstract void ReadPfx( byte[] pfxBytes, string correctPassword, X509Certificate2 expectedCert, + X509KeyStorageFlags nonExportFlags, Action otherWork = null); protected abstract void ReadMultiPfx( @@ -52,14 +82,26 @@ namespace System.Security.Cryptography.X509Certificates.Tests string correctPassword, X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, + X509KeyStorageFlags nonExportFlags, Action perCertOtherWork = null); + private void ReadUnreadablePfx( + byte[] pfxBytes, + string bestPassword, + // NTE_FAIL + int win32Error = -2146893792, + int altWin32Error = 0) + { + ReadUnreadablePfx(pfxBytes, bestPassword, s_importFlags, win32Error, altWin32Error); + } + protected abstract void ReadEmptyPfx(byte[] pfxBytes, string correctPassword); protected abstract void ReadWrongPassword(byte[] pfxBytes, string wrongPassword); protected abstract void ReadUnreadablePfx( byte[] pfxBytes, string bestPassword, + X509KeyStorageFlags importFlags, // NTE_FAIL int win32Error = -2146893792, int altWin32Error = 0); @@ -657,7 +699,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests [Fact] public void TwoCerts_CrossedKeys() { - string pw = nameof(SameCertTwice_NoKeys); + string pw = nameof(TwoCerts_CrossedKeys); using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags)) using (var cert2 = new X509Certificate2(TestData.MsCertificate)) @@ -680,12 +722,48 @@ namespace System.Security.Cryptography.X509Certificates.Tests byte[] pfxBytes = builder.Encode(); // Windows seems to be applying both the implicit match and the LocalKeyId match, - // so it detects two hits against the same key and fails. - ReadUnreadablePfx( - pfxBytes, - pw, - // NTE_BAD_DATA - -2146893819); + // so it detects two hits against the same key and fails for ephemeral import. + + if (s_importFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)) + { + ReadUnreadablePfx( + pfxBytes, + pw, + // NTE_BAD_DATA + -2146893819); + } + else + { + // This is somewhat circular logic, but it's hard to bind cert2 with the wrong + // private key any other way. + + using (ImportedCollection coll = Cert.Import(pfxBytes, pw, s_perphemeralImportFlags)) + { + X509Certificate2 cert2WithKey = coll.Collection[0]; + + ReadMultiPfx( + pfxBytes, + pw, + cert2WithKey, + new[] { cert2WithKey, cert }, + x => + { + if (x.Equals(cert)) + { + CheckMultiBoundKeyConsistency(x); + } + else if (x.HasPrivateKey) + { + // On macOS cert2WithKey.HasPrivateKey is + // false because the SecIdentityRef won't + // bind the mismatched private key. + CheckMultiBoundKeyConsistencyFails(x); + } + }); + + AssertExtensions.SequenceEqual(cert2.RawData, cert2WithKey.RawData); + } + } } } @@ -724,14 +802,17 @@ namespace System.Security.Cryptography.X509Certificates.Tests builder.SealWithMac(pw, s_digestAlgorithm, MacCount); byte[] pfxBytes = builder.Encode(); - if (addLocalKeyId) + // If we add the localKeyId values then everything works out, otherwise + // we end up doing SubjectPublicKeyInfo matching logic which says two certs + // mapped to one key. This fails for ephemeral import, and succeeds otherwise. + if (addLocalKeyId || !s_importFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)) { ReadMultiPfx( pfxBytes, pw, cert, new[] { cert, cert }, - CheckKeyConsistency); + addLocalKeyId ? CheckKeyConsistency : CheckMultiBoundKeyConsistency); } else { @@ -774,11 +855,23 @@ namespace System.Security.Cryptography.X509Certificates.Tests builder.SealWithMac(pw, s_digestAlgorithm, MacCount); byte[] pfxBytes = builder.Encode(); - ReadUnreadablePfx( - pfxBytes, - pw, - // NTE_BAD_DATA - -2146893819); + if (s_importFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)) + { + ReadUnreadablePfx( + pfxBytes, + pw, + // NTE_BAD_DATA + -2146893819); + } + else + { + ReadMultiPfx( + pfxBytes, + pw, + cert, + new[] { cert, cert }, + CheckMultiBoundKeyConsistency); + } } } @@ -812,11 +905,88 @@ namespace System.Security.Cryptography.X509Certificates.Tests builder.SealWithMac(pw, s_digestAlgorithm, MacCount); byte[] pfxBytes = builder.Encode(); - ReadUnreadablePfx( + if (s_importFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)) + { + ReadUnreadablePfx( + pfxBytes, + pw, + // NTE_BAD_DATA + -2146893819); + } + + // On Windows with perphemeral import the private key gets written then + // erased during import (cleaning resources associated with non-returned certs) + // so on Windows with a single-object load using the key will fail. + // + // The collection import is on a razors edge with the perphemeral import: + // once one of the certs gets disposed the other(s) can no longer open + // their private key. + + ReadMultiPfx( pfxBytes, pw, - // NTE_BAD_DATA - -2146893819); + cert, + new[] { cert, cert }, + s_perphemeralImportFlags, + CheckMultiBoundKeyConsistency); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CertTwice_KeyOnce_OtherCertBetter(bool addLocalKeyId) + { + string pw = nameof(CertTwice_KeyOnce); + + using (var rsaCert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags)) + using (var ecCert = new X509Certificate2(TestData.ECDsaP256_DigitalSignature_Pfx_Windows, "Test", s_exportableImportFlags)) + using (RSA rsa = rsaCert.GetRSAPrivateKey()) + using (ECDsa ecdsa = ecCert.GetECDsaPrivateKey()) + { + Pkcs12Builder builder = new Pkcs12Builder(); + Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); + Pkcs12SafeContents certContents = new Pkcs12SafeContents(); + + Pkcs12SafeBag rsaKeyBag = keyContents.AddShroudedKey(rsa, pw, s_windowsPbe); + Pkcs12SafeBag ecKeyBag = keyContents.AddShroudedKey(ecdsa, pw, s_windowsPbe); + Pkcs12SafeBag certBag = certContents.AddCertificate(ecCert); + Pkcs12SafeBag certBag2 = certContents.AddCertificate(ecCert); + Pkcs12SafeBag rsaCertBag = certContents.AddCertificate(rsaCert); + + if (addLocalKeyId) + { + certBag.Attributes.Add(s_keyIdOne); + certBag2.Attributes.Add(s_keyIdOne); + ecKeyBag.Attributes.Add(s_keyIdOne); + rsaCertBag.Attributes.Add(s_keyIdTwo); + rsaKeyBag.Attributes.Add(s_keyIdTwo); + } + + AddContents(keyContents, builder, pw, encrypt: false); + AddContents(certContents, builder, pw, encrypt: true); + builder.SealWithMac(pw, s_digestAlgorithm, MacCount); + byte[] pfxBytes = builder.Encode(); + + if (s_importFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)) + { + ReadUnreadablePfx( + pfxBytes, + pw, + // NTE_BAD_DATA + -2146893819); + } + + // Because cert2 is what gets returned from the single cert ctor, the + // multiply referenced key doesn't cause a runtime problem on Windows. + + ReadMultiPfx( + pfxBytes, + pw, + rsaCert, + new[] { rsaCert, ecCert, ecCert }, + s_perphemeralImportFlags, + CheckKeyConsistency); } } @@ -928,18 +1098,46 @@ namespace System.Security.Cryptography.X509Certificates.Tests private static void CheckKeyConsistency(X509Certificate2 cert) { - using (RSA priv = cert.GetRSAPrivateKey()) - using (RSA pub = cert.GetRSAPublicKey()) - { - byte[] data = { 2, 7, 4 }; - byte[] signature = priv.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + byte[] data = { 2, 7, 4 }; - Assert.True( - pub.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), - "Cert public key verifies signature from cert private key"); + switch (cert.GetKeyAlgorithm()) + { + case "1.2.840.113549.1.1.1": + { + using (RSA priv = cert.GetRSAPrivateKey()) + using (RSA pub = cert.GetRSAPublicKey()) + { + byte[] signature = priv.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + + Assert.True( + pub.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), + "Cert public key verifies signature from cert private key"); + } + + break; + } + default: + { + using (ECDsa priv = cert.GetECDsaPrivateKey()) + using (ECDsa pub = cert.GetECDsaPublicKey()) + { + byte[] signature = priv.SignData(data, HashAlgorithmName.SHA256); + + Assert.True( + pub.VerifyData(data, signature, HashAlgorithmName.SHA256), + "Cert public key verifies signature from cert private key"); + } + + break; + } } } + protected virtual void CheckMultiBoundKeyConsistency(X509Certificate2 cert) + { + CheckKeyConsistency(cert); + } + private static void CheckKeyConsistencyFails(X509Certificate2 cert) { using (RSA priv = cert.GetRSAPrivateKey()) @@ -954,6 +1152,11 @@ namespace System.Security.Cryptography.X509Certificates.Tests } } + protected virtual void CheckMultiBoundKeyConsistencyFails(X509Certificate2 cert) + { + CheckKeyConsistencyFails(cert); + } + private static void AddContents( Pkcs12SafeContents contents, Pkcs12Builder builder, diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_Collection.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_Collection.cs index 37ca1cc3023..c25d3391ddb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_Collection.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_Collection.cs @@ -12,10 +12,13 @@ namespace System.Security.Cryptography.X509Certificates.Tests byte[] pfxBytes, string correctPassword, X509Certificate2 expectedCert, + X509KeyStorageFlags nonExportFlags, Action otherWork) { - ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, s_importFlags); - ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, s_exportableImportFlags); + X509KeyStorageFlags exportFlags = nonExportFlags | X509KeyStorageFlags.Exportable; + + ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, nonExportFlags); + ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -23,6 +26,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests string correctPassword, X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, + X509KeyStorageFlags nonExportFlags, Action perCertOtherWork) { ReadPfx( @@ -31,7 +35,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests expectedSingleCert, expectedOrder, perCertOtherWork, - s_importFlags); + nonExportFlags); ReadPfx( pfxBytes, @@ -39,7 +43,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests expectedSingleCert, expectedOrder, perCertOtherWork, - s_exportableImportFlags); + nonExportFlags | X509KeyStorageFlags.Exportable); } private void ReadPfx( @@ -89,13 +93,14 @@ namespace System.Security.Cryptography.X509Certificates.Tests protected override void ReadUnreadablePfx( byte[] pfxBytes, string bestPassword, + X509KeyStorageFlags importFlags, int win32Error, int altWin32Error) { X509Certificate2Collection coll = new X509Certificate2Collection(); CryptographicException ex = Assert.ThrowsAny( - () => coll.Import(pfxBytes, bestPassword, s_importFlags)); + () => coll.Import(pfxBytes, bestPassword, importFlags)); if (OperatingSystem.IsWindows()) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_SingleCert.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_SingleCert.cs index 39f736d259d..4e219e346a9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_SingleCert.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_SingleCert.cs @@ -11,10 +11,13 @@ namespace System.Security.Cryptography.X509Certificates.Tests byte[] pfxBytes, string correctPassword, X509Certificate2 expectedCert, + X509KeyStorageFlags nonExportFlags, Action otherWork) { - ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, s_importFlags); - ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, s_exportableImportFlags); + X509KeyStorageFlags exportFlags = nonExportFlags | X509KeyStorageFlags.Exportable; + + ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, nonExportFlags); + ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -22,10 +25,13 @@ namespace System.Security.Cryptography.X509Certificates.Tests string correctPassword, X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, + X509KeyStorageFlags nonExportFlags, Action perCertOtherWork) { - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, s_importFlags); - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, s_exportableImportFlags); + X509KeyStorageFlags exportFlags = nonExportFlags | X509KeyStorageFlags.Exportable; + + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); } private void ReadPfx( @@ -62,11 +68,12 @@ namespace System.Security.Cryptography.X509Certificates.Tests protected override void ReadUnreadablePfx( byte[] pfxBytes, string bestPassword, + X509KeyStorageFlags importFlags, int win32Error, int altWin32Error) { CryptographicException ex = Assert.ThrowsAny( - () => new X509Certificate2(pfxBytes, bestPassword, s_importFlags)); + () => new X509Certificate2(pfxBytes, bestPassword, importFlags)); if (OperatingSystem.IsWindows()) { @@ -80,5 +87,38 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.NotNull(ex.InnerException); } } + + private static void CheckBadKeyset(X509Certificate2 cert) + { + CryptographicException ex = Assert.ThrowsAny( + () => cert.GetRSAPrivateKey()); + + // NTE_BAD_KEYSET + Assert.Equal(-2146893802, ex.HResult); + } + + protected override void CheckMultiBoundKeyConsistency(X509Certificate2 cert) + { + if (PlatformDetection.IsWindows) + { + CheckBadKeyset(cert); + } + else + { + base.CheckMultiBoundKeyConsistency(cert); + } + } + + protected override void CheckMultiBoundKeyConsistencyFails(X509Certificate2 cert) + { + if (PlatformDetection.IsWindows) + { + CheckBadKeyset(cert); + } + else + { + base.CheckMultiBoundKeyConsistencyFails(cert); + } + } } } From b72548e53115d1913f218a1275944fc2d01eaf18 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 13 Jul 2021 01:38:33 -0700 Subject: [PATCH 468/926] Adjust crypto shim for functions renamed for OSSL3 beta1 --- .../opensslshim.h | 15 +++++++++------ .../osslcompat_30.h | 3 +++ .../System.Security.Cryptography.Native/pal_evp.c | 2 +- .../pal_evp_pkey.c | 2 +- .../pal_evp_pkey_rsa.c | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h index 09d8351288c..050cdc25140 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h @@ -318,7 +318,7 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); 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_size) \ + RENAMED_FUNCTION(EVP_MD_get_size, EVP_MD_size) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \ @@ -329,7 +329,6 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_padding) \ FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_pss_saltlen) \ FALLBACK_FUNCTION(EVP_PKEY_CTX_set_signature_md) \ - REQUIRED_FUNCTION(EVP_PKEY_base_id) \ REQUIRED_FUNCTION(EVP_PKEY_decrypt) \ REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \ REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \ @@ -338,6 +337,8 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); REQUIRED_FUNCTION(EVP_PKEY_encrypt) \ REQUIRED_FUNCTION(EVP_PKEY_encrypt_init) \ REQUIRED_FUNCTION(EVP_PKEY_free) \ + RENAMED_FUNCTION(EVP_PKEY_get_base_id, EVP_PKEY_base_id) \ + RENAMED_FUNCTION(EVP_PKEY_get_size, EVP_PKEY_size) \ FALLBACK_FUNCTION(EVP_PKEY_get0_RSA) \ REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \ REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \ @@ -350,7 +351,6 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \ REQUIRED_FUNCTION(EVP_PKEY_sign) \ REQUIRED_FUNCTION(EVP_PKEY_sign_init) \ - REQUIRED_FUNCTION(EVP_PKEY_size) \ FALLBACK_FUNCTION(EVP_PKEY_up_ref) \ REQUIRED_FUNCTION(EVP_PKEY_verify) \ REQUIRED_FUNCTION(EVP_PKEY_verify_init) \ @@ -754,7 +754,7 @@ 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_size EVP_MD_size_ptr +#define EVP_MD_get_size EVP_MD_get_size_ptr #define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr @@ -765,7 +765,6 @@ FOR_ALL_OPENSSL_FUNCTIONS #define EVP_PKEY_CTX_set_rsa_padding EVP_PKEY_CTX_set_rsa_padding_ptr #define EVP_PKEY_CTX_set_rsa_pss_saltlen EVP_PKEY_CTX_set_rsa_pss_saltlen_ptr #define EVP_PKEY_CTX_set_signature_md EVP_PKEY_CTX_set_signature_md_ptr -#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr #define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr #define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr @@ -774,6 +773,8 @@ FOR_ALL_OPENSSL_FUNCTIONS #define EVP_PKEY_encrypt_init EVP_PKEY_encrypt_init_ptr #define EVP_PKEY_encrypt EVP_PKEY_encrypt_ptr #define EVP_PKEY_free EVP_PKEY_free_ptr +#define EVP_PKEY_get_base_id EVP_PKEY_get_base_id_ptr +#define EVP_PKEY_get_size EVP_PKEY_get_size_ptr #define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr @@ -786,7 +787,6 @@ FOR_ALL_OPENSSL_FUNCTIONS #define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr #define EVP_PKEY_sign_init EVP_PKEY_sign_init_ptr #define EVP_PKEY_sign EVP_PKEY_sign_ptr -#define EVP_PKEY_size EVP_PKEY_size_ptr #define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr #define EVP_PKEY_verify_init EVP_PKEY_verify_init_ptr #define EVP_PKEY_verify EVP_PKEY_verify_ptr @@ -1078,6 +1078,9 @@ FOR_ALL_OPENSSL_FUNCTIONS #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM // Undo renames for renamed-in-3.0 +#define EVP_MD_get_size EVP_MD_size +#define EVP_PKEY_get_base_id EVP_PKEY_base_id +#define EVP_PKEY_get_size EVP_PKEY_size #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h index bb529df51ee..dba69f1382d 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h @@ -19,10 +19,13 @@ 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_CTX_get_original_iv(EVP_CIPHER_CTX *ctx, void *buf, size_t len); +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); int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode); int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen); int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md); +int EVP_PKEY_get_base_id(const EVP_PKEY* pkey); +int EVP_PKEY_get_size(const EVP_PKEY* pkey); OSSL_PROVIDER* OSSL_PROVIDER_try_load(OSSL_LIB_CTX* , const char* name, int retain_fallbacks); X509* SSL_get1_peer_certificate(const SSL* ssl); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp.c index a119f8178f0..9cf042a29e9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp.c @@ -119,7 +119,7 @@ int32_t CryptoNative_EvpDigestOneShot(const EVP_MD* type, const void* source, in int32_t CryptoNative_EvpMdSize(const EVP_MD* md) { - return EVP_MD_size(md); + return EVP_MD_get_size(md); } const EVP_MD* CryptoNative_EvpMd5() diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c index 83b18f7621f..a9fd4b2acd5 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c @@ -20,7 +20,7 @@ void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey) int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey) { assert(pkey != NULL); - return EVP_PKEY_size(pkey); + return EVP_PKEY_get_size(pkey); } int32_t CryptoNative_UpRefEvpPkey(EVP_PKEY* pkey) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c index fa88420cf96..0efe651cfe8 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c @@ -277,7 +277,7 @@ int32_t CryptoNative_RsaVerifyHash(EVP_PKEY* pkey, // EVP_PKEY_verify is not consistent on whether a mis-sized hash is an error or just a mismatch. // Normalize to mismatch. - if (hashLen != EVP_MD_size(digest)) + if (hashLen != EVP_MD_get_size(digest)) { ret = 0; goto done; From 455c0d846e56a40d0806a5551e0d88a01b5e4407 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 9 Jul 2021 15:25:19 -0400 Subject: [PATCH 469/926] Update pinned C# compiler version --- eng/Versions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 24788db6f92..4f3f2a4c21a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -17,8 +17,8 @@ release true - - 4.0.0-2.21323.11 + + 4.0.0-3.21362.7 true false false From 85ff03d4a86d58059ab9ee1bb998aafac91ab067 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 10 Jul 2021 09:56:43 -0400 Subject: [PATCH 470/926] Fix async method builder override tests for compiler constraint --- .../tests/PoolingAsyncValueTaskMethodBuilderTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs index c2e83881ce5..62a6406c309 100644 --- a/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs @@ -390,7 +390,7 @@ namespace System.Threading.Tasks.Tests Assert.Equal(42 + yields, await ValueTaskReturningAsyncMethod(42)); Assert.Equal(84 + yields, await ValueTaskReturningAsyncMethod(84)); - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] async ValueTask ValueTaskReturningAsyncMethod(int result) { for (int i = 0; i < yields; i++) @@ -440,7 +440,7 @@ namespace System.Threading.Tasks.Tests } } - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] async ValueTask ValueTaskInt32ReturningMethod() { for (int i = 0; i < 3; i++) @@ -499,7 +499,7 @@ namespace System.Threading.Tasks.Tests { Assert.Equal(42 + i, await ValueTaskAsync(i)); - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] static async ValueTask ValueTaskAsync(int i) { await Task.Delay(1); @@ -549,7 +549,7 @@ namespace System.Threading.Tasks.Tests Assert.InRange(boxes.Distinct().Count(), 1, boxes.Count - 1); }, new RemoteInvokeOptions() { StartInfo = psi }).Dispose(); - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] static async ValueTask ComputeAsync(int input, ConcurrentQueue boxes) { await RecursiveValueTaskAsync(3, boxes); From 6e6c9c3aa12721108163e01cf86bce7c7ae1823b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 12:54:46 -0400 Subject: [PATCH 471/926] Temporarily remove string interpolation from NetCoreServer test helper --- .../NetCoreServer/Handlers/EchoWebSocketHandler.cs | 2 +- .../NetCoreServer/Handlers/RedirectHandler.cs | 8 ++++---- .../NetCoreServer/Handlers/StatusCodeHandler.cs | 2 +- .../NetCoreServer/Handlers/VersionHandler.cs | 14 +++++++------- .../NetCoreServer/Helpers/AuthenticationHelper.cs | 9 ++++----- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoWebSocketHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoWebSocketHandler.cs index 21ee6346eff..8e8951179e8 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoWebSocketHandler.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoWebSocketHandler.cs @@ -80,7 +80,7 @@ namespace NetCoreServer await socket.CloseAsync( closeStatus, replyWithEnhancedCloseMessage ? - $"Server received: {(int)closeStatus} {receiveResult.CloseStatusDescription}" : + ("Server received: " + (int)closeStatus + " " + receiveResult.CloseStatusDescription) : receiveResult.CloseStatusDescription, CancellationToken.None); } diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/RedirectHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/RedirectHandler.cs index cb7ce162907..596b8be559e 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/RedirectHandler.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/RedirectHandler.cs @@ -20,14 +20,14 @@ namespace NetCoreServer if (statusCode < 300 || statusCode > 308) { context.Response.StatusCode = 400; - context.Response.SetStatusDescription($"Invalid redirect statuscode: {statusCodeString}"); + context.Response.SetStatusDescription("Invalid redirect statuscode: " + statusCodeString); return; } } catch (Exception) { context.Response.StatusCode = 400; - context.Response.SetStatusDescription($"Error parsing statuscode: {statusCodeString}"); + context.Response.SetStatusDescription("Error parsing statuscode: " + statusCodeString); return; } } @@ -36,7 +36,7 @@ namespace NetCoreServer if (string.IsNullOrEmpty(redirectUri)) { context.Response.StatusCode = 400; - context.Response.SetStatusDescription($"Missing redirection uri"); + context.Response.SetStatusDescription("Missing redirection uri"); return; } @@ -51,7 +51,7 @@ namespace NetCoreServer catch (Exception) { context.Response.StatusCode = 400; - context.Response.SetStatusDescription($"Error parsing hops: {hopsString}"); + context.Response.SetStatusDescription("Error parsing hops: " + hopsString); return; } } diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/StatusCodeHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/StatusCodeHandler.cs index ae039a3c38f..73cb4bba880 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/StatusCodeHandler.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/StatusCodeHandler.cs @@ -22,7 +22,7 @@ namespace NetCoreServer catch (Exception) { context.Response.StatusCode = 400; - context.Response.SetStatusDescription($"Error parsing statuscode: {statusCodeString}"); + context.Response.SetStatusDescription("Error parsing statuscode: " + statusCodeString); } } } diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/VersionHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/VersionHandler.cs index 2cffa413a75..cba65e49d52 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/VersionHandler.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/VersionHandler.cs @@ -30,13 +30,13 @@ namespace NetCoreServer FileVersionInfo fi = FileVersionInfo.GetVersionInfo(path); var buffer = new StringBuilder(); - buffer.AppendLine($"Information for: {Path.GetFileName(path)}"); - buffer.AppendLine($"Location: {Path.GetDirectoryName(path)}"); - buffer.AppendLine($"Framework: {RuntimeInformation.FrameworkDescription}"); - buffer.AppendLine($"File Version: {fi.FileVersion}"); - buffer.AppendLine($"Product Version: {fi.ProductVersion}"); - buffer.AppendLine($"Creation Date: {File.GetCreationTime(path)}"); - buffer.AppendLine($"Last Modified: {File.GetLastWriteTime(path)}"); + buffer.AppendLine("Information for: " + Path.GetFileName(path)); + buffer.AppendLine("Location: " + Path.GetDirectoryName(path)); + buffer.AppendLine("Framework: " + RuntimeInformation.FrameworkDescription); + buffer.AppendLine("File Version: " + fi.FileVersion); + buffer.AppendLine("Product Version: " + fi.ProductVersion); + buffer.AppendLine("Creation Date: " + File.GetCreationTime(path)); + buffer.AppendLine("Last Modified: " + File.GetLastWriteTime(path)); return buffer.ToString(); } diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Helpers/AuthenticationHelper.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Helpers/AuthenticationHelper.cs index c0d0d2e1dee..e7a89febbc2 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Helpers/AuthenticationHelper.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Helpers/AuthenticationHelper.cs @@ -34,7 +34,7 @@ namespace NetCoreServer else if (authType != null) { context.Response.StatusCode = 501; - context.Response.SetStatusDescription($"Unsupported auth type: {authType}"); + context.Response.SetStatusDescription("Unsupported auth type: " + authType); return false; } @@ -57,14 +57,14 @@ namespace NetCoreServer if (split.Length < 2) { context.Response.StatusCode = 500; - context.Response.SetStatusDescription($"Invalid Authorization header: {authHeader}"); + context.Response.SetStatusDescription("Invalid Authorization header: " + authHeader); ; return false; } if (!string.Equals("basic", split[0], StringComparison.OrdinalIgnoreCase)) { context.Response.StatusCode = 500; - context.Response.SetStatusDescription($"Unsupported auth type: {split[0]}"); + context.Response.SetStatusDescription("Unsupported auth type: " + split[0]); return false; } @@ -106,8 +106,7 @@ namespace NetCoreServer // We don't fully support this authentication method. context.Response.StatusCode = 501; - context.Response.SetStatusDescription( - $"Attempt to use unsupported challenge/response auth type. {authType}: {authHeader}"); + context.Response.SetStatusDescription("Attempt to use unsupported challenge/response auth type. " + authType + ": " + authHeader); return false; } From 7eba2f3989eaafe9fb7676dd63a9f5a285f65fa5 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 14:32:36 -0400 Subject: [PATCH 472/926] Workaround eventpipe deadlock due to ArrayPool usage --- src/tests/profiler/eventpipe/eventpipe.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/profiler/eventpipe/eventpipe.cs b/src/tests/profiler/eventpipe/eventpipe.cs index 17f6d3f6ed9..5b58b6d62fe 100644 --- a/src/tests/profiler/eventpipe/eventpipe.cs +++ b/src/tests/profiler/eventpipe/eventpipe.cs @@ -3,6 +3,7 @@ using Profiler.Tests; using System; +using System.Buffers; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Tracing; @@ -35,6 +36,8 @@ namespace EventPipeTests public static int RunTest() { + ArrayPool.Shared.Rent(1); // workaround for https://github.com/dotnet/runtime/issues/1892 + bool success = true; int allTypesEventCount = 0; int arrayTypeEventCount = 0; From d7d32f835bacdebfae6e9b819761c9b227d025dd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 15:18:29 -0400 Subject: [PATCH 473/926] Avoid obscuring failure by ODE thrown from Dispose --- src/libraries/System.Console/tests/Helpers.cs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Console/tests/Helpers.cs b/src/libraries/System.Console/tests/Helpers.cs index f1d6205dc9b..8c057a1c32d 100644 --- a/src/libraries/System.Console/tests/Helpers.cs +++ b/src/libraries/System.Console/tests/Helpers.cs @@ -17,29 +17,17 @@ class Helpers try { - using (MemoryStream memStream = new MemoryStream()) - { - using (StreamWriter sw = new StreamWriter(memStream)) - { - setHelper(sw); + var memStream = new MemoryStream(); + var sw = new StreamWriter(memStream); + setHelper(sw); - TextWriter newStream = getHelper(); + TextWriter newStream = getHelper(); + Assert.NotNull(newStream); + newStream.Write(TestString); + newStream.Flush(); - Assert.NotNull(newStream); - - newStream.Write(TestString); - newStream.Flush(); - - memStream.Seek(0, SeekOrigin.Begin); - - using (StreamReader sr = new StreamReader(memStream)) - { - string fromConsole = readHelper(sr); - - Assert.Equal(TestString, fromConsole); - } - } - } + memStream.Seek(0, SeekOrigin.Begin); + Assert.Equal(TestString, readHelper(new StreamReader(memStream))); } finally { From b1375a9c34966c563bc5ebeab9b5964682a3e939 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 21:37:58 -0400 Subject: [PATCH 474/926] Fix nullable warning in AssemblyLoadContext30Extensions.cs --- .../AssemblyLoadContext30Extensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/Loader/AssemblyLoadContext30Extensions/AssemblyLoadContext30Extensions.cs b/src/tests/Loader/AssemblyLoadContext30Extensions/AssemblyLoadContext30Extensions.cs index bb05cb2ec72..cd730ea27ba 100644 --- a/src/tests/Loader/AssemblyLoadContext30Extensions/AssemblyLoadContext30Extensions.cs +++ b/src/tests/Loader/AssemblyLoadContext30Extensions/AssemblyLoadContext30Extensions.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + using System; using System.Reflection; using System.Reflection.Emit; @@ -218,7 +219,7 @@ public class Program assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); } - AssemblyLoadContext? context = AssemblyLoadContext.GetLoadContext(assemblyBuilder); + AssemblyLoadContext context = AssemblyLoadContext.GetLoadContext(assemblyBuilder); Assert(context != null); Assert(alc == context); From 50f86dde07a4fb9ece65133ba53b923b5d8725e7 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 13 Jul 2021 15:04:15 +0200 Subject: [PATCH 475/926] Fix UMThunkMarshInfo delete (#55559) There was a forgotten call to c++ delete that should have been converted to LoaderHeap::BackoutMem in a code path invoked only if two threads raced for allocating the UMThunkMarshInfo and the one that lost the race needed to delete it. --- src/coreclr/vm/comdelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 1b61e16dec5..03cecd83802 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1263,7 +1263,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) pUMThunkMarshInfo, NULL ) != NULL) { - delete pUMThunkMarshInfo; + pMT->GetLoaderAllocator()->GetStubHeap()->BackoutMem(pUMThunkMarshInfo, sizeof(UMThunkMarshInfo)); pUMThunkMarshInfo = pClass->m_pUMThunkMarshInfo; } } From 38c187aa5de1719cdf7167fb5880a3a7dd01ec15 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Tue, 13 Jul 2021 16:16:17 +0300 Subject: [PATCH 476/926] Use ReflectionOnly as data contract serializer option for iOS/tvOS/MacCatalyst (#55503) An attempt to address #47114 by making the System.Runtime.Serialization.DataContractSerializer.Option property return ReflectionOnly value when RuntimeFeature.IsDynamicCodeSupported is true. --- .../Serialization/DataContractSerializer.cs | 2 +- .../XmlFormatWriterGeneratorAOT/Program.cs | 30 +++++++++++++++++++ ...or.XmlFormatWriterGeneratorAot.Test.csproj | 19 ++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/Program.cs create mode 100644 src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/iOS.Simulator.XmlFormatWriterGeneratorAot.Test.csproj diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index 71f4530f4f3..72229faaf8d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -38,7 +38,7 @@ namespace System.Runtime.Serialization private static bool _optionAlreadySet; internal static SerializationOption Option { - get { return _option; } + get { return RuntimeFeature.IsDynamicCodeSupported ? _option : SerializationOption.ReflectionOnly; } set { if (_optionAlreadySet) diff --git a/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/Program.cs new file mode 100644 index 00000000000..884dfa7ae89 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/Program.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +public static class Program +{ + [DllImport("__Internal")] + public static extern void mono_ios_set_summary (string value); + + public static async Task Main(string[] args) + { + mono_ios_set_summary($"Starting functional test"); + + var ds = new DataContractSerializer (typeof (IEnumerable)); + using (var xw = XmlWriter.Create (System.IO.Stream.Null)) + ds.WriteObject (xw, new int [] { 1, 2, 3 }); + + Console.WriteLine("Done!"); + await Task.Delay(5000); + + return 42; + } +} diff --git a/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/iOS.Simulator.XmlFormatWriterGeneratorAot.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/iOS.Simulator.XmlFormatWriterGeneratorAot.Test.csproj new file mode 100644 index 00000000000..f0a56aa7fdf --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT/iOS.Simulator.XmlFormatWriterGeneratorAot.Test.csproj @@ -0,0 +1,19 @@ + + + + Exe + false + true + true + $(NetCoreAppCurrent) + iOSSimulator + iOS.Simulator.XmlFormatWriterGeneratorAot.Test.dll + false + 42 + true + + + + + + From 0aa781c265e941efd73b1e6f35d6e182255bb94d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 13 Jul 2021 15:27:54 +0200 Subject: [PATCH 477/926] Fix exception (#55324) --- src/libraries/System.Text.Json/src/Resources/Strings.resx | 2 +- .../src/System/Text/Json/ThrowHelper.Serialization.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index e289dfbb1ea..e654d368051 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -352,7 +352,7 @@ The data extension property '{0}.{1}' does not match the required signature of 'IDictionary<string, JsonElement>', 'IDictionary<string, object>' or 'JsonObject'. - The type '{0}' cannot have more than one property that has the attribute '{1}'. + The type '{0}' cannot have more than one member that has the attribute '{1}'. The type '{0}' is not supported. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 42af83d147a..61d953ac94c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -421,7 +421,7 @@ namespace System.Text.Json [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidOperationException_SerializationDuplicateTypeAttribute(Type classType) { - throw new InvalidOperationException(SR.Format(SR.SerializationDuplicateTypeAttribute, classType, typeof(Attribute))); + throw new InvalidOperationException(SR.Format(SR.SerializationDuplicateTypeAttribute, classType, typeof(TAttribute))); } [DoesNotReturn] From f91513241907441106934c9398799f63d3f8bc85 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 13 Jul 2021 15:43:49 +0200 Subject: [PATCH 478/926] Process.Unix: consider executable permission while searching PATH. (#55504) --- .../Unix/System.Native/Interop.GetGroups.cs | 50 +++++++++++++++++ .../Unix/System.Native/Interop.Permissions.cs | 2 + .../Native/Unix/System.Native/entrypoints.c | 1 + .../Native/Unix/System.Native/pal_uid.c | 8 +++ .../Native/Unix/System.Native/pal_uid.h | 9 ++++ .../src/System.Diagnostics.Process.csproj | 12 ++++- .../src/System/Diagnostics/Process.Unix.cs | 53 ++++++++++++++++++- .../tests/ProcessTests.Unix.cs | 34 ++++++++++++ .../System.Private.CoreLib.Shared.projitems | 2 +- 9 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs new file mode 100644 index 00000000000..2fd31563f17 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal static unsafe uint[]? GetGroups() + { + const int InitialGroupsLength = +#if DEBUG + 1; +#else + 64; +#endif + Span groups = stackalloc uint[InitialGroupsLength]; + do + { + int rv; + fixed (uint* pGroups = groups) + { + rv = Interop.Sys.GetGroups(groups.Length, pGroups); + } + + if (rv >= 0) + { + // success + return groups.Slice(0, rv).ToArray(); + } + else if (rv == -1 && Interop.Sys.GetLastError() == Interop.Error.EINVAL) + { + // increase buffer size + groups = new uint[groups.Length * 2]; + } + else + { + // failure + return null; + } + } + while (true); + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetGroups", SetLastError = true)] + private static extern unsafe int GetGroups(int ngroups, uint* groups); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs index cbc54d4072c..8248077deaf 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs @@ -26,6 +26,8 @@ internal static partial class Interop S_IROTH = 0x4, S_IWOTH = 0x2, S_IXOTH = 0x1, + + S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH, } } } diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index bf6f98953f9..c8b678ec083 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -256,6 +256,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_HandleNonCanceledPosixSignal) DllImportEntry(SystemNative_SetPosixSignalHandler) DllImportEntry(SystemNative_GetPlatformSignalNumber) + DllImportEntry(SystemNative_GetGroups) }; EXTERN_C const void* SystemResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Native/pal_uid.c b/src/libraries/Native/Unix/System.Native/pal_uid.c index 2807d06b77d..6571fb1e600 100644 --- a/src/libraries/Native/Unix/System.Native/pal_uid.c +++ b/src/libraries/Native/Unix/System.Native/pal_uid.c @@ -234,3 +234,11 @@ int32_t SystemNative_GetGroupList(const char* name, uint32_t group, uint32_t* gr return rv; } + +int32_t SystemNative_GetGroups(int32_t ngroups, uint32_t* groups) +{ + assert(ngroups >= 0); + assert(groups != NULL); + + return getgroups(ngroups, groups); +} diff --git a/src/libraries/Native/Unix/System.Native/pal_uid.h b/src/libraries/Native/Unix/System.Native/pal_uid.h index 03d347a61e3..d0b16eef97a 100644 --- a/src/libraries/Native/Unix/System.Native/pal_uid.h +++ b/src/libraries/Native/Unix/System.Native/pal_uid.h @@ -81,3 +81,12 @@ PALEXPORT int32_t SystemNative_GetGroupList(const char* name, uint32_t group, ui * Always succeeds. */ PALEXPORT uint32_t SystemNative_GetUid(void); + +/** +* Gets groups associated with current process. +* +* Returns number of groups for success. +* On error, -1 is returned and errno is set. +* If the buffer is too small, errno is EINVAL. +*/ +PALEXPORT int32_t SystemNative_GetGroups(int32_t ngroups, uint32_t* groups); diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 882b1beb85f..e4f10fa9063 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -273,6 +273,16 @@ Link="Common\Interop\Unix\Interop.WaitPid.cs" /> + + + + + - diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index cce32e37601..6c1268ebf50 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -17,6 +17,9 @@ namespace System.Diagnostics public partial class Process : IDisposable { private static volatile bool s_initialized; + private static uint s_euid; + private static uint s_egid; + private static uint[]? s_groups; private static readonly object s_initializedGate = new object(); private static readonly ReaderWriterLockSlim s_processStartLock = new ReaderWriterLockSlim(); @@ -743,7 +746,7 @@ namespace System.Diagnostics { string subPath = pathParser.ExtractCurrent(); path = Path.Combine(subPath, program); - if (File.Exists(path)) + if (IsExecutable(path)) { return path; } @@ -752,6 +755,46 @@ namespace System.Diagnostics return null; } + private static bool IsExecutable(string fullPath) + { + Interop.Sys.FileStatus fileinfo; + + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0) + { + return false; + } + + // Check if the path is a directory. + if ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) + { + return false; + } + + Interop.Sys.Permissions permissions = (Interop.Sys.Permissions)fileinfo.Mode; + + if (s_euid == 0) + { + // We're root. + return (permissions & Interop.Sys.Permissions.S_IXUGO) != 0; + } + + if (s_euid == fileinfo.Uid) + { + // We own the file. + return (permissions & Interop.Sys.Permissions.S_IXUSR) != 0; + } + + if (s_egid == fileinfo.Gid || + (s_groups != null && Array.BinarySearch(s_groups, fileinfo.Gid) >= 0)) + { + // A group we're a member of owns the file. + return (permissions & Interop.Sys.Permissions.S_IXGRP) != 0; + } + + // Other. + return (permissions & Interop.Sys.Permissions.S_IXOTH) != 0; + } + private static long s_ticksPerSecond; /// Convert a number of "jiffies", or ticks, to a TimeSpan. @@ -1021,6 +1064,14 @@ namespace System.Diagnostics throw new Win32Exception(); } + s_euid = Interop.Sys.GetEUid(); + s_egid = Interop.Sys.GetEGid(); + s_groups = Interop.Sys.GetGroups(); + if (s_groups != null) + { + Array.Sort(s_groups); + } + // Register our callback. Interop.Sys.RegisterForSigChld(&OnSigChild); SetDelayedSigChildConsoleConfigurationHandler(); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs index a4ff703332a..63d7f3d6d02 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs @@ -185,6 +185,40 @@ namespace System.Diagnostics.Tests } } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void ProcessStart_SkipsNonExecutableFilesOnPATH() + { + const string ScriptName = "script"; + + // Create a directory named ScriptName. + string path1 = Path.Combine(TestDirectory, "Path1"); + Directory.CreateDirectory(Path.Combine(path1, ScriptName)); + + // Create a non-executable file named ScriptName + string path2 = Path.Combine(TestDirectory, "Path2"); + Directory.CreateDirectory(path2); + File.WriteAllText(Path.Combine(path2, ScriptName), "Not executable"); + + // Create an executable script named ScriptName + string path3 = Path.Combine(TestDirectory, "Path3"); + Directory.CreateDirectory(path3); + string filename = WriteScriptFile(path3, ScriptName, returnValue: 42); + + // Process.Start ScriptName with the above on PATH. + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.StartInfo.EnvironmentVariables["PATH"] = $"{path1}:{path2}:{path3}"; + RemoteExecutor.Invoke(() => + { + using (var px = Process.Start(new ProcessStartInfo { FileName = ScriptName })) + { + Assert.NotNull(px); + px.WaitForExit(); + Assert.True(px.HasExited); + Assert.Equal(42, px.ExitCode); + } + }, options).Dispose(); + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [PlatformSpecific(TestPlatforms.Linux)] // s_allowedProgramsToRun is Linux specific public void ProcessStart_UseShellExecute_OnUnix_FallsBackWhenNotRealExecutable() diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3dcfc2c7313..23ffc05ed32 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1954,7 +1954,7 @@ Common\Interop\Unix\System.Native\Interop.GetCwd.cs - Common\Interop\Unix\System.Native\Interop.GetHostName.cs + Common\Interop\Unix\System.Native\Interop.GetEGid.cs Common\Interop\Unix\System.Native\Interop.GetHostName.cs From aaade776c48b6faa212a30a64586eb907824235f Mon Sep 17 00:00:00 2001 From: Hong Li Date: Tue, 13 Jul 2021 06:44:22 -0700 Subject: [PATCH 479/926] Fix for a couple of Microsoft.XmlSerializer.Generator issues (#55427) * Fix for a couple of Microsoft.XmlSerializer.Generator issues * fix the name of the AssemblyInfo.cs file --- .../Microsoft.XmlSerializer.Generator.targets | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets b/src/libraries/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets index a66dd7eb7a2..6d905920740 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets +++ b/src/libraries/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets @@ -4,6 +4,7 @@ <_SerializerDllIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).dll <_SerializerPdbIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).pdb <_SerializerCsIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).cs + <_SerializerCsAssemblyInfoIntermediateFolder>$(IntermediateOutputPath)SgenAssemblyInfo.cs <_SGenWarningText>SGEN: Failed to generate the serializer for $(AssemblyName)$(TargetExt). Please follow the instructions at https://go.microsoft.com/fwlink/?linkid=858594 and try again. <_SGenRspWarningText>Failed to generate response file for Microsoft.XmlSerializer.Generator, serializer is generating with default settings. <_SerializationAssemblyDisabledWarnings>$(NoWarn);219;162;$(SerializationAssemblyDisabledWarnings) @@ -29,6 +30,19 @@ + + + + + + + @@ -36,8 +50,8 @@ - - + + @@ -70,7 +84,7 @@ - + From adad29284f8db4ca1dc852e11cddf797eb309919 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 25 May 2021 20:41:54 -0400 Subject: [PATCH 480/926] Add Span.TryWrite, StringBuilder.Append, and String.Create interpolated strings support --- .../System.Memory/ref/System.Memory.cs | 21 + .../System.Memory/tests/Span/TryWrite.cs | 728 ++++++++++++++++++ .../tests/System.Memory.Tests.csproj | 1 + .../src/System/MemoryExtensions.cs | 436 +++++++++++ .../src/System/String.cs | 15 + .../src/System/Text/StringBuilder.cs | 343 +++++++++ .../System.Runtime/ref/System.Runtime.cs | 25 + .../tests/System.Runtime.Tests.csproj | 1 + .../tests/System/StringTests.cs | 16 + .../Text/StringBuilderInterpolationTests.cs | 653 ++++++++++++++++ 10 files changed, 2239 insertions(+) create mode 100644 src/libraries/System.Memory/tests/Span/TryWrite.cs create mode 100644 src/libraries/System.Runtime/tests/System/Text/StringBuilderInterpolationTests.cs diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 7318b4a0d3c..1466b1fa1b7 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -143,6 +143,27 @@ namespace System public static System.ReadOnlySpan Trim(this System.ReadOnlySpan span, T trimElement) where T : System.IEquatable { throw null; } public static System.Span Trim(this System.Span span, System.ReadOnlySpan trimElements) where T : System.IEquatable { throw null; } public static System.Span Trim(this System.Span span, T trimElement) where T : System.IEquatable { throw null; } + public static bool TryWrite(this System.Span destination, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("destination")] ref System.MemoryExtensions.TryWriteInterpolatedStringHandler handler, out int charsWritten) { throw null; } + public static bool TryWrite(this System.Span destination, IFormatProvider? provider, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("destination", "provider")] ref System.MemoryExtensions.TryWriteInterpolatedStringHandler handler, out int charsWritten) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute] + public ref struct TryWriteInterpolatedStringHandler + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, out bool success) { throw null; } + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, IFormatProvider? provider, out bool success) { throw null; } + public bool AppendLiteral(string value) { throw null; } + public bool AppendFormatted(System.ReadOnlySpan value) { throw null; } + public bool AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) { throw null; } + public bool AppendFormatted(T value) { throw null; } + public bool AppendFormatted(T value, string? format) { throw null; } + public bool AppendFormatted(T value, int alignment) { throw null; } + public bool AppendFormatted(T value, int alignment, string? format) { throw null; } + public bool AppendFormatted(object? value, int alignment = 0, string? format = null) { throw null; } + public bool AppendFormatted(string? value) { throw null; } + public bool AppendFormatted(string? value, int alignment = 0, string? format = null) { throw null; } + } } public readonly partial struct SequencePosition : System.IEquatable { diff --git a/src/libraries/System.Memory/tests/Span/TryWrite.cs b/src/libraries/System.Memory/tests/Span/TryWrite.cs new file mode 100644 index 00000000000..993f68fc36d --- /dev/null +++ b/src/libraries/System.Memory/tests/Span/TryWrite.cs @@ -0,0 +1,728 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; +using System.Tests; +using System.Text; +using Xunit; + +// TODO: Once compiler support is available, augment tests to exercise interpolated strings. + +namespace System.SpanTests +{ + public class TryWriteTests + { + private char[] _largeBuffer = new char[4096]; + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(42, 84)] + [InlineData(-1, 0)] + [InlineData(-1, -1)] + [InlineData(-16, 1)] + public void LengthAndHoleArguments_Valid(int literalLength, int formattedCount) + { + bool success; + + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out success); + Assert.True(success); + + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out success); + Assert.True(success); + + if (literalLength > 0) + { + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out success); + Assert.False(success); + } + + foreach (IFormatProvider provider in new IFormatProvider[] { null, new ConcatFormatter(), CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") }) + { + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out success); + Assert.True(success); + + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out success); + Assert.True(success); + + if (literalLength > 0) + { + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out success); + Assert.False(success); + } + } + } + + [Fact] + public void AppendLiteral() + { + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, out _); + + foreach (string s in new[] { "", "a", "bc", "def", "this is a long string", "!" }) + { + expected.Append(s); + actual.AppendLiteral(s); + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_ReadOnlySpanChar() + { + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, out _); + + foreach (string s in new[] { "", "a", "bc", "def", "this is a longer string", "!" }) + { + // span + expected.Append(s); + actual.AppendFormatted((ReadOnlySpan)s); + + // span, format + expected.AppendFormat("{0:X2}", s); + actual.AppendFormatted((ReadOnlySpan)s, format: "X2"); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // span, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", s); + actual.AppendFormatted((ReadOnlySpan)s, alignment); + + // span, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", s); + actual.AppendFormatted((ReadOnlySpan)s, alignment, "X2"); + } + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_String() + { + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, out _); + + foreach (string s in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" }) + { + // string + expected.AppendFormat("{0}", s); + actual.AppendFormatted(s); + + // string, format + expected.AppendFormat("{0:X2}", s); + actual.AppendFormatted(s, "X2"); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // string, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", s); + actual.AppendFormatted(s, alignment); + + // string, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", s); + actual.AppendFormatted(s, alignment, "X2"); + } + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_String_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, provider, out _); + + foreach (string s in new[] { null, "", "a" }) + { + // string + expected.AppendFormat(provider, "{0}", s); + actual.AppendFormatted(s); + + // string, format + expected.AppendFormat(provider, "{0:X2}", s); + actual.AppendFormatted(s, "X2"); + + // string, alignment + expected.AppendFormat(provider, "{0,3}", s); + actual.AppendFormatted(s, 3); + + // string, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", s); + actual.AppendFormatted(s, -3, "X2"); + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_ReferenceTypes() + { + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, out _); + + foreach (string rawInput in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" }) + { + foreach (object o in new object[] + { + rawInput, // raw string directly; ToString will return itself + new StringWrapper(rawInput), // wrapper object that returns string from ToString + new FormattableStringWrapper(rawInput), // IFormattable wrapper around string + new SpanFormattableStringWrapper(rawInput) // ISpanFormattable wrapper around string + }) + { + // object + expected.AppendFormat("{0}", o); + actual.AppendFormatted(o); + if (o is IHasToStringState tss1) + { + Assert.True(string.IsNullOrEmpty(tss1.ToStringState.LastFormat)); + AssertModeMatchesType(tss1); + } + + // object, format + expected.AppendFormat("{0:X2}", o); + actual.AppendFormatted(o, "X2"); + if (o is IHasToStringState tss2) + { + Assert.Equal("X2", tss2.ToStringState.LastFormat); + AssertModeMatchesType(tss2); + } + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // object, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", o); + actual.AppendFormatted(o, alignment); + if (o is IHasToStringState tss3) + { + Assert.True(string.IsNullOrEmpty(tss3.ToStringState.LastFormat)); + AssertModeMatchesType(tss3); + } + + // object, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", o); + actual.AppendFormatted(o, alignment, "X2"); + if (o is IHasToStringState tss4) + { + Assert.Equal("X2", tss4.ToStringState.LastFormat); + AssertModeMatchesType(tss4); + } + } + } + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_ReferenceTypes_CreateProviderFlowed() + { + var provider = new CultureInfo("en-US"); + MemoryExtensions.TryWriteInterpolatedStringHandler handler = new MemoryExtensions.TryWriteInterpolatedStringHandler(1, 2, _largeBuffer, provider, out _); + + foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper("hello"), new SpanFormattableStringWrapper("hello") }) + { + handler.AppendFormatted(tss); + Assert.Same(provider, tss.ToStringState.LastProvider); + + handler.AppendFormatted(tss, 1); + Assert.Same(provider, tss.ToStringState.LastProvider); + + handler.AppendFormatted(tss, "X2"); + Assert.Same(provider, tss.ToStringState.LastProvider); + + handler.AppendFormatted(tss, 1, "X2"); + Assert.Same(provider, tss.ToStringState.LastProvider); + } + } + + [Fact] + public void AppendFormatted_ReferenceTypes_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, provider, out _); + + foreach (string s in new[] { null, "", "a" }) + { + foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper(s), new SpanFormattableStringWrapper(s) }) + { + void AssertTss(IHasToStringState tss, string format) + { + Assert.Equal(format, tss.ToStringState.LastFormat); + Assert.Same(provider, tss.ToStringState.LastProvider); + Assert.Equal(ToStringMode.ICustomFormatterFormat, tss.ToStringState.ToStringMode); + } + + // object + expected.AppendFormat(provider, "{0}", tss); + actual.AppendFormatted(tss); + AssertTss(tss, null); + + // object, format + expected.AppendFormat(provider, "{0:X2}", tss); + actual.AppendFormatted(tss, "X2"); + AssertTss(tss, "X2"); + + // object, alignment + expected.AppendFormat(provider, "{0,3}", tss); + actual.AppendFormatted(tss, 3); + AssertTss(tss, null); + + // object, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", tss); + actual.AppendFormatted(tss, -3, "X2"); + AssertTss(tss, "X2"); + } + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + [Fact] + public void AppendFormatted_ValueTypes() + { + void Test(T t) + { + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, out _); + + // struct + expected.AppendFormat("{0}", t); + actual.AppendFormatted(t); + Assert.True(string.IsNullOrEmpty(((IHasToStringState)t).ToStringState.LastFormat)); + AssertModeMatchesType(((IHasToStringState)t)); + + // struct, format + expected.AppendFormat("{0:X2}", t); + actual.AppendFormatted(t, "X2"); + Assert.Equal("X2", ((IHasToStringState)t).ToStringState.LastFormat); + AssertModeMatchesType(((IHasToStringState)t)); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // struct, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", t); + actual.AppendFormatted(t, alignment); + Assert.True(string.IsNullOrEmpty(((IHasToStringState)t).ToStringState.LastFormat)); + AssertModeMatchesType(((IHasToStringState)t)); + + // struct, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", t); + actual.AppendFormatted(t, alignment, "X2"); + Assert.Equal("X2", ((IHasToStringState)t).ToStringState.LastFormat); + AssertModeMatchesType(((IHasToStringState)t)); + } + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Fact] + public void AppendFormatted_ValueTypes_CreateProviderFlowed() + { + void Test(T t) + { + var provider = new CultureInfo("en-US"); + MemoryExtensions.TryWriteInterpolatedStringHandler handler = new MemoryExtensions.TryWriteInterpolatedStringHandler(1, 2, _largeBuffer, provider, out _); + + handler.AppendFormatted(t); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + handler.AppendFormatted(t, 1); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + handler.AppendFormatted(t, "X2"); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + handler.AppendFormatted(t, 1, "X2"); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Fact] + public void AppendFormatted_ValueTypes_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + void Test(T t) + { + void AssertTss(T tss, string format) + { + Assert.Equal(format, ((IHasToStringState)tss).ToStringState.LastFormat); + Assert.Same(provider, ((IHasToStringState)tss).ToStringState.LastProvider); + Assert.Equal(ToStringMode.ICustomFormatterFormat, ((IHasToStringState)tss).ToStringState.ToStringMode); + } + + var expected = new StringBuilder(); + MemoryExtensions.TryWriteInterpolatedStringHandler actual = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, _largeBuffer, provider, out _); + + // struct + expected.AppendFormat(provider, "{0}", t); + actual.AppendFormatted(t); + AssertTss(t, null); + + // struct, format + expected.AppendFormat(provider, "{0:X2}", t); + actual.AppendFormatted(t, "X2"); + AssertTss(t, "X2"); + + // struct, alignment + expected.AppendFormat(provider, "{0,3}", t); + actual.AppendFormatted(t, 3); + AssertTss(t, null); + + // struct, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", t); + actual.AppendFormatted(t, -3, "X2"); + AssertTss(t, "X2"); + + Assert.True(MemoryExtensions.TryWrite(_largeBuffer, ref actual, out int charsWritten)); + Assert.Equal(expected.ToString(), _largeBuffer.AsSpan(0, charsWritten).ToString()); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Fact] + public void AppendFormatted_EmptyBuffer_ZeroLengthWritesSuccessful() + { + var buffer = new char[100]; + + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), out bool success); + Assert.True(success); + + Assert.True(b.AppendLiteral("")); + Assert.True(b.AppendFormatted((object)"", alignment: 0, format: "X2")); + Assert.True(b.AppendFormatted(null)); + Assert.True(b.AppendFormatted("")); + Assert.True(b.AppendFormatted("", alignment: 0, format: "X2")); + Assert.True(b.AppendFormatted("")); + Assert.True(b.AppendFormatted("", alignment: 0)); + Assert.True(b.AppendFormatted("", format: "X2")); + Assert.True(b.AppendFormatted("", alignment: 0, format: "X2")); + Assert.True(b.AppendFormatted("".AsSpan())); + Assert.True(b.AppendFormatted("".AsSpan(), alignment: 0, format: "X2")); + + Assert.True(MemoryExtensions.TryWrite(buffer.AsSpan(0, 0), ref b, out int charsWritten)); + Assert.Equal(0, charsWritten); + } + + [Theory] + [InlineData(0)] + [InlineData(100)] + public void AppendFormatted_BufferTooSmall(int bufferLength) + { + var buffer = new char[bufferLength]; + + for (int i = 0; i <= 29; i++) + { + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer, out bool success); + Assert.True(success); + + Assert.True(b.AppendLiteral(new string('s', bufferLength))); + + bool result = i switch + { + 0 => b.AppendLiteral(" "), + 1 => b.AppendFormatted((object)" ", alignment: 0, format: "X2"), + 2 => b.AppendFormatted(" "), + 3 => b.AppendFormatted(" ", alignment: 0, format: "X2"), + 4 => b.AppendFormatted(" "), + 5 => b.AppendFormatted(" ", alignment: 0), + 6 => b.AppendFormatted(" ", format: "X2"), + 7 => b.AppendFormatted(" ", alignment: 0, format: "X2"), + 8 => b.AppendFormatted(" ".AsSpan()), + 9 => b.AppendFormatted(" ".AsSpan(), alignment: 0, format: "X2"), + 10 => b.AppendFormatted(new FormattableStringWrapper(" ")), + 11 => b.AppendFormatted(new FormattableStringWrapper(" "), alignment: 0), + 12 => b.AppendFormatted(new FormattableStringWrapper(" "), format: "X2"), + 13 => b.AppendFormatted(new FormattableStringWrapper(" "), alignment: 0, format: "X2"), + 14 => b.AppendFormatted(new SpanFormattableStringWrapper(" ")), + 15 => b.AppendFormatted(new SpanFormattableStringWrapper(" "), alignment: 0), + 16 => b.AppendFormatted(new SpanFormattableStringWrapper(" "), format: "X2"), + 17 => b.AppendFormatted(new SpanFormattableStringWrapper(" "), alignment: 0, format: "X2"), + 18 => b.AppendFormatted(new FormattableInt32Wrapper(1)), + 19 => b.AppendFormatted(new FormattableInt32Wrapper(1), alignment: 0), + 20 => b.AppendFormatted(new FormattableInt32Wrapper(1), format: "X2"), + 21 => b.AppendFormatted(new FormattableInt32Wrapper(1), alignment: 0, format: "X2"), + 22 => b.AppendFormatted(new SpanFormattableInt32Wrapper(1)), + 23 => b.AppendFormatted(new SpanFormattableInt32Wrapper(1), alignment: 0), + 24 => b.AppendFormatted(new SpanFormattableInt32Wrapper(1), format: "X2"), + 25 => b.AppendFormatted(new SpanFormattableInt32Wrapper(1), alignment: 0, format: "X2"), + 26 => b.AppendFormatted("", alignment: 1), + 27 => b.AppendFormatted("", alignment: -1), + 28 => b.AppendFormatted(" ", alignment: 1, format: "X2"), + 29 => b.AppendFormatted(" ", alignment: -1, format: "X2"), + _ => throw new Exception(), + }; + Assert.False(result); + + Assert.False(MemoryExtensions.TryWrite(buffer.AsSpan(0, 0), ref b, out int charsWritten)); + Assert.Equal(0, charsWritten); + } + } + [Fact] + public void AppendFormatted_BufferTooSmall_CustomFormatter() + { + var buffer = new char[100]; + var provider = new ConstFormatter(" "); + + { + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), provider, out bool success); + Assert.True(success); + + // don't use custom formatter + Assert.True(b.AppendLiteral("")); + Assert.True(b.AppendFormatted("".AsSpan())); + Assert.True(b.AppendFormatted("".AsSpan(), alignment: 0, format: "X2")); + + // do use custom formatter + Assert.False(b.AppendFormatted((object)"", alignment: 0, format: "X2")); + Assert.False(b.AppendFormatted(null)); + Assert.False(b.AppendFormatted("")); + Assert.False(b.AppendFormatted("", alignment: 0, format: "X2")); + Assert.False(b.AppendFormatted("")); + Assert.False(b.AppendFormatted("", alignment: 0)); + Assert.False(b.AppendFormatted("", format: "X2")); + Assert.False(b.AppendFormatted("", alignment: 0, format: "X2")); + + Assert.False(MemoryExtensions.TryWrite(buffer.AsSpan(0, 0), ref b, out int charsWritten)); + Assert.Equal(0, charsWritten); + } + } + + private static void AssertModeMatchesType(T tss) where T : IHasToStringState + { + ToStringMode expected = + tss is ISpanFormattable ? ToStringMode.ISpanFormattableTryFormat : + tss is IFormattable ? ToStringMode.IFormattableToString : + ToStringMode.ObjectToString; + Assert.Equal(expected, tss.ToStringState.ToStringMode); + } + + private sealed class SpanFormattableStringWrapper : IFormattable, ISpanFormattable, IHasToStringState + { + private readonly string _value; + public ToStringState ToStringState { get; } = new ToStringState(); + + public SpanFormattableStringWrapper(string value) => _value = value; + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + ToStringState.LastFormat = format.ToString(); + ToStringState.LastProvider = provider; + ToStringState.ToStringMode = ToStringMode.ISpanFormattableTryFormat; + + if (_value is null) + { + charsWritten = 0; + return true; + } + + if (_value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + + charsWritten = _value.Length; + _value.AsSpan().CopyTo(destination); + return true; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value; + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value; + } + } + + private struct SpanFormattableInt32Wrapper : IFormattable, ISpanFormattable, IHasToStringState + { + private readonly int _value; + public ToStringState ToStringState { get; } + + public SpanFormattableInt32Wrapper(int value) + { + ToStringState = new ToStringState(); + _value = value; + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + ToStringState.LastFormat = format.ToString(); + ToStringState.LastProvider = provider; + ToStringState.ToStringMode = ToStringMode.ISpanFormattableTryFormat; + + return _value.TryFormat(destination, out charsWritten, format, provider); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value.ToString(format, formatProvider); + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value.ToString(); + } + } + + private sealed class FormattableStringWrapper : IFormattable, IHasToStringState + { + private readonly string _value; + public ToStringState ToStringState { get; } = new ToStringState(); + + public FormattableStringWrapper(string s) => _value = s; + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value; + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value; + } + } + + private struct FormattableInt32Wrapper : IFormattable, IHasToStringState + { + private readonly int _value; + public ToStringState ToStringState { get; } + + public FormattableInt32Wrapper(int i) + { + ToStringState = new ToStringState(); + _value = i; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value.ToString(format, formatProvider); + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value.ToString(); + } + } + + private sealed class ToStringState + { + public string LastFormat { get; set; } + public IFormatProvider LastProvider { get; set; } + public ToStringMode ToStringMode { get; set; } + } + + private interface IHasToStringState + { + ToStringState ToStringState { get; } + } + + private enum ToStringMode + { + ObjectToString, + IFormattableToString, + ISpanFormattableTryFormat, + ICustomFormatterFormat, + } + + private sealed class StringWrapper + { + private readonly string _value; + + public StringWrapper(string s) => _value = s; + + public override string ToString() => _value; + } + + private sealed class ConcatFormatter : IFormatProvider, ICustomFormatter + { + public object GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null; + + public string Format(string format, object arg, IFormatProvider formatProvider) + { + string s = format + " " + arg + formatProvider; + + if (arg is IHasToStringState tss) + { + // Set after using arg.ToString() in concat above + tss.ToStringState.LastFormat = format; + tss.ToStringState.LastProvider = formatProvider; + tss.ToStringState.ToStringMode = ToStringMode.ICustomFormatterFormat; + } + + return s; + } + } + + private sealed class ConstFormatter : IFormatProvider, ICustomFormatter + { + private readonly string _value; + + public ConstFormatter(string value) => _value = value; + + public object GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null; + + public string Format(string format, object arg, IFormatProvider formatProvider) => _value; + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index 5ab1670c7df..e6638ed601f 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -112,6 +112,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 31608d643a2..bcf142a1f8a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -1868,5 +1870,439 @@ namespace System ArraySortHelper.Default.Sort(keys, items, new ComparisonComparer(comparison)); } } + + /// Writes the specified interpolated string to the character span. + /// The span to which the interpolated string should be formatted. + /// The interpolated string. + /// The number of characters written to the span. + /// true if the entire interpolated string could be formatted successfully; otherwise, false. + public static bool TryWrite(this Span destination, [InterpolatedStringHandlerArgument("destination")] ref TryWriteInterpolatedStringHandler handler, out int charsWritten) + { + // The span argument isn't used directly in the method; rather, it'll be used by the compiler to create the handler. + // We could validate here that span == handler._destination, but that doesn't seem necessary. + if (handler._success) + { + charsWritten = handler._pos; + return true; + } + + charsWritten = 0; + return false; + } + + /// Writes the specified interpolated string to the character span. + /// The span to which the interpolated string should be formatted. + /// An object that supplies culture-specific formatting information. + /// The interpolated string. + /// The number of characters written to the span. + /// true if the entire interpolated string could be formatted successfully; otherwise, false. + public static bool TryWrite(this Span destination, IFormatProvider? provider, [InterpolatedStringHandlerArgument("destination", "provider")] ref TryWriteInterpolatedStringHandler handler, out int charsWritten) => + // The provider is passed to the handler by the compiler, so the actual implementation of the method + // is the same as the non-provider overload. + TryWrite(destination, ref handler, out charsWritten); + + /// Provides a handler used by the language compiler to format interpolated strings into character spans. + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public ref struct TryWriteInterpolatedStringHandler + { + // Implementation note: + // As this type is only intended to be targeted by the compiler, public APIs eschew argument validation logic + // in a variety of places, e.g. allowing a null input when one isn't expected to produce a NullReferenceException rather + // than an ArgumentNullException. + + /// The destination buffer. + private readonly Span _destination; + /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. + private readonly IFormatProvider? _provider; + /// The number of characters written to . + internal int _pos; + /// true if all formatting operations have succeeded; otherwise, false. + internal bool _success; + /// Whether provides an ICustomFormatter. + /// + /// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive + /// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field + /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider + /// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a + /// formatter, we pay for the extra interface call on each AppendFormatted that needs it. + /// + private readonly bool _hasCustomFormatter; + + /// Creates a handler used to write an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The destination buffer. + /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, out bool success) + { + _destination = destination; + _provider = null; + _pos = 0; + _success = success = destination.Length >= literalLength; + _hasCustomFormatter = false; + } + + /// Creates a handler used to write an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The destination buffer. + /// An object that supplies culture-specific formatting information. + /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, IFormatProvider? provider, out bool success) + { + _destination = destination; + _provider = provider; + _pos = 0; + _success = success = destination.Length >= literalLength; + _hasCustomFormatter = provider is not null && DefaultInterpolatedStringHandler.HasCustomFormatter(provider); + } + + /// Writes the specified string to the handler. + /// The string to write. + /// true if the value could be formatted to the span; otherwise, false. + public bool AppendLiteral(string value) + { + if (value.TryCopyTo(_destination.Slice(_pos))) + { + _pos += value.Length; + return true; + } + + return Fail(); + } + + #region AppendFormatted + // Design note: + // This provides the same set of overloads and semantics as DefaultInterpolatedStringHandler. + + #region AppendFormatted T + /// Writes the specified value to the handler. + /// The value to write. + public bool AppendFormatted(T value) + { + // This method could delegate to AppendFormatted with a null format, but explicitly passing + // default as the format to TryFormat helps to improve code quality in some cases when TryFormat is inlined, + // e.g. for Int32 it enables the JIT to eliminate code in the inlined method based on a length check on the format. + + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + return AppendCustomFormatter(value, format: null); + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // derives from the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + if (((ISpanFormattable)value).TryFormat(_destination.Slice(_pos), out charsWritten, default, _provider)) // constrained call avoiding boxing for value types + { + _pos += charsWritten; + return true; + } + + return Fail(); + } + + s = ((IFormattable)value).ToString(format: null, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + return s is null || AppendLiteral(s); // use AppendLiteral to avoid going back through this method recursively + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public bool AppendFormatted(T value, string? format) + { + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + return AppendCustomFormatter(value, format); + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // derives from the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + if (((ISpanFormattable)value).TryFormat(_destination.Slice(_pos), out charsWritten, format, _provider)) // constrained call avoiding boxing for value types + { + _pos += charsWritten; + return true; + } + + return Fail(); + } + + s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + return s is null || AppendLiteral(s); // use AppendLiteral to avoid going back through this method recursively + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public bool AppendFormatted(T value, int alignment) + { + int startingPos = _pos; + if (AppendFormatted(value)) + { + return alignment == 0 || TryAppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + + return Fail(); + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public bool AppendFormatted(T value, int alignment, string? format) + { + int startingPos = _pos; + if (AppendFormatted(value, format)) + { + return alignment == 0 || TryAppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + + return Fail(); + } + #endregion + + #region AppendFormatted ReadOnlySpan + /// Writes the specified character span to the handler. + /// The span to write. + public bool AppendFormatted(ReadOnlySpan value) + { + // Fast path for when the value fits in the current buffer + if (value.TryCopyTo(_destination.Slice(_pos))) + { + _pos += value.Length; + return true; + } + + return Fail(); + } + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public bool AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + // The value is as large or larger than the required amount of padding, + // so just write the value. + return AppendFormatted(value); + } + + // Write the value along with the appropriate padding. + Debug.Assert(alignment > value.Length); + if (alignment <= _destination.Length - _pos) + { + if (leftAlign) + { + value.CopyTo(_destination.Slice(_pos)); + _pos += value.Length; + _destination.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + } + else + { + _destination.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + value.CopyTo(_destination.Slice(_pos)); + _pos += value.Length; + } + + return true; + } + + return Fail(); + } + #endregion + + #region AppendFormatted string + /// Writes the specified value to the handler. + /// The value to write. + public bool AppendFormatted(string? value) + { + // Fast-path for no custom formatter and a non-null string that fits in the current destination buffer. + if (!_hasCustomFormatter && + value is not null && + value.TryCopyTo(_destination.Slice(_pos))) + { + _pos += value.Length; + return true; + } + + return AppendFormattedSlow(value); + } + + /// Writes the specified value to the handler. + /// The value to write. + /// + /// Slow path to handle a custom formatter, potentially null value, + /// or a string that doesn't fit in the current buffer. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private bool AppendFormattedSlow(string? value) + { + if (_hasCustomFormatter) + { + return AppendCustomFormatter(value, format: null); + } + + if (value is null) + { + return true; + } + + if (value.TryCopyTo(_destination.Slice(_pos))) + { + _pos += value.Length; + return true; + } + + return Fail(); + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public bool AppendFormatted(string? value, int alignment = 0, string? format = null) => + // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload + // simply to disambiguate between ROS and object, just in case someone does specify a format, as + // string is implicitly convertible to both. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + + #region AppendFormatted object + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public bool AppendFormatted(object? value, int alignment = 0, string? format = null) => + // This overload is expected to be used rarely, only if either a) something strongly typed as object is + // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It + // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + #endregion + + /// Formats the value using the custom formatter from the provider. + /// The value to write. + /// The format string. + [MethodImpl(MethodImplOptions.NoInlining)] + private bool AppendCustomFormatter(T value, string? format) + { + // This case is very rare, but we need to handle it prior to the other checks in case + // a provider was used that supplied an ICustomFormatter which wanted to intercept the particular value. + // We do the cast here rather than in the ctor, even though this could be executed multiple times per + // formatting, to make the cast pay for play. + Debug.Assert(_hasCustomFormatter); + Debug.Assert(_provider != null); + + ICustomFormatter? formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); + Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); + + if (formatter is not null && formatter.Format(format, value, _provider) is string customFormatted) + { + return AppendLiteral(customFormatted); + } + + return true; + } + + /// Handles adding any padding required for aligning a formatted value in an interpolation expression. + /// The position at which the written value started. + /// Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + private bool TryAppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) + { + Debug.Assert(startingPos >= 0 && startingPos <= _pos); + Debug.Assert(alignment != 0); + + int charsWritten = _pos - startingPos; + + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingNeeded = alignment - charsWritten; + if (paddingNeeded <= 0) + { + return true; + } + + if (paddingNeeded <= _destination.Length - _pos) + { + if (leftAlign) + { + _destination.Slice(_pos, paddingNeeded).Fill(' '); + } + else + { + _destination.Slice(startingPos, charsWritten).CopyTo(_destination.Slice(startingPos + paddingNeeded)); + _destination.Slice(startingPos, paddingNeeded).Fill(' '); + } + + _pos += paddingNeeded; + return true; + } + + return Fail(); + } + + /// Marks formatting as having failed and returns false. + private bool Fail() + { + _success = false; + return false; + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs index 25ac8df2c58..7ca88a4b29c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.cs @@ -391,6 +391,21 @@ namespace System return result; } + /// Creates a new string by using the specified provider to control the formatting of the specified interpolated string. + /// An object that supplies culture-specific formatting information. + /// The interpolated string. + /// The string that results for formatting the interpolated string using the specified format provider. + public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument("provider")] ref DefaultInterpolatedStringHandler handler) => + handler.ToStringAndClear(); + + /// Creates a new string by using the specified provider to control the formatting of the specified interpolated string. + /// An object that supplies culture-specific formatting information. + /// The initial buffer that may be used as temporary space as part of the formatting operation. The contents of this buffer may be overwritten. + /// The interpolated string. + /// The string that results for formatting the interpolated string using the specified format provider. + public static string Create(IFormatProvider? provider, Span initialBuffer, [InterpolatedStringHandlerArgument("provider", "initialBuffer")] ref DefaultInterpolatedStringHandler handler) => + handler.ToStringAndClear(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(string? value) => value != null ? new ReadOnlySpan(ref value.GetRawStringData(), value.Length) : default; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 3ff675c1d40..9bec0d08499 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -1232,6 +1233,28 @@ namespace System.Text public StringBuilder Append(ReadOnlyMemory value) => Append(value.Span); + /// Appends the specified interpolated string to this instance. + /// The interpolated string to append. + /// A reference to this instance after the append operation has completed. + public StringBuilder Append([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) => this; + + /// Appends the specified interpolated string to this instance. + /// An object that supplies culture-specific formatting information. + /// The interpolated string to append. + /// A reference to this instance after the append operation has completed. + public StringBuilder Append(IFormatProvider? provider, [InterpolatedStringHandlerArgument("", "provider")] ref AppendInterpolatedStringHandler handler) => this; + + /// Appends the specified interpolated string followed by the default line terminator to the end of the current StringBuilder object. + /// The interpolated string to append. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendLine([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) => AppendLine(); + + /// Appends the specified interpolated string followed by the default line terminator to the end of the current StringBuilder object. + /// An object that supplies culture-specific formatting information. + /// The interpolated string to append. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendLine(IFormatProvider? provider, [InterpolatedStringHandlerArgument("", "provider")] ref AppendInterpolatedStringHandler handler) => AppendLine(); + #region AppendJoin public unsafe StringBuilder AppendJoin(string? separator, params object?[] values) @@ -2600,5 +2623,325 @@ namespace System.Text Debug.Assert(chunk != null, "We fell off the beginning of the string!"); AssertInvariants(); } + + /// Provides a handler used by the language compiler to append interpolated strings into instances. + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public struct AppendInterpolatedStringHandler + { + // Implementation note: + // As this type is only intended to be targeted by the compiler, public APIs eschew argument validation logic + // in a variety of places, e.g. allowing a null input when one isn't expected to produce a NullReferenceException rather + // than an ArgumentNullException. + + /// The associated StringBuilder to which to append. + private readonly StringBuilder _stringBuilder; + /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. + private readonly IFormatProvider? _provider; + /// Whether provides an ICustomFormatter. + /// + /// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive + /// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field + /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider + /// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a + /// formatter, we pay for the extra interface call on each AppendFormatted that needs it. + /// + private readonly bool _hasCustomFormatter; + + /// Creates a handler used to append an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The associated StringBuilder to which to append. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public AppendInterpolatedStringHandler(int literalLength, int formattedCount, StringBuilder stringBuilder) + { + _stringBuilder = stringBuilder; + _provider = null; + _hasCustomFormatter = false; + } + + /// Creates a handler used to translate an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The associated StringBuilder to which to append. + /// An object that supplies culture-specific formatting information. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public AppendInterpolatedStringHandler(int literalLength, int formattedCount, StringBuilder stringBuilder, IFormatProvider? provider) + { + _stringBuilder = stringBuilder; + _provider = provider; + _hasCustomFormatter = provider is not null && DefaultInterpolatedStringHandler.HasCustomFormatter(provider); + } + + /// Writes the specified string to the handler. + /// The string to write. + public void AppendLiteral(string value) => _stringBuilder.Append(value); + + #region AppendFormatted + // Design note: + // This provides the same set of overloads and semantics as DefaultInterpolatedStringHandler. + + #region AppendFormatted T + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) + { + // This method could delegate to AppendFormatted with a null format, but explicitly passing + // default as the format to TryFormat helps to improve code quality in some cases when TryFormat is inlined, + // e.g. for Int32 it enables the JIT to eliminate code in the inlined method based on a length check on the format. + + if (_hasCustomFormatter) + { + // If there's a custom formatter, always use it. + AppendCustomFormatter(value, format: null); + } + else if (value is IFormattable) + { + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + + if (value is ISpanFormattable) + { + Span destination = _stringBuilder.RemainingCurrentChunk; + if (((ISpanFormattable)value).TryFormat(destination, out int charsWritten, default, _provider)) // constrained call avoiding boxing for value types + { + if ((uint)charsWritten > (uint)destination.Length) + { + // Protect against faulty ISpanFormattable implementations returning invalid charsWritten values. + // Other code in _stringBuilder uses unsafe manipulation, and we want to ensure m_ChunkLength remains safe. + FormatError(); + } + + _stringBuilder.m_ChunkLength += charsWritten; + } + else + { + // Not enough room in the current chunk. Take the slow path that formats into temporary space + // and then copies the result into the StringBuilder. + AppendFormattedWithTempSpace(value, 0, format: null); + } + } + else + { + _stringBuilder.Append(((IFormattable)value).ToString(format: null, _provider)); // constrained call avoiding boxing for value types + } + } + else if (value is not null) + { + _stringBuilder.Append(value.ToString()); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) + { + if (_hasCustomFormatter) + { + // If there's a custom formatter, always use it. + AppendCustomFormatter(value, format); + } + else if (value is IFormattable) + { + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + + if (value is ISpanFormattable) + { + Span destination = _stringBuilder.RemainingCurrentChunk; + if (((ISpanFormattable)value).TryFormat(destination, out int charsWritten, format, _provider)) // constrained call avoiding boxing for value types + { + if ((uint)charsWritten > (uint)destination.Length) + { + // Protect against faulty ISpanFormattable implementations returning invalid charsWritten values. + // Other code in _stringBuilder uses unsafe manipulation, and we want to ensure m_ChunkLength remains safe. + FormatError(); + } + + _stringBuilder.m_ChunkLength += charsWritten; + } + else + { + // Not enough room in the current chunk. Take the slow path that formats into temporary space + // and then copies the result into the StringBuilder. + AppendFormattedWithTempSpace(value, 0, format); + } + } + else + { + _stringBuilder.Append(((IFormattable)value).ToString(format, _provider)); // constrained call avoiding boxing for value types + } + } + else if (value is not null) + { + _stringBuilder.Append(value.ToString()); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) => + AppendFormatted(value, alignment, format: null); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) + { + if (alignment == 0) + { + // This overload is used as a fallback from several disambiguation overloads, so special-case 0. + AppendFormatted(value, format); + } + else if (alignment < 0) + { + // Left aligned: format into the handler, then append any additional padding required. + int start = _stringBuilder.Length; + AppendFormatted(value, format); + int paddingRequired = -alignment - (_stringBuilder.Length - start); + if (paddingRequired > 0) + { + _stringBuilder.Append(' ', paddingRequired); + } + } + else + { + // Right aligned: format into temporary space and then copy that into the handler, appropriately aligned. + AppendFormattedWithTempSpace(value, alignment, format); + } + } + + /// Formats into temporary space and then appends the result into the StringBuilder. + private void AppendFormattedWithTempSpace(T value, int alignment, string? format) + { + // It's expected that either there's not enough space in the current chunk to store this formatted value, + // or we have a non-0 alignment that could require padding inserted. So format into temporary space and + // then append that written span into the StringBuilder: StringBuilder.Append(span) is able to split the + // span across the current chunk and any additional chunks required. + + var handler = new DefaultInterpolatedStringHandler(0, 0, _provider, stackalloc char[256]); + handler.AppendFormatted(value, format); + AppendFormatted(handler.Text, alignment); + handler.Clear(); + } + #endregion + + #region AppendFormatted ReadOnlySpan + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) => _stringBuilder.Append(value); + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + if (alignment == 0) + { + _stringBuilder.Append(value); + } + else + { + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + _stringBuilder.Append(value); + } + else if (leftAlign) + { + _stringBuilder.Append(value); + _stringBuilder.Append(' ', paddingRequired); + } + else + { + _stringBuilder.Append(' ', paddingRequired); + _stringBuilder.Append(value); + } + } + } + #endregion + + #region AppendFormatted string + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) + { + if (!_hasCustomFormatter) + { + _stringBuilder.Append(value); + } + else + { + AppendFormatted(value); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => + // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload + // simply to disambiguate between ROS and object, just in case someone does specify a format, as + // string is implicitly convertible to both. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + + #region AppendFormatted object + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + // This overload is expected to be used rarely, only if either a) something strongly typed as object is + // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It + // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + #endregion + + /// Formats the value using the custom formatter from the provider. + /// The value to write. + /// The format string. + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendCustomFormatter(T value, string? format) + { + // This case is very rare, but we need to handle it prior to the other checks in case + // a provider was used that supplied an ICustomFormatter which wanted to intercept the particular value. + // We do the cast here rather than in the ctor, even though this could be executed multiple times per + // formatting, to make the cast pay for play. + Debug.Assert(_hasCustomFormatter); + Debug.Assert(_provider != null); + + ICustomFormatter? formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); + Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); + + if (formatter is not null) + { + _stringBuilder.Append(formatter.Format(format, value, _provider)); + } + } + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 4f0c63dd430..8bd40241869 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -5763,6 +5763,8 @@ namespace System public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { } public void CopyTo(System.Span destination) { } public static System.String Create(int length, TState state, System.Buffers.SpanAction action) { throw null; } + public static string Create(System.IFormatProvider? provider, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("provider")] ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { throw null; } + public static string Create(System.IFormatProvider? provider, System.Span initialBuffer, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("provider", "initialBuffer")] ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { throw null; } public bool EndsWith(char value) { throw null; } public bool EndsWith(System.String value) { throw null; } public bool EndsWith(System.String value, bool ignoreCase, System.Globalization.CultureInfo? culture) { throw null; } @@ -14283,6 +14285,8 @@ namespace System.Text public System.Text.StringBuilder Append(uint value) { throw null; } [System.CLSCompliantAttribute(false)] public System.Text.StringBuilder Append(ulong value) { throw null; } + public System.Text.StringBuilder Append([System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("")] ref System.Text.StringBuilder.AppendInterpolatedStringHandler handler) { throw null; } + public System.Text.StringBuilder Append(System.IFormatProvider? provider, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("", "provider")] ref System.Text.StringBuilder.AppendInterpolatedStringHandler handler) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, string format, object? arg0) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, string format, object? arg0, object? arg1) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) { throw null; } @@ -14299,6 +14303,8 @@ namespace System.Text public System.Text.StringBuilder AppendJoin(string? separator, System.Collections.Generic.IEnumerable values) { throw null; } public System.Text.StringBuilder AppendLine() { throw null; } public System.Text.StringBuilder AppendLine(string? value) { throw null; } + public System.Text.StringBuilder AppendLine([System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("")] ref System.Text.StringBuilder.AppendInterpolatedStringHandler handler) { throw null; } + public System.Text.StringBuilder AppendLine(System.IFormatProvider? provider, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("", "provider")] ref System.Text.StringBuilder.AppendInterpolatedStringHandler handler) { throw null; } public System.Text.StringBuilder Clear() { throw null; } public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { } public void CopyTo(int sourceIndex, System.Span destination, int count) { } @@ -14346,6 +14352,25 @@ namespace System.Text public System.Text.StringBuilder.ChunkEnumerator GetEnumerator() { throw null; } public bool MoveNext() { throw null; } } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute] + public struct AppendInterpolatedStringHandler + { + private object _dummy; + private int _dummyPrimitive; + public AppendInterpolatedStringHandler(int literalLength, int formattedCount, System.Text.StringBuilder stringBuilder) { throw null; } + public AppendInterpolatedStringHandler(int literalLength, int formattedCount, System.Text.StringBuilder stringBuilder, System.IFormatProvider? provider) { throw null; } + public void AppendLiteral(string value) { } + public void AppendFormatted(T value) { } + public void AppendFormatted(T value, string? format) { } + public void AppendFormatted(T value, int alignment) { } + public void AppendFormatted(T value, int alignment, string? format) { } + public void AppendFormatted(System.ReadOnlySpan value) { } + public void AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) { } + public void AppendFormatted(string? value) { } + public void AppendFormatted(string? value, int alignment = 0, string? format = null) { } + public void AppendFormatted(object? value, int alignment = 0, string? format = null) { } + } } public partial struct StringRuneEnumerator : System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerator, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 0f4092248ae..0ddefe7dcf0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -238,6 +238,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System/StringTests.cs index aa311ff6d7d..b0786bb2370 100644 --- a/src/libraries/System.Runtime/tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System/StringTests.cs @@ -148,6 +148,22 @@ namespace System.Tests Assert.Equal(expected, result); } + [Fact] + public static void Create_InterpolatedString_ConstructsStringAndClearsBuilder() + { + Span initialBuffer = stackalloc char[16]; + + DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(0, 0, CultureInfo.InvariantCulture, initialBuffer); + handler.AppendLiteral("hello"); + Assert.Equal("hello", string.Create(CultureInfo.InvariantCulture, initialBuffer, ref handler)); + Assert.Equal("", string.Create(CultureInfo.InvariantCulture, initialBuffer, ref handler)); + + handler = new DefaultInterpolatedStringHandler(0, 0, CultureInfo.InvariantCulture); + handler.AppendLiteral("hello"); + Assert.Equal("hello", string.Create(CultureInfo.InvariantCulture, ref handler)); + Assert.Equal("", string.Create(CultureInfo.InvariantCulture, ref handler)); + } + [Theory] [InlineData("Hello", 'H', true)] [InlineData("Hello", 'Z', false)] diff --git a/src/libraries/System.Runtime/tests/System/Text/StringBuilderInterpolationTests.cs b/src/libraries/System.Runtime/tests/System/Text/StringBuilderInterpolationTests.cs new file mode 100644 index 00000000000..c8de8c8f9a4 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Text/StringBuilderInterpolationTests.cs @@ -0,0 +1,653 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using Xunit; + +namespace System.Text.Tests +{ + public class StringBuilderInterpolationTests + { + [Fact] + public void Append_Nop() + { + var sb = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(1, 2, sb); + + Assert.Same(sb, sb.Append(ref iab)); + Assert.Same(sb, sb.Append(CultureInfo.InvariantCulture, ref iab)); + + Assert.Equal(0, sb.Length); + } + + [Fact] + public void AppendLine_AppendsNewLine() + { + var sb = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(1, 2, sb); + + Assert.Same(sb, sb.AppendLine(ref iab)); + Assert.Same(sb, sb.AppendLine(CultureInfo.InvariantCulture, ref iab)); + + Assert.Equal(Environment.NewLine + Environment.NewLine, sb.ToString()); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(42, 84)] + [InlineData(-1, 0)] + [InlineData(-1, -1)] + [InlineData(-16, 1)] + public void LengthAndHoleArguments_Valid(int baseLength, int holeCount) + { + var sb = new StringBuilder(); + + new StringBuilder.AppendInterpolatedStringHandler(baseLength, holeCount, sb); + + foreach (IFormatProvider provider in new IFormatProvider[] { null, new ConcatFormatter(), CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") }) + { + new StringBuilder.AppendInterpolatedStringHandler(baseLength, holeCount, sb, provider); + } + } + + [Fact] + public void AppendLiteral() + { + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual); + + foreach (string s in new[] { "", "a", "bc", "def", "this is a long string", "!" }) + { + expected.Append(s); + iab.AppendLiteral(s); + } + + actual.Append(ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_ReadOnlySpanChar() + { + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual); + + foreach (string s in new[] { "", "a", "bc", "def", "this is a longer string", "!" }) + { + // span + expected.Append(s); + iab.AppendFormatted((ReadOnlySpan)s); + + // span, format + expected.AppendFormat("{0:X2}", s); + iab.AppendFormatted((ReadOnlySpan)s, format: "X2"); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // span, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", s); + iab.AppendFormatted((ReadOnlySpan)s, alignment); + + // span, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", s); + iab.AppendFormatted((ReadOnlySpan)s, alignment, "X2"); + } + } + + actual.Append(ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_String() + { + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual); + + foreach (string s in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" }) + { + // string + expected.AppendFormat("{0}", s); + iab.AppendFormatted(s); + + // string, format + expected.AppendFormat("{0:X2}", s); + iab.AppendFormatted(s, "X2"); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // string, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", s); + iab.AppendFormatted(s, alignment); + + // string, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", s); + iab.AppendFormatted(s, alignment, "X2"); + } + } + + actual.Append(ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_String_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual, provider); + + foreach (string s in new[] { null, "", "a" }) + { + // string + expected.AppendFormat(provider, "{0}", s); + iab.AppendFormatted(s); + + // string, format + expected.AppendFormat(provider, "{0:X2}", s); + iab.AppendFormatted(s, "X2"); + + // string, alignment + expected.AppendFormat(provider, "{0,3}", s); + iab.AppendFormatted(s, 3); + + // string, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", s); + iab.AppendFormatted(s, -3, "X2"); + } + + actual.Append(provider, ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_ReferenceTypes() + { + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual); + + foreach (string rawInput in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" }) + { + foreach (object o in new object[] + { + rawInput, // raw string directly; ToString will return itself + new StringWrapper(rawInput), // wrapper object that returns string from ToString + new FormattableStringWrapper(rawInput), // IFormattable wrapper around string + new SpanFormattableStringWrapper(rawInput) // ISpanFormattable wrapper around string + }) + { + // object + expected.AppendFormat("{0}", o); + iab.AppendFormatted(o); + if (o is IHasToStringState tss1) + { + Assert.True(string.IsNullOrEmpty(tss1.ToStringState.LastFormat)); + AssertModeMatchesType(tss1); + } + + // object, format + expected.AppendFormat("{0:X2}", o); + iab.AppendFormatted(o, "X2"); + if (o is IHasToStringState tss2) + { + Assert.Equal("X2", tss2.ToStringState.LastFormat); + AssertModeMatchesType(tss2); + } + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // object, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", o); + iab.AppendFormatted(o, alignment); + if (o is IHasToStringState tss3) + { + Assert.True(string.IsNullOrEmpty(tss3.ToStringState.LastFormat)); + AssertModeMatchesType(tss3); + } + + // object, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", o); + iab.AppendFormatted(o, alignment, "X2"); + if (o is IHasToStringState tss4) + { + Assert.Equal("X2", tss4.ToStringState.LastFormat); + AssertModeMatchesType(tss4); + } + } + } + } + + actual.Append(ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_ReferenceTypes_CreateProviderFlowed() + { + var provider = new CultureInfo("en-US"); + var sb = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(1, 2, sb, provider); + + foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper("hello"), new SpanFormattableStringWrapper("hello") }) + { + iab.AppendFormatted(tss); + Assert.Same(provider, tss.ToStringState.LastProvider); + + iab.AppendFormatted(tss, 1); + Assert.Same(provider, tss.ToStringState.LastProvider); + + iab.AppendFormatted(tss, "X2"); + Assert.Same(provider, tss.ToStringState.LastProvider); + + iab.AppendFormatted(tss, 1, "X2"); + Assert.Same(provider, tss.ToStringState.LastProvider); + } + + sb.Append(ref iab); + } + + [Fact] + public void AppendFormatted_ReferenceTypes_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual, provider); + + foreach (string s in new[] { null, "", "a" }) + { + foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper(s), new SpanFormattableStringWrapper(s) }) + { + void AssertTss(IHasToStringState tss, string format) + { + Assert.Equal(format, tss.ToStringState.LastFormat); + Assert.Same(provider, tss.ToStringState.LastProvider); + Assert.Equal(ToStringMode.ICustomFormatterFormat, tss.ToStringState.ToStringMode); + } + + // object + expected.AppendFormat(provider, "{0}", tss); + iab.AppendFormatted(tss); + AssertTss(tss, null); + + // object, format + expected.AppendFormat(provider, "{0:X2}", tss); + iab.AppendFormatted(tss, "X2"); + AssertTss(tss, "X2"); + + // object, alignment + expected.AppendFormat(provider, "{0,3}", tss); + iab.AppendFormatted(tss, 3); + AssertTss(tss, null); + + // object, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", tss); + iab.AppendFormatted(tss, -3, "X2"); + AssertTss(tss, "X2"); + } + } + + actual.Append(provider, ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + [Fact] + public void AppendFormatted_ValueTypes() + { + void Test(T t) + { + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual); + + // struct + expected.AppendFormat("{0}", t); + iab.AppendFormatted(t); + Assert.True(string.IsNullOrEmpty(((IHasToStringState)t).ToStringState.LastFormat)); + AssertModeMatchesType(((IHasToStringState)t)); + + // struct, format + expected.AppendFormat("{0:X2}", t); + iab.AppendFormatted(t, "X2"); + Assert.Equal("X2", ((IHasToStringState)t).ToStringState.LastFormat); + AssertModeMatchesType(((IHasToStringState)t)); + + foreach (int alignment in new[] { 0, 3, -3 }) + { + // struct, alignment + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + "}", t); + iab.AppendFormatted(t, alignment); + Assert.True(string.IsNullOrEmpty(((IHasToStringState)t).ToStringState.LastFormat)); + AssertModeMatchesType(((IHasToStringState)t)); + + // struct, alignment, format + expected.AppendFormat("{0," + alignment.ToString(CultureInfo.InvariantCulture) + ":X2}", t); + iab.AppendFormatted(t, alignment, "X2"); + Assert.Equal("X2", ((IHasToStringState)t).ToStringState.LastFormat); + AssertModeMatchesType(((IHasToStringState)t)); + } + + actual.Append(ref iab); + + Assert.Equal(expected.ToString(), actual.ToString()); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Fact] + public void AppendFormatted_ValueTypes_CreateProviderFlowed() + { + void Test(T t) + { + var provider = new CultureInfo("en-US"); + var sb = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(1, 2, sb, provider); + + iab.AppendFormatted(t); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + iab.AppendFormatted(t, 1); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + iab.AppendFormatted(t, "X2"); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + iab.AppendFormatted(t, 1, "X2"); + Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider); + + sb.Append(ref iab); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Fact] + public void AppendFormatted_ValueTypes_ICustomFormatter() + { + var provider = new ConcatFormatter(); + + void Test(T t) + { + void AssertTss(T tss, string format) + { + Assert.Equal(format, ((IHasToStringState)tss).ToStringState.LastFormat); + Assert.Same(provider, ((IHasToStringState)tss).ToStringState.LastProvider); + Assert.Equal(ToStringMode.ICustomFormatterFormat, ((IHasToStringState)tss).ToStringState.ToStringMode); + } + + var expected = new StringBuilder(); + var actual = new StringBuilder(); + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, actual, provider); + + // struct + expected.AppendFormat(provider, "{0}", t); + iab.AppendFormatted(t); + AssertTss(t, null); + + // struct, format + expected.AppendFormat(provider, "{0:X2}", t); + iab.AppendFormatted(t, "X2"); + AssertTss(t, "X2"); + + // struct, alignment + expected.AppendFormat(provider, "{0,3}", t); + iab.AppendFormatted(t, 3); + AssertTss(t, null); + + // struct, alignment, format + expected.AppendFormat(provider, "{0,-3:X2}", t); + iab.AppendFormatted(t, -3, "X2"); + AssertTss(t, "X2"); + + Assert.Equal(expected.ToString(), actual.ToString()); + + actual.Append(ref iab); + } + + Test(new FormattableInt32Wrapper(42)); + Test(new SpanFormattableInt32Wrapper(84)); + Test((FormattableInt32Wrapper?)new FormattableInt32Wrapper(42)); + Test((SpanFormattableInt32Wrapper?)new SpanFormattableInt32Wrapper(84)); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AppendFormatted_InvalidTryFormatCharsWritten_Throws(bool tooBig) // vs tooSmall + { + StringBuilder.AppendInterpolatedStringHandler iab = new StringBuilder.AppendInterpolatedStringHandler(0, 0, new StringBuilder()); + Assert.Throws(() => iab.AppendFormatted(new InvalidCharsWritten(tooBig))); + } + + private static void AssertModeMatchesType(T tss) where T : IHasToStringState + { + ToStringMode expected = + tss is ISpanFormattable ? ToStringMode.ISpanFormattableTryFormat : + tss is IFormattable ? ToStringMode.IFormattableToString : + ToStringMode.ObjectToString; + Assert.Equal(expected, tss.ToStringState.ToStringMode); + } + + private sealed class SpanFormattableStringWrapper : IFormattable, ISpanFormattable, IHasToStringState + { + private readonly string _value; + public ToStringState ToStringState { get; } = new ToStringState(); + + public SpanFormattableStringWrapper(string value) => _value = value; + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + ToStringState.LastFormat = format.ToString(); + ToStringState.LastProvider = provider; + ToStringState.ToStringMode = ToStringMode.ISpanFormattableTryFormat; + + if (_value is null) + { + charsWritten = 0; + return true; + } + + if (_value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + + charsWritten = _value.Length; + _value.AsSpan().CopyTo(destination); + return true; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value; + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value; + } + } + + private struct SpanFormattableInt32Wrapper : IFormattable, ISpanFormattable, IHasToStringState + { + private readonly int _value; + public ToStringState ToStringState { get; } + + public SpanFormattableInt32Wrapper(int value) + { + ToStringState = new ToStringState(); + _value = value; + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + ToStringState.LastFormat = format.ToString(); + ToStringState.LastProvider = provider; + ToStringState.ToStringMode = ToStringMode.ISpanFormattableTryFormat; + + return _value.TryFormat(destination, out charsWritten, format, provider); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value.ToString(format, formatProvider); + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value.ToString(); + } + } + + private sealed class FormattableStringWrapper : IFormattable, IHasToStringState + { + private readonly string _value; + public ToStringState ToStringState { get; } = new ToStringState(); + + public FormattableStringWrapper(string s) => _value = s; + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value; + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value; + } + } + + private struct FormattableInt32Wrapper : IFormattable, IHasToStringState + { + private readonly int _value; + public ToStringState ToStringState { get; } + + public FormattableInt32Wrapper(int i) + { + ToStringState = new ToStringState(); + _value = i; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + ToStringState.LastFormat = format; + ToStringState.LastProvider = formatProvider; + ToStringState.ToStringMode = ToStringMode.IFormattableToString; + return _value.ToString(format, formatProvider); + } + + public override string ToString() + { + ToStringState.LastFormat = null; + ToStringState.LastProvider = null; + ToStringState.ToStringMode = ToStringMode.ObjectToString; + return _value.ToString(); + } + } + + private sealed class ToStringState + { + public string LastFormat { get; set; } + public IFormatProvider LastProvider { get; set; } + public ToStringMode ToStringMode { get; set; } + } + + private interface IHasToStringState + { + ToStringState ToStringState { get; } + } + + private enum ToStringMode + { + ObjectToString, + IFormattableToString, + ISpanFormattableTryFormat, + ICustomFormatterFormat, + } + + private sealed class StringWrapper + { + private readonly string _value; + + public StringWrapper(string s) => _value = s; + + public override string ToString() => _value; + } + + private sealed class ConcatFormatter : IFormatProvider, ICustomFormatter + { + public object GetFormat(Type formatType) => formatType == typeof(ICustomFormatter) ? this : null; + + public string Format(string format, object arg, IFormatProvider formatProvider) + { + string s = format + " " + arg + formatProvider; + + if (arg is IHasToStringState tss) + { + // Set after using arg.ToString() in concat above + tss.ToStringState.LastFormat = format; + tss.ToStringState.LastProvider = formatProvider; + tss.ToStringState.ToStringMode = ToStringMode.ICustomFormatterFormat; + } + + return s; + } + } + + private sealed class InvalidCharsWritten : ISpanFormattable + { + private bool _tooBig; + + public InvalidCharsWritten(bool tooBig) => _tooBig = tooBig; + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + charsWritten = _tooBig ? destination.Length + 1 : -1; + return true; + } + + public string ToString(string format, IFormatProvider formatProvider) => + throw new NotImplementedException(); + } + } +} From f3fedf9f4843b70744b40c5e4268edf511583598 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 18 Jun 2021 17:57:11 -0400 Subject: [PATCH 481/926] Add Debug.Assert/Write{Line}If interpolated overloads --- .../DebugTestsNoListeners.Interpolation.cs | 180 ++++++++++++ .../tests/DebugTestsNoListeners.cs | 2 +- .../System.Diagnostics.Debug.Tests.csproj | 6 +- .../System.Memory/ref/System.Memory.cs | 4 +- .../System.Memory/tests/Span/TryWrite.cs | 38 +-- .../src/System/Diagnostics/Debug.cs | 262 +++++++++++++++--- .../src/System/MemoryExtensions.cs | 21 -- .../src/System/Random.cs | 16 +- .../DefaultInterpolatedStringHandler.cs | 2 +- .../src/System/Text/StringBuilder.cs | 2 +- .../System.Runtime/ref/System.Runtime.cs | 48 ++++ 11 files changed, 487 insertions(+), 94 deletions(-) create mode 100644 src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.Interpolation.cs diff --git a/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.Interpolation.cs b/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.Interpolation.cs new file mode 100644 index 00000000000..cd05cdfdc8b --- /dev/null +++ b/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.Interpolation.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#define DEBUG +using System.Text; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public partial class DebugTestsNoListeners : DebugTests + { + [Fact] + public void Asserts_Interpolation() + { + Debug.AssertInterpolatedStringHandler message; + Debug.AssertInterpolatedStringHandler detailedMessage; + bool shouldAppend; + + message = new Debug.AssertInterpolatedStringHandler(0, 0, true, out shouldAppend); + VerifyLogged(() => Debug.Assert(true, message), ""); + + message = new Debug.AssertInterpolatedStringHandler(0, 0, true, out shouldAppend); + detailedMessage = new Debug.AssertInterpolatedStringHandler(0, 0, true, out shouldAppend); + VerifyLogged(() => Debug.Assert(true, message, detailedMessage), ""); + + message = new Debug.AssertInterpolatedStringHandler(0, 0, false, out shouldAppend); + message.AppendLiteral("assert passed"); + VerifyAssert(() => Debug.Assert(false, message), "assert passed"); + + message = new Debug.AssertInterpolatedStringHandler(0, 0, false, out shouldAppend); + message.AppendLiteral("assert passed"); + detailedMessage = new Debug.AssertInterpolatedStringHandler(0, 0, false, out shouldAppend); + detailedMessage.AppendLiteral("nothing is wrong"); + VerifyAssert(() => Debug.Assert(false, message, detailedMessage), "assert passed", "nothing is wrong"); + } + + [Fact] + public void WriteIf_Interpolation() + { + Debug.WriteIfInterpolatedStringHandler handler; + bool shouldAppend; + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, true, out shouldAppend); + handler.AppendLiteral("logged"); + VerifyLogged(() => Debug.WriteIf(true, handler), "logged"); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, false, out shouldAppend); + VerifyLogged(() => Debug.WriteIf(false, handler), ""); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, true, out shouldAppend); + handler.AppendLiteral("logged"); + VerifyLogged(() => Debug.WriteIf(true, handler, "category"), "category: logged"); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, false, out shouldAppend); + VerifyLogged(() => Debug.WriteIf(false, handler, "category"), ""); + + GoToNextLine(); + } + + [Fact] + public void WriteLineIf_Interpolation() + { + Debug.WriteIfInterpolatedStringHandler handler; + bool shouldAppend; + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, true, out shouldAppend); + handler.AppendLiteral("logged"); + VerifyLogged(() => Debug.WriteLineIf(true, handler), "logged" + Environment.NewLine); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, false, out shouldAppend); + VerifyLogged(() => Debug.WriteLineIf(false, handler), ""); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, true, out shouldAppend); + handler.AppendLiteral("logged"); + VerifyLogged(() => Debug.WriteLineIf(true, handler, "category"), "category: logged" + Environment.NewLine); + + handler = new Debug.WriteIfInterpolatedStringHandler(0, 0, false, out shouldAppend); + VerifyLogged(() => Debug.WriteLineIf(false, handler, "category"), ""); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Condition_ShouldAppend_Matches(bool condition) + { + bool shouldAppend; + + new Debug.AssertInterpolatedStringHandler(1, 2, condition, out shouldAppend); + Assert.Equal(!condition, shouldAppend); + + new Debug.WriteIfInterpolatedStringHandler(1, 2, condition, out shouldAppend); + Assert.Equal(condition, shouldAppend); + } + + [Fact] + public void DebugHandler_AppendOverloads_MatchStringBuilderHandler() + { + var actual = new Debug.AssertInterpolatedStringHandler(0, 0, condition: false, out bool shouldAppend); + Assert.True(shouldAppend); + + var sb = new StringBuilder(); + var expected = new StringBuilder.AppendInterpolatedStringHandler(0, 0, sb); + + actual.AppendLiteral("abcd"); + expected.AppendLiteral("abcd"); + + actual.AppendFormatted(123); + expected.AppendFormatted(123); + + actual.AppendFormatted(45.6, 10); + expected.AppendFormatted(45.6, 10); + + actual.AppendFormatted(default(Guid), "X"); + expected.AppendFormatted(default(Guid), "X"); + + DateTime dt = DateTime.UtcNow; + actual.AppendFormatted(dt, -100, "r"); + expected.AppendFormatted(dt, -100, "r"); + + actual.AppendFormatted("hello"); + expected.AppendFormatted("hello"); + + actual.AppendFormatted("world", -10, null); + expected.AppendFormatted("world", -10, null); + + actual.AppendFormatted((ReadOnlySpan)"nice to"); + expected.AppendFormatted((ReadOnlySpan)"nice to"); + + actual.AppendFormatted((ReadOnlySpan)"nice to", 0, "anything"); + expected.AppendFormatted((ReadOnlySpan)"nice to", 0, "anything"); + + actual.AppendFormatted((object)DayOfWeek.Monday, 42, null); + expected.AppendFormatted((object)DayOfWeek.Monday, 42, null); + + VerifyAssert(() => Debug.Assert(false, actual), sb.ToString()); + } + + [Fact] + public void WriteIfHandler_AppendOverloads_MatchStringBuilderHandler() + { + var actual = new Debug.WriteIfInterpolatedStringHandler(0, 0, condition: true, out bool shouldAppend); + Assert.True(shouldAppend); + + var sb = new StringBuilder(); + var expected = new StringBuilder.AppendInterpolatedStringHandler(0, 0, sb); + + actual.AppendLiteral("abcd"); + expected.AppendLiteral("abcd"); + + actual.AppendFormatted(123); + expected.AppendFormatted(123); + + actual.AppendFormatted(45.6, 10); + expected.AppendFormatted(45.6, 10); + + actual.AppendFormatted(default(Guid), "X"); + expected.AppendFormatted(default(Guid), "X"); + + DateTime dt = DateTime.UtcNow; + actual.AppendFormatted(dt, -100, "r"); + expected.AppendFormatted(dt, -100, "r"); + + actual.AppendFormatted("hello"); + expected.AppendFormatted("hello"); + + actual.AppendFormatted("world", -10, null); + expected.AppendFormatted("world", -10, null); + + actual.AppendFormatted((ReadOnlySpan)"nice to"); + expected.AppendFormatted((ReadOnlySpan)"nice to"); + + actual.AppendFormatted((ReadOnlySpan)"nice to", 0, "anything"); + expected.AppendFormatted((ReadOnlySpan)"nice to", 0, "anything"); + + actual.AppendFormatted((object)DayOfWeek.Monday, 42, null); + expected.AppendFormatted((object)DayOfWeek.Monday, 42, null); + + VerifyLogged(() => Debug.WriteIf(true, actual), sb.ToString()); + } + } +} diff --git a/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs b/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs index cabe5a7f4ec..4277946a7a2 100644 --- a/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs +++ b/src/libraries/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs @@ -9,7 +9,7 @@ namespace System.Diagnostics.Tests // These tests test the static Debug class. They cannot be run in parallel // DebugTestsNoListeners: tests Debug behavior before Debug is set with Trace Listeners. [Collection("System.Diagnostics.Debug")] - public class DebugTestsNoListeners : DebugTests + public partial class DebugTestsNoListeners : DebugTests { protected override bool DebugUsesTraceListeners { get { return false; } } diff --git a/src/libraries/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj b/src/libraries/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj index 6e8c0ad320d..e842a465e60 100644 --- a/src/libraries/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj +++ b/src/libraries/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj @@ -10,12 +10,12 @@ - + + + diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 1466b1fa1b7..1ddd8df16a6 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -151,8 +151,8 @@ namespace System { private readonly object _dummy; private readonly int _dummyPrimitive; - public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, out bool success) { throw null; } - public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, IFormatProvider? provider, out bool success) { throw null; } + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, out bool shouldAppend) { throw null; } + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, System.Span destination, IFormatProvider? provider, out bool shouldAppend) { throw null; } public bool AppendLiteral(string value) { throw null; } public bool AppendFormatted(System.ReadOnlySpan value) { throw null; } public bool AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) { throw null; } diff --git a/src/libraries/System.Memory/tests/Span/TryWrite.cs b/src/libraries/System.Memory/tests/Span/TryWrite.cs index 993f68fc36d..b3d7987bfb6 100644 --- a/src/libraries/System.Memory/tests/Span/TryWrite.cs +++ b/src/libraries/System.Memory/tests/Span/TryWrite.cs @@ -24,32 +24,32 @@ namespace System.SpanTests [InlineData(-16, 1)] public void LengthAndHoleArguments_Valid(int literalLength, int formattedCount) { - bool success; + bool shouldAppend; - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out success); - Assert.True(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out shouldAppend); + Assert.True(shouldAppend); - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out success); - Assert.True(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out shouldAppend); + Assert.True(shouldAppend); if (literalLength > 0) { - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out success); - Assert.False(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out shouldAppend); + Assert.False(shouldAppend); } foreach (IFormatProvider provider in new IFormatProvider[] { null, new ConcatFormatter(), CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") }) { - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out success); - Assert.True(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[Math.Max(0, literalLength)], out shouldAppend); + Assert.True(shouldAppend); - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out success); - Assert.True(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[1 + Math.Max(0, literalLength)], out shouldAppend); + Assert.True(shouldAppend); if (literalLength > 0) { - new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out success); - Assert.False(success); + new MemoryExtensions.TryWriteInterpolatedStringHandler(literalLength, formattedCount, new char[literalLength - 1], out shouldAppend); + Assert.False(shouldAppend); } } } @@ -417,8 +417,8 @@ namespace System.SpanTests { var buffer = new char[100]; - MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), out bool success); - Assert.True(success); + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), out bool shouldAppend); + Assert.True(shouldAppend); Assert.True(b.AppendLiteral("")); Assert.True(b.AppendFormatted((object)"", alignment: 0, format: "X2")); @@ -445,8 +445,8 @@ namespace System.SpanTests for (int i = 0; i <= 29; i++) { - MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer, out bool success); - Assert.True(success); + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer, out bool shouldAppend); + Assert.True(shouldAppend); Assert.True(b.AppendLiteral(new string('s', bufferLength))); @@ -497,8 +497,8 @@ namespace System.SpanTests var provider = new ConstFormatter(" "); { - MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), provider, out bool success); - Assert.True(success); + MemoryExtensions.TryWriteInterpolatedStringHandler b = new MemoryExtensions.TryWriteInterpolatedStringHandler(0, 0, buffer.AsSpan(0, 0), provider, out bool shouldAppend); + Assert.True(shouldAppend); // don't use custom formatter Assert.True(b.AppendLiteral("")); diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs index da744f6d7be..317e319e3f9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs @@ -4,8 +4,10 @@ // Do not remove this, it is needed to retain calls to these conditional methods in release builds #define DEBUG +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Text; using System.Threading; namespace System.Diagnostics @@ -54,37 +56,41 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Close() { } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Flush() { } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Indent() => IndentLevel++; - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Unindent() => IndentLevel--; - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Print(string? message) => WriteLine(message); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Print(string format, params object?[] args) => WriteLine(string.Format(null, format, args)); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Assert([DoesNotReturnIf(false)] bool condition) => Assert(condition, string.Empty, string.Empty); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Assert([DoesNotReturnIf(false)] bool condition, string? message) => Assert(condition, message, string.Empty); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool condition, [InterpolatedStringHandlerArgument("condition")] AssertInterpolatedStringHandler message) => + Assert(condition, message.ToString()); + + [Conditional("DEBUG")] public static void Assert([DoesNotReturnIf(false)] bool condition, string? message, string? detailMessage) { if (!condition) @@ -93,12 +99,20 @@ namespace System.Diagnostics } } + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool condition, [InterpolatedStringHandlerArgument("condition")] AssertInterpolatedStringHandler message, [InterpolatedStringHandlerArgument("condition")] AssertInterpolatedStringHandler detailMessage) => + Assert(condition, message.ToString(), detailMessage.ToString()); + + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool condition, string? message, string detailMessageFormat, params object?[] args) => + Assert(condition, message, string.Format(detailMessageFormat, args)); + internal static void ContractFailure(string message, string detailMessage, string failureKindMessage) { string stackTrace; try { - stackTrace = new StackTrace(2, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal); + stackTrace = new StackTrace(2, true).ToString(StackTrace.TraceFormat.Normal); } catch { @@ -108,42 +122,38 @@ namespace System.Diagnostics DebugProvider.FailCore(stackTrace, message, detailMessage, failureKindMessage); } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] [DoesNotReturn] public static void Fail(string? message) => Fail(message, string.Empty); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] // Preserve the frame for debugger public static void Fail(string? message, string? detailMessage) => s_provider.Fail(message, detailMessage); - [System.Diagnostics.Conditional("DEBUG")] - public static void Assert([DoesNotReturnIf(false)] bool condition, string? message, string detailMessageFormat, params object?[] args) => - Assert(condition, message, string.Format(detailMessageFormat, args)); - - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLine(string? message) => s_provider.WriteLine(message); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Write(string? message) => s_provider.Write(message); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLine(object? value) => WriteLine(value?.ToString()); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLine(object? value, string? category) => WriteLine(value?.ToString(), category); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLine(string format, params object?[] args) => WriteLine(string.Format(null, format, args)); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLine(string? message, string? category) { if (category == null) @@ -156,11 +166,11 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Write(object? value) => Write(value?.ToString()); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Write(string? message, string? category) { if (category == null) @@ -173,11 +183,11 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void Write(object? value, string? category) => Write(value?.ToString(), category); - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteIf(bool condition, string? message) { if (condition) @@ -186,7 +196,11 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] + public static void WriteIf(bool condition, [InterpolatedStringHandlerArgument("condition")] WriteIfInterpolatedStringHandler message) => + WriteIf(condition, message.ToString()); + + [Conditional("DEBUG")] public static void WriteIf(bool condition, object? value) { if (condition) @@ -195,7 +209,7 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteIf(bool condition, string? message, string? category) { if (condition) @@ -204,7 +218,11 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] + public static void WriteIf(bool condition, [InterpolatedStringHandlerArgument("condition")] WriteIfInterpolatedStringHandler message, string? category) => + WriteIf(condition, message.ToString(), category); + + [Conditional("DEBUG")] public static void WriteIf(bool condition, object? value, string? category) { if (condition) @@ -213,7 +231,7 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLineIf(bool condition, object? value) { if (condition) @@ -222,7 +240,7 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLineIf(bool condition, object? value, string? category) { if (condition) @@ -231,7 +249,7 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] public static void WriteLineIf(bool condition, string? message) { if (condition) @@ -240,7 +258,11 @@ namespace System.Diagnostics } } - [System.Diagnostics.Conditional("DEBUG")] + [Conditional("DEBUG")] + public static void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("condition")] WriteIfInterpolatedStringHandler message) => + WriteLineIf(condition, message.ToString()); + + [Conditional("DEBUG")] public static void WriteLineIf(bool condition, string? message, string? category) { if (condition) @@ -248,5 +270,179 @@ namespace System.Diagnostics WriteLine(message, category); } } + + [Conditional("DEBUG")] + public static void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("condition")] WriteIfInterpolatedStringHandler message, string? category) => + WriteLineIf(condition, message.ToString(), category); + + /// Provides an interpolated string handler for that only performs formatting if the assert fails. + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public struct AssertInterpolatedStringHandler + { + /// The handler we use to perform the formatting. + private StringBuilder.AppendInterpolatedStringHandler _stringBuilderHandler; + + /// Creates an instance of the handler.. + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The condition Boolean passed to the method. + /// A value indicating whether formatting should proceed. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public AssertInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) + { + if (condition) + { + _stringBuilderHandler = default; + shouldAppend = false; + } + else + { + _stringBuilderHandler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, new StringBuilder(DefaultInterpolatedStringHandler.GetDefaultLength(literalLength, formattedCount))); + shouldAppend = true; + } + } + + /// Extracts the built string from the handler. + internal new string ToString() => + _stringBuilderHandler._stringBuilder is StringBuilder sb ? + sb.ToString() : + string.Empty; + + /// Writes the specified string to the handler. + /// The string to write. + public void AppendLiteral(string value) => _stringBuilderHandler.AppendLiteral(value); + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) => _stringBuilderHandler.AppendFormatted(value, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) => _stringBuilderHandler.AppendFormatted(value, alignment); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + } + + /// Provides an interpolated string handler for and that only performs formatting if the condition applies. + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public struct WriteIfInterpolatedStringHandler + { + /// The handler we use to perform the formatting. + private StringBuilder.AppendInterpolatedStringHandler _stringBuilderHandler; + + /// Creates an instance of the handler.. + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The condition Boolean passed to the method. + /// A value indicating whether formatting should proceed. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public WriteIfInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) + { + if (condition) + { + _stringBuilderHandler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, new StringBuilder(DefaultInterpolatedStringHandler.GetDefaultLength(literalLength, formattedCount))); + shouldAppend = true; + } + else + { + _stringBuilderHandler = default; + shouldAppend = false; + } + } + + /// Extracts the built string from the handler. + internal new string ToString() => + _stringBuilderHandler._stringBuilder is StringBuilder sb ? + sb.ToString() : + string.Empty; + + /// Writes the specified string to the handler. + /// The string to write. + public void AppendLiteral(string value) => _stringBuilderHandler.AppendLiteral(value); + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) => _stringBuilderHandler.AppendFormatted(value, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) => _stringBuilderHandler.AppendFormatted(value, alignment); + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) => _stringBuilderHandler.AppendFormatted(value); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index bcf142a1f8a..9a6eb161d77 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -2166,27 +2166,6 @@ namespace System /// Writes the specified value to the handler. /// The value to write. public bool AppendFormatted(string? value) - { - // Fast-path for no custom formatter and a non-null string that fits in the current destination buffer. - if (!_hasCustomFormatter && - value is not null && - value.TryCopyTo(_destination.Slice(_pos))) - { - _pos += value.Length; - return true; - } - - return AppendFormattedSlow(value); - } - - /// Writes the specified value to the handler. - /// The value to write. - /// - /// Slow path to handle a custom formatter, potentially null value, - /// or a string that doesn't fit in the current buffer. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private bool AppendFormattedSlow(string? value) { if (_hasCustomFormatter) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index b09da359a62..396af2e5d6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -201,28 +201,18 @@ namespace System { if (maxExclusive > minInclusive) { - if (result < minInclusive || result >= maxExclusive) - { - Debug.Fail($"Expected {minInclusive} <= {result} < {maxExclusive}"); - } + Debug.Assert(result >= minInclusive && result < maxExclusive, $"Expected {minInclusive} <= {result} < {maxExclusive}"); } else { - if (result != minInclusive) - { - Debug.Fail($"Expected {minInclusive} == {result}"); - } + Debug.Assert(result == minInclusive, $"Expected {minInclusive} == {result}"); } } [Conditional("DEBUG")] private static void AssertInRange(double result) { - if (result < 0.0 || result >= 1.0) - { - // Avoid calling result.ToString() when the Assert condition is not met - Debug.Fail($"Expected 0.0 <= {result} < 1.0"); - } + Debug.Assert(result >= 0.0 && result < 1.0, $"Expected 0.0 <= {result} < 1.0"); } /// Random implementation that delegates all calls to a ThreadStatic Impl instance. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cs index efad54ee604..ae6fb22f32c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cs @@ -91,7 +91,7 @@ namespace System.Runtime.CompilerServices /// The number of constant characters outside of interpolation expressions in the interpolated string. /// The number of interpolation expressions in the interpolated string. [MethodImpl(MethodImplOptions.AggressiveInlining)] // becomes a constant when inputs are constant - private static int GetDefaultLength(int literalLength, int formattedCount) => + internal static int GetDefaultLength(int literalLength, int formattedCount) => Math.Max(MinimumArrayPoolLength, literalLength + (formattedCount * GuessedLengthPerHole)); /// Gets the built . diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 9bec0d08499..8835e0ccae7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -2635,7 +2635,7 @@ namespace System.Text // than an ArgumentNullException. /// The associated StringBuilder to which to append. - private readonly StringBuilder _stringBuilder; + internal readonly StringBuilder _stringBuilder; /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. private readonly IFormatProvider? _provider; /// Whether provides an ICustomFormatter. diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 8bd40241869..43ca97b21d3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8669,9 +8669,13 @@ namespace System.Diagnostics public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition, string? message) { } + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.AssertInterpolatedStringHandler message) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition, string? message, string? detailMessage) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] + public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.AssertInterpolatedStringHandler message, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.AssertInterpolatedStringHandler detailMessage) { } + [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void Assert([System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute(false)] bool condition, string? message, string detailMessageFormat, params object?[] args) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void Close() { } @@ -8705,8 +8709,12 @@ namespace System.Diagnostics public static void WriteIf(bool condition, object? value, string? category) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void WriteIf(bool condition, string? message) { } + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.WriteIfInterpolatedStringHandler message) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void WriteIf(bool condition, string? message, string? category) { } + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.WriteIfInterpolatedStringHandler message, string? category) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void WriteLine(object? value) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] @@ -8724,7 +8732,47 @@ namespace System.Diagnostics [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void WriteLineIf(bool condition, string? message) { } [System.Diagnostics.ConditionalAttribute("DEBUG")] + public static void WriteLineIf(bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.WriteIfInterpolatedStringHandler message) { } + [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void WriteLineIf(bool condition, string? message, string? category) { } + [System.Diagnostics.ConditionalAttribute("DEBUG")] + public static void WriteLineIf(bool condition, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("condition")] System.Diagnostics.Debug.WriteIfInterpolatedStringHandler message, string? category) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute] + public struct AssertInterpolatedStringHandler + { + private object _dummy; + private int _dummyPrimitive; + public AssertInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) { throw null; } + public void AppendLiteral(string value) { } + public void AppendFormatted(T value) { } + public void AppendFormatted(T value, string? format) { } + public void AppendFormatted(T value, int alignment) { } + public void AppendFormatted(T value, int alignment, string? format) { } + public void AppendFormatted(ReadOnlySpan value) { } + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) { } + public void AppendFormatted(string? value) { } + public void AppendFormatted(string? value, int alignment = 0, string? format = null) { } + public void AppendFormatted(object? value, int alignment = 0, string? format = null) { } + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute] + public struct WriteIfInterpolatedStringHandler + { + private object _dummy; + private int _dummyPrimitive; + public WriteIfInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) { throw null; } + public void AppendLiteral(string value) { } + public void AppendFormatted(T value) { } + public void AppendFormatted(T value, string? format) { } + public void AppendFormatted(T value, int alignment) { } + public void AppendFormatted(T value, int alignment, string? format) { } + public void AppendFormatted(ReadOnlySpan value) { } + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) { } + public void AppendFormatted(string? value) { } + public void AppendFormatted(string? value, int alignment = 0, string? format = null) { } + public void AppendFormatted(object? value, int alignment = 0, string? format = null) { } + } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Module, AllowMultiple=false)] public sealed partial class DebuggableAttribute : System.Attribute From 1446bdb7762920357732a5c47dd7d546a696a031 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 10 Jul 2021 10:09:23 -0400 Subject: [PATCH 482/926] Fix LangVersion on several test projects --- .../tests/StaticTestGenerator/StaticTestGenerator.csproj | 4 ++-- .../System.Net.Http.WinHttpHandler.Functional.Tests.csproj | 4 ++-- .../HPackHuffmanBenchmark/HPackHuffmanBenchmark.csproj | 6 +++--- .../generators/System.Private.CoreLib.Generators.csproj | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/Common/tests/StaticTestGenerator/StaticTestGenerator.csproj b/src/libraries/Common/tests/StaticTestGenerator/StaticTestGenerator.csproj index b4107546efd..035387e854d 100644 --- a/src/libraries/Common/tests/StaticTestGenerator/StaticTestGenerator.csproj +++ b/src/libraries/Common/tests/StaticTestGenerator/StaticTestGenerator.csproj @@ -1,7 +1,7 @@ Exe - net5.0 + net6.0 false preview true @@ -21,4 +21,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj index 4b6596d1432..47dbdd20171 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj @@ -1,9 +1,9 @@ - + $(NetCoreAppCurrent)-windows;net48-windows true $(DefineConstants);WINHTTPHANDLER_TEST - 8.0 + 10.0 + Exe - net5.0 + net6.0 ../../../src/Resources/Strings.resx enable - 9.0 + preview diff --git a/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj index d6a0cb1c693..bf6c663b564 100644 --- a/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj +++ b/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj @@ -1,7 +1,7 @@ netstandard2.0 - latest + 10.0 enable false $(NoWarn);CS3001 From dfd618dc648ba9b11dd0f8034f78113d69f223cd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 12 Jul 2021 06:30:34 -0400 Subject: [PATCH 483/926] Use interpolated strings in more places --- .../Interop.ProcFsStat.ParseMapModules.cs | 3 +- .../Interop.ProcFsStat.TryReadStatusFile.cs | 5 +- .../Linux/procfs/Interop.ProcFsStat.cs | 36 ++-------- .../Interop.GssBuffer.cs | 2 +- .../Interop.OpenSsl.cs | 4 +- .../SspiCli/SecurityPackageInfoClass.cs | 11 +--- .../src/System/CodeDom/CodeTypeReference.cs | 2 +- .../Diagnostics/DebuggerTraceWriter.cs | 3 +- .../Data/Common/DbConnectionOptions.Common.cs | 4 +- .../src/System/Drawing/ColorTranslator.cs | 4 +- .../Common/src/System/IO/RowConfigReader.cs | 2 +- .../Security/NegotiateStreamPal.Windows.cs | 4 +- .../tests/StaticTestGenerator/Program.cs | 24 +++---- .../System/Net/Http/HttpProtocolTests.cs | 2 +- .../System/Security/Cryptography/ByteUtils.cs | 2 +- .../Xml/XmlCoreTest/ManagedNodeWriter.cs | 12 ++-- .../System/Xml/XmlDiff/XmlDiffDocument.cs | 2 +- .../System/Text/Unicode/ParsedUnicodeData.cs | 2 +- .../src/Microsoft.CSharp.csproj | 1 + .../RuntimeBinder/ComInterop/VariantArray.cs | 2 +- .../System/ComponentModel/Win32Exception.cs | 4 +- .../Microsoft/Win32/RegistryKey.Windows.cs | 2 +- .../Microsoft/CSharp/CSharpCodeGenerator.cs | 6 +- .../Microsoft/VisualBasic/VBCodeGenerator.cs | 2 +- .../Concurrent/PartitionerStatic.cs | 6 +- .../ReflectionModel/GenericServices.cs | 2 +- .../System/ComponentModel/Design/CommandID.cs | 2 +- .../ReflectTypeDescriptionProvider.cs | 2 +- .../src/System/Configuration/XmlUtilWriter.cs | 2 +- .../System.Console/src/System/TermInfo.cs | 5 +- .../Data/Common/DbConnectionStringBuilder.cs | 2 +- .../src/System/Data/DataException.cs | 2 +- .../src/System/Data/Filter/AggregateNode.cs | 2 +- .../src/System/Data/Filter/FunctionNode.cs | 36 +++++----- .../src/System/Data/Filter/NameNode.cs | 2 +- .../src/System/Data/ForeignKeyConstraint.cs | 4 +- .../src/System/Data/Merger.cs | 2 +- .../src/System/Data/SQLTypes/SQLBinary.cs | 3 +- .../src/System/Data/SQLTypes/SQLDecimal.cs | 2 +- .../src/System/Data/XDRSchema.cs | 6 +- .../src/System/Data/XMLSchema.cs | 10 +-- .../src/System/Data/xmlsaver.cs | 2 +- .../tests/System/Data/DataSetTest2.cs | 4 +- .../src/System/Data/Odbc/OdbcParameter.cs | 2 +- .../tests/TestCommon/DataTestUtility.cs | 7 +- .../System.Data.OleDb/src/ColumnBinding.cs | 2 +- .../src/DbConnectionOptions.cs | 8 +-- .../System.Data.OleDb/src/DbPropSet.cs | 2 +- .../System.Data.OleDb/src/OleDbConnection.cs | 2 +- .../System.Data.OleDb/src/OleDbDataReader.cs | 18 ++--- .../System.Data.OleDb/src/OleDbStruct.cs | 52 +++++++-------- .../System.Data.OleDb/src/OleDb_Util.cs | 8 +-- .../src/System/Data/Common/AdapterUtil.cs | 10 +-- .../Diagnostics/FileVersionInfo.Windows.cs | 28 ++++---- .../Diagnostics/TextWriterTraceListener.cs | 2 +- .../src/System/Diagnostics/TraceListener.cs | 14 ++-- .../src/System/Drawing/Brush.cs | 2 +- .../Drawing/Drawing2D/GraphicsPath.Windows.cs | 2 +- .../Drawing/Drawing2D/GraphicsPathIterator.cs | 2 +- .../Drawing2D/SafeCustomLineCapHandle.cs | 2 +- .../src/System/Drawing/Font.Unix.cs | 2 +- .../src/System/Drawing/Font.cs | 2 +- .../src/System/Drawing/FontConverter.cs | 6 +- .../src/System/Drawing/FontFamily.cs | 2 +- .../src/System/Drawing/Graphics.Windows.cs | 2 +- .../src/System/Drawing/Image.Windows.cs | 2 +- .../System/Drawing/Imaging/FrameDimension.cs | 2 +- .../System/Drawing/Imaging/ImageAttributes.cs | 2 +- .../src/System/Drawing/Imaging/ImageFormat.cs | 2 +- .../Drawing/Internal/SystemColorTracker.cs | 2 +- .../src/System/Drawing/Pen.cs | 2 +- .../src/System/Drawing/Printing/Margins.cs | 10 +-- .../Drawing/Printing/PageSettings.Unix.cs | 6 +- .../Drawing/Printing/PageSettings.Windows.cs | 13 +--- .../src/System/Drawing/Printing/PaperSize.cs | 9 +-- .../System/Drawing/Printing/PaperSource.cs | 7 +- .../Drawing/Printing/PrintDocument.Unix.cs | 5 +- .../Drawing/Printing/PrintDocument.Windows.cs | 5 +- .../Drawing/Printing/PrinterSettings.Unix.cs | 8 +-- .../Printing/PrinterSettings.Windows.cs | 20 +++--- .../src/System/Drawing/Region.cs | 2 +- .../src/System/Drawing/StringFormat.cs | 7 +- .../Drawing/Text/PrivateFontCollection.cs | 2 +- .../src/System/Drawing/Color.cs | 19 ++---- .../src/System/Drawing/Point.cs | 2 +- .../src/System/Drawing/PointF.cs | 2 +- .../src/System/Drawing/Rectangle.cs | 4 +- .../src/System/Drawing/RectangleF.cs | 4 +- .../src/System/Drawing/Size.cs | 2 +- .../src/System/Drawing/SizeF.cs | 2 +- .../tests/Writer/WriteInteger.cs | 4 +- .../tests/Normalization/NormalizationAll.cs | 3 +- .../Globalization/CharUnicodeInfoTests.cs | 2 +- .../tests/PortedCommon/CommonUtilities.cs | 2 +- .../tests/NonCryptoHashTestDriver.cs | 2 +- .../IO/IsolatedStorage/IsolatedStorage.cs | 2 +- .../MemoryMappedFile.Unix.cs | 2 +- .../tests/Infrastructure/TestMemoryPool.cs | 2 +- .../System/IO/Pipes/PipeCompletionSource.cs | 2 +- .../System/IO/Ports/SerialStream.Windows.cs | 6 +- .../tests/SerialPort/GetPortNames.cs | 4 +- .../Expressions/Interpreter/BranchLabel.cs | 6 +- .../Interpreter/InstructionList.cs | 4 +- .../Expressions/Interpreter/LightCompiler.cs | 6 +- .../Expressions/Interpreter/LightLambda.cs | 2 +- .../Expressions/Interpreter/LocalVariables.cs | 6 +- .../Interpreter/StackOperations.cs | 6 +- .../System/Net/Http/WinHttpRequestState.cs | 2 +- .../src/System/Net/Http/WinHttpTraceHelper.cs | 2 +- .../Net/Http/Headers/AltSvcHeaderValue.cs | 5 +- .../Http/Headers/CacheControlHeaderValue.cs | 8 +-- .../System/Net/Http/Headers/HttpHeaders.cs | 2 +- .../Net/Http/Headers/RangeItemHeaderValue.cs | 13 ++-- .../Headers/StringWithQualityHeaderValue.cs | 2 +- .../Net/Http/Headers/WarningHeaderValue.cs | 2 +- .../src/System/Net/Http/HttpContent.cs | 24 +++---- .../AuthenticationHelper.NtAuth.cs | 2 +- .../SocketsHttpHandler/Http3Connection.cs | 3 +- .../SocketsHttpHandler/HttpConnectionPool.cs | 9 +-- .../src/System/Net/HttpListenerException.cs | 8 +-- .../Net/HttpListenerRequestUriBuilder.cs | 8 +-- .../Net/Windows/HttpListener.Windows.cs | 4 +- .../Net/Windows/HttpRequestStream.Windows.cs | 4 +- .../Windows/HttpResponseStreamAsyncResult.cs | 2 +- .../Windows/ListenerAsyncResult.Windows.cs | 2 +- .../WebSockets/HttpWebSocket.Windows.cs | 9 --- .../Net/Windows/WebSockets/WebSocketBase.cs | 60 +---------------- .../Net/Windows/WebSockets/WebSocketBuffer.cs | 36 ++++------ .../System/Net/Mail/DomainLiteralReader.cs | 2 +- .../src/System/Net/Mail/DotAtomReader.cs | 2 +- .../src/System/Net/Mail/MailAddressParser.cs | 8 +-- .../src/System/Net/Mail/QuotedPairReader.cs | 6 +- .../Net/Mail/QuotedStringFormatReader.cs | 6 +- .../src/System/Net/Mail/SmtpClient.cs | 2 +- .../src/System/Net/Mime/MimeMultiPart.cs | 7 +- .../src/System/Net/NameResolutionPal.Unix.cs | 2 +- .../StringParsingHelpers.Addresses.cs | 13 ++-- .../StringParsingHelpers.Connections.cs | 22 +++---- .../src/System/Net/Cookie.cs | 2 +- .../src/System/Net/CredentialCache.cs | 4 +- .../src/System/Net/Security/SecureChannel.cs | 2 +- .../ExtendedProtectionPolicy.cs | 4 +- .../src/System/Net/WebClient.cs | 2 +- .../src/System/Net/WebProxy.cs | 7 +- .../Net/WebSockets/WebSocketHandle.Managed.cs | 7 +- .../src/Internal/Win32/RegistryKey.cs | 2 +- .../src/System/ApplicationId.cs | 12 +--- .../Collections/Generic/KeyValuePair.cs | 34 ++-------- .../Diagnostics/Tracing/ActivityTracker.cs | 2 +- .../Diagnostics/Tracing/EventCounter.cs | 2 +- .../System/Diagnostics/Tracing/EventSource.cs | 12 ++-- .../Diagnostics/Tracing/PollingCounter.cs | 2 +- .../src/System/Environment.Win32.cs | 2 +- .../System/Globalization/DateTimeFormat.cs | 2 +- .../src/System/Globalization/DateTimeParse.cs | 2 +- .../System/Globalization/HebrewCalendar.cs | 2 +- .../src/System/IO/BufferedStream.cs | 4 +- .../Strategies/BufferedFileStreamStrategy.cs | 4 +- .../src/System/MemoryExtensions.cs | 12 ++-- .../src/System/Numerics/Plane.cs | 3 +- .../src/System/OperatingSystem.cs | 5 +- .../src/System/Resources/ResourceReader.cs | 4 +- .../Runtime/InteropServices/COMException.cs | 3 +- .../InteropServices/ExternalException.cs | 3 +- .../StandardOleMarshalObject.Windows.cs | 2 +- .../System.Private.CoreLib/src/System/SR.cs | 2 +- .../System/Text/DecoderExceptionFallback.cs | 4 +- .../Text/EncoderLatin1BestFitFallback.cs | 4 +- .../src/System/Text/Rune.cs | 2 +- .../src/System/Text/UnicodeDebug.cs | 38 +++-------- .../src/System/Text/UnicodeEncoding.cs | 4 +- ...TimeZoneInfo.FullGlobalizationData.Unix.cs | 2 +- .../src/System/TimeZoneInfo.Unix.cs | 2 +- .../src/System/Version.cs | 66 +++++++------------ .../Serialization/Json/JsonWriterDelegator.cs | 2 +- .../Serialization/Json/XmlJsonWriter.cs | 2 +- .../Serialization/XmlObjectSerializer.cs | 2 +- .../XmlObjectSerializerWriteContext.cs | 4 +- .../Serialization/XmlWriterDelegator.cs | 2 +- .../src/System/Xml/XmlBaseReader.cs | 8 +-- .../src/System/Xml/XmlDictionaryReader.cs | 8 +-- .../src/System/IPv4AddressHelper.cs | 5 +- .../System.Private.Uri/src/System/Uri.cs | 17 ++--- .../IriRelativeFileResolutionTest.cs | 12 ++-- .../UriRelativeResolutionTest.cs | 14 ++-- .../src/System/Xml/Linq/XNodeReader.cs | 3 +- .../XDocument.Common/ManagedNodeWriter.cs | 6 +- .../src/System/Xml/Cache/XPathNode.cs | 6 +- .../Xml/Core/CharEntityEncoderFallback.cs | 4 +- .../src/System/Xml/Core/XmlTextWriter.cs | 2 +- .../src/System/Xml/Schema/ContentValidator.cs | 6 +- .../src/System/Xml/Schema/Inference/Infer.cs | 2 +- .../Xml/Schema/SchemaCollectionCompiler.cs | 2 +- .../src/System/Xml/Schema/XsdDateTime.cs | 2 +- .../Serialization/XmlSerializationReader.cs | 38 +++++------ .../XmlSerializationReaderILGen.cs | 24 +++---- .../Serialization/XmlSerializationWriter.cs | 22 +++---- .../XmlSerializationWriterILGen.cs | 12 ++-- .../src/System/Xml/XmlConvert.cs | 8 +-- .../src/System/Xml/XmlException.cs | 7 +- .../Xml/Xsl/IlGen/IteratorDescriptor.cs | 2 +- .../System/Xml/Xsl/IlGen/OptimizerPatterns.cs | 2 +- .../Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs | 2 +- .../src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs | 2 +- .../System/Xml/Xsl/Runtime/EarlyBoundInfo.cs | 2 +- .../System/Xml/Xsl/Runtime/TreeIterators.cs | 2 +- .../System/Xml/Xsl/Runtime/XmlQueryOutput.cs | 34 +++++----- .../System/Xml/Xsl/Runtime/XmlQueryRuntime.cs | 6 +- .../src/System/Xml/Xsl/Runtime/XslNumber.cs | 2 +- .../src/System/Xml/Xsl/Runtime/XsltConvert.cs | 4 +- .../System/Xml/Xsl/Runtime/XsltFunctions.cs | 2 +- .../src/System/Xml/Xsl/Runtime/XsltLibrary.cs | 2 +- .../System/Xml/Xsl/XPath/XPathQilFactory.cs | 2 +- .../src/System/Xml/Xsl/Xslt/MatcherBuilder.cs | 2 +- .../src/System/Xml/Xsl/Xslt/XsltQilFactory.cs | 2 +- .../XslCompiledTransform.cs | 4 +- .../Metadata/Internal/StringHeap.cs | 2 +- .../tests/src/TestUtils/TestUtils.cs | 2 +- .../src/System/Runtime/Caching/CacheUsage.cs | 2 +- .../Runtime/Caching/HostFileChangeMonitor.cs | 2 +- .../Caching/MemoryCacheEntryChangeMonitor.cs | 4 +- .../Runtime/Caching/PhysicalMemoryMonitor.cs | 9 +-- .../tests/System/Convert.FromHexString.cs | 2 +- .../tests/System/Convert.ToHexString.cs | 2 +- .../tests/System/Math.cs | 4 +- ....InteropServices.RuntimeInformation.csproj | 1 + .../RuntimeInformation.Windows.cs | 5 +- .../RuntimeInformation/RuntimeInformation.cs | 13 ++-- .../Marshal/GetObjectForNativeVariantTests.cs | 2 +- .../BigInteger/BigIntegerToStringTests.cs | 4 +- .../tests/SerializationTypes.RuntimeOnly.cs | 4 +- .../Internal/Cryptography/OidLookup.Unix.cs | 2 +- .../Cryptography/Xml/CanonicalXmlAttribute.cs | 2 +- .../Cryptography/Xml/CanonicalXmlElement.cs | 2 +- .../Xml/CanonicalXmlProcessingInstruction.cs | 2 +- .../Cryptography/Xml/SignedXmlDebugLog.cs | 2 +- .../tests/XmlDsigXsltTransformTest.cs | 2 +- .../ServiceModel/Syndication/FeedUtils.cs | 2 +- .../tests/Utils/XmlDiffDocument.cs | 2 +- .../src/Result/RecognizedPhrase.cs | 2 +- .../src/System/Text/BaseCodePageEncoding.cs | 2 +- .../src/System/Text/DBCSCodePageEncoding.cs | 2 +- .../src/System/Text/EncoderBestFitFallback.cs | 4 +- .../src/System/Text/GB18030Encoding.cs | 8 +-- .../src/System/Text/ISCIIEncoding.cs | 8 +-- .../Text/RegularExpressions/RegexCode.cs | 6 +- .../RegularExpressions/RegexLWCGCompiler.cs | 2 +- .../tests/RegexCharacterSetTests.cs | 2 +- .../Transactions/TransactionsEtwProvider.cs | 6 +- .../tests/ExtensionsTests.cs | 40 +++++------ .../src/System/Web/HttpUtility.cs | 4 +- .../src/System/Web/Util/HttpEncoder.cs | 3 +- src/tasks/AppleAppBuilder/Xcode.cs | 28 ++++---- .../WasmAppBuilder/PInvokeTableGenerator.cs | 8 +-- 254 files changed, 671 insertions(+), 979 deletions(-) diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs index eeb472a19e5..8be9f32cbed 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs @@ -15,8 +15,7 @@ internal static partial class Interop { private const string MapsFileName = "/maps"; - private static string GetMapsFilePathForProcess(int pid) => - RootPath + pid.ToString(CultureInfo.InvariantCulture) + MapsFileName; + private static string GetMapsFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{MapsFileName}"); internal static ProcessModuleCollection? ParseMapsModules(int pid) { diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs index 5dc24477bee..22b06128926 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs @@ -30,10 +30,7 @@ internal static partial class Interop internal ulong VmPeak; } - internal static string GetStatusFilePathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatusFileName; - } + internal static string GetStatusFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatusFileName}"); internal static bool TryReadStatusFile(int pid, out ParsedStatus result) { diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs index 0ca179b736e..a85ec0a5f4f 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs @@ -76,41 +76,17 @@ internal static partial class Interop //internal long cguest_time; } - internal static string GetExeFilePathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + ExeFileName; - } + internal static string GetExeFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{ExeFileName}"); - internal static string GetCmdLinePathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + CmdLineFileName; - } + internal static string GetCmdLinePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{CmdLineFileName}"); - internal static string GetStatFilePathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatFileName; - } + internal static string GetStatFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatFileName}"); - internal static string GetTaskDirectoryPathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + TaskDirectoryName; - } + internal static string GetTaskDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}"); - internal static string GetFileDescriptorDirectoryPathForProcess(int pid) - { - return RootPath + pid.ToString(CultureInfo.InvariantCulture) + FileDescriptorDirectoryName; - } + internal static string GetFileDescriptorDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{FileDescriptorDirectoryName}"); - private static string GetStatFilePathForThread(int pid, int tid) - { - // Perf note: Calling GetTaskDirectoryPathForProcess will allocate a string, - // which we then use in another Concat call to produce another string. The straightforward alternative, - // though, since we have five input strings, is to use the string.Concat overload that takes a params array. - // This results in allocating not only the params array but also a defensive copy inside of Concat, - // which means allocating two five-element arrays. This two-string approach will result not only in fewer - // allocations, but also typically in less memory allocated, and it's a bit more maintainable. - return GetTaskDirectoryPathForProcess(pid) + tid.ToString(CultureInfo.InvariantCulture) + StatFileName; - } + private static string GetStatFilePathForThread(int pid, int tid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}{(uint)tid}{StatFileName}"); internal static bool TryReadStatFile(int pid, out ParsedStat result) { diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs index a2c6255541e..f3344792900 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs @@ -19,7 +19,7 @@ internal static partial class Interop internal int Copy(byte[] destination, int offset) { Debug.Assert(destination != null, "target destination cannot be null"); - Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, "invalid offset " + offset); + Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, $"invalid offset {offset}"); if (_data == IntPtr.Zero || _length == 0) { diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 604ac8b4c79..47a9312266e 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -295,7 +295,7 @@ internal static partial class Interop { #if DEBUG ulong assertNoError = Crypto.ErrPeekError(); - Debug.Assert(assertNoError == 0, "OpenSsl error queue is not empty, run: 'openssl errstr " + assertNoError.ToString("X") + "' for original error."); + Debug.Assert(assertNoError == 0, $"OpenSsl error queue is not empty, run: 'openssl errstr {assertNoError:X}' for original error."); #endif errorCode = Ssl.SslErrorCode.SSL_ERROR_NONE; @@ -349,7 +349,7 @@ internal static partial class Interop { #if DEBUG ulong assertNoError = Crypto.ErrPeekError(); - Debug.Assert(assertNoError == 0, "OpenSsl error queue is not empty, run: 'openssl errstr " + assertNoError.ToString("X") + "' for original error."); + Debug.Assert(assertNoError == 0, $"OpenSsl error queue is not empty, run: 'openssl errstr {assertNoError:X}' for original error."); #endif errorCode = Ssl.SslErrorCode.SSL_ERROR_NONE; diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs index ea65be9600e..ada41ba189f 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs @@ -59,14 +59,7 @@ namespace System.Net if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, this.ToString()); } - public override string ToString() - { - return "Capabilities:" + string.Format(CultureInfo.InvariantCulture, "0x{0:x}", Capabilities) - + " Version:" + Version.ToString(NumberFormatInfo.InvariantInfo) - + " RPCID:" + RPCID.ToString(NumberFormatInfo.InvariantInfo) - + " MaxToken:" + MaxToken.ToString(NumberFormatInfo.InvariantInfo) - + " Name:" + ((Name == null) ? "(null)" : Name) - + " Comment:" + ((Comment == null) ? "(null)" : Comment); - } + public override string ToString() => + $"Capabilities:0x{Capabilities:x} Version:{Version} RPCID:{RPCID} MaxToken:{MaxToken} Name:{Name ?? "(null)"} Comment: {Comment ?? "(null)"}"; } } diff --git a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs index 281691f7907..4ddf9950f5c 100644 --- a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs +++ b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs @@ -327,7 +327,7 @@ namespace System.Runtime.Serialization string returnType = _baseType; return _needsFixup && TypeArguments.Count > 0 ? - returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture) : + $"{returnType}`{(uint)TypeArguments.Count}" : returnType; } set diff --git a/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs b/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs index 8630d366a86..f716210055d 100644 --- a/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs +++ b/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs @@ -57,8 +57,7 @@ namespace System.Composition.Diagnostics StringBuilder messageBuilder = new StringBuilder(); // Format taken from TraceListener.TraceEvent in .NET Framework - messageBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}: {2} : ", - s_sourceName, eventType.ToString(), (int)traceId); + messageBuilder.Append($"{s_sourceName} {eventType}: {(int)traceId} : "); if (arguments == null) { diff --git a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs index 80a092104ae..54df9fa7798 100644 --- a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs +++ b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs @@ -525,11 +525,11 @@ namespace System.Data.Common } } } - Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">"); + Debug.Assert(isEquivalent, $"ParseInternal code vs regex message mismatch: <{msg1}> <{msg2}>"); } else { - Debug.Fail("ParseInternal code vs regex throw mismatch " + f.Message); + Debug.Fail($"ParseInternal code vs regex throw mismatch {f.Message}"); } e = null; } diff --git a/src/libraries/Common/src/System/Drawing/ColorTranslator.cs b/src/libraries/Common/src/System/Drawing/ColorTranslator.cs index d1caed09be2..b8fc7866abe 100644 --- a/src/libraries/Common/src/System/Drawing/ColorTranslator.cs +++ b/src/libraries/Common/src/System/Drawing/ColorTranslator.cs @@ -386,9 +386,7 @@ namespace System.Drawing } else { - colorString = "#" + c.R.ToString("X2", null) + - c.G.ToString("X2", null) + - c.B.ToString("X2", null); + colorString = $"#{c.R:X2}{c.G:X2}{c.B:X2}"; } return colorString; diff --git a/src/libraries/Common/src/System/IO/RowConfigReader.cs b/src/libraries/Common/src/System/IO/RowConfigReader.cs index a6bfd96daf4..571aa9afd9e 100644 --- a/src/libraries/Common/src/System/IO/RowConfigReader.cs +++ b/src/libraries/Common/src/System/IO/RowConfigReader.cs @@ -132,7 +132,7 @@ namespace System.IO } // Check If the match is at the beginning of the string, or is preceded by a newline. else if (keyIndex == 0 - || (keyIndex >= Environment.NewLine.Length && _buffer.Substring(keyIndex - Environment.NewLine.Length, Environment.NewLine.Length) == Environment.NewLine)) + || (keyIndex >= Environment.NewLine.Length && _buffer.AsSpan(keyIndex - Environment.NewLine.Length, Environment.NewLine.Length).SequenceEqual(Environment.NewLine))) { // Check if the match is followed by whitespace, meaning it is not part of a larger word. if (HasFollowingWhitespace(keyIndex, key.Length)) diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs index 4e4e0c672b4..a145951254a 100644 --- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs +++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs @@ -212,7 +212,7 @@ namespace System.Net.Security // throw if error if (errorCode != 0) { - NetEventSource.Info($"VerifySignature threw error: {errorCode.ToString("x", NumberFormatInfo.InvariantInfo)}"); + NetEventSource.Info($"VerifySignature threw error: {errorCode:x}"); throw new Win32Exception(errorCode); } @@ -256,7 +256,7 @@ namespace System.Net.Security // throw if error if (errorCode != 0) { - NetEventSource.Info($"MakeSignature threw error: {errorCode.ToString("x", NumberFormatInfo.InvariantInfo)}"); + NetEventSource.Info($"MakeSignature threw error: {errorCode:x}"); throw new Win32Exception(errorCode); } diff --git a/src/libraries/Common/tests/StaticTestGenerator/Program.cs b/src/libraries/Common/tests/StaticTestGenerator/Program.cs index 1c25d5ccd4c..2274463eba9 100644 --- a/src/libraries/Common/tests/StaticTestGenerator/Program.cs +++ b/src/libraries/Common/tests/StaticTestGenerator/Program.cs @@ -710,12 +710,12 @@ namespace StaticTestGenerator if (literal is IntPtr ptr) { - return $"new IntPtr(0x{((long)ptr).ToString("X")})"; + return $"new IntPtr(0x{(long)ptr:X})"; } if (literal is UIntPtr uptr) { - return $"new UIntPtr(0x{((ulong)uptr).ToString("X")})"; + return $"new UIntPtr(0x{(ulong)uptr:X})"; } string? result = null; @@ -732,34 +732,34 @@ namespace StaticTestGenerator result = ((bool)literal).ToString().ToLowerInvariant(); break; case TypeCode.Char: - result = $"'\\u{((int)(char)literal).ToString("X4")}'"; + result = $"'\\u{(int)(char)literal:X4}'"; break; case TypeCode.SByte: - result = $"(sbyte)({literal.ToString()})"; + result = $"(sbyte)({literal})"; break; case TypeCode.Byte: - result = $"(byte){literal.ToString()}"; + result = $"(byte){literal}"; break; case TypeCode.Int16: - result = $"(short)({literal.ToString()})"; + result = $"(short)({literal})"; break; case TypeCode.UInt16: - result = $"(ushort){literal.ToString()}"; + result = $"(ushort){literal}"; break; case TypeCode.Int32: - result = $"({literal.ToString()})"; + result = $"({literal})"; break; case TypeCode.UInt32: - result = $"{literal.ToString()}U"; + result = $"{literal}U"; break; case TypeCode.Int64: - result = $"({literal.ToString()}L)"; + result = $"({literal}L)"; break; case TypeCode.UInt64: - result = $"{literal.ToString()}UL"; + result = $"{literal}UL"; break; case TypeCode.Decimal: - result = $"({literal.ToString()}M)"; + result = $"({literal}M)"; break; case TypeCode.Single: result = diff --git a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs index ec7bb197e81..b90cffafeab 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs @@ -447,7 +447,7 @@ namespace System.Net.Http.Functional.Tests { int bytesRemaining = expectedData.Length - bytesSent; int bytesToSend = rand.Next(1, Math.Min(bytesRemaining, maxChunkSize + 1)); - await connection.WriteStringAsync(bytesToSend.ToString("X") + lineEnding); + await connection.WriteStringAsync($"{bytesToSend:X}{lineEnding}"); await connection.Stream.WriteAsync(new Memory(expectedData, bytesSent, bytesToSend)); await connection.WriteStringAsync(lineEnding); bytesSent += bytesToSend; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs b/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs index 997b09af0cb..8a1a95bb098 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs @@ -55,7 +55,7 @@ namespace Test.Cryptography for (int i = 0; i < bytes.Length; i++) { - builder.Append(bytes[i].ToString("X2")); + builder.Append($"{bytes[i]:X2}"); } return builder.ToString(); diff --git a/src/libraries/Common/tests/System/Xml/XmlCoreTest/ManagedNodeWriter.cs b/src/libraries/Common/tests/System/Xml/XmlCoreTest/ManagedNodeWriter.cs index a96b25090b7..d51c5e3cf87 100644 --- a/src/libraries/Common/tests/System/Xml/XmlCoreTest/ManagedNodeWriter.cs +++ b/src/libraries/Common/tests/System/Xml/XmlCoreTest/ManagedNodeWriter.cs @@ -109,14 +109,14 @@ namespace XmlCoreTest.Common if (pubid == null) { if (sysid != null) - dt.Append(" SYSTEM " + sysid); + dt.Append($" SYSTEM {sysid}"); } else { - dt.Append(" PUBLIC " + pubid); + dt.Append($" PUBLIC {pubid}"); if (sysid != null) { - dt.Append(" " + sysid); + dt.Append($" {sysid}"); } } @@ -298,7 +298,7 @@ namespace XmlCoreTest.Common /// public void PutCData() { - _q.Append(""); + _q.Append($""); } /// @@ -306,7 +306,7 @@ namespace XmlCoreTest.Common /// public void PutPI() { - _q.Append(""); + _q.Append($""); } /// @@ -314,7 +314,7 @@ namespace XmlCoreTest.Common /// public void PutComment() { - _q.Append(""); + _q.Append($""); } /// diff --git a/src/libraries/Common/tests/System/Xml/XmlDiff/XmlDiffDocument.cs b/src/libraries/Common/tests/System/Xml/XmlDiff/XmlDiffDocument.cs index c765bf4a589..23cfda1c710 100644 --- a/src/libraries/Common/tests/System/Xml/XmlDiff/XmlDiffDocument.cs +++ b/src/libraries/Common/tests/System/Xml/XmlDiff/XmlDiffDocument.cs @@ -1594,7 +1594,7 @@ namespace System.Xml.XmlDiff w.WriteString(Value); break; default: - Debug.Assert(false, "Wrong type for text-like node : " + this._nodetype.ToString()); + Debug.Assert(false, $"Wrong type for text-like node : {this._nodetype}"); break; } } diff --git a/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/ParsedUnicodeData.cs b/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/ParsedUnicodeData.cs index 6496568c75b..c7155243400 100644 --- a/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/ParsedUnicodeData.cs +++ b/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/ParsedUnicodeData.cs @@ -151,7 +151,7 @@ namespace System.Text.Unicode string baseName = value.PropName[..^1]; for (int i = value.FirstCodePoint; i <= value.LastCodePoint /* inclusive */; i++) { - dict.Add(i, baseName + i.ToString("X4", CultureInfo.InvariantCulture)); + dict.Add(i, $"{baseName}{i:X4}"); } } } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj b/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj index 322c4c10680..8f9de151dfd 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj +++ b/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj @@ -247,6 +247,7 @@ + diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs index 289a0531458..9d2373f0b69 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArray.cs @@ -82,7 +82,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop // See if we can find an existing type foreach (Type t in s_generatedTypes) { - int arity = int.Parse(t.Name.Substring("VariantArray".Length), CultureInfo.InvariantCulture); + int arity = int.Parse(t.Name.AsSpan("VariantArray".Length), provider: CultureInfo.InvariantCulture); if (size == arity) { return t; diff --git a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs index bf78cbc03c9..e3159637dc7 100644 --- a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs +++ b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs @@ -91,11 +91,11 @@ namespace System.ComponentModel : NativeErrorCode.ToString(CultureInfo.InvariantCulture); if (HResult == E_FAIL) { - s.AppendFormat(CultureInfo.InvariantCulture, " ({0})", nativeErrorString); + s.AppendFormat($" ({nativeErrorString})"); } else { - s.AppendFormat(CultureInfo.InvariantCulture, " ({0:X8}, {1})", HResult, nativeErrorString); + s.AppendFormat($" ({HResult:X8}, {nativeErrorString})"); } if (!(string.IsNullOrEmpty(message))) diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs index 16b3d1aecfb..7e9998e9516 100644 --- a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs +++ b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs @@ -173,7 +173,7 @@ namespace Microsoft.Win32 } // We really should throw an exception here if errorCode was bad, // but we can't for compatibility reasons. - Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode); + Debug.Assert(errorCode == 0, $"RegDeleteValue failed. Here's your error code: {errorCode}"); } /// diff --git a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs index 0d7bc48f361..356529d80d2 100644 --- a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs +++ b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs @@ -793,12 +793,12 @@ namespace Microsoft.CSharp if (b == null) { Output.Write("\\u"); - Output.Write(((int)value).ToString("X4", CultureInfo.InvariantCulture)); + Output.Write(((int)value).ToString("X4")); } else { b.Append("\\u"); - b.Append(((int)value).ToString("X4", CultureInfo.InvariantCulture)); + b.Append(((int)value).ToString("X4")); } } @@ -2501,7 +2501,7 @@ namespace Microsoft.CSharp { foreach (byte b in checksumPragma.ChecksumData) { - Output.Write(b.ToString("X2", CultureInfo.InvariantCulture)); + Output.Write(b.ToString("X2")); } } Output.WriteLine("\""); diff --git a/src/libraries/System.CodeDom/src/Microsoft/VisualBasic/VBCodeGenerator.cs b/src/libraries/System.CodeDom/src/Microsoft/VisualBasic/VBCodeGenerator.cs index a25eb3ed7f4..5c55dfc0650 100644 --- a/src/libraries/System.CodeDom/src/Microsoft/VisualBasic/VBCodeGenerator.cs +++ b/src/libraries/System.CodeDom/src/Microsoft/VisualBasic/VBCodeGenerator.cs @@ -2273,7 +2273,7 @@ namespace Microsoft.VisualBasic { foreach (byte b in checksumPragma.ChecksumData) { - Output.Write(b.ToString("X2", CultureInfo.InvariantCulture)); + Output.Write(b.ToString("X2")); } } Output.WriteLine("\")"); diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs index 1433e38cf95..3d494454c0f 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs @@ -707,9 +707,9 @@ namespace System.Collections.Concurrent internal bool GrabChunk_Single(KeyValuePair[] destArray, int requestedChunkSize, ref int actualNumElementsGrabbed) { Debug.Assert(_useSingleChunking, "Expected _useSingleChecking to be true"); - Debug.Assert(requestedChunkSize == 1, "Got requested chunk size of " + requestedChunkSize + " when single-chunking was on"); - Debug.Assert(actualNumElementsGrabbed == 0, "Expected actualNumElementsGrabbed == 0, instead it is " + actualNumElementsGrabbed); - Debug.Assert(destArray.Length == 1, "Expected destArray to be of length 1, instead its length is " + destArray.Length); + Debug.Assert(requestedChunkSize == 1, $"Got requested chunk size of {requestedChunkSize} when single-chunking was on"); + Debug.Assert(actualNumElementsGrabbed == 0, $"Expected actualNumElementsGrabbed == 0, instead it is {actualNumElementsGrabbed}"); + Debug.Assert(destArray.Length == 1, $"Expected destArray to be of length 1, instead its length is {destArray.Length}"); lock (_sharedLock) { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs index 907b838a8f7..999a67dc439 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs @@ -79,7 +79,7 @@ namespace System.ComponentModel.Composition.ReflectionModel string[] genericFormatArgs = new string[genericArity]; for (int i = 0; i < genericParametersOrder.Length; i++) { - genericFormatArgs[genericParametersOrder[i]] = string.Format(CultureInfo.InvariantCulture, "{{{0}}}", i); + genericFormatArgs[genericParametersOrder[i]] = $"{{{i}}}"; } return string.Format(CultureInfo.InvariantCulture, originalGenericName, genericFormatArgs); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs index 5640c431c5e..827acc9c4e8 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CommandID.cs @@ -46,6 +46,6 @@ namespace System.ComponentModel.Design /// /// Overrides Object's ToString method. /// - public override string ToString() => Guid.ToString() + " : " + ID.ToString(CultureInfo.CurrentCulture); + public override string ToString() => $"{Guid} : {ID}"; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index f41b9fca2a1..ef3cc90c16d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -1114,7 +1114,7 @@ namespace System.ComponentModel #if DEBUG foreach (EventDescriptor dbgEvent in events) { - Debug.Assert(dbgEvent != null, "Holes in event array for type " + type); + Debug.Assert(dbgEvent != null, $"Holes in event array for type {type}"); } #endif eventCache[type] = events; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs index 9094bc6c449..69017ea3367 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs @@ -176,7 +176,7 @@ namespace System.Configuration internal int AppendCharEntity(char ch) { - string numberToWrite = ((int)ch).ToString("X", CultureInfo.InvariantCulture); + string numberToWrite = ((int)ch).ToString("X"); Write('&'); Write('#'); Write('x'); diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs index eb545649d36..19e94551a6f 100644 --- a/src/libraries/System.Console/src/System/TermInfo.cs +++ b/src/libraries/System.Console/src/System/TermInfo.cs @@ -245,9 +245,10 @@ namespace System return null; } + Span stackBuffer = stackalloc char[256]; SafeFileHandle? fd; - if (!TryOpen(directoryPath + "/" + term[0].ToString() + "/" + term, out fd) && // /directory/termFirstLetter/term (Linux) - !TryOpen(directoryPath + "/" + ((int)term[0]).ToString("X") + "/" + term, out fd)) // /directory/termFirstLetterAsHex/term (Mac) + if (!TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{term[0]}/{term}"), out fd) && // /directory/termFirstLetter/term (Linux) + !TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{(int)term[0]:X}/{term}"), out fd)) // /directory/termFirstLetterAsHex/term (Mac) { return null; } diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 27ffe33faa3..1b6c5fe1b7e 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -242,7 +242,7 @@ namespace System.Data.Common { keylist.MoveNext(); values[i] = this[keylist.Current]; - Debug.Assert(null != values[i], "null value " + keylist.Current); + Debug.Assert(null != values[i], $"null value {keylist.Current}"); } return new ReadOnlyCollection(values); } diff --git a/src/libraries/System.Data.Common/src/System/Data/DataException.cs b/src/libraries/System.Data.Common/src/System/Data/DataException.cs index 301075bf543..91409b4aa8c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataException.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataException.cs @@ -649,7 +649,7 @@ namespace System.Data public static Exception RangeArgument(int min, int max) => _Argument(SR.Format(SR.Range_Argument, (min).ToString(CultureInfo.InvariantCulture), (max).ToString(CultureInfo.InvariantCulture))); public static Exception NullRange() => _Data(SR.Range_NullRange); public static Exception NegativeMinimumCapacity() => _Argument(SR.RecordManager_MinimumCapacity); - public static Exception ProblematicChars(char charValue) => _Argument(SR.Format(SR.DataStorage_ProblematicChars, "0x" + ((ushort)charValue).ToString("X", CultureInfo.InvariantCulture))); + public static Exception ProblematicChars(char charValue) => _Argument(SR.Format(SR.DataStorage_ProblematicChars, $"0x{(ushort)charValue:X}")); public static Exception StorageSetFailed() => _Argument(SR.DataStorage_SetInvalidDataType); diff --git a/src/libraries/System.Data.Common/src/System/Data/Filter/AggregateNode.cs b/src/libraries/System.Data.Common/src/System/Data/Filter/AggregateNode.cs index 0dca43b20ca..edefa695a40 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Filter/AggregateNode.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Filter/AggregateNode.cs @@ -114,7 +114,7 @@ namespace System.Data // add column to the dependency list, do not add duplicate columns - Debug.Assert(_column != null, "Failed to bind column " + _columnName); + Debug.Assert(_column != null, $"Failed to bind column {_columnName}"); int i; for (i = 0; i < list.Count; i++) diff --git a/src/libraries/System.Data.Common/src/System/Data/Filter/FunctionNode.cs b/src/libraries/System.Data.Common/src/System/Data/Filter/FunctionNode.cs index 79577f945b9..2202a8c9f81 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Filter/FunctionNode.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Filter/FunctionNode.cs @@ -318,7 +318,7 @@ namespace System.Data switch (id) { case FunctionId.Abs: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); storageType = DataStorage.GetStorageType(argumentValues[0].GetType()); if (ExpressionNode.IsInteger(storageType)) @@ -329,7 +329,7 @@ namespace System.Data throw ExprException.ArgumentTypeInteger(s_funcs[_info]._name, 1); case FunctionId.cBool: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); storageType = DataStorage.GetStorageType(argumentValues[0].GetType()); return storageType switch @@ -341,26 +341,26 @@ namespace System.Data _ => throw ExprException.DatatypeConvertion(argumentValues[0].GetType(), typeof(bool)), }; case FunctionId.cInt: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); return Convert.ToInt32(argumentValues[0], FormatProvider); case FunctionId.cDate: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); return Convert.ToDateTime(argumentValues[0], FormatProvider); case FunctionId.cDbl: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); return Convert.ToDouble(argumentValues[0], FormatProvider); case FunctionId.cStr: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); return Convert.ToString(argumentValues[0], FormatProvider)!; case FunctionId.Charindex: - Debug.Assert(_argumentCount == 2, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 2, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); - Debug.Assert(argumentValues[0] is string, "Invalid argument type for " + s_funcs[_info]._name); - Debug.Assert(argumentValues[1] is string, "Invalid argument type for " + s_funcs[_info]._name); + Debug.Assert(argumentValues[0] is string, $"Invalid argument type for {s_funcs[_info]._name}"); + Debug.Assert(argumentValues[1] is string, $"Invalid argument type for {s_funcs[_info]._name}"); if (DataStorage.IsObjectNull(argumentValues[0]) || DataStorage.IsObjectNull(argumentValues[1])) return DBNull.Value; @@ -374,7 +374,7 @@ namespace System.Data return ((string)argumentValues[1]).IndexOf((string)argumentValues[0], StringComparison.Ordinal); case FunctionId.Iif: - Debug.Assert(_argumentCount == 3, "Invalid argument argumentCount: " + _argumentCount.ToString(FormatProvider)); + Debug.Assert(_argumentCount == 3, $"Invalid argument argumentCount: {_argumentCount.ToString(FormatProvider)}"); object first = _arguments![0].Eval(row, version); @@ -401,8 +401,8 @@ namespace System.Data return argumentValues[0]; case FunctionId.Len: - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); - Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), "Invalid argument type for " + s_funcs[_info]._name); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); + Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), $"Invalid argument type for {s_funcs[_info]._name}"); if (argumentValues[0] is SqlString) { @@ -420,10 +420,10 @@ namespace System.Data case FunctionId.Substring: - Debug.Assert(_argumentCount == 3, "Invalid argument argumentCount: " + _argumentCount.ToString(FormatProvider)); - Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), "Invalid first argument " + argumentValues[0].GetType().FullName + " in " + s_funcs[_info]._name); - Debug.Assert(argumentValues[1] is int, "Invalid second argument " + argumentValues[1].GetType().FullName + " in " + s_funcs[_info]._name); - Debug.Assert(argumentValues[2] is int, "Invalid third argument " + argumentValues[2].GetType().FullName + " in " + s_funcs[_info]._name); + Debug.Assert(_argumentCount == 3, $"Invalid argument argumentCount: {_argumentCount.ToString(FormatProvider)}"); + Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), $"Invalid first argument {argumentValues[0].GetType().FullName} in {s_funcs[_info]._name}"); + Debug.Assert(argumentValues[1] is int, $"Invalid second argument {argumentValues[1].GetType().FullName} in {s_funcs[_info]._name}"); + Debug.Assert(argumentValues[2] is int, $"Invalid third argument {argumentValues[2].GetType().FullName} in {s_funcs[_info]._name}"); // work around the differences in .NET and VBA implementation of the Substring function // 1. The Argument is 0-based in .NET, and 1-based in VBA @@ -462,8 +462,8 @@ namespace System.Data case FunctionId.Trim: { - Debug.Assert(_argumentCount == 1, "Invalid argument argumentCount for " + s_funcs[_info]._name + " : " + _argumentCount.ToString(FormatProvider)); - Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), "Invalid argument type for " + s_funcs[_info]._name); + Debug.Assert(_argumentCount == 1, $"Invalid argument argumentCount for {s_funcs[_info]._name} : {_argumentCount.ToString(FormatProvider)}"); + Debug.Assert((argumentValues[0] is string) || (argumentValues[0] is SqlString), $"Invalid argument type for {s_funcs[_info]._name}"); if (DataStorage.IsObjectNull(argumentValues[0])) return DBNull.Value; diff --git a/src/libraries/System.Data.Common/src/System/Data/Filter/NameNode.cs b/src/libraries/System.Data.Common/src/System/Data/Filter/NameNode.cs index ded0f3982a6..ea2cc9de54a 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Filter/NameNode.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Filter/NameNode.cs @@ -58,7 +58,7 @@ namespace System.Data _found = true; // add column to the dependency list, do not add duplicate columns - Debug.Assert(_column != null, "Failed to bind column " + _name); + Debug.Assert(_column != null, $"Failed to bind column {_name}"); int i; for (i = 0; i < list.Count; i++) diff --git a/src/libraries/System.Data.Common/src/System/Data/ForeignKeyConstraint.cs b/src/libraries/System.Data.Common/src/System/Data/ForeignKeyConstraint.cs index 1d7ee94a478..39d130e4ff0 100644 --- a/src/libraries/System.Data.Common/src/System/Data/ForeignKeyConstraint.cs +++ b/src/libraries/System.Data.Common/src/System/Data/ForeignKeyConstraint.cs @@ -504,7 +504,7 @@ namespace System.Data internal void CheckCanRemoveParentRow(DataRow row) { - Debug.Assert(Table?.DataSet != null, "Relation " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening."); + Debug.Assert(Table?.DataSet != null, $"Relation {ConstraintName} isn't part of a DataSet, so this check shouldn't be happening."); if (!Table.DataSet.EnforceConstraints) { return; @@ -517,7 +517,7 @@ namespace System.Data internal void CheckCascade(DataRow row, DataRowAction action) { - Debug.Assert(Table?.DataSet != null, "ForeignKeyConstraint " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening."); + Debug.Assert(Table?.DataSet != null, $"ForeignKeyConstraint {ConstraintName} isn't part of a DataSet, so this check shouldn't be happening."); if (row._inCascade) { diff --git a/src/libraries/System.Data.Common/src/System/Data/Merger.cs b/src/libraries/System.Data.Common/src/System/Data/Merger.cs index 5a4c7b1179f..616acba1372 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Merger.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Merger.cs @@ -608,7 +608,7 @@ namespace System.Data } else { - Debug.Assert(MissingSchemaAction.Error == _missingSchemaAction, "Unexpected value of MissingSchemaAction parameter : " + _missingSchemaAction.ToString()); + Debug.Assert(MissingSchemaAction.Error == _missingSchemaAction, $"Unexpected value of MissingSchemaAction parameter : {_missingSchemaAction}"); throw ExceptionBuilder.MergeMissingDefinition(relation.RelationName); } } diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBinary.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBinary.cs index 3792151f36d..a757be446e7 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBinary.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLBinary.cs @@ -119,8 +119,7 @@ namespace System.Data.SqlTypes /// /// Returns a string describing a object. /// - public override string ToString() => - _value is null ? SQLResource.NullString : "SqlBinary(" + _value.Length.ToString(CultureInfo.InvariantCulture) + ")"; + public override string ToString() => _value is null ? SQLResource.NullString : $"SqlBinary({_value.Length})"; // Unary operators diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLDecimal.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLDecimal.cs index fd41a8fb191..64e7815e294 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLDecimal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLDecimal.cs @@ -1911,7 +1911,7 @@ namespace System.Data.SqlTypes private bool FGt10_38(Span rglData) { - Debug.Assert(rglData.Length == 4, "rglData.Length == 4", "Wrong array length: " + rglData.Length.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(rglData.Length == 4, "rglData.Length == 4", $"Wrong array length: {rglData.Length}"); return rglData[3] >= 0x4b3b4ca8L && ((rglData[3] > 0x4b3b4ca8L) || (rglData[2] > 0x5a86c47aL) || diff --git a/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs index df76c1da336..a025c3f7f2b 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XDRSchema.cs @@ -130,7 +130,7 @@ namespace System.Data internal bool IsTextOnlyContent(XmlElement node) { - Debug.Assert(FEqualIdentity(node, Keywords.XDR_ELEMENTTYPE, Keywords.XDRNS), "Invalid node type " + node.LocalName); + Debug.Assert(FEqualIdentity(node, Keywords.XDR_ELEMENTTYPE, Keywords.XDRNS), $"Invalid node type {node.LocalName}"); string value = node.GetAttribute(Keywords.CONTENT); if (value == null || value.Length == 0) @@ -282,12 +282,12 @@ namespace System.Data // Let's check that we realy don't have this name: foreach (NameType nt in s_mapNameTypeXdr) { - Debug.Assert(nt.name != name, "FindNameType('" + name + "') -- failed. Existed name not found"); + Debug.Assert(nt.name != name, $"FindNameType('{name}') -- failed. Existed name not found"); } #endif throw ExceptionBuilder.UndefinedDatatype(name); } - Debug.Assert(s_mapNameTypeXdr[index].name == name, "FindNameType('" + name + "') -- failed. Wrong name found"); + Debug.Assert(s_mapNameTypeXdr[index].name == name, $"FindNameType('{name}') -- failed. Wrong name found"); return s_mapNameTypeXdr[index]; } diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs index 1094e6ed536..ab2fef64dbc 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs @@ -1790,7 +1790,7 @@ namespace System.Data #if DEBUG for (int i = 1; i < s_mapNameTypeXsd.Length; ++i) { - Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, "incorrect sorting " + s_mapNameTypeXsd[i].name); + Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, $"incorrect sorting {s_mapNameTypeXsd[i].name}"); } #endif int index = Array.BinarySearch(s_mapNameTypeXsd, xsdTypeName); @@ -1856,7 +1856,7 @@ namespace System.Data #if DEBUG for (int i = 1; i < s_mapNameTypeXsd.Length; ++i) { - Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, "incorrect sorting " + s_mapNameTypeXsd[i].name); + Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, $"incorrect sorting {s_mapNameTypeXsd[i].name}"); } #endif int index = Array.BinarySearch(s_mapNameTypeXsd, name); @@ -1904,7 +1904,7 @@ namespace System.Data #if DEBUG for (int i = 1; i < s_mapNameTypeXsd.Length; ++i) { - Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, "incorrect sorting " + s_mapNameTypeXsd[i].name); + Debug.Assert((s_mapNameTypeXsd[i - 1].CompareTo(s_mapNameTypeXsd[i].name)) < 0, $"incorrect sorting {s_mapNameTypeXsd[i].name}"); } #endif int index = Array.BinarySearch(s_mapNameTypeXsd, name); @@ -1914,12 +1914,12 @@ namespace System.Data // Let's check that we realy don't have this name: foreach (NameType nt in s_mapNameTypeXsd) { - Debug.Assert(nt.name != name, "FindNameType('" + name + "') -- failed. Existed name not found"); + Debug.Assert(nt.name != name, $"FindNameType('{name}') -- failed. Existed name not found"); } #endif return false; } - Debug.Assert(s_mapNameTypeXsd[index].name == name, "FindNameType('" + name + "') -- failed. Wrong name found"); + Debug.Assert(s_mapNameTypeXsd[index].name == name, $"FindNameType('{name}') -- failed. Wrong name found"); return true; } diff --git a/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs b/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs index 46ad2ac3a80..a65a2798e5f 100644 --- a/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs +++ b/src/libraries/System.Data.Common/src/System/Data/xmlsaver.cs @@ -1295,7 +1295,7 @@ namespace System.Data { #if DEBUG // enzol: TO DO: replace the constructor with IsEqual(XmlElement) - // Debug.Assert(col.SimpleType.IsEqual(new SimpleType(elmSimpeType)), "simpleTypes with the same name have to be the same: "+name); + // Debug.Assert(col.SimpleType.IsEqual(new SimpleType(elmSimpeType)), $"simpleTypes with the same name have to be the same: {name}"); #endif } } diff --git a/src/libraries/System.Data.Common/tests/System/Data/DataSetTest2.cs b/src/libraries/System.Data.Common/tests/System/Data/DataSetTest2.cs index d5943b3da38..f2d3d31cdfe 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DataSetTest2.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DataSetTest2.cs @@ -435,7 +435,7 @@ namespace System.Data.Tests StringBuilder resultXML = new StringBuilder(); - resultXML.Append("<" + ds.DataSetName + "xmlns=\"namespace\">"); + resultXML.Append($"<{ds.DataSetName}xmlns=\"namespace\">"); resultXML.Append(""); resultXML.Append("1"); @@ -455,7 +455,7 @@ namespace System.Data.Tests resultXML.Append("Value5"); resultXML.Append(""); - resultXML.Append(""); + resultXML.Append($""); ds.Tables.Add(dt); string strXML = ds.GetXml(); diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcParameter.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcParameter.cs index 08c95dc69d0..7bcba69eb24 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcParameter.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcParameter.cs @@ -527,7 +527,7 @@ namespace System.Data.Odbc } } } - Debug.Assert((0 <= ccb) && (ccb < 0x3fffffff), "GetParameterSize: out of range " + ccb); + Debug.Assert((0 <= ccb) && (ccb < 0x3fffffff), $"GetParameterSize: out of range {ccb}"); return ccb; } diff --git a/src/libraries/System.Data.Odbc/tests/TestCommon/DataTestUtility.cs b/src/libraries/System.Data.Odbc/tests/TestCommon/DataTestUtility.cs index 5055dfb97ce..b563a7e7b85 100644 --- a/src/libraries/System.Data.Odbc/tests/TestCommon/DataTestUtility.cs +++ b/src/libraries/System.Data.Odbc/tests/TestCommon/DataTestUtility.cs @@ -23,12 +23,7 @@ namespace System.Data.Odbc.Tests // some providers does not support names (Oracle supports up to 30) public static string GetUniqueName(string prefix, string escapeLeft, string escapeRight) { - string uniqueName = string.Format("{0}{1}_{2}_{3}{4}", - escapeLeft, - prefix, - DateTime.Now.Ticks.ToString("X", CultureInfo.InvariantCulture), // up to 8 characters - Guid.NewGuid().ToString().Substring(0, 6), // take the first 6 characters only - escapeRight); + string uniqueName = $"{escapeLeft}{prefix}_{DateTime.Now.Ticks:X}_{Guid.NewGuid().ToString().Substring(0, 6)}{escapeRight}"; return uniqueName; } diff --git a/src/libraries/System.Data.OleDb/src/ColumnBinding.cs b/src/libraries/System.Data.OleDb/src/ColumnBinding.cs index 8eca4d62806..344ccae3b8a 100644 --- a/src/libraries/System.Data.OleDb/src/ColumnBinding.cs +++ b/src/libraries/System.Data.OleDb/src/ColumnBinding.cs @@ -55,7 +55,7 @@ namespace System.Data.OleDb { Debug.Assert(null != rowbinding, "null rowbinding"); Debug.Assert(null != bindings, "null bindings"); - Debug.Assert(ODB.SizeOf_tagDBBINDING <= offset, "invalid offset" + offset); + Debug.Assert(ODB.SizeOf_tagDBBINDING <= offset, $"invalid offset {offset}"); _dataReader = dataReader; _rowbinding = rowbinding; diff --git a/src/libraries/System.Data.OleDb/src/DbConnectionOptions.cs b/src/libraries/System.Data.OleDb/src/DbConnectionOptions.cs index 2d47d32c4d9..afce7a7dbc3 100644 --- a/src/libraries/System.Data.OleDb/src/DbConnectionOptions.cs +++ b/src/libraries/System.Data.OleDb/src/DbConnectionOptions.cs @@ -844,8 +844,8 @@ namespace System.Data.Common string keyname = (string)entry.Key; string? value1 = (string?)entry.Value; string? value2 = (string?)parsetable[keyname]; - Debug.Assert(parsetable.Contains(keyname), "ParseInternal code vs. regex mismatch keyname <" + keyname + ">"); - Debug.Assert(value1 == value2, "ParseInternal code vs. regex mismatch keyvalue <" + value1 + "> <" + value2 + ">"); + Debug.Assert(parsetable.Contains(keyname), $"ParseInternal code vs. regex mismatch keyname <{keyname}>"); + Debug.Assert(value1 == value2, $"ParseInternal code vs. regex mismatch keyvalue <{value1}> <{value2}>"); } } @@ -871,11 +871,11 @@ namespace System.Data.Common } } } - Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">"); + Debug.Assert(isEquivalent, $"ParseInternal code vs regex message mismatch: <{msg1}> <{msg2}>"); } else { - Debug.Assert(false, "ParseInternal code vs regex throw mismatch " + f.Message); + Debug.Assert(false, $"ParseInternal code vs regex throw mismatch {f.Message}"); } e = null; } diff --git a/src/libraries/System.Data.OleDb/src/DbPropSet.cs b/src/libraries/System.Data.OleDb/src/DbPropSet.cs index 756c4612c3f..c0b719c7373 100644 --- a/src/libraries/System.Data.OleDb/src/DbPropSet.cs +++ b/src/libraries/System.Data.OleDb/src/DbPropSet.cs @@ -254,7 +254,7 @@ namespace System.Data.OleDb for (int i = 0; i < properties.Length; ++i) { - Debug.Assert(null != properties[i], "null tagDBPROP " + i.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(null != properties[i], $"null tagDBPROP {i.ToString(CultureInfo.InvariantCulture)}"); IntPtr propertyPtr = ADP.IntPtrOffset(propset.rgProperties, i * ODB.SizeOf_tagDBPROP); Marshal.StructureToPtr(properties[i], propertyPtr, false/*deleteold*/); } diff --git a/src/libraries/System.Data.OleDb/src/OleDbConnection.cs b/src/libraries/System.Data.OleDb/src/OleDbConnection.cs index 6417087aa62..8d78f2ac493 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbConnection.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbConnection.cs @@ -235,7 +235,7 @@ namespace System.Data.OleDb break; default: // have to assume everything is okay - Debug.Assert(false, "Unknown 'Connection Status' value " + connectionStatus.ToString("G", CultureInfo.InvariantCulture)); + Debug.Assert(false, $"Unknown 'Connection Status' value {connectionStatus.ToString("G", CultureInfo.InvariantCulture)}"); break; } } diff --git a/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs b/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs index e50ba5a23b1..e305bfd0416 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbDataReader.cs @@ -584,7 +584,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("OleDbDataReader[" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + ", " + dbColumnInfo.pwszName + "]=" + dbType.enumOleDbType.ToString() + "," + dbType.dataSourceType + ", " + dbType.wType); + Debug.WriteLine($"OleDbDataReader[{info.ordinal}, {dbColumnInfo.pwszName}]={dbType.enumOleDbType},{dbType.dataSourceType}, {dbType.wType}"); } #endif rowCount++; @@ -2108,7 +2108,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("PartialKeyColumn detected: <" + name + "> metaindex=" + metaindex); + Debug.WriteLine($"PartialKeyColumn detected: <{name}> metaindex={metaindex}"); } #endif partialPrimaryKey = true; @@ -2199,7 +2199,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("MultipleUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">"); + Debug.WriteLine($"MultipleUniqueIndexes detected: <{uniqueIndexName}> <{indexname}>"); } #endif uniq = null; @@ -2211,7 +2211,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("PartialKeyColumn detected: " + name); + Debug.WriteLine($"PartialKeyColumn detected: {name}"); } #endif partialPrimaryKey = true; @@ -2226,7 +2226,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("PartialUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">"); + Debug.WriteLine($"PartialUniqueIndexes detected: <{uniqueIndexName}> <{indexname}>"); } #endif uniq = null; @@ -2247,7 +2247,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("upgrade single unique index to be a key: <" + uniqueIndexName + ">"); + Debug.WriteLine($"upgrade single unique index to be a key: <{uniqueIndexName}>"); } #endif // upgrade single unique index to be a key @@ -2498,7 +2498,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("Filtered Column: DBCOLUMN_GUID=DBCOL_SPECIALCOL DBCOLUMN_NAME=" + info.columnName + " DBCOLUMN_KEYCOLUMN=" + info.isKeyColumn); + Debug.WriteLine($"Filtered Column: DBCOLUMN_GUID=DBCOL_SPECIALCOL DBCOLUMN_NAME={info.columnName} DBCOLUMN_KEYCOLUMN={info.isKeyColumn}"); } #endif info.isHidden = true; @@ -2509,7 +2509,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("Filtered Column: DBCOLUMN_NUMBER=" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + " DBCOLUMN_NAME=" + info.columnName); + Debug.WriteLine($"Filtered Column: DBCOLUMN_NUMBER={info.ordinal} DBCOLUMN_NAME={info.columnName}"); } #endif info.isHidden = true; @@ -2520,7 +2520,7 @@ namespace System.Data.OleDb #if DEBUG if (AdapterSwitches.DataSchema.TraceVerbose) { - Debug.WriteLine("Filtered Column: DBCOLUMN_FLAGS=" + info.flags.ToString("X8", null) + " DBCOLUMN_NAME=" + info.columnName); + Debug.WriteLine($"Filtered Column: DBCOLUMN_FLAGS={info.flags:X8} DBCOLUMN_NAME={info.columnName}"); } #endif info.isHidden = true; diff --git a/src/libraries/System.Data.OleDb/src/OleDbStruct.cs b/src/libraries/System.Data.OleDb/src/OleDbStruct.cs index 69a0e4807db..0c7dd5ad35c 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbStruct.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbStruct.cs @@ -52,10 +52,10 @@ namespace System.Data.OleDb { builder.Append("pwszDataSourceType =").Append(Marshal.PtrToStringUni(pwszDataSourceType)).Append(Environment.NewLine); } - builder.Append("\tulParamSize =" + ulParamSize.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tdwFlags =0x" + dwFlags.ToString("X4", CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tPrecision =" + bPrecision.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tScale =" + bScale.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); + builder.AppendLine($"\tulParamSize ={ulParamSize}"); + builder.AppendLine($"\tdwFlags =0x{dwFlags:X4}"); + builder.AppendLine($"\tPrecision ={bPrecision}"); + builder.AppendLine($"\tScale ={bScale}"); return builder.ToString(); } #endif @@ -78,12 +78,12 @@ namespace System.Data.OleDb builder.Append("tagDBPARAMBINDINFO").Append(Environment.NewLine); if (IntPtr.Zero != pwszDataSourceType) { - builder.Append("pwszDataSourceType =").Append(Marshal.PtrToStringUni(pwszDataSourceType)).Append(Environment.NewLine); + builder.AppendLine($"pwszDataSourceType ={Marshal.PtrToStringUni(pwszDataSourceType)}"); } - builder.Append("\tulParamSize =" + ulParamSize.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tdwFlags =0x" + dwFlags.ToString("X4", CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tPrecision =" + bPrecision.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tScale =" + bScale.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); + builder.AppendLine($"\tulParamSize ={ulParamSize}"); + builder.AppendLine($"\tdwFlags =0x{dwFlags:X4}"); + builder.AppendLine($"\tPrecision ={bPrecision}"); + builder.AppendLine($"\tScale ={bScale}"); return builder.ToString(); } #endif @@ -144,15 +144,15 @@ namespace System.Data.OleDb public override string ToString() { StringBuilder builder = new StringBuilder(); - builder.Append("tagDBBINDING").Append(Environment.NewLine); - builder.Append("\tOrdinal =" + iOrdinal.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tValueOffset =" + obValue.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tLengthOffset=" + obLength.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tStatusOffset=" + obStatus.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tMaxLength =" + cbMaxLen.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tDB_Type =" + ODB.WLookup(wType)).Append(Environment.NewLine); - builder.Append("\tPrecision =" + bPrecision.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\tScale =" + bScale.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); + builder.AppendLine("tagDBBINDING"); + builder.AppendLine($"\tOrdinal ={iOrdinal}"); + builder.AppendLine($"\tValueOffset ={obValue}"); + builder.AppendLine($"\tLengthOffset={obLength}"); + builder.AppendLine($"\tStatusOffset={obStatus}"); + builder.AppendLine($"\tMaxLength ={cbMaxLen}"); + builder.AppendLine($"\tDB_Type ={ODB.WLookup(wType)}"); + builder.AppendLine($"\tPrecision ={bPrecision}"); + builder.AppendLine($"\tScale ={bScale}"); return builder.ToString(); } #endif @@ -442,14 +442,14 @@ namespace System.Data.OleDb public override string ToString() { StringBuilder builder = new StringBuilder(); - builder.Append("tagDBCOLUMNINFO: " + Convert.ToString(pwszName, CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + iOrdinal.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + "0x" + dwFlags.ToString("X8", CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + ulColumnSize.ToInt64().ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + "0x" + wType.ToString("X2", CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + bPrecision.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + bScale.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); - builder.Append("\t" + columnid.eKind.ToString(CultureInfo.InvariantCulture)).Append(Environment.NewLine); + builder.AppendLine($"tagDBCOLUMNINFO: {Convert.ToString(pwszName, CultureInfo.InvariantCulture)}"); + builder.AppendLine($"\t{iOrdinal.ToInt64().ToString(CultureInfo.InvariantCulture)}"); + builder.AppendLine($"\t0x{dwFlags:X8}"); + builder.AppendLine($"\t{ulColumnSize}"); + builder.AppendLine($"\t0x{wType:X2}"); + builder.AppendLine($"\t{bPrecision}"); + builder.AppendLine($"\t{bScale}"); + builder.AppendLine($"\t{columnid.eKind}"); return builder.ToString(); } #endif diff --git a/src/libraries/System.Data.OleDb/src/OleDb_Util.cs b/src/libraries/System.Data.OleDb/src/OleDb_Util.cs index b0a39dea96c..1426320311c 100644 --- a/src/libraries/System.Data.OleDb/src/OleDb_Util.cs +++ b/src/libraries/System.Data.OleDb/src/OleDb_Util.cs @@ -697,9 +697,7 @@ namespace System.Data.OleDb { builder.Length = 0; } - builder.Append("(0x"); - builder.Append(((int)hr).ToString("X8", CultureInfo.InvariantCulture)); - builder.Append(')'); + builder.Append($"(0x{(int)hr:X8})"); return builder.ToString(); } @@ -710,9 +708,7 @@ namespace System.Data.OleDb string? value = (string?)g_wlookpup[id]; if (null == value) { - value = "0x" + ((short)id).ToString("X2", CultureInfo.InvariantCulture) + " " + ((short)id); - value += " " + ((DBTypeEnum)id).ToString(); - g_wlookpup[id] = value; + g_wlookpup[id] = value = $"0x{(short)id:X2} {(short)id} {(DBTypeEnum)id}"; } return value; } diff --git a/src/libraries/System.Data.OleDb/src/System/Data/Common/AdapterUtil.cs b/src/libraries/System.Data.OleDb/src/System/Data/Common/AdapterUtil.cs index f5bacfd6239..473789ae89f 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/Common/AdapterUtil.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/Common/AdapterUtil.cs @@ -266,7 +266,7 @@ namespace System.Data.Common case CommandType.Text: case CommandType.StoredProcedure: case CommandType.TableDirect: - Debug.Assert(false, "valid CommandType " + value.ToString()); + Debug.Assert(false, $"valid CommandType {value}"); break; } #endif @@ -283,7 +283,7 @@ namespace System.Data.Common case DataRowVersion.Current: case DataRowVersion.Original: case DataRowVersion.Proposed: - Debug.Assert(false, "valid DataRowVersion " + value.ToString()); + Debug.Assert(false, $"valid DataRowVersion {value}"); break; } #endif @@ -303,7 +303,7 @@ namespace System.Data.Common case IsolationLevel.RepeatableRead: case IsolationLevel.Serializable: case IsolationLevel.Snapshot: - Debug.Assert(false, "valid IsolationLevel " + value.ToString()); + Debug.Assert(false, $"valid IsolationLevel {value}"); break; } #endif @@ -320,7 +320,7 @@ namespace System.Data.Common case ParameterDirection.Output: case ParameterDirection.InputOutput: case ParameterDirection.ReturnValue: - Debug.Assert(false, "valid ParameterDirection " + value.ToString()); + Debug.Assert(false, $"valid ParameterDirection {value}"); break; } #endif @@ -337,7 +337,7 @@ namespace System.Data.Common case UpdateRowSource.OutputParameters: case UpdateRowSource.FirstReturnedRecord: case UpdateRowSource.Both: - Debug.Assert(false, "valid UpdateRowSource " + value.ToString()); + Debug.Assert(false, $"valid UpdateRowSource {value}"); break; } #endif diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs index d92bd9471ba..ce77b52cc67 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs @@ -65,7 +65,7 @@ namespace System.Diagnostics private static string ConvertTo8DigitHex(uint value) { - return value.ToString("X8", CultureInfo.InvariantCulture); + return value.ToString("X8"); } private static Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr memPtr) @@ -126,18 +126,20 @@ namespace System.Diagnostics // private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage) { - _companyName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\CompanyName"); - _fileDescription = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\FileDescription"); - _fileVersion = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\FileVersion"); - _internalName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\InternalName"); - _legalCopyright = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalCopyright"); - _originalFilename = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\OriginalFilename"); - _productName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductName"); - _productVersion = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductVersion"); - _comments = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\Comments"); - _legalTrademarks = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalTrademarks"); - _privateBuild = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\PrivateBuild"); - _specialBuild = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\SpecialBuild"); + Span stackBuffer = stackalloc char[256]; + + _companyName = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\CompanyName")); + _fileDescription = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\FileDescription")); + _fileVersion = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\FileVersion")); + _internalName = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\InternalName")); + _legalCopyright = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalCopyright")); + _originalFilename = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\OriginalFilename")); + _productName = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductName")); + _productVersion = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductVersion")); + _comments = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\Comments")); + _legalTrademarks = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalTrademarks")); + _privateBuild = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\PrivateBuild")); + _specialBuild = GetFileVersionString(memIntPtr, string.Create(null, stackBuffer, $"\\\\StringFileInfo\\\\{codepage}\\\\SpecialBuild")); _language = GetFileVersionLanguage(memIntPtr); diff --git a/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/TextWriterTraceListener.cs b/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/TextWriterTraceListener.cs index 9c4015842b1..28325cc356a 100644 --- a/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/TextWriterTraceListener.cs +++ b/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/TextWriterTraceListener.cs @@ -240,7 +240,7 @@ namespace System.Diagnostics } catch (IOException) { - fileNameOnly = Guid.NewGuid().ToString() + fileNameOnly; + fileNameOnly = $"{Guid.NewGuid()}{fileNameOnly}"; fullPath = Path.Combine(dirPath, fileNameOnly); continue; } diff --git a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs index 3935b1609fa..a8441268574 100644 --- a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs +++ b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs @@ -184,7 +184,7 @@ namespace System.Diagnostics public virtual void TraceTransfer(TraceEventCache? eventCache, string source, int id, string? message, Guid relatedActivityId) { - TraceEvent(eventCache, source, TraceEventType.Transfer, id, message + ", relatedActivityId=" + relatedActivityId.ToString()); + TraceEvent(eventCache, source, TraceEventType.Transfer, id, string.Create(null, stackalloc char[256], $"{message}, relatedActivityId={relatedActivityId}")); } /// @@ -201,8 +201,8 @@ namespace System.Diagnostics public virtual void Fail(string? message, string? detailMessage) { WriteLine(detailMessage is null ? - SR.TraceListenerFail + " " + message : - SR.TraceListenerFail + " " + message + " " + detailMessage); + $"{SR.TraceListenerFail} {message}" : + $"{SR.TraceListenerFail} {message} {detailMessage}"); } /// @@ -390,7 +390,7 @@ namespace System.Diagnostics private void WriteHeader(string source, TraceEventType eventType, int id) { - Write($"{source} {eventType.ToString()}: {id.ToString(CultureInfo.InvariantCulture)} : "); + Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[256], $"{source} {eventType}: {id} : ")); } private void WriteFooter(TraceEventCache? eventCache) @@ -425,14 +425,16 @@ namespace System.Diagnostics WriteLine(string.Empty); } + Span stackBuffer = stackalloc char[128]; + if (IsEnabled(TraceOptions.ThreadId)) WriteLine("ThreadId=" + eventCache.ThreadId); if (IsEnabled(TraceOptions.DateTime)) - WriteLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture)); + WriteLine(string.Create(null, stackBuffer, $"DateTime={eventCache.DateTime:o}")); if (IsEnabled(TraceOptions.Timestamp)) - WriteLine("Timestamp=" + eventCache.Timestamp); + WriteLine(string.Create(null, stackBuffer, $"Timestamp={eventCache.Timestamp}")); if (IsEnabled(TraceOptions.Callstack)) WriteLine("Callstack=" + eventCache.Callstack); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Brush.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Brush.cs index af4e81ea8af..de964a2b13d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Brush.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Brush.cs @@ -47,7 +47,7 @@ namespace System.Drawing #endif Gdip.GdipDeleteBrush(new HandleRef(this, _nativeBrush)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs index a1c04e7d2f5..f0a1bd9aec3 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs @@ -94,7 +94,7 @@ namespace System.Drawing.Drawing2D #endif Gdip.GdipDeletePath(new HandleRef(this, _nativePath)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs index 4b4135665ee..8e83841c4f6 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs @@ -38,7 +38,7 @@ namespace System.Drawing.Drawing2D #endif Gdip.GdipDeletePathIter(new HandleRef(this, nativeIter)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs index b01b88392b9..410f4478913 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs @@ -48,7 +48,7 @@ namespace System.Drawing.Drawing2D { handle = IntPtr.Zero; } - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); } return status == Gdip.Ok; } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs index 91a932b60dc..8163d0eefb1 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs @@ -65,7 +65,7 @@ namespace System.Drawing int status = Gdip.GdipCreateFont(new HandleRef(this, family.NativeFamily), emSize, style, unit, out _nativeFont); if (status == Gdip.FontStyleNotFound) - throw new ArgumentException($"Style {style.ToString()} isn't supported by font {familyName}."); + throw new ArgumentException($"Style {style} isn't supported by font {familyName}."); Gdip.CheckStatus(status); } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs index 6634ee78841..0a738e0e3e8 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs @@ -183,7 +183,7 @@ namespace System.Drawing #endif Gdip.GdipDeleteFont(new HandleRef(this, _nativeFont)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs index 5bb82adbc80..ace1758b4d5 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs @@ -40,7 +40,8 @@ namespace System.Drawing ValueStringBuilder sb = default; sb.Append(font.Name); - sb.Append(culture.TextInfo.ListSeparator[0] + " "); + sb.Append(culture.TextInfo.ListSeparator[0]); + sb.Append(" "); sb.Append(font.Size.ToString(culture.NumberFormat)); switch (font.Unit) @@ -79,7 +80,8 @@ namespace System.Drawing if (font.Style != FontStyle.Regular) { - sb.Append(culture.TextInfo.ListSeparator[0] + " style="); + sb.Append(culture.TextInfo.ListSeparator[0]); + sb.Append(" style="); sb.Append(font.Style.ToString()); } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/FontFamily.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/FontFamily.cs index 6e0438be885..89b80a06cbc 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/FontFamily.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/FontFamily.cs @@ -162,7 +162,7 @@ namespace System.Drawing #endif Gdip.GdipDeleteFontFamily(new HandleRef(this, _nativeFamily)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs index 929ea19c4fa..8dd2ad15b22 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs @@ -178,7 +178,7 @@ namespace System.Drawing Gdip.GdipDeleteGraphics(new HandleRef(this, NativeGraphics)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index ccea5ff42d5..c359e7af7fd 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -95,7 +95,7 @@ namespace System.Drawing #endif Gdip.GdipDisposeImage(new HandleRef(this, nativeImage)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs index 5fc556efad7..44d36d291cb 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs @@ -78,7 +78,7 @@ namespace System.Drawing.Imaging if (this == s_time) return "Time"; if (this == s_resolution) return "Resolution"; if (this == s_page) return "Page"; - return "[FrameDimension: " + _guid + "]"; + return $"[FrameDimension: {_guid}]"; } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs index 5831014b1e6..dca1674b2d5 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs @@ -94,7 +94,7 @@ namespace System.Drawing.Imaging #endif Gdip.GdipDisposeImageAttributes(new HandleRef(this, nativeImageAttributes)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs index 705fad55ee5..ca4e867fcba 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs @@ -170,7 +170,7 @@ namespace System.Drawing.Imaging if (this.Guid == s_tiff.Guid) return "Tiff"; if (this.Guid == s_exif.Guid) return "Exif"; if (this.Guid == s_icon.Guid) return "Icon"; - return "[ImageFormat: " + _guid + "]"; + return $"[ImageFormat: {_guid}]"; } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs index 9d2343d83b5..8645b31c10c 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs @@ -55,7 +55,7 @@ namespace System.Drawing.Internal list[index] = new WeakReference(obj); else { - Debug.Assert(list[index].Target == null, "Trying to reuse a weak reference that isn't broken yet: list[" + index + "], length =" + list.Length); + Debug.Assert(list[index].Target == null, $"Trying to reuse a weak reference that isn't broken yet: list[{index}], length = {list.Length}"); list[index].Target = obj; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Pen.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Pen.cs index 606f8cc57af..ca5d045000d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Pen.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Pen.cs @@ -161,7 +161,7 @@ namespace System.Drawing #endif Gdip.GdipDeletePen(new HandleRef(this, NativePen)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs index 39691a9ad5e..05764ce8f21 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs @@ -235,14 +235,6 @@ namespace System.Drawing.Printing /// /// Provides some interesting information for the Margins in String form. /// - public override string ToString() - { - return "[Margins" - + " Left=" + Left.ToString(CultureInfo.InvariantCulture) - + " Right=" + Right.ToString(CultureInfo.InvariantCulture) - + " Top=" + Top.ToString(CultureInfo.InvariantCulture) - + " Bottom=" + Bottom.ToString(CultureInfo.InvariantCulture) - + "]"; - } + public override string ToString() => $"[Margins Left={Left} Right={Right} Top={Top} Bottom={Bottom}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Unix.cs index a7e8c1dbdbc..a79fb02f0a2 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Unix.cs @@ -255,9 +255,7 @@ namespace System.Drawing.Printing throw new NotImplementedException(); } - public override string ToString() - { - return $"[{nameof(PageSettings)}: {nameof(Color)}={color}, {nameof(Landscape)}={landscape}, {nameof(Margins)}={margins}, {nameof(PaperSize)}={paperSize}, {nameof(PaperSource)}={paperSource}, {nameof(PrinterResolution)}={printerResolution}]"; - } + public override string ToString() => + $"[{nameof(PageSettings)}: {nameof(Color)}={color}, {nameof(Landscape)}={landscape}, {nameof(Margins)}={margins}, {nameof(PaperSize)}={paperSize}, {nameof(PaperSource)}={paperSource}, {nameof(PrinterResolution)}={printerResolution}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Windows.cs index 8ac8e9ce247..f0e41dbc667 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Windows.cs @@ -536,16 +536,7 @@ namespace System.Drawing.Printing /// /// Provides some interesting information about the PageSettings in String form. /// - public override string ToString() - { - return "[PageSettings:" - + " Color=" + Color.ToString() - + ", Landscape=" + Landscape.ToString() - + ", Margins=" + Margins.ToString() - + ", PaperSize=" + PaperSize.ToString() - + ", PaperSource=" + PaperSource.ToString() - + ", PrinterResolution=" + PrinterResolution.ToString() - + "]"; - } + public override string ToString() => + $"[{nameof(PageSettings)}: Color={Color}, Landscape={Landscape}, Margins={Margins}, PaperSize={PaperSize}, PaperSource={PaperSource}, PrinterResolution={PrinterResolution}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs index 34b12d52c9d..4b9b49ae33d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs @@ -127,13 +127,6 @@ namespace System.Drawing.Printing /// /// Provides some interesting information about the PaperSize in String form. /// - public override string ToString() - { - return "[PaperSize " + PaperName - + " Kind=" + Kind.ToString() - + " Height=" + Height.ToString(CultureInfo.InvariantCulture) - + " Width=" + Width.ToString(CultureInfo.InvariantCulture) - + "]"; - } + public override string ToString() => $"[PaperSize {PaperName} Kind={Kind.ToString()} Height={Height.ToString(CultureInfo.InvariantCulture)} Width={Width.ToString(CultureInfo.InvariantCulture)}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs index 9db90421050..5a321cc4d81 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs @@ -63,11 +63,6 @@ namespace System.Drawing.Printing /// /// Provides some interesting information about the PaperSource in String form. /// - public override string ToString() - { - return "[PaperSource " + SourceName - + " Kind=" + Kind.ToString() - + "]"; - } + public override string ToString() => $"[PaperSource {SourceName} Kind={Kind}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Unix.cs index 25ea2116850..2d3306ed8a2 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Unix.cs @@ -187,10 +187,7 @@ namespace System.Drawing.Printing PrintController.OnEndPrint(this, printArgs); } - public override string ToString() - { - return "[PrintDocument " + this.DocumentName + "]"; - } + public override string ToString() => $"[PrintDocument {this.DocumentName}]"; // events protected virtual void OnBeginPrint(PrintEventArgs e) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Windows.cs index 3d0b09a7321..eaa73074f87 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Windows.cs @@ -248,9 +248,6 @@ namespace System.Drawing.Printing /// /// Provides some interesting information about the PrintDocument in String form. /// - public override string ToString() - { - return "[PrintDocument " + DocumentName + "]"; - } + public override string ToString() => $"[PrintDocument {DocumentName}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Unix.cs index d3e567777cd..4043ec72213 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Unix.cs @@ -365,13 +365,7 @@ namespace System.Drawing.Printing throw new NotImplementedException(); } - public override string ToString() - { - return "Printer [PrinterSettings " + printer_name + " Copies=" + copies + " Collate=" + collate - + " Duplex=" + can_duplex + " FromPage=" + from_page + " LandscapeAngle=" + landscape_angle - + " MaximumCopies=" + maximum_copies + " OutputPort=" + " ToPage=" + to_page + "]"; - - } + public override string ToString() => $"Printer [PrinterSettings {printer_name} Copies={copies} Collate={collate} Duplex={can_duplex} FromPage={from_page} LandscapeAngle={landscape_angle} MaximumCopies={maximum_copies} OutputPort= ToPage={to_page}]"; // Public subclasses #region Public Subclasses diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs index 1889516f87a..d750390d07d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs @@ -1258,16 +1258,16 @@ namespace System.Drawing.Printing { string printerName = PrinterName; return "[PrinterSettings " - + printerName - + " Copies=" + Copies.ToString(CultureInfo.InvariantCulture) - + " Collate=" + Collate.ToString(CultureInfo.InvariantCulture) - + " Duplex=" + Duplex.ToString() - + " FromPage=" + FromPage.ToString(CultureInfo.InvariantCulture) - + " LandscapeAngle=" + LandscapeAngle.ToString(CultureInfo.InvariantCulture) - + " MaximumCopies=" + MaximumCopies.ToString(CultureInfo.InvariantCulture) - + " OutputPort=" + OutputPort.ToString(CultureInfo.InvariantCulture) - + " ToPage=" + ToPage.ToString(CultureInfo.InvariantCulture) - + "]"; + + printerName + + " Copies=" + Copies.ToString(CultureInfo.InvariantCulture) + + " Collate=" + Collate.ToString(CultureInfo.InvariantCulture) + + " Duplex=" + Duplex.ToString() + + " FromPage=" + FromPage.ToString(CultureInfo.InvariantCulture) + + " LandscapeAngle=" + LandscapeAngle.ToString(CultureInfo.InvariantCulture) + + " MaximumCopies=" + MaximumCopies.ToString(CultureInfo.InvariantCulture) + + " OutputPort=" + OutputPort.ToString(CultureInfo.InvariantCulture) + + " ToPage=" + ToPage.ToString(CultureInfo.InvariantCulture) + + "]"; } // Write null terminated string, return length of string in characters (including null) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Region.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Region.cs index d87c739d8ca..379fc61e2e1 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Region.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Region.cs @@ -101,7 +101,7 @@ namespace System.Drawing #endif Gdip.GdipDeleteRegion(new HandleRef(this, NativeRegion)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/StringFormat.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/StringFormat.cs index 6a905918200..cd0a7d074d1 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/StringFormat.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/StringFormat.cs @@ -86,7 +86,7 @@ namespace System.Drawing #endif Gdip.GdipDeleteStringFormat(new HandleRef(this, nativeFormat)); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) @@ -442,9 +442,6 @@ namespace System.Drawing /// /// Converts this to a human-readable string. /// - public override string ToString() - { - return "[StringFormat, FormatFlags=" + FormatFlags.ToString() + "]"; - } + public override string ToString() => $"[StringFormat, FormatFlags={FormatFlags.ToString()}]"; } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs index 8541e3069c2..609ccae35b8 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs @@ -37,7 +37,7 @@ namespace System.Drawing.Text #endif Gdip.GdipDeletePrivateFontCollection(ref _nativeFontCollection); #if DEBUG - Debug.Assert(status == Gdip.Ok, "GDI+ returned an error status: " + status.ToString(CultureInfo.InvariantCulture)); + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); #endif } catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs index f711d9a5fe6..a7f116edada 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs @@ -565,21 +565,10 @@ namespace System.Drawing public KnownColor ToKnownColor() => (KnownColor)knownColor; - public override string ToString() - { - if (IsNamedColor) - { - return nameof(Color) + " [" + Name + "]"; - } - else if ((state & StateValueMask) != 0) - { - return nameof(Color) + " [A=" + A.ToString() + ", R=" + R.ToString() + ", G=" + G.ToString() + ", B=" + B.ToString() + "]"; - } - else - { - return nameof(Color) + " [Empty]"; - } - } + public override string ToString() => + IsNamedColor ? $"{nameof(Color)} [{Name}]": + (state & StateValueMask) != 0 ? $"{nameof(Color)} [A={A}, R={R}, G={G}, B={B}]" : + $"{nameof(Color)} [Empty]"; public static bool operator ==(Color left, Color right) => left.value == right.value diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Point.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Point.cs index 36c56ce1d0c..678cecd313e 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Point.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Point.cs @@ -165,7 +165,7 @@ namespace System.Drawing /// /// Converts this to a human readable string. /// - public override readonly string ToString() => "{X=" + X.ToString() + ",Y=" + Y.ToString() + "}"; + public override readonly string ToString() => $"{{X={X},Y={Y}}}"; private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/PointF.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/PointF.cs index 47ae16677be..1a35a72826c 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/PointF.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/PointF.cs @@ -140,6 +140,6 @@ namespace System.Drawing public override readonly int GetHashCode() => HashCode.Combine(X.GetHashCode(), Y.GetHashCode()); - public override readonly string ToString() => "{X=" + x.ToString() + ", Y=" + y.ToString() + "}"; + public override readonly string ToString() => $"{{X={x}, Y={y}}}"; } } diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs index 7d728a1fea5..e4f7500ced2 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs @@ -340,8 +340,6 @@ namespace System.Drawing /// /// Converts the attributes of this to a human readable string. /// - public override readonly string ToString() => - "{X=" + X.ToString() + ",Y=" + Y.ToString() + - ",Width=" + Width.ToString() + ",Height=" + Height.ToString() + "}"; + public override readonly string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}"; } } diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs index 508cd1252db..96f75808613 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs @@ -326,8 +326,6 @@ namespace System.Drawing /// Converts the and /// of this to a human-readable string. /// - public override readonly string ToString() => - "{X=" + X.ToString() + ",Y=" + Y.ToString() + - ",Width=" + Width.ToString() + ",Height=" + Height.ToString() + "}"; + public override readonly string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}"; } } diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Size.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Size.cs index bd140b7e080..c23a66d9142 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Size.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Size.cs @@ -189,7 +189,7 @@ namespace System.Drawing /// /// Creates a human-readable string that represents this . /// - public override readonly string ToString() => "{Width=" + width.ToString() + ", Height=" + height.ToString() + "}"; + public override readonly string ToString() => $"{{Width={width}, Height={height}}}"; /// /// Multiplies by an producing . diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/SizeF.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/SizeF.cs index 4d0eb252dd6..d386f4dad85 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/SizeF.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/SizeF.cs @@ -177,7 +177,7 @@ namespace System.Drawing /// /// Creates a human-readable string that represents this . /// - public override readonly string ToString() => "{Width=" + width.ToString() + ", Height=" + height.ToString() + "}"; + public override readonly string ToString() => $"{{Width={width}, Height={height}}}"; /// /// Multiplies by a producing . diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs index 29e2f2ce89c..938c758bd57 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs @@ -278,7 +278,7 @@ namespace System.Formats.Asn1.Tests.Writer [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] public void VerifyWriteInteger_EncodedBytes(string valueHex) { - string expectedHex = "02" + (valueHex.Length / 2).ToString("X2") + valueHex; + string expectedHex = $"02{valueHex.Length / 2:X2}{valueHex}"; AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); writer.WriteInteger(valueHex.HexToByteArray()); @@ -298,7 +298,7 @@ namespace System.Formats.Asn1.Tests.Writer [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) { - string expectedHex = "84" + (valueHex.Length / 2).ToString("X2") + valueHex; + string expectedHex = $"84{valueHex.Length / 2:X2}{valueHex}"; AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); writer.WriteInteger(valueHex.HexToByteArray(), new Asn1Tag(TagClass.ContextSpecific, 4)); diff --git a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs index 0f52f67c6b5..48f73a4527a 100644 --- a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs +++ b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs @@ -167,8 +167,7 @@ namespace System.Globalization.Tests StringBuilder sb = new StringBuilder(); for (int i=0; i()); _listOfAllDirs.Add(dirName); Directory.CreateDirectory(dirName); diff --git a/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs b/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs index 979e60a2806..3cec2c09836 100644 --- a/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs +++ b/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs @@ -331,7 +331,7 @@ namespace System.IO.Hashing.Tests foreach (byte b in input) { - builder.Append(b.ToString("X2")); + builder.Append($"{b:X2}"); } return builder.ToString(); diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorage.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorage.cs index 170ce2feb97..1ad1801c270 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorage.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorage.cs @@ -153,7 +153,7 @@ namespace System.IO.IsolatedStorage if (Helper.IsDomain(scope)) { _domainIdentity = identity; - hash = $"{hash}{SeparatorExternal}{hash}"; + hash = string.Create(null, stackalloc char[128], $"{hash}{SeparatorExternal}{hash}"); } _assemblyIdentity = identity; diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs index 174b91bb219..389ab072d9a 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs @@ -174,7 +174,7 @@ namespace System.IO.MemoryMappedFiles Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability) { // The POSIX shared memory object name must begin with '/'. After that we just want something short and unique. - string mapName = "/corefx_map_" + Guid.NewGuid().ToString("N"); + string mapName = string.Create(null, stackalloc char[128], $"/corefx_map_{Guid.NewGuid():N}"); // Determine the flags to use when creating the shared memory object Interop.Sys.OpenFlags flags = (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 ? diff --git a/src/libraries/System.IO.Pipelines/tests/Infrastructure/TestMemoryPool.cs b/src/libraries/System.IO.Pipelines/tests/Infrastructure/TestMemoryPool.cs index 9567b03e903..f990345a9bc 100644 --- a/src/libraries/System.IO.Pipelines/tests/Infrastructure/TestMemoryPool.cs +++ b/src/libraries/System.IO.Pipelines/tests/Infrastructure/TestMemoryPool.cs @@ -59,7 +59,7 @@ namespace System.IO.Pipelines ~PooledMemory() { - Debug.Assert(_returned, "Block being garbage collected instead of returned to pool" + Environment.NewLine + _leaser); + Debug.Assert(_returned, $"Block being garbage collected instead of returned to pool{Environment.NewLine}{_leaser}"); } protected override void Dispose(bool disposing) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs index 39f47249a69..dc2180b39b7 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs @@ -156,7 +156,7 @@ namespace System.IO.Pipes private void CompleteCallback(int resultState) { - Debug.Assert(resultState == ResultSuccess || resultState == ResultError, "Unexpected result state " + resultState); + Debug.Assert(resultState == ResultSuccess || resultState == ResultError, $"Unexpected result state {resultState}"); CancellationToken cancellationToken = _cancellationRegistration.Token; ReleaseResources(); diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Windows.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Windows.cs index 0e46efe6998..85eb2f4eaf8 100644 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Windows.cs +++ b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Windows.cs @@ -1005,7 +1005,7 @@ namespace System.IO.Ports if (count == 0) return 0; // return immediately if no bytes requested; no need for overhead. - Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Read - called with timeout " + timeout); + Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, $"Serial Stream Read - called with timeout {timeout}"); int numBytes = 0; if (_isAsync) @@ -1069,7 +1069,7 @@ namespace System.IO.Ports if (count == 0) return; // no need to expend overhead in creating asyncResult, etc. - Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Write - write timeout is " + timeout); + Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, $"Serial Stream Write - write timeout is {timeout}"); int numBytes; if (_isAsync) @@ -1627,7 +1627,7 @@ namespace System.IO.Ports // if we get IO pending, MSDN says we should wait on the WaitHandle, then call GetOverlappedResult // to get the results of WaitCommEvent. bool success = waitCommEventWaitHandle.WaitOne(); - Debug.Assert(success, "waitCommEventWaitHandle.WaitOne() returned error " + Marshal.GetLastWin32Error()); + Debug.Assert(success, $"waitCommEventWaitHandle.WaitOne() returned error {Marshal.GetLastWin32Error()}"); do { diff --git a/src/libraries/System.IO.Ports/tests/SerialPort/GetPortNames.cs b/src/libraries/System.IO.Ports/tests/SerialPort/GetPortNames.cs index 8376f729aa5..99be124701c 100644 --- a/src/libraries/System.IO.Ports/tests/SerialPort/GetPortNames.cs +++ b/src/libraries/System.IO.Ports/tests/SerialPort/GetPortNames.cs @@ -80,8 +80,8 @@ namespace System.IO.Ports.Tests get { var sb = new StringBuilder(); - sb.AppendLine("PortHelper Ports: " + string.Join(",", PortHelper.GetPorts())); - sb.AppendLine("SerialPort Ports: " + string.Join(",", SerialPort.GetPortNames())); + sb.AppendLine($"PortHelper Ports: {string.Join(",", PortHelper.GetPorts())}"); + sb.AppendLine($"SerialPort Ports: {string.Join(",", SerialPort.GetPortNames())}"); return sb.ToString(); } } diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs index 0c3ef9769ae..585d5901506 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs @@ -20,10 +20,8 @@ namespace System.Linq.Expressions.Interpreter StackDepth = stackDepth; } - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "->{0} C({1}) S({2})", Index, ContinuationStackDepth, StackDepth); - } + public override string ToString() => + string.Create(CultureInfo.InvariantCulture, $"->{Index} C({ContinuationStackDepth}) S({StackDepth})"); } internal sealed class BranchLabel diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs index 0fbabd6289f..cf1f973204a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs @@ -195,7 +195,7 @@ namespace System.Linq.Expressions.Interpreter instruction.ConsumedContinuations >= 0 && instruction.ProducedContinuations >= 0, "bad instruction " + instruction.ToString()); _currentStackDepth -= instruction.ConsumedStack; - Debug.Assert(_currentStackDepth >= 0, "negative stack depth " + instruction.ToString()); + Debug.Assert(_currentStackDepth >= 0, $"negative stack depth {instruction}"); _currentStackDepth += instruction.ProducedStack; if (_currentStackDepth > _maxStackDepth) { @@ -203,7 +203,7 @@ namespace System.Linq.Expressions.Interpreter } _currentContinuationsDepth -= instruction.ConsumedContinuations; - Debug.Assert(_currentContinuationsDepth >= 0, "negative continuations " + instruction.ToString()); + Debug.Assert(_currentContinuationsDepth >= 0, $"negative continuations {instruction}"); _currentContinuationsDepth += instruction.ProducedContinuations; if (_currentContinuationsDepth > _maxContinuationDepth) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs index 784baac2de9..b5753f67732 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs @@ -51,7 +51,7 @@ namespace System.Linq.Expressions.Interpreter public bool Matches(Type exceptionType) => _exceptionType.IsAssignableFrom(exceptionType); public override string ToString() => - string.Format(CultureInfo.InvariantCulture, "catch ({0}) [{1}->{2}]", _exceptionType.Name, HandlerStartIndex, HandlerEndIndex); + string.Create(CultureInfo.InvariantCulture, $"catch ({_exceptionType.Name}) [{HandlerStartIndex}->{HandlerEndIndex}]"); } internal sealed class TryCatchFinallyHandler @@ -251,11 +251,11 @@ namespace System.Linq.Expressions.Interpreter { if (IsClear) { - return string.Format(CultureInfo.InvariantCulture, "{0}: clear", Index); + return string.Create(CultureInfo.InvariantCulture, $"{Index}: clear"); } else { - return string.Format(CultureInfo.InvariantCulture, "{0}: [{1}-{2}] '{3}'", Index, StartLine, EndLine, FileName); + return string.Create(CultureInfo.InvariantCulture, $"{Index}: [{StartLine}-{EndLine}] '{FileName}'"); } } } diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs index 3956e67b29a..77b399b366d 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs @@ -167,7 +167,7 @@ namespace System.Linq.Expressions.Interpreter InstructionList.DebugView.InstructionView instructionView = instructionViews[i]; - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}IP_{1}: {2}", _indent, i.ToString().PadLeft(4, '0'), instructionView.GetValue()).AppendLine(); + sb.AppendLine(CultureInfo.InvariantCulture, $"{_indent}IP_{i.ToString().PadLeft(4, '0')}: {instructionView.GetValue()}"); } EmitExits(sb, instructions.Length); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs index 83177991b77..d078d82a597 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs @@ -40,10 +40,8 @@ namespace System.Linq.Expressions.Interpreter _flags = (closure ? InClosureFlag : 0); } - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "{0}: {1} {2}", Index, IsBoxed ? "boxed" : null, InClosure ? "in closure" : null); - } + public override string ToString() => + string.Create(CultureInfo.InvariantCulture, $"{Index}: {(IsBoxed ? "boxed" : null)} {(InClosure ? "in closure" : null)}"); } internal readonly struct LocalDefinition diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs index 0a3586ed514..53e3fa831e1 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs @@ -45,10 +45,8 @@ namespace System.Linq.Expressions.Interpreter return 1; } - public override string ToDebugString(int instructionIndex, object? cookie, Func labelIndexer, IReadOnlyList? objects) - { - return string.Format(CultureInfo.InvariantCulture, "LoadCached({0}: {1})", _index, objects![(int)_index]); - } + public override string ToDebugString(int instructionIndex, object? cookie, Func labelIndexer, IReadOnlyList? objects) => + string.Create(CultureInfo.InvariantCulture, $"LoadCached({_index}: {objects![(int)_index]})"); public override string ToString() => "LoadCached(" + _index + ")"; } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs index 93595b1d12a..40275b5c8d7 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs @@ -175,7 +175,7 @@ namespace System.Net.Http #if DEBUG Interlocked.Increment(ref s_dbg_callDispose); #endif - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"GCHandle=0x{ToIntPtr().ToString("X")}, disposed={_disposed}, disposing={disposing}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"GCHandle=0x{ToIntPtr():X}, disposed={_disposed}, disposing={disposing}"); // Since there is no finalizer and this class is sealed, the disposing parameter should be TRUE. Debug.Assert(disposing, "WinHttpRequestState.Dispose() should have disposing=TRUE"); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs index 104808b472a..e039d50dad1 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs @@ -15,7 +15,7 @@ namespace System.Net.Http NetEventSource.Info( thisOrContextObject, - $"handle=0x{handle.ToString("X")}, context=0x{context.ToString("X")}, {GetStringFromInternetStatus(status)}", + $"handle=0x{handle:X}, context=0x{context:X}, {GetStringFromInternetStatus(status)}", memberName); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderValue.cs index ce534b0e1d5..f04f569b725 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderValue.cs @@ -56,13 +56,12 @@ namespace System.Net.Http.Headers sb.Append("=\""); if (Host != null) sb.Append(Host); sb.Append(':'); - sb.Append(Port.ToString(CultureInfo.InvariantCulture)); + sb.Append((uint)Port); sb.Append('"'); if (MaxAge != TimeSpan.FromTicks(AltSvcHeaderParser.DefaultMaxAgeTicks)) { - sb.Append("; ma="); - sb.Append((MaxAge.Ticks / TimeSpan.TicksPerSecond).ToString(CultureInfo.InvariantCulture)); + sb.Append(CultureInfo.InvariantCulture, $"; ma={MaxAge.Ticks / TimeSpan.TicksPerSecond}"); } if (Persist) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderValue.cs index b38c3396ca9..e8186404aa4 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderValue.cs @@ -205,7 +205,7 @@ namespace System.Net.Http.Headers { // In the corner case where the value is negative, ensure it uses // the invariant's negative sign rather than the current culture's. - sb.Append(maxAge.ToString(NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{maxAge}"); } } @@ -222,7 +222,7 @@ namespace System.Net.Http.Headers { // In the corner case where the value is negative, ensure it uses // the invariant's negative sign rather than the current culture's. - sb.Append(sharedMaxAge.ToString(NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{sharedMaxAge}"); } } @@ -241,7 +241,7 @@ namespace System.Net.Http.Headers { // In the corner case where the value is negative, ensure it uses // the invariant's negative sign rather than the current culture's. - sb.Append(maxStaleLimit.ToString(NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{maxStaleLimit}"); } } } @@ -259,7 +259,7 @@ namespace System.Net.Http.Headers { // In the corner case where the value is negative, ensure it uses // the invariant's negative sign rather than the current culture's. - sb.Append(minFresh.ToString(NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{minFresh}"); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs index fd1074f16f9..d52a92875a6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs @@ -449,7 +449,7 @@ namespace System.Net.Http.Headers if (info.IsEmpty) { bool headerRemoved = Remove(descriptor); - Debug.Assert(headerRemoved, "Existing header '" + descriptor.Name + "' couldn't be removed."); + Debug.Assert(headerRemoved, $"Existing header '{descriptor.Name}' couldn't be removed."); } return result; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/RangeItemHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/RangeItemHeaderValue.cs index cba711f89a2..91262983d8c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/RangeItemHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/RangeItemHeaderValue.cs @@ -56,17 +56,20 @@ namespace System.Net.Http.Headers public override string ToString() { + Span stackBuffer = stackalloc char[128]; + if (!_from.HasValue) { Debug.Assert(_to != null); - return "-" + _to.Value.ToString(NumberFormatInfo.InvariantInfo); + return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"-{_to.Value}"); } - else if (!_to.HasValue) + + if (!_to.HasValue) { - return _from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-"; + return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"{_from.Value}-"); ; } - return _from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-" + - _to.Value.ToString(NumberFormatInfo.InvariantInfo); + + return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"{_from.Value}-{_to.Value}"); } public override bool Equals([NotNullWhen(true)] object? obj) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/StringWithQualityHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/StringWithQualityHeaderValue.cs index 814a053f5ad..377a2113dfa 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/StringWithQualityHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/StringWithQualityHeaderValue.cs @@ -54,7 +54,7 @@ namespace System.Net.Http.Headers { if (_quality.HasValue) { - return _value + "; q=" + _quality.Value.ToString("0.0##", NumberFormatInfo.InvariantInfo); + return string.Create(CultureInfo.InvariantCulture, stackalloc char[128], $"{_value}; q={_quality.Value:0.0##}"); } return _value; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/WarningHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/WarningHeaderValue.cs index c232ba8371d..caa70684ec7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/WarningHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/WarningHeaderValue.cs @@ -74,7 +74,7 @@ namespace System.Net.Http.Headers StringBuilder sb = StringBuilderCache.Acquire(); // Warning codes are always 3 digits according to RFC2616 - sb.Append(_code.ToString("000", NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{_code:000}"); sb.Append(' '); sb.Append(_agent); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs index afb7082a57a..eabc0efe68e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs @@ -80,35 +80,31 @@ namespace System.Net.Http Debug.Assert(preamble != null); Debug.Assert(codePage == encoding.CodePage, - "Encoding code page mismatch for encoding: " + encoding.EncodingName, - "Expected (constant): {0}, Actual (Encoding.CodePage): {1}", codePage, encoding.CodePage); + $"Encoding code page mismatch for encoding: {encoding.EncodingName}", + $"Expected (constant): {codePage}, Actual (Encoding.CodePage): {encoding.CodePage}"); byte[] actualPreamble = encoding.GetPreamble(); Debug.Assert(preambleLength == actualPreamble.Length, - "Encoding preamble length mismatch for encoding: " + encoding.EncodingName, - "Expected (constant): {0}, Actual (Encoding.GetPreamble().Length): {1}", preambleLength, actualPreamble.Length); + $"Encoding preamble length mismatch for encoding: {encoding.EncodingName}", + $"Expected (constant): {preambleLength}, Actual (Encoding.GetPreamble().Length): {actualPreamble.Length}"); Debug.Assert(actualPreamble.Length >= 2); int actualFirst2Bytes = actualPreamble[0] << 8 | actualPreamble[1]; Debug.Assert(first2Bytes == actualFirst2Bytes, - "Encoding preamble first 2 bytes mismatch for encoding: " + encoding.EncodingName, - "Expected (constant): {0}, Actual: {1}", first2Bytes, actualFirst2Bytes); + $"Encoding preamble first 2 bytes mismatch for encoding: {encoding.EncodingName}", + $"Expected (constant): {first2Bytes}, Actual: {actualFirst2Bytes}"); Debug.Assert(preamble.Length == actualPreamble.Length, - "Encoding preamble mismatch for encoding: " + encoding.EncodingName, - "Expected (constant): {0}, Actual (Encoding.GetPreamble()): {1}", - BitConverter.ToString(preamble), - BitConverter.ToString(actualPreamble)); + $"Encoding preamble mismatch for encoding: {encoding.EncodingName}", + $"Expected (constant): {BitConverter.ToString(preamble)}, Actual (Encoding.GetPreamble()): {BitConverter.ToString(actualPreamble)}"); for (int i = 0; i < preamble.Length; i++) { Debug.Assert(preamble[i] == actualPreamble[i], - "Encoding preamble mismatch for encoding: " + encoding.EncodingName, - "Expected (constant): {0}, Actual (Encoding.GetPreamble()): {1}", - BitConverter.ToString(preamble), - BitConverter.ToString(actualPreamble)); + $"Encoding preamble mismatch for encoding: {encoding.EncodingName}", + $"Expected (constant): {BitConverter.ToString(preamble)}, Actual (Encoding.GetPreamble()): {BitConverter.ToString(actualPreamble)}"); } } #endif diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index 74abe97e80d..e9468042f64 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -108,7 +108,7 @@ namespace System.Net.Http if (!isProxyAuth && !authUri.IsDefaultPort) { - hostName = $"{hostName}:{authUri.Port}"; + hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}"); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index ce4f3e74169..4667b7bec32 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -9,6 +9,7 @@ using System.Net.Quic; using System.IO; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Net.Http.Headers; using System.Net.Security; @@ -78,7 +79,7 @@ namespace System.Net.Http _connection = connection; bool altUsedDefaultPort = pool.Kind == HttpConnectionKind.Http && authority.Port == HttpConnectionPool.DefaultHttpPort || pool.Kind == HttpConnectionKind.Https && authority.Port == HttpConnectionPool.DefaultHttpsPort; - string altUsedValue = altUsedDefaultPort ? authority.IdnHost : authority.IdnHost + ":" + authority.Port.ToString(Globalization.CultureInfo.InvariantCulture); + string altUsedValue = altUsedDefaultPort ? authority.IdnHost : string.Create(CultureInfo.InvariantCulture, $"{authority.IdnHost}:{authority.Port}"); _altUsedEncodedHeader = QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(KnownHeaders.AltUsed.Name, altUsedValue); // Errors are observed via Abort(). diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 00a1a376799..e84f21dce3b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -349,15 +349,12 @@ namespace System.Net.Http var sb = new StringBuilder(); Debug.Assert(_originAuthority != null); - sb - .Append(IsSecure ? "https://" : "http://") - .Append(_originAuthority.IdnHost); + sb.Append(IsSecure ? "https://" : "http://") + .Append(_originAuthority.IdnHost); if (_originAuthority.Port != (IsSecure ? DefaultHttpsPort : DefaultHttpPort)) { - sb - .Append(':') - .Append(_originAuthority.Port.ToString(CultureInfo.InvariantCulture)); + sb.Append(CultureInfo.InvariantCulture, $":{_originAuthority.Port}"); } _http2AltSvcOriginUri = Encoding.ASCII.GetBytes(sb.ToString()); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerException.cs b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerException.cs index 968adb53345..249e579d47d 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerException.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerException.cs @@ -13,23 +13,23 @@ namespace System.Net { public HttpListenerException() : base(Marshal.GetLastPInvokeError()) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, NativeErrorCode.ToString() + ":" + Message); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}"); } public HttpListenerException(int errorCode) : base(errorCode) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, NativeErrorCode.ToString() + ":" + Message); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}"); } public HttpListenerException(int errorCode, string message) : base(errorCode, message) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, NativeErrorCode.ToString() + ":" + Message); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}"); } protected HttpListenerException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, NativeErrorCode.ToString() + ":" + Message); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}"); } // the base class returns the HResult with this property diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs index 61f05ac248e..f078b5bdbee 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequestUriBuilder.cs @@ -119,8 +119,7 @@ namespace System.Net private static Encoding GetEncoding(EncodingType type) { - Debug.Assert((type == EncodingType.Primary) || (type == EncodingType.Secondary), - "Unknown 'EncodingType' value: " + type.ToString()); + Debug.Assert((type == EncodingType.Primary) || (type == EncodingType.Secondary), $"Unknown 'EncodingType' value: {type}"); if (type == EncodingType.Secondary) { @@ -330,8 +329,7 @@ namespace System.Net { foreach (byte octet in octets) { - target.Append('%'); - target.Append(octet.ToString("X2", CultureInfo.InvariantCulture)); + target.Append($"%{octet:X2}"); } } @@ -350,7 +348,7 @@ namespace System.Net { octetString.Append(' '); } - octetString.Append(octet.ToString("X2", CultureInfo.InvariantCulture)); + octetString.Append($"{octet:X2}"); } return octetString.ToString(); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs index 13562495cb4..63c96497909 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs @@ -962,7 +962,7 @@ namespace System.Net if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, - $"HandleAuthentication creating new WindowsIdentity from user context: {userContext.DangerousGetHandle().ToString("x8")}"); + $"HandleAuthentication creating new WindowsIdentity from user context: {userContext.DangerousGetHandle():x8}"); } WindowsPrincipal windowsPrincipal = new WindowsPrincipal( @@ -1881,7 +1881,7 @@ namespace System.Net private static unsafe void WaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"errorCode: {errorCode}, numBytes: {numBytes}, nativeOverlapped: {((IntPtr)nativeOverlapped).ToString("x")}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"errorCode: {errorCode}, numBytes: {numBytes}, nativeOverlapped: {(IntPtr)nativeOverlapped:x}"); // take the DisconnectAsyncResult object from the state DisconnectAsyncResult asyncResult = (DisconnectAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped)!; IOCompleted(asyncResult, errorCode, numBytes, nativeOverlapped); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpRequestStream.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpRequestStream.Windows.cs index 3eadb3cf7e5..ecbadd5943c 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpRequestStream.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpRequestStream.Windows.cs @@ -306,7 +306,7 @@ namespace System.Net private static void IOCompleted(HttpRequestStreamAsyncResult asyncResult, uint errorCode, uint numBytes) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"asyncResult: {asyncResult} errorCode:0x {errorCode.ToString("x8")} numBytes: {numBytes}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"asyncResult: {asyncResult} errorCode:0x {errorCode:x8} numBytes: {numBytes}"); object? result = null; try { @@ -333,7 +333,7 @@ namespace System.Net { HttpRequestStreamAsyncResult asyncResult = (HttpRequestStreamAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped)!; - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"asyncResult: {asyncResult} errorCode:0x {errorCode.ToString("x8")} numBytes: {numBytes} nativeOverlapped:0x {((IntPtr)nativeOverlapped).ToString("x8")}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"asyncResult: {asyncResult} errorCode:0x {errorCode:x8} numBytes: {numBytes} nativeOverlapped:0x{(IntPtr)nativeOverlapped:x8}"); IOCompleted(asyncResult, errorCode, numBytes); } diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStreamAsyncResult.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStreamAsyncResult.cs index 5a41b0ddeed..d41ff445938 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStreamAsyncResult.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStreamAsyncResult.cs @@ -185,7 +185,7 @@ namespace System.Net private static void IOCompleted(HttpResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"errorCode:0x {errorCode.ToString("x8")} numBytes: {numBytes}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"errorCode:0x{errorCode:x8} numBytes: {numBytes}"); object? result = null; try { diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerAsyncResult.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerAsyncResult.Windows.cs index b36252e9de8..0b57602b1b7 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerAsyncResult.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerAsyncResult.Windows.cs @@ -106,7 +106,7 @@ namespace System.Net while (true) { Debug.Assert(_requestContext != null); - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Calling Interop.HttpApi.HttpReceiveHttpRequest RequestId: {_requestContext.RequestBlob->RequestId}Buffer:0x {((IntPtr)_requestContext.RequestBlob).ToString("x")} Size: {_requestContext.Size}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Calling Interop.HttpApi.HttpReceiveHttpRequest RequestId: {_requestContext.RequestBlob->RequestId} Buffer: 0x{(IntPtr)_requestContext.RequestBlob:x} Size: {_requestContext.Size}"); uint bytesTransferred = 0; Debug.Assert(AsyncObject != null); HttpListenerSession listenerSession = (HttpListenerSession)AsyncObject!; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/HttpWebSocket.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/HttpWebSocket.Windows.cs index ee844910e4d..9b9c501746d 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/HttpWebSocket.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/HttpWebSocket.Windows.cs @@ -137,15 +137,6 @@ namespace System.Net.WebSockets return webSocketContext; } - internal static string GetTraceMsgForParameters(int offset, int count, CancellationToken cancellationToken) - { - return string.Format(CultureInfo.InvariantCulture, - "offset: {0}, count: {1}, cancellationToken.CanBeCanceled: {2}", - offset, - count, - cancellationToken.CanBeCanceled); - } - internal static ConfiguredTaskAwaitable SuppressContextFlow(this Task task) { // We don't flow the synchronization context within WebSocket.xxxAsync - but the calling application diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs index 84d4222f8cd..9355cd36ccb 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs @@ -63,20 +63,6 @@ namespace System.Net.WebSockets HttpWebSocket.ValidateOptions(subProtocol, internalBuffer.ReceiveBufferSize, internalBuffer.SendBufferSize, keepAliveInterval); - string parameters = string.Empty; - - if (NetEventSource.Log.IsEnabled()) - { - parameters = string.Format(CultureInfo.InvariantCulture, - "ReceiveBufferSize: {0}, SendBufferSize: {1}, Protocols: {2}, KeepAliveInterval: {3}, innerStream: {4}, internalBuffer: {5}", - internalBuffer.ReceiveBufferSize, - internalBuffer.SendBufferSize, - subProtocol, - keepAliveInterval, - NetEventSource.GetHashCode(innerStream), - NetEventSource.GetHashCode(internalBuffer)); - } - _thisLock = new object(); _innerStream = innerStream; @@ -251,15 +237,6 @@ namespace System.Net.WebSockets "'messageType' MUST be either 'WebSocketMessageType.Binary' or 'WebSocketMessageType.Text'."); Debug.Assert(buffer.Array != null); - string inputParameter = string.Empty; - if (NetEventSource.Log.IsEnabled()) - { - inputParameter = string.Format(CultureInfo.InvariantCulture, - "messageType: {0}, endOfMessage: {1}", - messageType, - endOfMessage); - } - ThrowIfPendingException(); ThrowIfDisposed(); ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived); @@ -421,15 +398,6 @@ namespace System.Net.WebSockets string statusDescription, CancellationToken cancellationToken) { - string inputParameter = string.Empty; - if (NetEventSource.Log.IsEnabled()) - { - inputParameter = string.Format(CultureInfo.InvariantCulture, - "closeStatus: {0}, statusDescription: {1}", - closeStatus, - statusDescription); - } - ThrowIfPendingException(); if (IsStateTerminal(State)) { @@ -648,15 +616,6 @@ namespace System.Net.WebSockets string? statusDescription, CancellationToken cancellationToken) { - string inputParameter = string.Empty; - if (NetEventSource.Log.IsEnabled()) - { - inputParameter = string.Format(CultureInfo.InvariantCulture, - "closeStatus: {0}, statusDescription: {1}", - closeStatus, - statusDescription); - } - ThrowIfPendingException(); if (IsStateTerminal(State)) { @@ -1067,15 +1026,7 @@ namespace System.Net.WebSockets // This indicates a contract violation of the websocket protocol component, // because we currently don't support any WebSocket extensions and would // not accept a Websocket handshake requesting extensions - Debug.Fail(string.Format(CultureInfo.InvariantCulture, - "The value of 'bufferType' ({0}) is invalid. Valid buffer types: {1}, {2}, {3}, {4}, {5}.", - bufferType, - WebSocketProtocolComponent.BufferType.Close, - WebSocketProtocolComponent.BufferType.BinaryFragment, - WebSocketProtocolComponent.BufferType.BinaryMessage, - WebSocketProtocolComponent.BufferType.UTF8Fragment, - WebSocketProtocolComponent.BufferType.UTF8Message)); - + Debug.Fail($"The value of 'bufferType' ({bufferType}) is invalid."); throw new WebSocketException(WebSocketError.NativeError, SR.Format(SR.net_WebSockets_InvalidBufferType, bufferType, @@ -1337,14 +1288,7 @@ namespace System.Net.WebSockets _closeStatus = closeStatus; _closeStatusDescription = closeStatusDescription; - if (NetEventSource.Log.IsEnabled()) - { - string parameters = string.Format(CultureInfo.InvariantCulture, - "closeStatus: {0}, closeStatusDescription: {1}, _State: {2}", - closeStatus, closeStatusDescription, _state); - - NetEventSource.Info(this, parameters); - } + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"closeStatus: {closeStatus}, closeStatusDescription: {closeStatusDescription}, _State: {_state}"); } private static async void OnKeepAlive(object? sender) diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs index 838963d98d8..712b0d7f39d 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs @@ -49,14 +49,10 @@ namespace System.Net.WebSockets private WebSocketBuffer(ArraySegment internalBuffer, int receiveBufferSize, int sendBufferSize) { Debug.Assert(internalBuffer.Array != null, "'internalBuffer.Array' MUST NOT be NULL."); - Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, - "'receiveBufferSize' MUST be at least " + HttpWebSocket.MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, - "'sendBufferSize' MUST be at least " + HttpWebSocket.MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(receiveBufferSize <= HttpWebSocket.MaxBufferSize, - "'receiveBufferSize' MUST NOT exceed " + HttpWebSocket.MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize <= HttpWebSocket.MaxBufferSize, - "'sendBufferSize' MUST NOT exceed " + HttpWebSocket.MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); + Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, $"'receiveBufferSize' MUST be at least {HttpWebSocket.MinReceiveBufferSize}."); + Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, $"'sendBufferSize' MUST be at least {HttpWebSocket.MinSendBufferSize}."); + Debug.Assert(receiveBufferSize <= HttpWebSocket.MaxBufferSize, $"'receiveBufferSize' MUST NOT exceed {HttpWebSocket.MaxBufferSize}."); + Debug.Assert(sendBufferSize <= HttpWebSocket.MaxBufferSize, $"'sendBufferSize' MUST NOT exceed {HttpWebSocket.MaxBufferSize}."); _receiveBufferSize = receiveBufferSize; _sendBufferSize = sendBufferSize; @@ -634,10 +630,8 @@ namespace System.Net.WebSockets internal static ArraySegment CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer) { - Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, - "'receiveBufferSize' MUST be at least " + HttpWebSocket.MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, - "'sendBufferSize' MUST be at least " + HttpWebSocket.MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); + Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, $"'receiveBufferSize' MUST be at least {HttpWebSocket.MinReceiveBufferSize}."); + Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, $"'sendBufferSize' MUST be at least {HttpWebSocket.MinSendBufferSize}."); int internalBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer); return new ArraySegment(new byte[internalBufferSize]); @@ -645,10 +639,8 @@ namespace System.Net.WebSockets internal static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer) { - Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, - "'receiveBufferSize' MUST be at least " + HttpWebSocket.MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, - "'sendBufferSize' MUST be at least " + HttpWebSocket.MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); + Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, $"'receiveBufferSize' MUST be at least {HttpWebSocket.MinReceiveBufferSize}."); + Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, $"'sendBufferSize' MUST be at least {HttpWebSocket.MinSendBufferSize}."); int minBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer); if (count < minBufferSize) @@ -660,15 +652,11 @@ namespace System.Net.WebSockets private static int GetInternalBufferSize(int receiveBufferSize, int sendBufferSize, bool isServerBuffer) { - Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, - "'receiveBufferSize' MUST be at least " + HttpWebSocket.MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, - "'sendBufferSize' MUST be at least " + HttpWebSocket.MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); + Debug.Assert(receiveBufferSize >= HttpWebSocket.MinReceiveBufferSize, $"'receiveBufferSize' MUST be at least {HttpWebSocket.MinReceiveBufferSize}."); + Debug.Assert(sendBufferSize >= HttpWebSocket.MinSendBufferSize, $"'sendBufferSize' MUST be at least {HttpWebSocket.MinSendBufferSize}."); - Debug.Assert(receiveBufferSize <= HttpWebSocket.MaxBufferSize, - "'receiveBufferSize' MUST be less than or equal to " + HttpWebSocket.MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); - Debug.Assert(sendBufferSize <= HttpWebSocket.MaxBufferSize, - "'sendBufferSize' MUST be at less than or equal to " + HttpWebSocket.MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + "."); + Debug.Assert(receiveBufferSize <= HttpWebSocket.MaxBufferSize, $"'receiveBufferSize' MUST be less than or equal to {HttpWebSocket.MaxBufferSize}."); + Debug.Assert(sendBufferSize <= HttpWebSocket.MaxBufferSize, $"'sendBufferSize' MUST be at less than or equal to {HttpWebSocket.MaxBufferSize}."); int nativeSendBufferSize = GetNativeSendBufferSize(sendBufferSize, isServerBuffer); return 2 * receiveBufferSize + nativeSendBufferSize + NativeOverheadBufferSize + s_PropertyBufferSize; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/DomainLiteralReader.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/DomainLiteralReader.cs index 29a31a1fc4c..2f03cf45b45 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/DomainLiteralReader.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/DomainLiteralReader.cs @@ -30,7 +30,7 @@ namespace System.Net.Mail // internal static bool TryReadReverse(string data, int index, out int outIndex, bool throwExceptionIfFail) { - Debug.Assert(0 <= index && index < data.Length, "index was outside the bounds of the string: " + index); + Debug.Assert(0 <= index && index < data.Length, $"index was outside the bounds of the string: {index}"); Debug.Assert(data[index] == MailBnfHelper.EndSquareBracket, "data did not end with a square bracket"); // Skip the end bracket diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs index f3a1da7361b..f50b9946f4f 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs @@ -36,7 +36,7 @@ namespace System.Net.Mail // internal static bool TryReadReverse(string data, int index, out int outIndex, bool throwExceptionIfFail) { - Debug.Assert(0 <= index && index < data.Length, "index was outside the bounds of the string: " + index); + Debug.Assert(0 <= index && index < data.Length, $"index was outside the bounds of the string: {index}"); int startIndex = index; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddressParser.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddressParser.cs index 38867b921b9..fe57dd93c14 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddressParser.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddressParser.cs @@ -27,7 +27,7 @@ namespace System.Net.Mail { int index = data.Length - 1; bool parseSuccess = TryParseAddress(data, false, ref index, out parsedAddress, throwExceptionIfFail); - Debug.Assert(!parseSuccess || index == -1, "The index indicates that part of the address was not parsed: " + index); + Debug.Assert(!parseSuccess || index == -1, $"The index indicates that part of the address was not parsed: {index}"); return parseSuccess; } @@ -66,7 +66,7 @@ namespace System.Net.Mail private static bool TryParseAddress(string data, bool expectMultipleAddresses, ref int index, out ParseAddressInfo parseAddressInfo, bool throwExceptionIfFail) { Debug.Assert(!string.IsNullOrEmpty(data)); - Debug.Assert(index >= 0 && index < data.Length, "Index out of range: " + index + ", " + data.Length); + Debug.Assert(index >= 0 && index < data.Length, $"Index out of range: {index}, {data.Length}"); // Parsed components to be assembled as a MailAddress later string? displayName; @@ -378,7 +378,7 @@ namespace System.Net.Mail return false; } - Debug.Assert(data[index + 1] == MailBnfHelper.Quote, "Mis-aligned index: " + index); + Debug.Assert(data[index + 1] == MailBnfHelper.Quote, $"Mis-aligned index: {index}"); // Do not include the bounding quotes on the display name int leftIndex = index + 2; @@ -417,7 +417,7 @@ namespace System.Net.Mail return false; } - Debug.Assert(index < 0 || data[index] == MailBnfHelper.Comma, "Mis-aligned index: " + index); + Debug.Assert(index < 0 || data[index] == MailBnfHelper.Comma, $"Mis-aligned index: {index}"); // Do not include the Comma (if any), and because there were no bounding quotes, // trim extra whitespace. diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedPairReader.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedPairReader.cs index e98a031e77c..34079edf51c 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedPairReader.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedPairReader.cs @@ -33,7 +33,7 @@ namespace System.Net.Mail // Throws a FormatException or false is returned if the an escaped Unicode character is found but was not permitted. internal static bool TryCountQuotedChars(string data, int index, bool permitUnicodeEscaping, out int outIndex, bool throwExceptionIfFail) { - Debug.Assert(0 <= index && index < data.Length, "Index out of range: " + index + ", " + data.Length); + Debug.Assert(0 <= index && index < data.Length, $"Index out of range: {index}, {data.Length}"); if (index <= 0 || data[index - 1] != MailBnfHelper.Backslash) { @@ -81,7 +81,7 @@ namespace System.Net.Mail // Return value: The number of consecutive backslashes, including the initial one at data[index]. private static int CountBackslashes(string data, int index) { - Debug.Assert(index >= 0 && data[index] == MailBnfHelper.Backslash, "index was not a backslash: " + index); + Debug.Assert(index >= 0 && data[index] == MailBnfHelper.Backslash, $"index was not a backslash: {index}"); // Find all the backslashes. It's possible that there are multiple escaped/quoted backslashes. int backslashCount = 0; @@ -92,7 +92,7 @@ namespace System.Net.Mail } while (index >= 0 && data[index] == MailBnfHelper.Backslash); // At this point data[index] should not be a backslash - Debug.Assert(index < 0 || data[index] != MailBnfHelper.Backslash, "index was a backslash: " + index); + Debug.Assert(index < 0 || data[index] != MailBnfHelper.Backslash, $"index was a backslash: {index}"); return backslashCount; } diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedStringFormatReader.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedStringFormatReader.cs index c62b7f83500..e12d7316400 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedStringFormatReader.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/QuotedStringFormatReader.cs @@ -34,9 +34,9 @@ namespace System.Net.Mail // internal static bool TryReadReverseQuoted(string data, int index, bool permitUnicode, out int outIndex, bool throwExceptionIfFail) { - Debug.Assert(0 <= index && index < data.Length, "Index out of range: " + index + ", " + data.Length); + Debug.Assert(0 <= index && index < data.Length, $"Index out of range: {index}, {data.Length}"); // Check for the first bounding quote - Debug.Assert(data[index] == MailBnfHelper.Quote, "Initial char at index " + index + " was not a quote."); + Debug.Assert(data[index] == MailBnfHelper.Quote, $"Initial char at index {index} was not a quote."); // Skip the bounding quote index--; @@ -125,7 +125,7 @@ namespace System.Net.Mail // internal static bool TryReadReverseUnQuoted(string data, int index, bool permitUnicode, bool expectCommaDelimiter, out int outIndex, bool throwExceptionIfFail) { - Debug.Assert(0 <= index && index < data.Length, "Index out of range: " + index + ", " + data.Length); + Debug.Assert(0 <= index && index < data.Length, $"Index out of range: {index}, {data.Length}"); do { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs index cceff5d15fa..415c1e6e972 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs @@ -393,7 +393,7 @@ namespace System.Net.Mail string pathAndFilename; while (true) { - filename = Guid.NewGuid().ToString() + ".eml"; + filename = $"{Guid.NewGuid()}.eml"; pathAndFilename = Path.Combine(pickupDirectory, filename); if (!File.Exists(pathAndFilename)) break; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs index bb300289dfa..4365cee08cb 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs @@ -36,7 +36,7 @@ namespace System.Net.Mime private void SetType(MimeMultiPartType type) { - ContentType.MediaType = "multipart" + "/" + type.ToString().ToLowerInvariant(); + ContentType.MediaType = "multipart/" + type.ToString().ToLowerInvariant(); ContentType.Boundary = GetNextBoundary(); } @@ -249,10 +249,7 @@ namespace System.Net.Mime internal string GetNextBoundary() { int b = Interlocked.Increment(ref s_boundary) - 1; - string boundaryString = "--boundary_" + b.ToString(CultureInfo.InvariantCulture) + "_" + Guid.NewGuid().ToString(null, CultureInfo.InvariantCulture); - - - return boundaryString; + return $"--boundary_{(uint)b}_{Guid.NewGuid()}"; } } } diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 74c62684a82..f18ee68a35a 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -39,7 +39,7 @@ namespace System.Net case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_MEMORY: throw new OutOfMemoryException(); default: - Debug.Fail("Unexpected error: " + error.ToString()); + Debug.Fail($"Unexpected error: {error}"); return SocketError.SocketError; } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Addresses.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Addresses.cs index 59463667713..c898a8539e4 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Addresses.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Addresses.cs @@ -99,8 +99,8 @@ namespace System.Net.NetworkInformation int interfaceIndex = fileContents.IndexOf("interface", firstBrace, blockLength, StringComparison.Ordinal); int afterName = fileContents.IndexOf(';', interfaceIndex); int beforeName = fileContents.LastIndexOf(' ', afterName); - string interfaceName = fileContents.Substring(beforeName + 2, afterName - beforeName - 3); - if (interfaceName != name) + ReadOnlySpan interfaceName = fileContents.AsSpan(beforeName + 2, afterName - beforeName - 3); + if (!interfaceName.SequenceEqual(name)) { continue; } @@ -108,9 +108,8 @@ namespace System.Net.NetworkInformation int indexOfDhcp = fileContents.IndexOf("dhcp-server-identifier", firstBrace, blockLength, StringComparison.Ordinal); int afterAddress = fileContents.IndexOf(';', indexOfDhcp); int beforeAddress = fileContents.LastIndexOf(' ', afterAddress); - string dhcpAddressString = fileContents.Substring(beforeAddress + 1, afterAddress - beforeAddress - 1); - IPAddress? dhcpAddress; - if (IPAddress.TryParse(dhcpAddressString, out dhcpAddress)) + ReadOnlySpan dhcpAddressSpan = fileContents.AsSpan(beforeAddress + 1, afterAddress - beforeAddress - 1); + if (IPAddress.TryParse(dhcpAddressSpan, out IPAddress? dhcpAddress)) { collection.Add(dhcpAddress); } @@ -143,8 +142,8 @@ namespace System.Net.NetworkInformation } } int endOfLine = fileContents.IndexOf(Environment.NewLine, labelIndex, StringComparison.Ordinal); - string addressString = fileContents.Substring(labelIndex + label.Length, endOfLine - (labelIndex + label.Length)); - IPAddress address = IPAddress.Parse(addressString); + ReadOnlySpan addressSpan = fileContents.AsSpan(labelIndex + label.Length, endOfLine - (labelIndex + label.Length)); + IPAddress address = IPAddress.Parse(addressSpan); collection.Add(address); } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs index d53d00eb588..ca042d47b74 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs @@ -249,12 +249,11 @@ namespace System.Net.NetworkInformation throw ExceptionHelper.CreateForParseFailure(); } - string remoteAddressString = localAddressAndPort.Substring(0, indexOfColon); - IPAddress localIPAddress = ParseHexIPAddress(remoteAddressString); + IPAddress localIPAddress = ParseHexIPAddress(localAddressAndPort.AsSpan(0, indexOfColon)); - string portString = localAddressAndPort.Substring(indexOfColon + 1, localAddressAndPort.Length - (indexOfColon + 1)); + ReadOnlySpan portSpan = localAddressAndPort.AsSpan(indexOfColon + 1, localAddressAndPort.Length - (indexOfColon + 1)); int localPort; - if (!int.TryParse(portString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out localPort)) + if (!int.TryParse(portSpan, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out localPort)) { throw ExceptionHelper.CreateForParseFailure(); } @@ -270,12 +269,11 @@ namespace System.Net.NetworkInformation throw ExceptionHelper.CreateForParseFailure(); } - string remoteAddressString = colonSeparatedAddress.Substring(0, indexOfColon); - IPAddress ipAddress = ParseHexIPAddress(remoteAddressString); + IPAddress ipAddress = ParseHexIPAddress(colonSeparatedAddress.AsSpan(0, indexOfColon)); - string portString = colonSeparatedAddress.Substring(indexOfColon + 1, colonSeparatedAddress.Length - (indexOfColon + 1)); + ReadOnlySpan portSpan = colonSeparatedAddress.AsSpan(indexOfColon + 1, colonSeparatedAddress.Length - (indexOfColon + 1)); int port; - if (!int.TryParse(portString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out port)) + if (!int.TryParse(portSpan, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out port)) { throw ExceptionHelper.CreateForParseFailure(); } @@ -289,7 +287,7 @@ namespace System.Net.NetworkInformation return Interop.Sys.MapTcpState((int)state); } - internal static IPAddress ParseHexIPAddress(string remoteAddressString) + internal static IPAddress ParseHexIPAddress(ReadOnlySpan remoteAddressString) { if (remoteAddressString.Length <= 8) // IPv4 Address { @@ -307,7 +305,7 @@ namespace System.Net.NetworkInformation // Simply converts the hex string into a long and uses the IPAddress(long) constructor. // Strings passed to this method must be 8 or less characters in length (32-bit address). - private static IPAddress ParseIPv4HexString(string hexAddress) + private static IPAddress ParseIPv4HexString(ReadOnlySpan hexAddress) { IPAddress ipAddress; long addressValue; @@ -328,10 +326,10 @@ namespace System.Net.NetworkInformation // It's represenation in /proc/net/tcp6: 00-00-80-FE 00-00-00-00 FF-5D-15-02 02-04-00-FE // (dashes and spaces added above for readability) // Strings passed to this must be 32 characters in length. - private static IPAddress ParseIPv6HexString(string hexAddress, bool isNetworkOrder = false) + private static IPAddress ParseIPv6HexString(ReadOnlySpan hexAddress, bool isNetworkOrder = false) { Debug.Assert(hexAddress.Length == 32); - byte[] addressBytes = new byte[16]; + Span addressBytes = stackalloc byte[16]; if (isNetworkOrder || !BitConverter.IsLittleEndian) { for (int i = 0; i < 16; i++) diff --git a/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs b/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs index 3893ae10651..8ed63ef9f97 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs @@ -742,7 +742,7 @@ namespace System.Net { sb.Append(SpecialAttributeLiteral + CookieFields.VersionAttributeName + EqualsLiteral); // const strings if (IsQuotedVersion) sb.Append('"'); - sb.Append(m_version.ToString(NumberFormatInfo.InvariantInfo)); + sb.Append(NumberFormatInfo.InvariantInfo, $"{m_version}"); if (IsQuotedVersion) sb.Append('"'); sb.Append(SeparatorLiteral); } diff --git a/src/libraries/System.Net.Primitives/src/System/Net/CredentialCache.cs b/src/libraries/System.Net.Primitives/src/System/Net/CredentialCache.cs index 212e3b6e094..bb7801cbebb 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/CredentialCache.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/CredentialCache.cs @@ -452,7 +452,7 @@ namespace System.Net obj is CredentialHostKey && Equals((CredentialHostKey)obj); public override string ToString() => - Host + ":" + Port.ToString(NumberFormatInfo.InvariantInfo) + ":" + AuthenticationType; + string.Create(CultureInfo.InvariantCulture, $"{Host}:{Port}:{AuthenticationType}"); } internal sealed class CredentialKey : IEquatable @@ -543,6 +543,6 @@ namespace System.Net public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as CredentialKey); public override string ToString() => - "[" + UriPrefixLength.ToString(NumberFormatInfo.InvariantInfo) + "]:" + UriPrefix + ":" + AuthenticationType; + string.Create(CultureInfo.InvariantCulture, $"[{UriPrefixLength}]:{UriPrefix}:{AuthenticationType}"); } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 2ebe218c407..10dd32c0316 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -737,7 +737,7 @@ namespace System.Net.Security { if (token.Failed) { - NetEventSource.Error(this, $"Authentication failed. Status: {status.ToString()}, Exception message: {token.GetException()!.Message}"); + NetEventSource.Error(this, $"Authentication failed. Status: {status}, Exception message: {token.GetException()!.Message}"); } } return token; diff --git a/src/libraries/System.Net.Security/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicy.cs b/src/libraries/System.Net.Security/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicy.cs index fe69152dc10..6a59d81957a 100644 --- a/src/libraries/System.Net.Security/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicy.cs +++ b/src/libraries/System.Net.Security/src/System/Security/Authentication/ExtendedProtection/ExtendedProtectionPolicy.cs @@ -105,9 +105,9 @@ namespace System.Security.Authentication.ExtendedProtection { StringBuilder sb = new StringBuilder(); sb.Append("ProtectionScenario="); - sb.Append(_protectionScenario.ToString()); + sb.Append($"{_protectionScenario}"); sb.Append("; PolicyEnforcement="); - sb.Append(_policyEnforcement.ToString()); + sb.Append($"{_policyEnforcement}"); sb.Append("; CustomChannelBinding="); if (_customChannelBinding == null) diff --git a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs index 0c32b6d906d..27289121acd 100644 --- a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs +++ b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs @@ -501,7 +501,7 @@ namespace System.Net { if (needsHeaderAndBoundary) { - string boundary = "---------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo); + string boundary = $"---------------------{DateTime.Now.Ticks:x}"; headers[HttpKnownHeaderNames.ContentType] = UploadFileContentType + "; boundary=" + boundary; diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs index 368cf805542..9959c66918e 100644 --- a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs @@ -35,7 +35,7 @@ namespace System.Net } public WebProxy(string Host, int Port) - : this(new Uri("http://" + Host + ":" + Port.ToString(CultureInfo.InvariantCulture)), false, null, null) + : this(new Uri(string.Create(CultureInfo.InvariantCulture, $"http://{Host}:{Port}")), false, null, null) { } @@ -142,9 +142,10 @@ namespace System.Net if (_regexBypassList != null) { + Span stackBuffer = stackalloc char[128]; string matchUriString = input.IsDefaultPort ? - $"{input.Scheme}://{input.Host}" : - $"{input.Scheme}://{input.Host}:{(uint)input.Port}"; + string.Create(null, stackBuffer, $"{input.Scheme}://{input.Host}") : + string.Create(null, stackBuffer, $"{input.Scheme}://{input.Host}:{(uint)input.Port}"); foreach (Regex r in _regexBypassList) { diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs index 0af0cd5fb14..f19f9a57277 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs @@ -338,8 +338,7 @@ namespace System.Net.WebSockets if (options.ClientMaxWindowBits != WebSocketValidate.MaxDeflateWindowBits) { - builder.Append(ClientWebSocketDeflateConstants.ClientMaxWindowBits).Append('=') - .Append(options.ClientMaxWindowBits.ToString(CultureInfo.InvariantCulture)); + builder.Append(CultureInfo.InvariantCulture, $"{ClientWebSocketDeflateConstants.ClientMaxWindowBits}={options.ClientMaxWindowBits}"); } else { @@ -354,9 +353,7 @@ namespace System.Net.WebSockets if (options.ServerMaxWindowBits != WebSocketValidate.MaxDeflateWindowBits) { - builder.Append("; ") - .Append(ClientWebSocketDeflateConstants.ServerMaxWindowBits).Append('=') - .Append(options.ServerMaxWindowBits.ToString(CultureInfo.InvariantCulture)); + builder.Append(CultureInfo.InvariantCulture, $"; {ClientWebSocketDeflateConstants.ServerMaxWindowBits}={options.ServerMaxWindowBits}"); } if (!options.ServerContextTakeover) diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Win32/RegistryKey.cs b/src/libraries/System.Private.CoreLib/src/Internal/Win32/RegistryKey.cs index f73025d545b..68eab6c5bba 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/Win32/RegistryKey.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/Win32/RegistryKey.cs @@ -67,7 +67,7 @@ namespace Internal.Win32 } // We really should throw an exception here if errorCode was bad, // but we can't for compatibility reasons. - Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode); + Debug.Assert(errorCode == 0, $"RegDeleteValue failed. Here's your error code: {errorCode}"); } internal static RegistryKey OpenBaseKey(IntPtr hKey) diff --git a/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs b/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs index 30542d17ece..11f807986d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs @@ -42,13 +42,9 @@ namespace System sb.Append(Name); if (Culture != null) { - sb.Append(", culture=\""); - sb.Append(Culture); - sb.Append('"'); + sb.Append($", culture=\"{Culture}\""); } - sb.Append(", version=\""); - sb.Append(Version.ToString()); - sb.Append('"'); + sb.Append($", version=\"{Version}\""); if (_publicKeyToken != null) { sb.Append(", publicKeyToken=\""); @@ -57,9 +53,7 @@ namespace System } if (ProcessorArchitecture != null) { - sb.Append(", processorArchitecture =\""); - sb.Append(ProcessorArchitecture); - sb.Append('"'); + sb.Append($", processorArchitecture =\"{ProcessorArchitecture}\""); } return sb.ToString(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs index 83f4fda598e..61325d2058f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs @@ -11,36 +11,12 @@ namespace System.Collections.Generic public static class KeyValuePair { // Creates a new KeyValuePair from the given values. - public static KeyValuePair Create(TKey key, TValue value) - { - return new KeyValuePair(key, value); - } + public static KeyValuePair Create(TKey key, TValue value) => + new KeyValuePair(key, value); - /// - /// Used by KeyValuePair.ToString to reduce generic code - /// - internal static string PairToString(object? key, object? value) - { - var s = new ValueStringBuilder(stackalloc char[64]); - - s.Append('['); - - if (key != null) - { - s.Append(key.ToString()); - } - - s.Append(", "); - - if (value != null) - { - s.Append(value.ToString()); - } - - s.Append(']'); - - return s.ToString(); - } + /// Used by KeyValuePair.ToString to reduce generic code + internal static string PairToString(object? key, object? value) => + string.Create(null, stackalloc char[256], $"[{key}, {value}]"); } // A KeyValuePair holds a key and a value from a dictionary. diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs index a1f724612d4..8aebbc4f7aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs @@ -321,7 +321,7 @@ namespace System.Diagnostics.Tracing { if (activityInfo == null) return ""; - return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId.ToString(); + return $"{Path(activityInfo.m_creator)}/{activityInfo.m_uniqueId}"; } public override string ToString() diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs index 8a53d569082..0c4460facc3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs @@ -71,7 +71,7 @@ namespace System.Diagnostics.Tracing int count = Volatile.Read(ref _count); return count == 0 ? $"EventCounter '{Name}' Count 0" : - $"EventCounter '{Name}' Count {count} Mean {(_sum / count).ToString("n3")}"; + $"EventCounter '{Name}' Count {count} Mean {_sum / count:n3}"; } #region Statistics Calculation diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index d6093b4fb66..4800a2cde47 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -3790,7 +3790,9 @@ namespace System.Diagnostics.Tracing try { if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte + { m_outOfBandMessageCount++; + } else { if (m_outOfBandMessageCount == 16) @@ -3800,7 +3802,7 @@ namespace System.Diagnostics.Tracing } // send message to debugger - System.Diagnostics.Debugger.Log(0, null, string.Format("EventSource Error: {0}{1}", msg, System.Environment.NewLine)); + Debugger.Log(0, null, $"EventSource Error: {msg}{System.Environment.NewLine}"); // Send it to all listeners. WriteEventString(msg); @@ -5220,14 +5222,12 @@ namespace System.Diagnostics.Tracing sb.AppendLine(""); sb.AppendLine(" "); sb.AppendLine(" "); - sb.Append(""); + sb.AppendLine($" symbol=\"{symbolsName}\">"); } public void AddOpcode(string name, int value) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs index fee9867d2c9..ed9697d2450 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs @@ -42,7 +42,7 @@ namespace System.Diagnostics.Tracing Publish(); } - public override string ToString() => $"PollingCounter '{Name}' Count 1 Mean {_lastVal.ToString("n3")}"; + public override string ToString() => $"PollingCounter '{Name}' Count 1 Mean {_lastVal:n3}"; private readonly Func _metricProvider; private double _lastVal; diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs index a499d898437..8e8e8c5e689 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs @@ -56,7 +56,7 @@ namespace System fixed (char* lParam = "Environment") { IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, (IntPtr)lParam, 0, 1000, out IntPtr _); - Debug.Assert(r != IntPtr.Zero, "SetEnvironmentVariable failed: " + Marshal.GetLastPInvokeError()); + Debug.Assert(r != IntPtr.Zero, $"SetEnvironmentVariable failed: {Marshal.GetLastPInvokeError()}"); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index e9e3618bfc6..593a9ab1e4f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -680,7 +680,7 @@ namespace System } else { - result.Append(year.ToString("D" + tokenLen.ToString(), CultureInfo.InvariantCulture)); + result.Append(year.ToString("D" + tokenLen.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture)); } } bTimeOnly = false; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index ce6e23b8c85..b4796fd92e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -5213,7 +5213,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (str[i] <= '\x007f') buffer.Append(str[i]); else - buffer.Append("\\u").Append(((int)str[i]).ToString("x4", CultureInfo.InvariantCulture)); + buffer.Append($"\\u{(int)str[i]:x4}"); } buffer.Append('"'); return buffer.ToString(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs index 4a6aef540a5..2f9d5f729e4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs @@ -660,7 +660,7 @@ namespace System.Globalization CheckHebrewMonthValue(year, month, era); Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6, - "hebrewYearType should be from 1 to 6, but now hebrewYearType = " + hebrewYearType + " for hebrew year " + year); + $"hebrewYearType should be from 1 to 6, but now hebrewYearType = {hebrewYearType} for hebrew year {year}"); int monthDays = LunarMonthLen[hebrewYearType * MaxMonthPlusOne + month]; if (monthDays == 0) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs index 3834bbeeffe..14da1aa038b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs @@ -389,7 +389,7 @@ namespace System.IO private void ClearReadBufferBeforeWrite() { Debug.Assert(_stream != null); - Debug.Assert(_readPos <= _readLen, "_readPos <= _readLen [" + _readPos + " <= " + _readLen + "]"); + Debug.Assert(_readPos <= _readLen, $"_readPos <= _readLen [{_readPos} <= {_readLen}]"); // No read data in the buffer: if (_readPos == _readLen) @@ -1241,7 +1241,7 @@ namespace System.IO _readPos = _readLen = 0; } - Debug.Assert(newPos == Position, "newPos (=" + newPos + ") == Position (=" + Position + ")"); + Debug.Assert(newPos == Position, $"newPos (={newPos}) == Position (={Position})"); return newPos; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 11d21121bee..9aaa16ff519 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -985,7 +985,7 @@ namespace System.IO.Strategies _readPos = _readLen = 0; } - Debug.Assert(newPos == Position, "newPos (=" + newPos + ") == Position (=" + Position + ")"); + Debug.Assert(newPos == Position, $"newPos (={newPos}) == Position (={Position})"); return newPos; } @@ -1023,7 +1023,7 @@ namespace System.IO.Strategies /// private void ClearReadBufferBeforeWrite() { - Debug.Assert(_readPos <= _readLen, "_readPos <= _readLen [" + _readPos + " <= " + _readLen + "]"); + Debug.Assert(_readPos <= _readLen, $"_readPos <= _readLen [{_readPos} <= {_readLen}]"); // No read data in the buffer: if (_readPos == _readLen) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 9a6eb161d77..f7acdcd21d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -1933,14 +1933,14 @@ namespace System /// The number of constant characters outside of interpolation expressions in the interpolated string. /// The number of interpolation expressions in the interpolated string. /// The destination buffer. - /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. + /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. - public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, out bool success) + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, out bool shouldAppend) { _destination = destination; _provider = null; _pos = 0; - _success = success = destination.Length >= literalLength; + _success = shouldAppend = destination.Length >= literalLength; _hasCustomFormatter = false; } @@ -1949,14 +1949,14 @@ namespace System /// The number of interpolation expressions in the interpolated string. /// The destination buffer. /// An object that supplies culture-specific formatting information. - /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. + /// Upon return, true if the destination may be long enough to support the formatting, or false if it won't be. /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. - public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, IFormatProvider? provider, out bool success) + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span destination, IFormatProvider? provider, out bool shouldAppend) { _destination = destination; _provider = provider; _pos = 0; - _success = success = destination.Length >= literalLength; + _success = shouldAppend = destination.Length >= literalLength; _hasCustomFormatter = provider is not null && DefaultInterpolatedStringHandler.HasCustomFormatter(provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs index aaab1903dc4..74f9b93fa67 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs @@ -321,7 +321,6 @@ namespace System.Numerics /// Returns the string representation of this plane object. /// A string that represents this object. /// The string representation of a object use the formatting conventions of the current culture to format the numeric values in the returned string. For example, a object whose string representation is formatted by using the conventions of the en-US culture might appear as {Normal:<1.1, 2.2, 3.3> D:4.4}. - public override readonly string ToString() => - $"{{Normal:{Normal.ToString()} D:{D.ToString()}}}"; + public override readonly string ToString() => $"{{Normal:{Normal} D:{D}}}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs index 0b483517b97..a0e17bfb349 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -75,9 +75,10 @@ namespace System os = " "; break; } + Span stackBuffer = stackalloc char[128]; _versionString = string.IsNullOrEmpty(_servicePack) ? - os + _version.ToString() : - os + _version.ToString(3) + " " + _servicePack; + string.Create(null, stackBuffer, $"{os}{_version}") : + string.Create(null, stackBuffer, $"{os}{_version.ToString(3)} {_servicePack}"); } return _versionString; diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs index 78ead82418c..16e2b161fc0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs @@ -180,7 +180,7 @@ namespace System.Resources private unsafe int GetNameHash(int index) { - Debug.Assert(index >= 0 && index < _numResources, "Bad index into hash array. index: " + index); + Debug.Assert(index >= 0 && index < _numResources, $"Bad index into hash array. index: {index}"); if (_ums == null) { @@ -196,7 +196,7 @@ namespace System.Resources private unsafe int GetNamePosition(int index) { - Debug.Assert(index >= 0 && index < _numResources, "Bad index into name position array. index: " + index); + Debug.Assert(index >= 0 && index < _numResources, $"Bad index into name position array. index: {index}"); int r; if (_ums == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs index 5713153e7b0..5fddcf97372 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs @@ -48,8 +48,7 @@ namespace System.Runtime.InteropServices { StringBuilder s = new StringBuilder(); - string className = GetType().ToString(); - s.Append(className).Append(" (0x").Append(HResult.ToString("X8", CultureInfo.InvariantCulture)).Append(')'); + s.Append($"{GetType()} (0x{HResult:X8})"); string message = Message; if (!string.IsNullOrEmpty(message)) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs index 33c8855e751..13b85e99139 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs @@ -56,9 +56,8 @@ namespace System.Runtime.InteropServices public override string ToString() { string message = Message; - string className = GetType().ToString(); - string s = className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")"; + string s = $"{GetType()} (0x{HResult:X8})"; if (!string.IsNullOrEmpty(message)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StandardOleMarshalObject.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StandardOleMarshalObject.Windows.cs index 175c1fc58d0..f5e59ee8c32 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StandardOleMarshalObject.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StandardOleMarshalObject.Windows.cs @@ -31,7 +31,7 @@ namespace System.Runtime.InteropServices int hr = Interop.Ole32.CoGetStandardMarshal(ref riid, pUnknown, dwDestContext, IntPtr.Zero, mshlflags, out pStandardMarshal); if (hr == HResults.S_OK) { - Debug.Assert(pStandardMarshal != IntPtr.Zero, "Failed to get marshaler for interface '" + riid.ToString() + "', CoGetStandardMarshal returned S_OK"); + Debug.Assert(pStandardMarshal != IntPtr.Zero, $"Failed to get marshaler for interface '{riid}', CoGetStandardMarshal returned S_OK"); return pStandardMarshal; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SR.cs b/src/libraries/System.Private.CoreLib/src/System/SR.cs index 097aeffb0d8..6f54dc2000e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SR.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SR.cs @@ -88,7 +88,7 @@ namespace System string? s = ResourceManager.GetString(key, null); _currentlyLoading.RemoveAt(_currentlyLoading.Count - 1); // Pop - Debug.Assert(s != null, "Managed resource string lookup failed. Was your resource name misspelled? Did you rebuild mscorlib after adding a resource to resources.txt? Debug this w/ cordbg and bug whoever owns the code that called SR.GetResourceString. Resource name was: \"" + key + "\""); + Debug.Assert(s != null, $"Managed resource string lookup failed. Was your resource name misspelled? Did you rebuild mscorlib after adding a resource to resources.txt? Debug this w/ cordbg and bug whoever owns the code that called SR.GetResourceString. Resource name was: \"{key}\""); return s ?? key; } catch diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs index 94b90901146..e3eb90b5393 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs @@ -51,9 +51,7 @@ namespace System.Text const int MaxLength = 20; for (int i = 0; i < bytesUnknown.Length && i < MaxLength; i++) { - strBytes.Append('['); - strBytes.Append(bytesUnknown[i].ToString("X2", CultureInfo.InvariantCulture)); - strBytes.Append(']'); + strBytes.Append($"[{bytesUnknown[i]:X2}]"); } // In case the string's really long diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.cs index 3a83763ea6a..8fb95150018 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.cs @@ -39,7 +39,7 @@ namespace System.Text // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. // Shouldn't be able to get here for all of our code pages, table would have to be messed up. - Debug.Assert(_iCount < 1, "[EncoderLatin1BestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, $"[EncoderLatin1BestFitFallbackBuffer.Fallback(non surrogate)] Fallback char {(int)_cBestFit:X4} caused recursive fallback"); _iCount = _iSize = 1; _cBestFit = TryBestFit(charUnknown); @@ -65,7 +65,7 @@ namespace System.Text // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. 0 is processing last character, < 0 is not falling back // Shouldn't be able to get here, table would have to be messed up. - Debug.Assert(_iCount < 1, "[EncoderLatin1BestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, $"[EncoderLatin1BestFitFallbackBuffer.Fallback(surrogate)] Fallback char {(int)_cBestFit:X4} caused recursive fallback"); // Go ahead and get our fallback, surrogates don't have best fit _cBestFit = '?'; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs index 7678b5e4802..3ef7028bacb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs @@ -140,7 +140,7 @@ namespace System.Text public static explicit operator Rune(int value) => new Rune(value); // Displayed as "'' (U+XXXX)"; e.g., "'e' (U+0065)" - private string DebuggerDisplay => FormattableString.Invariant($"U+{_value:X4} '{(IsValid(_value) ? ToString() : "\uFFFD")}'"); + private string DebuggerDisplay => string.Create(CultureInfo.InvariantCulture, $"U+{_value:X4} '{(IsValid(_value) ? ToString() : "\uFFFD")}'"); /// /// Returns true if and only if this scalar value is ASCII ([ U+0000..U+007F ]) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs index 4caacbf856c..ecd50f3f7c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Globalization; namespace System.Text { @@ -10,55 +11,39 @@ namespace System.Text [Conditional("DEBUG")] internal static void AssertIsBmpCodePoint(uint codePoint) { - if (!UnicodeUtility.IsBmpCodePoint(codePoint)) - { - Debug.Fail($"The value {ToHexString(codePoint)} is not a valid BMP code point."); - } + Debug.Assert(UnicodeUtility.IsBmpCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid BMP code point."); } [Conditional("DEBUG")] internal static void AssertIsHighSurrogateCodePoint(uint codePoint) { - if (!UnicodeUtility.IsHighSurrogateCodePoint(codePoint)) - { - Debug.Fail($"The value {ToHexString(codePoint)} is not a valid UTF-16 high surrogate code point."); - } + Debug.Assert(UnicodeUtility.IsHighSurrogateCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid UTF-16 high surrogate code point."); } [Conditional("DEBUG")] internal static void AssertIsLowSurrogateCodePoint(uint codePoint) { - if (!UnicodeUtility.IsLowSurrogateCodePoint(codePoint)) - { - Debug.Fail($"The value {ToHexString(codePoint)} is not a valid UTF-16 low surrogate code point."); - } + Debug.Assert(UnicodeUtility.IsLowSurrogateCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid UTF-16 low surrogate code point."); } [Conditional("DEBUG")] internal static void AssertIsValidCodePoint(uint codePoint) { - if (!UnicodeUtility.IsValidCodePoint(codePoint)) - { - Debug.Fail($"The value {ToHexString(codePoint)} is not a valid Unicode code point."); - } + Debug.Assert(UnicodeUtility.IsValidCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid Unicode code point."); } [Conditional("DEBUG")] internal static void AssertIsValidScalar(uint scalarValue) { - if (!UnicodeUtility.IsValidUnicodeScalar(scalarValue)) - { - Debug.Fail($"The value {ToHexString(scalarValue)} is not a valid Unicode scalar value."); - } + Debug.Assert(UnicodeUtility.IsValidUnicodeScalar(scalarValue), $"The value {ToHexString(scalarValue)} is not a valid Unicode scalar value."); } [Conditional("DEBUG")] internal static void AssertIsValidSupplementaryPlaneScalar(uint scalarValue) { - if (!UnicodeUtility.IsValidUnicodeScalar(scalarValue) || UnicodeUtility.IsBmpCodePoint(scalarValue)) - { - Debug.Fail($"The value {ToHexString(scalarValue)} is not a valid supplementary plane Unicode scalar value."); - } + Debug.Assert( + UnicodeUtility.IsValidUnicodeScalar(scalarValue) && !UnicodeUtility.IsBmpCodePoint(scalarValue), + $"The value {ToHexString(scalarValue)} is not a valid supplementary plane Unicode scalar value."); } /// @@ -67,9 +52,6 @@ namespace System.Text /// /// The input value doesn't have to be a real code point in the Unicode codespace. It can be any integer. /// - private static string ToHexString(uint codePoint) - { - return FormattableString.Invariant($"U+{codePoint:X4}"); - } + private static string ToHexString(uint codePoint) => $"U+{codePoint:X4}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeEncoding.cs b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeEncoding.cs index 7e859d19e7a..50faeb94ea2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeEncoding.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeEncoding.cs @@ -1740,9 +1740,7 @@ namespace System.Text if (decoder != null) { Debug.Assert(!decoder.MustFlush || ((lastChar == (char)0) && (lastByte == -1)), - "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing" - // + " " + ((int)lastChar).ToString("X4") + " " + lastByte.ToString("X2") - ); + "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing"); decoder._bytesUsed = (int)(bytes - byteStart); decoder.lastChar = lastChar; diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs index d1f306abb0f..5a80d7b4486 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs @@ -125,7 +125,7 @@ namespace System // Get the base offset to prefix in front of the time zone. // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. - string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; + string baseOffsetText = string.Create(null, stackalloc char[128], $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"); // Get the generic location name. string? genericLocationName = null; diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 1356e65758c..4756a932715 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -109,7 +109,7 @@ namespace System // These are expected in environments without time zone globalization data _standardDisplayName = standardAbbrevName; _daylightDisplayName = daylightAbbrevName ?? standardAbbrevName; - _displayName = $"(UTC{(_baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{_baseUtcOffset:hh\\:mm}) {_id}"; + _displayName = string.Create(null, stackalloc char[256], $"(UTC{(_baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{_baseUtcOffset:hh\\:mm}) {_id}"); // Try to populate the display names from the globalization data TryPopulateTimeZoneDisplayNamesFromGlobalizationData(_id, _baseUtcOffset, ref _standardDisplayName, ref _daylightDisplayName, ref _displayName); diff --git a/src/libraries/System.Private.CoreLib/src/System/Version.cs b/src/libraries/System.Private.CoreLib/src/System/Version.cs index 455978045f3..bf0ce8e2b4c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Version.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Version.cs @@ -197,55 +197,33 @@ namespace System public bool TryFormat(Span destination, int fieldCount, out int charsWritten) { - string? failureUpperBound = (uint)fieldCount switch + switch (fieldCount) { - > 4 => "4", - >= 3 when _Build == -1 => "2", - 4 when _Revision == -1 => "3", - _ => null - }; - if (failureUpperBound is not null) - { - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount)); - } - - int totalCharsWritten = 0; - - for (int i = 0; i < fieldCount; i++) - { - if (i != 0) - { - if (destination.IsEmpty) - { - charsWritten = 0; - return false; - } - - destination[0] = '.'; - destination = destination.Slice(1); - totalCharsWritten++; - } - - int value = i switch - { - 0 => _Major, - 1 => _Minor, - 2 => _Build, - _ => _Revision - }; - - if (!value.TryFormat(destination, out int valueCharsWritten)) - { + case 0: charsWritten = 0; - return false; - } + return true; - totalCharsWritten += valueCharsWritten; - destination = destination.Slice(valueCharsWritten); + case 1: + return ((uint)_Major).TryFormat(destination, out charsWritten); + + case 2: + return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}", out charsWritten); + + case 3: + if (_Build == -1) throw CreateBoundException("3"); + return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}", out charsWritten); + + case 4: + if (_Build == -1) throw CreateBoundException("2"); + if (_Revision == -1) throw CreateBoundException("3"); + return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}.{(uint)_Revision}", out charsWritten); + + default: + throw CreateBoundException("4"); } - charsWritten = totalCharsWritten; - return true; + static Exception CreateBoundException(string failureUpperBound) => + new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount)); } bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs index 9c164eed113..eab7d2e6c28 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs @@ -209,7 +209,7 @@ namespace System.Runtime.Serialization.Json // +"zzzz"; //TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime()); TimeSpan ts = TimeZoneInfo.Local.GetUtcOffset(value.ToLocalTime()); - writer.WriteString(string.Format(CultureInfo.InvariantCulture, "{0:+00;-00}{1:00;00}", ts.Hours, ts.Minutes)); + writer.WriteString(string.Create(CultureInfo.InvariantCulture, $"{ts.Hours:+00;-00}{ts.Minutes:00;00}")); break; case DateTimeKind.Utc: break; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs index 103013f0959..d52d1fabaf4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs @@ -1420,7 +1420,7 @@ namespace System.Runtime.Serialization.Json _nodeWriter.WriteChars(chars + i, j - i); _nodeWriter.WriteText(BACK_SLASH); _nodeWriter.WriteText('u'); - _nodeWriter.WriteText(string.Format(CultureInfo.InvariantCulture, "{0:x4}", (int)ch)); + _nodeWriter.WriteText($"{(int)ch:x4}"); i = j + 1; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs index 97e134fe4e3..40584d6d2d5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializer.cs @@ -393,7 +393,7 @@ namespace System.Runtime.Serialization internal static string TryAddLineInfo(XmlReaderDelegator reader, string errorMessage) { if (reader.HasLineInfo()) - return string.Format(CultureInfo.InvariantCulture, "{0} {1}", SR.Format(SR.ErrorInLine, reader.LineNumber, reader.LinePosition), errorMessage); + return string.Create(CultureInfo.InvariantCulture, $"{SR.Format(SR.ErrorInLine, reader.LineNumber, reader.LinePosition)} {errorMessage}"); return errorMessage; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs index 48273f97922..967e30458d3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerWriteContext.cs @@ -221,12 +221,12 @@ namespace System.Runtime.Serialization if (isNew) { xmlWriter.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.IdLocalName, - DictionaryGlobals.SerializationNamespace, string.Format(CultureInfo.InvariantCulture, "i{0}", objectId)); + DictionaryGlobals.SerializationNamespace, string.Create(CultureInfo.InvariantCulture, $"i{objectId}")); return false; } else { - xmlWriter.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.RefLocalName, DictionaryGlobals.SerializationNamespace, string.Format(CultureInfo.InvariantCulture, "i{0}", objectId)); + xmlWriter.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.RefLocalName, DictionaryGlobals.SerializationNamespace, string.Create(CultureInfo.InvariantCulture, $"i{objectId}")); return true; } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs index 50f78a1dd4a..f0be0f4e66f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlWriterDelegator.cs @@ -84,7 +84,7 @@ namespace System.Runtime.Serialization string? prefix = writer.LookupPrefix(ns); if (prefix == null) { - prefix = string.Format(CultureInfo.InvariantCulture, "d{0}p{1}", depth, _prefixes); + prefix = string.Create(CultureInfo.InvariantCulture, $"d{depth}p{_prefixes}"); _prefixes++; writer.WriteAttributeString("xmlns", prefix, null, ns); } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs index 0fe175fc227..50d93601ae4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs @@ -1085,7 +1085,7 @@ namespace System.Xml { string value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (localName == value) { return i; @@ -1100,7 +1100,7 @@ namespace System.Xml { string value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (prefix == value) { return i; @@ -1127,7 +1127,7 @@ namespace System.Xml { XmlDictionaryString value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (localName == value) { return i; @@ -1142,7 +1142,7 @@ namespace System.Xml { XmlDictionaryString value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (prefix == value) { return i; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs index 13098a6aa29..6692a66ac2b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs @@ -312,7 +312,7 @@ namespace System.Xml { string value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (localName == value) { return i; @@ -338,7 +338,7 @@ namespace System.Xml { XmlDictionaryString value = localNames[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "localNames[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"localNames[{i}]"); if (localName == value.Value) { return i; @@ -630,7 +630,7 @@ namespace System.Xml { string value = strings[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "strings[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"strings[{i}]"); if (value == s) { index = i; @@ -650,7 +650,7 @@ namespace System.Xml { XmlDictionaryString value = strings[i]; if (value == null) - throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(string.Format(CultureInfo.InvariantCulture, "strings[{0}]", i)); + throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull($"strings[{i}]"); if (value.Value == s) { index = i; diff --git a/src/libraries/System.Private.Uri/src/System/IPv4AddressHelper.cs b/src/libraries/System.Private.Uri/src/System/IPv4AddressHelper.cs index ef18d5348c9..1f751eeb576 100644 --- a/src/libraries/System.Private.Uri/src/System/IPv4AddressHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/IPv4AddressHelper.cs @@ -41,10 +41,11 @@ namespace System { fixed (char* ipString = name) { + // end includes ports, so changedEnd may be different from end int changedEnd = end; long result = IPv4AddressHelper.ParseNonCanonical(ipString, start, ref changedEnd, true); - // end includes ports, so changedEnd may be different from end - Debug.Assert(result != Invalid, "Failed to parse after already validated: " + name); + + Debug.Assert(result != Invalid, $"Failed to parse after already validated: {name}"); unchecked { diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index 4b7e1f4ceff..dc687676f03 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -4824,27 +4824,22 @@ namespace System // For compatibility with V1.0 parser we restrict the compression scope to Unc Share, i.e. \\host\share\ if (basePart.IsUnc) { - string share = basePart.GetParts(UriComponents.Path | UriComponents.KeepDelimiter, - UriFormat.Unescaped); + ReadOnlySpan share = basePart.GetParts(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); for (int i = 1; i < share.Length; ++i) { if (share[i] == '/') { - share = share.Substring(0, i); + share = share.Slice(0, i); break; } } + if (basePart.IsImplicitFile) { - return @"\\" - + basePart.GetParts(UriComponents.Host, UriFormat.Unescaped) - + share - + relativePart; + return string.Concat(@"\\", basePart.GetParts(UriComponents.Host, UriFormat.Unescaped), share, relativePart); } - return "file://" - + basePart.GetParts(UriComponents.Host, uriFormat) - + share - + relativePart; + + return string.Concat("file://", basePart.GetParts(UriComponents.Host, uriFormat), share, relativePart); } // It's not obvious but we've checked (for this relativePart format) that baseUti is nor UNC nor DOS path // diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/IriRelativeFileResolutionTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/IriRelativeFileResolutionTest.cs index befeda705c5..7715d7669f8 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/IriRelativeFileResolutionTest.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/IriRelativeFileResolutionTest.cs @@ -123,8 +123,7 @@ namespace System.PrivateUri.Tests if (!(implicitValue.Equals(explicitValue) || (fileSchemePrefix + implicitValue).Equals(explicitValue))) { errorCount++; - testResults.Append("Property mismatch: " + info.Name + ", implicit value: " + implicitValue - + ", explicit value: " + explicitValue + "; "); + testResults.Append($"Property mismatch: {info.Name}, implicit value: {implicitValue}, explicit value: {explicitValue}; "); } } @@ -133,8 +132,7 @@ namespace System.PrivateUri.Tests if (!implicitString.Equals(explicitString)) { errorCount++; - testResults.Append("ToString mismatch; implicit value: " + implicitString - + ", explicit value: " + explicitString); + testResults.Append($"ToString mismatch; implicit value: {implicitString}, explicit value: {explicitString}"); } errors = testResults.ToString(); @@ -275,8 +273,7 @@ namespace System.PrivateUri.Tests if (!resultValue.Equals(startValue)) { errorCount++; - testResults.Append("Property mismatch: " + info.Name + ", result value: " - + resultValue + ", start value: " + startValue + "; "); + testResults.Append($"Property mismatch: {info.Name}, result value: {resultValue}, start value: {startValue}; "); } } @@ -285,8 +282,7 @@ namespace System.PrivateUri.Tests if (!resultString.Equals(startString)) { errorCount++; - testResults.Append("ToString mismatch; result value: " + resultString - + ", start value: " + startString); + testResults.Append($"ToString mismatch; result value: {resultString}, start value: {startString}"); } errors = testResults.ToString(); diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs index 8a548c85abe..fa4ee9299a8 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs @@ -686,12 +686,9 @@ namespace System.PrivateUri.Tests { var result = new StringBuilder(); - byte[] bytes = Encoding.UTF8.GetBytes(input); - - foreach (byte b in bytes) + foreach (byte b in Encoding.UTF8.GetBytes(input)) { - result.Append('%'); - result.Append(b.ToString("X2")); + result.Append($"%{b:X2}"); } return result.ToString(); @@ -710,12 +707,9 @@ namespace System.PrivateUri.Tests continue; } - byte[] bytes = Encoding.UTF8.GetBytes(c.ToString()); - - foreach (byte b in bytes) + foreach (byte b in Encoding.UTF8.GetBytes(c.ToString())) { - result.Append('%'); - result.Append(b.ToString("X2")); + result.Append($"%{b:X2}"); } } diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs index 2ee9abc0851..547c8196d58 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs @@ -1388,8 +1388,7 @@ namespace System.Xml.Linq /// The first attribute which is not a namespace attribute or null if the end of attributes has bean reached private XAttribute? GetFirstNonDuplicateNamespaceAttribute(XAttribute candidate) { - Debug.Assert(_omitDuplicateNamespaces, "This method should only be called if we're omitting duplicate namespace attribute." + - "For perf reason it's better to test this flag in the caller method."); + Debug.Assert(_omitDuplicateNamespaces, "This method should only be called if we're omitting duplicate namespace attribute. For perf reason it's better to test this flag in the caller method."); if (!IsDuplicateNamespaceAttribute(candidate)) { return candidate; diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/ManagedNodeWriter.cs b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/ManagedNodeWriter.cs index 0e4a7033b10..f8773b85ffc 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/ManagedNodeWriter.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/ManagedNodeWriter.cs @@ -218,7 +218,7 @@ namespace CoreXml.Test.XLinq /// public void PutCData() { - _nodeQueue.Append(""); + _nodeQueue.Append($""); } /// @@ -226,7 +226,7 @@ namespace CoreXml.Test.XLinq /// public void PutPI() { - _nodeQueue.Append(""); + _nodeQueue.Append($""); } /// @@ -234,7 +234,7 @@ namespace CoreXml.Test.XLinq /// public void PutComment() { - _nodeQueue.Append(""); + _nodeQueue.Append($""); } /// diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs index 30e93b62429..a2ab5b21217 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNode.cs @@ -369,8 +369,8 @@ namespace MS.Internal.Xml.Cache /// public void SetLineInfoOffsets(int lineNumOffset, int linePosOffset) { - Debug.Assert(lineNumOffset >= 0 && lineNumOffset <= MaxLineNumberOffset, "Line number offset too large or small: " + lineNumOffset); - Debug.Assert(linePosOffset >= 0 && linePosOffset <= MaxLinePositionOffset, "Line position offset too large or small: " + linePosOffset); + Debug.Assert(lineNumOffset >= 0 && lineNumOffset <= MaxLineNumberOffset, $"Line number offset too large or small: {lineNumOffset}"); + Debug.Assert(linePosOffset >= 0 && linePosOffset <= MaxLinePositionOffset, $"Line position offset too large or small: {linePosOffset}"); _props |= ((uint)lineNumOffset << LineNumberShift); _posOffset = (ushort)linePosOffset; } @@ -380,7 +380,7 @@ namespace MS.Internal.Xml.Cache /// public void SetCollapsedLineInfoOffset(int posOffset) { - Debug.Assert(posOffset >= 0 && posOffset <= MaxCollapsedPositionOffset, "Collapsed text line position offset too large or small: " + posOffset); + Debug.Assert(posOffset >= 0 && posOffset <= MaxCollapsedPositionOffset, $"Collapsed text line position offset too large or small: {posOffset}"); _props |= ((uint)posOffset << CollapsedPositionShift); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs index cfbdc9e8a0d..5cdccd48008 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs @@ -102,7 +102,7 @@ namespace System.Xml if (_parent.CanReplaceAt(index)) { // Create the replacement character entity - _charEntity = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", new object[] { (int)charUnknown }); + _charEntity = string.Create(null, stackalloc char[64], $"&#x{(int)charUnknown:X};"); _charEntityIndex = 0; return true; } @@ -131,7 +131,7 @@ namespace System.Xml if (_parent.CanReplaceAt(index)) { // Create the replacement character entity - _charEntity = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", new object[] { SurrogateCharToUtf32(charUnknownHigh, charUnknownLow) }); + _charEntity = string.Create(null, stackalloc char[64], $"&#x{SurrogateCharToUtf32(charUnknownHigh, charUnknownLow):X};"); _charEntityIndex = 0; return true; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs index ffb19d21d52..c39f6155d3d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs @@ -1186,7 +1186,7 @@ namespace System.Xml _currentState = State.Prolog; StringBuilder bufBld = new StringBuilder(128); - bufBld.Append("version=" + _quoteChar + "1.0" + _quoteChar); + bufBld.Append($"version={_quoteChar}1.0{_quoteChar}"); if (_encoding != null) { bufBld.Append(" encoding="); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs index 2e2baa40287..0a4ad3d346e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs @@ -354,7 +354,7 @@ namespace System.Xml.Schema #if DEBUG public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) { - bb.Append("\"" + symbols.NameOf(positions[_pos].symbol) + "\""); + bb.Append($"\"{symbols.NameOf(positions[_pos].symbol)}\""); } #endif } @@ -427,7 +427,7 @@ namespace System.Xml.Schema #if DEBUG public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) { - bb.Append("[" + namespaceList.ToString() + "]"); + bb.Append($"[{namespaceList}]"); } #endif } @@ -876,7 +876,7 @@ namespace System.Xml.Schema public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) { LeftChild.Dump(bb, symbols, positions); - bb.Append("{" + Convert.ToString(min, NumberFormatInfo.InvariantInfo) + ", " + Convert.ToString(max, NumberFormatInfo.InvariantInfo) + "}"); + bb.Append($"{{{Convert.ToString(min, NumberFormatInfo.InvariantInfo)}, {Convert.ToString(max, NumberFormatInfo.InvariantInfo)}}}"); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Inference/Infer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Inference/Infer.cs index 86fc6cd3953..9f2a8d5c0f7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Inference/Infer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Inference/Infer.cs @@ -1782,7 +1782,7 @@ namespace System.Xml.Schema //else case 'I': //try to match "INF" INF: - if (s.Substring(i) == "INF") + if (s.AsSpan(i).SequenceEqual("INF")) return TF_float | TF_double | TF_string; else return TF_string; case '.': //try to match ".9999" decimal/float/double diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaCollectionCompiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaCollectionCompiler.cs index 5312db2a96c..615c405d31b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaCollectionCompiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaCollectionCompiler.cs @@ -2526,7 +2526,7 @@ namespace System.Xml.Schema } else { - sb.Append("{" + particle.MinOccurs.ToString(NumberFormatInfo.InvariantInfo) + ", " + particle.MaxOccurs.ToString(NumberFormatInfo.InvariantInfo) + "}"); + sb.Append($"{{{particle.MinOccurs.ToString(NumberFormatInfo.InvariantInfo)}, {particle.MaxOccurs.ToString(NumberFormatInfo.InvariantInfo)}}}"); } } #endif diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdDateTime.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdDateTime.cs index 717bc204e58..71f43082b5e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdDateTime.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdDateTime.cs @@ -191,7 +191,7 @@ namespace System.Xml.Schema default: { - Debug.Assert(dateTime.Kind == DateTimeKind.Local, "Unknown DateTimeKind: " + dateTime.Kind); + Debug.Assert(dateTime.Kind == DateTimeKind.Local, $"Unknown DateTimeKind: {dateTime.Kind}"); TimeSpan utcOffset = TimeZoneInfo.Local.GetUtcOffset(dateTime); if (utcOffset.Ticks < 0) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index 6ef46a0f686..931735f60c6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -2464,7 +2464,7 @@ namespace System.Xml.Serialization { if (mappings[j].Name == member.ChoiceIdentifier.MemberName) { - choiceSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + choiceSource = $"p[{j}]"; break; } } @@ -2520,17 +2520,17 @@ namespace System.Xml.Serialization for (int i = 0; i < mappings.Length; i++) { MemberMapping mapping = mappings[i]; - string source = "p[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + string source = $"p[{i}]"; string arraySource = source; if (mapping.Xmlns != null) { - arraySource = "((" + mapping.TypeDesc!.CSharpName + ")" + source + ")"; + arraySource = $"(({mapping.TypeDesc!.CSharpName}){source})"; } string? choiceSource = GetChoiceIdentifierSource(mappings, mapping); Member member = new Member(this, source, arraySource, "a", i, mapping, choiceSource); Member anyMember = new Member(this, source, null, "a", i, mapping, choiceSource); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) { string nameSpecified = mapping.Name + "Specified"; @@ -2538,7 +2538,7 @@ namespace System.Xml.Serialization { if (mappings[j].Name == nameSpecified) { - member.CheckSpecifiedSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + member.CheckSpecifiedSource = $"p[{j}]"; break; } } @@ -2700,15 +2700,15 @@ namespace System.Xml.Serialization for (int i = 0; i < mappings.Length; i++) { MemberMapping mapping = mappings[i]; - string source = "p[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + string source = $"p[{i}]"; string arraySource = source; if (mapping.Xmlns != null) { - arraySource = "((" + mapping.TypeDesc!.CSharpName + ")" + source + ")"; + arraySource = $"(({mapping.TypeDesc!.CSharpName}){source})"; } Member member = new Member(this, source, arraySource, "a", i, mapping); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; members[i] = member; if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) @@ -2718,7 +2718,7 @@ namespace System.Xml.Serialization { if (mappings[j].Name == nameSpecified) { - member.CheckSpecifiedSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + member.CheckSpecifiedSource = $"p[{j}]"; break; } } @@ -2826,15 +2826,11 @@ namespace System.Xml.Serialization return methodName; } - private string NextMethodName(string name) - { - return "Read" + (++NextMethodNumber).ToString(CultureInfo.InvariantCulture) + "_" + CodeIdentifier.MakeValidInternal(name); - } + private string NextMethodName(string name) => + string.Create(CultureInfo.InvariantCulture, $"Read{++NextMethodNumber}_{CodeIdentifier.MakeValidInternal(name)}"); - private string NextIdName(string name) - { - return "id" + (++_nextIdNumber).ToString(CultureInfo.InvariantCulture) + "_" + CodeIdentifier.MakeValidInternal(name); - } + private string NextIdName(string name) => + string.Create(CultureInfo.InvariantCulture, $"id{(++_nextIdNumber)}_{CodeIdentifier.MakeValidInternal(name)}"); private void WritePrimitive(TypeMapping mapping, string source) { @@ -2961,7 +2957,7 @@ namespace System.Xml.Serialization else { Writer.Write(", "); - Writer.Write(constants[i].Value.ToString(CultureInfo.InvariantCulture) + "L"); + Writer.Write(string.Create(CultureInfo.InvariantCulture, $"{constants[i].Value}L")); } Writer.WriteLine(");"); @@ -3336,7 +3332,7 @@ namespace System.Xml.Serialization string source = RaCodeGen.GetStringForMember("o", mapping.Name, structMapping.TypeDesc); Member member = new Member(this, source, "a", i, mapping, GetChoiceIdentifierSource(mapping, "o", structMapping.TypeDesc)); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; member.IsNullable = mapping.TypeDesc!.IsNullable; if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) member.CheckSpecifiedSource = RaCodeGen.GetStringForMember("o", mapping.Name + "Specified", structMapping.TypeDesc); @@ -3478,7 +3474,7 @@ namespace System.Xml.Serialization if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) member.CheckSpecifiedSource = RaCodeGen.GetStringForMember("o", mapping.Name + "Specified", structMapping.TypeDesc); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; members[i] = member; } @@ -3581,7 +3577,7 @@ namespace System.Xml.Serialization CreateCollectionInfo? create = (CreateCollectionInfo?)_createMethods[typeDesc]; if (create == null) { - string createName = "create" + (++_nextCreateMethodNumber).ToString(CultureInfo.InvariantCulture) + "_" + typeDesc.Name; + string createName = string.Create(CultureInfo.InvariantCulture, $"create{++_nextCreateMethodNumber}_{typeDesc.Name}"); create = new CreateCollectionInfo(createName, typeDesc); _createMethods.Add(typeDesc, create); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs index 010d537da38..41c5c83506d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs @@ -373,7 +373,7 @@ namespace System.Xml.Serialization { if (mappings[j].Name == member.ChoiceIdentifier.MemberName) { - choiceSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + choiceSource = $"p[{j}]"; break; } } @@ -447,17 +447,17 @@ namespace System.Xml.Serialization for (int i = 0; i < mappings.Length; i++) { MemberMapping mapping = mappings[i]; - string source = "p[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + string source = $"p[{i}]"; string arraySource = source; if (mapping.Xmlns != null) { - arraySource = "((" + mapping.TypeDesc!.CSharpName + ")" + source + ")"; + arraySource = $"(({mapping.TypeDesc!.CSharpName}){source})"; } string choiceSource = GetChoiceIdentifierSource(mappings, mapping); Member member = new Member(this, source, arraySource, "a", i, mapping, choiceSource); Member anyMember = new Member(this, source, null, "a", i, mapping, choiceSource); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) { string nameSpecified = mapping.Name + "Specified"; @@ -465,7 +465,7 @@ namespace System.Xml.Serialization { if (mappings[j].Name == nameSpecified) { - member.CheckSpecifiedSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + member.CheckSpecifiedSource = $"p[{j}]"; break; } } @@ -685,15 +685,11 @@ namespace System.Xml.Serialization return methodName; } - private string NextMethodName(string name) - { - return "Read" + (++NextMethodNumber).ToString(CultureInfo.InvariantCulture) + "_" + CodeIdentifier.MakeValidInternal(name); - } + private string NextMethodName(string name) => + string.Create(CultureInfo.InvariantCulture, $"Read{++NextMethodNumber}_{CodeIdentifier.MakeValidInternal(name)}"); - private string NextIdName(string name) - { - return "id" + (++_nextIdNumber).ToString(CultureInfo.InvariantCulture) + "_" + CodeIdentifier.MakeValidInternal(name); - } + private string NextIdName(string name) => + string.Create(CultureInfo.InvariantCulture, $"id{++_nextIdNumber}_{CodeIdentifier.MakeValidInternal(name)}"); [RequiresUnreferencedCode("XmlSerializationReader methods have RequiresUnreferencedCode")] private void WritePrimitive(TypeMapping mapping, string source) @@ -1571,7 +1567,7 @@ namespace System.Xml.Serialization string source = RaCodeGen.GetStringForMember("o", mapping.Name, structMapping.TypeDesc); Member member = new Member(this, source, "a", i, mapping, GetChoiceIdentifierSource(mapping, "o", structMapping.TypeDesc)); if (!mapping.IsSequence) - member.ParamsReadSource = "paramsRead[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + member.ParamsReadSource = $"paramsRead[{i}]"; member.IsNullable = mapping.TypeDesc!.IsNullable; if (mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) member.CheckSpecifiedSource = RaCodeGen.GetStringForMember("o", mapping.Name + "Specified", structMapping.TypeDesc); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs index 62dc75b1c99..8308e0de386 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs @@ -1241,7 +1241,7 @@ namespace System.Xml.Serialization } else { - _w.WriteAttributeString("arrayType", Soap.Encoding, GetQualifiedName(typeName, typeNs) + "[" + arrayLength.ToString(CultureInfo.InvariantCulture) + "]"); + _w.WriteAttributeString("arrayType", Soap.Encoding, $"{GetQualifiedName(typeName, typeNs)}[{arrayLength}]"); } for (int i = 0; i < arrayLength; i++) { @@ -2605,7 +2605,7 @@ namespace System.Xml.Serialization int xmlnsMember = FindXmlnsIndex(mapping.Members!); if (xmlnsMember >= 0) { - string source = "((" + typeof(System.Xml.Serialization.XmlSerializerNamespaces).FullName + ")p[" + xmlnsMember.ToString(CultureInfo.InvariantCulture) + "])"; + string source = $"(({typeof(System.Xml.Serialization.XmlSerializerNamespaces).FullName})p[{xmlnsMember}])"; Writer.Write("if (pLength > "); Writer.Write(xmlnsMember.ToString(CultureInfo.InvariantCulture)); @@ -2623,7 +2623,7 @@ namespace System.Xml.Serialization if (member.Attribute != null && !member.Ignore) { string index = i.ToString(CultureInfo.InvariantCulture); - string source = "p[" + index + "]"; + string source = $"p[{index}]"; string? specifiedSource = null; int specifiedPosition = 0; @@ -2634,7 +2634,7 @@ namespace System.Xml.Serialization { if (mapping.Members[j].Name == memberNameSpecified) { - specifiedSource = "((bool) p[" + j.ToString(CultureInfo.InvariantCulture) + "])"; + specifiedSource = $"((bool) p[{j}])"; specifiedPosition = j; break; } @@ -2688,7 +2688,7 @@ namespace System.Xml.Serialization { if (mapping.Members[j].Name == memberNameSpecified) { - specifiedSource = "((bool) p[" + j.ToString(CultureInfo.InvariantCulture) + "])"; + specifiedSource = $"((bool) p[{j}])"; specifiedPosition = j; break; } @@ -2720,9 +2720,9 @@ namespace System.Xml.Serialization if (mapping.Members[j].Name == member.ChoiceIdentifier.MemberName) { if (member.ChoiceIdentifier.Mapping!.TypeDesc!.UseReflection) - enumSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; + enumSource = $"p[{j}]"; else - enumSource = "((" + mapping.Members[j].TypeDesc!.CSharpName + ")p[" + j.ToString(CultureInfo.InvariantCulture) + "]" + ")"; + enumSource = $"(({mapping.Members[j].TypeDesc!.CSharpName })p[{j}])"; break; } } @@ -4427,12 +4427,12 @@ namespace System.Xml.Serialization continue; } int colon = xmlName.LastIndexOf(':'); - string? choiceNs = colon < 0 ? choiceMapping.Namespace : xmlName.Substring(0, colon); - string choiceName = colon < 0 ? xmlName : xmlName.Substring(colon + 1); + ReadOnlySpan choiceNs = colon < 0 ? choiceMapping.Namespace : xmlName.AsSpan(0, colon); + ReadOnlySpan choiceName = colon < 0 ? xmlName : xmlName.AsSpan(colon + 1); - if (element.Name == choiceName) + if (choiceName.SequenceEqual(element.Name)) { - if ((element.Form == XmlSchemaForm.Unqualified && string.IsNullOrEmpty(choiceNs)) || element.Namespace == choiceNs) + if ((element.Form == XmlSchemaForm.Unqualified && choiceNs.IsEmpty) || choiceNs.SequenceEqual(element.Namespace)) { if (useReflection) enumValue = choiceMapping.Constants[i].Value.ToString(CultureInfo.InvariantCulture); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs index f348fdfa953..1bf9b7069b1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs @@ -407,7 +407,7 @@ namespace System.Xml.Serialization int xmlnsMember = FindXmlnsIndex(mapping.Members!); if (xmlnsMember >= 0) { - string source = "((" + typeof(XmlSerializerNamespaces).FullName + ")p[" + xmlnsMember.ToString(CultureInfo.InvariantCulture) + "])"; + string source = string.Create(CultureInfo.InvariantCulture, $"(({typeof(XmlSerializerNamespaces).FullName})p[{xmlnsMember}])"); ilg.Ldloc(pLengthLoc); ilg.Ldc(xmlnsMember); @@ -422,7 +422,7 @@ namespace System.Xml.Serialization if (member.Attribute != null && !member.Ignore) { - SourceInfo source = new SourceInfo("p[" + i.ToString(CultureInfo.InvariantCulture) + "]", null, null, pLengthLoc.LocalType.GetElementType()!, ilg); + SourceInfo source = new SourceInfo($"p[{i}]", null, null, pLengthLoc.LocalType.GetElementType()!, ilg); SourceInfo? specifiedSource = null; int specifiedPosition = 0; @@ -433,7 +433,7 @@ namespace System.Xml.Serialization { if (mapping.Members[j].Name == memberNameSpecified) { - specifiedSource = new SourceInfo("((bool)p[" + j.ToString(CultureInfo.InvariantCulture) + "])", null, null, typeof(bool), ilg); + specifiedSource = new SourceInfo($"((bool)p[{j}])", null, null, typeof(bool), ilg); specifiedPosition = j; break; } @@ -489,7 +489,7 @@ namespace System.Xml.Serialization { if (mapping.Members[j].Name == memberNameSpecified) { - specifiedSource = new SourceInfo("((bool)p[" + j.ToString(CultureInfo.InvariantCulture) + "])", null, null, typeof(bool), ilg); + specifiedSource = new SourceInfo($"((bool)p[{j}])", null, null, typeof(bool), ilg); specifiedPosition = j; break; } @@ -515,7 +515,7 @@ namespace System.Xml.Serialization ilg.If(); } - string source = "p[" + i.ToString(CultureInfo.InvariantCulture) + "]"; + string source = $"p[{i}]"; string? enumSource = null; if (member.ChoiceIdentifier != null) { @@ -523,7 +523,7 @@ namespace System.Xml.Serialization { if (mapping.Members[j].Name == member.ChoiceIdentifier.MemberName) { - enumSource = "((" + mapping.Members[j].TypeDesc!.CSharpName + ")p[" + j.ToString(CultureInfo.InvariantCulture) + "]" + ")"; + enumSource = $"(({mapping.Members[j].TypeDesc!.CSharpName})p[{j}])"; break; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlConvert.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlConvert.cs index b28f0d6eb8b..c2f54c2715c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlConvert.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlConvert.cs @@ -237,13 +237,13 @@ namespace System.Xml int x = name[0]; int y = name[1]; int u = XmlCharType.CombineSurrogateChar(y, x); - bufBld.Append(u.ToString("X8", CultureInfo.InvariantCulture)); + bufBld.Append($"{u:X8}"); position++; copyPosition = 2; } else { - bufBld.Append(((int)name[0]).ToString("X4", CultureInfo.InvariantCulture)); + bufBld.Append($"{(int)name[0]:X4}"); copyPosition = 1; } @@ -282,13 +282,13 @@ namespace System.Xml int x = name[position]; int y = name[position + 1]; int u = XmlCharType.CombineSurrogateChar(y, x); - bufBld.Append(u.ToString("X8", CultureInfo.InvariantCulture)); + bufBld.Append($"{u:X8}"); copyPosition = position + 2; position++; } else { - bufBld.Append(((int)name[position]).ToString("X4", CultureInfo.InvariantCulture)); + bufBld.Append($"{(int)name[position]:X4}"); copyPosition = position + 1; } bufBld.Append('_'); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlException.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlException.cs index dde7f76f54a..7d8276f0851 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlException.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlException.cs @@ -234,9 +234,8 @@ namespace System.Xml if (XmlCharType.IsHighSurrogate(invChar) && nextChar != 0) { int combinedChar = XmlCharType.CombineSurrogateChar(nextChar, invChar); - ReadOnlySpan invAndNextChars = stackalloc char[] { invChar, nextChar }; - aStringList[0] = new string(invAndNextChars); - aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", combinedChar); + aStringList[0] = new string(stackalloc char[] { invChar, nextChar }); + aStringList[1] = $"0x{combinedChar:X2}"; } else { @@ -249,7 +248,7 @@ namespace System.Xml { aStringList[0] = invChar.ToString(); } - aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", (int)invChar); + aStringList[1] = $"0x{(int)invChar:X2}"; } return aStringList; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs index cc7b1fcc839..9e142049756 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs @@ -109,7 +109,7 @@ namespace System.Xml.Xsl.IlGen public static StorageDescriptor Current(LocalBuilder locIter, MethodInfo currentMethod, Type itemStorageType) { Debug.Assert(currentMethod.ReturnType == itemStorageType, - "Type " + itemStorageType + " does not match type of Current property."); + $"Type {itemStorageType} does not match type of Current property."); StorageDescriptor storage = default; storage._location = ItemLocation.Current; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs index dc3bd0d9a38..ab2c645e9a0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs @@ -233,7 +233,7 @@ namespace System.Xml.Xsl.IlGen case 2: arg = _arg2; break; } - Debug.Assert(arg != null, "There is no '" + argNum + "' argument."); + Debug.Assert(arg != null, $"There is no '{argNum}' argument."); return arg; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs index 1c7ea589ad3..29145c1c737 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs @@ -5440,7 +5440,7 @@ namespace System.Xml.Xsl.IlGen { object litLeft, litRight; int cmp; - Debug.Assert(left.XmlType == right.XmlType, "Comparison is not defined between " + left.XmlType + " and " + right.XmlType); + Debug.Assert(left.XmlType == right.XmlType, $"Comparison is not defined between {left.XmlType} and {right.XmlType}"); // Extract objects that represent each literal value litLeft = ExtractLiteralValue(left); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs index 7c5a4c78db4..fb15bb3ecc6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs @@ -1749,7 +1749,7 @@ namespace System.Xml.Xsl.IlGen } else { - Debug.Assert(code != XmlTypeCode.QName, "QName values do not support the " + relOp + " operation"); + Debug.Assert(code != XmlTypeCode.QName, $"QName values do not support the {relOp} operation"); // Push -1, 0, or 1 onto the stack depending upon the result of the comparison _helper.CallCompare(code); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs index b63284285b4..d4f0fe916b9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs @@ -26,7 +26,7 @@ namespace System.Xml.Xsl.Runtime _namespaceUri = namespaceUri; _ebType = ebType; _constrInfo = ebType.GetConstructor(Type.EmptyTypes); - Debug.Assert(_constrInfo != null, "The early bound object type " + ebType.FullName + " must have a public default constructor"); + Debug.Assert(_constrInfo != null, $"The early bound object type {ebType.FullName} must have a public default constructor"); } /// diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/TreeIterators.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/TreeIterators.cs index 07a97985894..65c54354394 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/TreeIterators.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/TreeIterators.cs @@ -928,7 +928,7 @@ namespace System.Xml.Xsl.Runtime return true; } - Debug.Assert(_state == IteratorState.NoNext, "Illegal state: " + _state); + Debug.Assert(_state == IteratorState.NoNext, $"Illegal state: {_state}"); return false; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryOutput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryOutput.cs index 83387e02926..f33347623ee 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryOutput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryOutput.cs @@ -418,7 +418,7 @@ namespace System.Xml.Xsl.Runtime /// public void StartTree(XPathNodeType rootType) { - Debug.Assert(_xstate == XmlState.WithinSequence, "StartTree cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinSequence, $"StartTree cannot be called in the {_xstate} state."); Writer = _seqwrt.StartTree(rootType, _nsmgr, _runtime.NameTable); _rootType = rootType; _xstate = (rootType == XPathNodeType.Attribute || rootType == XPathNodeType.Namespace) ? XmlState.EnumAttrs : XmlState.WithinContent; @@ -429,7 +429,7 @@ namespace System.Xml.Xsl.Runtime /// public void EndTree() { - Debug.Assert(_xstate == XmlState.EnumAttrs || _xstate == XmlState.WithinContent, "EndTree cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.EnumAttrs || _xstate == XmlState.WithinContent, $"EndTree cannot be called in the {_xstate} state."); _seqwrt.EndTree(); _xstate = XmlState.WithinSequence; Writer = null; @@ -445,7 +445,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteStartElementUnchecked(string prefix, string localName, string ns) { - Debug.Assert(_xstate == XmlState.WithinContent, "WriteStartElement cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinContent, $"WriteStartElement cannot be called in the {_xstate} state."); if (_nsmgr != null) _nsmgr.PushScope(); Writer.WriteStartElement(prefix, localName, ns); //reset when enter element @@ -468,7 +468,7 @@ namespace System.Xml.Xsl.Runtime /// public void StartElementContentUnchecked() { - Debug.Assert(_xstate == XmlState.EnumAttrs, "StartElementContent cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.EnumAttrs, $"StartElementContent cannot be called in the {_xstate} state."); // Output any cached namespaces if (_cntNmsp != 0) @@ -483,7 +483,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteEndElementUnchecked(string prefix, string localName, string ns) { - Debug.Assert(_xstate == XmlState.EnumAttrs || _xstate == XmlState.WithinContent, "WriteEndElement cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.EnumAttrs || _xstate == XmlState.WithinContent, $"WriteEndElement cannot be called in the {_xstate} state."); Writer.WriteEndElement(prefix, localName, ns); _xstate = XmlState.WithinContent; _depth--; @@ -503,7 +503,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteStartAttributeUnchecked(string prefix, string localName, string ns) { - Debug.Assert(_xstate == XmlState.EnumAttrs, "WriteStartAttribute cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.EnumAttrs, $"WriteStartAttribute cannot be called in the {_xstate} state."); Writer.WriteStartAttribute(prefix, localName, ns); _xstate = XmlState.WithinAttr; _depth++; @@ -522,7 +522,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteEndAttributeUnchecked() { - Debug.Assert(_xstate == XmlState.WithinAttr, "WriteEndAttribute cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinAttr, $"WriteEndAttribute cannot be called in the {_xstate} state."); Writer.WriteEndAttribute(); _xstate = XmlState.EnumAttrs; _depth--; @@ -538,7 +538,7 @@ namespace System.Xml.Xsl.Runtime public void WriteNamespaceDeclarationUnchecked(string prefix, string ns) { Debug.Assert(prefix != null && ns != null); - Debug.Assert(_xstate == XmlState.EnumAttrs, "WriteNamespaceDeclaration cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.EnumAttrs, $"WriteNamespaceDeclaration cannot be called in the {_xstate} state."); // xmlns:foo="" is illegal Debug.Assert(prefix.Length == 0 || ns.Length != 0); @@ -571,7 +571,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteStringUnchecked(string text) { - Debug.Assert(_xstate != XmlState.WithinSequence && _xstate != XmlState.EnumAttrs, "WriteTextBlock cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate != XmlState.WithinSequence && _xstate != XmlState.EnumAttrs, $"WriteTextBlock cannot be called in the {_xstate} state."); Writer.WriteString(text); } @@ -580,7 +580,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteRawUnchecked(string text) { - Debug.Assert(_xstate != XmlState.WithinSequence && _xstate != XmlState.EnumAttrs, "WriteTextBlockNoEntities cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate != XmlState.WithinSequence && _xstate != XmlState.EnumAttrs, $"WriteTextBlockNoEntities cannot be called in the {_xstate} state."); Writer.WriteRaw(text); } @@ -761,7 +761,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteNamespaceString(string text) { - Debug.Assert(_xstate == XmlState.WithinNmsp, "WriteNamespaceString cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinNmsp, $"WriteNamespaceString cannot be called in the {_xstate} state."); _nodeText.ConcatNoDelimiter(text); } @@ -770,7 +770,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteEndNamespace() { - Debug.Assert(_xstate == XmlState.WithinNmsp, "WriteEndNamespace cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinNmsp, $"WriteEndNamespace cannot be called in the {_xstate} state."); _xstate = XmlState.EnumAttrs; _depth--; @@ -800,7 +800,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteCommentString(string text) { - Debug.Assert(_xstate == XmlState.WithinComment, "WriteCommentString cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinComment, $"WriteCommentString cannot be called in the {_xstate} state."); _nodeText.ConcatNoDelimiter(text); } @@ -809,7 +809,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteEndComment() { - Debug.Assert(_xstate == XmlState.WithinComment, "WriteEndComment cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinComment, $"WriteEndComment cannot be called in the {_xstate} state."); Writer.WriteComment(_nodeText.GetResult()); @@ -843,7 +843,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteProcessingInstructionString(string text) { - Debug.Assert(_xstate == XmlState.WithinPI, "WriteProcessingInstructionString cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinPI, $"WriteProcessingInstructionString cannot be called in the {_xstate} state."); _nodeText.ConcatNoDelimiter(text); } @@ -852,7 +852,7 @@ namespace System.Xml.Xsl.Runtime /// public void WriteEndProcessingInstruction() { - Debug.Assert(_xstate == XmlState.WithinPI, "WriteEndProcessingInstruction cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinPI, $"WriteEndProcessingInstruction cannot be called in the {_xstate} state."); Writer.WriteProcessingInstruction(_piTarget, _nodeText.GetResult()); @@ -1205,7 +1205,7 @@ namespace System.Xml.Xsl.Runtime private void EndCopy(XPathNavigator navigator, bool callChk) { Debug.Assert(navigator.NodeType == XPathNodeType.Element); - Debug.Assert(_xstate == XmlState.WithinContent, "EndCopy cannot be called in the " + _xstate + " state."); + Debug.Assert(_xstate == XmlState.WithinContent, $"EndCopy cannot be called in the {_xstate} state."); if (callChk) WriteEndElement(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs index 4a490b3128f..4bdaf545111 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs @@ -492,7 +492,7 @@ namespace System.Xml.Xsl.Runtime "Values passed to ChangeTypeXsltArgument should be in ILGen's default Clr representation."); Debug.Assert(destinationType == XsltConvert.ObjectType || !destinationType.IsAssignableFrom(value.GetType()), - "No need to call ChangeTypeXsltArgument since value is already assignable to destinationType " + destinationType); + $"No need to call ChangeTypeXsltArgument since value is already assignable to destinationType {destinationType}"); switch (xmlType.TypeCode) { @@ -566,7 +566,7 @@ namespace System.Xml.Xsl.Runtime } } - Debug.Assert(destinationType.IsAssignableFrom(value.GetType()), "ChangeType from type " + value.GetType().Name + " to type " + destinationType.Name + " failed"); + Debug.Assert(destinationType.IsAssignableFrom(value.GetType()), $"ChangeType from type {value.GetType().Name} to type {destinationType.Name} failed"); return value; } @@ -688,7 +688,7 @@ namespace System.Xml.Xsl.Runtime } } - Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Xml type " + xmlType + " is not represented in ILGen as " + value.GetType().Name); + Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), $"Xml type {xmlType} is not represented in ILGen as {value.GetType().Name}"); return value; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs index a0f45514476..e5b4d1a655b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs @@ -296,7 +296,7 @@ namespace System.Xml.Xsl.Runtime } break; default: - Debug.Assert(CharUtil.IsDecimalDigitOne(startChar), "Unexpected startChar: " + startChar); + Debug.Assert(CharUtil.IsDecimalDigitOne(startChar), $"Unexpected startChar: {startChar}"); zero = (char)(startChar - 1); break; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltConvert.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltConvert.cs index 26b902273ee..a3d392d571c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltConvert.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltConvert.cs @@ -82,7 +82,7 @@ namespace System.Xml.Xsl.Runtime } else { - Debug.Assert(itemType == BooleanType, "Unexpected type of atomic sequence " + itemType.ToString()); + Debug.Assert(itemType == BooleanType, $"Unexpected type of atomic sequence {itemType}"); return item.ValueAsBoolean; } } @@ -126,7 +126,7 @@ namespace System.Xml.Xsl.Runtime } else { - Debug.Assert(itemType == BooleanType, "Unexpected type of atomic sequence " + itemType.ToString()); + Debug.Assert(itemType == BooleanType, $"Unexpected type of atomic sequence {itemType}"); return item.ValueAsBoolean ? 1d : 0d; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs index 2d45c788d2f..c227f2cf9d3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs @@ -340,7 +340,7 @@ namespace System.Xml.Xsl.Runtime } else { - Debug.Assert(itemType == XsltConvert.BooleanType, "Unexpected type of atomic value " + itemType.ToString()); + Debug.Assert(itemType == XsltConvert.BooleanType, $"Unexpected type of atomic value {itemType}"); return item.ValueAsBoolean ? 1d : 0d; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs index 31c779d6abd..c6b516c7fdb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs @@ -353,7 +353,7 @@ namespace System.Xml.Xsl.Runtime } else { - Debug.Assert(itemType == XsltConvert.BooleanType, "Unexpected type of atomic value " + itemType.ToString()); + Debug.Assert(itemType == XsltConvert.BooleanType, $"Unexpected type of atomic value {itemType}"); return TypeCode.Boolean; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathQilFactory.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathQilFactory.cs index 879e8d997c7..d27a7d3470e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathQilFactory.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathQilFactory.cs @@ -168,7 +168,7 @@ namespace System.Xml.Xsl.XPath [Conditional("DEBUG")] private void ExpectAny(QilNode n) { - Debug.Assert(IsAnyType(n), "Unexpected expression type: " + n.XmlType!.ToString()); + Debug.Assert(IsAnyType(n), $"Unexpected expression type: {n.XmlType}"); } public QilNode ConvertToType(XmlTypeCode requiredType, QilNode n) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs index 0df9d3b3a9d..327bd70c9f6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs @@ -569,7 +569,7 @@ namespace System.Xml.Xsl.Xslt default : nodeType = null ; break; } - Debug.Assert(nodeType != null, "Unexpected nodeKind: " + nodeKind); + Debug.Assert(nodeType != null, $"Unexpected nodeKind: {nodeKind}"); QilNode typeNameCheck = f.IsType(it, nodeType); if (qname != null) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs index e8e8b9be0f3..d923e086c9b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs @@ -35,7 +35,7 @@ namespace System.Xml.Xsl.Xslt Debug.Assert(IsDebug, "QName is reserved as the marker for missing values"); break; default: - Debug.Assert(xt.IsNode, "Unexpected expression type: " + xt.ToString()); + Debug.Assert(xt.IsNode, $"Unexpected expression type: {xt}"); break; } } diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs index 54d1fd9a7f8..568072314f3 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs @@ -28,14 +28,14 @@ namespace System.Xml.Tests public static MethodInfo GetInstanceMethod(Type type, string methName) { MethodInfo methInfo = type.GetMethod(methName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - Debug.Assert(methInfo != null, "Instance method " + type.Name + "." + methName + " not found"); + Debug.Assert(methInfo != null, $"Instance method {type.Name}.{methName} not found"); return methInfo; } public static MethodInfo GetStaticMethod(Type type, string methName) { MethodInfo methInfo = type.GetMethod(methName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - Debug.Assert(methInfo != null, "Static method " + type.Name + "." + methName + " not found"); + Debug.Assert(methInfo != null, $"Static method {type.Name}.{methName} not found"); return methInfo; } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/StringHeap.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/StringHeap.cs index ea0e38e3cde..c2b0151b9ff 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/StringHeap.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/StringHeap.cs @@ -113,7 +113,7 @@ namespace System.Reflection.Metadata.Ecma335 { for (int i = 0; i < s_virtualValues!.Length; i++) { - Debug.Assert(s_virtualValues[i] != null, "Missing virtual value for StringHandle.VirtualIndex." + (StringHandle.VirtualIndex)i); + Debug.Assert(s_virtualValues[i] != null, $"Missing virtual value for StringHandle.VirtualIndex.{(StringHandle.VirtualIndex)i}"); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.cs index ac71eddcbaa..5b6635cabfa 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.cs @@ -123,7 +123,7 @@ namespace System.Reflection.Tests for (int i = 0; i < bytes.Length; i++) { - builder.Append(bytes[i].ToString("X2")); + builder.Append($"{bytes[i]:X2}"); } return builder.ToString(); diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs index 4b6df845ef5..73ed9f30d3d 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs @@ -717,7 +717,7 @@ namespace System.Runtime.Caching if (_cEntriesInUse == 0) return 0; - Debug.Assert(maxFlush > 0, "maxFlush is not greater than 0, instead is " + maxFlush); + Debug.Assert(maxFlush > 0, $"maxFlush is not greater than 0, instead is {maxFlush}"); Debug.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); UsageEntryRef inFlushHead = UsageEntryRef.INVALID; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs index c80daaac7a8..9d3cb73564b 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs @@ -38,7 +38,7 @@ namespace System.Runtime.Caching DateTimeOffset lastWrite; long fileSize; s_fcn.StartMonitoring(path, new OnChangedCallback(OnChanged), out _fcnState, out lastWrite, out fileSize); - uniqueId = path + lastWrite.UtcDateTime.Ticks.ToString("X", CultureInfo.InvariantCulture) + fileSize.ToString("X", CultureInfo.InvariantCulture); + uniqueId = $"{path}{lastWrite.UtcDateTime.Ticks:X}{fileSize:X}"; _lastModified = lastWrite; } else diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs index b04afc5149e..05d3aeca4b1 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs @@ -39,7 +39,7 @@ namespace System.Runtime.Caching MemoryCacheEntry entry = cache.GetEntry(k); DateTime utcCreated = s_DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); - uniqueId = k + utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture); + uniqueId = $"{k}{utcCreated.Ticks:X}"; _lastModified = utcCreated; } else @@ -56,7 +56,7 @@ namespace System.Runtime.Caching DateTime utcCreated = s_DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); sb.Append(key); - sb.Append(utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture)); + sb.Append($"{utcCreated.Ticks:X}"); if (utcCreated > _lastModified) { _lastModified = utcCreated; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs index 9d4ba7c85cd..901754ba815 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs @@ -91,11 +91,7 @@ namespace System.Runtime.Caching } #if PERF - Debug.WriteLine(string.Format("PhysicalMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}, secondsSinceTrim={2:N}{3}", - percent, - lastTrimPercent, - ticksSinceTrim/TimeSpan.TicksPerSecond, - Environment.NewLine)); + Debug.WriteLine($"PhysicalMemoryMonitor.GetPercentToTrim: percent={percent:N}, lastTrimPercent={lastTrimPercent:N}, secondsSinceTrim={ticksSinceTrim/TimeSpan.TicksPerSecond:N}{Environment.NewLine}"); #endif } @@ -111,8 +107,7 @@ namespace System.Runtime.Caching } _pressureHigh = Math.Max(3, physicalMemoryLimitPercentage); _pressureLow = Math.Max(1, _pressureHigh - 9); - Dbg.Trace("MemoryCacheStats", "PhysicalMemoryMonitor.SetLimit: _pressureHigh=" + _pressureHigh + - ", _pressureLow=" + _pressureLow); + Dbg.Trace($"MemoryCacheStats", "PhysicalMemoryMonitor.SetLimit: _pressureHigh={_pressureHigh}, _pressureLow={_pressureLow}"); } } } diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 488cda85c35..da7a83aab1c 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -25,7 +25,7 @@ namespace System.Tests for (int i = 0; i < values.Length; i++) { values[i] = (byte)i; - sb.Append(i.ToString("X2")); + sb.Append($"{i:X2}"); } TestSequence(values, sb.ToString()); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToHexString.cs index 3b0986a46e4..b92685667dd 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToHexString.cs @@ -24,7 +24,7 @@ namespace System.Tests for (int i = 0; i < values.Length; i++) { values[i] = (byte)i; - sb.Append(i.ToString("X2")); + sb.Append($"{i:X2}"); } Assert.Equal(sb.ToString(), Convert.ToHexString(values)); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs index fbe995267af..44f4bf6d282 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs @@ -1949,7 +1949,7 @@ namespace System.Tests public static void BigMul128_Unsigned(ulong a, ulong b, string result) { ulong high = Math.BigMul(a, b, out ulong low); - Assert.Equal(result, high.ToString("X16") + low.ToString("X16")); + Assert.Equal(result, $"{high:X16}{low:X16}"); } [Theory] @@ -1968,7 +1968,7 @@ namespace System.Tests public static void BigMul128_Signed(long a, long b, string result) { long high = Math.BigMul(a, b, out long low); - Assert.Equal(result, high.ToString("X16") + low.ToString("X16")); + Assert.Equal(result, $"{high:X16}{low:X16}"); } [Theory] diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj index 2ee0caf2d56..1e04c274aa8 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System.Runtime.InteropServices.RuntimeInformation.csproj @@ -40,6 +40,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.Windows.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.Windows.cs index 0177eec2317..2cd38459dc7 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.Windows.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.Windows.cs @@ -21,10 +21,11 @@ namespace System.Runtime.InteropServices OperatingSystem os = Environment.OSVersion; Version v = os.Version; + Span stackBuffer = stackalloc char[256]; const string Version = "Microsoft Windows"; s_osDescription = osDescription = string.IsNullOrEmpty(os.ServicePack) ? - $"{Version} {(uint)v.Major}.{(uint)v.Minor}.{(uint)v.Build}" : - $"{Version} {(uint)v.Major}.{(uint)v.Minor}.{(uint)v.Build} {os.ServicePack}"; + string.Create(null, stackBuffer, $"{Version} {(uint)v.Major}.{(uint)v.Minor}.{(uint)v.Build}") : + string.Create(null, stackBuffer, $"{Version} {(uint)v.Major}.{(uint)v.Minor}.{(uint)v.Build} {os.ServicePack}"); } return osDescription; diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs index df0fb53273d..6e892ce5204 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs @@ -17,19 +17,16 @@ namespace System.Runtime.InteropServices { if (s_frameworkDescription == null) { - string? versionString = typeof(object).Assembly.GetCustomAttribute()?.InformationalVersion; + ReadOnlySpan versionString = typeof(object).Assembly.GetCustomAttribute()?.InformationalVersion; // Strip the git hash if there is one - if (versionString != null) + int plusIndex = versionString.IndexOf('+'); + if (plusIndex != -1) { - int plusIndex = versionString.IndexOf('+'); - if (plusIndex != -1) - { - versionString = versionString.Substring(0, plusIndex); - } + versionString = versionString.Slice(0, plusIndex); } - s_frameworkDescription = !string.IsNullOrWhiteSpace(versionString) ? $"{FrameworkName} {versionString}" : FrameworkName; + s_frameworkDescription = !versionString.Trim().IsEmpty ? $"{FrameworkName} {versionString}" : FrameworkName; } return s_frameworkDescription; diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs index a4fe32e9c02..d8a6bc398f5 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs @@ -61,7 +61,7 @@ namespace System.Runtime.InteropServices.Tests [FieldOffset(0)] public TypeUnion m_Variant; [FieldOffset(0)] public decimal m_decimal; - public override string ToString() => "0x" + m_Variant.vt.ToString("X"); + public override string ToString() => $"0x{m_Variant.vt:X}"; } // Taken from wtypes.h diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs index 060200698b2..2bbc23476c5 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs @@ -1676,11 +1676,11 @@ namespace System.Numerics.Tests { if (upper) { - output = output + chars[start].ToString("X"); + output = $"{output}{chars[start]:X}"; } else { - output = output + chars[start].ToString("x"); + output = $"{output}{chars[start]:x}"; } } diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs index de485ec10b5..a7cc9748bab 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs @@ -2387,7 +2387,7 @@ public class Family sb.AppendLine("Family members:"); foreach (var member in this.Members) { - sb.AppendLine(" " + member); + sb.AppendLine($" {member}"); } return sb.ToString(); @@ -2404,7 +2404,7 @@ public class FamilyForStress sb.AppendLine("Family members:"); foreach (var member in this.Members) { - sb.AppendLine(" " + member); + sb.AppendLine($" {member}"); } return sb.ToString(); diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.Unix.cs b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.Unix.cs index 63862ea65b4..cfef4e4d096 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.Unix.cs @@ -30,7 +30,7 @@ namespace Internal.Cryptography case -1: /* OpenSSL internal error */ throw Interop.Crypto.CreateOpenSslCryptographicException(); default: - Debug.Assert(result == 0, "LookupFriendlyNameByOid returned unexpected result " + result); + Debug.Assert(result == 0, $"LookupFriendlyNameByOid returned unexpected result {result}"); // The lookup may have left errors in this case, clean up for precaution. Interop.Crypto.ErrClearError(); diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs index ed06c365672..761fc37439a 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs @@ -25,7 +25,7 @@ namespace System.Security.Cryptography.Xml public void Write(StringBuilder strBuilder, DocPosition docPos, AncestralNamespaceContextManager anc) { - strBuilder.Append(" " + Name + "=\""); + strBuilder.Append($" {Name}=\""); strBuilder.Append(Utils.EscapeAttributeValue(Value)); strBuilder.Append('"'); } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs index fb1f9c599cf..7d3174b0780 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs @@ -91,7 +91,7 @@ namespace System.Security.Cryptography.Xml if (IsInNodeSet) { - strBuilder.Append(""); + strBuilder.Append($""); } } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs index 2bc2502eb9f..de71302a1c0 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs @@ -33,7 +33,7 @@ namespace System.Security.Cryptography.Xml strBuilder.Append(" 0)) - strBuilder.Append(" " + Value); + strBuilder.Append(' ').Append(Value); strBuilder.Append("?>"); if (docPos == DocPosition.BeforeRootElement) strBuilder.Append((char)10); diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs index 5f7a5090097..e70615426c2 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs @@ -249,7 +249,7 @@ namespace System.Security.Cryptography.Xml { Debug.Assert(o != null, "o != null"); - return $"{o.GetType().Name}#{o.GetHashCode().ToString("x8", CultureInfo.InvariantCulture)}"; + return $"{o.GetType().Name}#{o.GetHashCode():x8}"; } /// diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/XmlDsigXsltTransformTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/XmlDsigXsltTransformTest.cs index 906e9676165..1fa91a3c912 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/XmlDsigXsltTransformTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/XmlDsigXsltTransformTest.cs @@ -111,7 +111,7 @@ namespace System.Security.Cryptography.Xml.Tests int b = s.ReadByte(); while (b != -1) { - sb.Append(b.ToString("X2")); + sb.Append($"{b:X2}"); b = s.ReadByte(); } return sb.ToString(); diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs index 7b20e996ad6..eacc8ca6861 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs @@ -14,7 +14,7 @@ namespace System.ServiceModel.Syndication IXmlLineInfo lineInfo = reader as IXmlLineInfo; if (lineInfo != null && lineInfo.HasLineInfo()) { - error = string.Format(CultureInfo.InvariantCulture, "{0} {1}", SR.Format(SR.ErrorInLine, lineInfo.LineNumber, lineInfo.LinePosition), SR.Format(error)); + error = $"{SR.Format(SR.ErrorInLine, lineInfo.LineNumber, lineInfo.LinePosition)} {error}"; } return error; } diff --git a/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs b/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs index f06e87cda95..be087593f9b 100644 --- a/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs +++ b/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs @@ -1612,7 +1612,7 @@ namespace System.ServiceModel.Syndication.Tests w.WriteString(Value); break; default: - Debug.Assert(false, "Wrong type for text-like node : " + _nodetype.ToString()); + Debug.Assert(false, $"Wrong type for text-like node : {_nodetype}"); break; } } diff --git a/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs b/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs index 446d744cae0..ebcde98e50a 100644 --- a/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs +++ b/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs @@ -1166,7 +1166,7 @@ namespace System.Speech.Recognition private string DisplayDebugInfo() { - return string.Format("'rule={0}", _rule); + return $"'rule={_rule}"; } internal Grammar _grammar; internal string _rule; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs index 01208d21183..ab66bca82c3 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs @@ -337,7 +337,7 @@ namespace System.Text if (pCodePageIndex->CodePage == codePage) { Debug.Assert(pCodePageIndex->ByteCount == 1 || pCodePageIndex->ByteCount == 2, - "[BaseCodePageEncoding] Code page (" + codePage + ") has invalid byte size (" + pCodePageIndex->ByteCount + ") in table"); + $"[BaseCodePageEncoding] Code page ({codePage}) has invalid byte size ({pCodePageIndex->ByteCount}) in table"); // Return what it says for byte count return pCodePageIndex->ByteCount; } diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs index 531f2687624..5255ed67b97 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs @@ -399,7 +399,7 @@ namespace System.Text if (bOutOfOrder) { Debug.Assert((arrayTemp.Length / 2) < 20, - "[DBCSCodePageEncoding.ReadBestFitTable]Expected small best fit table < 20 for code page " + CodePage + ", not " + arrayTemp.Length / 2); + $"[DBCSCodePageEncoding.ReadBestFitTable]Expected small best fit table < 20 for code page {CodePage}, not {arrayTemp.Length / 2}"); for (int i = 0; i < arrayTemp.Length - 2; i += 2) { diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs index 0d9bed68345..e232be86168 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs @@ -84,7 +84,7 @@ namespace System.Text // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. // Shouldn't be able to get here for all of our code pages, table would have to be messed up. - Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, $"[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char {(int)_cBestFit:X4} caused recursive fallback"); _iCount = _iSize = 1; _cBestFit = TryBestFit(charUnknown); @@ -106,7 +106,7 @@ namespace System.Text // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. 0 is processing last character, < 0 is not falling back // Shouldn't be able to get here, table would have to be messed up. - Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, $"[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char {(int)_cBestFit:X4} caused recursive fallback"); // Go ahead and get our fallback, surrogates don't have best fit _cBestFit = '?'; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs index 2f4042f334c..7a27288fd4e 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs @@ -188,7 +188,7 @@ namespace System.Text // We should've read in GBLast4ByteCode 4 byte sequences Debug.Assert(count4Byte == GBLast4ByteCode + 1, - "[GB18030Encoding.LoadManagedCodePage] Expected 0x99FB to be last 4 byte offset, found 0x" + count4Byte.ToString("X4", CultureInfo.InvariantCulture)); + $"[GB18030Encoding.LoadManagedCodePage] Expected 0x99FB to be last 4 byte offset, found 0x{count4Byte:X4}"); } // Is4Byte @@ -242,7 +242,7 @@ namespace System.Text if (charLeftOver != 0) { Debug.Assert(char.IsHighSurrogate(charLeftOver), - "[GB18030Encoding.GetBytes] leftover character should be high surrogate, not 0x" + ((int)charLeftOver).ToString("X4", CultureInfo.InvariantCulture)); + $"[GB18030Encoding.GetBytes] leftover character should be high surrogate, not 0x{(int)charLeftOver:X4}"); // If our next char isn't a low surrogate, then we need to do fallback. if (!char.IsLowSurrogate(ch)) @@ -272,7 +272,7 @@ namespace System.Text byte byte2 = (byte)((offset % 0x0a) + 0x30); offset /= 0x0a; Debug.Assert(offset < 0x6f, - "[GB18030Encoding.GetBytes](1) Expected offset < 0x6f, not 0x" + offset.ToString("X2", CultureInfo.InvariantCulture)); + $"[GB18030Encoding.GetBytes](1) Expected offset < 0x6f, not 0x{offset:X2}"); charLeftOver = (char)0; if (!buffer.AddByte((byte)(offset + 0x90), byte2, byte3, byte4)) @@ -322,7 +322,7 @@ namespace System.Text byte byte2 = (byte)((iBytes % 0x0a) + 0x30); iBytes /= 0x0a; Debug.Assert(iBytes < 0x7e, - "[GB18030Encoding.GetBytes]Expected iBytes < 0x7e, not 0x" + iBytes.ToString("X2", CultureInfo.InvariantCulture)); + $"[GB18030Encoding.GetBytes]Expected iBytes < 0x7e, not 0x{iBytes:X2}"); if (!buffer.AddByte((byte)(iBytes + 0x81), byte2, byte3, byte4)) break; } diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs index e630706877a..204c45dbd39 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs @@ -58,7 +58,7 @@ namespace System.Text // Legal windows code pages are between Devanagari and Punjabi Debug.Assert(_defaultCodePage >= CodeDevanagari && _defaultCodePage <= CodePunjabi, - "[ISCIIEncoding] Code page (" + codePage + " isn't supported by ISCIIEncoding!"); + $"[ISCIIEncoding] Code page ({codePage} isn't supported by ISCIIEncoding!"); // This shouldn't really be possible if (_defaultCodePage < CodeDevanagari || _defaultCodePage > CodePunjabi) @@ -237,7 +237,7 @@ namespace System.Text // We only know how to map from Unicode to pages from Devanagari to Punjabi (2 to 11) Debug.Assert(currentCodePage >= CodeDevanagari && currentCodePage <= CodePunjabi, - "[ISCIIEncoding.GetBytes]Code page (" + currentCodePage + " shouldn't appear in ISCII from Unicode table!"); + $"[ISCIIEncoding.GetBytes]Code page ({currentCodePage} shouldn't appear in ISCII from Unicode table!"); } // Safe to add our byte now @@ -252,7 +252,7 @@ namespace System.Text { // This one needs another byte Debug.Assert((indicTwoBytes >> 12) > 0 && (indicTwoBytes >> 12) <= 3, - "[ISCIIEncoding.GetBytes]Expected indicTwoBytes from 1-3, not " + (indicTwoBytes >> 12)); + $"[ISCIIEncoding.GetBytes]Expected indicTwoBytes from 1-3, not {(indicTwoBytes >> 12)}"); // Already did buffer checking, but... if (!buffer.AddByte(s_SecondIndicByte[indicTwoBytes >> 12])) @@ -350,7 +350,7 @@ namespace System.Text // Get our current code page index (some code pages are dups) int currentCodePageIndex = -1; Debug.Assert(currentCodePage >= CodeDevanagari && currentCodePage <= CodePunjabi, - "[ISCIIEncoding.GetChars]Decoder code page must be >= Devanagari and <= Punjabi, not " + currentCodePage); + $"[ISCIIEncoding.GetChars]Decoder code page must be >= Devanagari and <= Punjabi, not {currentCodePage}"); if (currentCodePage >= CodeDevanagari && currentCodePage <= CodePunjabi) { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs index dbfa9a5cdbd..e76e3ea160a 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs @@ -403,9 +403,9 @@ namespace System.Text.RegularExpressions { var sb = new StringBuilder(); - sb.AppendLine("Direction: " + (RightToLeft ? "right-to-left" : "left-to-right")); - sb.AppendLine("Anchor: " + RegexPrefixAnalyzer.AnchorDescription(LeadingAnchor)); - sb.AppendLine(""); + sb.AppendLine($"Direction: {(RightToLeft ? "right-to-left" : "left-to-right")}"); + sb.AppendLine($"Anchor: {RegexPrefixAnalyzer.AnchorDescription(LeadingAnchor)}"); + sb.AppendLine(); if (BoyerMoorePrefix != null) { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs index 52bd5b89ec2..4c8fe4f9190 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs @@ -47,7 +47,7 @@ namespace System.Text.RegularExpressions _hasTimeout = hasTimeout; // Pick a unique number for the methods we generate. - object regexNum = (uint)Interlocked.Increment(ref s_regexCount); // object to box once instead of twice below + uint regexNum = (uint)Interlocked.Increment(ref s_regexCount); // Get a description of the regex to use in the name. This is helpful when profiling, and is opt-in. string description = string.Empty; diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexCharacterSetTests.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexCharacterSetTests.cs index b11071d595e..a029d679c8b 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/RegexCharacterSetTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/RegexCharacterSetTests.cs @@ -82,7 +82,7 @@ namespace System.Text.RegularExpressions.Tests [InlineData('\u0100')] public void SingleExpected(char c) { - string s = @"\u" + ((int)c).ToString("X4"); + string s = $@"\u{(int)c:X4}"; var set = new HashSet() { c }; // One diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs index 0be2514b344..5da12d24196 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionsEtwProvider.cs @@ -1168,16 +1168,14 @@ namespace System.Transactions { // GUID with dash if (str.Length >= 36) { - string str_part1 = str.Substring(0, 36); - Guid.TryParse(str_part1, out guid); + Guid.TryParse(str.AsSpan(0, 36), out guid); } } else { if (str.Length >= 32) { - string str_part1 = str.Substring(0, 32); - Guid.TryParse(str_part1, out guid); + Guid.TryParse(str.AsSpan(0, 32), out guid); } } SetCurrentThreadActivityId(guid); diff --git a/src/libraries/System.ValueTuple/tests/ExtensionsTests.cs b/src/libraries/System.ValueTuple/tests/ExtensionsTests.cs index a4db2b40b5e..758f7d8da62 100644 --- a/src/libraries/System.ValueTuple/tests/ExtensionsTests.cs +++ b/src/libraries/System.ValueTuple/tests/ExtensionsTests.cs @@ -674,26 +674,26 @@ namespace System.Tests { var builder = new StringBuilder(); if (x1 > 0) { builder.Append(x1); } - if (x2 > 0) { builder.Append(" " + x2); } - if (x3 > 0) { builder.Append(" " + x3); } - if (x4 > 0) { builder.Append(" " + x4); } - if (x5 > 0) { builder.Append(" " + x5); } - if (x6 > 0) { builder.Append(" " + x6); } - if (x7 > 0) { builder.Append(" " + x7); } - if (x8 > 0) { builder.Append(" " + x8); } - if (x9 > 0) { builder.Append(" " + x9); } - if (x10 > 0) { builder.Append(" " + x10); } - if (x11 > 0) { builder.Append(" " + x11); } - if (x12 > 0) { builder.Append(" " + x12); } - if (x13 > 0) { builder.Append(" " + x13); } - if (x14 > 0) { builder.Append(" " + x14); } - if (x15 > 0) { builder.Append(" " + x15); } - if (x16 > 0) { builder.Append(" " + x16); } - if (x17 > 0) { builder.Append(" " + x17); } - if (x18 > 0) { builder.Append(" " + x18); } - if (x19 > 0) { builder.Append(" " + x19); } - if (x20 > 0) { builder.Append(" " + x20); } - if (x21 > 0) { builder.Append(" " + x21); } + if (x2 > 0) { builder.Append($" {x2}"); } + if (x3 > 0) { builder.Append($" {x3}"); } + if (x4 > 0) { builder.Append($" {x4}"); } + if (x5 > 0) { builder.Append($" {x5}"); } + if (x6 > 0) { builder.Append($" {x6}"); } + if (x7 > 0) { builder.Append($" {x7}"); } + if (x8 > 0) { builder.Append($" {x8}"); } + if (x9 > 0) { builder.Append($" {x9}"); } + if (x10 > 0) { builder.Append($" {x10}"); } + if (x11 > 0) { builder.Append($" {x11}"); } + if (x12 > 0) { builder.Append($" {x12}"); } + if (x13 > 0) { builder.Append($" {x13}"); } + if (x14 > 0) { builder.Append($" {x14}"); } + if (x15 > 0) { builder.Append($" {x15}"); } + if (x16 > 0) { builder.Append($" {x16}"); } + if (x17 > 0) { builder.Append($" {x17}"); } + if (x18 > 0) { builder.Append($" {x18}"); } + if (x19 > 0) { builder.Append($" {x19}"); } + if (x20 > 0) { builder.Append($" {x20}"); } + if (x21 > 0) { builder.Append($" {x21}"); } return builder.ToString(); } diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs index b6b46d1fda9..d991cc61d3f 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs @@ -66,11 +66,11 @@ namespace System.Web { if (string.IsNullOrEmpty(keys[i])) { - sb.AppendFormat("{0}&", UrlEncode(value)); + sb.Append(UrlEncode(value)).Append('&'); } else { - sb.AppendFormat("{0}={1}&", keys[i], UrlEncode(value)); + sb.AppendFormat($"{keys[i]}={UrlEncode(value)}&"); } } } diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs index 868418b2292..658665d3c66 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs @@ -14,8 +14,7 @@ namespace System.Web.Util { private static void AppendCharAsUnicodeJavaScript(StringBuilder builder, char c) { - builder.Append("\\u"); - builder.Append(((int)c).ToString("x4", CultureInfo.InvariantCulture)); + builder.Append($"\\u{(int)c:x4}"); } private static bool CharRequiresJavaScriptEncoding(char c) => diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index 9c0af486b64..b79387da224 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -222,7 +222,7 @@ internal class Xcode if (!string.IsNullOrEmpty(DiagnosticPorts)) { - defines.AppendLine("\nadd_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")"); + defines.AppendLine($"\nadd_definitions(-DDIAGNOSTIC_PORTS=\"{DiagnosticPorts}\")"); } cmakeLists = cmakeLists.Replace("%Defines%", defines.ToString()); @@ -272,7 +272,7 @@ internal class Xcode .Append("-S.") .Append(" -B").Append(projectName) .Append(" -GXcode") - .Append(" -DCMAKE_SYSTEM_NAME=" + targetName) + .Append(" -DCMAKE_SYSTEM_NAME=").Append(targetName) .Append(deployTarget); File.WriteAllText(Path.Combine(binDir, "runtime.h"), @@ -334,30 +334,30 @@ internal class Xcode case TargetNames.iOS: sdk = "iphoneos"; args.Append(" -arch arm64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; case TargetNames.iOSsim: sdk = "iphonesimulator"; args.Append(" -arch arm64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; case TargetNames.tvOS: sdk = "appletvos"; args.Append(" -arch arm64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; case TargetNames.tvOSsim: sdk = "appletvsimulator"; args.Append(" -arch arm64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; default: sdk = "maccatalyst"; - args.Append(" -scheme \"" + Path.GetFileNameWithoutExtension(xcodePrjPath) + "\"") + args.Append(" -scheme \"").Append(Path.GetFileNameWithoutExtension(xcodePrjPath)).Append('"') .Append(" -destination \"generic/platform=macOS,name=Any Mac,variant=Mac Catalyst\"") .Append(" -UseModernBuildSystem=YES") - .Append(" -archivePath \"" + Path.GetDirectoryName(xcodePrjPath) + "\"") - .Append(" -derivedDataPath \"" + Path.GetDirectoryName(xcodePrjPath) + "\"") + .Append(" -archivePath \"").Append(Path.GetDirectoryName(xcodePrjPath)).Append('"') + .Append(" -derivedDataPath \"").Append(Path.GetDirectoryName(xcodePrjPath)).Append('"') .Append(" IPHONEOS_DEPLOYMENT_TARGET=14.2"); break; } @@ -369,20 +369,20 @@ internal class Xcode case TargetNames.iOSsim: sdk = "iphonesimulator"; args.Append(" -arch x86_64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; case TargetNames.tvOSsim: sdk = "appletvsimulator"; args.Append(" -arch x86_64") - .Append(" -sdk " + sdk); + .Append(" -sdk ").Append(sdk); break; default: sdk = "maccatalyst"; - args.Append(" -scheme \"" + Path.GetFileNameWithoutExtension(xcodePrjPath) + "\"") + args.Append(" -scheme \"").Append(Path.GetFileNameWithoutExtension(xcodePrjPath)).Append('"') .Append(" -destination \"generic/platform=macOS,name=Any Mac,variant=Mac Catalyst\"") .Append(" -UseModernBuildSystem=YES") - .Append(" -archivePath \"" + Path.GetDirectoryName(xcodePrjPath) + "\"") - .Append(" -derivedDataPath \"" + Path.GetDirectoryName(xcodePrjPath) + "\"") + .Append(" -archivePath \"").Append(Path.GetDirectoryName(xcodePrjPath)).Append('"') + .Append(" -derivedDataPath \"").Append(Path.GetDirectoryName(xcodePrjPath)).Append('"') .Append(" IPHONEOS_DEPLOYMENT_TARGET=13.5"); break; } diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 603fbcf1700..bdd84c295ee 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -209,7 +209,7 @@ public class PInvokeTableGenerator : Task // The signature of the interp entry function // This is a gsharedvt_in signature sb.Append("typedef void "); - sb.Append(" (*WasmInterpEntrySig_" + cb_index + ") ("); + sb.Append($" (*WasmInterpEntrySig_{cb_index}) ("); int pindex = 0; if (method.ReturnType.Name != "Void") { sb.Append("int"); @@ -247,13 +247,13 @@ public class PInvokeTableGenerator : Task if (pindex > 0) sb.Append(','); sb.Append(MapType(method.GetParameters()[pindex].ParameterType)); - sb.Append(" arg" + pindex); + sb.Append($" arg{pindex}"); pindex++; } sb.Append(") { \n"); if (!is_void) sb.Append(MapType(method.ReturnType) + " res;\n"); - sb.Append("((WasmInterpEntrySig_" + cb_index + ")wasm_native_to_interp_ftndescs [" + cb_index + "].func) ("); + sb.Append($"((WasmInterpEntrySig_{cb_index})wasm_native_to_interp_ftndescs [{cb_index}].func) ("); pindex = 0; if (!is_void) { sb.Append("&res"); @@ -263,7 +263,7 @@ public class PInvokeTableGenerator : Task foreach (var p in method.GetParameters()) { if (pindex > 0) sb.Append(", "); - sb.Append("&arg" + aindex); + sb.Append($"&arg{aindex}"); pindex++; aindex++; } From c6acf8dbeff5e2c54b055f496cb8dafc5d0b3e20 Mon Sep 17 00:00:00 2001 From: Dzmitry Konanka <85848222+DzmitryKN@users.noreply.github.com> Date: Tue, 13 Jul 2021 18:02:43 +0300 Subject: [PATCH 484/926] optimize adding unwind info entries by hashtable (#55398) --- src/mono/mono/mini/unwind.c | 101 ++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/src/mono/mono/mini/unwind.c b/src/mono/mono/mini/unwind.c index bcf9bd4682e..bdcfe78604b 100644 --- a/src/mono/mono/mini/unwind.c +++ b/src/mono/mono/mini/unwind.c @@ -30,14 +30,15 @@ typedef struct { typedef struct { guint32 len; - guint8 info [MONO_ZERO_LEN_ARRAY]; + guint8 *info; } MonoUnwindInfo; static mono_mutex_t unwind_mutex; -static MonoUnwindInfo **cached_info; +static MonoUnwindInfo *cached_info; static int cached_info_next, cached_info_size; static GSList *cached_info_list; +static GHashTable *cached_info_ht; /* Statistics */ static int unwind_info_size; @@ -729,6 +730,34 @@ mono_unwind_init (void) mono_counters_register ("Unwind info size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &unwind_info_size); } +static guint +cached_info_hash(gconstpointer key) +{ + guint i, a; + const guint8 *info = cached_info [GPOINTER_TO_UINT (key)].info; + const guint len = cached_info [GPOINTER_TO_UINT (key)].len; + + for (i = a = 0; i != len; ++i) + a ^= (((guint)info [i]) << (i & 0xf)); + + return a; +} + +static gboolean +cached_info_eq(gconstpointer a, gconstpointer b) +{ + const guint32 lena = cached_info [GPOINTER_TO_UINT (a)].len; + const guint32 lenb = cached_info [GPOINTER_TO_UINT (b)].len; + if (lena == lenb) { + const guint8 *infoa = cached_info [GPOINTER_TO_UINT (a)].info; + const guint8 *infob = cached_info [GPOINTER_TO_UINT (b)].info; + if (memcmp (infoa, infob, lena) == 0) + return TRUE; + } + + return FALSE; +} + /* * mono_cache_unwind_info * @@ -742,42 +771,31 @@ mono_unwind_init (void) guint32 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) { - int i; - MonoUnwindInfo *info; - + gpointer orig_key; + guint32 i; unwind_lock (); - if (cached_info == NULL) { - cached_info_size = 16; - cached_info = g_new0 (MonoUnwindInfo*, cached_info_size); - } + if (!cached_info_ht) + cached_info_ht = g_hash_table_new (cached_info_hash, cached_info_eq); - for (i = 0; i < cached_info_next; ++i) { - MonoUnwindInfo *cached = cached_info [i]; - - if (cached->len == unwind_info_len && memcmp (cached->info, unwind_info, unwind_info_len) == 0) { - unwind_unlock (); - return i; - } - } - - info = (MonoUnwindInfo *)g_malloc (sizeof (MonoUnwindInfo) + unwind_info_len); - info->len = unwind_info_len; - memcpy (&info->info, unwind_info, unwind_info_len); - - i = cached_info_next; - if (cached_info_next >= cached_info_size) { - MonoUnwindInfo **new_table; + MonoUnwindInfo *new_table; + int new_cached_info_size = cached_info_size ? cached_info_size * 2 : 16; + + /* ensure no integer overflow */ + g_assert (new_cached_info_size > cached_info_size); /* * Avoid freeing the old table so mono_get_cached_unwind_info () * doesn't need locks/hazard pointers. */ + new_table = g_new0 (MonoUnwindInfo, new_cached_info_size ); - new_table = g_new0 (MonoUnwindInfo*, cached_info_size * 2); + /* include array allocations into statistics of memory totally consumed by unwind info */ + unwind_info_size += sizeof (MonoUnwindInfo) * new_cached_info_size ; - memcpy (new_table, cached_info, cached_info_size * sizeof (MonoUnwindInfo*)); + if (cached_info_size) + memcpy (new_table, cached_info, sizeof (MonoUnwindInfo) * cached_info_size); mono_memory_barrier (); @@ -785,14 +803,32 @@ mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) cached_info = new_table; - cached_info_size *= 2; + cached_info_size = new_cached_info_size; } - cached_info [cached_info_next ++] = info; + i = cached_info_next; - unwind_info_size += sizeof (MonoUnwindInfo) + unwind_info_len; + /* construct temporary element at array's edge without allocated info copy - it will be used for hashtable lookup */ + cached_info [i].len = unwind_info_len; + cached_info [i].info = unwind_info; + + if (!g_hash_table_lookup_extended (cached_info_ht, GUINT_TO_POINTER (i), &orig_key, NULL) ) { + /* hashtable lookup didnt find match - now need to really add new element with allocated copy of unwind info */ + cached_info [i].info = g_new (guint8, unwind_info_len); + memcpy (cached_info [i].info, unwind_info, unwind_info_len); + + /* include allocated memory in stats, note that hashtable allocates struct of 3 pointers per each entry */ + unwind_info_size += sizeof (void *) * 3 + unwind_info_len; + g_hash_table_insert_replace (cached_info_ht, GUINT_TO_POINTER (i), NULL, TRUE); + + cached_info_next = i + 1; + + } else { + i = GPOINTER_TO_UINT (orig_key); + } unwind_unlock (); + return i; } @@ -802,7 +838,6 @@ mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) guint8* mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) { - MonoUnwindInfo **table; MonoUnwindInfo *info; guint8 *data; @@ -810,9 +845,7 @@ mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) * This doesn't need any locks/hazard pointers, * since new tables are copies of the old ones. */ - table = cached_info; - - info = table [index]; + info = &cached_info [index]; *unwind_info_len = info->len; data = info->info; From 53db2ec476e4b3b47168a1bd93f29351b5df23bd Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 13 Jul 2021 18:10:39 +0300 Subject: [PATCH 485/926] Do not eliminate casts from FP types (#53667) An optimization in morph was deleting casts on the RHS of a narrow store if the cast-to-type was wider than the type being stored. This is only valid for casts from integral types, as the backend does not handle "STOREIND(byte*, double)", nor is there an instruction to go from an XMM register to a narrow memory location on x86/x64. The issue is not reproducible right now because fgMorphCast wraps the casts in question, but it is an invalid IR transformation nonetheless, and similar code in fgMorphSmpOpOptional guards against non-integral sources. --- src/coreclr/jit/morph.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b6ee82d3b80..f0bc076f235 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -12071,10 +12071,11 @@ DONE_MORPHING_CHILDREN: tree->AsOp()->gtOp1 = op1; } - /* If we are storing a small type, we might be able to omit a cast */ - if ((effectiveOp1->gtOper == GT_IND) && varTypeIsSmall(effectiveOp1->TypeGet())) + // If we are storing a small type, we might be able to omit a cast. + if (effectiveOp1->OperIs(GT_IND) && varTypeIsSmall(effectiveOp1)) { - if (!gtIsActiveCSE_Candidate(op2) && (op2->gtOper == GT_CAST) && !op2->gtOverflow()) + if (!gtIsActiveCSE_Candidate(op2) && op2->OperIs(GT_CAST) && + varTypeIsIntegral(op2->AsCast()->CastOp()) && !op2->gtOverflow()) { var_types castType = op2->CastToType(); @@ -12082,7 +12083,7 @@ DONE_MORPHING_CHILDREN: // castType is larger or the same as op1's type // then we can discard the cast. - if (varTypeIsSmall(castType) && (genTypeSize(castType) >= genTypeSize(effectiveOp1->TypeGet()))) + if (varTypeIsSmall(castType) && (genTypeSize(castType) >= genTypeSize(effectiveOp1))) { tree->AsOp()->gtOp2 = op2 = op2->AsCast()->CastOp(); } From b2a670b5421af99edad62f8cb5ae172cd8d05030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 13 Jul 2021 17:12:28 +0200 Subject: [PATCH 486/926] Improve resolving runtime pack in WasmApp targets (#55258) Use similar logic to `eng/targetingpacks.targets` in `WasmApp.InTree.targets` and `WasmApp.LocalBuild.targets`. Also set `UseMonoRuntime=true` to make sure we get the Mono-based runtime pack. --- eng/targetingpacks.targets | 3 ++- src/mono/wasm/build/WasmApp.InTree.targets | 17 +++++++++++------ src/mono/wasm/build/WasmApp.LocalBuild.targets | 13 +++++++------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets index f80b26fb984..0f44b205eb6 100644 --- a/eng/targetingpacks.targets +++ b/eng/targetingpacks.targets @@ -11,6 +11,7 @@ $(MicrosoftNetCoreAppFrameworkName) + true diff --git a/src/mono/wasm/build/WasmApp.InTree.targets b/src/mono/wasm/build/WasmApp.InTree.targets index f92f2abd62b..1537277f087 100644 --- a/src/mono/wasm/build/WasmApp.InTree.targets +++ b/src/mono/wasm/build/WasmApp.InTree.targets @@ -2,16 +2,21 @@ + + - - + + + + <_LocalMicrosoftNetCoreAppRuntimePackDir>$([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(Configuration))) + - - $([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(Configuration))) - + - + diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets index fac5252a4ef..b34d8f03bfa 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.targets +++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets @@ -26,6 +26,7 @@ true link + true @@ -37,14 +38,14 @@ false - + + - - $(MicrosoftNetCoreAppRuntimePackLocationToUse) - - + - + From 4ff3762a2e08245a5f005beffeb42cca5c4b6c10 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 13 Jul 2021 08:59:20 -0700 Subject: [PATCH 487/926] Update spin-wait pause/yield normalization (#55295) Update spin-wait pause/yield normalization - Modified the measurement to use much less time and to remeasure periodically to reduce CPU usage during startup - Each measurement does a low-microsecond-level measurement of pause/yield times - Some small amount of history of recent measurements is retained and used to for now take the lowest measurement for normalization - Measurements are done lazily, and at most every few seconds another measurement is taken - Added a profiling event that includes info about a measurement and the established value from recent measurements that is used for normalization --- .../src/System/Threading/Thread.CoreCLR.cs | 23 +- src/coreclr/inc/yieldprocessornormalized.h | 120 +++++-- .../utilcode/yieldprocessornormalized.cpp | 16 +- src/coreclr/vm/CMakeLists.txt | 2 +- src/coreclr/vm/ClrEtwAll.man | 27 +- src/coreclr/vm/ClrEtwAllMeta.lst | 8 +- src/coreclr/vm/comsynchronizable.cpp | 17 +- src/coreclr/vm/comsynchronizable.h | 2 +- src/coreclr/vm/ecalllist.h | 2 +- src/coreclr/vm/eventtrace.cpp | 6 + src/coreclr/vm/finalizerthread.cpp | 5 - src/coreclr/vm/threads.cpp | 9 +- src/coreclr/vm/yieldprocessornormalized.cpp | 315 ++++++++++++++---- .../System/Threading/LowLevelSpinWaiter.cs | 4 - .../src/System/Threading/SpinWait.cs | 4 - 15 files changed, 417 insertions(+), 143 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index c1a968ed9f9..83be00cf5e0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -322,33 +322,14 @@ namespace System.Threading [MethodImpl(MethodImplOptions.InternalCall)] public extern bool Join(int millisecondsTimeout); - private static int s_optimalMaxSpinWaitsPerSpinIteration; - - [DllImport(RuntimeHelpers.QCall)] - private static extern int GetOptimalMaxSpinWaitsPerSpinIterationInternal(); - /// /// Max value to be passed into for optimal delaying. This value is normalized to be /// appropriate for the processor. /// internal static int OptimalMaxSpinWaitsPerSpinIteration { - get - { - int optimalMaxSpinWaitsPerSpinIteration = s_optimalMaxSpinWaitsPerSpinIteration; - return optimalMaxSpinWaitsPerSpinIteration != 0 ? optimalMaxSpinWaitsPerSpinIteration : CalculateOptimalMaxSpinWaitsPerSpinIteration(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static int CalculateOptimalMaxSpinWaitsPerSpinIteration() - { - // This is done lazily because the first call to the function below in the process triggers a measurement that - // takes a nontrivial amount of time if the measurement has not already been done in the backgorund. - // See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. - s_optimalMaxSpinWaitsPerSpinIteration = GetOptimalMaxSpinWaitsPerSpinIterationInternal(); - Debug.Assert(s_optimalMaxSpinWaitsPerSpinIteration > 0); - return s_optimalMaxSpinWaitsPerSpinIteration; + [MethodImpl(MethodImplOptions.InternalCall)] + get; } [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/inc/yieldprocessornormalized.h b/src/coreclr/inc/yieldprocessornormalized.h index ba349bb83ad..121e60b0333 100644 --- a/src/coreclr/inc/yieldprocessornormalized.h +++ b/src/coreclr/inc/yieldprocessornormalized.h @@ -12,14 +12,59 @@ FORCEINLINE void System_YieldProcessor() { YieldProcessor(); } #endif #define YieldProcessor Dont_Use_YieldProcessor -const unsigned int MinNsPerNormalizedYield = 37; // measured typically 37-46 on post-Skylake -const unsigned int NsPerOptimalMaxSpinIterationDuration = 272; // approx. 900 cycles, measured 281 on pre-Skylake, 263 on post-Skylake +#define DISABLE_COPY(T) \ + T(const T &) = delete; \ + T &operator =(const T &) = delete -extern unsigned int g_yieldsPerNormalizedYield; -extern unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration; +#define DISABLE_CONSTRUCT_COPY(T) \ + T() = delete; \ + DISABLE_COPY(T) -void InitializeYieldProcessorNormalizedCrst(); -void EnsureYieldProcessorNormalizedInitialized(); +class YieldProcessorNormalization +{ +public: + static const unsigned int TargetNsPerNormalizedYield = 37; + static const unsigned int TargetMaxNsPerSpinIteration = 272; + + // These are maximums for the computed values for normalization based their calculation + static const unsigned int MaxYieldsPerNormalizedYield = TargetNsPerNormalizedYield * 10; + static const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration = + TargetMaxNsPerSpinIteration * 3 / (TargetNsPerNormalizedYield * 2) + 1; + +private: + static bool s_isMeasurementScheduled; + + static unsigned int s_yieldsPerNormalizedYield; + static unsigned int s_optimalMaxNormalizedYieldsPerSpinIteration; + +public: + static bool IsMeasurementScheduled() + { + return s_isMeasurementScheduled; + } + + static void PerformMeasurement(); + +private: + static void ScheduleMeasurementIfNecessary(); + +public: + static unsigned int GetOptimalMaxNormalizedYieldsPerSpinIteration() + { + return s_optimalMaxNormalizedYieldsPerSpinIteration; + } + + static void FireMeasurementEvents(); + +private: + static double AtomicLoad(double *valueRef); + static void AtomicStore(double *valueRef, double value); + + DISABLE_CONSTRUCT_COPY(YieldProcessorNormalization); + + friend class YieldProcessorNormalizationInfo; + friend void YieldProcessorNormalizedForPreSkylakeCount(unsigned int); +}; class YieldProcessorNormalizationInfo { @@ -30,12 +75,15 @@ private: public: YieldProcessorNormalizationInfo() - : yieldsPerNormalizedYield(g_yieldsPerNormalizedYield), - optimalMaxNormalizedYieldsPerSpinIteration(g_optimalMaxNormalizedYieldsPerSpinIteration), + : yieldsPerNormalizedYield(YieldProcessorNormalization::s_yieldsPerNormalizedYield), + optimalMaxNormalizedYieldsPerSpinIteration(YieldProcessorNormalization::s_optimalMaxNormalizedYieldsPerSpinIteration), optimalMaxYieldsPerSpinIteration(yieldsPerNormalizedYield * optimalMaxNormalizedYieldsPerSpinIteration) { + YieldProcessorNormalization::ScheduleMeasurementIfNecessary(); } + DISABLE_COPY(YieldProcessorNormalizationInfo); + friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &); friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &, unsigned int); friend void YieldProcessorNormalizedForPreSkylakeCount(const YieldProcessorNormalizationInfo &, unsigned int); @@ -98,9 +146,8 @@ FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo if (sizeof(SIZE_T) <= sizeof(unsigned int)) { - // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield - // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized(). - const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield; + // On platforms with a small SIZE_T, prevent overflow on the multiply below + const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield; if (count > MaxCount) { count = MaxCount; @@ -144,9 +191,8 @@ FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount( if (sizeof(SIZE_T) <= sizeof(unsigned int)) { - // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield - // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized(). - const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield; + // On platforms with a small SIZE_T, prevent overflow on the multiply below + const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield; if (preSkylakeCount > MaxCount) { preSkylakeCount = MaxCount; @@ -175,7 +221,35 @@ FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount( // } FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(unsigned int preSkylakeCount) { - YieldProcessorNormalizedForPreSkylakeCount(YieldProcessorNormalizationInfo(), preSkylakeCount); + // This function does not forward to the one above because it is used by some code under utilcode, where + // YieldProcessorNormalizationInfo cannot be used since normalization does not happen in some of its consumers. So this + // version uses the fields in YieldProcessorNormalization directly. + + _ASSERTE(preSkylakeCount != 0); + + if (sizeof(SIZE_T) <= sizeof(unsigned int)) + { + // On platforms with a small SIZE_T, prevent overflow on the multiply below + const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield; + if (preSkylakeCount > MaxCount) + { + preSkylakeCount = MaxCount; + } + } + + const unsigned int PreSkylakeCountToSkylakeCountDivisor = 8; + SIZE_T n = + (SIZE_T)preSkylakeCount * + YieldProcessorNormalization::s_yieldsPerNormalizedYield / + PreSkylakeCountToSkylakeCountDivisor; + if (n == 0) + { + n = 1; + } + do + { + System_YieldProcessor(); + } while (--n != 0); } // See YieldProcessorNormalized() for preliminary info. This function is to be used when there is a decent possibility that the @@ -193,15 +267,12 @@ FORCEINLINE void YieldProcessorWithBackOffNormalized( const YieldProcessorNormalizationInfo &normalizationInfo, unsigned int spinIteration) { - // normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration cannot exceed the value below based on calculations done in - // InitializeYieldProcessorNormalized() - const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration = - NsPerOptimalMaxSpinIterationDuration * 3 / (MinNsPerNormalizedYield * 2) + 1; - _ASSERTE(normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration); - - // This shift value should be adjusted based on the asserted condition below + // This shift value should be adjusted based on the asserted conditions below const UINT8 MaxShift = 3; - static_assert_no_msg(((unsigned int)1 << (MaxShift + 1)) >= MaxOptimalMaxNormalizedYieldsPerSpinIteration); + static_assert_no_msg( + ((unsigned int)1 << MaxShift) <= YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration); + static_assert_no_msg( + ((unsigned int)1 << (MaxShift + 1)) > YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration); unsigned int n; if (spinIteration <= MaxShift && @@ -219,3 +290,6 @@ FORCEINLINE void YieldProcessorWithBackOffNormalized( System_YieldProcessor(); } while (--n != 0); } + +#undef DISABLE_CONSTRUCT_COPY +#undef DISABLE_COPY diff --git a/src/coreclr/utilcode/yieldprocessornormalized.cpp b/src/coreclr/utilcode/yieldprocessornormalized.cpp index 4242f82792b..020d8d7cc79 100644 --- a/src/coreclr/utilcode/yieldprocessornormalized.cpp +++ b/src/coreclr/utilcode/yieldprocessornormalized.cpp @@ -2,8 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "stdafx.h" +#include "yieldprocessornormalized.h" -// Defaults are for when InitializeYieldProcessorNormalized has not yet been called or when no measurement is done, and are -// tuned for Skylake processors -unsigned int g_yieldsPerNormalizedYield = 1; // current value is for Skylake processors, this is expected to be ~8 for pre-Skylake -unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration = 7; +bool YieldProcessorNormalization::s_isMeasurementScheduled; + +// Defaults are for when normalization has not yet been done +unsigned int YieldProcessorNormalization::s_yieldsPerNormalizedYield = 1; +unsigned int YieldProcessorNormalization::s_optimalMaxNormalizedYieldsPerSpinIteration = + (unsigned int) + ( + (double)YieldProcessorNormalization::TargetMaxNsPerSpinIteration / + YieldProcessorNormalization::TargetNsPerNormalizedYield + + 0.5 + ); diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 9c2cb3df0b7..f31e5a3ca12 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -136,7 +136,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON versionresilienthashcode.cpp virtualcallstub.cpp win32threadpool.cpp - yieldprocessornormalized.cpp zapsig.cpp ) @@ -389,6 +388,7 @@ set(VM_SOURCES_WKS threadsuspend.cpp typeparse.cpp weakreferencenative.cpp + yieldprocessornormalized.cpp ${VM_SOURCES_GDBJIT} ) diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index 45895f16fce..0eed049c17b 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -438,7 +438,13 @@ - + + + + + @@ -2916,6 +2922,19 @@ + + @@ -3313,6 +3332,10 @@ keywords ="ThreadingKeyword" opcode="Wait" task="ThreadPoolWorkerThread" symbol="ThreadPoolWorkerThreadWait" message="$(string.RuntimePublisher.ThreadPoolWorkerThreadEventMessage)"/> + + + @@ -8334,6 +8358,7 @@ + diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst index 4ac4fe405d9..9c5738ef43d 100644 --- a/src/coreclr/vm/ClrEtwAllMeta.lst +++ b/src/coreclr/vm/ClrEtwAllMeta.lst @@ -134,9 +134,9 @@ nomac:GarbageCollection:::GCJoin_V2 nostack:Type:::BulkType -################### -# Threadpool events -################### +################################# +# Threading and Threadpool events +################################# nomac:WorkerThreadCreation:::WorkerThreadCreate noclrinstanceid:WorkerThreadCreation:::WorkerThreadCreate nomac:WorkerThreadCreation:::WorkerThreadTerminate @@ -170,6 +170,8 @@ nomac:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentSample nostack:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentSample nomac:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentAdjustment nostack:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentAdjustment +nomac:YieldProcessorMeasurement:::YieldProcessorMeasurement +nostack:YieldProcessorMeasurement:::YieldProcessorMeasurement ################## # Exception events diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 39f00d06741..15a33c711e7 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -1089,22 +1089,13 @@ FCIMPL1(void, ThreadNative::SetIsThreadpoolThread, ThreadBaseObject* thread) } FCIMPLEND -INT32 QCALLTYPE ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration() +FCIMPL0(INT32, ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) { - QCALL_CONTRACT; + FCALL_CONTRACT; - INT32 optimalMaxNormalizedYieldsPerSpinIteration; - - BEGIN_QCALL; - - // RuntimeThread calls this function only once lazily and caches the result, so ensure initialization - EnsureYieldProcessorNormalizedInitialized(); - optimalMaxNormalizedYieldsPerSpinIteration = g_optimalMaxNormalizedYieldsPerSpinIteration; - - END_QCALL; - - return optimalMaxNormalizedYieldsPerSpinIteration; + return (INT32)YieldProcessorNormalization::GetOptimalMaxNormalizedYieldsPerSpinIteration(); } +FCIMPLEND FCIMPL1(void, ThreadNative::SpinWait, int iterations) { diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index e9968201b8b..cfab18d9010 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -86,7 +86,7 @@ public: UINT64 QCALLTYPE GetProcessDefaultStackSize(); static FCDECL1(INT32, GetManagedThreadId, ThreadBaseObject* th); - static INT32 QCALLTYPE GetOptimalMaxSpinWaitsPerSpinIteration(); + static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); static FCDECL1(void, SpinWait, int iterations); static BOOL QCALLTYPE YieldThread(); static FCDECL0(Object*, GetCurrentThread); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index f77dc75c80b..ea3f65d7291 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -602,7 +602,7 @@ FCFuncStart(gThreadFuncs) #endif // FEATURE_COMINTEROP FCFuncElement("Interrupt", ThreadNative::Interrupt) FCFuncElement("Join", ThreadNative::Join) - QCFuncElement("GetOptimalMaxSpinWaitsPerSpinIterationInternal", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) + FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) FCFuncElement("GetCurrentProcessorNumber", ThreadNative::GetCurrentProcessorNumber) FCFuncEnd() diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index ac7be2a9439..aded74deda6 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -4417,6 +4417,12 @@ VOID EtwCallbackCommon( { ETW::TypeSystemLog::OnKeywordsChanged(); } + + if (g_fEEStarted && !g_fEEShutDown) + { + // Emit the YieldProcessor measured values at the beginning of the trace + YieldProcessorNormalization::FireMeasurementEvents(); + } } // Individual callbacks for each EventPipe provider. diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index 1e4dbf913c8..e8370315e66 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -379,11 +379,6 @@ DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args) { GetFinalizerThread()->SetBackground(TRUE); - { - GCX_PREEMP(); - EnsureYieldProcessorNormalizedInitialized(); - } - while (!fQuitFinalizer) { // This will apply any policy for swallowing exceptions during normal diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 2c55f8770b0..c6485b86d59 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1150,8 +1150,6 @@ void InitThreadManager() } CONTRACTL_END; - InitializeYieldProcessorNormalizedCrst(); - // All patched helpers should fit into one page. // If you hit this assert on retail build, there is most likely problem with BBT script. _ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart > (ptrdiff_t)0); @@ -7194,6 +7192,7 @@ BOOL Thread::HaveExtraWorkForFinalizer() || Thread::CleanupNeededForFinalizedThread() || (m_DetachCount > 0) || SystemDomain::System()->RequireAppDomainCleanup() + || YieldProcessorNormalization::IsMeasurementScheduled() || ThreadStore::s_pThreadStore->ShouldTriggerGCForDeadThreads(); } @@ -7240,6 +7239,12 @@ void Thread::DoExtraWorkForFinalizer() // If there were any TimerInfos waiting to be released, they'll get flushed now ThreadpoolMgr::FlushQueueOfTimerInfos(); + if (YieldProcessorNormalization::IsMeasurementScheduled()) + { + GCX_PREEMP(); + YieldProcessorNormalization::PerformMeasurement(); + } + ThreadStore::s_pThreadStore->TriggerGCForDeadThreadsIfNecessary(); } diff --git a/src/coreclr/vm/yieldprocessornormalized.cpp b/src/coreclr/vm/yieldprocessornormalized.cpp index 91547923310..2c51e73b678 100644 --- a/src/coreclr/vm/yieldprocessornormalized.cpp +++ b/src/coreclr/vm/yieldprocessornormalized.cpp @@ -2,17 +2,33 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "common.h" +#include "yieldprocessornormalized.h" -static Volatile s_isYieldProcessorNormalizedInitialized = false; -static CrstStatic s_initializeYieldProcessorNormalizedCrst; +#ifndef CROSSGEN_COMPILE -void InitializeYieldProcessorNormalizedCrst() +#include "finalizerthread.h" + +enum class NormalizationState : UINT8 { - WRAPPER_NO_CONTRACT; - s_initializeYieldProcessorNormalizedCrst.Init(CrstLeafLock); -} + Uninitialized, + Initialized, + Failed +}; -static void InitializeYieldProcessorNormalized() +static const int NsPerYieldMeasurementCount = 8; +static const unsigned int MeasurementPeriodMs = 4000; + +static const unsigned int NsPerS = 1000 * 1000 * 1000; + +static NormalizationState s_normalizationState = NormalizationState::Uninitialized; +static unsigned int s_previousNormalizationTimeMs; + +static UINT64 s_performanceCounterTicksPerS; +static double s_nsPerYieldMeasurements[NsPerYieldMeasurementCount]; +static int s_nextMeasurementIndex; +static double s_establishedNsPerYield = YieldProcessorNormalization::TargetNsPerNormalizedYield; + +static unsigned int DetermineMeasureDurationUs() { CONTRACTL { @@ -22,92 +38,271 @@ static void InitializeYieldProcessorNormalized() } CONTRACTL_END; - CrstHolder lock(&s_initializeYieldProcessorNormalizedCrst); + _ASSERTE(s_normalizationState != NormalizationState::Failed); - if (s_isYieldProcessorNormalizedInitialized) + // On some systems, querying the high performance counter has relatively significant overhead. Increase the measure duration + // if the overhead seems high relative to the measure duration. + unsigned int measureDurationUs = 1; + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + UINT64 startTicks = li.QuadPart; + QueryPerformanceCounter(&li); + UINT64 elapsedTicks = li.QuadPart - startTicks; + if (elapsedTicks >= s_performanceCounterTicksPerS * measureDurationUs * (1000 / 4) / NsPerS) // elapsed >= 1/4 of the measure duration { - return; + measureDurationUs *= 4; } + return measureDurationUs; +} - // Intel pre-Skylake processor: measured typically 14-17 cycles per yield - // Intel post-Skylake processor: measured typically 125-150 cycles per yield - const int MeasureDurationMs = 10; - const int NsPerSecond = 1000 * 1000 * 1000; +static double MeasureNsPerYield(unsigned int measureDurationUs) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + _ASSERTE(s_normalizationState != NormalizationState::Failed); + + int yieldCount = (int)(measureDurationUs * 1000 / s_establishedNsPerYield) + 1; + UINT64 ticksPerS = s_performanceCounterTicksPerS; + UINT64 measureDurationTicks = ticksPerS * measureDurationUs / (1000 * 1000); LARGE_INTEGER li; - if (!QueryPerformanceFrequency(&li) || (ULONGLONG)li.QuadPart < 1000 / MeasureDurationMs) - { - // High precision clock not available or clock resolution is too low, resort to defaults - s_isYieldProcessorNormalizedInitialized = true; - return; - } - ULONGLONG ticksPerSecond = li.QuadPart; - - // Measure the nanosecond delay per yield - ULONGLONG measureDurationTicks = ticksPerSecond / (1000 / MeasureDurationMs); - unsigned int yieldCount = 0; QueryPerformanceCounter(&li); - ULONGLONG startTicks = li.QuadPart; - ULONGLONG elapsedTicks; - do + UINT64 startTicks = li.QuadPart; + + for (int i = 0; i < yieldCount; ++i) { - // On some systems, querying the high performance counter has relatively significant overhead. Do enough yields to mask - // the timing overhead. Assuming one yield has a delay of MinNsPerNormalizedYield, 1000 yields would have a delay in the - // low microsecond range. - for (int i = 0; i < 1000; ++i) + System_YieldProcessor(); + } + + QueryPerformanceCounter(&li); + UINT64 elapsedTicks = li.QuadPart - startTicks; + while (elapsedTicks < measureDurationTicks) + { + int nextYieldCount = + Max(4, + elapsedTicks == 0 + ? yieldCount / 4 + : (int)(yieldCount * (measureDurationTicks - elapsedTicks) / (double)elapsedTicks) + 1); + for (int i = 0; i < nextYieldCount; ++i) { System_YieldProcessor(); } - yieldCount += 1000; QueryPerformanceCounter(&li); - ULONGLONG nowTicks = li.QuadPart; - elapsedTicks = nowTicks - startTicks; - } while (elapsedTicks < measureDurationTicks); - double nsPerYield = (double)elapsedTicks * NsPerSecond / ((double)yieldCount * ticksPerSecond); - if (nsPerYield < 1) - { - nsPerYield = 1; + elapsedTicks = li.QuadPart - startTicks; + yieldCount += nextYieldCount; } - // Calculate the number of yields required to span the duration of a normalized yield. Since nsPerYield is at least 1, this - // value is naturally limited to MinNsPerNormalizedYield. - int yieldsPerNormalizedYield = (int)(MinNsPerNormalizedYield / nsPerYield + 0.5); - if (yieldsPerNormalizedYield < 1) + // Limit the minimum to a reasonable value considering that on some systems a yield may be implemented as a no-op + const double MinNsPerYield = 0.1; + + // Measured values higher than this don't affect values calculated for normalization, and it's very unlikely for a yield to + // really take this long. Limit the maximum to keep the recorded values reasonable. + const double MaxNsPerYield = YieldProcessorNormalization::TargetMaxNsPerSpinIteration / 1.5 + 1; + + return Max(MinNsPerYield, Min((double)elapsedTicks * NsPerS / ((double)yieldCount * ticksPerS), MaxNsPerYield)); +} + +void YieldProcessorNormalization::PerformMeasurement() +{ + CONTRACTL { - yieldsPerNormalizedYield = 1; + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; } - _ASSERTE(yieldsPerNormalizedYield <= (int)MinNsPerNormalizedYield); + CONTRACTL_END; + + _ASSERTE(s_isMeasurementScheduled); + + double latestNsPerYield; + if (s_normalizationState == NormalizationState::Initialized) + { + if (GetTickCount() - s_previousNormalizationTimeMs < MeasurementPeriodMs) + { + return; + } + + int nextMeasurementIndex = s_nextMeasurementIndex; + latestNsPerYield = MeasureNsPerYield(DetermineMeasureDurationUs()); + AtomicStore(&s_nsPerYieldMeasurements[nextMeasurementIndex], latestNsPerYield); + if (++nextMeasurementIndex >= NsPerYieldMeasurementCount) + { + nextMeasurementIndex = 0; + } + s_nextMeasurementIndex = nextMeasurementIndex; + } + else if (s_normalizationState == NormalizationState::Uninitialized) + { + LARGE_INTEGER li; + if (!QueryPerformanceFrequency(&li) || li.QuadPart < 1000 * 1000) + { + // High precision clock not available or clock resolution is too low, resort to defaults + s_normalizationState = NormalizationState::Failed; + return; + } + s_performanceCounterTicksPerS = li.QuadPart; + + unsigned int measureDurationUs = DetermineMeasureDurationUs(); + for (int i = 0; i < NsPerYieldMeasurementCount; ++i) + { + latestNsPerYield = MeasureNsPerYield(measureDurationUs); + AtomicStore(&s_nsPerYieldMeasurements[i], latestNsPerYield); + if (i == 0 || latestNsPerYield < s_establishedNsPerYield) + { + AtomicStore(&s_establishedNsPerYield, latestNsPerYield); + } + + if (i < NsPerYieldMeasurementCount - 1) + { + FireEtwYieldProcessorMeasurement(GetClrInstanceId(), latestNsPerYield, s_establishedNsPerYield); + } + } + } + else + { + _ASSERTE(s_normalizationState == NormalizationState::Failed); + return; + } + + double establishedNsPerYield = s_nsPerYieldMeasurements[0]; + for (int i = 1; i < NsPerYieldMeasurementCount; ++i) + { + double nsPerYield = s_nsPerYieldMeasurements[i]; + if (nsPerYield < establishedNsPerYield) + { + establishedNsPerYield = nsPerYield; + } + } + if (establishedNsPerYield != s_establishedNsPerYield) + { + AtomicStore(&s_establishedNsPerYield, establishedNsPerYield); + } + + FireEtwYieldProcessorMeasurement(GetClrInstanceId(), latestNsPerYield, s_establishedNsPerYield); + + // Calculate the number of yields required to span the duration of a normalized yield + unsigned int yieldsPerNormalizedYield = Max(1u, (unsigned int)(TargetNsPerNormalizedYield / establishedNsPerYield + 0.5)); + _ASSERTE(yieldsPerNormalizedYield <= MaxYieldsPerNormalizedYield); + s_yieldsPerNormalizedYield = yieldsPerNormalizedYield; // Calculate the maximum number of yields that would be optimal for a late spin iteration. Typically, we would not want to // spend excessive amounts of time (thousands of cycles) doing only YieldProcessor, as SwitchToThread/Sleep would do a // better job of allowing other work to run. - int optimalMaxNormalizedYieldsPerSpinIteration = - (int)(NsPerOptimalMaxSpinIterationDuration / (yieldsPerNormalizedYield * nsPerYield) + 0.5); - if (optimalMaxNormalizedYieldsPerSpinIteration < 1) - { - optimalMaxNormalizedYieldsPerSpinIteration = 1; - } - - g_yieldsPerNormalizedYield = yieldsPerNormalizedYield; - g_optimalMaxNormalizedYieldsPerSpinIteration = optimalMaxNormalizedYieldsPerSpinIteration; - s_isYieldProcessorNormalizedInitialized = true; + s_optimalMaxNormalizedYieldsPerSpinIteration = + Max(1u, (unsigned int)(TargetMaxNsPerSpinIteration / (yieldsPerNormalizedYield * establishedNsPerYield) + 0.5)); + _ASSERTE(s_optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration); GCHeapUtilities::GetGCHeap()->SetYieldProcessorScalingFactor((float)yieldsPerNormalizedYield); + + s_previousNormalizationTimeMs = GetTickCount(); + s_normalizationState = NormalizationState::Initialized; + s_isMeasurementScheduled = false; } -void EnsureYieldProcessorNormalizedInitialized() +#endif // !CROSSGEN_COMPILE + +void YieldProcessorNormalization::ScheduleMeasurementIfNecessary() { CONTRACTL { NOTHROW; GC_NOTRIGGER; - MODE_PREEMPTIVE; + MODE_ANY; } CONTRACTL_END; - if (!s_isYieldProcessorNormalizedInitialized) +#ifndef CROSSGEN_COMPILE + NormalizationState normalizationState = VolatileLoadWithoutBarrier(&s_normalizationState); + if (normalizationState == NormalizationState::Initialized) { - InitializeYieldProcessorNormalized(); + if (GetTickCount() - s_previousNormalizationTimeMs < MeasurementPeriodMs) + { + return; + } + } + else if (normalizationState == NormalizationState::Uninitialized) + { + } + else + { + _ASSERTE(normalizationState == NormalizationState::Failed); + return; + } + + // !g_fEEStarted is required for FinalizerThread::EnableFinalization() below + if (s_isMeasurementScheduled || !g_fEEStarted) + { + return; + } + + s_isMeasurementScheduled = true; + FinalizerThread::EnableFinalization(); +#endif // !CROSSGEN_COMPILE +} + +#ifndef CROSSGEN_COMPILE + +void YieldProcessorNormalization::FireMeasurementEvents() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!EventEnabledYieldProcessorMeasurement()) + { + return; + } + + // This function may be called at any time to fire events about recorded measurements. There is no synchronization for the + // recorded information, so try to enumerate the array with some care. + double establishedNsPerYield = AtomicLoad(&s_establishedNsPerYield); + int nextIndex = VolatileLoadWithoutBarrier(&s_nextMeasurementIndex); + for (int i = 0; i < NsPerYieldMeasurementCount; ++i) + { + double nsPerYield = AtomicLoad(&s_nsPerYieldMeasurements[nextIndex]); + if (nsPerYield != 0) // the array may not be fully initialized yet + { + FireEtwYieldProcessorMeasurement(GetClrInstanceId(), nsPerYield, establishedNsPerYield); + } + + if (++nextIndex >= NsPerYieldMeasurementCount) + { + nextIndex = 0; + } } } + +double YieldProcessorNormalization::AtomicLoad(double *valueRef) +{ + WRAPPER_NO_CONTRACT; + +#ifdef TARGET_64BIT + return VolatileLoadWithoutBarrier(valueRef); +#else + return InterlockedCompareExchangeT(valueRef, 0.0, 0.0); +#endif +} + +void YieldProcessorNormalization::AtomicStore(double *valueRef, double value) +{ + WRAPPER_NO_CONTRACT; + +#ifdef TARGET_64BIT + *valueRef = value; +#else + InterlockedExchangeT(valueRef, value); +#endif +} + +#endif // !CROSSGEN_COMPILE diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelSpinWaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelSpinWaiter.cs index e1c0766b3f0..8e8198de392 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelSpinWaiter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelSpinWaiter.cs @@ -71,10 +71,6 @@ namespace System.Threading // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is // usually better for that. - // - // Thread.OptimalMaxSpinWaitsPerSpinIteration: - // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. - // int n = Thread.OptimalMaxSpinWaitsPerSpinIteration; if (spinIndex <= 30 && (1 << spinIndex) < n) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs index b45cc7d5d38..66b73f8be02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinWait.cs @@ -225,10 +225,6 @@ namespace System.Threading // the equivalent of YieldProcessor(), as at that point SwitchToThread/Sleep(0) are more likely to be able to // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is // usually better for that. - // - // Thread.OptimalMaxSpinWaitsPerSpinIteration: - // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. - // int n = Thread.OptimalMaxSpinWaitsPerSpinIteration; if (_count <= 30 && (1 << _count) < n) { From 1904cfcbe436e73b3aef7f83bc36ee4c262b3196 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Tue, 13 Jul 2021 09:00:40 -0700 Subject: [PATCH 488/926] Rename TextMapPropagator to DistributedContextPropagator (#55539) --- ...em.Diagnostics.DiagnosticSourceActivity.cs | 10 +-- ...System.Diagnostics.DiagnosticSource.csproj | 2 +- ...tor.cs => DistributedContextPropagator.cs} | 18 ++--- .../System/Diagnostics/LegacyPropagator.cs | 4 +- .../System/Diagnostics/NoOutputPropagator.cs | 4 +- .../Diagnostics/PassThroughPropagator.cs | 4 +- .../tests/PropagatorTests.cs | 74 +++++++++---------- 7 files changed, 58 insertions(+), 58 deletions(-) rename src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/{TextMapPropagator.cs => DistributedContextPropagator.cs} (89%) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 7e50f732e42..68fa62b8be9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -254,7 +254,7 @@ namespace System.Diagnostics public System.Diagnostics.SampleActivity? Sample { get { throw null; } set { throw null; } } public void Dispose() { throw null; } } - public abstract class TextMapPropagator + public abstract class DistributedContextPropagator { public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out System.Collections.Generic.IEnumerable? fieldValues); public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue); @@ -262,10 +262,10 @@ namespace System.Diagnostics public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter); public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState); public abstract System.Collections.Generic.IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter); - public static TextMapPropagator Current { get; set; } - public static TextMapPropagator CreateDefaultPropagator() { throw null; } - public static TextMapPropagator CreatePassThroughPropagator() { throw null; } - public static TextMapPropagator CreateNoOutputPropagator() { throw null; } + public static DistributedContextPropagator Current { get; set; } + public static DistributedContextPropagator CreateDefaultPropagator() { throw null; } + public static DistributedContextPropagator CreatePassThroughPropagator() { throw null; } + public static DistributedContextPropagator CreateNoOutputPropagator() { throw null; } } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index e4a9cbd28d7..dab222792b1 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -39,11 +39,11 @@ + - diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs similarity index 89% rename from src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs rename to src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs index 0dab622a298..9c54baf9c25 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/TextMapPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs @@ -9,13 +9,13 @@ using System.Collections.Generic; namespace System.Diagnostics { /// - /// An implementation of TextMapPropagator determines if and how distributed context information is encoded and decoded as it traverses the network. + /// An implementation of DistributedContextPropagator determines if and how distributed context information is encoded and decoded as it traverses the network. /// The encoding can be transported over any network protocol that supports string key-value pairs. For example when using HTTP, each key value pair is an HTTP header. - /// TextMapPropagator inject values into and extracts values from carriers as string key/value pairs. + /// DistributedContextPropagator inject values into and extracts values from carriers as string key/value pairs. /// - public abstract class TextMapPropagator + public abstract class DistributedContextPropagator { - private static TextMapPropagator s_current = CreateDefaultPropagator(); + private static DistributedContextPropagator s_current = CreateDefaultPropagator(); /// /// The callback that is used in propagators' extract methods. The callback is invoked to lookup the value of a named field. @@ -38,7 +38,7 @@ namespace System.Diagnostics /// /// The set of field names this propagator is likely to read or write. /// - /// Returns list of fields that will be used by the TextMapPropagator. + /// Returns list of fields that will be used by the DistributedContextPropagator. public abstract IReadOnlyCollection Fields { get; } /// @@ -69,7 +69,7 @@ namespace System.Diagnostics /// /// Get or set the process wide propagator object which used as the current selected propagator. /// - public static TextMapPropagator Current + public static DistributedContextPropagator Current { get { @@ -91,18 +91,18 @@ namespace System.Diagnostics /// "traceparent" of the identifiers which are formatted as W3C trace parent, "Request-Id" of the identifiers which are formatted as a hierarchical identifier. /// The returned propagator can inject the baggage key-value pair list with header name "Correlation-Context" and it can extract the baggage values mapped to header names "Correlation-Context" and "baggage". /// - public static TextMapPropagator CreateDefaultPropagator() => LegacyPropagator.Instance; + public static DistributedContextPropagator CreateDefaultPropagator() => LegacyPropagator.Instance; /// /// Returns a propagator which attempts to act transparently, emitting the same data on outbound network requests that was received on the in-bound request. /// When encoding the outbound message, this propagator uses information from the request's root Activity, ignoring any intermediate Activities that may have been created while processing the request. /// - public static TextMapPropagator CreatePassThroughPropagator() => PassThroughPropagator.Instance; + public static DistributedContextPropagator CreatePassThroughPropagator() => PassThroughPropagator.Instance; /// /// Returns a propagator which does not transmit any distributed context information in outbound network messages. /// - public static TextMapPropagator CreateNoOutputPropagator() => NoOutputPropagator.Instance; + public static DistributedContextPropagator CreateNoOutputPropagator() => NoOutputPropagator.Instance; // internal stuff diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs index a469dd5b56b..9a3c909bb17 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs @@ -7,9 +7,9 @@ using System.Collections.ObjectModel; namespace System.Diagnostics { - internal sealed class LegacyPropagator : TextMapPropagator + internal sealed class LegacyPropagator : DistributedContextPropagator { - internal static TextMapPropagator Instance { get; } = new LegacyPropagator(); + internal static DistributedContextPropagator Instance { get; } = new LegacyPropagator(); public override IReadOnlyCollection Fields { get; } = new ReadOnlyCollection(new[] { TraceParent, RequestId, TraceState, Baggage, CorrelationContext }); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs index f9d503a8f1c..1655ef466e0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/NoOutputPropagator.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; namespace System.Diagnostics { - internal sealed class NoOutputPropagator : TextMapPropagator + internal sealed class NoOutputPropagator : DistributedContextPropagator { - internal static TextMapPropagator Instance { get; } = new NoOutputPropagator(); + internal static DistributedContextPropagator Instance { get; } = new NoOutputPropagator(); public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs index 01bf821a5cb..12515555fcf 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; namespace System.Diagnostics { - internal sealed class PassThroughPropagator : TextMapPropagator + internal sealed class PassThroughPropagator : DistributedContextPropagator { - internal static TextMapPropagator Instance { get; } = new PassThroughPropagator(); + internal static DistributedContextPropagator Instance { get; } = new PassThroughPropagator(); public override IReadOnlyCollection Fields { get; } = LegacyPropagator.Instance.Fields; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs index a0ade495467..2a914f62ad5 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs @@ -21,89 +21,89 @@ namespace System.Diagnostics.Tests public void TestAllPropagators() { RemoteExecutor.Invoke(() => { - Assert.NotNull(TextMapPropagator.Current); + Assert.NotNull(DistributedContextPropagator.Current); // // Default Propagator // - Assert.Same(TextMapPropagator.CreateDefaultPropagator(), TextMapPropagator.Current); + Assert.Same(DistributedContextPropagator.CreateDefaultPropagator(), DistributedContextPropagator.Current); TestDefaultPropagatorUsingW3CActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "Legacy1=true", new List>() { new KeyValuePair(" LegacyKey1 ", " LegacyValue1 ") }); TestDefaultPropagatorUsingHierarchicalActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "Legacy2=true", new List>() { new KeyValuePair("LegacyKey2", "LegacyValue2") }); - TestFields(TextMapPropagator.Current); + TestFields(DistributedContextPropagator.Current); // // NoOutput Propagator // - TextMapPropagator.Current = TextMapPropagator.CreateNoOutputPropagator(); - Assert.NotNull(TextMapPropagator.Current); + DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator(); + Assert.NotNull(DistributedContextPropagator.Current); TestNoOutputPropagatorUsingHierarchicalActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "ActivityState=1", new List>() { new KeyValuePair("B1", "V1"), new KeyValuePair(" B2 ", " V2 ")}); TestNoOutputPropagatorUsingHierarchicalActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "ActivityState=2", null); TestNoOutputPropagatorUsingW3CActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "ActivityState=1", new List>() { new KeyValuePair(" B3 ", " V3"), new KeyValuePair(" B4 ", " V4 "), new KeyValuePair("B5", "V5")}); TestNoOutputPropagatorUsingW3CActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "ActivityState=2", null); - TestFields(TextMapPropagator.Current); + TestFields(DistributedContextPropagator.Current); // // Pass Through Propagator // - TextMapPropagator.Current = TextMapPropagator.CreatePassThroughPropagator(); - Assert.NotNull(TextMapPropagator.Current); + DistributedContextPropagator.Current = DistributedContextPropagator.CreatePassThroughPropagator(); + Assert.NotNull(DistributedContextPropagator.Current); TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "PassThrough=true", new List>() { new KeyValuePair("PassThroughKey1", "PassThroughValue1"), new KeyValuePair("PassThroughKey2", "PassThroughValue2")}); TestPassThroughPropagatorUsingHierarchicalActivityWithParentId( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "PassThrough1=true", new List>() { new KeyValuePair("PassThroughKey3", "PassThroughValue3"), new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ")}); TestPassThroughPropagatorUsingW3CActivity( - TextMapPropagator.Current, + DistributedContextPropagator.Current, "PassThrough2=1", new List>() { new KeyValuePair(" PassThroughKey4 ", " PassThroughValue4 ") }); - TestPassThroughPropagatorWithNullCurrent(TextMapPropagator.Current); + TestPassThroughPropagatorWithNullCurrent(DistributedContextPropagator.Current); - TestFields(TextMapPropagator.Current); + TestFields(DistributedContextPropagator.Current); // // Test Current // - Assert.Throws(() => TextMapPropagator.Current = null); + Assert.Throws(() => DistributedContextPropagator.Current = null); }).Dispose(); } - private void TestDefaultPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestDefaultPropagatorUsingW3CActivity(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateW3CActivity("LegacyW3C1", "LegacyW3CState=1", baggage); using Activity b = CreateW3CActivity("LegacyW3C2", "LegacyW3CState=2", baggage); @@ -117,7 +117,7 @@ namespace System.Diagnostics.Tests TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); } - private void TestDefaultPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestDefaultPropagatorUsingHierarchicalActivity(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateHierarchicalActivity("LegacyHierarchical1", null, "LegacyHierarchicalState=1", baggage); using Activity b = CreateHierarchicalActivity("LegacyHierarchical2", null, "LegacyHierarchicalState=2", baggage); @@ -131,7 +131,7 @@ namespace System.Diagnostics.Tests TestDefaultPropagatorUsing(Activity.Current, propagator, state, baggage); } - private void TestDefaultPropagatorUsing(Activity a, TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestDefaultPropagatorUsing(Activity a, DistributedContextPropagator propagator, string state, IEnumerable> baggage) { // Test with non-current propagator.Inject(a, null, (object carrier, string fieldName, string value) => @@ -167,7 +167,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, a); } - private void TestNoOutputPropagatorUsingHierarchicalActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestNoOutputPropagatorUsingHierarchicalActivity(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateHierarchicalActivity("NoOutputHierarchical", null, state, baggage); @@ -181,7 +181,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, a); } - private void TestNoOutputPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestNoOutputPropagatorUsingW3CActivity(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateW3CActivity("NoOutputW3C", state, baggage); @@ -195,7 +195,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, a); } - private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentChain(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateHierarchicalActivity("PassThrough", null, state, baggage); using Activity b = CreateHierarchicalActivity("PassThroughChild1", null, state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); @@ -239,7 +239,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, c); } - private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentId(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestPassThroughPropagatorUsingHierarchicalActivityWithParentId(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateHierarchicalActivity("PassThrough", "Parent1", state, baggage); using Activity b = CreateHierarchicalActivity("PassThroughChild1", "Parent2", state + "1", new List>() { new KeyValuePair("Child1Key", "Child1Value") } ); @@ -283,7 +283,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, c); } - private void TestPassThroughPropagatorUsingW3CActivity(TextMapPropagator propagator, string state, IEnumerable> baggage) + private void TestPassThroughPropagatorUsingW3CActivity(DistributedContextPropagator propagator, string state, IEnumerable> baggage) { using Activity a = CreateW3CActivity("PassThroughW3C", "PassThroughW3CState=1", baggage); @@ -314,7 +314,7 @@ namespace System.Diagnostics.Tests TestBaggageExtraction(propagator, a); } - private void TestPassThroughPropagatorWithNullCurrent(TextMapPropagator propagator) + private void TestPassThroughPropagatorWithNullCurrent(DistributedContextPropagator propagator) { Activity.Current = null; @@ -337,7 +337,7 @@ namespace System.Diagnostics.Tests }); } - private void TestDefaultExtraction(TextMapPropagator propagator, Activity a) + private void TestDefaultExtraction(DistributedContextPropagator propagator, Activity a) { bool traceParentEncountered = false; @@ -388,7 +388,7 @@ namespace System.Diagnostics.Tests Assert.Equal(a.TraceStateString, traceState); } - private void TestBaggageExtraction(TextMapPropagator propagator, Activity a) + private void TestBaggageExtraction(DistributedContextPropagator propagator, Activity a) { bool baggageEncountered = false; @@ -425,7 +425,7 @@ namespace System.Diagnostics.Tests Assert.Equal(GetFormattedBaggage(a.Baggage, false, true), GetFormattedBaggage(b, true)); } - private void TestFields(TextMapPropagator propagator) + private void TestFields(DistributedContextPropagator propagator) { Assert.True(propagator.Fields.Contains(TraceParent)); Assert.True(propagator.Fields.Contains(RequestId)); @@ -532,14 +532,14 @@ namespace System.Diagnostics.Tests { RemoteExecutor.Invoke(() => { - TextMapPropagator.Current = new CustomPropagator(); + DistributedContextPropagator.Current = new CustomPropagator(); using Activity a = CreateW3CActivity("CustomW3C1", "CustomW3CState=1", new List>() { new KeyValuePair(" CustomKey1 ", " CustomValue1 ") }); string traceParent = "x-" + a.Id ; string traceState = "x-" + a.TraceStateString; string baggageString = "x=y, " + GetFormattedBaggage(a.Baggage); - TextMapPropagator.Current.Inject(a, null, (object carrier, string fieldName, string value) => + DistributedContextPropagator.Current.Inject(a, null, (object carrier, string fieldName, string value) => { if (fieldName == CustomPropagator.XTraceParent) { @@ -562,7 +562,7 @@ namespace System.Diagnostics.Tests Assert.False(true, $"Encountered wrong header name '{fieldName}' in the Custom Propagator"); }); - TextMapPropagator.Current.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + DistributedContextPropagator.Current.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { fieldValues = null; fieldValue = null; @@ -585,7 +585,7 @@ namespace System.Diagnostics.Tests Assert.Equal(traceParent, traceId); Assert.Equal(traceState, state); - IEnumerable>? b = TextMapPropagator.Current.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + IEnumerable>? b = DistributedContextPropagator.Current.ExtractBaggage(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { Assert.Null(carrier); fieldValue = null; @@ -607,7 +607,7 @@ namespace System.Diagnostics.Tests }).Dispose(); } - internal class CustomPropagator : TextMapPropagator + internal class CustomPropagator : DistributedContextPropagator { internal const string XTraceParent = "x-traceparent"; internal const string XTraceState = "x-tracestate"; From d2e9f42c609453a1104afa1a006242ba69b524cf Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 13 Jul 2021 12:05:31 -0400 Subject: [PATCH 489/926] Augment tests for FileMode.Append (#55513) To validate that seeking and writing are valid in the region of the file since its initial length. --- .../tests/FileStream/Position.cs | 4 +++ .../tests/FileStream/Seek.cs | 3 ++ .../tests/FileStream/SetLength.cs | 6 ++++ .../tests/FileStream/ctor_str_fm.cs | 34 +++++++++++++++++-- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs index 8157f6cc91e..32817dcee26 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Position.cs @@ -26,6 +26,10 @@ namespace System.IO.Tests fs.Position = length + 1; Assert.Equal(length + 1, fs.Position); + + fs.Write(TestBuffer); + fs.Position = length + 1; + Assert.Equal(length + 1, fs.Position); } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/Seek.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/Seek.cs index 6971eb49927..963a1fddff5 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/Seek.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/Seek.cs @@ -32,6 +32,9 @@ namespace System.IO.Tests Assert.Equal(length, fs.Position); Assert.Throws(() => fs.Seek(-length, SeekOrigin.End)); Assert.Equal(length, fs.Position); + + fs.Write(TestBuffer); + Assert.Equal(length, fs.Seek(length, SeekOrigin.Begin)); } } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs index 67944a45771..3a3f547d070 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/SetLength.cs @@ -23,6 +23,12 @@ namespace System.IO.Tests Assert.Equal(length, fs.Length); Assert.Throws(() => fs.SetLength(0)); Assert.Equal(length, fs.Length); + + fs.Write(TestBuffer); + Assert.Equal(length + TestBuffer.Length, fs.Length); + + fs.SetLength(length); + Assert.Equal(length, fs.Length); } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs index 60e3cd49ea7..47e3b492373 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; using Xunit; namespace System.IO.Tests @@ -213,11 +214,23 @@ namespace System.IO.Tests [Theory, MemberData(nameof(StreamSpecifiers))] public virtual void FileModeAppend(string streamSpecifier) { - using (FileStream fs = CreateFileStream(GetTestFilePath() + streamSpecifier, FileMode.Append)) + string fileName = GetTestFilePath() + streamSpecifier; + using (FileStream fs = CreateFileStream(fileName, FileMode.Append)) { Assert.False(fs.CanRead); Assert.True(fs.CanWrite); + + fs.Write(Encoding.ASCII.GetBytes("abcde")); + Assert.Equal(5, fs.Length); + Assert.Equal(5, fs.Position); + Assert.Equal(1, fs.Seek(1, SeekOrigin.Begin)); + + fs.Write(Encoding.ASCII.GetBytes("xyz")); + Assert.Equal(4, fs.Position); + Assert.Equal(5, fs.Length); } + + Assert.Equal("axyze", File.ReadAllText(fileName)); } [Theory, MemberData(nameof(StreamSpecifiers))] @@ -226,20 +239,35 @@ namespace System.IO.Tests string fileName = GetTestFilePath() + streamSpecifier; using (FileStream fs = CreateFileStream(fileName, FileMode.Create)) { - fs.WriteByte(0); + fs.WriteByte((byte)'s'); } + string initialContents = File.ReadAllText(fileName); using (FileStream fs = CreateFileStream(fileName, FileMode.Append)) { // Ensure that the file was re-opened and position set to end Assert.Equal(Math.Max(1L, InitialLength), fs.Length); - Assert.Equal(fs.Length, fs.Position); + + long position = fs.Position; + Assert.Equal(fs.Length, position); + Assert.False(fs.CanRead); Assert.True(fs.CanSeek); Assert.True(fs.CanWrite); + Assert.Throws(() => fs.Seek(-1, SeekOrigin.Current)); + Assert.Throws(() => fs.Seek(0, SeekOrigin.Begin)); Assert.Throws(() => fs.ReadByte()); + + fs.Write(Encoding.ASCII.GetBytes("abcde")); + Assert.Equal(position + 5, fs.Position); + + Assert.Equal(position, fs.Seek(position, SeekOrigin.Begin)); + Assert.Equal(position + 1, fs.Seek(1, SeekOrigin.Current)); + fs.Write(Encoding.ASCII.GetBytes("xyz")); } + + Assert.Equal(initialContents + "axyze", File.ReadAllText(fileName)); } } } From 62966bcaf01204f4afca5d550785f48a5aeb0ee9 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 13 Jul 2021 09:34:34 -0700 Subject: [PATCH 490/926] Expose nullability info (#54985) * Expose nullability info from Reflection --- .../System.Private.CoreLib.Shared.projitems | 4 +- .../src/System/Reflection/NullabilityInfo.cs | 64 ++ .../Reflection/NullabilityInfoContext.cs | 521 +++++++++ .../System.Runtime/ref/System.Runtime.cs | 26 + .../tests/System.Runtime.Tests.csproj | 3 + .../Reflection/NullabilityInfoContextTests.cs | 985 ++++++++++++++++++ 6 files changed, 1602 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfo.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs create mode 100644 src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 23ffc05ed32..4be53fea197 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -615,6 +615,8 @@ + + @@ -2319,4 +2321,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfo.cs new file mode 100644 index 00000000000..2da593d7628 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfo.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.ObjectModel; + +namespace System.Reflection +{ + /// + /// A class that represents nullability info + /// + public sealed class NullabilityInfo + { + internal NullabilityInfo(Type type, NullabilityState readState, NullabilityState writeState, + NullabilityInfo? elementType, NullabilityInfo[] typeArguments) + { + Type = type; + ReadState = readState; + WriteState = writeState; + ElementType = elementType; + GenericTypeArguments = typeArguments; + } + + /// + /// The of the member or generic parameter + /// to which this NullabilityInfo belongs + /// + public Type Type { get; } + /// + /// The nullability read state of the member + /// + public NullabilityState ReadState { get; internal set; } + /// + /// The nullability write state of the member + /// + public NullabilityState WriteState { get; internal set; } + /// + /// If the member type is an array, gives the of the elements of the array, null otherwise + /// + public NullabilityInfo? ElementType { get; } + /// + /// If the member type is a generic type, gives the array of for each type parameter + /// + public NullabilityInfo[] GenericTypeArguments { get; } + } + + /// + /// An enum that represents nullability state + /// + public enum NullabilityState + { + /// + /// Nullability context not enabled (oblivious) + /// + Unknown, + /// + /// Non nullable value or reference type + /// + NotNull, + /// + /// Nullable value or reference type + /// + Nullable + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs new file mode 100644 index 00000000000..3d8b3766961 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs @@ -0,0 +1,521 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection +{ + /// + /// Provides APIs for populating nullability information/context from reflection members: + /// , , and . + /// + public sealed class NullabilityInfoContext + { + private const string CompilerServicesNameSpace = "System.Runtime.CompilerServices"; + private readonly Dictionary _publicOnlyModules = new(); + private readonly Dictionary _context = new(); + + [Flags] + private enum NotAnnotatedStatus + { + None = 0x0, // no restriction, all members annotated + Private = 0x1, // private members not annotated + Internal = 0x2 // internal members not annotated + } + + private NullabilityState GetNullableContext(MemberInfo? memberInfo) + { + while (memberInfo != null) + { + if (_context.TryGetValue(memberInfo, out NullabilityState state)) + { + return state; + } + + foreach (CustomAttributeData attribute in memberInfo.GetCustomAttributesData()) + { + if (attribute.AttributeType.Name == "NullableContextAttribute" && + attribute.AttributeType.Namespace == CompilerServicesNameSpace && + attribute.ConstructorArguments.Count == 1) + { + state = TranslateByte(attribute.ConstructorArguments[0].Value); + _context.Add(memberInfo, state); + return state; + } + } + + memberInfo = memberInfo.DeclaringType; + } + + return NullabilityState.Unknown; + } + + /// + /// Populates for the given . + /// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's + /// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state. + /// + /// The parameter which nullability info gets populated + /// If the parameterInfo parameter is null + /// + [RequiresUnreferencedCode("By default nullability attributes are trimmed by the trimmer")] + public NullabilityInfo Create(ParameterInfo parameterInfo) + { + if (parameterInfo is null) + { + throw new ArgumentNullException(nameof(parameterInfo)); + } + + if (parameterInfo.Member is MethodInfo method && IsPrivateOrInternalMethodAndAnnotationDisabled(method)) + { + return new NullabilityInfo(parameterInfo.ParameterType, NullabilityState.Unknown, NullabilityState.Unknown, null, Array.Empty()); + } + + IList attributes = parameterInfo.GetCustomAttributesData(); + NullabilityInfo nullability = GetNullabilityInfo(parameterInfo.Member, parameterInfo.ParameterType, attributes); + + if (nullability.ReadState != NullabilityState.Unknown) + { + CheckParameterMetadataType(parameterInfo, nullability); + } + + CheckNullabilityAttributes(nullability, attributes); + return nullability; + } + + private void CheckParameterMetadataType(ParameterInfo parameter, NullabilityInfo nullability) + { + if (parameter.Member is MethodInfo method) + { + MethodInfo metaMethod = GetMethodMetadataDefinition(method); + ParameterInfo? metaParameter = null; + if (string.IsNullOrEmpty(parameter.Name)) + { + metaParameter = metaMethod.ReturnParameter; + } + else + { + ParameterInfo[] parameters = metaMethod.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + if (parameter.Position == i && + parameter.Name == parameters[i].Name) + { + metaParameter = parameters[i]; + break; + } + } + } + + if (metaParameter != null) + { + CheckGenericParameters(nullability, metaMethod, metaParameter.ParameterType); + } + } + } + + private static MethodInfo GetMethodMetadataDefinition(MethodInfo method) + { + if (method.IsGenericMethod && !method.IsGenericMethodDefinition) + { + method = method.GetGenericMethodDefinition(); + } + + return (MethodInfo)GetMemberMetadataDefinition(method); + } + + private void CheckNullabilityAttributes(NullabilityInfo nullability, IList attributes) + { + foreach (CustomAttributeData attribute in attributes) + { + if (attribute.AttributeType.Namespace == "System.Diagnostics.CodeAnalysis") + { + if (attribute.AttributeType.Name == "NotNullAttribute" && + nullability.ReadState == NullabilityState.Nullable) + { + nullability.ReadState = NullabilityState.NotNull; + break; + } + else if ((attribute.AttributeType.Name == "MaybeNullAttribute" || + attribute.AttributeType.Name == "MaybeNullWhenAttribute") && + nullability.ReadState == NullabilityState.NotNull && + !nullability.Type.IsValueType) + { + nullability.ReadState = NullabilityState.Nullable; + break; + } + + if (attribute.AttributeType.Name == "DisallowNullAttribute" && + nullability.WriteState == NullabilityState.Nullable) + { + nullability.WriteState = NullabilityState.NotNull; + break; + } + else if (attribute.AttributeType.Name == "AllowNullAttribute" && + nullability.WriteState == NullabilityState.NotNull && + !nullability.Type.IsValueType) + { + nullability.WriteState = NullabilityState.Nullable; + break; + } + } + } + } + + /// + /// Populates for the given . + /// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's + /// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state. + /// + /// The parameter which nullability info gets populated + /// If the propertyInfo parameter is null + /// + [RequiresUnreferencedCode("By default nullability attributes are trimmed by the trimmer")] + public NullabilityInfo Create(PropertyInfo propertyInfo) + { + if (propertyInfo is null) + { + throw new ArgumentNullException(nameof(propertyInfo)); + } + + NullabilityInfo nullability = GetNullabilityInfo(propertyInfo, propertyInfo.PropertyType, propertyInfo.GetCustomAttributesData()); + MethodInfo? getter = propertyInfo.GetGetMethod(true); + MethodInfo? setter = propertyInfo.GetSetMethod(true); + + if (getter != null) + { + if (IsPrivateOrInternalMethodAndAnnotationDisabled(getter)) + { + nullability.ReadState = NullabilityState.Unknown; + } + + CheckNullabilityAttributes(nullability, getter.ReturnParameter.GetCustomAttributesData()); + } + else + { + nullability.ReadState = NullabilityState.Unknown; + } + + if (setter != null) + { + if (IsPrivateOrInternalMethodAndAnnotationDisabled(setter)) + { + nullability.WriteState = NullabilityState.Unknown; + } + + CheckNullabilityAttributes(nullability, setter.GetParameters()[0].GetCustomAttributesData()); + } + else + { + nullability.WriteState = NullabilityState.Unknown; + } + + return nullability; + } + + private bool IsPrivateOrInternalMethodAndAnnotationDisabled(MethodInfo method) + { + if ((method.IsPrivate || method.IsFamilyAndAssembly || method.IsAssembly) && + IsPublicOnly(method.IsPrivate, method.IsFamilyAndAssembly, method.IsAssembly, method.Module)) + { + return true; + } + + return false; + } + + /// + /// Populates for the given . + /// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's + /// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state. + /// + /// The parameter which nullability info gets populated + /// If the eventInfo parameter is null + /// + [RequiresUnreferencedCode("By default nullability attributes are trimmed by the trimmer")] + public NullabilityInfo Create(EventInfo eventInfo) + { + if (eventInfo is null) + { + throw new ArgumentNullException(nameof(eventInfo)); + } + + return GetNullabilityInfo(eventInfo, eventInfo.EventHandlerType!, eventInfo.GetCustomAttributesData()); + } + + /// + /// Populates for the given + /// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's + /// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state. + /// + /// The parameter which nullability info gets populated + /// If the fieldInfo parameter is null + /// + [RequiresUnreferencedCode("By default nullability attributes are trimmed by the trimmer")] + public NullabilityInfo Create(FieldInfo fieldInfo) + { + if (fieldInfo is null) + { + throw new ArgumentNullException(nameof(fieldInfo)); + } + + if (IsPrivateOrInternalFieldAndAnnotationDisabled(fieldInfo)) + { + return new NullabilityInfo(fieldInfo.FieldType, NullabilityState.Unknown, NullabilityState.Unknown, null, Array.Empty()); + } + + IList attributes = fieldInfo.GetCustomAttributesData(); + NullabilityInfo nullability = GetNullabilityInfo(fieldInfo, fieldInfo.FieldType, attributes); + CheckNullabilityAttributes(nullability, attributes); + return nullability; + } + + private bool IsPrivateOrInternalFieldAndAnnotationDisabled(FieldInfo fieldInfo) + { + if ((fieldInfo.IsPrivate || fieldInfo.IsFamilyAndAssembly || fieldInfo.IsAssembly) && + IsPublicOnly(fieldInfo.IsPrivate, fieldInfo.IsFamilyAndAssembly, fieldInfo.IsAssembly, fieldInfo.Module)) + { + return true; + } + + return false; + } + + private bool IsPublicOnly(bool isPrivate, bool isFamilyAndAssembly, bool isAssembly, Module module) + { + if (!_publicOnlyModules.TryGetValue(module, out NotAnnotatedStatus value)) + { + value = PopulateAnnotationInfo(module.GetCustomAttributesData()); + _publicOnlyModules.Add(module, value); + } + + if (value == NotAnnotatedStatus.None) + { + return false; + } + + if ((isPrivate || isFamilyAndAssembly) && value.HasFlag(NotAnnotatedStatus.Private) || + isAssembly && value.HasFlag(NotAnnotatedStatus.Internal)) + { + return true; + } + + return false; + } + + private NotAnnotatedStatus PopulateAnnotationInfo(IList customAttributes) + { + foreach (CustomAttributeData attribute in customAttributes) + { + if (attribute.AttributeType.Name == "NullablePublicOnlyAttribute" && + attribute.AttributeType.Namespace == CompilerServicesNameSpace && + attribute.ConstructorArguments.Count == 1) + { + if (attribute.ConstructorArguments[0].Value is bool boolValue && boolValue) + { + return NotAnnotatedStatus.Internal | NotAnnotatedStatus.Private; + } + else + { + return NotAnnotatedStatus.Private; + } + } + } + + return NotAnnotatedStatus.None; + } + + private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, IList customAttributes) => + GetNullabilityInfo(memberInfo, type, customAttributes, 0); + + private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, IList customAttributes, int index) + { + NullabilityState state = NullabilityState.Unknown; + + if (type.IsValueType) + { + if (Nullable.GetUnderlyingType(type) != null) + { + state = NullabilityState.Nullable; + } + else + { + state = NullabilityState.NotNull; + } + + return new NullabilityInfo(type, state, state, null, Array.Empty()); + } + else + { + if (!ParseNullableState(customAttributes, index, ref state)) + { + state = GetNullableContext(memberInfo); + } + + NullabilityInfo? elementState = null; + NullabilityInfo[]? genericArgumentsState = null; + + if (type.IsArray) + { + elementState = GetNullabilityInfo(memberInfo, type.GetElementType()!, customAttributes, index + 1); + } + else if (type.IsGenericType) + { + Type[] genericArguments = type.GetGenericArguments(); + genericArgumentsState = new NullabilityInfo[genericArguments.Length]; + + for (int i = 0; i < genericArguments.Length; i++) + { + genericArgumentsState[i] = GetNullabilityInfo(memberInfo, genericArguments[i], customAttributes, i + 1); + } + } + + NullabilityInfo nullability = new NullabilityInfo(type, state, state, elementState, genericArgumentsState ?? Array.Empty()); + if (state != NullabilityState.Unknown) + { + TryLoadGenericMetaTypeNullability(memberInfo, nullability); + } + + return nullability; + } + } + + private static bool ParseNullableState(IList customAttributes, int index, ref NullabilityState state) + { + foreach (CustomAttributeData attribute in customAttributes) + { + if (attribute.AttributeType.Name == "NullableAttribute" && + attribute.AttributeType.Namespace == CompilerServicesNameSpace && + attribute.ConstructorArguments.Count == 1) + { + object? o = attribute.ConstructorArguments[0].Value; + + if (o is byte b) + { + state = TranslateByte(b); + return true; + } + else if (o is ReadOnlyCollection args && + index < args.Count && + args[index].Value is byte elementB) + { + state = TranslateByte(elementB); + return true; + } + + break; + } + } + + return false; + } + + private void TryLoadGenericMetaTypeNullability(MemberInfo memberInfo, NullabilityInfo nullability) + { + MemberInfo? metaMember = GetMemberMetadataDefinition(memberInfo); + Type? metaType = null; + if (metaMember is FieldInfo field) + { + metaType = field.FieldType; + } + else if (metaMember is PropertyInfo property) + { + metaType = GetPropertyMetaType(property); + } + + if (metaType != null) + { + CheckGenericParameters(nullability, metaMember!, metaType); + } + } + + private static MemberInfo GetMemberMetadataDefinition(MemberInfo member) + { + Type? type = member.DeclaringType; + if ((type != null) && type.IsGenericType && !type.IsGenericTypeDefinition) + { + return type.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(member); + } + + return member; + } + + private static Type GetPropertyMetaType(PropertyInfo property) + { + if (property.GetGetMethod(true) is MethodInfo method) + { + return method.ReturnType; + } + + return property.GetSetMethod(true)!.GetParameters()[0].ParameterType; + } + + private void CheckGenericParameters(NullabilityInfo nullability, MemberInfo metaMember, Type metaType) + { + if (metaType.IsGenericParameter) + { + NullabilityState state = nullability.ReadState; + + if (!ParseNullableState(metaType.GetCustomAttributesData(), 0, ref state)) + { + state = GetNullableContext(metaType); + } + + nullability.ReadState = state; + nullability.WriteState = state; + } + else if (metaType.ContainsGenericParameters) + { + if (nullability.GenericTypeArguments.Length > 0) + { + Type[] genericArguments = metaType.GetGenericArguments(); + + for (int i = 0; i < genericArguments.Length; i++) + { + if (genericArguments[i].IsGenericParameter) + { + NullabilityInfo n = GetNullabilityInfo(metaMember, genericArguments[i], genericArguments[i].GetCustomAttributesData(), i + 1); + nullability.GenericTypeArguments[i].ReadState = n.ReadState; + nullability.GenericTypeArguments[i].WriteState = n.WriteState; + } + else + { + UpdateGenericArrayElements(nullability.GenericTypeArguments[i].ElementType, metaMember, genericArguments[i]); + } + } + } + else + { + UpdateGenericArrayElements(nullability.ElementType, metaMember, metaType); + } + } + } + + private void UpdateGenericArrayElements(NullabilityInfo? elementState, MemberInfo metaMember, Type metaType) + { + if (metaType.IsArray && elementState != null + && metaType.GetElementType()!.IsGenericParameter) + { + Type elementType = metaType.GetElementType()!; + NullabilityInfo n = GetNullabilityInfo(metaMember, elementType, elementType.GetCustomAttributesData(), 0); + elementState.ReadState = n.ReadState; + elementState.WriteState = n.WriteState; + } + } + + private static NullabilityState TranslateByte(object? value) + { + return value is byte b ? TranslateByte(b) : NullabilityState.Unknown; + } + + private static NullabilityState TranslateByte(byte b) => + b switch + { + 1 => NullabilityState.NotNull, + 2 => NullabilityState.Nullable, + _ => NullabilityState.Unknown + }; + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 43ca97b21d3..2656da8c999 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11933,6 +11933,32 @@ namespace System.Reflection public virtual System.Type ResolveType(int metadataToken, System.Type[]? genericTypeArguments, System.Type[]? genericMethodArguments) { throw null; } public override string ToString() { throw null; } } + public sealed class NullabilityInfoContext + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("By default nullability attributes are trimmed by the trimmer")] + public System.Reflection.NullabilityInfo Create(System.Reflection.EventInfo eventInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("By default nullability attributes are trimmed by the trimmer")] + public System.Reflection.NullabilityInfo Create(System.Reflection.FieldInfo fieldInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("By default nullability attributes are trimmed by the trimmer")] + public System.Reflection.NullabilityInfo Create(System.Reflection.ParameterInfo parameterInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("By default nullability attributes are trimmed by the trimmer")] + public System.Reflection.NullabilityInfo Create(System.Reflection.PropertyInfo propertyInfo) { throw null; } + } + public sealed class NullabilityInfo + { + internal NullabilityInfo(System.Type type, System.Reflection.NullabilityState readState, System.Reflection.NullabilityState writeState, System.Reflection.NullabilityInfo? elementType, System.Reflection.NullabilityInfo[] genericTypeArguments) { } + public System.Type Type { get; } + public System.Reflection.NullabilityState ReadState { get; } + public System.Reflection.NullabilityState WriteState { get; } + public System.Reflection.NullabilityInfo? ElementType { get; } + public System.Reflection.NullabilityInfo[] GenericTypeArguments { get; } + } + public enum NullabilityState + { + Unknown, + NotNull, + Nullable + } public delegate System.Reflection.Module ModuleResolveEventHandler(object sender, System.ResolveEventArgs e); [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] public sealed partial class ObfuscateAssemblyAttribute : System.Attribute diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 0ddefe7dcf0..405fd8b8701 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -7,6 +7,8 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser disable + + $(Features.Replace('nullablePublicOnly', '') $(DefineConstants);FEATURE_GENERIC_MATH @@ -123,6 +125,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs new file mode 100644 index 00000000000..07f244e9172 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs @@ -0,0 +1,985 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO.Enumeration; +using System.Text.RegularExpressions; +using Xunit; + +namespace System.Reflection.Tests +{ + public class NullabilityInfoContextTests + { + private static readonly NullabilityInfoContext nullabilityContext = new NullabilityInfoContext(); + private static readonly Type testType = typeof(TypeWithNotNullContext); + private static readonly Type genericType = typeof(GenericTest); + private static readonly Type stringType = typeof(string); + private static readonly BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + + public static IEnumerable FieldTestData() + { + yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "FieldUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldNonNullable", NullabilityState.NotNull, NullabilityState.NotNull, typeof(NullabilityInfoContextTests) }; + yield return new object[] { "FieldValueTypeUnknown", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) }; + yield return new object[] { "FieldValueTypeNotNull", NullabilityState.NotNull, NullabilityState.NotNull, typeof(double) }; + yield return new object[] { "FieldValueTypeNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "FieldAllowNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "FieldDisallowNull2", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "FieldAllowNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "FieldNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "FieldNotNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + } + + [Theory] + [MemberData(nameof(FieldTestData))] + public void FieldTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + FieldInfo field = testType.GetField(fieldName, flags); + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + Assert.Empty(nullability.GenericTypeArguments); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable EventTestData() + { + yield return new object[] { "EventNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(EventHandler) }; + yield return new object[] { "EventUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(EventHandler) }; + yield return new object[] { "EventNotNull", NullabilityState.NotNull, NullabilityState.NotNull, typeof(EventHandler) }; + } + + [Theory] + [MemberData(nameof(EventTestData))] + public void EventTest(string eventName, NullabilityState readState, NullabilityState writeState, Type type) + { + EventInfo @event = testType.GetEvent(eventName); + NullabilityInfo nullability = nullabilityContext.Create(@event); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + Assert.Empty(nullability.GenericTypeArguments); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable PropertyTestData() + { + yield return new object[] { "PropertyNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyNullableReadOnly", NullabilityState.Nullable, NullabilityState.Unknown, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) }; + yield return new object[] { "PropertyNonNullable", NullabilityState.NotNull, NullabilityState.NotNull, typeof(NullabilityInfoContextTests) }; + yield return new object[] { "PropertyValueTypeUnknown", NullabilityState.NotNull, NullabilityState.NotNull, typeof(short) }; + yield return new object[] { "PropertyValueType", NullabilityState.NotNull, NullabilityState.NotNull, typeof(float) }; + yield return new object[] { "PropertyValueTypeNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(long?) }; + yield return new object[] { "PropertyValueTypeDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(int?) }; + yield return new object[] { "PropertyValueTypeAllowNull", NullabilityState.NotNull, NullabilityState.NotNull, typeof(byte) }; + yield return new object[] { "PropertyValueTypeNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "PropertyValueTypeMaybeNull", NullabilityState.NotNull, NullabilityState.NotNull, typeof(byte) }; + yield return new object[] { "PropertyDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyAllowNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyDisallowNull2", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyAllowNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyMaybeNull2", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyNotNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + } + + [Theory] + [MemberData(nameof(PropertyTestData))] + public void PropertyTest(string propertyName, NullabilityState readState, NullabilityState writeState, Type type) + { + PropertyInfo property = testType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(readState, nullabilityContext.Create(property.GetMethod.ReturnParameter).ReadState); + Assert.Equal(writeState, nullability.WriteState); + if (property.SetMethod != null) + { + Assert.Equal(writeState, nullabilityContext.Create(property.SetMethod.GetParameters()[0]).WriteState); + } + Assert.Equal(type, nullability.Type); + Assert.Empty(nullability.GenericTypeArguments); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable ArrayPropertyTestData() + { + yield return new object[] { "PropertyArrayUnknown", NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyArrayNullNull", NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "PropertyArrayNullNon", NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "PropertyArrayNonNull", NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyArrayNonNon", NullabilityState.NotNull, NullabilityState.NotNull }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(ArrayPropertyTestData))] + public void ArrayPropertyTest(string propertyName, NullabilityState elementState, NullabilityState propertyState) + { + PropertyInfo property = testType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotNull(nullability.ElementType); + Assert.Equal(elementState, nullability.ElementType.ReadState); + Assert.Empty(nullability.GenericTypeArguments); + } + + public static IEnumerable GenericArrayPropertyTestData() + { + yield return new object[] { "PropertyArrayUnknown", NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyArrayNullNull", NullabilityState.Nullable, NullabilityState.Nullable }; // T?[]? PropertyArrayNullNull { get; set; } + yield return new object[] { "PropertyArrayNullNon", NullabilityState.Nullable, NullabilityState.NotNull }; // T?[] PropertyArrayNullNon { get; set; } + yield return new object[] { "PropertyArrayNonNull", NullabilityState.Nullable, NullabilityState.Nullable }; // T[]? PropertyArrayNonNull { get; set; } + yield return new object[] { "PropertyArrayNonNon", NullabilityState.Nullable, NullabilityState.NotNull }; // T[] PropertyArrayNonNon { get; set; } + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(GenericArrayPropertyTestData))] + public void GenericArrayPropertyTest(string propertyName, NullabilityState elementState, NullabilityState propertyState) + { + PropertyInfo property = genericType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotNull(nullability.ElementType); + Assert.Equal(elementState, nullability.ElementType.ReadState); + Assert.Empty(nullability.GenericTypeArguments); + } + + public static IEnumerable JaggedArrayPropertyTestData() + { + yield return new object[] { "PropertyJaggedArrayUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyJaggedArrayNullNullNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "PropertyJaggedArrayNullNullNon", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "PropertyJaggedArrayNullNonNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyJaggedArrayNonNullNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "PropertyJaggedArrayNullNonNon", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; + yield return new object[] { "PropertyJaggedArrayNonNonNull", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(JaggedArrayPropertyTestData))] + public void JaggedArrayPropertyTest(string propertyName, NullabilityState innermodtElementState, NullabilityState elementState, NullabilityState propertyState) + { + PropertyInfo property = testType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotNull(nullability.ElementType); + Assert.Equal(elementState, nullability.ElementType.ReadState); + Assert.NotNull(nullability.ElementType.ElementType); + Assert.Equal(innermodtElementState, nullability.ElementType.ElementType.ReadState); + Assert.Empty(nullability.GenericTypeArguments); + } + + public static IEnumerable TuplePropertyTestData() + { + yield return new object[] { "PropertyTupleUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyTupleNullNullNullNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "PropertyTupleNonNullNonNon", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; + yield return new object[] { "PropertyTupleNullNonNullNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "PropertyTupleNonNullNonNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyTupleNonNonNonNon", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(TuplePropertyTestData))] + public void TuplePropertyTest(string propertyName, NullabilityState genericParam1, NullabilityState genericParam2, NullabilityState genericParam3, NullabilityState propertyState) + { + PropertyInfo property = testType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotEmpty(nullability.GenericTypeArguments); + Assert.Equal(genericParam1, nullability.GenericTypeArguments[0].ReadState); + Assert.Equal(genericParam2, nullability.GenericTypeArguments[1].ReadState); + Assert.Equal(genericParam3, nullability.GenericTypeArguments[2].ReadState); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable GenericTuplePropertyTestData() + { + yield return new object[] { "PropertyTupleUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyTupleNullNullNullNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable }; // Tuple? + yield return new object[] { "PropertyTupleNonNullNonNon", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; // Tuple + yield return new object[] { "PropertyTupleNullNonNullNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable }; // Tuple? + yield return new object[] { "PropertyTupleNonNullNonNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; // Tuple? + yield return new object[] { "PropertyTupleNonNonNonNon", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; // Tuple + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(GenericTuplePropertyTestData))] + public void GenericTuplePropertyTest(string propertyName, NullabilityState genericParam1, NullabilityState genericParam2, NullabilityState genericParam3, NullabilityState propertyState) + { + PropertyInfo property = genericType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotEmpty(nullability.GenericTypeArguments); + Assert.Equal(genericParam1, nullability.GenericTypeArguments[0].ReadState); + Assert.Equal(genericParam2, nullability.GenericTypeArguments[1].ReadState); + Assert.Equal(genericParam3, nullability.GenericTypeArguments[2].ReadState); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable DictionaryPropertyTestData() + { + yield return new object[] { "PropertyDictionaryUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyDictionaryNullNullNullNon", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "PropertyDictionaryNonNullNonNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyDictionaryNullNonNonNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyDictionaryNonNullNonNon", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; + yield return new object[] { "PropertyDictionaryNonNonNonNull", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "PropertyDictionaryNonNonNonNon", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(DictionaryPropertyTestData))] + public void DictionaryPropertyTest(string propertyName, NullabilityState keyState, NullabilityState valueElement, NullabilityState valueState, NullabilityState propertyState) + { + PropertyInfo property = testType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotEmpty(nullability.GenericTypeArguments); + Assert.Equal(keyState, nullability.GenericTypeArguments[0].ReadState); + Assert.Equal(valueState, nullability.GenericTypeArguments[1].ReadState); + Assert.Equal(valueElement, nullability.GenericTypeArguments[1].ElementType.ReadState); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable GenericDictionaryPropertyTestData() + { + yield return new object[] { "PropertyDictionaryUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "PropertyDictionaryNullNullNullNon", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull }; // IDictionary PropertyDictionaryNullNullNullNon { get; set; } + yield return new object[] { "PropertyDictionaryNonNullNonNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; // IDictionary? PropertyDictionaryNonNullNonNull + yield return new object[] { "PropertyDictionaryNullNonNonNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; // IDictionary? PropertyDictionaryNullNonNonNull + yield return new object[] { "PropertyDictionaryNonNullNonNon", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; // IDictionary PropertyDictionaryNonNullNonNon + yield return new object[] { "PropertyDictionaryNonNonNonNull", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; // IDictionary? PropertyDictionaryNonNonNonNull + yield return new object[] { "PropertyDictionaryNonNonNonNon", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull }; // IDictionary PropertyDictionaryNonNonNonNon + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(GenericDictionaryPropertyTestData))] + public void GenericDictionaryPropertyTest(string propertyName, NullabilityState keyState, NullabilityState valueElement, NullabilityState valueState, NullabilityState propertyState) + { + PropertyInfo property = genericType.GetProperty(propertyName, flags); + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(propertyState, nullability.ReadState); + Assert.NotEmpty(nullability.GenericTypeArguments); + Assert.Equal(keyState, nullability.GenericTypeArguments[0].ReadState); + Assert.Equal(valueState, nullability.GenericTypeArguments[1].ReadState); + Assert.Equal(valueElement, nullability.GenericTypeArguments[1].ElementType.ReadState); + Assert.Null(nullability.ElementType); + } + + public static IEnumerable GenericPropertyReferenceTypeTestData() + { + yield return new object[] { "PropertyNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyNonNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyAllowNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "PropertyMaybeNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + } + +#nullable enable + [Theory] + [MemberData(nameof(GenericPropertyReferenceTypeTestData))] + public void GenericPropertyReferenceTypeTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + PropertyInfo property = typeof(GenericTest).GetProperty(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + Assert.Empty(nullability.GenericTypeArguments); + Assert.Null(nullability.ElementType); + + property = typeof(GenericTest).GetProperty(fieldName, flags)!; + nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + + property = typeof(GenericTest<>).GetProperty(fieldName, flags)!; + nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + } + + public static IEnumerable GenericFieldReferenceTypeTestData() + { + yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldNonNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldAllowNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; + } + + [Theory] + [MemberData(nameof(GenericFieldReferenceTypeTestData))] + public void GenericFieldReferenceTypeTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + FieldInfo field = typeof(GenericTest).GetField(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + Assert.Empty(nullability.GenericTypeArguments); + Assert.Null(nullability.ElementType); + + field = typeof(GenericTest).GetField(fieldName, flags)!; + nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + + field = typeof(GenericTest<>).GetField(fieldName, flags)!; + nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + } + + public static IEnumerable GenericFieldValueTypeTestData() + { + yield return new object[] { "FieldNullable", typeof(int) }; + yield return new object[] { "FieldUnknown", typeof(int) }; + yield return new object[] { "FieldNonNullable", typeof(int) }; + yield return new object[] { "FieldDisallowNull", typeof(int) }; + yield return new object[] { "FieldAllowNull", typeof(int) }; + yield return new object[] { "FieldMaybeNull", typeof(int) }; + yield return new object[] { "FieldNotNull", typeof(int) }; + } + + [Theory] + [MemberData(nameof(GenericFieldValueTypeTestData))] + public void GenericFieldValueTypeTest(string fieldName, Type type) + { + FieldInfo field = typeof(GenericTest).GetField(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(NullabilityState.NotNull, nullability.ReadState); + Assert.Equal(NullabilityState.NotNull, nullability.WriteState); + Assert.Equal(type, nullability.Type); + } + + public static IEnumerable GenericFieldNullableValueTypeTestData() + { + yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldUnknown", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldNonNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(int?) }; + yield return new object[] { "FieldAllowNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(int?) }; + } + + [Theory] + [MemberData(nameof(GenericFieldNullableValueTypeTestData))] + public void GenericFieldNullableValueTypeTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + FieldInfo field = typeof(GenericTest).GetField(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + } + + public static IEnumerable GenericNotnullConstraintTestData() + { + yield return new object[] { "FieldNullable", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "FieldUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) }; + yield return new object[] { "FieldNullableEnabled", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) }; + } + + [Theory] + [MemberData(nameof(GenericNotnullConstraintTestData))] + public void GenericNotNullConstraintTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + FieldInfo field = typeof(GenericTestConstrainedNotNull).GetField(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + } + + public static IEnumerable GenericStructConstraintTestData() + { + yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) }; + yield return new object[] { "FieldUnknown", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) }; + yield return new object[] { "FieldNullableEnabled", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) }; + } + + [Theory] + [MemberData(nameof(GenericStructConstraintTestData))] + public void GenericStructConstraintTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type) + { + FieldInfo field = typeof(GenericTestConstrainedStruct).GetField(fieldName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(field); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void GenericListTest() + { + Type listNullable = typeof(List); + MethodInfo addNullable = listNullable.GetMethod("Add")!; + NullabilityInfo nullability = nullabilityContext.Create(addNullable.GetParameters()[0]); + Assert.Equal(NullabilityState.Nullable, nullability.ReadState); + Assert.Equal(NullabilityState.Nullable, nullability.WriteState); + Assert.Equal(typeof(string), nullability.Type); + + Type lisNontNull = typeof(List); + MethodInfo addNotNull = lisNontNull.GetMethod("Add")!; + nullability = nullabilityContext.Create(addNotNull.GetParameters()[0]); + Assert.Equal(NullabilityState.Nullable, nullability.ReadState); + Assert.Equal(typeof(string), nullability.Type); + + Type listOpen = typeof(List<>); + MethodInfo addOpen = listOpen.GetMethod("Add")!; + nullability = nullabilityContext.Create(addOpen.GetParameters()[0]); + Assert.Equal(NullabilityState.Nullable, nullability.ReadState); + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void GenericListAndDictionaryFieldTest() + { + Type typeNullable = typeof(GenericTest); + FieldInfo listOfTNullable = typeNullable.GetField("FieldListOfT")!; + NullabilityInfo listNullability = nullabilityContext.Create(listOfTNullable); + Assert.Equal(NullabilityState.Nullable, listNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(typeof(string), listNullability.GenericTypeArguments[0].Type); + + FieldInfo dictStringToTNullable = typeNullable.GetField("FieldDictionaryStringToT")!; + NullabilityInfo dictNullability = nullabilityContext.Create(dictStringToTNullable); + Assert.Equal(NullabilityState.NotNull, dictNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(NullabilityState.Nullable, dictNullability.GenericTypeArguments[1].ReadState); + Assert.Equal(typeof(string), dictNullability.GenericTypeArguments[1].Type); + + Type typeNonNull = typeof(GenericTest); + FieldInfo listOfTNotNull = typeNonNull.GetField("FieldListOfT")!; + listNullability = nullabilityContext.Create(listOfTNotNull); + Assert.Equal(NullabilityState.Nullable, listNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(typeof(string), listNullability.GenericTypeArguments[0].Type); + + FieldInfo dictStringToTNotNull = typeNonNull.GetField("FieldDictionaryStringToT")!; + dictNullability = nullabilityContext.Create(dictStringToTNotNull); + Assert.Equal(NullabilityState.NotNull, dictNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(NullabilityState.Nullable, dictNullability.GenericTypeArguments[1].ReadState); + Assert.Equal(typeof(string), dictNullability.GenericTypeArguments[1].Type); + + Type typeOpen = typeof(GenericTest<>); + FieldInfo listOfTOpen = typeOpen.GetField("FieldListOfT")!; + listNullability = nullabilityContext.Create(listOfTOpen); + Assert.Equal(NullabilityState.Nullable, listNullability.GenericTypeArguments[0].ReadState); + // Assert.Equal(typeof(T), listNullability.TypeArguments[0].Type); + + FieldInfo dictStringToTOpen = typeOpen.GetField("FieldDictionaryStringToT")!; + dictNullability = nullabilityContext.Create(dictStringToTOpen); + Assert.Equal(NullabilityState.NotNull, dictNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(NullabilityState.Nullable, dictNullability.GenericTypeArguments[1].ReadState); + } + + public static IEnumerable MethodReturnParameterTestData() + { + yield return new object[] { "MethodReturnsUnknown", NullabilityState.Unknown, NullabilityState.Unknown}; + yield return new object[] { "MethodReturnsNullNon", NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "MethodReturnsNullNull", NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "MethodReturnsNonNull", NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "MethodReturnsNonNotNull", NullabilityState.NotNull, NullabilityState.NotNull }; + yield return new object[] { "MethodReturnsNonMaybeNull", NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "MethodReturnsNonNon", NullabilityState.NotNull, NullabilityState.NotNull }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(MethodReturnParameterTestData))] + public void MethodReturnParameterTest(string methodName, NullabilityState elementState, NullabilityState readState) + { + MethodInfo method = testType.GetMethod(methodName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(method.ReturnParameter); + Assert.Equal(readState, nullability.ReadState); + //Assert.Equal(readState, nullability.WriteState); + Assert.NotNull(nullability.ElementType); + Assert.Equal(elementState, nullability.ElementType!.ReadState); + Assert.Empty(nullability.GenericTypeArguments); + } + + public static IEnumerable MethodGenericReturnParameterTestData() + { + yield return new object[] { "MethodReturnsUnknown", NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "MethodReturnsGeneric", NullabilityState.Nullable, NullabilityState.Unknown }; + yield return new object[] { "MethodReturnsNullGeneric", NullabilityState.Nullable, NullabilityState.Unknown }; + yield return new object[] { "MethodReturnsGenericNotNull", NullabilityState.NotNull, NullabilityState.Unknown }; + yield return new object[] { "MethodReturnsGenericMaybeNull", NullabilityState.Nullable, NullabilityState.Unknown }; + yield return new object[] { "MethodNonNullListNullGeneric", NullabilityState.NotNull, NullabilityState.Nullable }; + yield return new object[] { "MethodNullListNonNullGeneric", NullabilityState.Nullable, NullabilityState.Nullable }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(MethodGenericReturnParameterTestData))] + public void MethodGenericReturnParameterTest(string methodName, NullabilityState readState, NullabilityState elementState) + { + MethodInfo method = typeof(GenericTest).GetMethod(methodName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(method.ReturnParameter); + Assert.Equal(readState, nullability.ReadState); + if (nullability.GenericTypeArguments.Length > 0) + { + Assert.Equal(elementState, nullability.GenericTypeArguments[0].ReadState); + } + } + + public static IEnumerable MethodParametersTestData() + { + yield return new object[] { "MethodParametersUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + yield return new object[] { "MethodNullNonNullNonNon", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull }; + yield return new object[] { "MethodNonNullNonNullNotNull", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "MethodNullNonNullNullNon", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull }; + yield return new object[] { "MethodAllowNullNonNonNonNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(MethodParametersTestData))] + public void MethodParametersTest(string methodName, NullabilityState stringState, NullabilityState dictKey, NullabilityState dictValueElement, NullabilityState dictValue, NullabilityState dictionaryState) + { + ParameterInfo[] parameters = testType.GetMethod(methodName, flags)!.GetParameters(); + NullabilityInfo stringNullability = nullabilityContext.Create(parameters[0]); + NullabilityInfo dictionaryNullability = nullabilityContext.Create(parameters[1]); + Assert.Equal(stringState, stringNullability.WriteState); + Assert.Equal(dictionaryState, dictionaryNullability.ReadState); + Assert.NotEmpty(dictionaryNullability.GenericTypeArguments); + Assert.Equal(dictKey, dictionaryNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(dictValue, dictionaryNullability.GenericTypeArguments[1].ReadState); + Assert.Equal(dictValueElement, dictionaryNullability.GenericTypeArguments[1].ElementType!.ReadState); + Assert.Null(dictionaryNullability.ElementType); + } + + public static IEnumerable MethodGenericParametersTestData() + { + yield return new object[] { "MethodParametersUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown}; + yield return new object[] { "MethodArgsNullGenericNullDictValueGeneric", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable }; + yield return new object[] { "MethodArgsGenericDictValueNullGeneric", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; + } + + [Theory] + [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")] + [MemberData(nameof(MethodGenericParametersTestData))] + public void MethodGenericParametersTest(string methodName, NullabilityState param1State, NullabilityState dictKey, NullabilityState dictValue, NullabilityState dictionaryState) + { + ParameterInfo[] parameters = typeof(GenericTest).GetMethod(methodName, flags)!.GetParameters(); + NullabilityInfo stringNullability = nullabilityContext.Create(parameters[0]); + NullabilityInfo dictionaryNullability = nullabilityContext.Create(parameters[1]); + Assert.Equal(param1State, stringNullability.WriteState); + Assert.Equal(dictionaryState, dictionaryNullability.ReadState); + Assert.NotEmpty(dictionaryNullability.GenericTypeArguments); + Assert.Equal(dictKey, dictionaryNullability.GenericTypeArguments[0].ReadState); + Assert.Equal(dictValue, dictionaryNullability.GenericTypeArguments[1].ReadState); + } + + public static IEnumerable StringTypeTestData() + { + yield return new object[] { "Format", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable, new Type[] { typeof(string), typeof(object), typeof(object) } }; + yield return new object[] { "ReplaceCore", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, new Type[] { typeof(string), typeof(string), typeof(CompareInfo), typeof(CompareOptions) } }; + yield return new object[] { "Join", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull, new Type[] { typeof(string), typeof(String?[]), typeof(int), typeof(int) } }; + } + + [Theory] + [SkipOnMono("Nullability attributes trimmed on Mono")] + [MemberData(nameof(StringTypeTestData))] + public void NullablePublicOnlyStringTypeTest(string methodName, NullabilityState param1State, NullabilityState param2State, NullabilityState param3State, Type[] types) + { + ParameterInfo[] parameters = stringType.GetMethod(methodName, flags, types)!.GetParameters(); + NullabilityInfo param1 = nullabilityContext.Create(parameters[0]); + NullabilityInfo param2 = nullabilityContext.Create(parameters[1]); + NullabilityInfo param3 = nullabilityContext.Create(parameters[2]); + Assert.Equal(param1State, param1.ReadState); + Assert.Equal(param2State, param2.ReadState); + Assert.Equal(param3State, param3.ReadState); + if (param2.ElementType != null) + { + Assert.Equal(NullabilityState.Nullable, param2.ElementType.ReadState); + } + } + + [Fact] + [SkipOnMono("Nullability attributes trimmed on Mono")] + public void NullablePublicOnlyOtherTypesTest() + { + Type type = typeof(Type); + FieldInfo privateNullableField = type.GetField("s_defaultBinder", flags)!; + NullabilityInfo info = nullabilityContext.Create(privateNullableField); + Assert.Equal(NullabilityState.Unknown, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + + MethodInfo internalNotNullableMethod = type.GetMethod("GetRootElementType", flags)!; + info = nullabilityContext.Create(internalNotNullableMethod.ReturnParameter); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.NotNull, info.WriteState); + + PropertyInfo publicNullableProperty = type.GetProperty("DeclaringType", flags)!; + info = nullabilityContext.Create(publicNullableProperty); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + + PropertyInfo publicGetPrivateSetNullableProperty = typeof(FileSystemEntry).GetProperty("Directory", flags)!; + info = nullabilityContext.Create(publicGetPrivateSetNullableProperty); + Assert.Equal(NullabilityState.NotNull, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + + MethodInfo protectedNullableReturnMethod = type.GetMethod("GetPropertyImpl", flags)!; + info = nullabilityContext.Create(protectedNullableReturnMethod.ReturnParameter); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + + MethodInfo privateValueTypeReturnMethod = type.GetMethod("BinarySearch", flags)!; + info = nullabilityContext.Create(privateValueTypeReturnMethod.ReturnParameter); + Assert.Equal(NullabilityState.Unknown, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + + Type regexType = typeof(Regex); + FieldInfo protectedInternalNullableField = regexType.GetField("pattern", flags)!; + info = nullabilityContext.Create(protectedInternalNullableField); + Assert.Equal(NullabilityState.Nullable, info.ReadState); + Assert.Equal(NullabilityState.Nullable, info.WriteState); + + privateNullableField = regexType.GetField("_code", flags)!; + info = nullabilityContext.Create(privateNullableField); + Assert.Equal(NullabilityState.Unknown, info.ReadState); + Assert.Equal(NullabilityState.Unknown, info.WriteState); + } + + public static IEnumerable DifferentContextTestData() + { + yield return new object[] { "PropertyDisabled", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) }; + yield return new object[] { "PropertyEnabledAllowNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyEnabledNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyEnabledMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyEnabledDisallowNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) }; + yield return new object[] { "PropertyEnabledNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(string) }; + yield return new object[] { "PropertyEnabledNonNullable", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) }; + } + [Theory] + [MemberData(nameof(DifferentContextTestData))] + public void NullabilityDifferentContextTest(string propertyName, NullabilityState readState, NullabilityState writeState, Type type) + { + Type noContext = typeof(TypeWithNoContext); + PropertyInfo property = noContext.GetProperty(propertyName, flags)!; + NullabilityInfo nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + + Type nullableContext = typeof(TypeWithNullableContext); + property = nullableContext.GetProperty(propertyName, flags)!; + nullability = nullabilityContext.Create(property); + Assert.Equal(readState, nullability.ReadState); + Assert.Equal(writeState, nullability.WriteState); + Assert.Equal(type, nullability.Type); + } + + [Fact] + public void AttributedParametersTest() + { + Type type = typeof(TypeWithNullableContext); + + // bool NotNullWhenParameter([DisallowNull] string? disallowNull, [NotNullWhen(true)] ref string? notNullWhen, Type? nullableType); + ParameterInfo[] notNullWhenParameters = type.GetMethod("NotNullWhenParameter", flags)!.GetParameters(); + NullabilityInfo disallowNull = nullabilityContext.Create(notNullWhenParameters[0]); + NullabilityInfo notNullWhen = nullabilityContext.Create(notNullWhenParameters[1]); + Assert.Equal(NullabilityState.Nullable, disallowNull.ReadState); + Assert.Equal(NullabilityState.NotNull, disallowNull.WriteState); + Assert.Equal(NullabilityState.Nullable, notNullWhen.ReadState); + Assert.Equal(NullabilityState.Nullable, notNullWhen.WriteState); + Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(notNullWhenParameters[1]).ReadState); + + // bool MaybeNullParameters([MaybeNull] string maybeNull, [MaybeNullWhen(false)] out string maybeNullWhen, Type? nullableType) + ParameterInfo[] maybeNullParameters = type.GetMethod("MaybeNullParameters", flags)!.GetParameters(); + NullabilityInfo maybeNull = nullabilityContext.Create(maybeNullParameters[0]); + NullabilityInfo maybeNullWhen = nullabilityContext.Create(maybeNullParameters[1]); + Assert.Equal(NullabilityState.Nullable, maybeNull.ReadState); + Assert.Equal(NullabilityState.NotNull, maybeNull.WriteState); + Assert.Equal(NullabilityState.Nullable, maybeNullWhen.ReadState); + Assert.Equal(NullabilityState.NotNull, maybeNullWhen.WriteState); + Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(maybeNullParameters[1]).ReadState); + + // string? AllowNullParameter([AllowNull] string allowNull, [NotNullIfNotNull("allowNull")] string? notNullIfNotNull) + ParameterInfo[] allowNullParameter = type.GetMethod("AllowNullParameter", flags)!.GetParameters(); + NullabilityInfo allowNull = nullabilityContext.Create(allowNullParameter[0]); + NullabilityInfo notNullIfNotNull = nullabilityContext.Create(allowNullParameter[1]); + Assert.Equal(NullabilityState.NotNull, allowNull.ReadState); + Assert.Equal(NullabilityState.Nullable, allowNull.WriteState); + Assert.Equal(NullabilityState.Nullable, notNullIfNotNull.ReadState); + Assert.Equal(NullabilityState.Nullable, notNullIfNotNull.WriteState); + Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(allowNullParameter[1]).ReadState); + + // [return: NotNullIfNotNull("nullable")] public string? NullablNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull) + ParameterInfo[] nullablNotNullIfNotNullReturn = type.GetMethod("NullablNotNullIfNotNullReturn", flags)!.GetParameters(); + NullabilityInfo returnNotNullIfNotNull = nullabilityContext.Create(type.GetMethod("NullablNotNullIfNotNullReturn", flags)!.ReturnParameter); + NullabilityInfo readNotNull = nullabilityContext.Create(nullablNotNullIfNotNullReturn[1]); + Assert.Equal(NullabilityState.Nullable, returnNotNullIfNotNull.ReadState); + Assert.Equal(NullabilityState.Nullable, returnNotNullIfNotNull.WriteState); + Assert.Equal(NullabilityState.NotNull, readNotNull.ReadState); + Assert.Equal(NullabilityState.Nullable, readNotNull.WriteState); + Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(nullablNotNullIfNotNullReturn[0]).ReadState); + + // public bool TryGetOutParameters(string id, [NotNullWhen(true)] out string? value, [MaybeNullWhen(false)] out string value2) + ParameterInfo[] tryGetOutParameters = type.GetMethod("TryGetOutParameters", flags)!.GetParameters(); + NullabilityInfo notNullWhenParam = nullabilityContext.Create(tryGetOutParameters[1]); + NullabilityInfo maybeNullWhenParam = nullabilityContext.Create(tryGetOutParameters[2]); + Assert.Equal(NullabilityState.Nullable, notNullWhenParam.ReadState); + Assert.Equal(NullabilityState.Nullable, notNullWhenParam.WriteState); + Assert.Equal(NullabilityState.Nullable, maybeNullWhenParam.ReadState); + Assert.Equal(NullabilityState.NotNull, maybeNullWhenParam.WriteState); + Assert.Equal(NullabilityState.NotNull, nullabilityContext.Create(tryGetOutParameters[0]).ReadState); + } + + public static IEnumerable RefReturnData() + { + yield return new object[] { "RefReturnUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown }; + // [return: MaybeNull] public ref string RefReturnMaybeNull([DisallowNull] ref string? id) + yield return new object[] { "RefReturnMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; + // public ref string RefReturnNotNullable([MaybeNull] ref string id) + yield return new object[] { "RefReturnNotNullable", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull }; + // [return: NotNull]public ref string? RefReturnNotNull([NotNull] ref string? id) + yield return new object[] { "RefReturnNotNull", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; + // publiic ref string? RefReturnNullable([AllowNull] ref string id) + yield return new object[] { "RefReturnNullable", NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable }; + } + + [Theory] + [MemberData(nameof(RefReturnData))] + public void RefReturnTestTest(string methodName, NullabilityState retReadState, NullabilityState retWriteState, NullabilityState paramReadState, NullabilityState paramWriteState) + { + MethodInfo method = typeof(TypeWithNullableContext).GetMethod(methodName, flags)!; + NullabilityInfo returnNullability = nullabilityContext.Create(method.ReturnParameter); + NullabilityInfo paramNullability = nullabilityContext.Create(method.GetParameters()[0]); + Assert.Equal(retReadState, returnNullability.ReadState); + Assert.Equal(retWriteState, returnNullability.WriteState); + Assert.Equal(paramReadState, paramNullability.ReadState); + Assert.Equal(paramWriteState, paramNullability.WriteState); + } + } + +#pragma warning disable CS0649, CS0067, CS0414 + public class TypeWithNullableContext + { +#nullable disable + public string PropertyDisabled { get; set; } + public ref string RefReturnUnknown(ref string id) { return ref id; } +#nullable enable + [AllowNull] public string PropertyEnabledAllowNull { get; set; } + [NotNull] public string? PropertyEnabledNotNull { get; set; } = null!; + [DisallowNull] public string? PropertyEnabledDisallowNull { get; set; } = null!; + [MaybeNull] public string PropertyEnabledMaybeNull { get; set; } + public string? PropertyEnabledNullable { get; set; } + public string PropertyEnabledNonNullable { get; set; } = null!; + bool NotNullWhenParameter([DisallowNull] string? disallowNull, [NotNullWhen(true)] ref string? notNullWhen, Type? nullableType) { return false; } + public bool MaybeNullParameters([MaybeNull] string maybeNull, [MaybeNullWhen(false)] out string maybeNullWhen, Type? nullableType) { maybeNullWhen = null; return false; } + public string? AllowNullParameter([AllowNull] string allowNull, [NotNullIfNotNull("allowNull")] string? notNullIfNotNull) { return null; } + [return: NotNullIfNotNull("nullable")] public string? NullablNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull) { readNotNull = string.Empty; return null!; } + public ref string? RefReturnNullable([AllowNull] ref string id) { return ref id!; } + [return: MaybeNull] public ref string RefReturnMaybeNull([DisallowNull] ref string? id) { return ref id; } + [return: NotNull] public ref string? RefReturnNotNull([NotNull] ref string? id) { id = string.Empty; return ref id!; } + public ref string RefReturnNotNullable([MaybeNull] ref string id) { return ref id; } + public bool TryGetOutParameters(string id, [NotNullWhen(true)] out string? value, [MaybeNullWhen(false)] out string value2) { value = null; value2 = null; return false; } + } + + public class TypeWithNoContext + { +#nullable disable + [AllowNull] public string PropertyDisabledAllowNull { get; set; } + [MaybeNull] public string PropertyDisabledMaybeNull { get; set; } + public string PropertyDisabled { get; set; } +#nullable enable + [AllowNull] public string PropertyEnabledAllowNull { get; set; } + [NotNull] public string? PropertyEnabledNotNull { get; set; } = null!; + [DisallowNull] public string? PropertyEnabledDisallowNull { get; set; } = null!; + [MaybeNull] public string PropertyEnabledMaybeNull { get; set; } + public string? PropertyEnabledNullable { get; set; } + public string PropertyEnabledNonNullable { get; set; } = null!; +#nullable disable + [return: NotNull, MaybeNull] + public string MethodNullableDisabled([AllowNull] string value, string ret) { return null; } + } + + public class TypeWithNotNullContext + { + public string PropertyUnknown { get; set; } + short PropertyValueTypeUnknown { get; set; } + public string[] PropertyArrayUnknown { get; set; } + private string[][] PropertyJaggedArrayUnknown { get; set; } + protected Tuple PropertyTupleUnknown { get; set; } + protected internal IDictionary PropertyDictionaryUnknown { get; set; } + + internal TypeWithNotNullContext FieldUnknown; + public int FieldValueTypeUnknown; + + public event EventHandler EventUnknown; + public string[] MethodReturnsUnknown() => null!; + public void MethodParametersUnknown(string s, IDictionary dict) { } +#nullable enable + public TypeWithNotNullContext? PropertyNullable { get; set; } + public TypeWithNotNullContext? PropertyNullableReadOnly { get; } + private NullabilityInfoContextTests PropertyNonNullable { get; set; } = null!; + internal float PropertyValueType { get; set; } + protected long? PropertyValueTypeNullable { get; set; } + [DisallowNull] public int? PropertyValueTypeDisallowNull { get; set; } + [NotNull] protected int? PropertyValueTypeNotNull { get; set; } + [MaybeNull] public byte PropertyValueTypeMaybeNull { get; set; } + [AllowNull] public byte PropertyValueTypeAllowNull { get; set; } + [DisallowNull] public string? PropertyDisallowNull { get; set; } + [AllowNull] public string PropertyAllowNull { get; set; } + [NotNull] public string? PropertyNotNull { get; set; } + [MaybeNull] public string PropertyMaybeNull { get; set; } + // only AllowNull matter + [AllowNull, DisallowNull] public string PropertyAllowNull2 { get; set; } + // only DisallowNull matter + [AllowNull, DisallowNull] public string? PropertyDisallowNull2 { get; set; } + // only NotNull matter + [NotNull, MaybeNull] public string? PropertyNotNull2 { get; set; } + // only MaybeNull matter + [NotNull, MaybeNull] public string PropertyMaybeNull2 { get; set; } + private protected string?[]?[]? PropertyJaggedArrayNullNullNull { get; set; } + public static string?[]?[] PropertyJaggedArrayNullNullNon { get; set; } = null!; + public string?[][]? PropertyJaggedArrayNullNonNull { get; set; } + public static string[]?[]? PropertyJaggedArrayNonNullNull { get; set; } + public string?[][] PropertyJaggedArrayNullNonNon { get; set; } = null!; + private static string[][]? PropertyJaggedArrayNonNonNull { get; set; } + public string?[]? PropertyArrayNullNull { get; set; } + static string?[] PropertyArrayNullNon { get; set; } = null!; + public string[]? PropertyArrayNonNull { get; } = null; + public string[] PropertyArrayNonNon { get; set; } = null!; + public Tuple? PropertyTupleNullNullNullNull { get; set; } + public Tuple PropertyTupleNonNullNonNon { get; set; } = null!; + internal Tuple? PropertyTupleNullNonNullNull { get; set; } + public Tuple? PropertyTupleNonNullNonNull { get; set; } + protected Tuple PropertyTupleNonNonNonNon { get; set; } = null!; + public IDictionary PropertyDictionaryNullNullNullNon { get; set; } = null!; + public IDictionary? PropertyDictionaryNonNullNonNull { get; set; } + IDictionary? PropertyDictionaryNullNonNonNull { get; set; } + public IDictionary PropertyDictionaryNonNullNonNon { get; set; } = null!; + private IDictionary? PropertyDictionaryNonNonNonNull { get; set; } + public IDictionary PropertyDictionaryNonNonNonNon { get; set; } = null!; + + private const string? FieldNullable = null; + protected static NullabilityInfoContextTests FieldNonNullable = null!; + public static double FieldValueTypeNotNull; + public readonly int? FieldValueTypeNullable; + [DisallowNull] public string? FieldDisallowNull; + [AllowNull] public string FieldAllowNull; + [NotNull] string? FieldNotNull = null; + [MaybeNull] public string FieldMaybeNull; + [AllowNull, DisallowNull] public string FieldAllowNull2; + [AllowNull, DisallowNull] public string? FieldDisallowNull2; + [NotNull, MaybeNull] internal string? FieldNotNull2; + [NotNull, MaybeNull] public string FieldMaybeNull2; + + public event EventHandler? EventNullable; + public event EventHandler EventNotNull = null!; + public string?[] MethodReturnsNullNon() => null!; + public string?[]? MethodReturnsNullNull() => null; + public string[]? MethodReturnsNonNull() => null; + [return: NotNull, MaybeNull] public string[]? MethodReturnsNonNotNull() => null!; // only NotNull is applicable + [return: MaybeNull] public string[] MethodReturnsNonMaybeNull() => null; + public string[] MethodReturnsNonNon() => null!; + public Tuple? MethodTupleNullNonNull() => null; + public IEnumerable?> MethodEnumerableNonNonNullUnknownNullNonNullNon() => null!; + public void MethodNullNonNullNonNon(string? s, IDictionary dict) { } + public void MethodNonNullNonNullNotNull(string s, [NotNull] IDictionary? dict) { dict = new Dictionary(); } + public void MethodNullNonNullNullNon(string? s, IDictionary dict) { } + public void MethodAllowNullNonNonNonNull([AllowNull] string s, IDictionary? dict) { } + } + + internal class GenericTest + { +#nullable disable + public T PropertyUnknown { get; set; } + protected T[] PropertyArrayUnknown { get; set; } + public Tuple PropertyTupleUnknown { get; set; } + private IDictionary PropertyDictionaryUnknown { get; set; } + public T FieldUnknown; + public T MethodReturnsUnknown() => default!; + public void MethodParametersUnknown(T s, IDictionary dict) { } +#nullable enable + + public T PropertyNonNullable { get; set; } = default!; + public T? PropertyNullable { get; set; } + [DisallowNull] public T PropertyDisallowNull { get; set; } = default!; + [NotNull] public T PropertyNotNull { get; set; } = default!; + [MaybeNull] public T PropertyMaybeNull { get; set; } + [AllowNull] public T PropertyAllowNull { get; set; } + internal T?[]? PropertyArrayNullNull { get; set; } + public T?[] PropertyArrayNullNon { get; set; } = null!; + T[]? PropertyArrayNonNull { get; set; } + public T[] PropertyArrayNonNon { get; set; } = null!; + public Tuple? PropertyTupleNullNullNullNull { get; set; } + public Tuple PropertyTupleNonNullNonNon { get; set; } = null!; + Tuple? PropertyTupleNullNonNullNull { get; set; } + public Tuple? PropertyTupleNonNullNonNull { get; set; } + public Tuple PropertyTupleNonNonNonNon { get; set; } = null!; + private IDictionary PropertyDictionaryNullNullNullNon { get; set; } = null!; + static IDictionary? PropertyDictionaryNonNullNonNull { get; set; } + public static IDictionary? PropertyDictionaryNullNonNonNull { get; set; } + public IDictionary PropertyDictionaryNonNullNonNon { get; set; } = null!; + protected IDictionary? PropertyDictionaryNonNonNonNull { get; set; } + public IDictionary PropertyDictionaryNonNonNonNon { get; set; } = null!; + + static T? FieldNullable = default; + public T FieldNonNullable = default!; + [DisallowNull] public T? FieldDisallowNull; + [AllowNull] protected T FieldAllowNull; + [NotNull] public T? FieldNotNull = default; + [MaybeNull] protected internal T FieldMaybeNull = default!; + public List FieldListOfT = default!; + public Dictionary FieldDictionaryStringToT = default!; + + public T MethodReturnsGeneric() => default!; + public T? MethodReturnsNullGeneric() => default; + [return: NotNull] public T MethodReturnsGenericNotNull() => default!; + [return: MaybeNull] public T MethodReturnsGenericMaybeNull() => default; + public List MethodNonNullListNullGeneric() => null!; + public List? MethodNullListNonNullGeneric() => null; + public void MethodArgsNullGenericNullDictValueGeneric(T? s, IDictionary? dict) { } + public void MethodArgsGenericDictValueNullGeneric(T s, IDictionary dict) { } + } + + internal class GenericTestConstrainedNotNull where T : notnull + { +#nullable disable + public T FieldUnknown; + public T PropertyUnknown { get; set; } +#nullable enable + + public T FieldNullableEnabled = default!; + public T? FieldNullable; + public T PropertyNullableEnabled { get; set; } = default!; + } + + internal class GenericTestConstrainedStruct where T : struct + { +#nullable disable + public T FieldUnknown; + public T PropertyUnknown { get; set; } +#nullable enable + + public T FieldNullableEnabled; + public T? FieldNullable; + public T PropertyNullableEnabled { get; set; } + } +} From 1c3d401d329c3305ca9b8fdbc36cb4d5ceba1a63 Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 13 Jul 2021 19:39:21 +0300 Subject: [PATCH 491/926] Import `cgt.un(op, 0)` as `NE(op, 0)` (#54539) * Some minor code modernization Use "genActualType" directly as it is now a templated method. Don't create casts to TYP_ULONG - they are identical to casts to TYP_LONG. TYP_ULONG is only relevant for checked casts. Add a TODO on addressing the duplicated logic that upcasts to native int from int32 on 64 bit. Use modern comments. Zero diffs. * Normalize GT_UN(op, 0) early in importer Normalizing this idiom helps downstream optimizations. * Solve most of the regressions In morph, when narrowing the AND operand, only insert casts if necessary - prefer to use "optNarrowTree". Otherwise we end up with redundant register shuffling. --- src/coreclr/jit/importer.cpp | 31 +++++++++----- src/coreclr/jit/morph.cpp | 82 ++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d49a12ffd13..c30e9a17784 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -13075,27 +13075,36 @@ void Compiler::impImportBlockCode(BasicBlock* block) op2 = impPopStack().val; op1 = impPopStack().val; -#ifdef TARGET_64BIT - if (varTypeIsI(op1->TypeGet()) && (genActualType(op2->TypeGet()) == TYP_INT)) + // Recognize the IL idiom of CGT_UN(op1, 0) and normalize + // it so that downstream optimizations don't have to. + if ((opcode == CEE_CGT_UN) && op2->IsIntegralConst(0)) { - op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, uns ? TYP_U_IMPL : TYP_I_IMPL); + oper = GT_NE; + uns = false; } - else if (varTypeIsI(op2->TypeGet()) && (genActualType(op1->TypeGet()) == TYP_INT)) + +#ifdef TARGET_64BIT + // TODO-Casts: create a helper that upcasts int32 -> native int when necessary. + // See also identical code in impGetByRefResultType and STSFLD import. + if (varTypeIsI(op1) && (genActualType(op2) == TYP_INT)) { - op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, uns ? TYP_U_IMPL : TYP_I_IMPL); + op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, TYP_I_IMPL); + } + else if (varTypeIsI(op2) && (genActualType(op1) == TYP_INT)) + { + op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, TYP_I_IMPL); } #endif // TARGET_64BIT - assertImp(genActualType(op1->TypeGet()) == genActualType(op2->TypeGet()) || - (varTypeIsI(op1->TypeGet()) && varTypeIsI(op2->TypeGet())) || - (varTypeIsFloating(op1->gtType) && varTypeIsFloating(op2->gtType))); + assertImp(genActualType(op1) == genActualType(op2) || (varTypeIsI(op1) && varTypeIsI(op2)) || + (varTypeIsFloating(op1) && varTypeIsFloating(op2))); - /* Create the comparison node */ + // Create the comparison node. op1 = gtNewOperNode(oper, TYP_INT, op1, op2); - /* TODO: setting both flags when only one is appropriate */ - if (opcode == CEE_CGT_UN || opcode == CEE_CLT_UN) + // TODO: setting both flags when only one is appropriate. + if (uns) { op1->gtFlags |= GTF_RELOP_NAN_UN | GTF_UNSIGNED; } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index f0bc076f235..354b1e4a357 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -12465,44 +12465,54 @@ DONE_MORPHING_CHILDREN: noway_assert(op1->TypeGet() == TYP_LONG && op1->OperGet() == GT_AND); - /* Is the result of the mask effectively an INT ? */ - - GenTree* andMask; - andMask = op1->AsOp()->gtOp2; - if (andMask->gtOper != GT_CNS_NATIVELONG) + // The transform below cannot preserve VNs. + if (fgGlobalMorph) { - goto COMPARE; + // Is the result of the mask effectively an INT ? + + GenTree* andMask = op1->AsOp()->gtOp2; + + if (andMask->gtOper != GT_CNS_NATIVELONG) + { + goto COMPARE; + } + if ((andMask->AsIntConCommon()->LngValue() >> 32) != 0) + { + goto COMPARE; + } + + // Now we narrow AsOp()->gtOp1 of AND to int. + if (optNarrowTree(op1->AsOp()->gtGetOp1(), TYP_LONG, TYP_INT, ValueNumPair(), false)) + { + optNarrowTree(op1->AsOp()->gtGetOp1(), TYP_LONG, TYP_INT, ValueNumPair(), true); + } + else + { + op1->AsOp()->gtOp1 = gtNewCastNode(TYP_INT, op1->AsOp()->gtGetOp1(), false, TYP_INT); + } + + // now replace the mask node (AsOp()->gtOp2 of AND node). + + noway_assert(andMask == op1->AsOp()->gtOp2); + + ival1 = (int)andMask->AsIntConCommon()->LngValue(); + andMask->SetOper(GT_CNS_INT); + andMask->gtType = TYP_INT; + andMask->AsIntCon()->gtIconVal = ival1; + + // now change the type of the AND node. + + op1->gtType = TYP_INT; + + // finally we replace the comparand. + + ival2 = (int)cns2->AsIntConCommon()->LngValue(); + cns2->SetOper(GT_CNS_INT); + cns2->gtType = TYP_INT; + + noway_assert(cns2 == op2); + cns2->AsIntCon()->gtIconVal = ival2; } - if ((andMask->AsIntConCommon()->LngValue() >> 32) != 0) - { - goto COMPARE; - } - - /* Now we know that we can cast AsOp()->gtOp1 of AND to int */ - - op1->AsOp()->gtOp1 = gtNewCastNode(TYP_INT, op1->AsOp()->gtOp1, false, TYP_INT); - - /* now replace the mask node (AsOp()->gtOp2 of AND node) */ - - noway_assert(andMask == op1->AsOp()->gtOp2); - - ival1 = (int)andMask->AsIntConCommon()->LngValue(); - andMask->SetOper(GT_CNS_INT); - andMask->gtType = TYP_INT; - andMask->AsIntCon()->gtIconVal = ival1; - - /* now change the type of the AND node */ - - op1->gtType = TYP_INT; - - /* finally we replace the comparand */ - - ival2 = (int)cns2->AsIntConCommon()->LngValue(); - cns2->SetOper(GT_CNS_INT); - cns2->gtType = TYP_INT; - - noway_assert(cns2 == op2); - cns2->AsIntCon()->gtIconVal = ival2; goto COMPARE; From 430d87f67d29bb2001ce9b53509488db754deab4 Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Tue, 13 Jul 2021 18:49:47 +0200 Subject: [PATCH 492/926] QUIC read pipeline changes (#55505) This brings changes to read states and behavior done initially in #52929 with my fixes to it to make all tests work --- .../Implementations/MsQuic/MsQuicStream.cs | 340 ++++++++++++------ .../tests/FunctionalTests/QuicStreamTests.cs | 122 ++++++- .../tests/FunctionalTests/QuicTestBase.cs | 54 ++- 3 files changed, 382 insertions(+), 134 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 437f81ce775..585aca851b3 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -36,11 +36,20 @@ namespace System.Net.Quic.Implementations.MsQuic public string TraceId = null!; // set in ctor. public ReadState ReadState; - public long ReadErrorCode = -1; - public readonly List ReceiveQuicBuffers = new List(); - // Resettable completions to be used for multiple calls to receive. - public readonly ResettableCompletionSource ReceiveResettableCompletionSource = new ResettableCompletionSource(); + // set when ReadState.Aborted: + public long ReadErrorCode = -1; + + // filled when ReadState.BuffersAvailable: + public QuicBuffer[] ReceiveQuicBuffers = Array.Empty(); + public int ReceiveQuicBuffersCount; + public int ReceiveQuicBuffersTotalBytes; + + // set when ReadState.PendingRead: + public Memory ReceiveUserBuffer; + public CancellationTokenRegistration ReceiveCancellationRegistration; + public MsQuicStream? RootedReceiveStream; // roots the stream in the pinned state to prevent GC during an async read I/O. + public readonly ResettableCompletionSource ReceiveResettableCompletionSource = new ResettableCompletionSource(); public SendState SendState; public long SendErrorCode = -1; @@ -340,7 +349,7 @@ namespace System.Net.Quic.Implementations.MsQuic } } - internal override async ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + internal override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -349,109 +358,151 @@ namespace System.Net.Quic.Implementations.MsQuic throw new InvalidOperationException(SR.net_quic_reading_notallowed); } - if (cancellationToken.IsCancellationRequested) - { - lock (_state) - { - if (_state.ReadState == ReadState.None) - { - _state.ReadState = ReadState.Aborted; - } - } - - throw new OperationCanceledException(cancellationToken); - } - if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{TraceId()} Stream reading into Memory of '{destination.Length}' bytes."); } - lock (_state) - { - if (_state.ReadState == ReadState.ReadsCompleted) - { - return 0; - } - else if (_state.ReadState == ReadState.Aborted) - { - throw ThrowHelper.GetStreamAbortedException(_state.ReadErrorCode); - } - else if (_state.ReadState == ReadState.ConnectionClosed) - { - throw GetConnectionAbortedException(_state); - } - } - - using CancellationTokenRegistration registration = cancellationToken.UnsafeRegister(static (s, token) => - { - var state = (State)s!; - bool shouldComplete = false; - lock (state) - { - if (state.ReadState == ReadState.None) - { - shouldComplete = true; - } - state.ReadState = ReadState.Aborted; - } - - if (shouldComplete) - { - state.ReceiveResettableCompletionSource.CompleteException( - ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException("Read was canceled", token))); - } - }, _state); - - // TODO there could potentially be a perf gain by storing the buffer from the initial read - // This reduces the amount of async calls, however it makes it so MsQuic holds onto the buffers - // longer than it needs to. We will need to benchmark this. - int length = (int)await _state.ReceiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); - - int actual = Math.Min(length, destination.Length); - - static unsafe void CopyToBuffer(Span destinationBuffer, List sourceBuffers) - { - Span slicedBuffer = destinationBuffer; - for (int i = 0; i < sourceBuffers.Count; i++) - { - QuicBuffer nativeBuffer = sourceBuffers[i]; - int length = Math.Min((int)nativeBuffer.Length, slicedBuffer.Length); - new Span(nativeBuffer.Buffer, length).CopyTo(slicedBuffer); - if (length < nativeBuffer.Length) - { - // The buffer passed in was larger that the received data, return - return; - } - slicedBuffer = slicedBuffer.Slice(length); - } - } - - CopyToBuffer(destination.Span, _state.ReceiveQuicBuffers); + ReadState readState; + long abortError = -1; + bool canceledSynchronously = false; lock (_state) { - if (_state.ReadState == ReadState.IndividualReadComplete) + readState = _state.ReadState; + abortError = _state.ReadErrorCode; + + if (readState != ReadState.PendingRead && cancellationToken.IsCancellationRequested) + { + readState = ReadState.Aborted; + _state.ReadState = ReadState.Aborted; + canceledSynchronously = true; + } + else if (readState == ReadState.None) + { + Debug.Assert(_state.RootedReceiveStream is null); + + _state.ReceiveUserBuffer = destination; + _state.RootedReceiveStream = this; + _state.ReadState = ReadState.PendingRead; + + if (cancellationToken.CanBeCanceled) + { + _state.ReceiveCancellationRegistration = cancellationToken.UnsafeRegister(static (obj, token) => + { + var state = (State)obj!; + bool completePendingRead; + + lock (state) + { + completePendingRead = state.ReadState == ReadState.PendingRead; + state.RootedReceiveStream = null; + state.ReceiveUserBuffer = null; + state.ReadState = ReadState.Aborted; + } + + if (completePendingRead) + { + state.ReceiveResettableCompletionSource.CompleteException(ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException(token))); + } + }, _state); + } + else + { + _state.ReceiveCancellationRegistration = default; + } + + return _state.ReceiveResettableCompletionSource.GetValueTask(); + } + else if (readState == ReadState.IndividualReadComplete) { - _state.ReceiveQuicBuffers.Clear(); - ReceiveComplete(actual); - EnableReceive(); _state.ReadState = ReadState.None; + + int taken = CopyMsQuicBuffersToUserBuffer(_state.ReceiveQuicBuffers.AsSpan(0, _state.ReceiveQuicBuffersCount), destination.Span); + ReceiveComplete(taken); + + if (taken != _state.ReceiveQuicBuffersTotalBytes) + { + // Need to re-enable receives because MsQuic will pause them when we don't consume the entire buffer. + EnableReceive(); + } + + return new ValueTask(taken); } } - return actual; + Exception? ex = null; + + switch (readState) + { + case ReadState.ReadsCompleted: + return new ValueTask(0); + case ReadState.PendingRead: + ex = new InvalidOperationException("Only one read is supported at a time."); + break; + case ReadState.Aborted: + ex = + canceledSynchronously ? new OperationCanceledException(cancellationToken) : // aborted by token being canceled before the async op started. + abortError == -1 ? new QuicOperationAbortedException() : // aborted by user via some other operation. + new QuicStreamAbortedException(abortError); // aborted by peer. + + break; + case ReadState.ConnectionClosed: + default: + Debug.Assert(readState == ReadState.ConnectionClosed, $"{nameof(ReadState)} of '{readState}' is unaccounted for in {nameof(ReadAsync)}."); + ex = GetConnectionAbortedException(_state); + break; + } + + return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(ex!)); + } + + /// The number of bytes copied. + private static unsafe int CopyMsQuicBuffersToUserBuffer(ReadOnlySpan sourceBuffers, Span destinationBuffer) + { + Debug.Assert(sourceBuffers.Length != 0); + + int originalDestinationLength = destinationBuffer.Length; + QuicBuffer nativeBuffer; + int takeLength = 0; + int i = 0; + + do + { + nativeBuffer = sourceBuffers[i]; + takeLength = Math.Min((int)nativeBuffer.Length, destinationBuffer.Length); + + new Span(nativeBuffer.Buffer, takeLength).CopyTo(destinationBuffer); + destinationBuffer = destinationBuffer.Slice(takeLength); + } + while (destinationBuffer.Length != 0 && ++i < sourceBuffers.Length); + + return originalDestinationLength - destinationBuffer.Length; } - // TODO do we want this to be a synchronization mechanism to cancel a pending read - // If so, we need to complete the read here as well. internal override void AbortRead(long errorCode) { ThrowIfDisposed(); + bool shouldComplete = false; lock (_state) { - _state.ReadState = ReadState.Aborted; + if (_state.ReadState == ReadState.PendingRead) + { + shouldComplete = true; + _state.RootedReceiveStream = null; + _state.ReceiveUserBuffer = null; + } + if (_state.ReadState < ReadState.ReadsCompleted) + { + _state.ReadState = ReadState.Aborted; + } + } + + if (shouldComplete) + { + _state.ReceiveResettableCompletionSource.CompleteException( + ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Read was aborted"))); } StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, errorCode); @@ -702,7 +753,8 @@ namespace System.Net.Quic.Implementations.MsQuic private void EnableReceive() { - MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_state.Handle, enabled: true); + uint status = MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_state.Handle, enabled: true); + QuicExceptionHelpers.ThrowIfFailed(status, "StreamReceiveSetEnabled failed."); } private static uint NativeCallbackHandler( @@ -776,31 +828,80 @@ namespace System.Net.Quic.Implementations.MsQuic private static unsafe uint HandleEventRecv(State state, ref StreamEvent evt) { - StreamEventDataReceive receiveEvent = evt.Data.Receive; - for (int i = 0; i < receiveEvent.BufferCount; i++) + ref StreamEventDataReceive receiveEvent = ref evt.Data.Receive; + + if (receiveEvent.BufferCount == 0) { - state.ReceiveQuicBuffers.Add(receiveEvent.Buffers[i]); + // This is a 0-length receive that happens once reads are finished (via abort or otherwise). + // State changes for this are handled elsewhere. + return MsQuicStatusCodes.Success; } + int readLength; + bool shouldComplete = false; lock (state) { - if (state.ReadState == ReadState.None) + switch (state.ReadState) { - shouldComplete = true; - } - if (state.ReadState != ReadState.ConnectionClosed) - { - state.ReadState = ReadState.IndividualReadComplete; + case ReadState.None: + // ReadAsync() hasn't been called yet. Stash the buffer so the next ReadAsync call completes synchronously. + + if ((uint)state.ReceiveQuicBuffers.Length < receiveEvent.BufferCount) + { + QuicBuffer[] oldReceiveBuffers = state.ReceiveQuicBuffers; + state.ReceiveQuicBuffers = ArrayPool.Shared.Rent((int)receiveEvent.BufferCount); + + if (oldReceiveBuffers.Length != 0) // don't return Array.Empty. + { + ArrayPool.Shared.Return(oldReceiveBuffers); + } + } + + for (uint i = 0; i < receiveEvent.BufferCount; ++i) + { + state.ReceiveQuicBuffers[i] = receiveEvent.Buffers[i]; + } + + state.ReceiveQuicBuffersCount = (int)receiveEvent.BufferCount; + state.ReceiveQuicBuffersTotalBytes = checked((int)receiveEvent.TotalBufferLength); + state.ReadState = ReadState.IndividualReadComplete; + return MsQuicStatusCodes.Pending; + case ReadState.PendingRead: + // There is a pending ReadAsync(). + + state.ReceiveCancellationRegistration.Unregister(); + shouldComplete = true; + state.RootedReceiveStream = null; + state.ReadState = ReadState.None; + + readLength = CopyMsQuicBuffersToUserBuffer(new ReadOnlySpan(receiveEvent.Buffers, (int)receiveEvent.BufferCount), state.ReceiveUserBuffer.Span); + state.ReceiveUserBuffer = null; + break; + default: + Debug.Assert(state.ReadState is ReadState.Aborted or ReadState.ConnectionClosed, $"Unexpected {nameof(ReadState)} '{state.ReadState}' in {nameof(HandleEventRecv)}."); + + // There was a race between a user aborting the read stream and the callback being ran. + // This will eat any received data. + return MsQuicStatusCodes.Success; } } + // We're completing a pending read. if (shouldComplete) { - state.ReceiveResettableCompletionSource.Complete((uint)receiveEvent.TotalBufferLength); + state.ReceiveResettableCompletionSource.Complete(readLength); } - return MsQuicStatusCodes.Pending; + // Returning Success when the entire buffer hasn't been consumed will cause MsQuic to disable further receive events until EnableReceive() is called. + // Returning Continue will cause a second receive event to fire immediately after this returns, but allows MsQuic to clean up its buffers. + + uint ret = (uint)readLength == receiveEvent.TotalBufferLength + ? MsQuicStatusCodes.Success + : MsQuicStatusCodes.Continue; + + receiveEvent.TotalBufferLength = (uint)readLength; + return ret; } private static uint HandleEventPeerRecvAborted(State state, ref StreamEvent evt) @@ -871,12 +972,13 @@ namespace System.Net.Quic.Implementations.MsQuic // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"{state.TraceId} Stream completing resettable event source."); - if (state.ReadState == ReadState.None) + if (state.ReadState == ReadState.PendingRead) { shouldReadComplete = true; + state.RootedReceiveStream = null; + state.ReceiveUserBuffer = null; } - - if (state.ReadState != ReadState.ConnectionClosed && state.ReadState != ReadState.Aborted) + if (state.ReadState < ReadState.ReadsCompleted) { state.ReadState = ReadState.ReadsCompleted; } @@ -924,9 +1026,11 @@ namespace System.Net.Quic.Implementations.MsQuic bool shouldComplete = false; lock (state) { - if (state.ReadState == ReadState.None) + if (state.ReadState == ReadState.PendingRead) { shouldComplete = true; + state.RootedReceiveStream = null; + state.ReceiveUserBuffer = null; } state.ReadState = ReadState.Aborted; state.ReadErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; @@ -950,12 +1054,13 @@ namespace System.Net.Quic.Implementations.MsQuic // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"{state.TraceId} Stream completing resettable event source."); - if (state.ReadState == ReadState.None) + if (state.ReadState == ReadState.PendingRead) { shouldComplete = true; + state.RootedReceiveStream = null; + state.ReceiveUserBuffer = null; } - - if (state.ReadState != ReadState.ConnectionClosed) + if (state.ReadState < ReadState.ReadsCompleted) { state.ReadState = ReadState.ReadsCompleted; } @@ -1249,11 +1354,11 @@ namespace System.Net.Quic.Implementations.MsQuic lock (state) { - if (state.ReadState == ReadState.None) + shouldCompleteRead = state.ReadState == ReadState.PendingRead; + if (state.ReadState < ReadState.ReadsCompleted) { - shouldCompleteRead = true; + state.ReadState = ReadState.ConnectionClosed; } - state.ReadState = ReadState.ConnectionClosed; if (state.SendState == SendState.None || state.SendState == SendState.Pending) { @@ -1304,6 +1409,16 @@ namespace System.Net.Quic.Implementations.MsQuic private static Exception GetConnectionAbortedException(State state) => ThrowHelper.GetConnectionAbortedException(state.ConnectionState.AbortErrorCode); + // Read state transitions: + // + // None --(data arrives in event RECV)-> IndividualReadComplete --(user calls ReadAsync() & completes syncronously)-> None + // None --(user calls ReadAsync() & waits)-> PendingRead --(data arrives in event RECV & completes user's ReadAsync())-> None + // Any non-final state --(event PEER_SEND_SHUTDOWN or SHUTDOWN_COMPLETED with ConnectionClosed=false)-> ReadsCompleted + // Any non-final state --(event PEER_SEND_ABORT)-> Aborted + // Any non-final state --(user calls AbortRead())-> Aborted + // Any state --(CancellationToken's cancellation for ReadAsync())-> Aborted (TODO: should it be only for non-final as others?) + // Any non-final state --(event SHUTDOWN_COMPLETED with ConnectionClosed=true)-> ConnectionClosed + // Closed - no transitions, set for Unidirectional write-only streams private enum ReadState { /// @@ -1316,6 +1431,13 @@ namespace System.Net.Quic.Implementations.MsQuic /// IndividualReadComplete, + /// + /// User called ReadAsync() + /// + PendingRead, + + // following states are final: + /// /// The peer has gracefully shutdown their sends / our receives; the stream's reads are complete. /// diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index b21135000ac..4890ebea5f3 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -408,32 +408,122 @@ namespace System.Net.Quic.Tests } [Fact] - public async Task Read_StreamAborted_Throws() + public async Task Read_WriteAborted_Throws() { const int ExpectedErrorCode = 0xfffffff; - await Task.Run(async () => + using SemaphoreSlim sem = new SemaphoreSlim(0); + + await RunBidirectionalClientServer( + async clientStream => + { + await clientStream.WriteAsync(new byte[1]); + + await sem.WaitAsync(); + clientStream.AbortWrite(ExpectedErrorCode); + }, + async serverStream => + { + int received = await serverStream.ReadAsync(new byte[1]); + Assert.Equal(1, received); + + sem.Release(); + + byte[] buffer = new byte[100]; + QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => serverStream.ReadAsync(buffer).AsTask()); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + }); + } + + [Fact] + public async Task Read_SynchronousCompletion_Success() + { + using SemaphoreSlim sem = new SemaphoreSlim(0); + + await RunBidirectionalClientServer( + async clientStream => + { + await clientStream.WriteAsync(new byte[1]); + sem.Release(); + clientStream.Shutdown(); + sem.Release(); + }, + async serverStream => + { + await sem.WaitAsync(); + await Task.Delay(1000); + + ValueTask task = serverStream.ReadAsync(new byte[1]); + Assert.True(task.IsCompleted); + + int received = await task; + Assert.Equal(1, received); + + await sem.WaitAsync(); + await Task.Delay(1000); + + task = serverStream.ReadAsync(new byte[1]); + Assert.True(task.IsCompleted); + + received = await task; + Assert.Equal(0, received); + }); + } + + [Fact] + public async Task ReadOutstanding_ReadAborted_Throws() + { + // aborting doesn't work properly on mock + if (typeof(T) == typeof(MockProviderFactory)) { - using QuicListener listener = CreateQuicListener(); - ValueTask serverConnectionTask = listener.AcceptConnectionAsync(); + return; + } - using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - await clientConnection.ConnectAsync(); + const int ExpectedErrorCode = 0xfffffff; - using QuicConnection serverConnection = await serverConnectionTask; + using SemaphoreSlim sem = new SemaphoreSlim(0); - await using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); - await clientStream.WriteAsync(new byte[1]); + await RunBidirectionalClientServer( + async clientStream => + { + await sem.WaitAsync(); + }, + async serverStream => + { + Task exTask = Assert.ThrowsAsync(() => serverStream.ReadAsync(new byte[1]).AsTask()); - await using QuicStream serverStream = await serverConnection.AcceptStreamAsync(); - await serverStream.ReadAsync(new byte[1]); + Assert.False(exTask.IsCompleted); - clientStream.AbortWrite(ExpectedErrorCode); + serverStream.AbortRead(ExpectedErrorCode); - byte[] buffer = new byte[100]; - QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => serverStream.ReadAsync(buffer).AsTask()); - Assert.Equal(ExpectedErrorCode, ex.ErrorCode); - }).WaitAsync(TimeSpan.FromSeconds(15)); + await exTask; + + sem.Release(); + }); + } + + [Fact] + public async Task Read_ConcurrentReads_Throws() + { + using SemaphoreSlim sem = new SemaphoreSlim(0); + + await RunBidirectionalClientServer( + async clientStream => + { + await sem.WaitAsync(); + }, + async serverStream => + { + ValueTask readTask = serverStream.ReadAsync(new byte[1]); + Assert.False(readTask.IsCompleted); + + await Assert.ThrowsAsync(async () => await serverStream.ReadAsync(new byte[1])); + + sem.Release(); + + int res = await readTask; + Assert.Equal(0, res); + }); } [Fact] diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index 803b2d40705..da2cfb37f41 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Xunit; +using System.Diagnostics.Tracing; namespace System.Net.Quic.Tests { @@ -114,22 +115,19 @@ namespace System.Net.Quic.Tests { using QuicListener listener = CreateQuicListener(); - var serverFinished = new ManualResetEventSlim(); - var clientFinished = new ManualResetEventSlim(); + using var serverFinished = new SemaphoreSlim(0); + using var clientFinished = new SemaphoreSlim(0); for (int i = 0; i < iterations; ++i) { - serverFinished.Reset(); - clientFinished.Reset(); - await new[] { Task.Run(async () => { using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await serverFunction(serverConnection); - serverFinished.Set(); - clientFinished.Wait(); + serverFinished.Release(); + await clientFinished.WaitAsync(); await serverConnection.CloseAsync(0); }), Task.Run(async () => @@ -137,14 +135,52 @@ namespace System.Net.Quic.Tests using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); await clientConnection.ConnectAsync(); await clientFunction(clientConnection); - clientFinished.Set(); - serverFinished.Wait(); + clientFinished.Release(); + await serverFinished.WaitAsync(); await clientConnection.CloseAsync(0); }) }.WhenAllOrAnyFailed(millisecondsTimeout); } } + internal async Task RunStreamClientServer(Func clientFunction, Func serverFunction, bool bidi, int iterations, int millisecondsTimeout) + { + byte[] buffer = new byte[1] { 42 }; + + await RunClientServer( + clientFunction: async connection => + { + await using QuicStream stream = bidi ? connection.OpenBidirectionalStream() : connection.OpenUnidirectionalStream(); + // Open(Bi|Uni)directionalStream only allocates ID. We will force stream opening + // by Writing there and receiving data on the other side. + await stream.WriteAsync(buffer); + + await clientFunction(stream); + + stream.Shutdown(); + await stream.ShutdownCompleted(); + }, + serverFunction: async connection => + { + await using QuicStream stream = await connection.AcceptStreamAsync(); + Assert.Equal(1, await stream.ReadAsync(buffer)); + + await serverFunction(stream); + + stream.Shutdown(); + await stream.ShutdownCompleted(); + }, + iterations, + millisecondsTimeout + ); + } + + internal Task RunBidirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000) + => RunStreamClientServer(clientFunction, serverFunction, bidi: true, iterations, millisecondsTimeout); + + internal Task RunUnirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000) + => RunStreamClientServer(clientFunction, serverFunction, bidi: false, iterations, millisecondsTimeout); + internal static async Task ReadAll(QuicStream stream, byte[] buffer) { Memory memory = buffer; From cf7394339937535dd97ed1082bb6d0d30f34eab0 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 13 Jul 2021 19:57:45 +0300 Subject: [PATCH 493/926] Add AttributeTargets.Interface to JsonConverterAttribute (#54922) * Allow JsonConverterAttribute usage on interfaces. Fix #33112 * update ApiCompat baseline --- .../System.Text.Json/ref/System.Text.Json.cs | 2 +- .../Attributes/JsonConverterAttribute.cs | 2 +- .../CustomConverterTests.Interface.cs | 60 +++++++++++++++++++ .../System.Text.Json.Tests.csproj | 1 + .../ApiCompatBaseline.PreviousNetCoreApp.txt | 3 +- 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Interface.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 8e87154e803..a7d5a44feb6 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -763,7 +763,7 @@ namespace System.Text.Json.Serialization internal JsonConverter() { } public abstract bool CanConvert(System.Type typeToConvert); } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Enum | System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface | System.AttributeTargets.Enum | System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple=false)] public partial class JsonConverterAttribute : System.Text.Json.Serialization.JsonAttribute { protected JsonConverterAttribute() { } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonConverterAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonConverterAttribute.cs index 098b1e175e0..e456a009178 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonConverterAttribute.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonConverterAttribute.cs @@ -15,7 +15,7 @@ namespace System.Text.Json.Serialization /// or there is another on a property or field /// of the same type. /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public class JsonConverterAttribute : JsonAttribute { /// diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Interface.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Interface.cs new file mode 100644 index 00000000000..da56c49591f --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Interface.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Collections.Generic; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public static partial class CustomConverterTests + { + [JsonConverter(typeof(MyInterfaceConverter))] + private interface IMyInterface + { + int IntValue { get; set; } + string StringValue { get; set; } + } + + // A custom converter that writes and reads the string property as a top-level value + private class MyInterfaceConverter : JsonConverter + { + public override IMyInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => new MyClass + { + IntValue = 42, + StringValue = reader.GetString() + }; + + public override void Write(Utf8JsonWriter writer, IMyInterface value, JsonSerializerOptions options) => writer.WriteStringValue(value.StringValue); + } + + private class MyClass : IMyInterface + { + public int IntValue { get; set; } + public string StringValue { get; set; } + } + + [Fact] + public static void CustomInterfaceConverter_Serialization() + { + IMyInterface value = new MyClass { IntValue = 11, StringValue = "myString" }; + + string expectedJson = "\"myString\""; + string actualJson = JsonSerializer.Serialize(value); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public static void CustomInterfaceConverter_Deserialization() + { + string json = "\"myString\""; + + IMyInterface result = JsonSerializer.Deserialize(json); + + Assert.IsType(result); + Assert.Equal("myString", result.StringValue); + Assert.Equal(42, result.IntValue); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 62b64663119..41a5ec38d5a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -119,6 +119,7 @@ + diff --git a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt index 23ae4522899..54945167821 100644 --- a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt +++ b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt @@ -184,4 +184,5 @@ CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesGcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesCcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesGcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. -Total Issues: 170 +CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Text.Json.Serialization.JsonConverterAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=false)]' in the implementation. +Total Issues: 171 From 9a9b105bfa8e6803534333a096cb5af8490b5340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Tue, 13 Jul 2021 19:18:26 +0200 Subject: [PATCH 494/926] H/3 Server Cert validation callback exception fix (#55526) * Fix and test. * MsQuicConnection now call the cert validation callback only once, removed code duplication --- .../Http/SocketsHttpHandler/ConnectHelper.cs | 13 +++-- .../SocketsHttpHandler/HttpConnectionPool.cs | 2 +- .../HttpClientHandlerTest.Http3.cs | 58 +++++++++++++++++++ .../MsQuic/MsQuicConnection.cs | 4 ++ 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index 97e8478fa74..84d6ff8b345 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -33,7 +33,7 @@ namespace System.Net.Http } } - public static ValueTask EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, bool async, Stream stream, CancellationToken cancellationToken) + private static SslClientAuthenticationOptions SetUpRemoteCertificateValidationCallback(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request) { // If there's a cert validation callback, and if it came from HttpClientHandler, // wrap the original delegate in order to change the sender to be the request message (expected by HttpClientHandler's delegate). @@ -52,12 +52,13 @@ namespace System.Net.Http }; } - // Create the SslStream, authenticate, and return it. - return EstablishSslConnectionAsyncCore(async, stream, sslOptions, cancellationToken); + return sslOptions; } - private static async ValueTask EstablishSslConnectionAsyncCore(bool async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken) + public static async ValueTask EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, bool async, Stream stream, CancellationToken cancellationToken) { + sslOptions = SetUpRemoteCertificateValidationCallback(sslOptions, request); + SslStream sslStream = new SslStream(stream); try @@ -104,8 +105,10 @@ namespace System.Net.Http [SupportedOSPlatform("windows")] [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - public static async ValueTask ConnectQuicAsync(QuicImplementationProvider quicImplementationProvider, DnsEndPoint endPoint, SslClientAuthenticationOptions? clientAuthenticationOptions, CancellationToken cancellationToken) + public static async ValueTask ConnectQuicAsync(HttpRequestMessage request, QuicImplementationProvider quicImplementationProvider, DnsEndPoint endPoint, SslClientAuthenticationOptions clientAuthenticationOptions, CancellationToken cancellationToken) { + clientAuthenticationOptions = SetUpRemoteCertificateValidationCallback(clientAuthenticationOptions, request); + QuicConnection con = new QuicConnection(quicImplementationProvider, endPoint, clientAuthenticationOptions); try { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index e84f21dce3b..480724dd8eb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -725,7 +725,7 @@ namespace System.Net.Http QuicConnection quicConnection; try { - quicConnection = await ConnectHelper.ConnectQuicAsync(Settings._quicImplementationProvider ?? QuicImplementationProviders.Default, new DnsEndPoint(authority.IdnHost, authority.Port), _sslOptionsHttp3, cancellationToken).ConfigureAwait(false); + quicConnection = await ConnectHelper.ConnectQuicAsync(request, Settings._quicImplementationProvider ?? QuicImplementationProviders.Default, new DnsEndPoint(authority.IdnHost, authority.Port), _sslOptionsHttp3!, cancellationToken).ConfigureAwait(false); } catch { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index d4d806f6881..b3551199a29 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -314,6 +314,64 @@ namespace System.Net.Http.Functional.Tests await new[] { clientTask, serverTask }.WhenAllOrAnyFailed(20_000); } + [Fact] + public async Task ServerCertificateCustomValidationCallback_Succeeds() + { + // Mock doesn't make use of cart validation callback. + if (UseQuicImplementationProvider == QuicImplementationProviders.Mock) + { + return; + } + + HttpRequestMessage? callbackRequest = null; + int invocationCount = 0; + + var httpClientHandler = CreateHttpClientHandler(); + httpClientHandler.ServerCertificateCustomValidationCallback = (request, _, _, _) => + { + callbackRequest = request; + ++invocationCount; + return true; + }; + + using Http3LoopbackServer server = CreateHttp3LoopbackServer(); + using HttpClient client = CreateHttpClient(httpClientHandler); + + Task serverTask = Task.Run(async () => + { + using Http3LoopbackConnection connection = (Http3LoopbackConnection)await server.EstablishGenericConnectionAsync(); + using Http3LoopbackStream stream = await connection.AcceptRequestStreamAsync(); + await stream.HandleRequestAsync(); + using Http3LoopbackStream stream2 = await connection.AcceptRequestStreamAsync(); + await stream2.HandleRequestAsync(); + }); + + var request = new HttpRequestMessage(HttpMethod.Get, server.Address); + request.Version = HttpVersion.Version30; + request.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + var response = await client.SendAsync(request); + + response.EnsureSuccessStatusCode(); + Assert.Equal(HttpVersion.Version30, response.Version); + Assert.Same(request, callbackRequest); + Assert.Equal(1, invocationCount); + + // Second request, the callback shouldn't be hit at all. + callbackRequest = null; + + request = new HttpRequestMessage(HttpMethod.Get, server.Address); + request.Version = HttpVersion.Version30; + request.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + response = await client.SendAsync(request); + + response.EnsureSuccessStatusCode(); + Assert.Equal(HttpVersion.Version30, response.Version); + Assert.Null(callbackRequest); + Assert.Equal(1, invocationCount); + } + [OuterLoop] [ConditionalTheory(nameof(IsMsQuicSupported))] [MemberData(nameof(InteropUris))] diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index ec1710316c1..3eea4d19c69 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -418,6 +418,10 @@ namespace System.Net.Quic.Implementations.MsQuic if (connection._remoteCertificateValidationCallback != null) { bool success = connection._remoteCertificateValidationCallback(connection, certificate, chain, sslPolicyErrors); + // Unset the callback to prevent multiple invocations of the callback per a single connection. + // Return the same value as the custom callback just did. + connection._remoteCertificateValidationCallback = (_, _, _, _) => success; + if (!success && NetEventSource.Log.IsEnabled()) NetEventSource.Error(state, $"{state.TraceId} Remote certificate rejected by verification callback"); return success ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; From a490a3417adb78dbc36891624e67720ebdca919f Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 13 Jul 2021 10:26:42 -0700 Subject: [PATCH 495/926] Add ConfigurationManager(#55338) * ConfigurationManager : IConfigurationRoot, IConfigurationBuilder --- .../ref/Microsoft.Extensions.Configuration.cs | 15 + .../src/ConfigurationManager.cs | 353 +++++ .../src/ConfigurationRoot.cs | 56 +- .../tests/ConfigurationManagerTest.cs | 1201 +++++++++++++++++ 4 files changed, 1599 insertions(+), 26 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs create mode 100644 src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs diff --git a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs index c3cc43f7eff..feef64fc2dd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs @@ -28,6 +28,21 @@ namespace Microsoft.Extensions.Configuration public bool ShouldDisposeConfiguration { get { throw null; } set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } + public sealed partial class ConfigurationManager : Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable + { + public ConfigurationManager() { } + public string this[string key] { get { throw null; } set { throw null; } } + public IConfigurationSection GetSection(string key) { throw null; } + public System.Collections.Generic.IEnumerable GetChildren() { throw null; } + public void Dispose() { throw null; } + System.Collections.Generic.IDictionary IConfigurationBuilder.Properties { get { throw null; } } + System.Collections.Generic.IList IConfigurationBuilder.Sources { get { throw null; } } + Microsoft.Extensions.Configuration.IConfigurationBuilder IConfigurationBuilder.Add(Microsoft.Extensions.Configuration.IConfigurationSource source) { throw null; } + Microsoft.Extensions.Configuration.IConfigurationRoot IConfigurationBuilder.Build() { throw null; } + System.Collections.Generic.IEnumerable IConfigurationRoot.Providers { get { throw null; } } + void IConfigurationRoot.Reload() { throw null; } + Primitives.IChangeToken IConfiguration.GetReloadToken() { throw null; } + } public partial class ConfigurationBuilder : Microsoft.Extensions.Configuration.IConfigurationBuilder { public ConfigurationBuilder() { } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs new file mode 100644 index 00000000000..9578f3c334a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs @@ -0,0 +1,353 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.Configuration +{ + /// + /// Configuration is mutable configuration object. It is both an and an . + /// As sources are added, it updates its current view of configuration. Once Build is called, configuration is frozen. + /// + public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable + { + private readonly ConfigurationSources _sources; + private readonly ConfigurationBuilderProperties _properties; + + private readonly object _providerLock = new(); + private readonly List _providers = new(); + private readonly List _changeTokenRegistrations = new(); + private ConfigurationReloadToken _changeToken = new(); + + /// + /// Creates an empty mutable configuration object that is both an and an . + /// + public ConfigurationManager() + { + _sources = new ConfigurationSources(this); + _properties = new ConfigurationBuilderProperties(this); + + // Make sure there's some default storage since there are no default providers. + this.AddInMemoryCollection(); + + AddSource(_sources[0]); + } + + /// + public string this[string key] + { + get + { + lock (_providerLock) + { + return ConfigurationRoot.GetConfiguration(_providers, key); + } + } + set + { + lock (_providerLock) + { + ConfigurationRoot.SetConfiguration(_providers, key, value); + } + } + } + + /// + public IConfigurationSection GetSection(string key) => new ConfigurationSection(this, key); + + /// + public IEnumerable GetChildren() + { + lock (_providerLock) + { + // ToList() to eagerly evaluate inside lock. + return this.GetChildrenImplementation(null).ToList(); + } + } + + IDictionary IConfigurationBuilder.Properties => _properties; + + IList IConfigurationBuilder.Sources => _sources; + + IEnumerable IConfigurationRoot.Providers + { + get + { + lock (_providerLock) + { + return new List(_providers); + } + } + } + + /// + public void Dispose() + { + lock (_providerLock) + { + DisposeRegistrationsAndProvidersUnsynchronized(); + } + } + + IConfigurationBuilder IConfigurationBuilder.Add(IConfigurationSource source) + { + _sources.Add(source ?? throw new ArgumentNullException(nameof(source))); + return this; + } + + IConfigurationRoot IConfigurationBuilder.Build() => this; + + IChangeToken IConfiguration.GetReloadToken() => _changeToken; + + void IConfigurationRoot.Reload() + { + lock (_providerLock) + { + foreach (var provider in _providers) + { + provider.Load(); + } + } + + RaiseChanged(); + } + + private void RaiseChanged() + { + var previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); + previousToken.OnReload(); + } + + // Don't rebuild and reload all providers in the common case when a source is simply added to the IList. + private void AddSource(IConfigurationSource source) + { + lock (_providerLock) + { + var provider = source.Build(this); + _providers.Add(provider); + + provider.Load(); + _changeTokenRegistrations.Add(ChangeToken.OnChange(() => provider.GetReloadToken(), () => RaiseChanged())); + } + + RaiseChanged(); + } + + // Something other than Add was called on IConfigurationBuilder.Sources or IConfigurationBuilder.Properties has changed. + private void ReloadSources() + { + lock (_providerLock) + { + DisposeRegistrationsAndProvidersUnsynchronized(); + + _changeTokenRegistrations.Clear(); + _providers.Clear(); + + foreach (var source in _sources) + { + _providers.Add(source.Build(this)); + } + + foreach (var p in _providers) + { + p.Load(); + _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged())); + } + } + + RaiseChanged(); + } + + private void DisposeRegistrationsAndProvidersUnsynchronized() + { + // dispose change token registrations + foreach (var registration in _changeTokenRegistrations) + { + registration.Dispose(); + } + + // dispose providers + foreach (var provider in _providers) + { + (provider as IDisposable)?.Dispose(); + } + } + + private class ConfigurationSources : IList + { + private readonly List _sources = new(); + private readonly ConfigurationManager _config; + + public ConfigurationSources(ConfigurationManager config) + { + _config = config; + } + + public IConfigurationSource this[int index] + { + get => _sources[index]; + set + { + _sources[index] = value; + _config.ReloadSources(); + } + } + + public int Count => _sources.Count; + + public bool IsReadOnly => false; + + public void Add(IConfigurationSource source) + { + _sources.Add(source); + _config.AddSource(source); + } + + public void Clear() + { + _sources.Clear(); + _config.ReloadSources(); + } + + public bool Contains(IConfigurationSource source) + { + return _sources.Contains(source); + } + + public void CopyTo(IConfigurationSource[] array, int arrayIndex) + { + _sources.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _sources.GetEnumerator(); + } + + public int IndexOf(IConfigurationSource source) + { + return _sources.IndexOf(source); + } + + public void Insert(int index, IConfigurationSource source) + { + _sources.Insert(index, source); + _config.ReloadSources(); + } + + public bool Remove(IConfigurationSource source) + { + var removed = _sources.Remove(source); + _config.ReloadSources(); + return removed; + } + + public void RemoveAt(int index) + { + _sources.RemoveAt(index); + _config.ReloadSources(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private class ConfigurationBuilderProperties : IDictionary + { + private readonly Dictionary _properties = new(); + private readonly ConfigurationManager _config; + + public ConfigurationBuilderProperties(ConfigurationManager config) + { + _config = config; + } + + public object this[string key] + { + get => _properties[key]; + set + { + _properties[key] = value; + _config.ReloadSources(); + } + } + + public ICollection Keys => _properties.Keys; + + public ICollection Values => _properties.Values; + + public int Count => _properties.Count; + + public bool IsReadOnly => false; + + public void Add(string key, object value) + { + _properties.Add(key, value); + _config.ReloadSources(); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)_properties).Add(item); + _config.ReloadSources(); + } + + public void Clear() + { + _properties.Clear(); + _config.ReloadSources(); + } + + public bool Contains(KeyValuePair item) + { + return _properties.Contains(item); + } + + public bool ContainsKey(string key) + { + return _properties.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_properties).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return _properties.GetEnumerator(); + } + + public bool Remove(string key) + { + var wasRemoved = _properties.Remove(key); + _config.ReloadSources(); + return wasRemoved; + } + + public bool Remove(KeyValuePair item) + { + var wasRemoved = ((IDictionary)_properties).Remove(item); + _config.ReloadSources(); + return wasRemoved; + } + + public bool TryGetValue(string key, out object value) + { + return _properties.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _properties.GetEnumerator(); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs index 455efdb4176..cfe56a20978 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs @@ -49,32 +49,8 @@ namespace Microsoft.Extensions.Configuration /// The configuration value. public string this[string key] { - get - { - for (int i = _providers.Count - 1; i >= 0; i--) - { - IConfigurationProvider provider = _providers[i]; - - if (provider.TryGet(key, out string value)) - { - return value; - } - } - - return null; - } - set - { - if (_providers.Count == 0) - { - throw new InvalidOperationException(SR.Error_NoSources); - } - - foreach (IConfigurationProvider provider in _providers) - { - provider.Set(key, value); - } - } + get => GetConfiguration(_providers, key); + set => SetConfiguration(_providers, key, value); } /// @@ -134,5 +110,33 @@ namespace Microsoft.Extensions.Configuration (provider as IDisposable)?.Dispose(); } } + + internal static string GetConfiguration(IList providers, string key) + { + for (int i = providers.Count - 1; i >= 0; i--) + { + IConfigurationProvider provider = providers[i]; + + if (provider.TryGet(key, out string value)) + { + return value; + } + } + + return null; + } + + internal static void SetConfiguration(IList providers, string key, string value) + { + if (providers.Count == 0) + { + throw new InvalidOperationException(SR.Error_NoSources); + } + + foreach (IConfigurationProvider provider in providers) + { + provider.Set(key, value); + } + } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs new file mode 100644 index 00000000000..6e7a4919b25 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs @@ -0,0 +1,1201 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration.Memory; +using Microsoft.Extensions.Primitives; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Configuration.Test +{ + public class ConfigurationManagerTest + { + [Fact] + public void AutoUpdates() + { + var config = new ConfigurationManager(); + + config.AddInMemoryCollection(new Dictionary + { + { "TestKey", "TestValue" }, + }); + + Assert.Equal("TestValue", config["TestKey"]); + } + + [Fact] + public void TriggersReloadTokenOnSourceAddition() + { + var config = new ConfigurationManager(); + + var reloadToken = ((IConfiguration)config).GetReloadToken(); + + Assert.False(reloadToken.HasChanged); + + config.AddInMemoryCollection(new Dictionary + { + { "TestKey", "TestValue" }, + }); + + Assert.True(reloadToken.HasChanged); + } + + [Fact] + public void SettingValuesWorksWithoutManuallyAddingSource() + { + var config = new ConfigurationManager + { + ["TestKey"] = "TestValue", + }; + + Assert.Equal("TestValue", config["TestKey"]); + } + + [Fact] + public void SettingConfigValuesDoesNotTriggerReloadToken() + { + var config = new ConfigurationManager(); + var reloadToken = ((IConfiguration)config).GetReloadToken(); + + config["TestKey"] = "TestValue"; + + Assert.Equal("TestValue", config["TestKey"]); + + // ConfigurationRoot doesn't fire the token today when the setter is called. + Assert.False(reloadToken.HasChanged); + } + + [Fact] + public void SettingIConfigurationBuilderPropertiesReloadsSources() + { + var config = new ConfigurationManager(); + IConfigurationBuilder configBuilder = config; + + config["PreReloadTestConfigKey"] = "PreReloadTestConfigValue"; + + var reloadToken1 = ((IConfiguration)config).GetReloadToken(); + // Changing Properties causes all the IConfigurationSources to be reload. + configBuilder.Properties["TestPropertyKey"] = "TestPropertyValue"; + + var reloadToken2 = ((IConfiguration)config).GetReloadToken(); + config["PostReloadTestConfigKey"] = "PostReloadTestConfigValue"; + + Assert.Equal("TestPropertyValue", configBuilder.Properties["TestPropertyKey"]); + Assert.Null(config["TestPropertyKey"]); + + // Changes before the reload are lost by the MemoryConfigurationSource. + Assert.Null(config["PreReloadTestConfigKey"]); + Assert.Equal("PostReloadTestConfigValue", config["PostReloadTestConfigKey"]); + + Assert.True(reloadToken1.HasChanged); + Assert.False(reloadToken2.HasChanged); + } + + [Fact] + public void DisposesProvidersOnDispose() + { + var provider1 = new TestConfigurationProvider("foo", "foo-value"); + var provider2 = new DisposableTestConfigurationProvider("bar", "bar-value"); + var provider3 = new TestConfigurationProvider("baz", "baz-value"); + var provider4 = new DisposableTestConfigurationProvider("qux", "qux-value"); + var provider5 = new DisposableTestConfigurationProvider("quux", "quux-value"); + + var config = new ConfigurationManager(); + IConfigurationBuilder builder = config; + + builder.Add(new TestConfigurationSource(provider1)); + builder.Add(new TestConfigurationSource(provider2)); + builder.Add(new TestConfigurationSource(provider3)); + builder.Add(new TestConfigurationSource(provider4)); + builder.Add(new TestConfigurationSource(provider5)); + + Assert.Equal("foo-value", config["foo"]); + Assert.Equal("bar-value", config["bar"]); + Assert.Equal("baz-value", config["baz"]); + Assert.Equal("qux-value", config["qux"]); + Assert.Equal("quux-value", config["quux"]); + + config.Dispose(); + + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + Assert.True(provider5.IsDisposed); + } + + [Fact] + public void DisposesProvidersOnRemoval() + { + var provider1 = new TestConfigurationProvider("foo", "foo-value"); + var provider2 = new DisposableTestConfigurationProvider("bar", "bar-value"); + var provider3 = new TestConfigurationProvider("baz", "baz-value"); + var provider4 = new DisposableTestConfigurationProvider("qux", "qux-value"); + var provider5 = new DisposableTestConfigurationProvider("quux", "quux-value"); + + var source1 = new TestConfigurationSource(provider1); + var source2 = new TestConfigurationSource(provider2); + var source3 = new TestConfigurationSource(provider3); + var source4 = new TestConfigurationSource(provider4); + var source5 = new TestConfigurationSource(provider5); + + var config = new ConfigurationManager(); + IConfigurationBuilder builder = config; + + builder.Add(source1); + builder.Add(source2); + builder.Add(source3); + builder.Add(source4); + builder.Add(source5); + + Assert.Equal("foo-value", config["foo"]); + Assert.Equal("bar-value", config["bar"]); + Assert.Equal("baz-value", config["baz"]); + Assert.Equal("qux-value", config["qux"]); + Assert.Equal("quux-value", config["quux"]); + + builder.Sources.Remove(source2); + builder.Sources.Remove(source4); + + // While only provider2 and provider4 need to be disposed here, we do not assert provider5 is not disposed + // because even though it's unnecessary, Configuration disposes all providers on removal and rebuilds + // all the sources. While not optimal, this should be a pretty rare scenario. + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + + config.Dispose(); + + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + Assert.True(provider5.IsDisposed); + } + + [Fact] + public void DisposesChangeTokenRegistrationsOnDispose() + { + var changeToken = new TestChangeToken(); + var providerMock = new Mock(); + providerMock.Setup(p => p.GetReloadToken()).Returns(changeToken); + + var config = new ConfigurationManager(); + + ((IConfigurationBuilder)config).Add(new TestConfigurationSource(providerMock.Object)); + + Assert.NotEmpty(changeToken.Callbacks); + + config.Dispose(); + + Assert.Empty(changeToken.Callbacks); + } + + [Fact] + public void DisposesChangeTokenRegistrationsOnRemoval() + { + var changeToken = new TestChangeToken(); + var providerMock = new Mock(); + providerMock.Setup(p => p.GetReloadToken()).Returns(changeToken); + + var source = new TestConfigurationSource(providerMock.Object); + + var config = new ConfigurationManager(); + IConfigurationBuilder builder = config; + + builder.Add(source); + + Assert.NotEmpty(changeToken.Callbacks); + + builder.Sources.Remove(source); + + Assert.Empty(changeToken.Callbacks); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ChainedConfigurationIsDisposedOnDispose(bool shouldDispose) + { + var provider = new DisposableTestConfigurationProvider("foo", "foo-value"); + var chainedConfig = new ConfigurationRoot(new IConfigurationProvider[] { + provider + }); + + var config = new ConfigurationManager(); + + config.AddConfiguration(chainedConfig, shouldDisposeConfiguration: shouldDispose); + + Assert.False(provider.IsDisposed); + + config.Dispose(); + + Assert.Equal(shouldDispose, provider.IsDisposed); + } + + [Fact] + public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Mem2:KeyInMem2", "ValueInMem2"} + }; + var dic3 = new Dictionary() + { + {"Mem3:KeyInMem3", "ValueInMem3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var memVal1 = config["mem1:keyinmem1"]; + var memVal2 = config["Mem2:KeyInMem2"]; + var memVal3 = config["MEM3:KEYINMEM3"]; + + // Assert + Assert.Contains(memConfigSrc1, configurationBuilder.Sources); + Assert.Contains(memConfigSrc2, configurationBuilder.Sources); + Assert.Contains(memConfigSrc3, configurationBuilder.Sources); + + Assert.Equal("ValueInMem1", memVal1); + Assert.Equal("ValueInMem2", memVal2); + Assert.Equal("ValueInMem3", memVal3); + + Assert.Equal("ValueInMem1", config["mem1:keyinmem1"]); + Assert.Equal("ValueInMem2", config["Mem2:KeyInMem2"]); + Assert.Equal("ValueInMem3", config["MEM3:KEYINMEM3"]); + Assert.Null(config["NotExist"]); + } + + [Fact] + public void CanChainConfiguration() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Mem2:KeyInMem2", "ValueInMem2"} + }; + var dic3 = new Dictionary() + { + {"Mem3:KeyInMem3", "ValueInMem3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var chained = new ConfigurationManager(); + chained.AddConfiguration(config); + var memVal1 = chained["mem1:keyinmem1"]; + var memVal2 = chained["Mem2:KeyInMem2"]; + var memVal3 = chained["MEM3:KEYINMEM3"]; + + // Assert + + Assert.Equal("ValueInMem1", memVal1); + Assert.Equal("ValueInMem2", memVal2); + Assert.Equal("ValueInMem3", memVal3); + + Assert.Null(chained["NotExist"]); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ChainedAsEnumerateFlattensIntoDictionaryTest(bool removePath) + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config1 = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config1; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + var config2 = new ConfigurationManager(); + + config2 + .AddConfiguration(config1) + .Add(memConfigSrc3); + + var dict = config2.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); + + // Assert + Assert.Equal("Value1", dict["Mem1"]); + Assert.Equal("NoKeyValue1", dict["Mem1:"]); + Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); + Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); + Assert.Equal("Value2", dict["Mem2"]); + Assert.Equal("NoKeyValue2", dict["Mem2:"]); + Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); + Assert.Equal("Value3", dict["Mem3"]); + Assert.Equal("NoKeyValue3", dict["Mem3:"]); + Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); + Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void AsEnumerateFlattensIntoDictionaryTest(bool removePath) + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + var dict = config.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); + + // Assert + Assert.Equal("Value1", dict["Mem1"]); + Assert.Equal("NoKeyValue1", dict["Mem1:"]); + Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); + Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); + Assert.Equal("Value2", dict["Mem2"]); + Assert.Equal("NoKeyValue2", dict["Mem2:"]); + Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); + Assert.Equal("Value3", dict["Mem3"]); + Assert.Equal("NoKeyValue3", dict["Mem3:"]); + Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); + Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); + } + + [Fact] + public void AsEnumerateStripsKeyFromChildren() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem4", "ValueInMem4"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"}, + {"Mem3:KeyInMem3:Deep4", "ValueDeep4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var dict = config.GetSection("Mem1").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(3, dict.Count); + Assert.Equal("NoKeyValue1", dict[""]); + Assert.Equal("ValueInMem1", dict["KeyInMem1"]); + Assert.Equal("ValueDeep1", dict["KeyInMem1:Deep1"]); + + var dict2 = config.GetSection("Mem2").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(3, dict2.Count); + Assert.Equal("NoKeyValue2", dict2[""]); + Assert.Equal("ValueInMem2", dict2["KeyInMem2"]); + Assert.Equal("ValueDeep2", dict2["KeyInMem2:Deep2"]); + + var dict3 = config.GetSection("Mem3").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(5, dict3.Count); + Assert.Equal("NoKeyValue3", dict3[""]); + Assert.Equal("ValueInMem3", dict3["KeyInMem3"]); + Assert.Equal("ValueInMem4", dict3["KeyInMem4"]); + Assert.Equal("ValueDeep3", dict3["KeyInMem3:Deep3"]); + Assert.Equal("ValueDeep4", dict3["KeyInMem3:Deep4"]); + } + + [Fact] + public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() + { + // Arrange + var dic1 = new Dictionary() + { + {"Key1:Key2", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Key1:Key2", "ValueInMem2"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + // Assert + Assert.Equal("ValueInMem2", config["Key1:Key2"]); + } + + [Fact] + public void NewConfigurationRootMayBeBuiltFromExistingWithDuplicateKeys() + { + var configurationRoot = new ConfigurationManager(); + + configurationRoot.AddInMemoryCollection(new Dictionary + { + {"keya:keyb", "valueA"}, + }); + configurationRoot.AddInMemoryCollection(new Dictionary + { + {"KEYA:KEYB", "valueB"}, + }); + + var newConfigurationRoot = new ConfigurationManager(); + + newConfigurationRoot.AddInMemoryCollection(configurationRoot.AsEnumerable()); + + Assert.Equal("valueB", newConfigurationRoot["keya:keyb"]); + } + + [Fact] + public void SettingValueUpdatesAllConfigurationProviders() + { + // Arrange + var dict = new Dictionary() + { + {"Key1", "Value1"}, + {"Key2", "Value2"} + }; + + var memConfigSrc1 = new TestMemorySourceProvider(dict); + var memConfigSrc2 = new TestMemorySourceProvider(dict); + var memConfigSrc3 = new TestMemorySourceProvider(dict); + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + config["Key1"] = "NewValue1"; + config["Key2"] = "NewValue2"; + + var memConfigProvider1 = memConfigSrc1.Build(configurationBuilder); + var memConfigProvider2 = memConfigSrc2.Build(configurationBuilder); + var memConfigProvider3 = memConfigSrc3.Build(configurationBuilder); + + // Assert + Assert.Equal("NewValue1", config["Key1"]); + Assert.Equal("NewValue1", Get(memConfigProvider1, "Key1")); + Assert.Equal("NewValue1", Get(memConfigProvider2, "Key1")); + Assert.Equal("NewValue1", Get(memConfigProvider3, "Key1")); + Assert.Equal("NewValue2", config["Key2"]); + Assert.Equal("NewValue2", Get(memConfigProvider1, "Key2")); + Assert.Equal("NewValue2", Get(memConfigProvider2, "Key2")); + Assert.Equal("NewValue2", Get(memConfigProvider3, "Key2")); + } + + [Fact] + public void CanGetConfigurationSection() + { + // Arrange + var dic1 = new Dictionary() + { + {"Data:DB1:Connection1", "MemVal1"}, + {"Data:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"DataSource:DB2:Connection", "MemVal3"} + }; + var dic3 = new Dictionary() + { + {"Data", "MemVal4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + var configFocus = config.GetSection("Data"); + + var memVal1 = configFocus["DB1:Connection1"]; + var memVal2 = configFocus["DB1:Connection2"]; + var memVal3 = configFocus["DB2:Connection"]; + var memVal4 = configFocus["Source:DB2:Connection"]; + var memVal5 = configFocus.Value; + + // Assert + Assert.Equal("MemVal1", memVal1); + Assert.Equal("MemVal2", memVal2); + Assert.Equal("MemVal4", memVal5); + + Assert.Equal("MemVal1", configFocus["DB1:Connection1"]); + Assert.Equal("MemVal2", configFocus["DB1:Connection2"]); + Assert.Null(configFocus["DB2:Connection"]); + Assert.Null(configFocus["Source:DB2:Connection"]); + Assert.Equal("MemVal4", configFocus.Value); + } + + [Fact] + public void CanGetConnectionStrings() + { + // Arrange + var dic1 = new Dictionary() + { + {"ConnectionStrings:DB1:Connection1", "MemVal1"}, + {"ConnectionStrings:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"ConnectionStrings:DB2:Connection", "MemVal3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + // Act + var memVal1 = config.GetConnectionString("DB1:Connection1"); + var memVal2 = config.GetConnectionString("DB1:Connection2"); + var memVal3 = config.GetConnectionString("DB2:Connection"); + + // Assert + Assert.Equal("MemVal1", memVal1); + Assert.Equal("MemVal2", memVal2); + Assert.Equal("MemVal3", memVal3); + } + + [Fact] + public void CanGetConfigurationChildren() + { + // Arrange + var dic1 = new Dictionary() + { + {"Data:DB1:Connection1", "MemVal1"}, + {"Data:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"Data:DB2Connection", "MemVal3"} + }; + var dic3 = new Dictionary() + { + {"DataSource:DB3:Connection", "MemVal4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + var configSections = config.GetSection("Data").GetChildren().ToList(); + + // Assert + Assert.Equal(2, configSections.Count()); + Assert.Equal("MemVal1", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection1"]); + Assert.Equal("MemVal2", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection2"]); + Assert.Equal("MemVal3", configSections.FirstOrDefault(c => c.Key == "DB2Connection").Value); + Assert.False(configSections.Exists(c => c.Key == "DB3")); + Assert.False(configSections.Exists(c => c.Key == "DB3")); + } + + [Fact] + public void SourcesReturnsAddedConfigurationProviders() + { + // Arrange + var dict = new Dictionary() + { + {"Mem:KeyInMem", "MemVal"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dict }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dict }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dict }; + + var config = new ConfigurationManager(); + IConfigurationBuilder configurationBuilder = config; + + // Act + + // A MemoryConfigurationSource is added by default, so there will be no error unless we clear it + configurationBuilder.Sources.Clear(); + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Assert + Assert.Equal(new[] { memConfigSrc1, memConfigSrc2, memConfigSrc3 }, configurationBuilder.Sources); + } + + [Fact] + public void SetValueThrowsExceptionNoSourceRegistered() + { + // Arrange + var config = new ConfigurationManager(); + + // A MemoryConfigurationSource is added by default, so there will be no error unless we clear it + config["Title"] = "Welcome"; + + ((IConfigurationBuilder)config).Sources.Clear(); + + // Act + var ex = Assert.Throws(() => config["Title"] = "Welcome"); + + // Assert + Assert.Equal(SR.Error_NoSources, ex.Message); + } + + [Fact] + public void SameReloadTokenIsReturnedRepeatedly() + { + // Arrange + IConfiguration config = new ConfigurationManager(); + + // Act + var token1 = config.GetReloadToken(); + var token2 = config.GetReloadToken(); + + // Assert + Assert.Same(token1, token2); + } + + [Fact] + public void DifferentReloadTokenReturnedAfterReloading() + { + // Arrange + IConfigurationRoot config = new ConfigurationManager(); + + // Act + var token1 = config.GetReloadToken(); + var token2 = config.GetReloadToken(); + config.Reload(); + var token3 = config.GetReloadToken(); + var token4 = config.GetReloadToken(); + + // Assert + Assert.Same(token1, token2); + Assert.Same(token3, token4); + Assert.NotSame(token1, token3); + } + + [Fact] + public void TokenTriggeredWhenReloadOccurs() + { + // Arrange + IConfigurationRoot config = new ConfigurationManager(); + + // Act + var token1 = config.GetReloadToken(); + var hasChanged1 = token1.HasChanged; + config.Reload(); + var hasChanged2 = token1.HasChanged; + + // Assert + Assert.False(hasChanged1); + Assert.True(hasChanged2); + } + + [Fact] + public void MultipleCallbacksCanBeRegisteredToReload() + { + // Arrange + IConfigurationRoot config = new ConfigurationManager(); + + // Act + var token1 = config.GetReloadToken(); + var called1 = 0; + token1.RegisterChangeCallback(_ => called1++, state: null); + var called2 = 0; + token1.RegisterChangeCallback(_ => called2++, state: null); + + // Assert + Assert.Equal(0, called1); + Assert.Equal(0, called2); + + config.Reload(); + Assert.Equal(1, called1); + Assert.Equal(1, called2); + + var token2 = config.GetReloadToken(); + var cleanup1 = token2.RegisterChangeCallback(_ => called1++, state: null); + token2.RegisterChangeCallback(_ => called2++, state: null); + + cleanup1.Dispose(); + + config.Reload(); + Assert.Equal(1, called1); + Assert.Equal(2, called2); + } + + [Fact] + public void NewTokenAfterReloadIsNotChanged() + { + // Arrange + IConfigurationRoot config = new ConfigurationManager(); + + // Act + var token1 = config.GetReloadToken(); + var hasChanged1 = token1.HasChanged; + config.Reload(); + var hasChanged2 = token1.HasChanged; + var token2 = config.GetReloadToken(); + var hasChanged3 = token2.HasChanged; + + // + // Assert + Assert.False(hasChanged1); + Assert.True(hasChanged2); + Assert.False(hasChanged3); + Assert.NotSame(token1, token2); + } + + [Fact] + public void KeyStartingWithColonMeansFirstSectionHasEmptyName() + { + // Arrange + var dict = new Dictionary + { + [":Key2"] = "value" + }; + var config = new ConfigurationManager(); + config.AddInMemoryCollection(dict); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal(string.Empty, children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal("Key2", children.First().GetChildren().First().Key); + } + + [Fact] + public void KeyWithDoubleColonHasSectionWithEmptyName() + { + // Arrange + var dict = new Dictionary + { + ["Key1::Key3"] = "value" + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal("Key1", children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal(string.Empty, children.First().GetChildren().First().Key); + Assert.Single(children.First().GetChildren().First().GetChildren()); + Assert.Equal("Key3", children.First().GetChildren().First().GetChildren().First().Key); + } + + [Fact] + public void KeyEndingWithColonMeansLastSectionHasEmptyName() + { + // Arrange + var dict = new Dictionary + { + ["Key1:"] = "value" + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal("Key1", children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal(string.Empty, children.First().GetChildren().First().Key); + } + + [Fact] + public void SectionWithValueExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetSection("Mem1").Exists(); + var sectionExists2 = config.GetSection("Mem1:KeyInMem1").Exists(); + var sectionNotExists = config.GetSection("Mem2").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + Assert.False(sectionNotExists); + } + + [Fact] + public void SectionGetRequiredSectionSuccess() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetRequiredSection("Mem1").Exists(); + var sectionExists2 = config.GetRequiredSection("Mem1:KeyInMem1").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + } + + [Fact] + public void SectionGetRequiredSectionMissingThrowException() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:Deep1", "Value1"}, + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + Assert.Throws(() => config.GetRequiredSection("Mem2")); + Assert.Throws(() => config.GetRequiredSection("Mem1:Deep2")); + } + + [Fact] + public void SectionWithChildrenExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"}, + {"Mem2:KeyInMem2:Deep1", "ValueDeep2"} + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetSection("Mem1").Exists(); + var sectionExists2 = config.GetSection("Mem2").Exists(); + var sectionNotExists = config.GetSection("Mem3").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + Assert.False(sectionNotExists); + } + + [Theory] + [InlineData("Value1")] + [InlineData("")] + public void KeyWithValueAndWithoutChildrenExistsAsSection(string value) + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", value} + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists = config.GetSection("Mem1").Exists(); + + // Assert + Assert.True(sectionExists); + } + + [Fact] + public void KeyWithNullValueAndWithoutChildrenIsASectionButNotExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", null} + }; + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sections = config.GetChildren(); + var sectionExists = config.GetSection("Mem1").Exists(); + var sectionChildren = config.GetSection("Mem1").GetChildren(); + + // Assert + Assert.Single(sections, section => section.Key == "Mem1"); + Assert.False(sectionExists); + Assert.Empty(sectionChildren); + } + + [Fact] + public void SectionWithChildrenHasNullValue() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"}, + }; + + + var config = new ConfigurationManager(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionValue = config.GetSection("Mem1").Value; + + // Assert + Assert.Null(sectionValue); + } + + [Fact] + public void ProviderWithNullReloadToken() + { + // Arrange + var config = new ConfigurationManager(); + IConfigurationBuilder builder = config; + + // Assert + Assert.NotNull(builder.Build()); + } + + [Fact] + public void BuildReturnsThis() + { + // Arrange + var config = new ConfigurationManager(); + + // Assert + Assert.Same(config, ((IConfigurationBuilder)config).Build()); + } + + private static string Get(IConfigurationProvider provider, string key) + { + string value; + + if (!provider.TryGet(key, out value)) + { + throw new InvalidOperationException("Key not found"); + } + + return value; + } + + private class TestConfigurationSource : IConfigurationSource + { + private readonly IConfigurationProvider _provider; + + public TestConfigurationSource(IConfigurationProvider provider) + { + _provider = provider; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return _provider; + } + } + + private class TestConfigurationProvider : ConfigurationProvider + { + public TestConfigurationProvider(string key, string value) + => Data.Add(key, value); + } + + private class DisposableTestConfigurationProvider : ConfigurationProvider, IDisposable + { + public bool IsDisposed { get; set; } + + public DisposableTestConfigurationProvider(string key, string value) + => Data.Add(key, value); + + public void Dispose() + => IsDisposed = true; + } + + private class TestChangeToken : IChangeToken + { + public List<(Action, object)> Callbacks { get; } = new List<(Action, object)>(); + + public bool HasChanged => false; + + public bool ActiveChangeCallbacks => true; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + var item = (callback, state); + Callbacks.Add(item); + return new DisposableAction(() => Callbacks.Remove(item)); + } + + private class DisposableAction : IDisposable + { + private Action _action; + + public DisposableAction(Action action) + { + _action = action; + } + + public void Dispose() + { + var a = _action; + if (a != null) + { + _action = null; + a(); + } + } + } + } + + private class TestMemorySourceProvider : MemoryConfigurationProvider, IConfigurationSource + { + public TestMemorySourceProvider(Dictionary initialData) + : base(new MemoryConfigurationSource { InitialData = initialData }) + { } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return this; + } + } + + private class NullReloadTokenConfigSource : IConfigurationSource, IConfigurationProvider + { + public IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath) => throw new NotImplementedException(); + public IChangeToken GetReloadToken() => null; + public void Load() { } + public void Set(string key, string value) => throw new NotImplementedException(); + public bool TryGet(string key, out string value) => throw new NotImplementedException(); + public IConfigurationProvider Build(IConfigurationBuilder builder) => this; + } + + } +} From 2c275e87c0033da3830bae29059d9ee5b663cf58 Mon Sep 17 00:00:00 2001 From: Jan Jahoda Date: Tue, 13 Jul 2021 19:45:59 +0200 Subject: [PATCH 496/926] Delayed client certificate (#54692) * initial prototype * Restore TLS 1.2 renegotiation * First windows functionality merge * reenable client certificate * Add more renegotiate tests * Remove client certificates * Cleanup * add test log * Apply PR comments * Add Data frame test * Add drain buffer test * Fix tls 1.3 incomming app data frame * Restore verify callback * Remove debug log * Remove keylog callback and unused method * Fix test build * Attempt to fix openssl version api difference * Sort shim * fix build * CI log * Restore mac tests * Add logs * fix test runs on old openssl * fix tests * fix w7 condition * feedback from review Co-authored-by: wfurt --- .../Interop.OpenSsl.cs | 14 ++ .../Interop.Ssl.cs | 3 + .../apibridge.c | 7 + .../apibridge.h | 1 + .../entrypoints.c | 1 + .../opensslshim.h | 12 +- .../osslcompat_111.h | 2 + .../pal_ssl.c | 28 +++ .../pal_ssl.h | 7 + .../src/Resources/Strings.resx | 3 + .../Net/Security/SslStream.Implementation.cs | 179 ++++++++++-------- .../src/System/Net/Security/SslStream.cs | 2 +- .../System/Net/Security/SslStreamPal.Unix.cs | 26 ++- .../SslStreamNetworkStreamTest.cs | 21 +- .../FunctionalTests/TestConfiguration.cs | 2 +- .../Fakes/FakeSslStream.Implementation.cs | 2 +- 16 files changed, 215 insertions(+), 95 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 47a9312266e..9dd310326b0 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Net; using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -223,6 +224,19 @@ internal static partial class Interop return context; } + internal static SecurityStatusPal SslRenegotiate(SafeSslHandle sslContext, out byte[]? outputBuffer) + { + int ret = Interop.Ssl.SslRenegotiate(sslContext); + + outputBuffer = Array.Empty(); + if (ret != 1) + { + GetSslError(sslContext, ret, out Exception? exception); + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exception); + } + return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + } + internal static bool DoSslHandshake(SafeSslHandle context, ReadOnlySpan input, out byte[]? sendBuf, out int sendCount) { sendBuf = null; diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index d080cf2f0d7..29154b77da6 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -74,6 +74,9 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRead", SetLastError = true)] internal static extern int SslRead(SafeSslHandle ssl, ref byte buf, int num); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRenegotiate")] + internal static extern int SslRenegotiate(SafeSslHandle ssl); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_IsSslRenegotiatePending")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IsSslRenegotiatePending(SafeSslHandle ssl); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c index daf13002b11..0414eb2b271 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c @@ -785,6 +785,13 @@ unsigned long local_SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options) return (unsigned long)SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, (long)options, NULL); } +unsigned long local_SSL_set_options(SSL* ssl, unsigned long options) +{ + // SSL_ctrl is signed long in and signed long out; but SSL_set_options, + // which was a macro call to SSL_ctrl in 1.0, is unsigned/unsigned. + return (unsigned long)SSL_ctrl(ssl, SSL_CTRL_OPTIONS, (long)options, NULL); +} + int local_SSL_session_reused(SSL* ssl) { return (int)SSL_ctrl(ssl, SSL_CTRL_GET_SESSION_REUSED, 0, NULL); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h index 1b866bc4474..968e12dad15 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h @@ -32,6 +32,7 @@ int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t local_SSL_is_init_finished(const SSL* ssl); int32_t local_SSL_CTX_config(SSL_CTX* ctx, const char* name); unsigned long local_SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options); +unsigned long local_SSL_set_options(SSL* ssl, unsigned long options); void local_SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level); int local_SSL_session_reused(SSL* ssl); int32_t local_X509_check_host(X509* x509, const char* name, size_t namelen, unsigned int flags, char** peername); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c index 996921777f6..99db84bfc3c 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c @@ -286,6 +286,7 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_BioWrite) DllImportEntry(CryptoNative_EnsureLibSslInitialized) DllImportEntry(CryptoNative_GetOpenSslCipherSuiteName) + DllImportEntry(CryptoNative_SslRenegotiate) DllImportEntry(CryptoNative_IsSslRenegotiatePending) DllImportEntry(CryptoNative_IsSslStateOK) DllImportEntry(CryptoNative_SetCiphers) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h index 050cdc25140..510d319a11d 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h @@ -458,7 +458,6 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); REQUIRED_FUNCTION(SSL_CTX_new) \ LIGHTUP_FUNCTION(SSL_CTX_set_alpn_protos) \ LIGHTUP_FUNCTION(SSL_CTX_set_alpn_select_cb) \ - REQUIRED_FUNCTION(SSL_CTX_set_cert_verify_callback) \ REQUIRED_FUNCTION(SSL_CTX_set_cipher_list) \ LIGHTUP_FUNCTION(SSL_CTX_set_ciphersuites) \ REQUIRED_FUNCTION(SSL_CTX_set_client_cert_cb) \ @@ -484,12 +483,16 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); LEGACY_FUNCTION(SSL_library_init) \ LEGACY_FUNCTION(SSL_load_error_strings) \ REQUIRED_FUNCTION(SSL_new) \ + REQUIRED_FUNCTION(SSL_peek) \ REQUIRED_FUNCTION(SSL_read) \ + REQUIRED_FUNCTION(SSL_renegotiate) \ REQUIRED_FUNCTION(SSL_renegotiate_pending) \ FALLBACK_FUNCTION(SSL_session_reused) \ REQUIRED_FUNCTION(SSL_set_accept_state) \ REQUIRED_FUNCTION(SSL_set_bio) \ REQUIRED_FUNCTION(SSL_set_connect_state) \ + FALLBACK_FUNCTION(SSL_set_options) \ + REQUIRED_FUNCTION(SSL_set_verify) \ REQUIRED_FUNCTION(SSL_shutdown) \ LEGACY_FUNCTION(SSL_state) \ LEGACY_FUNCTION(SSLeay) \ @@ -895,7 +898,6 @@ FOR_ALL_OPENSSL_FUNCTIONS #define SSL_CTX_new SSL_CTX_new_ptr #define SSL_CTX_set_alpn_protos SSL_CTX_set_alpn_protos_ptr #define SSL_CTX_set_alpn_select_cb SSL_CTX_set_alpn_select_cb_ptr -#define SSL_CTX_set_cert_verify_callback SSL_CTX_set_cert_verify_callback_ptr #define SSL_CTX_set_cipher_list SSL_CTX_set_cipher_list_ptr #define SSL_CTX_set_ciphersuites SSL_CTX_set_ciphersuites_ptr #define SSL_CTX_set_client_cert_cb SSL_CTX_set_client_cert_cb_ptr @@ -922,12 +924,18 @@ FOR_ALL_OPENSSL_FUNCTIONS #define SSL_library_init SSL_library_init_ptr #define SSL_load_error_strings SSL_load_error_strings_ptr #define SSL_new SSL_new_ptr +#define SSL_peek SSL_peek_ptr +#define SSL_state_string_long SSL_state_string_long_ptr #define SSL_read SSL_read_ptr +#define ERR_print_errors_fp ERR_print_errors_fp_ptr +#define SSL_renegotiate SSL_renegotiate_ptr #define SSL_renegotiate_pending SSL_renegotiate_pending_ptr #define SSL_session_reused SSL_session_reused_ptr #define SSL_set_accept_state SSL_set_accept_state_ptr #define SSL_set_bio SSL_set_bio_ptr #define SSL_set_connect_state SSL_set_connect_state_ptr +#define SSL_set_options SSL_set_options_ptr +#define SSL_set_verify SSL_set_verify_ptr #define SSL_shutdown SSL_shutdown_ptr #define SSL_state SSL_state_ptr #define SSLeay SSLeay_ptr diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h index e9a1b4939ba..6791e306019 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h @@ -7,6 +7,7 @@ #include "pal_types.h" #undef SSL_CTX_set_options +#undef SSL_set_options #undef SSL_session_reused typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS; @@ -56,6 +57,7 @@ int SSL_CTX_config(SSL_CTX* ctx, const char* name); unsigned long SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options); void SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level); int32_t SSL_is_init_finished(SSL* ssl); +unsigned long SSL_set_options(SSL* ctx, unsigned long options); int SSL_session_reused(SSL* ssl); const SSL_METHOD* TLS_method(void); const ASN1_TIME* X509_CRL_get0_nextUpdate(const X509_CRL* crl); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c index 524179cdcb2..acdc977b5f3 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c @@ -368,8 +368,36 @@ int32_t CryptoNative_SslRead(SSL* ssl, void* buf, int32_t num) return SSL_read(ssl, buf, num); } +static int verify_callback(int preverify_ok, X509_STORE_CTX* store) +{ + (void)preverify_ok; + (void)store; + // We don't care. Real verification happens in managed code. + return 1; +} + +int32_t CryptoNative_SslRenegotiate(SSL* ssl) +{ + // The openssl context is destroyed so we can't use ticket or session resumption. + SSL_set_options(ssl, SSL_OP_NO_TICKET | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + int pending = SSL_renegotiate_pending(ssl); + if (!pending) + { + SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_callback); + int ret = SSL_renegotiate(ssl); + if(ret != 1) + return ret; + + return SSL_do_handshake(ssl); + } + + return 0; +} + int32_t CryptoNative_IsSslRenegotiatePending(SSL* ssl) { + SSL_peek(ssl, NULL, 0); return SSL_renegotiate_pending(ssl) != 0; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.h index d7b995c5a19..79c6cbe22f9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.h @@ -213,6 +213,13 @@ when an error is encountered. */ PALEXPORT int32_t CryptoNative_SslRead(SSL* ssl, void* buf, int32_t num); +/* +Shims the SSL_renegotiate method. + +Returns 1 when renegotiation started; 0 on error. +*/ +PALEXPORT int32_t CryptoNative_SslRenegotiate(SSL* ssl); + /* Shims the SSL_renegotiate_pending method. diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index 513a54ccc2a..e33b4122a7d 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -455,6 +455,9 @@ Received data during renegotiation. + + Client stream needs to be drained before renegotiation. + Setting an SNI hostname is not supported on this API level. diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 2ecc2a3932a..f0f0b954d4a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -19,6 +19,7 @@ namespace System.Net.Security private SslAuthenticationOptions? _sslAuthenticationOptions; private int _nestedAuth; + private bool _isRenego; private enum Framing { @@ -296,7 +297,8 @@ namespace System.Net.Security } // This will initiate renegotiation or PHA for Tls1.3 - private async Task RenegotiateAsync(CancellationToken cancellationToken) + private async Task RenegotiateAsync(TIOAdapter adapter) + where TIOAdapter : IReadWriteAdapter { if (Interlocked.Exchange(ref _nestedAuth, 1) == 1) { @@ -314,13 +316,19 @@ namespace System.Net.Security throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(WriteAsync), "write")); } + if (_decryptedBytesCount is not 0) + { + throw new InvalidOperationException(SR.net_ssl_renegotiate_buffer); + } + _sslAuthenticationOptions!.RemoteCertRequired = true; - IReadWriteAdapter adapter = new AsyncReadWriteAdapter(InnerStream, cancellationToken); + _isRenego = true; try { SecurityStatusPal status = _context!.Renegotiate(out byte[]? nextmsg); - if (nextmsg?.Length > 0) + + if (nextmsg is {} && nextmsg.Length > 0) { await adapter.WriteAsync(nextmsg, 0, nextmsg.Length).ConfigureAwait(false); await adapter.FlushAsync().ConfigureAwait(false); @@ -330,20 +338,39 @@ namespace System.Net.Security { if (status.ErrorCode == SecurityStatusPalErrorCode.NoRenegotiation) { - // peer does not want to renegotiate. That should keep session usable. + // Peer does not want to renegotiate. That should keep session usable. return; } throw SslStreamPal.GetException(status); } - // Issue empty read to get renegotiation going. - await ReadAsyncInternal(adapter, Memory.Empty, renegotiation: true).ConfigureAwait(false); + _handshakeBuffer = new ArrayBuffer(InitialHandshakeBufferSize); + ProtocolToken message = null!; + do { + message = await ReceiveBlobAsync(adapter).ConfigureAwait(false); + if (message.Size > 0) + { + await adapter.WriteAsync(message.Payload!, 0, message.Size).ConfigureAwait(false); + await adapter.FlushAsync().ConfigureAwait(false); + } + } while (message.Status.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded); + + if (_handshakeBuffer.ActiveLength > 0) + { + // If we read more than we needed for handshake, move it to input buffer for further processing. + ResetReadBuffer(); + _handshakeBuffer.ActiveSpan.CopyTo(_internalBuffer); + _internalBufferCount = _handshakeBuffer.ActiveLength; + } + + CompleteHandshake(_sslAuthenticationOptions!); } finally { _nestedRead = 0; _nestedWrite = 0; + _isRenego = false; // We will not release _nestedAuth at this point to prevent another renegotiation attempt. } } @@ -452,25 +479,7 @@ namespace System.Net.Security _internalBufferCount = _handshakeBuffer.ActiveLength; } - ProtocolToken? alertToken = null; - if (!CompleteHandshake(ref alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)) - { - if (_sslAuthenticationOptions!.CertValidationDelegate != null) - { - // there may be some chain errors but the decision was made by custom callback. Details should be tracing if enabled. - SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.net_ssl_io_cert_custom_validation, null))); - } - else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors && chainStatus != X509ChainStatusFlags.NoError) - { - // We failed only because of chain and we have some insight. - SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.Format(SR.net_ssl_io_cert_chain_validation, chainStatus), null))); - } - else - { - // Simple add sslPolicyErrors as crude info. - SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.Format(SR.net_ssl_io_cert_validation, sslPolicyErrors), null))); - } - } + CompleteHandshake(_sslAuthenticationOptions!); } finally { @@ -478,6 +487,7 @@ namespace System.Net.Security if (reAuthenticationData == null) { _nestedAuth = 0; + _isRenego = false; } } @@ -534,51 +544,60 @@ namespace System.Net.Security } // At this point, we have at least one TLS frame. - if (_lastFrame.Header.Type == TlsContentType.Alert) + switch (_lastFrame.Header.Type) { - if (TlsFrameHelper.TryGetFrameInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame)) - { - if (NetEventSource.Log.IsEnabled() && _lastFrame.AlertDescription != TlsAlertDescription.CloseNotify) NetEventSource.Error(this, $"Received TLS alert {_lastFrame.AlertDescription}"); - } - } - else if (_lastFrame.Header.Type == TlsContentType.Handshake) - { - if (_handshakeBuffer.ActiveReadOnlySpan[TlsFrameHelper.HeaderSize] == (byte)TlsHandshakeType.ClientHello && - (_sslAuthenticationOptions!.ServerCertSelectionDelegate != null || - _sslAuthenticationOptions!.ServerOptionDelegate != null)) - { - TlsFrameHelper.ProcessingOptions options = NetEventSource.Log.IsEnabled() ? - TlsFrameHelper.ProcessingOptions.All : - TlsFrameHelper.ProcessingOptions.ServerName; - - // Process SNI from Client Hello message - if (!TlsFrameHelper.TryGetFrameInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame, options)) + case TlsContentType.Alert: + if (TlsFrameHelper.TryGetFrameInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame)) { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Failed to parse TLS hello."); + if (NetEventSource.Log.IsEnabled() && _lastFrame.AlertDescription != TlsAlertDescription.CloseNotify) NetEventSource.Error(this, $"Received TLS alert {_lastFrame.AlertDescription}"); } - - if (_lastFrame.HandshakeType == TlsHandshakeType.ClientHello) + break; + case TlsContentType.Handshake: + if (!_isRenego && _handshakeBuffer.ActiveReadOnlySpan[TlsFrameHelper.HeaderSize] == (byte)TlsHandshakeType.ClientHello && + (_sslAuthenticationOptions!.ServerCertSelectionDelegate != null || + _sslAuthenticationOptions!.ServerOptionDelegate != null)) { - // SNI if it exist. Even if we could not parse the hello, we can fall-back to default certificate. - if (_lastFrame.TargetName != null) + TlsFrameHelper.ProcessingOptions options = NetEventSource.Log.IsEnabled() ? + TlsFrameHelper.ProcessingOptions.All : + TlsFrameHelper.ProcessingOptions.ServerName; + + // Process SNI from Client Hello message + if (!TlsFrameHelper.TryGetFrameInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame, options)) { - _sslAuthenticationOptions!.TargetHost = _lastFrame.TargetName; + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Failed to parse TLS hello."); } - if (_sslAuthenticationOptions.ServerOptionDelegate != null) + if (_lastFrame.HandshakeType == TlsHandshakeType.ClientHello) { - SslServerAuthenticationOptions userOptions = - await _sslAuthenticationOptions.ServerOptionDelegate(this, new SslClientHelloInfo(_sslAuthenticationOptions.TargetHost, _lastFrame.SupportedVersions), - _sslAuthenticationOptions.UserState, adapter.CancellationToken).ConfigureAwait(false); - _sslAuthenticationOptions.UpdateOptions(userOptions); + // SNI if it exist. Even if we could not parse the hello, we can fall-back to default certificate. + if (_lastFrame.TargetName != null) + { + _sslAuthenticationOptions!.TargetHost = _lastFrame.TargetName; + } + + if (_sslAuthenticationOptions.ServerOptionDelegate != null) + { + SslServerAuthenticationOptions userOptions = + await _sslAuthenticationOptions.ServerOptionDelegate(this, new SslClientHelloInfo(_sslAuthenticationOptions.TargetHost, _lastFrame.SupportedVersions), + _sslAuthenticationOptions.UserState, adapter.CancellationToken).ConfigureAwait(false); + _sslAuthenticationOptions.UpdateOptions(userOptions); + } + } + + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Log.ReceivedFrame(this, _lastFrame); } } - - if (NetEventSource.Log.IsEnabled()) + break; + case TlsContentType.AppData: + // TLS1.3 it is not possible to distinguish between late Handshake and Application Data + if (_isRenego && SslProtocol != SslProtocols.Tls13) { - NetEventSource.Log.ReceivedFrame(this, _lastFrame); + throw new InvalidOperationException(SR.net_ssl_renegotiate_data); } - } + break; + } return ProcessBlob(frameSize); @@ -674,6 +693,29 @@ namespace System.Net.Security return true; } + private void CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions) + { + ProtocolToken? alertToken = null; + if (!CompleteHandshake(ref alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)) + { + if (sslAuthenticationOptions!.CertValidationDelegate != null) + { + // there may be some chain errors but the decision was made by custom callback. Details should be tracing if enabled. + SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.net_ssl_io_cert_custom_validation, null))); + } + else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors && chainStatus != X509ChainStatusFlags.NoError) + { + // We failed only because of chain and we have some insight. + SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.Format(SR.net_ssl_io_cert_chain_validation, chainStatus), null))); + } + else + { + // Simple add sslPolicyErrors as crude info. + SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.Format(SR.net_ssl_io_cert_validation, sslPolicyErrors), null))); + } + } + } + private async ValueTask WriteAsyncChunked(TIOAdapter writeAdapter, ReadOnlyMemory buffer) where TIOAdapter : struct, IReadWriteAdapter { @@ -916,15 +958,12 @@ namespace System.Net.Security return status; } - private async ValueTask ReadAsyncInternal(TIOAdapter adapter, Memory buffer, bool renegotiation = false) + private async ValueTask ReadAsyncInternal(TIOAdapter adapter, Memory buffer) where TIOAdapter : IReadWriteAdapter { - if (!renegotiation) + if (Interlocked.Exchange(ref _nestedRead, 1) == 1) { - if (Interlocked.Exchange(ref _nestedRead, 1) == 1) - { - throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(SslStream.ReadAsync), "read")); - } + throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(SslStream.ReadAsync), "read")); } ThrowIfExceptionalOrNotAuthenticated(); @@ -937,11 +976,6 @@ namespace System.Net.Security { if (_decryptedBytesCount != 0) { - if (renegotiation) - { - throw new InvalidOperationException(SR.net_ssl_renegotiate_data); - } - processedLength = CopyDecryptedData(buffer); if (processedLength == buffer.Length || !HaveFullTlsFrame(out payloadBytes)) { @@ -1006,11 +1040,6 @@ namespace System.Net.Security throw new IOException(SR.net_ssl_io_renego); } await ReplyOnReAuthenticationAsync(adapter, extraBuffer).ConfigureAwait(false); - if (renegotiation) - { - // if we received data frame instead, we would not be here but we would decrypt data and hit check above. - return 0; - } // Loop on read. continue; } @@ -1064,7 +1093,7 @@ namespace System.Net.Security } catch (Exception e) { - if (e is IOException || (e is OperationCanceledException && adapter.CancellationToken.IsCancellationRequested) || renegotiation) + if (e is IOException || (e is OperationCanceledException && adapter.CancellationToken.IsCancellationRequested)) { throw; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs index 75c2938d444..276652dbcb9 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -697,7 +697,7 @@ namespace System.Net.Security throw new InvalidOperationException(SR.net_ssl_certificate_exist); } - return RenegotiateAsync(cancellationToken); + return RenegotiateAsync(new AsyncReadWriteAdapter(InnerStream, cancellationToken)); } protected override void Dispose(bool disposing) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 02045613170..ed0bcaaccd1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -36,11 +36,6 @@ namespace System.Net.Security return HandshakeInternal(credential!, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } - public static SecurityStatusPal Renegotiate(ref SafeFreeCredentials? credentialsHandle, ref SafeDeleteSslContext? context, SslAuthenticationOptions sslAuthenticationOptions, out byte[]? outputBuffer) - { - throw new PlatformNotSupportedException(); - } - public static SafeFreeCredentials AcquireCredentialsHandle(SslStreamCertificateContext? certificateContext, SslProtocols protocols, EncryptionPolicy policy, bool isServer) { @@ -120,6 +115,19 @@ namespace System.Net.Security return bindingHandle; } + public static SecurityStatusPal Renegotiate(ref SafeFreeCredentials? credentialsHandle, ref SafeDeleteSslContext? securityContext, SslAuthenticationOptions sslAuthenticationOptions, out byte[]? outputBuffer) + { + var sslContext = ((SafeDeleteSslContext)securityContext!).SslContext; + SecurityStatusPal status = Interop.OpenSsl.SslRenegotiate(sslContext, out outputBuffer); + + outputBuffer = Array.Empty(); + if (status.ErrorCode != SecurityStatusPalErrorCode.OK) + { + return status; + } + return HandshakeInternal(credentialsHandle!, ref securityContext, null, ref outputBuffer, sslAuthenticationOptions); + } + public static void QueryContextStreamSizes(SafeDeleteContext? securityContext, out StreamSizes streamSizes) { streamSizes = StreamSizes.Default; @@ -150,7 +158,13 @@ namespace System.Net.Security context = new SafeDeleteSslContext((credential as SafeFreeSslCredentials)!, sslAuthenticationOptions); } - bool done = Interop.OpenSsl.DoSslHandshake(context.SslContext, inputBuffer, out output, out outputSize); + bool done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer, out output, out outputSize); + // sometimes during renegotiation processing messgae does not yield new output. + // That seems to be flaw in OpenSSL state machine and we have workaround to peek it and try it again. + if (outputSize == 0 && Interop.Ssl.IsSslRenegotiatePending(((SafeDeleteSslContext)context).SslContext)) + { + done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, ReadOnlySpan.Empty, out output, out outputSize); + } // When the handshake is done, and the context is server, check if the alpnHandle target was set to null during ALPN. // If it was, then that indicates ALPN failed, send failure. diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 6ff3afb23f3..b3a5f5e21f4 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -42,6 +42,8 @@ namespace System.Net.Security.Tests public class SslStreamNetworkStreamTest : IClassFixture { + private static bool SupportsRenegotiation => TestConfiguration.SupportsRenegotiation; + readonly ITestOutputHelper _output; readonly CertificateSetup certificates; @@ -172,10 +174,10 @@ namespace System.Net.Security.Tests } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] + [ConditionalTheory(nameof(SupportsRenegotiation))] [InlineData(true)] [InlineData(false)] - [PlatformSpecific(TestPlatforms.Windows)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] public async Task SslStream_NegotiateClientCertificateAsync_Succeeds(bool sendClientCertificate) { bool negotiateClientCertificateCalled = false; @@ -214,6 +216,7 @@ namespace System.Net.Security.Tests return true; }; + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( client.AuthenticateAsClientAsync(clientOptions, cts.Token), server.AuthenticateAsServerAsync(serverOptions, cts.Token)); @@ -234,19 +237,21 @@ namespace System.Net.Security.Tests { Assert.Null(server.RemoteCertificate); } + // Finish the client's read await server.WriteAsync(TestHelper.s_ping, cts.Token); await t; + // verify that the session is usable with or without client's certificate await TestHelper.PingPong(client, server, cts.Token); await TestHelper.PingPong(server, client, cts.Token); } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] + [ConditionalTheory(nameof(SupportsRenegotiation))] [InlineData(true)] [InlineData(false)] - [PlatformSpecific(TestPlatforms.Windows)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] public async Task SslStream_NegotiateClientCertificateAsyncNoRenego_Succeeds(bool sendClientCertificate) { bool negotiateClientCertificateCalled = false; @@ -316,8 +321,7 @@ namespace System.Net.Security.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] - [PlatformSpecific(TestPlatforms.Windows)] - [ActiveIssue("https://github.com/dotnet/runtime/pull/54692")] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] public async Task SslStream_NegotiateClientCertificateAsync_ClientWriteData() { using CancellationTokenSource cts = new CancellationTokenSource(); @@ -344,7 +348,6 @@ namespace System.Net.Security.Tests Assert.Null(server.RemoteCertificate); - var t = server.NegotiateClientCertificateAsync(cts.Token); // Send application data instead of Client hello. @@ -354,8 +357,8 @@ namespace System.Net.Security.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] - [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalFact(nameof(SupportsRenegotiation))] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] public async Task SslStream_NegotiateClientCertificateAsync_ServerDontDrainClientData() { using CancellationTokenSource cts = new CancellationTokenSource(); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index a7dbdc9451e..4bd1b0ee30f 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -27,8 +27,8 @@ namespace System.Net.Security.Tests public const string NtlmUserFilePath = "/var/tmp/ntlm_user_file"; public static bool SupportsNullEncryption { get { return s_supportsNullEncryption.Value; } } - public static bool SupportsHandshakeAlerts { get { return OperatingSystem.IsLinux() || OperatingSystem.IsWindows(); } } + public static bool SupportsRenegotiation { get { return (OperatingSystem.IsWindows() && !PlatformDetection.IsWindows7) || ((OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) && PlatformDetection.OpenSslVersion >= new Version(1, 1, 1)); } } public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) => tasks.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds); diff --git a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs index 4e146153626..ee160631b40 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs @@ -59,7 +59,7 @@ namespace System.Net.Security return Task.Run(() => {}); } - private Task RenegotiateAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + private Task RenegotiateAsync(AsyncReadWriteAdapter adapter) => throw new PlatformNotSupportedException(); private void ReturnReadBufferIfEmpty() { From 04429ca36aa2a4075a1638a6025d2df74792741d Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 13 Jul 2021 12:57:48 -0500 Subject: [PATCH 497/926] Remove C# dynamic support from JsonNode (#55430) --- .../System.Text.Json/ref/System.Text.Json.cs | 3 +- .../ref/System.Text.Json.csproj | 2 - .../ILLink.Suppressions.LibraryBuild.xml | 12 - .../src/System.Text.Json.csproj | 5 - .../Text/Json/Nodes/JsonNode.Dynamic.cs | 27 -- .../Text/Json/Nodes/JsonObject.Dynamic.cs | 55 --- .../src/System/Text/Json/Nodes/MetaDynamic.cs | 438 ------------------ .../Collection/StackOfTConverter.cs | 1 + .../Converters/Node/JsonNodeConverter.cs | 6 +- .../Converters/Node/JsonObjectConverter.cs | 2 +- .../JsonNode/DynamicTests.cs | 282 ----------- .../JsonNode/JsonArrayTests.cs | 42 ++ .../System.Text.Json.Tests.csproj | 1 - .../TrimmingTests/Collections/StackOfT.cs | 14 +- 14 files changed, 56 insertions(+), 834 deletions(-) delete mode 100644 src/libraries/System.Text.Json/src/ILLink/ILLink.Suppressions.LibraryBuild.xml delete mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.Dynamic.cs delete mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.Dynamic.cs delete mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Nodes/MetaDynamic.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/DynamicTests.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index a7d5a44feb6..26b72dd6493 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -547,7 +547,7 @@ namespace System.Text.Json.Nodes System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public override void WriteTo(System.Text.Json.Utf8JsonWriter writer, System.Text.Json.JsonSerializerOptions? options = null) { } } - public abstract partial class JsonNode : System.Dynamic.IDynamicMetaObjectProvider + public abstract partial class JsonNode { internal JsonNode() { } public System.Text.Json.Nodes.JsonNode? this[int index] { get { throw null; } set { } } @@ -646,7 +646,6 @@ namespace System.Text.Json.Nodes public static System.Text.Json.Nodes.JsonNode? Parse(System.ReadOnlySpan utf8Json, System.Text.Json.Nodes.JsonNodeOptions? nodeOptions = default(System.Text.Json.Nodes.JsonNodeOptions?), System.Text.Json.JsonDocumentOptions documentOptions = default(System.Text.Json.JsonDocumentOptions)) { throw null; } public static System.Text.Json.Nodes.JsonNode? Parse(string json, System.Text.Json.Nodes.JsonNodeOptions? nodeOptions = default(System.Text.Json.Nodes.JsonNodeOptions?), System.Text.Json.JsonDocumentOptions documentOptions = default(System.Text.Json.JsonDocumentOptions)) { throw null; } public static System.Text.Json.Nodes.JsonNode? Parse(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.Nodes.JsonNodeOptions? nodeOptions = default(System.Text.Json.Nodes.JsonNodeOptions?)) { throw null; } - System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public string ToJsonString(System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public override string ToString() { throw null; } public abstract void WriteTo(System.Text.Json.Utf8JsonWriter writer, System.Text.Json.JsonSerializerOptions? options = null); diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj index dc0bb21c008..f015d29c355 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj @@ -13,7 +13,6 @@ - @@ -21,7 +20,6 @@ - diff --git a/src/libraries/System.Text.Json/src/ILLink/ILLink.Suppressions.LibraryBuild.xml b/src/libraries/System.Text.Json/src/ILLink/ILLink.Suppressions.LibraryBuild.xml deleted file mode 100644 index b12ccebb9c9..00000000000 --- a/src/libraries/System.Text.Json/src/ILLink/ILLink.Suppressions.LibraryBuild.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Text.Json.Nodes.JsonNode.System#Dynamic#IDynamicMetaObjectProvider#GetMetaObject(System.Linq.Expressions.Expression) - System.Text.Json's integration with dynamic is not trim compatible. However, there isn't a direct API developers call. Instead they use the 'dynamic' keyword, which gets compiled into calls to Microsoft.CSharp. Microsoft.CSharp looks for IDynamicMetaObjectProvider implementations. Leaving this warning in the product so developers get a warning stating that using dynamic JsonValue code may be broken in trimmed apps. - - - diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 195c640c85a..3206cbd6de1 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -59,20 +59,17 @@ - - - @@ -298,7 +295,6 @@ - @@ -315,7 +311,6 @@ - diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.Dynamic.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.Dynamic.cs deleted file mode 100644 index 457af37eb0f..00000000000 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.Dynamic.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Dynamic; -using System.Linq.Expressions; -using System.Reflection; - -namespace System.Text.Json.Nodes -{ - public partial class JsonNode : IDynamicMetaObjectProvider - { - internal virtual MethodInfo? TryGetMemberMethodInfo => null; - internal virtual MethodInfo? TrySetMemberMethodInfo - { - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - get => null; - } - - DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) => - CreateDynamicObject(parameter, this); - - [RequiresUnreferencedCode("Using JsonNode instances as dynamic types is not compatible with trimming. It can result in non-primitive types being serialized, which may have their members trimmed.")] - private static DynamicMetaObject CreateDynamicObject(Expression parameter, JsonNode node) => - new MetaDynamic(parameter, node); - } -} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.Dynamic.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.Dynamic.cs deleted file mode 100644 index b343b70d3fa..00000000000 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonObject.Dynamic.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Dynamic; -using System.Reflection; - -namespace System.Text.Json.Nodes -{ - public partial class JsonObject - { - private bool TryGetMemberCallback(GetMemberBinder binder, out object? result) - { - if (TryGetPropertyValue(binder.Name, out JsonNode? node)) - { - result = node; - return true; - } - - // Return null for missing properties. - result = null; - return true; - } - - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - private bool TrySetMemberCallback(SetMemberBinder binder, object? value) - { - JsonNode? node = null; - if (value != null) - { - node = value as JsonNode; - if (node == null) - { - node = new JsonValueNotTrimmable(value, Options); - } - } - - this[binder.Name] = node; - return true; - } - - private const BindingFlags MemberInfoBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; - - private static MethodInfo? s_TryGetMember; - internal override MethodInfo? TryGetMemberMethodInfo => - s_TryGetMember ??= typeof(JsonObject).GetMethod(nameof(TryGetMemberCallback), MemberInfoBindingFlags); - - private static MethodInfo? s_TrySetMember; - internal override MethodInfo? TrySetMemberMethodInfo - { - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - get => s_TrySetMember ??= typeof(JsonObject).GetMethod(nameof(TrySetMemberCallback), MemberInfoBindingFlags); - } - } -} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/MetaDynamic.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/MetaDynamic.cs deleted file mode 100644 index 8806fe30352..00000000000 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/MetaDynamic.cs +++ /dev/null @@ -1,438 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Dynamic; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace System.Text.Json.Nodes -{ - // The bulk of this code was pulled from src/libraries/System.Linq.Expressions/src/System/Dynamic/DynamicObject.cs - // and then refactored. - internal sealed class MetaDynamic : DynamicMetaObject - { - private static readonly ConstantExpression NullExpression = Expression.Constant(null); - private static readonly DefaultExpression EmptyExpression = Expression.Empty(); - private static readonly ConstantExpression Int1Expression = Expression.Constant((object)1); - - private JsonNode Dynamic { get; } - - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - internal MetaDynamic(Expression expression, JsonNode dynamicObject) - : base(expression, BindingRestrictions.Empty, dynamicObject) - { - Dynamic = dynamicObject; - } - - public override DynamicMetaObject BindGetMember(GetMemberBinder binder) - { - MethodInfo? methodInfo = Dynamic.TryGetMemberMethodInfo; - if (methodInfo == null) - { - return base.BindGetMember(binder); - } - - return CallMethodWithResult( - methodInfo, - binder, - s_noArgs, - (MetaDynamic @this, GetMemberBinder b, DynamicMetaObject? e) => b.FallbackGetMember(@this, e) - ); - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked with RequiresUnreferencedCode.")] - public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) - { - MethodInfo? methodInfo = Dynamic.TrySetMemberMethodInfo; - if (methodInfo == null) - { - return base.BindSetMember(binder, value); - } - - DynamicMetaObject localValue = value; - - return CallMethodReturnLast( - methodInfo, - binder, - s_noArgs, - value.Expression, - (MetaDynamic @this, SetMemberBinder b, DynamicMetaObject? e) => b.FallbackSetMember(@this, localValue, e) - ); - } - - private delegate DynamicMetaObject Fallback(MetaDynamic @this, TBinder binder, DynamicMetaObject? errorSuggestion); - -#pragma warning disable CA1825 // used in reference comparison, requires unique object identity - private static readonly Expression[] s_noArgs = new Expression[0]; -#pragma warning restore CA1825 - - private static ReadOnlyCollection GetConvertedArgs(params Expression[] args) - { - var paramArgs = new Expression[args.Length]; - - for (int i = 0; i < args.Length; i++) - { - paramArgs[i] = Expression.Convert(args[i], typeof(object)); - } - - return new ReadOnlyCollection(paramArgs); - } - - /// - /// Helper method for generating expressions that assign byRef call - /// parameters back to their original variables. - /// - private static Expression ReferenceArgAssign(Expression callArgs, Expression[] args) - { - ReadOnlyCollectionBuilder? block = null; - - for (int i = 0; i < args.Length; i++) - { - ParameterExpression variable = (ParameterExpression)args[i]; - - if (variable.IsByRef) - { - if (block == null) - block = new ReadOnlyCollectionBuilder(); - - block.Add( - Expression.Assign( - variable, - Expression.Convert( - Expression.ArrayIndex( - callArgs, - Int1Expression - ), - variable.Type - ) - ) - ); - } - } - - if (block != null) - return Expression.Block(block); - else - return EmptyExpression; - } - - /// - /// Helper method for generating arguments for calling methods - /// on DynamicObject. parameters is either a list of ParameterExpressions - /// to be passed to the method as an object[], or NoArgs to signify that - /// the target method takes no object[] parameter. - /// - private static Expression[] BuildCallArgs(TBinder binder, Expression[] parameters, Expression arg0, Expression? arg1) - where TBinder : DynamicMetaObjectBinder - { - if (!ReferenceEquals(parameters, s_noArgs)) - return arg1 != null ? new Expression[] { Constant(binder), arg0, arg1 } : new Expression[] { Constant(binder), arg0 }; - else - return arg1 != null ? new Expression[] { Constant(binder), arg1 } : new Expression[] { Constant(binder) }; - } - - private static ConstantExpression Constant(TBinder binder) - { - return Expression.Constant(binder, typeof(TBinder)); - } - - /// - /// Helper method for generating a MetaObject which calls a - /// specific method on Dynamic that returns a result - /// - private DynamicMetaObject CallMethodWithResult(MethodInfo method, TBinder binder, Expression[] args, Fallback fallback) - where TBinder : DynamicMetaObjectBinder - { - return CallMethodWithResult(method, binder, args, fallback, null); - } - - /// - /// Helper method for generating a MetaObject which calls a - /// specific method on Dynamic that returns a result - /// - private DynamicMetaObject CallMethodWithResult(MethodInfo method, TBinder binder, Expression[] args, Fallback fallback, Fallback? fallbackInvoke) - where TBinder : DynamicMetaObjectBinder - { - // - // First, call fallback to do default binding - // This produces either an error or a call to a .NET member - // - DynamicMetaObject fallbackResult = fallback(this, binder, null); - - DynamicMetaObject callDynamic = BuildCallMethodWithResult(method, binder, args, fallbackResult, fallbackInvoke); - - // - // Now, call fallback again using our new MO as the error - // When we do this, one of two things can happen: - // 1. Binding will succeed, and it will ignore our call to - // the dynamic method, OR - // 2. Binding will fail, and it will use the MO we created - // above. - // - return fallback(this, binder, callDynamic); - } - - private DynamicMetaObject BuildCallMethodWithResult(MethodInfo method, TBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback? fallbackInvoke) - where TBinder : DynamicMetaObjectBinder - { - ParameterExpression result = Expression.Parameter(typeof(object), null); - ParameterExpression callArgs = Expression.Parameter(typeof(object[]), null); - ReadOnlyCollection callArgsValue = GetConvertedArgs(args); - - var resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty); - - // Need to add a conversion if calling TryConvert - if (binder.ReturnType != typeof(object)) - { - Debug.Assert(binder is ConvertBinder && fallbackInvoke == null); - - UnaryExpression convert = Expression.Convert(resultMO.Expression, binder.ReturnType); - // will always be a cast or unbox - Debug.Assert(convert.Method == null); - - // Prepare a good exception message in case the convert will fail - string convertFailed = SR.Format(SR.NodeDynamicObjectResultNotAssignable, - "{0}", - this.Value.GetType(), - binder.GetType(), - binder.ReturnType - ); - - Expression condition; - // If the return type can not be assigned null then just check for type assignability otherwise allow null. - if (binder.ReturnType.IsValueType && Nullable.GetUnderlyingType(binder.ReturnType) == null) - { - condition = Expression.TypeIs(resultMO.Expression, binder.ReturnType); - } - else - { - condition = Expression.OrElse( - Expression.Equal(resultMO.Expression, NullExpression), - Expression.TypeIs(resultMO.Expression, binder.ReturnType)); - } - - Expression checkedConvert = Expression.Condition( - condition, - convert, - Expression.Throw( - Expression.New( - CachedReflectionInfo.InvalidCastException_Ctor_String, - new TrueReadOnlyCollection( - Expression.Call( - CachedReflectionInfo.String_Format_String_ObjectArray, - Expression.Constant(convertFailed), - Expression.NewArrayInit( - typeof(object), - new TrueReadOnlyCollection( - Expression.Condition( - Expression.Equal(resultMO.Expression, NullExpression), - Expression.Constant("null"), - Expression.Call( - resultMO.Expression, - CachedReflectionInfo.Object_GetType - ), - typeof(object) - ) - ) - ) - ) - ) - ), - binder.ReturnType - ), - binder.ReturnType - ); - - resultMO = new DynamicMetaObject(checkedConvert, resultMO.Restrictions); - } - - if (fallbackInvoke != null) - { - resultMO = fallbackInvoke(this, binder, resultMO); - } - - var callDynamic = new DynamicMetaObject( - Expression.Block( - new TrueReadOnlyCollection(result, callArgs), - new TrueReadOnlyCollection( - Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)), - Expression.Condition( - Expression.Call( - GetLimitedSelf(), - method, - BuildCallArgs( - binder, - args, - callArgs, - result - ) - ), - Expression.Block( - ReferenceArgAssign(callArgs, args), - resultMO.Expression - ), - fallbackResult.Expression, - binder.ReturnType - ) - ) - ), - GetRestrictions().Merge(resultMO.Restrictions).Merge(fallbackResult.Restrictions) - ); - return callDynamic; - } - - private DynamicMetaObject CallMethodReturnLast(MethodInfo method, TBinder binder, Expression[] args, Expression value, Fallback fallback) - where TBinder : DynamicMetaObjectBinder - { - // - // First, call fallback to do default binding - // This produces either an error or a call to a .NET member - // - DynamicMetaObject fallbackResult = fallback(this, binder, null); - - // - // Build a new expression like: - // { - // object result; - // TrySetMember(payload, result = value) ? result : fallbackResult - // } - // - - ParameterExpression result = Expression.Parameter(typeof(object), null); - ParameterExpression callArgs = Expression.Parameter(typeof(object[]), null); - ReadOnlyCollection callArgsValue = GetConvertedArgs(args); - - var callDynamic = new DynamicMetaObject( - Expression.Block( - new TrueReadOnlyCollection(result, callArgs), - new TrueReadOnlyCollection( - Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)), - Expression.Condition( - Expression.Call( - GetLimitedSelf(), - method, - BuildCallArgs( - binder, - args, - callArgs, - Expression.Assign(result, Expression.Convert(value, typeof(object))) - ) - ), - Expression.Block( - ReferenceArgAssign(callArgs, args), - result - ), - fallbackResult.Expression, - typeof(object) - ) - ) - ), - GetRestrictions().Merge(fallbackResult.Restrictions) - ); - - // - // Now, call fallback again using our new MO as the error - // When we do this, one of two things can happen: - // 1. Binding will succeed, and it will ignore our call to - // the dynamic method, OR - // 2. Binding will fail, and it will use the MO we created - // above. - // - return fallback(this, binder, callDynamic); - } - - /// - /// Returns a Restrictions object which includes our current restrictions merged - /// with a restriction limiting our type - /// - private BindingRestrictions GetRestrictions() - { - Debug.Assert(Restrictions == BindingRestrictions.Empty, "We don't merge, restrictions are always empty"); - - return GetTypeRestriction(this); - } - - /// - /// Returns our Expression converted to DynamicObject - /// - private Expression GetLimitedSelf() - { - // Convert to DynamicObject rather than LimitType, because - // the limit type might be non-public. - if (AreEquivalent(Expression.Type, Value.GetType())) - { - return Expression; - } - return Expression.Convert(Expression, Value.GetType()); - } - - private static bool AreEquivalent(Type? t1, Type? t2) => t1 != null && t1.IsEquivalentTo(t2); - - private new object Value => base.Value!; - - // It is okay to throw NotSupported from this binder. This object - // is only used by DynamicObject.GetMember--it is not expected to - // (and cannot) implement binding semantics. It is just so the DO - // can use the Name and IgnoreCase properties. - private sealed class GetBinderAdapter : GetMemberBinder - { - internal GetBinderAdapter(InvokeMemberBinder binder) - : base(binder.Name, binder.IgnoreCase) - { - } - - public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject? errorSuggestion) - { - throw new NotSupportedException(); - } - } - - private sealed class TrueReadOnlyCollection : ReadOnlyCollection - { - /// - /// Creates instance of TrueReadOnlyCollection, wrapping passed in array. - /// !!! DOES NOT COPY THE ARRAY !!! - /// - public TrueReadOnlyCollection(params T[] list) - : base(list) - { - } - } - - internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) - { - Debug.Assert(obj != null); - if (obj.Value == null && obj.HasValue) - { - return BindingRestrictions.GetInstanceRestriction(obj.Expression, null); - } - else - { - return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType); - } - } - - internal static partial class CachedReflectionInfo - { - private static MethodInfo? s_String_Format_String_ObjectArray; - public static MethodInfo String_Format_String_ObjectArray => - s_String_Format_String_ObjectArray ?? - (s_String_Format_String_ObjectArray = typeof(string).GetMethod(nameof(string.Format), new Type[] { typeof(string), typeof(object[]) })!); - - private static ConstructorInfo? s_InvalidCastException_Ctor_String; - public static ConstructorInfo InvalidCastException_Ctor_String => - s_InvalidCastException_Ctor_String ?? - (s_InvalidCastException_Ctor_String = typeof(InvalidCastException).GetConstructor(new Type[] { typeof(string) })!); - - private static MethodInfo? s_Object_GetType; - public static MethodInfo Object_GetType => - s_Object_GetType ?? - (s_Object_GetType = typeof(object).GetMethod(nameof(object.GetType))!); - } - } -} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs index df483b6986e..7e1badfb968 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Text.Json.Serialization.Converters { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs index 1e1856ccdaa..07da2c99ebf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs @@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Converters /// Converter for JsonNode-derived types. The {T} value must be Object and not JsonNode /// since we allow Object-declared members\variables to deserialize as {JsonNode}. /// - internal sealed class JsonNodeConverter : JsonConverter + internal sealed class JsonNodeConverter : JsonConverter { private static JsonNodeConverter? s_nodeConverter; private static JsonArrayConverter? s_arrayConverter; @@ -23,7 +23,7 @@ namespace System.Text.Json.Serialization.Converters public static JsonObjectConverter ObjectConverter => s_objectConverter ??= new JsonObjectConverter(); public static JsonValueConverter ValueConverter => s_valueConverter ??= new JsonValueConverter(); - public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, JsonNode? value, JsonSerializerOptions options) { if (value == null) { @@ -47,7 +47,7 @@ namespace System.Text.Json.Serialization.Converters } } - public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override JsonNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { switch (reader.TokenType) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs index da3be52fde0..b25f544d3c9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs @@ -20,7 +20,7 @@ namespace System.Text.Json.Serialization.Converters JsonSerializerOptions options, ref ReadStack state) { - bool success = JsonNodeConverter.Instance.TryRead(ref reader, typeof(JsonNode), options, ref state, out object? value); + bool success = JsonNodeConverter.Instance.TryRead(ref reader, typeof(JsonNode), options, ref state, out JsonNode? value); Debug.Assert(success); // Node converters are not resumable. Debug.Assert(obj is JsonObject); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/DynamicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/DynamicTests.cs deleted file mode 100644 index 63afb3920b0..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/DynamicTests.cs +++ /dev/null @@ -1,282 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; -using Microsoft.CSharp.RuntimeBinder; -using Xunit; - -namespace System.Text.Json.Nodes.Tests -{ - public static class DynamicTests - { - [Fact] - public static void ImplicitOperators() - { - dynamic jObj = new JsonObject(); - - // Dynamic objects do not support object initializers. - - // Primitives - jObj.MyString = "Hello!"; - Assert.IsAssignableFrom(jObj.MyString); - - jObj.MyNull = null; - jObj.MyBoolean = false; - - // Nested array - jObj.MyArray = new JsonArray(2, 3, 42); - - // Additional primitives - jObj.MyInt = 43; - jObj.MyDateTime = new DateTime(2020, 7, 8); - jObj.MyGuid = new Guid("ed957609-cdfe-412f-88c1-02daca1b4f51"); - - // Nested objects - jObj.MyObject = new JsonObject(); - jObj.MyObject.MyString = "Hello!!"; - - jObj.Child = new JsonObject(); - jObj.Child.ChildProp = 1; - - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - - string json = jObj.ToJsonString(options); - JsonTestHelper.AssertJsonEqual(JsonNodeTests.ExpectedDomJson, json); - } - - private enum MyCustomEnum - { - Default = 0, - FortyTwo = 42, - Hello = 77 - } - - [Fact] - public static void Primitives_UnknownTypeHandling() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - options.Converters.Add(new JsonStringEnumConverter()); - - dynamic obj = JsonSerializer.Deserialize(Serialization.Tests.DynamicTests.Json, options); - Assert.IsAssignableFrom(obj); - - // JsonValue created from a JSON string. - Assert.IsAssignableFrom(obj.MyString); - Assert.Equal("Hello", (string)obj.MyString); - - // Verify other string-based types. - // Even though a custom converter was used, an explicit deserialize needs to be done. - Assert.Equal(42, (int)obj.MyInt); - Assert.ThrowsAny(() => (MyCustomEnum)obj.MyInt); - // Perform the explicit deserialize on the enum. - Assert.Equal(MyCustomEnum.FortyTwo, JsonSerializer.Deserialize(obj.MyInt.ToJsonString())); - - Assert.Equal(Serialization.Tests.DynamicTests.MyDateTime, (DateTime)obj.MyDateTime); - Assert.Equal(Serialization.Tests.DynamicTests.MyGuid, (Guid)obj.MyGuid); - - // JsonValue created from a JSON bool. - Assert.IsAssignableFrom(obj.MyBoolean); - bool b = (bool)obj.MyBoolean; - Assert.True(b); - - // Numbers must specify the type through a cast or assignment. - Assert.IsAssignableFrom(obj.MyInt); - Assert.ThrowsAny(() => obj.MyInt == 42L); - Assert.Equal(42L, (long)obj.MyInt); - Assert.Equal((byte)42, (byte)obj.MyInt); - - // Verify floating point. - obj = JsonSerializer.Deserialize("4.2", options); - Assert.IsAssignableFrom(obj); - - double dbl = (double)obj; - Assert.Equal(4.2, dbl); - } - - [Fact] - public static void Array_UnknownTypeHandling() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - - dynamic obj = JsonSerializer.Deserialize(Serialization.Tests.DynamicTests.Json, options); - Assert.IsAssignableFrom(obj); - Assert.IsAssignableFrom(obj.MyArray); - - Assert.Equal(2, obj.MyArray.Count); - Assert.Equal(1, (int)obj.MyArray[0]); - Assert.Equal(2, (int)obj.MyArray[1]); - - int count = 0; - foreach (object value in obj.MyArray) - { - count++; - } - Assert.Equal(2, count); - Assert.Equal(2, obj.MyArray.Count); - - obj.MyArray[0] = 10; - Assert.IsAssignableFrom(obj.MyArray[0]); - - Assert.Equal(10, (int)obj.MyArray[0]); - } - - [Fact] - public static void CreateDom_UnknownTypeHandling() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - - string GuidJson = $"{Serialization.Tests.DynamicTests.MyGuid.ToString("D")}"; - - // We can't convert an unquoted string to a Guid - dynamic dynamicString = JsonValue.Create(GuidJson); - InvalidOperationException ex = Assert.Throws(() => (Guid)dynamicString); - // "A value of type 'System.String' cannot be converted to a 'System.Guid'." - Assert.Contains(typeof(string).ToString(), ex.Message); - Assert.Contains(typeof(Guid).ToString(), ex.Message); - - string json; - - // Number (JsonElement) - using (JsonDocument doc = JsonDocument.Parse($"{decimal.MaxValue}")) - { - dynamic dynamicNumber = JsonValue.Create(doc.RootElement); - Assert.Equal(decimal.MaxValue, (decimal)dynamicNumber); - json = dynamicNumber.ToJsonString(options); - Assert.Equal(decimal.MaxValue.ToString(), json); - } - - // Boolean - dynamic dynamicBool = JsonValue.Create(true); - Assert.True((bool)dynamicBool); - json = dynamicBool.ToJsonString(options); - Assert.Equal("true", json); - - // Array - dynamic arr = new JsonArray(); - arr.Add(1); - arr.Add(2); - json = arr.ToJsonString(options); - Assert.Equal("[1,2]", json); - - // Object - dynamic dynamicObject = new JsonObject(); - dynamicObject.One = 1; - dynamicObject.Two = 2; - - json = dynamicObject.ToJsonString(options); - JsonTestHelper.AssertJsonEqual("{\"One\":1,\"Two\":2}", json); - } - - /// - /// Use a mutable DOM with the 'dynamic' keyword. - /// - [Fact] - public static void UnknownTypeHandling_Object() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - - dynamic obj = JsonSerializer.Deserialize(Serialization.Tests.DynamicTests.Json, options); - Assert.IsAssignableFrom(obj); - - // Change some primitives. - obj.MyString = "Hello!"; - obj.MyBoolean = false; - obj.MyInt = 43; - - // Add nested objects. - // Use JsonObject; ExpandoObject should not be used since it doesn't have the same semantics including - // null handling and case-sensitivity that respects JsonSerializerOptions.PropertyNameCaseInsensitive. - dynamic myObject = new JsonObject(); - myObject.MyString = "Hello!!"; - obj.MyObject = myObject; - - dynamic child = new JsonObject(); - child.ChildProp = 1; - obj.Child = child; - - // Modify number elements. - dynamic arr = obj.MyArray; - arr[0] = (int)arr[0] + 1; - arr[1] = (int)arr[1] + 1; - - // Add an element. - arr.Add(42); - - string json = obj.ToJsonString(options); - JsonTestHelper.AssertJsonEqual(JsonNodeTests.ExpectedDomJson, json); - } - - [Fact] - public static void ConvertJsonArrayToIListOfJsonNode() - { - dynamic obj = JsonSerializer.Deserialize("[42]"); - Assert.Equal(42, (int)obj[0]); - - IList ilist = obj; - Assert.NotNull(ilist); - Assert.Equal(42, (int)ilist[0]); - } - - [Fact] - public static void UnknownTypeHandling_CaseSensitivity() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - dynamic obj = JsonSerializer.Deserialize("{\"MyProperty\":42}", options); - - Assert.IsType(obj); - Assert.IsAssignableFrom(obj.MyProperty); - - Assert.Equal(42, (int)obj.MyProperty); - Assert.Null(obj.myProperty); - Assert.Null(obj.MYPROPERTY); - - options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - options.PropertyNameCaseInsensitive = true; - obj = JsonSerializer.Deserialize("{\"MyProperty\":42}", options); - - Assert.Equal(42, (int)obj.MyProperty); - Assert.Equal(42, (int)obj.myproperty); - Assert.Equal(42, (int)obj.MYPROPERTY); - } - - [Fact] - public static void MissingProperty_UnknownTypeHandling() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - dynamic obj = JsonSerializer.Deserialize("{}", options); - Assert.Equal(null, obj.NonExistingProperty); - } - - [Fact] - public static void Linq_UnknownTypeHandling() - { - var options = new JsonSerializerOptions(); - options.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; - - IEnumerable allOrders = JsonSerializer.Deserialize>(JsonNodeTests.Linq_Query_Json, options); - IEnumerable orders = allOrders.Where(o => ((string)o.Customer.City) == "Fargo"); - - Assert.Equal(2, orders.Count()); - Assert.Equal(100, (int)orders.ElementAt(0).OrderId); - Assert.Equal(300, (int)orders.ElementAt(1).OrderId); - Assert.Equal("Customer1", (string)orders.ElementAt(0).Customer.Name); - Assert.Equal("Customer3", (string)orders.ElementAt(1).Customer.Name); - - // Verify methods can be called as well. - Assert.Equal(100, orders.ElementAt(0).OrderId.GetValue()); - Assert.Equal(300, orders.ElementAt(1).OrderId.GetValue()); - Assert.Equal("Customer1", orders.ElementAt(0).Customer.Name.GetValue()); - Assert.Equal("Customer3", orders.ElementAt(1).Customer.Name.GetValue()); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs index b451ff0bc22..e5b1c18cf57 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs @@ -196,6 +196,48 @@ namespace System.Text.Json.Nodes.Tests Assert.Throws(() => jArray.CopyTo(arr, -1)); } + [Fact] + public static void ConvertJSONArrayToIListOfJsonNode() + { + dynamic obj = JsonSerializer.Deserialize("[42]"); + Assert.Equal(42, (int)obj[0]); + + IList ilist = obj; + Assert.NotNull(ilist); + Assert.Equal(42, (int)ilist[0]); + } + + [Fact] + public static void ConvertJSONArrayToJsonArray() + { + JsonArray nodes = JsonSerializer.Deserialize("[1,1.1,\"Hello\"]"); + Assert.Equal(1, (long)nodes[0]); + Assert.Equal(1.1, (double)nodes[1]); + Assert.Equal("Hello", (string)nodes[2]); + } + + [Fact] + public static void ConvertJSONArrayToJsonNodeArray() + { + // Instead of JsonArray, use array of JsonNodes + JsonNode[] nodes = JsonSerializer.Deserialize("[1,1.1,\"Hello\"]"); + Assert.Equal(1, (long)nodes[0]); + Assert.Equal(1.1, (double)nodes[1]); + Assert.Equal("Hello", (string)nodes[2]); + } + + [Fact] + public static void ConvertJSONArrayToObjectArray() + { + // Instead of JsonArray, use array of objects + JsonSerializerOptions options = new(); + options.UnknownTypeHandling = Serialization.JsonUnknownTypeHandling.JsonNode; + object[] nodes = JsonSerializer.Deserialize("[1,1.1,\"Hello\"]", options); + Assert.Equal(1, (long)(JsonNode)nodes[0]); + Assert.Equal(1.1, (double)(JsonNode)nodes[1]); + Assert.Equal("Hello", (string)(JsonNode)nodes[2]); + } + [Fact] public static void ReAddSameNode_Throws() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 41a5ec38d5a..20d919ca164 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -53,7 +53,6 @@ - diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/StackOfT.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/StackOfT.cs index 48435095e57..c408c57cf90 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/StackOfT.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/StackOfT.cs @@ -13,12 +13,14 @@ namespace SerializerTrimmingTest { static int Main(string[] args) { - string json = "[1]"; - object obj = JsonSerializer.Deserialize(json, typeof(Stack)); - if (!(TestHelper.AssertCollectionAndSerialize>(obj, json))) - { - return -1; - } + // Test is currently disabled until issue #53393 is addressed. + + //string json = "[1]"; + //object obj = JsonSerializer.Deserialize(json, typeof(Stack)); + //if (!(TestHelper.AssertCollectionAndSerialize>(obj, json))) + //{ + // return -1; + //} return 100; } From 59ab54cd8c4af11f64f8a42615437e44f4712fb1 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Wed, 14 Jul 2021 03:01:19 +0900 Subject: [PATCH 498/926] Fix Tizen ARMEL Build Failure (#55545) * Fix Tizen ARMEL Build Failure * Check with MFD_CLOEXEC instead of ARM_SOFTFP --- src/coreclr/minipal/Unix/doublemapping.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp index a50b326861a..6e0278e3ccd 100644 --- a/src/coreclr/minipal/Unix/doublemapping.cpp +++ b/src/coreclr/minipal/Unix/doublemapping.cpp @@ -14,10 +14,11 @@ #include #include #include -#ifdef TARGET_LINUX +#if defined(TARGET_LINUX) && !defined(MFD_CLOEXEC) #include #include // __NR_memfd_create -#endif // TARGET_LINUX +#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__) +#endif // TARGET_LINUX && !MFD_CLOEXEC #include "minipal.h" #if defined(TARGET_OSX) && defined(TARGET_AMD64) @@ -32,10 +33,6 @@ static const off_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024; static const off_t MaxDoubleMappedSize = UINT_MAX; #endif -#ifdef TARGET_LINUX -#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__) -#endif // TARGET_LINUX - #endif // TARGET_OSX bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize) From 2d3acc6a3dff03f89cc3d08d5f1fb6f5fb3077fc Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 13 Jul 2021 14:01:31 -0400 Subject: [PATCH 499/926] Add one-shot CFB encrypt/decrypt to SymmetricAlgorithm Adds the CFB versions similar to the already-added ECB and CBC versions. Co-authored-by: Jeremy Barton --- .../src/Internal/Cryptography/Helpers.cs | 9 +- .../AES/AesCipherOneShotTests.cs | 605 +++++++++++++++++- .../AES/AesCipherTests.cs | 8 +- .../DES/DESCipherOneShotTests.cs | 299 ++++++++- .../DES/DESCipherTests.cs | 27 +- .../DES/DESFactory.cs | 7 +- .../RC2/RC2CipherOneShotTests.cs | 20 + .../Symmetric/SymmetricOneShotBase.cs | 77 ++- .../TripleDES/TripleDESCipherOneShotTests.cs | 583 ++++++++++++++++- .../TripleDES/TripleDESCipherTests.cs | 8 +- .../Cryptography/AesImplementation.cs | 52 ++ .../Cryptography/DesImplementation.cs | 52 ++ .../Cryptography/RC2Implementation.cs | 22 + .../Cryptography/TripleDesImplementation.cs | 52 ++ .../tests/DESProvider.cs | 6 +- .../Cryptography/CngSymmetricAlgorithmCore.cs | 33 +- .../Cryptography/ICngSymmetricAlgorithm.cs | 2 +- .../src/Resources/Strings.resx | 3 + .../System/Security/Cryptography/AesCng.cs | 82 ++- .../Security/Cryptography/TripleDESCng.cs | 82 ++- .../tests/AesCngTests.cs | 24 +- .../tests/SymmetricCngTestHelpers.cs | 49 +- .../tests/TripleDESCngTests.cs | 18 +- .../tests/DESCryptoServiceProviderProvider.cs | 6 +- .../tests/ShimHelpers.cs | 2 + ...System.Security.Cryptography.Primitives.cs | 10 + .../Cryptography/SymmetricAlgorithm.cs | 545 ++++++++++++++++ .../tests/SymmetricAlgorithmTests.cs | 350 ++++++++++ 28 files changed, 2857 insertions(+), 176 deletions(-) diff --git a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs index 2ee5ffc39c4..6614b3c6d00 100644 --- a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs +++ b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs @@ -30,14 +30,9 @@ namespace Internal.Cryptography return (byte[])(src.Clone()); } - public static int GetPaddingSize(this SymmetricAlgorithm algorithm, CipherMode mode, int feedbackSizeBits) + public static int GetPaddingSize(this SymmetricAlgorithm algorithm, CipherMode mode, int feedbackSizeInBits) { - // CFB8 does not require any padding at all - // otherwise, it is always required to pad for block size - if (mode == CipherMode.CFB && feedbackSizeBits == 8) - return 1; - - return algorithm.BlockSize / 8; + return (mode == CipherMode.CFB ? feedbackSizeInBits : algorithm.BlockSize) / 8; } internal static bool TryCopyToDestination(ReadOnlySpan source, Span destination, out int bytesWritten) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs index 77d5e0cf778..bc109f9bd03 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherOneShotTests.cs @@ -22,68 +22,88 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests [Theory] [MemberData(nameof(TestCases))] - public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); + + [Fact] + public void EncryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryEncryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 120)); + } + } + + [Fact] + public void DecryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryDecryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 120)); + } + } public static IEnumerable TestCases { @@ -91,6 +111,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { yield return new object[] { + // plaintext new byte[] { @@ -568,6 +589,538 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests PaddingMode.PKCS7, CipherMode.ECB, }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x8B, 0x08, 0x3E, 0x07, 0xA4, 0x03, 0x16, + 0x0A, 0x75, 0x1A, 0x15, 0xF6, 0x1D, 0xAB, 0xD9, + 0xD2, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x8B, 0x08, 0x3E, 0x07, 0xA4, 0x03, 0x16, + 0x0A, 0x75, 0x1A, 0x15, 0xF6, 0x1D, 0xAB, 0xD9, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x8B, 0x08, 0x3E, 0x07, 0xA4, 0x03, 0x16, + 0x0A, 0x75, 0x1A, 0x15, 0xF6, 0x1D, 0xAB, 0xD9, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x8B, 0x08, 0x3E, 0x07, 0xA4, 0x03, 0x16, + 0x0A, 0x75, 0x1A, 0x15, 0xF6, 0x1D, 0xAB, 0xD9, + 0xD2, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x8B, 0x08, 0x3E, 0x07, 0xA4, 0x03, 0x16, + 0x0A, 0x75, 0x1A, 0x15, 0xF6, 0x1D, 0xAB, 0xD9, + 0xD2, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x84, 0x44, 0xEB, 0x82, 0x11, 0xEA, 0x28, + 0x91, 0x8E, 0xA8, 0x40, 0xE4, 0x12, 0x3F, 0x72, + 0xF9, 0x97, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x84, 0x44, 0xEB, 0x82, 0x11, 0xEA, 0x28, + 0x91, 0x8E, 0xA8, 0x40, 0xE4, 0x12, 0x3F, 0x72, + 0xF9, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x84, 0x44, 0xEB, 0x82, 0x11, 0xEA, 0x28, + 0x91, 0x8E, 0xA8, 0x40, 0xE4, 0x12, 0x3F, 0x72, + 0xF9, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x84, 0x44, 0xEB, 0x82, 0x11, 0xEA, 0x28, + 0x91, 0x8E, 0xA8, 0x40, 0xE4, 0x12, 0x3F, 0x72, + 0xF9, 0x97, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x84, 0x44, 0xEB, 0x82, 0x11, 0xEA, 0x28, + 0x91, 0x8E, 0xA8, 0x40, 0xE4, 0x12, 0x3F, 0x72, + 0xF9, 0x97, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x02, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + // CFB128 is not supported on Windows 7. + if (PlatformDetection.IsNotWindows7) + { + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x3F, 0x49, 0x1D, 0x53, 0x29, 0x39, 0x67, + 0x8A, 0x06, 0x28, 0x76, 0x34, 0x9A, 0x2D, 0xE3, + 0x2B, 0x63, 0xD4, 0x34, 0x86, 0x05, 0x9B, 0x52, + 0x20, 0x46, 0x65, 0xD5, 0xBC, 0xA1, 0xED, 0x11, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x3F, 0x49, 0x1D, 0x53, 0x29, 0x39, 0x67, + 0x8A, 0x06, 0x28, 0x76, 0x34, 0x9A, 0x2D, 0xE3, + }, + + PaddingMode.None, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x3F, 0x49, 0x1D, 0x53, 0x29, 0x39, 0x67, + 0x8A, 0x06, 0x28, 0x76, 0x34, 0x9A, 0x2D, 0xE3, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x3F, 0x49, 0x1D, 0x53, 0x29, 0x39, 0x67, + 0x8A, 0x06, 0x28, 0x76, 0x34, 0x9A, 0x2D, 0xE3, + 0x3B, 0x73, 0xC4, 0x24, 0x96, 0x15, 0x8B, 0x42, + 0x30, 0x56, 0x75, 0xC5, 0xAC, 0xB1, 0xFD, 0x11, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x53, 0x3F, 0x49, 0x1D, 0x53, 0x29, 0x39, 0x67, + 0x8A, 0x06, 0x28, 0x76, 0x34, 0x9A, 0x2D, 0xE3, + 0x3E, 0x5D, 0xED, 0x96, 0x51, 0x93, 0xF0, 0x12, + 0x95, 0x98, 0x51, 0x29, 0xB6, 0xF8, 0x84, 0x11, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x07, 0x33, 0xAB, 0xA8, 0x7E, 0xF9, 0x26, + 0xBA, 0xC0, 0x0E, 0xAF, 0xB7, 0x12, 0x25, 0x39, + 0x0C, 0xD0, 0xD4, 0xF1, 0x60, 0x93, 0xD0, 0x20, + 0x91, 0x11, 0xD8, 0xF6, 0x27, 0xE3, 0xAF, 0x0F, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x07, 0x33, 0xAB, 0xA8, 0x7E, 0xF9, 0x26, + 0xBA, 0xC0, 0x0E, 0xAF, 0xB7, 0x12, 0x25, 0x39, + 0x0C, 0xDF, 0xDB, 0xFE, 0x6F, 0x9C, 0xDF, 0x2F, + 0x9E, 0x1E, 0xD7, 0xF9, 0x28, 0xEC, 0xA0, 0x00, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x07, 0x33, 0xAB, 0xA8, 0x7E, 0xF9, 0x26, + 0xBA, 0xC0, 0x0E, 0xAF, 0xB7, 0x12, 0x25, 0x39, + 0x0C, 0xDF, 0xDB, 0xFE, 0x6F, 0x9C, 0xDF, 0x2F, + 0x9E, 0x1E, 0xD7, 0xF9, 0x28, 0xEC, 0xA0, 0x0F, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x9A, 0x07, 0x33, 0xAB, 0xA8, 0x7E, 0xF9, 0x26, + 0xBA, 0xC0, 0x0E, 0xAF, 0xB7, 0x12, 0x25, 0x39, + 0x0C, 0x0C, 0x39, 0x31, 0x1C, 0xAA, 0x41, 0x45, + 0x78, 0xD0, 0x9F, 0x0F, 0x44, 0xD9, 0x37, 0x0F, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x13, 0x47, 0x4B, 0xA9, 0x1C, 0x31, 0xE1, 0xFE, + 0x23, 0x69, 0x61, 0xE6, 0x27, 0x01, 0xBE, 0xAA, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CFB, + 128, + }; + + yield return new object[] + { + + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CFB, + 128, + }; + } } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs index 465958943f8..2144dad5aaf 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesCipherTests.cs @@ -1124,6 +1124,7 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests { aes.Mode = cipherMode; aes.Padding = paddingMode; + aes.Key = key; if (feedbackSize.HasValue) { @@ -1135,16 +1136,19 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests if (cipherMode == CipherMode.ECB) { - aes.Key = key; liveOneShotDecryptBytes = aes.DecryptEcb(cipherBytes, paddingMode); liveOneShotEncryptBytes = aes.EncryptEcb(plainBytes, paddingMode); } else if (cipherMode == CipherMode.CBC) { - aes.Key = key; liveOneShotDecryptBytes = aes.DecryptCbc(cipherBytes, iv, paddingMode); liveOneShotEncryptBytes = aes.EncryptCbc(plainBytes, iv, paddingMode); } + else if (cipherMode == CipherMode.CFB) + { + liveOneShotDecryptBytes = aes.DecryptCfb(cipherBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + liveOneShotEncryptBytes = aes.EncryptCfb(plainBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + } } Assert.Equal(cipherBytes, liveEncryptBytes); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs index d775ffb9f12..0a050f84273 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherOneShotTests.cs @@ -26,68 +26,88 @@ namespace System.Security.Cryptography.Encryption.Des.Tests [Theory] [MemberData(nameof(TestCases))] - public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); + + [Fact] + public void EncryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryEncryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 56)); + } + } + + [Fact] + public void DecryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryDecryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 56)); + } + } public static IEnumerable TestCases { @@ -553,6 +573,233 @@ namespace System.Security.Cryptography.Encryption.Des.Tests PaddingMode.PKCS7, CipherMode.ECB, }; + + // Windows 7 does not support CFB8 + if (PlatformDetection.IsNotWindows7) + { + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x6F, 0xF8, 0x24, 0x52, 0x68, 0x8E, 0x53, 0x97, + 0x2A, 0x6B, 0x8A, 0x5E, 0xBE, 0x98, 0x84, 0x28, + 0x39, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x6F, 0xF8, 0x24, 0x52, 0x68, 0x8E, 0x53, 0x97, + 0x2A, 0x6B, 0x8A, 0x5E, 0xBE, 0x98, 0x84, 0x28, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x6F, 0xF8, 0x24, 0x52, 0x68, 0x8E, 0x53, 0x97, + 0x2A, 0x6B, 0x8A, 0x5E, 0xBE, 0x98, 0x84, 0x28, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x6F, 0xF8, 0x24, 0x52, 0x68, 0x8E, 0x53, 0x97, + 0x2A, 0x6B, 0x8A, 0x5E, 0xBE, 0x98, 0x84, 0x28, + 0x39, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x6F, 0xF8, 0x24, 0x52, 0x68, 0x8E, 0x53, 0x97, + 0x2A, 0x6B, 0x8A, 0x5E, 0xBE, 0x98, 0x84, 0x28, + 0x39, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xA6, 0x51, 0xB4, 0xF2, 0x2B, 0xFA, 0x22, 0xF5, + 0x15, 0x1E, 0x5E, 0x65, 0x39, 0xFD, 0x84, 0x4F, + 0xE1, 0x7E, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xA6, 0x51, 0xB4, 0xF2, 0x2B, 0xFA, 0x22, 0xF5, + 0x15, 0x1E, 0x5E, 0x65, 0x39, 0xFD, 0x84, 0x4F, + 0xE1, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xA6, 0x51, 0xB4, 0xF2, 0x2B, 0xFA, 0x22, 0xF5, + 0x15, 0x1E, 0x5E, 0x65, 0x39, 0xFD, 0x84, 0x4F, + 0xE1, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xA6, 0x51, 0xB4, 0xF2, 0x2B, 0xFA, 0x22, 0xF5, + 0x15, 0x1E, 0x5E, 0x65, 0x39, 0xFD, 0x84, 0x4F, + 0xE1, 0x7E, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0xA6, 0x51, 0xB4, 0xF2, 0x2B, 0xFA, 0x22, 0xF5, + 0x15, 0x1E, 0x5E, 0x65, 0x39, 0xFD, 0x84, 0x4F, + 0xE1, 0x7E, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + } } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs index d23a48a2851..707db864be1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESCipherTests.cs @@ -531,6 +531,7 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { des.Mode = cipherMode; des.Padding = paddingMode; + des.Key = key; if (feedbackSize.HasValue) { @@ -540,17 +541,23 @@ namespace System.Security.Cryptography.Encryption.Des.Tests liveEncryptBytes = DESEncryptDirectKey(des, key, iv, plainBytes); liveDecryptBytes = DESDecryptDirectKey(des, key, iv, cipherBytes); - if (cipherMode == CipherMode.ECB) + if (DESFactory.OneShotSupported) { - des.Key = key; - liveOneShotDecryptBytes = des.DecryptEcb(cipherBytes, paddingMode); - liveOneShotEncryptBytes = des.EncryptEcb(plainBytes, paddingMode); - } - else if (cipherMode == CipherMode.CBC) - { - des.Key = key; - liveOneShotDecryptBytes = des.DecryptCbc(cipherBytes, iv, paddingMode); - liveOneShotEncryptBytes = des.EncryptCbc(plainBytes, iv, paddingMode); + if (cipherMode == CipherMode.ECB) + { + liveOneShotDecryptBytes = des.DecryptEcb(cipherBytes, paddingMode); + liveOneShotEncryptBytes = des.EncryptEcb(plainBytes, paddingMode); + } + else if (cipherMode == CipherMode.CBC) + { + liveOneShotDecryptBytes = des.DecryptCbc(cipherBytes, iv, paddingMode); + liveOneShotEncryptBytes = des.EncryptCbc(plainBytes, iv, paddingMode); + } + else if (cipherMode == CipherMode.CFB) + { + liveOneShotDecryptBytes = des.DecryptCfb(cipherBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + liveOneShotEncryptBytes = des.EncryptCfb(plainBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESFactory.cs index 5b191ca5ea0..fd7a2f626e4 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESFactory.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DES/DESFactory.cs @@ -6,13 +6,12 @@ namespace System.Security.Cryptography.Encryption.Des.Tests public interface IDESProvider { DES Create(); + bool OneShotSupported { get; } } public static partial class DESFactory { - public static DES Create() - { - return s_provider.Create(); - } + public static DES Create() => s_provider.Create(); + public static bool OneShotSupported => s_provider.OneShotSupported; } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs index 411aadaf347..24d2befc125 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RC2/RC2CipherOneShotTests.cs @@ -90,6 +90,26 @@ namespace System.Security.Cryptography.Encryption.RC2.Tests public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + [Fact] + public void EncryptOneShot_CfbNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryEncryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _)); + } + } + + [Fact] + public void DecryptOneShot_CfbNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryDecryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _)); + } + } + public static IEnumerable TestCases { get diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs index 89e6b5d285d..42a9b584366 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/Symmetric/SymmetricOneShotBase.cs @@ -17,10 +17,11 @@ namespace System.Security.Cryptography.Tests protected abstract byte[] IV { get; } protected abstract SymmetricAlgorithm CreateAlgorithm(); - protected void OneShotRoundtripTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void OneShotRoundtripTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; alg.Key = Key; // Set the instance to use a different mode and padding than what will be used @@ -33,37 +34,41 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.EncryptEcb(plaintext, padding), CipherMode.CBC => alg.EncryptCbc(plaintext, IV, padding), + CipherMode.CFB => alg.EncryptCfb(plaintext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; byte[] decrypted = mode switch { CipherMode.ECB => alg.DecryptEcb(encrypted, padding), CipherMode.CBC => alg.DecryptCbc(encrypted, IV, padding), + CipherMode.CFB => alg.DecryptCfb(encrypted, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; AssertPlaintexts(plaintext, decrypted, padding); - AssertCiphertexts(encrypted, ciphertext, padding, alg.BlockSize / 8); + AssertCiphertexts(encrypted, ciphertext, padding, paddingSizeBytes); decrypted = mode switch { CipherMode.ECB => alg.DecryptEcb(ciphertext, padding), CipherMode.CBC => alg.DecryptCbc(ciphertext, IV, padding), + CipherMode.CFB => alg.DecryptCfb(ciphertext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; encrypted = mode switch { CipherMode.ECB => alg.EncryptEcb(decrypted, padding), CipherMode.CBC => alg.EncryptCbc(decrypted, IV, padding), + CipherMode.CFB => alg.EncryptCfb(decrypted, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; AssertPlaintexts(plaintext, decrypted, padding); - AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, encrypted, padding, paddingSizeBytes); } } - protected void TryDecryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryDecryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { if (plaintext.Length == 0) { @@ -82,6 +87,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryDecryptCfb(ciphertext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; @@ -90,7 +96,7 @@ namespace System.Security.Cryptography.Tests } } - protected void TryEncryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryEncryptOneShot_DestinationTooSmallTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { if (ciphertext.Length == 0) { @@ -109,6 +115,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryEncryptCfb(plaintext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; Assert.False(result, "TryEncrypt"); @@ -116,7 +123,7 @@ namespace System.Security.Cryptography.Tests } } - protected void TryDecryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryDecryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { @@ -127,11 +134,12 @@ namespace System.Security.Cryptography.Tests int bytesWritten; bool result = mode switch - { - CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), - CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), - _ => throw new NotImplementedException(), - }; + { + CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), + CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryDecryptCfb(ciphertext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), + _ => throw new NotImplementedException(), + }; Assert.True(result, "TryDecrypt"); Assert.Equal(destinationBuffer.Length, bytesWritten); @@ -139,16 +147,18 @@ namespace System.Security.Cryptography.Tests } } - protected void TryEncryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryEncryptOneShot_DestinationJustRightTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; alg.Key = Key; int expectedCiphertextSize = mode switch { CipherMode.ECB => alg.GetCiphertextLengthEcb(plaintext.Length, padding), CipherMode.CBC => alg.GetCiphertextLengthCbc(plaintext.Length, padding), + CipherMode.CFB => alg.GetCiphertextLengthCfb(plaintext.Length, padding, feedbackSize), _ => throw new NotImplementedException(), }; Span destinationBuffer = new byte[expectedCiphertextSize]; @@ -158,16 +168,17 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryEncryptCfb(plaintext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; Assert.True(result, "TryEncrypt"); Assert.Equal(expectedCiphertextSize, bytesWritten); - AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, destinationBuffer, padding, paddingSizeBytes); } } - protected void TryDecryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryDecryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { @@ -183,6 +194,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryDecryptEcb(ciphertext, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryDecryptCbc(ciphertext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryDecryptCfb(ciphertext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; @@ -196,10 +208,11 @@ namespace System.Security.Cryptography.Tests } } - protected void TryEncryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryEncryptOneShot_DestinationLargerTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; alg.Key = Key; Span largeBuffer = new byte[ciphertext.Length + 10]; @@ -211,18 +224,19 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryEncryptEcb(plaintext, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryEncryptCbc(plaintext, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryEncryptCfb(plaintext, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; Assert.True(result, "TryEncrypt"); Assert.Equal(destinationBuffer.Length, bytesWritten); - AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, destinationBuffer, padding, paddingSizeBytes); AssertExtensions.FilledWith(0xCC, largeBuffer.Slice(ciphertext.Length)); } } - protected void TryDecryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryDecryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { (int plaintextOffset, int ciphertextOffset)[] offsets = { @@ -247,6 +261,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryDecryptEcb(ciphertextBuffer, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryDecryptCbc(ciphertextBuffer, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryDecryptCfb(ciphertextBuffer, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; Assert.True(result, "TryDecrypt"); @@ -258,7 +273,7 @@ namespace System.Security.Cryptography.Tests } } - protected void TryEncryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void TryEncryptOneShot_OverlapsTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { (int plaintextOffset, int ciphertextOffset)[] offsets = { @@ -269,6 +284,7 @@ namespace System.Security.Cryptography.Tests { using (SymmetricAlgorithm alg = CreateAlgorithm()) { + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; alg.Key = Key; int destinationSize = ciphertext.Length + Math.Max(plaintextOffset, ciphertextOffset); @@ -282,18 +298,19 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.TryEncryptEcb(plaintextBuffer, destinationBuffer, padding, out bytesWritten), CipherMode.CBC => alg.TryEncryptCbc(plaintextBuffer, IV, destinationBuffer, out bytesWritten, padding), + CipherMode.CFB => alg.TryEncryptCfb(plaintextBuffer, IV, destinationBuffer, out bytesWritten, padding, feedbackSize), _ => throw new NotImplementedException(), }; Assert.True(result, "TryEncrypt"); Assert.Equal(destinationBuffer.Length, bytesWritten); - AssertCiphertexts(ciphertext, destinationBuffer, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, destinationBuffer, padding, paddingSizeBytes); Assert.True(destinationBuffer.Overlaps(plaintextBuffer) || plaintext.Length == 0 || ciphertext.Length == 0); } } } - protected void DecryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void DecryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { @@ -302,6 +319,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.DecryptEcb(ciphertext.AsSpan(), padding), CipherMode.CBC => alg.DecryptCbc(ciphertext.AsSpan(), IV.AsSpan(), padding), + CipherMode.CFB => alg.DecryptCfb(ciphertext.AsSpan(), IV.AsSpan(), padding, feedbackSize), _ => throw new NotImplementedException(), }; @@ -309,23 +327,25 @@ namespace System.Security.Cryptography.Tests } } - protected void EncryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void EncryptOneShot_SpanTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { alg.Key = Key; + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; byte[] encrypted = mode switch { CipherMode.ECB => alg.EncryptEcb(plaintext.AsSpan(), padding), CipherMode.CBC => alg.EncryptCbc(plaintext.AsSpan(), IV.AsSpan(), padding), + CipherMode.CFB => alg.EncryptCfb(plaintext.AsSpan(), IV.AsSpan(), padding, feedbackSize), _ => throw new NotImplementedException(), }; - AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, encrypted, padding, paddingSizeBytes); } } - protected void DecryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void DecryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { @@ -334,6 +354,7 @@ namespace System.Security.Cryptography.Tests { CipherMode.ECB => alg.DecryptEcb(ciphertext, padding), CipherMode.CBC => alg.DecryptCbc(ciphertext, IV, padding), + CipherMode.CFB => alg.DecryptCfb(ciphertext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; @@ -341,19 +362,21 @@ namespace System.Security.Cryptography.Tests } } - protected void EncryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) + protected void EncryptOneShot_ArrayTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { alg.Key = Key; + int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; byte[] encrypted = mode switch { CipherMode.ECB => alg.EncryptEcb(plaintext, padding), CipherMode.CBC => alg.EncryptCbc(plaintext, IV, padding), + CipherMode.CFB => alg.EncryptCfb(plaintext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; - AssertCiphertexts(ciphertext, encrypted, padding, alg.BlockSize / 8); + AssertCiphertexts(ciphertext, encrypted, padding, paddingSizeBytes); } } @@ -398,12 +421,12 @@ namespace System.Security.Cryptography.Tests } } - private static void AssertCiphertexts(ReadOnlySpan expected, ReadOnlySpan actual, PaddingMode padding, int blockSizeBytes) + private static void AssertCiphertexts(ReadOnlySpan expected, ReadOnlySpan actual, PaddingMode padding, int paddingSizeBytes) { if (padding == PaddingMode.ISO10126) { // The padding is random, so we can't check the exact ciphertext. - AssertExtensions.SequenceEqual(expected[..^blockSizeBytes], actual[..^blockSizeBytes]); + AssertExtensions.SequenceEqual(expected[..^paddingSizeBytes], actual[..^paddingSizeBytes]); } else { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs index d5706706858..1d9e69c0a87 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherOneShotTests.cs @@ -28,68 +28,88 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests [Theory] [MemberData(nameof(TestCases))] - public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - OneShotRoundtripTest(plaintext, ciphertext, padding, mode); + public void OneShotRoundtrip(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + OneShotRoundtripTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationTooSmall(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationTooSmallTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationJustRight(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationJustRightTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_DestinationLarger(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_DestinationLargerTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryDecryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryDecryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode); + public void TryEncryptOneShot_Overlaps(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + TryEncryptOneShot_OverlapsTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Span(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_SpanTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void DecryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + DecryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); [Theory] [MemberData(nameof(TestCases))] - public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode) => - EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode); + public void EncryptOneShot_Array(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) => + EncryptOneShot_ArrayTest(plaintext, ciphertext, padding, mode, feedbackSize); + + [Fact] + public void EncryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryEncryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 48)); + } + } + + [Fact] + public void DecryptOneShot_CfbFeedbackSizeNotSupported() + { + using (SymmetricAlgorithm alg = CreateAlgorithm()) + { + Assert.ThrowsAny(() => + alg.TryDecryptCfb(ReadOnlySpan.Empty, IV, Span.Empty, out _, feedbackSizeInBits: 48)); + } + } public static IEnumerable TestCases { @@ -557,6 +577,517 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests PaddingMode.PKCS7, CipherMode.ECB, }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x85, 0x45, 0x43, 0x3E, 0xD9, 0x40, 0xAF, + 0x16, 0xBE, 0xC5, 0xEF, 0xD9, 0x12, 0xFE, 0x07, + 0x66, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x85, 0x45, 0x43, 0x3E, 0xD9, 0x40, 0xAF, + 0x16, 0xBE, 0xC5, 0xEF, 0xD9, 0x12, 0xFE, 0x07, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x85, 0x45, 0x43, 0x3E, 0xD9, 0x40, 0xAF, + 0x16, 0xBE, 0xC5, 0xEF, 0xD9, 0x12, 0xFE, 0x07, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x85, 0x45, 0x43, 0x3E, 0xD9, 0x40, 0xAF, + 0x16, 0xBE, 0xC5, 0xEF, 0xD9, 0x12, 0xFE, 0x07, + 0x66, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x85, 0x45, 0x43, 0x3E, 0xD9, 0x40, 0xAF, + 0x16, 0xBE, 0xC5, 0xEF, 0xD9, 0x12, 0xFE, 0x07, + 0x66, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xD6, 0x1C, 0xF3, 0xAF, 0x0D, 0x3C, 0x98, + 0xCF, 0x29, 0x20, 0xB3, 0xF3, 0xFC, 0x34, 0xF0, + 0x38, 0xA0, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xD6, 0x1C, 0xF3, 0xAF, 0x0D, 0x3C, 0x98, + 0xCF, 0x29, 0x20, 0xB3, 0xF3, 0xFC, 0x34, 0xF0, + 0x38, + }, + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xD6, 0x1C, 0xF3, 0xAF, 0x0D, 0x3C, 0x98, + 0xCF, 0x29, 0x20, 0xB3, 0xF3, 0xFC, 0x34, 0xF0, + 0x38, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xD6, 0x1C, 0xF3, 0xAF, 0x0D, 0x3C, 0x98, + 0xCF, 0x29, 0x20, 0xB3, 0xF3, 0xFC, 0x34, 0xF0, + 0x38, 0xA0, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xD6, 0x1C, 0xF3, 0xAF, 0x0D, 0x3C, 0x98, + 0xCF, 0x29, 0x20, 0xB3, 0xF3, 0xFC, 0x34, 0xF0, + 0x38, 0xA0, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x17, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CFB, + 8, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CFB, + 8, + }; + + // 3DES CFB64 is not supported on Windows 7. + if (PlatformDetection.IsNotWindows7) + { + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x82, 0xB5, 0xFC, 0x6A, 0xD3, 0x92, 0xF9, + 0xB1, 0xF6, 0xD9, 0xF3, 0xBB, 0x8D, 0x57, 0xE5, + 0xFF, 0x66, 0x88, 0x3C, 0x53, 0xC4, 0x5A, 0xC6, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x82, 0xB5, 0xFC, 0x6A, 0xD3, 0x92, 0xF9, + 0xB1, 0xF6, 0xD9, 0xF3, 0xBB, 0x8D, 0x57, 0xE5, + }, + + PaddingMode.None, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x82, 0xB5, 0xFC, 0x6A, 0xD3, 0x92, 0xF9, + 0xB1, 0xF6, 0xD9, 0xF3, 0xBB, 0x8D, 0x57, 0xE5, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x82, 0xB5, 0xFC, 0x6A, 0xD3, 0x92, 0xF9, + 0xB1, 0xF6, 0xD9, 0xF3, 0xBB, 0x8D, 0x57, 0xE5, + 0xF7, 0x6E, 0x80, 0x34, 0x5B, 0xCC, 0x52, 0xC6, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, 0x89, + 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, 0x59, + }, + + // ciphertext + new byte[] + { + 0x46, 0x82, 0xB5, 0xFC, 0x6A, 0xD3, 0x92, 0xF9, + 0xB1, 0xF6, 0xD9, 0xF3, 0xBB, 0x8D, 0x57, 0xE5, + 0x47, 0x0F, 0x9A, 0x12, 0x6F, 0x92, 0xB4, 0xC6, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xBA, 0xCF, 0x4A, 0x91, 0x84, 0x52, 0xB8, + 0xFF, 0x69, 0xE0, 0xAD, 0x2C, 0xEC, 0xE4, 0x8F, + 0xE0, 0x50, 0x64, 0xD5, 0xA3, 0x32, 0x38, 0xA9, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xBA, 0xCF, 0x4A, 0x91, 0x84, 0x52, 0xB8, + 0xFF, 0x69, 0xE0, 0xAD, 0x2C, 0xEC, 0xE4, 0x8F, + 0xE0, 0x57, 0x63, 0xD2, 0xA4, 0x35, 0x3F, 0xAE, + }, + + PaddingMode.Zeros, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xBA, 0xCF, 0x4A, 0x91, 0x84, 0x52, 0xB8, + 0xFF, 0x69, 0xE0, 0xAD, 0x2C, 0xEC, 0xE4, 0x8F, + 0xE0, 0x57, 0x63, 0xD2, 0xA4, 0x35, 0x3F, 0xA9, + }, + + PaddingMode.ANSIX923, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + new byte[] + { + 0x99, 0x50, 0x68, 0x12, 0xA4, 0x5F, 0x08, 0xC8, + 0x89, 0xB9, 0x7F, 0x59, 0x80, 0x03, 0x8B, 0x83, + 0x59, + }, + + // ciphertext + new byte[] + { + 0x8F, 0xBA, 0xCF, 0x4A, 0x91, 0x84, 0x52, 0xB8, + 0xFF, 0x69, 0xE0, 0xAD, 0x2C, 0xEC, 0xE4, 0x8F, + 0xE0, 0xE7, 0xF6, 0x44, 0xBE, 0xDD, 0x3D, 0xA9, + }, + + PaddingMode.ISO10126, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + new byte[] + { + 0x1E, 0xE2, 0xAF, 0x50, 0x3D, 0xD3, 0x52, 0x78, + }, + + PaddingMode.PKCS7, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.None, + CipherMode.CFB, + 64, + }; + + yield return new object[] + { + // plaintext + Array.Empty(), + + // ciphertext + Array.Empty(), + + PaddingMode.Zeros, + CipherMode.CFB, + 64, + }; + } } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs index 64b3203b4b7..fc627effbc7 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs @@ -499,6 +499,7 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests { tdes.Mode = cipherMode; tdes.Padding = paddingMode; + tdes.Key = key; if (feedbackSize.HasValue) { @@ -510,16 +511,19 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests if (cipherMode == CipherMode.ECB) { - tdes.Key = key; liveOneShotDecryptBytes = tdes.DecryptEcb(cipherBytes, paddingMode); liveOneShotEncryptBytes = tdes.EncryptEcb(plainBytes, paddingMode); } else if (cipherMode == CipherMode.CBC) { - tdes.Key = key; liveOneShotDecryptBytes = tdes.DecryptCbc(cipherBytes, iv, paddingMode); liveOneShotEncryptBytes = tdes.EncryptCbc(plainBytes, iv, paddingMode); } + else if (cipherMode == CipherMode.CFB) + { + liveOneShotDecryptBytes = tdes.DecryptCfb(cipherBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + liveOneShotEncryptBytes = tdes.EncryptCfb(plainBytes, iv, paddingMode, feedbackSizeInBits: feedbackSize.Value); + } if (liveOneShotDecryptBytes is not null) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs index b08732a5aea..dee729afd93 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.cs @@ -133,6 +133,58 @@ namespace Internal.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + private ICryptoTransform CreateTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting) { // note: rbgIV is guaranteed to be cloned before this method, so no need to clone it again diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs index d0b960fc4ae..e2fbafdbd15 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.cs @@ -177,6 +177,58 @@ namespace Internal.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // only 8bits feedback is available on all platforms diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs index 33f39984990..0221bb5775b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.cs @@ -190,6 +190,28 @@ namespace Internal.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + throw new CryptographicException(SR.Format(SR.Cryptography_CipherModeNotSupported, CipherMode.CFB)); + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + throw new CryptographicException(SR.Format(SR.Cryptography_CipherModeNotSupported, CipherMode.CFB)); + } + private static void ValidateCFBFeedbackSize(int feedback) { // CFB not supported at all diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs index 59328765642..e79f466f131 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs @@ -182,6 +182,58 @@ namespace Internal.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + encrypting: false); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = CreateTransformCore( + CipherMode.CFB, + paddingMode, + Key, + iv: iv.ToArray(), + blockSize: BlockSize / BitsPerByte, + paddingSize: feedbackSizeInBits / BitsPerByte, + feedbackSizeInBits / BitsPerByte, + encrypting: true); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + private static void ValidateCFBFeedbackSize(int feedback) { // only 8bits/64bits feedback would be valid. diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DESProvider.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DESProvider.cs index e4cb6a6302e..4679eb99b16 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DESProvider.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DESProvider.cs @@ -5,10 +5,8 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { internal class DesProvider : IDESProvider { - public DES Create() - { - return DES.Create(); - } + public DES Create() => DES.Create(); + public bool OneShotSupported => true; } public partial class DESFactory diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs index 6b323ccf460..6da56739bb8 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs @@ -111,35 +111,35 @@ namespace Internal.Cryptography public ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV) { - return CreateCryptoTransform(rgbKey, rgbIV, encrypting: true, _outer.Padding, _outer.Mode); + return CreateCryptoTransform(rgbKey, rgbIV, encrypting: true, _outer.Padding, _outer.Mode, _outer.FeedbackSize); } public ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV) { - return CreateCryptoTransform(rgbKey, rgbIV, encrypting: false, _outer.Padding, _outer.Mode); + return CreateCryptoTransform(rgbKey, rgbIV, encrypting: false, _outer.Padding, _outer.Mode, _outer.FeedbackSize); } private ICryptoTransform CreateCryptoTransform(bool encrypting) { if (KeyInPlainText) { - return CreateCryptoTransform(_outer.BaseKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode); + return CreateCryptoTransform(_outer.BaseKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode, _outer.FeedbackSize); } - return CreatePersistedCryptoTransformCore(ProduceCngKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode); + return CreatePersistedCryptoTransformCore(ProduceCngKey, _outer.IV, encrypting, _outer.Padding, _outer.Mode, _outer.FeedbackSize); } - public UniversalCryptoTransform CreateCryptoTransform(byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) + public UniversalCryptoTransform CreateCryptoTransform(byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode, int feedbackSizeInBits) { if (KeyInPlainText) { - return CreateCryptoTransform(_outer.BaseKey, iv, encrypting, padding, mode); + return CreateCryptoTransform(_outer.BaseKey, iv, encrypting, padding, mode, feedbackSizeInBits); } - return CreatePersistedCryptoTransformCore(ProduceCngKey, iv, encrypting, padding, mode); + return CreatePersistedCryptoTransformCore(ProduceCngKey, iv, encrypting, padding, mode, feedbackSizeInBits); } - private UniversalCryptoTransform CreateCryptoTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting, PaddingMode padding, CipherMode mode) + private UniversalCryptoTransform CreateCryptoTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting, PaddingMode padding, CipherMode mode, int feedbackSizeInBits) { if (rgbKey == null) throw new ArgumentNullException(nameof(rgbKey)); @@ -162,19 +162,19 @@ namespace Internal.Cryptography key = _outer.PreprocessKey(key); - return CreateEphemeralCryptoTransformCore(key, iv, encrypting, padding, mode); + return CreateEphemeralCryptoTransformCore(key, iv, encrypting, padding, mode, feedbackSizeInBits); } - private UniversalCryptoTransform CreateEphemeralCryptoTransformCore(byte[] key, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) + private UniversalCryptoTransform CreateEphemeralCryptoTransformCore(byte[] key, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode, int feedbackSizeInBits) { int blockSizeInBytes = _outer.BlockSize.BitSizeToByteSize(); - SafeAlgorithmHandle algorithmModeHandle = _outer.GetEphemeralModeHandle(mode); + SafeAlgorithmHandle algorithmModeHandle = _outer.GetEphemeralModeHandle(mode, feedbackSizeInBits); BasicSymmetricCipher cipher = new BasicSymmetricCipherBCrypt( algorithmModeHandle, mode, blockSizeInBytes, - _outer.GetPaddingSize(mode, _outer.FeedbackSize), + _outer.GetPaddingSize(mode, feedbackSizeInBits), key, ownsParentHandle: false, iv, @@ -183,20 +183,19 @@ namespace Internal.Cryptography return UniversalCryptoTransform.Create(padding, cipher, encrypting); } - private UniversalCryptoTransform CreatePersistedCryptoTransformCore(Func cngKeyFactory, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode) + private UniversalCryptoTransform CreatePersistedCryptoTransformCore(Func cngKeyFactory, byte[]? iv, bool encrypting, PaddingMode padding, CipherMode mode, int feedbackSizeInBits) { // note: iv is guaranteed to be cloned before this method, so no need to clone it again int blockSizeInBytes = _outer.BlockSize.BitSizeToByteSize(); - int feedbackSizeInBytes = _outer.FeedbackSize; BasicSymmetricCipher cipher = new BasicSymmetricCipherNCrypt( cngKeyFactory, mode, blockSizeInBytes, iv, encrypting, - feedbackSizeInBytes, - _outer.GetPaddingSize(mode, _outer.FeedbackSize)); + feedbackSizeInBits, + _outer.GetPaddingSize(mode, feedbackSizeInBits)); return UniversalCryptoTransform.Create(padding, cipher, encrypting); } @@ -207,7 +206,7 @@ namespace Internal.Cryptography return CngKey.Open(_keyName!, _provider!, _optionOptions); } - private bool KeyInPlainText + public bool KeyInPlainText { get { return _keyName == null; } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs index 1e1edf7c792..eae2d44dec5 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs @@ -28,7 +28,7 @@ namespace Internal.Cryptography // Other members. bool IsWeakKey(byte[] key); - SafeAlgorithmHandle GetEphemeralModeHandle(CipherMode mode); + SafeAlgorithmHandle GetEphemeralModeHandle(CipherMode mode, int feedbackSizeInBits); string GetNCryptAlgorithmIdentifier(); byte[] PreprocessKey(byte[] key); int GetPaddingSize(CipherMode mode, int feedbackSizeBits); diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx index bc28c3819fc..b919b72f362 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx @@ -126,6 +126,9 @@ Keys used with the RSACng algorithm must have an algorithm group of RSA. + + The specified feedback size '{0}' for CipherMode '{1}' is not supported. + This key is for algorithm '{0}'. Expected '{1}'. diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs index 311bf2702ab..dbcbd0bd8ce 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs @@ -102,7 +102,8 @@ namespace System.Security.Cryptography iv: null, encrypting: false, paddingMode, - CipherMode.ECB); + CipherMode.ECB, + feedbackSizeInBits: 0); using (transform) { @@ -120,7 +121,8 @@ namespace System.Security.Cryptography iv: null, encrypting: true, paddingMode, - CipherMode.ECB); + CipherMode.ECB, + feedbackSizeInBits: 0); using (transform) { @@ -139,7 +141,8 @@ namespace System.Security.Cryptography iv: iv.ToArray(), encrypting: true, paddingMode, - CipherMode.CBC); + CipherMode.CBC, + feedbackSizeInBits: 0); using (transform) { @@ -158,7 +161,8 @@ namespace System.Security.Cryptography iv: iv.ToArray(), encrypting: false, paddingMode, - CipherMode.CBC); + CipherMode.CBC, + feedbackSizeInBits: 0); using (transform) { @@ -166,6 +170,72 @@ namespace System.Security.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: false, + paddingMode, + CipherMode.CFB, + feedbackSizeInBits); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: true, + paddingMode, + CipherMode.CFB, + feedbackSizeInBits); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + + private void ValidateCFBFeedbackSize(int feedback) + { + if (_core.KeyInPlainText) + { + // CFB8 and CFB128 are valid for bcrypt keys. + if (feedback != 8 && feedback != 128) + { + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); + } + } + else + { + // only CFB8 is supported for ncrypt keys. + if (feedback != 8) + { + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); + } + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -184,11 +254,11 @@ namespace System.Security.Cryptography return this.GetPaddingSize(mode, feedbackSizeBits); } - SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode) + SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode, int feedbackSizeInBits) { try { - return AesBCryptModes.GetSharedHandle(mode, FeedbackSize / 8); + return AesBCryptModes.GetSharedHandle(mode, feedbackSizeInBits / 8); } catch (NotSupportedException) { diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs index b03c756c1ad..09d2b022435 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs @@ -103,7 +103,8 @@ namespace System.Security.Cryptography iv: null, encrypting: false, paddingMode, - CipherMode.ECB); + CipherMode.ECB, + feedbackSizeInBits: 0); using (transform) { @@ -121,7 +122,8 @@ namespace System.Security.Cryptography iv: null, encrypting: true, paddingMode, - CipherMode.ECB); + CipherMode.ECB, + feedbackSizeInBits: 0); using (transform) { @@ -140,7 +142,8 @@ namespace System.Security.Cryptography iv: iv.ToArray(), encrypting: true, paddingMode, - CipherMode.CBC); + CipherMode.CBC, + feedbackSizeInBits: 0); using (transform) { @@ -159,7 +162,8 @@ namespace System.Security.Cryptography iv: iv.ToArray(), encrypting: false, paddingMode, - CipherMode.CBC); + CipherMode.CBC, + feedbackSizeInBits: 0); using (transform) { @@ -167,11 +171,77 @@ namespace System.Security.Cryptography } } + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: false, + paddingMode, + CipherMode.CFB, + feedbackSizeInBits); + + using (transform) + { + return transform.TransformOneShot(ciphertext, destination, out bytesWritten); + } + } + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + ValidateCFBFeedbackSize(feedbackSizeInBits); + + UniversalCryptoTransform transform = _core.CreateCryptoTransform( + iv: iv.ToArray(), + encrypting: true, + paddingMode, + CipherMode.CFB, + feedbackSizeInBits); + + using (transform) + { + return transform.TransformOneShot(plaintext, destination, out bytesWritten); + } + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); } + private void ValidateCFBFeedbackSize(int feedback) + { + if (_core.KeyInPlainText) + { + // CFB8 and CFB164 are valid for bcrypt keys. + if (feedback != 8 && feedback != 64) + { + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); + } + } + else + { + // only CFB8 is supported for ncrypt keys. + if (feedback != 8) + { + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); + } + } + } + byte[] ICngSymmetricAlgorithm.BaseKey { get { return base.Key; } set { base.Key = value; } } int ICngSymmetricAlgorithm.BaseKeySize { get { return base.KeySize; } set { base.KeySize = value; } } @@ -185,9 +255,9 @@ namespace System.Security.Cryptography return this.GetPaddingSize(mode, feedbackSizeBits); } - SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode) + SafeAlgorithmHandle ICngSymmetricAlgorithm.GetEphemeralModeHandle(CipherMode mode, int feedbackSizeInBits) { - return TripleDesBCryptModes.GetSharedHandle(mode, FeedbackSize / 8); + return TripleDesBCryptModes.GetSharedHandle(mode, feedbackSizeInBits / 8); } string ICngSymmetricAlgorithm.GetNCryptAlgorithmIdentifier() diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs index dc6d484263c..47d4f4b5dd6 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs @@ -25,12 +25,21 @@ namespace System.Security.Cryptography.Cng.Tests [InlineData(256, BlockSizeBytes + BlockSizeBytes / 2, CipherMode.CBC, PaddingMode.Zeros)] // AES192-CBC-PKCS7 at 1.5 blocks [InlineData(192, BlockSizeBytes + BlockSizeBytes / 2, CipherMode.CBC, PaddingMode.PKCS7)] + // AES128-CFB8-NoPadding at 2 blocks + [InlineData(128, 2 * BlockSizeBytes, CipherMode.CFB, PaddingMode.None, 8)] public static void VerifyPersistedKey( int keySize, int plainBytesCount, CipherMode cipherMode, - PaddingMode paddingMode) + PaddingMode paddingMode, + int feedbackSizeInBits = 0) { + // Windows 7 does not support CFB except in CFB8 mode. + if (cipherMode == CipherMode.CFB && feedbackSizeInBits != 8 && PlatformDetection.IsWindows7) + { + return; + } + SymmetricCngTestHelpers.VerifyPersistedKey( s_cngAlgorithm, keySize, @@ -38,7 +47,8 @@ namespace System.Security.Cryptography.Cng.Tests keyName => new AesCng(keyName), () => new AesCng(), cipherMode, - paddingMode); + paddingMode, + feedbackSizeInBits); } @@ -88,6 +98,16 @@ namespace System.Security.Cryptography.Cng.Tests () => new AesCng()); } + [OuterLoop("Creates/Deletes a persisted key, limit exposure to key leaking")] + [ConditionalFact(nameof(SupportsPersistedSymmetricKeys))] + public static void VerifyUnsupportedFeedbackSizeForPersistedCfb() + { + SymmetricCngTestHelpers.VerifyOneShotCfbPersistedUnsupportedFeedbackSize( + s_cngAlgorithm, + keyName => new AesCng(keyName), + notSupportedFeedbackSizeInBits: 128); + } + public static bool SupportsPersistedSymmetricKeys { get { return SymmetricCngTestHelpers.SupportsPersistedSymmetricKeys; } diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs index 9810072403f..9a3c728efb1 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs @@ -18,7 +18,8 @@ namespace System.Security.Cryptography.Cng.Tests Func persistedFunc, Func ephemeralFunc, CipherMode cipherMode, - PaddingMode paddingMode) + PaddingMode paddingMode, + int feedbackSizeInBits) { string keyName = Guid.NewGuid().ToString(); CngKeyCreationParameters creationParameters = new CngKeyCreationParameters @@ -41,7 +42,8 @@ namespace System.Security.Cryptography.Cng.Tests persistedFunc, ephemeralFunc, cipherMode, - paddingMode); + paddingMode, + feedbackSizeInBits); } finally { @@ -56,7 +58,8 @@ namespace System.Security.Cryptography.Cng.Tests Func persistedFunc, Func ephemeralFunc, CipherMode cipherMode, - PaddingMode paddingMode) + PaddingMode paddingMode, + int feedbackSizeInBits) { byte[] plainBytes = GenerateRandom(plainBytesCount); @@ -66,6 +69,11 @@ namespace System.Security.Cryptography.Cng.Tests persisted.Mode = ephemeral.Mode = cipherMode; persisted.Padding = ephemeral.Padding = paddingMode; + if (cipherMode == CipherMode.CFB) + { + persisted.FeedbackSize = ephemeral.FeedbackSize = feedbackSizeInBits; + } + ephemeral.Key = persisted.Key; ephemeral.GenerateIV(); persisted.IV = ephemeral.IV; @@ -117,6 +125,12 @@ namespace System.Security.Cryptography.Cng.Tests oneShotEphemeralEncrypted = ephemeral.EncryptCbc(plainBytes, ephemeral.IV, paddingMode); oneShotPersistedDecrypted = persisted.DecryptCbc(oneShotEphemeralEncrypted, persisted.IV, paddingMode); } + else if (cipherMode == CipherMode.CFB) + { + oneShotPersistedEncrypted = persisted.EncryptCfb(plainBytes, persisted.IV, paddingMode, feedbackSizeInBits); + oneShotEphemeralEncrypted = ephemeral.EncryptCfb(plainBytes, ephemeral.IV, paddingMode, feedbackSizeInBits); + oneShotPersistedDecrypted = persisted.DecryptCfb(oneShotEphemeralEncrypted, persisted.IV, paddingMode, feedbackSizeInBits); + } if (oneShotPersistedEncrypted is not null) { @@ -280,7 +294,8 @@ namespace System.Security.Cryptography.Cng.Tests persistedFunc, ephemeralFunc, CipherMode.CBC, - PaddingMode.PKCS7); + PaddingMode.PKCS7, + feedbackSizeInBits: 0); } finally { @@ -289,6 +304,32 @@ namespace System.Security.Cryptography.Cng.Tests } } + public static void VerifyOneShotCfbPersistedUnsupportedFeedbackSize( + CngAlgorithm algorithm, + Func persistedFunc, + int notSupportedFeedbackSizeInBits) + { + string keyName = Guid.NewGuid().ToString(); + + // We try to delete the key later which will also dispose of it, so no need + // to put this in a using. + CngKey cngKey = CngKey.Create(algorithm, keyName); + + try + { + using (SymmetricAlgorithm alg = persistedFunc(keyName)) + { + byte[] destination = new byte[alg.BlockSize / 8]; + Assert.ThrowsAny(() => + alg.EncryptCfb(Array.Empty(), destination, PaddingMode.None, notSupportedFeedbackSizeInBits)); + } + } + finally + { + cngKey.Delete(); + } + } + private static bool? s_supportsPersistedSymmetricKeys; internal static bool SupportsPersistedSymmetricKeys { diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs b/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs index d68aa596c27..4a913be0a2b 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/TripleDESCngTests.cs @@ -39,10 +39,13 @@ namespace System.Security.Cryptography.Cng.Tests [InlineData(BlockSizeBytes + BlockSizeBytes / 2, CipherMode.CBC, PaddingMode.Zeros)] // 3DES192-CBC-PKCS7 at 1.5 blocks [InlineData(BlockSizeBytes + BlockSizeBytes / 2, CipherMode.CBC, PaddingMode.PKCS7)] + // 3DES192-CFB8-NoPadding at 2 blocks + [InlineData(2 * BlockSizeBytes, CipherMode.CFB, PaddingMode.None, 8)] public static void VerifyPersistedKey( int plainBytesCount, CipherMode cipherMode, - PaddingMode paddingMode) + PaddingMode paddingMode, + int feedbackSizeInBits = 0) { SymmetricCngTestHelpers.VerifyPersistedKey( s_cngAlgorithm, @@ -51,7 +54,8 @@ namespace System.Security.Cryptography.Cng.Tests keyName => new TripleDESCng(keyName), () => new TripleDESCng(), cipherMode, - paddingMode); + paddingMode, + feedbackSizeInBits); } [OuterLoop(/* Creates/Deletes a persisted key, limit exposure to key leaking */)] @@ -100,6 +104,16 @@ namespace System.Security.Cryptography.Cng.Tests () => new TripleDESCng()); } + [OuterLoop("Creates/Deletes a persisted key, limit exposure to key leaking")] + [ConditionalFact(nameof(SupportsPersistedSymmetricKeys))] + public static void VerifyUnsupportedFeedbackSizeForPersistedCfb() + { + SymmetricCngTestHelpers.VerifyOneShotCfbPersistedUnsupportedFeedbackSize( + s_cngAlgorithm, + keyName => new TripleDESCng(keyName), + notSupportedFeedbackSizeInBits: 64); + } + public static bool SupportsPersistedSymmetricKeys { get { return SymmetricCngTestHelpers.SupportsPersistedSymmetricKeys; } diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/DESCryptoServiceProviderProvider.cs b/src/libraries/System.Security.Cryptography.Csp/tests/DESCryptoServiceProviderProvider.cs index 7181d95edc5..7d049de7e66 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/DESCryptoServiceProviderProvider.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/DESCryptoServiceProviderProvider.cs @@ -5,10 +5,8 @@ namespace System.Security.Cryptography.Encryption.Des.Tests { public class DESCryptoServiceProviderProvider : IDESProvider { - public DES Create() - { - return new DESCryptoServiceProvider(); - } + public DES Create() => new DESCryptoServiceProvider(); + public bool OneShotSupported => false; } public partial class DESFactory diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs index b14a21f2daa..bf8d40dcf4e 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs @@ -82,6 +82,8 @@ namespace System.Security.Cryptography.Csp.Tests "TryDecryptEcbCore", "TryEncryptCbcCore", "TryDecryptCbcCore", + "TryEncryptCfbCore", + "TryDecryptCfbCore", }; IEnumerable baseMethods = shimType. diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 5eee91bb97a..d04196601cf 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -256,6 +256,9 @@ namespace System.Security.Cryptography public byte[] DecryptCbc(byte[] ciphertext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public byte[] DecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public int DecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public byte[] DecryptCfb(byte[] ciphertext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + public byte[] DecryptCfb(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + public int DecryptCfb(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } public byte[] DecryptEcb(byte[] ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public byte[] DecryptEcb(System.ReadOnlySpan ciphertext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public int DecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } @@ -264,6 +267,9 @@ namespace System.Security.Cryptography public byte[] EncryptCbc(byte[] plaintext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public byte[] EncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } public int EncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } + public byte[] EncryptCfb(byte[] plaintext, byte[] iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + public byte[] EncryptCfb(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + public int EncryptCfb(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } public byte[] EncryptEcb(byte[] plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public byte[] EncryptEcb(System.ReadOnlySpan plaintext, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public int EncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } @@ -274,10 +280,14 @@ namespace System.Security.Cryptography public int GetCiphertextLengthEcb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode) { throw null; } public bool TryDecryptCbc(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } protected virtual bool TryDecryptCbcCore(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + public bool TryDecryptCfb(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + protected virtual bool TryDecryptCfbCore(System.ReadOnlySpan ciphertext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, int feedbackSizeInBits, out int bytesWritten) { throw null; } public bool TryDecryptEcb(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } protected virtual bool TryDecryptEcbCore(System.ReadOnlySpan ciphertext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool TryEncryptCbc(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; } protected virtual bool TryEncryptCbcCore(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } + public bool TryEncryptCfb(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, out int bytesWritten, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; } + protected virtual bool TryEncryptCfbCore(System.ReadOnlySpan plaintext, System.ReadOnlySpan iv, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, int feedbackSizeInBits, out int bytesWritten) { throw null; } public bool TryEncryptEcb(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } protected virtual bool TryEncryptEcbCore(System.ReadOnlySpan plaintext, System.Span destination, System.Security.Cryptography.PaddingMode paddingMode, out int bytesWritten) { throw null; } public bool ValidKeySize(int bitLength) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs index f5485577c07..5e5cfa33a07 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/SymmetricAlgorithm.cs @@ -980,6 +980,481 @@ namespace System.Security.Cryptography return TryEncryptCbcCore(plaintext, iv, destination, paddingMode, out bytesWritten); } + /// + /// Decrypts data using CFB mode with the specified padding mode and + /// feedback size. + /// + /// The data to decrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The decrypted plaintext data. + /// + /// or is . + /// + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptCfb(byte[] ciphertext, byte[] iv, PaddingMode paddingMode = PaddingMode.None, int feedbackSizeInBits = 8) + { + if (ciphertext is null) + throw new ArgumentNullException(nameof(ciphertext)); + if (iv is null) + throw new ArgumentNullException(nameof(iv)); + + return DecryptCfb( + new ReadOnlySpan(ciphertext), + new ReadOnlySpan(iv), + paddingMode, + feedbackSizeInBits); + } + + /// + /// Decrypts data using CFB mode with the specified padding mode and + /// feedback size. + /// + /// The data to decrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The decrypted plaintext data. + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public byte[] DecryptCfb( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + // The default is CFB8 with no padding, so allocate a buffer + // that is not from the pool since we can return this directly if + // padding does not need to be removed. + byte[] decryptBuffer = GC.AllocateUninitializedArray(ciphertext.Length); + + if (!TryDecryptCfbCore(ciphertext, iv, decryptBuffer, paddingMode, feedbackSizeInBits, out int written) + || (uint)written > decryptBuffer.Length) + { + // This means decrypting the ciphertext grew in to a larger plaintext or overflowed. + // A user-derived class could do this, but it is not expected in any of the + // implementations that we ship. + throw new CryptographicException(SR.Argument_DestinationTooShort); + } + + // Array.Resize will no-op if the array does not need to be resized. + Array.Resize(ref decryptBuffer, written); + return decryptBuffer; + } + + /// + /// Decrypts data into the specified buffer, using CFB mode with the specified padding mode and + /// feedback size. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The total number of bytes written to . + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// -or- + /// + /// + /// The buffer in is too small to hold the plaintext data. + /// + /// + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// -or- + /// + /// + /// is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public int DecryptCfb( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + if (!TryDecryptCfbCore(ciphertext, iv, destination, paddingMode, feedbackSizeInBits, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to decrypt data into the specified buffer, using CFB mode + /// with the specified padding mode and feedback size. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// When this method returns, the total number of bytes written to . + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The ciphertext could not be decrypted successfully. + /// + /// + /// -or- + /// + /// + /// is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public bool TryDecryptCfb( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + out int bytesWritten, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + return TryDecryptCfbCore(ciphertext, iv, destination, paddingMode, feedbackSizeInBits, out bytesWritten); + } + + /// + /// Encrypts data using CFB mode with the specified padding mode and feedback size. + /// + /// The data to encrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The encrypted ciphertext data. + /// + /// or is . + /// + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptCfb( + byte[] plaintext, + byte[] iv, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + return EncryptCfb( + new ReadOnlySpan(plaintext), + new ReadOnlySpan(iv), + paddingMode, + feedbackSizeInBits); + } + + /// + /// Encrypts data using CFB mode with the specified padding mode and feedback size. + /// + /// The data to encrypt. + /// The initialization vector. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The encrypted ciphertext data. + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public byte[] EncryptCfb( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + int ciphertextLength = GetCiphertextLengthCfb(plaintext.Length, paddingMode, feedbackSizeInBits); + + // We expect most if not all uses to encrypt to exactly the ciphertextLength + byte[] buffer = GC.AllocateUninitializedArray(ciphertextLength); + + if (!TryEncryptCfbCore(plaintext, iv, buffer, paddingMode, feedbackSizeInBits, out int written) || + written != ciphertextLength) + { + // This means a user-derived implementation added more padding than we expected or + // did something non-standard (encrypt to a partial block). This can't happen for + // multiple padding blocks since the buffer would have been too small in the first + // place. It doesn't make sense to try and support partial block encryption, likely + // something went very wrong. So throw. + throw new CryptographicException(SR.Format(SR.Cryptography_EncryptedIncorrectLength, nameof(TryEncryptCfbCore))); + } + + return buffer; + } + + /// + /// Encrypts data into the specified buffer, using CFB mode with the specified padding mode + /// and feedback size. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// The total number of bytes written to . + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public int EncryptCfb( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + if (!TryEncryptCfbCore(plaintext, iv, destination, paddingMode, feedbackSizeInBits, out int written)) + { + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + } + + return written; + } + + /// + /// Attempts to encrypt data into the specified buffer, using CFB mode with the specified padding mode + /// and feedback size. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// When this method returns, the total number of bytes written to . + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// + /// is not a valid padding mode. + /// + /// + /// -or- + /// + /// + /// is not positive or represent a whole number of bytes. + /// + /// + /// + /// is the incorrect length. Callers are expected to pass an initialization vector + /// that is exactly in length, converted to bytes (BlockSize / 8). + /// + /// + /// + /// The plaintext could not be encrypted successfully. + /// + /// + /// -or- + /// + /// + /// The feedback size is not valid for the algorithm. + /// + /// + /// + /// This method's behavior is defined by . + /// + public bool TryEncryptCfb( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + out int bytesWritten, + PaddingMode paddingMode = PaddingMode.None, + int feedbackSizeInBits = 8) + { + CheckPaddingMode(paddingMode); + CheckInitializationVectorSize(iv); + CheckFeedbackSize(feedbackSizeInBits); + + return TryEncryptCfbCore(plaintext, iv, destination, paddingMode, feedbackSizeInBits, out bytesWritten); + } + /// /// When overridden in a derived class, attempts to encrypt data into the specified /// buffer, using ECB mode with the specified padding mode. @@ -1090,6 +1565,68 @@ namespace System.Security.Cryptography throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + /// + /// When overridden in a derived class, attempts to decrypt data + /// into the specified buffer, using CFB mode with the specified padding mode + /// and feedback size. + /// + /// The data to decrypt. + /// The initialization vector. + /// The buffer to receive the plaintext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the decrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + protected virtual bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + + /// + /// When overridden in a derived class, attempts to encrypt data into the specified + /// buffer, using CFB mode with the specified padding mode and feedback size. + /// + /// The data to encrypt. + /// The initialization vector. + /// The buffer to receive the ciphertext data. + /// The padding mode used to produce the ciphertext and remove during decryption. + /// The feedback size, specified in bits. + /// When this method returns, the total number of bytes written to . + /// if was large enough to receive the encrypted data; otherwise, . + /// + /// A derived class has not provided an implementation. + /// + /// + /// Derived classes must override this and provide an implementation. + /// + /// Implementations of this method must write precisely + /// GetCiphertextLengthCfb(plaintext.Length, paddingMode, feedbackSizeInBits) + /// bytes to and report that via . + /// + /// + protected virtual bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + throw new NotSupportedException(SR.NotSupported_SubclassOverride); + } + private static void CheckPaddingMode(PaddingMode paddingMode) { if (paddingMode < PaddingMode.None || paddingMode > PaddingMode.ISO10126) @@ -1102,6 +1639,14 @@ namespace System.Security.Cryptography throw new ArgumentException(SR.Cryptography_InvalidIVSize, nameof(iv)); } + private void CheckFeedbackSize(int feedbackSizeInBits) + { + if (feedbackSizeInBits < 8 || (feedbackSizeInBits & 0b111) != 0 || feedbackSizeInBits > BlockSize) + { + throw new ArgumentException(SR.Cryptography_InvalidFeedbackSize, nameof(feedbackSizeInBits)); + } + } + protected CipherMode ModeValue; protected PaddingMode PaddingValue; protected byte[]? KeyValue; diff --git a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs index 380cd19b099..c03909efd39 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/tests/SymmetricAlgorithmTests.cs @@ -495,6 +495,322 @@ namespace System.Security.Cryptography.Primitives.Tests alg.TryEncryptCbc(Array.Empty(), badIv, destination, out _, PaddingMode.None)); } + [Fact] + public static void EncryptCfb_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.EncryptCfb(Array.Empty(), new byte[alg.BlockSize / 8])); + } + + [Fact] + public static void DecryptCfb_NotSupportedInDerived() + { + AnySizeAlgorithm alg = new AnySizeAlgorithm { BlockSize = 128 }; + + Assert.Throws(() => + alg.DecryptCfb(Array.Empty(), new byte[alg.BlockSize / 8])); + } + + [Fact] + public static void EncryptCfb_EncryptProducesIncorrectlyPaddedValue() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCfbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCfb(Array.Empty(), new byte[alg.BlockSize / 8], PaddingMode.None)); + } + + [Fact] + public static void DecryptCfb_DecryptBytesWrittenLies() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = destination.Length + 1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCfbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCfb(new byte[128 / 8], new byte[128 / 8], feedbackSizeInBits: 128)); + } + + [Fact] + public static void EncryptCfb_EncryptCoreFails() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCfbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCfb(Array.Empty(), new byte[128 / 8], feedbackSizeInBits: 128)); + } + + [Fact] + public static void EncryptCfb_EncryptCoreOverflowWritten() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCfbCoreImpl = EncryptImpl, + }; + + Assert.Throws(() => + alg.EncryptCfb(Array.Empty(), new byte[128 / 8], feedbackSizeInBits: 128)); + } + + [Fact] + public static void DecryptCfb_DecryptCoreFails() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCfbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCfb(Array.Empty(), new byte[128 / 8], feedbackSizeInBits: 128)); + } + + [Fact] + public static void DecryptCfb_DecryptCoreOverflowWritten() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + bytesWritten = -1; + return true; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCfbCoreImpl = DecryptImpl, + }; + + Assert.Throws(() => + alg.DecryptCfb(Array.Empty(), new byte[128 / 8], feedbackSizeInBits: 8)); + } + + [Fact] + public static void DecryptCfb_BadInitializationVectorLength() + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + Assert.True(false, "Initialization vector was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCfbCoreImpl = DecryptImpl, + }; + + byte[] badIv = new byte[alg.BlockSize / 8 + 1]; + byte[] destination = new byte[128 / 8]; + + AssertExtensions.Throws("iv", () => + alg.DecryptCfb(Array.Empty(), badIv, feedbackSizeInBits: 128)); + + AssertExtensions.Throws("iv", () => + alg.DecryptCfb(Array.Empty(), badIv, destination, feedbackSizeInBits: 128)); + + AssertExtensions.Throws("iv", () => + alg.TryDecryptCfb(Array.Empty(), badIv, destination, out _, feedbackSizeInBits: 128)); + } + + [Fact] + public static void EncryptCfb_BadInitializationVectorLength() + { + static bool EncryptImpl( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + Assert.True(false, "Initialization vector was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCfbCoreImpl = EncryptImpl, + }; + + byte[] badIv = new byte[alg.BlockSize / 8 + 1]; + byte[] destination = new byte[128 / 8]; + + AssertExtensions.Throws("iv", () => + alg.EncryptCfb(Array.Empty(), badIv, feedbackSizeInBits: 128)); + + AssertExtensions.Throws("iv", () => + alg.EncryptCfb(Array.Empty(), badIv, destination, feedbackSizeInBits: 128)); + + AssertExtensions.Throws("iv", () => + alg.TryEncryptCfb(Array.Empty(), badIv, destination, out _, feedbackSizeInBits: 128)); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(19)] + [InlineData(256)] + public static void DecryptCfb_BadFeedbackSizes(int feedbackSize) + { + static bool DecryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + Assert.True(false, "Feedback size was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryDecryptCfbCoreImpl = DecryptImpl, + }; + + byte[] iv = new byte[alg.BlockSize / 8]; + byte[] destination = Array.Empty(); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.DecryptCfb(Array.Empty(), iv, feedbackSizeInBits: feedbackSize)); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.DecryptCfb(Array.Empty(), iv, destination, feedbackSizeInBits: feedbackSize)); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.TryDecryptCfb(Array.Empty(), iv, destination, out _, feedbackSizeInBits: feedbackSize)); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(19)] + [InlineData(256)] + public static void EncryptCfb_BadFeedbackSizes(int feedbackSize) + { + static bool EncryptImpl( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) + { + Assert.True(false, "Feedback size was not validated, core should not have been called."); + bytesWritten = 0; + return false; + } + + OneShotSymmetricAlgorithm alg = new OneShotSymmetricAlgorithm + { + BlockSize = 128, + TryEncryptCfbCoreImpl = EncryptImpl, + }; + + byte[] iv = new byte[alg.BlockSize / 8]; + byte[] destination = Array.Empty(); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.DecryptCfb(Array.Empty(), iv, feedbackSizeInBits: feedbackSize)); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.DecryptCfb(Array.Empty(), iv, destination, feedbackSizeInBits: feedbackSize)); + + AssertExtensions.Throws("feedbackSizeInBits", () => + alg.TryDecryptCfb(Array.Empty(), iv, destination, out _, feedbackSizeInBits: feedbackSize)); + } + public static IEnumerable CiphertextLengthTheories { get @@ -622,10 +938,28 @@ namespace System.Security.Cryptography.Primitives.Tests PaddingMode paddingMode, out int bytesWritten); + public delegate bool TryEncryptCfbCoreFunc( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten); + + public delegate bool TryDecryptCfbCoreFunc( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten); + public TryEncryptEcbCoreFunc TryEncryptEcbCoreImpl { get; set; } public TryDecryptEcbCoreFunc TryDecryptEcbCoreImpl { get; set; } public TryEncryptCbcCoreFunc TryEncryptCbcCoreImpl { get; set; } public TryDecryptCbcCoreFunc TryDecryptCbcCoreImpl { get; set; } + public TryEncryptCfbCoreFunc TryEncryptCfbCoreImpl { get; set; } + public TryDecryptCfbCoreFunc TryDecryptCfbCoreImpl { get; set; } protected override bool TryEncryptEcbCore( ReadOnlySpan plaintext, @@ -652,6 +986,22 @@ namespace System.Security.Cryptography.Primitives.Tests Span destination, PaddingMode paddingMode, out int bytesWritten) => TryDecryptCbcCoreImpl(ciphertext, iv, destination, paddingMode, out bytesWritten); + + protected override bool TryEncryptCfbCore( + ReadOnlySpan plaintext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) => TryEncryptCfbCoreImpl(plaintext, iv, destination, paddingMode, feedbackSizeInBits, out bytesWritten); + + protected override bool TryDecryptCfbCore( + ReadOnlySpan ciphertext, + ReadOnlySpan iv, + Span destination, + PaddingMode paddingMode, + int feedbackSizeInBits, + out int bytesWritten) => TryDecryptCfbCoreImpl(ciphertext, iv, destination, paddingMode, feedbackSizeInBits, out bytesWritten); } } } From 39000f186175b2037cdadea3848c6c944524f57a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 20:13:22 +0200 Subject: [PATCH 500/926] [main] Update dependencies from 7 repositories (#55565) * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210712.2 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21361.3 -> To Version 1.0.0-prerelease.21362.2 * Update dependencies from https://github.com/dotnet/arcade build 20210709.3 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21357.3 -> To Version 6.0.0-beta.21359.3 * Update dependencies from https://github.com/dotnet/icu build 20210712.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 6.0.0-preview.7.21328.1 -> To Version 6.0.0-preview.7.21362.1 * Update dependencies from https://github.com/dotnet/llvm-project build 20210712.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-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 From Version 11.1.0-alpha.1.21357.1 -> To Version 11.1.0-alpha.1.21362.1 * Update dependencies from https://github.com/mono/linker build 20210712.3 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21358.3 -> To Version 6.0.100-preview.6.21362.3 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210712.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21361.1 -> To Version 1.0.1-alpha.0.21362.1 * Update dependencies from https://github.com/dotnet/emsdk build 20210713.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21362.2 -> To Version 6.0.0-preview.7.21363.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 132 ++++++++++++++++++++-------------------- eng/Versions.props | 58 +++++++++--------- eng/common/tools.ps1 | 4 +- eng/common/tools.sh | 4 +- global.json | 8 +-- 5 files changed, 103 insertions(+), 103 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 73444fdb70f..58a2c18606c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,82 +1,82 @@ - + https://github.com/dotnet/icu - e7626ad8c04b150de635f920b5e8dede0aafaf73 + 1782cd21854f8cb2b60355f4773714a8e0130696 https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - 7e218d66bf9ec3ca4fc70c0b63e9a162f2e33451 + 8a6313ffbdbef76fd01e32e37c802b57945531d3 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 https://github.com/microsoft/vstest @@ -118,37 +118,37 @@ https://github.com/dotnet/runtime-assets 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b - + https://github.com/dotnet/llvm-project - a05e5e9fb80f9bb6fd9100775dfe55be6f84729d + 97d42b6f43e31449758c353cfbaf83322941611b https://github.com/dotnet/runtime @@ -182,9 +182,9 @@ https://github.com/dotnet/runtime 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/mono/linker - b9501922637806f4135df09a9922d5540e203858 + 664e78edc72dd0a48e6f55e352051b6ba61bba9a https://github.com/dotnet/xharness @@ -194,29 +194,29 @@ https://github.com/dotnet/xharness c6d444eaf7e95339589ceef371cbef0a90a4add5 - + https://github.com/dotnet/arcade - 286d98094b830b8dad769542b2669cb1b75f7097 + 55262f114b0c1b82f0b081bca0d919b657ba24c5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - ae45cbdfa6d15fce7e3cf089462f0d2b55727273 + a83e4392b6d3f377239726eedc19b6dc85b85496 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - ae45cbdfa6d15fce7e3cf089462f0d2b55727273 + a83e4392b6d3f377239726eedc19b6dc85b85496 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - ae45cbdfa6d15fce7e3cf089462f0d2b55727273 + a83e4392b6d3f377239726eedc19b6dc85b85496 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - ae45cbdfa6d15fce7e3cf089462f0d2b55727273 + a83e4392b6d3f377239726eedc19b6dc85b85496 - + https://github.com/dotnet/hotreload-utils - 640e908a67b5bc63fa615d31c7877e62c2b15062 + 33219d2b3fbc957e05f8e52a33363cf9b858bb08 https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index 4f3f2a4c21a..1e7be3b4cb9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -52,19 +52,19 @@ 3.10.0 6.0.0-rc1.21356.1 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 2.5.1-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 - 6.0.0-beta.21357.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 2.5.1-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 + 6.0.0-beta.21359.3 6.0.0-preview.1.102 @@ -124,10 +124,10 @@ 6.0.0-beta.21358.1 6.0.0-beta.21358.1 - 1.0.0-prerelease.21361.3 - 1.0.0-prerelease.21361.3 - 1.0.0-prerelease.21361.3 - 1.0.0-prerelease.21361.3 + 1.0.0-prerelease.21362.2 + 1.0.0-prerelease.21362.2 + 1.0.0-prerelease.21362.2 + 1.0.0-prerelease.21362.2 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -153,7 +153,7 @@ 16.9.0-preview-20201201-01 1.0.0-prerelease.21357.4 1.0.0-prerelease.21357.4 - 1.0.1-alpha.0.21361.1 + 1.0.1-alpha.0.21362.1 2.4.1 2.4.2 1.3.0 @@ -164,23 +164,23 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21358.3 + 6.0.100-preview.6.21362.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21328.1 + 6.0.0-preview.7.21362.1 6.0.0-preview.7.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 - 11.1.0-alpha.1.21357.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21362.1 - 6.0.0-preview.7.21358.1 + 6.0.0-preview.7.21363.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 4b255203249..2df0909937d 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -42,7 +42,7 @@ [bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true } # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dot.net/v1/dotnet-install.ps1 +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1 [string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' } # True to use global NuGet cache instead of restoring packages to repository-local directory. @@ -223,7 +223,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { if (!(Test-Path $installScript)) { Create-Directory $dotnetRoot $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit - $uri = "https://dot.net/$dotnetInstallScriptVersion/dotnet-install.ps1" + $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" Retry({ Write-Host "GET $uri" diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 05ca99c6b28..828119be411 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -54,7 +54,7 @@ warn_as_error=${warn_as_error:-true} use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dot.net/v1/dotnet-install.sh +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'} # True to use global NuGet cache instead of restoring packages to repository-local directory. @@ -262,7 +262,7 @@ function with_retries { function GetDotNetInstallScript { local root=$1 local install_script="$root/dotnet-install.sh" - local install_script_url="https://dot.net/$dotnetInstallScriptVersion/dotnet-install.sh" + local install_script_url="https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" if [[ ! -a "$install_script" ]]; then mkdir -p "$root" diff --git a/global.json b/global.json index e9516bcfa8f..e25c47c09f7 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21359.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21357.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21357.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21357.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21359.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21359.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21359.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10" From dc2d0cc61f58fde5c04b5a29ba476ccd3779ab74 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Tue, 13 Jul 2021 20:20:10 +0200 Subject: [PATCH 501/926] [wasm][http] Improve compatibility of abort and cancelation of BrowserHttpHandler (#55084) * fixed handling of cancelation and abots exceptions to match unit test expectations added [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] for redirect outerloop tests * more * code review feedback --- .../HttpClientHandlerTest.RemoteServer.cs | 10 +++ .../BrowserHttpHandler/BrowserHttpHandler.cs | 64 +++++++++++++------ .../tests/FunctionalTests/HttpClientTest.cs | 7 -- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs index 36d732d79ff..316a58cb33f 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs @@ -928,6 +928,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(Configuration.Http.RemoteServer remoteServer, int statusCode) { if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) @@ -955,6 +956,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) { if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) @@ -982,6 +984,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1003,6 +1006,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1025,6 +1029,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Theory, MemberData(nameof(RemoteServersMemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet(Configuration.Http.RemoteServer remoteServer) { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1050,6 +1055,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(3, 2)] [InlineData(3, 3)] [InlineData(3, 4)] + [SkipOnPlatform(TestPlatforms.Browser, "MaxConnectionsPerServer not supported on Browser")] public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) { if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1703OrGreater) @@ -1095,6 +1101,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Theory, MemberData(nameof(RemoteServersMemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55083", TestPlatforms.Browser)] public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation(Configuration.Http.RemoteServer remoteServer) { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1118,6 +1125,7 @@ namespace System.Net.Http.Functional.Tests [Theory, MemberData(nameof(RemoteServersMemberData))] [OuterLoop("Uses external servers")] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized(Configuration.Http.RemoteServer remoteServer) { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1137,6 +1145,7 @@ namespace System.Net.Http.Functional.Tests [Theory, MemberData(nameof(RemoteServersMemberData))] [OuterLoop("Uses external servers")] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public async Task HttpClientHandler_CredentialIsNotCredentialCacheAfterRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer) { HttpClientHandler handler = CreateHttpClientHandler(); @@ -1163,6 +1172,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers")] [Theory, MemberData(nameof(RemoteServersAndRedirectStatusCodes))] + [SkipOnPlatform(TestPlatforms.Browser, "Credentials is not supported on Browser")] public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(Configuration.Http.RemoteServer remoteServer, int statusCode) { if (statusCode == 308 && (IsWinHttpHandler && PlatformDetection.WindowsVersion < 10)) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index b4450bb4256..8d022091ea3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -139,7 +139,7 @@ namespace System.Net.Http protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) { - throw new PlatformNotSupportedException (); + throw new PlatformNotSupportedException(); } protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -323,10 +323,27 @@ namespace System.Net.Http return httpResponse; } - catch (JSException jsExc) + catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested) { - throw new System.Net.Http.HttpRequestException(jsExc.Message); + throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken); } + catch (JSException jse) + { + throw TranslateJSException(jse, cancellationToken); + } + } + + private static Exception TranslateJSException(JSException jse, CancellationToken cancellationToken) + { + if (jse.Message.StartsWith("AbortError", StringComparison.Ordinal)) + { + return CancellationHelper.CreateOperationCanceledException(jse, CancellationToken.None); + } + if (cancellationToken.IsCancellationRequested) + { + return CancellationHelper.CreateOperationCanceledException(jse, cancellationToken); + } + return new HttpRequestException(jse.Message, jse); } private sealed class WasmFetchResponse : IDisposable @@ -366,7 +383,6 @@ namespace System.Net.Http _isDisposed = true; - _abortCts.Cancel(); _abortCts.Dispose(); _abortRegistration.Dispose(); @@ -385,28 +401,34 @@ namespace System.Net.Http _status = status ?? throw new ArgumentNullException(nameof(status)); } - private async Task GetResponseData() + private async Task GetResponseData(CancellationToken cancellationToken) { if (_data != null) { return _data; } - - using (System.Runtime.InteropServices.JavaScript.ArrayBuffer dataBuffer = (System.Runtime.InteropServices.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true)) + try { - using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) + using (System.Runtime.InteropServices.JavaScript.ArrayBuffer dataBuffer = (System.Runtime.InteropServices.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true)) { - _data = dataBinView.ToArray(); - _status.Dispose(); + using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) + { + _data = dataBinView.ToArray(); + _status.Dispose(); + } } } + catch (JSException jse) + { + throw TranslateJSException(jse, cancellationToken); + } return _data; } protected override async Task CreateContentReadStreamAsync() { - byte[] data = await GetResponseData().ConfigureAwait(continueOnCapturedContext: true); + byte[] data = await GetResponseData(CancellationToken.None).ConfigureAwait(continueOnCapturedContext: true); return new MemoryStream(data, writable: false); } @@ -414,7 +436,7 @@ namespace System.Net.Http SerializeToStreamAsync(stream, context, CancellationToken.None); protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) { - byte[] data = await GetResponseData().ConfigureAwait(continueOnCapturedContext: true); + byte[] data = await GetResponseData(cancellationToken).ConfigureAwait(continueOnCapturedContext: true); await stream.WriteAsync(data, cancellationToken).ConfigureAwait(continueOnCapturedContext: true); } protected internal override bool TryComputeLength(out long length) @@ -482,10 +504,13 @@ namespace System.Net.Http _reader = (JSObject)body.Invoke("getReader"); } } - catch (JSException) + catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested) { - cancellationToken.ThrowIfCancellationRequested(); - throw; + throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken); + } + catch (JSException jse) + { + throw TranslateJSException(jse, cancellationToken); } } @@ -515,10 +540,13 @@ namespace System.Net.Http _bufferedBytes = binValue.ToArray(); } } - catch (JSException) + catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested) { - cancellationToken.ThrowIfCancellationRequested(); - throw; + throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken); + } + catch (JSException jse) + { + throw TranslateJSException(jse, cancellationToken); } return ReadBuffered(); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 61727faa433..26a01e5a133 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -385,7 +385,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -410,7 +409,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStringAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -445,7 +443,6 @@ namespace System.Net.Http.Functional.Tests [InlineData(1, 0)] [InlineData(1, 1)] [InlineData(1, 2)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetAsync_ContentCanBeCanceled(int getMode, int cancelMode) { // cancelMode: @@ -555,7 +552,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -580,7 +576,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetByteArrayAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); @@ -631,7 +626,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled_AlreadyCanceledCts() { var onClientFinished = new SemaphoreSlim(0, 1); @@ -656,7 +650,6 @@ namespace System.Net.Http.Functional.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54270", TestPlatforms.Browser)] public async Task GetStreamAsync_CanBeCanceled() { var cts = new CancellationTokenSource(); From ffed50cdf9e417f8d4e7148580c7812601bafb40 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 13 Jul 2021 14:37:00 -0400 Subject: [PATCH 502/926] Add leeway to revocation timeout tests. --- .../tests/RevocationTests/TimeoutTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs index dc860faf1c3..e782a0c3e50 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs @@ -59,7 +59,11 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests // OCSP/CRL to root to get intermediate statuses. It should take at least 2x the delay // plus other non-network time, so we can at least ensure it took as long as // the delay for each fetch. - Assert.True(watch.Elapsed >= delay * 2, $"watch.Elapsed: {watch.Elapsed}"); + // We expect the chain to build in at least 16 seconds (2 * delay) since each fetch + // should take `delay` number of seconds, and there are two fetchs that need to be + // performed. We allow a small amount of leeway to account for differences between + // how long the the delay is performed and the stopwatch. + Assert.True(watch.Elapsed >= delay * 2 - TimeSpan.FromSeconds(1), $"watch.Elapsed: {watch.Elapsed}"); } }); } From 4d52a53df1ef9f07f5f0122f477af13888bdd60c Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Tue, 13 Jul 2021 21:10:16 +0200 Subject: [PATCH 503/926] Disable Http2_MultipleConnectionsEnabled_InfiniteRequestsCompletelyBlockOneConnection_RemaningRequestsAreHandledByNewConnection (#55593) --- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index d8b7ec4e712..b326c8bbb02 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2119,6 +2119,7 @@ namespace System.Net.Http.Functional.Tests } [ConditionalFact(nameof(SupportsAlpn))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/45204")] public async Task Http2_MultipleConnectionsEnabled_InfiniteRequestsCompletelyBlockOneConnection_RemaningRequestsAreHandledByNewConnection() { const int MaxConcurrentStreams = 2; From ca85119b00e8f9f88934b0130d0cee0ef87c8870 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 13 Jul 2021 15:39:29 -0400 Subject: [PATCH 504/926] Disable some PosixSignalRegistration tests on mobile targets (#55569) xunit doesn't like the fact that some of these MemberDatas were yielding 0 entries on these platforms. --- .../PosixSignalRegistrationTests.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs index 599d1adc6c6..4f800d51dca 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.cs @@ -25,21 +25,21 @@ namespace System.Tests Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] [MemberData(nameof(UninstallableSignals))] public void Create_UninstallableSignal_Throws(PosixSignal signal) { Assert.Throws(() => PosixSignalRegistration.Create(signal, ctx => { })); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] [MemberData(nameof(SupportedSignals))] public void Create_ValidSignal_Success(PosixSignal signal) { PosixSignalRegistration.Create(signal, ctx => { }).Dispose(); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] [MemberData(nameof(SupportedSignals))] public void Dispose_Idempotent(PosixSignal signal) { @@ -48,26 +48,21 @@ namespace System.Tests registration.Dispose(); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))] public void Create_RegisterForMultipleSignalsMultipletimes_Success() { var registrations = new List(); - for (int i = 0; i < 3; i++) + for (int iter = 0; iter < 3; iter++) { - foreach (object[] signal in SupportedSignals()) + for (int i = 0; i < 2; i++) { - registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + foreach (object[] signal in SupportedSignals()) + { + registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); + } } - foreach (object[] signal in SupportedSignals()) - { - registrations.Add(PosixSignalRegistration.Create((PosixSignal)signal[0], _ => { })); - } - - foreach (PosixSignalRegistration registration in registrations) - { - registration.Dispose(); - } + registrations.ForEach(r => r.Dispose()); } } } From 4f9deeb818a89b2835533da7ee4c253d4475d316 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 13 Jul 2021 15:39:47 -0400 Subject: [PATCH 505/926] Simplify PosixSignalRegistration implementation on Unix (#55552) * Simplify PosixSignalRegistration implementation on Unix Bring it onto the same scheme as the Windows implementation. * Address PR feedback * Delete more dead code * Update PosixSignalRegistration.Unix.cs --- .../Unix/System.Native/Interop.PosixSignal.cs | 2 +- .../Native/Unix/System.Native/pal_signal.c | 10 +- .../Native/Unix/System.Native/pal_signal.h | 2 +- ...SignalRegistration.PlatformNotSupported.cs | 11 +- .../PosixSignalRegistration.Unix.cs | 283 +++++------------- .../PosixSignalRegistration.Windows.cs | 72 ++--- .../PosixSignalRegistration.cs | 50 +++- 7 files changed, 157 insertions(+), 273 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs index b42b5138ec6..65e3ca35f48 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs @@ -18,7 +18,7 @@ internal static partial class Interop internal static extern void DisablePosixSignalHandling(int signal); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_HandleNonCanceledPosixSignal")] - internal static extern bool HandleNonCanceledPosixSignal(int signal, int handlersDisposed); + internal static extern void HandleNonCanceledPosixSignal(int signal); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")] [SuppressGCTransition] diff --git a/src/libraries/Native/Unix/System.Native/pal_signal.c b/src/libraries/Native/Unix/System.Native/pal_signal.c index 4c4017f6547..ee6ccd833c0 100644 --- a/src/libraries/Native/Unix/System.Native/pal_signal.c +++ b/src/libraries/Native/Unix/System.Native/pal_signal.c @@ -233,7 +233,7 @@ static void SignalHandler(int sig, siginfo_t* siginfo, void* context) } } -int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t handlersDisposed) +void SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode) { switch (signalCode) { @@ -276,11 +276,6 @@ int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t ha // Original handler doesn't do anything. break; } - if (handlersDisposed && g_hasPosixSignalRegistrations[signalCode - 1]) - { - // New handlers got registered. - return 0; - } // Restore and invoke the original handler. pthread_mutex_lock(&lock); { @@ -293,7 +288,6 @@ int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t ha kill(getpid(), signalCode); break; } - return 1; } // Entrypoint for the thread that handles signals where our handling @@ -385,7 +379,7 @@ static void* SignalHandlerLoop(void* arg) if (!usePosixSignalHandler) { - SystemNative_HandleNonCanceledPosixSignal(signalCode, 0); + SystemNative_HandleNonCanceledPosixSignal(signalCode); } } } diff --git a/src/libraries/Native/Unix/System.Native/pal_signal.h b/src/libraries/Native/Unix/System.Native/pal_signal.h index 138f37835a8..146fdabbfce 100644 --- a/src/libraries/Native/Unix/System.Native/pal_signal.h +++ b/src/libraries/Native/Unix/System.Native/pal_signal.h @@ -75,7 +75,7 @@ PALEXPORT void SystemNative_DisablePosixSignalHandling(int signalCode); /** * Performs the default runtime action for a non-canceled PosixSignal. */ -PALEXPORT int32_t SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode, int32_t handlersDisposed); +PALEXPORT void SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode); typedef void (*ConsoleSigTtouHandler)(void); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs index 3ccc169b1a8..e726194c0c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.PlatformNotSupported.cs @@ -10,16 +10,9 @@ namespace System.Runtime.InteropServices private PosixSignalRegistration() { } [DynamicDependency("#ctor")] // Prevent the private ctor and the IDisposable implementation from getting linked away - public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) - { - if (handler is null) - { - throw new ArgumentNullException(nameof(handler)); - } - + private static PosixSignalRegistration Register(PosixSignal signal, Action handler) => throw new PlatformNotSupportedException(); - } - public partial void Dispose() { } + private void Unregister() { } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs index f01b2de0130..d91dd1c3a8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Unix.cs @@ -2,265 +2,138 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Threading; namespace System.Runtime.InteropServices { public sealed partial class PosixSignalRegistration { - private static volatile bool s_initialized; - private static readonly Dictionary?>> s_registrations = new(); + private static readonly Dictionary> s_registrations = Initialize(); - private readonly Action _handler; - private readonly PosixSignal _signal; - private readonly int _signo; - private bool _registered; - private readonly object _gate = new object(); - - private PosixSignalRegistration(PosixSignal signal, int signo, Action handler) + private static unsafe Dictionary> Initialize() { - _signal = signal; - _signo = signo; - _handler = handler; - } - - public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) - { - if (handler == null) + if (!Interop.Sys.InitializeTerminalAndSignalHandling()) { - throw new ArgumentNullException(nameof(handler)); + Interop.CheckIo(-1); } + Interop.Sys.SetPosixSignalHandler(&OnPosixSignal); + + return new Dictionary>(); + } + + private static PosixSignalRegistration Register(PosixSignal signal, Action handler) + { int signo = Interop.Sys.GetPlatformSignalNumber(signal); if (signo == 0) { throw new PlatformNotSupportedException(); } - PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler); - registration.Register(); - return registration; - } - - private unsafe void Register() - { - if (!s_initialized) - { - if (!Interop.Sys.InitializeTerminalAndSignalHandling()) - { - // We can't use Win32Exception because that causes a cycle with - // Microsoft.Win32.Primitives. - Interop.CheckIo(-1); - } - - Interop.Sys.SetPosixSignalHandler(&OnPosixSignal); - s_initialized = true; - } + var token = new Token(signal, signo, handler); + var registration = new PosixSignalRegistration(token); lock (s_registrations) { - if (!s_registrations.TryGetValue(_signo, out List?>? signalRegistrations)) + if (!s_registrations.TryGetValue(signo, out HashSet? tokens)) { - signalRegistrations = new List?>(); - s_registrations.Add(_signo, signalRegistrations); + s_registrations[signo] = tokens = new HashSet(); } - if (signalRegistrations.Count == 0) + if (tokens.Count == 0 && + !Interop.Sys.EnablePosixSignalHandling(signo)) { - if (!Interop.Sys.EnablePosixSignalHandling(_signo)) - { - // We can't use Win32Exception because that causes a cycle with - // Microsoft.Win32.Primitives. - Interop.CheckIo(-1); - } + Interop.CheckIo(-1); } - signalRegistrations.Add(new WeakReference(this)); + tokens.Add(token); } - _registered = true; + return registration; } - private bool CallHandler(PosixSignalContext context) + private void Unregister() { - lock (_gate) + lock (s_registrations) { - if (_registered) + if (_token is Token token) { - _handler(context); - return true; - } + _token = null; - return false; + if (s_registrations.TryGetValue(token.SigNo, out HashSet? tokens)) + { + tokens.Remove(token); + if (tokens.Count == 0) + { + s_registrations.Remove(token.SigNo); + Interop.Sys.DisablePosixSignalHandling(token.SigNo); + } + } + } } } [UnmanagedCallersOnly] private static int OnPosixSignal(int signo, PosixSignal signal) { - PosixSignalRegistration?[]? registrations = GetRegistrations(signo); - if (registrations != null) + Token[]? tokens = null; + + lock (s_registrations) { - // This is called on the native signal handling thread. We need to move to another thread so - // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends - // on work triggered from the signal handling thread. - - // For terminate/interrupt signals we use a dedicated Thread - // in case the ThreadPool is saturated. - bool useDedicatedThread = signal == PosixSignal.SIGINT || - signal == PosixSignal.SIGQUIT || - signal == PosixSignal.SIGTERM; - - if (useDedicatedThread) + if (s_registrations.TryGetValue(signo, out HashSet? registrations)) { - Thread handlerThread = new Thread(HandleSignal) + tokens = new Token[registrations.Count]; + registrations.CopyTo(tokens); + } + } + + if (tokens is null) + { + return 0; + } + + Debug.Assert(tokens.Length != 0); + + // This is called on the native signal handling thread. We need to move to another thread so + // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends + // on work triggered from the signal handling thread. + switch (signal) + { + case PosixSignal.SIGINT: + case PosixSignal.SIGQUIT: + case PosixSignal.SIGTERM: + // For terminate/interrupt signals we use a dedicated Thread in case the ThreadPool is saturated. + new Thread(HandleSignal) { IsBackground = true, Name = ".NET Signal Handler" - }; - handlerThread.UnsafeStart((signo, registrations)); - } - else - { - ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations)); - } + }.UnsafeStart((signo, tokens)); + break; - return 1; + default: + ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, tokens)); + break; } - return 0; - } + return 1; - private static PosixSignalRegistration?[]? GetRegistrations(int signo) - { - lock (s_registrations) + static void HandleSignal(object? state) { - if (s_registrations.TryGetValue(signo, out List?>? signalRegistrations)) + (int signo, Token[] tokens) = ((int, Token[]))state!; + + PosixSignalContext ctx = new(0); + foreach (Token token in tokens) { - if (signalRegistrations.Count != 0) - { - var registrations = new PosixSignalRegistration?[signalRegistrations.Count]; - bool hasRegistrations = false; - bool pruneWeakReferences = false; - - for (int i = 0; i < signalRegistrations.Count; i++) - { - if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) - { - registrations[i] = registration; - hasRegistrations = true; - } - else - { - // WeakReference no longer holds an object. PosixSignalRegistration got finalized. - signalRegistrations[i] = null; - pruneWeakReferences = true; - } - } - - if (pruneWeakReferences) - { - signalRegistrations.RemoveAll(item => item is null); - } - - if (hasRegistrations) - { - return registrations; - } - else - { - Interop.Sys.DisablePosixSignalHandling(signo); - } - } - } - return null; - } - } - - private static void HandleSignal(object? state) - { - HandleSignal(((int, PosixSignalRegistration?[]))state!); - } - - private static void HandleSignal((int signo, PosixSignalRegistration?[]? registrations) state) - { - do - { - bool handlersCalled = false; - if (state.registrations != null) - { - PosixSignalContext ctx = new(0); - foreach (PosixSignalRegistration? registration in state.registrations) - { - if (registration != null) - { - // Different values for PosixSignal map to the same signo. - // Match the PosixSignal value used when registering. - ctx.Signal = registration._signal; - if (registration.CallHandler(ctx)) - { - handlersCalled = true; - } - } - } - - if (ctx.Cancel) - { - return; - } + // Different values for PosixSignal map to the same signo. + // Match the PosixSignal value used when registering. + ctx.Signal = token.Signal; + token.Handler(ctx); } - if (Interop.Sys.HandleNonCanceledPosixSignal(state.signo, handlersCalled ? 0 : 1)) + if (!ctx.Cancel) { - return; - } - - // HandleNonCanceledPosixSignal returns false when handlers got registered. - state.registrations = GetRegistrations(state.signo); - } while (true); - } - - public partial void Dispose() - { - if (_registered) - { - lock (s_registrations) - { - List?> signalRegistrations = s_registrations[_signo]; - bool pruneWeakReferences = false; - for (int i = 0; i < signalRegistrations.Count; i++) - { - if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration)) - { - if (ReferenceEquals(this, registration)) - { - signalRegistrations.RemoveAt(i); - break; - } - } - else - { - // WeakReference no longer holds an object. PosixSignalRegistration got finalized. - signalRegistrations[i] = null; - pruneWeakReferences = true; - } - } - - if (pruneWeakReferences) - { - signalRegistrations.RemoveAll(item => item is null); - } - - if (signalRegistrations.Count == 0) - { - Interop.Sys.DisablePosixSignalHandling(_signo); - } - } - - // Synchronize with _handler invocations. - lock (_gate) - { - _registered = false; + Interop.Sys.HandleNonCanceledPosixSignal(signo); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs index d3e5405053e..a50fca2bbee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.Windows.cs @@ -6,59 +6,51 @@ using System.IO; namespace System.Runtime.InteropServices { - public sealed unsafe partial class PosixSignalRegistration + public sealed partial class PosixSignalRegistration { - private static readonly HashSet s_handlers = new(); + private static readonly HashSet s_registrations = new(); - private Token? _token; - - private PosixSignalRegistration(Token token) => _token = token; - - private static object SyncObj => s_handlers; - - public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler) + private static unsafe PosixSignalRegistration Register(PosixSignal signal, Action handler) { - if (handler is null) + switch (signal) { - throw new ArgumentNullException(nameof(handler)); + case PosixSignal.SIGINT: + case PosixSignal.SIGQUIT: + case PosixSignal.SIGTERM: + case PosixSignal.SIGHUP: + break; + + default: + throw new PlatformNotSupportedException(); } - lock (SyncObj) + var token = new Token(signal, handler); + var registration = new PosixSignalRegistration(token); + + lock (s_registrations) { - switch (signal) - { - case PosixSignal.SIGINT: - case PosixSignal.SIGQUIT: - case PosixSignal.SIGTERM: - case PosixSignal.SIGHUP: - break; - - default: - throw new PlatformNotSupportedException(); - } - - if (s_handlers.Count == 0 && + if (s_registrations.Count == 0 && !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: true)) { throw Win32Marshal.GetExceptionForLastWin32Error(); } - var token = new Token(signal, handler); - s_handlers.Add(token); - return new PosixSignalRegistration(token); + s_registrations.Add(token); } + + return registration; } - public partial void Dispose() + private unsafe void Unregister() { - lock (SyncObj) + lock (s_registrations) { if (_token is Token token) { _token = null; - s_handlers.Remove(token); - if (s_handlers.Count == 0 && + s_registrations.Remove(token); + if (s_registrations.Count == 0 && !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: false)) { throw Win32Marshal.GetExceptionForLastWin32Error(); @@ -94,9 +86,9 @@ namespace System.Runtime.InteropServices } List? tokens = null; - lock (SyncObj) + lock (s_registrations) { - foreach (Token token in s_handlers) + foreach (Token token in s_registrations) { if (token.Signal == signal) { @@ -118,17 +110,5 @@ namespace System.Runtime.InteropServices return context.Cancel ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; } - - private sealed class Token - { - public Token(PosixSignal signal, Action handler) - { - Signal = signal; - Handler = handler; - } - - public PosixSignal Signal { get; } - public Action Handler { get; } - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs index fb2c675acb8..811d75ddd51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PosixSignalRegistration.cs @@ -9,6 +9,13 @@ namespace System.Runtime.InteropServices /// Handles a . public sealed partial class PosixSignalRegistration : IDisposable { + /// The state associated with this registration. + /// + /// This is separate from the registration instance so that this token may be stored + /// in a statically rooted table, with a finalizer on the registration able to remove it. + /// + private Token? _token; + /// Registers a that is invoked when the occurs. /// The signal to register for. /// The handler that gets invoked. @@ -28,11 +35,48 @@ namespace System.Runtime.InteropServices [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("maccatalyst")] [UnsupportedOSPlatform("tvos")] - public static partial PosixSignalRegistration Create(PosixSignal signal, Action handler); + public static PosixSignalRegistration Create(PosixSignal signal, Action handler) + { + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + return Register(signal, handler); + } + + /// Initializes the registration to wrap the specified token. + private PosixSignalRegistration(Token token) => _token = token; /// Unregister the handler. - public partial void Dispose(); + public void Dispose() + { + Unregister(); + GC.SuppressFinalize(this); + } - ~PosixSignalRegistration() => Dispose(); + /// Unregister the handler. + ~PosixSignalRegistration() => Unregister(); + + /// The state associated with a registration. + private sealed class Token + { + public Token(PosixSignal signal, Action handler) + { + Signal = signal; + Handler = handler; + } + + public Token(PosixSignal signal, int sigNo, Action handler) + { + Signal = signal; + Handler = handler; + SigNo = sigNo; + } + + public PosixSignal Signal { get; } + public Action Handler { get; } + public int SigNo { get; } + } } } From f82b730ddf4d6170c3e63fb12a52efe72629822f Mon Sep 17 00:00:00 2001 From: hrrrrustic <35951936+hrrrrustic@users.noreply.github.com> Date: Tue, 13 Jul 2021 22:53:12 +0300 Subject: [PATCH 506/926] Remove EventSourceActivity (#55575) * remove file * remove a few references to deleted file * fix corelib --- ...gnostics.Tracing.EventSource.Redist.csproj | 1 - .../System.Private.CoreLib.Shared.projitems | 1 - .../TraceLogging/EventSourceActivity.cs | 313 ------------------ 3 files changed, 315 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs diff --git a/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj b/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj index fc47c82f88a..36aaba499cb 100644 --- a/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj +++ b/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj @@ -52,7 +52,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 4be53fea197..6c1182acbd1 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1287,7 +1287,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs deleted file mode 100644 index bfd8d0ff989..00000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs +++ /dev/null @@ -1,313 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#if ES_BUILD_STANDALONE -using System; -using System.Diagnostics; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// - /// Provides support for EventSource activities by marking the start and - /// end of a particular operation. - /// - internal sealed class EventSourceActivity - : IDisposable - { - /// - /// Initializes a new instance of the EventSourceActivity class that - /// is attached to the specified event source. The new activity will - /// not be attached to any related (parent) activity. - /// The activity is created in the Initialized state. - /// - /// - /// The event source to which the activity information is written. - /// - public EventSourceActivity(EventSource eventSource) - { - if (eventSource == null) - throw new ArgumentNullException(nameof(eventSource)); - - this.eventSource = eventSource; - } - - /// - /// You can make an activity out of just an EventSource. - /// - public static implicit operator EventSourceActivity(EventSource eventSource) => - new EventSourceActivity(eventSource); - - /* Properties */ - /// - /// Gets the event source to which this activity writes events. - /// - public EventSource EventSource => this.eventSource; - - /// - /// Gets this activity's unique identifier, or the default Guid if the - /// event source was disabled when the activity was initialized. - /// - public Guid Id => this.activityId; - -#if false // don't expose RelatedActivityId unless there is a need. - /// - /// Gets the unique identifier of this activity's related (parent) - /// activity. - /// - public Guid RelatedId - { - get { return this.relatedActivityId; } - } -#endif - - /// - /// Writes a Start event with the specified name and data. If the start event is not active (because the provider - /// is not on or keyword-level indicates the event is off, then the returned activity is simply the 'this' pointer - /// and it is effectively like start did not get called. - /// - /// A new activityID GUID is generated and the returned - /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes) - /// with this activityID. In addition the Start activity will log a 'relatedActivityID' that was the activity - /// ID before the start event. This way event processors can form a linked list of all the activities that - /// caused this one (directly or indirectly). - /// - /// - /// The name to use for the event. It is strongly suggested that this name end in 'Start' (e.g. DownloadStart). - /// If you do this, then the Stop() method will automatically replace the 'Start' suffix with a 'Stop' suffix. - /// - /// Allow options (keywords, level) to be set for the write associated with this start - /// These will also be used for the stop event. - /// The data to include in the event. - public EventSourceActivity Start(string? eventName, EventSourceOptions options, T data) - { - return this.Start(eventName, ref options, ref data); - } - /// - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords - /// and level==Info) Data payload is empty. - /// - public EventSourceActivity Start(string? eventName) - { - EventSourceOptions options = default; - EmptyStruct data = default; - return this.Start(eventName, ref options, ref data); - } - /// - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data). Data payload is empty. - /// - public EventSourceActivity Start(string? eventName, EventSourceOptions options) - { - EmptyStruct data = default; - return this.Start(eventName, ref options, ref data); - } - /// - /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords - /// and level==Info) - /// - public EventSourceActivity Start(string? eventName, T data) - { - EventSourceOptions options = default; - return this.Start(eventName, ref options, ref data); - } - - /// - /// Writes a Stop event with the specified data, and sets the activity - /// to the Stopped state. The name is determined by the eventName used in Start. - /// If that Start event name is suffixed with 'Start' that is removed, and regardless - /// 'Stop' is appended to the result to form the Stop event name. - /// May only be called when the activity is in the Started state. - /// - /// The data to include in the event. - public void Stop(T data) - { - this.Stop(null, ref data); - } - /// - /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that - /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. - /// - public void Stop(string? eventName) - { - EmptyStruct data = default; - this.Stop(eventName, ref data); - } - /// - /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that - /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. - /// - public void Stop(string? eventName, T data) - { - this.Stop(eventName, ref data); - } - - /// - /// Writes an event associated with this activity to the eventSource associated with this activity. - /// May only be called when the activity is in the Started state. - /// - /// - /// The name to use for the event. If null, the name is determined from - /// data's type. - /// - /// - /// The options to use for the event. - /// - /// The data to include in the event. - public void Write(string? eventName, EventSourceOptions options, T data) - { - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// - /// Writes an event associated with this activity. - /// May only be called when the activity is in the Started state. - /// - /// - /// The name to use for the event. If null, the name is determined from - /// data's type. - /// - /// The data to include in the event. - public void Write(string? eventName, T data) - { - EventSourceOptions options = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// - /// Writes a trivial event associated with this activity. - /// May only be called when the activity is in the Started state. - /// - /// - /// The name to use for the event. Must not be null. - /// - /// - /// The options to use for the event. - /// - public void Write(string? eventName, EventSourceOptions options) - { - EmptyStruct data = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// - /// Writes a trivial event associated with this activity. - /// May only be called when the activity is in the Started state. - /// - /// - /// The name to use for the event. Must not be null. - /// - public void Write(string? eventName) - { - EventSourceOptions options = default; - EmptyStruct data = default; - this.Write(this.eventSource, eventName, ref options, ref data); - } - /// - /// Writes an event to a arbitrary eventSource stamped with the activity ID of this activity. - /// - public void Write(EventSource source, string? eventName, EventSourceOptions options, T data) - { - this.Write(source, eventName, ref options, ref data); - } - - /// - /// Releases any unmanaged resources associated with this object. - /// If the activity is in the Started state, calls Stop(). - /// - public void Dispose() - { - if (this.state == State.Started) - { - EmptyStruct data = default; - this.Stop(null, ref data); - } - } - -#region private - private EventSourceActivity Start(string? eventName, ref EventSourceOptions options, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); - - // If the source is not on at all, then we don't need to do anything and we can simply return ourselves. - if (!this.eventSource.IsEnabled()) - return this; - - var newActivity = new EventSourceActivity(eventSource); - if (!this.eventSource.IsEnabled(options.Level, options.Keywords)) - { - // newActivity.relatedActivityId = this.Id; - Guid relatedActivityId = this.Id; - newActivity.activityId = Guid.NewGuid(); - newActivity.startStopOptions = options; - newActivity.eventName = eventName; - newActivity.startStopOptions.Opcode = EventOpcode.Start; - this.eventSource.Write(eventName, ref newActivity.startStopOptions, ref newActivity.activityId, ref relatedActivityId, ref data); - } - else - { - // If we are not active, we don't set the eventName, which basically also turns off the Stop event as well. - newActivity.activityId = this.Id; - } - - return newActivity; - } - - private void Write(EventSource eventSource, string? eventName, ref EventSourceOptions options, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); // Write after stop. - if (eventName == null) - throw new ArgumentNullException(); - - eventSource.Write(eventName, ref options, ref this.activityId, ref s_empty, ref data); - } - - private void Stop(string? eventName, ref T data) - { - if (this.state != State.Started) - throw new InvalidOperationException(); - - // If start was not fired, then stop isn't as well. - if (!StartEventWasFired) - return; - - Debug.Assert(this.eventName != null); - - this.state = State.Stopped; - if (eventName == null) - { - eventName = this.eventName; - if (eventName.EndsWith("Start", StringComparison.Ordinal)) - eventName = eventName.Substring(0, eventName.Length - 5); - eventName += "Stop"; - } - this.startStopOptions.Opcode = EventOpcode.Stop; - this.eventSource.Write(eventName, ref this.startStopOptions, ref this.activityId, ref s_empty, ref data); - } - - private enum State - { - Started, - Stopped - } - - /// - /// If eventName is non-null then we logged a start event - /// - private bool StartEventWasFired => eventName != null; - - private readonly EventSource eventSource; - private EventSourceOptions startStopOptions; - internal Guid activityId; - // internal Guid relatedActivityId; - private State state; - private string? eventName; - - internal static Guid s_empty; -#endregion - } -} From ae64899ca74968c1c0dd2d81564c1b9fab12e2b4 Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Tue, 13 Jul 2021 13:16:29 -0700 Subject: [PATCH 507/926] Add runtime property HOSTFXR_PATH (#55369) * Add property HOSTFXR_PATH * Comment * PR feedback * Use get_own_module_path since we are on hostfxr * Dispose FileStream --- .../TestProjects/RuntimeProperties/Program.cs | 9 ++- .../HostActivation.Tests/DotNetBuilder.cs | 16 +++++ .../HostActivation.Tests/RuntimeProperties.cs | 58 +++++++++++++++---- src/native/corehost/fxr/corehost_init.cpp | 9 ++- src/native/corehost/fxr/corehost_init.h | 3 +- src/native/corehost/fxr/fx_muxer.cpp | 23 +++++++- src/native/corehost/fxr/fx_muxer.h | 1 + 7 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs b/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs index d22677aa617..96fa3b2adba 100644 --- a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs +++ b/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs @@ -14,7 +14,14 @@ namespace RuntimeProperties foreach (string propertyName in args) { - Console.WriteLine($"AppContext.GetData({propertyName}) = {System.AppContext.GetData(propertyName)}"); + var propertyValue = (string)System.AppContext.GetData(propertyName); + if (string.IsNullOrEmpty(propertyValue)) + { + Console.WriteLine($"Property '{propertyName}' was not found."); + continue; + } + + Console.WriteLine($"AppContext.GetData({propertyName}) = {propertyValue}"); } } } diff --git a/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs b/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs index 2a99ab5681f..0a8d54559e2 100644 --- a/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs +++ b/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs @@ -174,6 +174,22 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation return this; } + public DotNetBuilder AddMockSDK( + string version, + string MNAVersion) + { + string path = Path.Combine(_path, "sdk", version); + Directory.CreateDirectory(path); + + using var _ = File.Create(Path.Combine(path, "dotnet.dll")); + + RuntimeConfig dotnetRuntimeConfig = new RuntimeConfig(Path.Combine(path, "dotnet.runtimeconfig.json")); + dotnetRuntimeConfig.WithFramework(new RuntimeConfig.Framework("Microsoft.NETCore.App", MNAVersion)); + dotnetRuntimeConfig.Save(); + + return this; + } + public DotNetCli Build() { return new DotNetCli(_path); diff --git a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs index 6d8949c4132..01b5b415ecd 100644 --- a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs +++ b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using Microsoft.DotNet.Cli.Build; using Xunit; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation @@ -25,9 +26,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation var dotnet = fixture.BuiltDotnet; var appDll = fixture.TestProject.AppDll; dotnet.Exec(appDll, sharedState.AppTestPropertyName) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdErr() - .CaptureStdOut() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdErrContaining($"Property {sharedState.AppTestPropertyName} = {sharedState.AppTestPropertyValue}") @@ -43,9 +42,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation var dotnet = fixture.BuiltDotnet; var appDll = fixture.TestProject.AppDll; dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdErr() - .CaptureStdOut() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.FrameworkTestPropertyValue}") @@ -65,15 +62,39 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation var dotnet = fixture.BuiltDotnet; var appDll = fixture.TestProject.AppDll; dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdErr() - .CaptureStdOut() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() .And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.AppTestPropertyValue}") .And.HaveStdOutContaining($"AppContext.GetData({sharedState.FrameworkTestPropertyName}) = {sharedState.AppTestPropertyValue}"); } + [Fact] + public void HostFxrPathProperty_SetWhenRunningSDKCommand() + { + var dotnet = sharedState.MockSDK; + dotnet.Exec("--info") + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.HaveStdErrContaining($"Property {sharedState.HostFxrPathPropertyName} = {dotnet.GreatestVersionHostFxrFilePath}"); + } + + [Fact] + public void HostFxrPathProperty_NotVisibleFromApp() + { + var fixture = sharedState.RuntimePropertiesFixture + .Copy(); + + var dotnet = fixture.BuiltDotnet; + var appDll = fixture.TestProject.AppDll; + dotnet.Exec(appDll, sharedState.HostFxrPathPropertyName) + .EnableTracingAndCaptureOutputs() + .Execute() + .Should().Pass() + .And.HaveStdOutContaining($"Property '{sharedState.HostFxrPathPropertyName}' was not found."); + } + [Fact] public void DuplicateCommonProperty_Fails() { @@ -88,9 +109,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation var dotnet = fixture.BuiltDotnet; var appDll = fixture.TestProject.AppDll; dotnet.Exec(appDll) - .EnvironmentVariable("COREHOST_TRACE", "1") - .CaptureStdErr() - .CaptureStdOut() + .EnableTracingAndCaptureOutputs() .Execute() .Should().Fail() .And.HaveStdErrContaining($"Duplicate runtime property found: {name}"); @@ -100,11 +119,13 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation { public TestProjectFixture RuntimePropertiesFixture { get; } public RepoDirectoriesProvider RepoDirectories { get; } + public DotNetCli MockSDK { get; } public string AppTestPropertyName => "APP_TEST_PROPERTY"; public string AppTestPropertyValue => "VALUE_FROM_APP"; public string FrameworkTestPropertyName => "FRAMEWORK_TEST_PROPERTY"; public string FrameworkTestPropertyValue => "VALUE_FROM_FRAMEWORK"; + public string HostFxrPathPropertyName => "HOSTFXR_PATH"; private readonly string copiedDotnet; @@ -113,6 +134,19 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation copiedDotnet = Path.Combine(TestArtifact.TestArtifactsPath, "runtimeProperties"); SharedFramework.CopyDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), copiedDotnet); + MockSDK = new DotNetBuilder(copiedDotnet, Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), "exe") + .AddMicrosoftNETCoreAppFrameworkMockCoreClr("9999.0.0") + .AddMockSDK("9999.0.0-dev", "9999.0.0") + .Build(); + + File.WriteAllText(Path.Combine(MockSDK.BinPath, "global.json"), + @" +{ + ""sdk"": { + ""version"": ""9999.0.0-dev"" + } +}"); + RepoDirectories = new RepoDirectoriesProvider(builtDotnet: copiedDotnet); RuntimePropertiesFixture = new TestProjectFixture("RuntimeProperties", RepoDirectories) diff --git a/src/native/corehost/fxr/corehost_init.cpp b/src/native/corehost/fxr/corehost_init.cpp index eea9b768f5f..2d169348fa6 100644 --- a/src/native/corehost/fxr/corehost_init.cpp +++ b/src/native/corehost/fxr/corehost_init.cpp @@ -20,7 +20,8 @@ corehost_init_t::corehost_init_t( const pal::string_t& additional_deps_serialized, const std::vector& probe_paths, const host_mode_t mode, - const fx_definition_vector_t& fx_definitions) + const fx_definition_vector_t& fx_definitions, + const std::vector>& additional_properties) : m_tfm(get_app(fx_definitions).get_runtime_config().get_tfm()) , m_deps_file(deps_file) , m_additional_deps_serialized(additional_deps_serialized) @@ -35,6 +36,12 @@ corehost_init_t::corehost_init_t( { make_cstr_arr(m_probe_paths, &m_probe_paths_cstr); + for (const auto& additional_property : additional_properties) + { + m_clr_keys.push_back(additional_property.first); + m_clr_values.push_back(additional_property.second); + } + size_t fx_count = fx_definitions.size(); m_fx_names.reserve(fx_count); m_fx_dirs.reserve(fx_count); diff --git a/src/native/corehost/fxr/corehost_init.h b/src/native/corehost/fxr/corehost_init.h index 70ea7be46fb..6c3148b6274 100644 --- a/src/native/corehost/fxr/corehost_init.h +++ b/src/native/corehost/fxr/corehost_init.h @@ -45,7 +45,8 @@ public: const pal::string_t& additional_deps_serialized, const std::vector& probe_paths, const host_mode_t mode, - const fx_definition_vector_t& fx_definitions); + const fx_definition_vector_t& fx_definitions, + const std::vector>& additional_properties); const host_interface_t& get_host_init_data(); diff --git a/src/native/corehost/fxr/fx_muxer.cpp b/src/native/corehost/fxr/fx_muxer.cpp index 6a20206e2ca..8647130c18d 100644 --- a/src/native/corehost/fxr/fx_muxer.cpp +++ b/src/native/corehost/fxr/fx_muxer.cpp @@ -370,6 +370,7 @@ namespace const pal::string_t &app_candidate, const opt_map_t &opts, host_mode_t mode, + const bool is_sdk_command, /*out*/ pal::string_t &hostpolicy_dir, /*out*/ std::unique_ptr &init) { @@ -473,6 +474,16 @@ namespace } } + std::vector> additional_properties; + if (is_sdk_command) + { + pal::string_t fxr_path; + pal::get_own_module_path(&fxr_path); + + // We pass the loaded hostfxr path to the SDK can load it without relying on dlopen/LoadLibrary to find it. + additional_properties.push_back(std::make_pair(_X("HOSTFXR_PATH"), fxr_path)); + } + const known_options opts_probe_path = known_options::additional_probing_path; std::vector spec_probe_paths = opts.count(opts_probe_path) ? opts.find(opts_probe_path)->second : std::vector(); std::vector probe_realpaths = get_probe_realpaths(fx_definitions, spec_probe_paths); @@ -485,7 +496,7 @@ namespace return StatusCode::CoreHostLibMissingFailure; } - init.reset(new corehost_init_t(host_command, host_info, deps_file, additional_deps_serialized, probe_realpaths, mode, fx_definitions)); + init.reset(new corehost_init_t(host_command, host_info, deps_file, additional_deps_serialized, probe_realpaths, mode, fx_definitions, additional_properties)); return StatusCode::Success; } @@ -498,6 +509,7 @@ namespace int new_argc, const pal::char_t** new_argv, host_mode_t mode, + const bool is_sdk_command, pal::char_t out_buffer[], int32_t buffer_size, int32_t* required_buffer_size) @@ -510,6 +522,7 @@ namespace app_candidate, opts, mode, + is_sdk_command, hostpolicy_dir, init); if (rc != StatusCode::Success) @@ -572,6 +585,7 @@ int fx_muxer_t::execute( argv, new_argoff, mode, + false /*is_sdk_command*/, result_buffer, buffer_size, required_buffer_size); @@ -621,7 +635,8 @@ namespace } const pal::string_t additional_deps_serialized; - init.reset(new corehost_init_t(pal::string_t{}, host_info, deps_file, additional_deps_serialized, probe_realpaths, mode, fx_definitions)); + const std::vector> additional_properties; + init.reset(new corehost_init_t(pal::string_t{}, host_info, deps_file, additional_deps_serialized, probe_realpaths, mode, fx_definitions, additional_properties)); return StatusCode::Success; } @@ -725,6 +740,7 @@ int fx_muxer_t::initialize_for_app( host_info.app_path, opts, mode, + false /*is_sdk_command*/, hostpolicy_dir, init); if (rc != StatusCode::Success) @@ -978,6 +994,7 @@ int fx_muxer_t::handle_exec_host_command( const pal::char_t* argv[], int argoff, host_mode_t mode, + const bool is_sdk_command, pal::char_t result_buffer[], int32_t buffer_size, int32_t* required_buffer_size) @@ -1006,6 +1023,7 @@ int fx_muxer_t::handle_exec_host_command( new_argc, new_argv, mode, + is_sdk_command, result_buffer, buffer_size, required_buffer_size); @@ -1096,6 +1114,7 @@ int fx_muxer_t::handle_cli( new_argv.data(), new_argoff, host_mode_t::muxer, + true /*is_sdk_command*/, nullptr /*result_buffer*/, 0 /*buffer_size*/, nullptr/*required_buffer_size*/); diff --git a/src/native/corehost/fxr/fx_muxer.h b/src/native/corehost/fxr/fx_muxer.h index 6794e2a1c82..b3a0e000470 100644 --- a/src/native/corehost/fxr/fx_muxer.h +++ b/src/native/corehost/fxr/fx_muxer.h @@ -47,6 +47,7 @@ private: const pal::char_t* argv[], int argoff, host_mode_t mode, + const bool is_sdk_command, pal::char_t result_buffer[], int32_t buffer_size, int32_t* required_buffer_size); From a13f713a25e5d2890f28e2394b508b4cc5fd909b Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Tue, 13 Jul 2021 22:38:24 +0200 Subject: [PATCH 508/926] [QUIC] Move ByteMixingOrNativeAVE_MinimalFailingTest to OuterLoop (#55595) Fixes #55588 --- .../System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 6ce1540fba5..884c1894756 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -453,6 +453,7 @@ namespace System.Net.Quic.Tests } [Fact] + [OuterLoop("May take several seconds")] public async Task ByteMixingOrNativeAVE_MinimalFailingTest() { const int writeSize = 64 * 1024; From 39152805901172400f1aad2ce511719663aa4e52 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 13 Jul 2021 22:42:47 +0200 Subject: [PATCH 509/926] [mono] Fix loader incompatibility w.r.t. Resolving event (#54815) * When loading a non-satellite assembly into a non-default ALC, invoke the Resolving event in the default ALC first. * Fixes https://github.com/dotnet/runtime/issues/54814 --- src/mono/mono/metadata/assembly.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/assembly.c b/src/mono/mono/metadata/assembly.c index e98264a2f93..8a52e63bc85 100644 --- a/src/mono/mono/metadata/assembly.c +++ b/src/mono/mono/metadata/assembly.c @@ -1061,7 +1061,8 @@ netcore_load_reference (MonoAssemblyName *aname, MonoAssemblyLoadContext *alc, M * * 7. If this is a satellite request, call the ALC ResolveSatelliteAssembly method. * - * 8. Call the ALC Resolving event. + * 8. Call the ALC Resolving event. If the ALC is not the default and this is not + * a satellite request, call the Resolving event in the default ALC first. * * 9. Call the ALC AssemblyResolve event (except for corlib satellite assemblies). * @@ -1138,6 +1139,15 @@ netcore_load_reference (MonoAssemblyName *aname, MonoAssemblyLoadContext *alc, M } } + // For compatibility with CoreCLR, invoke the Resolving event in the default ALC first whenever loading + // a non-satellite assembly into a non-default ALC. See: https://github.com/dotnet/runtime/issues/54814 + if (!is_default && !is_satellite) { + reference = mono_alc_invoke_resolve_using_resolving_event_nofail (mono_alc_get_default (), aname); + if (reference) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly found with the Resolving event (default ALC): '%s'.", aname->name); + goto leave; + } + } reference = mono_alc_invoke_resolve_using_resolving_event_nofail (alc, aname); if (reference) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly found with the Resolving event: '%s'.", aname->name); From 271eea2a1720bfbb4098bd950198c2ebb6a96637 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 14 Jul 2021 02:44:03 +0600 Subject: [PATCH 510/926] Remove IL205 warning for System.Data.Odbc (#54809) Function which produce warning not used anywhere --- .../System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml | 6 ------ .../src/System/Data/Odbc/OdbcConnectionHandle.cs | 7 ------- 2 files changed, 13 deletions(-) diff --git a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml index 6ea564dbbf6..c6e3fb619c2 100644 --- a/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Odbc/src/ILLink/ILLink.Suppressions.xml @@ -1,12 +1,6 @@  - - ILLink - IL2050 - member - M:System.Data.Odbc.OdbcConnectionHandle.SetConnectionAttribute4(System.Data.Odbc.ODBC32.SQL_ATTR,System.Transactions.IDtcTransaction,System.Int32) - ILLink IL2026 diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs index b2c7ee7cb33..8339b330196 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs @@ -263,12 +263,5 @@ namespace System.Data.Odbc ODBC32.RetCode retcode = Interop.Odbc.SQLSetConnectAttrW(this, attribute, buffer, length); return retcode; } - - internal ODBC32.RetCode SetConnectionAttribute4(ODBC32.SQL_ATTR attribute, System.Transactions.IDtcTransaction transaction, int length) - { - ODBC32.RetCode retcode = Interop.Odbc.SQLSetConnectAttrW(this, attribute, transaction, length); - ODBC.TraceODBC(3, "SQLSetConnectAttrW", retcode); - return retcode; - } } } From a38fc2fd272313165e30f8301d4d767a5c30f659 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 14 Jul 2021 02:47:02 +0600 Subject: [PATCH 511/926] Remove IL2050 in System.Management (#54810) * Remove IL2050 in System.Management Remove unused functions which trigger IL2050 As per discussed in #54317 * Fix location of variables --- .../Ole32/Interop.CoGetObjectContext.cs | 4 +-- .../Ole32/Interop.CreateStreamOnHGlobal.cs | 4 +-- .../src/ILLink/ILLink.Suppressions.xml | 26 +------------------ .../src/System.Management.csproj | 8 ------ 4 files changed, 5 insertions(+), 37 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs index 43263270343..c73b065ea59 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class Ole32 { - [DllImport(Libraries.Ole32, PreserveSig = false)] - internal static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease); + [DllImport(Libraries.Ole32)] + internal static extern int CoGetObjectContext([MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv); } } diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs index 1b153d786df..d65814a2f51 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Ole32 { - [DllImport(Libraries.Ole32)] - internal static extern int CoGetObjectContext([MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv); + [DllImport(Libraries.Ole32, PreserveSig = false)] + internal static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease); } } diff --git a/src/libraries/System.Management/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Management/src/ILLink/ILLink.Suppressions.xml index 54f262023c9..fbf2a09e7eb 100644 --- a/src/libraries/System.Management/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Management/src/ILLink/ILLink.Suppressions.xml @@ -31,29 +31,5 @@ member M:System.Management.MTAHelper.WorkerThread - - ILLink - IL2050 - member - M:Interop.Ole32.CoMarshalInterface(System.Runtime.InteropServices.ComTypes.IStream,System.Guid,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32) - - - ILLink - IL2050 - member - M:Interop.Ole32.CoUnmarshalInterface(System.Runtime.InteropServices.ComTypes.IStream,System.Guid) - - - ILLink - IL2050 - member - M:Interop.Ole32.CreateStreamOnHGlobal(System.IntPtr,System.Boolean) - - - ILLink - IL2050 - member - M:Interop.Ole32.GetHGlobalFromStream(System.Runtime.InteropServices.ComTypes.IStream) - - \ No newline at end of file + diff --git a/src/libraries/System.Management/src/System.Management.csproj b/src/libraries/System.Management/src/System.Management.csproj index 9fac8892aed..2d353115180 100644 --- a/src/libraries/System.Management/src/System.Management.csproj +++ b/src/libraries/System.Management/src/System.Management.csproj @@ -22,14 +22,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.LoadLibrary.cs" /> - - - - From ce0982256cd83960a97d8745a84e97acc8ebf98d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 13 Jul 2021 17:10:18 -0400 Subject: [PATCH 512/926] Fix length check for Regex BOL FindFirstChar optimization (#55574) For a beginning-of-line anchor, in FindFirstChar we use IndexOf to quickly skip ahead to the next \n. But we neglected to check to see whether that brought us past an explicitly specified end position. This just adds the missing check. --- .../System/Text/RegularExpressions/RegexCompiler.cs | 12 +++++++----- .../Text/RegularExpressions/RegexInterpreter.cs | 4 ++-- .../tests/Regex.Match.Tests.cs | 13 ++++++++----- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index c06b5af67c6..b9b6e791ed5 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -1240,19 +1240,21 @@ namespace System.Text.RegularExpressions { Stloc(newlinePos); - // if (newlinePos == -1) + // if (newlinePos == -1 || newlinePos + 1 > runtextend) // { // runtextpos = runtextend; // return false; // } - Label foundNextLine = DefineLabel(); Ldloc(newlinePos); Ldc(-1); - Bne(foundNextLine); - BrFar(returnFalse); + Beq(returnFalse); + Ldloc(newlinePos); + Ldc(1); + Add(); + Ldloc(_runtextendLocal); + Bgt(returnFalse); // runtextpos = newlinePos + 1; - MarkLabel(foundNextLine); Ldloc(newlinePos); Ldc(1); Add(); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs index 4cc3dc52860..d557ec6c3aa 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs @@ -406,7 +406,7 @@ namespace System.Text.RegularExpressions if (runtextpos > runtextbeg && runtext![runtextpos - 1] != '\n') { int newline = runtext.IndexOf('\n', runtextpos); - if (newline == -1) + if (newline == -1 || newline + 1 > runtextend) { runtextpos = runtextend; return false; @@ -457,7 +457,7 @@ namespace System.Text.RegularExpressions if (!_code.LeadingCharClasses[0].CaseInsensitive) { // singleton, left-to-right, case-sensitive - int i = runtext.AsSpan(runtextpos, runtextend - runtextpos).IndexOf(ch); + int i = span.IndexOf(ch); if (i >= 0) { runtextpos += i; diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs index d5d690b0c29..719f280c917 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs @@ -152,6 +152,9 @@ namespace System.Text.RegularExpressions.Tests // Using beginning/end of string chars \A, \Z: Actual - "\\Aaaa\\w+zzz\\Z" yield return new object[] { @"\Aaaa\w+zzz\Z", "aaaasdfajsdlfjzzza", RegexOptions.None, 0, 18, false, string.Empty }; + // Anchors and multiline + yield return new object[] { @"^A$", "ABC\n", RegexOptions.Multiline, 0, 2, false, string.Empty }; + // Using beginning/end of string chars \A, \Z: Actual - "\\Aaaa\\w+zzz\\Z" yield return new object[] { @"\A(line2\n)line3\Z", "line2\nline3\n", RegexOptions.Multiline, 0, 12, true, "line2\nline3" }; @@ -813,7 +816,7 @@ namespace System.Text.RegularExpressions.Tests } }; - // Mutliline + // Multiline yield return new object[] { "(line2$\n)line3", "line1\nline2\nline3\n\nline4", RegexOptions.Multiline, 0, 24, @@ -824,7 +827,7 @@ namespace System.Text.RegularExpressions.Tests } }; - // Mutliline + // Multiline yield return new object[] { "(line2\n^)line3", "line1\nline2\nline3\n\nline4", RegexOptions.Multiline, 0, 24, @@ -835,7 +838,7 @@ namespace System.Text.RegularExpressions.Tests } }; - // Mutliline + // Multiline yield return new object[] { "(line3\n$\n)line4", "line1\nline2\nline3\n\nline4", RegexOptions.Multiline, 0, 24, @@ -846,7 +849,7 @@ namespace System.Text.RegularExpressions.Tests } }; - // Mutliline + // Multiline yield return new object[] { "(line3\n^\n)line4", "line1\nline2\nline3\n\nline4", RegexOptions.Multiline, 0, 24, @@ -857,7 +860,7 @@ namespace System.Text.RegularExpressions.Tests } }; - // Mutliline + // Multiline yield return new object[] { "(line2$\n^)line3", "line1\nline2\nline3\n\nline4", RegexOptions.Multiline, 0, 24, From 14854e209868749262bd58a64407b3802177542f Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 13 Jul 2021 14:33:41 -0700 Subject: [PATCH 513/926] Recognize MacCatalyst as a superset of iOS (#55550) Recognize MacCatalyst as a superset of iOS and apply platform guard attributes that will inform the analyzer of the relationship. --- .../src/System/OperatingSystem.cs | 15 ++++++-- .../tests/System/OperatingSystemTests.cs | 37 ++++++++++++++----- .../System.Runtime/ref/System.Runtime.cs | 2 + 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs index a0e17bfb349..ce70c49dfc1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -3,12 +3,13 @@ using System.Diagnostics; using System.Runtime.Serialization; +using System.Runtime.Versioning; namespace System { public sealed class OperatingSystem : ISerializable, ICloneable { -#if TARGET_UNIX && !TARGET_OSX +#if TARGET_UNIX && !TARGET_OSX && !TARGET_MACCATALYST && !TARGET_IOS private static readonly string s_osPlatformName = Interop.Sys.GetUnixName(); #endif @@ -102,6 +103,10 @@ namespace System return platform.Equals("WINDOWS", StringComparison.OrdinalIgnoreCase); #elif TARGET_OSX return platform.Equals("OSX", StringComparison.OrdinalIgnoreCase) || platform.Equals("MACOS", StringComparison.OrdinalIgnoreCase); +#elif TARGET_MACCATALYST + return platform.Equals("MACCATALYST", StringComparison.OrdinalIgnoreCase) || platform.Equals("IOS", StringComparison.OrdinalIgnoreCase); +#elif TARGET_IOS + return platform.Equals("IOS", StringComparison.OrdinalIgnoreCase); #elif TARGET_UNIX return platform.Equals(s_osPlatformName, StringComparison.OrdinalIgnoreCase); #else @@ -173,18 +178,20 @@ namespace System => IsAndroid() && IsOSVersionAtLeast(major, minor, build, revision); /// - /// Indicates whether the current application is running on iOS. + /// Indicates whether the current application is running on iOS or MacCatalyst. /// + [SupportedOSPlatformGuard("maccatalyst")] public static bool IsIOS() => -#if TARGET_IOS +#if TARGET_IOS || TARGET_MACCATALYST true; #else false; #endif /// - /// Check for the iOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given iOS release. + /// Check for the iOS/MacCatalyst version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given iOS release. /// + [SupportedOSPlatformGuard("maccatalyst")] public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0) => IsIOS() && IsOSVersionAtLeast(major, minor, build, 0); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs index 6a6b1cdd6b6..405cf4373f9 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs @@ -132,6 +132,23 @@ namespace System.Tests [Fact, PlatformSpecific(TestPlatforms.MacCatalyst)] public static void TestIsOSVersionAtLeast_MacCatalyst() => TestIsOSVersionAtLeast("MacCatalyst"); + [Fact, PlatformSpecific(TestPlatforms.MacCatalyst)] + public static void MacCatalyst_Is_Also_iOS() + { + Assert.True(OperatingSystem.IsOSPlatform("IOS")); + Assert.True(OperatingSystem.IsIOS()); + + AssertVersionChecks(true, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast("IOS", major, minor, build, revision)); + AssertVersionChecks(true, (major, minor, build) => OperatingSystem.IsOSPlatformVersionAtLeast("IOS", major, minor, build)); + } + + [Fact, PlatformSpecific(TestPlatforms.iOS)] + public static void IOS_Is_Not_Also_MacCatalyst() + { + Assert.False(OperatingSystem.IsOSPlatform("MacCatalyst")); + Assert.False(OperatingSystem.IsMacCatalyst()); + } + [Fact, PlatformSpecific(TestPlatforms.tvOS)] public static void TestIsOSPlatform_TvOS() => TestIsOSPlatform("tvOS", OperatingSystem.IsTvOS); @@ -146,13 +163,13 @@ namespace System.Tests private static void TestIsOSPlatform(string currentOSName, Func currentOSCheck) { - foreach (string platfromName in AllKnownPlatformNames) + foreach (string platformName in AllKnownPlatformNames) { - bool expected = currentOSName.Equals(platfromName, StringComparison.OrdinalIgnoreCase); + bool expected = currentOSName.Equals(platformName, StringComparison.OrdinalIgnoreCase); - Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName)); - Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName.ToUpper())); - Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName.ToLower())); + Assert.Equal(expected, OperatingSystem.IsOSPlatform(platformName)); + Assert.Equal(expected, OperatingSystem.IsOSPlatform(platformName.ToUpper())); + Assert.Equal(expected, OperatingSystem.IsOSPlatform(platformName.ToLower())); } Assert.True(currentOSCheck()); @@ -176,13 +193,13 @@ namespace System.Tests private static void TestIsOSVersionAtLeast(string currentOSName) { - foreach (string platfromName in AllKnownPlatformNames) + foreach (string platformName in AllKnownPlatformNames) { - bool isCurrentOS = currentOSName.Equals(platfromName, StringComparison.OrdinalIgnoreCase); + bool isCurrentOS = currentOSName.Equals(platformName, StringComparison.OrdinalIgnoreCase); - AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName, major, minor, build, revision)); - AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName.ToLower(), major, minor, build, revision)); - AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName.ToUpper(), major, minor, build, revision)); + AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platformName, major, minor, build, revision)); + AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platformName.ToLower(), major, minor, build, revision)); + AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platformName.ToUpper(), major, minor, build, revision)); } AssertVersionChecks(currentOSName.Equals("Android", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsAndroidVersionAtLeast); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 2656da8c999..35fe09cc662 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -4965,7 +4965,9 @@ namespace System public static bool IsFreeBSDVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { throw null; } public static bool IsAndroid() { throw null; } public static bool IsAndroidVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { throw null; } + [System.Runtime.Versioning.SupportedOSPlatformGuardAttribute("maccatalyst")] public static bool IsIOS() { throw null; } + [System.Runtime.Versioning.SupportedOSPlatformGuardAttribute("maccatalyst")] public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } public static bool IsMacOS() { throw null; } public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; } From f5316845d5a0069f1b265b5f6d3b81b827450737 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 13 Jul 2021 15:10:34 -0700 Subject: [PATCH 514/926] Avoid declaring an object cannot fit in a segment if we already have committed space for it (#55585) --- src/coreclr/gc/gc.cpp | 16 +++++++++++----- src/coreclr/gc/gcpriv.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 268fffcd4d7..6cf5283476a 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -14646,6 +14646,7 @@ BOOL gc_heap::short_on_end_of_seg (heap_segment* seg) BOOL sufficient_p = sufficient_space_regions (end_gen0_region_space, end_space_after_gc()); #else BOOL sufficient_p = sufficient_space_end_seg (allocated, + heap_segment_committed (seg), heap_segment_reserved (seg), end_space_after_gc()); #endif //USE_REGIONS @@ -37875,13 +37876,18 @@ bool gc_heap::sufficient_space_regions (size_t end_space, size_t end_space_requi return false; } #else //USE_REGIONS -BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end, size_t end_space_required) +BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* committed, uint8_t* reserved, size_t end_space_required) { BOOL can_fit = FALSE; - size_t end_seg_space = (size_t)(seg_end - start); - if (end_seg_space > end_space_required) + size_t committed_space = (size_t)(committed - start); + size_t end_seg_space = (size_t)(reserved - start); + if (committed_space > end_space_required) { - return check_against_hard_limit (end_space_required); + return true; + } + else if (end_seg_space > end_space_required) + { + return check_against_hard_limit (end_space_required - committed_space); } else return false; @@ -38045,7 +38051,7 @@ BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp) size_t gen0_end_space = get_gen0_end_space(); BOOL can_fit = sufficient_space_regions (gen0_end_space, end_space); #else //USE_REGIONS - BOOL can_fit = sufficient_space_end_seg (start, heap_segment_reserved (ephemeral_heap_segment), end_space); + BOOL can_fit = sufficient_space_end_seg (start, heap_segment_committed (ephemeral_heap_segment), heap_segment_reserved (ephemeral_heap_segment), end_space); #endif //USE_REGIONS return can_fit; } diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 09bd97d1891..d3f3526925d 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -3182,7 +3182,7 @@ protected: BOOL& should_expand); #ifndef USE_REGIONS PER_HEAP - BOOL sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end, + BOOL sufficient_space_end_seg (uint8_t* start, uint8_t* committed, uint8_t* reserved, size_t end_space_required); #endif //!USE_REGIONS From 002370bd1b2d848dcf65ef11cf6b2f4592a6fb89 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 13 Jul 2021 15:25:10 -0700 Subject: [PATCH 515/926] Open types can exist as entries in interface map (#55372) * Open types can exist as entries in interface map - While invalid via the ECMA spec, the runtime currently represents a type explicitly instantiated over its own generic type parameters via the open type MethodTable - This is not strictly correct, as per the spec, these should be represented via an instantiated type, but changing that detail at this time is considered highly risky - This conflicts with the perf optimization around partialy interface loading which uses the open type of an interface to represent a type instantiated in the curiously recurring fashion. - The fix is to detect types instantiated over generic variables, and make them ineligible for the optimization, and to detect those cases where the optimization is ineligible, and revert back to the non-optimized behavior Fixes #55323 --- src/coreclr/vm/methodtable.cpp | 5 ++-- src/coreclr/vm/methodtable.h | 3 ++- src/coreclr/vm/methodtable.inl | 2 +- src/coreclr/vm/methodtablebuilder.cpp | 11 ++++++--- .../CuriouslyRecurringThroughInterface.cs | 23 +++++++++++++++++++ .../CuriouslyRecurringThroughInterface.csproj | 11 +++++++++ 6 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.cs create mode 100644 src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.csproj diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 834d3cfc8c4..8bdc5f0c2a8 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -1577,6 +1577,7 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, // Shortcut for generic approx type scenario if (pMTInterfaceMapOwner != NULL && + !pMTInterfaceMapOwner->ContainsGenericVariables() && IsSpecialMarkerTypeForGenericCasting() && GetTypeDefRid() == pTargetMT->GetTypeDefRid() && GetModule() == pTargetMT->GetModule() && @@ -1603,7 +1604,7 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT, for (DWORD i = 0; i < inst.GetNumArgs(); i++) { TypeHandle thArg = inst[i]; - if (IsSpecialMarkerTypeForGenericCasting() && pMTInterfaceMapOwner) + if (IsSpecialMarkerTypeForGenericCasting() && pMTInterfaceMapOwner && !pMTInterfaceMapOwner->ContainsGenericVariables()) { thArg = pMTInterfaceMapOwner; } @@ -9820,7 +9821,7 @@ PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMT CONTRACT_END; MethodTable *pResult = m_pMap->GetMethodTable(); - if (pResult->IsSpecialMarkerTypeForGenericCasting()) + if (pResult->IsSpecialMarkerTypeForGenericCasting() && !pMTOwner->ContainsGenericVariables()) { TypeHandle ownerAsInst[MaxGenericParametersForSpecialMarkerType]; for (DWORD i = 0; i < MaxGenericParametersForSpecialMarkerType; i++) diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 4063e50b7b6..df712a378d4 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2245,7 +2245,8 @@ public: { if (pCurrentMethodTable->HasSameTypeDefAs(pMT) && pMT->HasInstantiation() && - pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() && + pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() && + !pMTOwner->ContainsGenericVariables() && pMT->GetInstantiation().ContainsAllOneType(pMTOwner)) { exactMatch = true; diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index a3adc702cbe..b1af313a295 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1571,7 +1571,7 @@ FORCEINLINE BOOL MethodTable::ImplementsInterfaceInline(MethodTable *pInterface) while (--numInterfaces); // Second scan, looking for the curiously recurring generic scenario - if (pInterface->HasInstantiation() && pInterface->GetInstantiation().ContainsAllOneType(this)) + if (pInterface->HasInstantiation() && !ContainsGenericVariables() && pInterface->GetInstantiation().ContainsAllOneType(this)) { numInterfaces = GetNumInterfaces(); pInfo = GetInterfaceMap(); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 89926ffdaae..154c642cf40 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -9101,8 +9101,13 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) MethodTable **pExactMTs = (MethodTable**) _alloca(sizeof(MethodTable *) * nInterfacesCount); BOOL duplicates; bool retry = false; - bool retryWithExactInterfaces = !pMT->IsValueType() || pMT->IsSharedByGenericInstantiations(); // Always use exact loading behavior with classes or shared generics, as they have to deal with inheritance, and the - // inexact matching logic for classes would be more complex to write. + + // Always use exact loading behavior with classes or shared generics, as they have to deal with inheritance, and the + // inexact matching logic for classes would be more complex to write. + // Also always use the exact loading behavior with any generic that contains generic variables, as the open type is used + // to represent a type instantiated over its own generic variables, and the special marker type is currently the open type + // and we make this case distinguishable by simply disallowing the optimization in those cases. + bool retryWithExactInterfaces = !pMT->IsValueType() || pMT->IsSharedByGenericInstantiations() || pMT->ContainsGenericVariables(); DWORD nAssigned = 0; do @@ -9132,7 +9137,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) (const Substitution*)0, retryWithExactInterfaces ? NULL : pMT).GetMethodTable(); - bool uninstGenericCase = pNewIntfMT->IsSpecialMarkerTypeForGenericCasting(); + bool uninstGenericCase = !retryWithExactInterfaces && pNewIntfMT->IsSpecialMarkerTypeForGenericCasting(); duplicates |= InsertMethodTable(pNewIntfMT, pExactMTs, nInterfacesCount, &nAssigned); diff --git a/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.cs b/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.cs new file mode 100644 index 00000000000..e8a1139f53b --- /dev/null +++ b/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.cs @@ -0,0 +1,23 @@ +namespace CuriouslyRecurringPatternThroughInterface +{ + interface IGeneric + { + } + interface ICuriouslyRecurring : IGeneric> + { + } + class CuriouslyRecurringThroughInterface : ICuriouslyRecurring + { + } + + class Program + { + static object _o; + static int Main(string[] args) + { + // Test that the a generic using a variant of the curiously recurring pattern involving an interface can be loaded. + _o = typeof(CuriouslyRecurringThroughInterface); + return 100; + } + } +} \ No newline at end of file diff --git a/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.csproj b/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.csproj new file mode 100644 index 00000000000..b566f023697 --- /dev/null +++ b/src/tests/Loader/classloader/generics/Instantiation/Positive/CuriouslyRecurringThroughInterface.csproj @@ -0,0 +1,11 @@ + + + true + Exe + BuildAndRun + 1 + + + + + \ No newline at end of file From b1a8e3ed946f07297716dbde2456571407875dfd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 13 Jul 2021 15:26:47 -0700 Subject: [PATCH 516/926] Do PGO restore as part of PGO path lookup. (#55584) --- src/coreclr/build-runtime.cmd | 27 +++++++-------------------- src/coreclr/build-runtime.sh | 21 +++++++-------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd index 731c255468e..ab805f370aa 100644 --- a/src/coreclr/build-runtime.cmd +++ b/src/coreclr/build-runtime.cmd @@ -246,10 +246,6 @@ if NOT "%__BuildType%"=="Release" ( set __PgoOptimize=0 ) -if %__PgoOptimize%==0 ( - set __RestoreOptData=0 -) - set "__BinDir=%__RootBinDir%\bin\coreclr\%__TargetOS%.%__BuildArch%.%__BuildType%" set "__IntermediatesDir=%__RootBinDir%\obj\coreclr\%__TargetOS%.%__BuildArch%.%__BuildType%" set "__LogsDir=%__RootBinDir%\log\!__BuildType!" @@ -335,29 +331,20 @@ REM === Restore optimization profile data REM === REM ========================================================================================= -set OptDataProjectFilePath=%__ProjectDir%\.nuget\optdata\optdata.csproj -if %__RestoreOptData% EQU 1 ( - echo %__MsgPrefix%Restoring the OptimizationData Package - set "__BinLog=\"%__LogsDir%\OptRestore_%__TargetOS%__%__BuildArch%__%__BuildType%.binlog\"" - powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__RepoRootDir%\eng\common\msbuild.ps1" /clp:nosummary %__ArcadeScriptArgs%^ - "%OptDataProjectFilePath%" /t:Restore^ - %__CommonMSBuildArgs% %__UnprocessedBuildArgs%^ - /nodereuse:false /bl:!__BinLog! - if not !errorlevel! == 0 ( - set __exitCode=!errorlevel! - echo %__ErrMsgPrefix%%__MsgPrefix%Error: Failed to restore the optimization data package. - goto ExitWithCode - ) -) set __PgoOptDataPath= if %__PgoOptimize% EQU 1 ( + set OptDataProjectFilePath=%__ProjectDir%\.nuget\optdata\optdata.csproj + set __OptDataRestoreArg= + if %__RestoreOptData% EQU 1 ( + set __OptDataRestoreArg=/restore + ) set PgoDataPackagePathOutputFile=%__IntermediatesDir%\optdatapath.txt set "__BinLog=\"%__LogsDir%\PgoVersionRead_%__TargetOS%__%__BuildArch%__%__BuildType%.binlog\"" REM Parse the optdata package versions out of msbuild so that we can pass them on to CMake powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__RepoRootDir%\eng\common\msbuild.ps1" /clp:nosummary %__ArcadeScriptArgs%^ - "%OptDataProjectFilePath%" /t:DumpPgoDataPackagePath^ - /p:PgoDataPackagePathOutputFile="!PgoDataPackagePathOutputFile!"^ + "!OptDataProjectFilePath!" /t:DumpPgoDataPackagePath^ + /p:PgoDataPackagePathOutputFile="!PgoDataPackagePathOutputFile!" !__OptDataRestoreArg!^ %__CommonMSBuildArgs% %__UnprocessedBuildArgs% /bl:!__BinLog! if not !errorlevel! == 0 ( diff --git a/src/coreclr/build-runtime.sh b/src/coreclr/build-runtime.sh index 0b39613aac3..1d9881f281b 100755 --- a/src/coreclr/build-runtime.sh +++ b/src/coreclr/build-runtime.sh @@ -39,27 +39,20 @@ setup_dirs_local() restore_optdata() { local OptDataProjectFilePath="$__ProjectRoot/.nuget/optdata/optdata.csproj" - if [[ "$__SkipRestoreOptData" == 0 && "$__IsMSBuildOnNETCoreSupported" == 1 ]]; then - echo "Restoring the OptimizationData package" - "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs \ - $OptDataProjectFilePath /t:Restore /m \ - -bl:"$__LogsDir/OptRestore_$__ConfigTriplet.binlog" \ - $__CommonMSBuildArgs $__UnprocessedBuildArgs \ - /nodereuse:false - local exit_code="$?" - if [[ "$exit_code" != 0 ]]; then - echo "${__ErrMsgPrefix}Failed to restore the optimization data package." - exit "$exit_code" - fi - fi if [[ "$__PgoOptimize" == 1 && "$__IsMSBuildOnNETCoreSupported" == 1 ]]; then # Parse the optdata package versions out of msbuild so that we can pass them on to CMake local PgoDataPackagePathOutputFile="${__IntermediatesDir}/optdatapath.txt" + local RestoreArg="" + + if [[ "$__SkipRestoreOptData" == "0" ]]; then + RestoreArg="/restore" + fi + # Writes into ${PgoDataPackagePathOutputFile} - "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs $OptDataProjectFilePath /t:DumpPgoDataPackagePath \ + "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs $OptDataProjectFilePath $RestoreArg /t:DumpPgoDataPackagePath \ ${__CommonMSBuildArgs} /p:PgoDataPackagePathOutputFile=${PgoDataPackagePathOutputFile} \ -bl:"$__LogsDir/PgoVersionRead_$__ConfigTriplet.binlog" > /dev/null 2>&1 local exit_code="$?" From f9076c7e3671c8b720d031ce541452a37bf56b95 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 13 Jul 2021 16:33:32 -0600 Subject: [PATCH 517/926] Reduce subtest count in Reflection (#55537) * Reduce reflection subtest count * typo --- .../tests/System/Reflection/ModuleTests.cs | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs index 29fe532e6c0..8c42d79063f 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs @@ -242,21 +242,20 @@ namespace System.Reflection.Tests AssertExtensions.SequenceEqual(new[]{ "TestMethodFoo", "TestMethodFoo", "TestMethodBar" }, methodNames ); } - public static IEnumerable Types => - Module.GetTypes().Select(t => new object[] { t }); + public static IEnumerable Types => Module.GetTypes(); - [Theory] - [MemberData(nameof(Types))] - public void ResolveType(Type t) + [Fact] + public void ResolveTypes() { - Assert.Equal(t, Module.ResolveType(t.MetadataToken)); + foreach(Type t in Types) + Assert.Equal(t, Module.ResolveType(t.MetadataToken)); } public static IEnumerable BadResolveTypes => new[] { new object[] { 1234 }, - new object[] { typeof(ModuleTests).GetMethod("ResolveType").MetadataToken }, + new object[] { typeof(ModuleTests).GetMethod("ResolveTypes").MetadataToken }, } .Union(NullTokens); @@ -270,14 +269,14 @@ namespace System.Reflection.Tests }); } - public static IEnumerable Methods => - typeof(ModuleTests).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly).Select(m => new object[] { m }); + public static IEnumerable Methods => + typeof(ModuleTests).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); - [Theory] - [MemberData(nameof(Methods))] - public void ResolveMethod(MethodInfo t) + [Fact] + public void ResolveMethodsByMethodInfo() { - Assert.Equal(t, Module.ResolveMethod(t.MetadataToken)); + foreach(MethodInfo mi in Methods) + Assert.Equal(mi, Module.ResolveMethod(mi.MetadataToken)); } public static IEnumerable BadResolveMethods => @@ -299,15 +298,15 @@ namespace System.Reflection.Tests }); } - public static IEnumerable Fields => - typeof(ModuleTests).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly).Select(f => new object[] { f }); + public static IEnumerable Fields => + typeof(ModuleTests).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); - [Theory] - [MemberData(nameof(Fields))] + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/52072", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public void ResolveField(FieldInfo t) + public void ResolveFieldsByFieldInfo() { - Assert.Equal(t, Module.ResolveField(t.MetadataToken)); + foreach(FieldInfo fi in Fields) + Assert.Equal(fi, Module.ResolveField(fi.MetadataToken)); } public static IEnumerable BadResolveFields => @@ -348,13 +347,25 @@ namespace System.Reflection.Tests }); } - [Theory] - [MemberData(nameof(Types))] - [MemberData(nameof(Methods))] - [MemberData(nameof(Fields))] - public void ResolveMember(MemberInfo member) + [Fact] + public void ResolveTypesByMemberInfo() { - Assert.Equal(member, Module.ResolveMember(member.MetadataToken)); + foreach(MemberInfo mi in Types) + Assert.Equal(mi, Module.ResolveMember(mi.MetadataToken)); + } + + [Fact] + public void ResolveMethodsByMemberInfo() + { + foreach (MemberInfo mi in Methods) + Assert.Equal(mi, Module.ResolveMember(mi.MetadataToken)); + } + + [Fact] + public void ResolveFieldsByMemberInfo() + { + foreach (MemberInfo mi in Fields) + Assert.Equal(mi, Module.ResolveMember(mi.MetadataToken)); } [Fact] From 97202193214ef74ff7d9518047be61c29e012ce7 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 13 Jul 2021 17:40:28 -0500 Subject: [PATCH 518/926] Add property ordering feature (#55586) --- .../System.Text.Json/ref/System.Text.Json.cs | 6 ++ .../src/System.Text.Json.csproj | 3 +- .../Attributes/JsonPropertyOrderAttribute.cs | 28 ++++++++ .../Metadata/JsonPropertyInfo.cs | 11 +++ .../Serialization/Metadata/JsonTypeInfo.cs | 11 +++ .../Serialization/PropertyOrderTests.cs | 68 +++++++++++++++++++ .../System.Text.Json.Tests.csproj | 1 + 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonPropertyOrderAttribute.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 26b72dd6493..97e39311a09 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -832,6 +832,12 @@ namespace System.Text.Json.Serialization public JsonPropertyNameAttribute(string name) { } public string Name { get { throw null; } } } + [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = false)] + public sealed partial class JsonPropertyOrderAttribute : System.Text.Json.Serialization.JsonAttribute + { + public JsonPropertyOrderAttribute(int order) { } + public int Order { get { throw null; } } + } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true)] public sealed partial class JsonSerializableAttribute : System.Text.Json.Serialization.JsonAttribute { diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 3206cbd6de1..08855731abe 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -22,7 +22,7 @@ - + @@ -89,6 +89,7 @@ + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonPropertyOrderAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonPropertyOrderAttribute.cs new file mode 100644 index 00000000000..8126e75690f --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonPropertyOrderAttribute.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization +{ + /// + /// Specifies the property order that is present in the JSON when serializing. Lower values are serialized first. + /// If the attribute is not specified, the default value is 0. + /// + /// If multiple properties have the same value, the ordering is undefined between them. + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class JsonPropertyOrderAttribute : JsonAttribute + { + /// + /// Initializes a new instance of with the specified order. + /// + /// The order of the property. + public JsonPropertyOrderAttribute(int order) + { + Order = order; + } + + /// + /// The serialization order of the property. + /// + public int Order { get; } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs index 95081b8519d..9e34cc60a35 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs @@ -74,6 +74,12 @@ namespace System.Text.Json.Serialization.Metadata DeterminePropertyName(); DetermineIgnoreCondition(ignoreCondition); + JsonPropertyOrderAttribute? orderAttr = GetAttribute(MemberInfo); + if (orderAttr != null) + { + Order = orderAttr.Order; + } + JsonNumberHandlingAttribute? attribute = GetAttribute(MemberInfo); DetermineNumberHandlingForProperty(attribute?.Handling, declaringTypeNumberHandling); } @@ -366,6 +372,11 @@ namespace System.Text.Json.Serialization.Metadata internal JsonSerializerOptions Options { get; set; } = null!; // initialized in Init method + /// + /// The property order. + /// + internal int Order { get; set; } + internal bool ReadJsonAndAddExtensionProperty( object obj, ref ReadStack state, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index ba90400e246..077de6d0537 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -197,6 +197,8 @@ namespace System.Text.Json.Serialization.Metadata PropertyInfo[] properties = type.GetProperties(bindingFlags); + bool propertyOrderSpecified = false; + // PropertyCache is not accessed by other threads until the current JsonTypeInfo instance // is finished initializing and added to the cache on JsonSerializerOptions. // Default 'capacity' to the common non-polymorphic + property case. @@ -229,6 +231,7 @@ namespace System.Text.Json.Serialization.Metadata propertyInfo, isVirtual, typeNumberHandling, + ref propertyOrderSpecified, ref ignoredMembers); } else @@ -263,6 +266,7 @@ namespace System.Text.Json.Serialization.Metadata fieldInfo, isVirtual: false, typeNumberHandling, + ref propertyOrderSpecified, ref ignoredMembers); } } @@ -286,6 +290,11 @@ namespace System.Text.Json.Serialization.Metadata properties = currentType.GetProperties(bindingFlags); }; + if (propertyOrderSpecified) + { + PropertyCache.List.Sort((p1, p2) => p1.Value!.Order.CompareTo(p2.Value!.Order)); + } + if (converter.ConstructorIsParameterized) { InitializeConstructorParameters(converter.ConstructorInfo!); @@ -327,6 +336,7 @@ namespace System.Text.Json.Serialization.Metadata MemberInfo memberInfo, bool isVirtual, JsonNumberHandling? typeNumberHandling, + ref bool propertyOrderSpecified, ref Dictionary? ignoredMembers) { bool hasExtensionAttribute = memberInfo.GetCustomAttribute(typeof(JsonExtensionDataAttribute)) != null; @@ -347,6 +357,7 @@ namespace System.Text.Json.Serialization.Metadata else { CacheMember(jsonPropertyInfo, PropertyCache, ref ignoredMembers); + propertyOrderSpecified |= jsonPropertyInfo.Order != 0; } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs new file mode 100644 index 00000000000..07b16caa00d --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public static class PropertyOrderTests + { + private class MyPoco_BeforeAndAfter + { + public int B { get; set; } + + [JsonPropertyOrder(1)] + public int A { get; set; } + + [JsonPropertyOrder(-1)] + public int C { get; set; } + } + + [Fact] + public static void BeforeAndAfterDefaultOrder() + { + string json = JsonSerializer.Serialize(new MyPoco_BeforeAndAfter()); + Assert.Equal("{\"C\":0,\"B\":0,\"A\":0}", json); + } + + private class MyPoco_After + { + [JsonPropertyOrder(2)] + public int C { get; set; } + + public int B { get; set; } + public int D { get; set; } + + [JsonPropertyOrder(1)] + public int A { get; set; } + } + + [Fact] + public static void AfterDefaultOrder() + { + string json = JsonSerializer.Serialize(new MyPoco_After()); + Assert.EndsWith("\"A\":0,\"C\":0}", json); + // Order of B and D are not defined except they come before A and C + } + + private class MyPoco_Before + { + [JsonPropertyOrder(-1)] + public int C { get; set; } + + public int B { get; set; } + public int D { get; set; } + + [JsonPropertyOrder(-2)] + public int A { get; set; } + } + + [Fact] + public static void BeforeDefaultOrder() + { + string json = JsonSerializer.Serialize(new MyPoco_Before()); + Assert.StartsWith("{\"A\":0,\"C\":0", json); + // Order of B and D are not defined except they come after A and C + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 20d919ca164..9e117638ddb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -166,6 +166,7 @@ + From ccfe21882e4a2206ce49cd5b32d3eb3cab3e530f Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 13 Jul 2021 16:44:57 -0700 Subject: [PATCH 519/926] Consume DistributedContextPropagator in DiagnosticsHandler (#55392) --- .../tests/PropagatorTests.cs | 8 + .../System.Net.Http/ref/System.Net.Http.cs | 2 + .../ref/System.Net.Http.csproj | 1 + .../BrowserHttpHandler/SocketsHttpHandler.cs | 10 +- .../src/System/Net/Http/DiagnosticsHandler.cs | 127 ++++++----- .../Http/DiagnosticsHandlerLoggingStrings.cs | 6 - .../src/System/Net/Http/HttpClientHandler.cs | 35 +-- .../src/System/Net/Http/HttpRequestMessage.cs | 12 +- .../HttpConnectionSettings.cs | 4 + .../SocketsHttpHandler/RedirectHandler.cs | 2 + .../SocketsHttpHandler/SocketsHttpHandler.cs | 23 ++ .../tests/FunctionalTests/DiagnosticsTests.cs | 209 ++++++++++++++---- 12 files changed, 308 insertions(+), 131 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs index 2a914f62ad5..5935ad4770e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/PropagatorTests.cs @@ -527,6 +527,14 @@ namespace System.Diagnostics.Tests return list; } + [Fact] + public void TestBuiltInPropagatorsAreCached() + { + Assert.Same(DistributedContextPropagator.CreateDefaultPropagator(), DistributedContextPropagator.CreateDefaultPropagator()); + Assert.Same(DistributedContextPropagator.CreateNoOutputPropagator(), DistributedContextPropagator.CreateNoOutputPropagator()); + Assert.Same(DistributedContextPropagator.CreatePassThroughPropagator(), DistributedContextPropagator.CreatePassThroughPropagator()); + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void TestCustomPropagator() { diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index 3cb29a543b0..ef465d5a3d0 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -394,6 +394,8 @@ namespace System.Net.Http public bool EnableMultipleHttp2Connections { get { throw null; } set { } } public Func>? ConnectCallback { get { throw null; } set { } } public Func>? PlaintextStreamFilter { get { throw null; } set { } } + [System.CLSCompliantAttribute(false)] + public System.Diagnostics.DistributedContextPropagator? ActivityHeadersPropagator { get { throw null; } set { } } } public sealed class SocketsHttpConnectionContext { diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.csproj b/src/libraries/System.Net.Http/ref/System.Net.Http.csproj index ae6a8158fa0..1b20e03c790 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.csproj @@ -14,5 +14,6 @@ + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 4805010002b..1cd0dbdb7a6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -3,13 +3,12 @@ using System.Collections.Generic; using System.IO; -using System.Net.Quic; -using System.Net.Quic.Implementations; using System.Net.Security; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; namespace System.Net.Http { @@ -173,6 +172,13 @@ namespace System.Net.Http set => throw new PlatformNotSupportedException(); } + [CLSCompliant(false)] + public DistributedContextPropagator? ActivityHeadersPropagator + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + protected internal override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index 21b161f9fbb..75954afccab 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -13,36 +13,59 @@ namespace System.Net.Http /// /// DiagnosticHandler notifies DiagnosticSource subscribers about outgoing Http requests /// - internal sealed class DiagnosticsHandler : DelegatingHandler + internal sealed class DiagnosticsHandler : HttpMessageHandlerStage { private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName); - /// - /// DiagnosticHandler constructor - /// - /// Inner handler: Windows or Unix implementation of HttpMessageHandler. - /// Note that DiagnosticHandler is the latest in the pipeline - public DiagnosticsHandler(HttpMessageHandler innerHandler) : base(innerHandler) + private readonly HttpMessageHandler _innerHandler; + private readonly DistributedContextPropagator _propagator; + private readonly HeaderDescriptor[]? _propagatorFields; + + public DiagnosticsHandler(HttpMessageHandler innerHandler, DistributedContextPropagator propagator, bool autoRedirect = false) { + Debug.Assert(IsGloballyEnabled()); + Debug.Assert(innerHandler is not null && propagator is not null); + + _innerHandler = innerHandler; + _propagator = propagator; + + // Prepare HeaderDescriptors for fields we need to clear when following redirects + if (autoRedirect && _propagator.Fields is IReadOnlyCollection fields && fields.Count > 0) + { + var fieldDescriptors = new List(fields.Count); + foreach (string field in fields) + { + if (field is not null && HeaderDescriptor.TryGet(field, out HeaderDescriptor descriptor)) + { + fieldDescriptors.Add(descriptor); + } + } + _propagatorFields = fieldDescriptors.ToArray(); + } } - internal static bool IsEnabled() + private static bool IsEnabled() { - // check if there is a parent Activity (and propagation is not suppressed) - // or if someone listens to HttpHandlerDiagnosticListener - return IsGloballyEnabled() && (Activity.Current != null || s_diagnosticListener.IsEnabled()); + // check if there is a parent Activity or if someone listens to HttpHandlerDiagnosticListener + return Activity.Current != null || s_diagnosticListener.IsEnabled(); } internal static bool IsGloballyEnabled() => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation; - // SendAsyncCore returns already completed ValueTask for when async: false is passed. - // Internally, it calls the synchronous Send method of the base class. - protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: false, cancellationToken).AsTask().GetAwaiter().GetResult(); - - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => - SendAsyncCore(request, async: true, cancellationToken).AsTask(); + internal override ValueTask SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + { + if (IsEnabled()) + { + return SendAsyncCore(request, async, cancellationToken); + } + else + { + return async ? + new ValueTask(_innerHandler.SendAsync(request, cancellationToken)) : + new ValueTask(_innerHandler.Send(request, cancellationToken)); + } + } private async ValueTask SendAsyncCore(HttpRequestMessage request, bool async, CancellationToken cancellationToken) @@ -58,6 +81,16 @@ namespace System.Net.Http throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } + // Since we are reusing the request message instance on redirects, clear any existing headers + // Do so before writing DiagnosticListener events as instrumentations use those to inject headers + if (request.WasRedirected() && _propagatorFields is HeaderDescriptor[] fields) + { + foreach (HeaderDescriptor field in fields) + { + request.Headers.Remove(field); + } + } + Activity? activity = null; DiagnosticListener diagnosticListener = s_diagnosticListener; @@ -72,8 +105,8 @@ namespace System.Net.Http try { return async ? - await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : - base.Send(request, cancellationToken); + await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false) : + _innerHandler.Send(request, cancellationToken); } finally { @@ -119,8 +152,8 @@ namespace System.Net.Http try { response = async ? - await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : - base.Send(request, cancellationToken); + await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false) : + _innerHandler.Send(request, cancellationToken); return response; } catch (OperationCanceledException) @@ -170,6 +203,16 @@ namespace System.Net.Http } } + protected override void Dispose(bool disposing) + { + if (disposing) + { + _innerHandler.Dispose(); + } + + base.Dispose(disposing); + } + #region private private sealed class ActivityStartData @@ -269,42 +312,18 @@ namespace System.Net.Http public override string ToString() => $"{{ {nameof(Response)} = {Response}, {nameof(LoggingRequestId)} = {LoggingRequestId}, {nameof(Timestamp)} = {Timestamp}, {nameof(RequestTaskStatus)} = {RequestTaskStatus} }}"; } - private static void InjectHeaders(Activity currentActivity, HttpRequestMessage request) + private void InjectHeaders(Activity currentActivity, HttpRequestMessage request) { - if (currentActivity.IdFormat == ActivityIdFormat.W3C) + _propagator.Inject(currentActivity, request, static (carrier, key, value) => { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName)) + if (carrier is HttpRequestMessage request && + key is not null && + HeaderDescriptor.TryGet(key, out HeaderDescriptor descriptor) && + !request.Headers.TryGetHeaderValue(descriptor, out _)) { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceParentHeaderName, currentActivity.Id); - if (currentActivity.TraceStateString != null) - { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.TraceStateHeaderName, currentActivity.TraceStateString); - } + request.Headers.TryAddWithoutValidation(descriptor, value); } - } - else - { - if (!request.Headers.Contains(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName)) - { - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.RequestIdHeaderName, currentActivity.Id); - } - } - - // we expect baggage to be empty or contain a few items - using (IEnumerator> e = currentActivity.Baggage.GetEnumerator()) - { - if (e.MoveNext()) - { - var baggage = new List(); - do - { - KeyValuePair item = e.Current; - baggage.Add(new NameValueHeaderValue(WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)).ToString()); - } - while (e.MoveNext()); - request.Headers.TryAddWithoutValidation(DiagnosticsHandlerLoggingStrings.CorrelationContextHeaderName, baggage); - } - } + }); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs index 0fa57394c1c..cd91daaed3c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandlerLoggingStrings.cs @@ -15,11 +15,5 @@ namespace System.Net.Http public const string ExceptionEventName = "System.Net.Http.Exception"; public const string ActivityName = "System.Net.Http.HttpRequestOut"; public const string ActivityStartName = "System.Net.Http.HttpRequestOut.Start"; - - public const string RequestIdHeaderName = "Request-Id"; - public const string CorrelationContextHeaderName = "Correlation-Context"; - - public const string TraceParentHeaderName = "traceparent"; - public const string TraceStateHeaderName = "tracestate"; } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 2e3289643cf..fce5166f279 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -9,6 +9,7 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using System.Diagnostics; #if TARGET_BROWSER using HttpHandlerType = System.Net.Http.BrowserHttpHandler; #else @@ -20,7 +21,14 @@ namespace System.Net.Http public partial class HttpClientHandler : HttpMessageHandler { private readonly HttpHandlerType _underlyingHandler; - private readonly DiagnosticsHandler? _diagnosticsHandler; + + private HttpMessageHandler Handler +#if TARGET_BROWSER + { get; } +#else + => _underlyingHandler; +#endif + private ClientCertificateOption _clientCertificateOptions; private volatile bool _disposed; @@ -28,10 +36,15 @@ namespace System.Net.Http public HttpClientHandler() { _underlyingHandler = new HttpHandlerType(); + +#if TARGET_BROWSER + Handler = _underlyingHandler; if (DiagnosticsHandler.IsGloballyEnabled()) { - _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); + Handler = new DiagnosticsHandler(Handler, DistributedContextPropagator.Current); } +#endif + ClientCertificateOptions = ClientCertificateOption.Manual; } @@ -288,21 +301,11 @@ namespace System.Net.Http public IDictionary Properties => _underlyingHandler.Properties; [UnsupportedOSPlatform("browser")] - protected internal override HttpResponseMessage Send(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.Send(request, cancellationToken) : - _underlyingHandler.Send(request, cancellationToken); - } + protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => + Handler.Send(request, cancellationToken); - protected internal override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return DiagnosticsHandler.IsEnabled() && _diagnosticsHandler != null ? - _diagnosticsHandler.SendAsync(request, cancellationToken) : - _underlyingHandler.SendAsync(request, cancellationToken); - } + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => + Handler.SendAsync(request, cancellationToken); // lazy-load the validator func so it can be trimmed by the ILLinker if it isn't used. private static Func? s_dangerousAcceptAnyServerCertificateValidator; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs index d0c88d60f77..daeda85fff7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs @@ -15,6 +15,7 @@ namespace System.Net.Http private const int MessageNotYetSent = 0; private const int MessageAlreadySent = 1; + private const int MessageIsRedirect = 2; // Track whether the message has been sent. // The message shouldn't be sent again if this field is equal to MessageAlreadySent. @@ -159,12 +160,13 @@ namespace System.Net.Http return sb.ToString(); } - internal bool MarkAsSent() - { - return Interlocked.Exchange(ref _sendStatus, MessageAlreadySent) == MessageNotYetSent; - } + internal bool MarkAsSent() => Interlocked.CompareExchange(ref _sendStatus, MessageAlreadySent, MessageNotYetSent) == MessageNotYetSent; - internal bool WasSentByHttpClient() => _sendStatus == MessageAlreadySent; + internal bool WasSentByHttpClient() => (_sendStatus & MessageAlreadySent) != 0; + + internal void MarkAsRedirected() => _sendStatus |= MessageIsRedirect; + + internal bool WasRedirected() => (_sendStatus & MessageIsRedirect) != 0; #region IDisposable Members diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs index 03cb3e9a406..a24a6403b29 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs @@ -8,6 +8,7 @@ using System.Net.Quic.Implementations; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; +using System.Diagnostics; namespace System.Net.Http { @@ -47,6 +48,8 @@ namespace System.Net.Http internal HeaderEncodingSelector? _requestHeaderEncodingSelector; internal HeaderEncodingSelector? _responseHeaderEncodingSelector; + internal DistributedContextPropagator? _activityHeadersPropagator = DistributedContextPropagator.Current; + internal Version _maxHttpVersion; internal SslClientAuthenticationOptions? _sslOptions; @@ -119,6 +122,7 @@ namespace System.Net.Http _connectCallback = _connectCallback, _plaintextStreamFilter = _plaintextStreamFilter, _initialHttp2StreamWindowSize = _initialHttp2StreamWindowSize, + _activityHeadersPropagator = _activityHeadersPropagator, }; // TODO: Remove if/when QuicImplementationProvider is removed from System.Net.Quic. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs index 856b4e61da3..5d4b4846a37 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs @@ -75,6 +75,8 @@ namespace System.Net.Http } } + request.MarkAsRedirected(); + // Issue the redirected request. response = await _redirectInnerHandler.SendAsync(request, async, cancellationToken).ConfigureAwait(false); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 42361fba080..68fbd071e7d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; using System.Text; +using System.Diagnostics; namespace System.Net.Http { @@ -448,6 +449,22 @@ namespace System.Net.Http } } + /// + /// Gets or sets the to use when propagating the distributed trace and context. + /// Use to disable propagation. + /// Defaults to . + /// + [CLSCompliant(false)] + public DistributedContextPropagator? ActivityHeadersPropagator + { + get => _settings._activityHeadersPropagator; + set + { + CheckDisposedOrStarted(); + _settings._activityHeadersPropagator = value; + } + } + protected override void Dispose(bool disposing) { if (disposing && !_disposed) @@ -478,6 +495,12 @@ namespace System.Net.Http handler = new HttpAuthenticatedConnectionHandler(poolManager); } + // DiagnosticsHandler is inserted before RedirectHandler so that trace propagation is done on redirects as well + if (DiagnosticsHandler.IsGloballyEnabled() && settings._activityHeadersPropagator is DistributedContextPropagator propagator) + { + handler = new DiagnosticsHandler(handler, propagator, settings._allowAutoRedirect); + } + if (settings._allowAutoRedirect) { // Just as with WinHttpHandler, for security reasons, we do not support authentication on redirects diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 1fb6fd925fd..49e4b0a3848 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -256,7 +256,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus status = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Canceled, status); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -308,6 +308,7 @@ namespace System.Net.Http.Functional.Tests parentActivity.AddBaggage("correlationId", Guid.NewGuid().ToString("N").ToString()); parentActivity.AddBaggage("moreBaggage", Guid.NewGuid().ToString("N").ToString()); parentActivity.AddTag("tag", "tag"); // add tag to ensure it is not injected into request + parentActivity.TraceStateString = "Foo"; parentActivity.Start(); @@ -344,7 +345,7 @@ namespace System.Net.Http.Functional.Tests activityStopResponseLogged = GetProperty(kvp.Value, "Response"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -403,13 +404,10 @@ namespace System.Net.Http.Functional.Tests HttpRequestMessage request = GetProperty(kvp.Value, "Request"); Assert.True(request.Headers.TryGetValues("Request-Id", out var requestId)); Assert.True(request.Headers.TryGetValues("Correlation-Context", out var correlationContext)); - Assert.Equal(3, correlationContext.Count()); - Assert.Contains("key=value", correlationContext); - Assert.Contains("bad%2Fkey=value", correlationContext); - Assert.Contains("goodkey=bad%2Fvalue", correlationContext); + Assert.Equal("key=value, goodkey=bad%2Fvalue, bad%2Fkey=value", Assert.Single(correlationContext)); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -467,7 +465,7 @@ namespace System.Net.Http.Functional.Tests Assert.False(request.Headers.TryGetValues("traceparent", out var _)); Assert.False(request.Headers.TryGetValues("tracestate", out var _)); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -519,7 +517,7 @@ namespace System.Net.Http.Functional.Tests } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Stop")) { - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } }); @@ -608,7 +606,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -647,7 +645,7 @@ namespace System.Net.Http.Functional.Tests GetProperty(kvp.Value, "Request"); TaskStatus requestStatus = GetProperty(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.Faulted, requestStatus); - activityStopTcs.SetResult();; + activityStopTcs.SetResult(); } else if (kvp.Key.Equals("System.Net.Http.Exception")) { @@ -796,42 +794,49 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListener() + public static IEnumerable UseSocketsHttpHandler_WithIdFormat_MemberData() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - Activity parent = new Activity("parent").Start(); - - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertHeadersAreInjected(requestData, parent); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + yield return new object[] { true, ActivityIdFormat.Hierarchical }; + yield return new object[] { true, ActivityIdFormat.W3C }; + yield return new object[] { false, ActivityIdFormat.Hierarchical }; + yield return new object[] { false, ActivityIdFormat.W3C }; } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedActivityPropagationWithoutListenerOrParentActivity() + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [MemberData(nameof(UseSocketsHttpHandler_WithIdFormat_MemberData))] + public async Task SendAsync_ExpectedActivityPropagationWithoutListener(bool useSocketsHttpHandler, ActivityIdFormat idFormat) { - RemoteExecutor.Invoke(async (useVersion, testAsync) => - { - await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( - async uri => - { - await GetAsync(useVersion, testAsync, uri); - }, - async server => - { - HttpRequestData requestData = await server.AcceptConnectionSendResponseAndCloseAsync(); - AssertNoHeadersAreInjected(requestData); - }); - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + Activity parent = new Activity("parent"); + parent.SetIdFormat(idFormat); + parent.Start(); + + await GetFactoryForVersion(UseVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(UseVersion.ToString(), TestAsync.ToString(), uri, useSocketsHttpHandler: useSocketsHttpHandler); + }, + async server => + { + HttpRequestData requestData = await server.HandleRequestAsync(); + AssertHeadersAreInjected(requestData, parent); + }); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [InlineData(true)] + [InlineData(false)] + public async Task SendAsync_ExpectedActivityPropagationWithoutListenerOrParentActivity(bool useSocketsHttpHandler) + { + await GetFactoryForVersion(UseVersion).CreateClientAndServerAsync( + async uri => + { + await GetAsync(UseVersion.ToString(), TestAsync.ToString(), uri, useSocketsHttpHandler: useSocketsHttpHandler); + }, + async server => + { + HttpRequestData requestData = await server.HandleRequestAsync(); + AssertNoHeadersAreInjected(requestData); + }); } [ConditionalTheory(nameof(EnableActivityPropagationEnvironmentVariableIsNotSetAndRemoteExecutorSupported))] @@ -877,6 +882,56 @@ namespace System.Net.Http.Functional.Tests }, UseVersion.ToString(), TestAsync.ToString(), envVarValue).Dispose(); } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [MemberData(nameof(UseSocketsHttpHandler_WithIdFormat_MemberData))] + public async Task SendAsync_HeadersAreInjectedOnRedirects(bool useSocketsHttpHandler, ActivityIdFormat idFormat) + { + Activity parent = new Activity("parent"); + parent.SetIdFormat(idFormat); + parent.TraceStateString = "Foo"; + parent.Start(); + + await GetFactoryForVersion(UseVersion).CreateServerAsync(async (originalServer, originalUri) => + { + await GetFactoryForVersion(UseVersion).CreateServerAsync(async (redirectServer, redirectUri) => + { + Task clientTask = GetAsync(UseVersion.ToString(), TestAsync.ToString(), originalUri, useSocketsHttpHandler: useSocketsHttpHandler); + + Task serverTask = originalServer.HandleRequestAsync(HttpStatusCode.Redirect, new[] { new HttpHeaderData("Location", redirectUri.AbsoluteUri) }); + + await Task.WhenAny(clientTask, serverTask); + Assert.False(clientTask.IsCompleted, $"{clientTask.Status}: {clientTask.Exception}"); + HttpRequestData firstRequestData = await serverTask; + AssertHeadersAreInjected(firstRequestData, parent); + + serverTask = redirectServer.HandleRequestAsync(); + await TestHelper.WhenAllCompletedOrAnyFailed(clientTask, serverTask); + HttpRequestData secondRequestData = await serverTask; + AssertHeadersAreInjected(secondRequestData, parent); + + if (idFormat == ActivityIdFormat.W3C) + { + string firstParent = GetHeaderValue(firstRequestData, "traceparent"); + string firstState = GetHeaderValue(firstRequestData, "tracestate"); + Assert.True(ActivityContext.TryParse(firstParent, firstState, out ActivityContext firstContext)); + + string secondParent = GetHeaderValue(secondRequestData, "traceparent"); + string secondState = GetHeaderValue(secondRequestData, "tracestate"); + Assert.True(ActivityContext.TryParse(secondParent, secondState, out ActivityContext secondContext)); + + Assert.Equal(firstContext.TraceId, secondContext.TraceId); + Assert.Equal(firstContext.TraceFlags, secondContext.TraceFlags); + Assert.Equal(firstContext.TraceState, secondContext.TraceState); + Assert.NotEqual(firstContext.SpanId, secondContext.SpanId); + } + else + { + Assert.NotEqual(GetHeaderValue(firstRequestData, "Request-Id"), GetHeaderValue(secondRequestData, "Request-Id")); + } + }); + }); + } + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(true)] [InlineData(false)] @@ -893,12 +948,56 @@ namespace System.Net.Http.Functional.Tests (HttpRequestMessage request, _) = await GetAsync(useVersion, testAsync, uri); string headerName = parent.IdFormat == ActivityIdFormat.Hierarchical ? "Request-Id" : "traceparent"; + Assert.Equal(bool.Parse(switchValue), request.Headers.Contains(headerName)); }, async server => await server.HandleRequestAsync()); }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).Dispose(); } + public static IEnumerable SocketsHttpHandlerPropagators_WithIdFormat_MemberData() + { + foreach (var propagator in new[] { null, DistributedContextPropagator.CreateDefaultPropagator(), DistributedContextPropagator.CreateNoOutputPropagator(), DistributedContextPropagator.CreatePassThroughPropagator() }) + { + foreach (ActivityIdFormat format in new[] { ActivityIdFormat.Hierarchical, ActivityIdFormat.W3C }) + { + yield return new object[] { propagator, format }; + } + } + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [MemberData(nameof(SocketsHttpHandlerPropagators_WithIdFormat_MemberData))] + public async Task SendAsync_CustomSocketsHttpHandlerPropagator_PropagatorIsUsed(DistributedContextPropagator propagator, ActivityIdFormat idFormat) + { + Activity parent = new Activity("parent"); + parent.SetIdFormat(idFormat); + parent.Start(); + + await GetFactoryForVersion(UseVersion).CreateClientAndServerAsync( + async uri => + { + using var handler = new SocketsHttpHandler { ActivityHeadersPropagator = propagator }; + handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; + using var client = new HttpClient(handler); + var request = CreateRequest(HttpMethod.Get, uri, UseVersion, exactVersion: true); + await client.SendAsync(TestAsync, request); + }, + async server => + { + HttpRequestData requestData = await server.HandleRequestAsync(); + + if (propagator is null || ReferenceEquals(propagator, DistributedContextPropagator.CreateNoOutputPropagator())) + { + AssertNoHeadersAreInjected(requestData); + } + else + { + AssertHeadersAreInjected(requestData, parent, ReferenceEquals(propagator, DistributedContextPropagator.CreatePassThroughPropagator())); + } + }); + } + private static T GetProperty(object obj, string propertyName) { Type t = obj.GetType(); @@ -925,7 +1024,7 @@ namespace System.Net.Http.Functional.Tests Assert.Null(GetHeaderValue(request, "Correlation-Context")); } - private static void AssertHeadersAreInjected(HttpRequestData request, Activity parent) + private static void AssertHeadersAreInjected(HttpRequestData request, Activity parent, bool passthrough = false) { string requestId = GetHeaderValue(request, "Request-Id"); string traceparent = GetHeaderValue(request, "traceparent"); @@ -935,7 +1034,7 @@ namespace System.Net.Http.Functional.Tests { Assert.True(requestId != null, "Request-Id was not injected when instrumentation was enabled"); Assert.StartsWith(parent.Id, requestId); - Assert.NotEqual(parent.Id, requestId); + Assert.Equal(passthrough, parent.Id == requestId); Assert.Null(traceparent); Assert.Null(tracestate); } @@ -944,6 +1043,7 @@ namespace System.Net.Http.Functional.Tests Assert.Null(requestId); Assert.True(traceparent != null, "traceparent was not injected when W3C instrumentation was enabled"); Assert.StartsWith($"00-{parent.TraceId.ToHexString()}-", traceparent); + Assert.Equal(passthrough, parent.Id == traceparent); Assert.Equal(parent.TraceStateString, tracestate); } @@ -960,10 +1060,23 @@ namespace System.Net.Http.Functional.Tests } } - private static async Task<(HttpRequestMessage, HttpResponseMessage)> GetAsync(string useVersion, string testAsync, Uri uri, CancellationToken cancellationToken = default) + private static async Task<(HttpRequestMessage, HttpResponseMessage)> GetAsync(string useVersion, string testAsync, Uri uri, CancellationToken cancellationToken = default, bool useSocketsHttpHandler = false) { - HttpClientHandler handler = CreateHttpClientHandler(useVersion); - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + HttpMessageHandler handler; + if (useSocketsHttpHandler) + { + var socketsHttpHandler = new SocketsHttpHandler(); + socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; + handler = socketsHttpHandler; + } + else + { + handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates + }; + } + using var client = new HttpClient(handler); var request = CreateRequest(HttpMethod.Get, uri, Version.Parse(useVersion), exactVersion: true); return (request, await client.SendAsync(bool.Parse(testAsync), request, cancellationToken)); From bf0fdf0a78a1411f5abed5d892dcac2ba21899e1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 13 Jul 2021 20:10:24 -0400 Subject: [PATCH 520/926] Fix Task.WhenAny failure mode when passed ICollection of zero tasks (#55580) --- .../src/System/Threading/Tasks/Task.cs | 8 +++++++- .../System.Threading.Tasks/tests/MethodCoverage.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index a64dc008429..89fab991d73 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -6305,8 +6305,14 @@ namespace System.Threading.Tasks return WhenAny(taskArray); } + int count = taskCollection.Count; + if (count <= 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); + } + int index = 0; - taskArray = new Task[taskCollection.Count]; + taskArray = new Task[count]; foreach (Task task in tasks) { if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); diff --git a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs index e696a38cf92..923875f6e43 100644 --- a/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs +++ b/src/libraries/System.Threading.Tasks/tests/MethodCoverage.cs @@ -188,6 +188,20 @@ namespace TaskCoverage AssertExtensions.Throws("task2", () => Task.WhenAny(Task.FromResult(2), null)); } + [Fact] + public static void Task_WhenAny_NoTasks_Throws() + { + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[0]); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new List()); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(EmptyIterator()); }); + + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[0]); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new List>()); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(EmptyIterator>()); }); + + static IEnumerable EmptyIterator() { yield break; } + } + [Fact] public static async Task Task_WhenAny_TwoTasks_OnePreCompleted() { From 3e8ef855c496b1aa1b884625ca90e5db75457d22 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Wed, 14 Jul 2021 02:10:52 +0200 Subject: [PATCH 521/926] Enable Http2_MultipleConnectionsEnabled_ConnectionLimitNotReached_ConcurrentRequestsSuccessfullyHandled (#55572) * Enable Http2_MultipleConnectionsEnabled_ConnectionLimitNotReached_ConcurrentRequestsSuccessfullyHandled * Update SocketsHttpHandlerTest.cs --- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index b326c8bbb02..1e364ea9665 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2078,7 +2078,6 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandlerTest_Http2(ITestOutputHelper output) : base(output) { } [ConditionalFact(nameof(SupportsAlpn))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/41078")] public async Task Http2_MultipleConnectionsEnabled_ConnectionLimitNotReached_ConcurrentRequestsSuccessfullyHandled() { const int MaxConcurrentStreams = 2; From 69a4f832e5cc7ed449a1be5bbb6d3a720e2f30fa Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 14 Jul 2021 08:11:58 +0800 Subject: [PATCH 522/926] Address System.Net.Http.WinHttpHandler's nullable warnings targeting .NETCoreApp (#54995) * Update nullability for WinHttpHandler * Update project file * Use ! for PtrToStringUni * Move assertion of _sessionHandle * Remove false assertion about RequestHandle. --- .../Windows/WinHttp/Interop.winhttp.cs | 10 +-- .../src/System.Net.Http.WinHttpHandler.csproj | 2 - .../src/System/Net/Http/WinHttpAuthHelper.cs | 49 +++++++---- .../Net/Http/WinHttpCertificateHelper.cs | 4 +- .../System/Net/Http/WinHttpChannelBinding.cs | 4 +- .../Net/Http/WinHttpCookieContainerAdapter.cs | 25 ++++-- .../src/System/Net/Http/WinHttpHandler.cs | 82 +++++++++++++------ .../System/Net/Http/WinHttpRequestCallback.cs | 15 ++-- .../System/Net/Http/WinHttpRequestState.cs | 30 +++---- .../System/Net/Http/WinHttpRequestStream.cs | 1 + .../Net/Http/WinHttpResponseHeaderReader.cs | 3 +- .../System/Net/Http/WinHttpResponseParser.cs | 18 ++-- .../System/Net/Http/WinHttpResponseStream.cs | 6 +- .../src/System/Net/Http/WinHttpTraceHelper.cs | 4 +- .../Net/Http/WinHttpTransportContext.cs | 4 +- 15 files changed, 163 insertions(+), 94 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs index 7ac85b3155b..82a8218338c 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -13,8 +13,8 @@ internal static partial class Interop public static extern SafeWinHttpHandle WinHttpOpen( IntPtr userAgent, uint accessType, - string proxyName, - string proxyBypass, int flags); + string? proxyName, + string? proxyBypass, int flags); [DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] @@ -33,7 +33,7 @@ internal static partial class Interop SafeWinHttpHandle connectHandle, string verb, string objectName, - string version, + string? version, string referrer, string acceptTypes, uint flags); @@ -161,8 +161,8 @@ internal static partial class Interop SafeWinHttpHandle requestHandle, uint authTargets, uint authScheme, - string userName, - string password, + string? userName, + string? password, IntPtr reserved); [DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)] diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj index 525d50608ff..99e54a8c729 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj @@ -10,8 +10,6 @@ SR.PlatformNotSupported_WinHttpHandler - - annotations diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs index f659c9c2f07..b0b6d86c675 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; - +using System.Diagnostics.CodeAnalysis; using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; namespace System.Net.Http @@ -14,7 +14,7 @@ namespace System.Net.Http // WINHTTP_AUTH_SCHEME_NTLM = 0x00000002; // WINHTTP_AUTH_SCHEME_DIGEST = 0x00000008; // WINHTTP_AUTH_SCHEME_NEGOTIATE = 0x00000010; - private static readonly string[] s_authSchemeStringMapping = + private static readonly string?[] s_authSchemeStringMapping = { null, "Basic", @@ -54,6 +54,10 @@ namespace System.Net.Http uint supportedSchemes = 0; uint firstSchemeIgnored = 0; uint authTarget = 0; + + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); + Debug.Assert(state.RequestHandle != null); Uri uri = state.RequestMessage.RequestUri; state.RetryRequest = false; @@ -121,7 +125,7 @@ namespace System.Net.Http state.LastStatusCode = statusCode; // If we don't have any proxy credentials to try, then we end up with 407. - ICredentials proxyCreds = state.Proxy == null ? + ICredentials? proxyCreds = state.Proxy == null ? state.DefaultProxyCredentials : state.Proxy.Credentials; if (proxyCreds == null) @@ -161,6 +165,7 @@ namespace System.Net.Http default: if (state.PreAuthenticate && serverAuthScheme != 0) { + Debug.Assert(state.ServerCredentials != null); SaveServerCredentialsToCache(uri, serverAuthScheme, state.ServerCredentials); } break; @@ -169,6 +174,10 @@ namespace System.Net.Http public void PreAuthenticateRequest(WinHttpRequestState state, uint proxyAuthScheme) { + Debug.Assert(state.RequestHandle != null); + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); + // Set proxy credentials if we have them. // If a proxy authentication challenge was responded to, reset // those credentials before each SendRequest, because the proxy @@ -177,8 +186,9 @@ namespace System.Net.Http // 407-401-407-401- loop. if (proxyAuthScheme != 0) { - ICredentials proxyCredentials; - Uri proxyUri; + ICredentials? proxyCredentials; + Uri? proxyUri; + if (state.Proxy != null) { proxyCredentials = state.Proxy.Credentials; @@ -190,6 +200,9 @@ namespace System.Net.Http proxyUri = state.RequestMessage.RequestUri; } + Debug.Assert(proxyCredentials != null); + Debug.Assert(proxyUri != null); + SetWinHttpCredential( state.RequestHandle, proxyCredentials, @@ -202,7 +215,7 @@ namespace System.Net.Http if (state.PreAuthenticate) { uint authScheme; - NetworkCredential serverCredentials; + NetworkCredential? serverCredentials; if (GetServerCredentialsFromCache( state.RequestMessage.RequestUri, out authScheme, @@ -226,18 +239,18 @@ namespace System.Net.Http public bool GetServerCredentialsFromCache( Uri uri, out uint serverAuthScheme, - out NetworkCredential serverCredentials) + [NotNullWhen(true)] out NetworkCredential? serverCredentials) { serverAuthScheme = 0; serverCredentials = null; - NetworkCredential cred = null; + NetworkCredential? cred = null; lock (_credentialCacheLock) { foreach (uint authScheme in s_authSchemePriorityOrder) { - cred = _credentialCache.GetCredential(uri, s_authSchemeStringMapping[authScheme]); + cred = _credentialCache.GetCredential(uri, s_authSchemeStringMapping[authScheme]!); if (cred != null) { serverAuthScheme = authScheme; @@ -253,10 +266,10 @@ namespace System.Net.Http public void SaveServerCredentialsToCache(Uri uri, uint authScheme, ICredentials serverCredentials) { - string authType = s_authSchemeStringMapping[authScheme]; + string? authType = s_authSchemeStringMapping[authScheme]; Debug.Assert(!string.IsNullOrEmpty(authType)); - NetworkCredential cred = serverCredentials.GetCredential(uri, authType); + NetworkCredential? cred = serverCredentials.GetCredential(uri, authType); if (cred != null) { lock (_credentialCacheLock) @@ -303,15 +316,17 @@ namespace System.Net.Http uint authScheme, uint authTarget) { - string userName; - string password; + string? userName; + string? password; Debug.Assert(credentials != null); Debug.Assert(authScheme != 0); Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); - NetworkCredential networkCredential = credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]); + string? authType = s_authSchemeStringMapping[authScheme]; + Debug.Assert(!string.IsNullOrEmpty(authType)); + NetworkCredential? networkCredential = credentials.GetCredential(uri, authType); if (networkCredential == null) { @@ -367,7 +382,7 @@ namespace System.Net.Http return true; } - private static uint ChooseAuthScheme(uint supportedSchemes, Uri uri, ICredentials credentials) + private static uint ChooseAuthScheme(uint supportedSchemes, Uri? uri, ICredentials? credentials) { if (credentials == null) { @@ -383,9 +398,11 @@ namespace System.Net.Http return 0; } + Debug.Assert(uri != null); + foreach (uint authScheme in s_authSchemePriorityOrder) { - if ((supportedSchemes & authScheme) != 0 && credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]) != null) + if ((supportedSchemes & authScheme) != 0 && credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]!) != null) { return authScheme; } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs index da4baadb359..472116113fa 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -20,7 +21,6 @@ namespace System.Net.Http out X509Chain chain, out SslPolicyErrors sslPolicyErrors) { - chain = null; sslPolicyErrors = SslPolicyErrors.None; // Build the chain. @@ -69,6 +69,8 @@ namespace System.Net.Http Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_ALL & ~Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; + Debug.Assert(chain.SafeHandle != null); + Interop.Crypt32.CERT_CHAIN_POLICY_STATUS status = default; status.cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_STATUS); if (Interop.Crypt32.CertVerifyCertificateChainPolicy( diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs index 8011f2859d0..9d3dbc81453 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs @@ -14,7 +14,7 @@ namespace System.Net.Http internal sealed class WinHttpChannelBinding : ChannelBinding { private int _size; - private string _cachedToString; + private string? _cachedToString; internal WinHttpChannelBinding(SafeWinHttpHandle requestHandle) { @@ -56,7 +56,7 @@ namespace System.Net.Http } } - public override string ToString() + public override string? ToString() { if (_cachedToString == null && !IsInvalid) { diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCookieContainerAdapter.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCookieContainerAdapter.cs index ff0ac8798ae..739995f5e73 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCookieContainerAdapter.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCookieContainerAdapter.cs @@ -15,17 +15,21 @@ namespace System.Net.Http public static void AddResponseCookiesToContainer(WinHttpRequestState state) { - HttpRequestMessage request = state.RequestMessage; - SafeWinHttpHandle requestHandle = state.RequestHandle; - CookieContainer cookieContainer = state.Handler.CookieContainer; + HttpRequestMessage? request = state.RequestMessage; + SafeWinHttpHandle? requestHandle = state.RequestHandle; + Debug.Assert(state.Handler != null); + CookieContainer? cookieContainer = state.Handler.CookieContainer; Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); + Debug.Assert(request != null); + Debug.Assert(requestHandle != null); Debug.Assert(cookieContainer != null); + Debug.Assert(request.RequestUri != null); // Get 'Set-Cookie' headers from response. - char[] buffer = null; + char[]? buffer = null; uint index = 0; - string cookieHeader; + string? cookieHeader; while (WinHttpResponseParser.GetResponseHeader( requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE, ref buffer, ref index, out cookieHeader)) { @@ -44,9 +48,12 @@ namespace System.Net.Http public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri) { - SafeWinHttpHandle requestHandle = state.RequestHandle; + SafeWinHttpHandle? requestHandle = state.RequestHandle; + Debug.Assert(state.Handler != null); Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); + Debug.Assert(state.Handler.CookieContainer != null); + Debug.Assert(requestHandle != null); // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( @@ -64,7 +71,7 @@ namespace System.Net.Http // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. - string cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer); + string? cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( @@ -78,9 +85,9 @@ namespace System.Net.Http } } - public static string GetCookieHeader(Uri uri, CookieContainer cookies) + public static string? GetCookieHeader(Uri uri, CookieContainer cookies) { - string cookieHeader = null; + string? cookieHeader = null; Debug.Assert(cookies != null); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 9ce26b2716d..93781b803cc 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -46,16 +46,16 @@ namespace System.Net.Http private static readonly StringWithQualityHeaderValue s_deflateHeaderValue = new StringWithQualityHeaderValue("deflate"); [ThreadStatic] - private static StringBuilder t_requestHeadersBuilder; + private static StringBuilder? t_requestHeadersBuilder; private readonly object _lockObject = new object(); private bool _doManualDecompressionCheck; - private WinInetProxyHelper _proxyHelper; + private WinInetProxyHelper? _proxyHelper; private bool _automaticRedirection = HttpHandlerDefaults.DefaultAutomaticRedirection; private int _maxAutomaticRedirections = HttpHandlerDefaults.DefaultMaxAutomaticRedirections; private DecompressionMethods _automaticDecompression = HttpHandlerDefaults.DefaultAutomaticDecompression; private CookieUsePolicy _cookieUsePolicy = CookieUsePolicy.UseInternalCookieStoreOnly; - private CookieContainer _cookieContainer; + private CookieContainer? _cookieContainer; private bool _enableMultipleHttp2Connections; private SslProtocols _sslProtocols = SslProtocols.None; // Use most secure protocols available. @@ -64,15 +64,15 @@ namespace System.Net.Http X509Certificate2, X509Chain, SslPolicyErrors, - bool> _serverCertificateValidationCallback; + bool>? _serverCertificateValidationCallback; private bool _checkCertificateRevocationList; private ClientCertificateOption _clientCertificateOption = ClientCertificateOption.Manual; - private X509Certificate2Collection _clientCertificates; // Only create collection when required. - private ICredentials _serverCredentials; + private X509Certificate2Collection? _clientCertificates; // Only create collection when required. + private ICredentials? _serverCredentials; private bool _preAuthenticate; private WindowsProxyUsePolicy _windowsProxyUsePolicy = WindowsProxyUsePolicy.UseWinHttpProxy; - private ICredentials _defaultProxyCredentials; - private IWebProxy _proxy; + private ICredentials? _defaultProxyCredentials; + private IWebProxy? _proxy; private int _maxConnectionsPerServer = int.MaxValue; private TimeSpan _sendTimeout = TimeSpan.FromSeconds(30); private TimeSpan _receiveHeadersTimeout = TimeSpan.FromSeconds(30); @@ -86,10 +86,10 @@ namespace System.Net.Http private int _maxResponseHeadersLength = HttpHandlerDefaults.DefaultMaxResponseHeadersLength; private int _maxResponseDrainSize = 64 * 1024; - private IDictionary _properties; // Only create dictionary when required. + private IDictionary? _properties; // Only create dictionary when required. private volatile bool _operationStarted; private volatile bool _disposed; - private SafeWinHttpHandle _sessionHandle; + private SafeWinHttpHandle? _sessionHandle; private readonly WinHttpAuthHelper _authHelper = new WinHttpAuthHelper(); public WinHttpHandler() @@ -628,8 +628,8 @@ namespace System.Net.Http Task.Factory.StartNew(s => { - var whrs = (WinHttpRequestState)s; - _ = whrs.Handler.StartRequestAsync(whrs); + var whrs = (WinHttpRequestState)s!; + _ = whrs.Handler!.StartRequestAsync(whrs); }, state, CancellationToken.None, @@ -649,7 +649,7 @@ namespace System.Net.Http chunkedMode = WinHttpChunkMode.Manual; } - HttpContent requestContent = requestMessage.Content; + HttpContent? requestContent = requestMessage.Content; if (requestContent != null) { if (requestContent.Headers.ContentLength.HasValue) @@ -697,12 +697,14 @@ namespace System.Net.Http private static void AddRequestHeaders( SafeWinHttpHandle requestHandle, HttpRequestMessage requestMessage, - CookieContainer cookies, + CookieContainer? cookies, DecompressionMethods manuallyProcessedDecompressionMethods) { + Debug.Assert(requestMessage.RequestUri != null); + // Get a StringBuilder to use for creating the request headers. // We cache one in TLS to avoid creating a new one for each request. - StringBuilder requestHeadersBuffer = t_requestHeadersBuilder; + StringBuilder? requestHeadersBuffer = t_requestHeadersBuilder; if (requestHeadersBuffer != null) { requestHeadersBuffer.Clear(); @@ -735,7 +737,7 @@ namespace System.Net.Http // Manually add cookies. if (cookies != null && cookies.Count > 0) { - string cookieHeader = WinHttpCookieContainerAdapter.GetCookieHeader(requestMessage.RequestUri, cookies); + string? cookieHeader = WinHttpCookieContainerAdapter.GetCookieHeader(requestMessage.RequestUri, cookies); if (!string.IsNullOrEmpty(cookieHeader)) { requestHeadersBuffer.AppendLine(cookieHeader); @@ -789,6 +791,8 @@ namespace System.Net.Http if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy) { Debug.Assert(state.Proxy != null); + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); try { state.Proxy.GetProxy(state.RequestMessage.RequestUri); @@ -878,6 +882,11 @@ namespace System.Net.Http private async Task StartRequestAsync(WinHttpRequestState state) { + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); + Debug.Assert(state.Handler != null); + Debug.Assert(state.Tcs != null); + if (state.CancellationToken.IsCancellationRequested) { state.Tcs.TrySetCanceled(state.CancellationToken); @@ -885,11 +894,12 @@ namespace System.Net.Http return; } - Task sendRequestBodyTask = null; - SafeWinHttpHandle connectHandle = null; + Task? sendRequestBodyTask = null; + SafeWinHttpHandle? connectHandle = null; try { EnsureSessionHandleExists(state); + Debug.Assert(_sessionHandle != null); SetEnableHttp2PlusClientCertificate(state.RequestMessage.RequestUri, state.RequestMessage.Version); @@ -904,7 +914,7 @@ namespace System.Net.Http // Try to use the requested version if a known/supported version was explicitly requested. // Otherwise, we simply use winhttp's default. - string httpVersion = null; + string? httpVersion = null; if (state.RequestMessage.Version == HttpVersion.Version10) { httpVersion = "HTTP/1.0"; @@ -940,7 +950,7 @@ namespace System.Net.Http // will have the side-effect of WinHTTP cancelling any pending I/O and accelerating its callbacks // on the handle and thus releasing the awaiting tasks in the loop below. This helps to provide // a more timely, cooperative, cancellation pattern. - using (state.CancellationToken.Register(s => ((WinHttpRequestState)s).RequestHandle.Dispose(), state)) + using (state.CancellationToken.Register(s => ((WinHttpRequestState)s!).RequestHandle!.Dispose(), state)) { do { @@ -1042,8 +1052,10 @@ namespace System.Net.Http } } - private void OpenRequestHandle(WinHttpRequestState state, SafeWinHttpHandle connectHandle, string httpVersion, out WinHttpChunkMode chunkedModeForSend, out SafeWinHttpHandle requestHandle) + private void OpenRequestHandle(WinHttpRequestState state, SafeWinHttpHandle connectHandle, string? httpVersion, out WinHttpChunkMode chunkedModeForSend, out SafeWinHttpHandle requestHandle) { + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); chunkedModeForSend = GetChunkedModeForSend(state.RequestMessage); // Create an HTTP request handle. @@ -1090,7 +1102,7 @@ namespace System.Net.Http // .NET Framework behavior. System.Uri establishes the baseline rules for percent-encoding // of reserved characters. uint flags = Interop.WinHttp.WINHTTP_FLAG_ESCAPE_DISABLE; - if (state.RequestMessage.RequestUri.Scheme == UriScheme.Https) + if (state.RequestMessage!.RequestUri!.Scheme == UriScheme.Https) { flags |= Interop.WinHttp.WINHTTP_FLAG_SECURE; } @@ -1204,6 +1216,10 @@ namespace System.Net.Http private void SetRequestHandleOptions(WinHttpRequestState state) { + Debug.Assert(state.RequestHandle != null); + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); + SetRequestHandleProxyOptions(state); SetRequestHandleDecompressionOptions(state.RequestHandle); SetRequestHandleRedirectionOptions(state.RequestHandle); @@ -1217,6 +1233,10 @@ namespace System.Net.Http private void SetRequestHandleProxyOptions(WinHttpRequestState state) { + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.RequestUri != null); + Debug.Assert(state.RequestHandle != null); + // We've already set the proxy on the session handle if we're using no proxy or default proxy settings. // We only need to change it on the request handle if we have a specific IWebProxy or need to manually // implement Wininet-style auto proxy detection. @@ -1233,14 +1253,15 @@ namespace System.Net.Http { Debug.Assert(state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy); updateProxySettings = true; - if (state.Proxy.IsBypassed(uri)) + + Uri? proxyUri = state.Proxy.IsBypassed(uri) ? null : state.Proxy.GetProxy(uri); + if (proxyUri == null) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; } else { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; - Uri proxyUri = state.Proxy.GetProxy(uri); string proxyString = proxyUri.Scheme + "://" + proxyUri.Authority; proxyInfo.Proxy = Marshal.StringToHGlobalUni(proxyString); } @@ -1373,7 +1394,7 @@ namespace System.Net.Http return; } - X509Certificate2 clientCertificate = null; + X509Certificate2? clientCertificate = null; if (_clientCertificateOption == ClientCertificateOption.Manual) { clientCertificate = CertificateHelper.GetEligibleClientCertificate(ClientCertificates); @@ -1399,6 +1420,8 @@ namespace System.Net.Http private void SetEnableHttp2PlusClientCertificate(Uri requestUri, Version requestVersion) { + Debug.Assert(_sessionHandle != null); + if (requestUri.Scheme != UriScheme.Https || requestVersion != HttpVersion20) { return; @@ -1447,6 +1470,7 @@ namespace System.Net.Http private void SetRequestHandleCredentialsOptions(WinHttpRequestState state) { + Debug.Assert(state.RequestHandle != null); // By default, WinHTTP sets the default credentials policy such that it automatically sends default credentials // (current user's logged on Windows credentials) to a proxy when needed (407 response). It only sends // default credentials to a server (401 response) if the server is considered to be on the Intranet. @@ -1517,6 +1541,7 @@ namespace System.Net.Http private void HandleAsyncException(WinHttpRequestState state, Exception ex) { + Debug.Assert(state.Tcs != null); if (state.CancellationToken.IsCancellationRequested) { // If the exception was due to the cancellation token being canceled, throw cancellation exception. @@ -1608,6 +1633,8 @@ namespace System.Net.Http { lock (state.Lock) { + Debug.Assert(state.RequestHandle != null); + state.Pin(); if (!Interop.WinHttp.WinHttpSendRequest( state.RequestHandle, @@ -1633,6 +1660,9 @@ namespace System.Net.Http private async Task InternalSendRequestBodyAsync(WinHttpRequestState state, WinHttpChunkMode chunkedModeForSend) { + Debug.Assert(state.RequestMessage != null); + Debug.Assert(state.RequestMessage.Content != null); + using (var requestStream = new WinHttpRequestStream(state, chunkedModeForSend)) { await state.RequestMessage.Content.CopyToAsync(requestStream, state.TransportContext).ConfigureAwait(false); @@ -1644,6 +1674,8 @@ namespace System.Net.Http { lock (state.Lock) { + Debug.Assert(state.RequestHandle != null); + if (!Interop.WinHttp.WinHttpReceiveResponse(state.RequestHandle, IntPtr.Zero)) { throw WinHttpException.CreateExceptionUsingLastError(nameof(Interop.WinHttp.WinHttpReceiveResponse)); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs index 3b2212ec42a..f4a8f6eef82 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs @@ -40,7 +40,7 @@ namespace System.Net.Http return; } - WinHttpRequestState state = WinHttpRequestState.FromIntPtr(context); + WinHttpRequestState? state = WinHttpRequestState.FromIntPtr(context); Debug.Assert(state != null, "WinHttpCallback must have a non-null state object"); RequestCallback(handle, state, internetStatus, statusInformation, statusInformationLength); @@ -84,8 +84,7 @@ namespace System.Net.Http return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REDIRECT: - string redirectUriString = Marshal.PtrToStringUni(statusInformation); - var redirectUri = new Uri(redirectUriString); + var redirectUri = new Uri(Marshal.PtrToStringUni(statusInformation)!); OnRequestRedirect(state, redirectUri); return; @@ -192,6 +191,8 @@ namespace System.Net.Http private static void OnRequestRedirect(WinHttpRequestState state, Uri redirectUri) { Debug.Assert(state != null, "OnRequestRedirect: state is null"); + Debug.Assert(state.Handler != null, "OnRequestRedirect: state.Handler is null"); + Debug.Assert(state.RequestMessage != null, "OnRequestRedirect: state.RequestMessage is null"); Debug.Assert(redirectUri != null, "OnRequestRedirect: redirectUri is null"); // If we're manually handling cookies, we need to reset them based on the new URI. @@ -234,6 +235,8 @@ namespace System.Net.Http { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle is null"); + Debug.Assert(state.RequestMessage != null, "OnRequestSendingRequest: state.RequestMessage is null"); + Debug.Assert(state.RequestMessage.RequestUri != null, "OnRequestSendingRequest: state.RequestMessage.RequestUri is null"); if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https) { @@ -280,7 +283,7 @@ namespace System.Net.Http var serverCertificate = new X509Certificate2(certHandle); Interop.Crypt32.CertFreeCertificateContext(certHandle); - X509Chain chain = null; + X509Chain? chain = null; SslPolicyErrors sslPolicyErrors; bool result = false; @@ -391,6 +394,7 @@ namespace System.Net.Http break; case Interop.WinHttp.API_WRITE_DATA: + Debug.Assert(state.TcsInternalWriteDataToRequestStream != null); if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(state, "API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); @@ -414,7 +418,8 @@ namespace System.Net.Http private static void ResetAuthRequestHeaders(WinHttpRequestState state) { const string AuthHeaderNameWithColon = "Authorization:"; - SafeWinHttpHandle requestHandle = state.RequestHandle; + SafeWinHttpHandle? requestHandle = state.RequestHandle; + Debug.Assert(requestHandle != null); // Clear auth headers. if (!Interop.WinHttp.WinHttpAddRequestHeaders( diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs index 40275b5c8d7..366acfa61ad 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestState.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -28,7 +29,7 @@ namespace System.Net.Http // A GCHandle for this operation object. // This is owned by the callback and will be deallocated when the sessionHandle has been closed. private GCHandle _operationHandle; - private WinHttpTransportContext _transportContext; + private WinHttpTransportContext? _transportContext; private volatile bool _disposed; // To detect redundant calls. public WinHttpRequestState() @@ -49,10 +50,10 @@ namespace System.Net.Http } } - public static WinHttpRequestState FromIntPtr(IntPtr gcHandle) + public static WinHttpRequestState? FromIntPtr(IntPtr gcHandle) { GCHandle stateHandle = GCHandle.FromIntPtr(gcHandle); - return (WinHttpRequestState)stateHandle.Target; + return (WinHttpRequestState?)stateHandle.Target; } public IntPtr ToIntPtr() @@ -92,16 +93,16 @@ namespace System.Net.Http } } - public TaskCompletionSource Tcs { get; set; } + public TaskCompletionSource? Tcs { get; set; } public CancellationToken CancellationToken { get; set; } - public HttpRequestMessage RequestMessage { get; set; } + public HttpRequestMessage? RequestMessage { get; set; } - public WinHttpHandler Handler { get; set; } + public WinHttpHandler? Handler { get; set; } - private SafeWinHttpHandle _requestHandle; - public SafeWinHttpHandle RequestHandle + private SafeWinHttpHandle? _requestHandle; + public SafeWinHttpHandle? RequestHandle { get { @@ -120,12 +121,13 @@ namespace System.Net.Http } } - public Exception SavedException { get; set; } + public Exception? SavedException { get; set; } public bool CheckCertificateRevocationList { get; set; } - public Func ServerCertificateValidationCallback { get; set; } + public Func? ServerCertificateValidationCallback { get; set; } + [AllowNull] public WinHttpTransportContext TransportContext { get { return _transportContext ?? (_transportContext = new WinHttpTransportContext()); } @@ -134,11 +136,11 @@ namespace System.Net.Http public WindowsProxyUsePolicy WindowsProxyUsePolicy { get; set; } - public IWebProxy Proxy { get; set; } + public IWebProxy? Proxy { get; set; } - public ICredentials ServerCredentials { get; set; } + public ICredentials? ServerCredentials { get; set; } - public ICredentials DefaultProxyCredentials { get; set; } + public ICredentials? DefaultProxyCredentials { get; set; } public bool PreAuthenticate { get; set; } @@ -147,7 +149,7 @@ namespace System.Net.Http public bool RetryRequest { get; set; } public RendezvousAwaitable LifecycleAwaitable { get; set; } = new RendezvousAwaitable(); - public TaskCompletionSource TcsInternalWriteDataToRequestStream { get; set; } + public TaskCompletionSource? TcsInternalWriteDataToRequestStream { get; set; } public bool AsyncReadInProgress { get; set; } // WinHttpResponseStream state. diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs index 301e5a6a21f..5e79072f44b 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs @@ -33,6 +33,7 @@ namespace System.Net.Http // Take copy of handle from state. // The state's request handle will be set to null once the response stream starts. + Debug.Assert(_state.RequestHandle != null); _requestHandle = _state.RequestHandle; } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseHeaderReader.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseHeaderReader.cs index 02ce3184f8f..f2e3292321c 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseHeaderReader.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseHeaderReader.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Net.Http { @@ -28,7 +29,7 @@ namespace System.Net.Http /// Empty header lines are skipped, as are malformed header lines that are missing a colon character. /// /// true if the next header was read successfully, or false if all characters have been read. - public bool ReadHeader(out string name, out string value) + public bool ReadHeader([NotNullWhen(true)] out string? name, [NotNullWhen(true)] out string? value) { int startIndex; int length; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseParser.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseParser.cs index 9f324ada27a..053091aadbb 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseParser.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseParser.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Net.Http.Headers; @@ -21,8 +22,11 @@ namespace System.Net.Http WinHttpRequestState state, DecompressionMethods manuallyProcessedDecompressionMethods) { - HttpRequestMessage request = state.RequestMessage; - SafeWinHttpHandle requestHandle = state.RequestHandle; + HttpRequestMessage? request = state.RequestMessage; + SafeWinHttpHandle? requestHandle = state.RequestHandle; + Debug.Assert(request != null); + Debug.Assert(requestHandle != null); + var response = new HttpResponseMessage(); bool stripEncodingHeaders = false; @@ -133,9 +137,9 @@ namespace System.Net.Http public static unsafe bool GetResponseHeader( SafeWinHttpHandle requestHandle, uint infoLevel, - ref char[] buffer, + ref char[]? buffer, ref uint index, - out string headerValue) + [NotNullWhen(true)] out string? headerValue) { const int StackLimit = 128; @@ -286,7 +290,7 @@ namespace System.Net.Http // If it's a known reason phrase, use the known reason phrase instead of allocating a new string. - string knownReasonPhrase = HttpStatusDescription.Get(statusCode); + string? knownReasonPhrase = HttpStatusDescription.Get(statusCode); return (knownReasonPhrase != null && knownReasonPhrase.AsSpan().SequenceEqual(buffer.AsSpan(0, bufferLength))) ? knownReasonPhrase : @@ -313,7 +317,7 @@ namespace System.Net.Http reader.ReadLine(); // Parse the array of headers and split them between Content headers and Response headers. - while (reader.ReadHeader(out string headerName, out string headerValue)) + while (reader.ReadHeader(out string? headerName, out string? headerValue)) { if (!responseHeaders.TryAddWithoutValidation(headerName, headerValue)) { @@ -350,7 +354,7 @@ namespace System.Net.Http var reader = new WinHttpResponseHeaderReader(buffer, 0, bufferLength); // Parse the array of headers and split them between Content headers and Response headers. - while (reader.ReadHeader(out string headerName, out string headerValue)) + while (reader.ReadHeader(out string? headerName, out string? headerValue)) { responseTrailers.TryAddWithoutValidation(headerName, headerValue); } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs index d7432541b94..74f0abbbb80 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpResponseStream.cs @@ -113,7 +113,7 @@ namespace System.Net.Http private async Task CopyToAsyncCore(Stream destination, byte[] buffer, CancellationToken cancellationToken) { _state.PinReceiveBuffer(buffer); - CancellationTokenRegistration ctr = cancellationToken.Register(s => ((WinHttpResponseStream)s).CancelPendingResponseStreamReadOperation(), this); + CancellationTokenRegistration ctr = cancellationToken.Register(s => ((WinHttpResponseStream)s!).CancelPendingResponseStreamReadOperation(), this); _state.AsyncReadInProgress = true; try { @@ -223,7 +223,7 @@ namespace System.Net.Http } _state.PinReceiveBuffer(buffer); - var ctr = token.Register(s => ((WinHttpResponseStream)s).CancelPendingResponseStreamReadOperation(), this); + var ctr = token.Register(s => ((WinHttpResponseStream)s!).CancelPendingResponseStreamReadOperation(), this); _state.AsyncReadInProgress = true; try { @@ -330,7 +330,7 @@ namespace System.Net.Http if (_requestHandle != null) { _requestHandle.Dispose(); - _requestHandle = null; + _requestHandle = null!; } } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs index e039d50dad1..096dc180c72 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs @@ -9,7 +9,7 @@ namespace System.Net.Http { internal static class WinHttpTraceHelper { - public static void TraceCallbackStatus(object thisOrContextObject, IntPtr handle, IntPtr context, uint status, [CallerMemberName] string memberName = null) + public static void TraceCallbackStatus(object? thisOrContextObject, IntPtr handle, IntPtr context, uint status, [CallerMemberName] string? memberName = null) { Debug.Assert(NetEventSource.Log.IsEnabled()); @@ -19,7 +19,7 @@ namespace System.Net.Http memberName); } - public static void TraceAsyncError(object thisOrContextObject, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult, [CallerMemberName] string memberName = null) + public static void TraceAsyncError(object thisOrContextObject, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult, [CallerMemberName] string? memberName = null) { Debug.Assert(NetEventSource.Log.IsEnabled()); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs index 1c3d5eea717..ab8ee83c1a3 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs @@ -10,13 +10,13 @@ namespace System.Net.Http { internal sealed class WinHttpTransportContext : TransportContext { - private WinHttpChannelBinding _channelBinding; + private WinHttpChannelBinding? _channelBinding; internal WinHttpTransportContext() { } - public override ChannelBinding GetChannelBinding(ChannelBindingKind kind) + public override ChannelBinding? GetChannelBinding(ChannelBindingKind kind) { // WinHTTP only supports retrieval of ChannelBindingKind.Endpoint for CBT. if (kind == ChannelBindingKind.Endpoint) From 87e98f36c72cfd7c129c85dee8ef727f32f8fd4b Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 13 Jul 2021 20:16:54 -0400 Subject: [PATCH 523/926] Obsolete CryptoConfig.EncodeOID (#55592) --- docs/project/list-of-diagnostics.md | 1 + src/libraries/Common/src/System/Obsoletions.cs | 3 +++ .../ref/System.Security.Cryptography.Algorithms.cs | 1 + .../Security/Cryptography/CryptoConfig.Browser.cs | 1 + .../src/System/Security/Cryptography/CryptoConfig.cs | 1 + .../tests/CryptoConfigTests.cs | 10 ++++++++++ 6 files changed, 17 insertions(+) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 2d168532317..cfb593d70b5 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -85,6 +85,7 @@ The PR that reveals the implementation of the ` throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported); [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.CryptoConfigEncodeOIDMessage, DiagnosticId = Obsoletions.CryptoConfigEncodeOIDDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static byte[] EncodeOID(string str) => throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported); [RequiresUnreferencedCode("The default algorithm implementations might be removed, use strong type references like 'RSA.Create()' instead.")] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CryptoConfig.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CryptoConfig.cs index ff73ce60505..e7d3919b2f3 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CryptoConfig.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/CryptoConfig.cs @@ -508,6 +508,7 @@ namespace System.Security.Cryptography } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.CryptoConfigEncodeOIDMessage, DiagnosticId = Obsoletions.CryptoConfigEncodeOIDDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static byte[] EncodeOID(string str) { if (str == null) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs index 067a50a1001..b7243709440 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs @@ -348,25 +348,30 @@ namespace System.Security.Cryptography.CryptoConfigTests [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void EncodeOID_Validation() { +#pragma warning disable SYSLIB0031 // EncodeOID is obsolete Assert.Throws(() => CryptoConfig.EncodeOID(null)); Assert.Throws(() => CryptoConfig.EncodeOID(string.Empty)); Assert.Throws(() => CryptoConfig.EncodeOID("BAD.OID")); Assert.Throws(() => CryptoConfig.EncodeOID("1.2.BAD.OID")); Assert.Throws(() => CryptoConfig.EncodeOID("1." + uint.MaxValue)); +#pragma warning restore SYSLIB0031 } [Fact] [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void EncodeOID_Compat() { +#pragma warning disable SYSLIB0031 // EncodeOID is obsolete string actual = CryptoConfig.EncodeOID("-1.2.-3").ByteArrayToHex(); Assert.Equal("0602DAFD", actual); // Negative values not checked +#pragma warning restore SYSLIB0031 } [Fact] [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public static void EncodeOID_Length_Boundary() { +#pragma warning disable SYSLIB0031 // EncodeOID is obsolete string valueToRepeat = "1.1"; // Build a string like 1.11.11.11. ... .11.1, which has 0x80 separators. @@ -379,6 +384,7 @@ namespace System.Security.Cryptography.CryptoConfigTests // and would just clutter up this test, so only verify it doesn't throw. s = new StringBuilder(valueToRepeat.Length * 0x7f).Insert(0, valueToRepeat, 0x7f).ToString(); CryptoConfig.EncodeOID(s); +#pragma warning restore SYSLIB0031 } [Theory] @@ -392,9 +398,11 @@ namespace System.Security.Cryptography.CryptoConfigTests { // Boundary cases in EncodeOID; output may produce the wrong value mathematically due to encoding // algorithm semantics but included here for compat reasons. +#pragma warning disable SYSLIB0031 // EncodeOID is obsolete byte[] actual = CryptoConfig.EncodeOID("1." + elementValue.ToString()); byte[] expected = expectedEncoding.HexToByteArray(); Assert.Equal(expected, actual); +#pragma warning restore SYSLIB0031 } [Theory] @@ -404,12 +412,14 @@ namespace System.Security.Cryptography.CryptoConfigTests [InlineData("MD5", "1.2.840.113549.2.5", "06082A864886F70D0205")] public static void MapAndEncodeOID(string alg, string expectedOid, string expectedEncoding) { +#pragma warning disable SYSLIB0031 // EncodeOID is obsolete string oid = CryptoConfig.MapNameToOID(alg); Assert.Equal(expectedOid, oid); byte[] actual = CryptoConfig.EncodeOID(oid); byte[] expected = expectedEncoding.HexToByteArray(); Assert.Equal(expected, actual); +#pragma warning restore SYSLIB0031 } private static void VerifyCreateFromName(string name) From f6eb259db626d563c15ba340feb6f440d1e1c8ee Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 13 Jul 2021 17:34:26 -0700 Subject: [PATCH 524/926] Disable generic math (#55540) --- src/coreclr/clr.featuredefines.props | 2 +- src/libraries/System.Runtime/ref/System.Runtime.csproj | 2 +- src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj | 2 +- src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 08fca8de6dd..184e885470f 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -9,7 +9,7 @@ true true true - true + false true diff --git a/src/libraries/System.Runtime/ref/System.Runtime.csproj b/src/libraries/System.Runtime/ref/System.Runtime.csproj index c978be13789..9cbad9dddb8 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.csproj +++ b/src/libraries/System.Runtime/ref/System.Runtime.csproj @@ -1,7 +1,7 @@ true - true + false v4.0.30319 + + + + + + + + + + diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.Unix.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.Unix.cs new file mode 100644 index 00000000000..d2e4f44a675 --- /dev/null +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.Unix.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.IO.Compression +{ + public static partial class ZipFileExtensions + { + static partial void SetExternalAttributes(FileStream fs, ZipArchiveEntry entry) + { + Interop.Sys.FileStatus status; + Interop.CheckIo(Interop.Sys.FStat(fs.SafeFileHandle, out status), fs.Name); + + entry.ExternalAttributes |= status.Mode << 16; + } + } +} diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.cs index 00d82c242b0..c9199a97f2a 100644 --- a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.cs +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.cs @@ -94,7 +94,7 @@ namespace System.IO.Compression // Argument checking gets passed down to FileStream's ctor and CreateEntry - using (Stream fs = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) + using (FileStream fs = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) { ZipArchiveEntry entry = compressionLevel.HasValue ? destination.CreateEntry(entryName, compressionLevel.Value) @@ -109,11 +109,15 @@ namespace System.IO.Compression entry.LastWriteTime = lastWrite; + SetExternalAttributes(fs, entry); + using (Stream es = entry.Open()) fs.CopyTo(es); return entry; } } + + static partial void SetExternalAttributes(FileStream fs, ZipArchiveEntry entry); } } diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.Unix.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.Unix.cs new file mode 100644 index 00000000000..4da8b87b43e --- /dev/null +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.Unix.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.IO.Compression +{ + public static partial class ZipFileExtensions + { + static partial void ExtractExternalAttributes(FileStream fs, ZipArchiveEntry entry) + { + // Only extract USR, GRP, and OTH file permissions, and ignore + // S_ISUID, S_ISGID, and S_ISVTX bits. This matches unzip's default behavior. + // It is off by default because of this comment: + + // "It's possible that a file in an archive could have one of these bits set + // and, unknown to the person unzipping, could allow others to execute the + // file as the user or group. The new option -K bypasses this check." + const int ExtractPermissionMask = 0x1FF; + int permissions = (entry.ExternalAttributes >> 16) & ExtractPermissionMask; + + // If the permissions weren't set at all, don't write the file's permissions, + // since the .zip could have been made using a previous version of .NET, which didn't + // include the permissions, or was made on Windows. + if (permissions != 0) + { + Interop.CheckIo(Interop.Sys.FChMod(fs.SafeFileHandle, permissions), fs.Name); + } + } + } +} diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs index cad1a12c5a4..a74aca915fa 100644 --- a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; - namespace System.IO.Compression { public static partial class ZipFileExtensions @@ -75,15 +73,19 @@ namespace System.IO.Compression // Rely on FileStream's ctor for further checking destinationFileName parameter FileMode fMode = overwrite ? FileMode.Create : FileMode.CreateNew; - using (Stream fs = new FileStream(destinationFileName, fMode, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false)) + using (FileStream fs = new FileStream(destinationFileName, fMode, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false)) { using (Stream es = source.Open()) es.CopyTo(fs); + + ExtractExternalAttributes(fs, source); } File.SetLastWriteTime(destinationFileName, source.LastWriteTime.DateTime); } + static partial void ExtractExternalAttributes(FileStream fs, ZipArchiveEntry entry); + internal static void ExtractRelativeToDirectory(this ZipArchiveEntry source, string destinationDirectoryName) => ExtractRelativeToDirectory(source, destinationDirectoryName, overwrite: false); diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj b/src/libraries/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj index bf9e78ab742..9fbde72eef7 100644 --- a/src/libraries/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj +++ b/src/libraries/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj @@ -1,6 +1,6 @@ - + - $(NetCoreAppCurrent) + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser @@ -23,6 +23,12 @@ + + + + + + diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Create.cs b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Create.cs index 3ba4081397b..18768b5a592 100644 --- a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Create.cs +++ b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Create.cs @@ -450,5 +450,28 @@ namespace System.IO.Compression.Tests } } } + + [Fact] + public void CreateSetsExternalAttributesCorrectly() + { + string folderName = zfolder("normal"); + string filepath = GetTestFilePath(); + ZipFile.CreateFromDirectory(folderName, filepath); + + using (ZipArchive archive = ZipFile.Open(filepath, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + if (OperatingSystem.IsWindows()) + { + Assert.Equal(0, entry.ExternalAttributes); + } + else + { + Assert.NotEqual(0, entry.ExternalAttributes); + } + } + } + } } } diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs new file mode 100644 index 00000000000..ab61ae92443 --- /dev/null +++ b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using Xunit; + +namespace System.IO.Compression.Tests +{ + public class ZipFile_Unix : ZipFileTestBase + { + [Fact] + public void UnixCreateSetsPermissionsInExternalAttributes() + { + // '7600' tests that S_ISUID, S_ISGID, and S_ISVTX bits get preserved in ExternalAttributes + string[] testPermissions = new[] { "777", "755", "644", "600", "7600" }; + + using (var tempFolder = new TempDirectory(Path.Combine(GetTestFilePath(), "testFolder"))) + { + foreach (string permission in testPermissions) + { + CreateFile(tempFolder.Path, permission); + } + + string archivePath = GetTestFilePath(); + ZipFile.CreateFromDirectory(tempFolder.Path, archivePath); + + using (ZipArchive archive = ZipFile.OpenRead(archivePath)) + { + Assert.Equal(5, archive.Entries.Count); + + foreach (ZipArchiveEntry entry in archive.Entries) + { + Assert.EndsWith(".txt", entry.Name, StringComparison.Ordinal); + EnsureExternalAttributes(entry.Name.Substring(0, entry.Name.Length - 4), entry); + } + + void EnsureExternalAttributes(string permissions, ZipArchiveEntry entry) + { + Assert.Equal(Convert.ToInt32(permissions, 8), (entry.ExternalAttributes >> 16) & 0xFFF); + } + } + + // test that round tripping the archive has the same file permissions + using (var extractFolder = new TempDirectory(Path.Combine(GetTestFilePath(), "extract"))) + { + ZipFile.ExtractToDirectory(archivePath, extractFolder.Path); + + foreach (string permission in testPermissions) + { + string filename = Path.Combine(extractFolder.Path, permission + ".txt"); + Assert.True(File.Exists(filename)); + + EnsureFilePermissions(filename, permission); + } + } + } + } + + [Fact] + public void UnixExtractSetsFilePermissionsFromExternalAttributes() + { + // '7600' tests that S_ISUID, S_ISGID, and S_ISVTX bits don't get extracted to file permissions + string[] testPermissions = new[] { "777", "755", "644", "754", "7600" }; + byte[] contents = Encoding.UTF8.GetBytes("contents"); + + string archivePath = GetTestFilePath(); + using (FileStream fileStream = new FileStream(archivePath, FileMode.CreateNew)) + using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Create)) + { + foreach (string permission in testPermissions) + { + ZipArchiveEntry entry = archive.CreateEntry(permission + ".txt"); + entry.ExternalAttributes = Convert.ToInt32(permission, 8) << 16; + using Stream stream = entry.Open(); + stream.Write(contents); + stream.Flush(); + } + } + + using (var tempFolder = new TempDirectory(GetTestFilePath())) + { + ZipFile.ExtractToDirectory(archivePath, tempFolder.Path); + + foreach (string permission in testPermissions) + { + string filename = Path.Combine(tempFolder.Path, permission + ".txt"); + Assert.True(File.Exists(filename)); + + EnsureFilePermissions(filename, permission); + } + } + } + + private static void CreateFile(string folderPath, string permissions) + { + string filename = Path.Combine(folderPath, $"{permissions}.txt"); + File.WriteAllText(filename, "contents"); + + Assert.Equal(0, Interop.Sys.ChMod(filename, Convert.ToInt32(permissions, 8))); + } + + private static void EnsureFilePermissions(string filename, string permissions) + { + Interop.Sys.FileStatus status; + Assert.Equal(0, Interop.Sys.Stat(filename, out status)); + + // note that we don't extract S_ISUID, S_ISGID, and S_ISVTX bits, + // so only use the last 3 numbers of permissions to verify the file permissions + permissions = permissions.Length > 3 ? permissions.Substring(permissions.Length - 3) : permissions; + Assert.Equal(Convert.ToInt32(permissions, 8), status.Mode & 0xFFF); + } + + [Theory] + [InlineData("sharpziplib.zip", null)] // ExternalAttributes are not set in this .zip, use the system default + [InlineData("Linux_RW_RW_R__.zip", "664")] + [InlineData("Linux_RWXRW_R__.zip", "764")] + [InlineData("OSX_RWXRW_R__.zip", "764")] + public void UnixExtractFilePermissionsCompat(string zipName, string expectedPermissions) + { + expectedPermissions = GetExpectedPermissions(expectedPermissions); + + string zipFileName = compat(zipName); + using (var tempFolder = new TempDirectory(GetTestFilePath())) + { + ZipFile.ExtractToDirectory(zipFileName, tempFolder.Path); + + using ZipArchive archive = ZipFile.Open(zipFileName, ZipArchiveMode.Read); + foreach (ZipArchiveEntry entry in archive.Entries) + { + string filename = Path.Combine(tempFolder.Path, entry.FullName); + Assert.True(File.Exists(filename), $"File '{filename}' should exist"); + + EnsureFilePermissions(filename, expectedPermissions); + } + } + } + + private static string GetExpectedPermissions(string expectedPermissions) + { + if (string.IsNullOrEmpty(expectedPermissions)) + { + // Create a new file, and get its permissions to get the current system default permissions + + using (var tempFolder = new TempDirectory()) + { + string filename = Path.Combine(tempFolder.Path, Path.GetRandomFileName()); + File.WriteAllText(filename, "contents"); + + Interop.Sys.FileStatus status; + Assert.Equal(0, Interop.Sys.Stat(filename, out status)); + + expectedPermissions = Convert.ToString(status.Mode & 0xFFF, 8); + } + } + + return expectedPermissions; + } + } +} From 1778ae292f58ff0c88c599f32c65383ff92ef746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Wed, 14 Jul 2021 09:19:45 +0200 Subject: [PATCH 530/926] H/3 and Quic AppContext switch (#55332) * Renamed AllowDraftHttp3 to AllowHttp3AndQuic app context switch * Usage of AllowHttp3AndQuic switch in S.N.Quic and enabling it in tests * Renamed AppContext switch to Http3Support --- .../src/System/Net/Http/GlobalHttpSettings.cs | 12 +++--- .../HttpConnectionSettings.cs | 2 +- .../System.Net.Http.Functional.Tests.csproj | 4 ++ .../StressTests/HttpStress/HttpStress.csproj | 4 ++ .../MsQuic/Internal/MsQuicApi.cs | 40 ++++++++++++++++++- .../System.Net.Quic.Functional.Tests.csproj | 3 ++ 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs index 7382f4ca0da..664d296dcb6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/GlobalHttpSettings.cs @@ -26,12 +26,12 @@ namespace System.Net.Http "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2SUPPORT", true); - // Default to allowing draft HTTP/3, but enable that to be overridden - // by an AppContext switch, or by an environment variable being set to false/0. - public static bool AllowDraftHttp3 { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( - "System.Net.SocketsHttpHandler.Http3DraftSupport", - "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3DRAFTSUPPORT", - true); + // Default to disable HTTP/3 (and by an extent QUIC), but enable that to be overridden + // by an AppContext switch, or by an environment variable being set to true/1. + public static bool AllowHttp3 { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.SocketsHttpHandler.Http3Support", + "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT", + false); // Switch to disable the HTTP/2 dynamic window scaling algorithm. Enabled by default. public static bool DisableDynamicHttp2WindowSizing { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch( diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs index a24a6403b29..410acc16b69 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs @@ -70,7 +70,7 @@ namespace System.Net.Http public HttpConnectionSettings() { bool allowHttp2 = GlobalHttpSettings.SocketsHttpHandler.AllowHttp2; - bool allowHttp3 = GlobalHttpSettings.SocketsHttpHandler.AllowDraftHttp3; + bool allowHttp3 = GlobalHttpSettings.SocketsHttpHandler.AllowHttp3; _maxHttpVersion = allowHttp3 && allowHttp2 ? HttpVersion.Version30 : allowHttp2 ? HttpVersion.Version20 : diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index b3413cbef30..667b35e9a8a 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -9,6 +9,10 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX + + + + WasmTestOnBrowser $(TestArchiveRoot)browseronly/ diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj index db333cc634f..80b4aa21b10 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpStress.csproj @@ -7,6 +7,10 @@ enable + + + + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index cba34f6dbba..b691d3c5c14 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -125,10 +125,18 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal static MsQuicApi() { + if (!IsHttp3Enabled()) + { + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(null, $"HTTP/3 and QUIC is not enabled, see 'System.Net.SocketsHttpHandler.Http3Support' AppContext switch."); + } + + return; + } + if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported()) { - IsQuicSupported = false; - if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); @@ -163,6 +171,34 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal } } + // Note that this is copy-pasted from S.N.Http just to hide S.N.Quic behind the same AppContext switch + // since this library is considered "private" for 6.0. + // We should get rid of this once S.N.Quic API surface is officially exposed. + private static bool IsHttp3Enabled() + { + bool value; + + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch("System.Net.SocketsHttpHandler.Http3Support", out value)) + { + return value; + } + + // AppContext switch wasn't used. Check the environment variable. + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT"); + + if (bool.TryParse(envVar, out value)) + { + return value; + } + else if (uint.TryParse(envVar, out uint intVal)) + { + return intVal != 0; + } + + return false; + } + private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major, MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index 2a17894a555..583b2b6c486 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -4,6 +4,9 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix + + + From 01ce681dc4f2e6720caae5043b04c3fe797cebb4 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 14 Jul 2021 05:07:37 -0400 Subject: [PATCH 531/926] [mono] Add wrapper info for native func wrappers. (#55602) Fixes https://github.com/dotnet/runtime/issues/55567. --- src/mono/mono/metadata/marshal.c | 8 ++++---- src/mono/mono/metadata/marshal.h | 1 + src/mono/mono/mini/aot-runtime.h | 2 +- src/mono/mono/mini/interp/transform.c | 5 ++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 0b5cdf06407..0a9d8f8ff1a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3443,7 +3443,7 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func) { MonoMethodSignature *csig; - + WrapperInfo *info; SignaturePointerPair key, *new_key; MonoMethodBuilder *mb; MonoMethod *res; @@ -3474,14 +3474,14 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig new_key->sig = csig; new_key->pointer = func; - res = mono_mb_create_and_cache_full (cache, new_key, mb, csig, csig->param_count + 16, NULL, &found); + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC); + + res = mono_mb_create_and_cache_full (cache, new_key, mb, csig, csig->param_count + 16, info, &found); if (found) g_free (new_key); mono_mb_free (mb); - mono_marshal_set_wrapper_info (res, NULL); - return res; } diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index 08300a001ef..d56f30e0f9d 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -113,6 +113,7 @@ typedef enum { WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL, /* Subtypes of MONO_WRAPPER_MANAGED_TO_NATIVE */ WRAPPER_SUBTYPE_ICALL_WRAPPER, // specifically JIT icalls + WRAPPER_SUBTYPE_NATIVE_FUNC, WRAPPER_SUBTYPE_NATIVE_FUNC_AOT, WRAPPER_SUBTYPE_NATIVE_FUNC_INDIRECT, WRAPPER_SUBTYPE_PINVOKE, diff --git a/src/mono/mono/mini/aot-runtime.h b/src/mono/mono/mini/aot-runtime.h index af40e8bbb12..dff55a92563 100644 --- a/src/mono/mono/mini/aot-runtime.h +++ b/src/mono/mono/mini/aot-runtime.h @@ -11,7 +11,7 @@ #include "mini.h" /* Version number of the AOT file format */ -#define MONO_AOT_FILE_VERSION 181 +#define MONO_AOT_FILE_VERSION 182 #define MONO_AOT_TRAMP_PAGE_SIZE 16384 diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index e75fefe393c..7c45880f8d5 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3458,9 +3458,8 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target * every time based on the signature. */ if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) { - WrapperInfo *info = mono_marshal_get_wrapper_info (method); - if (info) { - MonoMethod *pinvoke_method = info->d.managed_to_native.method; + MonoMethod *pinvoke_method = mono_marshal_method_from_wrapper (method); + if (pinvoke_method) { imethod = mono_interp_get_imethod (pinvoke_method, error); return_val_if_nok (error, FALSE); } From 7bd472498e690e9421df86d5a9d728faa939742c Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 14 Jul 2021 11:52:27 +0200 Subject: [PATCH 532/926] [mono] Fix race during mono_image_storage_open (#55201) --- src/mono/mono/metadata/image.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 68b7b377266..3bd2a6306ad 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -1263,8 +1263,11 @@ mono_image_storage_trypublish (MonoImageStorage *candidate, MonoImageStorage **o gboolean result; mono_images_storage_lock (); MonoImageStorage *val = (MonoImageStorage *)g_hash_table_lookup (images_storage_hash, candidate->key); + if (val && !mono_refcount_tryinc (val)) { + // We raced against a mono_image_storage_dtor in progress. + val = NULL; + } if (val) { - mono_refcount_inc (val); *out_storage = val; result = FALSE; } else { @@ -1295,8 +1298,11 @@ mono_image_storage_tryaddref (const char *key, MonoImageStorage **found) gboolean result = FALSE; mono_images_storage_lock (); MonoImageStorage *val = (MonoImageStorage *)g_hash_table_lookup (images_storage_hash, key); + if (val && !mono_refcount_tryinc (val)) { + // We raced against a mono_image_storage_dtor in progress. + val = NULL; + } if (val) { - mono_refcount_inc (val); *found = val; result = TRUE; } From afaf666eff08435123eb649ac138419f4c9b9344 Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Wed, 14 Jul 2021 03:29:04 -0700 Subject: [PATCH 533/926] DiagnosticSourceEventSource supports base class properties (#55613) Fixes #41300 Previously DiagnosticSourceEventSource would only iterate and recognize properties that were declared on the most derived type. Now it can capture properties that were inherited from a base class too. --- .../DiagnosticSourceEventSource.cs | 4 +- .../DiagnosticSourceEventSourceBridgeTests.cs | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index 72f7572313f..d2ee34fe439 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -1099,7 +1099,7 @@ namespace System.Diagnostics { TransformSpec? newSerializableArgs = null; TypeInfo curTypeInfo = type.GetTypeInfo(); - foreach (PropertyInfo property in curTypeInfo.DeclaredProperties) + foreach (PropertyInfo property in curTypeInfo.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { // prevent TransformSpec from attempting to implicitly transform index properties if (property.GetMethod == null || property.GetMethod!.GetParameters().Length > 0) @@ -1319,7 +1319,7 @@ namespace System.Diagnostics } else { - PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName); + PropertyInfo? propertyInfo = typeInfo.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); if (propertyInfo == null) { Log.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away."); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs index 2a59c8b48e4..ecf73aa7f06 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs @@ -490,6 +490,40 @@ namespace System.Diagnostics.Tests }).Dispose(); } + /// + /// Tests that DiagnosticSourceEventSource can read property values from base classes + /// + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestBaseClassProperties() + { + RemoteExecutor.Invoke(() => + { + using (var eventSourceListener = new TestDiagnosticSourceEventListener()) + using (var diagnosticSourceListener = new DiagnosticListener("TestBaseClassProperties")) + { + Assert.Equal(0, eventSourceListener.EventCount); + eventSourceListener.Enable( + " TestBaseClassProperties/TestEvent1:Point_X=Point.X;Point_Y=Point.Y;Url_2=Url2\r\n"); + + /***************************************************************************************/ + // Emit an event that matches the first pattern. + MyClass val = new MyDerivedClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 }, Url2 = "Second url", AnotherString = "another" }; + if (diagnosticSourceListener.IsEnabled("TestEvent1")) + diagnosticSourceListener.Write("TestEvent1", val); + + Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted. + Assert.Equal("TestBaseClassProperties", eventSourceListener.LastEvent.SourceName); + Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName); + Assert.Equal(7, eventSourceListener.LastEvent.Arguments.Count); + Assert.Equal("another", eventSourceListener.LastEvent.Arguments["AnotherString"]); + Assert.Equal("3", eventSourceListener.LastEvent.Arguments["Point_X"]); + Assert.Equal("5", eventSourceListener.LastEvent.Arguments["Point_Y"]); + Assert.Equal("Second url", eventSourceListener.LastEvent.Arguments["Url_2"]); + eventSourceListener.ResetEventCountAndLastEvent(); + } + }).Dispose(); + } + /// /// Test that things work properly for Linux newline conventions. /// @@ -1314,6 +1348,12 @@ namespace System.Diagnostics.Tests public MyPoint Point { get; set; } } + internal class MyDerivedClass : MyClass + { + public string Url2 { get; set; } + public string AnotherString { get; set; } + } + /// /// classes for test data. /// From 21e36e872556c157d4b65a1c997d92dfb31f18c5 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 14 Jul 2021 14:01:14 +0300 Subject: [PATCH 534/926] Inliner: Extend IL limit for profiled call-sites, allow inlining for switches. (#55478) --- src/coreclr/jit/block.cpp | 3 +- src/coreclr/jit/compiler.cpp | 3 + src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/fgbasic.cpp | 64 +++++++++++++--- src/coreclr/jit/fgprofile.cpp | 25 +++++++ src/coreclr/jit/importer.cpp | 41 ++++++----- src/coreclr/jit/inline.def | 1 + src/coreclr/jit/inline.h | 1 + src/coreclr/jit/inlinepolicy.cpp | 73 ++++++++++++++----- src/coreclr/jit/inlinepolicy.h | 4 + src/coreclr/jit/jitconfigvalues.h | 3 +- .../Linq/Expressions/DebugViewWriter.cs | 2 + 12 files changed, 171 insertions(+), 50 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 128c4e033d9..5df8c1e4358 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -1431,6 +1431,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind) /* Give the block a number, set the ancestor count and weight */ ++fgBBcount; + ++fgBBNumMax; if (compIsForInlining()) { @@ -1438,7 +1439,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind) } else { - block->bbNum = ++fgBBNumMax; + block->bbNum = fgBBNumMax; } if (compRationalIRForm) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index bf49e63ab0c..fe5734213af 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6272,6 +6272,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, // a potential inline candidate. InlineResult prejitResult(this, methodHnd, "prejit"); + // Profile data allows us to avoid early "too many IL bytes" outs. + prejitResult.NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, fgHaveSufficientProfileData()); + // Do the initial inline screen. impCanInlineIL(methodHnd, methodInfo, forceInline, &prejitResult); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b60353d8e84..9027a5d2335 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5814,6 +5814,7 @@ public: void WalkSpanningTree(SpanningTreeVisitor* visitor); void fgSetProfileWeight(BasicBlock* block, BasicBlock::weight_t weight); void fgApplyProfileScale(); + bool fgHaveSufficientProfileData(); // fgIsUsingProfileWeights - returns true if we have real profile data for this method // or if we have some fake profile data for the stress mode diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 62a9467da63..6c65586fbca 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -818,8 +818,24 @@ public: return false; } const unsigned argNum = value - SLOT_ARGUMENT; - assert(argNum < info->argCnt); - return info->inlArgInfo[argNum].argIsInvariant; + if (argNum < info->argCnt) + { + return info->inlArgInfo[argNum].argIsInvariant; + } + return false; + } + static bool IsExactArgument(FgSlot value, InlineInfo* info) + { + if ((info == nullptr) || !IsArgument(value)) + { + return false; + } + const unsigned argNum = value - SLOT_ARGUMENT; + if (argNum < info->argCnt) + { + return info->inlArgInfo[argNum].argIsExact; + } + return false; } static unsigned SlotTypeToArgNum(FgSlot value) { @@ -876,6 +892,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { + // Set default values for profile (to avoid NoteFailed in CALLEE_IL_CODE_SIZE's handler) + // these will be overridden later. + compInlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true); + compInlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, 1.0); // Observe force inline state and code size. compInlineResult->NoteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline); compInlineResult->NoteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize); @@ -1031,7 +1051,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { FgStack::FgSlot slot = pushedStack.Top(); - if (FgStack::IsConstantOrConstArg(slot, impInlineInfo)) + if (FgStack::IsConstantOrConstArg(slot, impInlineInfo) || + FgStack::IsExactArgument(slot, impInlineInfo)) { compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN); handled = true; // and keep argument in the pushedStack @@ -1338,44 +1359,59 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed FgStack::FgSlot arg0 = pushedStack.Top(1); FgStack::FgSlot arg1 = pushedStack.Top(0); - if ((FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo)) || - (FgStack::IsConstant(arg1) && FgStack::IsConstArgument(arg0, impInlineInfo)) || - (FgStack::IsConstArgument(arg0, impInlineInfo) && - FgStack::IsConstArgument(arg1, impInlineInfo))) + // Const op ConstArg -> ConstArg + if (FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo)) { // keep stack unchanged handled = true; compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR); } - if ((FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1)) || - (FgStack::IsConstant(arg1) && FgStack::IsConstant(arg0))) + // ConstArg op Const -> ConstArg + // ConstArg op ConstArg -> ConstArg + else if (FgStack::IsConstArgument(arg0, impInlineInfo) && + FgStack::IsConstantOrConstArg(arg1, impInlineInfo)) + { + if (FgStack::IsConstant(arg1)) + { + pushedStack.Push(arg0); + } + handled = true; + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR); + } + // Const op Const -> Const + else if (FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1)) { // both are constants, but we're mostly interested in cases where a const arg leads to // a foldable expression. handled = true; } + // Arg op ConstArg + // Arg op Const else if (FgStack::IsArgument(arg0) && FgStack::IsConstantOrConstArg(arg1, impInlineInfo)) { // "Arg op CNS" --> keep arg0 in the stack for the next ops + pushedStack.Push(arg0); handled = true; compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS); } + // ConstArg op Arg + // Const op Arg else if (FgStack::IsArgument(arg1) && FgStack::IsConstantOrConstArg(arg0, impInlineInfo)) { // "CNS op ARG" --> keep arg1 in the stack for the next ops - pushedStack.Push(arg1); handled = true; compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS); } - + // X / ConstArg + // X % ConstArg if (FgStack::IsConstArgument(arg1, impInlineInfo)) { - // Special case: "X / ConstArg" or "X % ConstArg" if ((opcode == CEE_DIV) || (opcode == CEE_DIV_UN) || (opcode == CEE_REM) || (opcode == CEE_REM_UN)) { compInlineResult->Note(InlineObservation::CALLSITE_DIV_BY_CNS); } + pushedStack.Push(arg0); handled = true; } } @@ -1583,6 +1619,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed if (makeInlineObservations) { compInlineResult->Note(InlineObservation::CALLEE_HAS_SWITCH); + if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo)) + { + compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_SWITCH); + } // Fail fast, if we're inlining and can't handle this. if (isInlining && compInlineResult->IsFailure()) diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 1659b2d3905..31d7c208eba 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -47,6 +47,31 @@ bool Compiler::fgHaveProfileData() return (fgPgoSchema != nullptr); } +//------------------------------------------------------------------------ +// fgHaveSufficientProfileData: check if profile data is available +// and is sufficient enough to be trustful. +// +// Returns: +// true if so +// +// Note: +// See notes for fgHaveProfileData. +// +bool Compiler::fgHaveSufficientProfileData() +{ + if (!fgHaveProfileData()) + { + return false; + } + + if ((fgFirstBB != nullptr) && (fgPgoSource == ICorJitInfo::PgoSource::Static)) + { + const BasicBlock::weight_t sufficientSamples = 1000; + return fgFirstBB->bbWeight > sufficientSamples; + } + return true; +} + //------------------------------------------------------------------------ // fgApplyProfileScale: scale inlinee counts by appropriate scale factor // diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index c30e9a17784..b3857cdec58 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -13267,8 +13267,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) goto COND_JUMP; case CEE_SWITCH: - assert(!compIsForInlining()); - if (tiVerificationNeeded) { Verify(impStackTop().seTypeInfo.IsType(TI_INT), "Bad switch val"); @@ -19080,33 +19078,30 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I inlineResult->NoteInt(InlineObservation::CALLSITE_FREQUENCY, static_cast(frequency)); inlineResult->NoteInt(InlineObservation::CALLSITE_WEIGHT, (int)(weight)); + bool hasProfile = false; + double profileFreq = 0.0; + // If the call site has profile data, report the relative frequency of the site. // - if ((pInlineInfo != nullptr) && rootCompiler->fgHaveProfileData() && pInlineInfo->iciBlock->hasProfileWeight()) + if ((pInlineInfo != nullptr) && rootCompiler->fgHaveSufficientProfileData()) { - BasicBlock::weight_t callSiteWeight = pInlineInfo->iciBlock->bbWeight; - BasicBlock::weight_t entryWeight = rootCompiler->fgFirstBB->bbWeight; - BasicBlock::weight_t profileFreq = entryWeight == 0.0f ? 0.0f : callSiteWeight / entryWeight; + const BasicBlock::weight_t callSiteWeight = pInlineInfo->iciBlock->bbWeight; + const BasicBlock::weight_t entryWeight = rootCompiler->fgFirstBB->bbWeight; + profileFreq = entryWeight == 0.0f ? 0.0 : callSiteWeight / entryWeight; + hasProfile = true; assert(callSiteWeight >= 0); assert(entryWeight >= 0); - - BasicBlock::weight_t sufficientSamples = 1000.0f; - - if (!rootCompiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) || - ((callSiteWeight + entryWeight) > sufficientSamples)) - { - // Let's not report profiles for methods with insufficient samples during prejitting. - inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true); - inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, profileFreq); - } } - else if ((pInlineInfo == nullptr) && rootCompiler->fgHaveProfileData()) + else if (pInlineInfo == nullptr) { // Simulate a hot callsite for PrejitRoot mode. - inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true); - inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, 1.0); + hasProfile = true; + profileFreq = 1.0; } + + inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, hasProfile); + inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, profileFreq); } /***************************************************************************** @@ -19252,6 +19247,10 @@ void Compiler::impCheckCanInline(GenTreeCall* call, goto _exit; } + // Profile data allows us to avoid early "too many IL bytes" outs. + pParam->result->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, + pParam->pThis->fgHaveSufficientProfileData()); + bool forceInline; forceInline = !!(pParam->methAttr & CORINFO_FLG_FORCEINLINE); @@ -19474,6 +19473,10 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo, } } + bool isExact = false; + bool isNonNull = false; + inlCurArgInfo->argIsExact = (gtGetClassHandle(curArgVal, &isExact, &isNonNull) != NO_CLASS_HANDLE) && isExact; + // If the arg is a local that is address-taken, we can't safely // directly substitute it into the inlinee. // diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def index a16d82888b6..cbd85ff240d 100644 --- a/src/coreclr/jit/inline.def +++ b/src/coreclr/jit/inline.def @@ -182,6 +182,7 @@ INLINE_OBSERVATION(FOLDABLE_INTRINSIC, int, "foldable intrinsic", INLINE_OBSERVATION(FOLDABLE_EXPR, int, "foldable binary expression", INFORMATION, CALLSITE) INLINE_OBSERVATION(FOLDABLE_EXPR_UN, int, "foldable unary expression", INFORMATION, CALLSITE) INLINE_OBSERVATION(FOLDABLE_BRANCH, int, "foldable branch", INFORMATION, CALLSITE) +INLINE_OBSERVATION(FOLDABLE_SWITCH, int, "foldable switch", INFORMATION, CALLSITE) INLINE_OBSERVATION(DIV_BY_CNS, int, "dividy by const", INFORMATION, CALLSITE) INLINE_OBSERVATION(CONSTANT_ARG_FEEDS_TEST, bool, "constant argument feeds test", INFORMATION, CALLSITE) INLINE_OBSERVATION(DEPTH, int, "depth", INFORMATION, CALLSITE) diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index bc08dd8110d..6a39b2d9cb5 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -610,6 +610,7 @@ struct InlArgInfo unsigned argHasStargOp : 1; // Is there STARG(s) operation on this argument? unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a // field in them? + unsigned argIsExact : 1; // Is this arg of an exact class? }; // InlLclVarInfo describes inline candidate argument and local variable properties. diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index fdf4ae19341..e84ff2858a0 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -326,7 +326,6 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value) m_ArgFeedsRangeCheck++; break; - case InlineObservation::CALLEE_HAS_SWITCH: case InlineObservation::CALLEE_UNSUPPORTED_OPCODE: propagate = true; break; @@ -1294,6 +1293,14 @@ void ExtendedDefaultPolicy::NoteBool(InlineObservation obs, bool value) m_FoldableBranch++; break; + case InlineObservation::CALLSITE_FOLDABLE_SWITCH: + m_FoldableSwitch++; + break; + + case InlineObservation::CALLEE_HAS_SWITCH: + m_Switch++; + break; + case InlineObservation::CALLSITE_DIV_BY_CNS: m_DivByCns++; break; @@ -1327,7 +1334,14 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value) { assert(m_IsForceInlineKnown); assert(value != 0); - m_CodeSize = static_cast(value); + m_CodeSize = static_cast(value); + unsigned maxCodeSize = static_cast(JitConfig.JitExtDefaultPolicyMaxIL()); + + // TODO: Enable for PgoSource::Static as well if it's not the generic profile we bundle. + if (m_HasProfile && (m_RootCompiler->fgPgoSource == ICorJitInfo::PgoSource::Dynamic)) + { + maxCodeSize = static_cast(JitConfig.JitExtDefaultPolicyMaxILProf()); + } if (m_IsForceInline) { @@ -1339,7 +1353,7 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value) // Candidate based on small size SetCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE); } - else if (m_CodeSize <= (unsigned)JitConfig.JitExtDefaultPolicyMaxIL()) + else if (m_CodeSize <= maxCodeSize) { // Candidate, pending profitability evaluation SetCandidate(InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE); @@ -1357,16 +1371,16 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value) { SetNever(InlineObservation::CALLEE_DOES_NOT_RETURN); } - else if (!m_IsForceInline) + else if (!m_IsForceInline && !m_HasProfile) { unsigned bbLimit = (unsigned)JitConfig.JitExtDefaultPolicyMaxBB(); if (m_IsPrejitRoot) { // We're not able to recognize arg-specific foldable branches // in prejit-root mode. - bbLimit += 3; + bbLimit += 5 + m_Switch * 10; } - bbLimit += m_FoldableBranch; + bbLimit += m_FoldableBranch + m_FoldableSwitch * 10; if ((unsigned)value > bbLimit) { SetNever(InlineObservation::CALLEE_TOO_MANY_BASIC_BLOCKS); @@ -1419,13 +1433,13 @@ double ExtendedDefaultPolicy::DetermineMultiplier() if (m_ReturnsStructByValue) { // For structs-passed-by-value we might avoid expensive copy operations if we inline. - multiplier += 1.5; + multiplier += 2.0; JITDUMP("\nInline candidate returns a struct by value. Multiplier increased to %g.", multiplier); } else if (m_ArgIsStructByValue > 0) { // Same here - multiplier += 1.5; + multiplier += 2.0; JITDUMP("\n%d arguments are structs passed by value. Multiplier increased to %g.", m_ArgIsStructByValue, multiplier); } @@ -1451,13 +1465,13 @@ double ExtendedDefaultPolicy::DetermineMultiplier() if (m_ArgFeedsRangeCheck > 0) { - multiplier += 0.5; + multiplier += 1.0; JITDUMP("\nInline candidate has arg that feeds range check. Multiplier increased to %g.", multiplier); } if (m_NonGenericCallsGeneric) { - multiplier += 1.5; + multiplier += 2.0; JITDUMP("\nInline candidate is generic and caller is not. Multiplier increased to %g.", multiplier); } @@ -1507,7 +1521,7 @@ double ExtendedDefaultPolicy::DetermineMultiplier() if (m_Intrinsic > 0) { // In most cases such intrinsics are lowered as single CPU instructions - multiplier += 1.5; + multiplier += 1.0 + m_Intrinsic * 0.3; JITDUMP("\nInline has %d intrinsics. Multiplier increased to %g.", m_Intrinsic, multiplier); } @@ -1636,6 +1650,28 @@ double ExtendedDefaultPolicy::DetermineMultiplier() break; } + if (m_FoldableSwitch > 0) + { + multiplier += 6.0; + JITDUMP("\nInline candidate has %d foldable switches. Multiplier increased to %g.", m_FoldableSwitch, + multiplier); + } + else if (m_Switch > 0) + { + if (m_IsPrejitRoot) + { + // Assume the switches can be foldable in PrejitRoot mode. + multiplier += 6.0; + JITDUMP("\nPrejit root candidate has %d switches. Multiplier increased to %g.", m_Switch, multiplier); + } + else + { + // TODO: Investigate cases where it makes sense to inline non-foldable switches + multiplier = 0.0; + JITDUMP("\nInline candidate has %d switches. Multiplier limited to %g.", m_Switch, multiplier); + } + } + if (m_HasProfile) { // There are cases when Profile Data can be misleading or polluted: @@ -1657,14 +1693,16 @@ double ExtendedDefaultPolicy::DetermineMultiplier() { multiplier *= min(m_ProfileFrequency, 1.0) * profileScale; } - JITDUMP("\nCallsite has profile data: %g.", m_ProfileFrequency); + JITDUMP("\nCallsite has profile data: %g. Multiplier limited to %g.", m_ProfileFrequency, multiplier); } - if (m_RootCompiler->lvaTableCnt > ((unsigned)(JitConfig.JitMaxLocalsToTrack() / 4))) + // Slow down if there are already too many locals + if (m_RootCompiler->lvaTableCnt > 64) { - // Slow down inlining if we already have to many locals in the rootCompiler. - multiplier /= ((double)m_RootCompiler->lvaTableCnt / ((double)JitConfig.JitMaxLocalsToTrack() / 4.0)); - JITDUMP("\nCaller %d locals. Multiplier decreased to %g.", m_RootCompiler->lvaTableCnt, multiplier); + // E.g. MaxLocalsToTrack = 1024 and lvaTableCnt = 512 -> multiplier *= 0.5; + const double lclFullness = min(1.0, (double)m_RootCompiler->lvaTableCnt / JitConfig.JitMaxLocalsToTrack()); + multiplier *= (1.0 - lclFullness); + JITDUMP("\nCaller has %d locals. Multiplier decreased to %g.", m_RootCompiler->lvaTableCnt, multiplier); } if (m_BackwardJump) @@ -1738,6 +1776,8 @@ void ExtendedDefaultPolicy::OnDumpXml(FILE* file, unsigned indent) const XATTR_I4(m_FoldableExpr) XATTR_I4(m_FoldableExprUn) XATTR_I4(m_FoldableBranch) + XATTR_I4(m_FoldableSwitch) + XATTR_I4(m_Switch) XATTR_I4(m_DivByCns) XATTR_B(m_ReturnsStructByValue) XATTR_B(m_IsFromValueClass) @@ -1927,7 +1967,6 @@ void DiscretionaryPolicy::NoteDouble(InlineObservation obs, double value) { assert(obs == InlineObservation::CALLSITE_PROFILE_FREQUENCY); assert(value >= 0.0); - assert(m_ProfileFrequency == 0.0); m_ProfileFrequency = value; } diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h index 7d0d83bd736..466e17fe0e1 100644 --- a/src/coreclr/jit/inlinepolicy.h +++ b/src/coreclr/jit/inlinepolicy.h @@ -204,6 +204,8 @@ public: , m_FoldableExpr(0) , m_FoldableExprUn(0) , m_FoldableBranch(0) + , m_FoldableSwitch(0) + , m_Switch(0) , m_DivByCns(0) , m_ReturnsStructByValue(false) , m_IsFromValueClass(false) @@ -252,6 +254,8 @@ protected: unsigned m_FoldableExpr; unsigned m_FoldableExprUn; unsigned m_FoldableBranch; + unsigned m_FoldableSwitch; + unsigned m_Switch; unsigned m_DivByCns; bool m_ReturnsStructByValue : 1; bool m_IsFromValueClass : 1; diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 3220193c662..cd685c2cc10 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -463,7 +463,8 @@ CONFIG_STRING(JitInlineReplayFile, W("JitInlineReplayFile")) // Extended version of DefaultPolicy that includes a more precise IL scan, // relies on PGO if it exists and generally is more aggressive. CONFIG_INTEGER(JitExtDefaultPolicy, W("JitExtDefaultPolicy"), 1) -CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x64) +CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x80) +CONFIG_INTEGER(JitExtDefaultPolicyMaxILProf, W("JitExtDefaultPolicyMaxILProf"), 0x400) CONFIG_INTEGER(JitExtDefaultPolicyMaxBB, W("JitExtDefaultPolicyMaxBB"), 7) // Inliner uses the following formula for PGO-driven decisions: diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs index af967733a5a..2f2ecf4b384 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs @@ -7,6 +7,7 @@ using System.Dynamic.Utils; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; namespace System.Linq.Expressions { @@ -168,6 +169,7 @@ namespace System.Linq.Expressions Out(Flow.None, s, after); } + [MethodImpl(MethodImplOptions.NoInlining)] private void Out(Flow before, string s, Flow after) { switch (GetFlow(before)) From 559011c93114421d99ce7c1938227f73593a1f2d Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 14 Jul 2021 05:13:32 -0700 Subject: [PATCH 535/926] [debugger] Fix debugging after hot reloading (#55599) * Fix deug after hotreload. * do not change mono.proj * fix formatting. * Remove extra space. --- src/mono/mono/metadata/mono-debug.c | 8 ++++++-- src/mono/mono/mini/interp/transform.c | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/mono-debug.c b/src/mono/mono/metadata/mono-debug.c index 58ecee24d45..10554e663ef 100644 --- a/src/mono/mono/metadata/mono-debug.c +++ b/src/mono/mono/metadata/mono-debug.c @@ -1120,8 +1120,12 @@ mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrA if (img->has_updates) { int idx = mono_metadata_token_index (minfo->method->token); gpointer ptr = mono_metadata_update_get_updated_method_ppdb (img, idx); - mono_ppdb_get_seq_points_enc (ptr, seq_points, n_seq_points); - } else if (minfo->handle->ppdb) + if (ptr != NULL) { + mono_ppdb_get_seq_points_enc (ptr, seq_points, n_seq_points); + return; + } + } + if (minfo->handle->ppdb) mono_ppdb_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); else mono_debug_symfile_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 7c45880f8d5..c677122e6a5 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9757,7 +9757,10 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); - g_hash_table_replace (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); + gpointer seq_points = g_hash_table_lookup (jit_mm->seq_points, imethod->method); + if (!seq_points || seq_points != imethod->jinfo->seq_points) + g_hash_table_replace (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); + jit_mm_unlock (jit_mm); // FIXME: Add a different callback ? From 599b7acd519ab28c48770fd11fd61d250a0eae35 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 14 Jul 2021 05:13:46 -0700 Subject: [PATCH 536/926] [wasm] [debugger] Support method calls (#55458) * Implement method calls and method calls with simple parameters. * removing useless change. * Support const char. Support object. * Adding more tests as suggested by @pavelsavara. * Adding a test calling a method that returns void and change an attribute value. * Changing what @pavelsavara suggested. * Fix merge. --- .../BrowserDebugProxy/DevToolsHelper.cs | 6 +- .../BrowserDebugProxy/EvaluateExpression.cs | 145 ++- .../MemberReferenceResolver.cs | 93 +- .../debugger/BrowserDebugProxy/MonoProxy.cs | 178 ++-- .../BrowserDebugProxy/MonoSDBHelper.cs | 827 ++++++++++-------- .../EvaluateOnCallFrameTests.cs | 99 +++ .../debugger/DebuggerTestSuite/MonoJsTests.cs | 10 +- .../debugger-test/debugger-evaluate-test.cs | 97 ++ 8 files changed, 946 insertions(+), 509 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index 6f1ec09317e..fb4eda76ca2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -301,13 +301,13 @@ namespace Microsoft.WebAssembly.Diagnostics } } - public PerScopeCache GetCacheForScope(int scope_id) + public PerScopeCache GetCacheForScope(int scopeId) { - if (perScopeCaches.TryGetValue(scope_id, out PerScopeCache cache)) + if (perScopeCaches.TryGetValue(scopeId, out PerScopeCache cache)) return cache; cache = new PerScopeCache(); - perScopeCaches[scope_id] = cache; + perScopeCaches[scopeId] = cache; return cache; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 73de23d6fcc..58374c43565 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -27,38 +27,54 @@ namespace Microsoft.WebAssembly.Diagnostics public List methodCall = new List(); public List memberAccesses = new List(); public List argValues = new List(); + public Dictionary memberAccessValues = new Dictionary(); + private int visitCount; + public bool hasMethodCalls; + public void VisitInternal(SyntaxNode node) + { + Visit(node); + visitCount++; + } public override void Visit(SyntaxNode node) { // TODO: PointerMemberAccessExpression - if (node is MemberAccessExpressionSyntax maes - && node.Kind() == SyntaxKind.SimpleMemberAccessExpression - && !(node.Parent is MemberAccessExpressionSyntax)) + if (visitCount == 0) { - memberAccesses.Add(maes); - } + if (node is MemberAccessExpressionSyntax maes + && node.Kind() == SyntaxKind.SimpleMemberAccessExpression + && !(node.Parent is MemberAccessExpressionSyntax) + && !(node.Parent is InvocationExpressionSyntax)) + { + memberAccesses.Add(maes); + } - if (node is IdentifierNameSyntax identifier - && !(identifier.Parent is MemberAccessExpressionSyntax) - && !identifiers.Any(x => x.Identifier.Text == identifier.Identifier.Text)) - { - identifiers.Add(identifier); + if (node is IdentifierNameSyntax identifier + && !(identifier.Parent is MemberAccessExpressionSyntax) + && !identifiers.Any(x => x.Identifier.Text == identifier.Identifier.Text)) + { + identifiers.Add(identifier); + } } if (node is InvocationExpressionSyntax) { - methodCall.Add(node as InvocationExpressionSyntax); - throw new Exception("Method Call is not implemented yet"); + if (visitCount == 1) + methodCall.Add(node as InvocationExpressionSyntax); + hasMethodCalls = true; } + if (node is AssignmentExpressionSyntax) throw new Exception("Assignment is not implemented yet"); base.Visit(node); } - public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_values, IEnumerable id_values) + public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_values, IEnumerable id_values, IEnumerable method_values) { - CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); var memberAccessToParamName = new Dictionary(); + var methodCallToParamName = new Dictionary(); + + CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); // 1. Replace all this.a occurrences with this_a_ABDE root = root.ReplaceNodes(memberAccesses, (maes, _) => @@ -77,25 +93,61 @@ namespace Microsoft.WebAssembly.Diagnostics return SyntaxFactory.IdentifierName(id_name); }); + // 1.1 Replace all this.a() occurrences with this_a_ABDE + root = root.ReplaceNodes(methodCall, (m, _) => + { + string iesStr = m.ToString(); + if (!methodCallToParamName.TryGetValue(iesStr, out string id_name)) + { + // Generate a random suffix + string suffix = Guid.NewGuid().ToString().Substring(0, 5); + string prefix = iesStr.Trim().Replace(".", "_").Replace("(", "_").Replace(")", "_"); + id_name = $"{prefix}_{suffix}"; + methodCallToParamName[iesStr] = id_name; + } + + return SyntaxFactory.IdentifierName(id_name); + }); + var paramsSet = new HashSet(); // 2. For every unique member ref, add a corresponding method param - foreach ((MemberAccessExpressionSyntax maes, JObject value) in memberAccesses.Zip(ma_values)) + if (ma_values != null) { - string node_str = maes.ToString(); - if (!memberAccessToParamName.TryGetValue(node_str, out string id_name)) + foreach ((MemberAccessExpressionSyntax maes, JObject value) in memberAccesses.Zip(ma_values)) { - throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); + string node_str = maes.ToString(); + if (!memberAccessToParamName.TryGetValue(node_str, out string id_name)) + { + throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); + } + memberAccessValues[id_name] = value; + root = UpdateWithNewMethodParam(root, id_name, value); } - - root = UpdateWithNewMethodParam(root, id_name, value); } - foreach ((IdentifierNameSyntax idns, JObject value) in identifiers.Zip(id_values)) + if (id_values != null) { - root = UpdateWithNewMethodParam(root, idns.Identifier.Text, value); + foreach ((IdentifierNameSyntax idns, JObject value) in identifiers.Zip(id_values)) + { + root = UpdateWithNewMethodParam(root, idns.Identifier.Text, value); + } } + if (method_values != null) + { + foreach ((InvocationExpressionSyntax ies, JObject value) in methodCall.Zip(method_values)) + { + string node_str = ies.ToString(); + if (!methodCallToParamName.TryGetValue(node_str, out string id_name)) + { + throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); + } + root = UpdateWithNewMethodParam(root, id_name, value); + } + } + + return syntaxTree.WithRootAndOptions(root, syntaxTree.Options); CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, string id_name, JObject value) @@ -139,9 +191,9 @@ namespace Microsoft.WebAssembly.Diagnostics case "boolean": return value?.Value(); case "object": - if (subType == "null") - return null; - break; + return null; + case "void": + return null; } throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); } @@ -158,8 +210,11 @@ namespace Microsoft.WebAssembly.Diagnostics { if (subType == "null") return variable["className"].Value(); - break; + else + return "object"; } + case "void": + return "object"; default: return value.GetType().FullName; } @@ -211,6 +266,20 @@ namespace Microsoft.WebAssembly.Diagnostics return values; } + private static async Task> ResolveMethodCalls(IEnumerable methodCalls, Dictionary memberAccessValues, MemberReferenceResolver resolver, CancellationToken token) + { + var values = new List(); + foreach (InvocationExpressionSyntax methodCall in methodCalls) + { + JObject value = await resolver.Resolve(methodCall, memberAccessValues, token); + if (value == null) + throw new ReturnAsErrorException($"Failed to resolve member access for {methodCall}", "ReferenceError"); + + values.Add(value); + } + return values; + } + [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "Suppressing the warning until gets fixed, see https://github.com/dotnet/runtime/issues/51202")] internal static async Task CompileAndRunTheExpression(string expression, MemberReferenceResolver resolver, CancellationToken token) @@ -231,17 +300,17 @@ namespace Microsoft.WebAssembly.Diagnostics throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); FindVariableNMethodCall findVarNMethodCall = new FindVariableNMethodCall(); - findVarNMethodCall.Visit(expressionTree); + findVarNMethodCall.VisitInternal(expressionTree); // this fails with `"a)"` // because the code becomes: return (a)); // and the returned expression from GetExpressionFromSyntaxTree is `a`! if (expressionTree.Kind() == SyntaxKind.IdentifierName || expressionTree.Kind() == SyntaxKind.ThisExpression) { - string var_name = expressionTree.ToString(); - JObject value = await resolver.Resolve(var_name, token); + string varName = expressionTree.ToString(); + JObject value = await resolver.Resolve(varName, token); if (value == null) - throw new ReturnAsErrorException($"Cannot find member named '{var_name}'.", "ReferenceError"); + throw new ReturnAsErrorException($"Cannot find member named '{varName}'.", "ReferenceError"); return value; } @@ -256,7 +325,19 @@ namespace Microsoft.WebAssembly.Diagnostics IList identifierValues = await ResolveIdentifiers(findVarNMethodCall.identifiers, resolver, token); - syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, memberAccessValues, identifierValues); + syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, memberAccessValues, identifierValues, null); + + if (findVarNMethodCall.hasMethodCalls) + { + expressionTree = GetExpressionFromSyntaxTree(syntaxTree); + + findVarNMethodCall.VisitInternal(expressionTree); + + IList methodValues = await ResolveMethodCalls(findVarNMethodCall.methodCall, findVarNMethodCall.memberAccessValues, resolver, token); + + syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, null, null, methodValues); + } + expressionTree = GetExpressionFromSyntaxTree(syntaxTree); if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); @@ -313,7 +394,7 @@ namespace Microsoft.WebAssembly.Diagnostics private static object ConvertCSharpToJSType(object v, ITypeSymbol type) { if (v == null) - return new { type = "object", subtype = "null", className = type.ToString() }; + return new { type = "object", subtype = "null", className = type.ToString(), description = type.ToString() }; if (v is string s) { diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index c4e578a0a2c..cd21ac64190 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -9,6 +9,8 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.IO; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; namespace Microsoft.WebAssembly.Diagnostics { @@ -22,14 +24,14 @@ namespace Microsoft.WebAssembly.Diagnostics private ILogger logger; private bool locals_fetched; - public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId session_id, int scope_id, ILogger logger) + public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId sessionId, int scopeId, ILogger logger) { - sessionId = session_id; - scopeId = scope_id; + this.sessionId = sessionId; + this.scopeId = scopeId; this.proxy = proxy; this.ctx = ctx; this.logger = logger; - scopeCache = ctx.GetCacheForScope(scope_id); + scopeCache = ctx.GetCacheForScope(scopeId); } public async Task GetValueFromObject(JToken objRet, CancellationToken token) { @@ -37,7 +39,7 @@ namespace Microsoft.WebAssembly.Diagnostics { if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) { - var exceptionObject = await proxy.sdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token); + var exceptionObject = await proxy.SdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token); var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("_message")); exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value() + ": " + exceptionObjectMessage["value"]?["value"]?.Value(); return exceptionObjectMessage["value"]?.Value(); @@ -51,10 +53,10 @@ namespace Microsoft.WebAssembly.Diagnostics { if (DotnetObjectId.TryParse(objRet?["get"]?["objectIdValue"]?.Value(), out DotnetObjectId objectId)) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.WriteObj(objectId, proxy.sdbHelper); - var ret = await proxy.sdbHelper.InvokeMethod(sessionId, command_params.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.WriteObj(objectId, proxy.SdbHelper); + var ret = await proxy.SdbHelper.InvokeMethod(sessionId, commandParams.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token); return await GetValueFromObject(ret, token); } @@ -62,12 +64,16 @@ namespace Microsoft.WebAssembly.Diagnostics return null; } // Checks Locals, followed by `this` - public async Task Resolve(string var_name, CancellationToken token) + public async Task Resolve(string varName, CancellationToken token) { - string[] parts = var_name.Split("."); + //has method calls + if (varName.Contains('(')) + return null; + + string[] parts = varName.Split("."); JObject rootObject = null; - if (scopeCache.MemberReferences.TryGetValue(var_name, out JObject ret)) { + if (scopeCache.MemberReferences.TryGetValue(varName, out JObject ret)) { return ret; } foreach (string part in parts) @@ -81,8 +87,8 @@ namespace Microsoft.WebAssembly.Diagnostics return null; if (DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId)) { - var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); - var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + var rootResObj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = rootResObj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); if (objRet == null) return null; @@ -109,8 +115,8 @@ namespace Microsoft.WebAssembly.Diagnostics } else if (DotnetObjectId.TryParse(objThis?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId)) { - var root_res_obj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); - var objRet = root_res_obj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); + var rootResObj = await proxy.RuntimeGetPropertiesInternal(sessionId, objectId, null, token); + var objRet = rootResObj.FirstOrDefault(objPropAttr => objPropAttr["name"].Value() == partTrimmed); if (objRet != null) { rootObject = await GetValueFromObject(objRet, token); @@ -122,9 +128,62 @@ namespace Microsoft.WebAssembly.Diagnostics } } } - scopeCache.MemberReferences[var_name] = rootObject; + scopeCache.MemberReferences[varName] = rootObject; return rootObject; } + public async Task Resolve(InvocationExpressionSyntax method, Dictionary memberAccessValues, CancellationToken token) + { + var methodName = ""; + try + { + JObject rootObject = null; + var expr = method.Expression; + if (expr is MemberAccessExpressionSyntax) + { + var memberAccessExpressionSyntax = expr as MemberAccessExpressionSyntax; + rootObject = await Resolve(memberAccessExpressionSyntax.Expression.ToString(), token); + methodName = memberAccessExpressionSyntax.Name.ToString(); + } + if (rootObject != null) + { + DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId); + var typeId = await proxy.SdbHelper.GetTypeIdFromObject(sessionId, int.Parse(objectId.Value), true, token); + int methodId = await proxy.SdbHelper.GetMethodIdByName(sessionId, typeId[0], methodName, token); + if (methodId == 0) { + var typeName = await proxy.SdbHelper.GetTypeName(sessionId, typeId[0], token); + throw new Exception($"Method '{methodName}' not found in type '{typeName}'"); + } + var command_params_obj = new MemoryStream(); + var commandParamsObjWriter = new MonoBinaryWriter(command_params_obj); + commandParamsObjWriter.WriteObj(objectId, proxy.SdbHelper); + if (method.ArgumentList != null) + { + commandParamsObjWriter.Write((int)method.ArgumentList.Arguments.Count); + foreach (var arg in method.ArgumentList.Arguments) + { + if (arg.Expression is LiteralExpressionSyntax) + { + if (!await commandParamsObjWriter.WriteConst(sessionId, arg.Expression as LiteralExpressionSyntax, proxy.SdbHelper, token)) + return null; + } + if (arg.Expression is IdentifierNameSyntax) + { + var argParm = arg.Expression as IdentifierNameSyntax; + if (!await commandParamsObjWriter.WriteJsonValue(sessionId, memberAccessValues[argParm.Identifier.Text], proxy.SdbHelper, token)) + return null; + } + } + var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, command_params_obj.ToArray(), methodId, "methodRet", token); + return await GetValueFromObject(retMethod, token); + } + } + return null; + } + catch (Exception) + { + throw new Exception($"Unable to evaluate method '{methodName}'"); + } + } } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index f14bc1e16a9..1872ec1d2a9 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -18,7 +18,7 @@ namespace Microsoft.WebAssembly.Diagnostics { internal class MonoProxy : DevToolsProxy { - internal MonoSDBHelper sdbHelper; + internal MonoSDBHelper SdbHelper { get; } private IList urlSymbolServerList; private static HttpClient client = new HttpClient(); private HashSet sessions = new HashSet(); @@ -29,7 +29,7 @@ namespace Microsoft.WebAssembly.Diagnostics public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { this.urlSymbolServerList = urlSymbolServerList ?? new List(); - sdbHelper = new MonoSDBHelper(this); + SdbHelper = new MonoSDBHelper(this); } internal ExecutionContext GetContext(SessionId sessionId) @@ -449,7 +449,7 @@ namespace Microsoft.WebAssembly.Diagnostics } } else - await sdbHelper.EnableExceptions(id, state, token); + await SdbHelper.EnableExceptions(id, state, token); // Pass this on to JS too return false; } @@ -542,16 +542,16 @@ namespace Microsoft.WebAssembly.Diagnostics switch (objectId.Scheme) { case "object": - args["details"] = await sdbHelper.GetObjectProxy(id, int.Parse(objectId.Value), token); + args["details"] = await SdbHelper.GetObjectProxy(id, int.Parse(objectId.Value), token); break; case "valuetype": - args["details"] = await sdbHelper.GetValueTypeProxy(id, int.Parse(objectId.Value), token); + args["details"] = await SdbHelper.GetValueTypeProxy(id, int.Parse(objectId.Value), token); break; case "pointer": - args["details"] = await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token); + args["details"] = await SdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token); break; case "array": - args["details"] = await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + args["details"] = await SdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); break; case "cfo_res": { @@ -580,10 +580,10 @@ namespace Microsoft.WebAssembly.Diagnostics if (res.Value?["result"]?["value"]?["type"] == null) //it means that is not a buffer returned from the debugger-agent { byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); - var ret_debugger_cmd = new MemoryStream(newBytes); - var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); - ret_debugger_cmd_reader.ReadByte(); //number of objects returned. - var obj = await sdbHelper.CreateJObjectForVariableValue(id, ret_debugger_cmd_reader, "ret", false, -1, token); + var retDebuggerCmd = new MemoryStream(newBytes); + var retDebuggerCmdReader = new MonoBinaryReader(retDebuggerCmd); + retDebuggerCmdReader.ReadByte(); //number of objects returned. + var obj = await SdbHelper.CreateJObjectForVariableValue(id, retDebuggerCmdReader, "ret", false, -1, token); /*JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;*/ res = Result.OkFromObject(new { result = obj["value"]}); SendResponse(id, res, token); @@ -606,7 +606,7 @@ namespace Microsoft.WebAssembly.Diagnostics var varToSetValue = varIds.FirstOrDefault(v => v.Name == varName); if (varToSetValue == null) return false; - var res = await sdbHelper.SetVariableValue(id, ctx.ThreadId, scopeId, varToSetValue.Index, varValue["value"].Value(), token); + var res = await SdbHelper.SetVariableValue(id, ctx.ThreadId, scopeId, varToSetValue.Index, varValue["value"].Value(), token); if (res) SendResponse(id, Result.Ok(new JObject()), token); else @@ -635,13 +635,13 @@ namespace Microsoft.WebAssembly.Diagnostics return res.Value?["result"]; } case "valuetype": - return await sdbHelper.GetValueTypeValues(id, int.Parse(objectId.Value), accessorPropertiesOnly, token); + return await SdbHelper.GetValueTypeValues(id, int.Parse(objectId.Value), accessorPropertiesOnly, token); case "array": - return await sdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); + return await SdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token); case "object": - return await sdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token); + return await SdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token); case "pointer": - return new JArray{await sdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)}; + return new JArray{await SdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)}; case "cfo_res": { Result res = await SendMonoCommand(id, MonoCommands.GetDetails(int.Parse(objectId.Value), args), token); @@ -690,34 +690,34 @@ namespace Microsoft.WebAssembly.Diagnostics return false; } - private async Task ProcessEnC(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + private async Task ProcessEnC(SessionId sessionId, ExecutionContext context, MonoBinaryReader retDebuggerCmdReader, CancellationToken token) { - int moduleId = ret_debugger_cmd_reader.ReadInt32(); - int meta_size = ret_debugger_cmd_reader.ReadInt32(); - byte[] meta_buf = ret_debugger_cmd_reader.ReadBytes(meta_size); - int pdb_size = ret_debugger_cmd_reader.ReadInt32(); - byte[] pdb_buf = ret_debugger_cmd_reader.ReadBytes(pdb_size); + int moduleId = retDebuggerCmdReader.ReadInt32(); + int meta_size = retDebuggerCmdReader.ReadInt32(); + byte[] meta_buf = retDebuggerCmdReader.ReadBytes(meta_size); + int pdb_size = retDebuggerCmdReader.ReadInt32(); + byte[] pdb_buf = retDebuggerCmdReader.ReadBytes(pdb_size); - var assembly_name = await sdbHelper.GetAssemblyNameFromModule(sessionId, moduleId, token); + var assemblyName = await SdbHelper.GetAssemblyNameFromModule(sessionId, moduleId, token); DebugStore store = await LoadStore(sessionId, token); - AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + AssemblyInfo asm = store.GetAssemblyByName(assemblyName); foreach (var method in store.EnC(sessionId, asm, meta_buf, pdb_buf)) await ResetBreakpoint(sessionId, method, token); return true; } - private async Task SendBreakpointsOfMethodUpdated(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + private async Task SendBreakpointsOfMethodUpdated(SessionId sessionId, ExecutionContext context, MonoBinaryReader retDebuggerCmdReader, CancellationToken token) { - var method_id = ret_debugger_cmd_reader.ReadInt32(); - var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); - var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); - var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); - var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + var method_id = retDebuggerCmdReader.ReadInt32(); + var method_token = await SdbHelper.GetMethodToken(sessionId, method_id, token); + var assembly_id = await SdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); + var assembly_name = await SdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await SdbHelper.GetMethodName(sessionId, method_id, token); DebugStore store = await LoadStore(sessionId, token); AssemblyInfo asm = store.GetAssemblyByName(assembly_name); if (asm == null) { - assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); + assembly_name = await SdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); asm = store.GetAssemblyByName(assembly_name); if (asm == null) { @@ -743,28 +743,28 @@ namespace Microsoft.WebAssembly.Diagnostics { var callFrames = new List(); var frames = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(thread_id); - command_params_writer.Write(0); - command_params_writer.Write(-1); - var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdThread.GetFrameInfo, command_params, token); - var frame_count = ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(thread_id); + commandParamsWriter.Write(0); + commandParamsWriter.Write(-1); + var retDebuggerCmdReader = await SdbHelper.SendDebuggerAgentCommand(sessionId, CmdThread.GetFrameInfo, commandParams, token); + var frame_count = retDebuggerCmdReader.ReadInt32(); //Console.WriteLine("frame_count - " + frame_count); for (int j = 0; j < frame_count; j++) { - var frame_id = ret_debugger_cmd_reader.ReadInt32(); - var method_id = ret_debugger_cmd_reader.ReadInt32(); - var il_pos = ret_debugger_cmd_reader.ReadInt32(); - var flags = ret_debugger_cmd_reader.ReadByte(); - var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); - var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); - var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); - var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + var frame_id = retDebuggerCmdReader.ReadInt32(); + var methodId = retDebuggerCmdReader.ReadInt32(); + var il_pos = retDebuggerCmdReader.ReadInt32(); + var flags = retDebuggerCmdReader.ReadByte(); + var method_token = await SdbHelper.GetMethodToken(sessionId, methodId, token); + var assembly_id = await SdbHelper.GetAssemblyIdFromMethod(sessionId, methodId, token); + var assembly_name = await SdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await SdbHelper.GetMethodName(sessionId, methodId, token); DebugStore store = await LoadStore(sessionId, token); AssemblyInfo asm = store.GetAssemblyByName(assembly_name); if (asm == null) { - assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly + assembly_name = await SdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly asm = store.GetAssemblyByName(assembly_name); if (asm == null) { @@ -794,7 +794,7 @@ namespace Microsoft.WebAssembly.Diagnostics continue; } - method.DebuggerId = method_id; + method.DebuggerId = methodId; SourceLocation location = method?.GetLocationByIl(il_pos); @@ -881,43 +881,43 @@ namespace Microsoft.WebAssembly.Diagnostics ExecutionContext context = GetContext(sessionId); byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); - var ret_debugger_cmd = new MemoryStream(newBytes); - var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); - ret_debugger_cmd_reader.ReadBytes(11); //skip HEADER_LEN - ret_debugger_cmd_reader.ReadByte(); //suspend_policy - var number_of_events = ret_debugger_cmd_reader.ReadInt32(); //number of events -> should be always one + var retDebuggerCmd = new MemoryStream(newBytes); + var retDebuggerCmdReader = new MonoBinaryReader(retDebuggerCmd); + retDebuggerCmdReader.ReadBytes(11); //skip HEADER_LEN + retDebuggerCmdReader.ReadByte(); //suspend_policy + var number_of_events = retDebuggerCmdReader.ReadInt32(); //number of events -> should be always one for (int i = 0 ; i < number_of_events; i++) { - var event_kind = (EventKind)ret_debugger_cmd_reader.ReadByte(); //event kind - var request_id = ret_debugger_cmd_reader.ReadInt32(); //request id + var event_kind = (EventKind)retDebuggerCmdReader.ReadByte(); //event kind + var request_id = retDebuggerCmdReader.ReadInt32(); //request id if (event_kind == EventKind.Step) - await sdbHelper.ClearSingleStep(sessionId, request_id, token); - int thread_id = ret_debugger_cmd_reader.ReadInt32(); + await SdbHelper.ClearSingleStep(sessionId, request_id, token); + int thread_id = retDebuggerCmdReader.ReadInt32(); switch (event_kind) { case EventKind.MethodUpdate: { - var ret = await SendBreakpointsOfMethodUpdated(sessionId, context, ret_debugger_cmd_reader, token); + var ret = await SendBreakpointsOfMethodUpdated(sessionId, context, retDebuggerCmdReader, token); await SendCommand(sessionId, "Debugger.resume", new JObject(), token); return ret; } case EventKind.EnC: { - var ret = await ProcessEnC(sessionId, context, ret_debugger_cmd_reader, token); + var ret = await ProcessEnC(sessionId, context, retDebuggerCmdReader, token); await SendCommand(sessionId, "Debugger.resume", new JObject(), token); return ret; } case EventKind.Exception: { string reason = "exception"; - int object_id = ret_debugger_cmd_reader.ReadInt32(); - var caught = ret_debugger_cmd_reader.ReadByte(); - var exceptionObject = await sdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token); + int object_id = retDebuggerCmdReader.ReadInt32(); + var caught = retDebuggerCmdReader.ReadByte(); + var exceptionObject = await SdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token); var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("message")); var data = JObject.FromObject(new { type = "object", subtype = "error", - className = await sdbHelper.GetClassNameFromObject(sessionId, object_id, token), + className = await SdbHelper.GetClassNameFromObject(sessionId, object_id, token), uncaught = caught == 0, description = exceptionObjectMessage["value"]["value"].Value(), objectId = $"dotnet:object:{object_id}" @@ -932,9 +932,9 @@ namespace Microsoft.WebAssembly.Diagnostics { Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id); string reason = "other";//other means breakpoint - int method_id = 0; + int methodId = 0; if (event_kind != EventKind.UserBreak) - method_id = ret_debugger_cmd_reader.ReadInt32(); + methodId = retDebuggerCmdReader.ReadInt32(); var ret = await SendCallStack(sessionId, context, reason, thread_id, bp, null, args?["callFrames"]?.Values(), token); return ret; } @@ -1018,7 +1018,7 @@ namespace Microsoft.WebAssembly.Diagnostics } //discard managed frames - sdbHelper.ClearCache(); + SdbHelper.ClearCache(); GetContext(msg_id).ClearState(); } @@ -1031,9 +1031,9 @@ namespace Microsoft.WebAssembly.Diagnostics if (context.CallStack.Count <= 1 && kind == StepKind.Out) return false; - var step = await sdbHelper.Step(msg_id, context.ThreadId, kind, token); + var step = await SdbHelper.Step(msg_id, context.ThreadId, kind, token); if (step == false) { - sdbHelper.ClearCache(); + SdbHelper.ClearCache(); context.ClearState(); await SendCommand(msg_id, "Debugger.stepOut", new JObject(), token); return false; @@ -1110,7 +1110,7 @@ namespace Microsoft.WebAssembly.Diagnostics } } - private async Task OnEvaluateOnCallFrame(MessageId msg_id, int scope_id, string expression, CancellationToken token) + private async Task OnEvaluateOnCallFrame(MessageId msg_id, int scopeId, string expression, CancellationToken token) { try { @@ -1118,7 +1118,7 @@ namespace Microsoft.WebAssembly.Diagnostics if (context.CallStack == null) return false; - var resolver = new MemberReferenceResolver(this, context, msg_id, scope_id, logger); + var resolver = new MemberReferenceResolver(this, context, msg_id, scopeId, logger); JObject retValue = await resolver.Resolve(expression, token); if (retValue == null) @@ -1151,24 +1151,24 @@ namespace Microsoft.WebAssembly.Diagnostics return true; } - internal async Task GetScopeProperties(SessionId msg_id, int scope_id, CancellationToken token) + internal async Task GetScopeProperties(SessionId msg_id, int scopeId, CancellationToken token) { try { ExecutionContext ctx = GetContext(msg_id); - Frame scope = ctx.CallStack.FirstOrDefault(s => s.Id == scope_id); + Frame scope = ctx.CallStack.FirstOrDefault(s => s.Id == scopeId); if (scope == null) - return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scope_id}" })); + return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scopeId}" })); - VarInfo[] var_ids = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); + VarInfo[] varIds = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset); - var values = await sdbHelper.StackFrameGetValues(msg_id, scope.Method, ctx.ThreadId, scope_id, var_ids, token); + var values = await SdbHelper.StackFrameGetValues(msg_id, scope.Method, ctx.ThreadId, scopeId, varIds, token); if (values != null) { if (values == null || values.Count == 0) return Result.OkFromObject(new { result = Array.Empty() }); - PerScopeCache frameCache = ctx.GetCacheForScope(scope_id); + PerScopeCache frameCache = ctx.GetCacheForScope(scopeId); foreach (JObject value in values) { frameCache.Locals[value["name"]?.Value()] = value; @@ -1191,9 +1191,9 @@ namespace Microsoft.WebAssembly.Diagnostics int method_token = bp.Location.CliLocation.Method.Token; int il_offset = bp.Location.CliLocation.Offset; - var assembly_id = await sdbHelper.GetAssemblyId(sessionId, asm_name, token); - var method_id = await sdbHelper.GetMethodIdByToken(sessionId, assembly_id, method_token, token); - var breakpoint_id = await sdbHelper.SetBreakpoint(sessionId, method_id, il_offset, token); + var assembly_id = await SdbHelper.GetAssemblyId(sessionId, asm_name, token); + var methodId = await SdbHelper.GetMethodIdByToken(sessionId, assembly_id, method_token, token); + var breakpoint_id = await SdbHelper.SetBreakpoint(sessionId, methodId, il_offset, token); if (breakpoint_id > 0) { @@ -1258,22 +1258,22 @@ namespace Microsoft.WebAssembly.Diagnostics if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource(), null) != null) return await context.ready.Task; - var command_params = new MemoryStream(); - var ret_debugger_cmd_reader = await sdbHelper.SendDebuggerAgentCommand(sessionId, CmdEventRequest.ClearAllBreakpoints, command_params, token); - if (ret_debugger_cmd_reader == null) + var commandParams = new MemoryStream(); + var retDebuggerCmdReader = await SdbHelper.SendDebuggerAgentCommand(sessionId, CmdEventRequest.ClearAllBreakpoints, commandParams, token); + if (retDebuggerCmdReader == null) { Log("verbose", $"Failed to clear breakpoints"); } if (context.PauseOnCaught && context.PauseOnUncaught) - await sdbHelper.EnableExceptions(sessionId, "all", token); + await SdbHelper.EnableExceptions(sessionId, "all", token); else if (context.PauseOnUncaught) - await sdbHelper.EnableExceptions(sessionId, "uncaught", token); + await SdbHelper.EnableExceptions(sessionId, "uncaught", token); - await sdbHelper.SetProtocolVersion(sessionId, token); - await sdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token); - await sdbHelper.EnableReceiveRequests(sessionId, EventKind.EnC, token); - await sdbHelper.EnableReceiveRequests(sessionId, EventKind.MethodUpdate, token); + await SdbHelper.SetProtocolVersion(sessionId, token); + await SdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token); + await SdbHelper.EnableReceiveRequests(sessionId, EventKind.EnC, token); + await SdbHelper.EnableReceiveRequests(sessionId, EventKind.MethodUpdate, token); DebugStore store = await LoadStore(sessionId, token); @@ -1306,7 +1306,7 @@ namespace Microsoft.WebAssembly.Diagnostics foreach (Breakpoint bp in breakpointRequest.Locations) { - var breakpoint_removed = await sdbHelper.RemoveBreakpoint(msg_id, bp.RemoteId, token); + var breakpoint_removed = await SdbHelper.RemoveBreakpoint(msg_id, bp.RemoteId, token); if (breakpoint_removed) { bp.RemoteId = -1; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 42ad54e6e2c..d12533c1e9f 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -14,6 +14,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net.Http; using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.WebAssembly.Diagnostics { @@ -462,7 +464,7 @@ namespace Microsoft.WebAssembly.Diagnostics Array.Reverse(bytes, 0, bytes.Length); Write(bytes); } - public void WriteObj(DotnetObjectId objectId, MonoSDBHelper sdbHelper) + public void WriteObj(DotnetObjectId objectId, MonoSDBHelper SdbHelper) { if (objectId.Scheme == "object") { @@ -471,9 +473,90 @@ namespace Microsoft.WebAssembly.Diagnostics } if (objectId.Scheme == "valuetype") { - Write(sdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer); + Write(SdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer); } } + public async Task WriteConst(SessionId sessionId, LiteralExpressionSyntax constValue, MonoSDBHelper SdbHelper, CancellationToken token) + { + switch (constValue.Kind()) + { + case SyntaxKind.NumericLiteralExpression: + { + Write((byte)ElementType.I4); + Write((int)constValue.Token.Value); + return true; + } + case SyntaxKind.StringLiteralExpression: + { + int stringId = await SdbHelper.CreateString(sessionId, (string)constValue.Token.Value, token); + Write((byte)ElementType.String); + Write((int)stringId); + return true; + } + case SyntaxKind.TrueLiteralExpression: + { + Write((byte)ElementType.Boolean); + Write((int)1); + return true; + } + case SyntaxKind.FalseLiteralExpression: + { + Write((byte)ElementType.Boolean); + Write((int)0); + return true; + } + case SyntaxKind.NullLiteralExpression: + { + Write((byte)ValueTypeId.Null); + Write((byte)0); //not used + Write((int)0); //not used + return true; + } + case SyntaxKind.CharacterLiteralExpression: + { + Write((byte)ElementType.Char); + Write((int)(char)constValue.Token.Value); + return true; + } + } + return false; + } + + public async Task WriteJsonValue(SessionId sessionId, JObject objValue, MonoSDBHelper SdbHelper, CancellationToken token) + { + switch (objValue["type"].Value()) + { + case "number": + { + Write((byte)ElementType.I4); + Write(objValue["value"].Value()); + return true; + } + case "string": + { + int stringId = await SdbHelper.CreateString(sessionId, objValue["value"].Value(), token); + Write((byte)ElementType.String); + Write((int)stringId); + return true; + } + case "boolean": + { + Write((byte)ElementType.Boolean); + if (objValue["value"].Value()) + Write((int)1); + else + Write((int)0); + return true; + } + case "object": + { + DotnetObjectId.TryParse(objValue["objectId"]?.Value(), out DotnetObjectId objectId); + WriteObj(objectId, SdbHelper); + return true; + } + } + return false; + } } internal class FieldTypeClass { @@ -545,24 +628,24 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task SetProtocolVersion(SessionId sessionId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(MAJOR_VERSION); - command_params_writer.Write(MINOR_VERSION); - command_params_writer.Write((byte)0); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(MAJOR_VERSION); + commandParamsWriter.Write(MINOR_VERSION); + commandParamsWriter.Write((byte)0); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, commandParams, token); return true; } - public async Task EnableReceiveRequests(SessionId sessionId, EventKind event_kind, CancellationToken token) + public async Task EnableReceiveRequests(SessionId sessionId, EventKind eventKind, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)event_kind); - command_params_writer.Write((byte)SuspendPolicy.None); - command_params_writer.Write((byte)0); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)eventKind); + commandParamsWriter.Write((byte)SuspendPolicy.None); + commandParamsWriter.Write((byte)0); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); return true; } @@ -573,9 +656,9 @@ namespace Microsoft.WebAssembly.Diagnostics throw new Exception($"SendDebuggerAgentCommand Error - {(CommandSet)command_set} - {command}"); } byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); - var ret_debugger_cmd = new MemoryStream(newBytes); - var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); - return ret_debugger_cmd_reader; + var retDebuggerCmd = new MemoryStream(newBytes); + var retDebuggerCmdReader = new MonoBinaryReader(retDebuggerCmd); + return retDebuggerCmdReader; } internal CommandSet GetCommandSetForCommand(T command) => @@ -611,49 +694,61 @@ namespace Microsoft.WebAssembly.Diagnostics throw new Exception("SendDebuggerAgentCommandWithParms Error"); } byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); - var ret_debugger_cmd = new MemoryStream(newBytes); - var ret_debugger_cmd_reader = new MonoBinaryReader(ret_debugger_cmd); - return ret_debugger_cmd_reader; + var retDebuggerCmd = new MemoryStream(newBytes); + var retDebuggerCmdReader = new MonoBinaryReader(retDebuggerCmd); + return retDebuggerCmdReader; } - public async Task GetMethodToken(SessionId sessionId, int method_id, CancellationToken token) + public async Task CreateString(SessionId sessionId, string value, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAppDomain.GetRootDomain, commandParams, token); + var root_domain = retDebuggerCmdReader.ReadInt32(); + commandParamsWriter.Write(root_domain); + commandParamsWriter.WriteString(value); + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAppDomain.CreateString, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); + } - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Token, command_params, token); - return ret_debugger_cmd_reader.ReadInt32() & 0xffffff; //token + public async Task GetMethodToken(SessionId sessionId, int methodId, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); + + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Token, commandParams, token); + return retDebuggerCmdReader.ReadInt32() & 0xffffff; //token } public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, int method_token, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(assembly_id); - command_params_writer.Write(method_token | (int)TokenType.MdtMethodDef); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetMethodFromToken, command_params, token); - return ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(assembly_id); + commandParamsWriter.Write(method_token | (int)TokenType.MdtMethodDef); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetMethodFromToken, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); } - public async Task GetAssemblyIdFromMethod(SessionId sessionId, int method_id, CancellationToken token) + public async Task GetAssemblyIdFromMethod(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Assembly, command_params, token); - return ret_debugger_cmd_reader.ReadInt32(); //assembly_id + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.Assembly, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); //assembly_id } public async Task GetAssemblyId(SessionId sessionId, string asm_name, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.WriteString(asm_name); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.WriteString(asm_name); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetAssemblyByName, command_params, token); - return ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetAssemblyByName, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); } public async Task GetAssemblyNameFromModule(SessionId sessionId, int moduleId, CancellationToken token) @@ -669,90 +764,90 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(assembly_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(assembly_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetLocation, command_params, token); - return ret_debugger_cmd_reader.ReadString(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetLocation, commandParams, token); + return retDebuggerCmdReader.ReadString(); } public async Task GetAssemblyNameFull(SessionId sessionId, int assembly_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(assembly_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(assembly_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, command_params, token); - var name = ret_debugger_cmd_reader.ReadString(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, commandParams, token); + var name = retDebuggerCmdReader.ReadString(); return name.Remove(name.IndexOf(",")) + ".dll"; } - public async Task GetMethodName(SessionId sessionId, int method_id, CancellationToken token) + public async Task GetMethodName(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetNameFull, command_params, token); - var methodName = ret_debugger_cmd_reader.ReadString(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetNameFull, commandParams, token); + var methodName = retDebuggerCmdReader.ReadString(); return methodName.Substring(methodName.IndexOf(":")+1); } - public async Task MethodIsStatic(SessionId sessionId, int method_id, CancellationToken token) + public async Task MethodIsStatic(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetInfo, command_params, token); - var flags = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetInfo, commandParams, token); + var flags = retDebuggerCmdReader.ReadInt32(); return (flags & 0x0010) > 0; //check method is static } - public async Task GetParamCount(SessionId sessionId, int method_id, CancellationToken token) + public async Task GetParamCount(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); - ret_debugger_cmd_reader.ReadInt32(); - int param_count = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, commandParams, token); + retDebuggerCmdReader.ReadInt32(); + int param_count = retDebuggerCmdReader.ReadInt32(); return param_count; } - public async Task GetReturnType(SessionId sessionId, int method_id, CancellationToken token) + public async Task GetReturnType(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); - ret_debugger_cmd_reader.ReadInt32(); - ret_debugger_cmd_reader.ReadInt32(); - ret_debugger_cmd_reader.ReadInt32(); - var retType = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, commandParams, token); + retDebuggerCmdReader.ReadInt32(); + retDebuggerCmdReader.ReadInt32(); + retDebuggerCmdReader.ReadInt32(); + var retType = retDebuggerCmdReader.ReadInt32(); var ret = await GetTypeName(sessionId, retType, token); return ret; } - public async Task GetParameters(SessionId sessionId, int method_id, CancellationToken token) + public async Task GetParameters(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(method_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, command_params, token); - ret_debugger_cmd_reader.ReadInt32(); - var paramCount = ret_debugger_cmd_reader.ReadInt32(); - ret_debugger_cmd_reader.ReadInt32(); - var retType = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetParamInfo, commandParams, token); + retDebuggerCmdReader.ReadInt32(); + var paramCount = retDebuggerCmdReader.ReadInt32(); + retDebuggerCmdReader.ReadInt32(); + var retType = retDebuggerCmdReader.ReadInt32(); var parameters = "("; for (int i = 0 ; i < paramCount; i++) { - var paramType = ret_debugger_cmd_reader.ReadInt32(); + var paramType = retDebuggerCmdReader.ReadInt32(); parameters += await GetTypeName(sessionId, paramType, token); parameters = parameters.Replace("System.Func", "Func"); if (i + 1 < paramCount) @@ -762,50 +857,50 @@ namespace Microsoft.WebAssembly.Diagnostics return parameters; } - public async Task SetBreakpoint(SessionId sessionId, int method_id, long il_offset, CancellationToken token) + public async Task SetBreakpoint(SessionId sessionId, int methodId, long il_offset, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.Breakpoint); - command_params_writer.Write((byte)SuspendPolicy.None); - command_params_writer.Write((byte)1); - command_params_writer.Write((byte)ModifierKind.LocationOnly); - command_params_writer.Write(method_id); - command_params_writer.WriteLong(il_offset); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); - return ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)EventKind.Breakpoint); + commandParamsWriter.Write((byte)SuspendPolicy.None); + commandParamsWriter.Write((byte)1); + commandParamsWriter.Write((byte)ModifierKind.LocationOnly); + commandParamsWriter.Write(methodId); + commandParamsWriter.WriteLong(il_offset); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); } public async Task RemoveBreakpoint(SessionId sessionId, int breakpoint_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.Breakpoint); - command_params_writer.Write((int) breakpoint_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)EventKind.Breakpoint); + commandParamsWriter.Write((int) breakpoint_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, commandParams, token); - if (ret_debugger_cmd_reader != null) + if (retDebuggerCmdReader != null) return true; return false; } public async Task Step(SessionId sessionId, int thread_id, StepKind kind, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.Step); - command_params_writer.Write((byte)SuspendPolicy.None); - command_params_writer.Write((byte)1); - command_params_writer.Write((byte)ModifierKind.Step); - command_params_writer.Write(thread_id); - command_params_writer.Write((int)0); - command_params_writer.Write((int)kind); - command_params_writer.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); - if (ret_debugger_cmd_reader == null) + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)EventKind.Step); + commandParamsWriter.Write((byte)SuspendPolicy.None); + commandParamsWriter.Write((byte)1); + commandParamsWriter.Write((byte)ModifierKind.Step); + commandParamsWriter.Write(thread_id); + commandParamsWriter.Write((int)0); + commandParamsWriter.Write((int)kind); + commandParamsWriter.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); + if (retDebuggerCmdReader == null) return false; - var isBPOnManagedCode = ret_debugger_cmd_reader.ReadInt32(); + var isBPOnManagedCode = retDebuggerCmdReader.ReadInt32(); if (isBPOnManagedCode == 0) return false; return true; @@ -813,14 +908,14 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task ClearSingleStep(SessionId sessionId, int req_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.Step); - command_params_writer.Write((int) req_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)EventKind.Step); + commandParamsWriter.Write((int) req_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, command_params, token); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Clear, commandParams, token); - if (ret_debugger_cmd_reader != null) + if (retDebuggerCmdReader != null) return true; return false; } @@ -828,19 +923,19 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token) { var ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(type_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(type_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetFields, command_params, token); - var nFields = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetFields, commandParams, token); + var nFields = retDebuggerCmdReader.ReadInt32(); for (int i = 0 ; i < nFields; i++) { - int fieldId = ret_debugger_cmd_reader.ReadInt32(); //fieldId - string fieldNameStr = ret_debugger_cmd_reader.ReadString(); - int typeId = ret_debugger_cmd_reader.ReadInt32(); //typeId - ret_debugger_cmd_reader.ReadInt32(); //attrs + int fieldId = retDebuggerCmdReader.ReadInt32(); //fieldId + string fieldNameStr = retDebuggerCmdReader.ReadString(); + int typeId = retDebuggerCmdReader.ReadInt32(); //typeId + retDebuggerCmdReader.ReadInt32(); //attrs if (fieldNameStr.Contains("k__BackingField")) { fieldNameStr = fieldNameStr.Replace("k__BackingField", ""); @@ -864,17 +959,17 @@ namespace Microsoft.WebAssembly.Diagnostics } public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(type_id); - command_params_writer.Write((int) MonoTypeNameFormat.FormatReflection); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, command_params, token); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(type_id); + commandParamsWriter.Write((int) MonoTypeNameFormat.FormatReflection); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, commandParams, token); - ret_debugger_cmd_reader.ReadString(); + retDebuggerCmdReader.ReadString(); - ret_debugger_cmd_reader.ReadString(); + retDebuggerCmdReader.ReadString(); - string className = ret_debugger_cmd_reader.ReadString(); + string className = retDebuggerCmdReader.ReadString(); className = className.Replace("+", "."); className = Regex.Replace(className, @"`\d+", ""); @@ -889,47 +984,47 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task GetStringValue(SessionId sessionId, int string_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(string_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(string_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdString.GetValue, command_params, token); - var isUtf16 = ret_debugger_cmd_reader.ReadByte(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdString.GetValue, commandParams, token); + var isUtf16 = retDebuggerCmdReader.ReadByte(); if (isUtf16 == 0) { - return ret_debugger_cmd_reader.ReadString(); + return retDebuggerCmdReader.ReadString(); } return null; } public async Task GetArrayLength(SessionId sessionId, int object_id, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(object_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetLength, command_params, token); - var length = ret_debugger_cmd_reader.ReadInt32(); - length = ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(object_id); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetLength, commandParams, token); + var length = retDebuggerCmdReader.ReadInt32(); + length = retDebuggerCmdReader.ReadInt32(); return length; } public async Task> GetTypeIdFromObject(SessionId sessionId, int object_id, bool withParents, CancellationToken token) { List ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(object_id); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(object_id); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetType, command_params, token); - var type_id = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetType, commandParams, token); + var type_id = retDebuggerCmdReader.ReadInt32(); ret.Add(type_id); if (withParents) { - command_params = new MemoryStream(); - command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(type_id); - ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); - var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + commandParams = new MemoryStream(); + commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(type_id); + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, commandParams, token); + var parentsCount = retDebuggerCmdReader.ReadInt32(); for (int i = 0 ; i < parentsCount; i++) { - ret.Add(ret_debugger_cmd_reader.ReadInt32()); + ret.Add(retDebuggerCmdReader.ReadInt32()); } } return ret; @@ -944,82 +1039,82 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task GetMethodIdByName(SessionId sessionId, int type_id, string method_name, CancellationToken token) { var ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((int)type_id); - command_params_writer.WriteString(method_name); - command_params_writer.Write((int)(0x10 | 4)); //instance methods - command_params_writer.Write((int)1); //case sensitive - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, command_params, token); - var nMethods = ret_debugger_cmd_reader.ReadInt32(); - return ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((int)type_id); + commandParamsWriter.WriteString(method_name); + commandParamsWriter.Write((int)(0x10 | 4)); //instance methods + commandParamsWriter.Write((int)1); //case sensitive + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, commandParams, token); + var nMethods = retDebuggerCmdReader.ReadInt32(); + return retDebuggerCmdReader.ReadInt32(); } public async Task IsDelegate(SessionId sessionId, int objectId, CancellationToken token) { var ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((int)objectId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefIsDelegate, command_params, token); - return ret_debugger_cmd_reader.ReadByte() == 1; + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((int)objectId); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefIsDelegate, commandParams, token); + return retDebuggerCmdReader.ReadByte() == 1; } public async Task GetDelegateMethod(SessionId sessionId, int objectId, CancellationToken token) { var ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((int)objectId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefDelegateGetMethod, command_params, token); - return ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((int)objectId); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefDelegateGetMethod, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); } public async Task GetDelegateMethodDescription(SessionId sessionId, int objectId, CancellationToken token) { var methodId = await GetDelegateMethod(sessionId, objectId, token); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(methodId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); //Console.WriteLine("methodId - " + methodId); if (methodId == 0) return ""; - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetName, command_params, token); - var methodName = ret_debugger_cmd_reader.ReadString(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetName, commandParams, token); + var methodName = retDebuggerCmdReader.ReadString(); var returnType = await GetReturnType(sessionId, methodId, token); var parameters = await GetParameters(sessionId, methodId, token); return $"{returnType} {methodName} {parameters}"; } - public async Task InvokeMethod(SessionId sessionId, byte[] valueTypeBuffer, int method_id, string varName, CancellationToken token) + public async Task InvokeMethod(SessionId sessionId, byte[] valueTypeBuffer, int methodId, string varName, CancellationToken token) { MemoryStream parms = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(parms); - command_params_writer.Write(method_id); - command_params_writer.Write(valueTypeBuffer); - command_params_writer.Write(0); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.InvokeMethod, parms, token); - ret_debugger_cmd_reader.ReadByte(); //number of objects returned. - return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, varName, false, -1, token); + var commandParamsWriter = new MonoBinaryWriter(parms); + commandParamsWriter.Write(methodId); + commandParamsWriter.Write(valueTypeBuffer); + commandParamsWriter.Write(0); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdVM.InvokeMethod, parms, token); + retDebuggerCmdReader.ReadByte(); //number of objects returned. + return await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, varName, false, -1, token); } public async Task CreateJArrayForProperties(SessionId sessionId, int typeId, byte[] object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token) { JArray ret = new JArray(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(typeId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(typeId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); - var nProperties = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, commandParams, token); + var nProperties = retDebuggerCmdReader.ReadInt32(); for (int i = 0 ; i < nProperties; i++) { - ret_debugger_cmd_reader.ReadInt32(); //propertyId - string propertyNameStr = ret_debugger_cmd_reader.ReadString(); - var getMethodId = ret_debugger_cmd_reader.ReadInt32(); - ret_debugger_cmd_reader.ReadInt32(); //setmethod - var attrs = ret_debugger_cmd_reader.ReadInt32(); //attrs + retDebuggerCmdReader.ReadInt32(); //propertyId + string propertyNameStr = retDebuggerCmdReader.ReadString(); + var getMethodId = retDebuggerCmdReader.ReadInt32(); + retDebuggerCmdReader.ReadInt32(); //setmethod + var attrs = retDebuggerCmdReader.ReadInt32(); //attrs if (getMethodId == 0 || await GetParamCount(sessionId, getMethodId, token) != 0 || await MethodIsStatic(sessionId, getMethodId, token)) continue; JObject propRet = null; @@ -1041,7 +1136,7 @@ namespace Microsoft.WebAssembly.Diagnostics get = new { type = "function", - objectId = $"{objectId}:method_id:{getMethodId}", + objectId = $"{objectId}:methodId:{getMethodId}", className = "Function", description = "get " + propertyNameStr + " ()", methodId = getMethodId, @@ -1059,30 +1154,30 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task GetPointerContent(SessionId sessionId, int pointerId, CancellationToken token) { var ret = new List(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.WriteLong(pointerValues[pointerId].address); - command_params_writer.Write(pointerValues[pointerId].typeId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdPointer.GetValue, command_params, token); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.WriteLong(pointerValues[pointerId].address); + commandParamsWriter.Write(pointerValues[pointerId].typeId); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdPointer.GetValue, commandParams, token); var varName = pointerValues[pointerId].varName; if (int.TryParse(varName, out _)) varName = $"[{varName}]"; - return await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "*" + varName, false, -1, token); + return await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, "*" + varName, false, -1, token); } public async Task GetPropertiesValuesOfValueType(SessionId sessionId, int valueTypeId, CancellationToken token) { JArray ret = new JArray(); var valueType = valueTypes[valueTypeId]; - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(valueType.typeId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, command_params, token); - var parentsCount = ret_debugger_cmd_reader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(valueType.typeId); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetParents, commandParams, token); + var parentsCount = retDebuggerCmdReader.ReadInt32(); List typesToGetProperties = new List(); typesToGetProperties.Add(valueType.typeId); for (int i = 0 ; i < parentsCount; i++) { - typesToGetProperties.Add(ret_debugger_cmd_reader.ReadInt32()); + typesToGetProperties.Add(retDebuggerCmdReader.ReadInt32()); } for (int i = 0 ; i < typesToGetProperties.Count; i++) { @@ -1155,12 +1250,12 @@ namespace Microsoft.WebAssembly.Diagnostics return CreateJObject(description, "symbol", description, true); } - public async Task CreateJObjectForPtr(SessionId sessionId, ElementType etype, MonoBinaryReader ret_debugger_cmd_reader, string name, CancellationToken token) + public async Task CreateJObjectForPtr(SessionId sessionId, ElementType etype, MonoBinaryReader retDebuggerCmdReader, string name, CancellationToken token) { string type; string value; - long valueAddress = ret_debugger_cmd_reader.ReadLong(); - var typeId = ret_debugger_cmd_reader.ReadInt32(); + long valueAddress = retDebuggerCmdReader.ReadLong(); + var typeId = retDebuggerCmdReader.ReadInt32(); var className = ""; if (etype == ElementType.FnPtr) className = "(*())"; //to keep the old behavior @@ -1183,24 +1278,24 @@ namespace Microsoft.WebAssembly.Diagnostics return CreateJObject(value, type, value, false, className, $"dotnet:pointer:{pointerId}", "pointer"); } - public async Task CreateJObjectForString(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + public async Task CreateJObjectForString(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, CancellationToken token) { - var string_id = ret_debugger_cmd_reader.ReadInt32(); + var string_id = retDebuggerCmdReader.ReadInt32(); var value = await GetStringValue(sessionId, string_id, token); return CreateJObject(value, "string", value, false); } - public async Task CreateJObjectForArray(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + public async Task CreateJObjectForArray(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, CancellationToken token) { - var objectId = ret_debugger_cmd_reader.ReadInt32(); + var objectId = retDebuggerCmdReader.ReadInt32(); var value = await GetClassNameFromObject(sessionId, objectId, token); var length = await GetArrayLength(sessionId, objectId, token); return CreateJObject(null, "object", $"{value.ToString()}({length})", false, value.ToString(), "dotnet:array:" + objectId, null, "array"); } - public async Task CreateJObjectForObject(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, int typeIdFromAttribute, CancellationToken token) + public async Task CreateJObjectForObject(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, int typeIdFromAttribute, CancellationToken token) { - var objectId = ret_debugger_cmd_reader.ReadInt32(); + var objectId = retDebuggerCmdReader.ReadInt32(); var className = ""; var type_id = await GetTypeIdFromObject(sessionId, objectId, false, token); className = await GetTypeName(sessionId, type_id[0], token); @@ -1221,22 +1316,22 @@ namespace Microsoft.WebAssembly.Diagnostics return CreateJObject(null, "object", description, false, className, $"dotnet:object:{objectId}"); } - public async Task CreateJObjectForValueType(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, long initialPos, CancellationToken token) + public async Task CreateJObjectForValueType(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, string name, long initialPos, CancellationToken token) { JObject fieldValueType = null; - var isEnum = ret_debugger_cmd_reader.ReadByte(); - var isBoxed = ret_debugger_cmd_reader.ReadByte() == 1; - var typeId = ret_debugger_cmd_reader.ReadInt32(); + var isEnum = retDebuggerCmdReader.ReadByte(); + var isBoxed = retDebuggerCmdReader.ReadByte() == 1; + var typeId = retDebuggerCmdReader.ReadInt32(); var className = await GetTypeName(sessionId, typeId, token); var description = className; - var numFields = ret_debugger_cmd_reader.ReadInt32(); + var numFields = retDebuggerCmdReader.ReadInt32(); var fields = await GetTypeFields(sessionId, typeId, token); JArray valueTypeFields = new JArray(); if (className.IndexOf("System.Nullable<") == 0) //should we call something on debugger-agent to check??? { - ret_debugger_cmd_reader.ReadByte(); //ignoring the boolean type - var isNull = ret_debugger_cmd_reader.ReadInt32(); - var value = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, name, false, -1, token); + retDebuggerCmdReader.ReadByte(); //ignoring the boolean type + var isNull = retDebuggerCmdReader.ReadInt32(); + var value = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, name, false, -1, token); if (isNull != 0) return value; else @@ -1244,21 +1339,21 @@ namespace Microsoft.WebAssembly.Diagnostics } for (int i = 0; i < numFields ; i++) { - fieldValueType = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, fields.ElementAt(i).Name, true, fields.ElementAt(i).TypeId, token); + fieldValueType = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, fields.ElementAt(i).Name, true, fields.ElementAt(i).TypeId, token); valueTypeFields.Add(fieldValueType); } - long endPos = ret_debugger_cmd_reader.BaseStream.Position; + long endPos = retDebuggerCmdReader.BaseStream.Position; var valueTypeId = Interlocked.Increment(ref debugger_object_id); - ret_debugger_cmd_reader.BaseStream.Position = initialPos; + retDebuggerCmdReader.BaseStream.Position = initialPos; byte[] valueTypeBuffer = new byte[endPos - initialPos]; - ret_debugger_cmd_reader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos)); - ret_debugger_cmd_reader.BaseStream.Position = endPos; + retDebuggerCmdReader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos)); + retDebuggerCmdReader.BaseStream.Position = endPos; valueTypes[valueTypeId] = new ValueTypeClass(name, valueTypeBuffer, valueTypeFields, typeId, AutoExpandable(className), valueTypeId); if (AutoInvokeToString(className) || isEnum == 1) { - int method_id = await GetMethodIdByName(sessionId, typeId, "ToString", token); - var retMethod = await InvokeMethod(sessionId, valueTypeBuffer, method_id, "methodRet", token); + int methodId = await GetMethodIdByName(sessionId, typeId, "ToString", token); + var retMethod = await InvokeMethod(sessionId, valueTypeBuffer, methodId, "methodRet", token); description = retMethod["value"]?["value"].Value(); if (className.Equals("System.Guid")) description = description.ToUpper(); //to keep the old behavior @@ -1269,16 +1364,16 @@ namespace Microsoft.WebAssembly.Diagnostics return CreateJObject(null, "object", description, false, className, $"dotnet:valuetype:{valueTypeId}", null, null, true, true, isEnum == 1); } - public async Task CreateJObjectForNull(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + public async Task CreateJObjectForNull(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, CancellationToken token) { string className = ""; - ElementType variableType = (ElementType)ret_debugger_cmd_reader.ReadByte(); + ElementType variableType = (ElementType)retDebuggerCmdReader.ReadByte(); switch (variableType) { case ElementType.String: case ElementType.Class: { - var type_id = ret_debugger_cmd_reader.ReadInt32(); + var type_id = retDebuggerCmdReader.ReadInt32(); className = await GetTypeName(sessionId, type_id, token); break; @@ -1286,18 +1381,18 @@ namespace Microsoft.WebAssembly.Diagnostics case ElementType.SzArray: case ElementType.Array: { - ElementType byte_type = (ElementType)ret_debugger_cmd_reader.ReadByte(); - var rank = ret_debugger_cmd_reader.ReadInt32(); + ElementType byte_type = (ElementType)retDebuggerCmdReader.ReadByte(); + var rank = retDebuggerCmdReader.ReadInt32(); if (byte_type == ElementType.Class) { - var internal_type_id = ret_debugger_cmd_reader.ReadInt32(); + var internal_type_id = retDebuggerCmdReader.ReadInt32(); } - var type_id = ret_debugger_cmd_reader.ReadInt32(); + var type_id = retDebuggerCmdReader.ReadInt32(); className = await GetTypeName(sessionId, type_id, token); break; } default: { - var type_id = ret_debugger_cmd_reader.ReadInt32(); + var type_id = retDebuggerCmdReader.ReadInt32(); className = await GetTypeName(sessionId, type_id, token); break; } @@ -1305,10 +1400,10 @@ namespace Microsoft.WebAssembly.Diagnostics return CreateJObject(null, "object", className, false, className, null, null, "null"); } - public async Task CreateJObjectForVariableValue(SessionId sessionId, MonoBinaryReader ret_debugger_cmd_reader, string name, bool isOwn, int typeIdFromAttribute, CancellationToken token) + public async Task CreateJObjectForVariableValue(SessionId sessionId, MonoBinaryReader retDebuggerCmdReader, string name, bool isOwn, int typeIdFromAttribute, CancellationToken token) { - long initialPos = ret_debugger_cmd_reader == null ? 0 : ret_debugger_cmd_reader.BaseStream.Position; - ElementType etype = (ElementType)ret_debugger_cmd_reader.ReadByte(); + long initialPos = retDebuggerCmdReader == null ? 0 : retDebuggerCmdReader.BaseStream.Position; + ElementType etype = (ElementType)retDebuggerCmdReader.ReadByte(); JObject ret = null; switch (etype) { case ElementType.I: @@ -1317,108 +1412,114 @@ namespace Microsoft.WebAssembly.Diagnostics case (ElementType)ValueTypeId.Type: case (ElementType)ValueTypeId.VType: case (ElementType)ValueTypeId.FixedArray: - ret = new JObject{{"Type", "void"}}; + ret = JObject.FromObject(new { + value = new + { + type = "void", + value = "void", + description = "void" + }}); break; case ElementType.Boolean: { - var value = ret_debugger_cmd_reader.ReadInt32(); + var value = retDebuggerCmdReader.ReadInt32(); ret = CreateJObjectForBoolean(value); break; } case ElementType.I1: { - var value = ret_debugger_cmd_reader.ReadSByte(); + var value = retDebuggerCmdReader.ReadSByte(); ret = CreateJObjectForNumber(value); break; } case ElementType.I2: case ElementType.I4: { - var value = ret_debugger_cmd_reader.ReadInt32(); + var value = retDebuggerCmdReader.ReadInt32(); ret = CreateJObjectForNumber(value); break; } case ElementType.U1: { - var value = ret_debugger_cmd_reader.ReadUByte(); + var value = retDebuggerCmdReader.ReadUByte(); ret = CreateJObjectForNumber(value); break; } case ElementType.U2: { - var value = ret_debugger_cmd_reader.ReadUShort(); + var value = retDebuggerCmdReader.ReadUShort(); ret = CreateJObjectForNumber(value); break; } case ElementType.U4: { - var value = ret_debugger_cmd_reader.ReadUInt32(); + var value = retDebuggerCmdReader.ReadUInt32(); ret = CreateJObjectForNumber(value); break; } case ElementType.R4: { - float value = BitConverter.Int32BitsToSingle(ret_debugger_cmd_reader.ReadInt32()); + float value = BitConverter.Int32BitsToSingle(retDebuggerCmdReader.ReadInt32()); ret = CreateJObjectForNumber(value); break; } case ElementType.Char: { - var value = ret_debugger_cmd_reader.ReadInt32(); + var value = retDebuggerCmdReader.ReadInt32(); ret = CreateJObjectForChar(value); break; } case ElementType.I8: { - long value = ret_debugger_cmd_reader.ReadLong(); + long value = retDebuggerCmdReader.ReadLong(); ret = CreateJObjectForNumber(value); break; } case ElementType.U8: { - ulong high = (ulong) ret_debugger_cmd_reader.ReadInt32(); - ulong low = (ulong) ret_debugger_cmd_reader.ReadInt32(); + ulong high = (ulong) retDebuggerCmdReader.ReadInt32(); + ulong low = (ulong) retDebuggerCmdReader.ReadInt32(); var value = ((high << 32) | low); ret = CreateJObjectForNumber(value); break; } case ElementType.R8: { - double value = ret_debugger_cmd_reader.ReadDouble(); + double value = retDebuggerCmdReader.ReadDouble(); ret = CreateJObjectForNumber(value); break; } case ElementType.FnPtr: case ElementType.Ptr: { - ret = await CreateJObjectForPtr(sessionId, etype, ret_debugger_cmd_reader, name, token); + ret = await CreateJObjectForPtr(sessionId, etype, retDebuggerCmdReader, name, token); break; } case ElementType.String: { - ret = await CreateJObjectForString(sessionId, ret_debugger_cmd_reader, token); + ret = await CreateJObjectForString(sessionId, retDebuggerCmdReader, token); break; } case ElementType.SzArray: case ElementType.Array: { - ret = await CreateJObjectForArray(sessionId, ret_debugger_cmd_reader, token); + ret = await CreateJObjectForArray(sessionId, retDebuggerCmdReader, token); break; } case ElementType.Class: case ElementType.Object: { - ret = await CreateJObjectForObject(sessionId, ret_debugger_cmd_reader, typeIdFromAttribute, token); + ret = await CreateJObjectForObject(sessionId, retDebuggerCmdReader, typeIdFromAttribute, token); break; } case ElementType.ValueType: { - ret = await CreateJObjectForValueType(sessionId, ret_debugger_cmd_reader, name, initialPos, token); + ret = await CreateJObjectForValueType(sessionId, retDebuggerCmdReader, name, initialPos, token); break; } case (ElementType)ValueTypeId.Null: { - ret = await CreateJObjectForNull(sessionId, ret_debugger_cmd_reader, token); + ret = await CreateJObjectForNull(sessionId, retDebuggerCmdReader, token); break; } } @@ -1430,32 +1531,32 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task IsAsyncMethod(SessionId sessionId, int methodId, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(methodId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdMethod.AsyncDebugInfo, command_params, token); - return ret_debugger_cmd_reader.ReadByte() == 1 ; //token + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.AsyncDebugInfo, commandParams, token); + return retDebuggerCmdReader.ReadByte() == 1 ; //token } - public async Task StackFrameGetValues(SessionId sessionId, MethodInfo method, int thread_id, int frame_id, VarInfo[] var_ids, CancellationToken token) + public async Task StackFrameGetValues(SessionId sessionId, MethodInfo method, int thread_id, int frame_id, VarInfo[] varIds, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - MonoBinaryReader ret_debugger_cmd_reader = null; - command_params_writer.Write(thread_id); - command_params_writer.Write(frame_id); - command_params_writer.Write(var_ids.Length); - foreach (var var in var_ids) + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + MonoBinaryReader retDebuggerCmdReader = null; + commandParamsWriter.Write(thread_id); + commandParamsWriter.Write(frame_id); + commandParamsWriter.Write(varIds.Length); + foreach (var var in varIds) { - command_params_writer.Write(var.Index); + commandParamsWriter.Write(var.Index); } if (await IsAsyncMethod(sessionId, method.DebuggerId, token)) { - ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); - ret_debugger_cmd_reader.ReadByte(); //ignore type - var objectId = ret_debugger_cmd_reader.ReadInt32(); + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, commandParams, token); + retDebuggerCmdReader.ReadByte(); //ignore type + var objectId = retDebuggerCmdReader.ReadInt32(); var asyncLocals = await GetObjectValues(sessionId, objectId, true, false, false, false, token); asyncLocals = new JArray(asyncLocals.Where( asyncLocal => !asyncLocal["name"].Value().Contains("<>") || asyncLocal["name"].Value().EndsWith("__this"))); foreach (var asyncLocal in asyncLocals) @@ -1469,16 +1570,16 @@ namespace Microsoft.WebAssembly.Diagnostics } JArray locals = new JArray(); - ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); - foreach (var var in var_ids) + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, commandParams, token); + foreach (var var in varIds) { - var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, var.Name, false, -1, token); + var var_json = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, var.Name, false, -1, token); locals.Add(var_json); } if (!method.IsStatic()) { - ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, command_params, token); - var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, "this", false, -1, token); + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, commandParams, token); + var var_json = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, "this", false, -1, token); var_json.Add("fieldOffset", -1); locals.Add(var_json); } @@ -1504,21 +1605,21 @@ namespace Microsoft.WebAssembly.Diagnostics return valueTypes[valueTypeId].valueTypeProxy; valueTypes[valueTypeId].valueTypeProxy = new JArray(valueTypes[valueTypeId].valueTypeJson); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(valueTypes[valueTypeId].typeId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(valueTypes[valueTypeId].typeId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); - var nProperties = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, commandParams, token); + var nProperties = retDebuggerCmdReader.ReadInt32(); for (int i = 0 ; i < nProperties; i++) { - ret_debugger_cmd_reader.ReadInt32(); //propertyId - string propertyNameStr = ret_debugger_cmd_reader.ReadString(); + retDebuggerCmdReader.ReadInt32(); //propertyId + string propertyNameStr = retDebuggerCmdReader.ReadString(); - var getMethodId = ret_debugger_cmd_reader.ReadInt32(); - ret_debugger_cmd_reader.ReadInt32(); //setmethod - ret_debugger_cmd_reader.ReadInt32(); //attrs + var getMethodId = retDebuggerCmdReader.ReadInt32(); + retDebuggerCmdReader.ReadInt32(); //setmethod + retDebuggerCmdReader.ReadInt32(); //attrs if (await MethodIsStatic(sessionId, getMethodId, token)) continue; var command_params_to_proxy = new MemoryStream(); @@ -1542,16 +1643,16 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task GetArrayValues(SessionId sessionId, int arrayId, CancellationToken token) { var length = await GetArrayLength(sessionId, arrayId, token); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(arrayId); - command_params_writer.Write(0); - command_params_writer.Write(length); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetValues, command_params, token); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(arrayId); + commandParamsWriter.Write(0); + commandParamsWriter.Write(length); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdArray.GetValues, commandParams, token); JArray array = new JArray(); for (int i = 0 ; i < length ; i++) { - var var_json = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, i.ToString(), false, -1, token); + var var_json = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, i.ToString(), false, -1, token); array.Add(var_json); } return array; @@ -1559,25 +1660,25 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task EnableExceptions(SessionId sessionId, string state, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.Exception); - command_params_writer.Write((byte)SuspendPolicy.None); - command_params_writer.Write((byte)1); - command_params_writer.Write((byte)ModifierKind.ExceptionOnly); - command_params_writer.Write(0); //exc_class + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write((byte)EventKind.Exception); + commandParamsWriter.Write((byte)SuspendPolicy.None); + commandParamsWriter.Write((byte)1); + commandParamsWriter.Write((byte)ModifierKind.ExceptionOnly); + commandParamsWriter.Write(0); //exc_class if (state == "all") - command_params_writer.Write((byte)1); //caught + commandParamsWriter.Write((byte)1); //caught else - command_params_writer.Write((byte)0); //caught + commandParamsWriter.Write((byte)0); //caught if (state == "uncaught" || state == "all") - command_params_writer.Write((byte)1); //uncaught + commandParamsWriter.Write((byte)1); //uncaught else - command_params_writer.Write((byte)0); //uncaught - command_params_writer.Write((byte)1);//subclasses - command_params_writer.Write((byte)0);//not_filtered_feature - command_params_writer.Write((byte)0);//everything_else - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); + commandParamsWriter.Write((byte)0); //uncaught + commandParamsWriter.Write((byte)1);//subclasses + commandParamsWriter.Write((byte)0);//not_filtered_feature + commandParamsWriter.Write((byte)0);//everything_else + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); return true; } public async Task GetObjectValues(SessionId sessionId, int objectId, bool withProperties, bool withSetter, bool accessorPropertiesOnly, bool ownProperties, CancellationToken token) @@ -1608,23 +1709,23 @@ namespace Microsoft.WebAssembly.Diagnostics var fields = await GetTypeFields(sessionId, typeId[i], token); JArray objectFields = new JArray(); - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(objectId); - command_params_writer.Write(fields.Count); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(objectId); + commandParamsWriter.Write(fields.Count); foreach (var field in fields) { - command_params_writer.Write(field.Id); + commandParamsWriter.Write(field.Id); } - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetValues, command_params, token); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdObject.RefGetValues, commandParams, token); foreach (var field in fields) { - long initialPos = ret_debugger_cmd_reader.BaseStream.Position; - int valtype = ret_debugger_cmd_reader.ReadByte(); - ret_debugger_cmd_reader.BaseStream.Position = initialPos; - var fieldValue = await CreateJObjectForVariableValue(sessionId, ret_debugger_cmd_reader, field.Name, i == 0, field.TypeId, token); + long initialPos = retDebuggerCmdReader.BaseStream.Position; + int valtype = retDebuggerCmdReader.ReadByte(); + retDebuggerCmdReader.BaseStream.Position = initialPos; + var fieldValue = await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, field.Name, i == 0, field.TypeId, token); if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { continue; @@ -1652,8 +1753,8 @@ namespace Microsoft.WebAssembly.Diagnostics if (!withProperties) return ret; var command_params_obj = new MemoryStream(); - var command_params_obj_writer = new MonoBinaryWriter(command_params_obj); - command_params_obj_writer.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); + var commandParamsObjWriter = new MonoBinaryWriter(command_params_obj); + commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); var props = await CreateJArrayForProperties(sessionId, typeId[i], command_params_obj.ToArray(), ret, false, $"dotnet:object:{objectId}", i == 0, token); ret = new JArray(ret.Union(props)); @@ -1706,19 +1807,19 @@ namespace Microsoft.WebAssembly.Diagnostics var typeIds = await GetTypeIdFromObject(sessionId, objectId, true, token); foreach (var typeId in typeIds) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write(typeId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(typeId); - var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, command_params, token); - var nProperties = ret_debugger_cmd_reader.ReadInt32(); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetProperties, commandParams, token); + var nProperties = retDebuggerCmdReader.ReadInt32(); for (int i = 0 ; i < nProperties; i++) { - ret_debugger_cmd_reader.ReadInt32(); //propertyId - string propertyNameStr = ret_debugger_cmd_reader.ReadString(); - var getMethodId = ret_debugger_cmd_reader.ReadInt32(); - var setMethodId = ret_debugger_cmd_reader.ReadInt32(); //setmethod - var attrValue = ret_debugger_cmd_reader.ReadInt32(); //attrs + retDebuggerCmdReader.ReadInt32(); //propertyId + string propertyNameStr = retDebuggerCmdReader.ReadString(); + var getMethodId = retDebuggerCmdReader.ReadInt32(); + var setMethodId = retDebuggerCmdReader.ReadInt32(); //setmethod + var attrValue = retDebuggerCmdReader.ReadInt32(); //attrs //Console.WriteLine($"{propertyNameStr} - {attrValue}"); if (ret.Where(attribute => attribute["name"].Value().Equals(propertyNameStr)).Any()) { @@ -1770,19 +1871,19 @@ namespace Microsoft.WebAssembly.Diagnostics public async Task SetVariableValue(SessionId sessionId, int thread_id, int frame_id, int varId, string newValue, CancellationToken token) { - var command_params = new MemoryStream(); - var command_params_writer = new MonoBinaryWriter(command_params); - MonoBinaryReader ret_debugger_cmd_reader = null; - command_params_writer.Write(thread_id); - command_params_writer.Write(frame_id); - command_params_writer.Write(1); - command_params_writer.Write(varId); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + MonoBinaryReader retDebuggerCmdReader = null; + commandParamsWriter.Write(thread_id); + commandParamsWriter.Write(frame_id); + commandParamsWriter.Write(1); + commandParamsWriter.Write(varId); JArray locals = new JArray(); - ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, command_params, token); - int etype = ret_debugger_cmd_reader.ReadByte(); + retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetValues, commandParams, token); + int etype = retDebuggerCmdReader.ReadByte(); try { - ret_debugger_cmd_reader = await SendDebuggerAgentCommandWithParms(sessionId, CmdFrame.SetValues, command_params, etype, newValue, token); + retDebuggerCmdReader = await SendDebuggerAgentCommandWithParms(sessionId, CmdFrame.SetValues, commandParams, etype, newValue, token); } catch (Exception) { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 993e6165cc0..d6803ee8670 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -502,6 +502,105 @@ namespace DebuggerTests AssertEqual(arg.class_name, res.Error["result"]?["className"]?.Value(), $"Error className did not match for expression '{arg.expression}'"); } } + + + [Fact] + public async Task EvaluateSimpleMethodCallsError() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var (_, res) = await EvaluateOnCallFrame(id, "this.objToTest.MyMethodWrong()", expect_ok: false ); + AssertEqual("Method 'MyMethodWrong' not found in type 'DebuggerTests.EvaluateMethodTestsClass.ParmToTest'", res.Error["message"]?.Value(), "wrong error message"); + + (_, res) = await EvaluateOnCallFrame(id, "this.objToTest.MyMethod(1)", expect_ok: false ); + AssertEqual("Unable to evaluate method 'MyMethod'", res.Error["message"]?.Value(), "wrong error message"); + + (_, res) = await EvaluateOnCallFrame(id, "this.CallMethodWithParm(\"1\")", expect_ok: false ); + AssertEqual("Unable to evaluate method 'CallMethodWithParm'", res.Error["message"]?.Value(), "wrong error message"); + + (_, res) = await EvaluateOnCallFrame(id, "this.ParmToTestObjNull.MyMethod()", expect_ok: false ); + AssertEqual("Object reference not set to an instance of an object.", res.Error["message"]?.Value(), "wrong error message"); + + (_, res) = await EvaluateOnCallFrame(id, "this.ParmToTestObjException.MyMethod()", expect_ok: false ); + AssertEqual("Object reference not set to an instance of an object.", res.Error["message"]?.Value(), "wrong error message"); + }); + + [Fact] + public async Task EvaluateSimpleMethodCallsWithoutParms() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + await EvaluateOnCallFrameAndCheck(id, + ("this.CallMethod()", TNumber(1)), + ("this.CallMethod()", TNumber(1)), + ("this.ParmToTestObj.MyMethod()", TString("methodOK")), + ("this.objToTest.MyMethod()", TString("methodOK"))); + }); + + + [Fact] + public async Task EvaluateSimpleMethodCallsWithConstParms() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + await EvaluateOnCallFrameAndCheck(id, + ("this.CallMethodWithParm(10)", TNumber(11)), + ("this.CallMethodWithMultipleParms(10, 10)", TNumber(21)), + ("this.CallMethodWithParmBool(true)", TString("TRUE")), + ("this.CallMethodWithParmBool(false)", TString("FALSE")), + ("this.CallMethodWithParmString(\"concat\")", TString("str_const_concat")), + ("this.CallMethodWithParm(10) + this.a", TNumber(12)), + ("this.CallMethodWithObj(null)", TNumber(-1)), + ("this.CallMethodWithChar('a')", TString("str_const_a"))); + }); + + [Fact] + public async Task EvaluateSimpleMethodCallsWithVariableParms() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + await EvaluateOnCallFrameAndCheck(id, + ("this.CallMethodWithParm(this.a)", TNumber(2)), + ("this.CallMethodWithMultipleParms(this.a, 10)", TNumber(12)), + ("this.CallMethodWithParmString(this.str)", TString("str_const_str_const_")), + ("this.CallMethodWithParmBool(this.t)", TString("TRUE")), + ("this.CallMethodWithParmBool(this.f)", TString("FALSE")), + ("this.CallMethodWithParm(this.a) + this.a", TNumber(3)), + ("this.CallMethodWithObj(this.objToTest)", TNumber(10))); + }); + + + [Fact] + public async Task EvaluateSimpleMethodCallsCheckChangedValue() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var frame = pause_location["callFrames"][0]; + var props = await GetObjectOnFrame(frame, "this"); + CheckNumber(props, "a", 1); + + await EvaluateOnCallFrameAndCheck(id, + ("this.CallMethodChangeValue()", TObject("object", is_null : true))); + + frame = pause_location["callFrames"][0]; + props = await GetObjectOnFrame(frame, "this"); + CheckNumber(props, "a", 11); + }); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index 4054c6a2cea..b45825acfcb 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -21,18 +21,18 @@ namespace DebuggerTests "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", null, -1, -1, "IntAdd"); - var var_ids = new[] + var varIds = new[] { new { index = 0, name = "one" }, }; - var scope_id = "-12"; - var expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; + var scopeId = "-12"; + var expression = $"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(varIds)})"; var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); - scope_id = "30000"; - expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; + scopeId = "30000"; + expression = $"MONO.mono_wasm_get_variables({scopeId}, {JsonConvert.SerializeObject(varIds)})"; res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index a5fbd57a300..1583ce1fd6f 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -309,4 +309,101 @@ namespace DebuggerTests return await Task.FromResult(default(T)); } } + public class EvaluateMethodTestsClass + { + public class ParmToTest + { + public int a; + public int b; + public ParmToTest() + { + a = 10; + b = 10; + } + public string MyMethod() + { + return "methodOK"; + } + } + public class TestEvaluate + { + public int a; + public int b; + public int c; + public string str = "str_const_"; + public bool t = true; + public bool f = false; + public ParmToTest objToTest; + public ParmToTest ParmToTestObj => objToTest; + public ParmToTest ParmToTestObjNull => null; + public ParmToTest ParmToTestObjException => throw new Exception("error2"); + public void run(int g, int h, string a, string valString, int this_a) + { + objToTest = new ParmToTest(); + int d = g + 1; + int e = g + 2; + int f = g + 3; + int i = d + e + f; + this.a = 1; + b = 2; + c = 3; + this.a = this.a + 1; + b = b + 1; + c = c + 1; + } + + public int CallMethod() + { + return a; + } + + public int CallMethodWithParm(int parm) + { + return a + parm; + } + + public void CallMethodChangeValue() + { + a = a + 10; + } + + public int CallMethodWithMultipleParms(int parm, int parm2) + { + return a + parm + parm2; + } + + public string CallMethodWithParmString(string parm) + { + return str + parm; + } + + public string CallMethodWithParmBool(bool parm) + { + if (parm) + return "TRUE"; + return "FALSE"; + } + + public int CallMethodWithObj(ParmToTest parm) + { + if (parm == null) + return -1; + return parm.a; + } + + + public string CallMethodWithChar(char parm) + { + return str + parm; + } + } + + public static void EvaluateMethods() + { + TestEvaluate f = new TestEvaluate(); + f.run(100, 200, "9000", "test", 45); + } + + } + } From 0a42d495324391f6bdce8b619bcc6eec9d6f9964 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 14 Jul 2021 15:32:27 +0300 Subject: [PATCH 537/926] remove superfluous comment (#55634) Fix #55389 --- .../src/System/Collections/Generic/LinkedList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs b/src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs index cd271869621..00431d4eeb4 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs @@ -177,7 +177,7 @@ namespace System.Collections.Generic while (current != null) { LinkedListNode temp = current; - current = current.Next; // use Next the instead of "next", otherwise it will loop forever + current = current.Next; temp.Invalidate(); } From 56d9474f530579c67b294cbbde0724e966dbb05b Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 14 Jul 2021 08:53:09 -0400 Subject: [PATCH 538/926] [mono][wasm] Avoid compiling llvm bitcode files with -emit-llvm. (#55630) It causes the output to be a bitcode file as well, which is passed to the emscripten link step, which will compile it to wasm sequentially, slowing down linking. Fixes https://github.com/dotnet/runtime/issues/54935. --- src/mono/wasm/build/WasmApp.Native.targets | 1 + src/mono/wasm/wasm.proj | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 4b8335a74d6..dec82153779 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -218,6 +218,7 @@ <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DCORE_BINDINGS" /> <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + <_EmccCFlags Include="-emit-llvm" /> <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 45640378ac3..b8ee77e5e27 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -69,7 +69,6 @@ <_EmccCommonFlags Include="-s "EXPORTED_RUNTIME_METHODS=['ccall', 'FS_createPath', 'FS_createDataFile', 'cwrap', 'setValue', 'getValue', 'UTF8ToString', 'UTF8ArrayToString', 'addFunction']"" /> <_EmccCommonFlags Include="-s "EXPORTED_FUNCTIONS=['_putchar']"" /> <_EmccCommonFlags Include="--source-map-base http://example.com" /> - <_EmccCommonFlags Include="-emit-llvm" /> <_EmccCommonFlags Include="-s MODULARIZE=1" Condition="'$(WasmEnableES6)' != 'false'" /> <_EmccCommonFlags Include="-s EXPORT_ES6=1" Condition="'$(WasmEnableES6)' != 'false'" /> From 4e9d89ac0a333b86867cf898b9ad438d324c66d1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 14 Jul 2021 09:12:09 -0400 Subject: [PATCH 539/926] Add ArgumentNullException.ThrowIfNull (#55594) --- .../src/System/Net/WebClient.cs | 70 +++++------- .../src/System/ArgumentNullException.cs | 17 +++ .../System.Reflection.TypeExtensions.csproj | 3 +- .../src/System/Reflection/Requires.cs | 16 --- .../src/System/Reflection/TypeExtensions.cs | 106 +++++++++--------- .../System.Runtime/ref/System.Runtime.cs | 1 + .../System/ArgumentNullExceptionTests.cs | 27 +++++ 7 files changed, 130 insertions(+), 110 deletions(-) delete mode 100644 src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/Requires.cs diff --git a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs index 27289121acd..553bf48943d 100644 --- a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs +++ b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs @@ -143,7 +143,7 @@ namespace System.Net get { return _encoding; } set { - ThrowIfNull(value, nameof(value)); + ArgumentNullException.ThrowIfNull(value, nameof(value)); _encoding = value; } } @@ -280,7 +280,7 @@ namespace System.Net public byte[] DownloadData(Uri address) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); StartOperation(); try @@ -320,8 +320,8 @@ namespace System.Net public void DownloadFile(Uri address, string fileName) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); WebRequest? request = null; FileStream? fs = null; @@ -359,7 +359,7 @@ namespace System.Net public Stream OpenRead(Uri address) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); WebRequest? request = null; StartOperation(); @@ -392,7 +392,7 @@ namespace System.Net public Stream OpenWrite(Uri address, string? method) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); if (method == null) { method = MapToDefaultMethod(address); @@ -432,8 +432,8 @@ namespace System.Net public byte[] UploadData(Uri address, string? method, byte[] data) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -553,8 +553,8 @@ namespace System.Net public byte[] UploadFile(Uri address, string? method, string fileName) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); if (method == null) { method = MapToDefaultMethod(address); @@ -626,8 +626,8 @@ namespace System.Net public byte[] UploadValues(Uri address, string? method, NameValueCollection data) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -665,8 +665,8 @@ namespace System.Net public string UploadString(Uri address, string? method, string data) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -691,7 +691,7 @@ namespace System.Net public string DownloadString(Uri address) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); StartOperation(); try @@ -781,7 +781,7 @@ namespace System.Net private Uri GetUri(string address) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); Uri? uri; if (_baseAddress != null) @@ -801,7 +801,7 @@ namespace System.Net private Uri GetUri(Uri address) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); Uri? uri = address; @@ -1297,7 +1297,7 @@ namespace System.Net public void OpenReadAsync(Uri address, object? userToken) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1335,7 +1335,7 @@ namespace System.Net public void OpenWriteAsync(Uri address, string? method, object? userToken) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); if (method == null) { method = MapToDefaultMethod(address); @@ -1396,7 +1396,7 @@ namespace System.Net public void DownloadStringAsync(Uri address, object? userToken) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1422,7 +1422,7 @@ namespace System.Net public void DownloadDataAsync(Uri address, object? userToken) { - ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1448,8 +1448,8 @@ namespace System.Net public void DownloadFileAsync(Uri address, string fileName, object? userToken) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); FileStream? fs = null; AsyncOperation asyncOp = StartAsyncOperation(userToken); @@ -1474,8 +1474,8 @@ namespace System.Net public void UploadStringAsync(Uri address, string? method, string data, object? userToken) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -1525,8 +1525,8 @@ namespace System.Net public void UploadDataAsync(Uri address, string? method, byte[] data, object? userToken) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -1566,8 +1566,8 @@ namespace System.Net public void UploadFileAsync(Uri address, string? method, string fileName, object? userToken) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); if (method == null) { method = MapToDefaultMethod(address); @@ -1605,8 +1605,8 @@ namespace System.Net public void UploadValuesAsync(Uri address, string? method, NameValueCollection data, object? userToken) { - ThrowIfNull(address, nameof(address)); - ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(data, nameof(data)); if (method == null) { method = MapToDefaultMethod(address); @@ -1940,14 +1940,6 @@ namespace System.Net } } - private static void ThrowIfNull(object argument, string parameterName) - { - if (argument == null) - { - throw new ArgumentNullException(parameterName); - } - } - #region Supporting Types private sealed class ProgressData { diff --git a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs index 0f8a1923ad5..5f8894e2914 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs @@ -10,6 +10,8 @@ ** =============================================================================*/ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace System @@ -50,5 +52,20 @@ namespace System protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + /// Throws an if is null. + /// The reference type argument to validate as non-null. + /// The name of the parameter with which corresponds. + public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression("argument")] string? paramName = null) + { + if (argument is null) + { + Throw(paramName); + } + } + + [DoesNotReturn] + private static void Throw(string? paramName) => + throw new ArgumentNullException(paramName); } } diff --git a/src/libraries/System.Reflection.TypeExtensions/src/System.Reflection.TypeExtensions.csproj b/src/libraries/System.Reflection.TypeExtensions/src/System.Reflection.TypeExtensions.csproj index 3e598d9015c..e078dd282eb 100644 --- a/src/libraries/System.Reflection.TypeExtensions/src/System.Reflection.TypeExtensions.csproj +++ b/src/libraries/System.Reflection.TypeExtensions/src/System.Reflection.TypeExtensions.csproj @@ -5,10 +5,9 @@ $(NetCoreAppCurrent) - - \ No newline at end of file + diff --git a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/Requires.cs b/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/Requires.cs deleted file mode 100644 index 8fa51255d4f..00000000000 --- a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/Requires.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Reflection -{ - internal static class Requires - { - internal static void NotNull(object obj, string name) - { - if (obj == null) - { - throw new ArgumentNullException(name); - } - } - } -} diff --git a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs b/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs index 3e3b6ca2961..6c020bbfffd 100644 --- a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs +++ b/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs @@ -11,14 +11,14 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type, Type[] types) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetConstructor(types); } public static ConstructorInfo[] GetConstructors( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetConstructors(); } @@ -26,7 +26,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetConstructors(bindingAttr); } @@ -39,7 +39,7 @@ namespace System.Reflection | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetDefaultMembers(); } @@ -47,7 +47,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] this Type type, string name) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetEvent(name); } @@ -56,14 +56,14 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetEvent(name, bindingAttr); } public static EventInfo[] GetEvents( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetEvents(); } @@ -71,7 +71,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetEvents(bindingAttr); } @@ -79,7 +79,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] this Type type, string name) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetField(name); } @@ -88,14 +88,14 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetField(name, bindingAttr); } public static FieldInfo[] GetFields( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetFields(); } @@ -103,20 +103,20 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetFields(bindingAttr); } public static Type[] GetGenericArguments(this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetGenericArguments(); } public static Type[] GetInterfaces( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetInterfaces(); } @@ -130,7 +130,7 @@ namespace System.Reflection | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type, string name) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMember(name); } @@ -139,7 +139,7 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMember(name, bindingAttr); } @@ -152,7 +152,7 @@ namespace System.Reflection | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMembers(); } @@ -160,7 +160,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMembers(bindingAttr); } @@ -168,7 +168,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type, string name) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMethod(name); } @@ -177,7 +177,7 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMethod(name, bindingAttr); } @@ -186,14 +186,14 @@ namespace System.Reflection string name, Type[] types) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMethod(name, types); } public static MethodInfo[] GetMethods( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMethods(); } @@ -201,7 +201,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetMethods(bindingAttr); } @@ -210,7 +210,7 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetNestedType(name, bindingAttr); } @@ -218,14 +218,14 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetNestedTypes(bindingAttr); } public static PropertyInfo[] GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type type) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperties(); } @@ -233,7 +233,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] this Type type, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperties(bindingAttr); } @@ -241,7 +241,7 @@ namespace System.Reflection [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type type, string name) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperty(name); } @@ -250,7 +250,7 @@ namespace System.Reflection string name, BindingFlags bindingAttr) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperty(name, bindingAttr); } @@ -259,7 +259,7 @@ namespace System.Reflection string name, Type? returnType) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperty(name, returnType); } @@ -269,19 +269,19 @@ namespace System.Reflection Type? returnType, Type[] types) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.GetProperty(name, returnType, types); } public static bool IsAssignableFrom(this Type type, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] Type? c) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.IsAssignableFrom(c); } public static bool IsInstanceOfType(this Type type, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? o) { - Requires.NotNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type, nameof(type)); return type.IsInstanceOfType(o); } } @@ -291,20 +291,20 @@ namespace System.Reflection [RequiresUnreferencedCode("Types might be removed")] public static Type[] GetExportedTypes(this Assembly assembly) { - Requires.NotNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); return assembly.GetExportedTypes(); } public static Module[] GetModules(this Assembly assembly) { - Requires.NotNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); return assembly.GetModules(); } [RequiresUnreferencedCode("Types might be removed")] public static Type[] GetTypes(this Assembly assembly) { - Requires.NotNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); return assembly.GetTypes(); } } @@ -313,37 +313,37 @@ namespace System.Reflection { public static MethodInfo? GetAddMethod(this EventInfo eventInfo) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetAddMethod(); } public static MethodInfo? GetAddMethod(this EventInfo eventInfo, bool nonPublic) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetAddMethod(nonPublic); } public static MethodInfo? GetRaiseMethod(this EventInfo eventInfo) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetRaiseMethod(); } public static MethodInfo? GetRaiseMethod(this EventInfo eventInfo, bool nonPublic) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetRaiseMethod(nonPublic); } public static MethodInfo? GetRemoveMethod(this EventInfo eventInfo) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetRemoveMethod(); } public static MethodInfo? GetRemoveMethod(this EventInfo eventInfo, bool nonPublic) { - Requires.NotNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); return eventInfo.GetRemoveMethod(nonPublic); } } @@ -358,7 +358,7 @@ namespace System.Reflection /// This maybe public static bool HasMetadataToken(this MemberInfo member) { - Requires.NotNull(member, nameof(member)); + ArgumentNullException.ThrowIfNull(member, nameof(member)); try { @@ -380,7 +380,7 @@ namespace System.Reflection /// public static int GetMetadataToken(this MemberInfo member) { - Requires.NotNull(member, nameof(member)); + ArgumentNullException.ThrowIfNull(member, nameof(member)); int token = GetMetadataTokenOrZeroOrThrow(member); @@ -413,7 +413,7 @@ namespace System.Reflection { public static MethodInfo GetBaseDefinition(this MethodInfo method) { - Requires.NotNull(method, nameof(method)); + ArgumentNullException.ThrowIfNull(method, nameof(method)); return method.GetBaseDefinition(); } } @@ -422,13 +422,13 @@ namespace System.Reflection { public static bool HasModuleVersionId(this Module module) { - Requires.NotNull(module, nameof(module)); + ArgumentNullException.ThrowIfNull(module, nameof(module)); return true; // not expected to fail on platforms with Module.ModuleVersionId built-in. } public static Guid GetModuleVersionId(this Module module) { - Requires.NotNull(module, nameof(module)); + ArgumentNullException.ThrowIfNull(module, nameof(module)); return module.ModuleVersionId; } } @@ -437,37 +437,37 @@ namespace System.Reflection { public static MethodInfo[] GetAccessors(this PropertyInfo property) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetAccessors(); } public static MethodInfo[] GetAccessors(this PropertyInfo property, bool nonPublic) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetAccessors(nonPublic); } public static MethodInfo? GetGetMethod(this PropertyInfo property) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetGetMethod(); } public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetGetMethod(nonPublic); } public static MethodInfo? GetSetMethod(this PropertyInfo property) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetSetMethod(); } public static MethodInfo? GetSetMethod(this PropertyInfo property, bool nonPublic) { - Requires.NotNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property, nameof(property)); return property.GetSetMethod(nonPublic); } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 35fe09cc662..76bbc5dd649 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -284,6 +284,7 @@ namespace System public ArgumentNullException(string? paramName) { } public ArgumentNullException(string? message, System.Exception? innerException) { } public ArgumentNullException(string? paramName, string? message) { } + public static void ThrowIfNull([System.Diagnostics.CodeAnalysis.NotNullAttribute] object? argument, [System.Runtime.CompilerServices.CallerArgumentExpressionAttribute("argument")] string? paramName = null) { throw null; } } public partial class ArgumentOutOfRangeException : System.ArgumentException { diff --git a/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs b/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs index 577e7c4ba26..78384466f7f 100644 --- a/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs @@ -47,5 +47,32 @@ namespace System.Tests Assert.Contains(message, exception.Message); Assert.Contains(argumentName, exception.Message); } + + [Fact] + public static void ThrowIfNull_NonNull_DoesntThrow() + { + foreach (object o in new[] { new object(), "", "argument" }) + { + ArgumentNullException.ThrowIfNull(o); + ArgumentNullException.ThrowIfNull(o, "paramName"); + } + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("name")] + public static void ThrowIfNull_Null_ThrowsArgumentNullException(string paramName) + { + AssertExtensions.Throws(paramName, () => ArgumentNullException.ThrowIfNull(null, paramName)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/csharplang/issues/287")] + public static void ThrowIfNull_UsesArgumentExpression() + { + object something = null; + AssertExtensions.Throws(nameof(something), () => ArgumentNullException.ThrowIfNull(something)); + } } } From a487e4ce5ae118d9b292b8b8b5130f77c577c821 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 14 Jul 2021 09:45:04 -0400 Subject: [PATCH 540/926] [mono][wasm] Mark memcpy LLVM instructions as volatile when they are used to copy (#55598) valuetypes containing references. This is needed to make sure llvm keeps the valuetypes on the C stack for GC tracking. Fixes random crashes when running the System.Collections.NonGeneric testsuite. --- src/mono/mono/mini/mini-llvm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 088d843cd06..c8a3a90e157 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -7230,6 +7230,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) MonoClass *klass = ins->klass; LLVMValueRef src = NULL, dst, args [5]; gboolean done = FALSE; + gboolean is_volatile = FALSE; if (!klass) { // FIXME: @@ -7285,15 +7286,20 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) if (done) break; +#ifdef TARGET_WASM + is_volatile = m_class_has_references (klass); +#endif + int aindex = 0; args [aindex ++] = dst; args [aindex ++] = src; args [aindex ++] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE); + #if LLVM_API_VERSION < 900 // FIXME: Alignment args [aindex ++] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); #endif - args [aindex ++] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); + args [aindex ++] = LLVMConstInt (LLVMInt1Type (), is_volatile ? 1 : 0, FALSE); call_intrins (ctx, INTRINS_MEMCPY, args, ""); break; } From 7cfa94509078e599df748fdea11a1cf7d371ebf1 Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Wed, 14 Jul 2021 16:00:44 +0200 Subject: [PATCH 541/926] [QUIC] Cosmetic changes to Read pipeline (#55591) Follow-up for NITs and cosmetic changes from #55505 --- .../Implementations/MsQuic/MsQuicStream.cs | 74 ++++++++++++------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 585aca851b3..0c29a9285d6 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -32,6 +32,8 @@ namespace System.Net.Quic.Implementations.MsQuic { public SafeMsQuicStreamHandle Handle = null!; // set in ctor. public GCHandle StateGCHandle; + + public MsQuicStream? Stream; // roots the stream in the pinned state to prevent GC during an async read I/O. public MsQuicConnection.State ConnectionState = null!; // set in ctor. public string TraceId = null!; // set in ctor. @@ -48,7 +50,7 @@ namespace System.Net.Quic.Implementations.MsQuic // set when ReadState.PendingRead: public Memory ReceiveUserBuffer; public CancellationTokenRegistration ReceiveCancellationRegistration; - public MsQuicStream? RootedReceiveStream; // roots the stream in the pinned state to prevent GC during an async read I/O. + // Resettable completions to be used for multiple calls to receive. public readonly ResettableCompletionSource ReceiveResettableCompletionSource = new ResettableCompletionSource(); public SendState SendState; @@ -363,27 +365,38 @@ namespace System.Net.Quic.Implementations.MsQuic NetEventSource.Info(_state, $"{TraceId()} Stream reading into Memory of '{destination.Length}' bytes."); } - ReadState readState; - long abortError = -1; - bool canceledSynchronously = false; + ReadState initialReadState; // value before transitions + long abortError; + bool preCanceled = false; lock (_state) { - readState = _state.ReadState; + initialReadState = _state.ReadState; abortError = _state.ReadErrorCode; - if (readState != ReadState.PendingRead && cancellationToken.IsCancellationRequested) + // Failure scenario: pre-canceled token. Transition: any -> Aborted + // PendingRead state indicates there is another concurrent read operation in flight + // which is forbidden, so it is handled separately + if (initialReadState != ReadState.PendingRead && cancellationToken.IsCancellationRequested) { - readState = ReadState.Aborted; + initialReadState = ReadState.Aborted; _state.ReadState = ReadState.Aborted; - canceledSynchronously = true; + preCanceled = true; } - else if (readState == ReadState.None) + + // Success scenario: EOS already reached, completing synchronously. No transition (final state) + if (initialReadState == ReadState.ReadsCompleted) { - Debug.Assert(_state.RootedReceiveStream is null); + return new ValueTask(0); + } + + // Success scenario: no data available yet, will return a task to wait on. Transition None->PendingRead + if (initialReadState == ReadState.None) + { + Debug.Assert(_state.Stream is null); _state.ReceiveUserBuffer = destination; - _state.RootedReceiveStream = this; + _state.Stream = this; _state.ReadState = ReadState.PendingRead; if (cancellationToken.CanBeCanceled) @@ -396,7 +409,7 @@ namespace System.Net.Quic.Implementations.MsQuic lock (state) { completePendingRead = state.ReadState == ReadState.PendingRead; - state.RootedReceiveStream = null; + state.Stream = null; state.ReceiveUserBuffer = null; state.ReadState = ReadState.Aborted; } @@ -414,7 +427,9 @@ namespace System.Net.Quic.Implementations.MsQuic return _state.ReceiveResettableCompletionSource.GetValueTask(); } - else if (readState == ReadState.IndividualReadComplete) + + // Success scenario: data already available, completing synchronously. Transition IndividualReadComplete->None + if (initialReadState == ReadState.IndividualReadComplete) { _state.ReadState = ReadState.None; @@ -431,25 +446,22 @@ namespace System.Net.Quic.Implementations.MsQuic } } + // All success scenarios returned at this point. Failure scenarios below: + Exception? ex = null; - switch (readState) + switch (initialReadState) { - case ReadState.ReadsCompleted: - return new ValueTask(0); case ReadState.PendingRead: ex = new InvalidOperationException("Only one read is supported at a time."); break; case ReadState.Aborted: - ex = - canceledSynchronously ? new OperationCanceledException(cancellationToken) : // aborted by token being canceled before the async op started. - abortError == -1 ? new QuicOperationAbortedException() : // aborted by user via some other operation. - new QuicStreamAbortedException(abortError); // aborted by peer. - + ex = preCanceled ? new OperationCanceledException(cancellationToken) : + ThrowHelper.GetStreamAbortedException(abortError); break; case ReadState.ConnectionClosed: default: - Debug.Assert(readState == ReadState.ConnectionClosed, $"{nameof(ReadState)} of '{readState}' is unaccounted for in {nameof(ReadAsync)}."); + Debug.Assert(initialReadState == ReadState.ConnectionClosed, $"{nameof(ReadState)} of '{initialReadState}' is unaccounted for in {nameof(ReadAsync)}."); ex = GetConnectionAbortedException(_state); break; } @@ -490,7 +502,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (_state.ReadState == ReadState.PendingRead) { shouldComplete = true; - _state.RootedReceiveStream = null; + _state.Stream = null; _state.ReceiveUserBuffer = null; } if (_state.ReadState < ReadState.ReadsCompleted) @@ -833,7 +845,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (receiveEvent.BufferCount == 0) { // This is a 0-length receive that happens once reads are finished (via abort or otherwise). - // State changes for this are handled elsewhere. + // State changes for this are handled in PEER_SEND_SHUTDOWN / PEER_SEND_ABORT / SHUTDOWN_COMPLETE event handlers. return MsQuicStatusCodes.Success; } @@ -847,6 +859,12 @@ namespace System.Net.Quic.Implementations.MsQuic case ReadState.None: // ReadAsync() hasn't been called yet. Stash the buffer so the next ReadAsync call completes synchronously. + // We are overwriting state.ReceiveQuicBuffers here even if we only partially consumed them + // and it is intended, because unconsumed data will arrive again from the point we've stopped. + // New RECEIVE event wouldn't come until we call EnableReceive(), and we call it only after we've consumed + // as much as we could and said so to msquic in ReceiveComplete(taken), so new event will have all the + // remaining data. + if ((uint)state.ReceiveQuicBuffers.Length < receiveEvent.BufferCount) { QuicBuffer[] oldReceiveBuffers = state.ReceiveQuicBuffers; @@ -872,7 +890,7 @@ namespace System.Net.Quic.Implementations.MsQuic state.ReceiveCancellationRegistration.Unregister(); shouldComplete = true; - state.RootedReceiveStream = null; + state.Stream = null; state.ReadState = ReadState.None; readLength = CopyMsQuicBuffersToUserBuffer(new ReadOnlySpan(receiveEvent.Buffers, (int)receiveEvent.BufferCount), state.ReceiveUserBuffer.Span); @@ -975,7 +993,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (state.ReadState == ReadState.PendingRead) { shouldReadComplete = true; - state.RootedReceiveStream = null; + state.Stream = null; state.ReceiveUserBuffer = null; } if (state.ReadState < ReadState.ReadsCompleted) @@ -1029,7 +1047,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (state.ReadState == ReadState.PendingRead) { shouldComplete = true; - state.RootedReceiveStream = null; + state.Stream = null; state.ReceiveUserBuffer = null; } state.ReadState = ReadState.Aborted; @@ -1057,7 +1075,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (state.ReadState == ReadState.PendingRead) { shouldComplete = true; - state.RootedReceiveStream = null; + state.Stream = null; state.ReceiveUserBuffer = null; } if (state.ReadState < ReadState.ReadsCompleted) From b45b5fc9b17a15536f416a55733258eebef5ad46 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Wed, 14 Jul 2021 16:05:27 +0200 Subject: [PATCH 542/926] Disable Http2_MultipleConnectionsEnabled_ConnectionLimitNotReached_ConcurrentRequestsSuccessfullyHandled Reverts #55572. The test is still failing. --- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 1e364ea9665..b326c8bbb02 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2078,6 +2078,7 @@ namespace System.Net.Http.Functional.Tests public SocketsHttpHandlerTest_Http2(ITestOutputHelper output) : base(output) { } [ConditionalFact(nameof(SupportsAlpn))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/41078")] public async Task Http2_MultipleConnectionsEnabled_ConnectionLimitNotReached_ConcurrentRequestsSuccessfullyHandled() { const int MaxConcurrentStreams = 2; From 9ad44f8347be52df4070d85264bfe528f34ba3a0 Mon Sep 17 00:00:00 2001 From: Jon Fortescue Date: Wed, 14 Jul 2021 07:26:52 -0700 Subject: [PATCH 543/926] Turn on loc PRs (#54293) * Turn on loc PRs * oops Co-authored-by: Jeremy Koritzinsky * Keep OneLocBuild running only in main Co-authored-by: Jeremy Koritzinsky --- eng/pipelines/runtime-official.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml index 84b0d2fcde4..6b02686235b 100644 --- a/eng/pipelines/runtime-official.yml +++ b/eng/pipelines/runtime-official.yml @@ -44,11 +44,12 @@ stages: # # Localization build # - - template: /eng/common/templates/job/onelocbuild.yml - parameters: - CreatePr: false - LclSource: lclFilesfromPackage - LclPackageId: 'LCL-JUNO-PROD-RUNTIME' + - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: + - template: /eng/common/templates/job/onelocbuild.yml + parameters: + MirrorRepo: runtime + LclSource: lclFilesfromPackage + LclPackageId: 'LCL-JUNO-PROD-RUNTIME' # # Source Index Build From d953229d5429d2ffde833740dd481aab864d3e0c Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 14 Jul 2021 10:47:03 -0400 Subject: [PATCH 544/926] [wasm] Run `Wasm.Build.Tests` against workloads (#54451) Co-authored-by: Larry Ewing --- Directory.Build.props | 4 + docs/workflow/testing/testing-workloads.md | 28 +++ eng/Subsets.props | 2 +- eng/Versions.props | 1 + eng/liveBuilds.targets | 2 +- eng/pipelines/runtime.yml | 41 +++- eng/testing/WasmRunnerTemplate.cmd | 4 +- eng/testing/linker/project.csproj.template | 1 + eng/testing/linker/trimmingTests.targets | 2 + eng/testing/tests.mobile.targets | 9 + eng/testing/tests.wasm.targets | 7 + .../Directory.Build.props | 2 +- ...Microsoft.NETCore.App.MonoCrossAOT.sfxproj | 23 +- src/libraries/Directory.Build.props | 7 + src/libraries/Directory.Build.targets | 93 +++++++ src/libraries/pretest.proj | 3 + src/libraries/sendtohelix.proj | 20 +- src/libraries/sendtohelixhelp.proj | 125 +++++++--- src/libraries/tests.proj | 9 +- .../Sdk/Sdk.props | 4 + ...T.Workload.Mono.Toolchain.Manifest.pkgproj | 1 + .../WorkloadManifest.targets.in | 2 +- src/mono/wasm/Makefile | 4 +- src/mono/wasm/README.md | 2 +- src/mono/wasm/build/EmSdkRepo.Defaults.props | 3 + src/mono/wasm/build/WasmApp.LocalBuild.props | 6 +- .../wasm/build/WasmApp.LocalBuild.targets | 4 +- src/mono/wasm/build/WasmApp.Native.targets | 25 +- src/mono/wasm/build/WasmApp.targets | 20 +- .../aot-tests/ProxyProjectForAOTOnHelix.proj | 2 +- src/mono/wasm/wasm.proj | 32 +-- src/tasks/Common/Utils.cs | 4 +- src/tasks/WasmAppBuilder/EmccCompile.cs | 4 +- .../InstallWorkloadFromArtifacts.cs | 228 ++++++++++++++++++ .../WorkloadBuildTasks/PackageInstaller.cs | 163 +++++++++++++ .../WorkloadBuildTasks.csproj | 2 + .../Wasm.Build.Tests/BlazorWasmTests.cs | 10 +- .../Wasm.Build.Tests/BuildEnvironment.cs | 29 +-- .../Wasm.Build.Tests/BuildTestBase.cs | 77 +++--- .../Wasm.Build.Tests/DotNetCommand.cs | 8 +- .../Wasm.Build.Tests/EnvironmentVariables.cs | 21 ++ .../Wasm.Build.Tests/NativeBuildTests.cs | 2 - .../BuildWasmApps/Wasm.Build.Tests/README.md | 19 ++ .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 18 +- .../Wasm.Build.Tests/WorkloadTests.cs | 12 +- .../data/Blazor.Directory.Build.targets | 6 +- .../data/Local.Directory.Build.targets | 2 +- .../data/RunScriptTemplate.cmd | 52 ++++ .../data/Workloads.Directory.Build.props | 7 + .../data/Workloads.Directory.Build.targets | 95 ++++++++ src/tests/Common/Directory.Build.targets | 4 + .../wasm-test-runner/WasmTestRunner.proj | 2 + src/tests/Directory.Build.targets | 4 + 53 files changed, 1096 insertions(+), 161 deletions(-) create mode 100644 docs/workflow/testing/testing-workloads.md create mode 100644 src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs create mode 100644 src/tasks/WorkloadBuildTasks/PackageInstaller.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/EnvironmentVariables.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/README.md create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.cmd create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets diff --git a/Directory.Build.props b/Directory.Build.props index 541ad0417e4..6cab9bcee30 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -73,8 +73,10 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AndroidAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WorkloadBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeConfigParser', 'Debug', '$(NetCoreAppToolCurrent)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'JsonToItemsTaskFactory', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll')) @@ -84,8 +86,10 @@ $([MSBuild]::NormalizePath('$(AndroidAppBuilderDir)', 'AndroidAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) + $([MSBuild]::NormalizePath('$(WorkloadBuildTasksDir)', 'WorkloadBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) + $([MSBuild]::NormalizePath('$(JsonToItemsTaskFactoryDir)', 'JsonToItemsTaskFactory.dll')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)')) diff --git a/docs/workflow/testing/testing-workloads.md b/docs/workflow/testing/testing-workloads.md new file mode 100644 index 00000000000..cb03515554a --- /dev/null +++ b/docs/workflow/testing/testing-workloads.md @@ -0,0 +1,28 @@ +# Testing Workloads + +Workloads based on packages in `artifacts` can be installed, and used for testing. + +- This is done by installing a specified SDK version (`$(SdkVersionForWorkloadTesting)`) in `artifacts/bin/dotnet-workload`. +- Then the manifest for the workload in `@(WorkloadIdForTesting)` is installed + - Then workload packs are installed + - packs, or manifests not generated by `runtime`, are restored from nuget + +- The SDK is installed by `ProvisionSdkForWorkloadTesting` target +- and the workload is installed by `InstallWorkloadUsingArtifacts` target, using the `InstallWorkloadFromArtifacts` task + +- `@(WorkloadIdForTesting)`: + +Example for wasm: +```xml + +``` + +- Currently, this is used only by `src/tests/BuildWasmApps/Wasm.Build.Tests` + +## Limitations: + +- The cross compiler package is built manually from the `InstallWorkloadUsingArtifacts` target diff --git a/eng/Subsets.props b/eng/Subsets.props index 105b34254c6..9234a8c2dde 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -317,7 +317,7 @@ - + diff --git a/eng/Versions.props b/eng/Versions.props index 1e7be3b4cb9..2e300bfd20c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -161,6 +161,7 @@ 2.0.4 4.12.0 2.14.3 + 6.0.100-preview.7.21362.5 5.0.0-preview-20201009.2 diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index fb05c25a395..7710f9d5e2f 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -178,7 +178,7 @@ $(LibrariesNativeArtifactsPath)src\*.c; $(LibrariesNativeArtifactsPath)src\*.js; $(LibrariesNativeArtifactsPath)src\emcc-default.rsp; - $(LibrariesNativeArtifactsPath)src\Emcc.props;" + $(LibrariesNativeArtifactsPath)src\emcc-props.json;" NativeSubDirectory="src" IsNative="true" /> - @@ -305,6 +304,46 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# +# Build the whole product using Mono and run libraries tests, for Wasm.Build.Tests +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Browser_wasm + variables: + # map dependencies variables to local variables + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + - name: installerContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_WasmBuildTests + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + scenarios: + - buildwasmapps + condition: >- + or( + eq(variables['monoContainsChange'], true), + eq(variables['installerContainsChange'], true), + eq(variables['isFullMatrix'], true)) + # # Build for Browser/wasm, with EnableAggressiveTrimming=true # diff --git a/eng/testing/WasmRunnerTemplate.cmd b/eng/testing/WasmRunnerTemplate.cmd index 63af8f917b8..603199341a2 100644 --- a/eng/testing/WasmRunnerTemplate.cmd +++ b/eng/testing/WasmRunnerTemplate.cmd @@ -5,9 +5,9 @@ set EXECUTION_DIR=%~dp0 set SCENARIO=%3 if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] ( - set XHARNESS_OUT="%EXECUTION_DIR%xharness-output" + set "XHARNESS_OUT=%EXECUTION_DIR%xharness-output" ) else ( - set XHARNESS_OUT="%HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output" + set "XHARNESS_OUT=%HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output" ) if [%XHARNESS_CLI_PATH%] NEQ [] ( diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 38c6d68f901..e1ba296072c 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -6,6 +6,7 @@ {MonoProjectRoot} {MonoAOTCompilerTasksAssemblyPath} {WasmAppBuilderTasksAssemblyPath} + {JsonToItemsTaskFactoryTasksAssemblyPath} {MicrosoftNetCoreAppRuntimePackRidDir} {RepositoryEngineeringDir} {TestTargetFramework} diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index 6f084b6ddb4..649ad3e25bf 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -82,8 +82,10 @@ .Replace('{AdditionalProjectReferences}', '$(_additionalProjectReferencesString)') .Replace('{RepositoryEngineeringDir}', '$(RepositoryEngineeringDir)') .Replace('{MonoAOTCompilerDir}', '$(MonoAOTCompilerDir)') + .Replace('{JsonToItemsTaskFactoryDir}', '$(JsonToItemsTaskFactoryDir)') .Replace('{MonoProjectRoot}', '$(MonoProjectRoot)') .Replace('{MonoAOTCompilerTasksAssemblyPath}', '$(MonoAOTCompilerTasksAssemblyPath)') + .Replace('{JsonToItemsTaskFactoryTasksAssemblyPath}', '$(JsonToItemsTaskFactoryTasksAssemblyPath)') .Replace('{WasmAppBuilderTasksAssemblyPath}', '$(WasmAppBuilderTasksAssemblyPath)') .Replace('{MicrosoftNetCoreAppRuntimePackRidDir}', '$(MicrosoftNetCoreAppRuntimePackRidDir)'))" Overwrite="true" /> diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 517ebf71985..c3107ec4a06 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -7,6 +7,10 @@ true BundleTestAppleApp;BundleTestAndroidApp + + + true @@ -291,5 +295,10 @@ AfterTargets="Build" DependsOnTargets="Publish;$(BundleTestAppTargets);ArchiveTests" /> + + diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index aa4fb305dbd..16edf35847b 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -60,6 +60,13 @@ $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix + + + + - + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj index a951a58b643..27a590bfd08 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.MonoCrossAOT.sfxproj @@ -18,14 +18,20 @@ - <_ToolFile Include="$(MonoAotCrossDir)$(TargetCrossRid)\**" /> - - - + + + + $(MonoAotCrossDir) + $(MonoAotCrossDir)$(TargetCrossRid)\ + + + <_ToolFile Include="$(AotCompilerPath)**" /> + + <_SdkPropsProperties Condition="!$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value="" /> <_SdkPropsProperties Condition="$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value=".exe" /> <_SdkPropsProperties Include="TargetRid" Value="$(TargetCrossRid)" /> @@ -36,19 +42,20 @@ <_PermissionsProperties Include="PermissionsProperties" Value="$(_PermissionsFiles)" /> + + OutputPath="$(IntermediateOutputPath)Microsoft.NETCore.App.MonoCrossAOT.UnixFilePermissions.xml" /> + OutputPath="$(IntermediateOutputPath)$(TargetCrossRid).Sdk.props" /> - + - + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 4d39fad397e..05ea159b1bb 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -163,6 +163,13 @@ --interpreter + + $(ArtifactsBinDir)dotnet-workload\ + $([MSBuild]::NormalizeDirectory($(SdkPathForWorkloadTesting))) + + $(SdkPathForWorkloadTesting)version-$(SdkVersionForWorkloadTesting).stamp + + diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index a3553ca940f..dd17dace8e4 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -7,6 +7,13 @@ $(TestStrongNameKeyId) + + + true + + $(SdkPathForWorkloadTesting)workload.stamp + + @@ -328,4 +335,90 @@ Text="Analyzers must only target netstandard2.0 since they run in the compiler which targets netstandard2.0. The following files were found to target '%(_AnalyzerPackFile.TargetFramework)': @(_AnalyzerPackFile)" /> + + + + + + + + + + + + + <_DotNetInstallScriptPath Condition="!$([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.sh + <_DotNetInstallScriptPath Condition=" $([MSBuild]::IsOSPlatform('windows'))">$(RepoRoot).dotnet\dotnet-install.ps1 + + + + + + + + + + + + + + + + + + + + + + + + + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="Configuration=$(Configuration)" /> + <_PropsForAOTCrossBuild Include="TargetOS=Browser" /> + <_PropsForAOTCrossBuild Include="TargetArchitecture=wasm" /> + <_PropsForAOTCrossBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> + + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> + + + + + + <_NuGetSourceForWorkloads Include="dotnet6" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" /> + <_BuiltNuGets Include="$(LibrariesShippingPackagesDir)\*.nupkg" /> + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersion).nupkg + + + + + + + + + + diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index 3e924e083ab..fc2fee13d5c 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -22,6 +22,9 @@ + + + - <_ProjectsToBuild Include="$(PerScenarioProjectFile)"> + <_BaseProjectsToBuild Include="$(PerScenarioProjectFile)" Condition="'%(_Scenarios.Identity)' != 'buildwasmapps'"> $(_PropertiesToPass);Scenario=%(_Scenarios.Identity);TestArchiveRuntimeFile=$(TestArchiveRuntimeFile) - %(_ProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) - + %(_BaseProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) + + + + + + <_TestUsingWorkloadsValues Include="true;false" /> + + <_BuildWasmAppsProjectsToBuild Include="$(PerScenarioProjectFile)"> + $(_PropertiesToPass);Scenario=BuildWasmApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestUsingWorkloadsValues.Identity) + %(_BuildWasmAppsProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) + + + + + <_ProjectsToBuild Include="@(_BuildWasmAppsProjectsToBuild);@(_BaseProjectsToBuild)" /> diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index b5f603cee4f..572481d7ccb 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -7,6 +7,7 @@ true + true $(BUILD_BUILDNUMBER) @@ -28,7 +29,7 @@ <_workItemTimeout Condition="'$(_workItemTimeout)' == '' and ('$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'iOSSimulator' or '$(TargetOS)' == 'tvOS' or '$(TargetOS)' == 'tvOSSimulator' or '$(TargetOS)' == 'MacCatalyst' or '$(TargetOS)' == 'Android')">00:30:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">00:45:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00 - <_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:00:00 + <_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:30:00 <_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00 @@ -53,6 +54,11 @@ $(WaitForWorkItemCompletion) true + $(RepoRoot)src\mono\wasm\emsdk\ + + true + true + true @@ -97,19 +103,29 @@ true - true + true - + - - true - true - true - + + + + false + true + + + + + true + true + true + + + + + + + + + + + + + true @@ -155,24 +183,6 @@ $([System.Text.RegularExpressions.Regex]::Match($(GlobalJsonContent), '(%3F<="dotnet": ").*(%3F=")')) - - dotnet dev-certs https && - - - powershell -command "New-SelfSignedCertificate -FriendlyName 'ASP.NET Core HTTPS development certificate' -DnsName @('localhost') -Subject 'CN = localhost' -KeyAlgorithm RSA -KeyLength 2048 -HashAlgorithm sha256 -CertStoreLocation 'Cert:\CurrentUser\My' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1','1.3.6.1.4.1.311.84.1.1={hex}02','2.5.29.19={text}') -KeyUsage DigitalSignature,KeyEncipherment" && - - - $(HelixCommand)call RunTests.cmd - $(HelixCommand) --runtime-path %HELIX_CORRELATION_PAYLOAD% - - $(HelixCommand)./RunTests.sh - $(HelixCommand) --runtime-path "$HELIX_CORRELATION_PAYLOAD" - - @@ -224,6 +234,34 @@ + + @(HelixPreCommand) + $(HelixCommandPrefix) @(HelixCommandPrefixItem -> 'set "%(Identity)"', ' & ') + $(HelixCommandPrefix) @(HelixCommandPrefixItem, ' ') + true + + + + $(HelixCommandPrefix) + $(HelixCommandPrefix) & + + $(HelixCommand) dotnet dev-certs https && + + + $(HelixCommand) powershell -command "New-SelfSignedCertificate -FriendlyName 'ASP.NET Core HTTPS development certificate' -DnsName @('localhost') -Subject 'CN = localhost' -KeyAlgorithm RSA -KeyLength 2048 -HashAlgorithm sha256 -CertStoreLocation 'Cert:\CurrentUser\My' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1','1.3.6.1.4.1.311.84.1.1={hex}02','2.5.29.19={text}') -KeyUsage DigitalSignature,KeyEncipherment" && + + + $(HelixCommand)call RunTests.cmd + $(HelixCommand) --runtime-path %HELIX_CORRELATION_PAYLOAD% + + $(HelixCommand)./RunTests.sh + $(HelixCommand) --runtime-path "$HELIX_CORRELATION_PAYLOAD" + + @@ -274,11 +312,13 @@ 768968 https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chrome-linux.zip - https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip + https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'RemoteLoopServer', '$(NetCoreAppCurrent)-$(Configuration)')) + Workloads/ + EMSDK/ @@ -289,7 +329,7 @@ - + @@ -302,13 +342,22 @@ - + + + + + + + + + @@ -320,15 +369,15 @@ - - + + <_WorkItem Include="$(WorkItemArchiveWildCard)" Exclude="$(HelixCorrelationPayload)" /> <_WorkItem Include="$(TestArchiveRoot)runonly/**/WebAssembly.Console.*.Test.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'" /> <_WorkItem Include="$(TestArchiveRoot)runonly/**/WebAssembly.Browser.*.Test.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmTestOnBrowser'" /> <_WorkItem Include="$(TestArchiveRoot)browseronly/**/*.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmTestOnBrowser'" /> - + %(Identity) $(HelixCommand) $(_workItemTimeout) @@ -375,7 +424,7 @@ - + @@ -393,6 +442,8 @@ + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b1f20442aac..4884a9416d9 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -347,9 +347,11 @@ Condition="'$(TestTrimming)' == 'true'" AdditionalProperties="%(AdditionalProperties);SkipTrimmingProjectsRestore=true" /> + + @@ -376,10 +378,11 @@ BuildInParallel="false" /> - + + + BuildInParallel="true" /> diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props index d292fe15f27..a8050972730 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props @@ -1,4 +1,8 @@ + + true + + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj index f89c42b2884..747da02070f 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj @@ -38,6 +38,7 @@ TemplateFile="WorkloadManifest.json.in" Properties="@(_WorkloadManifestValues)" OutputPath="$(WorkloadManifestPath)" /> + - + + + + diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.props b/src/mono/wasm/build/WasmApp.LocalBuild.props index 69a719a0e6d..df76040d2c7 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.props +++ b/src/mono/wasm/build/WasmApp.LocalBuild.props @@ -37,6 +37,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(_NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(_NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(_NetCoreAppToolCurrent)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'JsonToItemsTaskFactory', 'Debug', '$(_NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'mono', '$(TargetOS).$(TargetArchitecture).$(RuntimeConfig)')) <_MonoAotCrossCompilerPath>$([MSBuild]::NormalizePath($(MonoArtifactsPath), 'cross', '$(TargetOS.ToLowerInvariant())-$(TargetArchitecture.ToLowerInvariant())', 'mono-aot-cross')) @@ -48,6 +49,7 @@ $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'microsoft.netcore.app.runtime.browser-wasm')) $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'MonoAOTCompiler')) + $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'JsonToItemsTaskFactory')) $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'WasmAppBuilder')) $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'WasmBuildTasks')) @@ -60,10 +62,12 @@ - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse), 'runtimes', 'browser-wasm')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse))) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) + $([MSBuild]::NormalizePath('$(JsonToItemsTaskFactoryDir)', 'JsonToItemsTaskFactory.dll')) diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets index b34d8f03bfa..ae05dae8a55 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.targets +++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets @@ -60,10 +60,12 @@ Text="%24(RuntimeSrcDir) is set, but %24(RuntimeConfig) is not" /> - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse), 'runtimes', 'browser-wasm')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse))) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) + $([MSBuild]::NormalizePath('$(JsonToItemsTaskFactoryDir)', 'JsonToItemsTaskFactory.dll')) .exe true - - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) - <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) - <_EmccPropsPath>$(_WasmRuntimePackSrcDir)Emcc.props - - - @@ -325,6 +315,14 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ + + + + + + + + @@ -469,4 +467,11 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ + + + + + + diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 03428743689..10bd9ac7973 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -80,12 +80,6 @@ false - - $([MSBuild]::NormalizeDirectory($(NuGetPackageRoot), 'microsoft.netcore.app.runtime.mono.browser-wasm', '$(BundledNETCoreAppPackageVersion)')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) - <_BeforeWasmBuildAppDependsOn /> @@ -97,6 +91,20 @@ + + + + %(ResolvedRuntimePack.PackageDirectory) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) + + + <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) + <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + + diff --git a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index cdd5e95a4e2..3a7b66fac73 100644 --- a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -22,7 +22,7 @@ - diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index b8ee77e5e27..3eacc3e1307 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -89,7 +89,7 @@ WriteOnlyWhenDifferent="true" Overwrite="true" /> - + <_EmccVersionRaw>%(_ReversedVersionLines.Identity) - <_EmccVersionRegexPattern>^ *emcc \([^\)]+\) *([^ \(]+) *\(([^\)]+)\)$ + <_EmccVersionRegexPattern>^ *emcc \([^\)]+\) *([0-9\.]+).*\(([^\)]+)\)$ <_EmccVersion>$([System.Text.RegularExpressions.Regex]::Match($(_EmccVersionRaw), $(_EmccVersionRegexPattern)).Groups[1].Value) <_EmccVersionHash>$([System.Text.RegularExpressions.Regex]::Match($(_EmccVersionRaw), $(_EmccVersionRegexPattern)).Groups[2].Value) - <_EmccPropsContent> + <_EmccPropsJson> - - $(_EmccVersionRaw) - $(_EmccVersion) - $(_EmccVersionHash) - - +{ + "items": { + "EmccProperties": [ + { "identity": "RuntimeEmccVersion", "value": "$(_EmccVersion)" }, + { "identity": "RuntimeEmccVersionRaw", "value": "$(_EmccVersionRaw)" }, + { "identity": "RuntimeEmccVersionHash", "value": "$(_EmccVersionHash)" } + ] + } +} ]]> - + - @@ -146,7 +148,7 @@ + $(NativeBinDir)src\emcc-props.json" /> @@ -259,7 +261,7 @@ + $(NativeBinDir)src\emcc-props.json" /> diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index ea2aba607dd..7b25107d56f 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -185,12 +185,12 @@ internal static class Utils } #if NETCOREAPP - public static void DirectoryCopy(string sourceDir, string destDir, Func predicate) + public static void DirectoryCopy(string sourceDir, string destDir, Func? predicate=null) { string[] files = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories); foreach (string file in files) { - if (!predicate(file)) + if (predicate != null && !predicate(file)) continue; string relativePath = Path.GetRelativePath(sourceDir, file); diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index 78a6a4b1faa..2a8a2f12cbb 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -123,7 +123,7 @@ namespace Microsoft.WebAssembly.Build.Tasks if (exitCode != 0) { - Log.LogError($"Failed to compile {srcFile} -> {objFile}: {output}"); + Log.LogError($"Failed to compile {srcFile} -> {objFile}{Environment.NewLine}{output}"); return false; } @@ -135,7 +135,7 @@ namespace Microsoft.WebAssembly.Build.Tasks } catch (Exception ex) { - Log.LogError($"Failed to compile {srcFile} -> {objFile}: {ex.Message}"); + Log.LogError($"Failed to compile {srcFile} -> {objFile}{Environment.NewLine}{ex.Message}"); return false; } } diff --git a/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs new file mode 100644 index 00000000000..c76659d5db3 --- /dev/null +++ b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs @@ -0,0 +1,228 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable enable + +namespace Microsoft.Workload.Build.Tasks +{ + public class InstallWorkloadFromArtifacts : Task + { + [Required, NotNull] + public ITaskItem? WorkloadId { get; set; } + + [Required, NotNull] + public string? VersionBand { get; set; } + + [Required, NotNull] + public string? LocalNuGetsPath { get; set; } + + [Required, NotNull] + public string? SdkDir { get; set; } + + public ITaskItem[] ExtraNuGetSources { get; set; } = Array.Empty(); + + public override bool Execute() + { + Utils.Logger = Log; + + if (!HasMetadata(WorkloadId, nameof(WorkloadId), "Version") || + !HasMetadata(WorkloadId, nameof(WorkloadId), "ManifestName")) + { + return false; + } + + if (!Directory.Exists(SdkDir)) + { + Log.LogError($"Cannot find SdkDir={SdkDir}"); + return false; + } + + Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** Installing workload manifest {WorkloadId.ItemSpec} **{Environment.NewLine}"); + + string nugetConfigContents = GetNuGetConfig(); + if (!InstallWorkloadManifest(WorkloadId.GetMetadata("ManifestName"), WorkloadId.GetMetadata("Version"), nugetConfigContents, stopOnMissing: true)) + return false; + + string nugetConfigPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + File.WriteAllText(nugetConfigPath, nugetConfigContents); + + Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** workload install **{Environment.NewLine}"); + (int exitCode, string output) = Utils.TryRunProcess( + Path.Combine(SdkDir, "dotnet"), + $"workload install --skip-manifest-update --no-cache --configfile \"{nugetConfigPath}\" {WorkloadId.ItemSpec}", + workingDir: Path.GetTempPath(), + silent: false, + debugMessageImportance: MessageImportance.High); + if (exitCode != 0) + { + Log.LogError($"workload install failed: {output}"); + + foreach (var dir in Directory.EnumerateDirectories(Path.Combine(SdkDir, "sdk-manifests"), "*", SearchOption.AllDirectories)) + Log.LogMessage(MessageImportance.Low, $"\t{Path.Combine(SdkDir, "sdk-manifests", dir)}"); + + foreach (var dir in Directory.EnumerateDirectories(Path.Combine(SdkDir, "packs"), "*", SearchOption.AllDirectories)) + Log.LogMessage(MessageImportance.Low, $"\t{Path.Combine(SdkDir, "packs", dir)}"); + + return false; + } + + return !Log.HasLoggedErrors; + } + + private string GetNuGetConfig() + { + StringBuilder nugetConfigBuilder = new(); + nugetConfigBuilder.AppendLine($"{Environment.NewLine}"); + + nugetConfigBuilder.AppendLine($@""); + foreach (ITaskItem source in ExtraNuGetSources) + { + string key = source.ItemSpec; + string value = source.GetMetadata("Value"); + if (string.IsNullOrEmpty(value)) + { + Log.LogWarning($"ExtraNuGetSource {key} is missing Value metadata"); + continue; + } + + nugetConfigBuilder.AppendLine($@""); + } + + nugetConfigBuilder.AppendLine($"{Environment.NewLine}"); + return nugetConfigBuilder.ToString(); + } + + private bool InstallWorkloadManifest(string name, string version, string nugetConfigContents, bool stopOnMissing) + { + Log.LogMessage(MessageImportance.High, $"Installing workload manifest for {name}/{version}"); + + // Find any existing directory with the manifest name, ignoring the case + // Multiple directories for a manifest, differing only in case causes + // workload install to fail due to duplicate manifests! + // This is applicable only on case-sensitive filesystems + string outputDir = FindSubDirIgnoringCase(Path.Combine(SdkDir, "sdk-manifests", VersionBand), name); + + PackageReference pkgRef = new(Name: $"{name}.Manifest-{VersionBand}", + Version: version, + OutputDir: outputDir, + relativeSourceDir: "data"); + + if (!PackageInstaller.Install(new[]{ pkgRef }, nugetConfigContents, Log, stopOnMissing)) + return false; + + string manifestDir = pkgRef.OutputDir; + string jsonPath = Path.Combine(manifestDir, "WorkloadManifest.json"); + if (!File.Exists(jsonPath)) + { + Log.LogError($"Could not find WorkloadManifest.json at {jsonPath}"); + return false; + } + + ManifestInformation? manifest; + try + { + manifest = JsonSerializer.Deserialize( + File.ReadAllBytes(jsonPath), + new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }); + + if (manifest == null) + { + Log.LogError($"Could not parse manifest from {jsonPath}."); + return false; + } + } + catch (JsonException je) + { + Log.LogError($"Failed to read from {jsonPath}: {je.Message}"); + return false; + } + + if (manifest.DependsOn != null) + { + foreach ((string depName, string depVersion) in manifest.DependsOn) + { + if (!InstallWorkloadManifest(depName, depVersion, nugetConfigContents, stopOnMissing: false)) + { + Log.LogWarning($"Could not install manifest {depName}/{depVersion}. This can be ignored if the workload {WorkloadId.ItemSpec} doesn't depend on it."); + continue; + } + } + } + + return true; + } + + private bool HasMetadata(ITaskItem item, string itemName, string metadataName) + { + if (!string.IsNullOrEmpty(item.GetMetadata(metadataName))) + return true; + + Log.LogError($"{itemName} item ({item.ItemSpec}) is missing Name metadata"); + return false; + } + + private string FindSubDirIgnoringCase(string parentDir, string dirName) + { + IEnumerable matchingDirs = Directory.EnumerateDirectories(parentDir, + dirName, + new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }); + + string? first = matchingDirs.FirstOrDefault(); + if (matchingDirs.Count() > 1) + { + Log.LogWarning($"Found multiple directories with names that differ only in case. {string.Join(", ", matchingDirs.ToArray())}" + + $"{Environment.NewLine}Using the first one: {first}"); + } + + return first ?? Path.Combine(parentDir, dirName); + } + + private record ManifestInformation( + object Version, + string Description, + + [property: JsonPropertyName("depends-on")] + IDictionary DependsOn, + IDictionary Workloads, + IDictionary Packs, + object Data + ); + + private record WorkloadInformation( + bool Abstract, + string Kind, + string Description, + + List Packs, + List Extends, + List Platforms + ); + + private record PackVersionInformation( + string Kind, + string Version, + [property: JsonPropertyName("alias-to")] + Dictionary AliasTo + ); + } + + internal record PackageReference(string Name, + string Version, + string OutputDir, + string relativeSourceDir = ""); +} diff --git a/src/tasks/WorkloadBuildTasks/PackageInstaller.cs b/src/tasks/WorkloadBuildTasks/PackageInstaller.cs new file mode 100644 index 00000000000..f6d4e85cf76 --- /dev/null +++ b/src/tasks/WorkloadBuildTasks/PackageInstaller.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable enable + +namespace Microsoft.Workload.Build.Tasks +{ + internal class PackageInstaller + { + private readonly string _tempDir; + private string _nugetConfigContents; + private TaskLoggingHelper _logger; + private string _packagesDir; + + private PackageInstaller(string nugetConfigContents, TaskLoggingHelper logger) + { + _nugetConfigContents = nugetConfigContents; + + _logger = logger; + _tempDir = Path.Combine(Path.GetTempPath(), "install-workload", Path.GetRandomFileName()); + _packagesDir = Path.Combine(_tempDir, "nuget-packages"); + } + + public static bool Install(PackageReference[] references, string nugetConfigContents, TaskLoggingHelper logger, bool stopOnMissing=true) + { + if (!references.Any()) + return true; + + return new PackageInstaller(nugetConfigContents, logger) + .InstallActual(references, stopOnMissing); + } + + private bool InstallActual(PackageReference[] references, bool stopOnMissing) + { + // Restore packages + if (Directory.Exists(_packagesDir)) + { + _logger.LogMessage(MessageImportance.Low, $"Deleting {_packagesDir}"); + Directory.Delete(_packagesDir, recursive: true); + } + + var projecDir = Path.Combine(_tempDir, "restore"); + var projectPath = Path.Combine(projecDir, "Restore.csproj"); + + Directory.CreateDirectory(projecDir); + + File.WriteAllText(Path.Combine(projecDir, "Directory.Build.props"), ""); + File.WriteAllText(Path.Combine(projecDir, "Directory.Build.targets"), ""); + File.WriteAllText(projectPath, GenerateProject(references)); + File.WriteAllText(Path.Combine(projecDir, "nuget.config"), _nugetConfigContents); + + _logger.LogMessage(MessageImportance.Low, $"Restoring packages: {string.Join(", ", references.Select(r => $"{r.Name}/{r.Version}"))}"); + + string args = $"restore \"{projectPath}\" /p:RestorePackagesPath=\"{_packagesDir}\""; + (int exitCode, string output) = Utils.TryRunProcess("dotnet", args, silent: false, debugMessageImportance: MessageImportance.Low); + if (exitCode != 0) + { + LogErrorOrWarning($"Restoring packages failed with exit code: {exitCode}. Output:{Environment.NewLine}{output}", stopOnMissing); + return false; + } + + IList<(PackageReference, string)> failedToRestore = references + .Select(r => (r, Path.Combine(_packagesDir, r.Name.ToLower(), r.Version))) + .Where(tuple => !Directory.Exists(tuple.Item2)) + .ToList(); + + if (failedToRestore.Count > 0) + { + _logger.LogMessage(MessageImportance.Normal, output); + foreach ((PackageReference pkgRef, string pkgDir) in failedToRestore) + LogErrorOrWarning($"Could not restore {pkgRef.Name}/{pkgRef.Version} (can't find {pkgDir})", stopOnMissing); + + return false; + } + + return LayoutPackages(references, stopOnMissing); + } + + private bool LayoutPackages(IEnumerable references, bool stopOnMissing) + { + foreach (var pkgRef in references) + { + var source = Path.Combine(_packagesDir, pkgRef.Name.ToLower(), pkgRef.Version, pkgRef.relativeSourceDir); + if (!Directory.Exists(source)) + { + LogErrorOrWarning($"Failed to restore {pkgRef.Name}/{pkgRef.Version} (could not find {source})", stopOnMissing); + if (stopOnMissing) + return false; + } + else + { + if (!CopyDirectoryAfresh(source, pkgRef.OutputDir) && stopOnMissing) + return false; + } + } + + return true; + } + + private static string GenerateProject(IEnumerable references) + { + StringBuilder projectFileBuilder = new(); + projectFileBuilder.Append(@" + + + net6.0 + + "); + + foreach (var reference in references) + projectFileBuilder.AppendLine($""); + + projectFileBuilder.Append(@" + + +"); + + return projectFileBuilder.ToString(); + } + + private bool CopyDirectoryAfresh(string srcDir, string destDir) + { + try + { + if (Directory.Exists(destDir)) + { + _logger.LogMessage(MessageImportance.Low, $"Deleting {destDir}"); + Directory.Delete(destDir, recursive: true); + } + + _logger.LogMessage(MessageImportance.Low, $"Copying {srcDir} to {destDir}"); + Directory.CreateDirectory(destDir); + Utils.DirectoryCopy(srcDir, destDir); + + return true; + } + catch (Exception ex) + { + _logger.LogError($"Failed while copying {srcDir} => {destDir}: {ex.Message}"); + if (ex is IOException) + return false; + + throw; + } + } + + private void LogErrorOrWarning(string msg, bool stopOnMissing) + { + if (stopOnMissing) + _logger.LogError(msg); + else + _logger.LogWarning(msg); + } + } +} diff --git a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj index 328672b4514..537418ef31d 100644 --- a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj +++ b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj @@ -6,6 +6,8 @@ $(NoWarn),CA1050 + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs index c58c4b55f3d..dacf67ff4f3 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs @@ -19,7 +19,8 @@ namespace Wasm.Build.Tests [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] public void PublishTemplateProject() { - InitPaths("id"); + string id = "blazorwasm"; + InitPaths(id); if (Directory.Exists(_projectDir)) Directory.Delete(_projectDir, recursive: true); Directory.CreateDirectory(_projectDir); @@ -29,14 +30,17 @@ namespace Wasm.Build.Tests File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.props"), Path.Combine(_projectDir, "Directory.Build.props")); File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets")); - new DotNetCommand(s_buildEnv) + string logPath = Path.Combine(s_buildEnv.LogRootPath, id); + + new DotNetCommand(s_buildEnv, useDefaultArgs: false) .WithWorkingDirectory(_projectDir) .ExecuteWithCapturedOutput("new blazorwasm") .EnsureSuccessful(); + string publishLogPath = Path.Combine(logPath, $"{id}.publish.binlog"); new DotNetCommand(s_buildEnv) .WithWorkingDirectory(_projectDir) - .ExecuteWithCapturedOutput("publish -bl -p:RunAOTCompilation=true") + .ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", "-p:RunAOTCompilation=true") .EnsureSuccessful(); //TODO: validate the build somehow? diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs index 231b215f88b..a8f67d09eb6 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; namespace Wasm.Build.Tests { @@ -24,7 +25,6 @@ namespace Wasm.Build.Tests public static readonly string TestDataPath = Path.Combine(AppContext.BaseDirectory, "data"); private static string s_runtimeConfig = "Release"; - private const string s_testLogPathEnvVar = "TEST_LOG_PATH"; public BuildEnvironment() { @@ -39,12 +39,11 @@ namespace Wasm.Build.Tests solutionRoot = solutionRoot.Parent; } - string? sdkForWorkloadPath = Environment.GetEnvironmentVariable("SDK_FOR_WORKLOAD_TESTING_PATH"); + string? sdkForWorkloadPath = EnvironmentVariables.SdkForWorkloadTestingPath; if (!string.IsNullOrEmpty(sdkForWorkloadPath)) { - DotNet = Path.Combine(sdkForWorkloadPath, "dotnet"); - var workloadPacksVersion = Environment.GetEnvironmentVariable("WORKLOAD_PACKS_VER"); + var workloadPacksVersion = EnvironmentVariables.WorkloadPacksVersion; if (string.IsNullOrEmpty(workloadPacksVersion)) throw new Exception($"Cannot test with workloads without WORKLOAD_PACKS_VER environment variable being set"); @@ -63,7 +62,7 @@ namespace Wasm.Build.Tests ["PATH"] = $"{sdkForWorkloadPath}{Path.PathSeparator}{Environment.GetEnvironmentVariable("PATH")}" }; - var appRefDir = Environment.GetEnvironmentVariable("AppRefDir"); + var appRefDir = EnvironmentVariables.AppRefDir; if (string.IsNullOrEmpty(appRefDir)) throw new Exception($"Cannot test with workloads without AppRefDir environment variable being set"); @@ -75,8 +74,7 @@ namespace Wasm.Build.Tests string emsdkPath; if (solutionRoot == null) { - string? buildDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); - + string? buildDir = EnvironmentVariables.WasmBuildSupportDir; if (buildDir == null || !Directory.Exists(buildDir)) throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); @@ -89,11 +87,10 @@ namespace Wasm.Build.Tests string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); RuntimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); - string? emsdkEnvValue = Environment.GetEnvironmentVariable("EMSDK_PATH"); - if (string.IsNullOrEmpty(emsdkEnvValue)) + if (string.IsNullOrEmpty(EnvironmentVariables.EMSDK_PATH)) emsdkPath = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); else - emsdkPath = emsdkEnvValue; + emsdkPath = EnvironmentVariables.EMSDK_PATH; DefaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={emsdkPath} "; } @@ -110,11 +107,12 @@ namespace Wasm.Build.Tests } RuntimeNativeDir = Path.Combine(RuntimePackDir, "runtimes", "browser-wasm", "native"); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + DotNet += ".exe"; - string? logPathEnvVar = Environment.GetEnvironmentVariable(s_testLogPathEnvVar); - if (!string.IsNullOrEmpty(logPathEnvVar)) + if (!string.IsNullOrEmpty(EnvironmentVariables.TestLogPath)) { - LogRootPath = logPathEnvVar; + LogRootPath = EnvironmentVariables.TestLogPath; if (!Directory.Exists(LogRootPath)) { Directory.CreateDirectory(LogRootPath); @@ -126,9 +124,8 @@ namespace Wasm.Build.Tests } } - // FIXME: update these to use Workload variants of the file, with the workload support - protected static string s_directoryBuildPropsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props")); - protected static string s_directoryBuildTargetsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets")); + protected static string s_directoryBuildPropsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Workloads.Directory.Build.props")); + protected static string s_directoryBuildTargetsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Workloads.Directory.Build.targets")); protected static string s_directoryBuildPropsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props")); protected static string s_directoryBuildTargetsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets")); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 021ce531b17..7d211ae6189 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -8,7 +8,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using Xunit; @@ -17,12 +17,12 @@ using Xunit.Sdk; #nullable enable +// [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] + namespace Wasm.Build.Tests { public abstract class BuildTestBase : IClassFixture, IDisposable { - protected const string SkipProjectCleanupEnvVar = "SKIP_PROJECT_CLEANUP"; - protected const string XHarnessRunnerCommandEnvVar = "XHARNESS_CLI_PATH"; protected const string s_targetFramework = "net6.0"; protected static readonly bool s_skipProjectCleanup; protected static readonly string s_xharnessRunnerCommand; @@ -32,6 +32,8 @@ namespace Wasm.Build.Tests protected bool _enablePerTestCleanup = false; protected SharedBuildPerTestClassFixture _buildContext; + // FIXME: use an envvar to override this + protected static int s_defaultPerTestTimeoutMs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 30*60*1000 : 15*60*1000; protected static BuildEnvironment s_buildEnv; private const string s_runtimePackPathPattern = "\\*\\* MicrosoftNetCoreAppRuntimePackDir : ([^ ]*)"; private static Regex s_runtimePackPathRegex; @@ -44,27 +46,18 @@ namespace Wasm.Build.Tests s_buildEnv = new BuildEnvironment(); s_runtimePackPathRegex = new Regex(s_runtimePackPathPattern); - string? cleanupVar = Environment.GetEnvironmentVariable(SkipProjectCleanupEnvVar); - s_skipProjectCleanup = !string.IsNullOrEmpty(cleanupVar) && cleanupVar == "1"; + s_skipProjectCleanup = !string.IsNullOrEmpty(EnvironmentVariables.SkipProjectCleanup) && EnvironmentVariables.SkipProjectCleanup == "1"; - s_xharnessRunnerCommand = GetEnvironmentVariableOrDefault(XHarnessRunnerCommandEnvVar, "xharness"); + if (string.IsNullOrEmpty(EnvironmentVariables.XHarnessCliPath)) + s_xharnessRunnerCommand = "xharness"; + else + s_xharnessRunnerCommand = EnvironmentVariables.XHarnessCliPath; string? nugetPackagesPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); if (!string.IsNullOrEmpty(nugetPackagesPath)) { if (!Directory.Exists(nugetPackagesPath)) - { Directory.CreateDirectory(nugetPackagesPath); - Console.WriteLine ($"-- Created {nugetPackagesPath}"); - } - else - { - Console.WriteLine ($"-- already exists {nugetPackagesPath}"); - } - } - else - { - Console.WriteLine ($"-- NUGET_PACKAGES envvar was empty"); } Console.WriteLine (""); @@ -205,7 +198,8 @@ namespace Wasm.Build.Tests args: args.ToString(), workingDir: bundleDir, envVars: envVars, - label: testCommand); + label: testCommand, + timeoutMs: s_defaultPerTestTimeoutMs); File.WriteAllText(Path.Combine(testLogPath, $"xharness.log"), output); @@ -256,7 +250,10 @@ namespace Wasm.Build.Tests protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProperties="", string extraItems="", string insertAtEnd="", string projectTemplate=SimpleProjectTemplate) { if (buildArgs.AOT) - extraProperties = $"{extraProperties}\ntrue\nfalse\n"; + { + extraProperties = $"{extraProperties}\ntrue"; + extraProperties += $"\n{RuntimeInformation.IsOSPlatform(OSPlatform.Windows)}\n"; + } string projectContents = projectTemplate .Replace("##EXTRA_PROPERTIES##", extraProperties) @@ -310,7 +307,8 @@ namespace Wasm.Build.Tests _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); Console.WriteLine($"Binlog path: {logFilePath}"); - sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); + sb.Append($" /bl:\"{logFilePath}\" /nologo"); + sb.Append($" /v:diag /fl /flp:\"v:diag,LogFile={logFilePath}.log\" /v:minimal"); if (buildArgs.ExtraBuildArgs != null) sb.Append($" {buildArgs.ExtraBuildArgs} "); @@ -451,9 +449,9 @@ namespace Wasm.Build.Tests Assert.True(finfo0.Length != finfo1.Length, $"{label}: File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})"); } - protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true, IDictionary? envVars=null) + protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true, IDictionary? envVars=null, int? timeoutMs=null) { - var result = RunProcess(s_buildEnv.DotNet, _testOutput, args, workingDir: _projectDir, label: label, envVars: envVars); + var result = RunProcess(s_buildEnv.DotNet, _testOutput, args, workingDir: _projectDir, label: label, envVars: envVars, timeoutMs: timeoutMs ?? s_defaultPerTestTimeoutMs); if (expectSuccess) Assert.True(0 == result.exitCode, $"Build process exited with non-zero exit code: {result.exitCode}"); else @@ -475,13 +473,16 @@ namespace Wasm.Build.Tests IDictionary? envVars = null, string? workingDir = null, string? label = null, - bool logToXUnit = true) + bool logToXUnit = true, + int? timeoutMs = null) { _testOutput.WriteLine($"Running {path} {args}"); Console.WriteLine($"Running: {path}: {args}"); Console.WriteLine($"WorkingDirectory: {workingDir}"); _testOutput.WriteLine($"WorkingDirectory: {workingDir}"); StringBuilder outputBuilder = new (); + object syncObj = new(); + var processStartInfo = new ProcessStartInfo { FileName = path, @@ -529,8 +530,23 @@ namespace Wasm.Build.Tests // process.WaitForExit doesn't work if the process exits too quickly? // resetEvent.WaitOne(); - process.WaitForExit(); - return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); + if (!process.WaitForExit(timeoutMs ?? s_defaultPerTestTimeoutMs)) + { + // process didn't exit + process.Kill(entireProcessTree: true); + lock (syncObj) + { + var lastLines = outputBuilder.ToString().Split('\r', '\n').TakeLast(20); + throw new XunitException($"Process timed out, output: {string.Join(Environment.NewLine, lastLines)}"); + } + + } + + lock (syncObj) + { + var exitCode = process.ExitCode; + return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); + } } catch (Exception ex) { @@ -540,12 +556,15 @@ namespace Wasm.Build.Tests void LogData(string label, string? message) { - if (logToXUnit && message != null) + lock (syncObj) { - _testOutput.WriteLine($"{label} {message}"); - Console.WriteLine($"{label} {message}"); + if (logToXUnit && message != null) + { + _testOutput.WriteLine($"{label} {message}"); + Console.WriteLine($"{label} {message}"); + } + outputBuilder.AppendLine($"{label} {message}"); } - outputBuilder.AppendLine($"{label} {message}"); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs index 2fefb87c0f0..4136bf8ccea 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs @@ -6,14 +6,18 @@ namespace Wasm.Build.Tests public class DotNetCommand : ToolCommand { private BuildEnvironment _buildEnvironment; + private bool _useDefaultArgs; - public DotNetCommand(BuildEnvironment buildEnv) : base(buildEnv.DotNet) + public DotNetCommand(BuildEnvironment buildEnv, bool useDefaultArgs=true) : base(buildEnv.DotNet) { _buildEnvironment = buildEnv; + _useDefaultArgs = useDefaultArgs; WithEnvironmentVariables(buildEnv.EnvVars); } protected override string GetFullArgs(params string[] args) - => $"{_buildEnvironment.DefaultBuildArgs} {string.Join(" ", args)}"; + => _useDefaultArgs + ? $"{string.Join(" ", args)} {_buildEnvironment.DefaultBuildArgs}" + : string.Join(" ", args); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/EnvironmentVariables.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/EnvironmentVariables.cs new file mode 100644 index 00000000000..5ab3ad4108c --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/EnvironmentVariables.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +#nullable enable + +namespace Wasm.Build.Tests +{ + internal static class EnvironmentVariables + { + internal static readonly string? SdkForWorkloadTestingPath = Environment.GetEnvironmentVariable("SDK_FOR_WORKLOAD_TESTING_PATH"); + internal static readonly string? WorkloadPacksVersion = Environment.GetEnvironmentVariable("WORKLOAD_PACKS_VER"); + internal static readonly string? AppRefDir = Environment.GetEnvironmentVariable("AppRefDir"); + internal static readonly string? WasmBuildSupportDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); + internal static readonly string? EMSDK_PATH = Environment.GetEnvironmentVariable("EMSDK_PATH"); + internal static readonly string? TestLogPath = Environment.GetEnvironmentVariable("TEST_LOG_PATH"); + internal static readonly string? SkipProjectCleanup = Environment.GetEnvironmentVariable("SKIP_PROJECT_CLEANUP"); + internal static readonly string? XHarnessCliPath = Environment.GetEnvironmentVariable("XHARNESS_CLI_PATH"); + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 130e59e0ea2..30e487f8171 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -27,8 +27,6 @@ namespace Wasm.Build.Tests public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id); - - private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id) { string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/README.md b/src/tests/BuildWasmApps/Wasm.Build.Tests/README.md new file mode 100644 index 00000000000..4f0be0c3672 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/README.md @@ -0,0 +1,19 @@ +# Wasm.Build.Tests + +Contains tests for wasm project builds, eg. for aot, relinking, globalization +etc. The intent is to check if the build inputs result in a correct app bundle +being generated. + +- When running locally, it tests against a local workload install (based on `artifacts`) + - but this can be turned off with `/p:TestUsingWorkloads=false` + - in which case, it will run against `emsdk` from `EMSDK_PATH` + +- On CI, both workload, and emsdk cases are tested + +- Running: + +Linux/macOS: `$ make -C src/mono/wasm run-build-tests` +Windows: `.\dotnet.cmd build .\src\tests\BuildWasmApps\Wasm.Build.Tests\Wasm.Build.Tests.csproj -c Release -t:Test -p:TargetOS=Browser -p:TargetArchitecture=wasm` + +- Specific tests can be run via `XUnitClassName`, and `XUnitMethodName` + - eg. `XUnitClassName=Wasm.Build.Tests.BlazorWasmTests` diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 0f06b6f08df..d6709715ef0 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -17,7 +17,12 @@ - RunScriptTemplate.sh + + + + RunScriptTemplate.cmd + RunScriptTemplate.sh + $(MSBuildThisFileDirectory)data\$(RunScriptInputName) @@ -28,7 +33,7 @@ - + @@ -40,9 +45,8 @@ - - - + + @@ -50,6 +54,7 @@ + @@ -60,6 +65,9 @@ $(RunScriptCommand) -method $(XUnitMethodName) $(RunScriptCommand) -class $(XUnitClassName) + + + $(RunScriptCommand) -notrait category=IgnoreForCI -notrait category=failing diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs index 89b656ee444..ae67ab1cb4b 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs @@ -4,10 +4,12 @@ using System; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Xml; using System.Xml.Serialization; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -21,6 +23,7 @@ namespace Wasm.Build.Tests } [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + [SkipOnPlatform(TestPlatforms.Windows, "Not applicable on windows")] public void FilesInUnixFilesPermissionsXmlExist() { // not doing any project generation here @@ -54,7 +57,14 @@ namespace Wasm.Build.Tests // We don't install the cross compiler pack from nupkg, so we don't // have the unixFilePermissions for that // Expect just the emscripten ones here for now - Assert.Equal(3, unixPermFiles.Count()); + + // linux doesn't have Emscripten.Python package, so only 2 there + int expectedPermFileCount = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 3 : 4; + + int permFileCount = unixPermFiles.Count(); + if (permFileCount != expectedPermFileCount) + throw new XunitException($"Expected to find 3 UnixFilePermissions.xml files, but got {permFileCount}." + + $"{Environment.NewLine}Files: {string.Join(", ", unixPermFiles)}"); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets index 1ae8c2cd6ff..b0384d714e3 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets @@ -5,7 +5,7 @@ - + @@ -33,7 +33,7 @@ FrameworkName="Microsoft.NETCore.App" NuGetPackageId="Microsoft.NETCore.App.Runtime.Mono.browser-wasm" NuGetPackageVersion="$(RuntimePackInWorkloadVersion)" - PackageDirectory="$(WorkloadPacksDir)\Microsoft.NETCore.App.Runtime.Mono.browser-wasm\$(RuntimePackInWorkloadVersion)" + PackageDirectory="$(NetCoreTargetingPackRoot)\Microsoft.NETCore.App.Runtime.Mono.browser-wasm\$(RuntimePackInWorkloadVersion)" RuntimeIdentifier="browser-wasm" /> diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets index 8c0b1e7e46e..65f76e4fc85 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets @@ -23,7 +23,7 @@ - + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.cmd b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.cmd new file mode 100644 index 00000000000..0c2cfd89d1e --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.cmd @@ -0,0 +1,52 @@ +@echo off +setlocal enabledelayedexpansion + +set EXECUTION_DIR=%~dp0 +set SCENARIO=%3 + +cd %EXECUTION_DIR% + +if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] ( + set XHARNESS_OUT=%EXECUTION_DIR%xharness-output +) else ( + set XHARNESS_OUT=%HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output +) + +if [%XHARNESS_CLI_PATH%] NEQ [] ( + :: When running in CI, we only have the .NET runtime available + :: We need to call the XHarness CLI DLL directly via dotnet exec + set HARNESS_RUNNER=dotnet.exe exec "%XHARNESS_CLI_PATH%" +) else ( + set HARNESS_RUNNER=dotnet.exe xharness +) + +set TEST_LOG_PATH=%XHARNESS_OUT%\logs + +:: ========================= BEGIN Test Execution ============================= +echo ----- start %DATE% %TIME% =============== To repro directly: ===================================================== +echo pushd %EXECUTION_DIR% +[[RunCommandsEcho]] +echo popd +echo =========================================================================================================== +pushd %EXECUTION_DIR% +@echo on +[[RunCommands]] +set EXIT_CODE=%ERRORLEVEL% +@echo off +popd +echo ----- end %DATE% %TIME% ----- exit code %EXIT_CODE% ---------------------------------------------------------- + +echo XHarness artifacts: %XHARNESS_OUT% + +exit /b %EXIT_CODE% + +REM Functions +:SetEnvVars +if [%TEST_USING_WORKLOADS%] == [true] ( + set "PATH=%BASE_DIR%\dotnet-workload;%PATH%" + set "SDK_FOR_WORKLOAD_TESTING_PATH=%BASE_DIR%\dotnet-workload" + set "AppRefDir=%BASE_DIR%\microsoft.netcore.app.ref" +) else ( + set "WasmBuildSupportDir=%BASE_DIR%\build" +) +EXIT /b 0 diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props new file mode 100644 index 00000000000..6d8504088b9 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props @@ -0,0 +1,7 @@ + + + browser-wasm + true + true + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets new file mode 100644 index 00000000000..19f795a01f2 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets @@ -0,0 +1,95 @@ + + + PrepareForWasmBuild;$(WasmBuildAppDependsOn) + <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ + Microsoft.NETCore.App + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_targetingPackReferenceExclusion Include="$(TargetName)" /> + <_targetingPackReferenceExclusion Include="@(_ResolvedProjectReferencePaths->'%(Filename)')" /> + <_targetingPackReferenceExclusion Include="@(DefaultReferenceExclusion)" /> + + + + <_targetingPackReferenceWithExclusion Include="@(Reference)"> + %(_targetingPackReferenceExclusion.Identity) + + + + + diff --git a/src/tests/Common/Directory.Build.targets b/src/tests/Common/Directory.Build.targets index 31e55c12ccf..ed56d4d011b 100644 --- a/src/tests/Common/Directory.Build.targets +++ b/src/tests/Common/Directory.Build.targets @@ -146,6 +146,10 @@ Include="$(ArtifactsBinDir)\WasmAppBuilder\Debug\$(NetCoreAppToolCurrent)\publish\**" TargetDir="WasmAppBuilder/"/> + + diff --git a/src/tests/Common/wasm-test-runner/WasmTestRunner.proj b/src/tests/Common/wasm-test-runner/WasmTestRunner.proj index c28b704c4f3..f9479f96746 100644 --- a/src/tests/Common/wasm-test-runner/WasmTestRunner.proj +++ b/src/tests/Common/wasm-test-runner/WasmTestRunner.proj @@ -4,6 +4,7 @@ false + $(CORE_ROOT)\runtimepack-non-existant $(CORE_ROOT)\runtimepack $(NetCoreAppCurrent) $(MSBuildThisFileDirectory)\obj\$(Configuration)\wasm @@ -12,6 +13,7 @@ $(CORE_ROOT)\WasmAppBuilder\WasmAppBuilder.dll $(CORE_ROOT)\MonoAOTCompiler\MonoAOTCompiler.dll + $(CORE_ROOT)\JsonToItemsTaskFactory\JsonToItemsTaskFactory.dll diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 71494ce1d6a..6a361c68483 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -377,6 +377,10 @@ Include="$(ArtifactsBinDir)\MonoAOTCompiler\Debug\$(NetCoreAppToolCurrent)\**" TargetDir="MonoAOTCompiler/"/> + + From 8d365a0d281cbb431f92e211184f98059df667e6 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 14 Jul 2021 07:54:04 -0700 Subject: [PATCH 545/926] Migrate various parts of the bindings code to use roots (#55530) We have a lot of code that passes raw pointers to managed objects around when they should really be using roots instead (i.e. root.value) so that if the GC relocates the object, they won't be using the old address. This PR migrates a bunch of that code so that it uses root objects. A lot of code remains that does use raw objects, but I read over all of it and the cases appear to be safe because they immediately return it (or return it without calling any other functions). --- src/mono/wasm/runtime/binding_support.js | 493 +++++++++++++---------- 1 file changed, 274 insertions(+), 219 deletions(-) diff --git a/src/mono/wasm/runtime/binding_support.js b/src/mono/wasm/runtime/binding_support.js index 6f0040fe598..916032d0db0 100644 --- a/src/mono/wasm/runtime/binding_support.js +++ b/src/mono/wasm/runtime/binding_support.js @@ -362,8 +362,8 @@ var BindingSupportLib = { } }, - _unbox_delegate_rooted: function (mono_obj) { - var obj = this.extract_js_obj (mono_obj); + _unbox_delegate_root: function (root) { + var obj = this.extract_js_obj_root (root); obj.__mono_delegate_alive__ = true; // FIXME: Should we root the object as long as this function has not been GCd? return function () { @@ -372,11 +372,11 @@ var BindingSupportLib = { }; }, - _unbox_task_rooted: function (mono_obj) { + _unbox_task_root: function (root) { if (!this._are_promises_supported) throw new Error ("Promises are not supported thus 'System.Threading.Tasks.Task' can not work in this context."); - var obj = this.extract_js_obj (mono_obj); + var obj = this.extract_js_obj_root (root); var cont_obj = null; var promise = new Promise (function (resolve, reject) { cont_obj = { @@ -385,15 +385,16 @@ var BindingSupportLib = { }; }); - this.call_method (this.setup_js_cont, null, "mo", [ mono_obj, cont_obj ]); + // FIXME: Lifetime management/pinning? + this.call_method (this.setup_js_cont, null, "mo", [ root.value, cont_obj ]); obj.__mono_js_cont__ = cont_obj.__mono_gchandle__; cont_obj.__mono_js_task__ = obj.__mono_gchandle__; return promise; }, - _unbox_safehandle_rooted: function (mono_obj) { + _unbox_safehandle_root: function (root) { var addRef = true; - var js_handle = this.call_method(this.safehandle_get_handle, null, "mi", [ mono_obj, addRef ]); + var js_handle = this.call_method(this.safehandle_get_handle, null, "mi", [ root.value, addRef ]); var requiredObject = BINDING.mono_wasm_require_handle (js_handle); if (addRef) { @@ -405,7 +406,10 @@ var BindingSupportLib = { return requiredObject; }, - _unbox_mono_obj_rooted_with_known_nonprimitive_type: function (mono_obj, type) { + _unbox_mono_obj_root_with_known_nonprimitive_type: function (root, type) { + if (root.value === undefined) + throw new Error(`Expected a root but got ${root}`); + //See MARSHAL_TYPE_ defines in driver.c switch (type) { case 26: // int64 @@ -414,15 +418,15 @@ var BindingSupportLib = { throw new Error ("int64 not available"); case 3: // string case 29: // interned string - return this.conv_string (mono_obj); + return this.conv_string (root.value); case 4: //vts throw new Error ("no idea on how to unbox value types"); case 5: // delegate - return this._unbox_delegate_rooted (mono_obj); + return this._unbox_delegate_root (root); case 6: // Task - return this._unbox_task_rooted (mono_obj); + return this._unbox_task_root (root); case 7: // ref type - return this.extract_js_obj (mono_obj); + return this.extract_js_obj_root (root); case 10: // arrays case 11: case 12: @@ -434,29 +438,28 @@ var BindingSupportLib = { case 18: throw new Error ("Marshalling of primitive arrays are not supported. Use the corresponding TypedArray instead."); case 20: // clr .NET DateTime - var dateValue = this.call_method(this.get_date_value, null, "m", [ mono_obj ]); + var dateValue = this.call_method(this.get_date_value, null, "m", [ root.value ]); return new Date(dateValue); case 21: // clr .NET DateTimeOffset - var dateoffsetValue = this._object_to_string (mono_obj); + var dateoffsetValue = this._object_to_string (root.value); return dateoffsetValue; case 22: // clr .NET Uri - var uriValue = this._object_to_string (mono_obj); + var uriValue = this._object_to_string (root.value); return uriValue; case 23: // clr .NET SafeHandle - return this._unbox_safehandle_rooted (mono_obj); + return this._unbox_safehandle_root (root); case 30: return undefined; default: - throw new Error ("no idea on how to unbox object kind " + type + " at offset " + mono_obj); + throw new Error (`no idea on how to unbox object kind ${type} at offset ${root.value} (root address is ${root.get_address()})`); } }, _unbox_mono_obj_root: function (root) { - var mono_obj = root.value; - if (mono_obj === 0) + if (root.value === 0) return undefined; - var type = this.mono_wasm_try_unbox_primitive_and_get_type (mono_obj, this._unbox_buffer); + var type = this.mono_wasm_try_unbox_primitive_and_get_type (root.value, this._unbox_buffer); switch (type) { case 1: // int return Module.HEAP32[this._unbox_buffer / 4]; @@ -471,7 +474,7 @@ var BindingSupportLib = { case 28: // char return String.fromCharCode(Module.HEAP32[this._unbox_buffer / 4]); default: - return this._unbox_mono_obj_rooted_with_known_nonprimitive_type (mono_obj, type); + return this._unbox_mono_obj_root_with_known_nonprimitive_type (root, type); } }, @@ -788,9 +791,14 @@ var BindingSupportLib = { }, mono_method_get_call_signature: function(method, mono_obj) { - this.bindings_lazy_init (); + let instanceRoot = MONO.mono_wasm_new_root (mono_obj); + try { + this.bindings_lazy_init (); - return this.call_method (this.get_call_sig, null, "im", [ method, mono_obj ]); + return this.call_method (this.get_call_sig, null, "im", [ method, instanceRoot.value ]); + } finally { + instanceRoot.release(); + } }, get_task_and_bind: function (tcs, js_obj) { @@ -842,16 +850,27 @@ var BindingSupportLib = { }, extract_js_obj: function (mono_obj) { - if (mono_obj == 0) + if (mono_obj === 0) + return null; + var root = MONO.mono_wasm_new_root (mono_obj); + try { + return this.extract_js_obj_root (root); + } finally { + root.release(); + } + }, + + extract_js_obj_root: function (root) { + if (root.value === 0) return null; - var js_id = this.wasm_get_js_id (mono_obj); + var js_id = this.wasm_get_js_id (root.value); if (js_id > 0) return this.mono_wasm_require_handle(js_id); var gcHandle = this.mono_wasm_free_list.length ? this.mono_wasm_free_list.pop() : this.mono_wasm_ref_counter++; var js_obj = { - __mono_gchandle__: this.wasm_bind_existing(mono_obj, gcHandle + 1), + __mono_gchandle__: this.wasm_bind_existing(root.value, gcHandle + 1), is_mono_bridged_obj: true }; @@ -1396,7 +1415,7 @@ var BindingSupportLib = { } // We inline a bunch of the invoke and marshaling logic here in order to eliminate the GC pressure normally - // created by the unboxing part of the call process. Because unbox_mono_obj(_rooted) can return non-numeric + // created by the unboxing part of the call process. Because unbox_mono_obj(_root) can return non-numeric // types, v8 and spidermonkey allocate and store its result on the heap (in the nursery, to be fair). // For a bound method however, we know the result will always be the same type because C# methods have known // return types. Inlining the invoke and marshaling logic means that even though the bound method has logic @@ -1409,15 +1428,15 @@ var BindingSupportLib = { "resultRoot.value = binding_support.invoke_method (method, this_arg, buffer, exceptionRoot.get_address ());", `binding_support._handle_exception_for_call (${converterKey}, buffer, resultRoot, exceptionRoot, argsRootBuffer);`, "", - "var resultPtr = resultRoot.value, result = undefined;", + "var result = undefined;", "if (!is_result_marshaled) ", - " result = resultPtr;", - "else if (resultPtr !== 0) {", + " result = resultRoot.value;", + "else if (resultRoot.value !== 0) {", // For the common scenario where the return type is a primitive, we want to try and unbox it directly // into our existing heap allocation and then read it out of the heap. Doing this all in one operation // means that we only need to enter a gc safe region twice (instead of 3+ times with the normal, // slower check-type-and-then-unbox flow which has extra checks since unbox verifies the type). - " var resultType = binding_support.mono_wasm_try_unbox_primitive_and_get_type (resultPtr, buffer);", + " var resultType = binding_support.mono_wasm_try_unbox_primitive_and_get_type (resultRoot.value, buffer);", " switch (resultType) {", " case 1:", // int " result = Module.HEAP32[buffer / 4]; break;", @@ -1432,7 +1451,7 @@ var BindingSupportLib = { " case 28:", // char " result = String.fromCharCode(Module.HEAP32[buffer / 4]); break;", " default:", - " result = binding_support._unbox_mono_obj_rooted_with_known_nonprimitive_type (resultPtr, resultType); break;", + " result = binding_support._unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType); break;", " }", "}", "", @@ -1460,11 +1479,17 @@ var BindingSupportLib = { // Check to make sure the delegate is still alive on the CLR side of things. if (typeof delegate_obj.__mono_delegate_alive__ !== "undefined") { - if (!delegate_obj.__mono_delegate_alive__) - throw new Error("The delegate target that is being invoked is no longer available. Please check if it has been prematurely GC'd."); + if (!delegate_obj.__mono_delegate_alive__) { + // HACK: It is possible (though unlikely) for a delegate to be invoked after it's been collected + // if it's being used as a JavaScript event handler and the host environment decides to fire events + // at a point where we've already disposed of the object the event handler is attached to. + // As such, we log here instead of throwing an error. We may want to not log at all... + console.log("The delegate target that is being invoked is no longer available. Please check if it has been prematurely GC'd."); + return; + } } - var [delegateRoot] = MONO.mono_wasm_new_roots ([this.extract_mono_obj (delegate_obj)]); + var delegateRoot = MONO.mono_wasm_new_root (this.extract_mono_obj (delegate_obj)); try { if (typeof delegate_obj.__mono_delegate_invoke__ === "undefined") delegate_obj.__mono_delegate_invoke__ = this.mono_wasm_get_delegate_invoke(delegateRoot.value); @@ -1476,7 +1501,7 @@ var BindingSupportLib = { return this.call_method (delegate_obj.__mono_delegate_invoke__, delegateRoot.value, delegate_obj.__mono_delegate_invoke_sig__, js_args); } finally { - MONO.mono_wasm_release_roots (delegateRoot); + delegateRoot.release(); } }, @@ -1572,22 +1597,22 @@ var BindingSupportLib = { }, // Object wrapping helper functions to handle reference handles that will // be used in managed code. - mono_wasm_register_obj: function(obj) { + mono_wasm_register_obj: function(js_obj) { var gc_handle = undefined; - if (obj !== null && typeof obj !== "undefined") + if (js_obj !== null && typeof js_obj !== "undefined") { - gc_handle = obj.__mono_gchandle__; + gc_handle = js_obj.__mono_gchandle__; if (typeof gc_handle === "undefined") { var handle = this.mono_wasm_free_list.length ? this.mono_wasm_free_list.pop() : this.mono_wasm_ref_counter++; - obj.__mono_jshandle__ = handle; + js_obj.__mono_jshandle__ = handle; // Obtain the JS -> C# type mapping. - var wasm_type = obj[Symbol.for("wasm type")]; - obj.__owns_handle__ = true; - gc_handle = obj.__mono_gchandle__ = this.wasm_binding_obj_new(handle + 1, obj.__owns_handle__, typeof wasm_type === "undefined" ? -1 : wasm_type); - this.mono_wasm_object_registry[handle] = obj; + var wasm_type = js_obj[Symbol.for("wasm type")]; + js_obj.__owns_handle__ = true; + gc_handle = js_obj.__mono_gchandle__ = this.wasm_binding_obj_new(handle + 1, js_obj.__owns_handle__, typeof wasm_type === "undefined" ? -1 : wasm_type); + this.mono_wasm_object_registry[handle] = js_obj; } } @@ -1613,8 +1638,8 @@ var BindingSupportLib = { obj.__mono_jshandle__ = undefined; // If we are unregistering a delegate then mark it as not being alive - // this will be checked in the delegate invoke and throw an appropriate - // error. + // so that attempts will not be made to invoke it even if a JS-side + // reference to it remains (registered as an event handler, etc) if (typeof obj.__mono_delegate_alive__ !== "undefined") obj.__mono_delegate_alive__ = false; @@ -1647,8 +1672,8 @@ var BindingSupportLib = { } return obj; }, - mono_wasm_parse_args : function (args) { - var js_args = this.mono_array_to_js_array(args); + mono_wasm_parse_args_root : function (argsRoot) { + var js_args = this._mono_array_root_to_js_array(argsRoot); this.mono_wasm_save_LMF(); return js_args; }, @@ -1662,7 +1687,7 @@ var BindingSupportLib = { // Release all managed objects that are loaded into the LMF if (typeof __owned_objects__ !== "undefined") { - // Look into passing the array of owned object handles in one pass. + // TODO: Look into passing the array of owned object handles in one pass. var refidx; for (refidx = 0; refidx < __owned_objects__.length; refidx++) { @@ -1680,115 +1705,130 @@ var BindingSupportLib = { }, mono_wasm_invoke_js_with_args: function(js_handle, method_name, args, is_exception) { - BINDING.bindings_lazy_init (); - - var obj = BINDING.get_js_obj (js_handle); - if (!obj) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); - } - - var js_name = BINDING.unbox_mono_obj (method_name); - if (!js_name || (typeof(js_name) !== "string")) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid method name object '" + method_name + "'"); - } - - var js_args = BINDING.mono_wasm_parse_args(args); - - var res; + let argsRoot = MONO.mono_wasm_new_root (args), nameRoot = MONO.mono_wasm_new_root (method_name); try { - var m = obj [js_name]; - if (typeof m === "undefined") - throw new Error("Method: '" + js_name + "' not found for: '" + Object.prototype.toString.call(obj) + "'"); - var res = m.apply (obj, js_args); - return BINDING.mono_wasm_convert_return_value(res); - } catch (e) { - // make sure we release object reference counts on errors. - BINDING.mono_wasm_unwind_LMF(); - var res = e.toString (); - setValue (is_exception, 1, "i32"); - if (res === null || res === undefined) - res = "unknown exception"; - return BINDING.js_string_to_mono_string (res); + BINDING.bindings_lazy_init (); + + var obj = BINDING.get_js_obj (js_handle); + if (!obj) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); + } + + var js_name = BINDING.conv_string (nameRoot.value); + if (!js_name || (typeof(js_name) !== "string")) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid method name object '" + nameRoot.value + "'"); + } + + var js_args = BINDING.mono_wasm_parse_args_root(argsRoot); + + var res; + try { + var m = obj [js_name]; + if (typeof m === "undefined") + throw new Error("Method: '" + js_name + "' not found for: '" + Object.prototype.toString.call(obj) + "'"); + var res = m.apply (obj, js_args); + return BINDING.mono_wasm_convert_return_value(res); + } catch (e) { + // make sure we release object reference counts on errors. + BINDING.mono_wasm_unwind_LMF(); + var res = e.toString (); + setValue (is_exception, 1, "i32"); + if (res === null || res === undefined) + res = "unknown exception"; + return BINDING.js_string_to_mono_string (res); + } + } finally { + argsRoot.release(); + nameRoot.release(); } }, mono_wasm_get_object_property: function(js_handle, property_name, is_exception) { BINDING.bindings_lazy_init (); - var obj = BINDING.mono_wasm_require_handle (js_handle); - if (!obj) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); - } - - var js_name = BINDING.conv_string (property_name); - if (!js_name) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid property name object '" + js_name + "'"); - } - - var res; + var nameRoot = MONO.mono_wasm_new_root (property_name); try { - var m = obj [js_name]; - if (m === Object(m) && obj.__is_mono_proxied__) - m.__is_mono_proxied__ = true; + var obj = BINDING.mono_wasm_require_handle (js_handle); + if (!obj) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); + } - return BINDING.js_to_mono_obj (m); - } catch (e) { - var res = e.toString (); - setValue (is_exception, 1, "i32"); - if (res === null || typeof res === "undefined") - res = "unknown exception"; - return BINDING.js_string_to_mono_string (res); + var js_name = BINDING.conv_string (nameRoot.value); + if (!js_name) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid property name object '" + nameRoot.value + "'"); + } + + var res; + try { + var m = obj [js_name]; + if (m === Object(m) && obj.__is_mono_proxied__) + m.__is_mono_proxied__ = true; + + return BINDING.js_to_mono_obj (m); + } catch (e) { + var res = e.toString (); + setValue (is_exception, 1, "i32"); + if (res === null || typeof res === "undefined") + res = "unknown exception"; + return BINDING.js_string_to_mono_string (res); + } + } finally { + nameRoot.release(); } }, mono_wasm_set_object_property: function (js_handle, property_name, value, createIfNotExist, hasOwnProperty, is_exception) { - - BINDING.bindings_lazy_init (); - - var requireObject = BINDING.mono_wasm_require_handle (js_handle); - if (!requireObject) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); - } - - var property = BINDING.conv_string (property_name); - if (!property) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid property name object '" + property_name + "'"); - } - - var result = false; - - var js_value = BINDING.unbox_mono_obj(value); - BINDING.mono_wasm_save_LMF(); - - if (createIfNotExist) { - requireObject[property] = js_value; - result = true; - } - else { - result = false; - if (!createIfNotExist) - { - if (!requireObject.hasOwnProperty(property)) - return false; + var valueRoot = MONO.mono_wasm_new_root (value), nameRoot = MONO.mono_wasm_new_root (property_name); + try { + BINDING.bindings_lazy_init (); + var requireObject = BINDING.mono_wasm_require_handle (js_handle); + if (!requireObject) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); } - if (hasOwnProperty === true) { - if (requireObject.hasOwnProperty(property)) { - requireObject[property] = js_value; - result = true; - } - } - else { - requireObject[property] = js_value; - result = true; - } + var property = BINDING.conv_string (nameRoot.value); + if (!property) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid property name object '" + property_name + "'"); + } + + var result = false; + + var js_value = BINDING._unbox_mono_obj_root(valueRoot); + BINDING.mono_wasm_save_LMF(); + + if (createIfNotExist) { + requireObject[property] = js_value; + result = true; + } + else { + result = false; + if (!createIfNotExist) + { + if (!requireObject.hasOwnProperty(property)) + return false; + } + if (hasOwnProperty === true) { + if (requireObject.hasOwnProperty(property)) { + requireObject[property] = js_value; + result = true; + } + } + else { + requireObject[property] = js_value; + result = true; + } + + } + BINDING.mono_wasm_unwind_LMF(); + return BINDING._box_js_bool (result); + } finally { + nameRoot.release(); + valueRoot.release(); } - BINDING.mono_wasm_unwind_LMF(); - return BINDING._box_js_bool (result); }, mono_wasm_get_by_index: function(js_handle, property_index, is_exception) { BINDING.bindings_lazy_init (); @@ -1811,49 +1851,59 @@ var BindingSupportLib = { } }, mono_wasm_set_by_index: function(js_handle, property_index, value, is_exception) { - BINDING.bindings_lazy_init (); - - var obj = BINDING.mono_wasm_require_handle (js_handle); - if (!obj) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); - } - - var js_value = BINDING.unbox_mono_obj(value); - BINDING.mono_wasm_save_LMF(); - + var valueRoot = MONO.mono_wasm_new_root (value); try { - obj [property_index] = js_value; - BINDING.mono_wasm_unwind_LMF(); - return true; - } catch (e) { - var res = e.toString (); - setValue (is_exception, 1, "i32"); - if (res === null || typeof res === "undefined") - res = "unknown exception"; - return BINDING.js_string_to_mono_string (res); + BINDING.bindings_lazy_init (); + + var obj = BINDING.mono_wasm_require_handle (js_handle); + if (!obj) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + js_handle + "'"); + } + + var js_value = BINDING._unbox_mono_obj_root(valueRoot); + BINDING.mono_wasm_save_LMF(); + + try { + obj [property_index] = js_value; + BINDING.mono_wasm_unwind_LMF(); + return true; + } catch (e) { + var res = e.toString (); + setValue (is_exception, 1, "i32"); + if (res === null || typeof res === "undefined") + res = "unknown exception"; + return BINDING.js_string_to_mono_string (res); + } + } finally { + valueRoot.release(); } }, mono_wasm_get_global_object: function(global_name, is_exception) { - BINDING.bindings_lazy_init (); + var nameRoot = MONO.mono_wasm_new_root (global_name); + try { + BINDING.bindings_lazy_init (); - var js_name = BINDING.conv_string (global_name); + var js_name = BINDING.conv_string (nameRoot.value); - var globalObj; + var globalObj; - if (!js_name) { - globalObj = globalThis; + if (!js_name) { + globalObj = globalThis; + } + else { + globalObj = globalThis[js_name]; + } + + if (globalObj === null || typeof globalObj === undefined) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Global object '" + js_name + "' not found."); + } + + return BINDING.js_to_mono_obj (globalObj); + } finally { + nameRoot.release(); } - else { - globalObj = globalThis[js_name]; - } - - if (globalObj === null || typeof globalObj === undefined) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Global object '" + js_name + "' not found."); - } - - return BINDING.js_to_mono_obj (globalObj); }, mono_wasm_release_handle: function(js_handle, is_exception) { BINDING.bindings_lazy_init (); @@ -1893,50 +1943,55 @@ var BindingSupportLib = { return gc_handle; }, mono_wasm_new: function (core_name, args, is_exception) { - BINDING.bindings_lazy_init (); - - var js_name = BINDING.conv_string (core_name); - - if (!js_name) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Core object '" + js_name + "' not found."); - } - - var coreObj = globalThis[js_name]; - - if (coreObj === null || typeof coreObj === "undefined") { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("JavaScript host object '" + js_name + "' not found."); - } - - var js_args = BINDING.mono_wasm_parse_args(args); - + var argsRoot = MONO.mono_wasm_new_root (args), nameRoot = MONO.mono_wasm_new_root (core_name); try { + BINDING.bindings_lazy_init (); - // This is all experimental !!!!!! - var allocator = function(constructor, js_args) { - // Not sure if we should be checking for anything here - var argsList = new Array(); - argsList[0] = constructor; - if (js_args) - argsList = argsList.concat (js_args); - var tempCtor = constructor.bind.apply (constructor, argsList); - var obj = new tempCtor (); - return obj; - }; + var js_name = BINDING.conv_string (nameRoot.value); - var res = allocator(coreObj, js_args); - var gc_handle = BINDING.mono_wasm_free_list.length ? BINDING.mono_wasm_free_list.pop() : BINDING.mono_wasm_ref_counter++; - BINDING.mono_wasm_object_registry[gc_handle] = res; - return BINDING.mono_wasm_convert_return_value(gc_handle + 1); - } catch (e) { - var res = e.toString (); - setValue (is_exception, 1, "i32"); - if (res === null || res === undefined) - res = "Error allocating object."; - return BINDING.js_string_to_mono_string (res); + if (!js_name) { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("Invalid name @" + nameRoot.value); + } + + var coreObj = globalThis[js_name]; + + if (coreObj === null || typeof coreObj === "undefined") { + setValue (is_exception, 1, "i32"); + return BINDING.js_string_to_mono_string ("JavaScript host object '" + js_name + "' not found."); + } + + var js_args = BINDING.mono_wasm_parse_args_root(argsRoot); + + try { + + // This is all experimental !!!!!! + var allocator = function(constructor, js_args) { + // Not sure if we should be checking for anything here + var argsList = new Array(); + argsList[0] = constructor; + if (js_args) + argsList = argsList.concat (js_args); + var tempCtor = constructor.bind.apply (constructor, argsList); + var obj = new tempCtor (); + return obj; + }; + + var res = allocator(coreObj, js_args); + var gc_handle = BINDING.mono_wasm_free_list.length ? BINDING.mono_wasm_free_list.pop() : BINDING.mono_wasm_ref_counter++; + BINDING.mono_wasm_object_registry[gc_handle] = res; + return BINDING.mono_wasm_convert_return_value(gc_handle + 1); + } catch (e) { + var res = e.toString (); + setValue (is_exception, 1, "i32"); + if (res === null || res === undefined) + res = "Error allocating object."; + return BINDING.js_string_to_mono_string (res); + } + } finally { + argsRoot.release(); + nameRoot.release(); } - }, mono_wasm_typed_array_to_array: function(js_handle, is_exception) { From 654dbc453e9bb59e1152662f598b731f0fb6e68b Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Wed, 14 Jul 2021 17:10:20 +0200 Subject: [PATCH 546/926] enable working websocket subprotocol test --- src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index 296bffbc620..43b119eaedc 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -170,7 +170,6 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/45583", TestPlatforms.Browser)] public async Task ConnectAsync_PassNoSubProtocol_ServerRequires_ThrowsWebSocketException(Uri server) { const string AcceptedProtocol = "CustomProtocol"; From fa779e8cb2b5868a0ac2fd4215f39ffb91f0dab0 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 14 Jul 2021 11:33:50 -0400 Subject: [PATCH 547/926] Raise the max size poolable by ArrayPool (#55621) We previously set an arbitrary cut-off of 2MB max array size. That was before we would trim arrays in response to memory pressure. This now raises the limit as high as we can while maintaining the current power-of-two-based scheme. --- .../tests/ArrayPool/UnitTests.cs | 47 +++++++++++++++---- .../Compression/WebSocketDeflater.cs | 6 +-- .../Compression/WebSocketInflater.cs | 5 +- .../TlsOverPerCoreLockedStacksArrayPool.cs | 3 +- .../System/Globalization/CompareInfo.Icu.cs | 4 +- 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs index 7456e1b46fe..5673d22d363 100644 --- a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs +++ b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Numerics; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Buffers.ArrayPool.Tests @@ -240,15 +241,11 @@ namespace System.Buffers.ArrayPool.Tests } [Fact] - public static void ReturningABufferGreaterThanMaxSizeDoesNotThrow() + public static void ReturningToCreatePoolABufferGreaterThanMaxSizeDoesNotThrow() { ArrayPool pool = ArrayPool.Create(maxArrayLength: 16, maxArraysPerBucket: 1); byte[] rented = pool.Rent(32); pool.Return(rented); - - ArrayPool.Shared.Return(new byte[3 * 1024 * 1024]); - ArrayPool.Shared.Return(new char[3 * 1024 * 1024]); - ArrayPool.Shared.Return(new string[3 * 1024 * 1024]); } [Fact] @@ -292,11 +289,11 @@ namespace System.Buffers.ArrayPool.Tests [InlineData(1024, 1024)] [InlineData(4096, 4096)] [InlineData(1024 * 1024, 1024 * 1024)] - [InlineData(1024 * 1024 + 1, 1024 * 1024 + 1)] + [InlineData(1024 * 1024 + 1, 1024 * 1024 * 2)] [InlineData(1024 * 1024 * 2, 1024 * 1024 * 2)] public static void RentingSpecificLengthsYieldsExpectedLengths(int requestedMinimum, int expectedLength) { - foreach (ArrayPool pool in new[] { ArrayPool.Create(), ArrayPool.Shared }) + foreach (ArrayPool pool in new[] { ArrayPool.Create((int)BitOperations.RoundUpToPowerOf2((uint)requestedMinimum), 1), ArrayPool.Shared }) { byte[] buffer1 = pool.Rent(requestedMinimum); byte[] buffer2 = pool.Rent(requestedMinimum); @@ -313,7 +310,7 @@ namespace System.Buffers.ArrayPool.Tests pool.Return(buffer1); } - foreach (ArrayPool pool in new[] { ArrayPool.Create(), ArrayPool.Shared }) + foreach (ArrayPool pool in new[] { ArrayPool.Create((int)BitOperations.RoundUpToPowerOf2((uint)requestedMinimum), 1), ArrayPool.Shared }) { char[] buffer1 = pool.Rent(requestedMinimum); char[] buffer2 = pool.Rent(requestedMinimum); @@ -330,7 +327,7 @@ namespace System.Buffers.ArrayPool.Tests pool.Return(buffer1); } - foreach (ArrayPool pool in new[] { ArrayPool.Create(), ArrayPool.Shared }) + foreach (ArrayPool pool in new[] { ArrayPool.Create((int)BitOperations.RoundUpToPowerOf2((uint)requestedMinimum), 1), ArrayPool.Shared }) { string[] buffer1 = pool.Rent(requestedMinimum); string[] buffer2 = pool.Rent(requestedMinimum); @@ -348,6 +345,38 @@ namespace System.Buffers.ArrayPool.Tests } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] + [InlineData(1024 * 1024 * 1024 - 1, true)] + [InlineData(1024 * 1024 * 1024, true)] + [InlineData(1024 * 1024 * 1024 + 1, false)] + [InlineData(0X7FFFFFC7 /* Array.MaxLength */, false)] + [OuterLoop] + public static void RentingGiganticArraySucceeds(int length, bool expectPooled) + { + var options = new RemoteInvokeOptions(); + options.StartInfo.UseShellExecute = false; + options.StartInfo.EnvironmentVariables.Add(TrimSwitchName, "false"); + + RemoteExecutor.Invoke((lengthStr, expectPooledStr) => + { + int length = int.Parse(lengthStr); + byte[] array; + try + { + array = ArrayPool.Shared.Rent(length); + } + catch (OutOfMemoryException) + { + return; + } + + Assert.InRange(array.Length, length, int.MaxValue); + ArrayPool.Shared.Return(array); + + Assert.Equal(bool.Parse(expectPooledStr), ReferenceEquals(array, ArrayPool.Shared.Rent(length))); + }, length.ToString(), expectPooled.ToString(), options).Dispose(); + } + [Fact] public static void RentingAfterPoolExhaustionReturnsSizeForCorrespondingBucket_SmallerThanLimit() { diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs index e7f18072842..f3ecf278886 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs @@ -46,15 +46,11 @@ namespace System.Net.WebSockets.Compression { Debug.Assert(_buffer is null, "Invalid state, ReleaseBuffer not called."); - // Do not try to rent more than 1MB initially, because it will actually allocate - // instead of renting. Be optimistic that what we're sending is actually going to fit. - const int MaxInitialBufferLength = 1024 * 1024; - // For small payloads there might actually be overhead in the compression and the resulting // output might be larger than the payload. This is why we rent at least 4KB initially. const int MinInitialBufferLength = 4 * 1024; - _buffer = ArrayPool.Shared.Rent(Math.Clamp(payload.Length, MinInitialBufferLength, MaxInitialBufferLength)); + _buffer = ArrayPool.Shared.Rent(Math.Max(payload.Length, MinInitialBufferLength)); int position = 0; while (true) diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs index 6ade12d539a..d47e646fa60 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs @@ -82,10 +82,9 @@ namespace System.Net.WebSockets.Compression } else { - // Rent a buffer as close to the size of the user buffer as possible, - // but not try to rent anything above 1MB because the array pool will allocate. + // Rent a buffer as close to the size of the user buffer as possible. // If the payload is smaller than the user buffer, rent only as much as we need. - _buffer = ArrayPool.Shared.Rent(Math.Min(userBufferLength, (int)Math.Min(payloadLength, 1024 * 1024))); + _buffer = ArrayPool.Shared.Rent((int)Math.Min(userBufferLength, payloadLength)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs index 59cb6556ad7..438ca8e9cb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs @@ -24,12 +24,11 @@ namespace System.Buffers // TODO https://github.com/dotnet/coreclr/pull/7747: "Investigate optimizing ArrayPool heuristics" // - Explore caching in TLS more than one array per size per thread, and moving stale buffers to the global queue. // - Explore changing the size of each per-core bucket, potentially dynamically or based on other factors like array size. - // - Explore changing number of buckets and what sizes of arrays are cached. // - Investigate whether false sharing is causing any issues, in particular on LockedStack's count and the contents of its array. // ... /// The number of buckets (array sizes) in the pool, one for each array length, starting from length 16. - private const int NumBuckets = 17; // Utilities.SelectBucketIndex(2*1024*1024) + private const int NumBuckets = 27; // Utilities.SelectBucketIndex(1024 * 1024 * 1024 + 1) /// Maximum number of per-core stacks to use per array size. private const int MaxPerCorePerArraySizeStacks = 64; // selected to avoid needing to worry about processor groups /// The maximum number of buffers to store in a bucket's global queue. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs index 4d27d9e881e..a9b9274952a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs @@ -722,7 +722,9 @@ namespace System.Globalization // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer // the solution is to try to fill the sort key in a temporary buffer of size equal 4 x string length - // 1MB is the biggest array that can be rented from ArrayPool.Shared without memory allocation + // (The ArrayPool used to have a limit on the length of buffers it would cache; this code was avoiding + // exceeding that limit to avoid a per-operation allocation, and the performance implications here + // were not re-evaluated when the limit was lifted.) int sortKeyLength = (source.Length > 1024 * 1024 / 4) ? 0 : 4 * source.Length; byte[]? borrowedArray = null; From 62c9312f40eef1d72b92c48d016f9768eecd106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 14 Jul 2021 19:04:48 +0200 Subject: [PATCH 548/926] Correctly skip PosixSignalRegistrationTests on mobile (#55643) https://github.com/dotnet/runtime/pull/55569 didn't fix the issue since xunit retrieves the MemberData and checks for non-empty *before* evaluating the ConditionalTheory condition. --- .../PosixSignalRegistrationTests.Unix.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs index e08d7756551..4b0c9fc01de 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/PosixSignalRegistrationTests.Unix.cs @@ -14,19 +14,13 @@ namespace System.Tests { public static IEnumerable UninstallableSignals() { - if (PlatformDetection.IsNotMobile) - { - yield return new object[] { (PosixSignal)9 }; - } + yield return new object[] { (PosixSignal)9 }; } public static IEnumerable SupportedSignals() { - if (PlatformDetection.IsNotMobile) - { - foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) - yield return new object[] { value }; - } + foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal))) + yield return new object[] { value }; } public static IEnumerable UnsupportedSignals() From 02ccab80525a7d8616eda1fb7d14ba71f4c93cc6 Mon Sep 17 00:00:00 2001 From: Immo Landwerth Date: Wed, 14 Jul 2021 10:41:46 -0700 Subject: [PATCH 549/926] Use GitHub issue form for API suggestions (#55659) --- .github/ISSUE_TEMPLATE/02_api_proposal.md | 53 ----------------- .github/ISSUE_TEMPLATE/02_api_proposal.yml | 66 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 53 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/02_api_proposal.md create mode 100644 .github/ISSUE_TEMPLATE/02_api_proposal.yml diff --git a/.github/ISSUE_TEMPLATE/02_api_proposal.md b/.github/ISSUE_TEMPLATE/02_api_proposal.md deleted file mode 100644 index d45db1eff4b..00000000000 --- a/.github/ISSUE_TEMPLATE/02_api_proposal.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -name: API proposal -about: Propose a change to the public API surface -title: '' -labels: api-suggestion -assignees: '' - ---- - -## Background and Motivation - - - -## Proposed API - - - -## Usage Examples - - - -## Alternative Designs - - - -## Risks - - diff --git a/.github/ISSUE_TEMPLATE/02_api_proposal.yml b/.github/ISSUE_TEMPLATE/02_api_proposal.yml new file mode 100644 index 00000000000..e161283b9cf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_api_proposal.yml @@ -0,0 +1,66 @@ +name: API Suggestion +description: Propose a change to the public API surface +title: "[API Proposal]: " +labels: [api-suggestion] +body: + - type: markdown + attributes: + value: | + We welcome API proposals! We have a process to evaluate the value and shape of new API. There is an overview of our process [here](https://github.com/dotnet/runtime/blob/main/docs/project/api-review-process.md). This template will help us gather the information we need to start the review process. + - type: textarea + id: background + attributes: + label: Background and motivation + description: Please describe the purpose and value of the new API here. + placeholder: Purpose + validations: + required: true + - type: textarea + id: api-proposal + attributes: + label: API Proposal + description: | + Please provide the specific public API signature diff that you are proposing. + + You may find the [Framework Design Guidelines](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/framework-design-guidelines-digest.md) helpful. + placeholder: API declaration (no method bodies) + value: | + ```C# + namespace System.Collections.Generic + { + public class MyFancyCollection : IEnumerable + { + public void Fancy(T item); + } + } + ``` + validations: + required: true + - type: textarea + id: api-usage + attributes: + label: API Usage + description: | + Please provide code examples that highlight how the proposed API additions are meant to be consumed. This will help suggest whether the API has the right shape to be functional, performant and useable. + placeholder: API usage + value: | + ```C# + // Fancy the value + var c = new MyFancyCollection(); + c.Fancy(42); + + // Getting the values out + foreach (var v in c) + Console.WriteLine(v); + ``` + validations: + required: true + - type: textarea + id: risks + attributes: + label: Risks + description: | + Please mention any risks that to your knowledge the API proposal might entail, such as breaking changes, performance regressions, etc. + placeholder: Risks + validations: + required: false From d86a382c1ccc1d01d5d5c91fc00e63c72e9d8df8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Jul 2021 10:44:24 -0700 Subject: [PATCH 550/926] Place the CMake command line cache file in the intermediates dir instead of the output dir (#55587) Fixes an issue where deleting the intermediates folder locally would get the build in a bad state where the command-line check would be up to date but the CMake files would not be generated. --- eng/native/gen-buildsys.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/native/gen-buildsys.sh b/eng/native/gen-buildsys.sh index f671182e473..bf04c26f2b1 100755 --- a/eng/native/gen-buildsys.sh +++ b/eng/native/gen-buildsys.sh @@ -98,7 +98,7 @@ if [[ "$build_arch" == "wasm" ]]; then fi cmake_args_to_cache="$scan_build\n$SCAN_BUILD_COMMAND\n$generator\n$__UnprocessedCMakeArgs" -cmake_args_cache_file="$__CMakeBinDir/cmake_cmd_line.txt" +cmake_args_cache_file="$2/cmake_cmd_line.txt" if [[ -z "$__ConfigureOnly" ]]; then if [[ -e "$cmake_args_cache_file" ]]; then cmake_args_cache=$(<"$cmake_args_cache_file") From 21a76328b8b64625a9ed0a95a433d742cf92f1de Mon Sep 17 00:00:00 2001 From: John Salem Date: Wed, 14 Jul 2021 10:47:10 -0700 Subject: [PATCH 551/926] Implement JitInfo API (#55046) --- .../System.Private.CoreLib.csproj | 1 + .../RuntimeHelpers.CoreCLR.cs | 6 - .../src/System/Runtime/JitInfo.CoreCLR.cs | 34 ++++ src/coreclr/vm/ecalllist.h | 9 +- src/coreclr/vm/jitinterface.cpp | 53 +++++- src/coreclr/vm/jitinterface.h | 13 +- src/coreclr/vm/util.cpp | 2 + src/coreclr/vm/util.hpp | 77 ++++++++ .../TestUtilities/System/PlatformDetection.cs | 1 + .../System.Private.CoreLib.Shared.projitems | 1 + .../Diagnostics/Tracing/RuntimeEventSource.cs | 6 +- .../src/System/Runtime/JitInfo.cs | 23 +++ .../System.Runtime/ref/System.Runtime.cs | 6 + .../tests/System.Runtime.Tests.csproj | 1 + .../tests/System/Runtime/JitInfoTests.cs | 176 ++++++++++++++++++ .../System.Private.CoreLib.csproj | 1 + .../Diagnostics/Tracing/EventPipe.Mono.cs | 3 +- .../CompilerServices/RuntimeHelpers.Mono.cs | 9 - .../src/System/Runtime/JitInfo.Mono.cs | 38 ++++ src/mono/mono/metadata/icall-eventpipe.c | 32 +++- src/mono/mono/metadata/object-internals.h | 2 +- src/mono/mono/mini/mini.h | 3 +- .../tracing/eventcounter/regression-25709.cs | 6 +- .../tracing/eventcounter/runtimecounters.cs | 3 +- 24 files changed, 464 insertions(+), 42 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreCLR.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/JitInfo.cs create mode 100644 src/libraries/System.Runtime/tests/System/Runtime/JitInfoTests.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Runtime/JitInfo.Mono.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index b42232633a7..1075a708b8b 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -214,6 +214,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 3d243e2021c..558e2553f99 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -349,12 +349,6 @@ namespace System.Runtime.CompilerServices } } } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern long GetILBytesJitted(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int GetMethodsJittedCount(); } // Helper class to assist with unsafe pinning of arbitrary objects. // It's used by VM code. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreCLR.cs new file mode 100644 index 00000000000..f1dafd58a81 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreCLR.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime +{ + public static partial class JitInfo + { + /// + /// Get the number of bytes of IL that have been compiled. If is true, + /// then this value is scoped to the current thread, otherwise, this is a global value. + /// + /// Whether the returned value should be specific to the current thread. Default: false + /// The number of bytes of IL the JIT has compiled. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern long GetCompiledILBytes(bool currentThread = false); + + /// + /// Get the number of methods that have been compiled. If is true, + /// then this value is scoped to the current thread, otherwise, this is a global value. + /// + /// Whether the returned value should be specific to the current thread. Default: false + /// The number of methods the JIT has compiled. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern long GetCompiledMethodCount(bool currentThread = false); + + // Normalized to 100ns ticks on vm side + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern long GetCompilationTimeInTicks(bool currentThread = false); + } +} \ No newline at end of file diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index ea3f65d7291..738ac9a3399 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -834,6 +834,12 @@ FCFuncStart(gInterlockedFuncs) QCFuncElement("_MemoryBarrierProcessWide", COMInterlocked::MemoryBarrierProcessWide) FCFuncEnd() +FCFuncStart(gJitInfoFuncs) + FCFuncElement("GetCompiledILBytes", GetCompiledILBytes) + FCFuncElement("GetCompiledMethodCount", GetCompiledMethodCount) + FCFuncElement("GetCompilationTimeInTicks", GetCompilationTimeInTicks) +FCFuncEnd() + FCFuncStart(gVarArgFuncs) FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_IntPtr_PtrVoid_RetVoid, VarArgsNative::Init2) FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_IntPtr_RetVoid, VarArgsNative::Init) @@ -879,8 +885,6 @@ FCFuncStart(gRuntimeHelpers) QCFuncElement("AllocateTypeAssociatedMemory", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) - FCFuncElement("GetILBytesJitted", GetJittedBytes) - FCFuncElement("GetMethodsJittedCount", GetJittedMethodsCount) FCFuncEnd() FCFuncStart(gMngdFixedArrayMarshalerFuncs) @@ -1158,6 +1162,7 @@ FCClassElement("IReflect", "System.Reflection", gStdMngIReflectFuncs) FCClassElement("InterfaceMarshaler", "System.StubHelpers", gInterfaceMarshalerFuncs) #endif FCClassElement("Interlocked", "System.Threading", gInterlockedFuncs) +FCClassElement("JitInfo", "System.Runtime", gJitInfoFuncs) #if TARGET_UNIX FCClassElement("Kernel32", "", gPalKernel32Funcs) #endif diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 882e2c29cef..b19fa365323 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -102,23 +102,47 @@ GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); #else // DACCESS_COMPILE -uint64_t g_cbILJitted = 0; -uint32_t g_cMethodsJitted = 0; +Volatile g_cbILJitted = 0; +Volatile g_cMethodsJitted = 0; +Volatile g_c100nsTicksInJit = 0; +thread_local int64_t t_cbILJittedForThread = 0; +thread_local int64_t t_cMethodsJittedForThread = 0; +thread_local int64_t t_c100nsTicksInJitForThread = 0; + +// This prevents tearing of 64 bit values on 32 bit systems +static inline +int64_t AtomicLoad64WithoutTearing(int64_t volatile *valueRef) +{ + WRAPPER_NO_CONTRACT; +#if TARGET_64BIT + return VolatileLoad(valueRef); +#else + return InterlockedCompareExchangeT((LONG64 volatile *)valueRef, (LONG64)0, (LONG64)0); +#endif // TARGET_64BIT +} #ifndef CROSSGEN_COMPILE -FCIMPL0(INT64, GetJittedBytes) +FCIMPL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread) { FCALL_CONTRACT; - return g_cbILJitted; + return currentThread ? t_cbILJittedForThread : AtomicLoad64WithoutTearing(&g_cbILJitted); } FCIMPLEND -FCIMPL0(INT32, GetJittedMethodsCount) +FCIMPL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread) { FCALL_CONTRACT; - return g_cMethodsJitted; + return currentThread ? t_cMethodsJittedForThread : AtomicLoad64WithoutTearing(&g_cMethodsJitted); +} +FCIMPLEND + +FCIMPL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread) +{ + FCALL_CONTRACT; + + return currentThread ? t_c100nsTicksInJitForThread : AtomicLoad64WithoutTearing(&g_c100nsTicksInJit); } FCIMPLEND #endif @@ -13030,9 +13054,13 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, MethodDesc* ftn = nativeCodeVersion.GetMethodDesc(); PCODE ret = NULL; + NormalizedTimer timer; + int64_t c100nsTicksInJit = 0; COOPERATIVE_TRANSITION_BEGIN(); + timer.Start(); + #ifdef FEATURE_PREJIT if (g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_ALL && @@ -13394,8 +13422,17 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, printf("."); #endif // _DEBUG - FastInterlockExchangeAddLong((LONG64*)&g_cbILJitted, methodInfo.ILCodeSize); - FastInterlockIncrement((LONG*)&g_cMethodsJitted); + timer.Stop(); + c100nsTicksInJit = timer.Elapsed100nsTicks(); + + InterlockedExchangeAdd64((LONG64*)&g_c100nsTicksInJit, c100nsTicksInJit); + t_c100nsTicksInJitForThread += c100nsTicksInJit; + + InterlockedExchangeAdd64((LONG64*)&g_cbILJitted, methodInfo.ILCodeSize); + t_cbILJittedForThread += methodInfo.ILCodeSize; + + InterlockedIncrement64((LONG64*)&g_cMethodsJitted); + t_cMethodsJittedForThread++; COOPERATIVE_TRANSITION_END(); return ret; diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index e071d0717d1..a84a948cda1 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -1149,8 +1149,17 @@ CORJIT_FLAGS GetDebuggerCompileFlags(Module* pModule, CORJIT_FLAGS flags); bool __stdcall TrackAllocationsEnabled(); -FCDECL0(INT64, GetJittedBytes); -FCDECL0(INT32, GetJittedMethodsCount); + +extern Volatile g_cbILJitted; +extern Volatile g_cMethodsJitted; +extern Volatile g_c100nsTicksInJit; +extern thread_local int64_t t_cbILJittedForThread; +extern thread_local int64_t t_cMethodsJittedForThread; +extern thread_local int64_t t_c100nsTicksInJitForThread; + +FCDECL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread); +FCDECL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread); +FCDECL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread); #endif // JITINTERFACE_H diff --git a/src/coreclr/vm/util.cpp b/src/coreclr/vm/util.cpp index 64130d699ea..fe51593c0ef 100644 --- a/src/coreclr/vm/util.cpp +++ b/src/coreclr/vm/util.cpp @@ -2272,4 +2272,6 @@ HRESULT GetFileVersion( // S_OK or error } #endif // !TARGET_UNIX +Volatile NormalizedTimer::s_frequency = -1.0; + #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/util.hpp b/src/coreclr/vm/util.hpp index 19f7932f178..4da40a3ead1 100644 --- a/src/coreclr/vm/util.hpp +++ b/src/coreclr/vm/util.hpp @@ -918,6 +918,83 @@ public: static BOOL nativeIsDigit(WCHAR c); }; +// ====================================================================================== +// Simple, reusable 100ns timer for normalizing ticks. For use in Q/FCalls to avoid discrepency with +// tick frequency between native and managed. +class NormalizedTimer +{ +private: + static const int64_t NormalizedTicksPerSecond = 10000000 /* 100ns ticks per second (1e7) */; + static Volatile s_frequency; + + LARGE_INTEGER startTimestamp; + LARGE_INTEGER stopTimestamp; + +#if _DEBUG + bool isRunning = false; +#endif // _DEBUG + +public: + NormalizedTimer() + { + LIMITED_METHOD_CONTRACT; + if (s_frequency.Load() == -1) + { + double frequency; + LARGE_INTEGER qpfValue; + QueryPerformanceFrequency(&qpfValue); + frequency = static_cast(qpfValue.QuadPart); + frequency /= NormalizedTicksPerSecond; + s_frequency.Store(frequency); + } + + startTimestamp.QuadPart = 0; + startTimestamp.QuadPart = 0; + } + + // ====================================================================================== + // Start the timer + inline + void Start() + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(!isRunning); + QueryPerformanceCounter(&startTimestamp); + +#if _DEBUG + isRunning = true; +#endif // _DEBUG + } + + // ====================================================================================== + // stop the timer. If called before starting, sets the start time to the same as the stop + inline + void Stop() + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(isRunning); + QueryPerformanceCounter(&stopTimestamp); + +#if _DEBUG + isRunning = false; +#endif // _DEBUG + } + + // ====================================================================================== + // Return elapsed ticks. This will stop a running timer. + // Will return 0 if called out of order. + // Only recalculated this value if it has been stopped/started since previous calculation. + inline + int64_t Elapsed100nsTicks() + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(!isRunning); + _ASSERTE(startTimestamp.QuadPart > 0); + _ASSERTE(stopTimestamp.QuadPart > 0); + return static_cast((stopTimestamp.QuadPart - startTimestamp.QuadPart) / s_frequency); + } +}; + #ifdef _DEBUG #define FORCEINLINE_NONDEBUG #else diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 90f21d02f5c..c1bb1854ff1 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -27,6 +27,7 @@ namespace System public static bool IsNotMonoRuntime => !IsMonoRuntime; public static bool IsMonoInterpreter => GetIsRunningOnMonoInterpreter(); public static bool IsMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") == "aot"; + public static bool IsNotMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") != "aot"; public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")); public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD")); public static bool IsAndroid => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 6c1182acbd1..ac992cdc3b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -867,6 +867,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index fe47aae73fd..6d57c82ef6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -39,6 +39,7 @@ namespace System.Diagnostics.Tracing private PollingCounter? _assemblyCounter; private PollingCounter? _ilBytesJittedCounter; private PollingCounter? _methodsJittedCounter; + private IncrementingPollingCounter? _jitTimeCounter; public static void Initialize() { @@ -83,8 +84,9 @@ namespace System.Diagnostics.Tracing _lohSizeCounter ??= new PollingCounter("loh-size", this, () => GC.GetGenerationSize(3)) { DisplayName = "LOH Size", DisplayUnits = "B" }; _pohSizeCounter ??= new PollingCounter("poh-size", this, () => GC.GetGenerationSize(4)) { DisplayName = "POH (Pinned Object Heap) Size", DisplayUnits = "B" }; _assemblyCounter ??= new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" }; - _ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetILBytesJitted()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" }; - _methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetMethodsJittedCount()) { DisplayName = "Number of Methods Jitted" }; + _ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.JitInfo.GetCompiledILBytes()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" }; + _methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.JitInfo.GetCompiledMethodCount()) { DisplayName = "Number of Methods Jitted" }; + _jitTimeCounter ??= new IncrementingPollingCounter("time-in-jit", this, () => System.Runtime.JitInfo.GetCompilationTime().TotalMilliseconds) { DisplayName = "Time spent in JIT", DisplayUnits = "ms", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/JitInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/JitInfo.cs new file mode 100644 index 00000000000..9176bdd84eb --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/JitInfo.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + /// + /// A static class for getting information about the Just In Time compiler. + /// + public static partial class JitInfo + { + /// + /// Get the amount of time the JIT Compiler has spent compiling methods. If is true, + /// then this value is scoped to the current thread, otherwise, this is a global value. + /// + /// Whether the returned value should be specific to the current thread. Default: false + /// The amount of time the JIT Compiler has spent compiling methods. + public static TimeSpan GetCompilationTime(bool currentThread = false) + { + // TimeSpan.FromTicks() takes 100ns ticks + return TimeSpan.FromTicks(GetCompilationTimeInTicks(currentThread)); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 76bbc5dd649..b1baaa1a95d 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -12472,6 +12472,12 @@ namespace System.Runtime public static System.Runtime.GCLargeObjectHeapCompactionMode LargeObjectHeapCompactionMode { get { throw null; } set { } } public static System.Runtime.GCLatencyMode LatencyMode { get { throw null; } set { } } } + public static partial class JitInfo + { + public static long GetCompiledILBytes(bool currentThread=false) { throw null; } + public static long GetCompiledMethodCount(bool currentThread=false) { throw null; } + public static TimeSpan GetCompilationTime(bool currentThread=false) { throw null; } + } public sealed partial class MemoryFailPoint : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, System.IDisposable { public MemoryFailPoint(int sizeInMegabytes) { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 3ada2d0cd26..6d888e344bc 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -223,6 +223,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Runtime/JitInfoTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/JitInfoTests.cs new file mode 100644 index 00000000000..f0a7eca83ee --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Runtime/JitInfoTests.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.XUnitExtensions; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using Xunit; + +namespace System.Runtime.Tests +{ + public class JitInfoTests + { + private long MakeAndInvokeDynamicSquareMethod(int input) + { + // example ref emit dynamic method from https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-define-and-execute-dynamic-methods + Type[] methodArgs = {typeof(int)}; + + DynamicMethod squareIt = new DynamicMethod( + "SquareIt", + typeof(long), + methodArgs, + typeof(JitInfoTests).Module); + + ILGenerator il = squareIt.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Conv_I8); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Mul); + il.Emit(OpCodes.Ret); + + Func invokeSquareIt = + (Func) + squareIt.CreateDelegate(typeof(Func)); + + return invokeSquareIt(input); + + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoAOT))] // JitInfo metrics will be 0 in AOT scenarios + public void JitInfoIsPopulated() + { + TimeSpan beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(); + long beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(); + long beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(); + + long square = MakeAndInvokeDynamicSquareMethod(100); + Assert.True(square == 10000); + + TimeSpan afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(); + long afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(); + long afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(); + + if (PlatformDetection.IsMonoInterpreter) + { + // special case the Mono interpreter where compilation time may be >0 but before and after will most likely be the same + Assert.True(beforeCompilationTime >= TimeSpan.Zero, $"Compilation time not greater than 0! ({beforeCompilationTime})"); + Assert.True(beforeCompiledILBytes >= 0, $"Compiled IL bytes not greater than 0! ({beforeCompiledILBytes})"); + Assert.True(beforeCompiledMethodCount >= 0, $"Compiled method count not greater than 0! ({beforeCompiledMethodCount})"); + + Assert.True(afterCompilationTime >= beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {afterCompilationTime}, before: {beforeCompilationTime})"); + Assert.True(afterCompiledILBytes >= beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {afterCompiledILBytes}, before: {beforeCompiledILBytes})"); + Assert.True(afterCompiledMethodCount >= beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {afterCompiledMethodCount}, before: {beforeCompiledMethodCount})"); + } + else + { + Assert.True(beforeCompilationTime > TimeSpan.Zero, $"Compilation time not greater than 0! ({beforeCompilationTime})"); + Assert.True(beforeCompiledILBytes > 0, $"Compiled IL bytes not greater than 0! ({beforeCompiledILBytes})"); + Assert.True(beforeCompiledMethodCount > 0, $"Compiled method count not greater than 0! ({beforeCompiledMethodCount})"); + + Assert.True(afterCompilationTime > beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {afterCompilationTime}, before: {beforeCompilationTime})"); + Assert.True(afterCompiledILBytes > beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {afterCompiledILBytes}, before: {beforeCompiledILBytes})"); + Assert.True(afterCompiledMethodCount > beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {afterCompiledMethodCount}, before: {beforeCompiledMethodCount})"); + } + } + + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsMonoAOT))] // JitInfo metrics will be 0 in AOT scenarios + public void JitInfoIsNotPopulated() + { + TimeSpan beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(); + long beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(); + long beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(); + + long square = MakeAndInvokeDynamicSquareMethod(100); + Assert.True(square == 10000); + + TimeSpan afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(); + long afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(); + long afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(); + + Assert.True(beforeCompilationTime == TimeSpan.Zero, $"Before Compilation time not eqeual to 0! ({beforeCompilationTime})"); + Assert.True(beforeCompiledILBytes == 0, $"Before Compiled IL bytes not eqeual to 0! ({beforeCompiledILBytes})"); + Assert.True(beforeCompiledMethodCount == 0, $"Before Compiled method count not eqeual to 0! ({beforeCompiledMethodCount})"); + + Assert.True(afterCompilationTime == TimeSpan.Zero, $"After Compilation time not eqeual to 0! ({afterCompilationTime})"); + Assert.True(afterCompiledILBytes == 0, $"After Compiled IL bytes not eqeual to 0! ({afterCompiledILBytes})"); + Assert.True(afterCompiledMethodCount == 0, $"After Compiled method count not eqeual to 0! ({afterCompiledMethodCount})"); + } + + [Fact] + [SkipOnMono("Mono does not track thread specific JIT information")] + public void JitInfoCurrentThreadIsPopulated() + { + TimeSpan t1_beforeCompilationTime = TimeSpan.Zero; + long t1_beforeCompiledILBytes = 0; + long t1_beforeCompiledMethodCount = 0; + + TimeSpan t1_afterCompilationTime = TimeSpan.Zero; + long t1_afterCompiledILBytes = 0; + long t1_afterCompiledMethodCount = 0; + + TimeSpan t2_beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true); + long t2_beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true); + long t2_beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true); + + var t1 = new Thread(() => { + t1_beforeCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true); + t1_beforeCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true); + t1_beforeCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true); + long square = MakeAndInvokeDynamicSquareMethod(100); + Assert.True(square == 10000); + t1_afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true); + t1_afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true); + t1_afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true); + }); + + t1.Start(); + t1.Join(); + + long square = MakeAndInvokeDynamicSquareMethod(100); + Assert.True(square == 10000); + + TimeSpan t2_afterCompilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true); + long t2_afterCompiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true); + long t2_afterCompiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true); + + Assert.True(t2_beforeCompilationTime > TimeSpan.Zero, $"Thread 2 Compilation time not greater than 0! ({t2_beforeCompilationTime})"); + Assert.True(t2_beforeCompiledILBytes > 0, $"Thread 2 Compiled IL bytes not greater than 0! ({t2_beforeCompiledILBytes})"); + Assert.True(t2_beforeCompiledMethodCount > 0, $"Thread 2 Compiled method count not greater than 0! ({t2_beforeCompiledMethodCount})"); + + Assert.True(t2_afterCompilationTime > t2_beforeCompilationTime, $"CompilationTime: after not greater than before! (after: {t2_afterCompilationTime}, before: {t2_beforeCompilationTime})"); + Assert.True(t2_afterCompiledILBytes > t2_beforeCompiledILBytes, $"Compiled IL bytes: after not greater than before! (after: {t2_afterCompiledILBytes}, before: {t2_beforeCompiledILBytes})"); + Assert.True(t2_afterCompiledMethodCount > t2_beforeCompiledMethodCount, $"Compiled method count: after not greater than before! (after: {t2_afterCompiledMethodCount}, before: {t2_beforeCompiledMethodCount})"); + + Assert.True(t1_beforeCompilationTime > TimeSpan.Zero, $"Thread 1 before compilation time not greater than 0! ({t1_beforeCompilationTime})"); + Assert.True(t1_beforeCompiledILBytes > 0, $"Thread 1 before compiled IL bytes not greater than 0! ({t1_beforeCompiledILBytes})"); + Assert.True(t1_beforeCompiledMethodCount > 0, $"Thread 1 before compiled method count not greater than 0! ({t1_beforeCompiledMethodCount})"); + + Assert.True(t1_afterCompilationTime > t1_beforeCompilationTime, $"Thread 1 compilation time: after not greater than before! (after: {t1_afterCompilationTime}, before: {t1_beforeCompilationTime})"); + Assert.True(t1_afterCompiledILBytes > t1_beforeCompiledILBytes, $"Thread 1 compiled IL bytes: after not greater than before! (after: {t1_afterCompiledILBytes}, before: {t1_beforeCompiledILBytes})"); + Assert.True(t1_afterCompiledMethodCount > t1_beforeCompiledMethodCount, $"Thread 1 compiled method count: after not greater than before! (after: {t1_afterCompiledMethodCount}, before: {t1_beforeCompiledMethodCount})"); + + Assert.True(t1_afterCompilationTime != t2_afterCompilationTime, $"Thread 1 compilation time: equal to other thread! (t1: {t1_afterCompilationTime}, t2: {t2_beforeCompilationTime})"); + Assert.True(t1_afterCompiledILBytes != t2_afterCompiledILBytes, $"Thread 1 compiled IL bytes: equal to other thread! (t1: {t1_afterCompiledILBytes}, t2: {t2_beforeCompiledILBytes})"); + Assert.True(t1_afterCompiledMethodCount != t2_afterCompiledMethodCount, $"Thread 1 compiled method count: equal to other thread! (t1: {t1_afterCompiledMethodCount}, t2: {t2_beforeCompiledMethodCount})"); + } + + [Fact] + [SkipOnCoreClr("CoreCLR does track thread specific JIT information")] + public void JitInfoCurrentThreadIsNotPopulated() + { + TimeSpan compilationTime = TimeSpan.Zero; + long compiledILBytes = 0; + long compiledMethodCount = 0; + + compilationTime = System.Runtime.JitInfo.GetCompilationTime(currentThread: true); + compiledILBytes = System.Runtime.JitInfo.GetCompiledILBytes(currentThread: true); + compiledMethodCount = System.Runtime.JitInfo.GetCompiledMethodCount(currentThread: true); + + Assert.True(compilationTime == TimeSpan.Zero, $"compilation time not equal to 0! ({compilationTime})"); + Assert.True(compiledILBytes == 0, $"compiled IL bytes not equal to 0! ({compiledILBytes})"); + Assert.True(compiledMethodCount == 0, $"compiled method count not equal to 0! ({compiledMethodCount})"); + } + } +} diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index ae9cf03ab13..8488d916e6d 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -242,6 +242,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.Mono.cs index 7f3db11f9e5..8d3ed1968d5 100644 --- a/src/mono/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.Mono.cs @@ -76,7 +76,8 @@ namespace System.Diagnostics.Tracing GC_LARGE_OBJECT_SIZE_BYTES, GC_LAST_PERCENT_TIME_IN_GC, JIT_IL_BYTES_JITTED, - JIT_METHODS_JITTED + JIT_METHODS_JITTED, + JIT_TICKS_IN_JIT } #if FEATURE_PERFTRACING diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index cbaba0038eb..ca14c4a823f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -154,15 +154,6 @@ namespace System.Runtime.CompilerServices return GetUninitializedObjectInternal(new RuntimeTypeHandle(rt).Value); } - internal static long GetILBytesJitted() - { - return (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_IL_BYTES_JITTED); - } - - internal static int GetMethodsJittedCount() - { - return (int)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_METHODS_JITTED); - } [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern unsafe void PrepareMethod(IntPtr method, IntPtr* instantiations, int ninst); diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/JitInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/JitInfo.Mono.cs new file mode 100644 index 00000000000..c22549cdf85 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/JitInfo.Mono.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.Tracing; + +namespace System.Runtime +{ + public static partial class JitInfo + { + /// + /// Get the number of bytes of IL that have been compiled. If is true, + /// then this value is scoped to the current thread, otherwise, this is a global value. + /// + /// Whether the returned value should be specific to the current thread. Default: false + /// The number of bytes of IL the JIT has compiled. + public static long GetCompiledILBytes(bool currentThread = false) + { + return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_IL_BYTES_JITTED); + } + + /// + /// Get the number of methods that have been compiled. If is true, + /// then this value is scoped to the current thread, otherwise, this is a global value. + /// + /// Whether the returned value should be specific to the current thread. Default: false + /// The number of methods the JIT has compiled. + public static long GetCompiledMethodCount(bool currentThread = false) + { + return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_METHODS_JITTED); + } + + // normalized to 100ns ticks on vm side + private static long GetCompilationTimeInTicks(bool currentThread = false) + { + return currentThread ? 0 : (long)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.JIT_TICKS_IN_JIT); + } + } +} \ No newline at end of file diff --git a/src/mono/mono/metadata/icall-eventpipe.c b/src/mono/mono/metadata/icall-eventpipe.c index 904b328ce87..7165efd3805 100644 --- a/src/mono/mono/metadata/icall-eventpipe.c +++ b/src/mono/mono/metadata/icall-eventpipe.c @@ -237,7 +237,8 @@ typedef enum { EP_RT_COUNTERS_GC_LARGE_OBJECT_SIZE_BYTES, EP_RT_COUNTERS_GC_LAST_PERCENT_TIME_IN_GC, EP_RT_COUNTERS_JIT_IL_BYTES_JITTED, - EP_RT_COUNTERS_JIT_METOHODS_JITTED + EP_RT_COUNTERS_JIT_METHODS_JITTED, + EP_RT_COUNTERS_JIT_TICKS_IN_JIT } EventPipeRuntimeCounters; static @@ -265,24 +266,26 @@ get_il_bytes_jitted (void) gint64 methods_compiled = 0; gint64 cil_code_size_bytes = 0; gint64 native_code_size_bytes = 0; + gint64 jit_time = 0; if (mono_get_runtime_callbacks ()->get_jit_stats) - mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes); + mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time); return cil_code_size_bytes; } static inline -gint32 +gint64 get_methods_jitted (void) { gint64 methods_compiled = 0; gint64 cil_code_size_bytes = 0; gint64 native_code_size_bytes = 0; + gint64 jit_time = 0; if (mono_get_runtime_callbacks ()->get_jit_stats) - mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes); - return (gint32)methods_compiled; + mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time); + return methods_compiled; } static @@ -296,6 +299,21 @@ get_exception_count (void) return excepion_count; } +static +inline +gint64 +get_ticks_in_jit (void) +{ + gint64 methods_compiled = 0; + gint64 cil_code_size_bytes = 0; + gint64 native_code_size_bytes = 0; + gint64 jit_time = 0; + + if (mono_get_runtime_callbacks ()->get_jit_stats) + mono_get_runtime_callbacks ()->get_jit_stats (&methods_compiled, &cil_code_size_bytes, &native_code_size_bytes, &jit_time); + return jit_time; +} + guint64 ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetRuntimeCounterValue (gint32 id) { EventPipeRuntimeCounters counterID = (EventPipeRuntimeCounters)id; @@ -314,8 +332,10 @@ guint64 ves_icall_System_Diagnostics_Tracing_EventPipeInternal_GetRuntimeCounter return (guint64)gc_last_percent_time_in_gc (); case EP_RT_COUNTERS_JIT_IL_BYTES_JITTED : return (guint64)get_il_bytes_jitted (); - case EP_RT_COUNTERS_JIT_METOHODS_JITTED : + case EP_RT_COUNTERS_JIT_METHODS_JITTED : return (guint64)get_methods_jitted (); + case EP_RT_COUNTERS_JIT_TICKS_IN_JIT : + return (gint64)get_ticks_in_jit (); default: return 0; } diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 53112ed0ea3..828b367f797 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -642,7 +642,7 @@ typedef struct { void (*init_mem_manager)(MonoMemoryManager*); void (*free_mem_manager)(MonoMemoryManager*); void (*metadata_update_published) (MonoAssemblyLoadContext *alc, uint32_t generation); - void (*get_jit_stats)(gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes); + void (*get_jit_stats)(gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes, gint64 *jit_time); void (*get_exception_stats)(guint32 *exception_count); // Same as compile_method, but returns a MonoFtnDesc in llvmonly mode gpointer (*get_ftnptr)(MonoMethod *method, MonoError *error); diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index aefa9a270c8..e454d89ef41 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1744,11 +1744,12 @@ typedef struct { extern MonoJitStats mono_jit_stats; static inline void -get_jit_stats (gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes) +get_jit_stats (gint64 *methods_compiled, gint64 *cil_code_size_bytes, gint64 *native_code_size_bytes, gint64 *jit_time) { *methods_compiled = mono_jit_stats.methods_compiled; *cil_code_size_bytes = mono_jit_stats.cil_code_size; *native_code_size_bytes = mono_jit_stats.native_code_size; + *jit_time = mono_jit_stats.jit_time; } guint32 diff --git a/src/tests/tracing/eventcounter/regression-25709.cs b/src/tests/tracing/eventcounter/regression-25709.cs index 9498ca47523..16447debe89 100644 --- a/src/tests/tracing/eventcounter/regression-25709.cs +++ b/src/tests/tracing/eventcounter/regression-25709.cs @@ -19,7 +19,7 @@ namespace EventCounterRegressionTests { private readonly EventLevel _level = EventLevel.Verbose; - public int MaxIncrement { get; private set; } = 0; + public double MaxIncrement { get; private set; } = 0; public SimpleEventListener() { @@ -38,7 +38,7 @@ namespace EventCounterRegressionTests protected override void OnEventWritten(EventWrittenEventArgs eventData) { - int increment = 0; + double increment = 0; bool isExceptionCounter = false; for (int i = 0; i < eventData.Payload.Count; i++) @@ -52,7 +52,7 @@ namespace EventCounterRegressionTests isExceptionCounter = true; if (payload.Key.Equals("Increment")) { - increment = Int32.Parse(payload.Value.ToString()); + increment = double.Parse(payload.Value.ToString()); } } if (isExceptionCounter) diff --git a/src/tests/tracing/eventcounter/runtimecounters.cs b/src/tests/tracing/eventcounter/runtimecounters.cs index eaa7c87d6d8..bdab454300a 100644 --- a/src/tests/tracing/eventcounter/runtimecounters.cs +++ b/src/tests/tracing/eventcounter/runtimecounters.cs @@ -42,7 +42,8 @@ namespace RuntimeEventCounterTests { "poh-size", false }, { "assembly-count", false }, { "il-bytes-jitted", false }, - { "methods-jitted-count", false } + { "methods-jitted-count", false }, + { "time-in-jit", false } }; } private Dictionary observedRuntimeCounters; From 8bf8030e24c75818c901d9c4d4674938aff3a28e Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Wed, 14 Jul 2021 20:11:16 +0200 Subject: [PATCH 552/926] HTTP/3 GET Aborted stress scenario fix (#55582) This adds checks for HTTP/3 specific exceptions to GET Aborted scenario --- .../HttpStress/ClientOperations.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs index 5708e48277e..f856652adb7 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/ClientOperations.cs @@ -318,6 +318,32 @@ namespace HttpStress } } + if (ctx.HttpVersion == HttpVersion.Version30) + { + // HTTP/3 exception nesting: + // HttpRequestException->IOException->HttpRequestException->QuicStreamAbortedException + // HttpRequestException->QuicStreamAbortedException + + if (e is IOException && e.InnerException is HttpRequestException) + { + e = e.InnerException; + } + + if (e is HttpRequestException) + { + string? name = e.InnerException?.GetType().Name; + switch (name) + { + case "QuicStreamAbortedException": + if (e.InnerException?.Message?.Equals("Stream aborted by peer (258).") ?? false) // 258 = H3_INTERNAL_ERROR (0x102) + { + return; + } + break; + } + } + } + throw; } }), From 44f7103e0e420ae81198c6c5301fa5d1cb82b23c Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 14 Jul 2021 11:24:33 -0700 Subject: [PATCH 553/926] [wasm][debugger] Implement support to DebuggerDisplay attribute (#55524) * Support DebuggerDisplay attribute on type. * Implementing DebuggerDisplay calling methods. * Apply suggestions from code review Co-authored-by: Larry Ewing * Fixing snake case. * Changing snake case. Co-authored-by: Larry Ewing --- src/mono/sample/wasm/browser/Program.cs | 33 +++++++ .../BrowserDebugProxy/DevToolsHelper.cs | 11 +++ .../BrowserDebugProxy/EvaluateExpression.cs | 1 + .../MemberReferenceResolver.cs | 29 +++++- .../debugger/BrowserDebugProxy/MonoProxy.cs | 2 +- .../BrowserDebugProxy/MonoSDBHelper.cs | 95 ++++++++++++++++++- .../DebuggerTestSuite/CustomViewTests.cs | 33 +++++++ .../debugger-custom-view-test.cs | 73 ++++++++++++++ 8 files changed, 271 insertions(+), 6 deletions(-) create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs create mode 100644 src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs diff --git a/src/mono/sample/wasm/browser/Program.cs b/src/mono/sample/wasm/browser/Program.cs index 743f481896f..f87cd9103c7 100644 --- a/src/mono/sample/wasm/browser/Program.cs +++ b/src/mono/sample/wasm/browser/Program.cs @@ -3,11 +3,40 @@ using System; using System.Runtime.CompilerServices; +using System.Diagnostics; namespace Sample { public class Test { + [DebuggerDisplay ("Some {Val1} Value {Val2} End")] + class WithDisplayString + { + internal string Val1 = "one"; + + public int Val2 { get { return 2; } } + } + + class WithToString + { + public override string ToString () + { + return "SomeString"; + } + } + + [DebuggerDisplay ("{GetDebuggerDisplay(), nq}")] + class DebuggerDisplayMethodTest + { + int someInt = 32; + int someInt2 = 43; + + string GetDebuggerDisplay () + { + return "First Int:" + someInt + " Second Int:" + someInt2; + } + } + public static void Main(string[] args) { Console.WriteLine ("Hello, World!"); @@ -16,6 +45,10 @@ namespace Sample [MethodImpl(MethodImplOptions.NoInlining)] public static int TestMeaning() { + var a = new WithDisplayString(); + var c = new DebuggerDisplayMethodTest(); + Console.WriteLine(a); + Console.WriteLine(c); return 42; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index fb4eda76ca2..de40842443c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -322,5 +322,16 @@ namespace Microsoft.WebAssembly.Diagnostics { public Dictionary Locals { get; } = new Dictionary(); public Dictionary MemberReferences { get; } = new Dictionary(); + public Dictionary ObjectFields { get; } = new Dictionary(); + public PerScopeCache(JArray objectValues) + { + foreach (var objectValue in objectValues) + { + ObjectFields[objectValue["name"].Value()] = objectValue.Value(); + } + } + public PerScopeCache() + { + } } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 58374c43565..e551c16dfe5 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -51,6 +51,7 @@ namespace Microsoft.WebAssembly.Diagnostics if (node is IdentifierNameSyntax identifier && !(identifier.Parent is MemberAccessExpressionSyntax) + && !(identifier.Parent is InvocationExpressionSyntax) && !identifiers.Any(x => x.Identifier.Text == identifier.Identifier.Text)) { identifiers.Add(identifier); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index cd21ac64190..3e791f2f3e0 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -22,7 +22,7 @@ namespace Microsoft.WebAssembly.Diagnostics private ExecutionContext ctx; private PerScopeCache scopeCache; private ILogger logger; - private bool locals_fetched; + private bool localsFetched; public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId sessionId, int scopeId, ILogger logger) { @@ -33,6 +33,18 @@ namespace Microsoft.WebAssembly.Diagnostics this.logger = logger; scopeCache = ctx.GetCacheForScope(scopeId); } + + public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId sessionId, JArray objectValues, ILogger logger) + { + this.sessionId = sessionId; + scopeId = -1; + this.proxy = proxy; + this.ctx = ctx; + this.logger = logger; + scopeCache = new PerScopeCache(objectValues); + localsFetched = true; + } + public async Task GetValueFromObject(JToken objRet, CancellationToken token) { if (objRet["value"]?["className"]?.Value() == "System.Exception") @@ -76,6 +88,11 @@ namespace Microsoft.WebAssembly.Diagnostics if (scopeCache.MemberReferences.TryGetValue(varName, out JObject ret)) { return ret; } + + if (scopeCache.ObjectFields.TryGetValue(varName, out JObject valueRet)) { + return await GetValueFromObject(valueRet, token); + } + foreach (string part in parts) { string partTrimmed = part.Trim(); @@ -96,12 +113,12 @@ namespace Microsoft.WebAssembly.Diagnostics } continue; } - if (scopeCache.Locals.Count == 0 && !locals_fetched) + if (scopeCache.Locals.Count == 0 && !localsFetched) { Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token); if (scope_res.IsErr) throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}"); - locals_fetched = true; + localsFetched = true; } if (scopeCache.Locals.TryGetValue(partTrimmed, out JObject obj)) { @@ -145,6 +162,12 @@ namespace Microsoft.WebAssembly.Diagnostics rootObject = await Resolve(memberAccessExpressionSyntax.Expression.ToString(), token); methodName = memberAccessExpressionSyntax.Name.ToString(); } + else if (expr is IdentifierNameSyntax) + if (scopeCache.ObjectFields.TryGetValue("this", out JObject valueRet)) { + rootObject = await GetValueFromObject(valueRet, token); + methodName = expr.ToString(); + } + if (rootObject != null) { DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId objectId); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 1872ec1d2a9..4dc7c374268 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -29,7 +29,7 @@ namespace Microsoft.WebAssembly.Diagnostics public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) { this.urlSymbolServerList = urlSymbolServerList ?? new List(); - SdbHelper = new MonoSDBHelper(this); + SdbHelper = new MonoSDBHelper(this, logger); } internal ExecutionContext GetContext(SessionId sessionId) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index d12533c1e9f..645a6a4ac2a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -615,9 +615,12 @@ namespace Microsoft.WebAssembly.Diagnostics private MonoProxy proxy; private static int MINOR_VERSION = 61; private static int MAJOR_VERSION = 2; - public MonoSDBHelper(MonoProxy proxy) + private readonly ILogger logger; + + public MonoSDBHelper(MonoProxy proxy, ILogger logger) { this.proxy = proxy; + this.logger = logger; } public void ClearCache() @@ -946,6 +949,7 @@ namespace Microsoft.WebAssembly.Diagnostics } return ret; } + public string ReplaceCommonClassNames(string className) { className = className.Replace("System.String", "string"); @@ -957,6 +961,88 @@ namespace Microsoft.WebAssembly.Diagnostics className = className.Replace("System.Byte", "byte"); return className; } + + public async Task GetDebuggerDisplayAttribute(SessionId sessionId, int objectId, int typeId, CancellationToken token) + { + string expr = ""; + var invokeParams = new MemoryStream(); + var invokeParamsWriter = new MonoBinaryWriter(invokeParams); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(typeId); + commandParamsWriter.Write(0); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetCattrs, commandParams, token); + var count = retDebuggerCmdReader.ReadInt32(); + if (count == 0) + return null; + for (int i = 0 ; i < count; i++) + { + var methodId = retDebuggerCmdReader.ReadInt32(); + if (methodId == 0) + continue; + commandParams = new MemoryStream(); + commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); + var retDebuggerCmdReader2 = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetDeclaringType, commandParams, token); + var customAttributeTypeId = retDebuggerCmdReader2.ReadInt32(); + var customAttributeName = await GetTypeName(sessionId, customAttributeTypeId, token); + if (customAttributeName == "System.Diagnostics.DebuggerDisplayAttribute") + { + invokeParamsWriter.Write((byte)ValueTypeId.Null); + invokeParamsWriter.Write((byte)0); //not used + invokeParamsWriter.Write(0); //not used + var parmCount = retDebuggerCmdReader.ReadInt32(); + invokeParamsWriter.Write((int)1); + for (int j = 0; j < parmCount; j++) + { + invokeParamsWriter.Write((byte)retDebuggerCmdReader.ReadByte()); + invokeParamsWriter.Write(retDebuggerCmdReader.ReadInt32()); + } + var retMethod = await InvokeMethod(sessionId, invokeParams.ToArray(), methodId, "methodRet", token); + DotnetObjectId.TryParse(retMethod?["value"]?["objectId"]?.Value(), out DotnetObjectId dotnetObjectId); + var displayAttrs = await GetObjectValues(sessionId, int.Parse(dotnetObjectId.Value), true, false, false, false, token); + var displayAttrValue = displayAttrs.FirstOrDefault(attr => attr["name"].Value().Equals("Value")); + try { + ExecutionContext context = proxy.GetContext(sessionId); + var objectValues = await GetObjectValues(sessionId, objectId, true, false, false, false, token); + + var thisObj = CreateJObject("", "object", "", false, objectId:$"dotnet:object:{objectId}"); + thisObj["name"] = "this"; + objectValues.Add(thisObj); + + var resolver = new MemberReferenceResolver(proxy, context, sessionId, objectValues, logger); + var dispAttrStr = displayAttrValue["value"]?["value"]?.Value(); + //bool noQuotes = false; + if (dispAttrStr.Contains(", nq")) + { + //noQuotes = true; + dispAttrStr = dispAttrStr.Replace(", nq", ""); + } + expr = "$\"" + dispAttrStr + "\""; + JObject retValue = await resolver.Resolve(expr, token); + if (retValue == null) + retValue = await EvaluateExpression.CompileAndRunTheExpression(expr, resolver, token); + return retValue?["value"]?.Value(); + } + catch (Exception) + { + logger.LogDebug($"Could not evaluate DebuggerDisplayAttribute - {expr}"); + return null; + } + } + else + { + var parmCount = retDebuggerCmdReader.ReadInt32(); + for (int j = 0; j < parmCount; j++) + { + //to read parameters + await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, "varName", false, -1, token); + } + } + } + return null; + } + public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token) { var commandParams = new MemoryStream(); @@ -1043,7 +1129,7 @@ namespace Microsoft.WebAssembly.Diagnostics var commandParamsWriter = new MonoBinaryWriter(commandParams); commandParamsWriter.Write((int)type_id); commandParamsWriter.WriteString(method_name); - commandParamsWriter.Write((int)(0x10 | 4)); //instance methods + commandParamsWriter.Write((int)(0x10 | 4 | 0x20)); //instance methods commandParamsWriter.Write((int)1); //case sensitive var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, commandParams, token); var nMethods = retDebuggerCmdReader.ReadInt32(); @@ -1299,7 +1385,12 @@ namespace Microsoft.WebAssembly.Diagnostics var className = ""; var type_id = await GetTypeIdFromObject(sessionId, objectId, false, token); className = await GetTypeName(sessionId, type_id[0], token); + var debuggerDisplayAttribute = await GetDebuggerDisplayAttribute(sessionId, objectId, type_id[0], token); var description = className.ToString(); + + if (debuggerDisplayAttribute != null) + description = debuggerDisplayAttribute; + if (await IsDelegate(sessionId, objectId, token)) { if (typeIdFromAttribute != -1) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs new file mode 100644 index 00000000000..c848c03c303 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.WebAssembly.Diagnostics; +using Newtonsoft.Json.Linq; +using System.Threading; +using Xunit; + +namespace DebuggerTests +{ + + public class CustomViewTests : DebuggerTestBase + { + [Fact] + public async Task CustomView() + { + var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.DebuggerCustomViewTest", "run", 5); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.DebuggerCustomViewTest:run'); }, 1);", + "dotnet://debugger-test.dll/debugger-custom-view-test.cs", + bp.Value["locations"][0]["lineNumber"].Value(), + bp.Value["locations"][0]["columnNumber"].Value(), + "run"); + + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckObject(locals, "a", "DebuggerTests.WithDisplayString", description:"Some one Value 2 End"); + CheckObject(locals, "c", "DebuggerTests.DebuggerDisplayMethodTest", description: "First Int:32 Second Int:43"); + } + } +} diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs new file mode 100644 index 00000000000..2d97a9b64a3 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs @@ -0,0 +1,73 @@ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace DebuggerTests +{ + [DebuggerDisplay("Some {Val1} Value {Val2} End")] + class WithDisplayString + { + internal string Val1 = "one"; + + public int Val2 { get { return 2; } } + } + + class WithToString + { + public override string ToString () + { + return "SomeString"; + } + } + + [DebuggerTypeProxy(typeof(TheProxy))] + class WithProxy + { + public string Val1 { + get { return "one"; } + } + } + + class TheProxy + { + WithProxy wp; + + public TheProxy (WithProxy wp) + { + this.wp = wp; + } + + public string Val2 { + get { return wp.Val1; } + } + } + + [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] + class DebuggerDisplayMethodTest + { + int someInt = 32; + int someInt2 = 43; + + string GetDebuggerDisplay () + { + return "First Int:" + someInt + " Second Int:" + someInt2; + } + } + + class DebuggerCustomViewTest + { + public static void run() + { + var a = new WithDisplayString(); + var b = new WithProxy(); + var c = new DebuggerDisplayMethodTest(); + Console.WriteLine(a); + Console.WriteLine(b); + Console.WriteLine(c); + } + } +} From 01955a2a0edf4d3e800a159172e46ca0e4d51de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 14 Jul 2021 20:51:25 +0200 Subject: [PATCH 554/926] Disable some System.Drawing.Common tests failing on Windows Mono (#55656) After https://github.com/dotnet/runtime/pull/54884 these tests started failing on Mono on Windows, see https://github.com/dotnet/runtime/issues/55655. --- src/libraries/System.Drawing.Common/tests/IconTests.cs | 2 ++ .../System.Drawing.Common/tests/Imaging/MetafileTests.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/libraries/System.Drawing.Common/tests/IconTests.cs b/src/libraries/System.Drawing.Common/tests/IconTests.cs index 108e277aee2..7a415016e42 100644 --- a/src/libraries/System.Drawing.Common/tests/IconTests.cs +++ b/src/libraries/System.Drawing.Common/tests/IconTests.cs @@ -610,6 +610,7 @@ namespace System.Drawing.Tests private const string DontSupportPngFramesInIcons = "Switch.System.Drawing.DontSupportPngFramesInIcons"; [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalFact(Helpers.IsDrawingSupported)] public void ToBitmap_PngIconSupportedInSwitches_Success() { @@ -647,6 +648,7 @@ namespace System.Drawing.Tests } [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalFact(Helpers.IsDrawingSupported)] public void ToBitmap_PngIconNotSupportedInSwitches_ThrowsArgumentOutOfRangeException() { diff --git a/src/libraries/System.Drawing.Common/tests/Imaging/MetafileTests.cs b/src/libraries/System.Drawing.Common/tests/Imaging/MetafileTests.cs index a70b44aa618..ef466a33c0d 100644 --- a/src/libraries/System.Drawing.Common/tests/Imaging/MetafileTests.cs +++ b/src/libraries/System.Drawing.Common/tests/Imaging/MetafileTests.cs @@ -852,6 +852,7 @@ namespace System.Drawing.Imaging.Tests } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalFact(Helpers.IsDrawingSupported)] public void Ctor_IntPtrZeroIV_ThrowsArgumentException() { @@ -866,6 +867,7 @@ namespace System.Drawing.Imaging.Tests } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalTheory(Helpers.IsDrawingSupported)] [MemberData(nameof(MetafileFrameUnit_Invalid_TestData))] public void Ctor_InvalidFrameUnitIII_ThrowsArgumentException(MetafileFrameUnit frameUnit) @@ -883,6 +885,7 @@ namespace System.Drawing.Imaging.Tests } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalTheory(Helpers.IsDrawingSupported)] [MemberData(nameof(EmfType_Invalid_TestData))] public void Ctor_InvalidEmfTypeIII_ThrowsArgumentException(EmfType emfType) From 95044fd7dfb174eb610eb343d8571d3854cb1771 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com> Date: Wed, 14 Jul 2021 22:01:43 +0300 Subject: [PATCH 555/926] Override the APM methods on AsyncWindowsFileStreamStrategy. (#55203) * Override the APM methods on AsyncWindowsFileStreamStrategy. Closes #55172. * Address PR feedback. * Update src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs * Update AsyncWindowsFileStreamStrategy.cs Co-authored-by: Stephen Toub --- .../IO/Strategies/AsyncWindowsFileStreamStrategy.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index c83b58921c4..0f59f6748a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -28,7 +28,7 @@ namespace System.IO.Strategies public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) => ReadAsyncInternal(destination, cancellationToken); - private unsafe ValueTask ReadAsyncInternal(Memory destination, CancellationToken cancellationToken = default) + private unsafe ValueTask ReadAsyncInternal(Memory destination, CancellationToken cancellationToken) { if (!CanRead) { @@ -139,5 +139,15 @@ namespace System.IO.Strategies } } } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => + TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); + + public override int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => + TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); } } From bf014b59dc52544c268496f9631f1916eb5fa5d7 Mon Sep 17 00:00:00 2001 From: Tlakaelel Axayakatl Ceja Date: Wed, 14 Jul 2021 12:09:16 -0700 Subject: [PATCH 556/926] Unify Requires Attribute (#55622) Add an overload to RequiresAssemblyFiles to be able to have the same user experience as if using RequiresUnreferencedCode --- .../src/System/Reflection/Emit/AssemblyBuilder.cs | 2 +- .../src/System/Reflection/RuntimeAssembly.cs | 2 +- .../ref/Microsoft.Extensions.DependencyModel.cs | 6 +++--- .../src/DependencyContext.cs | 6 +++--- .../src/DependencyContextLoader.cs | 6 +++--- .../CodeAnalysis/RequiresAssemblyFilesAttribute.cs | 13 ++++++++++++- .../src/System/Reflection/Assembly.cs | 6 +++--- .../src/System/Reflection/AssemblyName.cs | 6 +++--- .../src/System/Reflection/Emit/AssemblyBuilder.cs | 2 +- .../Context/Delegation/DelegatingAssembly.cs | 8 ++++---- .../Reflection/TypeLoading/Assemblies/RoAssembly.cs | 4 ++-- src/libraries/System.Runtime/ref/System.Runtime.cs | 13 +++++++------ .../RequiresAssemblyFilesAttributeTests.cs | 4 ++-- .../src/System/Reflection/RuntimeAssembly.cs | 2 +- 14 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index a0faa6ad6e0..5eea3c9dae6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -104,7 +104,7 @@ namespace System.Reflection.Emit public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); [RequiresUnreferencedCode("Types might be removed")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index dc866cf6ba3..a45d81d4b23 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -83,7 +83,7 @@ namespace System.Reflection return null; } - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs index 10786ab1afe..8874af552ba 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs @@ -62,12 +62,12 @@ namespace Microsoft.Extensions.DependencyModel public DependencyContext(Microsoft.Extensions.DependencyModel.TargetInfo target, Microsoft.Extensions.DependencyModel.CompilationOptions compilationOptions, System.Collections.Generic.IEnumerable compileLibraries, System.Collections.Generic.IEnumerable runtimeLibraries, System.Collections.Generic.IEnumerable runtimeGraph) { } public Microsoft.Extensions.DependencyModel.CompilationOptions CompilationOptions { get { throw null; } } public System.Collections.Generic.IReadOnlyList CompileLibraries { get { throw null; } } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static Microsoft.Extensions.DependencyModel.DependencyContext Default { get { throw null; } } public System.Collections.Generic.IReadOnlyList RuntimeGraph { get { throw null; } } public System.Collections.Generic.IReadOnlyList RuntimeLibraries { get { throw null; } } public Microsoft.Extensions.DependencyModel.TargetInfo Target { get { throw null; } } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static Microsoft.Extensions.DependencyModel.DependencyContext Load(System.Reflection.Assembly assembly) { throw null; } public Microsoft.Extensions.DependencyModel.DependencyContext Merge(Microsoft.Extensions.DependencyModel.DependencyContext other) { throw null; } } @@ -97,7 +97,7 @@ namespace Microsoft.Extensions.DependencyModel { public DependencyContextLoader() { } public static Microsoft.Extensions.DependencyModel.DependencyContextLoader Default { get { throw null; } } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public Microsoft.Extensions.DependencyModel.DependencyContext Load(System.Reflection.Assembly assembly) { throw null; } } public partial class DependencyContextWriter diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs index 6e58abf3134..770123f2beb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs @@ -50,7 +50,7 @@ namespace Microsoft.Extensions.DependencyModel RuntimeGraph = runtimeGraph.ToArray(); } - [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [RequiresAssemblyFiles("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static DependencyContext Default => _defaultContext.Value; public TargetInfo Target { get; } @@ -79,7 +79,7 @@ namespace Microsoft.Extensions.DependencyModel ); } - [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [RequiresAssemblyFiles("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] private static DependencyContext LoadDefault() { var entryAssembly = Assembly.GetEntryAssembly(); @@ -91,7 +91,7 @@ namespace Microsoft.Extensions.DependencyModel return Load(entryAssembly); } - [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [RequiresAssemblyFiles("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static DependencyContext Load(Assembly assembly) { return DependencyContextLoader.Default.Load(assembly); diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs index 2bd5d410d46..6032c69281a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs @@ -51,7 +51,7 @@ namespace Microsoft.Extensions.DependencyModel return assembly.GetManifestResourceStream(name); } - [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [RequiresAssemblyFiles("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public DependencyContext Load(Assembly assembly) { if (assembly == null) @@ -105,7 +105,7 @@ namespace Microsoft.Extensions.DependencyModel return null; } - [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] + [RequiresAssemblyFiles("DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] private DependencyContext LoadAssemblyContext(Assembly assembly, IDependencyContextReader reader) { using (Stream stream = GetResourceStream(assembly, assembly.GetName().Name + DepsJsonExtension)) @@ -128,7 +128,7 @@ namespace Microsoft.Extensions.DependencyModel return null; } - [RequiresAssemblyFiles(Message = "The use of DependencyContextLoader is not supported when publishing as single-file")] + [RequiresAssemblyFiles("The use of DependencyContextLoader is not supported when publishing as single-file")] private string GetDepsJsonPath(Assembly assembly) { // Assemblies loaded in memory (e.g. single file) return empty string from Location. diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs index 358bf3136da..9c90c326948 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs @@ -24,11 +24,22 @@ namespace System.Diagnostics.CodeAnalysis /// public RequiresAssemblyFilesAttribute() { } + /// + /// Initializes a new instance of the class. + /// + /// + /// A message that contains information about the need for assembly files to be on disk. + /// + public RequiresAssemblyFilesAttribute(string message) + { + Message = message; + } + /// /// Gets or sets an optional message that contains information about the need for /// assembly files to be on disk. /// - public string? Message { get; set; } + public string? Message { get; } /// /// Gets or sets an optional URL that contains more information about the member, diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs index 6baab555151..5b5035b2720 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -81,7 +81,7 @@ namespace System.Reflection [RequiresUnreferencedCode("Types might be removed")] public virtual Type[] GetForwardedTypes() { throw NotImplemented.ByDesign; } - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public virtual string? CodeBase => throw NotImplemented.ByDesign; public virtual MethodInfo? EntryPoint => throw NotImplemented.ByDesign; public virtual string? FullName => throw NotImplemented.ByDesign; @@ -116,7 +116,7 @@ namespace System.Reflection public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; } public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public virtual string EscapedCodeBase => AssemblyName.EscapeCodeBase(CodeBase); [RequiresUnreferencedCode("Assembly.CreateInstance is not supported with trimming. Use Type.GetType instead.")] @@ -154,7 +154,7 @@ namespace System.Reflection public virtual Assembly GetSatelliteAssembly(CultureInfo culture, Version? version) { throw NotImplemented.ByDesign; } public virtual FileStream? GetFile(string name) { throw NotImplemented.ByDesign; } - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public virtual FileStream[] GetFiles() => GetFiles(getResourceModules: false); public virtual FileStream[] GetFiles(bool getResourceModules) { throw NotImplemented.ByDesign; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 4291dded861..45369f7528e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -60,12 +60,12 @@ namespace System.Reflection public string? CodeBase { - [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will return an empty string for assemblies embedded in a single-file app")] get => _codeBase; set => _codeBase = value; } - [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will return an empty string for assemblies embedded in a single-file app")] public string? EscapedCodeBase { get @@ -261,7 +261,7 @@ namespace System.Reflection return refName.Equals(defName, StringComparison.OrdinalIgnoreCase); } - [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will return an empty string for assemblies embedded in a single-file app")] internal static string EscapeCodeBase(string? codebase) { if (codebase == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 5dbdeeb3a67..dc77c941297 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Emit { public sealed partial class AssemblyBuilder : Assembly { - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override string Location => throw new NotSupportedException(SR.NotSupported_DynamicAssembly); public override MethodInfo? EntryPoint => null; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 990302be8c5..dd48ebd0d9e 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -23,7 +23,7 @@ namespace System.Reflection.Context.Delegation } #pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly - [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } @@ -111,19 +111,19 @@ namespace System.Reflection.Context.Delegation } #pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly - [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream? GetFile(string name) { return UnderlyingAssembly.GetFile(name); } - [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles() { return UnderlyingAssembly.GetFiles(); } - [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFiles(bool)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles(bool)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles(bool getResourceModules) { return UnderlyingAssembly.GetFiles(getResourceModules); diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs index 949994d04b3..114f581d069 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/RoAssembly.cs @@ -44,12 +44,12 @@ namespace System.Reflection.TypeLoading public abstract override string Location { get; } #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string CodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); #if NET5_0_OR_GREATER [Obsolete(Obsoletions.CodeBaseMessage, DiagnosticId = Obsoletions.CodeBaseDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] #endif public sealed override string EscapedCodeBase => throw new NotSupportedException(SR.NotSupported_AssemblyCodeBase); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index b1baaa1a95d..c18894bd257 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9011,7 +9011,8 @@ namespace System.Diagnostics.CodeAnalysis public sealed partial class RequiresAssemblyFilesAttribute : System.Attribute { public RequiresAssemblyFilesAttribute() { } - public string? Message { get { throw null; } set { } } + public RequiresAssemblyFilesAttribute(string message) { } + public string? Message { get { throw null; } } public string? Url { get { throw null; } set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Constructor | System.AttributeTargets.Method, Inherited=false)] @@ -11059,13 +11060,13 @@ namespace System.Reflection { protected Assembly() { } [System.ObsoleteAttribute("Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead.", DiagnosticId = "SYSLIB0012", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will throw for assemblies embedded in a single-file app")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("The code will throw for assemblies embedded in a single-file app")] public virtual string? CodeBase { get { throw null; } } public virtual System.Collections.Generic.IEnumerable CustomAttributes { get { throw null; } } public virtual System.Collections.Generic.IEnumerable DefinedTypes { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] get { throw null; } } public virtual System.Reflection.MethodInfo? EntryPoint { get { throw null; } } [System.ObsoleteAttribute("Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead.", DiagnosticId = "SYSLIB0012", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will throw for assemblies embedded in a single-file app")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("The code will throw for assemblies embedded in a single-file app")] public virtual string EscapedCodeBase { get { throw null; } } public virtual System.Collections.Generic.IEnumerable ExportedTypes { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] get { throw null; } } public virtual string? FullName { get { throw null; } } @@ -11100,7 +11101,7 @@ namespace System.Reflection [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] public virtual System.Type[] GetExportedTypes() { throw null; } public virtual System.IO.FileStream? GetFile(string name) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public virtual System.IO.FileStream[] GetFiles() { throw null; } public virtual System.IO.FileStream[] GetFiles(bool getResourceModules) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] @@ -11269,11 +11270,11 @@ namespace System.Reflection { public AssemblyName() { } public AssemblyName(string assemblyName) { } - public string? CodeBase { [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "The code will return an empty string for assemblies embedded in a single-file app")] get { throw null; } set { } } + public string? CodeBase { [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("The code will return an empty string for assemblies embedded in a single-file app")] get { throw null; } set { } } public System.Reflection.AssemblyContentType ContentType { get { throw null; } set { } } public System.Globalization.CultureInfo? CultureInfo { get { throw null; } set { } } public string? CultureName { get { throw null; } set { } } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will return an empty string for assemblies embedded in a single-file app")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute("The code will return an empty string for assemblies embedded in a single-file app")] public string? EscapedCodeBase { get { throw null; } } public System.Reflection.AssemblyNameFlags Flags { get { throw null; } set { } } public string FullName { get { throw null; } } diff --git a/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttributeTests.cs b/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttributeTests.cs index da098f97f5a..974de11e89b 100644 --- a/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttributeTests.cs +++ b/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttributeTests.cs @@ -22,7 +22,7 @@ namespace System.Diagnostics.CodeAnalysis.Tests [InlineData(null)] public void TestSetMessage(string message) { - var attr = new RequiresAssemblyFilesAttribute(Message = message); + var attr = new RequiresAssemblyFilesAttribute(message); Assert.Equal(message, attr.Message); Assert.Null(attr.Url); @@ -52,7 +52,7 @@ namespace System.Diagnostics.CodeAnalysis.Tests [InlineData(null, null)] public void TestSetMessageAndUrl(string message, string url) { - var attr = new RequiresAssemblyFilesAttribute(Message = message, Url = url); + var attr = new RequiresAssemblyFilesAttribute(message, Url = url); Assert.Equal(message, attr.Message); Assert.Equal(ur, attr.Url); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 21ac12509e6..cf2511fa283 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -74,7 +74,7 @@ namespace System.Reflection public override bool ReflectionOnly => false; - [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] public override string? CodeBase { get From 32f6a56ee5e172dbb8e6be7529fe6562b849f4b6 Mon Sep 17 00:00:00 2001 From: Jeff Schwartz Date: Wed, 14 Jul 2021 12:15:55 -0700 Subject: [PATCH 557/926] remove Steve as the os-mac-os-x owner (#55671) --- docs/area-owners.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/area-owners.md b/docs/area-owners.md index 740404f771a..75a26412657 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -146,7 +146,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | os-alpine | | | | | os-android | @steveisok | @akoeplinger | | | os-freebsd | | | | -| os-mac-os-x | @steveisok | | | +| os-mac-os-x | | | | | os-maccatalyst | @steveisok | | | | os-ios | @steveisok | @vargaz | | | os-tvos | @steveisok | @vargaz | | From f6e9532005099d0f7b90cfc0411e58ddd1a67da4 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 14 Jul 2021 15:22:10 -0400 Subject: [PATCH 558/926] Require CFB8 for persisted CNG keys in CFB mode This changes AesCng and TripleDESCng to require the feedback size to be set to '8' when in CFB mode and using a persisted CNG key. Prior to this change, BasicSymmetricCipherNCrypt ignored the feedback size which resulted in CFB8 always being used, even if the FeedbackSize was set to another value. However, when padding was applied, the padding size would be padded to the feedback size. In the case of AesCng, this would mean it would be encrypted with CFB8 and padded as if it were CFB128. This changes the implementation so that the feedback size is required to be set to 8 for persisted keys. No change is made for ephemeral keys. --- .../AES/AesContractTests.cs | 20 +++++++++++++ .../TripleDES/TripleDESContractTests.cs | 19 ++++++++++++ .../BasicSymmetricCipherNCrypt.cs | 2 +- .../Cryptography/CngSymmetricAlgorithmCore.cs | 28 ++++++++++++++++-- .../Cryptography/ICngSymmetricAlgorithm.cs | 1 + .../System/Security/Cryptography/AesCng.cs | 29 ++++--------------- .../Security/Cryptography/TripleDESCng.cs | 29 ++++--------------- .../tests/AesCngTests.cs | 2 +- .../tests/SymmetricCngTestHelpers.cs | 22 ++++++++++++-- ...tem.Security.Cryptography.Cng.Tests.csproj | 2 ++ .../tests/TripleDESCngTests.cs | 2 +- 11 files changed, 101 insertions(+), 55 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs index 89e3ebbe61f..878d2c00a07 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/AES/AesContractTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.Encryption.Aes.Tests @@ -380,6 +381,25 @@ namespace System.Security.Cryptography.Encryption.Aes.Tests } } + [Fact] + public static void Cfb8ModeCanDepadCfb128Padding() + { + using (Aes aes = AesFactory.Create()) + { + // 1, 2, 3, 4, 5 encrypted with CFB8 but padded with block-size padding. + byte[] ciphertext = "68C272ACF16BE005A361DB1C147CA3AD".HexToByteArray(); + aes.Key = "3279CE2E9669A54E038AA62818672150D0B5A13F6757C27F378115501F83B119".HexToByteArray(); + aes.IV = new byte[16]; + aes.Padding = PaddingMode.PKCS7; + aes.Mode = CipherMode.CFB; + aes.FeedbackSize = 8; + + using ICryptoTransform transform = aes.CreateDecryptor(); + byte[] decrypted = transform.TransformFinalBlock(ciphertext, 0, ciphertext.Length); + Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, decrypted); + } + } + private static void ValidateTransformProperties(Aes aes, ICryptoTransform transform) { Assert.NotNull(transform); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs index e12aa7196c8..725fa30eca7 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESContractTests.cs @@ -89,5 +89,24 @@ namespace System.Security.Cryptography.Encryption.TripleDes.Tests Assert.NotNull(encryptor); } } + + [Fact] + public static void Cfb8ModeCanDepadCfb64Padding() + { + using (TripleDES tdes = TripleDESFactory.Create()) + { + // 1, 2, 3, 4, 5 encrypted with CFB8 but padded with block-size padding. + byte[] ciphertext = "97F1CE6A6D869A85".HexToByteArray(); + tdes.Key = "3D1ECCEE6C99B029950ED23688AA229AF85177421609F7BF".HexToByteArray(); + tdes.IV = new byte[8]; + tdes.Padding = PaddingMode.PKCS7; + tdes.Mode = CipherMode.CFB; + tdes.FeedbackSize = 8; + + using ICryptoTransform transform = tdes.CreateDecryptor(); + byte[] decrypted = transform.TransformFinalBlock(ciphertext, 0, ciphertext.Length); + Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, decrypted); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/BasicSymmetricCipherNCrypt.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/BasicSymmetricCipherNCrypt.cs index 4efd7174c96..1e38c44fef8 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/BasicSymmetricCipherNCrypt.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/BasicSymmetricCipherNCrypt.cs @@ -21,7 +21,7 @@ namespace Internal.Cryptography // // The delegate must instantiate a new CngKey, based on a new underlying NCryptKeyHandle, each time is called. // - public BasicSymmetricCipherNCrypt(Func cngKeyFactory, CipherMode cipherMode, int blockSizeInBytes, byte[]? iv, bool encrypting, int feedbackSizeInBytes, int paddingSize) + public BasicSymmetricCipherNCrypt(Func cngKeyFactory, CipherMode cipherMode, int blockSizeInBytes, byte[]? iv, bool encrypting, int paddingSize) : base(iv, blockSizeInBytes, paddingSize) { _encrypting = encrypting; diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs index 6da56739bb8..401a8c1a7d6 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs @@ -144,6 +144,8 @@ namespace Internal.Cryptography if (rgbKey == null) throw new ArgumentNullException(nameof(rgbKey)); + ValidateFeedbackSize(mode, feedbackSizeInBits); + byte[] key = rgbKey.CloneByteArray(); long keySize = key.Length * (long)BitsPerByte; @@ -187,6 +189,9 @@ namespace Internal.Cryptography { // note: iv is guaranteed to be cloned before this method, so no need to clone it again + ValidateFeedbackSize(mode, feedbackSizeInBits); + Debug.Assert(mode == CipherMode.CFB ? feedbackSizeInBits == 8 : true); + int blockSizeInBytes = _outer.BlockSize.BitSizeToByteSize(); BasicSymmetricCipher cipher = new BasicSymmetricCipherNCrypt( cngKeyFactory, @@ -194,7 +199,6 @@ namespace Internal.Cryptography blockSizeInBytes, iv, encrypting, - feedbackSizeInBits, _outer.GetPaddingSize(mode, feedbackSizeInBits)); return UniversalCryptoTransform.Create(padding, cipher, encrypting); } @@ -206,11 +210,31 @@ namespace Internal.Cryptography return CngKey.Open(_keyName!, _provider!, _optionOptions); } - public bool KeyInPlainText + private bool KeyInPlainText { get { return _keyName == null; } } + private void ValidateFeedbackSize(CipherMode mode, int feedbackSizeInBits) + { + if (mode != CipherMode.CFB) + return; + + if (KeyInPlainText) + { + if (!_outer.IsValidEphemeralFeedbackSize(feedbackSizeInBits)) + { + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedbackSizeInBits, CipherMode.CFB)); + } + } + else if (feedbackSizeInBits != 8) + { + // Persisted CNG keys in CFB mode always use CFB8 when in CFB mode, + // so require the feedback size to be set to 8. + throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedbackSizeInBits, CipherMode.CFB)); + } + } + private readonly ICngSymmetricAlgorithm _outer; // If using a stored CNG key, these fields provide the CngKey.Open() parameters. If using a plaintext key, _keyName is set to null. diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs index eae2d44dec5..fa46b5cd2ad 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs @@ -32,5 +32,6 @@ namespace Internal.Cryptography string GetNCryptAlgorithmIdentifier(); byte[] PreprocessKey(byte[] key); int GetPaddingSize(CipherMode mode, int feedbackSizeBits); + bool IsValidEphemeralFeedbackSize(int feedbackSizeInBits); } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs index dbcbd0bd8ce..297cd511688 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs @@ -178,8 +178,6 @@ namespace System.Security.Cryptography int feedbackSizeInBits, out int bytesWritten) { - ValidateCFBFeedbackSize(feedbackSizeInBits); - UniversalCryptoTransform transform = _core.CreateCryptoTransform( iv: iv.ToArray(), encrypting: false, @@ -201,8 +199,6 @@ namespace System.Security.Cryptography int feedbackSizeInBits, out int bytesWritten) { - ValidateCFBFeedbackSize(feedbackSizeInBits); - UniversalCryptoTransform transform = _core.CreateCryptoTransform( iv: iv.ToArray(), encrypting: true, @@ -216,26 +212,6 @@ namespace System.Security.Cryptography } } - private void ValidateCFBFeedbackSize(int feedback) - { - if (_core.KeyInPlainText) - { - // CFB8 and CFB128 are valid for bcrypt keys. - if (feedback != 8 && feedback != 128) - { - throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); - } - } - else - { - // only CFB8 is supported for ncrypt keys. - if (feedback != 8) - { - throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); - } - } - } - protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -276,6 +252,11 @@ namespace System.Security.Cryptography return key; } + bool ICngSymmetricAlgorithm.IsValidEphemeralFeedbackSize(int feedbackSizeInBits) + { + return feedbackSizeInBits == 8 || feedbackSizeInBits == 128; + } + private CngSymmetricAlgorithmCore _core; } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs index 09d2b022435..710053fa4e5 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs @@ -179,8 +179,6 @@ namespace System.Security.Cryptography int feedbackSizeInBits, out int bytesWritten) { - ValidateCFBFeedbackSize(feedbackSizeInBits); - UniversalCryptoTransform transform = _core.CreateCryptoTransform( iv: iv.ToArray(), encrypting: false, @@ -202,8 +200,6 @@ namespace System.Security.Cryptography int feedbackSizeInBits, out int bytesWritten) { - ValidateCFBFeedbackSize(feedbackSizeInBits); - UniversalCryptoTransform transform = _core.CreateCryptoTransform( iv: iv.ToArray(), encrypting: true, @@ -222,26 +218,6 @@ namespace System.Security.Cryptography base.Dispose(disposing); } - private void ValidateCFBFeedbackSize(int feedback) - { - if (_core.KeyInPlainText) - { - // CFB8 and CFB164 are valid for bcrypt keys. - if (feedback != 8 && feedback != 64) - { - throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); - } - } - else - { - // only CFB8 is supported for ncrypt keys. - if (feedback != 8) - { - throw new CryptographicException(string.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB)); - } - } - } - byte[] ICngSymmetricAlgorithm.BaseKey { get { return base.Key; } set { base.Key = value; } } int ICngSymmetricAlgorithm.BaseKeySize { get { return base.KeySize; } set { base.KeySize = value; } } @@ -280,6 +256,11 @@ namespace System.Security.Cryptography return key; } + bool ICngSymmetricAlgorithm.IsValidEphemeralFeedbackSize(int feedbackSizeInBits) + { + return feedbackSizeInBits == 8 || feedbackSizeInBits == 64; + } + private CngSymmetricAlgorithmCore _core; } } diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs index 47d4f4b5dd6..94d5defd5cb 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/AesCngTests.cs @@ -102,7 +102,7 @@ namespace System.Security.Cryptography.Cng.Tests [ConditionalFact(nameof(SupportsPersistedSymmetricKeys))] public static void VerifyUnsupportedFeedbackSizeForPersistedCfb() { - SymmetricCngTestHelpers.VerifyOneShotCfbPersistedUnsupportedFeedbackSize( + SymmetricCngTestHelpers.VerifyCfbPersistedUnsupportedFeedbackSize( s_cngAlgorithm, keyName => new AesCng(keyName), notSupportedFeedbackSizeInBits: 128); diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs index 9a3c728efb1..1923c6b2427 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/SymmetricCngTestHelpers.cs @@ -304,12 +304,13 @@ namespace System.Security.Cryptography.Cng.Tests } } - public static void VerifyOneShotCfbPersistedUnsupportedFeedbackSize( + public static void VerifyCfbPersistedUnsupportedFeedbackSize( CngAlgorithm algorithm, Func persistedFunc, int notSupportedFeedbackSizeInBits) { string keyName = Guid.NewGuid().ToString(); + string feedbackSizeString = notSupportedFeedbackSizeInBits.ToString(); // We try to delete the key later which will also dispose of it, so no need // to put this in a using. @@ -319,9 +320,26 @@ namespace System.Security.Cryptography.Cng.Tests { using (SymmetricAlgorithm alg = persistedFunc(keyName)) { + alg.Mode = CipherMode.CFB; + alg.FeedbackSize = notSupportedFeedbackSizeInBits; + alg.Padding = PaddingMode.None; + byte[] destination = new byte[alg.BlockSize / 8]; - Assert.ThrowsAny(() => + CryptographicException ce = Assert.ThrowsAny(() => alg.EncryptCfb(Array.Empty(), destination, PaddingMode.None, notSupportedFeedbackSizeInBits)); + + Assert.Contains(feedbackSizeString, ce.Message); + + ce = Assert.ThrowsAny(() => + alg.DecryptCfb(Array.Empty(), destination, PaddingMode.None, notSupportedFeedbackSizeInBits)); + + Assert.Contains(feedbackSizeString, ce.Message); + + ce = Assert.ThrowsAny(() => alg.CreateDecryptor()); + Assert.Contains(feedbackSizeString, ce.Message); + + ce = Assert.ThrowsAny(() => alg.CreateEncryptor()); + Assert.Contains(feedbackSizeString, ce.Message); } } finally diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 8b6195ed23f..3ab50a56b04 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -142,6 +142,8 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\SignVerify.netcoreapp.cs" /> + new TripleDESCng(keyName), notSupportedFeedbackSizeInBits: 64); From ca18d6699bce0451d7fe1d25a1c21058d5e847fb Mon Sep 17 00:00:00 2001 From: Tlakaelel Axayakatl Ceja Date: Wed, 14 Jul 2021 12:23:32 -0700 Subject: [PATCH 559/926] Update comments on RequiresAssemblyFiles (#55675) The setter of the Message property in RequiresAssemblyFiles was removed, updating the documentation to reflect that change --- .../Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs index 9c90c326948..bb0057181bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs @@ -36,7 +36,7 @@ namespace System.Diagnostics.CodeAnalysis } /// - /// Gets or sets an optional message that contains information about the need for + /// Gets an optional message that contains information about the need for /// assembly files to be on disk. /// public string? Message { get; } From 673bc3564f277f07d9d917cb5a0d491f61c107ff Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 14 Jul 2021 15:42:40 -0400 Subject: [PATCH 560/926] Remove char[] allocation from UniqueId.ToString() (#55652) --- .../src/System/Xml/UniqueId.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/UniqueId.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/UniqueId.cs index de9f654f53d..2a922cce9c5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/UniqueId.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/UniqueId.cs @@ -198,7 +198,7 @@ namespace System.Xml } } - public unsafe int ToCharArray(char[] chars, int offset) + public int ToCharArray(char[] chars, int offset) { int count = CharArrayLength; @@ -213,9 +213,15 @@ namespace System.Xml if (count > chars.Length - offset) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(chars), SR.Format(SR.XmlArrayTooSmallOutput, count))); + ToSpan(chars.AsSpan(offset, count)); + return count; + } + + private unsafe void ToSpan(Span chars) + { if (_s != null) { - _s.CopyTo(0, chars, offset, count); + _s.CopyTo(chars); } else { @@ -223,7 +229,7 @@ namespace System.Xml UnsafeSetInt64(_idLow, bytes); UnsafeSetInt64(_idHigh, &bytes[8]); - fixed (char* _pch = &chars[offset]) + fixed (char* _pch = &chars[0]) { char* pch = _pch; pch[0] = 'u'; @@ -258,8 +264,6 @@ namespace System.Xml UnsafeEncode(bytes[15], &pch[43]); } } - - return count; } public bool TryGetGuid(out Guid guid) @@ -300,17 +304,8 @@ namespace System.Xml return true; } - public unsafe override string ToString() - { - if (_s == null) - { - int length = CharArrayLength; - char[] chars = new char[length]; - ToCharArray(chars, 0); - _s = new string(chars, 0, length); - } - return _s; - } + public override string ToString() => + _s ??= string.Create(CharArrayLength, this, (destination, thisRef) => thisRef.ToSpan(destination)); public static bool operator ==(UniqueId? id1, UniqueId? id2) { From 8de6d056ff427dcf8f432bd7087525558de664dd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 14 Jul 2021 15:44:14 -0400 Subject: [PATCH 561/926] Remove a few duplicated S.R.InteropServices tests (#55650) --- .../InteropServices/Marshal/GetObjectForNativeVariantTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs index d8a6bc398f5..4da69030fde 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs @@ -351,8 +351,8 @@ namespace System.Runtime.InteropServices.Tests yield return new object[] { 128 }; yield return new object[] { 4094 }; yield return new object[] { VT_BSTR_BLOB }; - yield return new object[] { VT_ILLEGALMASKED }; - yield return new object[] { VT_TYPEMASK }; + Assert.Equal(VT_BSTR_BLOB, VT_ILLEGALMASKED); + Assert.Equal(VT_BSTR_BLOB, VT_TYPEMASK); yield return new object[] { VT_VECTOR }; yield return new object[] { 4097 }; yield return new object[] { 8191 }; From 3ec6907d3e565c17219235183c85060e1c2bfafd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 14 Jul 2021 13:40:34 -0700 Subject: [PATCH 562/926] Add tests for generic types/values with the Marshal APIs (#55533) --- .../Marshal/Common/CommonTypes.cs | 6 +++ .../InteropServices/Marshal/OffsetOfTests.cs | 43 ++++++++++++++++++- .../Marshal/PtrToStructureTests.cs | 29 +++++++++---- .../InteropServices/Marshal/SizeOfTests.cs | 28 ++++++++++++ 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/Common/CommonTypes.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/Common/CommonTypes.cs index 4c7a993138a..6a6cab70b73 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/Common/CommonTypes.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/Common/CommonTypes.cs @@ -6,6 +6,12 @@ namespace System.Runtime.InteropServices.Tests.Common [ComVisible(true)] public class GenericClass { } + [StructLayout(LayoutKind.Sequential)] + public class SequentialGenericClass + { + public T field; + } + [ComVisible(true)] public class NonGenericClass { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs index dd3ccea3e2d..d4257da655a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs @@ -224,6 +224,34 @@ namespace System.Runtime.InteropServices.Tests AssertExtensions.Throws(null, () => Marshal.OffsetOf(nameof(NoLayoutPoint.x))); } + [Fact] + public void OffsetOf_Field_ValueType_Generic() + { + Assert.Equal((IntPtr)4, Marshal.OffsetOf>(nameof(Point2.y))); + Assert.Equal((IntPtr)8, Marshal.OffsetOf>(nameof(Point2.y))); + Assert.Equal((IntPtr)4, Marshal.OffsetOf>(nameof(Point2.y))); + Assert.Equal((IntPtr)8, Marshal.OffsetOf>(nameof(Point2.y))); + + // [COMPAT] Non-blittable generic types with value-type generic arguments are supported in OffsetOf since they've always been allowed + // and it likely doesn't meet the bar to break back-compat. + Assert.Equal((IntPtr)1, Marshal.OffsetOf>(nameof(Point2.y))); + Assert.Equal((IntPtr)1, Marshal.OffsetOf>(nameof(Point2.y))); + } + + [Fact] + public void OffsetOf_Field_ReferenceType_Generic() + { + // [COMPAT] Generic types with value-type generic arguments are supported in OffsetOf since they've always been allowed + // and it likely doesn't meet the bar to break back-compat. + Assert.Equal((IntPtr)4, Marshal.OffsetOf>(nameof(Point2Class.y))); + Assert.Equal((IntPtr)8, Marshal.OffsetOf>(nameof(Point2Class.y))); + Assert.Equal((IntPtr)4, Marshal.OffsetOf>(nameof(Point2Class.y))); + Assert.Equal((IntPtr)8, Marshal.OffsetOf>(nameof(Point2Class.y))); + + Assert.Equal((IntPtr)1, Marshal.OffsetOf>(nameof(Point2Class.y))); + Assert.Equal((IntPtr)1, Marshal.OffsetOf>(nameof(Point2Class.y))); + } + public class NonRuntimeType : Type { public override FieldInfo GetField(string name, BindingFlags bindingAttr) @@ -495,6 +523,19 @@ namespace System.Runtime.InteropServices.Tests public short s; // 2 bytes // 6 bytes of padding - }; + } + + struct Point2 + { + public T x; + public T y; + } + + [StructLayout(LayoutKind.Sequential)] + class Point2Class + { + public T x; + public T y; + } #pragma warning restore 169, 649, 618 } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/PtrToStructureTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/PtrToStructureTests.cs index f2a6c2b5d80..dbd31cfa4ad 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/PtrToStructureTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/PtrToStructureTests.cs @@ -138,18 +138,31 @@ namespace System.Runtime.InteropServices.Tests AssertExtensions.Throws("structure", () => Marshal.PtrToStructure((IntPtr)1, null)); } - public static IEnumerable PtrToStructure_GenericClass_TestData() + [Fact] + public void PtrToStructure_AutoLayoutClass_ThrowsArgumentException() { - yield return new object[] { new GenericClass() }; - yield return new object[] { new GenericStruct() }; + AssertExtensions.Throws("structure", () => Marshal.PtrToStructure((IntPtr)1, (object)new NonGenericClass())); + AssertExtensions.Throws("structure", () => Marshal.PtrToStructure((IntPtr)1, new NonGenericClass())); } - [Theory] - [MemberData(nameof(PtrToStructure_GenericClass_TestData))] - public void PtrToStructure_GenericObject_ThrowsArgumentException(object o) + [Fact] + public unsafe void PtrToStructure_GenericLayoutClass_Generic() { - AssertExtensions.Throws("structure", () => Marshal.PtrToStructure((IntPtr)1, o)); - AssertExtensions.Throws("structure", () => Marshal.PtrToStructure((IntPtr)1, o)); + int i = 42; + IntPtr ptr = (IntPtr)(&i); + SequentialGenericClass obj = new SequentialGenericClass(); + Marshal.PtrToStructure(ptr, obj); + Assert.Equal(i, obj.field); + } + + [Fact] + public unsafe void PtrToStructure_GenericLayoutClass() + { + int i = 42; + IntPtr ptr = (IntPtr)(&i); + SequentialGenericClass obj = new SequentialGenericClass(); + Marshal.PtrToStructure(ptr, (object)obj); + Assert.Equal(i, obj.field); } public static IEnumerable PtrToStructure_ObjectNotValueClass_TestData() diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/SizeOfTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/SizeOfTests.cs index 6a009a06437..a91bc349f4c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/SizeOfTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/SizeOfTests.cs @@ -95,6 +95,34 @@ namespace System.Runtime.InteropServices.Tests AssertExtensions.Throws(paramName, () => Marshal.SizeOf(type)); } + [Fact] + public void SizeOf_GenericStruct_Value_NonGeneric() + { + GenericStruct value = default; + Assert.Equal(8, Marshal.SizeOf((object)value)); + } + + [Fact] + public void SizeOf_GenericStruct_Value_Generic() + { + GenericStruct value = default; + Assert.Equal(8, Marshal.SizeOf(value)); + } + + [Fact] + public void SizeOf_GenericClass_Value_NonGeneric() + { + SequentialGenericClass value = new(); + Assert.Equal(4, Marshal.SizeOf((object)value)); + } + + [Fact] + public void SizeOf_GenericClass_Value_Generic() + { + SequentialGenericClass value = new(); + Assert.Equal(4, Marshal.SizeOf(value)); + } + public struct TestStructWithEnumArray { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] From b412aac8757d7ac29ab66910e462649eeaecda18 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 14 Jul 2021 16:32:40 -0500 Subject: [PATCH 563/926] Add property ordering to source gen (#55662) --- .../gen/JsonSourceGenerator.Parser.cs | 19 +++++++++++++++++++ .../gen/PropertyGenerationSpec.cs | 5 +++++ .../ContextClasses.cs | 1 + .../MetadataAndSerializationContextTests.cs | 2 ++ .../MetadataContextTests.cs | 3 +++ .../MixedModeContextTests.cs | 2 ++ .../RealWorldContextTests.cs | 8 ++++++++ .../SerializationContextTests.cs | 4 ++++ .../TestClasses.cs | 12 ++++++++++++ .../Serialization/PropertyOrderTests.cs | 9 ++++++--- 10 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index cefb9de2d71..727d80c6b10 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -34,6 +34,8 @@ namespace System.Text.Json.SourceGeneration private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute"; + private const string JsonPropertyOrderAttributeFullName = "System.Text.Json.Serialization.JsonPropertyOrderAttribute"; + private readonly GeneratorExecutionContext _executionContext; private readonly MetadataLoadContextInternal _metadataLoadContext; @@ -606,6 +608,8 @@ namespace System.Text.Json.SourceGeneration BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + bool propertyOrderSpecified = false; + for (Type? currentType = type; currentType != null; currentType = currentType.BaseType) { foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags)) @@ -620,6 +624,7 @@ namespace System.Text.Json.SourceGeneration PropertyGenerationSpec spec = GetPropertyGenerationSpec(propertyInfo, isVirtual, generationMode); CacheMember(spec, ref propGenSpecList, ref ignoredMembers); + propertyOrderSpecified |= spec.Order != 0; } foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags)) @@ -631,8 +636,14 @@ namespace System.Text.Json.SourceGeneration PropertyGenerationSpec spec = GetPropertyGenerationSpec(fieldInfo, isVirtual: false, generationMode); CacheMember(spec, ref propGenSpecList, ref ignoredMembers); + propertyOrderSpecified |= spec.Order != 0; } } + + if (propertyOrderSpecified) + { + propGenSpecList.Sort((p1, p2) => p1.Order.CompareTo(p2.Order)); + } } typeMetadata.Initialize( @@ -697,6 +708,7 @@ namespace System.Text.Json.SourceGeneration bool foundDesignTimeCustomConverter = false; string? converterInstantiationLogic = null; + int order = 0; foreach (CustomAttributeData attributeData in attributeDataList) { @@ -745,6 +757,12 @@ namespace System.Text.Json.SourceGeneration // Null check here is done at runtime within JsonSerializer. } break; + case JsonPropertyOrderAttributeFullName: + { + IList ctorArgs = attributeData.ConstructorArguments; + order = (int)ctorArgs[0].Value; + } + break; default: break; } @@ -839,6 +857,7 @@ namespace System.Text.Json.SourceGeneration SetterIsVirtual = setterIsVirtual, DefaultIgnoreCondition = ignoreCondition, NumberHandling = numberHandling, + Order = order, HasJsonInclude = hasJsonInclude, TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode), DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}", diff --git a/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs b/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs index 5d52ae2e355..9d05fdf297c 100644 --- a/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs @@ -63,6 +63,11 @@ namespace System.Text.Json.SourceGeneration /// public JsonNumberHandling? NumberHandling { get; init; } + /// + /// The serialization order of the property. + /// + public int Order { get; init; } + /// /// Whether the property has the JsonIncludeAttribute. If so, non-public accessors can be used for (de)serialziation. /// diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs index d07a96cd6f2..c4ade896e1d 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs @@ -20,6 +20,7 @@ namespace System.Text.Json.SourceGeneration.Tests public JsonTypeInfo MyType { get; } public JsonTypeInfo MyType2 { get; } public JsonTypeInfo MyTypeWithCallbacks { get; } + public JsonTypeInfo MyTypeWithPropertyOrdering { get; } public JsonTypeInfo MyIntermediateType { get; } public JsonTypeInfo HighLowTempsImmutable { get; } public JsonTypeInfo MyNestedClass { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs index 3c67d58bfd3..94f77f5bc0c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs @@ -18,6 +18,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType))] [JsonSerializable(typeof(MyType2))] [JsonSerializable(typeof(MyTypeWithCallbacks))] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] @@ -47,6 +48,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.NotNull(MetadataAndSerializationContext.Default.MyType.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.MyType2.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithCallbacks.Serialize); + Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithPropertyOrdering.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.MyIntermediateType.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsImmutable.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedClass.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index 0b253d2ec64..101a53e0224 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -17,6 +17,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -67,6 +68,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType))] [JsonSerializable(typeof(MyType2))] [JsonSerializable(typeof(MyTypeWithCallbacks))] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] @@ -96,6 +98,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.Null(MetadataContext.Default.MyType.Serialize); Assert.Null(MetadataContext.Default.MyType2.Serialize); Assert.Null(MetadataContext.Default.MyTypeWithCallbacks.Serialize); + Assert.Null(MetadataContext.Default.MyTypeWithPropertyOrdering.Serialize); Assert.Null(MetadataContext.Default.MyIntermediateType.Serialize); Assert.Null(MetadataContext.Default.HighLowTempsImmutable.Serialize); Assert.Null(MetadataContext.Default.MyNestedClass.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index b4f64091ca5..aaf1f17fc22 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -17,6 +17,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Default)] [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -45,6 +46,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.NotNull(MixedModeContext.Default.MyType.Serialize); Assert.NotNull(MixedModeContext.Default.MyType2.Serialize); Assert.NotNull(MixedModeContext.Default.MyTypeWithCallbacks.Serialize); + Assert.NotNull(MixedModeContext.Default.MyTypeWithPropertyOrdering.Serialize); Assert.NotNull(MixedModeContext.Default.MyIntermediateType.Serialize); Assert.Null(MixedModeContext.Default.HighLowTempsImmutable.Serialize); Assert.NotNull(MixedModeContext.Default.MyNestedClass.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 83902a98966..0017021fb4e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -561,5 +561,13 @@ namespace System.Text.Json.SourceGeneration.Tests JsonTestHelper.AssertJsonEqual(expectedJson, Encoding.UTF8.GetString(ms.ToArray())); } + + [Fact] + public void PropertyOrdering() + { + MyTypeWithPropertyOrdering obj = new(); + string json = JsonSerializer.Serialize(obj, DefaultContext.MyTypeWithPropertyOrdering); + Assert.Equal("{\"C\":0,\"B\":0,\"A\":0}", json); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index e58856ee97a..0fb3c4e9dde 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -18,6 +18,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType))] [JsonSerializable(typeof(MyType2))] [JsonSerializable(typeof(MyTypeWithCallbacks))] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] @@ -40,6 +41,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -63,6 +65,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyTypeWithCallbacks), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -97,6 +100,7 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.NotNull(SerializationContext.Default.MyType.Serialize); Assert.NotNull(SerializationContext.Default.MyType2.Serialize); Assert.NotNull(SerializationContext.Default.MyTypeWithCallbacks.Serialize); + Assert.NotNull(SerializationContext.Default.MyTypeWithPropertyOrdering.Serialize); Assert.NotNull(SerializationContext.Default.MyIntermediateType.Serialize); Assert.NotNull(SerializationContext.Default.HighLowTempsImmutable.Serialize); Assert.NotNull(SerializationContext.Default.MyNestedClass.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs index e43e1bb2b9e..ff110fe3779 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs @@ -117,6 +117,18 @@ namespace System.Text.Json.SourceGeneration.Tests void IJsonOnSerialized.OnSerialized() => MyProperty = "After"; } + public class MyTypeWithPropertyOrdering + { + public int B { get; set; } + + [JsonPropertyOrder(1)] + public int A { get; set; } + + [JsonPropertyOrder(-1)] + [JsonInclude] + public int C = 0; + } + public class JsonMessage { public string Message { get; set; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs index 07b16caa00d..c5f4d73a9b6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyOrderTests.cs @@ -15,7 +15,8 @@ namespace System.Text.Json.Serialization.Tests public int A { get; set; } [JsonPropertyOrder(-1)] - public int C { get; set; } + [JsonInclude] + public int C = 0; } [Fact] @@ -28,7 +29,8 @@ namespace System.Text.Json.Serialization.Tests private class MyPoco_After { [JsonPropertyOrder(2)] - public int C { get; set; } + [JsonInclude] + public int C = 0; public int B { get; set; } public int D { get; set; } @@ -48,7 +50,8 @@ namespace System.Text.Json.Serialization.Tests private class MyPoco_Before { [JsonPropertyOrder(-1)] - public int C { get; set; } + [JsonInclude] + public int C = 0; public int B { get; set; } public int D { get; set; } From 8a34e765c07c792ff6d9a129d8ab58405be24b06 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Wed, 14 Jul 2021 14:53:09 -0700 Subject: [PATCH 564/926] "==0" optimization in Boolean logic #13573 (#49548) * Equals to 0 optimization in Boolean logic * Limit bool optimization to Integral return type only * Use the updated flowList:setEdgeWeights method with the 3rd parameter * Skip bool optimization for cases that require NOT transformation * Skip bool optimization when the third block GT_RETURN is not CNT_INT int * format patch * Added more bool optimization cases * format patch * Refactored setting fold type and comparison type to fix jitstress error * format patch * Refactored common codes for conditional block and return block boolean optimizations * format patch * Unit test changed to remove EH handling and add return value checks * Unit test: add back test cases for ANDing and NE cases * Made OptBoolsDsc struct to pass it off to the helper methods. * format patch * Changed to substructure OptTestInfo within OptBoolsDisc * Cleaned up tree variables in OptBoolsDsc struct * Moved some methods for Boolean Optimization to OptBoolsDsc struct * Moved all private methods for Boolean Optimization to OptBoolsDsc struct * Boolean Optimization: Handled code review feedback * Optimize bools: hoisted jump destination check to optOptimizeBools() and added test cases * format patch * Moved initialization to OptBoolsDsc constructor * format patch Co-authored-by: Julie Lee --- src/coreclr/jit/compiler.h | 6 +- src/coreclr/jit/optimizer.cpp | 1195 ++++++++++++----- .../JIT/opt/OptimizeBools/optboolsreturn.cs | 182 +++ .../opt/OptimizeBools/optboolsreturn.csproj | 13 + 4 files changed, 1052 insertions(+), 344 deletions(-) create mode 100644 src/tests/JIT/opt/OptimizeBools/optboolsreturn.cs create mode 100644 src/tests/JIT/opt/OptimizeBools/optboolsreturn.csproj diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9027a5d2335..2cf09127f67 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -79,6 +79,7 @@ class FgStack; // defined in fgbasic.cpp class Instrumentor; // defined in fgprofile.cpp class SpanningTreeVisitor; // defined in fgprofile.cpp class CSE_DataFlow; // defined in OptCSE.cpp +class OptBoolsDsc; // defined in optimizer.cpp #ifdef DEBUG struct IndentStack; #endif @@ -6350,11 +6351,6 @@ private: public: void optOptimizeBools(); -private: - GenTree* optIsBoolCond(GenTree* condBranch, GenTree** compPtr, bool* boolPtr); -#ifdef DEBUG - void optOptimizeBoolsGcStress(BasicBlock* condBlock); -#endif public: PhaseStatus optInvertLoops(); // Invert loops so they're entered at top and tested at bottom. PhaseStatus optOptimizeLayout(); // Optimize the BasicBlock layout of the method diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 748d5feabb5..12916baa06e 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -7609,46 +7609,762 @@ bool Compiler::optIsRangeCheckRemovable(GenTree* tree) return true; } -/****************************************************************************** - * - * Replace x==null with (x|x)==0 if x is a GC-type. - * This will stress code-gen and the emitter to make sure they support such trees. - */ +//----------------------------------------------------------------------------- +// OptTestInfo: Member of OptBoolsDsc struct used to test if a GT_JTRUE or GT_RETURN node +// is a boolean comparison +// +struct OptTestInfo +{ + GenTree* testTree; // The root node of basic block with GT_JTRUE or GT_RETURN type to check boolean condition on + GenTree* compTree; // The compare node (i.e. GT_EQ or GT_NE node) of the testTree + bool isBool; // If the compTree is boolean expression +}; + +//----------------------------------------------------------------------------- +// OptBoolsDsc: Descriptor used for Boolean Optimization +// +class OptBoolsDsc +{ +public: + OptBoolsDsc(BasicBlock* b1, BasicBlock* b2, Compiler* comp) + { + m_b1 = b1; + m_b2 = b2; + m_b3 = nullptr; + m_comp = comp; + } + +private: + BasicBlock* m_b1; // The first basic block with the BBJ_COND conditional jump type + BasicBlock* m_b2; // The next basic block of m_b1. Either BBJ_COND or BBJ_RETURN type + BasicBlock* m_b3; // m_b1->bbJumpDest. Null if m_b2 is not a return block. + + Compiler* m_comp; // The pointer to the Compiler instance + + OptTestInfo m_testInfo1; // The first test info + OptTestInfo m_testInfo2; // The second test info + GenTree* m_t3; // The root node of the first statement of m_b3 + + GenTree* m_c1; // The first operand of m_testInfo1.compTree + GenTree* m_c2; // The first operand of m_testInfo2.compTree + + bool m_sameTarget; // if m_b1 and m_b2 jumps to the same destination + + genTreeOps m_foldOp; // The fold operator (e.g., GT_AND or GT_OR) + var_types m_foldType; // The type of the folded tree + genTreeOps m_cmpOp; // The comparison operator (e.g., GT_EQ or GT_NE) + +public: + bool optOptimizeBoolsCondBlock(); + bool optOptimizeBoolsReturnBlock(BasicBlock* b3); +#ifdef DEBUG + void optOptimizeBoolsGcStress(); +#endif + +private: + Statement* optOptimizeBoolsChkBlkCond(); + GenTree* optIsBoolComp(OptTestInfo* pOptTest); + bool optOptimizeBoolsChkTypeCostCond(); + void optOptimizeBoolsUpdateTrees(); +}; + +//----------------------------------------------------------------------------- +// optOptimizeBoolsCondBlock: Optimize boolean when bbJumpKind of both m_b1 and m_b2 are BBJ_COND +// +// Returns: +// true if boolean optimization is done and m_b1 and m_b2 are folded into m_b1, else false. +// +// Notes: +// m_b1 and m_b2 are set on entry. +// +// Case 1: if b1.bbJumpDest == b2.bbJumpDest, it transforms +// B1 : brtrue(t1, Bx) +// B2 : brtrue(t2, Bx) +// B3 : +// to +// B1 : brtrue(t1|t2, BX) +// B3 : +// +// For example, (x == 0 && y == 0 && z == 0) generates +// B1: GT_JTRUE (BBJ_COND), jump to B4 +// B2: GT_JTRUE (BBJ_COND), jump to B4 +// B3: GT_RETURN (BBJ_RETURN) +// B4: GT_RETURN (BBJ_RETURN) +// and B1 and B2 are folded into B1: +// B1: GT_JTRUE (BBJ_COND), jump to B4 +// B3: GT_RETURN (BBJ_RETURN) +// B4: GT_RETURN (BBJ_RETURN) +// +// Case 2: if B1.bbJumpDest == B2->bbNext, it transforms +// B1 : brtrue(t1, B3) +// B2 : brtrue(t2, Bx) +// B3 : +// to +// B1 : brtrue((!t1) && t2, Bx) +// B3 : +// +bool OptBoolsDsc::optOptimizeBoolsCondBlock() +{ + assert(m_b1 != nullptr && m_b2 != nullptr && m_b3 == nullptr); + + // Check if m_b1 and m_b2 jump to the same target and get back pointers to m_testInfo1 and t2 tree nodes + + m_t3 = nullptr; + + // Check if m_b1 and m_b2 have the same bbJumpDest + + if (m_b1->bbJumpDest == m_b2->bbJumpDest) + { + // Given the following sequence of blocks : + // B1: brtrue(t1, BX) + // B2: brtrue(t2, BX) + // B3: + // we will try to fold it to : + // B1: brtrue(t1|t2, BX) + // B3: + + m_sameTarget = true; + } + else if (m_b1->bbJumpDest == m_b2->bbNext) + { + // Given the following sequence of blocks : + // B1: brtrue(t1, B3) + // B2: brtrue(t2, BX) + // B3: + // we will try to fold it to : + // B1: brtrue((!t1)&&t2, BX) + // B3: + + m_sameTarget = false; + } + else + { + return false; + } + + Statement* const s1 = optOptimizeBoolsChkBlkCond(); + if (s1 == nullptr) + { + return false; + } + + // Find the branch conditions of m_b1 and m_b2 + + m_c1 = optIsBoolComp(&m_testInfo1); + if (m_c1 == nullptr) + { + return false; + } + + m_c2 = optIsBoolComp(&m_testInfo2); + if (m_c2 == nullptr) + { + return false; + } + + // Find the type and cost conditions of m_testInfo1 and m_testInfo2 + + if (!optOptimizeBoolsChkTypeCostCond()) + { + return false; + } + + // Get the fold operator and the comparison operator + + genTreeOps foldOp; + genTreeOps cmpOp; + var_types foldType = m_c1->TypeGet(); + if (varTypeIsGC(foldType)) + { + foldType = TYP_I_IMPL; + } + + assert(m_testInfo1.compTree->gtOper == GT_EQ || m_testInfo1.compTree->gtOper == GT_NE); + + if (m_sameTarget) + { + // Both conditions must be the same + + if (m_testInfo1.compTree->gtOper != m_testInfo2.compTree->gtOper) + { + return false; + } + + if (m_testInfo1.compTree->gtOper == GT_EQ) + { + // t1:c1==0 t2:c2==0 ==> Branch to BX if either value is 0 + // So we will branch to BX if (c1&c2)==0 + + foldOp = GT_AND; + cmpOp = GT_EQ; + } + else + { + // t1:c1!=0 t2:c2!=0 ==> Branch to BX if either value is non-0 + // So we will branch to BX if (c1|c2)!=0 + + foldOp = GT_OR; + cmpOp = GT_NE; + } + } + else + { + // The m_b1 condition must be the reverse of the m_b2 condition because the only operators + // that we will see here are GT_EQ and GT_NE. So, if they are not the same, we have one of each. + + if (m_testInfo1.compTree->gtOper == m_testInfo2.compTree->gtOper) + { + return false; + } + + if (m_testInfo1.compTree->gtOper == GT_EQ) + { + // t1:c1==0 t2:c2!=0 ==> Branch to BX if both values are non-0 + // So we will branch to BX if (c1&c2)!=0 + + foldOp = GT_AND; + cmpOp = GT_NE; + } + else + { + // t1:c1!=0 t2:c2==0 ==> Branch to BX if both values are 0 + // So we will branch to BX if (c1|c2)==0 + + foldOp = GT_OR; + cmpOp = GT_EQ; + } + } + + // Anding requires both values to be 0 or 1 + + if ((foldOp == GT_AND) && (!m_testInfo1.isBool || !m_testInfo2.isBool)) + { + return false; + } + + // + // Now update the trees + // + + m_foldOp = foldOp; + m_foldType = foldType; + m_cmpOp = cmpOp; + + optOptimizeBoolsUpdateTrees(); #ifdef DEBUG + if (m_comp->verbose) + { + printf("Folded %sboolean conditions of " FMT_BB " and " FMT_BB " to :\n", m_c2->OperIsLeaf() ? "" : "non-leaf ", + m_b1->bbNum, m_b2->bbNum); + m_comp->gtDispStmt(s1); + printf("\n"); + } +#endif -void Compiler::optOptimizeBoolsGcStress(BasicBlock* condBlock) + // Return true to continue the bool optimization for the rest of the BB chain + return true; +} + +//----------------------------------------------------------------------------- +// optOptimizeBoolsChkBlkCond: Checks block conditions if it can be boolean optimized +// +// Return: +// If all conditions pass, returns the last statement of m_b1, else return nullptr. +// +// Notes: +// This method checks if the second (and third block for cond/return/return case) contains only one statement, +// and checks if tree operators are of the right type, e.g, GT_JTRUE, GT_RETURN. +// +// On entry, m_b1, m_b2 are set and m_b3 is set for cond/return/return case. +// If it passes all the conditions, m_testInfo1.testTree, m_testInfo2.testTree and m_t3 are set +// to the root nodes of m_b1, m_b2 and m_b3 each. +// SameTarget is also updated to true if m_b1 and m_b2 jump to the same destination. +// +Statement* OptBoolsDsc::optOptimizeBoolsChkBlkCond() { - if (!compStressCompile(STRESS_OPT_BOOLS_GC, 20)) + assert(m_b1 != nullptr && m_b2 != nullptr); + + bool optReturnBlock = false; + if (m_b3 != nullptr) + { + optReturnBlock = true; + } + + // Find the block conditions of m_b1 and m_b2 + + if (m_b2->countOfInEdges() > 1 || (optReturnBlock && m_b3->countOfInEdges() > 1)) + { + return nullptr; + } + + // Find the condition for the first block + + Statement* s1 = m_b1->lastStmt(); + + GenTree* testTree1 = s1->GetRootNode(); + assert(testTree1->gtOper == GT_JTRUE); + + // The second and the third block must contain a single statement + + Statement* s2 = m_b2->firstStmt(); + if (s2->GetPrevStmt() != s2) + { + return nullptr; + } + + GenTree* testTree2 = s2->GetRootNode(); + + if (!optReturnBlock) + { + assert(testTree2->gtOper == GT_JTRUE); + } + else + { + if (testTree2->gtOper != GT_RETURN) + { + return nullptr; + } + + Statement* s3 = m_b3->firstStmt(); + if (s3->GetPrevStmt() != s3) + { + return nullptr; + } + + GenTree* testTree3 = s3->GetRootNode(); + if (testTree3->gtOper != GT_RETURN) + { + return nullptr; + } + + if (!varTypeIsIntegral(testTree2->TypeGet()) || !varTypeIsIntegral(testTree3->TypeGet())) + { + return nullptr; + } + + // The third block is Return with "CNS_INT int 0/1" + if (testTree3->AsOp()->gtOp1->gtOper != GT_CNS_INT) + { + return nullptr; + } + + if (testTree3->AsOp()->gtOp1->gtType != TYP_INT) + { + return nullptr; + } + + m_t3 = testTree3; + } + + m_testInfo1.testTree = testTree1; + m_testInfo2.testTree = testTree2; + + return s1; +} + +//----------------------------------------------------------------------------- +// optOptimizeBoolsChkTypeCostCond: Checks if type conditions meet the folding condition, and +// if cost to fold is not too expensive +// +// Return: +// True if it meets type conditions and cost conditions. Else false. +// +bool OptBoolsDsc::optOptimizeBoolsChkTypeCostCond() +{ + assert(m_testInfo1.compTree->OperIs(GT_EQ, GT_NE) && m_testInfo1.compTree->AsOp()->gtOp1 == m_c1); + assert(m_testInfo2.compTree->OperIs(GT_EQ, GT_NE) && m_testInfo2.compTree->AsOp()->gtOp1 == m_c2); + + // + // Leave out floats where the bit-representation is more complicated + // - there are two representations for 0. + // + if (varTypeIsFloating(m_c1->TypeGet()) || varTypeIsFloating(m_c2->TypeGet())) + { + return false; + } + + // Make sure the types involved are of the same sizes + if (genTypeSize(m_c1->TypeGet()) != genTypeSize(m_c2->TypeGet())) + { + return false; + } + if (genTypeSize(m_testInfo1.compTree->TypeGet()) != genTypeSize(m_testInfo2.compTree->TypeGet())) + { + return false; + } +#ifdef TARGET_ARMARCH + // Skip the small operand which we cannot encode. + if (varTypeIsSmall(m_c1->TypeGet())) + return false; +#endif + // The second condition must not contain side effects + + if (m_c2->gtFlags & GTF_GLOB_EFFECT) + { + return false; + } + + // The second condition must not be too expensive + + m_comp->gtPrepareCost(m_c2); + + if (m_c2->GetCostEx() > 12) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// optOptimizeBoolsUpdateTrees: Fold the trees based on fold type and comparison type, +// update the edges, unlink removed blocks and update loop table +// +void OptBoolsDsc::optOptimizeBoolsUpdateTrees() +{ + assert(m_b1 != nullptr && m_b2 != nullptr); + + bool optReturnBlock = false; + if (m_b3 != nullptr) + { + optReturnBlock = true; + } + + assert(m_foldOp != NULL && m_foldType != NULL && m_c1 != nullptr && m_c2 != nullptr); + + GenTree* cmpOp1 = m_comp->gtNewOperNode(m_foldOp, m_foldType, m_c1, m_c2); + if (m_testInfo1.isBool && m_testInfo2.isBool) + { + // When we 'OR'/'AND' two booleans, the result is boolean as well + cmpOp1->gtFlags |= GTF_BOOLEAN; + } + + GenTree* t1Comp = m_testInfo1.compTree; + t1Comp->SetOper(m_cmpOp); + t1Comp->AsOp()->gtOp1 = cmpOp1; + t1Comp->AsOp()->gtOp2->gtType = m_foldType; // Could have been varTypeIsGC() + if (optReturnBlock) + { + // Update tree when m_b1 is BBJ_COND and m_b2 and m_b3 are GT_RETURN (BBJ_RETURN) + t1Comp->AsOp()->gtOp2->AsIntCon()->gtIconVal = 0; + m_testInfo1.testTree->gtOper = GT_RETURN; + m_testInfo1.testTree->gtType = m_testInfo2.testTree->gtType; + + // Update the return count of flow graph + assert(m_comp->fgReturnCount >= 2); + --m_comp->fgReturnCount; + } + +#if FEATURE_SET_FLAGS + // For comparisons against zero we will have the GTF_SET_FLAGS set + // and this can cause an assert to fire in fgMoveOpsLeft(GenTree* tree) + // during the CSE phase. + // + // So make sure to clear any GTF_SET_FLAGS bit on these operations + // as they are no longer feeding directly into a comparisons against zero + + // Make sure that the GTF_SET_FLAGS bit is cleared. + // Fix 388436 ARM JitStress WP7 + m_c1->gtFlags &= ~GTF_SET_FLAGS; + m_c2->gtFlags &= ~GTF_SET_FLAGS; + + // The new top level node that we just created does feed directly into + // a comparison against zero, so set the GTF_SET_FLAGS bit so that + // we generate an instruction that sets the flags, which allows us + // to omit the cmp with zero instruction. + + // Request that the codegen for cmpOp1 sets the condition flags + // when it generates the code for cmpOp1. + // + cmpOp1->gtRequestSetFlags(); +#endif + + if (!optReturnBlock) + { + // Update edges if m_b1: BBJ_COND and m_b2: BBJ_COND + + flowList* edge1 = m_comp->fgGetPredForBlock(m_b1->bbJumpDest, m_b1); + flowList* edge2; + + if (m_sameTarget) + { + edge2 = m_comp->fgGetPredForBlock(m_b2->bbJumpDest, m_b2); + } + else + { + edge2 = m_comp->fgGetPredForBlock(m_b2->bbNext, m_b2); + + m_comp->fgRemoveRefPred(m_b1->bbJumpDest, m_b1); + + m_b1->bbJumpDest = m_b2->bbJumpDest; + + m_comp->fgAddRefPred(m_b2->bbJumpDest, m_b1); + } + + assert(edge1 != nullptr); + assert(edge2 != nullptr); + + BasicBlock::weight_t edgeSumMin = edge1->edgeWeightMin() + edge2->edgeWeightMin(); + BasicBlock::weight_t edgeSumMax = edge1->edgeWeightMax() + edge2->edgeWeightMax(); + if ((edgeSumMax >= edge1->edgeWeightMax()) && (edgeSumMax >= edge2->edgeWeightMax())) + { + edge1->setEdgeWeights(edgeSumMin, edgeSumMax, m_b1->bbJumpDest); + } + else + { + edge1->setEdgeWeights(BB_ZERO_WEIGHT, BB_MAX_WEIGHT, m_b1->bbJumpDest); + } + } + + /* Modify the target of the conditional jump and update bbRefs and bbPreds */ + + if (optReturnBlock) + { + m_b1->bbJumpDest = nullptr; + m_b1->bbJumpKind = BBJ_RETURN; +#ifdef DEBUG + m_b1->bbJumpSwt = m_b2->bbJumpSwt; +#endif + assert(m_b2->bbJumpKind == BBJ_RETURN); + assert(m_b1->bbNext == m_b2); + assert(m_b3 != nullptr); + } + else + { + assert(m_b1->bbJumpKind == BBJ_COND); + assert(m_b2->bbJumpKind == BBJ_COND); + assert(m_b1->bbJumpDest == m_b2->bbJumpDest); + assert(m_b1->bbNext == m_b2); + assert(m_b2->bbNext != nullptr); + } + + if (!optReturnBlock) + { + // Update bbRefs and bbPreds + // + // Replace pred 'm_b2' for 'm_b2->bbNext' with 'm_b1' + // Remove pred 'm_b2' for 'm_b2->bbJumpDest' + m_comp->fgReplacePred(m_b2->bbNext, m_b2, m_b1); + m_comp->fgRemoveRefPred(m_b2->bbJumpDest, m_b2); + } + + // Get rid of the second block + + m_comp->fgUnlinkBlock(m_b2); + m_b2->bbFlags |= BBF_REMOVED; + // If m_b2 was the last block of a try or handler, update the EH table. + m_comp->ehUpdateForDeletedBlock(m_b2); + + if (optReturnBlock) + { + // Get rid of the third block + m_comp->fgUnlinkBlock(m_b3); + m_b3->bbFlags |= BBF_REMOVED; + // If m_b3 was the last block of a try or handler, update the EH table. + m_comp->ehUpdateForDeletedBlock(m_b3); + } + + // Update loop table + m_comp->fgUpdateLoopsAfterCompacting(m_b1, m_b2); + if (optReturnBlock) + { + m_comp->fgUpdateLoopsAfterCompacting(m_b1, m_b3); + } +} + +//----------------------------------------------------------------------------- +// optOptimizeBoolsReturnBlock: Optimize boolean when m_b1 is BBJ_COND and m_b2 and m_b3 are BBJ_RETURN +// +// Arguments: +// b3: Pointer to basic block b3 +// +// Returns: +// true if boolean optimization is done and m_b1, m_b2 and m_b3 are folded into m_b1, else false. +// +// Notes: +// m_b1, m_b2 and m_b3 of OptBoolsDsc are set on entry. +// +// if B1.bbJumpDest == b3, it transforms +// B1 : brtrue(t1, B3) +// B2 : ret(t2) +// B3 : ret(0) +// to +// B1 : ret((!t1) && t2) +// +// For example, (x==0 && y==0) generates: +// B1: GT_JTRUE (BBJ_COND), jumps to B3 +// B2: GT_RETURN (BBJ_RETURN) +// B3: GT_RETURN (BBJ_RETURN), +// and it is folded into +// B1: GT_RETURN (BBJ_RETURN) +// +bool OptBoolsDsc::optOptimizeBoolsReturnBlock(BasicBlock* b3) +{ + assert(m_b1 != nullptr && m_b2 != nullptr); + + // m_b3 is set for cond/return/return case + m_b3 = b3; + + m_sameTarget = false; + Statement* const s1 = optOptimizeBoolsChkBlkCond(); + if (s1 == nullptr) + { + return false; + } + + // Find the branch conditions of m_b1 and m_b2 + + m_c1 = optIsBoolComp(&m_testInfo1); + if (m_c1 == nullptr) + { + return false; + } + + m_c2 = optIsBoolComp(&m_testInfo2); + if (m_c2 == nullptr) + { + return false; + } + + // Find the type and cost conditions of m_testInfo1 and m_testInfo2 + + if (!optOptimizeBoolsChkTypeCostCond()) + { + return false; + } + + // Get the fold operator (m_foldOp, e.g., GT_OR/GT_AND) and + // the comparison operator (m_cmpOp, e.g., GT_EQ/GT_NE) + + var_types foldType = m_c1->TypeGet(); + if (varTypeIsGC(foldType)) + { + foldType = TYP_I_IMPL; + } + m_foldType = foldType; + + m_foldOp = GT_NONE; + m_cmpOp = GT_NONE; + + genTreeOps foldOp; + genTreeOps cmpOp; + + ssize_t it1val = m_testInfo1.compTree->AsOp()->gtOp2->AsIntCon()->gtIconVal; + ssize_t it2val = m_testInfo2.compTree->AsOp()->gtOp2->AsIntCon()->gtIconVal; + ssize_t it3val = m_t3->AsOp()->gtOp1->AsIntCon()->gtIconVal; + + if ((m_testInfo1.compTree->gtOper == GT_NE && m_testInfo2.compTree->gtOper == GT_EQ) && + (it1val == 0 && it2val == 0 && it3val == 0)) + { + // Case: x == 0 && y == 0 + // t1:c1!=0 t2:c2==0 t3:c3==0 + // ==> true if (c1|c2)==0 + foldOp = GT_OR; + cmpOp = GT_EQ; + } + else if ((m_testInfo1.compTree->gtOper == GT_EQ && m_testInfo2.compTree->gtOper == GT_NE) && + (it1val == 0 && it2val == 0 && it3val == 0)) + { + // Case: x == 1 && y ==1 + // t1:c1!=1 t2:c2==1 t3:c3==0 is reversed from optIsBoolComp() to: t1:c1==0 t2:c2!=0 t3:c3==0 + // ==> true if (c1&c2)!=0 + foldOp = GT_AND; + cmpOp = GT_NE; + } + else if ((m_testInfo1.compTree->gtOper == GT_EQ && m_testInfo2.compTree->gtOper == GT_EQ) && + (it1val == 0 && it2val == 0 && it3val == 1)) + { + // Case: x == 0 || y == 0 + // t1:c1==0 t2:c2==0 t3:c3==1 + // ==> true if (c1&c2)==0 + foldOp = GT_AND; + cmpOp = GT_EQ; + } + else if ((m_testInfo1.compTree->gtOper == GT_NE && m_testInfo2.compTree->gtOper == GT_NE) && + (it1val == 0 && it2val == 0 && it3val == 1)) + { + // Case: x == 1 || y == 1 + // t1:c1==1 t2:c2==1 t3:c3==1 is reversed from optIsBoolComp() to: t1:c1!=0 t2:c2!=0 t3:c3==1 + // ==> true if (c1|c2)!=0 + foldOp = GT_OR; + cmpOp = GT_NE; + } + else + { + // Require NOT operation for operand(s). Do Not fold. + return false; + } + + if ((foldOp == GT_AND || cmpOp == GT_NE) && (!m_testInfo1.isBool || !m_testInfo2.isBool)) + { + // x == 1 && y == 1: Skip cases where x or y is greather than 1, e.g., x=3, y=1 + // x == 0 || y == 0: Skip cases where x and y have opposite bits set, e.g., x=2, y=1 + // x == 1 || y == 1: Skip cases where either x or y is greater than 1, e.g., x=2, y=0 + return false; + } + + m_foldOp = foldOp; + m_cmpOp = cmpOp; + + // Now update the trees + + optOptimizeBoolsUpdateTrees(); + +#ifdef DEBUG + if (m_comp->verbose) + { + printf("Folded %sboolean conditions of " FMT_BB ", " FMT_BB " and " FMT_BB " to :\n", + m_c2->OperIsLeaf() ? "" : "non-leaf ", m_b1->bbNum, m_b2->bbNum, m_b3->bbNum); + m_comp->gtDispStmt(s1); + printf("\n"); + } +#endif + + // Return true to continue the bool optimization for the rest of the BB chain + return true; +} + +//----------------------------------------------------------------------------- +// optOptimizeBoolsGcStress: Replace x==null with (x|x)==0 if x is a GC-type. +// This will stress code-gen and the emitter to make sure they support such trees. +// +#ifdef DEBUG + +void OptBoolsDsc::optOptimizeBoolsGcStress() +{ + if (!m_comp->compStressCompile(m_comp->STRESS_OPT_BOOLS_GC, 20)) { return; } - noway_assert(condBlock->bbJumpKind == BBJ_COND); - GenTree* cond = condBlock->lastStmt()->GetRootNode(); + assert(m_b1->bbJumpKind == BBJ_COND); + GenTree* cond = m_b1->lastStmt()->GetRootNode(); - noway_assert(cond->gtOper == GT_JTRUE); + assert(cond->gtOper == GT_JTRUE); - bool isBool; - GenTree* relop; + OptTestInfo test; + test.testTree = cond; - GenTree* comparand = optIsBoolCond(cond, &relop, &isBool); + GenTree* comparand = optIsBoolComp(&test); if (comparand == nullptr || !varTypeIsGC(comparand->TypeGet())) { return; } + GenTree* relop = test.compTree; + bool isBool = test.isBool; if (comparand->gtFlags & (GTF_ASG | GTF_CALL | GTF_ORDER_SIDEEFF)) { return; } - GenTree* comparandClone = gtCloneExpr(comparand); + GenTree* comparandClone = m_comp->gtCloneExpr(comparand); noway_assert(relop->AsOp()->gtOp1 == comparand); - genTreeOps oper = compStressCompile(STRESS_OPT_BOOLS_GC, 50) ? GT_OR : GT_AND; - relop->AsOp()->gtOp1 = gtNewOperNode(oper, TYP_I_IMPL, comparand, comparandClone); + genTreeOps oper = m_comp->compStressCompile(m_comp->STRESS_OPT_BOOLS_GC, 50) ? GT_OR : GT_AND; + relop->AsOp()->gtOp1 = m_comp->gtNewOperNode(oper, TYP_I_IMPL, comparand, comparandClone); // Comparand type is already checked, and we have const int, there is no harm // morphing it into a TYP_I_IMPL. @@ -7658,37 +8374,46 @@ void Compiler::optOptimizeBoolsGcStress(BasicBlock* condBlock) #endif -/****************************************************************************** - * Function used by folding of boolean conditionals - * Given a GT_JTRUE node, checks that it is a boolean comparison of the form - * "if (boolVal ==/!= 0/1)". This is translated into a GT_EQ node with "op1" - * being a boolean lclVar and "op2" the const 0/1. - * On success, the comparand (ie. boolVal) is returned. Else NULL. - * compPtr returns the compare node (i.e. GT_EQ or GT_NE node) - * boolPtr returns whether the comparand is a boolean value (must be 0 or 1). - * When return boolPtr equal to true, if the comparison was against a 1 (i.e true) - * value then we morph the tree by reversing the GT_EQ/GT_NE and change the 1 to 0. - */ - -GenTree* Compiler::optIsBoolCond(GenTree* condBranch, GenTree** compPtr, bool* boolPtr) +//----------------------------------------------------------------------------- +// optIsBoolComp: Function used by folding of boolean conditionals +// +// Arguments: +// pOptTest The test info for the test tree +// +// Return: +// On success, return the first operand (gtOp1) of compTree, else return nullptr. +// +// Notes: +// On entry, testTree is set. +// On success, compTree is set to the compare node (i.e. GT_EQ or GT_NE) of the testTree. +// isBool is set to true if the comparand (i.e., operand 1 of compTree is boolean. Otherwise, false. +// +// Given a GT_JTRUE or GT_RETURN node, this method checks if it is a boolean comparison +// of the form "if (boolVal ==/!= 0/1)".This is translated into +// a GT_EQ/GT_NE node with "opr1" being a boolean lclVar and "opr2" the const 0/1. +// +// When isBool == true, if the comparison was against a 1 (i.e true) +// then we morph the tree by reversing the GT_EQ/GT_NE and change the 1 to 0. +// +GenTree* OptBoolsDsc::optIsBoolComp(OptTestInfo* pOptTest) { - bool isBool = false; + pOptTest->isBool = false; - noway_assert(condBranch->gtOper == GT_JTRUE); - GenTree* cond = condBranch->AsOp()->gtOp1; + assert(pOptTest->testTree->gtOper == GT_JTRUE || pOptTest->testTree->gtOper == GT_RETURN); + GenTree* cond = pOptTest->testTree->AsOp()->gtOp1; - /* The condition must be "!= 0" or "== 0" */ + // The condition must be "!= 0" or "== 0" if ((cond->gtOper != GT_EQ) && (cond->gtOper != GT_NE)) { return nullptr; } - /* Return the compare node to the caller */ + // Return the compare node to the caller - *compPtr = cond; + pOptTest->compTree = cond; - /* Get hold of the comparands */ + // Get hold of the comparands GenTree* opr1 = cond->AsOp()->gtOp1; GenTree* opr2 = cond->AsOp()->gtOp2; @@ -7705,39 +8430,39 @@ GenTree* Compiler::optIsBoolCond(GenTree* condBranch, GenTree** compPtr, bool* b ssize_t ival2 = opr2->AsIntCon()->gtIconVal; - /* Is the value a boolean? - * We can either have a boolean expression (marked GTF_BOOLEAN) or - * a local variable that is marked as being boolean (lvIsBoolean) */ + // Is the value a boolean? + // We can either have a boolean expression (marked GTF_BOOLEAN) or + // a local variable that is marked as being boolean (lvIsBoolean) if (opr1->gtFlags & GTF_BOOLEAN) { - isBool = true; + pOptTest->isBool = true; } else if ((opr1->gtOper == GT_CNS_INT) && (opr1->IsIntegralConst(0) || opr1->IsIntegralConst(1))) { - isBool = true; + pOptTest->isBool = true; } else if (opr1->gtOper == GT_LCL_VAR) { - /* is it a boolean local variable */ + // is it a boolean local variable? unsigned lclNum = opr1->AsLclVarCommon()->GetLclNum(); - noway_assert(lclNum < lvaCount); + noway_assert(lclNum < m_comp->lvaCount); - if (lvaTable[lclNum].lvIsBoolean) + if (m_comp->lvaTable[lclNum].lvIsBoolean) { - isBool = true; + pOptTest->isBool = true; } } - /* Was our comparison against the constant 1 (i.e. true) */ + // Was our comparison against the constant 1 (i.e. true) if (ival2 == 1) { // If this is a boolean expression tree we can reverse the relop // and change the true to false. - if (isBool) + if (pOptTest->isBool) { - gtReverseCond(cond); + m_comp->gtReverseCond(cond); opr2->AsIntCon()->gtIconVal = 0; } else @@ -7746,10 +8471,65 @@ GenTree* Compiler::optIsBoolCond(GenTree* condBranch, GenTree** compPtr, bool* b } } - *boolPtr = isBool; return opr1; } +//----------------------------------------------------------------------------- +// optOptimizeBools: Folds boolean conditionals for GT_JTRUE/GT_RETURN nodes +// +// Notes: +// If the operand of GT_JTRUE/GT_RETURN node is GT_EQ/GT_NE of the form +// "if (boolVal ==/!= 0/1)", the GT_EQ/GT_NE nodes are translated into a +// GT_EQ/GT_NE node with +// "op1" being a boolean GT_OR/GT_AND lclVar and +// "op2" the const 0/1. +// For example, the folded tree for the below boolean optimization is shown below: +// Case 1: (x == 0 && y ==0) => (x | y) == 0 +// * RETURN int +// \--* EQ int +// +--* OR int +// | +--* LCL_VAR int V00 arg0 +// | \--* LCL_VAR int V01 arg1 +// \--* CNS_INT int 0 +// +// Case 2: (x == null && y == null) ==> (x | y) == 0 +// * RETURN int +// \-- * EQ int +// + -- * OR long +// | +-- * LCL_VAR ref V00 arg0 +// | \-- * LCL_VAR ref V01 arg1 +// \-- * CNS_INT long 0 +// +// Case 3: (x == 0 && y == 0 && z == 0) ==> ((x | y) | z) == 0 +// * RETURN int +// \-- * EQ int +// + -- * OR int +// | +-- * OR int +// | | +-- * LCL_VAR int V00 arg0 +// | | \-- * LCL_VAR int V01 arg1 +// | \-- * LCL_VAR int V02 arg2 +// \-- * CNS_INT int 0 +// +// Case 4: (x == 0 && y == 0 && z == 0 && w == 0) ==> (((x | y) | z) | w) == 0 +// * RETURN int +// \-- * EQ int +// + * OR int +// | +--* OR int +// | | +--* OR int +// | | | +--* LCL_VAR int V00 arg0 +// | | | \--* LCL_VAR int V01 arg1 +// | | \--* LCL_VAR int V02 arg2 +// | \--* LCL_VAR int V03 arg3 +// \--* CNS_INT int 0 +// +// Patterns that are not optimized include (x == 1 && y == 1), (x == 1 || y == 1), +// (x == 0 || y == 0) because currently their comptree is not marked as boolean expression. +// When m_foldOp == GT_AND or m_cmpOp == GT_NE, both compTrees must be boolean expression +// in order to skip below cases when compTree is not boolean expression: +// - x == 1 && y == 1 ==> (x&y)!=0: Skip cases where x or y is greather than 1, e.g., x=3, y=1 +// - x == 1 || y == 1 ==> (x|y)!=0: Skip cases where either x or y is greater than 1, e.g., x=2, y=0 +// - x == 0 || y == 0 ==> (x&y)==0: Skip cases where x and y have opposite bits set, e.g., x=2, y=1 +// void Compiler::optOptimizeBools() { #ifdef DEBUG @@ -7771,338 +8551,75 @@ void Compiler::optOptimizeBools() for (BasicBlock* const b1 : Blocks()) { - /* We're only interested in conditional jumps here */ + // We're only interested in conditional jumps here if (b1->bbJumpKind != BBJ_COND) { continue; } - /* If there is no next block, we're done */ + // If there is no next block, we're done BasicBlock* b2 = b1->bbNext; - if (!b2) + if (b2 == nullptr) { break; } - /* The next block must not be marked as BBF_DONT_REMOVE */ + // The next block must not be marked as BBF_DONT_REMOVE if (b2->bbFlags & BBF_DONT_REMOVE) { continue; } - /* The next block also needs to be a condition */ + OptBoolsDsc optBoolsDsc(b1, b2, this); - if (b2->bbJumpKind != BBJ_COND) + // The next block needs to be a condition or return block. + + if (b2->bbJumpKind == BBJ_COND) { -#ifdef DEBUG - optOptimizeBoolsGcStress(b1); -#endif - continue; - } - - bool sameTarget; // Do b1 and b2 have the same bbJumpDest? - - if (b1->bbJumpDest == b2->bbJumpDest) - { - /* Given the following sequence of blocks : - B1: brtrue(t1, BX) - B2: brtrue(t2, BX) - B3: - we will try to fold it to : - B1: brtrue(t1|t2, BX) - B3: - */ - - sameTarget = true; - } - else if (b1->bbJumpDest == b2->bbNext) /*b1->bbJumpDest->bbNum == n1+2*/ - { - /* Given the following sequence of blocks : - B1: brtrue(t1, B3) - B2: brtrue(t2, BX) - B3: - we will try to fold it to : - B1: brtrue((!t1)&&t2, BX) - B3: - */ - - sameTarget = false; - } - else - { - continue; - } - - /* The second block must contain a single statement */ - - Statement* s2 = b2->firstStmt(); - if (s2->GetPrevStmt() != s2) - { - continue; - } - - GenTree* t2 = s2->GetRootNode(); - noway_assert(t2->gtOper == GT_JTRUE); - - /* Find the condition for the first block */ - - Statement* s1 = b1->lastStmt(); - - GenTree* t1 = s1->GetRootNode(); - noway_assert(t1->gtOper == GT_JTRUE); - - if (b2->countOfInEdges() > 1) - { - continue; - } - - /* Find the branch conditions of b1 and b2 */ - - bool bool1, bool2; - - GenTree* c1 = optIsBoolCond(t1, &t1, &bool1); - if (!c1) - { - continue; - } - - GenTree* c2 = optIsBoolCond(t2, &t2, &bool2); - if (!c2) - { - continue; - } - - noway_assert(t1->OperIs(GT_EQ, GT_NE) && t1->AsOp()->gtOp1 == c1); - noway_assert(t2->OperIs(GT_EQ, GT_NE) && t2->AsOp()->gtOp1 == c2); - - // Leave out floats where the bit-representation is more complicated - // - there are two representations for 0. - // - if (varTypeIsFloating(c1->TypeGet()) || varTypeIsFloating(c2->TypeGet())) - { - continue; - } - - // Make sure the types involved are of the same sizes - if (genTypeSize(c1->TypeGet()) != genTypeSize(c2->TypeGet())) - { - continue; - } - if (genTypeSize(t1->TypeGet()) != genTypeSize(t2->TypeGet())) - { - continue; - } -#ifdef TARGET_ARMARCH - // Skip the small operand which we cannot encode. - if (varTypeIsSmall(c1->TypeGet())) - continue; -#endif - /* The second condition must not contain side effects */ - - if (c2->gtFlags & GTF_GLOB_EFFECT) - { - continue; - } - - /* The second condition must not be too expensive */ - - gtPrepareCost(c2); - - if (c2->GetCostEx() > 12) - { - continue; - } - - genTreeOps foldOp; - genTreeOps cmpOp; - var_types foldType = c1->TypeGet(); - if (varTypeIsGC(foldType)) - { - foldType = TYP_I_IMPL; - } - - if (sameTarget) - { - /* Both conditions must be the same */ - - if (t1->gtOper != t2->gtOper) + if ((b1->bbJumpDest != b2->bbJumpDest) && (b1->bbJumpDest != b2->bbNext)) { continue; } - if (t1->gtOper == GT_EQ) - { - /* t1:c1==0 t2:c2==0 ==> Branch to BX if either value is 0 - So we will branch to BX if (c1&c2)==0 */ + // When it is conditional jumps - foldOp = GT_AND; - cmpOp = GT_EQ; - } - else + if (optBoolsDsc.optOptimizeBoolsCondBlock()) { - /* t1:c1!=0 t2:c2!=0 ==> Branch to BX if either value is non-0 - So we will branch to BX if (c1|c2)!=0 */ - - foldOp = GT_OR; - cmpOp = GT_NE; + change = true; } } - else + else if (b2->bbJumpKind == BBJ_RETURN) { - /* The b1 condition must be the reverse of the b2 condition */ + // Set b3 to b1 jump destination + BasicBlock* b3 = b1->bbJumpDest; - if (t1->gtOper == t2->gtOper) + // b3 must not be marked as BBF_DONT_REMOVE + + if (b3->bbFlags & BBF_DONT_REMOVE) { continue; } - if (t1->gtOper == GT_EQ) + // b3 must be RETURN type + + if (b3->bbJumpKind != BBJ_RETURN) { - /* t1:c1==0 t2:c2!=0 ==> Branch to BX if both values are non-0 - So we will branch to BX if (c1&c2)!=0 */ - - foldOp = GT_AND; - cmpOp = GT_NE; + continue; } - else + + if (optBoolsDsc.optOptimizeBoolsReturnBlock(b3)) { - /* t1:c1!=0 t2:c2==0 ==> Branch to BX if both values are 0 - So we will branch to BX if (c1|c2)==0 */ - - foldOp = GT_OR; - cmpOp = GT_EQ; + change = true; } } - - // Anding requires both values to be 0 or 1 - - if ((foldOp == GT_AND) && (!bool1 || !bool2)) - { - continue; - } - - // - // Now update the trees - // - GenTree* cmpOp1 = gtNewOperNode(foldOp, foldType, c1, c2); - if (bool1 && bool2) - { - /* When we 'OR'/'AND' two booleans, the result is boolean as well */ - cmpOp1->gtFlags |= GTF_BOOLEAN; - } - - t1->SetOper(cmpOp); - t1->AsOp()->gtOp1 = cmpOp1; - t1->AsOp()->gtOp2->gtType = foldType; // Could have been varTypeIsGC() - -#if FEATURE_SET_FLAGS - // For comparisons against zero we will have the GTF_SET_FLAGS set - // and this can cause an assert to fire in fgMoveOpsLeft(GenTree* tree) - // during the CSE phase. - // - // So make sure to clear any GTF_SET_FLAGS bit on these operations - // as they are no longer feeding directly into a comparisons against zero - - // Make sure that the GTF_SET_FLAGS bit is cleared. - // Fix 388436 ARM JitStress WP7 - c1->gtFlags &= ~GTF_SET_FLAGS; - c2->gtFlags &= ~GTF_SET_FLAGS; - - // The new top level node that we just created does feed directly into - // a comparison against zero, so set the GTF_SET_FLAGS bit so that - // we generate an instruction that sets the flags, which allows us - // to omit the cmp with zero instruction. - - // Request that the codegen for cmpOp1 sets the condition flags - // when it generates the code for cmpOp1. - // - cmpOp1->gtRequestSetFlags(); -#endif - - flowList* edge1 = fgGetPredForBlock(b1->bbJumpDest, b1); - flowList* edge2; - - /* Modify the target of the conditional jump and update bbRefs and bbPreds */ - - if (sameTarget) - { - edge2 = fgGetPredForBlock(b2->bbJumpDest, b2); - } else { - edge2 = fgGetPredForBlock(b2->bbNext, b2); - - fgRemoveRefPred(b1->bbJumpDest, b1); - - b1->bbJumpDest = b2->bbJumpDest; - - fgAddRefPred(b2->bbJumpDest, b1); - } - - noway_assert(edge1 != nullptr); - noway_assert(edge2 != nullptr); - - BasicBlock::weight_t edgeSumMin = edge1->edgeWeightMin() + edge2->edgeWeightMin(); - BasicBlock::weight_t edgeSumMax = edge1->edgeWeightMax() + edge2->edgeWeightMax(); - if ((edgeSumMax >= edge1->edgeWeightMax()) && (edgeSumMax >= edge2->edgeWeightMax())) - { - edge1->setEdgeWeights(edgeSumMin, edgeSumMax, b1->bbJumpDest); - } - else - { - edge1->setEdgeWeights(BB_ZERO_WEIGHT, BB_MAX_WEIGHT, b1->bbJumpDest); - } - - /* Get rid of the second block (which is a BBJ_COND) */ - - noway_assert(b1->bbJumpKind == BBJ_COND); - noway_assert(b2->bbJumpKind == BBJ_COND); - noway_assert(b1->bbJumpDest == b2->bbJumpDest); - noway_assert(b1->bbNext == b2); - noway_assert(b2->bbNext); - - fgUnlinkBlock(b2); - b2->bbFlags |= BBF_REMOVED; - - // If b2 was the last block of a try or handler, update the EH table. - - ehUpdateForDeletedBlock(b2); - - /* Update bbRefs and bbPreds */ - - /* Replace pred 'b2' for 'b2->bbNext' with 'b1' - * Remove pred 'b2' for 'b2->bbJumpDest' */ - - fgReplacePred(b2->bbNext, b2, b1); - - fgRemoveRefPred(b2->bbJumpDest, b2); - - /* Update the block numbers and try again */ - - change = true; - /* - do - { - b2->bbNum = ++n1; - b2 = b2->bbNext; - } - while (b2); - */ - - // Update loop table - fgUpdateLoopsAfterCompacting(b1, b2); - #ifdef DEBUG - if (verbose) - { - printf("Folded %sboolean conditions of " FMT_BB " and " FMT_BB " to :\n", - c2->OperIsLeaf() ? "" : "non-leaf ", b1->bbNum, b2->bbNum); - gtDispStmt(s1); - printf("\n"); - } + optBoolsDsc.optOptimizeBoolsGcStress(); #endif + } } } while (change); diff --git a/src/tests/JIT/opt/OptimizeBools/optboolsreturn.cs b/src/tests/JIT/opt/OptimizeBools/optboolsreturn.cs new file mode 100644 index 00000000000..c6c3964191d --- /dev/null +++ b/src/tests/JIT/opt/OptimizeBools/optboolsreturn.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// unit test for boolean optimization + +using System; +using System.Runtime.CompilerServices; + +public class CBoolTest +{ + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreZero(int x, int y) + { + return (x == 0 && y == 0); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreNull(object x, object y) + { + return (x == null && y == null); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreZero2(int x, int y) + { + return x == 0 && y == 0 && BitConverter.IsLittleEndian; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreZero3(int x, int y, int z) + { + return x == 0 && y == 0 && z == 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreZero4(int x, int y, int z, int w) + { + return (x == 0 && y == 0 && z == 0 && w == 0); + } + + // Cases that skip optimization + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool AreOne(int x, int y) + { + return (x == 1 && y == 1); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool IsEitherZero(int x, int y) + { + return (x == 0 || y == 0); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool IsEitherOne(int x, int y) + { + return (x == 1 || y == 1); + } + + public static int Main() + { + // Optimize boolean + + if (!AreZero(0, 0)) + { + Console.WriteLine("CBoolTest:AreZero(0, 0) failed"); + return 101; + } + + if (AreZero(1, 1)) + { + Console.WriteLine("CBoolTest:AreZero(1, 1) failed"); + return 101; + } + + if (AreZero(0, 2)) + { + Console.WriteLine("CBoolTest:AreZero(0, 2) failed"); + return 101; + } + + if (AreZero(3, 0)) + { + Console.WriteLine("CBoolTest:AreZero(3, 0) failed"); + return 101; + } + + if (!AreNull(null, null)) + { + Console.WriteLine("CBoolTest:AreNull(null, null) failed"); + return 101; + } + + if (AreNull(new Object(), new Object())) + { + Console.WriteLine("CBoolTest:AreNull(obj, obj) failed"); + return 101; + } + + if (!AreZero2(0, 0)) + { + Console.WriteLine("CBoolTest:AreZero2(0, 0) failed"); + return 101; + } + + if (AreZero2(2, 1)) + { + Console.WriteLine("CBoolTest:AreZero2(2, 1) failed"); + return 101; + } + + if (!AreZero3(0, 0, 0)) + { + Console.WriteLine("CBoolTest:AreZero3(0, 0, 0) failed"); + return 101; + } + + if (AreZero3(0, 1, 2)) + { + Console.WriteLine("CBoolTest:AreZero3(0, 1, 2) failed"); + return 101; + } + + if (!AreZero4(0, 0, 0, 0)) + { + Console.WriteLine("CBoolTest:AreZero4(0, 0, 0, 0) failed"); + return 101; + } + + if (AreZero4(0, 1, 2, 3)) + { + Console.WriteLine("CBoolTest:AreZero4(0, 1, 2, 3) failed"); + return 101; + } + + // Skip optimization + + // Test if ANDing or GT_NE requires both operands to be boolean + if (!AreOne(1, 1)) + { + Console.WriteLine("CBoolTest:AreOne(1, 1) failed"); + return 101; + } + + // Skip cases where x or y is greather than 1 + if (AreOne(3, 1)) + { + Console.WriteLine("CBoolTest:AreOne(1, 3) failed"); + return 101; + } + + // Test if ANDing requires both operands to be boolean + if (!IsEitherZero(0, 1)) + { + Console.WriteLine("CBoolTest:IsEitherZero(0, 1) failed"); + return 101; + } + + // Skip cases where x and y have opposite bits set + if (IsEitherZero(2, 1)) + { + Console.WriteLine("CBoolTest:IsEitherZero(2, 1) failed"); + return 101; + } + + // Test if GT_NE requires both operands to be boolean + if (!IsEitherOne(0, 1)) + { + Console.WriteLine("CBoolTest:IsEitherOne(0, 1) failed"); + return 101; + } + + // Skip cases where either x or y is greater than 1 + if (IsEitherOne(2, 0)) + { + Console.WriteLine("CBoolTest:IsEitherOne(2, 0) failed"); + return 101; + } + + return 100; + } +} diff --git a/src/tests/JIT/opt/OptimizeBools/optboolsreturn.csproj b/src/tests/JIT/opt/OptimizeBools/optboolsreturn.csproj new file mode 100644 index 00000000000..318a0eb9c3d --- /dev/null +++ b/src/tests/JIT/opt/OptimizeBools/optboolsreturn.csproj @@ -0,0 +1,13 @@ + + + Exe + 0 + + + PdbOnly + True + + + + + From 22c1e12edcf861b64ceff0a7d8f3ab309b7cd8dc Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 14 Jul 2021 18:06:29 -0400 Subject: [PATCH 565/926] Fix building S.S.Cryptography.Native with cmake 3.6.2 This uses our add_linker_flag function for adding linker flags instead of add_link_options which is only available in newer cmake versions. --- .../Unix/System.Security.Cryptography.Native/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt index 37a7f5ccc5b..a9987063b54 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt @@ -61,7 +61,7 @@ if (FEATURE_DISTRO_AGNOSTIC_SSL) ) add_definitions(-DFEATURE_DISTRO_AGNOSTIC_SSL) add_compile_options(-pthread) - add_link_options(-pthread) + add_linker_flag(-pthread) endif() add_library(objlib OBJECT ${NATIVECRYPTO_SOURCES} ${VERSION_FILE_PATH}) From 2e2670ea39ac25c11d8c7248d938a89870411f5c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 14 Jul 2021 16:30:31 -0700 Subject: [PATCH 566/926] Don't validate signature for _VTblGap* methods (#55616) --- src/coreclr/vm/methodtablebuilder.cpp | 149 +++++++++++++------------- 1 file changed, 73 insertions(+), 76 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 154c642cf40..b426204e443 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2751,15 +2751,7 @@ MethodTableBuilder::EnumerateClassMethods() BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); } - // Signature validation - if (!bmtProp->fNoSanityChecks) - { - hr = validateTokenSig(tok,pMemberSignature,cMemberSignature,dwMemberAttrs,pMDInternalImport); - if (FAILED(hr)) - { - BuildMethodTableThrowException(hr, BFA_BAD_SIGNATURE, mdMethodDefNil); - } - } + bool isVtblGap = false; if (IsMdRTSpecialName(dwMemberAttrs) || IsMdVirtual(dwMemberAttrs) || IsDelegate()) { if (FAILED(pMDInternalImport->GetNameOfMethodDef(tok, (LPCSTR *)&strMethodName))) @@ -2770,12 +2762,24 @@ MethodTableBuilder::EnumerateClassMethods() { BuildMethodTableThrowException(BFA_METHOD_NAME_TOO_LONG); } + + isVtblGap = IsMdRTSpecialName(dwMemberAttrs) && strncmp(strMethodName, "_VtblGap", 8) == 0; } else { strMethodName = NULL; } + // Signature validation + if (!bmtProp->fNoSanityChecks && !isVtblGap) + { + hr = validateTokenSig(tok,pMemberSignature,cMemberSignature,dwMemberAttrs,pMDInternalImport); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, BFA_BAD_SIGNATURE, mdMethodDefNil); + } + } + DWORD numGenericMethodArgs = 0; { @@ -2847,84 +2851,77 @@ MethodTableBuilder::EnumerateClassMethods() // single empty slot). // - if (IsMdRTSpecialName(dwMemberAttrs)) + if (isVtblGap) { - PREFIX_ASSUME(strMethodName != NULL); // if we've gotten here we've called GetNameOfMethodDef + // + // This slot doesn't really exist, don't add it to the method + // table. Instead it represents one or more empty slots, encoded + // in the method name. Locate the beginning of the count in the + // name. There are these points to consider: + // There may be no count present at all (in which case the + // count is taken as one). + // There may be an additional count just after Gap but before + // the '_'. We ignore this. + // - // The slot is special, but it might not be a vtable spacer. To - // determine that we must look at the name. - if (strncmp(strMethodName, "_VtblGap", 8) == 0) + LPCSTR pos = strMethodName + 8; + + // Skip optional number. + while (IS_DIGIT(*pos)) + pos++; + + WORD n = 0; + + // Check for presence of count. + if (*pos == '\0') { - // - // This slot doesn't really exist, don't add it to the method - // table. Instead it represents one or more empty slots, encoded - // in the method name. Locate the beginning of the count in the - // name. There are these points to consider: - // There may be no count present at all (in which case the - // count is taken as one). - // There may be an additional count just after Gap but before - // the '_'. We ignore this. - // - - LPCSTR pos = strMethodName + 8; - - // Skip optional number. - while (IS_DIGIT(*pos)) - pos++; - - WORD n = 0; - - // Check for presence of count. - if (*pos == '\0') - n = 1; - else + n = 1; + } + else + { + if (*pos != '_') { - if (*pos != '_') - { - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, - IDS_CLASSLOAD_BADSPECIALMETHOD, - tok); - } - - // Skip '_'. - pos++; - - // Read count. - bool fReadAtLeastOneDigit = false; - while (IS_DIGIT(*pos)) - { - _ASSERTE(n < 6552); - n *= 10; - n += DIGIT_TO_INT(*pos); - pos++; - fReadAtLeastOneDigit = true; - } - - // Check for end of name. - if (*pos != '\0' || !fReadAtLeastOneDigit) - { - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, - IDS_CLASSLOAD_BADSPECIALMETHOD, - tok); - } + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_BADSPECIALMETHOD, + tok); } -#ifdef FEATURE_COMINTEROP - // Record vtable gap in mapping list. The map is an optional field, so ensure we've allocated - // these fields first. - EnsureOptionalFieldsAreAllocated(GetHalfBakedClass(), m_pAllocMemTracker, GetLoaderAllocator()->GetLowFrequencyHeap()); - if (GetHalfBakedClass()->GetSparseCOMInteropVTableMap() == NULL) - GetHalfBakedClass()->SetSparseCOMInteropVTableMap(new SparseVTableMap()); + // Skip '_'. + pos++; - GetHalfBakedClass()->GetSparseCOMInteropVTableMap()->RecordGap((WORD)NumDeclaredMethods(), n); + // Read count. + bool fReadAtLeastOneDigit = false; + while (IS_DIGIT(*pos)) + { + _ASSERTE(n < 6552); + n *= 10; + n += DIGIT_TO_INT(*pos); + pos++; + fReadAtLeastOneDigit = true; + } - bmtProp->fSparse = true; -#endif // FEATURE_COMINTEROP - continue; + // Check for end of name. + if (*pos != '\0' || !fReadAtLeastOneDigit) + { + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_BADSPECIALMETHOD, + tok); + } } - } +#ifdef FEATURE_COMINTEROP + // Record vtable gap in mapping list. The map is an optional field, so ensure we've allocated + // these fields first. + EnsureOptionalFieldsAreAllocated(GetHalfBakedClass(), m_pAllocMemTracker, GetLoaderAllocator()->GetLowFrequencyHeap()); + if (GetHalfBakedClass()->GetSparseCOMInteropVTableMap() == NULL) + GetHalfBakedClass()->SetSparseCOMInteropVTableMap(new SparseVTableMap()); + GetHalfBakedClass()->GetSparseCOMInteropVTableMap()->RecordGap((WORD)NumDeclaredMethods(), n); + + bmtProp->fSparse = true; +#endif // FEATURE_COMINTEROP + continue; + } // // This is a real method so add it to the enumeration of methods. We now need to retrieve From 720279cc437cb05418155a407b5ac7a698de879d Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Thu, 15 Jul 2021 01:47:40 +0200 Subject: [PATCH 567/926] Process.Start() failure should include path (#46417) * Adding ProcessStartException * Reverting ProcessStartException * First version * Fixing merge issue * Using interop call instead of intermediate exception to get error msg * Fixing typo * Change checks for exception message * Update src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs Co-authored-by: Adam Sitnik * Update src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs Co-authored-by: Adam Sitnik * Using common method to create exception and updating error message * Fixing merge mistake * Using AssertExtensions.ThrowsContains * Fixing code for unix * Address PR feedback * Apply suggestions from code review Co-authored-by: Stephen Toub * Fix ErrorInfo type reference Co-authored-by: Adam Sitnik Co-authored-by: Jeff Handley Co-authored-by: Jeff Handley Co-authored-by: Stephen Toub --- src/installer/pkg/snap/snapcraft.yaml | 0 .../src/Resources/Strings.resx | 3 ++ .../src/System.Diagnostics.Process.csproj | 2 ++ .../src/System/Diagnostics/Process.Unix.cs | 32 +++++++++---------- .../src/System/Diagnostics/Process.Win32.cs | 18 ++++++----- .../src/System/Diagnostics/Process.Windows.cs | 12 ++++--- .../src/System/Diagnostics/Process.cs | 7 ++++ .../tests/ProcessTests.cs | 7 +++- 8 files changed, 51 insertions(+), 30 deletions(-) mode change 100755 => 100644 src/installer/pkg/snap/snapcraft.yaml diff --git a/src/installer/pkg/snap/snapcraft.yaml b/src/installer/pkg/snap/snapcraft.yaml old mode 100755 new mode 100644 diff --git a/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx b/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx index c3877c0f365..82c5f81597e 100644 --- a/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx +++ b/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx @@ -185,6 +185,9 @@ The specified executable is not a valid application for this OS platform. + + An error occurred trying to start process '{0}' with working directory '{1}'. {2} + StandardOutputEncoding is only supported when standard output is redirected. diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index e4f10fa9063..abc273f110f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -46,6 +46,8 @@ + Interop.Kernel32.GetMessage(error); } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index 7cc5538ddb4..396b0f0dae2 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -1686,6 +1686,13 @@ namespace System.Diagnostics } } + private static Win32Exception CreateExceptionForErrorStartingProcess(string errorMessage, int errorCode, string fileName, string? workingDirectory) + { + string directoryForException = string.IsNullOrEmpty(workingDirectory) ? Directory.GetCurrentDirectory() : workingDirectory; + string msg = SR.Format(SR.ErrorStartingProcess, fileName, directoryForException, errorMessage); + return new Win32Exception(errorCode, msg); + } + /// /// This enum defines the operation mode for redirected process stream. /// We don't support switching between synchronous mode and asynchronous mode. diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 9921aac3e94..cbc80bebc8d 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -220,6 +220,8 @@ namespace System.Diagnostics.Tests Win32Exception e = Assert.Throws(() => Process.Start(psi)); Assert.NotEqual(0, e.NativeErrorCode); + Assert.Contains(program, e.Message); + Assert.Contains(workingDirectory, e.Message); } else { @@ -232,7 +234,9 @@ namespace System.Diagnostics.Tests public void ProcessStart_UseShellExecute_OnWindows_OpenMissingFile_Throws() { string fileToOpen = Path.Combine(Environment.CurrentDirectory, "_no_such_file.TXT"); - Assert.Throws(() => Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = fileToOpen })); + AssertExtensions.ThrowsContains( + () => Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = fileToOpen }), + fileToOpen); } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.HasWindowsShell))] @@ -1398,6 +1402,7 @@ namespace System.Diagnostics.Tests Win32Exception e = Assert.Throws(() => Process.Start(path)); Assert.NotEqual(0, e.NativeErrorCode); + Assert.Contains(path, e.Message); } [Fact] From bbcb6b7707e55056a59b704f27af2db0c740da86 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 15 Jul 2021 02:02:16 +0200 Subject: [PATCH 568/926] Add CollectionsMarshal.GetValueRefOrAddDefault (#54611) --- .../OutOfBoundsRegression.cs | 11 + .../System/Collections/Generic/Dictionary.cs | 187 +++++++++++++++++ .../InteropServices/CollectionsMarshal.cs | 10 + .../ref/System.Runtime.InteropServices.cs | 1 + .../CollectionsMarshalTests.cs | 195 ++++++++++++++++++ 5 files changed, 404 insertions(+) diff --git a/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs b/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs index eb7a7bf2875..d5dee500215 100644 --- a/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs +++ b/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs @@ -89,6 +89,17 @@ namespace System.Collections.Tests expectedInternalComparerTypeBeforeCollisionThreshold: StringComparer.InvariantCulture.GetType(), expectedPublicComparerBeforeCollisionThreshold: StringComparer.InvariantCulture, expectedInternalComparerTypeAfterCollisionThreshold: StringComparer.InvariantCulture.GetType()); + + // CollectionsMarshal.GetValueRefOrAddDefault + + RunCollectionTestCommon( + () => new Dictionary(StringComparer.Ordinal), + (dictionary, key) => CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out _) = null, + (dictionary, key) => dictionary.ContainsKey(key), + dictionary => dictionary.Comparer, + expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedOrdinalComparerType, + expectedPublicComparerBeforeCollisionThreshold: StringComparer.Ordinal, + expectedInternalComparerTypeAfterCollisionThreshold: randomizedOrdinalComparerType); static void RunDictionaryTest( IEqualityComparer equalityComparer, diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index c08031b58d6..bc6eb587e80 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -497,6 +497,9 @@ namespace System.Collections.Generic private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) { + // NOTE: this method is mirrored in CollectionsMarshal.GetValueRefOrAddDefault below. + // If you make any changes here, make sure to keep that version in sync as well. + if (key == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); @@ -681,6 +684,190 @@ namespace System.Collections.Generic return true; } + /// + /// A helper class containing APIs exposed through . + /// These methods are relatively niche and only used in specific scenarios, so adding them in a separate type avoids + /// the additional overhead on each instantiation, especially in AOT scenarios. + /// + internal static class CollectionsMarshalHelper + { + /// + public static ref TValue? GetValueRefOrAddDefault(Dictionary dictionary, TKey key, out bool exists) + { + // NOTE: this method is mirrored by Dictionary.TryInsert above. + // If you make any changes here, make sure to keep that version in sync as well. + + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (dictionary._buckets == null) + { + dictionary.Initialize(0); + } + Debug.Assert(dictionary._buckets != null); + + Entry[]? entries = dictionary._entries; + Debug.Assert(entries != null, "expected entries to be non-null"); + + IEqualityComparer? comparer = dictionary._comparer; + uint hashCode = (uint)((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)); + + uint collisionCount = 0; + ref int bucket = ref dictionary.GetBucket(hashCode); + int i = bucket - 1; // Value in _buckets is 1-based + + if (comparer == null) + { + if (typeof(TKey).IsValueType) + { + // ValueType: Devirtualize with EqualityComparer.Default intrinsic + while (true) + { + // Should be a while loop https://github.com/dotnet/runtime/issues/9422 + // Test uint in if rather than loop condition to drop range check for following array access + if ((uint)i >= (uint)entries.Length) + { + break; + } + + if (entries[i].hashCode == hashCode && EqualityComparer.Default.Equals(entries[i].key, key)) + { + exists = true; + + return ref entries[i].value!; + } + + i = entries[i].next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + } + else + { + // Object type: Shared Generic, EqualityComparer.Default won't devirtualize + // https://github.com/dotnet/runtime/issues/10050 + // So cache in a local rather than get EqualityComparer per loop iteration + EqualityComparer defaultComparer = EqualityComparer.Default; + while (true) + { + // Should be a while loop https://github.com/dotnet/runtime/issues/9422 + // Test uint in if rather than loop condition to drop range check for following array access + if ((uint)i >= (uint)entries.Length) + { + break; + } + + if (entries[i].hashCode == hashCode && defaultComparer.Equals(entries[i].key, key)) + { + exists = true; + + return ref entries[i].value!; + } + + i = entries[i].next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + } + } + else + { + while (true) + { + // Should be a while loop https://github.com/dotnet/runtime/issues/9422 + // Test uint in if rather than loop condition to drop range check for following array access + if ((uint)i >= (uint)entries.Length) + { + break; + } + + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + { + exists = true; + + return ref entries[i].value!; + } + + i = entries[i].next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + } + + int index; + if (dictionary._freeCount > 0) + { + index = dictionary._freeList; + Debug.Assert((StartOfFreeList - entries[dictionary._freeList].next) >= -1, "shouldn't overflow because `next` cannot underflow"); + dictionary._freeList = StartOfFreeList - entries[dictionary._freeList].next; + dictionary._freeCount--; + } + else + { + int count = dictionary._count; + if (count == entries.Length) + { + dictionary.Resize(); + bucket = ref dictionary.GetBucket(hashCode); + } + index = count; + dictionary._count = count + 1; + entries = dictionary._entries; + } + + ref Entry entry = ref entries![index]; + entry.hashCode = hashCode; + entry.next = bucket - 1; // Value in _buckets is 1-based + entry.key = key; + entry.value = default!; + bucket = index + 1; // Value in _buckets is 1-based + dictionary._version++; + + // Value types never rehash + if (!typeof(TKey).IsValueType && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) + { + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + dictionary.Resize(entries.Length, true); + + exists = false; + + // At this point the entries array has been resized, so the current reference we have is no longer valid. + // We're forced to do a new lookup and return an updated reference to the new entry instance. This new + // lookup is guaranteed to always find a value though and it will never return a null reference here. + ref TValue? value = ref dictionary.FindValue(key)!; + + Debug.Assert(!Unsafe.IsNullRef(ref value), "the lookup result cannot be a null ref here"); + + return ref value; + } + + exists = false; + + return ref entry.value!; + } + } + public virtual void OnDeserialization(object? sender) { HashHelpers.SerializationInfoTable.TryGetValue(this, out SerializationInfo? siInfo); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs index a64f4468332..6a60224305a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs @@ -29,5 +29,15 @@ namespace System.Runtime.InteropServices /// public static ref TValue GetValueRefOrNullRef(Dictionary dictionary, TKey key) where TKey : notnull => ref dictionary.FindValue(key); + + /// + /// Gets a ref to a in the , adding a new entry with a default value if it does not exist in the . + /// + /// The dictionary to get the ref to from. + /// The key used for lookup. + /// Whether or not a new entry for the given key was added to the dictionary. + /// Items should not be added to or removed from the while the ref is in use. + public static ref TValue? GetValueRefOrAddDefault(Dictionary dictionary, TKey key, out bool exists) where TKey : notnull + => ref Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault(dictionary, key, out exists); } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 374a3f14c55..e6c03d03d24 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -177,6 +177,7 @@ namespace System.Runtime.InteropServices { public static System.Span AsSpan(System.Collections.Generic.List? list) { throw null; } public static ref TValue GetValueRefOrNullRef(System.Collections.Generic.Dictionary dictionary, TKey key) where TKey : notnull { throw null; } + public static ref TValue? GetValueRefOrAddDefault(System.Collections.Generic.Dictionary dictionary, TKey key, out bool exists) where TKey : notnull { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, Inherited=false)] public sealed partial class ComAliasNameAttribute : System.Attribute diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs index 5c5116b2a72..164d98287ad 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs @@ -299,6 +299,201 @@ namespace System.Runtime.InteropServices.Tests Assert.Equal(50, dict.Count); } + [Fact] + public void GetValueRefOrAddDefaultValueType() + { + // This test is the same as the one for GetValueRefOrNullRef, but it uses + // GetValueRefOrAddDefault instead, and also checks for incorrect additions. + // The two APIs should behave the same when values already exist. + var dict = new Dictionary + { + { 1, default }, + { 2, default } + }; + + Assert.Equal(2, dict.Count); + + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + var itemVal = dict[1]; + itemVal.Value = 1; + itemVal.Property = 2; + + // Does not change values in dictionary + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + CollectionsMarshal.GetValueRefOrAddDefault(dict, 1, out bool exists).Value = 3; + + Assert.True(exists); + Assert.Equal(2, dict.Count); + + CollectionsMarshal.GetValueRefOrAddDefault(dict, 1, out exists).Property = 4; + + Assert.True(exists); + Assert.Equal(2, dict.Count); + Assert.Equal(3, dict[1].Value); + Assert.Equal(4, dict[1].Property); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, 2, out exists); + + Assert.True(exists); + Assert.Equal(2, dict.Count); + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 5; + itemRef.Property = 6; + + Assert.Equal(5, itemRef.Value); + Assert.Equal(6, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + itemRef = new() { Value = 7, Property = 8 }; + + Assert.Equal(7, itemRef.Value); + Assert.Equal(8, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + // Check for correct additions + + ref var entry3Ref = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, 3, out exists); + + Assert.False(exists); + Assert.Equal(3, dict.Count); + Assert.False(Unsafe.IsNullRef(ref entry3Ref)); + Assert.True(EqualityComparer.Default.Equals(entry3Ref, default)); + + entry3Ref.Property = 42; + entry3Ref.Value = 12345; + + var value3 = dict[3]; + + Assert.Equal(42, value3.Property); + Assert.Equal(12345, value3.Value); + } + + [Fact] + public void GetValueRefOrAddDefaultClass() + { + var dict = new Dictionary + { + { 1, new() }, + { 2, new() } + }; + + Assert.Equal(2, dict.Count); + + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + var itemVal = dict[1]; + itemVal.Value = 1; + itemVal.Property = 2; + + // Does change values in dictionary + Assert.Equal(1, dict[1].Value); + Assert.Equal(2, dict[1].Property); + + CollectionsMarshal.GetValueRefOrAddDefault(dict, 1, out bool exists).Value = 3; + + Assert.True(exists); + Assert.Equal(2, dict.Count); + + CollectionsMarshal.GetValueRefOrAddDefault(dict, 1, out exists).Property = 4; + + Assert.True(exists); + Assert.Equal(2, dict.Count); + Assert.Equal(3, dict[1].Value); + Assert.Equal(4, dict[1].Property); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, 2, out exists); + + Assert.True(exists); + Assert.Equal(2, dict.Count); + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 5; + itemRef.Property = 6; + + Assert.Equal(5, itemRef.Value); + Assert.Equal(6, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + itemRef = new() { Value = 7, Property = 8 }; + + Assert.Equal(7, itemRef.Value); + Assert.Equal(8, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + // Check for correct additions + + ref var entry3Ref = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, 3, out exists); + + Assert.False(exists); + Assert.Equal(3, dict.Count); + Assert.False(Unsafe.IsNullRef(ref entry3Ref)); + Assert.Null(entry3Ref); + + entry3Ref = new() { Value = 12345, Property = 42 }; + + var value3 = dict[3]; + + Assert.Equal(42, value3.Property); + Assert.Equal(12345, value3.Value); + } + + [Fact] + public void GetValueRefOrAddDefaultLinkBreaksOnResize() + { + var dict = new Dictionary + { + { 1, new() } + }; + + Assert.Equal(1, dict.Count); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, 1, out bool exists); + + Assert.True(exists); + Assert.Equal(1, dict.Count); + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 1; + itemRef.Property = 2; + + Assert.Equal(1, itemRef.Value); + Assert.Equal(2, itemRef.Property); + Assert.Equal(dict[1].Value, itemRef.Value); + Assert.Equal(dict[1].Property, itemRef.Property); + + // Resize + dict.EnsureCapacity(100); + for (int i = 2; i <= 50; i++) + { + dict.Add(i, new()); + } + + itemRef.Value = 3; + itemRef.Property = 4; + + Assert.Equal(3, itemRef.Value); + Assert.Equal(4, itemRef.Property); + + // Check connection broken + Assert.NotEqual(dict[1].Value, itemRef.Value); + Assert.NotEqual(dict[1].Property, itemRef.Property); + + Assert.Equal(50, dict.Count); + } + private struct Struct { public int Value; From 39135a4a666020c718386a353d2d522387d3b509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 15 Jul 2021 02:18:08 +0200 Subject: [PATCH 569/926] Remove unnecessary reflection in Environment.GetFolderPathCore.Unix.cs (#55677) System.IO.Directory was moved into CoreLib with https://github.com/dotnet/runtime/pull/53231 so we can call it directly now. --- .../System/Environment.GetFolderPathCore.Unix.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs index 4ce955a3c40..27dbc327aec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs @@ -14,8 +14,6 @@ namespace System { public static partial class Environment { - private static Func? s_directoryCreateDirectory; - private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) { // Get the path for the SpecialFolder @@ -40,16 +38,7 @@ namespace System Debug.Assert(option == SpecialFolderOption.Create); - Func? createDirectory = Volatile.Read(ref s_directoryCreateDirectory); - if (createDirectory is null) - { - Type dirType = Type.GetType("System.IO.Directory, System.IO.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: true)!; - MethodInfo mi = dirType.GetMethod("CreateDirectory", BindingFlags.Public | BindingFlags.Static)!; - createDirectory = mi.CreateDelegate>(); - Volatile.Write(ref s_directoryCreateDirectory, createDirectory); - } - - createDirectory(path); + Directory.CreateDirectory(path); return path; } From 5bcf2c67107a9790b4638d3414d728916327230c Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Thu, 15 Jul 2021 03:37:14 +0300 Subject: [PATCH 570/926] Surgical fix for bad assertion generation (#55626) * Add a test * Surgical fix for bad assertion generation Say we have a cast like this: CAST(uint <- long). What value does this tree compute? [int.MinValue..int.MaxValue] - IR operates on signed TYP_INTs. But assertion prop generated [0..uint.MaxValue] for it. The confusion created by this "how to interpret TYP_UINT" question caused a bug where for the assertion generated for the above cast, in the form of [0..uint.MaxValue], propagation could remove a checked cast in the form of CAST_OVF(uint < int). The proper fix is to generate proper ranges for such casts. The surgical fix proposed here is to always treat casts to TYP_UINT as if they were to TYP_INT. This is conservative, but always correct. The generated assertion is useless of course, but that makes this a zero-diff change. * Add a comment explaining the quirk --- src/coreclr/jit/assertionprop.cpp | 13 ++++++++ .../JitBlue/Runtime_54842/Runtime_54842.cs | 32 +++++++++++++++++++ .../Runtime_54842/Runtime_54842.csproj | 12 +++++++ 3 files changed, 57 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.csproj diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ffad32a61b2..ef18f2292ec 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -1271,6 +1271,19 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, } toType = op2->CastToType(); + + // Casts to TYP_UINT produce the same ranges as casts to TYP_INT, + // except in overflow cases which we do not yet handle. To avoid + // issues with the propagation code dropping, e. g., CAST_OVF(uint <- int) + // based on an assertion created from CAST(uint <- ulong), normalize the + // type for the range here. Note that TYP_ULONG theoretically has the same + // problem, but we do not create assertions for it. + // TODO-Cleanup: this assertion is not useful - this code exists to preserve + // previous behavior. Refactor it to stop generating such assertions. + if (toType == TYP_UINT) + { + toType = TYP_INT; + } SUBRANGE_COMMON: if ((assertionKind != OAK_SUBRANGE) && (assertionKind != OAK_EQUAL)) { diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs new file mode 100644 index 00000000000..8941e1c2d59 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; + +public class Runtime_54842 +{ + public static int Main() + { + try + { + DoubleCheckedConvert(uint.MaxValue); + } + catch (OverflowException) + { + return 100; + } + + return 101; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint DoubleCheckedConvert(ulong a) + { + var b = (int)checked((uint)a); + + // Make sure the importer spills "b" to a local. + Use(b); + + return checked((uint)b); + } + + private static void Use(int value) { } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.csproj new file mode 100644 index 00000000000..f3e1cbd44b4 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + From ab219299db07b76fc06482bf3a2bfbd7c5f75e63 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Wed, 14 Jul 2021 18:01:07 -0700 Subject: [PATCH 571/926] Use XmlWriter.Create instead of XmlTextWriter (#54949) * Use a different XmlWriter which will check for invalid characters like null. * Bring our XmlReader use into the 21st century as well. * Update src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs Co-authored-by: Stephen Toub * Update src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs Co-authored-by: Stephen Toub * Update src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs Co-authored-by: Stephen Toub * Don't need to specify options that are defaults on XmlWriter. Co-authored-by: Stephen Toub --- .../src/System/Xml/Serialization/XmlSerializer.cs | 13 +++---------- .../XmlSerializer/XmlSerializerTests.RuntimeOnly.cs | 7 +++++++ .../tests/XmlSerializer/XmlSerializerTests.cs | 11 +++++++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 9241eadc040..f0310fa2abb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -317,9 +317,7 @@ namespace System.Xml.Serialization [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(TextWriter textWriter, object? o, XmlSerializerNamespaces? namespaces) { - XmlTextWriter xmlWriter = new XmlTextWriter(textWriter); - xmlWriter.Formatting = Formatting.Indented; - xmlWriter.Indentation = 2; + XmlWriter xmlWriter = XmlWriter.Create(textWriter, new XmlWriterSettings() { Indent = true }); Serialize(xmlWriter, o, namespaces); } @@ -332,9 +330,7 @@ namespace System.Xml.Serialization [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(Stream stream, object? o, XmlSerializerNamespaces? namespaces) { - XmlTextWriter xmlWriter = new XmlTextWriter(stream, null); - xmlWriter.Formatting = Formatting.Indented; - xmlWriter.Indentation = 2; + XmlWriter xmlWriter = XmlWriter.Create(stream, new XmlWriterSettings() { Indent = true }); Serialize(xmlWriter, o, namespaces); } @@ -421,10 +417,7 @@ namespace System.Xml.Serialization [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(Stream stream) { - XmlTextReader xmlReader = new XmlTextReader(stream); - xmlReader.WhitespaceHandling = WhitespaceHandling.Significant; - xmlReader.Normalization = true; - xmlReader.XmlResolver = null; + XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { IgnoreWhitespace = true }); return Deserialize(xmlReader, null); } diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs index 167e812048c..c81f2df09c1 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -275,6 +275,13 @@ public static partial class XmlSerializerTests "Hello World! \u6F22 \u00F1")); } + [Fact] + public static void Xml_StringWithNullChar() + { + Assert.Throws(() => SerializeWithDefaultValue("Sample\0String", null)); + Assert.Throws(() => DeserializeFromXmlString("Sample�String")); + } + [Fact] public static void Xml_UintAsRoot() { diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index e648a51d5c6..6df78b2ab90 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -1764,6 +1764,17 @@ string.Format(@" } } + private static T DeserializeFromXmlString(string xmlString) + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + using (Stream ms = GenerateStreamFromString(xmlString)) + { + T value = (T)serializer.Deserialize(ms); + return value; + } + + } + [Fact] public static void Xml_TypeWithMismatchBetweenAttributeAndPropertyType() { From e3d319bb727efb82f1ab4236f6f58a5dfffcfc5c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 14 Jul 2021 18:04:02 -0700 Subject: [PATCH 572/926] Handle getting the address of an RVA static correctly in a composite image (#55301) * Handle getting the address of an RVA static correctly in a composite image - Since composite images have the original RVA statics in the original files, use the value from there - This required re-enabling the ability of getFieldAddress to return an indirection - And adding some new processing for the fixup as needed * Silence pointless assert - There is an assert around incorrect handling of byref pointers, that isn't right. It fails for various issues with the Unsafe.* apis under jit stress, and this change makes it occur during one of our regular builds. As this is just an assert firing when it isn't appropriate, I'm disabling the warning as suggested by the JIT team. --- src/coreclr/jit/emitxarch.cpp | 10 ++++--- src/coreclr/jit/importer.cpp | 27 ++++++++++++++++--- .../ReadyToRunSymbolNodeFactory.cs | 15 +++++++++++ .../Compiler/ReadyToRunCodegenCompilation.cs | 2 +- src/coreclr/vm/jitinterface.cpp | 15 +++++++++++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index c35a6675fe7..076250103bb 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -12226,10 +12226,12 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) regMaskTP regMask; regMask = genRegMask(reg1) | genRegMask(reg2); - // r1/r2 could have been a GCREF as GCREF + int=BYREF - // or BYREF+/-int=BYREF - assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || - ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub))); +// Assert disabled as requested in PR 55301. A use of the Unsafe api +// which produces correct code, but isn't handled correctly here. +// r1/r2 could have been a GCREF as GCREF + int=BYREF +// or BYREF+/-int=BYREF +// assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || +// ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub))); #endif // Mark r1 as holding a byref emitGCregLiveUpd(GCT_BYREF, reg1, dst); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b3857cdec58..b4fb5a0a20e 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -7704,14 +7704,24 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT void** pFldAddr = nullptr; void* fldAddr = info.compCompHnd->getFieldAddress(pResolvedToken->hField, (void**)&pFldAddr); - // We should always be able to access this static's address directly - // +// We should always be able to access this static's address directly unless this is a field RVA +// used within a composite image during R2R composite image building. +// +#ifdef FEATURE_READYTORUN_COMPILER + assert((pFldAddr == nullptr) || + (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_RVA_ADDRESS && opts.IsReadyToRun())); +#else assert(pFldAddr == nullptr); +#endif FieldSeqNode* fldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField); /* Create the data member node */ - op1 = gtNewIconHandleNode(pFldAddr == nullptr ? (size_t)fldAddr : (size_t)pFldAddr, GTF_ICON_STATIC_HDL, + op1 = gtNewIconHandleNode(pFldAddr == nullptr ? (size_t)fldAddr : (size_t)pFldAddr, +#ifdef FEATURE_READYTORUN_COMPILER + pFldAddr != nullptr ? GTF_ICON_CONST_PTR : +#endif + GTF_ICON_STATIC_HDL, fldSeq); #ifdef DEBUG op1->AsIntCon()->gtTargetHandle = op1->AsIntCon()->gtIconVal; @@ -7721,6 +7731,17 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT { op1->gtFlags |= GTF_ICON_INITCLASS; } + +#ifdef FEATURE_READYTORUN_COMPILER + if (pFldAddr != nullptr) + { + // Indirection used to get to initial actual field RVA when building a composite image + // where the actual field does not move from the original file + assert(!varTypeIsGC(lclTyp)); + op1 = gtNewOperNode(GT_IND, TYP_I_IMPL, op1); + op1->gtFlags |= GTF_IND_INVARIANT | GTF_IND_NONFAULTING; + } +#endif } else // We need the value of a static field { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 38b1893f531..4302d1ecca5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -64,6 +64,14 @@ namespace ILCompiler.DependencyAnalysis new ReadyToRunInstructionSetSupportSignature(key)); }); + _precodeFieldAddressCache = new NodeCache(key => + { + return new PrecodeHelperImport( + _codegenNodeFactory, + new FieldFixupSignature(ReadyToRunFixupKind.FieldAddress, key, _codegenNodeFactory) + ); + }); + _fieldAddressCache = new NodeCache(key => { return new DelayLoadHelperImport( @@ -398,6 +406,13 @@ namespace ILCompiler.DependencyAnalysis return _fieldAddressCache.GetOrAdd(fieldDesc); } + private NodeCache _precodeFieldAddressCache; + + public ISymbolNode PrecodeFieldAddress(FieldDesc fieldDesc) + { + return _precodeFieldAddressCache.GetOrAdd(fieldDesc); + } + private NodeCache _fieldOffsetCache; public ISymbolNode FieldOffset(FieldDesc fieldDesc) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index ac7058bf90d..c1e4c69013f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -641,7 +641,7 @@ namespace ILCompiler } } - public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CopiedFieldRva(field); + public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CompilationModuleGroup.IsCompositeBuildMode ? SymbolNodeFactory.PrecodeFieldAddress(field) : NodeFactory.CopiedFieldRva(field); public override void Dispose() { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b19fa365323..6d6c104cbb9 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14034,6 +14034,21 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, break; #endif // PROFILING_SUPPORTED + case ENCODE_FIELD_ADDRESS: + { + FieldDesc *pField = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); + + pField->GetEnclosingMethodTable()->CheckRestore(); + + // We can only take address of RVA field here as we don't handle scenarios where the static variable may move + // Also, this cannot be used with a ZapImage as it relies on a separate signatures block as an RVA static + // address may be unaligned which would interfere with the tagged pointer approach. + _ASSERTE(currentModule->IsReadyToRun()); + _ASSERTE(pField->IsRVA()); + result = (size_t)pField->GetStaticAddressHandle(NULL); + } + break; + case ENCODE_STATIC_FIELD_ADDRESS: { FieldDesc *pField = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); From d3a8a19db1d8df774927f35f3f6d393ffbd075cb Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Wed, 14 Jul 2021 18:49:39 -0700 Subject: [PATCH 573/926] rework SocketsHttpHandler request queue handling for HTTP/1.1 and HTTP/2 (#53851) * rework SocketsHttpHandler request queue handling for HTTP/1.1 and HTTP/2 Co-authored-by: Geoffrey Kizer --- .../TaskCompletionSourceWithCancellation.cs | 15 + .../Tracing/ConsoleEventListener.cs | 2 +- .../Net/Http/HttpClientHandlerTest.Cookies.cs | 2 +- .../src/Resources/Strings.resx | 3 + .../AuthenticationHelper.NtAuth.cs | 6 +- .../SocketsHttpHandler/Http2Connection.cs | 604 ++++---- .../SocketsHttpHandler/Http3Connection.cs | 8 +- .../Http/SocketsHttpHandler/HttpConnection.cs | 46 +- .../SocketsHttpHandler/HttpConnectionBase.cs | 20 +- .../SocketsHttpHandler/HttpConnectionPool.cs | 1370 ++++++++++------- .../HttpClientHandlerTest.Http2.cs | 17 +- .../SocketsHttpHandlerTest.Cancellation.cs | 53 +- .../tests/FunctionalTests/TelemetryTest.cs | 25 +- 13 files changed, 1252 insertions(+), 919 deletions(-) diff --git a/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs b/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs index 5fd1509e7bd..8964fdba9a7 100644 --- a/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs +++ b/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs @@ -21,5 +21,20 @@ namespace System.Threading.Tasks return await Task.ConfigureAwait(false); } } + + public T WaitWithCancellation(CancellationToken cancellationToken) + { + using (cancellationToken.UnsafeRegister(static (s, cancellationToken) => ((TaskCompletionSourceWithCancellation)s!).TrySetCanceled(cancellationToken), this)) + { + return Task.GetAwaiter().GetResult(); + } + } + + public ValueTask WaitWithCancellationAsync(bool async, CancellationToken cancellationToken) + { + return async ? + WaitWithCancellationAsync(cancellationToken) : + new ValueTask(WaitWithCancellation(cancellationToken)); + } } } diff --git a/src/libraries/Common/tests/System/Diagnostics/Tracing/ConsoleEventListener.cs b/src/libraries/Common/tests/System/Diagnostics/Tracing/ConsoleEventListener.cs index 457b8e1660b..98b51d70539 100644 --- a/src/libraries/Common/tests/System/Diagnostics/Tracing/ConsoleEventListener.cs +++ b/src/libraries/Common/tests/System/Diagnostics/Tracing/ConsoleEventListener.cs @@ -32,7 +32,7 @@ namespace System.Diagnostics.Tracing { lock (Console.Out) { - string text = $"[{eventData.EventSource.Name}-{eventData.EventId}]{(eventData.Payload != null ? $" ({string.Join(", ", eventData.Payload)})." : "")}"; + string text = $"[{eventData.EventSource.Name}-{eventData.EventName}]{(eventData.Payload != null ? $" ({string.Join(", ", eventData.Payload)})." : "")}"; if (_eventFilter != null && text.Contains(_eventFilter)) { ConsoleColor origForeground = Console.ForegroundColor; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index dbb1382c9aa..09b58739d5b 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -315,7 +315,7 @@ namespace System.Net.Http.Functional.Tests using (HttpClient client = CreateHttpClient(handler)) { client.DefaultRequestHeaders.ConnectionClose = true; // to avoid issues with connection pooling - await client.GetAsync(url1); + await client.GetAsync(url1); } }, async server => diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 03081881762..501ca3c8f97 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -540,6 +540,9 @@ The request was canceled due to the configured HttpClient.Timeout of {0} seconds elapsing. + + A connection could not be established within the configured ConnectTimeout. + Connection aborted by peer ({0}). diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index e9468042f64..8ee91309381 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -63,8 +63,12 @@ namespace System.Net.Http if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. + + // First, detach the current connection from the pool. This means it will no longer count against the connection limit. + // Instead, it will be replaced by the new connection below. + connection.DetachFromPool(); + connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); - connectionPool.IncrementConnectionCount(); connection!.Acquire(); isNewConnection = true; needDrain = false; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index af132084673..26058a454a9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; @@ -15,11 +14,10 @@ using System.Text; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using Microsoft.Extensions.Internal; namespace System.Net.Http { - internal sealed partial class Http2Connection : HttpConnectionBase, IDisposable + internal sealed partial class Http2Connection : HttpConnectionBase { private readonly HttpConnectionPool _pool; private readonly Stream _stream; @@ -37,36 +35,41 @@ namespace System.Net.Http private readonly Dictionary _httpStreams; private readonly CreditManager _connectionWindow; - private readonly CreditManager _concurrentStreams; private RttEstimator _rttEstimator; private int _nextStream; private bool _expectingSettingsAck; private int _initialServerStreamWindowSize; - private int _maxConcurrentStreams; private int _pendingWindowUpdate; private long _idleSinceTickCount; + private uint _maxConcurrentStreams; + private uint _streamsInUse; + private TaskCompletionSource? _availableStreamsWaiter; + private readonly Channel _writeChannel; private bool _lastPendingWriterShouldFlush; - // This means that the pool has disposed us, but there may still be - // requests in flight that will continue to be processed. - private bool _disposed; + // This flag indicates that the connection is shutting down and cannot accept new requests, because of one of the following conditions: + // (1) We received a GOAWAY frame from the server + // (2) We have exhaustead StreamIds (i.e. _nextStream == MaxStreamId) + // (3) A connection-level error occurred, in which case _abortException below is set. + private bool _shutdown; + private TaskCompletionSource? _shutdownWaiter; - // This will be set when: - // (1) We receive GOAWAY -- will be set to the value sent in the GOAWAY frame - // (2) A connection IO error occurs -- will be set to int.MaxValue - // (meaning we must assume all streams have been processed by the server) - private int _lastStreamId = -1; + // If this is set, the connection is aborting due to an IO failure (IOException) or a protocol violation (Http2ProtocolException). + // _shutdown above is true, and requests in flight have been (or are being) failed. + private Exception? _abortException; + + // This means that the user (i.e. the connection pool) has disposed us and will not submit further requests. + // Requests currently in flight will continue to be processed. + // When all requests have completed, the connection will be torn down. + private bool _disposed; private const int TelemetryStatus_Opened = 1; private const int TelemetryStatus_Closed = 2; private int _markedByTelemetryStatus; - // This will be set when a connection IO error occurs - private Exception? _abortException; - private const int MaxStreamId = int.MaxValue; // Temporary workaround for request burst handling on connection start. @@ -125,6 +128,7 @@ namespace System.Net.Http { _pool = pool; _stream = stream; + _incomingBuffer = new ArrayBuffer(InitialConnectionBufferSize); _outgoingBuffer = new ArrayBuffer(InitialConnectionBufferSize); @@ -133,7 +137,6 @@ namespace System.Net.Http _httpStreams = new Dictionary(); _connectionWindow = new CreditManager(this, nameof(_connectionWindow), DefaultInitialWindowSize); - _concurrentStreams = new CreditManager(this, nameof(_concurrentStreams), InitialMaxConcurrentStreams); _rttEstimator = RttEstimator.Create(); @@ -143,10 +146,11 @@ namespace System.Net.Http _initialServerStreamWindowSize = DefaultInitialWindowSize; _maxConcurrentStreams = InitialMaxConcurrentStreams; + _streamsInUse = 0; + _pendingWindowUpdate = 0; _idleSinceTickCount = Environment.TickCount64; - _keepAlivePingDelay = TimeSpanToMs(_pool.Settings._keepAlivePingDelay); _keepAlivePingTimeout = TimeSpanToMs(_pool.Settings._keepAlivePingTimeout); _nextPingRequestTimestamp = Environment.TickCount64 + _keepAlivePingDelay; @@ -170,8 +174,6 @@ namespace System.Net.Http private object SyncObject => _httpStreams; - public bool CanAddNewStream => _concurrentStreams.IsCreditAvailable; - public async ValueTask SetupAsync() { try @@ -222,6 +224,158 @@ namespace System.Net.Http _ = ProcessOutgoingFramesAsync(); } + // This will complete when the connection begins to shut down and cannot be used anymore, or if it is disposed. + public ValueTask WaitForShutdownAsync() + { + lock (SyncObject) + { + if (_disposed) + { + Debug.Fail("As currently used, we don't expect to call this after disposing and we don't handle the ODE"); + throw new ObjectDisposedException(nameof(Http2Connection)); + } + + if (_shutdown) + { + Debug.Assert(_shutdownWaiter is null); + return default; + } + + _shutdownWaiter ??= new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + return new ValueTask(_shutdownWaiter.Task); + } + } + + private void Shutdown() + { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_shutdown)}={_shutdown}, {nameof(_abortException)}={_abortException}"); + + Debug.Assert(Monitor.IsEntered(SyncObject)); + + SignalAvailableStreamsWaiter(false); + SignalShutdownWaiter(); + + // Note _shutdown could already be set, but that's fine. + _shutdown = true; + } + + private void SignalShutdownWaiter() + { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_shutdownWaiter)}?={_shutdownWaiter is not null}"); + + Debug.Assert(Monitor.IsEntered(SyncObject)); + + if (_shutdownWaiter is not null) + { + Debug.Assert(!_disposed); + Debug.Assert(!_shutdown); + _shutdownWaiter.SetResult(); + _shutdownWaiter = null; + } + } + + public bool TryReserveStream() + { + lock (SyncObject) + { + if (_disposed) + { + Debug.Fail("As currently used, we don't expect to call this after disposing and we don't handle the ODE"); + throw new ObjectDisposedException(nameof(Http2Connection)); + } + + if (_shutdown) + { + return false; + } + + if (_streamsInUse < _maxConcurrentStreams) + { + _streamsInUse++; + return true; + } + } + + return false; + } + + // Can be called by the HttpConnectionPool after TryReserveStream if the stream doesn't end up being used. + // Otherwise, will be called when the request is complete and stream is closed. + public void ReleaseStream() + { + lock (SyncObject) + { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_streamsInUse)}={_streamsInUse}"); + + Debug.Assert(_availableStreamsWaiter is null || _streamsInUse >= _maxConcurrentStreams); + + _streamsInUse--; + + Debug.Assert(_streamsInUse >= _httpStreams.Count); + + if (_streamsInUse < _maxConcurrentStreams) + { + SignalAvailableStreamsWaiter(true); + } + + if (_streamsInUse == 0) + { + _idleSinceTickCount = Environment.TickCount64; + + if (_disposed) + { + FinalTeardown(); + } + } + } + } + + // Returns true to indicate at least one stream is available + // Returns false to indicate that the connection is shutting down and cannot be used anymore + public ValueTask WaitForAvailableStreamsAsync() + { + lock (SyncObject) + { + if (_disposed) + { + Debug.Fail("As currently used, we don't expect to call this after disposing and we don't handle the ODE"); + throw new ObjectDisposedException(nameof(Http2Connection)); + } + + Debug.Assert(_availableStreamsWaiter is null, "As used currently, shouldn't already have a waiter"); + + if (_shutdown) + { + return ValueTask.FromResult(false); + } + + if (_streamsInUse < _maxConcurrentStreams) + { + return ValueTask.FromResult(true); + } + + // Need to wait for streams to become available. + _availableStreamsWaiter = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + return new ValueTask(_availableStreamsWaiter.Task); + } + } + + private void SignalAvailableStreamsWaiter(bool result) + { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(result)}={result}, {nameof(_availableStreamsWaiter)}?={_availableStreamsWaiter is not null}"); + + Debug.Assert(Monitor.IsEntered(SyncObject)); + + if (_availableStreamsWaiter is not null) + { + Debug.Assert(!_disposed); + Debug.Assert(!_shutdown); + _availableStreamsWaiter.SetResult(result); + _availableStreamsWaiter = null; + } + } + private async Task FlushOutgoingBytesAsync() { if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_outgoingBuffer.ActiveLength)}={_outgoingBuffer.ActiveLength}"); @@ -305,7 +459,6 @@ namespace System.Net.Http void ThrowMissingFrame() => throw new IOException(SR.net_http_invalid_response_missing_frame); - } private async Task ProcessIncomingFramesAsync() @@ -695,16 +848,18 @@ namespace System.Net.Http private void ChangeMaxConcurrentStreams(uint newValue) { - if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(newValue)}={newValue}"); + lock (SyncObject) + { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(newValue)}={newValue}, {nameof(_streamsInUse)}={_streamsInUse}, {nameof(_availableStreamsWaiter)}?={_availableStreamsWaiter is not null}"); - // The value is provided as a uint. - // Limit this to int.MaxValue since the CreditManager implementation only supports singed values. - // In practice, we should never reach this value. - int effectiveValue = newValue > (uint)int.MaxValue ? int.MaxValue : (int)newValue; - int delta = effectiveValue - _maxConcurrentStreams; - _maxConcurrentStreams = effectiveValue; + Debug.Assert(_availableStreamsWaiter is null || _streamsInUse >= _maxConcurrentStreams); - _concurrentStreams.AdjustCredit(delta); + _maxConcurrentStreams = newValue; + if (_streamsInUse < _maxConcurrentStreams) + { + SignalAvailableStreamsWaiter(true); + } + } } private void ChangeInitialWindowSize(int newSize) @@ -854,20 +1009,45 @@ namespace System.Net.Http ThrowProtocolError(Http2ProtocolErrorCode.FrameSizeError); } - // GoAway frames always apply to the whole connection, never to a single stream. - // According to RFC 7540 section 6.8, this should be a connection error. if (frameHeader.StreamId != 0) { ThrowProtocolError(); } - int lastValidStream = (int)(BinaryPrimitives.ReadUInt32BigEndian(_incomingBuffer.ActiveSpan) & 0x7FFFFFFF); - var errorCode = (Http2ProtocolErrorCode)BinaryPrimitives.ReadInt32BigEndian(_incomingBuffer.ActiveSpan.Slice(sizeof(int))); - if (NetEventSource.Log.IsEnabled()) Trace(frameHeader.StreamId, $"{nameof(lastValidStream)}={lastValidStream}, {nameof(errorCode)}={errorCode}"); - - StartTerminatingConnection(lastValidStream, new Http2ConnectionException(errorCode)); + int lastStreamId = (int)(BinaryPrimitives.ReadUInt32BigEndian(_incomingBuffer.ActiveSpan) & 0x7FFFFFFF); + Http2ProtocolErrorCode errorCode = (Http2ProtocolErrorCode)BinaryPrimitives.ReadInt32BigEndian(_incomingBuffer.ActiveSpan.Slice(sizeof(int))); + if (NetEventSource.Log.IsEnabled()) Trace(frameHeader.StreamId, $"{nameof(lastStreamId)}={lastStreamId}, {nameof(errorCode)}={errorCode}"); _incomingBuffer.Discard(frameHeader.PayloadLength); + + Debug.Assert(lastStreamId >= 0); + Exception resetException = new Http2ConnectionException(errorCode); + + // There is no point sending more PING frames for RTT estimation: + _rttEstimator.OnGoAwayReceived(); + + List streamsToAbort = new List(); + lock (SyncObject) + { + Shutdown(); + + foreach (KeyValuePair kvp in _httpStreams) + { + int streamId = kvp.Key; + Debug.Assert(streamId == kvp.Value.StreamId); + + if (streamId > lastStreamId) + { + streamsToAbort.Add(kvp.Value); + } + } + } + + // Avoid calling OnReset under the lock, as it may cause the Http2Stream to call back in to RemoveStream + foreach (Http2Stream s in streamsToAbort) + { + s.OnReset(resetException, canRetry: true); + } } internal Task FlushAsync(CancellationToken cancellationToken) => @@ -924,8 +1104,16 @@ namespace System.Net.Http if (!_writeChannel.Writer.TryWrite(writeEntry)) { - Debug.Assert(_abortException is not null); - return Task.FromException(GetRequestAbortedException(_abortException)); + if (_abortException is not null) + { + return Task.FromException(GetRequestAbortedException(_abortException)); + } + + // We must be trying to send something asynchronously (like RST_STREAM or a PING or a SETTINGS ACK) and it has raced with the connection tear down. + // As such, it should not matter that we were not able to actually send the frame. + // But just in case, throw ObjectDisposedException. Asynchronous callers will ignore the failure. + Debug.Assert(_disposed && _streamsInUse == 0); + return Task.FromException(new ObjectDisposedException(nameof(Http2Connection))); } return writeEntry.Task; @@ -994,9 +1182,6 @@ namespace System.Net.Http await FlushOutgoingBytesAsync().ConfigureAwait(false); } } - - // Connection should be aborting at this point. - Debug.Assert(_abortException is not null); } catch (Exception e) { @@ -1297,83 +1482,44 @@ namespace System.Net.Http } } - [DoesNotReturn] - private void ThrowShutdownException() + private void AddStream(Http2Stream http2Stream) { - Debug.Assert(Monitor.IsEntered(SyncObject)); - - if (_abortException != null) + lock (SyncObject) { - // We had an IO failure on the connection. Don't retry in this case. - throw new HttpRequestException(SR.net_http_client_execution_error, _abortException); - } + if (_nextStream == MaxStreamId) + { + // We have exhausted StreamIds. Shut down the connection. + Shutdown(); + } - // Connection is being gracefully shutdown. Allow the request to be retried. - Exception innerException; - if (_lastStreamId != -1) - { - // We must have received a GOAWAY. - innerException = new IOException(SR.net_http_server_shutdown); - } - else - { - // We must either be disposed or out of stream IDs. - Debug.Assert(_disposed || _nextStream == MaxStreamId); + if (_shutdown) + { + // The connection has shut down. Throw a retryable exception so that this request will be handled on another connection. + ThrowRetry(SR.net_http_server_shutdown); + } - innerException = new ObjectDisposedException(nameof(Http2Connection)); - } + if (_streamsInUse > _maxConcurrentStreams) + { + // The server must have sent a downward adjustment to SETTINGS_MAX_CONCURRENT_STREAMS, so our previous stream reservation is no longer valid. + // We might want a better exception message here, but in general the user shouldn't see this anyway since it will be retried. + ThrowRetry(SR.net_http_request_aborted); + } - ThrowRetry(SR.net_http_client_execution_error, innerException); + // Now that we're holding the lock, configure the stream. The lock must be held while + // assigning the stream ID to ensure only one stream gets an ID, and it must be held + // across setting the initial window size (available credit) and storing the stream into + // collection such that window size updates are able to atomically affect all known streams. + http2Stream.Initialize(_nextStream, _initialServerStreamWindowSize); + + // Client-initiated streams are always odd-numbered, so increase by 2. + _nextStream += 2; + + _httpStreams.Add(http2Stream.StreamId, http2Stream); + } } private async ValueTask SendHeadersAsync(HttpRequestMessage request, CancellationToken cancellationToken, bool mustFlush) { - // Enforce MAX_CONCURRENT_STREAMS setting value. We do this before anything else, e.g. renting buffers to serialize headers, - // in order to avoid consuming resources in potentially many requests waiting for access. - try - { - if (!_concurrentStreams.TryRequestCreditNoWait(1)) - { - if (_pool.EnableMultipleHttp2Connections) - { - throw new HttpRequestException(null, null, RequestRetryType.RetryOnStreamLimitReached); - } - - if (HttpTelemetry.Log.IsEnabled()) - { - // Only log Http20RequestLeftQueue if we spent time waiting on the queue - ValueStopwatch stopwatch = ValueStopwatch.StartNew(); - await _concurrentStreams.RequestCreditAsync(1, cancellationToken).ConfigureAwait(false); - HttpTelemetry.Log.Http20RequestLeftQueue(stopwatch.GetElapsedTime().TotalMilliseconds); - } - else - { - await _concurrentStreams.RequestCreditAsync(1, cancellationToken).ConfigureAwait(false); - } - } - } - catch (ObjectDisposedException) - { - // We have race condition between shutting down and initiating new requests. - // When we are shutting down the connection (e.g. due to receiving GOAWAY, etc) - // we will wait until the stream count goes to 0, and then we will close the connetion - // and perform clean up, including disposing _concurrentStreams. - // So if we get ObjectDisposedException here, we must have shut down the connection. - // Throw a retryable request exception if this is not result of some other error. - // This will cause retry logic to kick in and perform another connection attempt. - // The user should never see this exception. See similar handling below. - // Throw a retryable request exception if this is not result of some other error. - // This will cause retry logic to kick in and perform another connection attempt. - // The user should never see this exception. See also below. - lock (SyncObject) - { - Debug.Assert(_disposed || _lastStreamId != -1); - Debug.Assert(_httpStreams.Count == 0); - ThrowShutdownException(); - throw; // unreachable - } - } - ArrayBuffer headerBuffer = default; try { @@ -1402,32 +1548,7 @@ namespace System.Net.Http { if (NetEventSource.Log.IsEnabled()) s.thisRef.Trace(s.http2Stream.StreamId, $"Started writing. Total header bytes={s.headerBytes.Length}"); - // Allocate the next available stream ID. Note that if we fail before sending the headers, - // we'll just skip this stream ID, which is fine. - lock (s.thisRef.SyncObject) - { - if (s.thisRef._nextStream == MaxStreamId || s.thisRef._disposed || s.thisRef._lastStreamId != -1) - { - // We ran out of stream IDs or we raced between acquiring the connection from the pool and shutting down. - // Throw a retryable request exception. This will cause retry logic to kick in - // and perform another connection attempt. The user should never see this exception. - s.thisRef.ThrowShutdownException(); - } - - // Now that we're holding the lock, configure the stream. The lock must be held while - // assigning the stream ID to ensure only one stream gets an ID, and it must be held - // across setting the initial window size (available credit) and storing the stream into - // collection such that window size updates are able to atomically affect all known streams. - s.http2Stream.Initialize(s.thisRef._nextStream, s.thisRef._initialServerStreamWindowSize); - - // Client-initiated streams are always odd-numbered, so increase by 2. - s.thisRef._nextStream += 2; - - // We're about to flush the HEADERS frame, so add the stream to the dictionary now. - // The lifetime of the stream is now controlled by the stream itself and the connection. - // This can fail if the connection is shutting down, in which case we will cancel sending this frame. - s.thisRef._httpStreams.Add(s.http2Stream.StreamId, s.http2Stream); - } + s.thisRef.AddStream(s.http2Stream); Span span = writeBuffer.Span; @@ -1466,7 +1587,7 @@ namespace System.Net.Http } catch { - _concurrentStreams.AdjustCredit(1); + ReleaseStream(); throw; } finally @@ -1572,94 +1693,33 @@ namespace System.Net.Http LogExceptions(SendWindowUpdateAsync(0, windowUpdateSize)); } + public override long GetIdleTicks(long nowTicks) + { + lock (SyncObject) + { + return _streamsInUse == 0 ? _idleSinceTickCount - nowTicks : 0; + } + } + /// Abort all streams and cause further processing to fail. /// Exception causing Abort to be called. private void Abort(Exception abortException) { - // The connection has failed, e.g. failed IO or a connection-level frame error. - bool alreadyAborting = false; - lock (SyncObject) - { - if (_abortException is null) - { - _abortException = abortException; - } - else - { - alreadyAborting = true; - } - } - - if (alreadyAborting) - { - if (NetEventSource.Log.IsEnabled()) Trace($"Abort called while already aborting. {nameof(abortException)}=={abortException}"); - - return; - } - - if (NetEventSource.Log.IsEnabled()) Trace($"Abort initiated. {nameof(abortException)}=={abortException}"); - - _writeChannel.Writer.Complete(); - - AbortStreams(_abortException); - } - - /// Gets whether the connection exceeded any of the connection limits. - /// The current tick count. Passed in to amortize the cost of calling Environment.TickCount64. - /// How long a connection can be open to be considered reusable. - /// How long a connection can have been idle in the pool to be considered reusable. - /// - /// true if we believe the connection is expired; otherwise, false. There is an inherent race condition here, - /// in that the server could terminate the connection or otherwise make it unusable immediately after we check it, - /// but there's not much difference between that and starting to use the connection and then having the server - /// terminate it, which would be considered a failure, so this race condition is largely benign and inherent to - /// the nature of connection pooling. - /// - public bool IsExpired(long nowTicks, - TimeSpan connectionLifetime, - TimeSpan connectionIdleTimeout) - { - if (_disposed) - { - return true; - } - - // Check idle timeout when there are not pending requests for a while. - if ((connectionIdleTimeout != Timeout.InfiniteTimeSpan) && - (_httpStreams.Count == 0) && - ((nowTicks - _idleSinceTickCount) > connectionIdleTimeout.TotalMilliseconds)) - { - if (NetEventSource.Log.IsEnabled()) Trace($"HTTP/2 connection no longer usable. Idle {TimeSpan.FromMilliseconds(nowTicks - _idleSinceTickCount)} > {connectionIdleTimeout}."); - - return true; - } - - if (LifetimeExpired(nowTicks, connectionLifetime)) - { - if (NetEventSource.Log.IsEnabled()) Trace($"HTTP/2 connection no longer usable. Lifetime {TimeSpan.FromMilliseconds(nowTicks - CreationTickCount)} > {connectionLifetime}."); - - return true; - } - - return false; - } - - private void AbortStreams(Exception abortException) - { - if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(abortException)}={abortException}"); - - // Invalidate outside of lock to avoid race with HttpPool Dispose() - // We should not try to grab pool lock while holding connection lock as on disposing pool, - // we could hold pool lock while trying to grab connection lock in Dispose(). - _pool.InvalidateHttp2Connection(this); + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(abortException)}=={abortException}"); + // The connection has failed, e.g. failed IO or a connection-level protocol error. List streamsToAbort = new List(); - lock (SyncObject) { - // Set _lastStreamId to int.MaxValue to indicate that we are shutting down - // and we must assume all active streams have been processed by the server - _lastStreamId = int.MaxValue; + if (_abortException is not null) + { + if (NetEventSource.Log.IsEnabled()) Trace($"Abort called while already aborting. {nameof(abortException)}={abortException}"); + return; + } + + _abortException = abortException; + + Shutdown(); foreach (KeyValuePair kvp in _httpStreams) { @@ -1668,91 +1728,28 @@ namespace System.Net.Http streamsToAbort.Add(kvp.Value); } - - CheckForShutdown(); } - // Avoid calling OnAbort under the lock, as it may cause the Http2Stream - // to call back in to RemoveStream + // Avoid calling OnReset under the lock, as it may cause the Http2Stream to call back in to RemoveStream foreach (Http2Stream s in streamsToAbort) { - s.OnReset(abortException); + s.OnReset(_abortException); } } - private void StartTerminatingConnection(int lastValidStream, Exception abortException) + private void FinalTeardown() { - Debug.Assert(lastValidStream >= 0); + if (NetEventSource.Log.IsEnabled()) Trace(""); - // Invalidate outside of lock to avoid race with HttpPool Dispose() - // We should not try to grab pool lock while holding connection lock as on disposing pool, - // we could hold pool lock while trying to grab connection lock in Dispose(). - _pool.InvalidateHttp2Connection(this); - - // There is no point sending more PING frames for RTT estimation: - _rttEstimator.OnGoAwayReceived(); - - List streamsToAbort = new List(); - - lock (SyncObject) - { - if (_lastStreamId == -1) - { - _lastStreamId = lastValidStream; - } - else - { - // We have already received GOAWAY before - // In this case the smaller valid stream is used - _lastStreamId = Math.Min(_lastStreamId, lastValidStream); - } - - if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(lastValidStream)}={lastValidStream}, {nameof(_lastStreamId)}={_lastStreamId}"); - - foreach (KeyValuePair kvp in _httpStreams) - { - int streamId = kvp.Key; - Debug.Assert(streamId == kvp.Value.StreamId); - - if (streamId > _lastStreamId) - { - streamsToAbort.Add(kvp.Value); - } - else - { - if (NetEventSource.Log.IsEnabled()) Trace($"Found {nameof(streamId)} {streamId} <= {_lastStreamId}."); - } - } - - CheckForShutdown(); - } - - // Avoid calling OnAbort under the lock, as it may cause the Http2Stream - // to call back in to RemoveStream - foreach (Http2Stream s in streamsToAbort) - { - s.OnReset(abortException, canRetry: true); - } - } - - private void CheckForShutdown() - { - Debug.Assert(_disposed || _lastStreamId != -1); - Debug.Assert(Monitor.IsEntered(SyncObject)); - - // Check if dictionary has become empty - if (_httpStreams.Count != 0) - { - return; - } + Debug.Assert(_disposed); + Debug.Assert(_streamsInUse == 0); GC.SuppressFinalize(this); - // Do shutdown. _stream.Dispose(); _connectionWindow.Dispose(); - _concurrentStreams.Dispose(); + _writeChannel.Writer.Complete(); if (HttpTelemetry.Log.IsEnabled()) { @@ -1763,15 +1760,23 @@ namespace System.Net.Http } } - public void Dispose() + public override void Dispose() { lock (SyncObject) { + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_disposed)}={_disposed}, {nameof(_streamsInUse)}={_streamsInUse}"); + if (!_disposed) { + SignalAvailableStreamsWaiter(false); + SignalShutdownWaiter(); + _disposed = true; - CheckForShutdown(); + if (_streamsInUse == 0) + { + FinalTeardown(); + } } } } @@ -1885,7 +1890,7 @@ namespace System.Net.Http // Note that this is safe to be called concurrently by multiple threads. - public sealed override async Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + public async Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { Debug.Assert(async); if (NetEventSource.Log.IsEnabled()) Trace($"{request}"); @@ -1969,7 +1974,6 @@ namespace System.Net.Http private void RemoveStream(Http2Stream http2Stream) { if (NetEventSource.Log.IsEnabled()) Trace(http2Stream.StreamId, ""); - Debug.Assert(http2Stream != null); lock (SyncObject) { @@ -1978,20 +1982,9 @@ namespace System.Net.Http Debug.Fail($"Stream {http2Stream.StreamId} not found in dictionary during RemoveStream???"); return; } - - if (_httpStreams.Count == 0) - { - // If this was last pending request, get timestamp so we can monitor idle time. - _idleSinceTickCount = Environment.TickCount64; - - if (_disposed || _lastStreamId != -1) - { - CheckForShutdown(); - } - } } - _concurrentStreams.AdjustCredit(1); + ReleaseStream(); } private void RefreshPingTimestamp() @@ -2023,7 +2016,10 @@ namespace System.Net.Http { lock (SyncObject) { - if (_httpStreams.Count == 0) return; + if (_streamsInUse == 0) + { + return; + } } } @@ -2067,7 +2063,7 @@ namespace System.Net.Http message); // message [DoesNotReturn] - private static void ThrowRetry(string message, Exception innerException) => + private static void ThrowRetry(string message, Exception? innerException = null) => throw new HttpRequestException(message, innerException, allowRetry: RequestRetryType.RetryOnConnectionFailure); private static Exception GetRequestAbortedException(Exception? innerException = null) => diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index 4667b7bec32..d3e6a90c430 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -18,7 +18,7 @@ namespace System.Net.Http [SupportedOSPlatform("windows")] [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] - internal sealed class Http3Connection : HttpConnectionBase, IDisposable + internal sealed class Http3Connection : HttpConnectionBase { // TODO: once HTTP/3 is standardized, create APIs for this. public static readonly SslApplicationProtocol Http3ApplicationProtocol29 = new SslApplicationProtocol("h3-29"); @@ -92,7 +92,7 @@ namespace System.Net.Http /// /// Starts shutting down the . Final cleanup will happen when there are no more active requests. /// - public void Dispose() + public override void Dispose() { lock (SyncObj) { @@ -155,7 +155,7 @@ namespace System.Net.Http } } - public override async Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + public async Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { Debug.Assert(async); @@ -329,6 +329,8 @@ namespace System.Net.Http } } + public override long GetIdleTicks(long nowTicks) => throw new NotImplementedException("We aren't scavenging HTTP3 connections yet"); + public override void Trace(string message, [CallerMemberName] string? memberName = null) => Trace(0, message, memberName); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 00e3f3b6fe3..7ef8ca09b06 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; namespace System.Net.Http { - internal sealed partial class HttpConnection : HttpConnectionBase, IDisposable + internal sealed partial class HttpConnection : HttpConnectionBase { /// Default size of the read buffer used for the connection. private const int InitialReadBufferSize = @@ -61,7 +61,9 @@ namespace System.Net.Http private int _readOffset; private int _readLength; + private long _idleSinceTickCount; private bool _inUse; + private bool _detachedFromPool; private bool _canRetry; private bool _startedSendingRequestBody; private bool _connectionClose; // Connection: close was seen on last response @@ -90,6 +92,8 @@ namespace System.Net.Http _weakThisRef = new WeakReference(this); + _idleSinceTickCount = Environment.TickCount64; + if (HttpTelemetry.Log.IsEnabled()) { HttpTelemetry.Log.Http11ConnectionEstablished(); @@ -101,7 +105,7 @@ namespace System.Net.Http ~HttpConnection() => Dispose(disposing: false); - public void Dispose() => Dispose(disposing: true); + public override void Dispose() => Dispose(disposing: true); private void Dispose(bool disposing) { @@ -118,7 +122,10 @@ namespace System.Net.Http HttpTelemetry.Log.Http11ConnectionClosed(); } - _pool.DecrementConnectionCount(); + if (!_detachedFromPool) + { + _pool.InvalidateHttp11Connection(this, disposing); + } if (disposing) { @@ -187,7 +194,7 @@ namespace System.Net.Http /// Check whether a currently idle connection is still usable, or should be scavenged. /// True if connection can be used, false if it is invalid due to receiving EOF or unexpected data. - public bool CheckUsabilityOnScavenge() + public override bool CheckUsabilityOnScavenge() { // We may already have a read-ahead task if we did a previous scavenge and haven't used the connection since. if (_readAheadTask is null) @@ -231,6 +238,8 @@ namespace System.Net.Http return null; } + public override long GetIdleTicks(long nowTicks) => nowTicks - _idleSinceTickCount; + public TransportContext? TransportContext => _transportContext; public HttpConnectionKind Kind => _pool.Kind; @@ -709,14 +718,24 @@ namespace System.Net.Http // Successful response to CONNECT does not have body. // What ever comes next should be opaque. responseStream = new RawConnectionStream(this); + // Don't put connection back to the pool if we upgraded to tunnel. // We cannot use it for normal HTTP requests any more. _connectionClose = true; + _pool.InvalidateHttp11Connection(this); + _detachedFromPool = true; } else if (response.StatusCode == HttpStatusCode.SwitchingProtocols) { responseStream = new RawConnectionStream(this); + + // Don't put connection back to the pool if we switched protocols. + // We cannot use it for normal HTTP requests any more. + _connectionClose = true; + + _pool.InvalidateHttp11Connection(this); + _detachedFromPool = true; } else if (response.Content.Headers.ContentLength != null) { @@ -802,7 +821,7 @@ namespace System.Net.Http } } - public sealed override Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) => + public Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) => SendAsyncCore(request, async, cancellationToken); private bool MapSendException(Exception exception, CancellationToken cancellationToken, out Exception mappedException) @@ -1932,6 +1951,17 @@ namespace System.Net.Http } } + /// + /// Detach the connection from the pool, so it is no longer counted against the connection limit. + /// This is used when we are creating a replacement connection for NT auth challenges. + /// + internal void DetachFromPool() + { + Debug.Assert(_inUse); + + _detachedFromPool = true; + } + private void CompleteResponse() { Debug.Assert(_currentRequest != null, "Expected the connection to be associated with a request."); @@ -2015,8 +2045,12 @@ namespace System.Net.Http } else { + Debug.Assert(!_detachedFromPool, "Should not be detached from pool unless _connectionClose is true"); + + _idleSinceTickCount = Environment.TickCount64; + // Put connection back in the pool. - _pool.ReturnConnection(this); + _pool.ReturnHttp11Connection(this); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs index d0e82bc6d46..ac3c852dc84 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace System.Net.Http { - internal abstract class HttpConnectionBase : IHttpTrace + internal abstract class HttpConnectionBase : IDisposable, IHttpTrace { /// Cached string for the last Date header received on this connection. private string? _lastDateHeaderValue; @@ -39,7 +39,6 @@ namespace System.Net.Http } } - public abstract Task SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken); public abstract void Trace(string message, [CallerMemberName] string? memberName = null); protected void TraceConnection(Stream stream) @@ -60,14 +59,15 @@ namespace System.Net.Http } } - public long CreationTickCount { get; } = Environment.TickCount64; + private readonly long _creationTickCount = Environment.TickCount64; - // Check if lifetime expired on connection. - public bool LifetimeExpired(long nowTicks, TimeSpan lifetime) - { - return lifetime != Timeout.InfiniteTimeSpan && - (lifetime == TimeSpan.Zero || (nowTicks - CreationTickCount) > lifetime.TotalMilliseconds); - } + public long GetLifetimeTicks(long nowTicks) => nowTicks - _creationTickCount; + + public abstract long GetIdleTicks(long nowTicks); + + /// Check whether a connection is still usable, or should be scavenged. + /// True if connection can be used. + public virtual bool CheckUsabilityOnScavenge() => true; internal static bool IsDigit(byte c) => (uint)(c - '0') <= '9' - '0'; @@ -126,5 +126,7 @@ namespace System.Net.Http if (NetEventSource.Log.IsEnabled()) connection.Trace($"Exception from asynchronous processing: {e}"); } } + + public abstract void Dispose(); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 480724dd8eb..c624cbd677e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -13,7 +13,7 @@ using System.Net.Quic; using System.Net.Security; using System.Net.Sockets; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.ExceptionServices; using System.Runtime.Versioning; using System.Security.Authentication; using System.Text; @@ -64,15 +64,31 @@ namespace System.Net.Http /// The time, in milliseconds, that an authority should remain in . private const int AltSvcBlocklistTimeoutInMilliseconds = 10 * 60 * 1000; - /// List of idle connections stored in the pool. - private readonly List _idleConnections = new List(); - /// The maximum number of connections allowed to be associated with the pool. - private readonly int _maxConnections; + // HTTP/1.1 connection pool + + /// List of available HTTP/1.1 connections stored in the pool. + private readonly List _availableHttp11Connections = new List(); + /// The maximum number of HTTP/1.1 connections allowed to be associated with the pool. + private readonly int _maxHttp11Connections; + /// The number of HTTP/1.1 connections associated with the pool, including in use, available, and pending. + private int _associatedHttp11ConnectionCount; + /// The number of HTTP/1.1 connections that are in the process of being established. + private int _pendingHttp11ConnectionCount; + /// Queue of requests waiting for an HTTP/1.1 connection. + private RequestQueue _http11RequestQueue; + + // HTTP/2 connection pool + + /// List of available HTTP/2 connections stored in the pool. + private List? _availableHttp2Connections; + /// The number of HTTP/2 connections associated with the pool, including in use, available, and pending. + private int _associatedHttp2ConnectionCount; + /// Indicates whether an HTTP/2 connection is in the process of being established. + private bool _pendingHttp2Connection; + /// Queue of requests waiting for an HTTP/2 connection. + private RequestQueue _http2RequestQueue; private bool _http2Enabled; - // This array must be treated as immutable. It can only be replaced with a new value in AddHttp2Connection method. - private volatile Http2Connection[]? _http2Connections; - private SemaphoreSlim? _http2ConnectionCreateLock; private byte[]? _http2AltSvcOriginUri; internal readonly byte[]? _http2EncodedAuthorityHostHeader; private readonly bool _http3Enabled; @@ -88,11 +104,6 @@ namespace System.Net.Http private readonly SslClientAuthenticationOptions? _sslOptionsHttp2Only; private readonly SslClientAuthenticationOptions? _sslOptionsHttp3; - /// Queue of waiters waiting for a connection. Created on demand. - private Queue>? _waiters; - - /// The number of connections associated with the pool. Some of these may be in , others may be in use. - private int _associatedConnectionCount; /// Whether the pool has been used since the last time a cleanup occurred. private bool _usedSinceLastCleanup = true; /// Whether the pool has been disposed. @@ -113,7 +124,7 @@ namespace System.Net.Http _poolManager = poolManager; _kind = kind; _proxyUri = proxyUri; - _maxConnections = Settings._maxConnectionsPerServer; + _maxHttp11Connections = Settings._maxConnectionsPerServer; if (host != null) { @@ -183,7 +194,7 @@ namespace System.Net.Http // Don't enforce the max connections limit on proxy tunnels; this would mean that connections to different origin servers // would compete for the same limited number of connections. // We will still enforce this limit on the user of the tunnel (i.e. ProxyTunnel or SslProxyTunnel). - _maxConnections = int.MaxValue; + _maxHttp11Connections = int.MaxValue; _http2Enabled = false; _http3Enabled = false; @@ -364,10 +375,54 @@ namespace System.Net.Http } } - public bool EnableMultipleHttp2Connections => _poolManager.Settings.EnableMultipleHttp2Connections; + private bool EnableMultipleHttp2Connections => _poolManager.Settings.EnableMultipleHttp2Connections; /// Object used to synchronize access to state in the pool. - private object SyncObj => _idleConnections; + private object SyncObj + { + get + { + Debug.Assert(!Monitor.IsEntered(_availableHttp11Connections)); + return _availableHttp11Connections; + } + } + + private bool HasSyncObjLock => Monitor.IsEntered(_availableHttp11Connections); + + // Overview of connection management (mostly HTTP version independent): + // + // Each version of HTTP (1.1, 2, 3) has its own connection pool, and each of these work in a similar manner, + // allowing for differences between the versions (most notably, HTTP/1.1 is not multiplexed.) + // + // When a request is submitted for a particular version (e.g. HTTP/1.1), we first look in the pool for available connections. + // An "available" connection is one that is (hopefully) usable for a new request. + // For HTTP/1.1, this is just an idle connection. + // For HTTP2/3, this is a connection that (hopefully) has available streams to use for new requests. + // If we find an available connection, we will attempt to validate it and then use it. + // We check the lifetime of the connection and discard it if the lifetime is exceeded. + // We check that the connection has not shut down; if so we discard it. + // For HTTP2/3, we reserve a stream on the connection. If this fails, we cannot use the connection right now. + // If validation fails, we will attempt to find a different available connection. + // + // Once we have found a usable connection, we use it to process the request. + // For HTTP/1.1, a connection can handle only a single request at a time, thus it is immediately removed from the list of available connections. + // For HTTP2/3, a connection is only removed from the available list when it has no more available streams. + // In either case, the connection still counts against the total associated connection count for the pool. + // + // If we cannot find a usable available connection, then the request is added the to the request queue for the appropriate version. + // + // Whenever a request is queued, or an existing connection shuts down, we will check to see if we should inject a new connection. + // Injection policy depends on both user settings and some simple heuristics. + // See comments on the relevant routines for details on connection injection policy. + // + // When a new connection is successfully created, or an existing unavailable connection becomes available again, + // we will attempt to use this connection to handle any queued requests (subject to lifetime restrictions on existing connections). + // This may result in the connection becoming unavailable again, because it cannot handle any more requests at the moment. + // If not, we will return the connection to the pool as an available connection for use by new requests. + // + // When a connection shuts down, either gracefully (e.g. GOAWAY) or abortively (e.g. IOException), + // we will remove it from the list of available connections, if it is present there. + // If not, then it must be unavailable at the moment; we will detect this and ensure it is not added back to the available pool. private static HttpRequestException GetVersionException(HttpRequestMessage request, int desiredVersion) { @@ -376,288 +431,369 @@ namespace System.Net.Http return new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, desiredVersion)); } - private ValueTask GetOrReserveHttp11ConnectionAsync(bool async, CancellationToken cancellationToken) + private bool CheckExpirationOnGet(HttpConnectionBase connection) { - long nowTicks = Environment.TickCount64; + TimeSpan pooledConnectionLifetime = _poolManager.Settings._pooledConnectionLifetime; + if (pooledConnectionLifetime != Timeout.InfiniteTimeSpan) + { + return connection.GetLifetimeTicks(Environment.TickCount64) > pooledConnectionLifetime.TotalMilliseconds; + } + return false; + } + + private static Exception CreateConnectTimeoutException(OperationCanceledException oce) + { + // The pattern for request timeouts (on HttpClient) is to throw an OCE with an inner exception of TimeoutException. + // Do the same for ConnectTimeout-based timeouts. + TimeoutException te = new TimeoutException(SR.net_http_connect_timedout, oce.InnerException); + Exception newException = CancellationHelper.CreateOperationCanceledException(te, oce.CancellationToken); + ExceptionDispatchInfo.SetCurrentStackTrace(newException); + return newException; + } + + private async Task AddHttp11ConnectionAsync(HttpRequestMessage request) + { + if (NetEventSource.Log.IsEnabled()) Trace("Creating new HTTP/1.1 connection for pool."); + + HttpConnection connection; + using (CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource()) + { + try + { + connection = await CreateHttp11ConnectionAsync(request, false, cts.Token).ConfigureAwait(false); + } + catch (OperationCanceledException oce) when (oce.CancellationToken == cts.Token) + { + HandleHttp11ConnectionFailure(CreateConnectTimeoutException(oce)); + return; + } + catch (Exception e) + { + HandleHttp11ConnectionFailure(e); + return; + } + } + + // Add the established connection to the pool. + ReturnHttp11Connection(connection, isNewConnection: true); + } + + private void CheckForHttp11ConnectionInjection() + { + Debug.Assert(HasSyncObjLock); + + if (!_http11RequestQueue.TryPeekNextRequest(out HttpRequestMessage? request)) + { + return; + } + + // Determine if we can and should add a new connection to the pool. + if (_availableHttp11Connections.Count == 0 && // No available connections + _http11RequestQueue.Count > _pendingHttp11ConnectionCount && // More requests queued than pending connections + _associatedHttp11ConnectionCount < _maxHttp11Connections) // Under the connection limit + { + _associatedHttp11ConnectionCount++; + _pendingHttp11ConnectionCount++; + + Task.Run(() => AddHttp11ConnectionAsync(request)); + } + } + + private async ValueTask GetHttp11ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + { // Look for a usable idle connection. - TaskCompletionSourceWithCancellation waiter; + TaskCompletionSourceWithCancellation waiter; while (true) { - HttpConnection connection; + HttpConnection? connection = null; lock (SyncObj) { - int idleConnectionCount = _idleConnections.Count; - if (idleConnectionCount > 0) + _usedSinceLastCleanup = true; + + int availableConnectionCount = _availableHttp11Connections.Count; + if (availableConnectionCount > 0) { - // We have an idle connection that we can attempt to use. + // We have a connection that we can attempt to use. // Validate it below outside the lock, to avoid doing expensive operations while holding the lock. - connection = _idleConnections[idleConnectionCount - 1]._connection; - _idleConnections.RemoveAt(idleConnectionCount - 1); + connection = _availableHttp11Connections[availableConnectionCount - 1]; + _availableHttp11Connections.RemoveAt(availableConnectionCount - 1); } else { - // No available idle connections. - if (_associatedConnectionCount < _maxConnections) - { - // We are under the connection limit, so just increment the count and return null - // to indicate to the caller that they should create a new connection. - IncrementConnectionCountNoLock(); - return new ValueTask((HttpConnection?)null); - } - else - { - // We've reached the connection limit and need to wait for an existing connection - // to become available, or to be closed so that we can create a new connection. - // Enqueue a waiter that will be signalled when this happens. - // Break out of the loop and then do the actual wait below. - waiter = EnqueueWaiter(); - break; - } + // No available connections. Add to the request queue. + waiter = _http11RequestQueue.EnqueueRequest(request); - // Note that we don't check for _disposed. We may end up disposing the - // created connection when it's returned, but we don't want to block use - // of the pool if it's already been disposed, as there's a race condition - // between getting a pool and someone disposing of it, and we don't want - // to complicate the logic about trying to get a different pool when the - // retrieved one has been disposed of. In the future we could alternatively - // try returning such connections to whatever pool is currently considered - // current for that endpoint, if there is one. + CheckForHttp11ConnectionInjection(); + + // Break out of the loop and continue processing below. + break; } } - if (connection.LifetimeExpired(nowTicks, _poolManager.Settings._pooledConnectionLifetime)) + if (CheckExpirationOnGet(connection)) { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Found expired connection in pool."); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found expired HTTP/1.1 connection in pool."); connection.Dispose(); continue; } if (!connection.PrepareForReuse(async)) { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Found invalid connection in pool."); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found invalid HTTP/1.1 connection in pool."); connection.Dispose(); continue; } - if (NetEventSource.Log.IsEnabled()) connection.Trace("Found usable connection in pool."); - return new ValueTask(connection); - } - - // We are at the connection limit. Wait for an available connection or connection count. - if (NetEventSource.Log.IsEnabled()) Trace($"{(async ? "As" : "S")}ynchronous request. Connection limit reached, waiting for available connection."); - - if (HttpTelemetry.Log.IsEnabled()) - { - return WaitOnWaiterWithTelemetryAsync(waiter, async, cancellationToken); - } - else - { - return waiter.WaitWithCancellationAsync(cancellationToken); - } - - static async ValueTask WaitOnWaiterWithTelemetryAsync(TaskCompletionSourceWithCancellation waiter, bool async, CancellationToken cancellationToken) - { - ValueStopwatch stopwatch = ValueStopwatch.StartNew(); - - HttpConnection? connection = await waiter.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); - - HttpTelemetry.Log.Http11RequestLeftQueue(stopwatch.GetElapsedTime().TotalMilliseconds); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found usable HTTP/1.1 connection in pool."); return connection; } + + // There were no available idle connections. This request has been added to the request queue. + if (NetEventSource.Log.IsEnabled()) Trace($"No available HTTP/1.1 connections; request queued."); + + ValueStopwatch stopwatch = ValueStopwatch.StartNew(); + try + { + return await waiter.WaitWithCancellationAsync(async, cancellationToken).ConfigureAwait(false); + } + finally + { + if (HttpTelemetry.Log.IsEnabled()) + { + HttpTelemetry.Log.Http11RequestLeftQueue(stopwatch.GetElapsedTime().TotalMilliseconds); + } + } + } + + private async Task HandleHttp11Downgrade(HttpRequestMessage request, Socket? socket, Stream stream, TransportContext? transportContext, CancellationToken cancellationToken) + { + if (NetEventSource.Log.IsEnabled()) Trace("Server does not support HTTP2; disabling HTTP2 use and proceeding with HTTP/1.1 connection"); + + bool canUse = true; + lock (SyncObj) + { + Debug.Assert(_pendingHttp2Connection); + Debug.Assert(_associatedHttp2ConnectionCount > 0); + + // Server does not support HTTP2. Disable further HTTP2 attempts. + _http2Enabled = false; + _associatedHttp2ConnectionCount--; + _pendingHttp2Connection = false; + + // Signal to any queued HTTP2 requests that they must downgrade. + while (_http2RequestQueue.TryDequeueNextRequest(null)) + ; + + if (_associatedHttp11ConnectionCount < _maxHttp11Connections) + { + _associatedHttp11ConnectionCount++; + _pendingHttp11ConnectionCount++; + } + else + { + // We are already at the limit for HTTP/1.1 connections, so do not proceed with this connection. + canUse = false; + } + } + + if (!canUse) + { + if (NetEventSource.Log.IsEnabled()) Trace("Discarding downgraded HTTP/1.1 connection because HTTP/1.1 connection limit is exceeded"); + stream.Dispose(); + } + + HttpConnection http11Connection; + try + { + // Note, the same CancellationToken from the original HTTP2 connection establishment still applies here. + http11Connection = await ConstructHttp11ConnectionAsync(false, socket, stream, transportContext, request, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException oce) when (oce.CancellationToken == cancellationToken) + { + HandleHttp11ConnectionFailure(CreateConnectTimeoutException(oce)); + return; + } + catch (Exception e) + { + HandleHttp11ConnectionFailure(e); + return; + } + + ReturnHttp11Connection(http11Connection, isNewConnection: true); + } + + private async Task AddHttp2ConnectionAsync(HttpRequestMessage request) + { + if (NetEventSource.Log.IsEnabled()) Trace("Creating new HTTP/2 connection for pool."); + + Http2Connection connection; + using (CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource()) + { + try + { + (Socket? socket, Stream stream, TransportContext? transportContext) = await ConnectAsync(request, false, cts.Token).ConfigureAwait(false); + + if (IsSecure) + { + SslStream sslStream = (SslStream)stream; + + if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2) + { + // The server accepted our request for HTTP2. + + if (sslStream.SslProtocol < SslProtocols.Tls12) + { + stream.Dispose(); + throw new HttpRequestException(SR.Format(SR.net_ssl_http2_requires_tls12, sslStream.SslProtocol)); + } + + connection = await ConstructHttp2ConnectionAsync(stream, request, cts.Token).ConfigureAwait(false); + } + else + { + // We established an SSL connection, but the server denied our request for HTTP2. + await HandleHttp11Downgrade(request, socket, stream, transportContext, cts.Token).ConfigureAwait(false); + return; + } + } + else + { + connection = await ConstructHttp2ConnectionAsync(stream, request, cts.Token).ConfigureAwait(false); + } + } + catch (OperationCanceledException oce) when (oce.CancellationToken == cts.Token) + { + HandleHttp2ConnectionFailure(CreateConnectTimeoutException(oce)); + return; + } + catch (Exception e) + { + HandleHttp2ConnectionFailure(e); + return; + } + } + + // Register for shutdown notification. + // Do this before we return the connection to the pool, because that may result in it being disposed. + ValueTask shutdownTask = connection.WaitForShutdownAsync(); + + // Add the new connection to the pool. + ReturnHttp2Connection(connection, isNewConnection: true); + + // Wait for connection shutdown. + await shutdownTask.ConfigureAwait(false); + + InvalidateHttp2Connection(connection); + } + + private void CheckForHttp2ConnectionInjection() + { + Debug.Assert(HasSyncObjLock); + + if (!_http2RequestQueue.TryPeekNextRequest(out HttpRequestMessage? request)) + { + return; + } + + // Determine if we can and should add a new connection to the pool. + if ((_availableHttp2Connections?.Count ?? 0) == 0 && // No available connections + !_pendingHttp2Connection && // Only allow one pending HTTP2 connection at a time + (_associatedHttp2ConnectionCount == 0 || EnableMultipleHttp2Connections)) // We allow multiple connections, or don't have a connection currently + { + _associatedHttp2ConnectionCount++; + _pendingHttp2Connection = true; + + Task.Run(() => AddHttp2ConnectionAsync(request)); + } } - // Returns null if HTTP2 cannot be used private async ValueTask GetHttp2ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { Debug.Assert(_kind == HttpConnectionKind.Https || _kind == HttpConnectionKind.SslProxyTunnel || _kind == HttpConnectionKind.Http || _kind == HttpConnectionKind.SocksTunnel || _kind == HttpConnectionKind.SslSocksTunnel); - // See if we have an HTTP2 connection - Http2Connection? http2Connection = GetExistingHttp2Connection(); - - if (http2Connection != null) - { - // Connection exists and it is still good to use. - if (NetEventSource.Log.IsEnabled()) Trace("Using existing HTTP2 connection."); - _usedSinceLastCleanup = true; - return http2Connection; - } - - // Ensure that the connection creation semaphore is created - if (_http2ConnectionCreateLock == null) + // Look for a usable connection. + TaskCompletionSourceWithCancellation waiter; + while (true) { + Http2Connection connection; lock (SyncObj) { - if (_http2ConnectionCreateLock == null) + _usedSinceLastCleanup = true; + + if (!_http2Enabled) { - _http2ConnectionCreateLock = new SemaphoreSlim(1); - } - } - } - - // Try to establish an HTTP2 connection - Socket? socket = null; - Stream? stream = null; - SslStream? sslStream = null; - TransportContext? transportContext = null; - - // Serialize creation attempt - await _http2ConnectionCreateLock.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - http2Connection = GetExistingHttp2Connection(); - if (http2Connection != null) - { - return http2Connection; - } - - // Recheck if HTTP2 has been disabled by a previous attempt. - if (_http2Enabled) - { - if (NetEventSource.Log.IsEnabled()) - { - Trace("Attempting new HTTP2 connection."); + return null; } - (socket, stream, transportContext) = - await ConnectAsync(request, async, cancellationToken).ConfigureAwait(false); - - Debug.Assert(stream != null); - - sslStream = stream as SslStream; - - if (!IsSecure) + int availableConnectionCount = _availableHttp2Connections?.Count ?? 0; + if (availableConnectionCount > 0) { - http2Connection = await ConstructHttp2ConnectionAsync(stream, request, cancellationToken).ConfigureAwait(false); - - if (NetEventSource.Log.IsEnabled()) - { - Trace("New unencrypted HTTP2 connection established."); - } - - return http2Connection; - } - - Debug.Assert(sslStream != null); - - if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2) - { - // The server accepted our request for HTTP2. - - if (sslStream.SslProtocol < SslProtocols.Tls12) - { - sslStream.Dispose(); - throw new HttpRequestException(SR.Format(SR.net_ssl_http2_requires_tls12, sslStream.SslProtocol)); - } - - http2Connection = await ConstructHttp2ConnectionAsync(stream, request, cancellationToken).ConfigureAwait(false); - - if (NetEventSource.Log.IsEnabled()) - { - Trace("New HTTP2 connection established."); - } - - return http2Connection; - } - } - } - finally - { - _http2ConnectionCreateLock.Release(); - } - - if (sslStream != null) - { - // We established an SSL connection, but the server denied our request for HTTP2. - // Try to establish a 1.1 connection instead and add it to the pool. - - if (NetEventSource.Log.IsEnabled()) - { - Trace("Server does not support HTTP2; disabling HTTP2 use and proceeding with HTTP/1.1 connection"); - } - - bool canUse = true; - lock (SyncObj) - { - // Server does not support HTTP2. Disable further HTTP2 attempts. - _http2Enabled = false; - - if (_associatedConnectionCount < _maxConnections) - { - IncrementConnectionCountNoLock(); + // We have a connection that we can attempt to use. + // Validate it below outside the lock, to avoid doing expensive operations while holding the lock. + connection = _availableHttp2Connections![availableConnectionCount - 1]; } else { - // We are already at the limit for HTTP/1.1 connections, so do not proceed with this connection. - canUse = false; + // No available connections. Add to the request queue. + waiter = _http2RequestQueue.EnqueueRequest(request); + + CheckForHttp2ConnectionInjection(); + + // Break out of the loop and continue processing below. + break; } } - if (canUse) + if (CheckExpirationOnGet(connection)) { - HttpConnection http11Connection = await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false); - ReturnConnection(http11Connection); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found expired HTTP/2 connection in pool."); + + InvalidateHttp2Connection(connection); + continue; } - else + + if (!connection.TryReserveStream()) { - if (NetEventSource.Log.IsEnabled()) + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found HTTP/2 connection in pool without available streams."); + + bool found = false; + lock (SyncObj) { - Trace("Discarding downgraded HTTP/1.1 connection because connection limit is exceeded"); + int index = _availableHttp2Connections.IndexOf(connection); + if (index != -1) + { + found = true; + _availableHttp2Connections.RemoveAt(index); + } } - stream!.Dispose(); + // If we didn't find the connection, then someone beat us to removing it (or it shut down) + if (found) + { + DisableHttp2Connection(connection); + } + continue; } + + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found usable HTTP/2 connection in pool."); + return connection; } - // We are unable to use HTTP2. - return null; - } + // There were no available connections. This request has been added to the request queue. + if (NetEventSource.Log.IsEnabled()) Trace($"No available HTTP/2 connections; request queued."); - private Http2Connection? GetExistingHttp2Connection() - { - Http2Connection[]? localConnections = _http2Connections; - - if (localConnections == null) + ValueStopwatch stopwatch = ValueStopwatch.StartNew(); + try { - return null; + return await waiter.WaitWithCancellationAsync(async, cancellationToken).ConfigureAwait(false); } - - for (int i = 0; i < localConnections.Length; i++) + finally { - Http2Connection http2Connection = localConnections[i]; - - TimeSpan pooledConnectionLifetime = _poolManager.Settings._pooledConnectionLifetime; - if (http2Connection.LifetimeExpired(Environment.TickCount64, pooledConnectionLifetime)) + if (HttpTelemetry.Log.IsEnabled()) { - // Connection expired. - if (NetEventSource.Log.IsEnabled()) http2Connection.Trace("Found expired HTTP2 connection."); - http2Connection.Dispose(); - InvalidateHttp2Connection(http2Connection); + HttpTelemetry.Log.Http20RequestLeftQueue(stopwatch.GetElapsedTime().TotalMilliseconds); } - else if (!EnableMultipleHttp2Connections || http2Connection.CanAddNewStream) - { - return http2Connection; - } - } - - return null; - } - - private void AddHttp2Connection(Http2Connection newConnection) - { - lock (SyncObj) - { - Http2Connection[]? localHttp2Connections = _http2Connections; - int newCollectionSize = localHttp2Connections == null ? 1 : localHttp2Connections.Length + 1; - Http2Connection[] newHttp2Connections = new Http2Connection[newCollectionSize]; - newHttp2Connections[0] = newConnection; - - if (localHttp2Connections != null) - { - Array.Copy(localHttp2Connections, 0, newHttp2Connections, 1, localHttp2Connections.Length); - } - - _http2Connections = newHttp2Connections; } } @@ -673,8 +809,7 @@ namespace System.Net.Http if (http3Connection != null) { - TimeSpan pooledConnectionLifetime = _poolManager.Settings._pooledConnectionLifetime; - if (http3Connection.LifetimeExpired(Environment.TickCount64, pooledConnectionLifetime) || http3Connection.Authority != authority) + if (CheckExpirationOnGet(http3Connection) || http3Connection.Authority != authority) { // Connection expired. if (NetEventSource.Log.IsEnabled()) http3Connection.Trace("Found expired HTTP3 connection."); @@ -831,21 +966,7 @@ namespace System.Net.Http private async ValueTask SendUsingHttp11Async(HttpRequestMessage request, bool async, bool doRequestAuth, CancellationToken cancellationToken) { - HttpConnection? connection = await GetOrReserveHttp11ConnectionAsync(async, cancellationToken).ConfigureAwait(false); - if (connection is null) - { - if (NetEventSource.Log.IsEnabled()) Trace("Creating new HTTP/1.1 connection for pool."); - - try - { - connection = await CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); - } - catch - { - DecrementConnectionCount(); - throw; - } - } + HttpConnection connection = await GetHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); // In case we are doing Windows (i.e. connection-based) auth, we need to ensure that we hold on to this specific connection while auth is underway. connection.Acquire(); @@ -1235,68 +1356,53 @@ namespace System.Net.Http return SendWithProxyAuthAsync(request, async, doRequestAuth, cancellationToken); } + private CancellationTokenSource GetConnectTimeoutCancellationTokenSource() => new CancellationTokenSource(Settings._connectTimeout); + private async ValueTask<(Socket?, Stream, TransportContext?)> ConnectAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { - // If a non-infinite connect timeout has been set, create and use a new CancellationToken that will be canceled - // when either the original token is canceled or a connect timeout occurs. - CancellationTokenSource? cancellationWithConnectTimeout = null; - if (Settings._connectTimeout != Timeout.InfiniteTimeSpan) + Stream? stream = null; + Socket? socket = null; + switch (_kind) { - cancellationWithConnectTimeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - cancellationWithConnectTimeout.CancelAfter(Settings._connectTimeout); - cancellationToken = cancellationWithConnectTimeout.Token; + case HttpConnectionKind.Http: + case HttpConnectionKind.Https: + case HttpConnectionKind.ProxyConnect: + Debug.Assert(_originAuthority != null); + (socket, stream) = await ConnectToTcpHostAsync(_originAuthority.IdnHost, _originAuthority.Port, request, async, cancellationToken).ConfigureAwait(false); + break; + + case HttpConnectionKind.Proxy: + (socket, stream) = await ConnectToTcpHostAsync(_proxyUri!.IdnHost, _proxyUri.Port, request, async, cancellationToken).ConfigureAwait(false); + break; + + case HttpConnectionKind.ProxyTunnel: + case HttpConnectionKind.SslProxyTunnel: + stream = await EstablishProxyTunnelAsync(async, request.HasHeaders ? request.Headers : null, cancellationToken).ConfigureAwait(false); + break; + + case HttpConnectionKind.SocksTunnel: + case HttpConnectionKind.SslSocksTunnel: + (socket, stream) = await EstablishSocksTunnel(request, async, cancellationToken).ConfigureAwait(false); + break; } - try + Debug.Assert(stream != null); + if (socket is null && stream is NetworkStream ns) { - Stream? stream = null; - Socket? socket = null; - switch (_kind) - { - case HttpConnectionKind.Http: - case HttpConnectionKind.Https: - case HttpConnectionKind.ProxyConnect: - Debug.Assert(_originAuthority != null); - (socket, stream) = await ConnectToTcpHostAsync(_originAuthority.IdnHost, _originAuthority.Port, request, async, cancellationToken).ConfigureAwait(false); - break; - - case HttpConnectionKind.Proxy: - (socket, stream) = await ConnectToTcpHostAsync(_proxyUri!.IdnHost, _proxyUri.Port, request, async, cancellationToken).ConfigureAwait(false); - break; - - case HttpConnectionKind.ProxyTunnel: - case HttpConnectionKind.SslProxyTunnel: - stream = await EstablishProxyTunnelAsync(async, request.HasHeaders ? request.Headers : null, cancellationToken).ConfigureAwait(false); - break; - - case HttpConnectionKind.SocksTunnel: - case HttpConnectionKind.SslSocksTunnel: - (socket, stream) = await EstablishSocksTunnel(request, async, cancellationToken).ConfigureAwait(false); - break; - } - - Debug.Assert(stream != null); - if (socket is null && stream is NetworkStream ns) - { - // We weren't handed a socket directly. But if we're able to extract one, do so. - // Most likely case here is a ConnectCallback was used and returned a NetworkStream. - socket = ns.Socket; - } - - TransportContext? transportContext = null; - if (IsSecure) - { - SslStream sslStream = await ConnectHelper.EstablishSslConnectionAsync(GetSslOptionsForRequest(request), request, async, stream, cancellationToken).ConfigureAwait(false); - transportContext = sslStream.TransportContext; - stream = sslStream; - } - - return (socket, stream, transportContext); + // We weren't handed a socket directly. But if we're able to extract one, do so. + // Most likely case here is a ConnectCallback was used and returned a NetworkStream. + socket = ns.Socket; } - finally + + TransportContext? transportContext = null; + if (IsSecure) { - cancellationWithConnectTimeout?.Dispose(); + SslStream sslStream = await ConnectHelper.EstablishSslConnectionAsync(GetSslOptionsForRequest(request), request, async, stream, cancellationToken).ConfigureAwait(false); + transportContext = sslStream.TransportContext; + stream = sslStream; } + + return (socket, stream, transportContext); } private async ValueTask<(Socket?, Stream)> ConnectToTcpHostAsync(string host, int port, HttpRequestMessage initialRequest, bool async, CancellationToken cancellationToken) @@ -1357,8 +1463,7 @@ namespace System.Net.Http internal async ValueTask CreateHttp11ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { - (Socket? socket, Stream? stream, TransportContext? transportContext) = - await ConnectAsync(request, async, cancellationToken).ConfigureAwait(false); + (Socket? socket, Stream stream, TransportContext? transportContext) = await ConnectAsync(request, async, cancellationToken).ConfigureAwait(false); return await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false); } @@ -1403,6 +1508,11 @@ namespace System.Net.Http newStream = await streamTask.ConfigureAwait(false); } + catch (OperationCanceledException oce) when (oce.CancellationToken == cancellationToken) + { + stream.Dispose(); + throw; + } catch (Exception e) { stream.Dispose(); @@ -1444,8 +1554,6 @@ namespace System.Net.Http throw new HttpRequestException(SR.net_http_client_execution_error, e); } - AddHttp2Connection(http2Connection); - return http2Connection; } @@ -1501,133 +1609,136 @@ namespace System.Net.Http return (socket, stream); } - /// Enqueues a waiter to the waiters list. - private TaskCompletionSourceWithCancellation EnqueueWaiter() + private void HandleHttp11ConnectionFailure(Exception e) { - Debug.Assert(Monitor.IsEntered(SyncObj)); - Debug.Assert(Settings._maxConnectionsPerServer != int.MaxValue); - Debug.Assert(_idleConnections.Count == 0, $"With {_idleConnections.Count} idle connections, we shouldn't have a waiter."); + if (NetEventSource.Log.IsEnabled()) Trace("HTTP/1.1 connection failed"); - if (_waiters == null) + lock (SyncObj) { - _waiters = new Queue>(); + Debug.Assert(_associatedHttp11ConnectionCount > 0); + Debug.Assert(_pendingHttp11ConnectionCount > 0); + + _associatedHttp11ConnectionCount--; + _pendingHttp11ConnectionCount--; + + // Fail the next queued request (if any) with this error. + _http11RequestQueue.TryFailNextRequest(e); + + CheckForHttp11ConnectionInjection(); } - - var waiter = new TaskCompletionSourceWithCancellation(); - _waiters.Enqueue(waiter); - return waiter; } - private void IncrementConnectionCountNoLock() + private void HandleHttp2ConnectionFailure(Exception e) { - Debug.Assert(Monitor.IsEntered(SyncObj), $"Expected to be holding {nameof(SyncObj)}"); + if (NetEventSource.Log.IsEnabled()) Trace("HTTP2 connection failed"); - if (NetEventSource.Log.IsEnabled()) Trace(null); - _usedSinceLastCleanup = true; + lock (SyncObj) + { + Debug.Assert(_associatedHttp2ConnectionCount > 0); + Debug.Assert(_pendingHttp2Connection); - Debug.Assert( - _associatedConnectionCount >= 0 && _associatedConnectionCount < _maxConnections, - $"Expected 0 <= {_associatedConnectionCount} < {_maxConnections}"); - _associatedConnectionCount++; + _associatedHttp2ConnectionCount--; + _pendingHttp2Connection = false; + + // Fail the next queued request (if any) with this error. + _http2RequestQueue.TryFailNextRequest(e); + + CheckForHttp2ConnectionInjection(); + } } - internal void IncrementConnectionCount() + /// + /// Called when an HttpConnection from this pool is no longer usable. + /// Note, this is always called from HttpConnection.Dispose, which is a bit different than how HTTP2 works. + /// + public void InvalidateHttp11Connection(HttpConnection connection, bool disposing = true) { lock (SyncObj) { - IncrementConnectionCountNoLock(); + Debug.Assert(_associatedHttp11ConnectionCount > 0); + Debug.Assert(!disposing || !_availableHttp11Connections.Contains(connection)); + + _associatedHttp11ConnectionCount--; + + CheckForHttp11ConnectionInjection(); } } - private bool TransferConnection(HttpConnection? connection) + /// + /// Called when an Http2Connection from this pool is no longer usable. + /// + public void InvalidateHttp2Connection(Http2Connection connection) { - Debug.Assert(Monitor.IsEntered(SyncObj)); + if (NetEventSource.Log.IsEnabled()) connection.Trace(""); - if (_waiters == null) + bool found = false; + lock (SyncObj) { - return false; - } - - Debug.Assert(_maxConnections != int.MaxValue, "_waiters queue is allocated but no connection limit is set??"); - - while (_waiters.TryDequeue(out TaskCompletionSourceWithCancellation? waiter)) - { - Debug.Assert(_idleConnections.Count == 0, $"With {_idleConnections.Count} idle connections, we shouldn't have a waiter."); - - // Try to complete the task. If it's been cancelled already, this will fail. - if (waiter.TrySetResult(connection)) + if (_availableHttp2Connections is not null) { - return true; + Debug.Assert(_associatedHttp2ConnectionCount >= _availableHttp2Connections.Count); + + int index = _availableHttp2Connections.IndexOf(connection); + if (index != -1) + { + found = true; + _availableHttp2Connections.RemoveAt(index); + _associatedHttp2ConnectionCount--; + } } - // Couldn't transfer to that waiter because it was cancelled. Try again. - Debug.Assert(waiter.Task.IsCanceled); + CheckForHttp2ConnectionInjection(); + } + + // If we found the connection in the available list, then dispose it now. + // Otherwise, when we try to put it back in the pool, we will see it is shut down and dispose it (and adjust connection counts). + if (found) + { + connection.Dispose(); + } + } + + private bool CheckExpirationOnReturn(HttpConnectionBase connection) + { + TimeSpan lifetime = _poolManager.Settings._pooledConnectionLifetime; + if (lifetime != Timeout.InfiniteTimeSpan) + { + return lifetime == TimeSpan.Zero || connection.GetLifetimeTicks(Environment.TickCount64) > lifetime.TotalMilliseconds; } return false; } - /// - /// Decrements the number of connections associated with the pool. - /// If there are waiters on the pool due to having reached the maximum, - /// this will instead try to transfer the count to one of them. - /// - public void DecrementConnectionCount() + public void ReturnHttp11Connection(HttpConnection connection, bool isNewConnection = false) { - if (NetEventSource.Log.IsEnabled()) Trace(null); - lock (SyncObj) + if (NetEventSource.Log.IsEnabled()) connection.Trace($"{nameof(isNewConnection)}={isNewConnection}"); + + if (!isNewConnection && CheckExpirationOnReturn(connection)) { - Debug.Assert(_associatedConnectionCount > 0 && _associatedConnectionCount <= _maxConnections, - $"Expected 0 < {_associatedConnectionCount} <= {_maxConnections}"); - - // Mark the pool as not being stale. - _usedSinceLastCleanup = true; - - if (TransferConnection(null)) - { - if (NetEventSource.Log.IsEnabled()) Trace("Transferred connection count to waiter."); - return; - } - - // There are no waiters to which the count should logically be transferred, - // so simply decrement the count. - _associatedConnectionCount--; - } - } - - /// Returns the connection to the pool for subsequent reuse. - /// The connection to return. - public void ReturnConnection(HttpConnection connection) - { - if (connection.LifetimeExpired(Environment.TickCount64, _poolManager.Settings._pooledConnectionLifetime)) - { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing connection return to pool. Connection lifetime expired."); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing HTTP/1.1 connection return to pool. Connection lifetime expired."); connection.Dispose(); return; } - List list = _idleConnections; lock (SyncObj) { - Debug.Assert(list.Count <= _maxConnections, $"Expected {list.Count} <= {_maxConnections}"); + Debug.Assert(!_availableHttp11Connections.Contains(connection)); - // Mark the pool as still being active. - _usedSinceLastCleanup = true; - - // If there's someone waiting for a connection and this one's still valid, simply transfer this one to them rather than pooling it. - // Note that while we checked connection lifetime above, we don't check idle timeout, as even if idle timeout - // is zero, we consider a connection that's just handed from one use to another to never actually be idle. - if (TransferConnection(connection)) + if (isNewConnection) { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Transferred connection to waiter."); + Debug.Assert(_pendingHttp11ConnectionCount > 0); + _pendingHttp11ConnectionCount--; + } + + if (_http11RequestQueue.TryDequeueNextRequest(connection)) + { + Debug.Assert(_availableHttp11Connections.Count == 0, $"With {_availableHttp11Connections.Count} available HTTP/1.1 connections, we shouldn't have a waiter."); + + if (NetEventSource.Log.IsEnabled()) connection.Trace("Dequeued waiting HTTP/1.1 request."); return; } - if (_poolManager.Settings._pooledConnectionIdleTimeout == TimeSpan.Zero) - { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing connection returned to pool. Zero idle timeout."); - } - else if (_disposed) + if (_disposed) { // If the pool has been disposed of, dispose the connection being returned, // as the pool is being deactivated. We do this after the above in order to @@ -1637,55 +1748,150 @@ namespace System.Net.Http } else { - // Pool the connection by adding it to the list. - list.Add(new CachedConnection(connection)); - if (NetEventSource.Log.IsEnabled()) connection.Trace("Stored connection in pool."); + // Add connection to the pool. + _availableHttp11Connections.Add(connection); + Debug.Assert(_availableHttp11Connections.Count <= _maxHttp11Connections, $"Expected {_availableHttp11Connections.Count} <= {_maxHttp11Connections}"); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Put connection in pool."); return; } } + // We determined that the connection is no longer usable. connection.Dispose(); } - public void InvalidateHttp2Connection(Http2Connection connection) + public void ReturnHttp2Connection(Http2Connection connection, bool isNewConnection) { + if (NetEventSource.Log.IsEnabled()) connection.Trace($"{nameof(isNewConnection)}={isNewConnection}"); + + if (!isNewConnection && CheckExpirationOnReturn(connection)) + { + lock (SyncObj) + { + Debug.Assert(_availableHttp2Connections is null || !_availableHttp2Connections.Contains(connection)); + Debug.Assert(_associatedHttp2ConnectionCount > (_availableHttp2Connections?.Count ?? 0)); + _associatedHttp2ConnectionCount--; + } + + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing HTTP/2 connection return to pool. Connection lifetime expired."); + connection.Dispose(); + return; + } + + bool usable = true; + bool poolDisposed = false; lock (SyncObj) { - Http2Connection[]? localHttp2Connections = _http2Connections; + Debug.Assert(_availableHttp2Connections is null || !_availableHttp2Connections.Contains(connection)); + Debug.Assert(_associatedHttp2ConnectionCount > (_availableHttp2Connections?.Count ?? 0)); - if (localHttp2Connections == null) + if (isNewConnection) { - return; + Debug.Assert(_pendingHttp2Connection); + _pendingHttp2Connection = false; } - if (localHttp2Connections.Length == 1) + while (!_http2RequestQueue.IsEmpty) { - // Fast shortcut for the most common case. - if (localHttp2Connections[0] == connection) + Debug.Assert((_availableHttp2Connections?.Count ?? 0) == 0, $"With {_availableHttp11Connections.Count} available HTTP2 connections, we shouldn't have a waiter."); + + if (!connection.TryReserveStream()) { - _http2Connections = null; + usable = false; + if (isNewConnection) + { + // The new connection could not handle even one request, either because it shut down before we could use it for any requests, + // or because it immediately set the max concurrent streams limit to 0. + // We don't want to get stuck in a loop where we keep trying to create new connections for the same request. + // Fail the next request, if any. + HttpRequestException hre = new HttpRequestException(SR.net_http_http2_connection_not_established); + ExceptionDispatchInfo.SetCurrentStackTrace(hre); + _http2RequestQueue.TryFailNextRequest(hre); + } + break; } - return; + + isNewConnection = false; + + if (!_http2RequestQueue.TryDequeueNextRequest(connection)) + { + connection.ReleaseStream(); + break; + } + + if (NetEventSource.Log.IsEnabled()) connection.Trace("Dequeued waiting HTTP/2 request."); } - int invalidatedIndex = Array.IndexOf(localHttp2Connections, connection); - if (invalidatedIndex >= 0) + if (_disposed) { - Http2Connection[] newHttp2Connections = new Http2Connection[localHttp2Connections.Length - 1]; - - if (invalidatedIndex > 0) + // If the pool has been disposed of, we want to dispose the connection being returned, as the pool is being deactivated. + // We do this after the above in order to satisfy any requests that were queued before the pool was disposed of. + Debug.Assert(_associatedHttp2ConnectionCount > (_availableHttp2Connections?.Count ?? 0)); + _associatedHttp2ConnectionCount--; + poolDisposed = true; + } + else if (usable) + { + if (_availableHttp2Connections is null) { - Array.Copy(localHttp2Connections, newHttp2Connections, invalidatedIndex); + _availableHttp2Connections = new List(); } - if (invalidatedIndex < localHttp2Connections.Length - 1) - { - Array.Copy(localHttp2Connections, invalidatedIndex + 1, newHttp2Connections, invalidatedIndex, newHttp2Connections.Length - invalidatedIndex); - } - - _http2Connections = newHttp2Connections; + // Add connection to the pool. + _availableHttp2Connections.Add(connection); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Put HTTP/2 connection in pool."); + return; } } + + if (poolDisposed) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing HTTP/2 connection returned to pool. Pool was disposed."); + connection.Dispose(); + return; + } + + Debug.Assert(!usable); + + // We need to wait until the connection is usable again. + DisableHttp2Connection(connection); + } + + /// + /// Disable usage of the specified connection because it cannot handle any more streams at the moment. + /// We will register to be notified when it can handle more streams (or becomes permanently unusable). + /// + private void DisableHttp2Connection(Http2Connection connection) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace(""); + + Task.Run(async () => + { + bool usable = await connection.WaitForAvailableStreamsAsync().ConfigureAwait(false); + + if (NetEventSource.Log.IsEnabled()) connection.Trace($"WaitForAvailableStreamsAsync completed, {nameof(usable)}={usable}"); + + if (usable) + { + ReturnHttp2Connection(connection, isNewConnection: false); + } + else + { + // Connection has shut down. + lock (SyncObj) + { + Debug.Assert(_availableHttp2Connections is null || !_availableHttp2Connections.Contains(connection)); + Debug.Assert(_associatedHttp2ConnectionCount > 0); + + _associatedHttp2ConnectionCount--; + + CheckForHttp2ConnectionInjection(); + } + + if (NetEventSource.Log.IsEnabled()) connection.Trace("HTTP2 connection no longer usable"); + connection.Dispose(); + } + }); } public void InvalidateHttp3Connection(Http3Connection connection) @@ -1705,25 +1911,34 @@ namespace System.Net.Http /// public void Dispose() { - List list = _idleConnections; + List? toDispose = null; + lock (SyncObj) { if (!_disposed) { if (NetEventSource.Log.IsEnabled()) Trace("Disposing pool."); - _disposed = true; - list.ForEach(c => c._connection.Dispose()); - list.Clear(); - if (_http2Connections != null) + _disposed = true; + + toDispose = new List(_availableHttp11Connections.Count + (_availableHttp2Connections?.Count ?? 0)); + toDispose.AddRange(_availableHttp11Connections); + if (_availableHttp2Connections is not null) { - for (int i = 0; i < _http2Connections.Length; i++) - { - _http2Connections[i].Dispose(); - } - _http2Connections = null; + toDispose.AddRange(_availableHttp2Connections); } + // Note: Http11 connections will decrement the _associatedHttp11ConnectionCount when disposed. + // Http2 connections will not, hence the difference in handing _associatedHttp2ConnectionCount. + + Debug.Assert(_associatedHttp11ConnectionCount >= _availableHttp11Connections.Count, + $"Expected {nameof(_associatedHttp11ConnectionCount)}={_associatedHttp11ConnectionCount} >= {nameof(_availableHttp11Connections)}.Count={_availableHttp11Connections.Count}"); + _availableHttp11Connections.Clear(); + + Debug.Assert(_associatedHttp2ConnectionCount >= (_availableHttp2Connections?.Count ?? 0)); + _associatedHttp2ConnectionCount -= (_availableHttp2Connections?.Count ?? 0); + _availableHttp2Connections?.Clear(); + if (_authorityExpireTimer != null) { _authorityExpireTimer.Dispose(); @@ -1737,8 +1952,13 @@ namespace System.Net.Http _altSvcBlocklistTimerCancellation = null; } } - Debug.Assert(list.Count == 0, $"Expected {nameof(list)}.{nameof(list.Count)} == 0"); + + Debug.Assert(_availableHttp11Connections.Count == 0, $"Expected {nameof(_availableHttp11Connections)}.{nameof(_availableHttp11Connections.Count)} == 0"); + Debug.Assert((_availableHttp2Connections?.Count ?? 0) == 0, $"Expected {nameof(_availableHttp2Connections)}.{nameof(_availableHttp2Connections.Count)} == 0"); } + + // Dispose outside the lock to avoid lock re-entrancy issues. + toDispose?.ForEach(c => c.Dispose()); } /// @@ -1753,79 +1973,60 @@ namespace System.Net.Http TimeSpan pooledConnectionLifetime = _poolManager.Settings._pooledConnectionLifetime; TimeSpan pooledConnectionIdleTimeout = _poolManager.Settings._pooledConnectionIdleTimeout; - List list = _idleConnections; - List? toDispose = null; - bool tookLock = false; + List? toDispose = null; - try + lock (SyncObj) { - if (NetEventSource.Log.IsEnabled()) Trace("Cleaning pool."); - Monitor.Enter(SyncObj, ref tookLock); - - // Get the current time. This is compared against each connection's last returned - // time to determine whether a connection is too old and should be closed. - long nowTicks = Environment.TickCount64; - // Copy the reference to a local variable to simplify the removal logic below. - Http2Connection[]? localHttp2Connections = _http2Connections; - - if (localHttp2Connections != null) + // If there are now no connections associated with this pool, we can dispose of it. We + // avoid aggressively cleaning up pools that have recently been used but currently aren't; + // if a pool was used since the last time we cleaned up, give it another chance. New pools + // start out saying they've recently been used, to give them a bit of breathing room and time + // for the initial collection to be added to it. + if (!_usedSinceLastCleanup && _associatedHttp11ConnectionCount == 0 && _associatedHttp2ConnectionCount == 0) { - Http2Connection[]? newHttp2Connections = null; - int newIndex = 0; - for (int i = 0; i < localHttp2Connections.Length; i++) - { - Http2Connection http2Connection = localHttp2Connections[i]; - if (http2Connection.IsExpired(nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout)) - { - http2Connection.Dispose(); - - if (newHttp2Connections == null) - { - newHttp2Connections = new Http2Connection[localHttp2Connections.Length]; - if (i > 0) - { - // Copy valid connections residing at the beggining of the current collection. - Array.Copy(localHttp2Connections, newHttp2Connections, i); - newIndex = i; - } - } - } - else if (newHttp2Connections != null) - { - newHttp2Connections[newIndex] = localHttp2Connections[i]; - newIndex++; - } - } - - if (newHttp2Connections != null) - { - //Some connections have been removed, so _http2Connections must be replaced. - if (newIndex > 0) - { - Array.Resize(ref newHttp2Connections, newIndex); - _http2Connections = newHttp2Connections; - } - else - { - // All connections expired. - _http2Connections = null; - } - } + _disposed = true; + return true; // Pool is disposed of. It should be removed. } - // Find the first item which needs to be removed. + // Reset the cleanup flag. Any pools that are empty and not used since the last cleanup + // will be purged next time around. + _usedSinceLastCleanup = false; + + long nowTicks = Environment.TickCount64; + + ScavengeConnectionList(_availableHttp11Connections, ref toDispose, nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout); + if (_availableHttp2Connections is not null) + { + int removed = ScavengeConnectionList(_availableHttp2Connections, ref toDispose, nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout); + _associatedHttp2ConnectionCount -= removed; + + // Note: Http11 connections will decrement the _associatedHttp11ConnectionCount when disposed. + // Http2 connections will not, hence the difference in handing _associatedHttp2ConnectionCount. + } + } + + // Dispose the stale connections outside the pool lock, to avoid holding the lock too long. + toDispose?.ForEach(c => c.Dispose()); + + // Pool is active. Should not be removed. + return false; + + static int ScavengeConnectionList(List list, ref List? toDispose, long nowTicks, TimeSpan pooledConnectionLifetime, TimeSpan pooledConnectionIdleTimeout) + where T : HttpConnectionBase + { int freeIndex = 0; - while (freeIndex < list.Count && list[freeIndex].IsUsable(nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout)) + while (freeIndex < list.Count && IsUsableConnection(list[freeIndex], nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout)) { freeIndex++; } // If freeIndex == list.Count, nothing needs to be removed. // But if it's < list.Count, at least one connection needs to be purged. + int removed = 0; if (freeIndex < list.Count) { // We know the connection at freeIndex is unusable, so dispose of it. - toDispose = new List { list[freeIndex]._connection }; + toDispose ??= new List { list[freeIndex] }; // Find the first item after the one to be removed that should be kept. int current = freeIndex + 1; @@ -1833,9 +2034,9 @@ namespace System.Net.Http { // Look for the first item to be kept. Along the way, any // that shouldn't be kept are disposed of. - while (current < list.Count && !list[current].IsUsable(nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout)) + while (current < list.Count && !IsUsableConnection(list[current], nowTicks, pooledConnectionLifetime, pooledConnectionIdleTimeout)) { - toDispose.Add(list[current]._connection); + toDispose.Add(list[current]); current++; } @@ -1851,38 +2052,45 @@ namespace System.Net.Http // At this point, good connections have been moved below freeIndex, and garbage connections have // been added to the dispose list, so clear the end of the list past freeIndex. - list.RemoveRange(freeIndex, list.Count - freeIndex); + removed = list.Count - freeIndex; + list.RemoveRange(freeIndex, removed); + } - // If there are now no connections associated with this pool, we can dispose of it. We - // avoid aggressively cleaning up pools that have recently been used but currently aren't; - // if a pool was used since the last time we cleaned up, give it another chance. New pools - // start out saying they've recently been used, to give them a bit of breathing room and time - // for the initial collection to be added to it. - if (_associatedConnectionCount == 0 && !_usedSinceLastCleanup && _http2Connections == null) + return removed; + } + + static bool IsUsableConnection(HttpConnectionBase connection, long nowTicks, TimeSpan pooledConnectionLifetime, TimeSpan pooledConnectionIdleTimeout) + { + // Validate that the connection hasn't been idle in the pool for longer than is allowed. + if (pooledConnectionIdleTimeout != Timeout.InfiniteTimeSpan) + { + long idleTicks = connection.GetIdleTicks(nowTicks); + if (idleTicks > pooledConnectionIdleTimeout.TotalMilliseconds) { - Debug.Assert(list.Count == 0, $"Expected {nameof(list)}.{nameof(list.Count)} == 0"); - _disposed = true; - return true; // Pool is disposed of. It should be removed. + if (NetEventSource.Log.IsEnabled()) connection.Trace($"Scavenging connection. Idle {TimeSpan.FromMilliseconds(idleTicks)} > {pooledConnectionIdleTimeout}."); + return false; } } - // Reset the cleanup flag. Any pools that are empty and not used since the last cleanup - // will be purged next time around. - _usedSinceLastCleanup = false; - } - finally - { - if (tookLock) + // Validate that the connection lifetime has not been exceeded. + if (pooledConnectionLifetime != Timeout.InfiniteTimeSpan) { - Monitor.Exit(SyncObj); + long lifetimeTicks = connection.GetLifetimeTicks(nowTicks); + if (lifetimeTicks > pooledConnectionLifetime.TotalMilliseconds) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace($"Scavenging connection. Lifetime {TimeSpan.FromMilliseconds(lifetimeTicks)} > {pooledConnectionLifetime}."); + return false; + } } - // Dispose the stale connections outside the pool lock. - toDispose?.ForEach(c => c.Dispose()); - } + if (!connection.CheckUsabilityOnScavenge()) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace($"Scavenging connection. Unexpected data or EOF received."); + return false; + } - // Pool is active. Should not be removed. - return false; + return true; + } } /// Gets whether we're running on Windows 7 or Windows 2008 R2. @@ -1900,8 +2108,13 @@ namespace System.Net.Http internal void HeartBeat() { - Http2Connection[]? localHttp2Connections = _http2Connections; - if (localHttp2Connections != null) + Http2Connection[]? localHttp2Connections; + lock (SyncObj) + { + localHttp2Connections = _availableHttp2Connections?.ToArray(); + } + + if (localHttp2Connections is not null) { foreach (Http2Connection http2Connection in localHttp2Connections) { @@ -1910,7 +2123,6 @@ namespace System.Net.Http } } - // For diagnostic purposes public override string ToString() => $"{nameof(HttpConnectionPool)} " + @@ -1930,67 +2142,89 @@ namespace System.Net.Http memberName, // method name message); // message - /// A cached idle connection and metadata about it. - [StructLayout(LayoutKind.Auto)] - private readonly struct CachedConnection : IEquatable + private struct RequestQueue { - /// The cached connection. - internal readonly HttpConnection _connection; - /// The last tick count at which the connection was used. - internal readonly long _returnedTickCount; - - /// Initializes the cached connection and its associated metadata. - /// The connection. - public CachedConnection(HttpConnection connection) + private struct QueueItem { - Debug.Assert(connection != null); - _connection = connection; - _returnedTickCount = Environment.TickCount64; + public HttpRequestMessage Request; + public TaskCompletionSourceWithCancellation Waiter; } - /// Gets whether the connection is currently usable. - /// The current tick count. Passed in to amortize the cost of calling Environment.TickCount. - /// How long a connection can be open to be considered reusable. - /// How long a connection can have been idle in the pool to be considered reusable. - /// - /// true if we believe the connection can be reused; otherwise, false. There is an inherent race condition here, - /// in that the server could terminate the connection or otherwise make it unusable immediately after we check it, - /// but there's not much difference between that and starting to use the connection and then having the server - /// terminate it, which would be considered a failure, so this race condition is largely benign and inherent to - /// the nature of connection pooling. - /// - public bool IsUsable( - long nowTicks, - TimeSpan pooledConnectionLifetime, - TimeSpan pooledConnectionIdleTimeout) + private Queue? _queue; + + public TaskCompletionSourceWithCancellation EnqueueRequest(HttpRequestMessage request) { - // Validate that the connection hasn't been idle in the pool for longer than is allowed. - if ((pooledConnectionIdleTimeout != Timeout.InfiniteTimeSpan) && - ((nowTicks - _returnedTickCount) > pooledConnectionIdleTimeout.TotalMilliseconds)) + if (_queue is null) { - if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Idle {TimeSpan.FromMilliseconds((nowTicks - _returnedTickCount))} > {pooledConnectionIdleTimeout}."); - return false; + _queue = new Queue(); } - // Validate that the connection lifetime has not been exceeded. - if (_connection.LifetimeExpired(nowTicks, pooledConnectionLifetime)) - { - if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Lifetime {TimeSpan.FromMilliseconds((nowTicks - _connection.CreationTickCount))} > {pooledConnectionLifetime}."); - return false; - } - - if (!_connection.CheckUsabilityOnScavenge()) - { - if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Unexpected data or EOF received."); - return false; - } - - return true; + TaskCompletionSourceWithCancellation waiter = new TaskCompletionSourceWithCancellation(); + _queue.Enqueue(new QueueItem { Request = request, Waiter = waiter }); + return waiter; } - public bool Equals(CachedConnection other) => ReferenceEquals(other._connection, _connection); - public override bool Equals([NotNullWhen(true)] object? obj) => obj is CachedConnection && Equals((CachedConnection)obj); - public override int GetHashCode() => _connection?.GetHashCode() ?? 0; + public bool TryFailNextRequest(Exception e) + { + Debug.Assert(e is HttpRequestException or OperationCanceledException, "Unexpected exception type for connection failure"); + + if (_queue is not null) + { + // Fail the next queued request (if any) with this error. + while (_queue.TryDequeue(out QueueItem item)) + { + // Try to complete the waiter task. If it's been cancelled already, this will fail. + if (item.Waiter.TrySetException(e)) + { + return true; + } + + // Couldn't transfer to that waiter because it was cancelled. Try again. + Debug.Assert(item.Waiter.Task.IsCanceled); + } + } + + return false; + } + + public bool TryDequeueNextRequest(T connection) + { + if (_queue is not null) + { + while (_queue.TryDequeue(out QueueItem item)) + { + // Try to complete the task. If it's been cancelled already, this will return false. + if (item.Waiter.TrySetResult(connection)) + { + return true; + } + + // Couldn't transfer to that waiter because it was cancelled. Try again. + Debug.Assert(item.Waiter.Task.IsCanceled); + } + } + + return false; + } + + public bool TryPeekNextRequest([NotNullWhen(true)] out HttpRequestMessage? request) + { + if (_queue is not null) + { + if (_queue.TryPeek(out QueueItem item)) + { + request = item.Request; + return true; + } + } + + request = null; + return false; + } + + public bool IsEmpty => Count == 0; + + public int Count => (_queue?.Count ?? 0); } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index 39e125abe1c..c9c5db60d0f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -129,7 +129,7 @@ namespace System.Net.Http.Functional.Tests DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.Padded, 10, 1); await connection.WriteFrameAsync(invalidFrame); - await AssertProtocolErrorAsync(sendTask, ProtocolErrors.PROTOCOL_ERROR); + await Assert.ThrowsAsync(() => sendTask); } } @@ -244,10 +244,10 @@ namespace System.Net.Http.Functional.Tests } [ConditionalTheory(nameof(SupportsAlpn))] - [InlineData(SettingId.MaxFrameSize, 16383, ProtocolErrors.PROTOCOL_ERROR)] - [InlineData(SettingId.MaxFrameSize, 162777216, ProtocolErrors.PROTOCOL_ERROR)] - [InlineData(SettingId.InitialWindowSize, 0x80000000, ProtocolErrors.FLOW_CONTROL_ERROR)] - public async Task Http2_ServerSendsInvalidSettingsValue_Error(SettingId settingId, uint value, ProtocolErrors expectedError) + [InlineData(SettingId.MaxFrameSize, 16383)] + [InlineData(SettingId.MaxFrameSize, 162777216)] + [InlineData(SettingId.InitialWindowSize, 0x80000000)] + public async Task Http2_ServerSendsInvalidSettingsValue_Error(SettingId settingId, uint value) { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) @@ -257,7 +257,7 @@ namespace System.Net.Http.Functional.Tests // Send invalid initial SETTINGS value Http2LoopbackConnection connection = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = settingId, Value = value }); - await AssertProtocolErrorAsync(sendTask, expectedError); + await Assert.ThrowsAsync(() => sendTask); connection.Dispose(); } @@ -3383,16 +3383,15 @@ namespace System.Net.Http.Functional.Tests options.ServerCertificate = Net.Test.Common.Configuration.Certificates.GetServerCertificate(); options.ApplicationProtocols = new List() { SslApplicationProtocol.Http2 }; options.ApplicationProtocols.Add(SslApplicationProtocol.Http2); + // Negotiate TLS. await sslStream.AuthenticateAsServerAsync(options, CancellationToken.None).ConfigureAwait(false); + // Send back HTTP/1.1 response await sslStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 400 Unrecognized request\r\n\r\n"), CancellationToken.None); }); - Exception e = await Assert.ThrowsAsync(() => requestTask); - Assert.NotNull(e.InnerException); - Assert.False(e.InnerException is ObjectDisposedException); }); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index 47716f3c2c9..3f7f506aaf3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -18,10 +18,11 @@ namespace System.Net.Http.Functional.Tests private async Task ValidateConnectTimeout(HttpMessageInvoker invoker, Uri uri, int minElapsed, int maxElapsed) { var sw = Stopwatch.StartNew(); - await Assert.ThrowsAnyAsync(() => + var oce = await Assert.ThrowsAnyAsync(() => invoker.SendAsync(TestAsync, new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }, default)); sw.Stop(); + Assert.IsType(oce.InnerException); Assert.InRange(sw.ElapsedMilliseconds, minElapsed, maxElapsed); } @@ -51,8 +52,10 @@ namespace System.Net.Http.Functional.Tests } [OuterLoop] - [Fact] - public async Task ConnectTimeout_ConnectCallbackTimesOut_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ConnectTimeout_ConnectCallbackTimesOut_Throws(bool useSsl) { if (UseVersion == HttpVersion.Version30) { @@ -71,7 +74,49 @@ namespace System.Net.Http.Functional.Tests await ValidateConnectTimeout(invoker, uri, 500, 85_000); } - }, server => Task.CompletedTask); // doesn't actually connect to server + }, server => Task.CompletedTask, // doesn't actually connect to server + options: new GenericLoopbackOptions() { UseSsl = useSsl }); + } + + [OuterLoop] + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ConnectTimeout_PlaintextStreamFilterTimesOut_Throws(bool useSsl) + { + if (UseVersion == HttpVersion.Version30) + { + // HTTP3 does not support PlaintextStreamFilter + return; + } + + var releaseServer = new TaskCompletionSource(); + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + { + using (var handler = CreateHttpClientHandler()) + using (var invoker = new HttpMessageInvoker(handler)) + { + handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; + var socketsHandler = GetUnderlyingSocketsHttpHandler(handler); + socketsHandler.ConnectTimeout = TimeSpan.FromSeconds(1); + socketsHandler.PlaintextStreamFilter = async (context, token) => { await Task.Delay(-1, token); return null; }; + + await ValidateConnectTimeout(invoker, uri, 500, 85_000); + + releaseServer.SetResult(); + } + }, + async server => + { + try + { + await server.AcceptConnectionAsync(async c => + { + await releaseServer.Task; + }); + } + catch (Exception) { } // Eat exception from client timing out + }, options: new GenericLoopbackOptions() { UseSsl = useSsl }); } [OuterLoop("Incurs significant delay")] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs index dc4f2ef45ab..660a93df25b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs @@ -552,10 +552,6 @@ namespace System.Net.Http.Functional.Tests Assert.Contains(http11requestQueueDurations, d => d > 0); Assert.All(http11requestQueueDurations, d => Assert.True(d >= 0)); } - else - { - Assert.All(http11requestQueueDurations, d => Assert.True(d == 0)); - } Assert.True(eventCounters.TryGetValue("http20-requests-queue-duration", out double[] http20requestQueueDurations)); Assert.Equal(0, http20requestQueueDurations[^1]); @@ -564,10 +560,6 @@ namespace System.Net.Http.Functional.Tests Assert.Contains(http20requestQueueDurations, d => d > 0); Assert.All(http20requestQueueDurations, d => Assert.True(d >= 0)); } - else - { - Assert.All(http20requestQueueDurations, d => Assert.True(d == 0)); - } } [OuterLoop] @@ -655,12 +647,19 @@ namespace System.Net.Http.Functional.Tests ValidateConnectionEstablishedClosed(events, version); - (EventWrittenEventArgs requestLeftQueue, Guid requestLeftQueueId) = Assert.Single(events, e => e.Event.EventName == "RequestLeftQueue"); - Assert.Equal(3, requestLeftQueue.Payload.Count); - Assert.True((double)requestLeftQueue.Payload.Count > 0); // timeSpentOnQueue - Assert.Equal(version.Major, (byte)requestLeftQueue.Payload[1]); - Assert.Equal(version.Minor, (byte)requestLeftQueue.Payload[2]); + var requestLeftEvents = events.Where(e => e.Event.EventName == "RequestLeftQueue"); + Assert.Equal(2, requestLeftEvents.Count()); + foreach (var (e, _) in requestLeftEvents) + { + Assert.Equal(3, e.Payload.Count); + Assert.True((double)e.Payload[0] > 0); // timeSpentOnQueue + Assert.Equal(version.Major, (byte)e.Payload[1]); + Assert.Equal(version.Minor, (byte)e.Payload[2]); + + } + + Guid requestLeftQueueId = requestLeftEvents.Last().ActivityId; Assert.Equal(requestLeftQueueId, events.Where(e => e.Event.EventName == "RequestStart").Last().ActivityId); ValidateRequestResponseStartStopEvents(events, requestContentLength: null, responseContentLength: 0, count: 3); From 9717cf578bc38180866461cba7907e4ad6e9dd0f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 14 Jul 2021 22:41:10 -0400 Subject: [PATCH 574/926] Simplify File.WriteAllTextAsync (#55644) Avoid the unnecessary ArrayPool usage and extra layer of buffering. --- .../System.Private.CoreLib/src/System/IO/File.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 1f24874266c..d4aa96ace0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -921,6 +921,7 @@ namespace System.IO private static async Task InternalWriteAllTextAsync(StreamWriter sw, string contents, CancellationToken cancellationToken) { +#if MS_IO_REDIST char[]? buffer = null; try { @@ -931,11 +932,7 @@ namespace System.IO { int batchSize = Math.Min(DefaultBufferSize, count - index); contents.CopyTo(index, buffer, 0, batchSize); -#if MS_IO_REDIST await sw.WriteAsync(buffer, 0, batchSize).ConfigureAwait(false); -#else - await sw.WriteAsync(new ReadOnlyMemory(buffer, 0, batchSize), cancellationToken).ConfigureAwait(false); -#endif index += batchSize; } @@ -950,6 +947,13 @@ namespace System.IO ArrayPool.Shared.Return(buffer); } } +#else + using (sw) + { + await sw.WriteAsync(contents.AsMemory(), cancellationToken).ConfigureAwait(false); + await sw.FlushAsync().ConfigureAwait(false); + } +#endif } public static Task AppendAllTextAsync(string path, string? contents, CancellationToken cancellationToken = default(CancellationToken)) From 546604c70e9fb4b56cfa7b0a14dcef9f1f42ef29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 14 Jul 2021 23:12:52 -0400 Subject: [PATCH 575/926] [sgen] Transition to GC Unsafe in mono_gc_wait_for_bridge_processing (#55681) * [sgen] Transition to GC Unsafe in mono_gc_wait_for_bridge_processing Mark it as an external only API. In the runtime, use mono_gc_wait_for_bridge_processing_internal. --- src/mono/mono/metadata/sgen-bridge.c | 13 +++++++++++++ src/mono/mono/metadata/sgen-bridge.h | 2 +- src/mono/mono/metadata/sgen-client-mono.h | 5 ++++- src/mono/mono/metadata/sgen-mono.c | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/sgen-bridge.c b/src/mono/mono/metadata/sgen-bridge.c index 18aa1c2c5a3..35146dd2d2e 100644 --- a/src/mono/mono/metadata/sgen-bridge.c +++ b/src/mono/mono/metadata/sgen-bridge.c @@ -54,6 +54,14 @@ volatile gboolean mono_bridge_processing_in_progress = FALSE; */ void mono_gc_wait_for_bridge_processing (void) +{ + MONO_ENTER_GC_UNSAFE; + mono_gc_wait_for_bridge_processing_internal (); + MONO_EXIT_GC_UNSAFE; +} + +void +mono_gc_wait_for_bridge_processing_internal (void) { if (!mono_bridge_processing_in_progress) return; @@ -738,6 +746,11 @@ mono_gc_wait_for_bridge_processing (void) { } +void +mono_gc_wait_for_bridge_processing_internal (void) +{ +} + MonoGCBridgeObjectKind sgen_bridge_class_kind (MonoClass *klass) { diff --git a/src/mono/mono/metadata/sgen-bridge.h b/src/mono/mono/metadata/sgen-bridge.h index c131f306691..3f11c11bcb0 100644 --- a/src/mono/mono/metadata/sgen-bridge.h +++ b/src/mono/mono/metadata/sgen-bridge.h @@ -103,7 +103,7 @@ typedef struct { */ MONO_API void mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks); -MONO_API void mono_gc_wait_for_bridge_processing (void); +MONO_API MONO_RT_EXTERNAL_ONLY void mono_gc_wait_for_bridge_processing (void); MONO_END_DECLS diff --git a/src/mono/mono/metadata/sgen-client-mono.h b/src/mono/mono/metadata/sgen-client-mono.h index 211d9ed5549..f69e05904f6 100644 --- a/src/mono/mono/metadata/sgen-client-mono.h +++ b/src/mono/mono/metadata/sgen-client-mono.h @@ -250,10 +250,13 @@ sgen_client_bridge_processing_stw_step (void) sgen_bridge_processing_stw_step (); } +void +mono_gc_wait_for_bridge_processing_internal (void); + static void G_GNUC_UNUSED sgen_client_bridge_wait_for_processing (void) { - mono_gc_wait_for_bridge_processing (); + mono_gc_wait_for_bridge_processing_internal (); } static void G_GNUC_UNUSED diff --git a/src/mono/mono/metadata/sgen-mono.c b/src/mono/mono/metadata/sgen-mono.c index ff3bca77b8c..c04e5dba6a5 100644 --- a/src/mono/mono/metadata/sgen-mono.c +++ b/src/mono/mono/metadata/sgen-mono.c @@ -2848,7 +2848,7 @@ sgen_client_ensure_weak_gchandles_accessible (void) * should wait for bridge processing but would fail to do so. */ if (G_UNLIKELY (mono_bridge_processing_in_progress)) - mono_gc_wait_for_bridge_processing (); + mono_gc_wait_for_bridge_processing_internal (); } void* From 54c3db5f4626d0f29a74575c914f9f91f27a74cf Mon Sep 17 00:00:00 2001 From: Manish Godse <61718172+mangod9@users.noreply.github.com> Date: Wed, 14 Jul 2021 20:22:41 -0700 Subject: [PATCH 576/926] exclude UTF8 string marshallers from cg2 (#55624) * exclude UTF8 and Unicode string marshallers from cg2 * keep unicodestringmarshaller The non-pinned cases which are not supported will throw * excluding one non-r2r case All calls to InteropHelpers should now be under !READYTORUN --- .../Common/TypeSystem/Interop/IL/Marshaller.cs | 14 ++++++++++++++ .../Interop/IL/Marshaller.ReadyToRun.cs | 2 -- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index f78e4df8a29..404f6559512 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -1483,8 +1483,12 @@ namespace Internal.TypeSystem.Interop internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) { +#if READYTORUN + throw new NotSupportedException(); +#else codeStream.Emit(ILOpcode.call, emitter.NewToken( Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree"))); +#endif } protected override void TransformManagedToNative(ILCodeStream codeStream) @@ -1536,12 +1540,16 @@ namespace Internal.TypeSystem.Interop } else { +#if READYTORUN + throw new NotSupportedException(); +#else var helper = Context.GetHelperEntryPoint("InteropHelpers", "StringToUnicodeBuffer"); LoadManagedValue(codeStream); codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); StoreNativeValue(codeStream); +#endif } } @@ -1597,8 +1605,12 @@ namespace Internal.TypeSystem.Interop internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) { +#if READYTORUN + throw new NotSupportedException(); +#else codeStream.Emit(ILOpcode.call, emitter.NewToken( Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree"))); +#endif } protected override void TransformManagedToNative(ILCodeStream codeStream) @@ -1752,6 +1764,7 @@ namespace Internal.TypeSystem.Interop } } +#if !READYTORUN class UTF8StringMarshaller : Marshaller { internal override bool CleanupRequired @@ -1806,6 +1819,7 @@ namespace Internal.TypeSystem.Interop codeStream.EmitLabel(lNullCheck); } } +#endif class SafeHandleMarshaller : Marshaller { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs index 636c698e8d5..7da42e09791 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs @@ -25,8 +25,6 @@ namespace Internal.TypeSystem.Interop return new BooleanMarshaller(); case MarshallerKind.AnsiString: return new AnsiStringMarshaller(); - case MarshallerKind.UTF8String: - return new UTF8StringMarshaller(); case MarshallerKind.SafeHandle: return new SafeHandleMarshaller(); case MarshallerKind.UnicodeString: From a8926ba6ecadb259694e09650225d9094081a7df Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 14 Jul 2021 20:35:45 -0700 Subject: [PATCH 577/926] Support cloning loops with array of struct indexing expressions (#55612) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support cloning loops with array of struct indexing expressions Loop cloning, in `optExtractArrIndex()`, was matching the post-morph trees representing array index expressions. This was not necessary, as all the information we actually need is in the array bound check node, namely the array base and the array index. The code was verifying that the bounds check information matched the morph expression, but we don't really need to do that anyway, as the main thing we care about it whether or not we can remove the bounds check. The full array index expression morphed tree can be complex, especially for arrays of structs. Simply deleting this parsing code enables cloning to kick in for arrays of structs. There was one bug needing fixing, though: after removing the array bounds check, the GTF_EXCEPT bits are recalculated over the entire Statement tree. It was doing this top-down, but needs to do it bottom-up to handle the array of structs case where we have `IND(COMMA(..., ADDR()))`, to propagate the bit through the COMMA. Fixes #48897 There are a few microbenchmark wins ("runtime2" is baseline, "runtime" is diff): ``` | Method | Job | Toolchain | Mean | Error | StdDev | Median | Min | Max | Ratio | |---------- |----------- |---------------------------------------------------------------------------------- |---------:|--------:|--------:|---------:|---------:|---------:|------:| | MulMatrix | Job-LARQLS | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 370.8 ms | 1.32 ms | 1.17 ms | 370.5 ms | 369.3 ms | 373.1 ms | 1.00 | | MulMatrix | Job-GYTYVK | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 351.6 ms | 1.92 ms | 1.60 ms | 351.6 ms | 349.2 ms | 355.2 ms | 0.95 | | | | | | | | | | | | | | SpectralNorm_1 | Job-LARQLS | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 865.0 us | 16.71 us | 18.57 us | 858.8 us | 843.3 us | 896.0 us | 1.00 | | SpectralNorm_1 | Job-GYTYVK | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 844.6 us | 9.47 us | 8.40 us | 841.5 us | 836.3 us | 860.6 us | 0.97 | | | | | | | | | | | | | | BenchEmFloat | Job-LARQLS | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 3,183.1 ms | 14.45 ms | 13.52 ms | 3,188.8 ms | 3,157.0 ms | 3,205.5 ms | 1.00 | 0.00 | | BenchEmFloat | Job-GYTYVK | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 3,115.8 ms | 13.19 ms | 12.34 ms | 3,109.3 ms | 3,105.3 ms | 3,147.1 ms | 0.98 | 0.00 | | | | | | | | | | | | | | BenchAssignJagged | Job-LARQLS | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 938.1 ms | 5.26 ms | 4.66 ms | 937.8 ms | 932.3 ms | 946.4 ms | 1.00 | 0.00 | | BenchAssignJagged | Job-GYTYVK | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 954.0 ms | 12.19 ms | 10.18 ms | 951.8 ms | 943.6 ms | 978.7 ms | 1.02 | 0.01 | | | | | | | | | | | | | | BenchLUDecomp | Job-LARQLS | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 1,094.6 ms | 14.91 ms | 13.95 ms | 1,095.0 ms | 1,072.5 ms | 1,117.7 ms | 1.00 | 0.00 | | BenchLUDecomp | Job-GYTYVK | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 1,133.9 ms | 16.55 ms | 15.48 ms | 1,139.1 ms | 1,107.9 ms | 1,154.0 ms | 1.04 | 0.02 | ``` spmi diffs: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 10605 Total bytes of diff: 13993 Total bytes of delta: 3388 (31.95% of base) Total relative delta: 13.43 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 190 : 41209.dasm (89.62% of base) 190 : 19281.dasm (89.62% of base) 163 : 41303.dasm (67.08% of base) 163 : 19393.dasm (67.08% of base) 126 : 38635.dasm (50.00% of base) 126 : 17973.dasm (50.00% of base) 125 : 35718.dasm (29.07% of base) 125 : 18582.dasm (29.07% of base) 125 : 39316.dasm (29.07% of base) 124 : 12493.dasm (47.51% of base) 124 : 15994.dasm (47.51% of base) 124 : 31342.dasm (49.80% of base) 124 : 36770.dasm (48.44% of base) 124 : 30776.dasm (48.44% of base) 106 : 32284.dasm (39.85% of base) 106 : 19181.dasm (39.85% of base) 105 : 41328.dasm (53.85% of base) 105 : 19427.dasm (53.85% of base) 94 : 19275.dasm (19.34% of base) 94 : 41203.dasm (19.34% of base) Top file improvements (bytes): -32 : 22627.dasm (-12.55% of base) -32 : 35972.dasm (-12.55% of base) -32 : 30648.dasm (-12.55% of base) 36 total files with Code Size differences (3 improved, 33 regressed), 1 unchanged. Top method regressions (bytes): 190 (89.62% of base) : 41209.dasm - Dictionary`2:CopyEntries(ref,int):this 190 (89.62% of base) : 19281.dasm - Dictionary`2:CopyEntries(ref,int):this 163 (67.08% of base) : 41303.dasm - Dictionary`2:CopyTo(ref,int):this 163 (67.08% of base) : 19393.dasm - Dictionary`2:CopyTo(ref,int):this 126 (50.00% of base) : 38635.dasm - Dictionary`2:Resize(int,bool):this 126 (50.00% of base) : 17973.dasm - Dictionary`2:Resize(int,bool):this 125 (29.07% of base) : 35718.dasm - Dictionary`2:Resize(int,bool):this 125 (29.07% of base) : 18582.dasm - Dictionary`2:Resize(int,bool):this 125 (29.07% of base) : 39316.dasm - Dictionary`2:Resize(int,bool):this 124 (47.51% of base) : 12493.dasm - Dictionary`2:Resize(int,bool):this 124 (47.51% of base) : 15994.dasm - Dictionary`2:Resize(int,bool):this 124 (49.80% of base) : 31342.dasm - Dictionary`2:Resize(int,bool):this 124 (48.44% of base) : 36770.dasm - Dictionary`2:Resize(int,bool):this 124 (48.44% of base) : 30776.dasm - Dictionary`2:Resize(int,bool):this 106 (39.85% of base) : 32284.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 106 (39.85% of base) : 19181.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 105 (53.85% of base) : 41328.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 105 (53.85% of base) : 19427.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 94 (19.34% of base) : 19275.dasm - Dictionary`2:AddRange(IEnumerable`1):this 94 (19.34% of base) : 41203.dasm - Dictionary`2:AddRange(IEnumerable`1):this Top method improvements (bytes): -32 (-12.55% of base) : 22627.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this -32 (-12.55% of base) : 35972.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this -32 (-12.55% of base) : 30648.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this Top method regressions (percentages): 190 (89.62% of base) : 41209.dasm - Dictionary`2:CopyEntries(ref,int):this 190 (89.62% of base) : 19281.dasm - Dictionary`2:CopyEntries(ref,int):this 163 (67.08% of base) : 41303.dasm - Dictionary`2:CopyTo(ref,int):this 163 (67.08% of base) : 19393.dasm - Dictionary`2:CopyTo(ref,int):this 105 (53.85% of base) : 41328.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 105 (53.85% of base) : 19427.dasm - EnumerableSorter`2:ComputeKeys(ref,int):this 78 (51.66% of base) : 37496.dasm - RouteValueDictionary:TryFindItem(String,byref):bool:this 126 (50.00% of base) : 38635.dasm - Dictionary`2:Resize(int,bool):this 126 (50.00% of base) : 17973.dasm - Dictionary`2:Resize(int,bool):this 124 (49.80% of base) : 31342.dasm - Dictionary`2:Resize(int,bool):this 124 (48.44% of base) : 36770.dasm - Dictionary`2:Resize(int,bool):this 124 (48.44% of base) : 30776.dasm - Dictionary`2:Resize(int,bool):this 88 (47.57% of base) : 37495.dasm - RouteValueDictionary:TryGetValue(String,byref):bool:this 88 (47.57% of base) : 22930.dasm - RouteValueDictionary:TryGetValue(String,byref):bool:this 124 (47.51% of base) : 12493.dasm - Dictionary`2:Resize(int,bool):this 124 (47.51% of base) : 15994.dasm - Dictionary`2:Resize(int,bool):this 76 (44.97% of base) : 18170.dasm - ValueCollection:CopyTo(ref,int):this 76 (44.97% of base) : 38751.dasm - ValueCollection:CopyTo(ref,int):this 76 (44.19% of base) : 30837.dasm - ValueCollection:CopyTo(ref,int):this 76 (44.19% of base) : 36738.dasm - ValueCollection:CopyTo(ref,int):this Top method improvements (percentages): -32 (-12.55% of base) : 22627.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this -32 (-12.55% of base) : 35972.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this -32 (-12.55% of base) : 30648.dasm - ObjectEqualityComparer`1:IndexOf(ref,__Canon,int,int):int:this 36 total methods with Code Size differences (3 improved, 33 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 19656 Total bytes of diff: 21755 Total bytes of delta: 2099 (10.68% of base) Total relative delta: 6.71 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 314 : 10636.dasm (65.42% of base) 281 : 25723.dasm (9.59% of base) 276 : 10626.dasm (85.98% of base) 191 : 17802.dasm (46.81% of base) 113 : 9414.dasm (49.78% of base) 98 : 11859.dasm (19.72% of base) 98 : 11860.dasm (19.72% of base) 89 : 7680.dasm (51.45% of base) 84 : 7683.dasm (48.00% of base) 84 : 15110.dasm (8.06% of base) 80 : 7673.dasm (13.01% of base) 80 : 3345.dasm (47.62% of base) 77 : 9246.dasm (82.80% of base) 76 : 2291.dasm (44.19% of base) 62 : 14611.dasm (13.45% of base) 62 : 14612.dasm (13.36% of base) 57 : 25481.dasm (36.54% of base) 45 : 20229.dasm (22.39% of base) 28 : 24281.dasm (31.11% of base) 28 : 12802.dasm (1.63% of base) Top file improvements (bytes): -48 : 1274.dasm (-14.20% of base) -36 : 2717.dasm (-10.17% of base) -14 : 14028.dasm (-2.84% of base) -13 : 24210.dasm (-3.06% of base) -11 : 20189.dasm (-6.25% of base) -10 : 24211.dasm (-2.35% of base) -8 : 21902.dasm (-7.69% of base) -6 : 17322.dasm (-4.44% of base) -5 : 8348.dasm (-1.40% of base) -4 : 15533.dasm (-0.35% of base) -3 : 13358.dasm (-4.76% of base) -3 : 15111.dasm (-0.43% of base) -2 : 15133.dasm (-0.35% of base) -2 : 19751.dasm (-0.36% of base) -2 : 15621.dasm (-0.35% of base) 39 total files with Code Size differences (15 improved, 24 regressed), 2 unchanged. Top method regressions (bytes): 314 (65.42% of base) : 10636.dasm - EMFloat:DoEmFloatIteration(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int,int):long 281 ( 9.59% of base) : 25723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 276 (85.98% of base) : 10626.dasm - EMFloat:SetupCPUEmFloatArrays(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int) 191 (46.81% of base) : 17802.dasm - Microsoft.CodeAnalysis.MetadataDecoder`5[__Canon,__Canon,__Canon,__Canon,__Canon][System.__Canon,System.__Canon,System.__Canon,System.__Canon,System.__Canon]:DoPropertySignaturesMatch(Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],bool,bool,bool):bool:this 113 (49.78% of base) : 9414.dasm - System.Collections.Generic.Dictionary`2[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.Collections.Generic.KeyValuePair`2[System.__Canon,System.__Canon][],int):this 98 (19.72% of base) : 11859.dasm - KeyCollection[Int32,Int32][System.Int32,System.Int32]:System.Collections.ICollection.CopyTo(System.Array,int):this 98 (19.72% of base) : 11860.dasm - ValueCollection[Int32,Int32][System.Int32,System.Int32]:System.Collections.ICollection.CopyTo(System.Array,int):this 89 (51.45% of base) : 7680.dasm - KeyCollection[Int32,Int32][System.Int32,System.Int32]:CopyTo(System.Int32[],int):this 84 (48.00% of base) : 7683.dasm - ValueCollection[Int32,Int32][System.Int32,System.Int32]:CopyTo(System.Int32[],int):this 84 ( 8.06% of base) : 15110.dasm - ProtoBuf.Compiler.CompilerContext:Switch(ProtoBuf.Compiler.CodeLabel[]):this 80 (13.01% of base) : 7673.dasm - System.Collections.Generic.Dictionary`2[Int32,Int32][System.Int32,System.Int32]:AddRange(System.Collections.Generic.IEnumerable`1[KeyValuePair`2]):this 80 (47.62% of base) : 3345.dasm - KeyCollection[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.__Canon[],int):this 77 (82.80% of base) : 9246.dasm - System.Collections.Generic.GenericEqualityComparer`1[OpCode][System.Reflection.Emit.OpCode]:IndexOf(System.Reflection.Emit.OpCode[],System.Reflection.Emit.OpCode,int,int):int:this 76 (44.19% of base) : 2291.dasm - ValueCollection[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.__Canon[],int):this 62 (13.45% of base) : 14611.dasm - KeyCollection[__Canon,__Canon][System.__Canon,System.__Canon]:System.Collections.ICollection.CopyTo(System.Array,int):this 62 (13.36% of base) : 14612.dasm - ValueCollection[__Canon,__Canon][System.__Canon,System.__Canon]:System.Collections.ICollection.CopyTo(System.Array,int):this 57 (36.54% of base) : 25481.dasm - System.Collections.Tests.Perf_PriorityQueue`2[__Canon,__Canon][System.__Canon,System.__Canon]:K_Max_Elements():this 45 (22.39% of base) : 20229.dasm - WithManyWeakChildren:.ctor(WithManyChildrenBase,Microsoft.CodeAnalysis.SyntaxNode,int):this 28 (31.11% of base) : 24281.dasm - SciMark2.FFT:inverse(System.Double[]) 28 ( 1.63% of base) : 12802.dasm - AssignJagged:second_assignments(System.Int32[][],System.Int16[][]) Top method improvements (bytes): -48 (-14.20% of base) : 1274.dasm - System.Collections.Generic.ObjectEqualityComparer`1[__Canon][System.__Canon]:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -36 (-10.17% of base) : 2717.dasm - System.Collections.Generic.GenericEqualityComparer`1[__Canon][System.__Canon]:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -14 (-2.84% of base) : 14028.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_NoExpiration():this -13 (-3.06% of base) : 24210.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAv(int,System.Double[],System.Double[]):this -11 (-6.25% of base) : 20189.dasm - Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList:List(Microsoft.CodeAnalysis.GreenNode[],int):Microsoft.CodeAnalysis.GreenNode -10 (-2.35% of base) : 24211.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAtv(int,System.Double[],System.Double[]):this -8 (-7.69% of base) : 21902.dasm - System.Reflection.Internal.ObjectPool`1[__Canon][System.__Canon]:Allocate():System.__Canon:this -6 (-4.44% of base) : 17322.dasm - Microsoft.CodeAnalysis.CommonReferenceManager`2[__Canon,__Canon][System.__Canon,System.__Canon]:CheckCircularReference(System.Collections.Generic.IReadOnlyList`1[__Canon]):bool -5 (-1.40% of base) : 8348.dasm - Internal.Cryptography.Pal.ChainPal:GetChainStatusInformation(int):System.Security.Cryptography.X509Certificates.X509ChainStatus[] -4 (-0.35% of base) : 15533.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -3 (-4.76% of base) : 13358.dasm - BenchmarksGame.Fasta_1:MakeCumulative(BenchmarksGame.Fasta_1+Frequency[]) -3 (-0.43% of base) : 15111.dasm - DynamicClass:proto_1(System.Object,ProtoBuf.ProtoWriter) -2 (-0.35% of base) : 15133.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_RelativeExpiration():this -2 (-0.36% of base) : 19751.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_ExpirationTokens():this -2 (-0.35% of base) : 15621.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_SlidingExpiration():this Top method regressions (percentages): 276 (85.98% of base) : 10626.dasm - EMFloat:SetupCPUEmFloatArrays(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int) 77 (82.80% of base) : 9246.dasm - System.Collections.Generic.GenericEqualityComparer`1[OpCode][System.Reflection.Emit.OpCode]:IndexOf(System.Reflection.Emit.OpCode[],System.Reflection.Emit.OpCode,int,int):int:this 314 (65.42% of base) : 10636.dasm - EMFloat:DoEmFloatIteration(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int,int):long 89 (51.45% of base) : 7680.dasm - KeyCollection[Int32,Int32][System.Int32,System.Int32]:CopyTo(System.Int32[],int):this 113 (49.78% of base) : 9414.dasm - System.Collections.Generic.Dictionary`2[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.Collections.Generic.KeyValuePair`2[System.__Canon,System.__Canon][],int):this 84 (48.00% of base) : 7683.dasm - ValueCollection[Int32,Int32][System.Int32,System.Int32]:CopyTo(System.Int32[],int):this 80 (47.62% of base) : 3345.dasm - KeyCollection[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.__Canon[],int):this 191 (46.81% of base) : 17802.dasm - Microsoft.CodeAnalysis.MetadataDecoder`5[__Canon,__Canon,__Canon,__Canon,__Canon][System.__Canon,System.__Canon,System.__Canon,System.__Canon,System.__Canon]:DoPropertySignaturesMatch(Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],bool,bool,bool):bool:this 76 (44.19% of base) : 2291.dasm - ValueCollection[__Canon,__Canon][System.__Canon,System.__Canon]:CopyTo(System.__Canon[],int):this 57 (36.54% of base) : 25481.dasm - System.Collections.Tests.Perf_PriorityQueue`2[__Canon,__Canon][System.__Canon,System.__Canon]:K_Max_Elements():this 28 (31.11% of base) : 24281.dasm - SciMark2.FFT:inverse(System.Double[]) 45 (22.39% of base) : 20229.dasm - WithManyWeakChildren:.ctor(WithManyChildrenBase,Microsoft.CodeAnalysis.SyntaxNode,int):this 98 (19.72% of base) : 11859.dasm - KeyCollection[Int32,Int32][System.Int32,System.Int32]:System.Collections.ICollection.CopyTo(System.Array,int):this 98 (19.72% of base) : 11860.dasm - ValueCollection[Int32,Int32][System.Int32,System.Int32]:System.Collections.ICollection.CopyTo(System.Array,int):this 27 (16.17% of base) : 10346.dasm - System.Security.Cryptography.Primitives.Tests.Performance.Perf_FixedTimeEquals:Setup(System.String,System.String):this 62 (13.45% of base) : 14611.dasm - KeyCollection[__Canon,__Canon][System.__Canon,System.__Canon]:System.Collections.ICollection.CopyTo(System.Array,int):this 62 (13.36% of base) : 14612.dasm - ValueCollection[__Canon,__Canon][System.__Canon,System.__Canon]:System.Collections.ICollection.CopyTo(System.Array,int):this 80 (13.01% of base) : 7673.dasm - System.Collections.Generic.Dictionary`2[Int32,Int32][System.Int32,System.Int32]:AddRange(System.Collections.Generic.IEnumerable`1[KeyValuePair`2]):this 281 ( 9.59% of base) : 25723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 84 ( 8.06% of base) : 15110.dasm - ProtoBuf.Compiler.CompilerContext:Switch(ProtoBuf.Compiler.CodeLabel[]):this Top method improvements (percentages): -48 (-14.20% of base) : 1274.dasm - System.Collections.Generic.ObjectEqualityComparer`1[__Canon][System.__Canon]:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -36 (-10.17% of base) : 2717.dasm - System.Collections.Generic.GenericEqualityComparer`1[__Canon][System.__Canon]:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -8 (-7.69% of base) : 21902.dasm - System.Reflection.Internal.ObjectPool`1[__Canon][System.__Canon]:Allocate():System.__Canon:this -11 (-6.25% of base) : 20189.dasm - Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList:List(Microsoft.CodeAnalysis.GreenNode[],int):Microsoft.CodeAnalysis.GreenNode -3 (-4.76% of base) : 13358.dasm - BenchmarksGame.Fasta_1:MakeCumulative(BenchmarksGame.Fasta_1+Frequency[]) -6 (-4.44% of base) : 17322.dasm - Microsoft.CodeAnalysis.CommonReferenceManager`2[__Canon,__Canon][System.__Canon,System.__Canon]:CheckCircularReference(System.Collections.Generic.IReadOnlyList`1[__Canon]):bool -13 (-3.06% of base) : 24210.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAv(int,System.Double[],System.Double[]):this -14 (-2.84% of base) : 14028.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_NoExpiration():this -10 (-2.35% of base) : 24211.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAtv(int,System.Double[],System.Double[]):this -5 (-1.40% of base) : 8348.dasm - Internal.Cryptography.Pal.ChainPal:GetChainStatusInformation(int):System.Security.Cryptography.X509Certificates.X509ChainStatus[] -3 (-0.43% of base) : 15111.dasm - DynamicClass:proto_1(System.Object,ProtoBuf.ProtoWriter) -2 (-0.36% of base) : 19751.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_ExpirationTokens():this -2 (-0.35% of base) : 15133.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_RelativeExpiration():this -2 (-0.35% of base) : 15621.dasm - Microsoft.Extensions.Caching.Memory.Tests.MemoryCacheTests:AddThenRemove_SlidingExpiration():this -4 (-0.35% of base) : 15533.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) 39 total methods with Code Size differences (15 improved, 24 regressed), 2 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 50789 Total bytes of diff: 57962 Total bytes of delta: 7173 (14.12% of base) Total relative delta: 10.94 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 314 : 194633.dasm (65.42% of base) 310 : 169017.dasm (26.12% of base) 310 : 168630.dasm (26.12% of base) 310 : 168802.dasm (26.12% of base) 310 : 169110.dasm (26.12% of base) 310 : 168724.dasm (26.12% of base) 310 : 168920.dasm (26.12% of base) 281 : 248723.dasm (9.59% of base) 276 : 194632.dasm (85.98% of base) 267 : 168730.dasm (16.11% of base) 267 : 168808.dasm (16.11% of base) 267 : 169021.dasm (16.11% of base) 267 : 168926.dasm (16.11% of base) 267 : 169114.dasm (16.11% of base) 267 : 168636.dasm (16.11% of base) 259 : 213032.dasm (102.37% of base) 249 : 167322.dasm (66.58% of base) 216 : 251496.dasm (91.14% of base) 216 : 194592.dasm (12.80% of base) 174 : 249196.dasm (20.67% of base) Top file improvements (bytes): -26 : 166707.dasm (-10.36% of base) -13 : 248646.dasm (-3.06% of base) -10 : 248647.dasm (-2.35% of base) -5 : 166775.dasm (-0.69% of base) -4 : 194691.dasm (-0.35% of base) -3 : 168634.dasm (-1.91% of base) -3 : 168918.dasm (-2.75% of base) -3 : 168803.dasm (-1.91% of base) -3 : 168631.dasm (-1.91% of base) -3 : 168800.dasm (-2.75% of base) -3 : 168921.dasm (-1.91% of base) -3 : 168628.dasm (-2.75% of base) -3 : 168806.dasm (-1.91% of base) -3 : 168924.dasm (-1.91% of base) -3 : 241130.dasm (-0.99% of base) -3 : 168722.dasm (-2.75% of base) -3 : 168728.dasm (-1.91% of base) -3 : 239163.dasm (-4.76% of base) -3 : 168725.dasm (-1.91% of base) -2 : 241128.dasm (-0.77% of base) 70 total files with Code Size differences (20 improved, 50 regressed), 0 unchanged. Top method regressions (bytes): 314 (65.42% of base) : 194633.dasm - EMFloat:DoEmFloatIteration(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int,int):long 310 (26.12% of base) : 169017.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168630.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168802.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 169110.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168724.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168920.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 281 ( 9.59% of base) : 248723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 276 (85.98% of base) : 194632.dasm - EMFloat:SetupCPUEmFloatArrays(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int) 267 (16.11% of base) : 168730.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 267 (16.11% of base) : 168808.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 267 (16.11% of base) : 169021.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 267 (16.11% of base) : 168926.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 267 (16.11% of base) : 169114.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 267 (16.11% of base) : 168636.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool 259 (102.37% of base) : 213032.dasm - ArrayMarshal:IsCorrect(ArrayMarshal+S2[],ArrayMarshal+S2[]):bool 249 (66.58% of base) : 167322.dasm - Internal.IL.EcmaMethodIL:GetExceptionRegions():Internal.IL.ILExceptionRegion[]:this 216 (91.14% of base) : 251496.dasm - VectorMathTests.Program:sum(System.Numerics.Vector4[]):float 216 (12.80% of base) : 194592.dasm - Huffman:DoHuffIteration(System.Byte[],System.Byte[],System.Byte[],int,int,huff_node[]):long 174 (20.67% of base) : 249196.dasm - SimpleArray_01.Test:BadMatrixMul1() Top method improvements (bytes): -26 (-10.36% of base) : 166707.dasm - Internal.TypeSystem.TypeSystemHelpers:RequiresSlotUnification(Internal.TypeSystem.MethodDesc):bool -13 (-3.06% of base) : 248646.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAv(int,System.Double[],System.Double[]):this -10 (-2.35% of base) : 248647.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAtv(int,System.Double[],System.Double[]):this -5 (-0.69% of base) : 166775.dasm - Internal.TypeSystem.MetadataVirtualMethodAlgorithm:ResolveInterfaceMethodToDefaultImplementationOnType(Internal.TypeSystem.MethodDesc,Internal.TypeSystem.MetadataType,byref):int -4 (-0.35% of base) : 194691.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) -3 (-1.91% of base) : 168634.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-2.75% of base) : 168918.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-1.91% of base) : 168803.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-1.91% of base) : 168631.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-2.75% of base) : 168800.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-1.91% of base) : 168921.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-2.75% of base) : 168628.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-1.91% of base) : 168806.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-1.91% of base) : 168924.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-0.99% of base) : 241130.dasm - Vector3Test:VectorArray(float):int -3 (-2.75% of base) : 168722.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-1.91% of base) : 168728.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-4.76% of base) : 239163.dasm - BenchmarksGame.Fasta_1:MakeCumulative(BenchmarksGame.Fasta_1+Frequency[]) -3 (-1.91% of base) : 168725.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -2 (-0.77% of base) : 241128.dasm - Vector4Test:VectorArray(float):int Top method regressions (percentages): 259 (102.37% of base) : 213032.dasm - ArrayMarshal:IsCorrect(ArrayMarshal+S2[],ArrayMarshal+S2[]):bool 216 (91.14% of base) : 251496.dasm - VectorMathTests.Program:sum(System.Numerics.Vector4[]):float 276 (85.98% of base) : 194632.dasm - EMFloat:SetupCPUEmFloatArrays(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int) 49 (74.24% of base) : 81558.dasm - Test:Check(int) 249 (66.58% of base) : 167322.dasm - Internal.IL.EcmaMethodIL:GetExceptionRegions():Internal.IL.ILExceptionRegion[]:this 314 (65.42% of base) : 194633.dasm - EMFloat:DoEmFloatIteration(EMFloat+InternalFPF[],EMFloat+InternalFPF[],EMFloat+InternalFPF[],int,int):long 123 (60.89% of base) : 246188.dasm - ToBoxOrNotToBox.Program:PerfTest1(int,int) 151 (52.61% of base) : 213030.dasm - ArrayMarshal:NewS2arr(int,int,int,short,ushort,ubyte,byte,short,ushort,long,long,float,double):ArrayMarshal+S2[] 28 (31.11% of base) : 228715.dasm - SciMark2.FFT:inverse(System.Double[]) 89 (27.73% of base) : 167364.dasm - Internal.IL.InstantiatedMethodIL:GetLocals():Internal.TypeSystem.LocalVariableDefinition[]:this 310 (26.12% of base) : 169017.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168630.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168802.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 169110.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168724.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 310 (26.12% of base) : 168920.dasm - Helper:ValidateInnerArraySequential(InnerArraySequential,InnerArraySequential,System.String):bool 174 (20.67% of base) : 249196.dasm - SimpleArray_01.Test:BadMatrixMul1() 158 (19.90% of base) : 253494.dasm - MatrixMul.Test:MatrixMul() 141 (16.34% of base) : 249197.dasm - SimpleArray_01.Test:BadMatrixMul2() 267 (16.11% of base) : 168730.dasm - Helper:ValidateOUTER3(OUTER3,OUTER3,System.String):bool Top method improvements (percentages): -26 (-10.36% of base) : 166707.dasm - Internal.TypeSystem.TypeSystemHelpers:RequiresSlotUnification(Internal.TypeSystem.MethodDesc):bool -3 (-4.76% of base) : 239163.dasm - BenchmarksGame.Fasta_1:MakeCumulative(BenchmarksGame.Fasta_1+Frequency[]) -13 (-3.06% of base) : 248646.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAv(int,System.Double[],System.Double[]):this -3 (-2.75% of base) : 168918.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-2.75% of base) : 168800.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-2.75% of base) : 168628.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -3 (-2.75% of base) : 168722.dasm - Helper:NewInnerArraySequential(int,float,System.String):InnerArraySequential -10 (-2.35% of base) : 248647.dasm - BenchmarksGame.SpectralNorm_1:MultiplyAtv(int,System.Double[],System.Double[]):this -3 (-1.91% of base) : 168634.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-1.91% of base) : 168803.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-1.91% of base) : 168631.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-1.91% of base) : 168921.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-1.91% of base) : 168806.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-1.91% of base) : 168924.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-1.91% of base) : 168728.dasm - Helper:NewOUTER3(int,float,System.String,System.String):OUTER3 -3 (-1.91% of base) : 168725.dasm - Helper:NewInnerArrayExplicit(int,float,System.String,System.String):InnerArrayExplicit -3 (-0.99% of base) : 241130.dasm - Vector3Test:VectorArray(float):int -2 (-0.77% of base) : 241128.dasm - Vector4Test:VectorArray(float):int -5 (-0.69% of base) : 166775.dasm - Internal.TypeSystem.MetadataVirtualMethodAlgorithm:ResolveInterfaceMethodToDefaultImplementationOnType(Internal.TypeSystem.MethodDesc,Internal.TypeSystem.MetadataType,byref):int -4 (-0.35% of base) : 194691.dasm - LUDecomp:build_problem(System.Double[][],int,System.Double[]) 70 total methods with Code Size differences (20 improved, 50 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 66954 Total bytes of diff: 74145 Total bytes of delta: 7191 (10.74% of base) Total relative delta: 20.86 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 528 : 2571.dasm (72.53% of base) 450 : 25232.dasm (15.59% of base) 293 : 92711.dasm (50.96% of base) 274 : 73747.dasm (53.83% of base) 271 : 171367.dasm (17.96% of base) 241 : 204928.dasm (60.10% of base) 224 : 171366.dasm (17.39% of base) 188 : 21977.dasm (12.56% of base) 171 : 181569.dasm (71.25% of base) 168 : 75937.dasm (12.19% of base) 162 : 44518.dasm (114.89% of base) 160 : 151231.dasm (44.08% of base) 155 : 95335.dasm (7.05% of base) 152 : 84491.dasm (77.95% of base) 139 : 216480.dasm (25.88% of base) 139 : 45298.dasm (46.96% of base) 138 : 73748.dasm (66.03% of base) 134 : 181566.dasm (54.92% of base) 134 : 52201.dasm (29.39% of base) 131 : 22122.dasm (15.45% of base) Top file improvements (bytes): -37 : 73754.dasm (-0.81% of base) -36 : 24021.dasm (-10.14% of base) -32 : 24016.dasm (-9.61% of base) -20 : 192694.dasm (-7.14% of base) -16 : 161256.dasm (-6.20% of base) -16 : 216474.dasm (-1.03% of base) -11 : 173459.dasm (-4.78% of base) -10 : 179730.dasm (-1.86% of base) -10 : 149598.dasm (-1.34% of base) -9 : 150742.dasm (-7.14% of base) -8 : 85767.dasm (-7.02% of base) -8 : 174207.dasm (-0.52% of base) -6 : 10430.dasm (-3.66% of base) -6 : 174026.dasm (-1.29% of base) -6 : 174027.dasm (-1.36% of base) -5 : 93729.dasm (-0.40% of base) -5 : 203938.dasm (-1.65% of base) -4 : 150744.dasm (-0.31% of base) -3 : 173392.dasm (-0.47% of base) -3 : 174233.dasm (-0.09% of base) 103 total files with Code Size differences (33 improved, 70 regressed), 8 unchanged. Top method regressions (bytes): 528 (72.53% of base) : 2571.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol:GetParameters(Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEModuleSymbol,Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol,Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],byref):System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.CSharp.Symbols.ParameterSymbol] 450 (15.59% of base) : 25232.dasm - System.Diagnostics.Tracing.EventProvider:WriteEvent(byref,long,long,long,System.Object[]):bool:this 293 (50.96% of base) : 92711.dasm - System.Xml.Xsl.IlGen.XmlILVisitor:VisitChoice(System.Xml.Xsl.Qil.QilChoice):System.Xml.Xsl.Qil.QilNode:this 274 (53.83% of base) : 73747.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusErrors(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 271 (17.96% of base) : 171367.dasm - BatchBlockTargetCore:RetrievePostponedItemsNonGreedy(bool):this 241 (60.10% of base) : 204928.dasm - System.Net.WebSockets.WebSocketBuffer:ValidateNativeBuffers(int,int,Interop+WebSocket+Buffer[],int):this 224 (17.39% of base) : 171366.dasm - BatchBlockTargetCore:RetrievePostponedItemsGreedyBounded(bool):this 188 (12.56% of base) : 21977.dasm - Microsoft.CodeAnalysis.CSharp.Binder:BindArrayCreationWithInitializer(Microsoft.CodeAnalysis.DiagnosticBag,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Symbols.ArrayTypeSymbol,System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.CSharp.BoundExpression],System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.CSharp.BoundExpression]):Microsoft.CodeAnalysis.CSharp.BoundArrayCreation:this 171 (71.25% of base) : 181569.dasm - System.Reflection.TypeLoading.Ecma.EcmaCustomAttributeHelpers:ToApiForm(System.Collections.Generic.IList`1[System.Reflection.Metadata.CustomAttributeTypedArgument`1[System.Reflection.TypeLoading.RoType]]):System.Collections.Generic.IList`1[System.Reflection.CustomAttributeTypedArgument] 168 (12.19% of base) : 75937.dasm - System.Data.Select:CreateIndex():this 162 (114.89% of base) : 44518.dasm - System.Collections.Generic.NullableEqualityComparer`1:IndexOf(System.Nullable`1[System.Int32][],System.Nullable`1[System.Int32],int,int):int:this 160 (44.08% of base) : 151231.dasm - Microsoft.CodeAnalysis.MetadataDecoder`5:DoPropertySignaturesMatch(Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],Microsoft.CodeAnalysis.ParamInfo`1[System.__Canon][],bool,bool,bool):bool:this 155 ( 7.05% of base) : 95335.dasm - System.Xml.Xsl.Xslt.XsltInput:GetAttributes(System.Xml.Xsl.Xslt.XsltInput+XsltAttribute[]):System.Xml.Xsl.Xslt.XsltInput+ContextInfo:this 152 (77.95% of base) : 84491.dasm - System.Speech.Internal.Synthesis.EngineSiteSapi:System.Speech.Internal.Synthesis.ISpEngineSite.AddEvents(System.Speech.Internal.Synthesis.SpeechEventSapi[],int):this 139 (25.88% of base) : 216480.dasm - System.Runtime.Caching.ExpiresBucket:Expand():this 139 (46.96% of base) : 45298.dasm - AttributeSorter:Sort():this 138 (66.03% of base) : 73748.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusContinue(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 134 (54.92% of base) : 181566.dasm - System.Reflection.TypeLoading.Ecma.EcmaCustomAttributeHelpers:ToApiForm(System.Collections.Generic.IList`1[System.Reflection.Metadata.CustomAttributeNamedArgument`1[System.Reflection.TypeLoading.RoType]],System.Type):System.Collections.Generic.IList`1[System.Reflection.CustomAttributeNamedArgument] 134 (29.39% of base) : 52201.dasm - System.Data.OleDb.OleDbCommand:ApplyParameterBindings(System.Data.Common.UnsafeNativeMethods+ICommandWithParameters,System.Data.OleDb.tagDBPARAMBINDINFO[]):this 131 (15.45% of base) : 22122.dasm - Microsoft.CodeAnalysis.CSharp.Binder:GetParamArrayArgument(Microsoft.CodeAnalysis.CSharp.Symbols.ParameterSymbol,System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.TypedConstant],int,int,Microsoft.CodeAnalysis.CSharp.Conversions):Microsoft.CodeAnalysis.TypedConstant Top method improvements (bytes): -37 (-0.81% of base) : 73754.dasm - System.Data.Common.DbDataAdapter:Update(System.Data.DataRow[],System.Data.Common.DataTableMapping):int:this -36 (-10.14% of base) : 24021.dasm - System.Collections.Generic.GenericEqualityComparer`1:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -32 (-9.61% of base) : 24016.dasm - System.Collections.Generic.ObjectEqualityComparer`1:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -20 (-7.14% of base) : 192694.dasm - Internal.Cryptography.Helpers:FixupKeyParity(System.Byte[]):System.Byte[] -16 (-6.20% of base) : 161256.dasm - System.Drawing.Drawing2D.PathGradientBrush:set_SurroundColors(System.Drawing.Color[]):this -16 (-1.03% of base) : 216474.dasm - System.Runtime.Caching.ExpiresBucket:FlushExpiredItems(System.DateTime,bool):int:this -11 (-4.78% of base) : 173459.dasm - Internal.TypeSystem.TypeSystemHelpers:RequiresSlotUnification(Internal.TypeSystem.MethodDesc):bool -10 (-1.86% of base) : 179730.dasm - System.Security.Cryptography.Pkcs.SignedCms:AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2):this -10 (-1.34% of base) : 149598.dasm - Microsoft.CodeAnalysis.VersionHelper:TryParse(System.String,bool,ushort,byref):bool -9 (-7.14% of base) : 150742.dasm - Microsoft.CodeAnalysis.CommonReferenceManager`2:CheckCircularReference(System.Collections.Generic.IReadOnlyList`1[Microsoft.CodeAnalysis.CommonReferenceManager`2+AssemblyReferenceBinding[System.__Canon, System.__Canon][]]):bool -8 (-7.02% of base) : 85767.dasm - System.Reflection.Internal.ObjectPool`1:Allocate():System.__Canon:this -8 (-0.52% of base) : 174207.dasm - Internal.IL.ILStackHelper:ComputeMaxStack(Internal.IL.MethodIL):int -6 (-3.66% of base) : 10430.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxList:List(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode[],int):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode -6 (-1.29% of base) : 174026.dasm - Internal.TypeSystem.DefType:ComputeStaticFieldLayout(int):this -6 (-1.36% of base) : 174027.dasm - Internal.TypeSystem.DefType:ComputeInstanceLayout(int):this -5 (-0.40% of base) : 93729.dasm - System.Xml.Xsl.Runtime.XmlQueryStaticData:GetObjectData(byref,byref):this -5 (-1.65% of base) : 203938.dasm - Internal.Cryptography.Pal.ChainPal:GetChainStatusInformation(int):System.Security.Cryptography.X509Certificates.X509ChainStatus[] -4 (-0.31% of base) : 150744.dasm - Microsoft.CodeAnalysis.CommonReferenceManager`2:ReuseAssemblySymbolsWithNoPiaLocalTypes(Microsoft.CodeAnalysis.CommonReferenceManager`2+BoundInputAssembly[System.__Canon, System.__Canon][],System.__Canon[],System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.CommonReferenceManager`2+AssemblyData[System.__Canon, System.__Canon]],int):bool:this -3 (-0.47% of base) : 173392.dasm - Internal.TypeSystem.MetadataVirtualMethodAlgorithm:ResolveInterfaceMethodToDefaultImplementationOnType(Internal.TypeSystem.MethodDesc,Internal.TypeSystem.MetadataType,byref):int -3 (-0.09% of base) : 174233.dasm - Internal.IL.MethodILDebugView:get_Disassembly():System.String:this Top method regressions (percentages): 162 (114.89% of base) : 44518.dasm - System.Collections.Generic.NullableEqualityComparer`1:IndexOf(System.Nullable`1[System.Int32][],System.Nullable`1[System.Int32],int,int):int:this 116 (89.23% of base) : 75940.dasm - System.Data.Select:CompareClosestCandidateIndexDesc(System.Data.IndexField[]):int:this 152 (77.95% of base) : 84491.dasm - System.Speech.Internal.Synthesis.EngineSiteSapi:System.Speech.Internal.Synthesis.ISpEngineSite.AddEvents(System.Speech.Internal.Synthesis.SpeechEventSapi[],int):this 528 (72.53% of base) : 2571.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol:GetParameters(Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEModuleSymbol,Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol,Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],byref):System.Collections.Immutable.ImmutableArray`1[Microsoft.CodeAnalysis.CSharp.Symbols.ParameterSymbol] 171 (71.25% of base) : 181569.dasm - System.Reflection.TypeLoading.Ecma.EcmaCustomAttributeHelpers:ToApiForm(System.Collections.Generic.IList`1[System.Reflection.Metadata.CustomAttributeTypedArgument`1[System.Reflection.TypeLoading.RoType]]):System.Collections.Generic.IList`1[System.Reflection.CustomAttributeTypedArgument] 138 (66.03% of base) : 73748.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusContinue(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 63 (61.76% of base) : 204826.dasm - System.Net.WebSockets.WebSocketProtocolComponent:MarshalHttpHeaders(long,int):Interop+WebSocket+HttpHeader[] 241 (60.10% of base) : 204928.dasm - System.Net.WebSockets.WebSocketBuffer:ValidateNativeBuffers(int,int,Interop+WebSocket+Buffer[],int):this 51 (60.00% of base) : 73746.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusSkip(System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 130 (58.82% of base) : 182039.dasm - System.Reflection.TypeLoading.CustomAttributeHelpers:CloneForApiReturn(System.Collections.Generic.IList`1[System.Reflection.CustomAttributeNamedArgument]):System.Collections.ObjectModel.ReadOnlyCollection`1[System.Reflection.CustomAttributeNamedArgument] 107 (58.47% of base) : 43851.dasm - System.Collections.Generic.Dictionary`2:CopyTo(System.Collections.Generic.KeyValuePair`2[System.UInt64, System.Char][],int):this 93 (58.13% of base) : 44836.dasm - System.Collections.Generic.GenericEqualityComparer`1:IndexOf(System.ValueTuple`3[System.IntPtr, System.__Canon, System.Int32][],System.ValueTuple`3[System.IntPtr, System.__Canon, System.Int32],int,int):int:this 114 (57.00% of base) : 182040.dasm - System.Reflection.TypeLoading.CustomAttributeHelpers:CloneForApiReturn(System.Collections.Generic.IList`1[System.Reflection.CustomAttributeTypedArgument]):System.Collections.ObjectModel.ReadOnlyCollection`1[System.Reflection.CustomAttributeTypedArgument] 97 (55.43% of base) : 44834.dasm - System.Collections.Generic.EqualityComparer`1:IndexOf(System.Resources.ResourceLocator[],System.Resources.ResourceLocator,int,int):int:this 95 (55.23% of base) : 44715.dasm - ValueCollection:CopyTo(System.Char[],int):this 134 (54.92% of base) : 181566.dasm - System.Reflection.TypeLoading.Ecma.EcmaCustomAttributeHelpers:ToApiForm(System.Collections.Generic.IList`1[System.Reflection.Metadata.CustomAttributeNamedArgument`1[System.Reflection.TypeLoading.RoType]],System.Type):System.Collections.Generic.IList`1[System.Reflection.CustomAttributeNamedArgument] 274 (53.83% of base) : 73747.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusErrors(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 118 (51.30% of base) : 23975.dasm - System.Collections.Generic.Dictionary`2:CopyTo(System.Collections.Generic.KeyValuePair`2[System.__Canon, System.__Canon][],int):this 63 (51.22% of base) : 179690.dasm - System.Security.Cryptography.Pkcs.SignerInfo:FindAttributeIndexByOid(System.Security.Cryptography.Asn1.AttributeAsn[],System.Security.Cryptography.Oid,int):int 293 (50.96% of base) : 92711.dasm - System.Xml.Xsl.IlGen.XmlILVisitor:VisitChoice(System.Xml.Xsl.Qil.QilChoice):System.Xml.Xsl.Qil.QilNode:this Top method improvements (percentages): -36 (-10.14% of base) : 24021.dasm - System.Collections.Generic.GenericEqualityComparer`1:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -32 (-9.61% of base) : 24016.dasm - System.Collections.Generic.ObjectEqualityComparer`1:IndexOf(System.__Canon[],System.__Canon,int,int):int:this -20 (-7.14% of base) : 192694.dasm - Internal.Cryptography.Helpers:FixupKeyParity(System.Byte[]):System.Byte[] -9 (-7.14% of base) : 150742.dasm - Microsoft.CodeAnalysis.CommonReferenceManager`2:CheckCircularReference(System.Collections.Generic.IReadOnlyList`1[Microsoft.CodeAnalysis.CommonReferenceManager`2+AssemblyReferenceBinding[System.__Canon, System.__Canon][]]):bool -8 (-7.02% of base) : 85767.dasm - System.Reflection.Internal.ObjectPool`1:Allocate():System.__Canon:this -16 (-6.20% of base) : 161256.dasm - System.Drawing.Drawing2D.PathGradientBrush:set_SurroundColors(System.Drawing.Color[]):this -11 (-4.78% of base) : 173459.dasm - Internal.TypeSystem.TypeSystemHelpers:RequiresSlotUnification(Internal.TypeSystem.MethodDesc):bool -6 (-3.66% of base) : 10430.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxList:List(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode[],int):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode -2 (-2.78% of base) : 33665.dasm - MultiElementAsyncLocalValueMap:TryGetValue(System.Threading.IAsyncLocal,byref):bool:this -2 (-2.67% of base) : 193636.dasm - System.Net.ContextFlagsAdapterPal:GetInteropFromContextFlagsPal(int):int -2 (-2.67% of base) : 83190.dasm - System.Net.ContextFlagsAdapterPal:GetInteropFromContextFlagsPal(int):int -2 (-2.67% of base) : 215902.dasm - System.Net.ContextFlagsAdapterPal:GetInteropFromContextFlagsPal(int):int -2 (-2.56% of base) : 205146.dasm - System.Net.ContextFlagsAdapterPal:GetInteropFromContextFlagsPal(int):int -10 (-1.86% of base) : 179730.dasm - System.Security.Cryptography.Pkcs.SignedCms:AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2):this -5 (-1.65% of base) : 203938.dasm - Internal.Cryptography.Pal.ChainPal:GetChainStatusInformation(int):System.Security.Cryptography.X509Certificates.X509ChainStatus[] -6 (-1.36% of base) : 174027.dasm - Internal.TypeSystem.DefType:ComputeInstanceLayout(int):this -1 (-1.35% of base) : 215903.dasm - System.Net.ContextFlagsAdapterPal:GetContextFlagsPalFromInterop(int):int -1 (-1.35% of base) : 83191.dasm - System.Net.ContextFlagsAdapterPal:GetContextFlagsPalFromInterop(int):int -1 (-1.35% of base) : 193637.dasm - System.Net.ContextFlagsAdapterPal:GetContextFlagsPalFromInterop(int):int -10 (-1.34% of base) : 149598.dasm - Microsoft.CodeAnalysis.VersionHelper:TryParse(System.String,bool,ushort,byref):bool 103 total methods with Code Size differences (33 improved, 70 regressed), 8 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 70371 Total bytes of diff: 77796 Total bytes of delta: 7425 (10.55% of base) Total relative delta: 25.59 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 491 : 45068.dasm (69.55% of base) 310 : 145953.dasm (43.79% of base) 299 : 203675.dasm (61.40% of base) 260 : 117169.dasm (43.12% of base) 249 : 160923.dasm (66.58% of base) 216 : 220005.dasm (74.23% of base) 184 : 20289.dasm (127.78% of base) 163 : 152884.dasm (76.53% of base) 160 : 20380.dasm (21.22% of base) 160 : 20410.dasm (70.18% of base) 158 : 40560.dasm (15.24% of base) 156 : 115022.dasm (11.76% of base) 156 : 132472.dasm (41.71% of base) 154 : 20309.dasm (105.48% of base) 147 : 41852.dasm (76.17% of base) 144 : 20362.dasm (50.35% of base) 144 : 40912.dasm (41.14% of base) 140 : 214346.dasm (24.39% of base) 140 : 214409.dasm (24.39% of base) 139 : 117168.dasm (73.54% of base) Top file improvements (bytes): -71 : 208247.dasm (-6.07% of base) -42 : 117208.dasm (-0.83% of base) -26 : 161794.dasm (-10.36% of base) -23 : 20296.dasm (-9.24% of base) -23 : 20312.dasm (-9.24% of base) -23 : 20302.dasm (-9.39% of base) -23 : 20299.dasm (-9.16% of base) -22 : 20270.dasm (-13.17% of base) -22 : 20276.dasm (-13.17% of base) -22 : 20306.dasm (-6.43% of base) -20 : 216496.dasm (-6.94% of base) -17 : 148574.dasm (-0.33% of base) -15 : 49464.dasm (-0.54% of base) -15 : 8645.dasm (-2.91% of base) -11 : 35759.dasm (-6.25% of base) -10 : 20287.dasm (-7.04% of base) -8 : 151645.dasm (-7.69% of base) -5 : 144804.dasm (-0.35% of base) -5 : 221018.dasm (-1.40% of base) -5 : 161876.dasm (-0.70% of base) 123 total files with Code Size differences (36 improved, 87 regressed), 5 unchanged. Top method regressions (bytes): 491 (69.55% of base) : 45068.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol:GetParameters(Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEModuleSymbol,Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.PEPropertySymbol,Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],Microsoft.CodeAnalysis.ParamInfo`1[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol][],byref):System.Collections.Immutable.ImmutableArray`1[[Microsoft.CodeAnalysis.CSharp.Symbols.ParameterSymbol, Microsoft.CodeAnalysis.CSharp, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]] 310 (43.79% of base) : 145953.dasm - System.Xml.Xsl.IlGen.XmlILVisitor:VisitChoice(System.Xml.Xsl.Qil.QilChoice):System.Xml.Xsl.Qil.QilNode:this 299 (61.40% of base) : 203675.dasm - System.Net.WebSockets.WebSocketBuffer:ValidateNativeBuffers(int,int,Interop+WebSocket+Buffer[],int):this 260 (43.12% of base) : 117169.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusErrors(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this 249 (66.58% of base) : 160923.dasm - Internal.IL.EcmaMethodIL:GetExceptionRegions():Internal.IL.ILExceptionRegion[]:this 216 (74.23% of base) : 220005.dasm - System.Security.Cryptography.Pkcs.SignerInfo:FindAttributeIndexByOid(System.Security.Cryptography.Asn1.AttributeAsn[],System.Security.Cryptography.Oid,int):int 184 (127.78% of base) : 20289.dasm - System.Collections.Generic.NullableEqualityComparer`1[Byte][System.Byte]:IndexOf(System.Nullable`1[System.Byte][],System.Nullable`1[Byte],int,int):int:this 163 (76.53% of base) : 152884.dasm - System.Speech.Internal.Synthesis.EngineSiteSapi:System.Speech.Internal.Synthesis.ISpEngineSite.AddEvents(System.Speech.Internal.Synthesis.SpeechEventSapi[],int):this 160 (21.22% of base) : 20380.dasm - System.Collections.Generic.Dictionary`2[Byte,Nullable`1][System.Byte,System.Nullable`1[System.Int32]]:System.Collections.ICollection.CopyTo(System.Array,int):this 160 (70.18% of base) : 20410.dasm - System.Collections.Generic.Dictionary`2[Byte,Nullable`1][System.Byte,System.Nullable`1[System.Int32]]:CopyTo(System.Collections.Generic.KeyValuePair`2[System.Byte,System.Nullable`1[System.Int32]][],int):this 158 (15.24% of base) : 40560.dasm - Microsoft.CodeAnalysis.CSharp.Emit.CSharpDefinitionMap:CreateLocalSlotMap(Microsoft.CodeAnalysis.Emit.EditAndContinueMethodDebugInformation,System.Collections.Immutable.ImmutableArray`1[[Microsoft.CodeAnalysis.LocalInfo`1[[Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol, Microsoft.CodeAnalysis.CSharp, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], Microsoft.CodeAnalysis, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]):System.Collections.Immutable.ImmutableArray`1[EncLocalInfo] 156 (11.76% of base) : 115022.dasm - System.Data.Select:CreateIndex():this 156 (41.71% of base) : 132472.dasm - AttributeSorter:Sort():this 154 (105.48% of base) : 20309.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Vector`1][System.Numerics.Vector`1[System.Single]]:IndexOf(System.Numerics.Vector`1[System.Single][],System.Numerics.Vector`1[Single],int,int):int:this 147 (76.17% of base) : 41852.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor:Equals(Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor,bool,bool):bool:this 144 (50.35% of base) : 20362.dasm - System.Collections.Generic.Dictionary`2[__Canon,Nullable`1][System.__Canon,System.Nullable`1[System.Int32]]:CopyTo(System.Collections.Generic.KeyValuePair`2[System.__Canon,System.Nullable`1[System.Int32]][],int):this 144 (41.14% of base) : 40912.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeManager:IsSameType(Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol,Microsoft.CodeAnalysis.CSharp.Symbols.TypeSymbol,bool,bool):bool 140 (24.39% of base) : 214346.dasm - System.Runtime.Caching.ExpiresBucket:Expand():this 140 (24.39% of base) : 214409.dasm - System.Runtime.Caching.UsageBucket:Expand():this 139 (73.54% of base) : 117168.dasm - System.Data.Common.DbDataAdapter:UpdatedRowStatusContinue(System.Data.Common.RowUpdatedEventArgs,System.Data.Common.DbDataAdapter+BatchCommandInfo[],int):int:this Top method improvements (bytes): -71 (-6.07% of base) : 208247.dasm - System.Net.Security.SslStreamCertificateContext:.ctor(System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.X509Certificates.X509Certificate2[],System.Net.Security.SslCertificateTrust):this -42 (-0.83% of base) : 117208.dasm - System.Data.Common.DbDataAdapter:Update(System.Data.DataRow[],System.Data.Common.DataTableMapping):int:this -26 (-10.36% of base) : 161794.dasm - Internal.TypeSystem.TypeSystemHelpers:RequiresSlotUnification(Internal.TypeSystem.MethodDesc):bool -23 (-9.24% of base) : 20296.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Byte][System.Byte]:IndexOf(System.Byte[],ubyte,int,int):int:this -23 (-9.24% of base) : 20312.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Int64][System.Int64]:IndexOf(System.Int64[],long,int,int):int:this -23 (-9.39% of base) : 20302.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Int32][System.Int32]:IndexOf(System.Int32[],int,int,int):int:this -23 (-9.16% of base) : 20299.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Int16][System.Int16]:IndexOf(System.Int16[],short,int,int):int:this -22 (-13.17% of base) : 20270.dasm - System.Collections.Generic.GenericEqualityComparer`1[Byte][System.Byte]:IndexOf(System.Byte[],ubyte,int,int):int:this -22 (-13.17% of base) : 20276.dasm - System.Collections.Generic.GenericEqualityComparer`1[Int16][System.Int16]:IndexOf(System.Int16[],short,int,int):int:this -22 (-6.43% of base) : 20306.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Double][System.Double]:IndexOf(System.Double[],double,int,int):int:this -20 (-6.94% of base) : 216496.dasm - Internal.Cryptography.Helpers:FixupKeyParity(System.Byte[]):System.Byte[] -17 (-0.33% of base) : 148574.dasm - System.Reflection.PortableExecutable.PEBuilder:WritePEHeader(System.Reflection.Metadata.BlobBuilder,System.Reflection.PortableExecutable.PEDirectoriesBuilder,System.Collections.Immutable.ImmutableArray`1[SerializedSection]):this -15 (-0.54% of base) : 49464.dasm - Microsoft.CodeAnalysis.VisualBasic.IteratorAndAsyncCaptureWalker:Analyze(Microsoft.CodeAnalysis.VisualBasic.FlowAnalysisInfo,Microsoft.CodeAnalysis.DiagnosticBag):Result -15 (-2.91% of base) : 8645.dasm - Microsoft.FSharp.Collections.ArrayModule:Zip(System.Byte[],System.Nullable`1[System.Int32][]):System.Tuple`2[System.Byte,System.Nullable`1[System.Int32]][] -11 (-6.25% of base) : 35759.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxList:List(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode[],int):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode -10 (-7.04% of base) : 20287.dasm - System.Collections.Generic.GenericEqualityComparer`1[Int64][System.Int64]:IndexOf(System.Int64[],long,int,int):int:this -8 (-7.69% of base) : 151645.dasm - System.Reflection.Internal.ObjectPool`1[__Canon][System.__Canon]:Allocate():System.__Canon:this -5 (-0.35% of base) : 144804.dasm - System.Xml.Xsl.Runtime.XmlQueryStaticData:GetObjectData(byref,byref):this -5 (-1.40% of base) : 221018.dasm - Internal.Cryptography.Pal.ChainPal:GetChainStatusInformation(int):System.Security.Cryptography.X509Certificates.X509ChainStatus[] -5 (-0.70% of base) : 161876.dasm - Internal.TypeSystem.MetadataVirtualMethodAlgorithm:ResolveInterfaceMethodToDefaultImplementationOnType(Internal.TypeSystem.MethodDesc,Internal.TypeSystem.MetadataType,byref):int Top method regressions (percentages): 184 (127.78% of base) : 20289.dasm - System.Collections.Generic.NullableEqualityComparer`1[Byte][System.Byte]:IndexOf(System.Nullable`1[System.Byte][],System.Nullable`1[Byte],int,int):int:this 154 (105.48% of base) : 20309.dasm - System.Collections.Generic.ObjectEqualityComparer`1[Vector`1][System.Numerics.Vector`1[System.Single]]:IndexOf(System.Numerics.Vector`1[System.Single][],System.Numerics.Vector`1[Single],int,int):int:this 108 (91.53% of base) : 115019.dasm - System.Data.Select:CompareClosestCandidateIndexDesc(System.Data.IndexField[]):int:this 122 (81.88% of base) : 41851.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor:Equals(Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor):bool:this 163 (76.53% of base) : 152884.dasm - System.Speech.Internal.Synthesis.EngineSiteSapi:System.Speech.Internal.Synthesis.ISpEngineSite.AddEvents(System.Speech.Internal.Synthesis.SpeechEventSapi[],int):this 147 (76.17% of base) : 41852.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor:Equals(Microsoft.CodeAnalysis.CSharp.Symbols.AnonymousTypeDescriptor,bool,bool):bool:this 82 (74.55% of base) : 20283.dasm - System.Collections.Generic.GenericEqualityComparer`1[Vector`1][System.Numerics.Vector`1[System.Single]]:IndexOf(System.Numerics.Vector`1[System.Single][],System.Numerics.Vector`1[Single],int,int):int:this 216 (74.23% of base) : 220005.dasm - System.Security.Cryptography.Pkcs.SignerInfo:FindAttributeIndexByOid(System.Security.Cryptography.Asn1.AttributeAsn[],System.Security.Cryptography.Oid,int):int 139 (73.54% of base) : 117168.da… * Simplify setting GTF_IND_NONFAULTING --- src/coreclr/jit/compiler.cpp | 7 ++ src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/fgdiagnostic.cpp | 16 ++--- src/coreclr/jit/flowgraph.cpp | 3 +- src/coreclr/jit/gentree.cpp | 83 +++++++++++++++++++----- src/coreclr/jit/loopcloning.cpp | 107 ++++++------------------------- src/coreclr/jit/morph.cpp | 13 ++-- src/coreclr/jit/optimizer.cpp | 6 +- 8 files changed, 116 insertions(+), 121 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index fe5734213af..47a8aabf980 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8526,6 +8526,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * cBlocks, dBlocks : Display all the basic blocks of a function (call fgDispBasicBlocks()). * cBlocksV, dBlocksV : Display all the basic blocks of a function (call fgDispBasicBlocks(true)). * "V" means "verbose", and will dump all the trees. + * cStmt, dStmt : Display a Statement (call gtDispStmt()). * cTree, dTree : Display a tree (call gtDispTree()). * cTreeLIR, dTreeLIR : Display a tree in LIR form (call gtDispLIRNode()). * cTrees, dTrees : Display all the trees in a function (call fgDumpTrees()). @@ -8548,6 +8549,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * * The following don't require a Compiler* to work: * dRegMask : Display a regMaskTP (call dspRegMask(mask)). + * dBlockList : Display a BasicBlockList*. */ void cBlock(Compiler* comp, BasicBlock* block) @@ -8722,6 +8724,11 @@ void dBlocksV() cBlocksV(JitTls::GetCompiler()); } +void dStmt(Statement* statement) +{ + cStmt(JitTls::GetCompiler(), statement); +} + void dTree(GenTree* tree) { cTree(JitTls::GetCompiler(), tree); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2cf09127f67..e185e520808 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3117,6 +3117,8 @@ public: void gtUpdateNodeOperSideEffects(GenTree* tree); + void gtUpdateNodeOperSideEffectsPost(GenTree* tree); + // Returns "true" iff the complexity (not formally defined, but first interpretation // is #of nodes in subtree) of "tree" is greater than "limit". // (This is somewhat redundant with the "GetCostEx()/GetCostSz()" fields, but can be used diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 65934aad6ad..9f7d52cb24c 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3253,14 +3253,13 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) } //------------------------------------------------------------------------------ -// fgDebugCheckDispFlags: -// Wrapper function that displays two GTF_IND_ flags -// and then calls ftDispFlags to display the rest. +// fgDebugCheckDispFlags: Wrapper function that displays GTF_IND_ flags +// and then calls gtDispFlags to display the rest. // // Arguments: // tree - Tree whose flags are being checked -// dispFlags - the first argument for gtDispFlags -// ands hold GTF_IND_INVARIANT and GTF_IND_NONFLUALTING +// dispFlags - the first argument for gtDispFlags (flags to display), +// including GTF_IND_INVARIANT, GTF_IND_NONFAULTING, GTF_IND_NONNULL // debugFlags - the second argument to gtDispFlags // void Compiler::fgDebugCheckDispFlags(GenTree* tree, GenTreeFlags dispFlags, GenTreeDebugFlags debugFlags) @@ -3277,15 +3276,14 @@ void Compiler::fgDebugCheckDispFlags(GenTree* tree, GenTreeFlags dispFlags, GenT //------------------------------------------------------------------------------ // fgDebugCheckFlagsHelper : Check if all bits that are set in chkFlags are also set in treeFlags. // -// // Arguments: -// tree - Tree whose flags are being checked +// tree - Tree whose flags are being checked // treeFlags - Actual flags on the tree -// chkFlags - Expected flags +// chkFlags - Expected flags // // Note: // Checking that all bits that are set in treeFlags are also set in chkFlags is currently disabled. - +// void Compiler::fgDebugCheckFlagsHelper(GenTree* tree, GenTreeFlags treeFlags, GenTreeFlags chkFlags) { if (chkFlags & ~treeFlags) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 1f3e71ade8a..2cc766532e4 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -933,13 +933,12 @@ GenTreeCall* Compiler::fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls) //------------------------------------------------------------------------------ // fgAddrCouldBeNull : Check whether the address tree can represent null. // -// // Arguments: // addr - Address to check // // Return Value: // True if address could be null; false otherwise - +// bool Compiler::fgAddrCouldBeNull(GenTree* addr) { addr = addr->gtEffectiveVal(); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7f16e6cd400..c38aae02964 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -8533,7 +8533,7 @@ void Compiler::gtUpdateSideEffects(Statement* stmt, GenTree* tree) // // Arguments: // tree - Tree to update the side effects for - +// void Compiler::gtUpdateTreeAncestorsSideEffects(GenTree* tree) { assert(fgStmtListThreaded); @@ -8549,7 +8549,7 @@ void Compiler::gtUpdateTreeAncestorsSideEffects(GenTree* tree) // // Arguments: // stmt - The statement to update side effects on - +// void Compiler::gtUpdateStmtSideEffects(Statement* stmt) { fgWalkTree(stmt->GetRootNodePointer(), fgUpdateSideEffectsPre, fgUpdateSideEffectsPost); @@ -8565,7 +8565,7 @@ void Compiler::gtUpdateStmtSideEffects(Statement* stmt) // This method currently only updates GTF_EXCEPT, GTF_ASG, and GTF_CALL flags. // The other side effect flags may remain unnecessarily (conservatively) set. // The caller of this method is expected to update the flags based on the children's flags. - +// void Compiler::gtUpdateNodeOperSideEffects(GenTree* tree) { if (tree->OperMayThrow(this)) @@ -8600,6 +8600,38 @@ void Compiler::gtUpdateNodeOperSideEffects(GenTree* tree) } } +//------------------------------------------------------------------------ +// gtUpdateNodeOperSideEffectsPost: Update the side effects based on the node operation, +// in the post-order visit of a tree walk. It is expected that the pre-order visit cleared +// the bits, so the post-order visit only sets them. This is important for binary nodes +// where one child already may have set the GTF_EXCEPT bit. Note that `SetIndirExceptionFlags` +// looks at its child, which is why we need to do this in a bottom-up walk. +// +// Arguments: +// tree - Tree to update the side effects on +// +// Notes: +// This method currently only updates GTF_ASG, GTF_CALL, and GTF_EXCEPT flags. +// The other side effect flags may remain unnecessarily (conservatively) set. +// +void Compiler::gtUpdateNodeOperSideEffectsPost(GenTree* tree) +{ + if (tree->OperMayThrow(this)) + { + tree->gtFlags |= GTF_EXCEPT; + } + + if (tree->OperRequiresAsgFlag()) + { + tree->gtFlags |= GTF_ASG; + } + + if (tree->OperRequiresCallFlag(this)) + { + tree->gtFlags |= GTF_CALL; + } +} + //------------------------------------------------------------------------ // gtUpdateNodeSideEffects: Update the side effects based on the node operation and // children's side efects. @@ -8608,9 +8640,9 @@ void Compiler::gtUpdateNodeOperSideEffects(GenTree* tree) // tree - Tree to update the side effects on // // Notes: -// This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect -// flags may remain unnecessarily (conservatively) set. - +// This method currently only updates GTF_EXCEPT, GTF_ASG, and GTF_CALL flags. +// The other side effect flags may remain unnecessarily (conservatively) set. +// void Compiler::gtUpdateNodeSideEffects(GenTree* tree) { gtUpdateNodeOperSideEffects(tree); @@ -8627,24 +8659,23 @@ void Compiler::gtUpdateNodeSideEffects(GenTree* tree) //------------------------------------------------------------------------ // fgUpdateSideEffectsPre: Update the side effects based on the tree operation. +// The pre-visit walk clears GTF_ASG, GTF_CALL, and GTF_EXCEPT; the post-visit walk sets +// the bits as necessary. // // Arguments: // pTree - Pointer to the tree to update the side effects // fgWalkPre - Walk data // -// Notes: -// This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect -// flags may remain unnecessarily (conservatively) set. - Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPre(GenTree** pTree, fgWalkData* fgWalkPre) { - fgWalkPre->compiler->gtUpdateNodeOperSideEffects(*pTree); + GenTree* tree = *pTree; + tree->gtFlags &= ~(GTF_ASG | GTF_CALL | GTF_EXCEPT); return WALK_CONTINUE; } //------------------------------------------------------------------------ -// fgUpdateSideEffectsPost: Update the side effects of the parent based on the tree's flags. +// fgUpdateSideEffectsPost: Update the side effects of the node and parent based on the tree's flags. // // Arguments: // pTree - Pointer to the tree @@ -8653,10 +8684,23 @@ Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPre(GenTree** pTree, fgWalkD // Notes: // The routine is used for updating the stale side effect flags for ancestor // nodes starting from treeParent up to the top-level stmt expr. - +// Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPost(GenTree** pTree, fgWalkData* fgWalkPost) { - GenTree* tree = *pTree; + GenTree* tree = *pTree; + + // Update the node's side effects first. + fgWalkPost->compiler->gtUpdateNodeOperSideEffectsPost(tree); + + // If this node is an indir or array length, and it doesn't have the GTF_EXCEPT bit set, we + // set the GTF_IND_NONFAULTING bit. This needs to be done after all children, and this node, have + // been processed. + if (tree->OperIsIndirOrArrLength() && ((tree->gtFlags & GTF_EXCEPT) == 0)) + { + tree->gtFlags |= GTF_IND_NONFAULTING; + } + + // Then update the parent's side effects based on this node. GenTree* parent = fgWalkPost->parent; if (parent != nullptr) { @@ -9908,9 +9952,16 @@ bool GenTree::Precedes(GenTree* other) // Arguments: // comp - compiler instance // - void GenTree::SetIndirExceptionFlags(Compiler* comp) { + assert(OperIsIndirOrArrLength()); + + if (OperMayThrow(comp)) + { + gtFlags |= GTF_EXCEPT; + return; + } + GenTree* addr = nullptr; if (OperIsIndir()) { @@ -9922,7 +9973,7 @@ void GenTree::SetIndirExceptionFlags(Compiler* comp) addr = AsArrLen()->ArrRef(); } - if (OperMayThrow(comp) || ((addr->gtFlags & GTF_EXCEPT) != 0)) + if ((addr->gtFlags & GTF_EXCEPT) != 0) { gtFlags |= GTF_EXCEPT; } diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index c3991e663e1..69e916c3249 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -2074,9 +2074,15 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum) // dimension of [] encountered. // // Operation: -// Given a "tree" extract the GT_INDEX node in "result" as ArrIndex. In FlowGraph morph -// we have converted a GT_INDEX tree into a scaled index base offset expression. We need -// to reconstruct this to be able to know if this is an array access. +// Given a "tree" extract the GT_INDEX node in "result" as ArrIndex. In morph +// we have converted a GT_INDEX tree into a scaled index base offset expression. +// However, we don't actually bother to parse the morphed tree. All we care about is +// the bounds check node: it contains the array base and element index. The other side +// of the COMMA node can vary between array of primitive type and array of struct. There's +// no need to parse that, as the array bounds check contains the only thing we care about. +// In particular, we are trying to find bounds checks to remove, so only looking at the bounds +// check makes sense. We could verify that the bounds check is against the same array base/index +// but it isn't necessary. // // Assumption: // The method extracts only if the array base and indices are GT_LCL_VAR. @@ -2115,6 +2121,12 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum) // | \--* LCL_VAR int V03 loc2 // \--* CNS_INT long 16 Fseq[#FirstElem] // +// The COMMA op2 expression is the array index expression (or SIMD/Span expression). If we've got +// a "LCL_VAR int" index and "ARR_LENGTH(LCL_VAR ref)", that's good enough for us: we'll assume +// op2 is an array index expression. We don't need to match it just to ensure the index var is +// used as an index expression, or array base var is used as the array base. This saves us from parsing +// all the forms that morph can create, especially for arrays of structs. +// bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsNum) { if (tree->gtOper != GT_COMMA) @@ -2150,72 +2162,6 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN unsigned indLcl = arrBndsChk->gtIndex->AsLclVarCommon()->GetLclNum(); - GenTree* after = tree->gtGetOp2(); - - if (after->gtOper != GT_IND) - { - return false; - } - // It used to be the case that arrBndsChks for struct types would fail the previous check because - // after->gtOper was an address (for a block op). In order to avoid asmDiffs we will for now - // return false if the type of 'after' is a struct type. (This was causing us to clone loops - // that we were not previously cloning.) - // TODO-1stClassStructs: Remove this check to enable optimization of array bounds checks for struct - // types. - if (varTypeIsStruct(after)) - { - return false; - } - - GenTree* sibo = after->gtGetOp1(); // sibo = scale*index + base + offset - if (sibo->gtOper != GT_ADD) - { - return false; - } - GenTree* base = sibo->gtGetOp1(); - GenTree* sio = sibo->gtGetOp2(); // sio == scale*index + offset - if (base->OperGet() != GT_LCL_VAR || base->AsLclVarCommon()->GetLclNum() != arrLcl) - { - return false; - } - if (sio->gtOper != GT_ADD) - { - return false; - } - GenTree* ofs = sio->gtGetOp2(); - GenTree* si = sio->gtGetOp1(); // si = scale*index - if (ofs->gtOper != GT_CNS_INT) - { - return false; - } - GenTree* index; - if (si->gtOper == GT_LSH) - { - GenTree* scale = si->gtGetOp2(); - index = si->gtGetOp1(); - if (scale->gtOper != GT_CNS_INT) - { - return false; - } - } - else - { - // No scale (e.g., byte array). - index = si; - } -#ifdef TARGET_64BIT - if (index->gtOper != GT_CAST) - { - return false; - } - GenTree* indexVar = index->gtGetOp1(); -#else - GenTree* indexVar = index; -#endif - if (indexVar->gtOper != GT_LCL_VAR || indexVar->AsLclVarCommon()->GetLclNum() != indLcl) - { - return false; - } if (lhsNum == BAD_VAR_NUM) { result->arrLcl = arrLcl; @@ -2241,26 +2187,13 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN // "result" contains the array access depth. The "indLcls" fields contain the indices. // // Operation: -// Recursively look for a list of array indices. In the example below, we encounter, -// V03 = ((V05 = V00[V01]), (V05[V02])) which corresponds to access of V00[V01][V02] -// The return value would then be: +// Recursively look for a list of array indices. For example, if the tree is +// V03 = (V05 = V00[V01]), V05[V02] +// that corresponds to access of V00[V01][V02]. The return value would then be: // ArrIndex result { arrLcl: V00, indLcls: [V01, V02], rank: 2 } // -// V00[V01][V02] would be morphed as: -// -// [000000001B366848] ---XG------- indir int -// [000000001B36BC50] ------------ V05 + (V02 << 2) + 16 -// [000000001B36C200] ---XG------- comma int -// [000000001B36BDB8] ---X-------- arrBndsChk(V05, V02) -// [000000001B36C278] -A-XG------- comma int -// [000000001B366730] R--XG------- indir ref -// [000000001B36C2F0] ------------ V00 + (V01 << 3) + 24 -// [000000001B36C818] ---XG------- comma ref -// [000000001B36C458] ---X-------- arrBndsChk(V00, V01) -// [000000001B36BB60] -A-XG------- = ref -// [000000001B36BAE8] D------N---- lclVar ref V05 tmp2 -// [000000001B36A668] -A-XG------- = int -// [000000001B36A5F0] D------N---- lclVar int V03 tmp0 +// Note that the array expression is implied by the array bounds check under the COMMA, and the array bounds +// checks is what is parsed from the morphed tree; the array addressing expression is not parsed. // // Assumption: // The method extracts only if the array base and indices are GT_LCL_VAR. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 354b1e4a357..000103b54c5 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5440,7 +5440,7 @@ BasicBlock* Compiler::fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay) * The orginal GT_INDEX node is bashed into the GT_IND node that accesses * the array element. We expand the GT_INDEX node into a larger tree that * evaluates the array base and index. The simplest expansion is a GT_COMMA - * with a GT_ARR_BOUND_CHK and a GT_IND with a GTF_INX_RNGCHK flag. + * with a GT_ARR_BOUNDS_CHECK and a GT_IND with a GTF_INX_RNGCHK flag. * For complex array or index expressions one or more GT_COMMA assignments * are inserted so that we only evaluate the array or index expressions once. * @@ -5530,7 +5530,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) // side-effecting. // 2. Evaluate the array index expression and store the result in a temp if the expression is complex or // side-effecting. - // 3. Perform an explicit bounds check: GT_ARR_BOUNDS_CHK(index, GT_ARR_LENGTH(array)) + // 3. Perform an explicit bounds check: GT_ARR_BOUNDS_CHECK(index, GT_ARR_LENGTH(array)) // 4. Compute the address of the element that will be accessed: // GT_ADD(GT_ADD(array, firstElementOffset), GT_MUL(index, elementSize)) // 5. Dereference the address with a GT_IND. @@ -5716,9 +5716,6 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) // play) to create a "partial" byref that doesn't point exactly to the correct object; there is risk that // the partial byref will not point within the object, and thus not get updated correctly during a GC. // This is mostly a risk in fully-interruptible code regions. - // - // NOTE: the tree form created here is pattern matched by optExtractArrIndex(), so changes here must - // be reflected there. /* Add the first element's offset */ @@ -5798,11 +5795,17 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), arrRefDefn, tree); } + JITDUMP("fgMorphArrayIndex (before remorph):\n"); + DISPTREE(tree); + // Currently we morph the tree to perform some folding operations prior // to attaching fieldSeq info and labeling constant array index contributions // fgMorphTree(tree); + JITDUMP("fgMorphArrayIndex (after remorph):\n"); + DISPTREE(tree); + // Ideally we just want to proceed to attaching fieldSeq info and labeling the // constant array index contributions, but the morphing operation may have changed // the 'tree' into something that now unconditionally throws an exception. diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 12916baa06e..b5eae0f8fab 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -7417,8 +7417,10 @@ GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, #ifdef DEBUG if (verbose) { - printf("After optRemoveRangeCheck:\n"); - gtDispTree(tree); + // gtUpdateSideEffects can update the side effects for ancestors in the tree, so display the whole statement + // tree, not just the sub-tree. + printf("After optRemoveRangeCheck for [%06u]:\n", dspTreeID(tree)); + gtDispTree(stmt->GetRootNode()); } #endif From 44655fd0dc7b08cd20eced97981bcad99e48ef67 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 15 Jul 2021 00:36:02 -0400 Subject: [PATCH 578/926] Include native underlying handler support in HttpClientHandler for iOS/tvOS/MacCatalyst and Android (#55384) Completes the plan for net6 laid out in https://github.com/dotnet/designs/blob/main/accepted/2020/mono-convergence/platform-specific-httpclient.md#the-plan-for-net-5 This change supports using either the native HttpMessageHandler types that exist in the Xamarin repos (NSUrlSessionHandler for iOS/tvOS/Catalyst and AndroidMessageHandler for Android) or SocketsHttpHandler as the underlying handler in HttpClientHandler. The behavior for new HttpClient() was established earlier in net6, but did not go all the way. For example, if the System.Net.Http.UseNativeHttpHandler feature switch was set to true, using HttpClient in different ways would lead to different underlying handlers. Before this PR: // System.Net.Http.UseNativeHttpHandler == true new HttpClient(); // Chooses the native handler as the underlying handler var handler = new HttpClientHandler(); // SocketsHttpHandler is the only choice new HttpClient(handler); The change creates a handful of partial HttpClientHandler files in order to split out the platform specific parts. As you review the PR, you'll notice a bunch of if (IsSocketHandler) blocks. The intent of these are to make use of the linker and get linked out once the linker knows which handler is the active one. get { if (IsSocketHandler) { return _socketHandler!.UseCookies; } else { return GetUseCookies(); } } Get and Set methods like GetUseCookies make it easier to tell the linker via DynamicDependency to preserve the reflection calls being made to the native underlying handler. [DynamicDependency("get_UseCookies", "Xamarin.Android.Net.AndroidMessageHandler", "Mono.Android")] private bool GetUseCookies() => (bool)InvokeNativeHandlerMethod("get_UseCookies"); It is important to point out that the underlying handler has to be derived from HttpMessageHandler. It cannot be HttpClientHandler or you'll end up with a circular dependency. --- eng/testing/tests.mobile.targets | 1 + .../System.Net.Http/ref/System.Net.Http.cs | 63 +++ .../src/ILLink/ILLink.Substitutions.xml | 4 +- .../ILLink.Suppressions.LibraryBuild.xml | 135 +++++- .../src/System.Net.Http.csproj | 16 +- ...HttpClient.CreateDefaultHandler.Android.cs | 29 -- ...Client.CreateDefaultHandler.MacCatalyst.cs | 29 -- .../Http/HttpClient.CreateDefaultHandler.cs | 13 - .../HttpClient.CreateDefaultHandler.iOS.cs | 29 -- .../HttpClient.CreateDefaultHandler.tvOS.cs | 29 -- .../src/System/Net/Http/HttpClient.cs | 2 +- .../Net/Http/HttpClientHandler.Android.cs | 237 ++++++++++ .../Net/Http/HttpClientHandler.AnyMobile.cs | 434 ++++++++++++++++++ .../Net/Http/HttpClientHandler.Apple.cs | 65 +++ .../Net/Http/HttpClientHandler.MacCatalyst.cs | 52 +++ .../src/System/Net/Http/HttpClientHandler.cs | 63 +++ .../System/Net/Http/HttpClientHandler.iOS.cs | 52 +++ .../System/Net/Http/HttpClientHandler.tvOS.cs | 52 +++ .../RuntimeSettingParser.cs | 16 + .../System.Net.Http.Unit.Tests.csproj | 6 - .../FunctionalTests/Directory.Build.props | 6 +- 21 files changed, 1185 insertions(+), 148 deletions(-) delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.Android.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.MacCatalyst.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.iOS.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.tvOS.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Android.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Apple.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.MacCatalyst.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.iOS.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.tvOS.cs diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index c3107ec4a06..45184d7f7ef 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -26,6 +26,7 @@ false false false + false diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index ef465d5a3d0..cf0b2d01f05 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -114,35 +114,87 @@ namespace System.Net.Http public HttpClientHandler() { } public bool AllowAutoRedirect { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Net.DecompressionMethods AutomaticDecompression { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public bool CheckCertificateRevocationList { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Net.Http.ClientCertificateOption ClientCertificateOptions { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Security.Cryptography.X509Certificates.X509CertificateCollection ClientCertificates { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public System.Net.CookieContainer CookieContainer { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public System.Net.ICredentials? Credentials { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public static System.Func DangerousAcceptAnyServerCertificateValidator { get { throw null; } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Net.ICredentials? DefaultProxyCredentials { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public int MaxAutomaticRedirections { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public int MaxConnectionsPerServer { get { throw null; } set { } } public long MaxRequestContentBufferSize { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public int MaxResponseHeadersLength { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public bool PreAuthenticate { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Collections.Generic.IDictionary Properties { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Net.IWebProxy? Proxy { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Func? ServerCertificateCustomValidationCallback { get { throw null; } set { } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public System.Security.Authentication.SslProtocols SslProtocols { get { throw null; } set { } } public virtual bool SupportsAutomaticDecompression { get { throw null; } } public virtual bool SupportsProxy { get { throw null; } } @@ -152,9 +204,20 @@ namespace System.Net.Http [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public bool UseDefaultCredentials { get { throw null; } set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] public bool UseProxy { get { throw null; } set { } } protected override void Dispose(bool disposing) { } + // + // Attributes are commented out due to https://github.com/dotnet/arcade/issues/7585 + // API compat will fail until this is fixed + // + //[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + //[System.Runtime.Versioning.UnsupportedOSPlatformAttributeUnsupportedOSPlatform("ios")] + //[System.Runtime.Versioning.UnsupportedOSPlatformAttributeUnsupportedOSPlatform("tvos")] + //[System.Runtime.Versioning.UnsupportedOSPlatformAttributeUnsupportedOSPlatform("maccatalyst")] protected internal override System.Net.Http.HttpResponseMessage Send(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { throw null; } protected internal override System.Threading.Tasks.Task SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { throw null; } } diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml index 314469c96d7..8af1dcbdfa8 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml @@ -3,8 +3,8 @@ - - + + diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml index e11daf0bd24..dbeca2aa0a9 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml @@ -5,7 +5,140 @@ ILLink IL2075 member - M:System.Net.Http.HttpClient.CreateDefaultHandler() + M:System.Net.Http.HttpClientHandler.CreateNativeHandler() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2075 + member + M:System.Net.Http.HttpClientHandler.InvokeNativeHandlerMethod(System.String,System.Object[]) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetUseCookies() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetUseCookies(System.Boolean) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetCookieContainer() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetCookieContainer(System.Net.CookieContainer) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetAllowAutoRedirect() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetAllowAutoRedirect(System.Boolean) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetCredentials() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetCredentials(System.Net.ICredentials) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetAutomaticDecompression() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetAutomaticDecompression(System.Net.DecompressionMethods) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetUseProxy() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetUseProxy(System.Boolean) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetProxy() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetProxy(System.Net.IWebProxy) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetPreAuthenticate() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetPreAuthenticate(System.Boolean) + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.GetMaxAutomaticRedirections() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. + + + ILLink + IL2035 + member + M:System.Net.Http.HttpClientHandler.SetMaxAutomaticRedirections(System.Int32) The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 0bbe56c0d87..330c6cf1614 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -34,12 +34,16 @@ - - - - - - + + + + + + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.Android.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.Android.cs deleted file mode 100644 index f770bbe22a2..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.Android.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; - -namespace System.Net.Http -{ - public partial class HttpClient - { - private static MethodInfo? handlerMethod; - - private static HttpMessageHandler CreateDefaultHandler() - { - // Default is to use the Android native handler - if (!IsNativeHandlerEnabled()) - { - return new HttpClientHandler(); - } - - if (handlerMethod == null) - { - Type? androidEnv = Type.GetType("Android.Runtime.AndroidEnvironment, Mono.Android"); - handlerMethod = androidEnv!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - } - - return (HttpMessageHandler)handlerMethod!.Invoke(null, null)!; - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.MacCatalyst.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.MacCatalyst.cs deleted file mode 100644 index dc19e0ed35d..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.MacCatalyst.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; - -namespace System.Net.Http -{ - public partial class HttpClient - { - private static MethodInfo? handlerMethod; - - private static HttpMessageHandler CreateDefaultHandler() - { - // Default is to use the iOS native handler - if (!IsNativeHandlerEnabled()) - { - return new HttpClientHandler(); - } - - if (handlerMethod == null) - { - Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.MacCatalyst"); - handlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - } - - return (HttpMessageHandler)handlerMethod!.Invoke(null, null)!; - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.cs deleted file mode 100644 index b1327d9cd0f..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Http -{ - public partial class HttpClient - { - private static HttpMessageHandler CreateDefaultHandler() - { - return new HttpClientHandler(); - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.iOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.iOS.cs deleted file mode 100644 index 03f4f945c9f..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.iOS.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; - -namespace System.Net.Http -{ - public partial class HttpClient - { - private static MethodInfo? handlerMethod; - - private static HttpMessageHandler CreateDefaultHandler() - { - // Default is to use the iOS native handler - if (!IsNativeHandlerEnabled()) - { - return new HttpClientHandler(); - } - - if (handlerMethod == null) - { - Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.iOS"); - handlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - } - - return (HttpMessageHandler)handlerMethod!.Invoke(null, null)!; - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.tvOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.tvOS.cs deleted file mode 100644 index e2dfbe0ffc1..00000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.CreateDefaultHandler.tvOS.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; - -namespace System.Net.Http -{ - public partial class HttpClient - { - private static MethodInfo? handlerMethod; - - private static HttpMessageHandler CreateDefaultHandler() - { - // Default is to use the tvOS native handler - if (!IsNativeHandlerEnabled()) - { - return new HttpClientHandler(); - } - - if (handlerMethod == null) - { - Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.TVOS"); - handlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - } - - return (HttpMessageHandler)handlerMethod!.Invoke(null, null)!; - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs index 27b0c357cc6..2e1434b0b49 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs @@ -132,7 +132,7 @@ namespace System.Net.Http #region Constructors - public HttpClient() : this(CreateDefaultHandler()) + public HttpClient() : this(new HttpClientHandler()) { } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Android.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Android.cs new file mode 100644 index 00000000000..cf9069a9f69 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Android.cs @@ -0,0 +1,237 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Versioning; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + private static MethodInfo? _nativeHandlerMethod; + + private const string NativeHandlerType = "Xamarin.Android.Net.AndroidMessageHandler"; + private const string AssemblyName = "Mono.Android"; + + public virtual bool SupportsAutomaticDecompression => true; + public virtual bool SupportsProxy => true; + public virtual bool SupportsRedirectConfiguration => true; + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public DecompressionMethods AutomaticDecompression + { + get + { + if (IsNativeHandlerEnabled) + { + return GetAutomaticDecompression(); + } + else + { + return _socketHandler!.AutomaticDecompression; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetAutomaticDecompression(value); + } + else + { + _socketHandler!.AutomaticDecompression = value; + } + } + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public bool UseProxy + { + get + { + if (IsNativeHandlerEnabled) + { + return GetUseProxy(); + } + else + { + return _socketHandler!.UseProxy; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetUseProxy(value); + } + else + { + _socketHandler!.UseProxy = value; + } + } + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public IWebProxy? Proxy + { + get + { + if (IsNativeHandlerEnabled) + { + return GetProxy(); + } + else + { + return _socketHandler!.Proxy; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetProxy(value!); + } + else + { + _socketHandler!.Proxy = value; + } + } + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public bool PreAuthenticate + { + get + { + if (IsNativeHandlerEnabled) + { + return GetPreAuthenticate(); + } + else + { + return _socketHandler!.PreAuthenticate; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetPreAuthenticate(value); + } + else + { + _socketHandler!.PreAuthenticate = value; + } + } + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public int MaxAutomaticRedirections + { + get + { + if (IsNativeHandlerEnabled) + { + return GetMaxAutomaticRedirections(); + } + else + { + return _socketHandler!.MaxAutomaticRedirections; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetMaxAutomaticRedirections(value); + } + else + { + _socketHandler!.MaxAutomaticRedirections = value; + } + } + } + + [DynamicDependency("get_MaxAutomaticRedirections", NativeHandlerType, AssemblyName)] + private int GetMaxAutomaticRedirections() => (int)InvokeNativeHandlerMethod("get_MaxAutomaticRedirections"); + + [DynamicDependency("set_MaxAutomaticRedirections", NativeHandlerType, AssemblyName)] + private void SetMaxAutomaticRedirections(int value) => InvokeNativeHandlerMethod("set_MaxAutomaticRedirections", value); + + [DynamicDependency("get_PreAuthenticate", NativeHandlerType, AssemblyName)] + private bool GetPreAuthenticate() => (bool)InvokeNativeHandlerMethod("get_PreAuthenticate"); + + [DynamicDependency("set_PreAuthenticate", NativeHandlerType, AssemblyName)] + private void SetPreAuthenticate(bool value) => InvokeNativeHandlerMethod("set_PreAuthenticate", value); + + [DynamicDependency("get_UseProxy", NativeHandlerType, AssemblyName)] + private bool GetUseProxy() => (bool)InvokeNativeHandlerMethod("get_UseProxy"); + + [DynamicDependency("set_UseProxy", NativeHandlerType, AssemblyName)] + private void SetUseProxy(bool value) => InvokeNativeHandlerMethod("set_UseProxy", value); + + [DynamicDependency("get_Proxy", NativeHandlerType, AssemblyName)] + private IWebProxy GetProxy() => (IWebProxy)InvokeNativeHandlerMethod("get_Proxy"); + + [DynamicDependency("set_Proxy", NativeHandlerType, AssemblyName)] + private void SetProxy(IWebProxy value) => InvokeNativeHandlerMethod("set_Proxy", value); + + [DynamicDependency("get_AutomaticDecompression", NativeHandlerType, AssemblyName)] + private DecompressionMethods GetAutomaticDecompression() => (DecompressionMethods)InvokeNativeHandlerMethod("get_AutomaticDecompression"); + + [DynamicDependency("set_AutomaticDecompression", NativeHandlerType, AssemblyName)] + private void SetAutomaticDecompression(DecompressionMethods value) => InvokeNativeHandlerMethod("set_AutomaticDecompression", value); + + [DynamicDependency("get_UseCookies", NativeHandlerType, AssemblyName)] + private bool GetUseCookies() => (bool)InvokeNativeHandlerMethod("get_UseCookies"); + + [DynamicDependency("set_UseCookies", NativeHandlerType, AssemblyName)] + private void SetUseCookies(bool value) => InvokeNativeHandlerMethod("set_UseCookies", value); + + [DynamicDependency("get_CookieContainer", NativeHandlerType, AssemblyName)] + private CookieContainer GetCookieContainer() => (CookieContainer)InvokeNativeHandlerMethod("get_CookieContainer"); + + [DynamicDependency("set_CookieContainer", NativeHandlerType, AssemblyName)] + private void SetCookieContainer(CookieContainer value) => InvokeNativeHandlerMethod("set_CookieContainer", value); + + [DynamicDependency("get_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private bool GetAllowAutoRedirect() => (bool)InvokeNativeHandlerMethod("get_AllowAutoRedirect"); + + [DynamicDependency("set_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private void SetAllowAutoRedirect(bool value) => InvokeNativeHandlerMethod("set_AllowAutoRedirect", value); + + [DynamicDependency("get_Credentials", NativeHandlerType, AssemblyName)] + private ICredentials GetCredentials() => (ICredentials)InvokeNativeHandlerMethod("get_Credentials"); + + [DynamicDependency("set_Credentials", NativeHandlerType, AssemblyName)] + private void SetCredentials(ICredentials? value) => InvokeNativeHandlerMethod("set_Credentials", value); + + private HttpMessageHandler CreateNativeHandler() + { + if (_nativeHandlerMethod == null) + { + Type? androidEnv = Type.GetType("Android.Runtime.AndroidEnvironment, Mono.Android"); + _nativeHandlerMethod = androidEnv!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + } + + return (HttpMessageHandler)_nativeHandlerMethod!.Invoke(null, null)!; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs new file mode 100644 index 00000000000..309e60f65a9 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs @@ -0,0 +1,434 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Net.Security; +using System.Reflection; +using System.Runtime.Versioning; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + private readonly SocketsHttpHandler? _socketHandler; + private readonly DiagnosticsHandler? _diagnosticsHandler; + + private readonly HttpMessageHandler? _nativeHandler; + + private static readonly ConcurrentDictionary s_cachedMethods = + new ConcurrentDictionary(); + + private volatile bool _disposed; + + public HttpClientHandler() + { + HttpMessageHandler handler; + + if (IsNativeHandlerEnabled) + { + _nativeHandler = CreateNativeHandler(); + handler = _nativeHandler; + } + else + { + _socketHandler = new SocketsHttpHandler(); + handler = _socketHandler; + } + + if (DiagnosticsHandler.IsGloballyEnabled()) + { + _diagnosticsHandler = new DiagnosticsHandler(handler, DistributedContextPropagator.Current); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + _disposed = true; + + if (IsNativeHandlerEnabled) + { + _nativeHandler!.Dispose(); + } + else + { + _socketHandler!.Dispose(); + } + } + + base.Dispose(disposing); + } + + [UnsupportedOSPlatform("browser")] + public bool UseCookies + { + get + { + if (IsNativeHandlerEnabled) + { + return GetUseCookies(); + } + else + { + return _socketHandler!.UseCookies; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetUseCookies(value); + } + else + { + _socketHandler!.UseCookies = value; + } + } + } + + [UnsupportedOSPlatform("browser")] + public CookieContainer CookieContainer + { + get + { + if (IsNativeHandlerEnabled) + { + return GetCookieContainer(); + } + else + { + return _socketHandler!.CookieContainer; + } + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (IsNativeHandlerEnabled) + { + SetCookieContainer(value); + } + else + { + _socketHandler!.CookieContainer = value; + } + } + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public ICredentials? DefaultProxyCredentials + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("browser")] + public bool UseDefaultCredentials + { + // SocketsHttpHandler doesn't have a separate UseDefaultCredentials property. There + // is just a Credentials property. So, we need to map the behavior. + // Same with the native handler. + get + { + ICredentials? creds; + if (IsNativeHandlerEnabled) + { + creds = GetCredentials(); + } + else + { + creds = _socketHandler!.Credentials; + } + + return creds == CredentialCache.DefaultCredentials; + } + set + { + if (value) + { + if (IsNativeHandlerEnabled) + { + SetCredentials(CredentialCache.DefaultCredentials); + } + else + { + _socketHandler!.Credentials = CredentialCache.DefaultCredentials; + } + } + else + { + if (IsNativeHandlerEnabled) + { + ICredentials? creds = GetCredentials(); + + if (creds == CredentialCache.DefaultCredentials) + { + SetCredentials(null!); + } + } + else + { + if (_socketHandler!.Credentials == CredentialCache.DefaultCredentials) + { + _socketHandler!.Credentials = null; + } + } + } + } + } + + [UnsupportedOSPlatform("browser")] + public ICredentials? Credentials + { + get + { + if (IsNativeHandlerEnabled) + { + return GetCredentials(); + } + else + { + return _socketHandler!.Credentials; + } + + } + set + { + if (IsNativeHandlerEnabled) + { + SetCredentials(value!); + } + else + { + _socketHandler!.Credentials = value; + } + } + } + + public bool AllowAutoRedirect + { + get + { + if (IsNativeHandlerEnabled) + { + return GetAllowAutoRedirect(); + } + else + { + return _socketHandler!.AllowAutoRedirect; + } + } + set + { + if (IsNativeHandlerEnabled) + { + SetAllowAutoRedirect(value); + } + else + { + _socketHandler!.AllowAutoRedirect = value; + } + } + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public int MaxConnectionsPerServer + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + public long MaxRequestContentBufferSize + { + // This property is not supported. In the .NET Framework it was only used when the handler needed to + // automatically buffer the request content. That only happened if neither 'Content-Length' nor + // 'Transfer-Encoding: chunked' request headers were specified. So, the handler thus needed to buffer + // in the request content to determine its length and then would choose 'Content-Length' semantics when + // POST'ing. In .NET Core, the handler will resolve the ambiguity by always choosing + // 'Transfer-Encoding: chunked'. The handler will never automatically buffer in the request content. + get + { + return 0; // Returning zero is appropriate since in .NET Framework it means no limit. + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + if (value > HttpContent.MaxBufferSize) + { + throw new ArgumentOutOfRangeException(nameof(value), value, + SR.Format(CultureInfo.InvariantCulture, SR.net_http_content_buffersize_limit, + HttpContent.MaxBufferSize)); + } + + CheckDisposed(); + + // No-op on property setter. + } + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public int MaxResponseHeadersLength + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public ClientCertificateOption ClientCertificateOptions + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public X509CertificateCollection ClientCertificates + { + get + { + throw new PlatformNotSupportedException(); + } + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public Func? ServerCertificateCustomValidationCallback + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public bool CheckCertificateRevocationList + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public SslProtocols SslProtocols + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public IDictionary Properties => throw new PlatformNotSupportedException(); + + // + // Attributes are commented out due to https://github.com/dotnet/arcade/issues/7585 + // API compat will fail until this is fixed + // + //[UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + //[UnsupportedOSPlatform("ios")] + //[UnsupportedOSPlatform("tvos")] + //[UnsupportedOSPlatform("maccatalyst")] + protected internal override HttpResponseMessage Send(HttpRequestMessage request, + CancellationToken cancellationToken) + { + throw new PlatformNotSupportedException(); + } + + protected internal override Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + if (DiagnosticsHandler.IsGloballyEnabled() && _diagnosticsHandler != null) + { + return _diagnosticsHandler!.SendAsync(request, cancellationToken); + } + + if (IsNativeHandlerEnabled) + { + return _nativeHandler!.SendAsync(request, cancellationToken); + } + else + { + return _socketHandler!.SendAsync(request, cancellationToken); + } + } + + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public static Func DangerousAcceptAnyServerCertificateValidator => + throw new PlatformNotSupportedException(); + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().ToString()); + } + } + + private object InvokeNativeHandlerMethod(string name, params object?[] parameters) + { + MethodInfo? method; + + if (!s_cachedMethods.TryGetValue(name, out method)) + { + method = _nativeHandler!.GetType()!.GetMethod(name); + s_cachedMethods[name] = method; + } + + return method!.Invoke(_nativeHandler, parameters)!; + } + + private static bool IsNativeHandlerEnabled => RuntimeSettingParser.QueryRuntimeSettingSwitch( + "System.Net.Http.UseNativeHttpHandler", + false); + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Apple.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Apple.cs new file mode 100644 index 00000000000..8569f35116c --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.Apple.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.Versioning; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + public virtual bool SupportsAutomaticDecompression => false; + public virtual bool SupportsProxy => false; + public virtual bool SupportsRedirectConfiguration => true; + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public DecompressionMethods AutomaticDecompression + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public bool UseProxy + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public IWebProxy? Proxy + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public int MaxAutomaticRedirections + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] + public bool PreAuthenticate + { + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.MacCatalyst.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.MacCatalyst.cs new file mode 100644 index 00000000000..886e9cd80c3 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.MacCatalyst.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + private static MethodInfo? _nativeHandlerMethod; + + private const string NativeHandlerType = "System.Net.Http.NSUrlSessionHandler"; + private const string AssemblyName = "Xamarin.MacCatalyst"; + + [DynamicDependency("get_UseCookies", NativeHandlerType, AssemblyName)] + private bool GetUseCookies() => (bool)InvokeNativeHandlerMethod("get_UseCookies"); + + [DynamicDependency("set_UseCookies", NativeHandlerType, AssemblyName)] + private void SetUseCookies(bool value) => InvokeNativeHandlerMethod("set_UseCookies", value); + + [DynamicDependency("get_CookieContainer", NativeHandlerType, AssemblyName)] + private CookieContainer GetCookieContainer() => (CookieContainer)InvokeNativeHandlerMethod("get_CookieContainer"); + + [DynamicDependency("set_CookieContainer", NativeHandlerType, AssemblyName)] + private void SetCookieContainer(CookieContainer value) => InvokeNativeHandlerMethod("set_CookieContainer", value); + + [DynamicDependency("get_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private bool GetAllowAutoRedirect() => (bool)InvokeNativeHandlerMethod("get_AllowAutoRedirect"); + + [DynamicDependency("set_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private void SetAllowAutoRedirect(bool value) => InvokeNativeHandlerMethod("set_AllowAutoRedirect", value); + + [DynamicDependency("get_Credentials", NativeHandlerType, AssemblyName)] + private ICredentials GetCredentials() => (ICredentials)InvokeNativeHandlerMethod("get_Credentials"); + + [DynamicDependency("set_Credentials", NativeHandlerType, AssemblyName)] + private void SetCredentials(ICredentials? value) => InvokeNativeHandlerMethod("set_Credentials", value); + + private HttpMessageHandler CreateNativeHandler() + { + if (_nativeHandlerMethod == null) + { + Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.MacCatalyst"); + _nativeHandlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + } + + return (HttpMessageHandler)_nativeHandlerMethod!.Invoke(null, null)!; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index fce5166f279..30ff13bd09b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -86,6 +86,9 @@ namespace System.Net.Http } [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public DecompressionMethods AutomaticDecompression { get => _underlyingHandler.AutomaticDecompression; @@ -93,6 +96,9 @@ namespace System.Net.Http } [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public bool UseProxy { get => _underlyingHandler.UseProxy; @@ -100,13 +106,20 @@ namespace System.Net.Http } [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public IWebProxy? Proxy { get => _underlyingHandler.Proxy; set => _underlyingHandler.Proxy = value; } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public ICredentials? DefaultProxyCredentials { get => _underlyingHandler.DefaultProxyCredentials; @@ -114,6 +127,9 @@ namespace System.Net.Http } [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public bool PreAuthenticate { get => _underlyingHandler.PreAuthenticate; @@ -157,13 +173,20 @@ namespace System.Net.Http } [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public int MaxAutomaticRedirections { get => _underlyingHandler.MaxAutomaticRedirections; set => _underlyingHandler.MaxAutomaticRedirections = value; } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public int MaxConnectionsPerServer { get => _underlyingHandler.MaxConnectionsPerServer; @@ -203,13 +226,21 @@ namespace System.Net.Http } } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public int MaxResponseHeadersLength { get => _underlyingHandler.MaxResponseHeadersLength; set => _underlyingHandler.MaxResponseHeadersLength = value; } + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public ClientCertificateOption ClientCertificateOptions { get => _clientCertificateOptions; @@ -243,7 +274,11 @@ namespace System.Net.Http } } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public X509CertificateCollection ClientCertificates { get @@ -258,7 +293,11 @@ namespace System.Net.Http } } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public Func? ServerCertificateCustomValidationCallback { #if TARGET_BROWSER @@ -276,7 +315,11 @@ namespace System.Net.Http #endif } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public bool CheckCertificateRevocationList { get => _underlyingHandler.SslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online; @@ -287,7 +330,11 @@ namespace System.Net.Http } } + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public SslProtocols SslProtocols { get => _underlyingHandler.SslOptions.EnabledSslProtocols; @@ -298,9 +345,21 @@ namespace System.Net.Http } } + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public IDictionary Properties => _underlyingHandler.Properties; + // + // Attributes are commented out due to https://github.com/dotnet/arcade/issues/7585 + // API compat will fail until this is fixed + // + //[UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + //[UnsupportedOSPlatform("ios")] + //[UnsupportedOSPlatform("tvos")] + //[UnsupportedOSPlatform("maccatalyst")] protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => Handler.Send(request, cancellationToken); @@ -309,7 +368,11 @@ namespace System.Net.Http // lazy-load the validator func so it can be trimmed by the ILLinker if it isn't used. private static Func? s_dangerousAcceptAnyServerCertificateValidator; + [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [UnsupportedOSPlatform("maccatalyst")] public static Func DangerousAcceptAnyServerCertificateValidator => Volatile.Read(ref s_dangerousAcceptAnyServerCertificateValidator) ?? Interlocked.CompareExchange(ref s_dangerousAcceptAnyServerCertificateValidator, delegate { return true; }, null) ?? diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.iOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.iOS.cs new file mode 100644 index 00000000000..ff9e9108d28 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.iOS.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + private static MethodInfo? _nativeHandlerMethod; + + private const string NativeHandlerType = "System.Net.Http.NSUrlSessionHandler"; + private const string AssemblyName = "Xamarin.iOS"; + + [DynamicDependency("get_UseCookies", NativeHandlerType, AssemblyName)] + private bool GetUseCookies() => (bool)InvokeNativeHandlerMethod("get_UseCookies"); + + [DynamicDependency("set_UseCookies", NativeHandlerType, AssemblyName)] + private void SetUseCookies(bool value) => InvokeNativeHandlerMethod("set_UseCookies", value); + + [DynamicDependency("get_CookieContainer", NativeHandlerType, AssemblyName)] + private CookieContainer GetCookieContainer() => (CookieContainer)InvokeNativeHandlerMethod("get_CookieContainer"); + + [DynamicDependency("set_CookieContainer", NativeHandlerType, AssemblyName)] + private void SetCookieContainer(CookieContainer value) => InvokeNativeHandlerMethod("set_CookieContainer", value); + + [DynamicDependency("get_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private bool GetAllowAutoRedirect() => (bool)InvokeNativeHandlerMethod("get_AllowAutoRedirect"); + + [DynamicDependency("set_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private void SetAllowAutoRedirect(bool value) => InvokeNativeHandlerMethod("set_AllowAutoRedirect", value); + + [DynamicDependency("get_Credentials", NativeHandlerType, AssemblyName)] + private ICredentials GetCredentials() => (ICredentials)InvokeNativeHandlerMethod("get_Credentials"); + + [DynamicDependency("set_Credentials", NativeHandlerType, AssemblyName)] + private void SetCredentials(ICredentials? value) => InvokeNativeHandlerMethod("set_Credentials", value); + + private HttpMessageHandler CreateNativeHandler() + { + if (_nativeHandlerMethod == null) + { + Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.iOS"); + _nativeHandlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + } + + return (HttpMessageHandler)_nativeHandlerMethod!.Invoke(null, null)!; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.tvOS.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.tvOS.cs new file mode 100644 index 00000000000..4eaa6a9ad43 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.tvOS.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace System.Net.Http +{ + public partial class HttpClientHandler : HttpMessageHandler + { + private static MethodInfo? _nativeHandlerMethod; + + private const string NativeHandlerType = "System.Net.Http.NSUrlSessionHandler"; + private const string AssemblyName = "Xamarin.TVOS"; + + [DynamicDependency("get_UseCookies", NativeHandlerType, AssemblyName)] + private bool GetUseCookies() => (bool)InvokeNativeHandlerMethod("get_UseCookies"); + + [DynamicDependency("set_UseCookies", NativeHandlerType, AssemblyName)] + private void SetUseCookies(bool value) => InvokeNativeHandlerMethod("set_UseCookies", value); + + [DynamicDependency("get_CookieContainer", NativeHandlerType, AssemblyName)] + private CookieContainer GetCookieContainer() => (CookieContainer)InvokeNativeHandlerMethod("get_CookieContainer"); + + [DynamicDependency("set_CookieContainer", NativeHandlerType, AssemblyName)] + private void SetCookieContainer(CookieContainer value) => InvokeNativeHandlerMethod("set_CookieContainer", value); + + [DynamicDependency("get_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private bool GetAllowAutoRedirect() => (bool)InvokeNativeHandlerMethod("get_AllowAutoRedirect"); + + [DynamicDependency("set_AllowAutoRedirect", NativeHandlerType, AssemblyName)] + private void SetAllowAutoRedirect(bool value) => InvokeNativeHandlerMethod("set_AllowAutoRedirect", value); + + [DynamicDependency("get_Credentials", NativeHandlerType, AssemblyName)] + private ICredentials GetCredentials() => (ICredentials)InvokeNativeHandlerMethod("get_Credentials"); + + [DynamicDependency("set_Credentials", NativeHandlerType, AssemblyName)] + private void SetCredentials(ICredentials? value) => InvokeNativeHandlerMethod("set_Credentials", value); + + private HttpMessageHandler CreateNativeHandler() + { + if (_nativeHandlerMethod == null) + { + Type? runtimeOptions = Type.GetType("ObjCRuntime.RuntimeOptions, Xamarin.TVOS"); + _nativeHandlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + } + + return (HttpMessageHandler)_nativeHandlerMethod!.Invoke(null, null)!; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs index 2fc23028f9b..e950e218552 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RuntimeSettingParser.cs @@ -35,6 +35,22 @@ namespace System.Net.Http return defaultValue; } + /// + /// Parse a value from an AppContext switch. + /// + public static bool QueryRuntimeSettingSwitch(string appCtxSettingName, bool defaultValue) + { + bool value; + + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(appCtxSettingName, out value)) + { + return value; + } + + return defaultValue; + } + /// /// Parse an environment variable for an value. /// diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index d6a2cc28d11..0a92e47df32 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -184,12 +184,6 @@ Link="ProductionCode\System\Net\Http\Headers\WarningHeaderValue.cs" /> - - false false - true + false @@ -31,7 +31,7 @@ - + false true false @@ -39,7 +39,7 @@ false true false - true + false From 30257e688b16c9437a3eef8c2fd59045c2f6fa08 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 15 Jul 2021 06:41:01 +0200 Subject: [PATCH 579/926] Support sigaction with SIG_DFL and SA_SIGINFO. (#55673) Fixes #55645 --- src/coreclr/pal/src/exception/signal.cpp | 77 +++++++++++-------- .../Native/Unix/System.Native/pal_signal.c | 39 ++++++---- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index 14dc01dca6a..cbcd1cac6f2 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -344,6 +344,26 @@ bool IsRunningOnAlternateStack(void *context) #endif // HAVE_MACH_EXCEPTIONS } +static bool IsSaSigInfo(struct sigaction* action) +{ + return (action->sa_flags & SA_SIGINFO) != 0; +} + +static bool IsSigDfl(struct sigaction* action) +{ + // macOS can return sigaction with SIG_DFL and SA_SIGINFO. + // SA_SIGINFO means we should use sa_sigaction, but here we want to check sa_handler. + // So we ignore SA_SIGINFO when sa_sigaction and sa_handler are at the same address. + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_DFL; +} + +static bool IsSigIgn(struct sigaction* action) +{ + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_IGN; +} + /*++ Function : invoke_previous_action @@ -363,7 +383,30 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t { _ASSERTE(action != NULL); - if (action->sa_flags & SA_SIGINFO) + if (IsSigIgn(action)) + { + if (signalRestarts) + { + // This signal mustn't be ignored because it will be restarted. + PROCAbort(code); + } + return; + } + else if (IsSigDfl(action)) + { + if (signalRestarts) + { + // Restore the original and restart h/w exception. + restore_signal(code, action); + } + else + { + // We can't invoke the original handler because returning from the + // handler doesn't restart the exception. + PROCAbort(code); + } + } + else if (IsSaSigInfo(action)) { // Directly call the previous handler. _ASSERTE(action->sa_sigaction != NULL); @@ -371,35 +414,9 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t } else { - if (action->sa_handler == SIG_IGN) - { - if (signalRestarts) - { - // This signal mustn't be ignored because it will be restarted. - PROCAbort(code); - } - return; - } - else if (action->sa_handler == SIG_DFL) - { - if (signalRestarts) - { - // Restore the original and restart h/w exception. - restore_signal(code, action); - } - else - { - // We can't invoke the original handler because returning from the - // handler doesn't restart the exception. - PROCAbort(code); - } - } - else - { - // Directly call the previous handler. - _ASSERTE(action->sa_handler != NULL); - action->sa_handler(code); - } + // Directly call the previous handler. + _ASSERTE(action->sa_handler != NULL); + action->sa_handler(code); } PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); diff --git a/src/libraries/Native/Unix/System.Native/pal_signal.c b/src/libraries/Native/Unix/System.Native/pal_signal.c index ee6ccd833c0..6e7243fca17 100644 --- a/src/libraries/Native/Unix/System.Native/pal_signal.c +++ b/src/libraries/Native/Unix/System.Native/pal_signal.c @@ -63,16 +63,21 @@ static bool IsSaSigInfo(struct sigaction* action) #pragma clang diagnostic pop } -static bool IsSigIgn(struct sigaction* action) -{ - assert(action); - return !IsSaSigInfo(action) && action->sa_handler == SIG_IGN; -} - static bool IsSigDfl(struct sigaction* action) { assert(action); - return !IsSaSigInfo(action) && action->sa_handler == SIG_DFL; + // macOS can return sigaction with SIG_DFL and SA_SIGINFO. + // SA_SIGINFO means we should use sa_sigaction, but here we want to check sa_handler. + // So we ignore SA_SIGINFO when sa_sigaction and sa_handler are at the same address. + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_DFL; +} + +static bool IsSigIgn(struct sigaction* action) +{ + assert(action); + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_IGN; } static bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal) @@ -209,15 +214,19 @@ static void SignalHandler(int sig, siginfo_t* siginfo, void* context) if (!IsCancelableTerminationSignal(sig)) { struct sigaction* origHandler = OrigActionFor(sig); - if (IsSaSigInfo(origHandler)) + if (!IsSigDfl(origHandler) && !IsSigIgn(origHandler)) { - assert(origHandler->sa_sigaction); - origHandler->sa_sigaction(sig, siginfo, context); - } - else if (origHandler->sa_handler != SIG_IGN && - origHandler->sa_handler != SIG_DFL) - { - origHandler->sa_handler(sig); + if (IsSaSigInfo(origHandler)) + { + assert(origHandler->sa_sigaction); + origHandler->sa_sigaction(sig, siginfo, context); + } + else + { + assert(origHandler->sa_handler); + origHandler->sa_handler(sig); + } + } } From 87fcf6ef986b6d670dc98e610eded4fb36f6a28f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 14 Jul 2021 21:57:22 -0700 Subject: [PATCH 580/926] Revert "Handle getting the address of an RVA static correctly in a composite image (#55301)" (#55713) This reverts commit e3d319bb727efb82f1ab4236f6f58a5dfffcfc5c. It breaks the crossgen2 outerloop runs --- src/coreclr/jit/emitxarch.cpp | 10 +++---- src/coreclr/jit/importer.cpp | 27 +++---------------- .../ReadyToRunSymbolNodeFactory.cs | 15 ----------- .../Compiler/ReadyToRunCodegenCompilation.cs | 2 +- src/coreclr/vm/jitinterface.cpp | 15 ----------- 5 files changed, 8 insertions(+), 61 deletions(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 076250103bb..c35a6675fe7 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -12226,12 +12226,10 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) regMaskTP regMask; regMask = genRegMask(reg1) | genRegMask(reg2); -// Assert disabled as requested in PR 55301. A use of the Unsafe api -// which produces correct code, but isn't handled correctly here. -// r1/r2 could have been a GCREF as GCREF + int=BYREF -// or BYREF+/-int=BYREF -// assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || -// ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub))); + // r1/r2 could have been a GCREF as GCREF + int=BYREF + // or BYREF+/-int=BYREF + assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || + ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub))); #endif // Mark r1 as holding a byref emitGCregLiveUpd(GCT_BYREF, reg1, dst); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b4fb5a0a20e..b3857cdec58 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -7704,24 +7704,14 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT void** pFldAddr = nullptr; void* fldAddr = info.compCompHnd->getFieldAddress(pResolvedToken->hField, (void**)&pFldAddr); -// We should always be able to access this static's address directly unless this is a field RVA -// used within a composite image during R2R composite image building. -// -#ifdef FEATURE_READYTORUN_COMPILER - assert((pFldAddr == nullptr) || - (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_RVA_ADDRESS && opts.IsReadyToRun())); -#else + // We should always be able to access this static's address directly + // assert(pFldAddr == nullptr); -#endif FieldSeqNode* fldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField); /* Create the data member node */ - op1 = gtNewIconHandleNode(pFldAddr == nullptr ? (size_t)fldAddr : (size_t)pFldAddr, -#ifdef FEATURE_READYTORUN_COMPILER - pFldAddr != nullptr ? GTF_ICON_CONST_PTR : -#endif - GTF_ICON_STATIC_HDL, + op1 = gtNewIconHandleNode(pFldAddr == nullptr ? (size_t)fldAddr : (size_t)pFldAddr, GTF_ICON_STATIC_HDL, fldSeq); #ifdef DEBUG op1->AsIntCon()->gtTargetHandle = op1->AsIntCon()->gtIconVal; @@ -7731,17 +7721,6 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT { op1->gtFlags |= GTF_ICON_INITCLASS; } - -#ifdef FEATURE_READYTORUN_COMPILER - if (pFldAddr != nullptr) - { - // Indirection used to get to initial actual field RVA when building a composite image - // where the actual field does not move from the original file - assert(!varTypeIsGC(lclTyp)); - op1 = gtNewOperNode(GT_IND, TYP_I_IMPL, op1); - op1->gtFlags |= GTF_IND_INVARIANT | GTF_IND_NONFAULTING; - } -#endif } else // We need the value of a static field { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 4302d1ecca5..38b1893f531 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -64,14 +64,6 @@ namespace ILCompiler.DependencyAnalysis new ReadyToRunInstructionSetSupportSignature(key)); }); - _precodeFieldAddressCache = new NodeCache(key => - { - return new PrecodeHelperImport( - _codegenNodeFactory, - new FieldFixupSignature(ReadyToRunFixupKind.FieldAddress, key, _codegenNodeFactory) - ); - }); - _fieldAddressCache = new NodeCache(key => { return new DelayLoadHelperImport( @@ -406,13 +398,6 @@ namespace ILCompiler.DependencyAnalysis return _fieldAddressCache.GetOrAdd(fieldDesc); } - private NodeCache _precodeFieldAddressCache; - - public ISymbolNode PrecodeFieldAddress(FieldDesc fieldDesc) - { - return _precodeFieldAddressCache.GetOrAdd(fieldDesc); - } - private NodeCache _fieldOffsetCache; public ISymbolNode FieldOffset(FieldDesc fieldDesc) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index c1e4c69013f..ac7058bf90d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -641,7 +641,7 @@ namespace ILCompiler } } - public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CompilationModuleGroup.IsCompositeBuildMode ? SymbolNodeFactory.PrecodeFieldAddress(field) : NodeFactory.CopiedFieldRva(field); + public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CopiedFieldRva(field); public override void Dispose() { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 6d6c104cbb9..b19fa365323 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14034,21 +14034,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, break; #endif // PROFILING_SUPPORTED - case ENCODE_FIELD_ADDRESS: - { - FieldDesc *pField = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); - - pField->GetEnclosingMethodTable()->CheckRestore(); - - // We can only take address of RVA field here as we don't handle scenarios where the static variable may move - // Also, this cannot be used with a ZapImage as it relies on a separate signatures block as an RVA static - // address may be unaligned which would interfere with the tagged pointer approach. - _ASSERTE(currentModule->IsReadyToRun()); - _ASSERTE(pField->IsRVA()); - result = (size_t)pField->GetStaticAddressHandle(NULL); - } - break; - case ENCODE_STATIC_FIELD_ADDRESS: { FieldDesc *pField = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); From 1b14c945a9c9ff8c90b8507fe8f0f9cccd20ad73 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Wed, 14 Jul 2021 23:05:03 -0700 Subject: [PATCH 581/926] Support Invariant Mode Case Mapping (#55520) * Support InvariantMode Case Mapping --- .../GenUnicodeProp/CategoryCasingInfo.cs | 3 +- .../Tools/GenUnicodeProp/Program.cs | 4 +- .../System/Text/Unicode/CodePoint.cs | 2 +- .../tests/Invariant/InvariantMode.cs | 67 +- .../System.Private.CoreLib.Shared.projitems | 2 + .../System/Globalization/CharUnicodeInfo.cs | 66 + .../Globalization/CharUnicodeInfoData.cs | 2183 +++++++++-------- .../Globalization/CompareInfo.Invariant.cs | 221 +- .../src/System/Globalization/CompareInfo.cs | 8 +- .../Globalization/InvariantModeCasing.cs | 346 +++ .../src/System/Globalization/Ordinal.cs | 77 +- .../System/Globalization/OrdinalCasing.Icu.cs | 104 +- .../System/Globalization/SurrogateCasing.cs | 39 + .../src/System/Globalization/TextInfo.cs | 38 +- .../System/MemoryExtensions.Globalization.cs | 8 +- .../src/System/Text/Rune.cs | 8 +- 16 files changed, 1746 insertions(+), 1430 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Globalization/SurrogateCasing.cs diff --git a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/CategoryCasingInfo.cs b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/CategoryCasingInfo.cs index dd76e88d0eb..e43ee61323d 100644 --- a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/CategoryCasingInfo.cs +++ b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/CategoryCasingInfo.cs @@ -43,7 +43,8 @@ namespace GenUnicodeProp break; } - if (Program.IncludeCasingData) + // For compatibility reasons we are not mapping the Turkish I's nor Latin small letter long S with invariant casing. + if (Program.IncludeCasingData && codePoint.Value != 0x0130 && codePoint.Value != 0x0131 && codePoint.Value != 0x017f) { _data.offsetToSimpleUppercase = (ushort)(codePoint.SimpleUppercaseMapping - codePoint.Value); _data.offsetToSimpleLowercase = (ushort)(codePoint.SimpleLowercaseMapping - codePoint.Value); diff --git a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/Program.cs b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/Program.cs index 49a455467a5..3bef2414ec6 100644 --- a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/Program.cs +++ b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/Program.cs @@ -11,8 +11,8 @@ namespace GenUnicodeProp { internal static class Program { - internal static bool Verbose = false; - internal static bool IncludeCasingData = false; + internal static bool Verbose; + internal static bool IncludeCasingData; private const string SOURCE_NAME = "CharUnicodeInfoData.cs"; diff --git a/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/CodePoint.cs b/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/CodePoint.cs index a77acf6eaaa..466cd75f99a 100644 --- a/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/CodePoint.cs +++ b/src/libraries/Common/tests/TestUtilities.Unicode/System/Text/Unicode/CodePoint.cs @@ -109,7 +109,7 @@ namespace System.Text.Unicode /// /// See https://www.unicode.org/reports/tr44/#PropList.txt. /// - public CodePointFlags Flags { get; } = default; // default is "no flags" + public CodePointFlags Flags { get; } // default is "no flags" /// /// The general Unicode category of this code point. diff --git a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs index a2cd0baf70f..0a0300afc5c 100644 --- a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs +++ b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs @@ -53,6 +53,12 @@ namespace System.Globalization.Tests yield return new object[] { "Hello", "L", 0, 5, CompareOptions.OrdinalIgnoreCase, 2 }; yield return new object[] { "Hello", "h", 0, 5, CompareOptions.OrdinalIgnoreCase, 0 }; + yield return new object[] { "Hello\u00D3\u00D4", "\u00F3\u00F4", 0, 7, CompareOptions.OrdinalIgnoreCase, 5 }; + yield return new object[] { "Hello\u00D3\u00D4", "\u00F3\u00F5", 0, 7, CompareOptions.OrdinalIgnoreCase, -1 }; + + yield return new object[] { "Hello\U00010400", "\U00010428", 0, 7, CompareOptions.OrdinalIgnoreCase, 5 }; + + // Long strings yield return new object[] { new string('b', 100) + new string('a', 5555), "aaaaaaaaaaaaaaa", 0, 5655, CompareOptions.None, 100 }; yield return new object[] { new string('b', 101) + new string('a', 5555), new string('a', 5000), 0, 5656, CompareOptions.None, 101 }; @@ -159,6 +165,12 @@ namespace System.Globalization.Tests yield return new object[] { "Hello", "L", 4, 5, CompareOptions.OrdinalIgnoreCase, 3 }; yield return new object[] { "Hello", "h", 4, 5, CompareOptions.OrdinalIgnoreCase, 0 }; + + yield return new object[] { "Hello\u00D3\u00D4\u00D3\u00D4", "\u00F3\u00F4", 8, 9, CompareOptions.OrdinalIgnoreCase, 7 }; + yield return new object[] { "Hello\u00D3\u00D4\u00D3\u00D4", "\u00F3\u00F5", 8, 9, CompareOptions.OrdinalIgnoreCase, -1 }; + + yield return new object[] { "Hello\U00010400\U00010400", "\U00010428", 8, 9, CompareOptions.OrdinalIgnoreCase, 7 }; + // Long strings yield return new object[] { new string('a', 5555) + new string('b', 100), "aaaaaaaaaaaaaaa", 5654, 5655, CompareOptions.None, 5540 }; yield return new object[] { new string('b', 101) + new string('a', 5555), new string('a', 5000), 5655, 5656, CompareOptions.None, 656 }; @@ -237,6 +249,10 @@ namespace System.Globalization.Tests yield return new object[] { "FooBar", "Foo\u0400Bar", CompareOptions.Ordinal, false }; yield return new object[] { "FooBA\u0300R", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, false }; + yield return new object[] { "\u00D3\u00D4\u00D3\u00D4Hello", "\u00F3\u00F4", CompareOptions.OrdinalIgnoreCase, true }; + yield return new object[] { "\u00D3\u00D4Hello\u00D3\u00D4", "\u00F3\u00F5", CompareOptions.OrdinalIgnoreCase, false }; + yield return new object[] { "\U00010400\U00010400Hello", "\U00010428", CompareOptions.OrdinalIgnoreCase, true }; + // Ignore symbols yield return new object[] { "Test's can be interesting", "Tests", CompareOptions.IgnoreSymbols, false }; yield return new object[] { "Test's can be interesting", "Tests", CompareOptions.None, false }; @@ -277,6 +293,11 @@ namespace System.Globalization.Tests yield return new object[] { "FooBar", "Foo\u0400Bar", CompareOptions.Ordinal, false }; yield return new object[] { "FooBA\u0300R", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, false }; + yield return new object[] { "\u00D3\u00D4\u00D3\u00D4Hello", "\u00F3\u00F4", CompareOptions.OrdinalIgnoreCase, false }; + yield return new object[] { "\u00D3\u00D4Hello\u00D3\u00D4", "\u00F3\u00F4", CompareOptions.OrdinalIgnoreCase, true }; + yield return new object[] { "\U00010400\U00010400Hello", "\U00010428", CompareOptions.OrdinalIgnoreCase, false }; + yield return new object[] { "Hello\U00010400", "\U00010428", CompareOptions.OrdinalIgnoreCase, true }; + // Weightless characters yield return new object[] { "", "\u200d", CompareOptions.None, false }; yield return new object[] { "", "\u200d", CompareOptions.IgnoreCase, false }; @@ -327,6 +348,21 @@ namespace System.Globalization.Tests yield return new object[] { "", "'", CompareOptions.None, -1 }; + yield return new object[] { "\u00D3\u00D4", "\u00F3\u00F4", CompareOptions.OrdinalIgnoreCase, 0 }; + yield return new object[] { "\U00010400", "\U00010428", CompareOptions.OrdinalIgnoreCase, 0 }; + yield return new object[] { "\u00D3\u00D4", "\u00F3\u00F4", CompareOptions.IgnoreCase, 0 }; + yield return new object[] { "\U00010400", "\U00010428", CompareOptions.IgnoreCase, 0 }; + + yield return new object[] { "\u00D3\u00D4G", "\u00F3\u00F4", CompareOptions.OrdinalIgnoreCase, 1 }; + yield return new object[] { "\U00010400G", "\U00010428", CompareOptions.OrdinalIgnoreCase, 1 }; + yield return new object[] { "\u00D3\u00D4G", "\u00F3\u00F4", CompareOptions.IgnoreCase, 1 }; + yield return new object[] { "\U00010400G", "\U00010428", CompareOptions.IgnoreCase, 1 }; + + yield return new object[] { "\u00D3\u00D4", "\u00F3\u00F4G", CompareOptions.OrdinalIgnoreCase, -1 }; + yield return new object[] { "\U00010400", "\U00010428G", CompareOptions.OrdinalIgnoreCase, -1 }; + yield return new object[] { "\u00D3\u00D4", "\u00F3\u00F4G", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { "\U00010400", "\U00010428G", CompareOptions.IgnoreCase, -1 }; + // Hungarian yield return new object[] { "dzsdzs", "ddzs", CompareOptions.Ordinal, 1 }; yield return new object[] { "dzsdzs", "ddzs", CompareOptions.None, 1 }; @@ -349,6 +385,14 @@ namespace System.Globalization.Tests yield return new object[] { "llegar", "lugar", CompareOptions.None, -1 }; yield return new object[] { "\u3042", "\u30A1", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, -1 }; + + // Surrogates + + yield return new object[] { "Hello\uFE6A", "Hello\U0001F601", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { "Hello\U0001F601", "Hello\uFE6A", CompareOptions.IgnoreCase, 1 }; + yield return new object[] { "\uDBFF", "\uD800\uDC00", CompareOptions.IgnoreCase, -1 }; + yield return new object[] { "\uD800\uDC00", "\uDBFF", CompareOptions.IgnoreCase, 1 }; + yield return new object[] { "abcdefg\uDBFF", "abcdefg\uD800\uDC00", CompareOptions.IgnoreCase, -1 }; } public static IEnumerable ToLower_TestData() @@ -375,7 +419,7 @@ namespace System.Globalization.Tests yield return new object[] { "EMBEDDED\0NuLL\0Byte\0", "embedded\0null\0byte\0", true }; // LATIN CAPITAL LETTER O WITH ACUTE, which has a lower case variant. - yield return new object[] { "\u00D3", "\u00F3", false }; + yield return new object[] { "\u00D3", "\u00F3", true }; // SNOWMAN, which does not have a lower case variant. yield return new object[] { "\u2603", "\u2603", true }; @@ -383,13 +427,16 @@ namespace System.Globalization.Tests // RAINBOW (outside the BMP and does not case) yield return new object[] { "\U0001F308", "\U0001F308", true }; + // Surrogate casing + yield return new object[] { "\U00010400", "\U00010428", true }; + // Unicode defines some codepoints which expand into multiple codepoints // when cased (see SpecialCasing.txt from UNIDATA for some examples). We have never done // these sorts of expansions, since it would cause string lengths to change when cased, // which is non-intuitive. In addition, there are some context sensitive mappings which // we also don't preform. // Greek Capital Letter Sigma (does not to case to U+03C2 with "final sigma" rule). - yield return new object[] { "\u03A3", "\u03C3", false }; + yield return new object[] { "\u03A3", "\u03C3", true }; } public static IEnumerable ToUpper_TestData() @@ -415,8 +462,8 @@ namespace System.Globalization.Tests yield return new object[] { "embedded\0NuLL\0Byte\0", "EMBEDDED\0NULL\0BYTE\0", true }; - // LATIN SMALL LETTER O WITH ACUTE, which has an upper case variant. - yield return new object[] { "\u00F3", "\u00D3", false }; + // LATIN SMALL LETTER O WITH ACUTE, mapped to LATIN CAPITAL LETTER O WITH ACUTE. + yield return new object[] { "\u00F3", "\u00D3", true }; // SNOWMAN, which does not have an upper case variant. yield return new object[] { "\u2603", "\u2603", true }; @@ -424,6 +471,9 @@ namespace System.Globalization.Tests // RAINBOW (outside the BMP and does not case) yield return new object[] { "\U0001F308", "\U0001F308", true }; + // Surrogate casing + yield return new object[] { "\U00010428", "\U00010400", true }; + // Unicode defines some codepoints which expand into multiple codepoints // when cased (see SpecialCasing.txt from UNIDATA for some examples). We have never done // these sorts of expansions, since it would cause string lengths to change when cased, @@ -439,7 +489,7 @@ namespace System.Globalization.Tests // as part of casing. yield return new object[] { "\u0149", "\u0149", true }; - yield return new object[] { "\u03C3", "\u03A3", false }; + yield return new object[] { "\u03C3", "\u03A3", true }; } public static IEnumerable GetAscii_TestData() @@ -722,7 +772,7 @@ namespace System.Globalization.Tests [InlineData("Hello", CompareOptions.IgnoreCase, "HELLO")] [InlineData("Hello", CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth, "HELLO")] [InlineData("Hell\u00F6", CompareOptions.None, "Hell\u00F6")] // U+00F6 = LATIN SMALL LETTER O WITH DIAERESIS - [InlineData("Hell\u00F6", CompareOptions.IgnoreCase, "HELL\u00F6")] // note the final "o with diaeresis" isn't capitalized + [InlineData("Hell\u00F6", CompareOptions.IgnoreCase, "HELL\u00D6")] public unsafe void TestSortKey_FromSpan(string input, CompareOptions options, string expected) { byte[] expectedOutputBytes = GetExpectedInvariantOrdinalSortKey(expected); @@ -1125,8 +1175,9 @@ namespace System.Globalization.Tests [InlineData('A', 'A', 'a')] [InlineData('i', 'I', 'i')] // to verify that we don't special-case the Turkish I in the invariant globalization mode [InlineData('I', 'I', 'i')] - [InlineData(0x00C1, 0x00C1, 0x00C1)] // U+00C1 LATIN CAPITAL LETTER A WITH ACUTE - [InlineData(0x00E1, 0x00E1, 0x00E1)] // U+00E1 LATIN SMALL LETTER A WITH ACUTE + [InlineData('\u017f', '\u017f', '\u017f')] // Latin small letter long S shouldn't be case mapped in the invariant mode. + [InlineData(0x00C1, 0x00C1, 0x00E1)] // U+00C1 LATIN CAPITAL LETTER A WITH ACUTE + [InlineData(0x00E1, 0x00C1, 0x00E1)] // U+00E1 LATIN SMALL LETTER A WITH ACUTE [InlineData(0x00D7, 0x00D7, 0x00D7)] // U+00D7 MULTIPLICATION SIGN public void TestRune(int original, int expectedToUpper, int expectedToLower) { diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index ac992cdc3b6..aa0a8b86536 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -342,6 +342,7 @@ + @@ -363,6 +364,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs index ad0056c4ec3..8f4e8cb6b19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs @@ -250,6 +250,72 @@ namespace System.Globalization } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static char ToUpper(char codePoint) + { + nuint offset = GetCategoryCasingTableOffsetNoBoundsChecks((uint)codePoint); + + // The offset is specified in shorts: + // Get the 'ref short' corresponding to where the addend is, read it as a signed 16-bit value, then add + + ref short rsStart = ref Unsafe.As(ref MemoryMarshal.GetReference(UppercaseValues)); + ref short rsDelta = ref Unsafe.Add(ref rsStart, (nint)offset); + int delta = (BitConverter.IsLittleEndian) ? rsDelta : BinaryPrimitives.ReverseEndianness(rsDelta); + return (char)(delta + codePoint); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint ToUpper(uint codePoint) + { + if (!UnicodeUtility.IsValidCodePoint(codePoint)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.codePoint); + } + + nuint offset = GetCategoryCasingTableOffsetNoBoundsChecks(codePoint); + + // The offset is specified in shorts: + // Get the 'ref short' corresponding to where the addend is, read it as a signed 16-bit value, then add + + ref short rsStart = ref Unsafe.As(ref MemoryMarshal.GetReference(UppercaseValues)); + ref short rsDelta = ref Unsafe.Add(ref rsStart, (nint)offset); + int delta = (BitConverter.IsLittleEndian) ? rsDelta : BinaryPrimitives.ReverseEndianness(rsDelta); + return (uint)delta + codePoint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static char ToLower(char codePoint) + { + nuint offset = GetCategoryCasingTableOffsetNoBoundsChecks((uint)codePoint); + + // The offset is specified in shorts: + // Get the 'ref short' corresponding to where the addend is, read it as a signed 16-bit value, then add + + ref short rsStart = ref Unsafe.As(ref MemoryMarshal.GetReference(LowercaseValues)); + ref short rsDelta = ref Unsafe.Add(ref rsStart, (nint)offset); + int delta = (BitConverter.IsLittleEndian) ? rsDelta : BinaryPrimitives.ReverseEndianness(rsDelta); + return (char)(delta + codePoint); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint ToLower(uint codePoint) + { + if (!UnicodeUtility.IsValidCodePoint(codePoint)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.codePoint); + } + + nuint offset = GetCategoryCasingTableOffsetNoBoundsChecks(codePoint); + + // If the offset is specified in shorts: + // Get the 'ref short' corresponding to where the addend is, read it as a signed 16-bit value, then add + + ref short rsStart = ref Unsafe.As(ref MemoryMarshal.GetReference(LowercaseValues)); + ref short rsDelta = ref Unsafe.Add(ref rsStart, (nint)offset); + int delta = (BitConverter.IsLittleEndian) ? rsDelta : BinaryPrimitives.ReverseEndianness(rsDelta); + return (uint)delta + codePoint; + } + /* * GetUnicodeCategory * ================== diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfoData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfoData.cs index 66d74733cbc..32d4f4a877a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfoData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfoData.cs @@ -1,6 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - using System.Diagnostics; namespace System.Globalization @@ -166,397 +165,397 @@ namespace System.Globalization 0x08, 0x00, 0x01, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x19, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x0d, 0x00, - 0x0d, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, - 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x23, 0x00, - 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x0d, 0x00, 0x27, 0x00, 0x28, 0x00, 0x0f, 0x00, 0x29, 0x00, - 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, - 0x2a, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x2b, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x2c, 0x00, 0x0b, 0x00, 0x2d, 0x00, 0x0d, 0x00, 0x0d, 0x00, - 0x2e, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, - 0x35, 0x00, 0x36, 0x00, 0x32, 0x00, 0x32, 0x00, 0x37, 0x00, 0x22, 0x00, 0x38, 0x00, 0x39, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x22, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x32, 0x00, 0x43, 0x00, 0x44, 0x00, - 0x32, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x32, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x22, 0x00, - 0x4f, 0x00, 0x50, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, - 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5e, 0x00, 0x57, 0x00, 0x58, 0x00, 0x5f, 0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, - 0x64, 0x00, 0x65, 0x00, 0x58, 0x00, 0x66, 0x00, 0x67, 0x00, 0x68, 0x00, 0x5c, 0x00, 0x69, 0x00, - 0x6a, 0x00, 0x57, 0x00, 0x58, 0x00, 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x6e, 0x00, - 0x6f, 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x62, 0x00, 0x75, 0x00, - 0x76, 0x00, 0x77, 0x00, 0x58, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x5c, 0x00, 0x7b, 0x00, - 0x7c, 0x00, 0x77, 0x00, 0x58, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x5c, 0x00, 0x80, 0x00, - 0x81, 0x00, 0x77, 0x00, 0x50, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x5c, 0x00, 0x85, 0x00, - 0x86, 0x00, 0x87, 0x00, 0x50, 0x00, 0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x62, 0x00, 0x8b, 0x00, - 0x8c, 0x00, 0x50, 0x00, 0x50, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x91, 0x00, 0x50, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x50, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9d, 0x00, 0x9e, 0x00, 0x22, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, - 0xa8, 0x00, 0xa9, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0xaa, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0xab, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xac, 0x00, 0xad, 0x00, 0x50, 0x00, 0x50, 0x00, - 0xac, 0x00, 0x50, 0x00, 0x50, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0xaf, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00, - 0x50, 0x00, 0xb4, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xb7, 0x00, 0x50, 0x00, - 0xb8, 0x00, 0xb9, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xba, 0x00, 0xbb, 0x00, - 0xbc, 0x00, 0xbd, 0x00, 0x50, 0x00, 0xbe, 0x00, 0x50, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xc0, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc5, 0x00, - 0xc6, 0x00, 0xc4, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc7, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc8, 0x00, - 0xc9, 0x00, 0x50, 0x00, 0xca, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xcb, 0x00, - 0x50, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0x50, 0x00, 0xd0, 0x00, 0xd1, 0x00, - 0x50, 0x00, 0x50, 0x00, 0xd2, 0x00, 0x50, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0x50, 0x00, 0xd6, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, - 0xc4, 0x00, 0xc4, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xdd, 0x00, 0x50, 0x00, 0x50, 0x00, 0xde, 0x00, 0xdf, 0x00, 0xa4, 0x00, 0xe0, 0x00, 0xe1, 0x00, - 0xe2, 0x00, 0x50, 0x00, 0xe3, 0x00, 0xe4, 0x00, 0x50, 0x00, 0x50, 0x00, 0xe5, 0x00, 0xe6, 0x00, - 0x50, 0x00, 0x50, 0x00, 0xe7, 0x00, 0xe8, 0x00, 0xe9, 0x00, 0xe4, 0x00, 0x50, 0x00, 0xea, 0x00, - 0xeb, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0xec, 0x00, 0xed, 0x00, 0xee, 0x00, 0xef, 0x00, 0xf0, 0x00, - 0x0d, 0x00, 0x0d, 0x00, 0xf1, 0x00, 0xf2, 0x00, 0xf2, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0xf4, 0x00, - 0x0d, 0x00, 0xf5, 0x00, 0xf2, 0x00, 0xf2, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0xf6, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x29, 0x00, 0x28, 0x00, 0x28, 0x00, 0x2a, 0x00, + 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x0f, 0x00, 0x31, 0x00, + 0x32, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x33, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x34, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x35, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x28, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, + 0x42, 0x00, 0x43, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x28, 0x00, 0x45, 0x00, 0x46, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, + 0x4a, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x4c, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x51, 0x00, + 0x3f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x3f, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x3f, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x28, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, + 0x63, 0x00, 0x64, 0x00, 0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6a, 0x00, + 0x6b, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x70, 0x00, + 0x71, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x69, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x64, 0x00, 0x65, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x69, 0x00, 0x7b, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x84, 0x00, 0x65, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0x69, 0x00, 0x88, 0x00, + 0x89, 0x00, 0x84, 0x00, 0x65, 0x00, 0x8a, 0x00, 0x8b, 0x00, 0x8c, 0x00, 0x69, 0x00, 0x8d, 0x00, + 0x8e, 0x00, 0x84, 0x00, 0x5d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x91, 0x00, 0x69, 0x00, 0x92, 0x00, + 0x93, 0x00, 0x94, 0x00, 0x5d, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9e, 0x00, 0x5d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x5d, 0x00, 0xa8, 0x00, 0xa9, 0x00, + 0xaa, 0x00, 0xab, 0x00, 0x28, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb4, 0x00, + 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0xbb, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0xbe, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, + 0x5d, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc4, 0x00, 0xc4, 0x00, 0xc4, 0x00, 0xc4, 0x00, 0xc5, 0x00, + 0xc6, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x5d, 0x00, + 0xc8, 0x00, 0xc9, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xca, 0x00, 0xcb, 0x00, + 0xcc, 0x00, 0xcd, 0x00, 0x5d, 0x00, 0xce, 0x00, 0x5d, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xd0, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, + 0xd6, 0x00, 0xd4, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xd7, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xd8, 0x00, + 0xd9, 0x00, 0x5d, 0x00, 0xda, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xdb, 0x00, + 0x5d, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00, 0x5d, 0x00, 0xe0, 0x00, 0xe1, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xe2, 0x00, 0x5d, 0x00, 0xe3, 0x00, 0xe4, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0x5d, 0x00, 0xe6, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe7, 0x00, 0xe8, 0x00, 0xe9, 0x00, + 0xd4, 0x00, 0xd4, 0x00, 0xea, 0x00, 0xeb, 0x00, 0xec, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xed, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xee, 0x00, 0xef, 0x00, 0xb1, 0x00, 0xf0, 0x00, 0xf1, 0x00, + 0xf2, 0x00, 0x5d, 0x00, 0xf3, 0x00, 0xf4, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xf5, 0x00, 0xf6, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xf7, 0x00, 0xf8, 0x00, 0xf9, 0x00, 0xf4, 0x00, 0x5d, 0x00, 0xfa, 0x00, + 0xfb, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x22, 0x00, 0x22, 0x00, 0x02, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, + 0x06, 0x01, 0x07, 0x01, 0x03, 0x01, 0x03, 0x01, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x08, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, - 0x0f, 0x00, 0xf7, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, - 0xf8, 0x00, 0xf9, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf9, 0x00, 0xfa, 0x00, 0xf8, 0x00, 0xfb, 0x00, - 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01, 0x08, 0x01, 0x09, 0x01, - 0x0a, 0x01, 0x0b, 0x01, 0x0c, 0x01, 0x0c, 0x01, 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0xdc, 0x00, - 0x10, 0x01, 0x11, 0x01, 0x12, 0x01, 0x13, 0x01, 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x16, 0x01, - 0x17, 0x01, 0x18, 0x01, 0x19, 0x01, 0xd5, 0x00, 0x1a, 0x01, 0x1b, 0x01, 0xd5, 0x00, 0x1c, 0x01, - 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, - 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, - 0x1e, 0x01, 0xd5, 0x00, 0x1f, 0x01, 0x20, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x22, 0x01, - 0xd5, 0x00, 0x23, 0x01, 0x1d, 0x01, 0x24, 0x01, 0xd5, 0x00, 0x25, 0x01, 0x26, 0x01, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0x27, 0x01, 0x90, 0x00, 0x28, 0x01, 0x90, 0x00, 0x15, 0x01, 0x15, 0x01, - 0x15, 0x01, 0x29, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x2a, 0x01, 0x15, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x2b, 0x01, 0x2c, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0x2d, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x2e, 0x01, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0x2f, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x30, 0x01, 0x31, 0x01, - 0x15, 0x01, 0x32, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0x33, 0x01, 0x1d, 0x01, 0x34, 0x01, 0x1d, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, - 0x35, 0x01, 0x36, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x37, 0x01, 0x1d, 0x01, 0x38, 0x01, - 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, - 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, 0x1d, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x1d, 0x01, 0x39, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0x3a, 0x01, - 0xd5, 0x00, 0x3b, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0x0b, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0x3d, 0x01, 0x3e, 0x01, 0x3f, 0x01, - 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x40, 0x01, 0x41, 0x01, - 0x0d, 0x00, 0x0d, 0x00, 0x42, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x43, 0x01, 0x44, 0x01, - 0x50, 0x00, 0x45, 0x01, 0x46, 0x01, 0x46, 0x01, 0x46, 0x01, 0x46, 0x01, 0x22, 0x00, 0x22, 0x00, - 0x47, 0x01, 0x48, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4b, 0x01, 0x4c, 0x01, 0x90, 0x00, 0x90, 0x00, - 0xd5, 0x00, 0x4d, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x4e, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x4f, 0x01, 0x90, 0x00, 0x50, 0x01, - 0x51, 0x01, 0x52, 0x01, 0x53, 0x01, 0x54, 0x01, 0x8c, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x55, 0x01, 0xb6, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x56, 0x01, - 0x57, 0x01, 0x50, 0x00, 0x50, 0x00, 0x8c, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0xcc, 0x00, 0x58, 0x01, 0x50, 0x00, 0x50, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x4e, 0x01, 0x50, 0x00, - 0x21, 0x01, 0x59, 0x01, 0x5a, 0x01, 0x21, 0x01, 0x5b, 0x01, 0x5c, 0x01, 0x21, 0x01, 0x5d, 0x01, - 0x5a, 0x01, 0x21, 0x01, 0x21, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x60, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01, 0x62, 0x01, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x9b, 0x00, - 0x50, 0x00, 0x63, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x9b, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x27, 0x01, 0x50, 0x00, 0x50, 0x00, 0xea, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x64, 0x01, 0x50, 0x00, 0x65, 0x01, 0x90, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x66, 0x01, 0x67, 0x01, - 0x0f, 0x00, 0x68, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x69, 0x01, 0x6a, 0x01, - 0x21, 0x00, 0x6b, 0x01, 0x6c, 0x01, 0x6d, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x6e, 0x01, - 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x90, 0x00, 0x90, 0x00, 0x74, 0x01, - 0x75, 0x01, 0x50, 0x00, 0x76, 0x01, 0x77, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x78, 0x01, - 0x79, 0x01, 0x50, 0x00, 0x50, 0x00, 0x7a, 0x01, 0x7b, 0x01, 0xc4, 0x00, 0x22, 0x00, 0x7c, 0x01, - 0xe4, 0x00, 0x50, 0x00, 0x7d, 0x01, 0x50, 0x00, 0x7e, 0x01, 0x7f, 0x01, 0x50, 0x00, 0x9b, 0x00, - 0x4f, 0x00, 0x50, 0x00, 0x50, 0x00, 0x80, 0x01, 0x81, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, - 0x50, 0x00, 0x50, 0x00, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x50, 0x00, 0x89, 0x01, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x8a, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, - 0x8f, 0x01, 0x90, 0x01, 0x46, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0x91, 0x01, 0x92, 0x01, 0x0d, 0x00, - 0x0d, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x50, 0x00, 0x50, 0x00, 0x93, 0x01, 0xc4, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x94, 0x01, 0x50, 0x00, 0x95, 0x01, 0x50, 0x00, 0x50, 0x00, 0xd2, 0x00, - 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, - 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, - 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, - 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, 0x96, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd0, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd3, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x9d, 0x01, 0x9e, 0x01, 0x9f, 0x01, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0xa0, 0x01, 0x4a, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0xa1, 0x01, 0x32, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x0d, 0x01, 0x0d, 0x01, 0xa2, 0x01, - 0x22, 0x00, 0xa3, 0x01, 0x22, 0x00, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0x4b, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0xa8, 0x01, - 0xa9, 0x01, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, - 0x50, 0x00, 0xad, 0x01, 0x50, 0x00, 0xcc, 0x00, 0xae, 0x01, 0xaf, 0x01, 0xb0, 0x01, 0xb1, 0x01, - 0xb2, 0x01, 0x50, 0x00, 0xb0, 0x00, 0xb3, 0x01, 0xd0, 0x00, 0xd0, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xb4, 0x01, - 0xb5, 0x01, 0xb6, 0x01, 0xb6, 0x01, 0xb7, 0x01, 0xb8, 0x01, 0xb8, 0x01, 0xb8, 0x01, 0xb9, 0x01, - 0xba, 0x01, 0xbb, 0x01, 0xbc, 0x01, 0x90, 0x00, 0x90, 0x00, 0x21, 0x01, 0x21, 0x01, 0xbd, 0x01, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x9b, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x68, 0x00, 0xbe, 0x01, 0xbf, 0x01, - 0x50, 0x00, 0x50, 0x00, 0xc0, 0x01, 0x50, 0x00, 0xc1, 0x01, 0x50, 0x00, 0x50, 0x00, 0xc2, 0x01, - 0x50, 0x00, 0xc3, 0x01, 0x50, 0x00, 0x50, 0x00, 0xc4, 0x01, 0xc5, 0x01, 0x90, 0x00, 0x90, 0x00, - 0x0b, 0x00, 0x0b, 0x00, 0xc6, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0xd0, 0x00, 0xc4, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0xc7, 0x01, 0x0d, 0x00, 0xc8, 0x01, - 0x50, 0x00, 0x50, 0x00, 0xc9, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xca, 0x01, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x45, 0x01, 0x50, 0x00, 0xcb, 0x00, 0xc9, 0x01, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xcb, 0x01, 0x32, 0x00, 0x32, 0x00, 0xcc, 0x01, 0x32, 0x00, 0xcd, 0x01, 0x32, 0x00, 0xce, 0x01, - 0x32, 0x00, 0xcf, 0x01, 0xd0, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x32, 0x00, 0xd1, 0x01, - 0x32, 0x00, 0xd2, 0x01, 0x32, 0x00, 0xd3, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0xd4, 0x01, 0xd5, 0x01, 0xd6, 0x01, 0xd5, 0x01, 0xd5, 0x01, - 0xd7, 0x01, 0xd8, 0x01, 0x32, 0x00, 0xd9, 0x01, 0xda, 0x01, 0xdb, 0x01, 0x32, 0x00, 0xdc, 0x01, - 0x32, 0x00, 0xdd, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0xde, 0x01, 0x32, 0x00, 0xdf, 0x01, 0xe0, 0x01, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0xe1, 0x01, 0x32, 0x00, 0xe2, 0x01, 0x32, 0x00, 0xe3, 0x01, - 0x32, 0x00, 0xe4, 0x01, 0xe5, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0xe6, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0xe7, 0x01, 0xe7, 0x01, 0xe7, 0x01, 0xe8, 0x01, 0xe9, 0x01, 0xe9, 0x01, 0xe9, 0x01, 0xea, 0x01, - 0x32, 0x00, 0x32, 0x00, 0xeb, 0x01, 0xec, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x15, 0x01, 0xed, 0x01, - 0x32, 0x00, 0x32, 0x00, 0xee, 0x01, 0xef, 0x01, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x32, 0x00, 0xdd, 0x01, 0xf0, 0x01, 0x32, 0x00, 0x40, 0x00, 0xf1, 0x01, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x32, 0x00, 0xf2, 0x01, 0x4a, 0x00, 0x32, 0x00, 0xf3, 0x01, - 0xf4, 0x01, 0x50, 0x00, 0x50, 0x00, 0xf5, 0x01, 0xf6, 0x01, 0xf7, 0x01, 0xf8, 0x01, 0xf9, 0x01, - 0xe2, 0x00, 0x50, 0x00, 0x50, 0x00, 0xfa, 0x01, 0xfb, 0x01, 0x50, 0x00, 0xc8, 0x00, 0xc4, 0x00, - 0xfc, 0x01, 0x50, 0x00, 0xfd, 0x01, 0xfe, 0x01, 0xff, 0x01, 0x50, 0x00, 0x50, 0x00, 0x00, 0x02, - 0xe2, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, 0x02, - 0x50, 0x00, 0x65, 0x00, 0x06, 0x02, 0x07, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x08, 0x02, 0x09, 0x02, 0x0a, 0x02, 0x50, 0x00, 0x50, 0x00, 0x0b, 0x02, 0x0c, 0x02, 0xc4, 0x00, - 0x0d, 0x02, 0x57, 0x00, 0x58, 0x00, 0x0e, 0x02, 0x0f, 0x02, 0x10, 0x02, 0x11, 0x02, 0x12, 0x02, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x13, 0x02, 0x14, 0x02, 0x15, 0x02, 0x16, 0x02, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x17, 0x02, 0x18, 0x02, 0xc4, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x19, 0x02, 0x1a, 0x02, 0x1b, 0x02, 0x1c, 0x02, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x1d, 0x02, 0x1e, 0x02, 0xc4, 0x00, 0x1f, 0x02, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x20, 0x02, 0x21, 0x02, 0xc4, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0xb1, 0x00, 0x22, 0x02, 0x23, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x06, 0x02, 0x24, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x98, 0x00, 0x25, 0x02, - 0x26, 0x02, 0x27, 0x02, 0x50, 0x00, 0x28, 0x02, 0x29, 0x02, 0xc4, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x2a, 0x02, 0x50, 0x00, 0x50, 0x00, 0x2b, 0x02, 0x2c, 0x02, 0x90, 0x00, - 0x2d, 0x02, 0x50, 0x00, 0x50, 0x00, 0x2e, 0x02, 0x2f, 0x02, 0x30, 0x02, 0x50, 0x00, 0x50, 0x00, - 0x31, 0x02, 0x32, 0x02, 0x33, 0x02, 0x90, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc8, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x58, 0x00, 0x50, 0x00, 0x19, 0x02, 0x34, 0x02, 0x35, 0x02, 0x98, 0x00, 0xb3, 0x00, 0x36, 0x02, - 0x50, 0x00, 0x37, 0x02, 0x38, 0x02, 0x39, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x3a, 0x02, 0x50, 0x00, 0x50, 0x00, 0x3b, 0x02, 0x3c, 0x02, 0xc4, 0x00, 0x3d, 0x02, 0x50, 0x00, - 0x3e, 0x02, 0x3f, 0x02, 0xc4, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x50, 0x00, 0x40, 0x02, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x68, 0x00, 0xb6, 0x01, 0x41, 0x02, 0x42, 0x02, 0x43, 0x02, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0xd3, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x16, 0x01, 0x16, 0x01, 0x16, 0x01, 0x16, 0x01, 0x16, 0x01, 0x16, 0x01, 0x44, 0x02, 0x45, 0x02, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x94, 0x01, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0xcc, 0x00, 0x46, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x45, 0x01, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc8, 0x00, 0x50, 0x00, 0xcc, 0x00, 0x82, 0x01, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x50, 0x00, 0xd0, 0x00, 0x47, 0x02, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x48, 0x02, 0x49, 0x02, 0x4a, 0x02, 0x4b, 0x02, 0x4c, 0x02, - 0x50, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, - 0xb6, 0x01, 0x4d, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x4e, 0x02, 0x4f, 0x02, 0x50, 0x02, 0x50, 0x02, - 0x51, 0x02, 0x52, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x53, 0x02, 0x54, 0x02, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xc9, 0x01, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xcb, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xc8, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0xcc, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x55, 0x02, 0x56, 0x02, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd2, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xb4, 0x01, 0x9b, 0x00, - 0xc8, 0x00, 0x57, 0x02, 0x58, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x59, 0x02, - 0x21, 0x01, 0x21, 0x01, 0x5a, 0x02, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x5b, 0x02, 0x5c, 0x02, - 0x5d, 0x02, 0x21, 0x01, 0x5e, 0x02, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x5f, 0x02, 0x90, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x60, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xb6, 0x01, 0x61, 0x02, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x27, 0x01, 0xb6, 0x01, 0x62, 0x02, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x0b, 0x00, 0x63, 0x02, 0x0d, 0x00, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0xf8, 0x00, 0x0b, 0x00, - 0x67, 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x0b, 0x00, 0x63, 0x02, 0x0d, 0x00, - 0x6c, 0x02, 0x6d, 0x02, 0x0d, 0x00, 0x6e, 0x02, 0x6f, 0x02, 0x70, 0x02, 0x71, 0x02, 0x0b, 0x00, - 0x72, 0x02, 0x0d, 0x00, 0x0b, 0x00, 0x63, 0x02, 0x0d, 0x00, 0x64, 0x02, 0x65, 0x02, 0x0d, 0x00, - 0xf8, 0x00, 0x0b, 0x00, 0x67, 0x02, 0x71, 0x02, 0x0b, 0x00, 0x72, 0x02, 0x0d, 0x00, 0x0b, 0x00, - 0x63, 0x02, 0x0d, 0x00, 0x73, 0x02, 0x0b, 0x00, 0x74, 0x02, 0x75, 0x02, 0x76, 0x02, 0x77, 0x02, - 0x0d, 0x00, 0x78, 0x02, 0x0b, 0x00, 0x79, 0x02, 0x7a, 0x02, 0x7b, 0x02, 0x7c, 0x02, 0x0d, 0x00, - 0x7d, 0x02, 0x0b, 0x00, 0x7e, 0x02, 0x0d, 0x00, 0x7f, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, - 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x81, 0x02, 0x22, 0x00, 0x22, 0x00, 0x82, 0x02, 0x83, 0x02, - 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x87, 0x02, 0x88, 0x02, 0x89, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x9b, 0x00, 0x8a, 0x02, 0x8b, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x50, 0x00, 0x50, 0x00, 0x8c, 0x02, 0x8d, 0x02, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, - 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x8e, 0x02, 0x8f, 0x02, 0x4a, 0x00, 0x4a, 0x00, - 0xe7, 0x01, 0xe7, 0x01, 0x90, 0x02, 0xe9, 0x01, 0x91, 0x02, 0x92, 0x02, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x93, 0x02, - 0xd5, 0x01, 0xd5, 0x01, 0x94, 0x02, 0x95, 0x02, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x93, 0x02, 0xd5, 0x01, 0x96, 0x02, 0x97, 0x02, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x98, 0x02, 0x32, 0x00, 0x99, 0x02, 0x9a, 0x02, 0x9b, 0x02, 0x9c, 0x02, 0x9d, 0x02, 0x9e, 0x02, - 0x9f, 0x02, 0xa0, 0x02, 0xa1, 0x02, 0xa0, 0x02, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0xa2, 0x02, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, 0x4a, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0x50, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0x4e, 0x01, 0xa3, 0x02, 0xa4, 0x02, 0xa4, 0x02, 0xa4, 0x02, 0xd5, 0x00, 0x4f, 0x01, - 0xa5, 0x02, 0x21, 0x01, 0x62, 0x01, 0x21, 0x01, 0x21, 0x01, 0x21, 0x01, 0xa6, 0x02, 0x21, 0x01, - 0x21, 0x01, 0x21, 0x01, 0xa7, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xa8, 0x02, 0x21, 0x01, - 0xa9, 0x02, 0x21, 0x01, 0x21, 0x01, 0xaa, 0x02, 0x5f, 0x02, 0xab, 0x02, 0x4f, 0x01, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xac, 0x02, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xad, 0x02, 0xbb, 0x01, 0xbb, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x4e, 0x01, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xae, 0x02, 0x50, 0x01, 0x90, 0x00, - 0x50, 0x01, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xad, 0x02, 0xb4, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xad, 0x02, 0xd5, 0x00, 0xaf, 0x02, 0xb0, 0x02, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xb1, 0x02, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xb2, 0x02, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0x4e, 0x01, 0xaf, 0x02, 0xb3, 0x02, - 0x27, 0x01, 0xd5, 0x00, 0xae, 0x02, 0x27, 0x01, 0xb4, 0x02, 0x27, 0x01, 0x90, 0x00, 0x90, 0x00, - 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd5, 0x00, - 0xd5, 0x00, 0xb5, 0x02, 0xd5, 0x00, 0xd5, 0x00, 0x28, 0x01, 0x90, 0x00, 0x90, 0x00, 0xb6, 0x02, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0xb7, 0x02, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd0, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xd1, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0xd0, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x16, 0x02, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x68, 0x00, 0x90, 0x00, - 0x50, 0x00, 0xd0, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, - 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0xb4, 0x01, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0xb8, 0x02, 0x0d, 0x01, 0xb9, 0x02, 0xb9, 0x02, 0xb9, 0x02, 0xb9, 0x02, 0xb9, 0x02, 0xb9, 0x02, - 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, - 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, - 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x0d, 0x01, - 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, - 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, - 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, - 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, 0x0d, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, - 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0x97, 0x01, 0xba, 0x02 + 0x0f, 0x00, 0x09, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0a, 0x01, 0x0b, 0x01, 0x0a, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c, 0x01, 0x0a, 0x01, 0x0d, 0x01, + 0x0e, 0x01, 0x0e, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10, 0x01, 0x11, 0x01, 0x12, 0x01, 0x13, 0x01, + 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x01, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, + 0x1c, 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1e, 0x01, 0x1f, 0x01, 0x20, 0x01, 0x21, 0x01, 0xec, 0x00, + 0x22, 0x01, 0x23, 0x01, 0x24, 0x01, 0x25, 0x01, 0x26, 0x01, 0x27, 0x01, 0x28, 0x01, 0x29, 0x01, + 0x2a, 0x01, 0x2b, 0x01, 0x2c, 0x01, 0xe5, 0x00, 0x2d, 0x01, 0x2e, 0x01, 0xe5, 0x00, 0x2f, 0x01, + 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, + 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, + 0x31, 0x01, 0xe5, 0x00, 0x32, 0x01, 0x33, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x35, 0x01, + 0xe5, 0x00, 0x36, 0x01, 0x30, 0x01, 0x37, 0x01, 0xe5, 0x00, 0x38, 0x01, 0x39, 0x01, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0x3a, 0x01, 0x9d, 0x00, 0x3b, 0x01, 0x9d, 0x00, 0x27, 0x01, 0x27, 0x01, + 0x27, 0x01, 0x3c, 0x01, 0x34, 0x01, 0x3d, 0x01, 0x3e, 0x01, 0x3f, 0x01, 0x40, 0x01, 0x27, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x41, 0x01, 0x42, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0x43, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x44, 0x01, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0x45, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x46, 0x01, 0x47, 0x01, + 0x27, 0x01, 0x48, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0x49, 0x01, 0x30, 0x01, 0x4a, 0x01, 0x30, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, + 0x4b, 0x01, 0x4c, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x4d, 0x01, 0x30, 0x01, 0x4e, 0x01, + 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, + 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x30, 0x01, 0x4f, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0x50, 0x01, + 0xe5, 0x00, 0x51, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0x37, 0x00, 0x37, 0x00, 0x52, 0x01, 0x3a, 0x00, 0x3a, 0x00, 0x53, 0x01, 0x54, 0x01, 0x55, 0x01, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x56, 0x01, 0x57, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x59, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5a, 0x01, 0x5b, 0x01, + 0x5d, 0x00, 0x5c, 0x01, 0x5d, 0x01, 0x5d, 0x01, 0x5d, 0x01, 0x5d, 0x01, 0x28, 0x00, 0x28, 0x00, + 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x61, 0x01, 0x62, 0x01, 0x63, 0x01, 0x9d, 0x00, 0x9d, 0x00, + 0xe5, 0x00, 0x64, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x65, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x66, 0x01, 0x9d, 0x00, 0x67, 0x01, + 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x99, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x6c, 0x01, 0xc6, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x6d, 0x01, + 0x6e, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x99, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0xdc, 0x00, 0x6f, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x65, 0x01, 0x5d, 0x00, + 0x34, 0x01, 0x70, 0x01, 0x71, 0x01, 0x34, 0x01, 0x72, 0x01, 0x73, 0x01, 0x34, 0x01, 0x74, 0x01, + 0x71, 0x01, 0x34, 0x01, 0x34, 0x01, 0x75, 0x01, 0x76, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x77, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x78, 0x01, 0x34, 0x01, 0x79, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xa8, 0x00, + 0x5d, 0x00, 0x7a, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0xa8, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x3a, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xfa, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x7b, 0x01, 0x5d, 0x00, 0x7c, 0x01, 0x9d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x7d, 0x01, 0x7e, 0x01, + 0x0f, 0x00, 0x7f, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x80, 0x01, 0x81, 0x01, + 0x27, 0x00, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x85, 0x01, + 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0x8b, 0x01, + 0x8c, 0x01, 0x5d, 0x00, 0x8d, 0x01, 0x8e, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x8f, 0x01, + 0x90, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x91, 0x01, 0x92, 0x01, 0xd4, 0x00, 0x28, 0x00, 0x93, 0x01, + 0xf4, 0x00, 0x5d, 0x00, 0x94, 0x01, 0x5d, 0x00, 0x95, 0x01, 0x96, 0x01, 0x5d, 0x00, 0xa8, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0x9c, 0x01, 0x9d, 0x01, 0x9e, 0x01, 0x9f, 0x01, 0x5d, 0x00, 0xa0, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, + 0xa6, 0x01, 0xa7, 0x01, 0x5d, 0x01, 0x22, 0x00, 0x22, 0x00, 0xa8, 0x01, 0xa9, 0x01, 0xaa, 0x01, + 0xaa, 0x01, 0xaa, 0x01, 0xaa, 0x01, 0xaa, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xab, 0x01, 0xd4, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xac, 0x01, 0x5d, 0x00, 0xad, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xe2, 0x00, + 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, + 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, + 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, + 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, 0xae, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe0, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe3, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xb0, 0x01, 0xb1, 0x01, 0xb2, 0x01, 0xb3, 0x01, 0xb4, 0x01, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xb5, 0x01, 0xb6, 0x01, 0xb7, 0x01, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xb8, 0x01, 0x57, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0xb9, 0x01, 0x3f, 0x00, 0x3f, 0x00, 0x59, 0x00, 0x1f, 0x01, 0x1f, 0x01, 0xba, 0x01, + 0x28, 0x00, 0xbb, 0x01, 0x28, 0x00, 0xbc, 0x01, 0xbd, 0x01, 0xbe, 0x01, 0xbf, 0x01, 0x58, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xc0, 0x01, + 0xc1, 0x01, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0xc2, 0x01, 0xc3, 0x01, 0xc4, 0x01, + 0x5d, 0x00, 0xc5, 0x01, 0x5d, 0x00, 0xdc, 0x00, 0xc6, 0x01, 0xc7, 0x01, 0xc8, 0x01, 0xc9, 0x01, + 0xca, 0x01, 0x5d, 0x00, 0xbf, 0x00, 0xcb, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xcc, 0x01, + 0xcd, 0x01, 0xce, 0x01, 0xce, 0x01, 0xcf, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd1, 0x01, + 0xd2, 0x01, 0xd3, 0x01, 0xd4, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0x34, 0x01, 0x34, 0x01, 0xd5, 0x01, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0xa8, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x75, 0x00, 0xd6, 0x01, 0xd7, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0xd8, 0x01, 0x5d, 0x00, 0xd9, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xda, 0x01, + 0x5d, 0x00, 0xdb, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0xdc, 0x01, 0xdd, 0x01, 0x9d, 0x00, 0x9d, 0x00, + 0xde, 0x01, 0xde, 0x01, 0xdf, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0xe0, 0x00, 0xd4, 0x00, 0xde, 0x01, 0xde, 0x01, 0xe1, 0x01, 0xe0, 0x01, 0xe2, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0xe3, 0x01, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe4, 0x01, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5c, 0x01, 0x5d, 0x00, 0xdb, 0x00, 0xe3, 0x01, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xe5, 0x01, 0x3f, 0x00, 0x3f, 0x00, 0xe6, 0x01, 0x3f, 0x00, 0xe7, 0x01, 0x3f, 0x00, 0xe8, 0x01, + 0x3f, 0x00, 0xe9, 0x01, 0xea, 0x01, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x3f, 0x00, 0xeb, 0x01, + 0x3f, 0x00, 0xec, 0x01, 0x3f, 0x00, 0xed, 0x01, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xee, 0x01, 0xef, 0x01, 0xf0, 0x01, 0xef, 0x01, 0xef, 0x01, + 0xf1, 0x01, 0xf2, 0x01, 0x3f, 0x00, 0xf3, 0x01, 0xf4, 0x01, 0xf5, 0x01, 0x3f, 0x00, 0xf6, 0x01, + 0x3f, 0x00, 0xf7, 0x01, 0x57, 0x00, 0x57, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0xf9, 0x01, 0xfa, 0x01, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xfb, 0x01, 0x3f, 0x00, 0xfc, 0x01, 0x3f, 0x00, 0xfd, 0x01, + 0x3f, 0x00, 0xfe, 0x01, 0xff, 0x01, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x02, + 0x3f, 0x00, 0x3f, 0x00, 0x05, 0x02, 0x06, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x27, 0x01, 0x07, 0x02, + 0x3f, 0x00, 0x3f, 0x00, 0x08, 0x02, 0x09, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x3f, 0x00, 0xf7, 0x01, 0x0a, 0x02, 0x3f, 0x00, 0x4d, 0x00, 0x0b, 0x02, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x3f, 0x00, 0x0c, 0x02, 0x57, 0x00, 0x3f, 0x00, 0x0d, 0x02, + 0x0e, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x0f, 0x02, 0x10, 0x02, 0x11, 0x02, 0x12, 0x02, 0x13, 0x02, + 0xf2, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x14, 0x02, 0x15, 0x02, 0x5d, 0x00, 0xd8, 0x00, 0xd4, 0x00, + 0x16, 0x02, 0x5d, 0x00, 0x17, 0x02, 0x18, 0x02, 0x19, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x1a, 0x02, + 0xf2, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x1b, 0x02, 0x1c, 0x02, 0x1d, 0x02, 0x1e, 0x02, 0x1f, 0x02, + 0x5d, 0x00, 0x72, 0x00, 0x20, 0x02, 0x21, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x22, 0x02, 0x23, 0x02, 0x24, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x25, 0x02, 0x26, 0x02, 0xd4, 0x00, + 0x27, 0x02, 0x64, 0x00, 0x65, 0x00, 0x28, 0x02, 0x29, 0x02, 0x2a, 0x02, 0x2b, 0x02, 0x2c, 0x02, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x2d, 0x02, 0x2e, 0x02, 0x2f, 0x02, 0x30, 0x02, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x31, 0x02, 0x32, 0x02, 0xd4, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x33, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x37, 0x02, 0x38, 0x02, 0xd4, 0x00, 0x39, 0x02, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x3a, 0x02, 0x3b, 0x02, 0xd4, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0xc0, 0x00, 0x3c, 0x02, 0x3d, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x20, 0x02, 0x3e, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0xa5, 0x00, 0x3f, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x5d, 0x00, 0x42, 0x02, 0x43, 0x02, 0xd4, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x44, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x45, 0x02, 0x46, 0x02, 0x9d, 0x00, + 0x47, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x48, 0x02, 0x49, 0x02, 0x4a, 0x02, 0x5d, 0x00, 0x5d, 0x00, + 0x4b, 0x02, 0x4c, 0x02, 0x4d, 0x02, 0x9d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xd8, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x65, 0x00, 0x5d, 0x00, 0x33, 0x02, 0x4e, 0x02, 0x4f, 0x02, 0xa5, 0x00, 0xc2, 0x00, 0x50, 0x02, + 0x5d, 0x00, 0x51, 0x02, 0x52, 0x02, 0x53, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x54, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x55, 0x02, 0x56, 0x02, 0xd4, 0x00, 0x57, 0x02, 0x5d, 0x00, + 0x58, 0x02, 0x59, 0x02, 0xd4, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x5d, 0x00, 0x5a, 0x02, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x75, 0x00, 0xce, 0x01, 0x5b, 0x02, 0x5c, 0x02, 0x5d, 0x02, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0xe3, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5f, 0x02, 0x60, 0x02, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xac, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xdc, 0x00, 0x61, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5c, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xd8, 0x00, 0x5d, 0x00, 0xdc, 0x00, 0x99, 0x01, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x5d, 0x00, 0xe0, 0x00, 0x62, 0x02, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x63, 0x02, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x5d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0d, 0x00, + 0xce, 0x01, 0x68, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, + 0x6c, 0x02, 0x6d, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x6e, 0x02, 0x6f, 0x02, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe3, 0x01, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xdb, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xd8, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0xdc, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x70, 0x02, 0x71, 0x02, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe2, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xcc, 0x01, 0xa8, 0x00, + 0xd8, 0x00, 0x72, 0x02, 0x73, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x74, 0x02, + 0x34, 0x01, 0x34, 0x01, 0x75, 0x02, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x34, 0x01, 0x79, 0x02, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x7a, 0x02, 0x9d, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x7b, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0xce, 0x01, 0x7c, 0x02, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x3a, 0x01, 0xce, 0x01, 0x7d, 0x02, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x7e, 0x02, 0x7f, 0x02, 0x22, 0x00, 0x80, 0x02, 0x81, 0x02, 0x82, 0x02, 0x83, 0x02, 0x7e, 0x02, + 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x7e, 0x02, 0x7f, 0x02, 0x22, 0x00, + 0x89, 0x02, 0x8a, 0x02, 0x22, 0x00, 0x8b, 0x02, 0x8c, 0x02, 0x8d, 0x02, 0x8e, 0x02, 0x7e, 0x02, + 0x8f, 0x02, 0x22, 0x00, 0x7e, 0x02, 0x7f, 0x02, 0x22, 0x00, 0x80, 0x02, 0x81, 0x02, 0x22, 0x00, + 0x83, 0x02, 0x7e, 0x02, 0x84, 0x02, 0x8e, 0x02, 0x7e, 0x02, 0x8f, 0x02, 0x22, 0x00, 0x7e, 0x02, + 0x7f, 0x02, 0x22, 0x00, 0x90, 0x02, 0x7e, 0x02, 0x91, 0x02, 0x92, 0x02, 0x93, 0x02, 0x94, 0x02, + 0x22, 0x00, 0x95, 0x02, 0x7e, 0x02, 0x96, 0x02, 0x97, 0x02, 0x98, 0x02, 0x99, 0x02, 0x22, 0x00, + 0x9a, 0x02, 0x7e, 0x02, 0x9b, 0x02, 0x22, 0x00, 0x9c, 0x02, 0x9d, 0x02, 0x9d, 0x02, 0x9d, 0x02, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x9e, 0x02, 0x28, 0x00, 0x28, 0x00, 0x9f, 0x02, 0xa0, 0x02, + 0xa1, 0x02, 0xa2, 0x02, 0xa3, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xa4, 0x02, 0xa5, 0x02, 0xa6, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0xa8, 0x00, 0xa7, 0x02, 0xa8, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xa9, 0x02, 0xaa, 0x02, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xab, 0x02, 0xac, 0x02, 0x57, 0x00, 0x57, 0x00, + 0xad, 0x02, 0xad, 0x02, 0xae, 0x02, 0xaf, 0x02, 0xb0, 0x02, 0xb1, 0x02, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0xb2, 0x02, + 0xef, 0x01, 0xef, 0x01, 0xb3, 0x02, 0xb4, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0xb2, 0x02, 0xef, 0x01, 0xb5, 0x02, 0xb6, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0xb7, 0x02, 0x3f, 0x00, 0xb8, 0x02, 0xb9, 0x02, 0xba, 0x02, 0xbb, 0x02, 0xbc, 0x02, 0xbd, 0x02, + 0xbe, 0x02, 0xbf, 0x02, 0xc0, 0x02, 0xbf, 0x02, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0xc1, 0x02, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, 0x57, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0x67, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0x65, 0x01, 0xc2, 0x02, 0xc3, 0x02, 0xc3, 0x02, 0xc3, 0x02, 0xe5, 0x00, 0x66, 0x01, + 0xc4, 0x02, 0x34, 0x01, 0x79, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x01, 0xc5, 0x02, 0x34, 0x01, + 0x34, 0x01, 0x34, 0x01, 0xc6, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0xc7, 0x02, 0x34, 0x01, + 0xc8, 0x02, 0x34, 0x01, 0x34, 0x01, 0xc9, 0x02, 0x7a, 0x02, 0xca, 0x02, 0x66, 0x01, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xcb, 0x02, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xcc, 0x02, 0xd3, 0x01, 0xd3, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x65, 0x01, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xcd, 0x02, 0x67, 0x01, 0x9d, 0x00, + 0x67, 0x01, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xcc, 0x02, 0xc3, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xcc, 0x02, 0xe5, 0x00, 0xce, 0x02, 0xcf, 0x02, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xd0, 0x02, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xd1, 0x02, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0x65, 0x01, 0xce, 0x02, 0xd2, 0x02, + 0x3a, 0x01, 0xe5, 0x00, 0xcd, 0x02, 0x3a, 0x01, 0xd3, 0x02, 0x3a, 0x01, 0x9d, 0x00, 0x9d, 0x00, + 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, 0xe5, 0x00, + 0xe5, 0x00, 0xd4, 0x02, 0xe5, 0x00, 0xe5, 0x00, 0x3b, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0xd5, 0x02, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0xd6, 0x02, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe0, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xe1, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0xe0, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x30, 0x02, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x75, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0xe0, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0xcc, 0x01, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0xd7, 0x02, 0x1f, 0x01, 0xd8, 0x02, 0xd8, 0x02, 0xd8, 0x02, 0xd8, 0x02, 0xd8, 0x02, 0xd8, 0x02, + 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x1f, 0x01, + 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, + 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, + 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, + 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x1f, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, + 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xaf, 0x01, 0xd9, 0x02 }; - private static ReadOnlySpan CategoryCasingLevel3Index => new byte[11184] + private static ReadOnlySpan CategoryCasingLevel3Index => new byte[11680] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -568,276 +567,295 @@ namespace System.Globalization 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x05, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04, 0x04, 0x04, 0x04, 0x0e, 0x03, 0x0b, 0x0e, 0x0f, 0x10, 0x07, 0x11, 0x0e, 0x0b, - 0x0e, 0x07, 0x12, 0x12, 0x0b, 0x0d, 0x03, 0x03, 0x0b, 0x12, 0x0f, 0x13, 0x12, 0x12, 0x12, 0x03, + 0x0e, 0x07, 0x12, 0x12, 0x0b, 0x13, 0x03, 0x03, 0x0b, 0x12, 0x0f, 0x14, 0x12, 0x12, 0x12, 0x03, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x15, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, - 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, - 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, - 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0d, 0x0f, 0x0a, 0x0d, 0x0d, 0x0d, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0a, 0x14, 0x0d, 0x0a, 0x14, 0x0d, 0x0a, 0x14, 0x0d, 0x0a, 0x0d, 0x0a, - 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, - 0x0d, 0x0a, 0x14, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, - 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0f, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x0b, 0x0b, 0x0b, 0x0b, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x15, 0x15, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x16, 0x0b, 0x15, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x0a, 0x0d, 0x0a, 0x0d, 0x16, 0x0b, 0x0a, 0x0d, 0x18, 0x18, 0x15, 0x0d, 0x0d, 0x0d, 0x03, 0x0a, - 0x18, 0x18, 0x18, 0x18, 0x0b, 0x0b, 0x0a, 0x03, 0x0a, 0x0a, 0x0a, 0x18, 0x0a, 0x18, 0x0a, 0x0a, - 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, - 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, 0x07, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0d, 0x19, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1a, 0x1a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, - 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, 0x15, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x1b, 0x08, 0x18, 0x18, 0x0e, 0x0e, 0x04, - 0x1c, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1d, 0x17, - 0x1e, 0x17, 0x17, 0x1e, 0x17, 0x17, 0x1e, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x07, 0x07, 0x20, 0x03, 0x03, 0x21, 0x03, 0x1e, 0x0e, 0x0e, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1e, 0x22, 0x1c, 0x1e, 0x1e, - 0x23, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x03, 0x03, 0x03, 0x1e, 0x1f, 0x1f, - 0x17, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x11, 0x0e, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x23, 0x23, 0x17, 0x17, 0x0e, 0x17, 0x17, 0x17, 0x17, 0x1f, 0x1f, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x1f, 0x1f, 0x1f, 0x24, 0x24, 0x1f, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, 0x22, - 0x1f, 0x17, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1c, 0x1c, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x23, 0x23, 0x0e, 0x03, 0x03, 0x03, 0x23, 0x1c, 0x1c, 0x17, 0x21, 0x21, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x23, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x23, 0x17, 0x17, 0x17, 0x23, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1c, 0x1c, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x1c, 0x1c, 0x1e, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x11, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x17, 0x0f, 0x26, 0x26, - 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x26, 0x26, 0x17, 0x26, 0x26, - 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x17, 0x17, 0x1b, 0x1b, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x1b, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x17, 0x26, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, - 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x18, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x0f, 0x26, 0x26, - 0x26, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x26, 0x26, 0x18, 0x18, 0x26, 0x26, 0x17, 0x0f, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x26, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x18, 0x0f, - 0x0f, 0x0f, 0x17, 0x17, 0x18, 0x18, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x0f, 0x0f, 0x04, 0x04, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x19, 0x04, 0x0f, 0x1b, 0x17, 0x18, - 0x18, 0x17, 0x17, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x18, 0x26, 0x26, - 0x26, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x18, 0x18, 0x17, 0x17, 0x17, 0x18, 0x18, - 0x18, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x17, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x17, 0x17, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, - 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x0f, 0x26, 0x26, - 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x26, 0x18, 0x26, 0x26, 0x17, 0x18, 0x18, - 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x1b, 0x04, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x18, 0x17, 0x26, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x0f, 0x26, 0x17, - 0x26, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x26, 0x26, 0x18, 0x18, 0x26, 0x26, 0x17, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x26, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x18, 0x0f, - 0x19, 0x0f, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x17, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x0f, 0x0f, - 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x26, 0x26, - 0x17, 0x26, 0x26, 0x18, 0x18, 0x18, 0x26, 0x26, 0x26, 0x18, 0x26, 0x26, 0x26, 0x17, 0x18, 0x18, - 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x04, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x26, 0x26, 0x26, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x17, 0x17, - 0x17, 0x26, 0x26, 0x26, 0x26, 0x18, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x18, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x19, - 0x0f, 0x17, 0x26, 0x26, 0x1b, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x0f, 0x26, 0x29, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x18, 0x29, 0x26, 0x26, 0x18, 0x26, 0x26, 0x17, 0x17, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x18, - 0x18, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x26, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x0f, 0x26, 0x26, - 0x26, 0x17, 0x17, 0x17, 0x17, 0x18, 0x26, 0x26, 0x26, 0x18, 0x26, 0x26, 0x26, 0x17, 0x0f, 0x19, - 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x0f, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x19, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x18, 0x17, 0x26, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x17, 0x18, 0x18, 0x18, 0x18, 0x26, - 0x26, 0x26, 0x17, 0x17, 0x17, 0x18, 0x17, 0x18, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x18, 0x18, 0x26, 0x26, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x17, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x04, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x17, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x0f, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x15, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x19, 0x19, 0x19, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x19, 0x1b, 0x19, 0x19, 0x19, 0x17, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x19, 0x17, 0x19, 0x17, 0x19, 0x17, 0x05, 0x06, 0x05, 0x06, 0x26, 0x26, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, - 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x19, 0x19, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x19, 0x19, 0x19, 0x19, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x17, 0x17, 0x17, - 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x0f, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, - 0x17, 0x0f, 0x26, 0x26, 0x26, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x0f, 0x0f, - 0x0f, 0x17, 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x17, 0x26, 0x26, 0x17, 0x17, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x17, 0x0f, 0x26, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x17, 0x19, 0x19, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0a, 0x18, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x1b, 0x15, 0x0d, 0x0d, 0x0d, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, - 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, - 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x17, 0x17, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, - 0x08, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x19, 0x1b, 0x0f, - 0x02, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x05, 0x06, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1b, 0x1b, 0x1b, 0x2a, 0x2a, - 0x2a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, - 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x18, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x17, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x15, 0x1b, 0x1b, 0x1b, 0x04, 0x0f, 0x17, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x03, 0x03, 0x03, 0x03, 0x17, 0x17, 0x17, 0x11, 0x18, - 0x0f, 0x0f, 0x0f, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, - 0x17, 0x17, 0x17, 0x26, 0x26, 0x26, 0x26, 0x17, 0x17, 0x26, 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, - 0x26, 0x26, 0x17, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x18, 0x18, 0x18, 0x03, 0x03, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x18, 0x18, 0x18, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x26, 0x26, 0x17, 0x18, 0x18, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, - 0x17, 0x26, 0x17, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x17, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x15, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1a, 0x17, - 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x17, 0x26, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, - 0x1b, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x26, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x26, 0x17, 0x17, 0x17, 0x0f, 0x0f, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x17, 0x17, 0x26, 0x26, 0x26, 0x17, 0x26, 0x17, - 0x17, 0x17, 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1b, 0x1b, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, 0x0a, 0x0a, 0x0a, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x1b, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x0f, 0x26, 0x17, 0x17, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x15, 0x15, 0x15, 0x15, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x16, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x19, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, + 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x1a, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x15, + 0x1b, 0x1c, 0x17, 0x18, 0x17, 0x18, 0x1d, 0x17, 0x18, 0x1e, 0x1e, 0x17, 0x18, 0x15, 0x1f, 0x20, + 0x21, 0x17, 0x18, 0x1e, 0x22, 0x23, 0x24, 0x25, 0x17, 0x18, 0x26, 0x15, 0x24, 0x27, 0x28, 0x29, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x2a, 0x17, 0x18, 0x2a, 0x15, 0x15, 0x17, 0x18, 0x2a, 0x17, + 0x18, 0x2b, 0x2b, 0x17, 0x18, 0x17, 0x18, 0x2c, 0x17, 0x18, 0x15, 0x0f, 0x17, 0x18, 0x15, 0x2d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x2e, 0x2f, 0x30, 0x2e, 0x2f, 0x30, 0x2e, 0x2f, 0x30, 0x17, 0x18, 0x17, + 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x31, 0x17, 0x18, + 0x15, 0x2e, 0x2f, 0x30, 0x17, 0x18, 0x32, 0x33, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x34, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x17, 0x18, 0x17, 0x18, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x35, 0x17, 0x18, 0x36, 0x37, 0x38, + 0x38, 0x17, 0x18, 0x39, 0x3a, 0x3b, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x15, 0x41, 0x41, 0x15, 0x42, 0x15, 0x43, 0x44, 0x15, 0x15, 0x15, + 0x41, 0x45, 0x15, 0x46, 0x15, 0x47, 0x48, 0x15, 0x49, 0x4a, 0x48, 0x4b, 0x4c, 0x15, 0x15, 0x4a, + 0x15, 0x4d, 0x4e, 0x15, 0x15, 0x4f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x50, 0x15, 0x15, + 0x51, 0x15, 0x52, 0x51, 0x15, 0x15, 0x15, 0x53, 0x51, 0x54, 0x55, 0x55, 0x56, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x57, 0x15, 0x0f, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x58, 0x59, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x15, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0a, 0x18, 0x0a, 0x18, 0x0a, 0x18, 0x0a, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x14, 0x0b, 0x0d, 0x0b, - 0x0b, 0x0b, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x14, 0x0b, 0x0b, 0x0b, - 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0b, 0x0b, 0x0b, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, - 0x18, 0x18, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x14, 0x0b, 0x0b, 0x18, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x11, 0x11, 0x11, 0x2b, 0x22, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x03, 0x03, 0x10, 0x13, 0x05, 0x10, 0x10, 0x13, 0x05, 0x10, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x2c, 0x2d, 0x11, 0x11, 0x11, 0x11, 0x11, 0x02, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x10, 0x13, 0x03, 0x03, 0x03, 0x03, 0x0c, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x0b, 0x0b, 0x0b, 0x0b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, + 0x5a, 0x5a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x5b, 0x0b, 0x5a, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x17, 0x18, 0x17, 0x18, 0x5b, 0x0b, 0x17, 0x18, 0x5e, 0x5e, 0x5a, 0x28, 0x28, 0x28, 0x03, 0x5f, + 0x5e, 0x5e, 0x5e, 0x5e, 0x0b, 0x0b, 0x60, 0x03, 0x61, 0x61, 0x61, 0x5e, 0x62, 0x5e, 0x63, 0x63, + 0x15, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x5e, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x64, 0x65, 0x65, 0x65, + 0x15, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x66, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x67, 0x68, 0x68, 0x69, + 0x6a, 0x6b, 0x19, 0x19, 0x19, 0x6c, 0x6d, 0x6e, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x07, 0x17, 0x18, 0x75, 0x17, 0x18, 0x15, 0x34, 0x34, 0x34, + 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x17, 0x18, 0x78, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x79, 0x79, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x7a, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x7b, + 0x5e, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x5e, 0x5e, 0x5a, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x15, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x15, 0x15, 0x7d, 0x08, 0x5e, 0x5e, 0x0e, 0x0e, 0x04, + 0x7f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x80, 0x5c, + 0x81, 0x5c, 0x5c, 0x81, 0x5c, 0x5c, 0x81, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x82, + 0x82, 0x82, 0x82, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x07, 0x07, 0x83, 0x03, 0x03, 0x84, 0x03, 0x81, 0x0e, 0x0e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x81, 0x85, 0x7f, 0x81, 0x81, + 0x86, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x03, 0x03, 0x03, 0x81, 0x82, 0x82, + 0x5c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x81, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x11, 0x0e, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x86, 0x86, 0x5c, 0x5c, 0x0e, 0x5c, 0x5c, 0x5c, 0x5c, 0x82, 0x82, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x82, 0x82, 0x82, 0x87, 0x87, 0x82, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x85, + 0x82, 0x5c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x86, 0x86, 0x0e, 0x03, 0x03, 0x03, 0x86, 0x7f, 0x7f, 0x5c, 0x84, 0x84, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x86, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x86, 0x5c, 0x5c, 0x5c, 0x86, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, 0x81, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x11, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x5c, 0x0f, 0x89, 0x89, + 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x89, 0x89, + 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x5c, 0x5c, 0x7d, 0x7d, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x7d, 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5c, 0x89, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, + 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5e, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x0f, 0x89, 0x89, + 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x89, 0x89, 0x5e, 0x5e, 0x89, 0x89, 0x5c, 0x0f, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, + 0x0f, 0x0f, 0x5c, 0x5c, 0x5e, 0x5e, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x0f, 0x0f, 0x04, 0x04, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x04, 0x0f, 0x7d, 0x5c, 0x5e, + 0x5e, 0x5c, 0x5c, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x5e, 0x89, 0x89, + 0x89, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, 0x5c, 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, + 0x5e, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x5c, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5c, 0x5c, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, + 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x0f, 0x89, 0x89, + 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x89, 0x5e, 0x89, 0x89, 0x5c, 0x5e, 0x5e, + 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x7d, 0x04, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5c, 0x89, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x0f, 0x89, 0x5c, + 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x89, 0x89, 0x5e, 0x5e, 0x89, 0x89, 0x5c, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, 0x5c, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, + 0x78, 0x0f, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5c, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x0f, 0x0f, + 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, 0x89, + 0x5c, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x89, 0x89, 0x89, 0x5e, 0x89, 0x89, 0x89, 0x5c, 0x5e, 0x5e, + 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x04, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x89, 0x89, 0x89, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x5c, 0x5c, + 0x5c, 0x89, 0x89, 0x89, 0x89, 0x5e, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, 0x5c, 0x5e, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x78, + 0x0f, 0x5c, 0x89, 0x89, 0x7d, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x0f, 0x89, 0x8c, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x5e, 0x8c, 0x89, 0x89, 0x5e, 0x89, 0x89, 0x5c, 0x5c, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x5e, + 0x5e, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x89, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x0f, 0x89, 0x89, + 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x89, 0x89, 0x89, 0x5e, 0x89, 0x89, 0x89, 0x5c, 0x0f, 0x78, + 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x89, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x0f, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x5e, 0x5c, 0x89, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, + 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5e, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x5e, 0x5e, 0x89, 0x89, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5c, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x04, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5a, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5c, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x0f, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5a, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x78, 0x78, 0x78, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7d, 0x7d, 0x78, 0x7d, 0x78, 0x78, 0x78, 0x5c, 0x5c, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x5c, 0x78, 0x5c, 0x78, 0x5c, 0x05, 0x06, 0x05, 0x06, 0x89, 0x89, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x78, 0x78, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x78, 0x78, 0x78, 0x78, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x5c, 0x5c, 0x5c, + 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x0f, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, + 0x5c, 0x0f, 0x89, 0x89, 0x89, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x0f, 0x0f, + 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x0f, 0x89, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x5c, 0x78, 0x78, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x5e, 0x8d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x8d, 0x5e, 0x5e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x7d, 0x5a, 0x8e, 0x8e, 0x8e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, + 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, + 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x5e, 0x5e, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x5e, 0x5e, + 0x08, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x78, 0x7d, 0x0f, + 0x02, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x05, 0x06, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x7d, 0x7d, 0x7d, 0x92, 0x92, + 0x92, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x5e, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x5a, 0x7d, 0x7d, 0x7d, 0x04, 0x0f, 0x5c, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x03, 0x03, 0x03, 0x03, 0x5c, 0x5c, 0x5c, 0x11, 0x5e, + 0x0f, 0x0f, 0x0f, 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, + 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, + 0x89, 0x89, 0x5c, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x5e, 0x5e, 0x5e, 0x03, 0x03, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x5e, 0x5e, 0x5e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5e, 0x5e, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, + 0x5c, 0x89, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5c, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5a, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x79, 0x5c, + 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x5c, 0x89, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, + 0x7d, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x5c, 0x89, 0x5c, + 0x5c, 0x5c, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, 0x7d, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x7d, 0x7d, + 0x93, 0x94, 0x95, 0x96, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x5e, 0x5e, 0x9b, 0x9b, 0x9b, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x7d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, 0x89, 0x5c, 0x5c, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5a, 0x9c, 0x15, 0x15, 0x15, 0x9d, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x9e, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x15, 0x15, 0x15, 0x15, 0x15, 0x9f, 0x15, 0x15, 0xa0, 0x15, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0x5e, 0x5e, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x5e, 0x5e, + 0x15, 0xa1, 0x15, 0xa1, 0x15, 0xa1, 0x15, 0xa1, 0x5e, 0xa2, 0x5e, 0xa2, 0x5e, 0xa2, 0x5e, 0xa2, + 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0x5e, 0x5e, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa1, 0xa1, 0x15, 0xaa, 0x15, 0x5e, 0x15, 0x15, 0xa2, 0xa2, 0xab, 0xab, 0xac, 0x0b, 0xad, 0x0b, + 0x0b, 0x0b, 0x15, 0xaa, 0x15, 0x5e, 0x15, 0x15, 0xae, 0xae, 0xae, 0xae, 0xac, 0x0b, 0x0b, 0x0b, + 0xa1, 0xa1, 0x15, 0x15, 0x5e, 0x5e, 0x15, 0x15, 0xa2, 0xa2, 0xaf, 0xaf, 0x5e, 0x0b, 0x0b, 0x0b, + 0xa1, 0xa1, 0x15, 0x15, 0x15, 0x71, 0x15, 0x15, 0xa2, 0xa2, 0xb0, 0xb0, 0x75, 0x0b, 0x0b, 0x0b, + 0x5e, 0x5e, 0x15, 0xaa, 0x15, 0x5e, 0x15, 0x15, 0xb1, 0xb1, 0xb2, 0xb2, 0xac, 0x0b, 0x0b, 0x5e, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x11, 0x11, 0x11, 0xb3, 0x85, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x03, 0x03, 0x10, 0x14, 0x05, 0x10, 0x10, 0x14, 0x05, 0x10, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xb4, 0xb5, 0x11, 0x11, 0x11, 0x11, 0x11, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x10, 0x14, 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x07, 0x05, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x03, 0x0c, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x2e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x12, 0x15, 0x18, 0x18, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x07, 0x07, 0x07, 0x05, 0x06, 0x15, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x07, 0x07, 0x07, 0x05, 0x06, 0x18, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x18, 0x18, 0x18, + 0x11, 0x11, 0x11, 0x11, 0x11, 0xb6, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x5a, 0x5e, 0x5e, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x07, 0x07, 0x07, 0x05, 0x06, 0x5a, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x07, 0x07, 0x07, 0x05, 0x06, 0x5e, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, 0x5e, 0x5e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1a, 0x1a, 0x1a, - 0x1a, 0x17, 0x1a, 0x1a, 0x1a, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x0e, 0x0e, 0x0a, 0x0e, 0x0e, 0x0e, 0x0e, 0x0a, 0x0e, 0x0e, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, - 0x0a, 0x0a, 0x0a, 0x0d, 0x0e, 0x0a, 0x0e, 0x0e, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x0e, 0x0d, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x0d, 0x0e, 0x0e, 0x0d, 0x0d, 0x0a, 0x0a, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x07, 0x0e, 0x0e, 0x0d, 0x19, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x79, 0x79, 0x79, + 0x79, 0x5c, 0x79, 0x79, 0x79, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x0e, 0x0e, 0x19, 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x0e, 0x0e, 0x15, 0x19, 0x19, 0x19, 0x15, 0x15, + 0x19, 0x19, 0x19, 0x15, 0x0e, 0x19, 0x0e, 0x0e, 0x07, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x0e, 0xb7, 0x0e, 0x19, 0x0e, 0xb8, 0xb9, 0x19, 0x19, 0x0e, 0x15, + 0x19, 0x19, 0xba, 0x19, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x0e, 0x0e, 0x15, 0x15, 0x19, 0x19, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x19, 0x15, 0x15, 0x15, 0x15, 0x0e, 0x07, 0x0e, 0x0e, 0xbb, 0x78, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x0a, 0x0d, 0x2a, 0x2a, 0x2a, 0x2a, 0x12, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0x92, 0x92, 0x92, 0x17, 0x18, 0x92, 0x92, 0x92, 0x92, 0x12, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, @@ -846,22 +864,25 @@ namespace System.Globalization 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x05, 0x06, 0x05, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x05, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x78, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x07, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x78, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, @@ -872,400 +893,488 @@ namespace System.Globalization 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x05, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, - 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x15, 0x15, 0x0a, 0x0a, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0a, 0x0d, 0x0a, 0x0d, 0x17, - 0x17, 0x17, 0x0a, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x03, 0x03, 0x03, 0x03, 0x12, 0x03, 0x03, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x15, - 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, - 0x03, 0x03, 0x10, 0x13, 0x10, 0x13, 0x03, 0x03, 0x03, 0x10, 0x13, 0x03, 0x10, 0x13, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x03, 0x03, 0x08, 0x03, 0x10, 0x13, 0x03, 0x03, - 0x10, 0x13, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x16, + 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x5e, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x5e, + 0x17, 0x18, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0xc5, 0xc6, 0xc7, + 0xc8, 0x15, 0x17, 0x18, 0x15, 0x17, 0x18, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5a, 0x5a, 0xc9, 0xc9, + 0x17, 0x18, 0x17, 0x18, 0x15, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x17, 0x18, 0x17, 0x18, 0x5c, + 0x5c, 0x5c, 0x17, 0x18, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x03, 0x03, 0x03, 0x03, 0x12, 0x03, 0x03, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0x5e, 0xca, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0xca, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5a, + 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, + 0x03, 0x03, 0x10, 0x14, 0x10, 0x14, 0x03, 0x03, 0x03, 0x10, 0x14, 0x03, 0x10, 0x14, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x03, 0x03, 0x08, 0x03, 0x10, 0x14, 0x03, 0x03, + 0x10, 0x14, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x5b, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x08, 0x03, 0x03, 0x03, 0x03, 0x08, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x0e, 0x0e, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, - 0x02, 0x03, 0x03, 0x03, 0x0e, 0x15, 0x0f, 0x2a, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, + 0x0e, 0x0e, 0x03, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x02, 0x03, 0x03, 0x03, 0x0e, 0x5a, 0x0f, 0x92, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x0e, 0x0e, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x08, 0x05, 0x06, 0x06, - 0x0e, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, - 0x08, 0x15, 0x15, 0x15, 0x15, 0x15, 0x0e, 0x0e, 0x2a, 0x2a, 0x2a, 0x15, 0x0f, 0x03, 0x0e, 0x0e, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x17, 0x17, 0x0b, 0x0b, 0x15, 0x15, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x15, 0x15, 0x15, 0x0f, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x19, 0x19, 0x28, 0x28, 0x28, 0x28, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x0e, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, + 0x08, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x0e, 0x0e, 0x92, 0x92, 0x92, 0x5a, 0x0f, 0x03, 0x0e, 0x0e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5c, 0x5c, 0x0b, 0x0b, 0x5a, 0x5a, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x5a, 0x5a, 0x5a, 0x0f, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x78, 0x78, 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x0e, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, 0x0e, 0x19, - 0x19, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, 0x0e, 0x0e, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, 0x0e, 0x0e, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x03, 0x03, 0x03, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0f, 0x17, - 0x1a, 0x1a, 0x1a, 0x03, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x03, 0x16, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x15, 0x15, 0x17, 0x17, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x0b, 0x0b, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x15, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x16, 0x2f, 0x2f, 0x0a, 0x0d, 0x0a, 0x0d, 0x0f, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, - 0x18, 0x18, 0x0a, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0a, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x0a, 0x0d, 0x0f, 0x15, 0x15, 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x17, 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x17, 0x17, 0x26, 0x0e, 0x0e, 0x0e, 0x0e, 0x17, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x19, 0x19, 0x04, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x26, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, - 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1b, 0x1b, 0x1b, 0x0f, 0x1b, 0x0f, 0x0f, 0x17, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, - 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x26, 0x26, - 0x26, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x15, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, - 0x26, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x1b, - 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x19, 0x19, 0x19, 0x0f, 0x26, 0x17, 0x26, 0x0f, 0x0f, - 0x17, 0x0f, 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, - 0x0f, 0x17, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x15, 0x1b, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x17, 0x17, 0x26, 0x26, - 0x1b, 0x1b, 0x0f, 0x15, 0x15, 0x26, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, - 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x2f, 0x15, 0x15, 0x15, 0x15, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x15, 0x0b, 0x0b, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x17, 0x26, 0x26, 0x17, 0x26, 0x26, 0x1b, 0x26, 0x17, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x17, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x07, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, - 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x06, 0x05, - 0x1c, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x21, 0x0e, 0x1c, 0x1c, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x05, 0x06, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, 0x0e, 0x78, + 0x78, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, 0x0e, 0x0e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, 0x0e, 0x0e, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5a, 0x03, 0x03, 0x03, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x0f, 0x5c, + 0x79, 0x79, 0x79, 0x03, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x03, 0x5b, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x5a, 0x5a, 0x5c, 0x5c, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, + 0x0b, 0x0b, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x15, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x5a, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x17, 0x18, 0x17, 0x18, 0xcb, 0x17, 0x18, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x5b, 0xcc, 0xcc, 0x17, 0x18, 0xcd, 0x15, 0x0f, + 0x17, 0x18, 0x17, 0x18, 0xce, 0x15, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0xcf, 0xd0, 0xd1, 0xd2, 0xcf, 0x15, + 0xd3, 0xd4, 0xd5, 0xd6, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, 0x17, 0x18, + 0x5e, 0x5e, 0x17, 0x18, 0xd7, 0xd8, 0xd9, 0x17, 0x18, 0x17, 0x18, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x17, 0x18, 0x0f, 0x5a, 0x5a, 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x0e, 0x0e, 0x0e, 0x0e, 0x5c, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x78, 0x78, 0x04, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x89, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, 0x7d, + 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x7d, 0x7d, 0x7d, 0x0f, 0x7d, 0x0f, 0x0f, 0x5c, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, + 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x89, + 0x89, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, + 0x89, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x7d, 0x7d, 0x7d, 0x7d, + 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x78, 0x78, 0x78, 0x0f, 0x89, 0x5c, 0x89, 0x0f, 0x0f, + 0x5c, 0x0f, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, + 0x0f, 0x5c, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x5a, 0x7d, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x5c, 0x5c, 0x89, 0x89, + 0x7d, 0x7d, 0x0f, 0x5a, 0x5a, 0x89, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, + 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x15, 0x15, 0x15, 0xda, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0xcc, 0x5a, 0x5a, 0x5a, 0x5a, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5a, 0x0b, 0x0b, 0x5e, 0x5e, 0x5e, 0x5e, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x5c, 0x89, 0x89, 0x5c, 0x89, 0x89, 0x7d, 0x89, 0x5c, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x82, 0x5c, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x07, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x7f, + 0x82, 0x82, 0x7f, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x06, 0x05, + 0x7f, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x84, 0x0e, 0x7f, 0x7f, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x05, 0x06, 0x03, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x03, 0x08, 0x08, 0x0c, 0x0c, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x03, 0x05, 0x06, 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, - 0x03, 0x03, 0x03, 0x18, 0x03, 0x03, 0x03, 0x03, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, - 0x03, 0x03, 0x07, 0x08, 0x07, 0x07, 0x07, 0x18, 0x03, 0x04, 0x03, 0x03, 0x18, 0x18, 0x18, 0x18, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x11, - 0x18, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x03, 0x05, 0x06, 0x03, 0x07, 0x03, 0x08, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x5e, 0x03, 0x03, 0x03, 0x03, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, + 0x03, 0x03, 0x07, 0x08, 0x07, 0x07, 0x07, 0x5e, 0x03, 0x04, 0x03, 0x03, 0x5e, 0x5e, 0x5e, 0x5e, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x11, + 0x5e, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x03, 0x05, 0x06, 0x03, 0x07, 0x03, 0x08, 0x03, 0x03, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x05, 0x07, 0x06, 0x07, 0x05, 0x06, 0x03, 0x05, 0x06, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x15, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x15, 0x15, - 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, - 0x04, 0x04, 0x07, 0x0b, 0x0e, 0x04, 0x04, 0x18, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x18, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x11, 0x11, 0x11, 0x0e, 0x0e, 0x2e, 0x2e, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x1b, 0x03, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x12, 0x12, 0x0e, 0x19, 0x19, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, - 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x17, 0x18, 0x18, - 0x17, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, - 0x0f, 0x2a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x2a, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x1b, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x1b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1f, 0x1c, 0x1c, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1e, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x24, 0x24, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x1c, 0x1c, 0x1c, 0x03, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x1f, 0x1f, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x17, 0x17, 0x17, 0x1c, 0x17, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x17, 0x17, 0x17, 0x17, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x17, 0x17, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x17, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x34, 0x34, 0x1e, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x1f, 0x1f, 0x1f, 0x1f, 0x17, 0x17, 0x17, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x17, 0x17, 0x1d, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x17, 0x34, 0x34, 0x34, 0x34, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x26, 0x17, 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, - 0x18, 0x18, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, - 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x1b, 0x1b, 0x2b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x2b, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x1b, 0x1b, 0x1b, 0x1b, 0x0f, 0x26, 0x26, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x17, 0x1b, 0x1b, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, - 0x26, 0x0f, 0x0f, 0x0f, 0x0f, 0x1b, 0x1b, 0x1b, 0x1b, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x26, 0x17, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x0f, 0x1b, 0x0f, 0x1b, 0x1b, 0x1b, - 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x17, - 0x17, 0x17, 0x26, 0x26, 0x17, 0x26, 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x17, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, - 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x26, 0x26, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, - 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x17, 0x17, 0x0f, 0x26, 0x26, - 0x17, 0x26, 0x26, 0x26, 0x26, 0x18, 0x18, 0x26, 0x26, 0x18, 0x18, 0x26, 0x26, 0x26, 0x18, 0x18, - 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x26, 0x26, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x26, 0x26, 0x17, 0x17, 0x17, 0x26, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x1b, 0x1b, 0x18, 0x1b, 0x17, 0x0f, - 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x26, 0x26, 0x26, 0x26, 0x17, - 0x17, 0x26, 0x17, 0x17, 0x0f, 0x0f, 0x1b, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, - 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x26, 0x26, 0x26, 0x26, 0x17, 0x17, 0x26, 0x17, - 0x17, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x18, 0x18, - 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x26, 0x17, - 0x17, 0x1b, 0x1b, 0x1b, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x26, 0x17, 0x26, 0x26, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x1b, 0x1b, 0x1b, 0x19, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x17, 0x1b, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x18, 0x26, 0x26, 0x18, 0x18, 0x17, 0x17, 0x26, 0x17, 0x0f, - 0x26, 0x0f, 0x26, 0x17, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x26, 0x26, 0x26, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x17, 0x17, 0x26, 0x26, 0x26, 0x26, - 0x17, 0x0f, 0x1b, 0x0f, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x29, 0x29, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x26, 0x17, 0x17, 0x17, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x0f, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x26, 0x29, - 0x0f, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x1b, 0x1b, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x26, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x26, 0x17, 0x17, 0x26, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x17, 0x18, 0x17, 0x17, 0x18, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x0f, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x26, 0x26, 0x26, 0x26, 0x26, 0x18, - 0x17, 0x17, 0x18, 0x26, 0x26, 0x17, 0x26, 0x17, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x26, 0x26, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x04, 0x04, 0x04, + 0x5a, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5a, 0x5a, + 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, + 0x04, 0x04, 0x07, 0x0b, 0x0e, 0x04, 0x04, 0x5e, 0x0e, 0x07, 0x07, 0x07, 0x07, 0x0e, 0x0e, 0x5e, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0x11, 0x11, 0x11, 0x0e, 0x0e, 0xb6, 0xb6, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x7d, 0x03, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x12, 0x12, 0x0e, 0x78, 0x78, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x5e, 0x5e, + 0x5c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x92, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x92, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x7d, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x7d, 0x92, 0x92, 0x92, 0x92, 0x92, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe0, 0xe0, 0xe0, 0xe0, 0x5e, 0x5e, 0x5e, 0x5e, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x82, 0x7f, 0x7f, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x81, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x87, 0x87, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x7f, 0x7f, 0x7f, 0x03, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x81, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0x82, 0x82, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x5c, 0x5c, 0x5c, 0x7f, 0x5c, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x5c, 0x5c, 0x5c, 0x5c, + 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0x5c, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xe2, 0xe2, 0x81, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x87, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x82, 0x82, 0x82, 0x82, 0x5c, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x5c, 0x5c, 0x80, 0x7f, 0x7f, + 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x5c, 0xe2, 0xe2, 0xe2, 0xe2, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x7f, 0x7f, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x89, 0x5c, 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, + 0x5e, 0x5e, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, + 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x7d, 0x7d, 0xb3, 0x7d, 0x7d, + 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0xb3, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x7d, 0x7d, 0x7d, 0x7d, 0x0f, 0x89, 0x89, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x5c, 0x7d, 0x7d, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, + 0x89, 0x0f, 0x0f, 0x0f, 0x0f, 0x7d, 0x7d, 0x7d, 0x7d, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x89, 0x5c, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x0f, 0x7d, 0x0f, 0x7d, 0x7d, 0x7d, + 0x5e, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x5c, + 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x89, 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5c, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, + 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x89, 0x89, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, + 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5c, 0x5c, 0x0f, 0x89, 0x89, + 0x5c, 0x89, 0x89, 0x89, 0x89, 0x5e, 0x5e, 0x89, 0x89, 0x5e, 0x5e, 0x89, 0x89, 0x89, 0x5e, 0x5e, + 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x89, 0x89, 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x7d, 0x7d, 0x5e, 0x7d, 0x5c, 0x0f, + 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x89, 0x89, 0x89, 0x89, 0x5c, + 0x5c, 0x89, 0x5c, 0x5c, 0x0f, 0x0f, 0x7d, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, + 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x89, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x89, 0x5c, + 0x5c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5e, 0x5e, + 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x89, 0x5c, + 0x5c, 0x7d, 0x7d, 0x7d, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x89, 0x5c, 0x89, 0x89, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x7d, 0x7d, 0x7d, 0x78, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5e, 0x89, 0x89, 0x5e, 0x5e, 0x5c, 0x5c, 0x89, 0x5c, 0x0f, + 0x89, 0x0f, 0x89, 0x5c, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x89, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5c, 0x5c, 0x89, 0x89, 0x89, 0x89, + 0x5c, 0x0f, 0x7d, 0x0f, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x8c, 0x8c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x0f, 0x7d, 0x7d, + 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x89, 0x8c, + 0x0f, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x7d, 0x7d, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x89, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x89, 0x5c, 0x5c, 0x89, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5c, 0x5e, 0x5c, 0x5c, 0x5e, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x0f, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5e, + 0x5c, 0x5c, 0x5e, 0x89, 0x89, 0x5c, 0x89, 0x5c, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x89, 0x89, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x04, 0x04, 0x04, 0x04, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x18, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x19, 0x19, 0x19, 0x19, - 0x15, 0x15, 0x15, 0x15, 0x1b, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x17, - 0x0f, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, - 0x17, 0x17, 0x17, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x03, 0x15, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x26, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18, 0x19, 0x17, 0x17, 0x1b, - 0x11, 0x11, 0x11, 0x11, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x26, 0x26, 0x17, 0x17, 0x17, 0x19, 0x19, 0x19, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x19, 0x19, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x17, 0x17, 0x17, 0x17, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x17, 0x17, 0x17, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x18, 0x0a, 0x0a, - 0x18, 0x18, 0x0a, 0x18, 0x18, 0x0a, 0x0a, 0x18, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x18, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x18, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x18, 0x0a, 0x18, 0x18, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x18, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x37, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x37, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x37, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x37, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x37, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x07, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0a, 0x0d, 0x18, 0x18, 0x09, 0x09, + 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x7d, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x5e, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x78, 0x78, 0x78, 0x78, + 0x5a, 0x5a, 0x5a, 0x5a, 0x7d, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, + 0x0f, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x03, 0x5a, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x89, 0x89, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5e, 0x5e, 0x78, 0x5c, 0x5c, 0x7d, + 0x11, 0x11, 0x11, 0x11, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x89, 0x89, 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x78, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x5c, 0x5c, 0x5c, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x5c, 0x5c, 0x5c, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x5e, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x5e, 0x19, 0x19, + 0x5e, 0x5e, 0x19, 0x5e, 0x5e, 0x19, 0x19, 0x5e, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x5e, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x15, 0x15, 0x15, 0x15, 0x5e, 0x15, 0x5e, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x5e, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x5e, 0x5e, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x5e, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x5e, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x5e, 0x19, 0x5e, 0x5e, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x5e, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x5e, 0x5e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0xe5, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x07, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0xe5, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x07, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0xe5, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x07, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0xe5, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x07, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0xe5, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x07, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x19, 0x15, 0x5e, 0x5e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x19, 0x19, 0x19, 0x19, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x17, 0x19, 0x19, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x18, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x18, 0x18, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x19, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x18, 0x18, 0x18, 0x18, 0x18, 0x04, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x23, 0x1c, 0x1c, 0x1c, 0x1c, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1e, - 0x1c, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x24, 0x34, 0x34, 0x34, - 0x21, 0x34, 0x34, 0x34, 0x34, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x24, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x1c, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1c, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, 0x1c, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, - 0x1c, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, 0x1c, 0x1f, - 0x1c, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1c, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x07, 0x07, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, - 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x5c, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x5c, 0x78, 0x78, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5e, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, 0x5e, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x5e, 0x5e, 0x0f, 0x78, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x5c, 0x5c, 0x5c, 0x5c, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x04, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x86, 0x7f, 0x7f, 0x7f, 0x7f, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x7f, 0x7f, 0x7f, 0x7f, 0x81, 0x81, + 0x7f, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x87, 0xe2, 0xe2, 0xe2, + 0x84, 0xe2, 0xe2, 0xe2, 0xe2, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x87, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x7f, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x7f, 0x82, 0x82, 0x7f, 0x82, 0x7f, 0x7f, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x82, 0x82, 0x82, + 0x7f, 0x82, 0x82, 0x7f, 0x82, 0x7f, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x82, 0x7f, 0x82, + 0x7f, 0x82, 0x82, 0x7f, 0x82, 0x7f, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x7f, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7f, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x07, 0x07, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, + 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0e, 0x0e, 0x0e, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x0e, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, - 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x0e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, + 0x78, 0x78, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, - 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x0e, 0x0e, 0x0e, 0x18, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x2e, 0x2e, - 0x2e, 0x11, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x0e, 0x0e, 0x0e, 0x5e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0xb6, 0xb6, + 0xb6, 0x11, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x2e, 0x2e + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb6, 0xb6 }; // Contains Unicode category & bidi class information - private static ReadOnlySpan CategoriesValues => new byte[56] + private static ReadOnlySpan CategoriesValues => new byte[232] { 0x0e, 0x8e, 0x8b, 0x18, 0x1a, 0x14, 0x15, 0x19, 0x13, 0x08, 0x20, 0x1b, 0x12, 0x21, 0x1c, 0x24, - 0x16, 0x0f, 0x0a, 0x17, 0x22, 0x23, 0x03, 0x05, 0x3d, 0x3c, 0x07, 0x38, 0x5d, 0x53, 0x58, 0x44, - 0x59, 0x5a, 0x4f, 0x43, 0x5c, 0x48, 0x26, 0x28, 0x2a, 0x25, 0x29, 0x2f, 0x8c, 0x8d, 0x1d, 0x3b, - 0x30, 0x31, 0x5b, 0x09, 0x4a, 0x40, 0x41, 0x39 + 0x16, 0x0f, 0x0a, 0x21, 0x17, 0x21, 0x21, 0x20, 0x21, 0x20, 0x20, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x20, 0x20, 0x21, 0x20, 0x21, 0x20, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, + 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x03, 0x05, 0x05, 0x3d, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x20, 0x21, 0x20, 0x20, 0x21, 0x3c, 0x07, 0x20, 0x21, 0x20, 0x38, 0x21, 0x5d, + 0x53, 0x58, 0x44, 0x59, 0x5a, 0x4f, 0x43, 0x5c, 0x48, 0x26, 0x28, 0x2a, 0x25, 0x20, 0x21, 0x20, + 0x20, 0x21, 0x29, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x21, 0x21, 0x21, 0x21, + 0x20, 0x21, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x21, 0x20, 0x22, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x8c, 0x8d, 0x1d, 0x20, 0x20, 0x20, 0x20, 0x21, 0x29, 0x29, 0x3c, 0x3c, + 0x20, 0x20, 0x20, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x20, 0x3b, 0x20, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x30, 0x31, 0x5b, 0x09, + 0x20, 0x21, 0x4a, 0x40, 0x41, 0x39, 0x40, 0x41 + }; + + // Contains simple culture-invariant uppercase mappings + private static ReadOnlySpan UppercaseValues => new byte[464] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xfe, 0xff, 0xb1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x2a, 0x1c, 0x2a, 0x1e, 0x2a, 0x2e, 0xff, + 0x32, 0xff, 0x33, 0xff, 0x36, 0xff, 0x35, 0xff, 0x4f, 0xa5, 0x4b, 0xa5, 0x31, 0xff, 0x28, 0xa5, + 0x44, 0xa5, 0x2f, 0xff, 0x2d, 0xff, 0xf7, 0x29, 0x41, 0xa5, 0xfd, 0x29, 0x2b, 0xff, 0x2a, 0xff, + 0xe7, 0x29, 0x26, 0xff, 0x43, 0xa5, 0x2a, 0xa5, 0xbb, 0xff, 0x27, 0xff, 0xb9, 0xff, 0x25, 0xff, + 0x15, 0xa5, 0x12, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xff, 0xdb, 0xff, 0xe1, 0xff, 0xc0, 0xff, + 0xc1, 0xff, 0x00, 0x00, 0xc2, 0xff, 0xc7, 0xff, 0xd1, 0xff, 0xca, 0xff, 0xf8, 0xff, 0xaa, 0xff, + 0xb0, 0xff, 0x07, 0x00, 0x8c, 0xff, 0x00, 0x00, 0xa0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x92, 0xe7, 0x93, 0xe7, 0x9c, 0xe7, 0x9e, 0xe7, 0x9d, 0xe7, + 0xa4, 0xe7, 0xdb, 0xe7, 0xc2, 0x89, 0x00, 0x00, 0x04, 0x8a, 0xe6, 0x0e, 0x38, 0x8a, 0xc5, 0xff, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x64, 0x00, 0x80, 0x00, 0x70, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xe3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xff, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe6, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0xd5, 0xd8, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xfc, 0x30, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xde, 0xff + }; + + // Contains simple culture-invariant lowercase mappings + private static ReadOnlySpan LowercaseValues => new byte[464] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x87, 0xff, 0x00, 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcd, 0x00, 0x4f, 0x00, + 0xca, 0x00, 0xcb, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xd3, 0x00, 0xd1, 0x00, 0x00, 0x00, 0xd5, 0x00, + 0x00, 0x00, 0xd6, 0x00, 0xda, 0x00, 0xd9, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xc8, 0xff, 0x7e, 0xff, 0x2b, 0x2a, 0x5d, 0xff, 0x28, 0x2a, + 0x00, 0x00, 0x3d, 0xff, 0x45, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, + 0x26, 0x00, 0x25, 0x00, 0x40, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0x00, 0x00, 0xf9, 0xff, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1c, 0x00, 0x00, 0xd0, 0x97, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0xe2, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xb6, 0xff, 0xf7, 0xff, 0x00, 0x00, 0xaa, 0xff, 0x9c, 0xff, + 0x90, 0xff, 0x80, 0xff, 0x82, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0xe2, + 0x41, 0xdf, 0xba, 0xdf, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x09, 0xd6, 0x1a, 0xf1, 0x19, 0xd6, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xd5, 0x03, 0xd6, 0xe1, 0xd5, + 0xe2, 0xd5, 0xc1, 0xd5, 0x00, 0x00, 0xfc, 0x75, 0x00, 0x00, 0xd8, 0x5a, 0x00, 0x00, 0xbc, 0x5a, + 0xb1, 0x5a, 0xb5, 0x5a, 0xbf, 0x5a, 0xee, 0x5a, 0xd6, 0x5a, 0xeb, 0x5a, 0xa0, 0x03, 0xd0, 0xff, + 0xbd, 0x5a, 0xc8, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00 }; [Conditional("DEBUG")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs index b1033b5f2b4..c3d56800290 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs @@ -9,210 +9,6 @@ namespace System.Globalization { public partial class CompareInfo { - internal static unsafe int InvariantIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) - { - Debug.Assert(source != null); - Debug.Assert(value != null); - Debug.Assert(startIndex >= 0 && startIndex < source.Length); - - fixed (char* pSource = source) fixed (char* pValue = value) - { - char* pSrc = &pSource[startIndex]; - int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, fromBeginning: true); - if (index >= 0) - { - return index + startIndex; - } - return -1; - } - } - - internal static unsafe int InvariantIndexOf(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning = true) - { - Debug.Assert(source.Length != 0); - Debug.Assert(value.Length != 0); - - fixed (char* pSource = &MemoryMarshal.GetReference(source)) - fixed (char* pValue = &MemoryMarshal.GetReference(value)) - { - return InvariantFindString(pSource, source.Length, pValue, value.Length, ignoreCase, fromBeginning); - } - } - - internal static unsafe int InvariantLastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) - { - Debug.Assert(!string.IsNullOrEmpty(source)); - Debug.Assert(value != null); - Debug.Assert(startIndex >= 0 && startIndex < source.Length); - - fixed (char* pSource = source) fixed (char* pValue = value) - { - char* pSrc = &pSource[startIndex - count + 1]; - int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, fromBeginning: false); - if (index >= 0) - { - return index + startIndex - count + 1; - } - return -1; - } - } - - private static unsafe int InvariantFindString(char* source, int sourceCount, char* value, int valueCount, bool ignoreCase, bool fromBeginning) - { - int ctrSource = 0; // index value into source - int ctrValue = 0; // index value into value - char sourceChar; // Character for case lookup in source - char valueChar; // Character for case lookup in value - int lastSourceStart; - - Debug.Assert(source != null); - Debug.Assert(value != null); - Debug.Assert(sourceCount >= 0); - Debug.Assert(valueCount >= 0); - - if (valueCount == 0) - { - return fromBeginning ? 0 : sourceCount; - } - - if (sourceCount < valueCount) - { - return -1; - } - - if (fromBeginning) - { - lastSourceStart = sourceCount - valueCount; - if (ignoreCase) - { - char firstValueChar = InvariantCaseFold(value[0]); - for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) - { - sourceChar = InvariantCaseFold(source[ctrSource]); - if (sourceChar != firstValueChar) - { - continue; - } - - for (ctrValue = 1; ctrValue < valueCount; ctrValue++) - { - sourceChar = InvariantCaseFold(source[ctrSource + ctrValue]); - valueChar = InvariantCaseFold(value[ctrValue]); - - if (sourceChar != valueChar) - { - break; - } - } - - if (ctrValue == valueCount) - { - return ctrSource; - } - } - } - else - { - char firstValueChar = value[0]; - for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) - { - sourceChar = source[ctrSource]; - if (sourceChar != firstValueChar) - { - continue; - } - - for (ctrValue = 1; ctrValue < valueCount; ctrValue++) - { - sourceChar = source[ctrSource + ctrValue]; - valueChar = value[ctrValue]; - - if (sourceChar != valueChar) - { - break; - } - } - - if (ctrValue == valueCount) - { - return ctrSource; - } - } - } - } - else - { - lastSourceStart = sourceCount - valueCount; - if (ignoreCase) - { - char firstValueChar = InvariantCaseFold(value[0]); - for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) - { - sourceChar = InvariantCaseFold(source[ctrSource]); - if (sourceChar != firstValueChar) - { - continue; - } - for (ctrValue = 1; ctrValue < valueCount; ctrValue++) - { - sourceChar = InvariantCaseFold(source[ctrSource + ctrValue]); - valueChar = InvariantCaseFold(value[ctrValue]); - - if (sourceChar != valueChar) - { - break; - } - } - - if (ctrValue == valueCount) - { - return ctrSource; - } - } - } - else - { - char firstValueChar = value[0]; - for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) - { - sourceChar = source[ctrSource]; - if (sourceChar != firstValueChar) - { - continue; - } - - for (ctrValue = 1; ctrValue < valueCount; ctrValue++) - { - sourceChar = source[ctrSource + ctrValue]; - valueChar = value[ctrValue]; - - if (sourceChar != valueChar) - { - break; - } - } - - if (ctrValue == valueCount) - { - return ctrSource; - } - } - } - } - - return -1; - } - - private static char InvariantCaseFold(char c) - { - // If we ever make Invariant mode support more than just simple ASCII-range case folding, - // then we should update this method to perform proper case folding instead of an - // uppercase conversion. For now it only understands the ASCII range and reflects all - // non-ASCII values unchanged. - - return (uint)(c - 'a') <= (uint)('z' - 'a') ? (char)(c - 0x20) : c; - } - private SortKey InvariantCreateSortKey(string source, CompareOptions options) { if (source == null) { throw new ArgumentNullException(nameof(source)); } @@ -263,8 +59,23 @@ namespace System.Globalization for (int i = 0; i < source.Length; i++) { + char c = source[i]; + if (char.IsHighSurrogate(c) && i < source.Length - 1) + { + char cl = source[i + 1]; + if (char.IsLowSurrogate(cl)) + { + SurrogateCasing.ToUpper(c, cl, out char hr, out char lr); + BinaryPrimitives.WriteUInt16BigEndian(sortKey, hr); + BinaryPrimitives.WriteUInt16BigEndian(sortKey, lr); + i++; + sortKey = sortKey.Slice(2 * sizeof(ushort)); + continue; + } + } + // convert machine-endian to big-endian - BinaryPrimitives.WriteUInt16BigEndian(sortKey, (ushort)InvariantCaseFold(source[i])); + BinaryPrimitives.WriteUInt16BigEndian(sortKey, (ushort)InvariantModeCasing.ToUpper(c)); sortKey = sortKey.Slice(sizeof(ushort)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index 3f35e96a69e..b8e7d60a24e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -300,12 +300,10 @@ namespace System.Globalization return retVal; } - internal int CompareOptionIgnoreCase(ReadOnlySpan string1, ReadOnlySpan string2) - { - return GlobalizationMode.Invariant ? - Ordinal.CompareIgnoreCaseInvariantMode(ref MemoryMarshal.GetReference(string1), string1.Length, ref MemoryMarshal.GetReference(string2), string2.Length) : + internal int CompareOptionIgnoreCase(ReadOnlySpan string1, ReadOnlySpan string2) => + GlobalizationMode.Invariant ? + InvariantModeCasing.CompareStringIgnoreCase(ref MemoryMarshal.GetReference(string1), string1.Length, ref MemoryMarshal.GetReference(string2), string2.Length) : CompareStringCore(string1, string2, CompareOptions.IgnoreCase); - } /// /// Compares the specified regions of the two strings with the given diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs new file mode 100644 index 00000000000..e4b09e9855d --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs @@ -0,0 +1,346 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System.Globalization +{ + internal static class InvariantModeCasing + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static char ToLower(char c) => CharUnicodeInfo.ToLower(c); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static char ToUpper(char c) => CharUnicodeInfo.ToUpper(c); + + internal static string ToLower(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + ReadOnlySpan source = s; + + int i = 0; + while (i < s.Length) + { + if (char.IsHighSurrogate(source[i]) && i < s.Length - 1 && char.IsLowSurrogate(source[i + 1])) + { + SurrogateCasing.ToLower(source[i], source[i + 1], out char h, out char l); + if (source[i] != h || source[i + 1] != l) + { + break; + } + + i += 2; + continue; + } + + if (ToLower(source[i]) != source[i]) + { + break; + } + + i++; + } + + if (i >= s.Length) + { + return s; + } + + return string.Create(s.Length, (s, i), static (destination, state) => + { + ReadOnlySpan src = state.s; + src.Slice(0, state.i).CopyTo(destination); + InvariantModeCasing.ToLower(src.Slice(state.i), destination.Slice(state.i)); + }); + + } + + internal static string ToUpper(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + ReadOnlySpan source = s; + + int i = 0; + while (i < s.Length) + { + if (char.IsHighSurrogate(source[i]) && i < s.Length - 1 && char.IsLowSurrogate(source[i + 1])) + { + SurrogateCasing.ToUpper(source[i], source[i + 1], out char h, out char l); + if (source[i] != h || source[i + 1] != l) + { + break; + } + + i += 2; + continue; + } + + if (ToUpper(source[i]) != source[i]) + { + break; + } + + i++; + } + + if (i >= s.Length) + { + return s; + } + + return string.Create(s.Length, (s, i), static (destination, state) => + { + ReadOnlySpan src = state.s; + src.Slice(0, state.i).CopyTo(destination); + InvariantModeCasing.ToUpper(src.Slice(state.i), destination.Slice(state.i)); + }); + } + + internal static void ToUpper(ReadOnlySpan source, Span destination) + { + Debug.Assert(GlobalizationMode.Invariant); + Debug.Assert(source.Length <= destination.Length); + + for (int i = 0; i < source.Length; i++) + { + char c = source[i]; + if (char.IsHighSurrogate(c) && i < source.Length - 1) + { + char cl = source[i + 1]; + if (char.IsLowSurrogate(cl)) + { + // well formed surrogates + SurrogateCasing.ToUpper(c, cl, out char h, out char l); + destination[i] = h; + destination[i+1] = l; + i++; // skip the low surrogate + continue; + } + } + + destination[i] = ToUpper(c); + } + } + + internal static void ToLower(ReadOnlySpan source, Span destination) + { + Debug.Assert(GlobalizationMode.Invariant); + Debug.Assert(source.Length <= destination.Length); + + for (int i = 0; i < source.Length; i++) + { + char c = source[i]; + if (char.IsHighSurrogate(c) && i < source.Length - 1 ) + { + char cl = source[i + 1]; + if (char.IsLowSurrogate(cl)) + { + // well formed surrogates + SurrogateCasing.ToLower(c, cl, out char h, out char l); + destination[i] = h; + destination[i+1] = l; + i++; // skip the low surrogate + continue; + } + } + + destination[i] = ToLower(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (uint, int) GetScalar(ref char charA, int index, int length) + { + if (!char.IsHighSurrogate(charA) || index >= length - 1) + { + return ((uint)charA, 1); + } + + ref char charB = ref Unsafe.Add(ref charA, 1); + if (!char.IsLowSurrogate(charB)) + { + return ((uint)charA, 1); + } + + return (UnicodeUtility.GetScalarFromUtf16SurrogatePair(charA, charB), 2); + } + + internal static int CompareStringIgnoreCase(ref char strA, int lengthA, ref char strB, int lengthB) + { + Debug.Assert(GlobalizationMode.Invariant); + + int length = Math.Min(lengthA, lengthB); + + ref char charA = ref strA; + ref char charB = ref strB; + + int index = 0; + + while (index < length) + { + (uint codePointA, int codePointLengthA) = GetScalar(ref charA, index, lengthA); + (uint codePointB, int codePointLengthB) = GetScalar(ref charB, index, lengthB); + + if (codePointA == codePointB) + { + Debug.Assert(codePointLengthA == codePointLengthB); + index += codePointLengthA; + charA = ref Unsafe.Add(ref charA, codePointLengthA); + charB = ref Unsafe.Add(ref charB, codePointLengthB); + continue; + } + + uint aUpper = CharUnicodeInfo.ToUpper(codePointA); + uint bUpper = CharUnicodeInfo.ToUpper(codePointB); + + if (aUpper == bUpper) + { + Debug.Assert(codePointLengthA == codePointLengthB); + index += codePointLengthA; + charA = ref Unsafe.Add(ref charA, codePointLengthA); + charB = ref Unsafe.Add(ref charB, codePointLengthB); + continue; + } + + return (int)codePointA - (int)codePointB; + } + + return lengthA - lengthB; + } + + internal static unsafe int IndexOfIgnoreCase(ReadOnlySpan source, ReadOnlySpan value) + { + Debug.Assert(value.Length > 0); + Debug.Assert(value.Length <= source.Length); + Debug.Assert(GlobalizationMode.Invariant); + + fixed (char* pSource = &MemoryMarshal.GetReference(source)) + fixed (char* pValue = &MemoryMarshal.GetReference(value)) + { + char* pSourceLimit = pSource + (source.Length - value.Length); + char* pValueLimit = pValue + value.Length - 1; + char* pCurrentSource = pSource; + + while (pCurrentSource <= pSourceLimit) + { + char *pVal = pValue; + char *pSrc = pCurrentSource; + + while (pVal <= pValueLimit) + { + if (!char.IsHighSurrogate(*pVal) || pVal == pValueLimit) + { + if (*pVal != *pSrc && ToUpper(*pVal) != ToUpper(*pSrc)) + break; // no match + + pVal++; + pSrc++; + continue; + } + + if (char.IsHighSurrogate(*pSrc) && char.IsLowSurrogate(*(pSrc + 1)) && char.IsLowSurrogate(*(pVal + 1))) + { + // Well formed surrogates + // both the source and the Value have well-formed surrogates. + if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) + break; // no match + + pSrc += 2; + pVal += 2; + continue; + } + + if (*pVal != *pSrc) + break; // no match + + pSrc++; + pVal++; + } + + if (pVal > pValueLimit) + { + // Found match. + return (int) (pCurrentSource - pSource); + } + + pCurrentSource++; + } + + return -1; + } + } + + internal static unsafe int LastIndexOfIgnoreCase(ReadOnlySpan source, ReadOnlySpan value) + { + Debug.Assert(value.Length > 0); + Debug.Assert(value.Length <= source.Length); + Debug.Assert(GlobalizationMode.Invariant); + + fixed (char* pSource = &MemoryMarshal.GetReference(source)) + fixed (char* pValue = &MemoryMarshal.GetReference(value)) + { + char* pValueLimit = pValue + value.Length - 1; + char* pCurrentSource = pSource + (source.Length - value.Length); + + while (pCurrentSource >= pSource) + { + char *pVal = pValue; + char *pSrc = pCurrentSource; + + while (pVal <= pValueLimit) + { + if (!char.IsHighSurrogate(*pVal) || pVal == pValueLimit) + { + if (*pVal != *pSrc && ToUpper(*pVal) != ToUpper(*pSrc)) + break; // no match + + pVal++; + pSrc++; + continue; + } + + if (char.IsHighSurrogate(*pSrc) && char.IsLowSurrogate(*(pSrc + 1)) && char.IsLowSurrogate(*(pVal + 1))) + { + // Well formed surrogates + // both the source and the Value have well-formed surrogates. + if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) + break; // no match + + pSrc += 2; + pVal += 2; + continue; + } + + if (*pVal != *pSrc) + break; // no match + + pSrc++; + pVal++; + } + + if (pVal > pValueLimit) + { + // Found match. + return (int) (pCurrentSource - pSource); + } + + pCurrentSource--; + } + + return -1; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index 50bff4353e1..7c09e692534 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -19,8 +19,7 @@ namespace System.Globalization ref char charA = ref strA; ref char charB = ref strB; - // in InvariantMode we support all range and not only the ascii characters. - char maxChar = (GlobalizationMode.Invariant ? (char)0xFFFF : (char)0x7F); + char maxChar = (char)0x7F; while (length != 0 && charA <= maxChar && charB <= maxChar) { @@ -53,7 +52,7 @@ namespace System.Globalization } } - if (length == 0 || GlobalizationMode.Invariant) + if (length == 0) { return lengthA - lengthB; } @@ -67,7 +66,7 @@ namespace System.Globalization { if (GlobalizationMode.Invariant) { - return CompareIgnoreCaseInvariantMode(ref strA, lengthA, ref strB, lengthB); + return InvariantModeCasing.CompareStringIgnoreCase(ref strA, lengthA, ref strB, lengthB); } if (GlobalizationMode.UseNls) @@ -181,41 +180,6 @@ namespace System.Globalization return CompareStringIgnoreCase(ref Unsafe.AddByteOffset(ref charA, byteOffset), length, ref Unsafe.AddByteOffset(ref charB, byteOffset), length) == 0; } - internal static int CompareIgnoreCaseInvariantMode(ref char strA, int lengthA, ref char strB, int lengthB) - { - Debug.Assert(GlobalizationMode.Invariant); - int length = Math.Min(lengthA, lengthB); - - ref char charA = ref strA; - ref char charB = ref strB; - - while (length != 0) - { - if (charA == charB) - { - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); - continue; - } - - char aUpper = OrdinalCasing.ToUpperInvariantMode(charA); - char bUpper = OrdinalCasing.ToUpperInvariantMode(charB); - - if (aUpper == bUpper) - { - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); - continue; - } - - return aUpper - bUpper; - } - - return lengthA - lengthB; - } - internal static unsafe int IndexOf(string source, string value, int startIndex, int count, bool ignoreCase) { if (source == null) @@ -266,7 +230,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - return CompareInfo.InvariantIndexOf(source, value, ignoreCase: true, fromBeginning: true); + return InvariantModeCasing.IndexOfIgnoreCase(source, value); } if (GlobalizationMode.UseNls) @@ -277,6 +241,13 @@ namespace System.Globalization return OrdinalCasing.IndexOf(source, value); } + internal static int LastIndexOf(string source, string value, int startIndex, int count) + { + int result = source.AsSpan(startIndex, count).LastIndexOf(value); + if (result >= 0) { result += startIndex; } // if match found, adjust 'result' by the actual start position + return result; + } + internal static unsafe int LastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) { if (source == null) @@ -301,7 +272,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - return CompareInfo.InvariantLastIndexOf(source, value, startIndex, count, ignoreCase); + return ignoreCase ? InvariantModeCasing.LastIndexOfIgnoreCase(source.AsSpan().Slice(startIndex, count), value) : LastIndexOf(source, value, startIndex, count); } if (GlobalizationMode.UseNls) @@ -311,25 +282,7 @@ namespace System.Globalization if (!ignoreCase) { - // startIndex is the index into source where we start search backwards from. - // leftStartIndex is the index into source of the start of the string that is - // count characters away from startIndex. - int leftStartIndex = startIndex - count + 1; - - for (int i = startIndex - value.Length + 1; i >= leftStartIndex; i--) - { - int valueIndex, sourceIndex; - - for (valueIndex = 0, sourceIndex = i; - valueIndex < value.Length && source[sourceIndex] == value[valueIndex]; - valueIndex++, sourceIndex++) ; - - if (valueIndex == value.Length) { - return i; - } - } - - return -1; + LastIndexOf(source, value, startIndex, count); } if (!source.TryGetSpan(startIndex, count, out ReadOnlySpan sourceSpan)) @@ -374,7 +327,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - return CompareInfo.InvariantIndexOf(source, value, ignoreCase: true, fromBeginning: false); + return InvariantModeCasing.LastIndexOfIgnoreCase(source, value); } if (GlobalizationMode.UseNls) @@ -396,7 +349,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - OrdinalCasing.ToUpperInvariantMode(source, destination); + InvariantModeCasing.ToUpper(source, destination); return source.Length; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs index 71da4dcfeba..8120c0e349c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs @@ -170,100 +170,22 @@ namespace System.Globalization continue; } - if (char.IsHighSurrogate(c) && i < source.Length - 1 && char.IsLowSurrogate(source[i + 1])) + if (char.IsHighSurrogate(c) && i < source.Length - 1) { - // well formed surrogates - ToUpperSurrogate(c, source[i + 1], out ushort h, out ushort l); - destination[i] = (char)h; - destination[i+1] = (char)l; - i++; // skip the low surrogate - continue; + char cl = source[i + 1]; + if (char.IsLowSurrogate(cl)) + { + // well formed surrogates + SurrogateCasing.ToUpper(c, cl, out destination[i], out destination[i+1]); + i++; // skip the low surrogate + continue; + } } destination[i] = ToUpper(c); } } - // For simplicity ToUpper doesn't expect the Surrogate be formed with - // S = ((H - 0xD800) * 0x400) + (L - 0xDC00) + 0x10000 - // Instead it expect to have it in the form (H << 16) | L - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void ToUpperSurrogate(ushort h, ushort l, out ushort hr, out ushort lr) - { - switch (h) - { - case 0xD801: - // DESERET SMALL LETTERS 10428 ~ 1044F - if ((uint) (l - 0xdc28) <= (uint) (0xdc4f - 0xdc28)) - { - hr = h; - lr = (ushort) ((l - 0xdc28) + 0xdc00); - return; - } - - // OSAGE SMALL LETTERS 104D8 ~ 104FB - if ((uint) (l - 0xdcd8) <= (uint) (0xdcfb - 0xdcd8)) - { - hr = h; - lr = (ushort) ((l - 0xdcd8) + 0xdcb0); - return; - } - break; - - case 0xd803: - // OLD HUNGARIAN SMALL LETTERS 10CC0 ~ 10CF2 - if ((uint) (l - 0xdcc0) <= (uint) (0xdcf2 - 0xdcc0)) - { - hr = h; - lr = (ushort) ((l - 0xdcc0) + 0xdc80); - return; - } - break; - - case 0xd806: - // WARANG CITI SMALL LETTERS 118C0 ~ 118DF - if ((uint) (l - 0xdcc0) <= (uint) (0xdcdf - 0xdcc0)) - { - hr = h; - lr = (ushort) ((l - 0xdcc0) + 0xdca0); - return; - } - break; - - case 0xd81b: - // MEDEFAIDRIN SMALL LETTERS 16E60 ~ 16E7F - if ((uint) (l - 0xde60) <= (uint) (0xde7f - 0xde60)) - { - hr = h; - lr = (ushort) ((l - 0xde60) + 0xde40); - return; - } - break; - - case 0xd83a: - // ADLAM SMALL LETTERS 1E922 ~ 1E943 - if ((uint) (l - 0xdd22) <= (uint) (0xdd43 - 0xdd22)) - { - hr = h; - lr = (ushort) ((l - 0xdd22) + 0xdd00); - return; - } - break; - } - - hr = h; - lr = l; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool EqualSurrogate(char h1, char l1, char h2, char l2) - { - ToUpperSurrogate(h1, l1, out ushort hr1, out ushort lr1); - ToUpperSurrogate(h2, l2, out ushort hr2, out ushort lr2); - - return hr1 == hr2 && lr1 == lr2; - } - internal static int CompareStringIgnoreCase(ref char strA, int lengthA, ref char strB, int lengthB) { Debug.Assert(!GlobalizationMode.Invariant); @@ -324,8 +246,8 @@ namespace System.Globalization } // we come here only if we have valid full surrogates - ToUpperSurrogate(a, charA, out ushort h1, out ushort l1); - ToUpperSurrogate(b, charB, out ushort h2, out ushort l2); + SurrogateCasing.ToUpper(a, charA, out char h1, out char l1); + SurrogateCasing.ToUpper(b, charB, out char h2, out char l2); if (h1 != h2) { @@ -381,7 +303,7 @@ namespace System.Globalization { // Well formed surrogates // both the source and the Value have well-formed surrogates. - if (!EqualSurrogate(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) + if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) break; // no match pSrc += 2; @@ -444,7 +366,7 @@ namespace System.Globalization { // Well formed surrogates // both the source and the Value have well-formed surrogates. - if (!EqualSurrogate(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) + if (!SurrogateCasing.Equal(*pSrc, *(pSrc + 1), *pVal, *(pVal + 1))) break; // no match pSrc += 2; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/SurrogateCasing.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/SurrogateCasing.cs new file mode 100644 index 00000000000..70b5e47be17 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/SurrogateCasing.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Globalization +{ + internal static class SurrogateCasing + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToUpper(char h, char l, out char hr, out char lr) + { + Debug.Assert(char.IsHighSurrogate(h)); + Debug.Assert(char.IsLowSurrogate(l)); + + UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar(CharUnicodeInfo.ToUpper(UnicodeUtility.GetScalarFromUtf16SurrogatePair(h, l)), out hr, out lr); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToLower(char h, char l, out char hr, out char lr) + { + Debug.Assert(char.IsHighSurrogate(h)); + Debug.Assert(char.IsLowSurrogate(l)); + + UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar(CharUnicodeInfo.ToLower(UnicodeUtility.GetScalarFromUtf16SurrogatePair(h, l)), out hr, out lr); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool Equal(char h1, char l1, char h2, char l2) + { + ToUpper(h1, l1, out char hr1, out char lr1); + ToUpper(h2, l2, out char hr2, out char lr2); + + return hr1 == hr2 && lr1 == lr2; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs index 38211f6158e..4679beebb4a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs @@ -144,7 +144,12 @@ namespace System.Globalization /// public char ToLower(char c) { - if (GlobalizationMode.Invariant || (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant)) + if (GlobalizationMode.Invariant) + { + return InvariantModeCasing.ToLower(c); + } + + if (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant) { return ToLowerAsciiInvariant(c); } @@ -154,7 +159,12 @@ namespace System.Globalization internal static char ToLowerInvariant(char c) { - if (GlobalizationMode.Invariant || UnicodeUtility.IsAsciiCodePoint(c)) + if (GlobalizationMode.Invariant) + { + return InvariantModeCasing.ToLower(c); + } + + if (UnicodeUtility.IsAsciiCodePoint(c)) { return ToLowerAsciiInvariant(c); } @@ -171,7 +181,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - return ToLowerAsciiInvariant(str); + return InvariantModeCasing.ToLower(str); } return ChangeCaseCommon(str); @@ -548,7 +558,12 @@ namespace System.Globalization /// public char ToUpper(char c) { - if (GlobalizationMode.Invariant || (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant)) + if (GlobalizationMode.Invariant) + { + return InvariantModeCasing.ToUpper(c); + } + + if (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant) { return ToUpperAsciiInvariant(c); } @@ -558,7 +573,12 @@ namespace System.Globalization internal static char ToUpperInvariant(char c) { - if (GlobalizationMode.Invariant || UnicodeUtility.IsAsciiCodePoint(c)) + if (GlobalizationMode.Invariant) + { + return InvariantModeCasing.ToUpper(c); + } + + if (UnicodeUtility.IsAsciiCodePoint(c)) { return ToUpperAsciiInvariant(c); } @@ -575,7 +595,7 @@ namespace System.Globalization if (GlobalizationMode.Invariant) { - return ToUpperAsciiInvariant(str); + return InvariantModeCasing.ToUpper(str); } return ChangeCaseCommon(str); @@ -789,7 +809,9 @@ namespace System.Globalization ReadOnlySpan src = input.AsSpan(inputIndex, 2); if (GlobalizationMode.Invariant) { - result.Append(src); // surrogate pair in invariant mode, so changing case is a nop + SurrogateCasing.ToUpper(src[0], src[1], out char h, out char l); + result.Append(h); + result.Append(l); } else { @@ -825,7 +847,7 @@ namespace System.Globalization result.Append((char)0x01F2); break; default: - result.Append(ToUpper(input[inputIndex])); + result.Append(GlobalizationMode.Invariant ? InvariantModeCasing.ToUpper(input[inputIndex]) : ToUpper(input[inputIndex])); break; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs index 9fd5bed5d6a..ccf8f509a99 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs @@ -206,7 +206,7 @@ namespace System return -1; if (GlobalizationMode.Invariant) - TextInfo.ToLowerAsciiInvariant(source, destination); + InvariantModeCasing.ToLower(source, destination); else culture.TextInfo.ChangeCaseToLower(source, destination); return source.Length; @@ -230,7 +230,7 @@ namespace System return -1; if (GlobalizationMode.Invariant) - TextInfo.ToLowerAsciiInvariant(source, destination); + InvariantModeCasing.ToLower(source, destination); else TextInfo.Invariant.ChangeCaseToLower(source, destination); return source.Length; @@ -258,7 +258,7 @@ namespace System return -1; if (GlobalizationMode.Invariant) - TextInfo.ToUpperAsciiInvariant(source, destination); + InvariantModeCasing.ToUpper(source, destination); else culture.TextInfo.ChangeCaseToUpper(source, destination); return source.Length; @@ -282,7 +282,7 @@ namespace System return -1; if (GlobalizationMode.Invariant) - TextInfo.ToUpperAsciiInvariant(source, destination); + InvariantModeCasing.ToUpper(source, destination); else TextInfo.Invariant.ChangeCaseToUpper(source, destination); return source.Length; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs index 3ef7028bacb..ee364f6ac9e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs @@ -1402,9 +1402,7 @@ namespace System.Text if (GlobalizationMode.Invariant) { - // If the value isn't ASCII and if the globalization tables aren't available, - // case changing has no effect. - return value; + return UnsafeCreate(CharUnicodeInfo.ToLower(value._value)); } // Non-ASCII data requires going through the case folding tables. @@ -1453,9 +1451,7 @@ namespace System.Text if (GlobalizationMode.Invariant) { - // If the value isn't ASCII and if the globalization tables aren't available, - // case changing has no effect. - return value; + return UnsafeCreate(CharUnicodeInfo.ToUpper(value._value)); } // Non-ASCII data requires going through the case folding tables. From 0dcf196495fe80822942e45c25fd9eeb3126a432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Wed, 14 Jul 2021 23:07:12 -0700 Subject: [PATCH 582/926] Remove default value from param in ResolveLinkTarget(bool returnFinalTarget) missed in the main PR (#55670) --- .../System.Private.CoreLib/src/System/IO/Directory.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/IO/File.cs | 2 +- src/libraries/System.Runtime/ref/System.Runtime.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index f6bc4f42b06..ac61bac5226 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -352,7 +352,7 @@ namespace System.IO /// -or- /// Too many levels of symbolic links. /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. - public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget) { FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index d4aa96ace0e..668e78ccd25 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1036,7 +1036,7 @@ namespace System.IO /// -or- /// Too many levels of symbolic links. /// When is , the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows. - public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget) { FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: false); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index c18894bd257..9e1a56a42a6 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10268,7 +10268,7 @@ namespace System.IO public static string[] GetLogicalDrives() { throw null; } public static System.IO.DirectoryInfo? GetParent(string path) { throw null; } public static void Move(string sourceDirName, string destDirName) { } - public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget) { throw null; } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } public static void SetCurrentDirectory(string path) { } @@ -10423,7 +10423,7 @@ namespace System.IO public static System.Collections.Generic.IEnumerable ReadLines(string path, System.Text.Encoding encoding) { throw null; } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName) { } public static void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) { } - public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) { throw null; } + public static System.IO.FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget) { throw null; } public static void SetAttributes(string path, System.IO.FileAttributes fileAttributes) { } public static void SetCreationTime(string path, System.DateTime creationTime) { } public static void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc) { } From 0663b300ceb8670c78e05d2f1122512c1c987b35 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Wed, 14 Jul 2021 23:59:35 -0700 Subject: [PATCH 583/926] Avoid nullref while trying to throw a more helpful exception. (#55316) --- .../src/Resources/Strings.resx | 3 +++ .../Runtime/Serialization/XmlObjectSerializerReadContext.cs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx index 2af9be2de62..b1fd0673389 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx +++ b/src/libraries/System.Private.DataContractSerialization/src/Resources/Strings.resx @@ -1167,4 +1167,7 @@ Collection type '{0}' cannot be deserialized since it + + Unknown Type for null value + \ No newline at end of file diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs index 4ba8da9b6cc..0fa085b0f75 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlObjectSerializerReadContext.cs @@ -361,9 +361,9 @@ namespace System.Runtime.Serialization // throw in such cases to allow us add fix-up support in the future if we need to. if (DeserializedObjects.IsObjectReferenced(id)) { - // https://github.com/dotnet/runtime/issues/41465 - oldObj or newObj may be null below - suppress compiler error by asserting non-null - Debug.Assert(oldObj != null && newObj != null); - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.FactoryObjectContainsSelfReference, DataContract.GetClrTypeFullName(oldObj.GetType()), DataContract.GetClrTypeFullName(newObj.GetType()), id))); + string oldType = (oldObj != null) ? DataContract.GetClrTypeFullName(oldObj.GetType()) : SR.UnknownNullType; + string newType = (newObj != null) ? DataContract.GetClrTypeFullName(newObj.GetType()) : SR.UnknownNullType; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.FactoryObjectContainsSelfReference, oldType, newType, id))); } DeserializedObjects.Remove(id); DeserializedObjects.Add(id, newObj); From 90b8ddd7a6b28e57aebed7d478547ce07b9b60a6 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 15 Jul 2021 02:01:47 -0700 Subject: [PATCH 584/926] Enable StructEnreg by default on all platforms. (#55558) * enable for arm32. fix arm32 Fix arm/arm64. now we can have contained lclRead for other platforms, not only xarch. * enable x64 unix. * Fix and enable arm64. * fix bad merge and arm32 failures. --- src/coreclr/jit/codegen.h | 2 - src/coreclr/jit/codegenarm.cpp | 16 ++++-- src/coreclr/jit/codegenarm64.cpp | 21 ++++---- src/coreclr/jit/codegenarmarch.cpp | 52 ------------------ src/coreclr/jit/codegencommon.cpp | 85 ++++++++++++++++++++++++++---- src/coreclr/jit/codegenlinear.cpp | 2 +- src/coreclr/jit/codegenxarch.cpp | 58 -------------------- src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/jitconfigvalues.h | 5 -- src/coreclr/jit/lclvars.cpp | 27 ++++++++-- src/coreclr/jit/lowerarmarch.cpp | 6 +++ src/coreclr/jit/lsra.cpp | 6 +-- src/coreclr/jit/lsrabuild.cpp | 37 ++++++++++++- 13 files changed, 167 insertions(+), 152 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 626cb3e5b7b..65cf32b0202 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -866,10 +866,8 @@ protected: // Generate code for a GT_BITCAST that is not contained. void genCodeForBitCast(GenTreeOp* treeNode); -#if defined(TARGET_XARCH) // Generate the instruction to move a value between register files void genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg); -#endif // TARGET_XARCH struct GenIntCastDesc { diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 5993f7015ec..49fc8c9c303 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1044,15 +1044,21 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree) // void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree) { - GenTree* data = tree->gtOp1; - + GenTree* data = tree->gtOp1; + GenTree* actualData = data->gtSkipReloadOrCopy(); + unsigned regCount = 1; // var = call, where call returns a multi-reg return value // case is handled separately. - if (data->gtSkipReloadOrCopy()->IsMultiRegNode()) + if (actualData->IsMultiRegNode()) { - genMultiRegStoreToLocal(tree); + regCount = actualData->IsMultiRegLclVar() ? actualData->AsLclVar()->GetFieldCount(compiler) + : actualData->GetMultiRegCount(); + if (regCount > 1) + { + genMultiRegStoreToLocal(tree); + } } - else + if (regCount == 1) { unsigned varNum = tree->GetLclNum(); assert(varNum < compiler->lvaCount); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 694cb55f4d2..b0dd9ad60a8 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4378,9 +4378,11 @@ void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode) { assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicUpperSave); - GenTree* op1 = simdNode->gtGetOp1(); - assert(op1->IsLocal()); - assert(emitTypeSize(op1->TypeGet()) == 16); + GenTree* op1 = simdNode->gtGetOp1(); + GenTreeLclVar* lclNode = op1->AsLclVar(); + LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode); + assert(emitTypeSize(varDsc->GetRegisterType(lclNode)) == 16); + regNumber targetReg = simdNode->GetRegNum(); regNumber op1Reg = genConsumeReg(op1); assert(op1Reg != REG_NA); @@ -4391,8 +4393,7 @@ void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode) { // This is not a normal spill; we'll spill it to the lclVar location. // The localVar must have a stack home. - unsigned varNum = op1->AsLclVarCommon()->GetLclNum(); - LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); + unsigned varNum = lclNode->GetLclNum(); assert(varDsc->lvOnFrame); // We want to store this to the upper 8 bytes of this localVar's home. int offset = 8; @@ -4429,16 +4430,18 @@ void CodeGen::genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode) GenTree* op1 = simdNode->gtGetOp1(); assert(op1->IsLocal()); - assert(emitTypeSize(op1->TypeGet()) == 16); + GenTreeLclVar* lclNode = op1->AsLclVar(); + LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode); + assert(emitTypeSize(varDsc->GetRegisterType(lclNode)) == 16); + regNumber srcReg = simdNode->GetRegNum(); - regNumber lclVarReg = genConsumeReg(op1); - unsigned varNum = op1->AsLclVarCommon()->GetLclNum(); + regNumber lclVarReg = genConsumeReg(lclNode); + unsigned varNum = lclNode->GetLclNum(); assert(lclVarReg != REG_NA); assert(srcReg != REG_NA); if (simdNode->gtFlags & GTF_SPILLED) { // The localVar must have a stack home. - LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); assert(varDsc->lvOnFrame); // We will load this from the upper 8 bytes of this localVar's home. int offset = 8; diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 71f775d70d8..292df210aab 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -1125,58 +1125,6 @@ void CodeGen::genPutArgReg(GenTreeOp* tree) genProduceReg(tree); } -//---------------------------------------------------------------------- -// genCodeForBitCast - Generate code for a GT_BITCAST that is not contained -// -// Arguments -// treeNode - the GT_BITCAST for which we're generating code -// -void CodeGen::genCodeForBitCast(GenTreeOp* treeNode) -{ - regNumber targetReg = treeNode->GetRegNum(); - var_types targetType = treeNode->TypeGet(); - GenTree* op1 = treeNode->gtGetOp1(); - genConsumeRegs(op1); - if (op1->isContained()) - { - assert(op1->IsLocal() || op1->isIndir()); - op1->gtType = treeNode->TypeGet(); - op1->SetRegNum(targetReg); - op1->ClearContained(); - JITDUMP("Changing type of BITCAST source to load directly."); - genCodeForTreeNode(op1); - } - else if (varTypeUsesFloatReg(treeNode) != varTypeUsesFloatReg(op1)) - { - regNumber srcReg = op1->GetRegNum(); - assert(genTypeSize(op1->TypeGet()) == genTypeSize(targetType)); -#ifdef TARGET_ARM - if (genTypeSize(targetType) == 8) - { - // Converting between long and double on ARM is a special case. - if (targetType == TYP_LONG) - { - regNumber otherReg = treeNode->AsMultiRegOp()->gtOtherReg; - assert(otherReg != REG_NA); - inst_RV_RV_RV(INS_vmov_d2i, targetReg, otherReg, srcReg, EA_8BYTE); - } - else - { - NYI_ARM("Converting from long to double"); - } - } - else -#endif // TARGET_ARM - { - inst_Mov(targetType, targetReg, srcReg, /* canSkip */ false); - } - } - else - { - inst_Mov(targetType, targetReg, genConsumeReg(op1), /* canSkip */ false); - } -} - #if FEATURE_ARG_SPLIT //--------------------------------------------------------------------- // genPutArgSplit - generate code for a GT_PUTARG_SPLIT node diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index d2e08159ec1..73e6f196cae 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -3783,7 +3783,8 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere varNum = regArgTab[argNum].varNum; noway_assert(varNum < compiler->lvaCount); - varDsc = compiler->lvaTable + varNum; + varDsc = compiler->lvaTable + varNum; + const var_types varRegType = varDsc->GetRegisterType(); noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg); /* cannot possibly have stack arguments */ @@ -3827,7 +3828,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere assert(argNum > 0); assert(regArgTab[argNum - 1].slot == 1); assert(regArgTab[argNum - 1].varNum == varNum); - assert((varDsc->lvType == TYP_SIMD12) || (varDsc->lvType == TYP_SIMD16)); + assert((varRegType == TYP_SIMD12) || (varRegType == TYP_SIMD16)); regArgMaskLive &= ~genRegMask(regNum); regArgTab[argNum].circular = false; change = true; @@ -4338,9 +4339,10 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere varNum = regArgTab[argNum].varNum; noway_assert(varNum < compiler->lvaCount); - varDsc = compiler->lvaTable + varNum; - var_types regType = regArgTab[argNum].getRegType(compiler); - regNumber regNum = genMapRegArgNumToRegNum(argNum, regType); + varDsc = compiler->lvaTable + varNum; + const var_types regType = regArgTab[argNum].getRegType(compiler); + const regNumber regNum = genMapRegArgNumToRegNum(argNum, regType); + const var_types varRegType = varDsc->GetRegisterType(); #if defined(UNIX_AMD64_ABI) if (regType == TYP_UNDEF) @@ -4439,7 +4441,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere assert(regArgTab[argNum].slot == 2); assert(argNum > 0); assert(regArgTab[argNum - 1].slot == 1); - assert((varDsc->lvType == TYP_SIMD12) || (varDsc->lvType == TYP_SIMD16)); + assert((varRegType == TYP_SIMD12) || (varRegType == TYP_SIMD16)); destRegNum = varDsc->GetRegNum(); noway_assert(regNum != destRegNum); continue; @@ -4509,7 +4511,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere noway_assert(regArgTab[nextArgNum].varNum == varNum); // Emit a shufpd with a 0 immediate, which preserves the 0th element of the dest reg // and moves the 0th element of the src reg into the 1st element of the dest reg. - GetEmitter()->emitIns_R_R_I(INS_shufpd, emitActualTypeSize(varDsc->lvType), destRegNum, nextRegNum, 0); + GetEmitter()->emitIns_R_R_I(INS_shufpd, emitActualTypeSize(varRegType), destRegNum, nextRegNum, 0); // Set destRegNum to regNum so that we skip the setting of the register below, // but mark argNum as processed and clear regNum from the live mask. destRegNum = regNum; @@ -11245,11 +11247,15 @@ void CodeGen::genStructReturn(GenTree* treeNode) assert(regCount <= MAX_RET_REG_COUNT); #if FEATURE_MULTIREG_RET + // Right now the only enregisterable structs supported are SIMD vector types. if (genIsRegCandidateLocal(actualOp1)) { - // Right now the only enregisterable structs supported are SIMD vector types. - assert(varTypeIsSIMD(op1)); - assert(!actualOp1->AsLclVar()->IsMultiReg()); +#if defined(DEBUG) + const GenTreeLclVar* lclVar = actualOp1->AsLclVar(); + const LclVarDsc* varDsc = compiler->lvaGetDesc(lclVar); + assert(varTypeIsSIMD(varDsc->GetRegisterType())); + assert(!lclVar->IsMultiReg()); +#endif // DEBUG #ifdef FEATURE_SIMD genSIMDSplitReturn(op1, &retTypeDesc); #endif // FEATURE_SIMD @@ -11329,6 +11335,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) assert(op1->IsMultiRegNode()); unsigned regCount = actualOp1->IsMultiRegLclVar() ? actualOp1->AsLclVar()->GetFieldCount(compiler) : actualOp1->GetMultiRegCount(); + assert(regCount > 1); // Assumption: current implementation requires that a multi-reg // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from @@ -12580,3 +12587,61 @@ void CodeGen::genPoisonFrame(regMaskTP regLiveIn) } } } + +//---------------------------------------------------------------------- +// genBitCast - Generate the instruction to move a value between register files +// +// Arguments +// targetType - the destination type +// targetReg - the destination register +// srcType - the source type +// srcReg - the source register +// +void CodeGen::genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg) +{ + const bool srcFltReg = varTypeUsesFloatReg(srcType) || varTypeIsSIMD(srcType); + assert(srcFltReg == genIsValidFloatReg(srcReg)); + + const bool dstFltReg = varTypeUsesFloatReg(targetType) || varTypeIsSIMD(targetType); + assert(dstFltReg == genIsValidFloatReg(targetReg)); + + inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); +} + +//---------------------------------------------------------------------- +// genCodeForBitCast - Generate code for a GT_BITCAST that is not contained +// +// Arguments +// treeNode - the GT_BITCAST for which we're generating code +// +void CodeGen::genCodeForBitCast(GenTreeOp* treeNode) +{ + regNumber targetReg = treeNode->GetRegNum(); + var_types targetType = treeNode->TypeGet(); + GenTree* op1 = treeNode->gtGetOp1(); + genConsumeRegs(op1); + + if (op1->isContained()) + { + assert(op1->IsLocal() || op1->isIndir()); + if (genIsRegCandidateLocal(op1)) + { + unsigned lclNum = op1->AsLclVar()->GetLclNum(); + GetEmitter()->emitIns_R_S(ins_Load(treeNode->TypeGet(), compiler->isSIMDTypeLocalAligned(lclNum)), + emitTypeSize(treeNode), targetReg, lclNum, 0); + } + else + { + op1->gtType = treeNode->TypeGet(); + op1->SetRegNum(targetReg); + op1->ClearContained(); + JITDUMP("Changing type of BITCAST source to load directly.\n"); + genCodeForTreeNode(op1); + } + } + else + { + genBitCast(targetType, targetReg, op1->TypeGet(), op1->GetRegNum()); + } + genProduceReg(treeNode); +} diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index aa2fb0f5895..f58a8db0997 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1577,7 +1577,6 @@ void CodeGen::genConsumeRegs(GenTree* tree) { genConsumeAddress(tree); } -#ifdef TARGET_XARCH else if (tree->OperIsLocalRead()) { // A contained lcl var must be living on stack and marked as reg optional, or not be a @@ -1591,6 +1590,7 @@ void CodeGen::genConsumeRegs(GenTree* tree) // Update the life of the lcl var. genUpdateLife(tree); } +#ifdef TARGET_XARCH #ifdef FEATURE_HW_INTRINSICS else if (tree->OperIs(GT_HWINTRINSIC)) { diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 2bd0142381f..6f605e5514b 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -7073,64 +7073,6 @@ void CodeGen::genIntrinsic(GenTree* treeNode) genProduceReg(treeNode); } -//---------------------------------------------------------------------- -// genBitCast - Generate the instruction to move a value between register files -// -// Arguments -// targetType - the destination type -// targetReg - the destination register -// srcType - the source type -// srcReg - the source register -// -void CodeGen::genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg) -{ - const bool srcFltReg = varTypeUsesFloatReg(srcType) || varTypeIsSIMD(srcType); - assert(srcFltReg == genIsValidFloatReg(srcReg)); - - const bool dstFltReg = varTypeUsesFloatReg(targetType) || varTypeIsSIMD(targetType); - assert(dstFltReg == genIsValidFloatReg(targetReg)); - - inst_Mov(targetType, targetReg, srcReg, /* canSkip */ true); -} - -//---------------------------------------------------------------------- -// genCodeForBitCast - Generate code for a GT_BITCAST that is not contained -// -// Arguments -// treeNode - the GT_BITCAST for which we're generating code -// -void CodeGen::genCodeForBitCast(GenTreeOp* treeNode) -{ - regNumber targetReg = treeNode->GetRegNum(); - var_types targetType = treeNode->TypeGet(); - GenTree* op1 = treeNode->gtGetOp1(); - genConsumeRegs(op1); - - if (op1->isContained()) - { - assert(op1->IsLocal() || op1->isIndir()); - if (genIsRegCandidateLocal(op1)) - { - unsigned lclNum = op1->AsLclVar()->GetLclNum(); - GetEmitter()->emitIns_R_S(ins_Load(treeNode->TypeGet(), compiler->isSIMDTypeLocalAligned(lclNum)), - emitTypeSize(treeNode), targetReg, lclNum, 0); - } - else - { - op1->gtType = treeNode->TypeGet(); - op1->SetRegNum(targetReg); - op1->ClearContained(); - JITDUMP("Changing type of BITCAST source to load directly."); - genCodeForTreeNode(op1); - } - } - else - { - genBitCast(targetType, targetReg, op1->TypeGet(), op1->GetRegNum()); - } - genProduceReg(treeNode); -} - //-------------------------------------------------------------------------- // // getBaseVarForPutArgStk - returns the baseVarNum for passing a stack arg. // diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e185e520808..b34535a6146 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7630,11 +7630,13 @@ public: #if defined(TARGET_AMD64) static bool varTypeNeedsPartialCalleeSave(var_types type) { + assert(type != TYP_STRUCT); return (type == TYP_SIMD32); } #elif defined(TARGET_ARM64) static bool varTypeNeedsPartialCalleeSave(var_types type) { + assert(type != TYP_STRUCT); // ARM64 ABI FP Callee save registers only require Callee to save lower 8 Bytes // For SIMD types longer than 8 bytes Caller is responsible for saving and restoring Upper bytes. return ((type == TYP_SIMD16) || (type == TYP_SIMD12)); diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index cd685c2cc10..a87e9a46433 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -556,12 +556,7 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave #endif // defined(TARGET_ARM64) #endif // DEBUG -#if defined(TARGET_WINDOWS) && defined(TARGET_XARCH) CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 1) // Allow to enregister locals with struct type. -#else -CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) // Don't allow to enregister locals with struct type - // yet. -#endif #undef CONFIG_INTEGER #undef CONFIG_STRING diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 75bcbfafbf2..0a852a43d0d 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3493,14 +3493,31 @@ void Compiler::lvaSortByRefCount() { varDsc->lvTracked = 0; } - else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct && !compEnregStructLocals()) - { - lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); - } else if (!varDsc->IsEnregisterableType()) { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); } + else if (varDsc->lvType == TYP_STRUCT) + { + if (!varDsc->lvRegStruct && !compEnregStructLocals()) + { + lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct)); + } + else if (varDsc->lvIsMultiRegArgOrRet()) + { + // Prolog and return generators do not support SIMD<->general register moves. + lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStructArg)); + } +#if defined(TARGET_ARM) + else if (varDsc->lvIsParam) + { + // On arm we prespill all struct args, + // TODO-Arm-CQ: keep them in registers, it will need a fix + // to "On the ARM we will spill any incoming struct args" logic in codegencommon. + lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStructArg)); + } +#endif // TARGET_ARM + } } if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT)) { @@ -4149,7 +4166,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, #if FEATURE_PARTIAL_SIMD_CALLEE_SAVE // TODO-CQ: If the varType needs partial callee save, conservatively do not enregister // such variable. In future, need to enable enregisteration for such variables. - if (!varTypeNeedsPartialCalleeSave(varDsc->lvType)) + if (!varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType())) #endif { varDsc->lvSingleDefRegCandidate = true; diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 134b77281f6..13af2eaa280 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -302,6 +302,12 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // address, not knowing that GT_IND is part of a block op that has containment restrictions. src->AsIndir()->Addr()->ClearContained(); } + else if (src->OperIs(GT_LCL_VAR)) + { + // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register. + const unsigned srcLclNum = src->AsLclVar()->GetLclNum(); + comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(Compiler::DNER_BlockOp)); + } if (blkNode->OperIs(GT_STORE_OBJ)) { diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index f382b1dcf46..9491b2c0773 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -1809,7 +1809,7 @@ void LinearScan::identifyCandidates() // Additionally, when we are generating code for a target with partial SIMD callee-save // (AVX on non-UNIX amd64 and 16-byte vectors on arm64), we keep a separate set of the // LargeVectorType vars. - if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType)) + if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType())) { largeVectorVarCount++; VarSetOps::AddElemD(compiler, largeVectorVars, varDsc->lvVarIndex); @@ -6242,7 +6242,7 @@ void LinearScan::insertUpperVectorSave(GenTree* tree, } LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum; - assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType)); + assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType())); // On Arm64, we must always have a register to save the upper half, // while on x86 we can spill directly to memory. @@ -6323,7 +6323,7 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree, // lclVar as spilled). assert(lclVarReg != REG_NA); LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum; - assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType)); + assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType())); GenTree* restoreLcl = nullptr; restoreLcl = compiler->gtNewLclvNode(lclVarInterval->varNum, varDsc->lvType); diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index da7cb15b7cd..7eeddf4ef4f 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -1165,7 +1165,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo { LclVarDsc* varDsc = compiler->lvaGetDescByTrackedIndex(varIndex); #if FEATURE_PARTIAL_SIMD_CALLEE_SAVE - if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->lvType)) + if (Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType())) { if (!VarSetOps::IsMember(compiler, largeVectorCalleeSaveCandidateVars, varIndex)) { @@ -1511,7 +1511,40 @@ void LinearScan::buildUpperVectorSaveRefPositions(GenTree* tree, LsraLocation cu for (RefInfoListNode *listNode = defList.Begin(), *end = defList.End(); listNode != end; listNode = listNode->Next()) { - if (Compiler::varTypeNeedsPartialCalleeSave(listNode->treeNode->TypeGet())) + const GenTree* defNode = listNode->treeNode; + var_types regType = defNode->TypeGet(); + if (regType == TYP_STRUCT) + { + assert(defNode->OperIs(GT_LCL_VAR, GT_CALL)); + if (defNode->OperIs(GT_LCL_VAR)) + { + const GenTreeLclVar* lcl = defNode->AsLclVar(); + const LclVarDsc* varDsc = compiler->lvaGetDesc(lcl); + regType = varDsc->GetRegisterType(); + } + else + { + const GenTreeCall* call = defNode->AsCall(); + const CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; + Compiler::structPassingKind howToReturnStruct; + regType = compiler->getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct); + if (howToReturnStruct == Compiler::SPK_ByValueAsHfa) + { + regType = compiler->GetHfaType(retClsHnd); + } +#if defined(TARGET_ARM64) + else if (howToReturnStruct == Compiler::SPK_ByValue) + { + // TODO-Cleanup: add a new Compiler::SPK for this case. + // This is the case when 16-byte struct is returned as [x0, x1]. + // We don't need a partial callee save. + regType = TYP_LONG; + } +#endif // TARGET_ARM64 + } + assert((regType != TYP_STRUCT) && (regType != TYP_UNDEF)); + } + if (Compiler::varTypeNeedsPartialCalleeSave(regType)) { // In the rare case where such an interval is live across nested calls, we don't need to insert another. if (listNode->ref->getInterval()->recentRefPosition->refType != RefTypeUpperVectorSave) From 617fce4aa6a5eec06f9fef2da95c0d6d1e26d2ac Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Thu, 15 Jul 2021 05:59:38 -0700 Subject: [PATCH 585/926] Prevent ActivityId leak across threads (#55625) Prevent ActivityId leak across threads Fixes #51608 Previously running an async task with two or more levels of nesting on the EventSource activity stack would leave the ActivityID set on the old thread after being swapped out at a yield point. This meant that threadpool threads would often have stale ActivityIDs present when they began a new work item. This in turn produced non-sensical logs where it appeared that unrelated work items were nested within each other. Most of this change is adding tests. The ActivityIdIsZeroedOnThreadSwitchOut test is the one being fixed. The remainder of the new tests pass both before and after the change. I added those to better describe some of the existing behavior and add a little confidence that the change didn't have unintended effects elsewhere. As best I can tell the Activity tracking feature didn't have any testing previously and there is certainly still room for improvement on test coverage. --- .../BasicEventSourceTest/ActivityTracking.cs | 154 ++++++++++++++++++ .../System.Diagnostics.Tracing.Tests.csproj | 1 + .../Diagnostics/Tracing/ActivityTracker.cs | 33 +++- 3 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/ActivityTracking.cs diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/ActivityTracking.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/ActivityTracking.cs new file mode 100644 index 00000000000..1330901b00b --- /dev/null +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/ActivityTracking.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace BasicEventSourceTests +{ + public class ActivityTracking + { + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void StartStopCreatesActivity() + { + using ActivityEventListener l = new ActivityEventListener(); + using ActivityEventSource es = new ActivityEventSource(); + + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + es.ExampleStart(); + Assert.NotEqual(Guid.Empty, EventSource.CurrentThreadActivityId); + es.ExampleStop(); + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public async Task ActivityFlowsAsync() + { + using ActivityEventListener l = new ActivityEventListener(); + using ActivityEventSource es = new ActivityEventSource(); + + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + es.ExampleStart(); + Assert.NotEqual(Guid.Empty, EventSource.CurrentThreadActivityId); + await Task.Yield(); + Assert.NotEqual(Guid.Empty, EventSource.CurrentThreadActivityId); + es.ExampleStop(); + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public async Task ActivityIdIsZeroedOnThreadSwitchOut() + { + using ActivityEventListener l = new ActivityEventListener(); + using ActivityEventSource es = new ActivityEventSource(); + + // Run tasks on many threads. If an activity id leaks it is likely + // that the thread will be sheduled to run one of our other tasks + // and we can detect the non-zero id at the start of the task + List tasks = new List(); + for (int i = 0; i < 100; i++) + { + tasks.Add(Task.Run(() => YieldTwoActivitiesDeep(es))); + } + + await Task.WhenAll(tasks.ToArray()); + } + + private async Task YieldTwoActivitiesDeep(ActivityEventSource es) + { + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + es.ExampleStart(); + es.Example2Start(); + await Task.Yield(); + es.Example2Stop(); + es.ExampleStop(); + Assert.Equal(Guid.Empty, EventSource.CurrentThreadActivityId); + } + + // I don't know if this behavior is by-design or accidental. For now + // I am attempting to preserve it to lower back compat risk, but in + // the future we might decide it wasn't even desirable to begin with. + // Compare with SetCurrentActivityIdAfterEventDoesNotFlowAsync below. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public async Task SetCurrentActivityIdBeforeEventFlowsAsync() + { + using ActivityEventListener l = new ActivityEventListener(); + using ActivityEventSource es = new ActivityEventSource(); + try + { + Guid g = Guid.NewGuid(); + EventSource.SetCurrentThreadActivityId(g); + Assert.Equal(g, EventSource.CurrentThreadActivityId); + es.ExampleStart(); + await Task.Yield(); + es.ExampleStop(); + Assert.Equal(g, EventSource.CurrentThreadActivityId); + } + finally + { + EventSource.SetCurrentThreadActivityId(Guid.Empty); + } + } + + // I don't know if this behavior is by-design or accidental. For now + // I am attempting to preserve it to lower back compat risk, but in + // the future we might decide it wasn't even desirable to begin with. + // Compare with SetCurrentActivityIdBeforeEventFlowsAsync above. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public async Task SetCurrentActivityIdAfterEventDoesNotFlowAsync() + { + using ActivityEventListener l = new ActivityEventListener(); + using ActivityEventSource es = new ActivityEventSource(); + try + { + es.ExampleStart(); + Guid g = Guid.NewGuid(); + EventSource.SetCurrentThreadActivityId(g); + Assert.Equal(g, EventSource.CurrentThreadActivityId); + await Task.Yield(); + Assert.NotEqual(g, EventSource.CurrentThreadActivityId); + es.ExampleStop(); + } + finally + { + EventSource.SetCurrentThreadActivityId(Guid.Empty); + } + } + } + + [EventSource(Name = "ActivityEventSource")] + class ActivityEventSource : EventSource + { + [Event(1)] + public void ExampleStart() => WriteEvent(1); + + [Event(2)] + public void ExampleStop() => WriteEvent(2); + + [Event(3)] + public void Example2Start() => WriteEvent(3); + + [Event(4)] + public void Example2Stop() => WriteEvent(4); + } + + class ActivityEventListener : EventListener + { + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource.Name == "System.Threading.Tasks.TplEventSource") + { + EnableEvents(eventSource, EventLevel.LogAlways, (EventKeywords)0x80); + } + else if (eventSource.Name == "ActivityEventSource") + { + EnableEvents(eventSource, EventLevel.Informational); + } + } + } +} diff --git a/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj b/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj index 270e6f2339d..66eb092dee1 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj +++ b/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj @@ -17,6 +17,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs index 8aebbc4f7aa..026e93cbdfd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs @@ -550,12 +550,36 @@ namespace System.Diagnostics.Tracing // This callback is used to initialize the m_current AsyncLocal Variable. // Its job is to keep the ETW Activity ID (part of thread local storage) in sync // with m_current.ActivityID + // + // WARNING: When mixing manual usage of EventSource.SetCurrentThreadActivityID + // and Start/Stop EventSource events I can't identify a clear design how this + // synchronization is intended to work. For example code that changes + // SetCurrentThreadActivityID after a FooStart() event will not flow the + // explicit ID with the async work, but if FooStart() event is called after + // SetCurrentThreadActivityID then the explicit ID change does flow. + // For now I've adopted the approach: + // Priority 1: Make the API predictable/sensible when only Start/Stop events + // are in use. + // Priority 2: If users aren't complaining and it doesn't interfere with + // goal #1, try to preserve the arbitrary/buggy? existing behavior + // for mixed usage of SetActivityID + events. + // + // For scenarios that only use start/stop events this is what we expect: + // calling start -> push new ID on stack and update thread-local to match new ID + // calling stop -> pop ID from stack and update thread-local to match new topmost + // still active ID. If there is none, set ID to zero + // thread swap -> update thread-local to match topmost active ID. + // If there is none, set ID to zero. private void ActivityChanging(AsyncLocalValueChangedArgs args) { ActivityInfo? cur = args.CurrentValue; ActivityInfo? prev = args.PreviousValue; - // Are we popping off a value? (we have a prev, and it creator is cur) + // Special case only relevant for mixed SetActivityID usage: + // + // Are we MAYBE popping off a value? (we have a prev, and it creator is cur) + // We can't be certain this is a pop because a thread swapping between two + // ExecutionContexts can also appear the same way. // Then check if we should use the GUID at the time of the start event if (prev != null && prev.m_creator == cur) { @@ -569,8 +593,7 @@ namespace System.Diagnostics.Tracing } } - // OK we did not have an explicit SetActivityID set. Then we should be - // setting the activity to current ActivityInfo. However that activity + // Set the activity to current ActivityInfo. However that activity // might be dead, in which case we should skip it, so we never set // the ID to dead things. while (cur != null) @@ -583,8 +606,10 @@ namespace System.Diagnostics.Tracing } cur = cur.m_creator; } + // we can get here if there is no information on our activity stack (everything is dead) - // currently we do nothing, as that seems better than setting to Guid.Emtpy. + // Set ActivityID to zero + EventSource.SetCurrentThreadActivityId(Guid.Empty); } /// From 54d4f622944702f001cf7b85457449ab5bc9ab3f Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 15 Jul 2021 07:33:23 -0700 Subject: [PATCH 586/926] [android][debugger] Implement support to debug after hot reload. (#55722) * Implement support to debug after hot reload. * Remove unused variable. --- src/mono/mono/component/debugger-agent.c | 12 ++-- src/mono/mono/component/hot_reload.c | 41 ++++++----- src/mono/mono/metadata/debug-mono-ppdb.c | 71 +++++++++++++------ src/mono/mono/metadata/debug-mono-ppdb.h | 7 +- src/mono/mono/metadata/mono-debug.c | 21 ++++-- src/mono/mono/metadata/mono-debug.h | 7 ++ .../DebuggerTestSuite/BreakpointTests.cs | 4 +- 7 files changed, 109 insertions(+), 54 deletions(-) diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index bec98edb3c2..4a01dd258ad 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -8449,7 +8449,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g ERROR_DECL (error); MonoDebugMethodInfo *minfo; char *source_file; - int i, j, n_il_offsets; + int i, j, n_il_offsets, n_il_offsets_original; int *source_files; GPtrArray *source_file_list; MonoSymSeqPoint *sym_seq_points; @@ -8472,7 +8472,8 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g break; } - mono_debug_get_seq_points (minfo, &source_file, &source_file_list, &source_files, &sym_seq_points, &n_il_offsets); + mono_debug_get_seq_points (minfo, &source_file, &source_file_list, &source_files, NULL, &n_il_offsets_original); + mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sym_seq_points, &n_il_offsets); buffer_add_int (buf, header->code_size); if (CHECK_PROTOCOL_VERSION (2, 13)) { buffer_add_int (buf, source_file_list->len); @@ -8494,14 +8495,17 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g const char *srcfile = ""; if (source_files [i] != -1) { - MonoDebugSourceInfo *sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, source_files [i]); + int idx = i; + if (i >= n_il_offsets_original) + idx = 0; + MonoDebugSourceInfo *sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, source_files [idx]); srcfile = sinfo->source_file; } PRINT_DEBUG_MSG (10, "IL%x -> %s:%d %d %d %d\n", sp->il_offset, srcfile, sp->line, sp->column, sp->end_line, sp->end_column); buffer_add_int (buf, sp->il_offset); buffer_add_int (buf, sp->line); if (CHECK_PROTOCOL_VERSION (2, 13)) - buffer_add_int (buf, source_files [i]); + buffer_add_int (buf, i >= n_il_offsets_original ? source_files [0] : source_files [i]); if (CHECK_PROTOCOL_VERSION (2, 19)) buffer_add_int (buf, sp->column); if (CHECK_PROTOCOL_VERSION (2, 32)) { diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 32820888b26..2cd1283454d 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -19,6 +19,7 @@ #include "mono/utils/mono-lazy-init.h" #include "mono/utils/mono-logger-internals.h" #include "mono/utils/mono-path.h" +#include "mono/metadata/mono-debug.h" #include @@ -321,13 +322,21 @@ baseline_info_lookup (MonoImage *base_image) static DeltaInfo* delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, BaselineInfo *base_info, uint32_t generation); +static void +free_ppdb_entry (gpointer key, gpointer val, gpointer user_data) +{ + g_free (val); +} + static void delta_info_destroy (DeltaInfo *dinfo) { if (dinfo->method_table_update) g_hash_table_destroy (dinfo->method_table_update); - if (dinfo->method_ppdb_table_update) + if (dinfo->method_ppdb_table_update) { + g_hash_table_foreach (dinfo->method_ppdb_table_update, free_ppdb_entry, NULL); g_hash_table_destroy (dinfo->method_ppdb_table_update); + } g_free (dinfo); } @@ -1182,7 +1191,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer } static void -set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address, const char* pdb_address) +set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address, MonoDebugInformationEnc* pdb_address) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "setting method 0x%08x in g=%d IL=%p", token_index, generation, (void*)il_address); /* FIXME: this is a race if other threads are doing a lookup. */ @@ -1191,7 +1200,7 @@ set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t gene g_hash_table_insert (delta_info->method_ppdb_table_update, GUINT_TO_POINTER (token_index), (gpointer) pdb_address); } -static const char * +static MonoDebugInformationEnc * hot_reload_get_method_debug_information (MonoImage *image_dppdb, int idx) { if (!image_dppdb) @@ -1204,21 +1213,15 @@ hot_reload_get_method_debug_information (MonoImage *image_dppdb, int idx) mono_metadata_decode_row (table_encmap, i, cols, MONO_ENCMAP_SIZE); int map_token = cols [MONO_ENCMAP_TOKEN]; int token_table = mono_metadata_token_table (map_token); - if (token_table != MONO_TABLE_METHODBODY) - continue; - int token_index = mono_metadata_token_index (map_token); - if (token_index == idx) - { - guint32 cols [MONO_METHODBODY_SIZE]; - MonoTableInfo *methodbody_table = &image_dppdb->tables [MONO_TABLE_METHODBODY]; - mono_metadata_decode_row (methodbody_table, i, cols, MONO_METHODBODY_SIZE); - if (!cols [MONO_METHODBODY_SEQ_POINTS]) - return NULL; - - const char *ptr = mono_metadata_blob_heap (image_dppdb, cols [MONO_METHODBODY_SEQ_POINTS]); - return ptr; + if (token_table == MONO_TABLE_METHODBODY) { + int token_index = mono_metadata_token_index (map_token); + if (token_index == idx) { + MonoDebugInformationEnc *encDebugInfo = g_new0 (MonoDebugInformationEnc, 1); + encDebugInfo->idx = i; + encDebugInfo->image = image_dppdb; + return encDebugInfo; + } } - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb encmap i=%d: token=0x%08x (table=%s)", i, map_token, mono_meta_table_name (token_table)); } return NULL; } @@ -1304,7 +1307,7 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); if (rva < dil_length) { char *il_address = ((char *) dil_data) + rva; - const char *method_debug_information = hot_reload_get_method_debug_information (image_dppdb, token_index); + MonoDebugInformationEnc *method_debug_information = hot_reload_get_method_debug_information (image_dppdb, token_index); set_update_method (image_base, base_info, generation, image_dmeta, delta_info, token_index, il_address, method_debug_information); } else { /* rva points probably into image_base IL stream. can this ever happen? */ @@ -1385,7 +1388,7 @@ hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint MonoImage *image_dpdb = NULL; if (dpdb_length > 0) { - MonoImage *image_dpdb = image_open_dmeta_from_data (image_base, generation, dpdb_bytes_orig, dpdb_length); + image_dpdb = image_open_dmeta_from_data (image_base, generation, dpdb_bytes_orig, dpdb_length); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image string size: 0x%08x", image_dpdb->heap_strings.size); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image user string size: 0x%08x", image_dpdb->heap_us.size); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image blob heap addr: %p", image_dpdb->heap_blob.data); diff --git a/src/mono/mono/metadata/debug-mono-ppdb.c b/src/mono/mono/metadata/debug-mono-ppdb.c index 67bd4715d33..101e5832692 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.c +++ b/src/mono/mono/metadata/debug-mono-ppdb.c @@ -543,19 +543,29 @@ mono_ppdb_get_seq_points_internal (const char* ptr, MonoSymSeqPoint **seq_points if (n_seq_points) { *n_seq_points = sps->len; - g_assert (seq_points); - *seq_points = g_new (MonoSymSeqPoint, sps->len); - memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); + if (seq_points) { + *seq_points = g_new (MonoSymSeqPoint, sps->len); + memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); + } } int sps_len = sps->len; g_array_free (sps, TRUE); return sps_len; } -void -mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points) +gboolean +mono_ppdb_get_seq_points_enc (MonoImage *image, int idx, MonoSymSeqPoint **seq_points, int *n_seq_points) { + guint32 cols [MONO_METHODBODY_SIZE]; + MonoTableInfo *tables = image->tables; + MonoTableInfo *methodbody_table = &tables [MONO_TABLE_METHODBODY]; + mono_metadata_decode_row (methodbody_table, idx, cols, MONO_METHODBODY_SIZE); + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return FALSE; + + const char *ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); mono_ppdb_get_seq_points_internal (ptr, seq_points, n_seq_points, 0, NULL, NULL, NULL, NULL, NULL, NULL, FALSE); + return TRUE; } void @@ -619,27 +629,16 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr } -MonoDebugLocalsInfo* -mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo) +static MonoDebugLocalsInfo* +mono_ppdb_lookup_locals_internal (MonoImage *image, int method_idx, gboolean is_enc) { - MonoPPDBFile *ppdb = minfo->handle->ppdb; - MonoImage *image = ppdb->image; + MonoDebugLocalsInfo *res; MonoTableInfo *tables = image->tables; - MonoMethod *method = minfo->method; + guint32 cols [MONO_LOCALSCOPE_SIZE]; guint32 locals_cols [MONO_LOCALVARIABLE_SIZE]; - int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes; - MonoDebugLocalsInfo *res; - MonoMethodSignature *sig; - - if (!method->token) - return NULL; - - sig = mono_method_signature_internal (method); - if (!sig) - return NULL; - - method_idx = mono_metadata_token_index (method->token); + + int i, lindex, sindex, locals_idx, locals_end_idx, nscopes, start_scope_idx, scope_idx; start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx); @@ -725,6 +724,34 @@ mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo) return res; } +MonoDebugLocalsInfo* +mono_ppdb_lookup_locals_enc (MonoImage *image, int method_idx) +{ + return mono_ppdb_lookup_locals_internal (image, method_idx + 1, TRUE); +} + +MonoDebugLocalsInfo* +mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo) +{ + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + MonoMethod *method = minfo->method; + int method_idx; + MonoMethodSignature *sig; + + if (!method->token) + return NULL; + + sig = mono_method_signature_internal (method); + if (!sig) + return NULL; + + method_idx = mono_metadata_token_index (method->token); + + + return mono_ppdb_lookup_locals_internal (image, method_idx, FALSE); +} + /* * We use this to pass context information to the row locator */ diff --git a/src/mono/mono/metadata/debug-mono-ppdb.h b/src/mono/mono/metadata/debug-mono-ppdb.h index e16317f9fc7..b44637e9a35 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.h +++ b/src/mono/mono/metadata/debug-mono-ppdb.h @@ -32,12 +32,15 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset); void mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); -void -mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points); +gboolean +mono_ppdb_get_seq_points_enc (MonoImage *image, int idx, MonoSymSeqPoint **seq_points, int *n_seq_points); MonoDebugLocalsInfo* mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo); +MonoDebugLocalsInfo* +mono_ppdb_lookup_locals_enc (MonoImage *image, int method_idx); + MonoDebugMethodAsyncInfo* mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo); diff --git a/src/mono/mono/metadata/mono-debug.c b/src/mono/mono/metadata/mono-debug.c index 10554e663ef..372114e4970 100644 --- a/src/mono/mono/metadata/mono-debug.c +++ b/src/mono/mono/metadata/mono-debug.c @@ -860,6 +860,17 @@ mono_debug_lookup_locals (MonoMethod *method, mono_bool ignore_pdb) { MonoDebugMethodInfo *minfo; MonoDebugLocalsInfo *res; + + MonoImage* img = m_class_get_image (method->klass); + if (img->has_updates) { + int idx = mono_metadata_token_index (method->token); + MonoDebugInformationEnc *mdie = (MonoDebugInformationEnc *) mono_metadata_update_get_updated_method_ppdb (img, idx); + if (mdie != NULL) { + res = mono_ppdb_lookup_locals_enc (mdie->image, mdie->idx); + if (res != NULL) + return res; + } + } if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) return NULL; @@ -1117,12 +1128,12 @@ void mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) { MonoImage* img = m_class_get_image (minfo->method->klass); - if (img->has_updates) { + if (img->has_updates && !source_file_list) { int idx = mono_metadata_token_index (minfo->method->token); - gpointer ptr = mono_metadata_update_get_updated_method_ppdb (img, idx); - if (ptr != NULL) { - mono_ppdb_get_seq_points_enc (ptr, seq_points, n_seq_points); - return; + MonoDebugInformationEnc *mdie = (MonoDebugInformationEnc *) mono_metadata_update_get_updated_method_ppdb (img, idx); + if (mdie != NULL) { + if (mono_ppdb_get_seq_points_enc (mdie->image, mdie->idx, seq_points, n_seq_points)) + return; } } if (minfo->handle->ppdb) diff --git a/src/mono/mono/metadata/mono-debug.h b/src/mono/mono/metadata/mono-debug.h index 2ddbce09947..ccc9d3a0054 100644 --- a/src/mono/mono/metadata/mono-debug.h +++ b/src/mono/mono/metadata/mono-debug.h @@ -33,6 +33,7 @@ typedef struct _MonoDebugMethodInfo MonoDebugMethodInfo; typedef struct _MonoDebugLocalsInfo MonoDebugLocalsInfo; typedef struct _MonoDebugMethodAsyncInfo MonoDebugMethodAsyncInfo; typedef struct _MonoDebugSourceLocation MonoDebugSourceLocation; +typedef struct _MonoDebugInformationEnc MonoDebugInformationEnc; typedef struct _MonoDebugList MonoDebugList; @@ -110,6 +111,12 @@ struct _MonoDebugSourceLocation { uint32_t il_offset; }; + +struct _MonoDebugInformationEnc { + MonoImage *image; + int idx; +}; + MONO_API mono_bool mono_debug_enabled (void); /* diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index 77a8cb6c12e..77a3317dd60 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -336,11 +336,11 @@ namespace DebuggerTests var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckNumber(locals, "a", 10); - pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 30, 12, "StaticMethod3"); locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckNumber(locals, "b", 15); - pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 30, 12, "StaticMethod3"); locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckBool(locals, "c", true); } From dc2fe34fd2cf04eb0f11ef60f1a841c7c18f29b4 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 15 Jul 2021 17:40:30 +0300 Subject: [PATCH 587/926] Add json support for F# options, lists, sets, maps and records (#55108) * Add json support for F# options, lists, sets, maps and records * fix ILLink warnings * address ILLink annotations feedback * add support for ValueOption * revert unneeded sln changes * add JsonIgnoreCondition tests for optional types * Revert "revert unneeded sln changes" This reverts commit 2e793422dca84bd22d55cdfa2cd6c9b6c5d4963e. * remove lock from singleton initialization * improve FSharp.Core missing member error mesages * throw NotSupportedException on discriminated unions * extend optional test coverage to include list, set and map payloads * simplify changes required to converter infrastructure --- .../System.Text.Json/System.Text.Json.sln | 55 ++-- .../src/Resources/Strings.resx | 8 +- .../src/System.Text.Json.csproj | 7 + .../Converters/FSharp/FSharpListConverter.cs | 75 ++++++ .../Converters/FSharp/FSharpMapConverter.cs | 91 +++++++ .../FSharp/FSharpOptionConverter.cs | 100 +++++++ .../Converters/FSharp/FSharpSetConverter.cs | 75 ++++++ .../FSharp/FSharpTypeConverterFactory.cs | 77 ++++++ .../FSharp/FSharpValueOptionConverter.cs | 102 +++++++ .../Object/ObjectConverterFactory.cs | 10 +- .../Json/Serialization/JsonConverterOfT.cs | 12 +- .../JsonSerializerOptions.Converters.cs | 1 + .../Metadata/FSharpCoreReflectionProxy.cs | 248 ++++++++++++++++++ .../Metadata/JsonPropertyInfoOfT.cs | 3 - .../Serialization/Metadata/JsonTypeInfo.cs | 7 +- .../Text/Json/ThrowHelper.Serialization.cs | 7 + .../CollectionTests.fs | 177 +++++++++++++ .../System.Text.Json.FSharp.Tests/Helpers.fs | 19 ++ .../OptionTests.fs | 166 ++++++++++++ .../RecordTests.fs | 51 ++++ .../System.Text.Json.FSharp.Tests.fsproj | 16 ++ .../UnionTests.fs | 39 +++ .../ValueOptionTests.fs | 166 ++++++++++++ 23 files changed, 1475 insertions(+), 37 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/CollectionTests.fs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/Helpers.fs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/OptionTests.fs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/System.Text.Json.FSharp.Tests.fsproj create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/UnionTests.fs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/ValueOptionTests.fs diff --git a/src/libraries/System.Text.Json/System.Text.Json.sln b/src/libraries/System.Text.Json/System.Text.Json.sln index 433c378f988..7e8af66bc59 100644 --- a/src/libraries/System.Text.Json/System.Text.Json.sln +++ b/src/libraries/System.Text.Json/System.Text.Json.sln @@ -35,35 +35,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{676B6044-FA4 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{74017ACD-3AC1-4BB5-804B-D57E305FFBD9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.Json.SourceGeneration", "gen\System.Text.Json.SourceGeneration.csproj", "{6485EED4-C313-4551-9865-8ADCED603629}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json.SourceGeneration", "gen\System.Text.Json.SourceGeneration.csproj", "{6485EED4-C313-4551-9865-8ADCED603629}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.Json.Tests", "tests\System.Text.Json.Tests\System.Text.Json.Tests.csproj", "{A0178BAA-A1AF-4C69-8E4A-A700A2723DDC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json.Tests", "tests\System.Text.Json.Tests\System.Text.Json.Tests.csproj", "{A0178BAA-A1AF-4C69-8E4A-A700A2723DDC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.Json.SourceGeneration.Tests", "tests\System.Text.Json.SourceGeneration.Tests\System.Text.Json.SourceGeneration.Tests.csproj", "{33599A6C-F340-4E1B-9B4D-CB8946C22140}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json.SourceGeneration.Tests", "tests\System.Text.Json.SourceGeneration.Tests\System.Text.Json.SourceGeneration.Tests.csproj", "{33599A6C-F340-4E1B-9B4D-CB8946C22140}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.Json.SourceGeneration.UnitTests", "tests\System.Text.Json.SourceGeneration.UnitTests\System.Text.Json.SourceGeneration.UnitTests.csproj", "{18173CEC-895F-4F62-B7BB-B724457FEDCD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json.SourceGeneration.UnitTests", "tests\System.Text.Json.SourceGeneration.UnitTests\System.Text.Json.SourceGeneration.UnitTests.csproj", "{18173CEC-895F-4F62-B7BB-B724457FEDCD}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "System.Text.Json.FSharp.Tests", "tests\System.Text.Json.FSharp.Tests\System.Text.Json.FSharp.Tests.fsproj", "{5720BF06-2031-4AD8-B9B4-31A01E27ABB8}" EndProject Global - GlobalSection(NestedProjects) = preSolution - {102945CA-3736-4B2C-8E68-242A0B247F2B} = {3C544454-BD8B-44F4-A174-B61F18957613} - {73D5739C-E382-4E22-A7D3-B82705C58C74} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {25C42754-B384-4842-8FA7-75D7A79ADF0D} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {4774F56D-16A8-4ABB-8C73-5F57609F1773} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {E2077991-EB83-471C-B17F-72F569FFCE6D} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {BE230195-2A1C-4674-BACB-502C2CD864E9} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {D7276D7D-F117-47C5-B514-8E3E964769BE} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {7015E94D-D20D-48C8-86D7-6A996BE99E0E} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} - {E9AA0AEB-AEAE-4B28-8D4D-17A6D7C89D17} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {1C8262DB-7355-40A8-A2EC-4EED7363134A} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {D05FD93A-BC51-466E-BD56-3F3D6BBE6B06} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {7909EB27-0D6E-46E6-B9F9-8A1EFD557018} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {9BCCDA15-8907-4AE3-8871-2F17775DDE4C} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {1285FF43-F491-4BE0-B92C-37DA689CBD4B} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} - {6485EED4-C313-4551-9865-8ADCED603629} = {74017ACD-3AC1-4BB5-804B-D57E305FFBD9} - {A0178BAA-A1AF-4C69-8E4A-A700A2723DDC} = {3C544454-BD8B-44F4-A174-B61F18957613} - {33599A6C-F340-4E1B-9B4D-CB8946C22140} = {3C544454-BD8B-44F4-A174-B61F18957613} - {18173CEC-895F-4F62-B7BB-B724457FEDCD} = {3C544454-BD8B-44F4-A174-B61F18957613} - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -141,10 +123,35 @@ Global {18173CEC-895F-4F62-B7BB-B724457FEDCD}.Debug|Any CPU.Build.0 = Debug|Any CPU {18173CEC-895F-4F62-B7BB-B724457FEDCD}.Release|Any CPU.ActiveCfg = Release|Any CPU {18173CEC-895F-4F62-B7BB-B724457FEDCD}.Release|Any CPU.Build.0 = Release|Any CPU + {5720BF06-2031-4AD8-B9B4-31A01E27ABB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5720BF06-2031-4AD8-B9B4-31A01E27ABB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5720BF06-2031-4AD8-B9B4-31A01E27ABB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5720BF06-2031-4AD8-B9B4-31A01E27ABB8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {102945CA-3736-4B2C-8E68-242A0B247F2B} = {3C544454-BD8B-44F4-A174-B61F18957613} + {73D5739C-E382-4E22-A7D3-B82705C58C74} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {E9AA0AEB-AEAE-4B28-8D4D-17A6D7C89D17} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {25C42754-B384-4842-8FA7-75D7A79ADF0D} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {1C8262DB-7355-40A8-A2EC-4EED7363134A} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {4774F56D-16A8-4ABB-8C73-5F57609F1773} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {D05FD93A-BC51-466E-BD56-3F3D6BBE6B06} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {E2077991-EB83-471C-B17F-72F569FFCE6D} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {7909EB27-0D6E-46E6-B9F9-8A1EFD557018} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {BE230195-2A1C-4674-BACB-502C2CD864E9} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {D7276D7D-F117-47C5-B514-8E3E964769BE} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {9BCCDA15-8907-4AE3-8871-2F17775DDE4C} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {7015E94D-D20D-48C8-86D7-6A996BE99E0E} = {EC8CE194-261A-4115-9582-E2DB1A25CAFB} + {1285FF43-F491-4BE0-B92C-37DA689CBD4B} = {676B6044-FA47-4B7D-AEC2-FA94DB23A423} + {6485EED4-C313-4551-9865-8ADCED603629} = {74017ACD-3AC1-4BB5-804B-D57E305FFBD9} + {A0178BAA-A1AF-4C69-8E4A-A700A2723DDC} = {3C544454-BD8B-44F4-A174-B61F18957613} + {33599A6C-F340-4E1B-9B4D-CB8946C22140} = {3C544454-BD8B-44F4-A174-B61F18957613} + {18173CEC-895F-4F62-B7BB-B724457FEDCD} = {3C544454-BD8B-44F4-A174-B61F18957613} + {5720BF06-2031-4AD8-B9B4-31A01E27ABB8} = {3C544454-BD8B-44F4-A174-B61F18957613} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5868B757-D821-41FC-952E-2113A0519506} EndGlobalSection diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index e654d368051..14f5e6b50a6 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -611,4 +611,10 @@ A 'field' member cannot be 'virtual'. See arguments for the '{0}' and '{1}' parameters. - \ No newline at end of file + + Could not locate required member '{0}' from FSharp.Core. This might happen because your application has enabled member-level trimming. + + + F# discriminated union serialization is not supported. Consider authoring a custom converter for the type. + + diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 08855731abe..7d428ffb194 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -97,6 +97,7 @@ + @@ -132,6 +133,12 @@ + + + + + + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs new file mode 100644 index 00000000000..036d04e8beb --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + // Converter for F# lists: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-list-1.html + internal sealed class FSharpListConverter : IEnumerableDefaultConverter + where TList : IEnumerable + { + private readonly Func, TList> _listConstructor; + + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpListConverter() + { + _listConstructor = FSharpCoreReflectionProxy.Instance.CreateFSharpListConstructor(); + } + + protected override void Add(in TElement value, ref ReadStack state) + { + ((List)state.Current.ReturnValue!).Add(value); + } + + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = new List(); + } + + protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = _listConstructor((List)state.Current.ReturnValue!); + } + + protected override bool OnWriteResume(Utf8JsonWriter writer, TList value, JsonSerializerOptions options, ref WriteStack state) + { + IEnumerator enumerator; + if (state.Current.CollectionEnumerator == null) + { + enumerator = value.GetEnumerator(); + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + return true; + } + } + else + { + enumerator = (IEnumerator)state.Current.CollectionEnumerator; + } + + JsonConverter converter = GetElementConverter(ref state); + do + { + if (ShouldFlush(writer, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + + TElement element = enumerator.Current; + if (!converter.TryWrite(writer, element, options, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + } while (enumerator.MoveNext()); + + enumerator.Dispose(); + return true; + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs new file mode 100644 index 00000000000..30d16a09df8 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + // Converter for F# maps: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-fsharpmap-2.html + internal sealed class FSharpMapConverter : DictionaryDefaultConverter + where TMap : IEnumerable> + where TKey : notnull + { + private readonly Func>, TMap> _mapConstructor; + + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpMapConverter() + { + _mapConstructor = FSharpCoreReflectionProxy.Instance.CreateFSharpMapConstructor(); + } + + protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) + { + ((List>)state.Current.ReturnValue!).Add (new Tuple(key, value)); + } + + internal override bool CanHaveIdMetadata => false; + + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + { + state.Current.ReturnValue = new List>(); + } + + protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = _mapConstructor((List>)state.Current.ReturnValue!); + } + + protected internal override bool OnWriteResume(Utf8JsonWriter writer, TMap value, JsonSerializerOptions options, ref WriteStack state) + { + IEnumerator> enumerator; + if (state.Current.CollectionEnumerator == null) + { + enumerator = value.GetEnumerator(); + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + return true; + } + } + else + { + enumerator = (IEnumerator>)state.Current.CollectionEnumerator; + } + + JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; + _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); + _valueConverter ??= GetConverter(typeInfo.ElementTypeInfo!); + + do + { + if (ShouldFlush(writer, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + + if (state.Current.PropertyState < StackFramePropertyState.Name) + { + state.Current.PropertyState = StackFramePropertyState.Name; + + TKey key = enumerator.Current.Key; + _keyConverter.WriteWithQuotes(writer, key, options, ref state); + } + + TValue element = enumerator.Current.Value; + if (!_valueConverter.TryWrite(writer, element, options, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + + state.Current.EndDictionaryElement(); + } while (enumerator.MoveNext()); + + enumerator.Dispose(); + return true; + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs new file mode 100644 index 00000000000..a02dd449a9d --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + // Converter for F# optional values: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-option-1.html + // Serializes `Some(value)` using the format of `value` and `None` values as `null`. + internal sealed class FSharpOptionConverter : JsonConverter + where TOption : class + { + // Reflect the converter strategy of the element type, since we use the identical contract for Some(_) values. + internal override ConverterStrategy ConverterStrategy => _converterStrategy; + internal override Type? ElementType => typeof(TElement); + // 'None' is encoded using 'null' at runtime and serialized as 'null' in JSON. + public override bool HandleNull => true; + + private readonly JsonConverter _elementConverter; + private readonly Func _optionValueGetter; + private readonly Func _optionConstructor; + private readonly ConverterStrategy _converterStrategy; + + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpOptionConverter(JsonConverter elementConverter) + { + _elementConverter = elementConverter; + _optionValueGetter = FSharpCoreReflectionProxy.Instance.CreateFSharpOptionValueGetter(); + _optionConstructor = FSharpCoreReflectionProxy.Instance.CreateFSharpOptionSomeConstructor(); + + // temporary workaround for JsonConverter base constructor needing to access + // ConverterStrategy when calculating `CanUseDirectReadOrWrite`. + // TODO move `CanUseDirectReadOrWrite` from JsonConverter to JsonTypeInfo. + _converterStrategy = _elementConverter.ConverterStrategy; + CanUseDirectReadOrWrite = _converterStrategy == ConverterStrategy.Value; + } + + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TOption? value) + { + // `null` values deserialize as `None` + if (!state.IsContinuation && reader.TokenType == JsonTokenType.Null) + { + value = null; + return true; + } + + state.Current.JsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; + if (_elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element)) + { + value = _optionConstructor(element); + return true; + } + + value = null; + return false; + } + + internal override bool OnTryWrite(Utf8JsonWriter writer, TOption value, JsonSerializerOptions options, ref WriteStack state) + { + if (value is null) + { + // Write `None` values as null + writer.WriteNullValue(); + return true; + } + + TElement element = _optionValueGetter(value); + state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; + return _elementConverter.TryWrite(writer, element, options, ref state); + } + + // Since this is a hybrid converter (ConverterStrategy depends on the element converter), + // we need to override the value converter Write and Read methods too. + + public override void Write(Utf8JsonWriter writer, TOption value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + TElement element = _optionValueGetter(value); + _elementConverter.Write(writer, element, options); + } + } + + public override TOption? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + TElement? element = _elementConverter.Read(ref reader, typeToConvert, options); + return _optionConstructor(element); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs new file mode 100644 index 00000000000..35c464643a3 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + // Converter for F# sets: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-fsharpset-1.html + internal sealed class FSharpSetConverter : IEnumerableDefaultConverter + where TSet : IEnumerable + { + private readonly Func, TSet> _setConstructor; + + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpSetConverter() + { + _setConstructor = FSharpCoreReflectionProxy.Instance.CreateFSharpSetConstructor(); + } + + protected override void Add(in TElement value, ref ReadStack state) + { + ((List)state.Current.ReturnValue!).Add(value); + } + + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = new List(); + } + + protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + { + state.Current.ReturnValue = _setConstructor((List)state.Current.ReturnValue!); + } + + protected override bool OnWriteResume(Utf8JsonWriter writer, TSet value, JsonSerializerOptions options, ref WriteStack state) + { + IEnumerator enumerator; + if (state.Current.CollectionEnumerator == null) + { + enumerator = value.GetEnumerator(); + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + return true; + } + } + else + { + enumerator = (IEnumerator)state.Current.CollectionEnumerator; + } + + JsonConverter converter = GetElementConverter(ref state); + do + { + if (ShouldFlush(writer, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + + TElement element = enumerator.Current; + if (!converter.TryWrite(writer, element, options, ref state)) + { + state.Current.CollectionEnumerator = enumerator; + return false; + } + } while (enumerator.MoveNext()); + + enumerator.Dispose(); + return true; + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs new file mode 100644 index 00000000000..c0cb9b88fd5 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpTypeConverterFactory.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; +using FSharpKind = System.Text.Json.Serialization.Metadata.FSharpCoreReflectionProxy.FSharpKind; + +namespace System.Text.Json.Serialization.Converters +{ + internal class FSharpTypeConverterFactory : JsonConverterFactory + { + // Temporary solution to account for not implemented support for type-level attributes + // TODO remove once addressed https://github.com/mono/linker/issues/1742#issuecomment-875036480 + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpTypeConverterFactory() { } + + private ObjectConverterFactory? _recordConverterFactory; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked with RequiresUnreferencedCode.")] + public override bool CanConvert(Type typeToConvert) => + FSharpCoreReflectionProxy.IsFSharpType(typeToConvert) && + FSharpCoreReflectionProxy.Instance.DetectFSharpKind(typeToConvert) is not FSharpKind.Unrecognized; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked with RequiresUnreferencedCode.")] + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + Debug.Assert(CanConvert(typeToConvert)); + + Type elementType; + Type converterFactoryType; + object?[]? constructorArguments = null; + + switch (FSharpCoreReflectionProxy.Instance.DetectFSharpKind(typeToConvert)) + { + case FSharpKind.Option: + elementType = typeToConvert.GetGenericArguments()[0]; + converterFactoryType = typeof(FSharpOptionConverter<,>).MakeGenericType(typeToConvert, elementType); + constructorArguments = new object[] { options.GetConverterInternal(elementType) }; + break; + case FSharpKind.ValueOption: + elementType = typeToConvert.GetGenericArguments()[0]; + converterFactoryType = typeof(FSharpValueOptionConverter<,>).MakeGenericType(typeToConvert, elementType); + constructorArguments = new object[] { options.GetConverterInternal(elementType) }; + break; + case FSharpKind.List: + elementType = typeToConvert.GetGenericArguments()[0]; + converterFactoryType = typeof(FSharpListConverter<,>).MakeGenericType(typeToConvert, elementType); + break; + case FSharpKind.Set: + elementType = typeToConvert.GetGenericArguments()[0]; + converterFactoryType = typeof(FSharpSetConverter<,>).MakeGenericType(typeToConvert, elementType); + break; + case FSharpKind.Map: + Type[] genericArgs = typeToConvert.GetGenericArguments(); + Type keyType = genericArgs[0]; + Type valueType = genericArgs[1]; + converterFactoryType = typeof(FSharpMapConverter<,,>).MakeGenericType(typeToConvert, keyType, valueType); + break; + case FSharpKind.Record: + // Use a modified object converter factory that picks the right constructor for struct record deserialization. + ObjectConverterFactory objectFactory = _recordConverterFactory ??= new ObjectConverterFactory(useDefaultConstructorInUnannotatedStructs: false); + Debug.Assert(objectFactory.CanConvert(typeToConvert)); + return objectFactory.CreateConverter(typeToConvert, options); + case FSharpKind.Union: + throw new NotSupportedException(SR.FSharpDiscriminatedUnionsNotSupported); + default: + Debug.Fail("Unrecognized F# type."); + throw new Exception(); + } + + return (JsonConverter)Activator.CreateInstance(converterFactoryType, constructorArguments)!; + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs new file mode 100644 index 00000000000..35d1640d88c --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + // Converter for F# struct optional values: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-fsharpvalueoption-1.html + // Serializes `ValueSome(value)` using the format of `value` and `ValueNone` values as `null`. + internal sealed class FSharpValueOptionConverter : JsonConverter + where TValueOption : struct, IEquatable + { + // Reflect the converter strategy of the element type, since we use the identical contract for ValueSome(_) values. + internal override ConverterStrategy ConverterStrategy => _converterStrategy; + internal override Type? ElementType => typeof(TElement); + // 'ValueNone' is encoded using 'default' at runtime and serialized as 'null' in JSON. + public override bool HandleNull => true; + + private readonly JsonConverter _elementConverter; + private readonly FSharpCoreReflectionProxy.StructGetter _optionValueGetter; + private readonly Func _optionConstructor; + private readonly ConverterStrategy _converterStrategy; + + [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)] + public FSharpValueOptionConverter(JsonConverter elementConverter) + { + _elementConverter = elementConverter; + _optionValueGetter = FSharpCoreReflectionProxy.Instance.CreateFSharpValueOptionValueGetter(); + _optionConstructor = FSharpCoreReflectionProxy.Instance.CreateFSharpValueOptionSomeConstructor(); + + // temporary workaround for JsonConverter base constructor needing to access + // ConverterStrategy when calculating `CanUseDirectReadOrWrite`. + // TODO move `CanUseDirectReadOrWrite` from JsonConverter to JsonTypeInfo. + _converterStrategy = _elementConverter.ConverterStrategy; + CanUseDirectReadOrWrite = _converterStrategy == ConverterStrategy.Value; + } + + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TValueOption value) + { + // `null` values deserialize as `ValueNone` + if (!state.IsContinuation && reader.TokenType == JsonTokenType.Null) + { + value = default; + return true; + } + + state.Current.JsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; + if (_elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element)) + { + value = _optionConstructor(element); + return true; + } + + value = default; + return false; + } + + internal override bool OnTryWrite(Utf8JsonWriter writer, TValueOption value, JsonSerializerOptions options, ref WriteStack state) + { + if (value.Equals(default)) + { + // Write `ValueNone` values as null + writer.WriteNullValue(); + return true; + } + + TElement element = _optionValueGetter(ref value); + + state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo; + return _elementConverter.TryWrite(writer, element, options, ref state); + } + + // Since this is a hybrid converter (ConverterStrategy depends on the element converter), + // we need to override the value converter Write and Read methods too. + + public override void Write(Utf8JsonWriter writer, TValueOption value, JsonSerializerOptions options) + { + if (value.Equals(default)) + { + // Write `ValueNone` values as null + writer.WriteNullValue(); + } + else + { + TElement element = _optionValueGetter(ref value); + _elementConverter.Write(writer, element, options); + } + } + + public override TValueOption Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + TElement? element = _elementConverter.Read(ref reader, typeToConvert, options); + return _optionConstructor(element); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs index 1a3903a5327..aee6ff86db3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs @@ -15,8 +15,14 @@ namespace System.Text.Json.Serialization.Converters /// internal sealed class ObjectConverterFactory : JsonConverterFactory { + // Need to toggle this behavior when generating converters for F# struct records. + private readonly bool _useDefaultConstructorInUnannotatedStructs; + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - public ObjectConverterFactory() { } + public ObjectConverterFactory(bool useDefaultConstructorInUnannotatedStructs = true) + { + _useDefaultConstructorInUnannotatedStructs = useDefaultConstructorInUnannotatedStructs; + } public override bool CanConvert(Type typeToConvert) { @@ -164,7 +170,7 @@ namespace System.Text.Json.Serialization.Converters } // Structs will use default constructor if attribute isn't used. - if (type.IsValueType && ctorWithAttribute == null) + if (_useDefaultConstructorInUnannotatedStructs && type.IsValueType && ctorWithAttribute == null) { return null; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 33f45fd3ced..3a83812d339 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -545,9 +545,17 @@ namespace System.Text.Json.Serialization break; default: - // A non-value converter (object or collection) should always have Start and End tokens. + // A non-value converter (object or collection) should always have Start and End tokens + if (!isValueConverter) + { + // with the exception of converters that support null value reads + if (!HandleNullOnRead || tokenType != JsonTokenType.Null) + { + ThrowHelper.ThrowJsonException_SerializationConverterRead(this); + } + } // A value converter should not make any reads. - if (!isValueConverter || reader.BytesConsumed != bytesConsumed) + else if (reader.BytesConsumed != bytesConsumed) { ThrowHelper.ThrowJsonException_SerializationConverterRead(this); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index a5270028def..27bcc4fb961 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -38,6 +38,7 @@ namespace System.Text.Json new NullableConverterFactory(), new EnumConverterFactory(), new JsonNodeConverterFactory(), + new FSharpTypeConverterFactory(), // IAsyncEnumerable takes precedence over IEnumerable. new IAsyncEnumerableConverterFactory(), // IEnumerable should always be second to last since they can convert any IEnumerable. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs new file mode 100644 index 00000000000..a201c4b2fd9 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs @@ -0,0 +1,248 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace System.Text.Json.Serialization.Metadata +{ + // Recognizing types emitted by the F# compiler requires consuming APIs from the FSharp.Core runtime library. + // Every F# application ships with a copy of FSharp.Core, however it is not available statically to System.Text.Json. + // The following class uses reflection to access the relevant APIs required to detect the various F# types we are looking to support. + + /// + /// Proxy class used to access FSharp.Core metadata and reflection APIs that are not statically available to System.Text.Json. + /// + internal sealed class FSharpCoreReflectionProxy + { + /// + /// The various categories of F# types that System.Text.Json supports. + /// + public enum FSharpKind + { + Unrecognized, + Option, + ValueOption, + List, + Set, + Map, + Record, + Union + } + + // Binding a struct getter method to a delegate requires that the struct parameter is passed byref. + public delegate TResult StructGetter(ref TStruct @this) where TStruct : struct; + + public const string FSharpCoreUnreferencedCodeMessage = "Uses Reflection to access FSharp.Core components at runtime."; + + private static FSharpCoreReflectionProxy? s_singletonInstance; + + // Every type generated by the F# compiler is annotated with the CompilationMappingAttribute + // containing all relevant metadata required to determine its kind: + // https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-compilationmappingattribute.html#SourceConstructFlags + private const string CompilationMappingAttributeTypeName = "Microsoft.FSharp.Core.CompilationMappingAttribute"; + private readonly Type _compilationMappingAttributeType; + private readonly MethodInfo? _sourceConstructFlagsGetter; + + private readonly Type? _fsharpOptionType; + private readonly Type? _fsharpValueOptionType; + private readonly Type? _fsharpListType; + private readonly Type? _fsharpSetType; + private readonly Type? _fsharpMapType; + + private readonly MethodInfo? _fsharpListCtor; + private readonly MethodInfo? _fsharpSetCtor; + private readonly MethodInfo? _fsharpMapCtor; + + /// + /// Checks if the provided System.Type instance is emitted by the F# compiler. + /// If true, also initializes the proxy singleton for future by other F# types. + /// + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public static bool IsFSharpType(Type type) + { + if (s_singletonInstance is null) + { + if (GetFSharpCoreAssembly(type) is Assembly fsharpCoreAssembly) + { + // Type is F# type, initialize the singleton instance. + s_singletonInstance ??= new FSharpCoreReflectionProxy(fsharpCoreAssembly); + + return true; + } + + return false; + } + + return s_singletonInstance.GetFSharpCompilationMappingAttribute(type) is not null; + } + + /// + /// Gets the singleton proxy instance; prerequires a successful IsFSharpType call for proxy initialization. + /// + public static FSharpCoreReflectionProxy Instance + { + get + { + Debug.Assert(s_singletonInstance is not null, "should be initialized via a successful IsFSharpType call."); + return s_singletonInstance; + } + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + private FSharpCoreReflectionProxy(Assembly fsharpCoreAssembly) + { + Debug.Assert(fsharpCoreAssembly.GetName().Name == "FSharp.Core"); + + Type compilationMappingAttributeType = fsharpCoreAssembly.GetType(CompilationMappingAttributeTypeName)!; + _sourceConstructFlagsGetter = compilationMappingAttributeType.GetMethod("get_SourceConstructFlags", BindingFlags.Public | BindingFlags.Instance); + _compilationMappingAttributeType = compilationMappingAttributeType; + + _fsharpOptionType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Core.FSharpOption`1"); + _fsharpValueOptionType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Core.FSharpValueOption`1"); + _fsharpListType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpList`1"); + _fsharpSetType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpSet`1"); + _fsharpMapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2"); + + _fsharpListCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.ListModule")?.GetMethod("OfSeq", BindingFlags.Public | BindingFlags.Static); + _fsharpSetCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.SetModule")?.GetMethod("OfSeq", BindingFlags.Public | BindingFlags.Static); + _fsharpMapCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.MapModule")?.GetMethod("OfSeq", BindingFlags.Public | BindingFlags.Static); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public FSharpKind DetectFSharpKind(Type type) + { + Attribute? compilationMappingAttribute = GetFSharpCompilationMappingAttribute(type); + + if (compilationMappingAttribute is null) + { + return FSharpKind.Unrecognized; + } + + if (type.IsGenericType) + { + Type genericType = type.GetGenericTypeDefinition(); + if (genericType == _fsharpOptionType) return FSharpKind.Option; + if (genericType == _fsharpValueOptionType) return FSharpKind.ValueOption; + if (genericType == _fsharpListType) return FSharpKind.List; + if (genericType == _fsharpSetType) return FSharpKind.Set; + if (genericType == _fsharpMapType) return FSharpKind.Map; + } + + return (GetSourceConstructFlags(compilationMappingAttribute) & SourceConstructFlags.KindMask) switch + { + SourceConstructFlags.RecordType => FSharpKind.Record, + SourceConstructFlags.SumType => FSharpKind.Union, + _ => FSharpKind.Unrecognized + }; + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func CreateFSharpOptionValueGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TFSharpOption, T>() + { + Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpOptionType); + MethodInfo valueGetter = EnsureMemberExists(typeof(TFSharpOption).GetMethod("get_Value", BindingFlags.Public | BindingFlags.Instance), "Microsoft.FSharp.Core.FSharpOption.get_Value()"); + return CreateDelegate>(valueGetter); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func CreateFSharpOptionSomeConstructor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TFSharpOption, TElement>() + { + Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpOptionType); + MethodInfo methodInfo = EnsureMemberExists(typeof(TFSharpOption).GetMethod("Some", BindingFlags.Public | BindingFlags.Static), "Microsoft.FSharp.Core.FSharpOption.Some(T value)"); + return CreateDelegate>(methodInfo); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public StructGetter CreateFSharpValueOptionValueGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TFSharpValueOption, TElement>() + where TFSharpValueOption : struct + { + Debug.Assert(typeof(TFSharpValueOption).GetGenericTypeDefinition() == _fsharpValueOptionType); + MethodInfo valueGetter = EnsureMemberExists(typeof(TFSharpValueOption).GetMethod("get_Value", BindingFlags.Public | BindingFlags.Instance), "Microsoft.FSharp.Core.FSharpValueOption.get_Value()"); + return CreateDelegate>(valueGetter); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func CreateFSharpValueOptionSomeConstructor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TFSharpOption, TElement>() + { + Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpValueOptionType); + MethodInfo methodInfo = EnsureMemberExists(typeof(TFSharpOption).GetMethod("Some", BindingFlags.Public | BindingFlags.Static), "Microsoft.FSharp.Core.FSharpValueOption.ValueSome(T value)"); + return CreateDelegate>(methodInfo); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func, TFSharpList> CreateFSharpListConstructor() + { + Debug.Assert(typeof(TFSharpList).GetGenericTypeDefinition() == _fsharpListType); + return CreateDelegate, TFSharpList>>(EnsureMemberExists(_fsharpListCtor, "Microsoft.FSharp.Collections.ListModule.OfSeq(IEnumerable source)").MakeGenericMethod(typeof(TElement))); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func, TFSharpSet> CreateFSharpSetConstructor() + { + Debug.Assert(typeof(TFSharpSet).GetGenericTypeDefinition() == _fsharpSetType); + return CreateDelegate, TFSharpSet>>(EnsureMemberExists(_fsharpSetCtor, "Microsoft.FSharp.Collections.SetModule.OfSeq(IEnumerable source)").MakeGenericMethod(typeof(TElement))); + } + + [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)] + public Func>, TFSharpMap> CreateFSharpMapConstructor() + { + Debug.Assert(typeof(TFSharpMap).GetGenericTypeDefinition() == _fsharpMapType); + return CreateDelegate>, TFSharpMap>>(EnsureMemberExists(_fsharpMapCtor, "Microsoft.FSharp.Collections.MapModule.OfSeq(IEnumerable> source)").MakeGenericMethod(typeof(TKey), typeof(TValue))); + } + + private Attribute? GetFSharpCompilationMappingAttribute(Type type) + => type.GetCustomAttribute(_compilationMappingAttributeType, inherit: true); + + private SourceConstructFlags GetSourceConstructFlags(Attribute compilationMappingAttribute) + => _sourceConstructFlagsGetter is null ? SourceConstructFlags.None : (SourceConstructFlags)_sourceConstructFlagsGetter.Invoke(compilationMappingAttribute, null)!; + + // If the provided type is generated by the F# compiler, returns the runtime FSharp.Core assembly. + private static Assembly? GetFSharpCoreAssembly(Type type) + { + foreach (Attribute attr in type.GetCustomAttributes(inherit: true)) + { + Type attributeType = attr.GetType(); + if (attributeType.FullName == CompilationMappingAttributeTypeName) + { + return attributeType.Assembly; + } + } + + return null; + } + + private static TDelegate CreateDelegate(MethodInfo methodInfo) where TDelegate : Delegate + => (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), methodInfo, throwOnBindFailure: true)!; + + private TMemberInfo EnsureMemberExists(TMemberInfo? memberInfo, string memberName) where TMemberInfo : MemberInfo + { + if (memberInfo is null) + { + ThrowHelper.ThrowMissingMemberException_MissingFSharpCoreMember(memberName); + } + + return memberInfo; + } + + // Replicates the F# source construct flags enum + // https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-sourceconstructflags.html + private enum SourceConstructFlags + { + None = 0, + SumType = 1, + RecordType = 2, + ObjectType = 3, + Field = 4, + Exception = 5, + Closure = 6, + Module = 7, + UnionCase = 8, + Value = 9, + KindMask = 31, + NonPublicRepresentation = 32 + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index dda0e7f12dd..28d244109f0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -296,9 +296,6 @@ namespace System.Text.Json.Serialization.Metadata if (Converter.HandleNullOnWrite) { - // No object, collection, or re-entrancy converter handles null. - Debug.Assert(Converter.ConverterStrategy == ConverterStrategy.Value); - if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 077de6d0537..956fefeee47 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -49,9 +49,6 @@ namespace System.Text.Json.Serialization.Metadata { if (_elementTypeInfo == null && ElementType != null) { - Debug.Assert(PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Enumerable || - PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Dictionary); - _elementTypeInfo = Options.GetOrAddClass(ElementType); } @@ -181,6 +178,8 @@ namespace System.Text.Json.Serialization.Metadata PropertyInfoForTypeInfo = CreatePropertyInfoForTypeInfo(Type, runtimeType, converter, typeNumberHandling, Options); + ElementType = converter.ElementType; + switch (PropertyInfoForTypeInfo.ConverterStrategy) { case ConverterStrategy.Object: @@ -303,14 +302,12 @@ namespace System.Text.Json.Serialization.Metadata break; case ConverterStrategy.Enumerable: { - ElementType = converter.ElementType; CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType); } break; case ConverterStrategy.Dictionary: { KeyType = converter.KeyType; - ElementType = converter.ElementType; CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType); } break; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 61d953ac94c..b8df6c1e10d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -716,5 +716,12 @@ namespace System.Text.Json { throw new InvalidOperationException(SR.Format(SR.NoDefaultOptionsForContext, context.GetType(), type)); } + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowMissingMemberException_MissingFSharpCoreMember(string missingFsharpCoreMember) + { + throw new MissingMemberException(SR.Format(SR.MissingFSharpCoreMember, missingFsharpCoreMember)); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/CollectionTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/CollectionTests.fs new file mode 100644 index 00000000000..af4326d9ccb --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/CollectionTests.fs @@ -0,0 +1,177 @@ +module System.Text.Json.Tests.FSharp.CollectionTests + +open System.Text.Json +open System.Text.Json.Tests.FSharp.Helpers +open Xunit + +let getListsAndSerializations() = seq { + let wrapArgs (list : 'T list) (json : string) = [| box list ; box json |] + + wrapArgs [] "[]" + wrapArgs [1] "[1]" + wrapArgs [1;2;1;2;3;2;2;1;3;3;3] "[1,2,1,2,3,2,2,1,3,3,3]" + wrapArgs [false;true] "[false,true]" + wrapArgs [3.14] "[3.14]" + wrapArgs ["apple";"banana";"cherry"] """["apple","banana","cherry"]""" + wrapArgs [{| x = 0 ; y = 1 |}] """[{"x":0,"y":1}]""" + wrapArgs Unchecked.defaultof "null" // we support null list serialization and deserialization +} + +[] +[] +let ``Lists should have expected serialization`` (list : 'T list) (expectedJson : string) = + let actualJson = JsonSerializer.Serialize list + Assert.Equal(expectedJson, actualJson) + +[] +[] +let ``Lists should have expected deserialization``(expectedList : 'T list) (json : string) = + let actualList = JsonSerializer.Deserialize<'T list> json + Assert.Equal<'T>(expectedList, actualList) + +[] +[] +[] +[] +[] +[] +let ``List deserialization should reject invalid inputs``(json : string) = + Assert.Throws(fun () -> JsonSerializer.Deserialize(json) |> ignore) |> ignore + +[] +let ``List async serialization should be supported``() = async { + let inputs = [1 .. 200] + let expectedJson = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! actualJson = JsonSerializer.SerializeAsync(inputs, options) + + Assert.Equal(expectedJson, actualJson) +} + +[] +let ``List async deserialization should be supported``() = async { + let inputs = [1 .. 200] + let json = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! result = JsonSerializer.DeserializeAsync(json, options) + + Assert.Equal(inputs, result) +} + +let getSetsAndSerializations() = seq { + let wrapArgs (set : Set<'T>) (json : string) = [| box set ; box json |] + + wrapArgs Set.empty "[]" + wrapArgs (set [1]) "[1]" + wrapArgs (set [1;2;3]) "[1,2,3]" + wrapArgs (set [false;true]) "[false,true]" + wrapArgs (set [3.14]) "[3.14]" + wrapArgs (set ["apple";"banana";"cherry"]) """["apple","banana","cherry"]""" + wrapArgs (set [{| x = 0 ; y = 1 |}]) """[{"x":0,"y":1}]""" + wrapArgs Unchecked.defaultof> "null" // we support null set serialization and deserialization +} + +[] +[] +let ``Sets should have expected serialization`` (set : Set<'T>) (expectedJson : string) = + let actualJson = JsonSerializer.Serialize set + Assert.Equal(expectedJson, actualJson) + +[] +[] +let ``Sets should have expected deserialization`` (expectedSet : Set<'T>) (json : string) = + let actualSet = JsonSerializer.Deserialize> json + Assert.Equal>(expectedSet, actualSet) + +[] +let ``Set deserialization should trim duplicate elements`` () = + let expectedSet = set [1;2;3] + let actualSet = JsonSerializer.Deserialize> "[1,2,1,2,3,2,2,1,3,3,3]" + Assert.Equal>(expectedSet, actualSet) + +[] +[] +[] +[] +[] +[] +let ``Set deserialization should reject invalid inputs``(json : string) = + Assert.Throws(fun () -> JsonSerializer.Deserialize>(json) |> ignore) |> ignore + +[] +let ``Set async serialization should be supported``() = async { + let inputs = set [1 .. 200] + let expectedJson = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! actualJson = JsonSerializer.SerializeAsync(inputs, options) + + Assert.Equal(expectedJson, actualJson) +} + +[] +let ``Set async deserialization should be supported``() = async { + let inputs = set [1 .. 200] + let json = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! result = JsonSerializer.DeserializeAsync>(json, options) + + Assert.Equal>(inputs, result) +} + +let getMapsAndSerializations() = seq { + let wrapArgs (set : Map<'K,'V>) (json : string) = [| box set ; box json |] + + wrapArgs Map.empty "{}" + wrapArgs (Map.ofList [("key", "value")]) """{"key":"value"}""" + wrapArgs (Map.ofList [(1, 1); (2, 1)]) """{"1":1,"2":1}""" + wrapArgs (Map.ofList [(false, 1); (true, 1)]) """{"False":1,"True":1}""" + wrapArgs (Map.ofList [("fruit", ["apple";"banana";"cherry"])]) """{"fruit":["apple","banana","cherry"]}""" + wrapArgs (Map.ofList [("coordinates", {| x = 0 ; y = 1 |})]) """{"coordinates":{"x":0,"y":1}}""" + wrapArgs Unchecked.defaultof> "null" // we support null set serialization and deserialization +} + +[] +[] +let ``Maps should have expected serialization`` (map : Map<'key, 'value>) (expectedJson : string) = + let actualJson = JsonSerializer.Serialize map + Assert.Equal(expectedJson, actualJson) + +[] +[] +let ``Maps should have expected deserialization`` (expectedMap : Map<'key, 'value>) (json : string) = + let actualMap = JsonSerializer.Deserialize> json + Assert.Equal>(expectedMap, actualMap) + +[] +[] +[] +[] +[] +let ``Map deserialization should reject invalid inputs``(json : string) = + Assert.Throws(fun () -> JsonSerializer.Deserialize>(json) |> ignore) |> ignore + +[] +let ``Map async serialization should be supported``() = async { + let inputs = Map.ofList [for i in 1 .. 200 -> (i.ToString(), i)] + let expectedJson = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! actualJson = JsonSerializer.SerializeAsync(inputs, options) + + Assert.Equal(expectedJson, actualJson) +} + +[] +let ``Map async deserialization should be supported``() = async { + let inputs = Map.ofList [for i in 1 .. 200 -> (i.ToString(), i)] + let json = JsonSerializer.Serialize inputs + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! result = JsonSerializer.DeserializeAsync>(json, options) + + Assert.Equal>(inputs, result) +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/Helpers.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/Helpers.fs new file mode 100644 index 00000000000..b2310053beb --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/Helpers.fs @@ -0,0 +1,19 @@ +module System.Text.Json.Tests.FSharp.Helpers + +open System.IO +open System.Text +open System.Text.Json + +type JsonSerializer with + static member SerializeAsync<'T>(value : 'T, options : JsonSerializerOptions) = async { + let! ct = Async.CancellationToken + use mem = new MemoryStream() + do! JsonSerializer.SerializeAsync(mem, value, options, ct) |> Async.AwaitTask + return Encoding.UTF8.GetString(mem.ToArray()) + } + + static member DeserializeAsync<'T>(json : string, options : JsonSerializerOptions) = async { + let! ct = Async.CancellationToken + use mem = new MemoryStream(Encoding.UTF8.GetBytes json) + return! JsonSerializer.DeserializeAsync<'T>(mem, options).AsTask() |> Async.AwaitTask + } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/OptionTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/OptionTests.fs new file mode 100644 index 00000000000..ba97af58af7 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/OptionTests.fs @@ -0,0 +1,166 @@ +module System.Text.Json.Tests.FSharp.OptionTests + +open System.Text.Json +open System.Text.Json.Serialization +open System.Text.Json.Tests.FSharp.Helpers +open Xunit + +let getOptionalElementInputs() = seq { + let wrap value = [| box value |] + + wrap 42 + wrap false + wrap "string" + wrap [|1..5|] + wrap (3,2) + wrap {| Name = "Mary" ; Age = 32 |} + wrap struct {| Name = "Mary" ; Age = 32 |} + wrap [false; true; false; false] + wrap (Set.ofSeq [1 .. 5]) + wrap (Map.ofSeq [("key1", "value1"); ("key2", "value2")]) +} + +[] +[] +let ``Root-level None should serialize as null``(_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T option>(None) + Assert.Equal(expected, actual) + +[] +[] +let ``None property should serialize as null``(_ : 'T) = + let expected = """{"value":null}""" + let actual = JsonSerializer.Serialize {| value = Option<'T>.None |} + Assert.Equal(expected, actual) + +[] +[] +let ``None collection element should serialize as null``(_ : 'T) = + let expected = """[null]""" + let actual = JsonSerializer.Serialize [| Option<'T>.None |] + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level Some should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize(value) + let actual = JsonSerializer.Serialize(Some value) + Assert.Equal(expected, actual) + +[] +[] +let ``Some property should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize {| value = value |} + let actual = JsonSerializer.Serialize {| value = Some value |} + Assert.Equal(expected, actual) + +[] +[] +let ``Some collection element should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize [|value|] + let actual = JsonSerializer.Serialize [|Some value|] + Assert.Equal(expected, actual) + +[] +let ``Some of null should serialize as null`` () = + let expected = "null" + let actual = JsonSerializer.Serialize(Some null) + Assert.Equal(expected, actual) + +[] +[] +let ``Some of None should serialize as null`` (_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T option option>(Some None) + Assert.Equal(expected, actual) + +[] +[] +let ``Some of Some of None should serialize as null`` (_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T option option option>(Some (Some None)) + Assert.Equal(expected, actual) + +[] +[] +let ``Some of Some of value should serialize as value`` (value : 'T) = + let expected = JsonSerializer.Serialize value + let actual = JsonSerializer.Serialize(Some (Some value)) + Assert.Equal(expected, actual) + +[] +[] +let ``WhenWritingNull enabled should skip None properties``(_ : 'T) = + let expected = "{}" + let options = new JsonSerializerOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull) + let actual = JsonSerializer.Serialize<{| value : 'T option |}>({| value = None |}, options) + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level null should deserialize as None``(_ : 'T) = + let actual = JsonSerializer.Deserialize<'T option>("null") + Assert.Equal(None, actual) + +[] +[] +let ``Null property should deserialize as None``(_ : 'T) = + let actual = JsonSerializer.Deserialize<{| value : 'T option |}>("""{"value":null}""") + Assert.Equal(None, actual.value) + +[] +[] +let ``Missing property should deserialize as None``(_ : 'T) = + let actual = JsonSerializer.Deserialize<{| value : 'T option |}>("{}") + Assert.Equal(None, actual.value) + +[] +[] +let ``Null element should deserialize as None``(_ : 'T) = + let expected = [Option<'T>.None] + let actual = JsonSerializer.Deserialize<'T option []>("""[null]""") + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level value should deserialize as Some``(value : 'T) = + let json = JsonSerializer.Serialize(value) + let actual = JsonSerializer.Deserialize<'T option>(json) + Assert.Equal(Some value, actual) + +[] +[] +let ``Property value should deserialize as Some``(value : 'T) = + let json = JsonSerializer.Serialize {| value = value |} + let actual = JsonSerializer.Deserialize<{| value : 'T option|}>(json) + Assert.Equal(Some value, actual.value) + +[] +[] +let ``Collection element should deserialize as Some``(value : 'T) = + let json = JsonSerializer.Serialize [| value |] + let actual = JsonSerializer.Deserialize<'T option []>(json) + Assert.Equal([Some value], actual) + +[] +let ``Optional value should support resumable serialization``() = async { + let valueToSerialize = {| Values = Some [|1 .. 200|] |} + let expectedJson = JsonSerializer.Serialize valueToSerialize + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! actualJson = JsonSerializer.SerializeAsync(valueToSerialize, options) + + Assert.Equal(expectedJson, actualJson) +} + +[] +let ``Optional value should support resumable deserialization``() = async { + let valueToSerialize = {| Values = Some [|1 .. 200|] |} + let json = JsonSerializer.Serialize valueToSerialize + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! result = JsonSerializer.DeserializeAsync<{| Values : int [] option |}>(json, options) + + Assert.Equal(valueToSerialize, result) +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs new file mode 100644 index 00000000000..0d78940904b --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs @@ -0,0 +1,51 @@ +module System.Text.Json.Tests.FSharp.RecordTests + +open System.Text.Json +open System.Text.Json.Serialization +open System.Text.Json.Tests.FSharp.Helpers +open Xunit + +type MyRecord = + { + Name : string + MiddleName : string option + LastName : string + Age : int + IsActive : bool + } +with + static member Value = { Name = "John" ; MiddleName = None ; LastName = "Doe" ; Age = 34 ; IsActive = true } + static member ExpectedJson = """{"Name":"John","MiddleName":null,"LastName":"Doe","Age":34,"IsActive":true}""" + +[] +let ``Support F# record serialization``() = + let actualJson = JsonSerializer.Serialize(MyRecord.Value) + Assert.Equal(MyRecord.ExpectedJson, actualJson) + +[] +let ``Support F# record deserialization``() = + let result = JsonSerializer.Deserialize(MyRecord.ExpectedJson) + Assert.Equal(MyRecord.Value, result) + +[] +type MyStructRecord = + { + Name : string + MiddleName : string option + LastName : string + Age : int + IsActive : bool + } +with + static member Value = { Name = "John" ; MiddleName = None ; LastName = "Doe" ; Age = 34 ; IsActive = true } + static member ExpectedJson = """{"Name":"John","MiddleName":null,"LastName":"Doe","Age":34,"IsActive":true}""" + +[] +let ``Support F# struct record serialization``() = + let actualJson = JsonSerializer.Serialize(MyStructRecord.Value) + Assert.Equal(MyStructRecord.ExpectedJson, actualJson) + +[] +let ``Support F# struct record deserialization``() = + let result = JsonSerializer.Deserialize(MyStructRecord.ExpectedJson) + Assert.Equal(MyStructRecord.Value, result) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/System.Text.Json.FSharp.Tests.fsproj b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/System.Text.Json.FSharp.Tests.fsproj new file mode 100644 index 00000000000..3fef790dcfe --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/System.Text.Json.FSharp.Tests.fsproj @@ -0,0 +1,16 @@ + + + + $(NetCoreAppCurrent) + + + + + + + + + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/UnionTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/UnionTests.fs new file mode 100644 index 00000000000..d149ea6c27a --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/UnionTests.fs @@ -0,0 +1,39 @@ +module System.Text.Json.Tests.FSharp.UnionTests + +open System +open System.Text.Json +open System.Text.Json.Serialization +open Xunit + +type MySingleCaseUnion = MySingleCaseUnion of string +type MyTypeSafeEnum = Label1 | Label2 | Label3 +type MyMultiCaseUnion = Point | Circle of radius:float | Rectangle of height:float * length:float + +[] +type MyStructSingleCaseUnion = MyStructSingleCaseUnion of string +[] +type MyStructTypeSafeEnum = StructLabel1 | StructLabel2 | StructLabel3 +[] +type MyStructMultiCaseUnion = StructPoint | StructCircle of radius:float | StructRectangle of height:float * length:float + +let getUnionValues() = seq { + let wrap value = [| value :> obj |] + + MySingleCaseUnion "value" |> wrap + Label1 |> wrap + Circle 1. |> wrap + + MyStructSingleCaseUnion "value" |> wrap + StructLabel2 |> wrap + StructCircle 1. |> wrap +} + +[] +[] +let ``Union serialization should throw NotSupportedException`` (value : 'T) = + Assert.Throws(fun () -> JsonSerializer.Serialize(value) |> ignore) + +[] +[] +let ``Union deserialization should throw NotSupportedException`` (value : 'T) = + Assert.Throws(fun () -> JsonSerializer.Deserialize<'T>("{}") |> ignore) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/ValueOptionTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/ValueOptionTests.fs new file mode 100644 index 00000000000..666ad64b8ec --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/ValueOptionTests.fs @@ -0,0 +1,166 @@ +module System.Text.Json.Tests.FSharp.ValueOptionTests + +open System.Text.Json +open System.Text.Json.Serialization +open System.Text.Json.Tests.FSharp.Helpers +open Xunit + +let getOptionalElementInputs() = seq { + let wrap value = [| box value |] + + wrap 42 + wrap false + wrap "string" + wrap [|1..5|] + wrap (3,2) + wrap {| Name = "Mary" ; Age = 32 |} + wrap struct {| Name = "Mary" ; Age = 32 |} + wrap [false; true; false; false] + wrap (Set.ofSeq [1 .. 5]) + wrap (Map.ofSeq [("key1", "value1"); ("key2", "value2")]) +} + +[] +[] +let ``Root-level ValueNone should serialize as null``(_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T voption>(ValueNone) + Assert.Equal(expected, actual) + +[] +[] +let ``ValueNone property should serialize as null``(_ : 'T) = + let expected = """{"value":null}""" + let actual = JsonSerializer.Serialize {| value = ValueOption<'T>.ValueNone |} + Assert.Equal(expected, actual) + +[] +[] +let ``ValueNone collection element should serialize as null``(_ : 'T) = + let expected = """[null]""" + let actual = JsonSerializer.Serialize [| ValueOption<'T>.ValueNone |] + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level ValueSome should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize(value) + let actual = JsonSerializer.Serialize(ValueSome value) + Assert.Equal(expected, actual) + +[] +[] +let ``ValueSome property should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize {| value = value |} + let actual = JsonSerializer.Serialize {| value = ValueSome value |} + Assert.Equal(expected, actual) + +[] +[] +let ``ValueSome collection element should serialize as the payload`` (value : 'T) = + let expected = JsonSerializer.Serialize [|value|] + let actual = JsonSerializer.Serialize [|ValueSome value|] + Assert.Equal(expected, actual) + +[] +let ``ValueSome of null should serialize as null`` () = + let expected = "null" + let actual = JsonSerializer.Serialize(ValueSome null) + Assert.Equal(expected, actual) + +[] +[] +let ``ValueSome of ValueNone should serialize as null`` (_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T voption voption>(ValueSome ValueNone) + Assert.Equal(expected, actual) + +[] +[] +let ``ValueSome of ValueSome of ValueNone should serialize as null`` (_ : 'T) = + let expected = "null" + let actual = JsonSerializer.Serialize<'T voption voption voption>(ValueSome (ValueSome ValueNone)) + Assert.Equal(expected, actual) + +[] +[] +let ``ValueSome of ValueSome of value should serialize as value`` (value : 'T) = + let expected = JsonSerializer.Serialize value + let actual = JsonSerializer.Serialize(ValueSome (ValueSome value)) + Assert.Equal(expected, actual) + +[] +[] +let ``WhenWritingDefault enabled should skip ValueNone properties``(_ : 'T) = + let expected = "{}" + let options = new JsonSerializerOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault) + let actual = JsonSerializer.Serialize<{| value : 'T voption |}>({| value = ValueNone |}, options) + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level null should deserialize as ValueNone``(_ : 'T) = + let actual = JsonSerializer.Deserialize<'T voption>("null") + Assert.Equal(ValueNone, actual) + +[] +[] +let ``Null property should deserialize as ValueNone``(_ : 'T) = + let actual = JsonSerializer.Deserialize<{| value : 'T voption |}>("""{"value":null}""") + Assert.Equal(ValueNone, actual.value) + +[] +[] +let ``Missing property should deserialize as ValueNone``(_ : 'T) = + let actual = JsonSerializer.Deserialize<{| value : 'T voption |}>("{}") + Assert.Equal(ValueNone, actual.value) + +[] +[] +let ``Null element should deserialize as ValueNone``(_ : 'T) = + let expected = [ValueOption<'T>.ValueNone] + let actual = JsonSerializer.Deserialize<'T voption []>("""[null]""") + Assert.Equal(expected, actual) + +[] +[] +let ``Root-level value should deserialize as ValueSome``(value : 'T) = + let json = JsonSerializer.Serialize(value) + let actual = JsonSerializer.Deserialize<'T voption>(json) + Assert.Equal(ValueSome value, actual) + +[] +[] +let ``Property value should deserialize as ValueSome``(value : 'T) = + let json = JsonSerializer.Serialize {| value = value |} + let actual = JsonSerializer.Deserialize<{| value : 'T voption|}>(json) + Assert.Equal(ValueSome value, actual.value) + +[] +[] +let ``Collection element should deserialize as ValueSome``(value : 'T) = + let json = JsonSerializer.Serialize [| value |] + let actual = JsonSerializer.Deserialize<'T voption []>(json) + Assert.Equal([ValueSome value], actual) + +[] +let ``Optional value should support resumable serialization``() = async { + let valueToSerialize = {| Values = ValueSome [|1 .. 200|] |} + let expectedJson = JsonSerializer.Serialize valueToSerialize + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! actualJson = JsonSerializer.SerializeAsync(valueToSerialize, options) + + Assert.Equal(expectedJson, actualJson) +} + +[] +let ``Optional value should support resumable deserialization``() = async { + let valueToSerialize = {| Values = ValueSome [|1 .. 200|] |} + let json = JsonSerializer.Serialize valueToSerialize + + let options = new JsonSerializerOptions(DefaultBufferSize = 1) + let! result = JsonSerializer.DeserializeAsync<{| Values : int [] voption |}>(json, options) + + Assert.Equal(valueToSerialize, result) +} From 2e93fab1fdedc3fd753c866df57e7c2514e94ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 15 Jul 2021 10:46:47 -0400 Subject: [PATCH 588/926] [mono] Disable unsupported apply update scenarios (#55698) * Disallow mixing ApplyChanges and debugger apply changes * Don't allow debugger thread to start if ApplyChanges has been called * Don't allow ApplyChanges managed API call if debugger is attached * MONO_COMPONENT_API mono_error_set_not_supported --- src/mono/mono/component/debugger-agent.c | 14 +++++++++++++- src/mono/mono/component/hot_reload-stub.c | 4 ++-- src/mono/mono/component/hot_reload.c | 15 +++++++++++++-- src/mono/mono/component/hot_reload.h | 2 +- src/mono/mono/metadata/icall.c | 10 +++++++++- src/mono/mono/metadata/metadata-internals.h | 12 +++++++++++- src/mono/mono/metadata/metadata-update.c | 10 ++++++++-- src/mono/mono/utils/mono-error-internals.h | 1 + 8 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 4a01dd258ad..45daa0481ce 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -6575,7 +6575,7 @@ module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoAr int32_t dil_len = mono_array_length_internal (dil); gpointer dpdb_bytes = !dpdb ? NULL : (gpointer)mono_array_addr_internal (dpdb, char, 0); int32_t dpdb_len = !dpdb ? 0 : mono_array_length_internal (dpdb); - mono_image_load_enc_delta (image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); + mono_image_load_enc_delta (MONO_ENC_DELTA_DBG, image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); return is_ok (error); } @@ -10203,6 +10203,18 @@ debugger_thread (void *arg) mono_set_is_debugger_attached (TRUE); } +#ifndef HOST_WASM + if (!attach_failed) { + if (mono_metadata_has_updates_api ()) { + PRINT_DEBUG_MSG (1, "[dbg] Cannot attach after System.Reflection.Metadata.MetadataUpdater.ApplyChanges has been called.\n"); + attach_failed = TRUE; + command_set = (CommandSet)0; + command = 0; + dispose_vm (); + } + } +#endif + while (!attach_failed) { res = transport_recv (header, HEADER_LENGTH); diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 0ebf79a50c8..35b8fe38a56 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -15,7 +15,7 @@ static bool hot_reload_stub_available (void); static void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); +hot_reload_stub_apply_changes (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static MonoComponentHotReload * component_hot_reload_stub_init (void); @@ -147,7 +147,7 @@ hot_reload_stub_relative_delta_index (MonoImage *image_dmeta, int token) } void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) +hot_reload_stub_apply_changes (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { mono_error_set_not_supported (error, "Hot reload not supported in this runtime."); } diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 2cd1283454d..d76fc8c5ad2 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -54,7 +54,7 @@ static void hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx); static void -hot_reload_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); +hot_reload_apply_changes (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static int hot_reload_relative_delta_index (MonoImage *image_dmeta, int token); @@ -1349,13 +1349,24 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * LOCKING: Takes the publish_lock */ void -hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) +hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { if (!assembly_update_supported (image_base->assembly)) { mono_error_set_invalid_operation (error, "The assembly can not be edited or changed."); return; } + static int first_origin = -1; + + if (first_origin < 0) { + first_origin = origin; + } + + if (first_origin != origin) { + mono_error_set_not_supported (error, "Applying deltas through the debugger and System.Reflection.Metadata.MetadataUpdater.ApplyUpdate simultaneously is not supported"); + return; + } + const char *basename = image_base->filename; if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) { diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 26ca0089a69..37dc1401bbd 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -23,7 +23,7 @@ typedef struct _MonoComponentHotReload { void (*cleanup_on_close) (MonoImage *image); void (*effective_table_slow) (const MonoTableInfo **t, int *idx); int (*relative_delta_index) (MonoImage *image_dmeta, int token); - void (*apply_changes) (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); + void (*apply_changes) (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); void (*image_close_except_pools_all) (MonoImage *base_image); void (*image_close_all) (MonoImage *base_image); gpointer (*get_updated_method_rva) (MonoImage *base_image, uint32_t idx); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 240479fb90a..b4f4d07502b 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5783,7 +5783,15 @@ ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, MonoImage *image_base = assm->image; g_assert (image_base); - mono_image_load_enc_delta (image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); +#ifndef HOST_WASM + if (mono_is_debugger_attached ()) { + mono_error_set_not_supported (error, "Cannot use System.Reflection.Metadata.MetadataUpdater.ApplyChanges while debugger is attached"); + mono_error_set_pending_exception (error); + return; + } +#endif + + mono_image_load_enc_delta (MONO_ENC_DELTA_API, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); mono_error_set_pending_exception (error); } diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 9a0d030a9f1..2aea7227de7 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -802,6 +802,11 @@ mono_metadata_has_updates (void) return mono_metadata_update_data_private.has_updates != 0; } +/* components can't call the inline function directly since the private data isn't exported */ +MONO_COMPONENT_API +gboolean +mono_metadata_has_updates_api (void); + void mono_image_effective_table_slow (const MonoTableInfo **t, int *idx); @@ -821,8 +826,13 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) int mono_image_relative_delta_index (MonoImage *image_dmeta, int token); +enum MonoEnCDeltaOrigin { + MONO_ENC_DELTA_API = 0, + MONO_ENC_DELTA_DBG = 1, +}; + MONO_COMPONENT_API void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error); +mono_image_load_enc_delta (int delta_origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error); gboolean mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 71520b8aa1e..99a2d99a5d7 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -73,9 +73,9 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token) } void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error) +mono_image_load_enc_delta (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error) { - mono_component_hot_reload ()->apply_changes (base_image, dmeta, dmeta_len, dil, dil_len, dpdb, dpdb_len, error); + mono_component_hot_reload ()->apply_changes (origin, base_image, dmeta, dmeta_len, dil, dil_len, dpdb, dpdb_len, error); if (is_ok (error)) { mono_component_debugger ()->send_enc_delta (base_image, dmeta, dmeta_len, dpdb, dpdb_len); } @@ -135,3 +135,9 @@ mono_metadata_update_has_modified_rows (const MonoTableInfo *table) { return mono_component_hot_reload ()->has_modified_rows (table); } + +gboolean +mono_metadata_has_updates_api (void) +{ + return mono_metadata_has_updates (); +} diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h index 2256c6b5adb..a26558689ae 100644 --- a/src/mono/mono/utils/mono-error-internals.h +++ b/src/mono/mono/utils/mono-error-internals.h @@ -205,6 +205,7 @@ mono_error_set_execution_engine (MonoError *error, const char *msg_format, ...) void mono_error_set_not_implemented (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3); +MONO_COMPONENT_API void mono_error_set_not_supported (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3); From bd9d4e0a4776fa8eb8320e1665b0a05b1a1ad3ea Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 15 Jul 2021 07:51:44 -0700 Subject: [PATCH 589/926] JIT: enable GDV when static class deduction fails. (#55660) Update the early bailout in impDevirtualizeCall that gives up if the object class cannot not be determined to check if we can do GDV (guarded devirtualization) based on profile data. Fixes one of the cases noted in #55079. --- src/coreclr/jit/importer.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b3857cdec58..5d6c0d7354f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -20935,6 +20935,13 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, return; } + // Fetch information about the class that introduced the virtual method. + CORINFO_CLASS_HANDLE baseClass = info.compCompHnd->getMethodClass(baseMethod); + const DWORD baseClassAttribs = info.compCompHnd->getClassAttribs(baseClass); + + // Is the call an interface call? + const bool isInterface = (baseClassAttribs & CORINFO_FLG_INTERFACE) != 0; + // See what we know about the type of 'this' in the call. GenTree* thisObj = call->gtCallThisArg->GetNode()->gtEffectiveVal(false); bool isExact = false; @@ -20942,19 +20949,24 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); // Bail if we know nothing. - if (objClass == nullptr) + if (objClass == NO_CLASS_HANDLE) { JITDUMP("\nimpDevirtualizeCall: no type available (op=%s)\n", GenTree::OpName(thisObj->OperGet())); + + // Don't try guarded devirtualiztion when we're doing late devirtualization. + // + if (isLateDevirtualization) + { + JITDUMP("No guarded devirt during late devirtualization\n"); + return; + } + + considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, + pContextHandle DEBUGARG(objClass) DEBUGARG("unknown")); + return; } - // Fetch information about the class that introduced the virtual method. - CORINFO_CLASS_HANDLE baseClass = info.compCompHnd->getMethodClass(baseMethod); - const DWORD baseClassAttribs = info.compCompHnd->getClassAttribs(baseClass); - - // Is the call an interface call? - const bool isInterface = (baseClassAttribs & CORINFO_FLG_INTERFACE) != 0; - // If the objClass is sealed (final), then we may be able to devirtualize. const DWORD objClassAttribs = info.compCompHnd->getClassAttribs(objClass); const bool objClassIsFinal = (objClassAttribs & CORINFO_FLG_FINAL) != 0; From 7aaffcb2b3d42c79a23a86aecd6d0e2df049ee44 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Thu, 15 Jul 2021 08:14:43 -0700 Subject: [PATCH 590/926] more fixes for msquic packaging (#55607) * more fixes for msquic packaging * feedback from review --- .../pkg/sfx/Microsoft.NETCore.App/Directory.Build.props | 1 + src/libraries/System.Net.Quic/src/System.Net.Quic.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 79af7fd8c90..19c48f2ec85 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -161,6 +161,7 @@ + diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 1cd17da1d87..1d31aab803c 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -89,6 +89,7 @@ + From 663013d579d5eb3e29b2e0f88aeaed0694e74f39 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Thu, 15 Jul 2021 09:34:36 -0700 Subject: [PATCH 591/926] Use alternatives to redirecting threads via IP changes in the context, for a few cases on Windows (#55649) * Use alternatives to redirecting threads via IP changes in the context, for a few cases on Windows - For GC/debugger suspension, a new variant of an APC is used to interrupt the thread - For hardware exceptions, an exception is raised from the vectored exception handler instead of redirecting and then raising an exception - Made changes to enable /guard:ehcont and /cetcompat for binaries, but commented out the enablement for now since it looks like we will first need .pgd files generated with /guard:ehcont and published for release builds to succeed --- eng/native/configurecompiler.cmake | 17 +- src/coreclr/clrdefinitions.cmake | 4 + src/coreclr/debug/ee/debugger.cpp | 28 +- src/coreclr/debug/ee/debugger.h | 10 +- src/coreclr/dlls/clretwrc/CMakeLists.txt | 6 +- src/coreclr/dlls/mscorrc/CMakeLists.txt | 6 +- .../vm/amd64/RedirectedHandledJITCase.asm | 33 +- src/coreclr/vm/amd64/asmconstants.h | 4 + src/coreclr/vm/ceemain.cpp | 1 + src/coreclr/vm/dbginterface.h | 2 +- src/coreclr/vm/excep.cpp | 28 +- src/coreclr/vm/threads.cpp | 55 +++ src/coreclr/vm/threads.h | 120 ++++++- src/coreclr/vm/threadsuspend.cpp | 332 ++++++++++++------ src/libraries/Native/Windows/CMakeLists.txt | 11 +- src/tests/Interop/IJW/IJW.cmake | 5 + 16 files changed, 528 insertions(+), 134 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 685949ca33d..92a715f38d9 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -48,7 +48,10 @@ add_compile_definitions("$<$:DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDL add_compile_definitions("$<$,$>:NDEBUG;URTBLDENV_FRIENDLY=Retail>") if (MSVC) - add_linker_flag(/GUARD:CF) + add_linker_flag(/guard:cf) + #if (CLR_CMAKE_HOST_ARCH_AMD64) + # add_linker_flag(/guard:ehcont) + #endif (CLR_CMAKE_HOST_ARCH_AMD64) # Linker flags # @@ -71,6 +74,10 @@ if (MSVC) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /IGNORE:4197,4013,4254,4070,4221") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,${WINDOWS_SUBSYSTEM_VERSION}") + #if (CLR_CMAKE_HOST_ARCH_AMD64) + # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /CETCOMPAT") + #endif (CLR_CMAKE_HOST_ARCH_AMD64) + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /IGNORE:4221") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") @@ -565,6 +572,14 @@ if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /guard:cf") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:cf") + # Enable EH-continuation table for native components for amd64 builds + # Added using variables instead of add_compile_options to let individual projects override it + #if (CLR_CMAKE_HOST_ARCH_AMD64) + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /guard:ehcont") + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") + # set(CMAKE_ASM_MASM_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") + #endif (CLR_CMAKE_HOST_ARCH_AMD64) + # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid # linking in VCRUNTIME140.DLL for a simplified xcopy experience by reducing the dependency on VC REDIST. # diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 0485ff99a99..257a9c377e3 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -228,6 +228,10 @@ if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) add_compile_definitions($<$>>:FEATURE_EH_FUNCLETS>) endif (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) +if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) + add_definitions(-DFEATURE_SPECIAL_USER_MODE_APC) +endif() + # Use this function to enable building with a specific target OS and architecture set of defines # This is known to work for the set of defines used by the JIT and gcinfo, it is not likely correct for diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index e4563a31757..9f7e2860594 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -992,10 +992,9 @@ Debugger::~Debugger() } #if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) -typedef void (*PFN_HIJACK_FUNCTION) (void); // Given the start address and the end address of a function, return a MemoryRange for the function. -inline MemoryRange GetMemoryRangeForFunction(PFN_HIJACK_FUNCTION pfnStart, PFN_HIJACK_FUNCTION pfnEnd) +inline MemoryRange GetMemoryRangeForFunction(void *pfnStart, void *pfnEnd) { PCODE pfnStartAddress = (PCODE)GetEEFuncEntryPoint(pfnStart); PCODE pfnEndAddress = (PCODE)GetEEFuncEntryPoint(pfnEnd); @@ -1016,6 +1015,11 @@ MemoryRange Debugger::s_hijackFunction[kMaxHijackFunctions] = GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCStress_Stub, RedirectedHandledJITCaseForGCStress_StubEnd) #endif // HAVE_GCCOVER && TARGET_AMD64 +#ifdef FEATURE_SPECIAL_USER_MODE_APC + , + GetMemoryRangeForFunction(ApcActivationCallbackStub, + ApcActivationCallbackStubEnd) +#endif // FEATURE_SPECIAL_USER_MODE_APC }; #endif // FEATURE_HIJACK && !TARGET_UNIX @@ -15890,7 +15894,7 @@ HRESULT Debugger::UpdateSpecialThreadList(DWORD cThreadArrayLength, // // 3) If the IP is in the prolog or epilog of a managed function. // -BOOL Debugger::IsThreadContextInvalid(Thread *pThread) +BOOL Debugger::IsThreadContextInvalid(Thread *pThread, CONTEXT *pCtx) { CONTRACTL { @@ -15902,14 +15906,22 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread) BOOL invalid = FALSE; // Get the thread context. + BOOL success = pCtx != NULL; CONTEXT ctx; - ctx.ContextFlags = CONTEXT_CONTROL; - BOOL success = pThread->GetThreadContext(&ctx); + if (!success) + { + ctx.ContextFlags = CONTEXT_CONTROL; + BOOL success = pThread->GetThreadContext(&ctx); + if (success) + { + pCtx = &ctx; + } + } if (success) { // Check single-step flag - if (IsSSFlagEnabled(reinterpret_cast(&ctx) ARM_ARG(pThread) ARM64_ARG(pThread))) + if (IsSSFlagEnabled(reinterpret_cast(pCtx) ARM_ARG(pThread) ARM64_ARG(pThread))) { // Can't hijack a thread whose SS-flag is set. This could lead to races // with the thread taking the SS-exception. @@ -15923,7 +15935,7 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread) { #ifdef TARGET_X86 // Grab Eip - 1 - LPVOID address = (((BYTE*)GetIP(&ctx)) - 1); + LPVOID address = (((BYTE*)GetIP(pCtx)) - 1); EX_TRY { @@ -15934,7 +15946,7 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread) if (AddressIsBreakpoint((CORDB_ADDRESS_TYPE*)address)) { size_t prologSize; // Unused... - if (g_pEEInterface->IsInPrologOrEpilog((BYTE*)GetIP(&ctx), &prologSize)) + if (g_pEEInterface->IsInPrologOrEpilog((BYTE*)GetIP(pCtx), &prologSize)) { LOG((LF_CORDB, LL_INFO1000, "D::ITCI: thread is after a BP and in prolog or epilog.\n")); invalid = TRUE; diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 5503de24590..4f8b13cf668 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -2505,7 +2505,7 @@ public: SIZE_T *rgVal2, BYTE **rgpVCs); - BOOL IsThreadContextInvalid(Thread *pThread); + BOOL IsThreadContextInvalid(Thread *pThread, T_CONTEXT *pCtx); // notification for SQL fiber debugging support void CreateConnection(CONNID dwConnectionId, __in_z WCHAR *wzName); @@ -2875,6 +2875,9 @@ private: #if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) kRedirectedForGCStress, #endif // HAVE_GCCOVER && TARGET_AMD64 +#ifdef FEATURE_SPECIAL_USER_MODE_APC + kRedirectedForApcActivation, +#endif // FEATURE_SPECIAL_USER_MODE_APC kMaxHijackFunctions, }; @@ -2976,6 +2979,11 @@ void RedirectedHandledJITCaseForUserSuspend_StubEnd(); void RedirectedHandledJITCaseForGCStress_Stub(); void RedirectedHandledJITCaseForGCStress_StubEnd(); #endif // HAVE_GCCOVER && TARGET_AMD64 + +#ifdef FEATURE_SPECIAL_USER_MODE_APC +void NTAPI ApcActivationCallbackStub(ULONG_PTR Parameter); +void ApcActivationCallbackStubEnd(); +#endif // FEATURE_SPECIAL_USER_MODE_APC }; diff --git a/src/coreclr/dlls/clretwrc/CMakeLists.txt b/src/coreclr/dlls/clretwrc/CMakeLists.txt index cc427efcde4..2b749fd7b37 100644 --- a/src/coreclr/dlls/clretwrc/CMakeLists.txt +++ b/src/coreclr/dlls/clretwrc/CMakeLists.txt @@ -7,8 +7,12 @@ if(CLR_CMAKE_HOST_WIN32) string(REPLACE "/LTCG" "" CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}) string(REPLACE "/LTCG" "" CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO ${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO}) - # remove /guard:cf from resource-only libraries + # remove /guard:cf, /guard:ehcont, and /CETCOMPAT from resource-only libraries string(REPLACE "/guard:cf" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + if (CLR_CMAKE_HOST_ARCH_AMD64) + string(REPLACE "/guard:ehcont" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + string(REPLACE "/CETCOMPAT" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + endif (CLR_CMAKE_HOST_ARCH_AMD64) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NOENTRY") endif(CLR_CMAKE_HOST_WIN32) diff --git a/src/coreclr/dlls/mscorrc/CMakeLists.txt b/src/coreclr/dlls/mscorrc/CMakeLists.txt index c6618f0b77b..8011997c1e5 100644 --- a/src/coreclr/dlls/mscorrc/CMakeLists.txt +++ b/src/coreclr/dlls/mscorrc/CMakeLists.txt @@ -8,8 +8,12 @@ if(CLR_CMAKE_HOST_WIN32) string(REPLACE "/LTCG" "" CMAKE_SHARED_LINKER_FLAGS_RELEASE ${CMAKE_SHARED_LINKER_FLAGS_RELEASE}) string(REPLACE "/LTCG" "" CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}) - # remove /guard:cf from resource-only libraries + # remove /guard:cf, /guard:ehcont, and /CETCOMPAT from resource-only libraries string(REPLACE "/guard:cf" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + if (CLR_CMAKE_HOST_ARCH_AMD64) + string(REPLACE "/guard:ehcont" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + string(REPLACE "/CETCOMPAT" "" CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + endif (CLR_CMAKE_HOST_ARCH_AMD64) add_library_clr(mscorrc SHARED include.rc diff --git a/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm b/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm index 5a3efffe358..0168dddb615 100644 --- a/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm +++ b/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm @@ -232,5 +232,36 @@ NESTED_END NakedThrowHelper2, _TEXT GenerateRedirectedStubWithFrame NakedThrowHelper, FixContextHandler, NakedThrowHelper2 - end +ifdef FEATURE_SPECIAL_USER_MODE_APC +extern ?ApcActivationCallback@Thread@@CAX_K@Z:proc + +; extern "C" void NTAPI ApcActivationCallbackStub(ULONG_PTR Parameter); +NESTED_ENTRY ApcActivationCallbackStub, _TEXT, FixRedirectContextHandler + + push_nonvol_reg rbp + alloc_stack 30h ; padding for alignment, CONTEXT *, callee scratch area + set_frame rbp, 0 + .errnz REDIRECTSTUB_ESTABLISHER_OFFSET_RBP, REDIRECTSTUB_ESTABLISHER_OFFSET_RBP has changed - update asm stubs + END_PROLOGUE + + ; Save the pointer to the interrupted context on the stack for the stack walker + mov rax, [rcx + OFFSETOF__APC_CALLBACK_DATA__ContextRecord] + mov [rbp + 20h], rax + .errnz REDIRECTSTUB_RBP_OFFSET_CONTEXT - 20h, REDIRECTSTUB_RBP_OFFSET_CONTEXT has changed - update asm stubs + + call ?ApcActivationCallback@Thread@@CAX_K@Z + + add rsp, 30h + pop rbp + ret + + ; Put a label here to tell the debugger where the end of this function is. + PATCH_LABEL ApcActivationCallbackStubEnd + +NESTED_END ApcActivationCallbackStub, _TEXT + +endif ; FEATURE_SPECIAL_USER_MODE_APC + + + end diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 4cc6ab1de2a..64ff4ebea6c 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -493,6 +493,10 @@ ASMCONSTANTS_C_ASSERT(OFFSET__TEB__ThreadLocalStoragePointer == offsetof(TEB, Th #define THROWSTUB_ESTABLISHER_OFFSET_FaultingExceptionFrame 0x30 +#ifdef FEATURE_SPECIAL_USER_MODE_APC +#define OFFSETOF__APC_CALLBACK_DATA__ContextRecord 0x8 +#endif + #define Thread__ObjectRefFlush ?ObjectRefFlush@Thread@@SAXPEAV1@@Z diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index b60aac924d2..87106bbff4b 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -677,6 +677,7 @@ void EEStartupHelper() IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); + Thread::StaticInitialize(); ThreadpoolMgr::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); diff --git a/src/coreclr/vm/dbginterface.h b/src/coreclr/vm/dbginterface.h index ac09c52311a..4875df7c207 100644 --- a/src/coreclr/vm/dbginterface.h +++ b/src/coreclr/vm/dbginterface.h @@ -363,7 +363,7 @@ public: SIZE_T *rgVal2, BYTE **rgpVCs) = 0; - virtual BOOL IsThreadContextInvalid(Thread *pThread) = 0; + virtual BOOL IsThreadContextInvalid(Thread *pThread, CONTEXT *pCtx) = 0; // For Just-My-Code (aka Just-User-Code). // The jit inserts probes that look like. diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 6bf5efcc802..7ee6baad9a4 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6971,13 +6971,31 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, WRAPPER_NO_CONTRACT; // Ok. Now we have a brand new fault in jitted code. - g_SavedExceptionInfo.Enter(); - g_SavedExceptionInfo.SaveExceptionRecord(pExceptionRecord); - g_SavedExceptionInfo.SaveContext(pContext); + if (!Thread::UseContextBasedThreadRedirection()) + { + // Once this code path gets enough bake time, perhaps this path could always be used instead of the alternative path to + // redirect the thread + FrameWithCookie frameWithCookie; + FaultingExceptionFrame *frame = &frameWithCookie; + #if defined(FEATURE_EH_FUNCLETS) + *frame->GetGSCookiePtr() = GetProcessGSCookie(); + #endif // FEATURE_EH_FUNCLETS + frame->InitAndLink(pContext); - SetNakedThrowHelperArgRegistersInContext(pContext); + SEHException exception(pExceptionRecord); + OBJECTREF managedException = CLRException::GetThrowableFromException(&exception); + RaiseTheExceptionInternalOnly(managedException, FALSE); + } + else + { + g_SavedExceptionInfo.Enter(); + g_SavedExceptionInfo.SaveExceptionRecord(pExceptionRecord); + g_SavedExceptionInfo.SaveContext(pContext); - SetIP(pContext, GetEEFuncEntryPoint(NakedThrowHelper)); + SetNakedThrowHelperArgRegistersInContext(pContext); + + SetIP(pContext, GetEEFuncEntryPoint(NakedThrowHelper)); + } } #else // USE_FEF && !TARGET_UNIX diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index c6485b86d59..b38dc33185a 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -50,6 +50,10 @@ #include "roapi.h" #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT +#ifdef FEATURE_SPECIAL_USER_MODE_APC +#include "asmconstants.h" +#endif + static const PortableTailCallFrame g_sentinelTailCallFrame = { NULL, NULL }; TailCallTls::TailCallTls() @@ -1641,6 +1645,7 @@ Thread::Thread() m_currentPrepareCodeConfig = nullptr; m_isInForbidSuspendForDebuggerRegion = false; + m_hasPendingActivation = false; #ifdef _DEBUG memset(dangerousObjRefs, 0, sizeof(dangerousObjRefs)); @@ -8304,7 +8309,57 @@ BOOL dbgOnly_IsSpecialEEThread() #endif // _DEBUG +void Thread::StaticInitialize() +{ + WRAPPER_NO_CONTRACT; +#ifdef FEATURE_SPECIAL_USER_MODE_APC + InitializeSpecialUserModeApc(); + + // When CET shadow stacks are enabled, support for special user-mode APCs with the necessary functionality is required + _ASSERTE_ALL_BUILDS(__FILE__, !AreCetShadowStacksEnabled() || UseSpecialUserModeApc()); +#endif +} + +#ifdef FEATURE_SPECIAL_USER_MODE_APC + +QueueUserAPC2Proc Thread::s_pfnQueueUserAPC2Proc; + +static void NTAPI EmptyApcCallback(ULONG_PTR Parameter) +{ + LIMITED_METHOD_CONTRACT; +} + +void Thread::InitializeSpecialUserModeApc() +{ + WRAPPER_NO_CONTRACT; + static_assert_no_msg(OFFSETOF__APC_CALLBACK_DATA__ContextRecord == offsetof(CLONE_APC_CALLBACK_DATA, ContextRecord)); + + HMODULE hKernel32 = WszLoadLibraryEx(WINDOWS_KERNEL32_DLLNAME_W, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + + // See if QueueUserAPC2 exists + QueueUserAPC2Proc pfnQueueUserAPC2Proc = (QueueUserAPC2Proc)GetProcAddress(hKernel32, "QueueUserAPC2"); + if (pfnQueueUserAPC2Proc == nullptr) + { + return; + } + + // See if QueueUserAPC2 supports the special user-mode APC with a callback that includes the interrupted CONTEXT. A special + // user-mode APC can interrupt a thread that is in user mode and not in a non-alertable wait. + if (!(*pfnQueueUserAPC2Proc)(EmptyApcCallback, GetCurrentThread(), 0, SpecialUserModeApcWithContextFlags)) + { + return; + } + + // In the future, once code paths using the special user-mode APC get some bake time, it should be used regardless of + // whether CET shadow stacks are enabled + if (AreCetShadowStacksEnabled()) + { + s_pfnQueueUserAPC2Proc = pfnQueueUserAPC2Proc; + } +} + +#endif // FEATURE_SPECIAL_USER_MODE_APC #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 7d600dab5ed..f579984f7ba 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -484,6 +484,10 @@ typedef Thread::ForbidSuspendThreadHolder ForbidSuspendThreadHolder; #define DISABLE_THREADSUSPEND #endif +#if defined(FEATURE_HIJACK) && (defined(TARGET_UNIX) || defined(FEATURE_SPECIAL_USER_MODE_APC)) +#define FEATURE_THREAD_ACTIVATION +#endif + // NT thread priorities range from -15 to +15. #define INVALID_THREAD_PRIORITY ((DWORD)0x80000000) @@ -530,6 +534,41 @@ struct HijackArgs; // manifest constant for waiting in the exposed classlibs const INT32 INFINITE_TIMEOUT = -1; +/***************************************************************************/ +#ifdef FEATURE_SPECIAL_USER_MODE_APC + +// These declarations are for a new special user-mode APC feature introduced in Windows. These are not yet available in Windows +// SDK headers, so some names below are prefixed with "CLONE_" to avoid conflicts in the future. Once the prefixed declarations +// become available in the Windows SDK headers, the prefixed declarations below can be removed in favor of the SDK ones. + +enum CLONE_QUEUE_USER_APC_FLAGS +{ + CLONE_QUEUE_USER_APC_FLAGS_NONE = 0x0, + CLONE_QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC = 0x1, + + CLONE_QUEUE_USER_APC_CALLBACK_DATA_CONTEXT = 0x10000 +}; + +struct CLONE_APC_CALLBACK_DATA +{ + ULONG_PTR Parameter; + PCONTEXT ContextRecord; + ULONG_PTR Reserved0; + ULONG_PTR Reserved1; +}; +typedef CLONE_APC_CALLBACK_DATA *CLONE_PAPC_CALLBACK_DATA; + +typedef BOOL (WINAPI *QueueUserAPC2Proc)(PAPCFUNC ApcRoutine, HANDLE Thread, ULONG_PTR Data, CLONE_QUEUE_USER_APC_FLAGS Flags); + +const CLONE_QUEUE_USER_APC_FLAGS SpecialUserModeApcWithContextFlags = + (CLONE_QUEUE_USER_APC_FLAGS) + ( + CLONE_QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC | // this will be a special user-mode APC + CLONE_QUEUE_USER_APC_CALLBACK_DATA_CONTEXT // the callback's parameter will be a PAPC_CALLBACK_DATA + ); + +#endif // FEATURE_SPECIAL_USER_MODE_APC + /***************************************************************************/ // Public enum shared between thread and threadpool // These are two kinds of threadpool thread that the threadpool mgr needs @@ -1022,9 +1061,9 @@ class Thread // MapWin32FaultToCOMPlusException needs access to Thread::IsAddrOfRedirectFunc() friend DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord); friend void STDCALL OnHijackWorker(HijackArgs * pArgs); -#ifdef TARGET_UNIX - friend void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext); -#endif // TARGET_UNIX +#ifdef FEATURE_THREAD_ACTIVATION + friend void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext); +#endif // FEATURE_THREAD_ACTIVATION #endif // FEATURE_HIJACK @@ -2388,9 +2427,15 @@ public: STR_NoStressLog, }; -#if defined(FEATURE_HIJACK) && defined(TARGET_UNIX) - bool InjectGcSuspension(); -#endif // FEATURE_HIJACK && TARGET_UNIX +#ifdef FEATURE_THREAD_ACTIVATION + enum class ActivationReason + { + SuspendForGC, + SuspendForDebugger, + }; + + bool InjectActivation(ActivationReason reason); +#endif // FEATURE_THREAD_ACTIVATION #ifndef DISABLE_THREADSUSPEND // SuspendThread @@ -4000,9 +4045,12 @@ public: { LIMITED_METHOD_CONTRACT; #ifdef _DEBUG - _ASSERTE(m_RedirectContextInUse); - _ASSERTE(pCtx == m_pSavedRedirectContext); - m_RedirectContextInUse = false; + _ASSERTE(!UseContextBasedThreadRedirection() || m_RedirectContextInUse); + if (m_RedirectContextInUse) + { + _ASSERTE(pCtx == m_pSavedRedirectContext); + m_RedirectContextInUse = false; + } #endif } #endif //DACCESS_COMPILE @@ -4610,6 +4658,60 @@ public: private: bool m_isInForbidSuspendForDebuggerRegion; + +#ifndef DACCESS_COMPILE +public: + static void StaticInitialize(); + +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) + static bool AreCetShadowStacksEnabled() + { + LIMITED_METHOD_CONTRACT; + + // The SSP is null when CET shadow stacks are not enabled. On processors that don't support shadow stacks, this is a + // no-op and the intrinsic returns 0. CET shadow stacks are enabled or disabled for all threads, so the result is the + // same from any thread. + return _rdsspq() != 0; + } +#endif + +#ifdef FEATURE_SPECIAL_USER_MODE_APC +private: + static void InitializeSpecialUserModeApc(); + static void ApcActivationCallback(ULONG_PTR Parameter); +#endif + +public: + static bool UseSpecialUserModeApc() + { + LIMITED_METHOD_CONTRACT; + + #ifdef FEATURE_SPECIAL_USER_MODE_APC + return s_pfnQueueUserAPC2Proc != nullptr; + #else + return false; + #endif + } + + static bool UseContextBasedThreadRedirection() + { + LIMITED_METHOD_CONTRACT; + + #ifndef DISABLE_THREADSUSPEND + return !UseSpecialUserModeApc(); + #else + return false; + #endif + } + +#ifdef FEATURE_SPECIAL_USER_MODE_APC +private: + static QueueUserAPC2Proc s_pfnQueueUserAPC2Proc; +#endif +#endif // !DACCESS_COMPILE + +private: + bool m_hasPendingActivation; }; // End of class Thread diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index cf09d505b1c..02e4747fcea 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -34,6 +34,9 @@ ThreadSuspend::SUSPEND_REASON ThreadSuspend::m_suspendReason; extern "C" void RedirectedHandledJITCaseForGCThreadControl_Stub(void); extern "C" void RedirectedHandledJITCaseForDbgThreadControl_Stub(void); extern "C" void RedirectedHandledJITCaseForUserSuspend_Stub(void); +#ifdef FEATURE_SPECIAL_USER_MODE_APC +extern "C" void NTAPI ApcActivationCallbackStub(ULONG_PTR Parameter); +#endif #define GetRedirectHandlerForGCThreadControl() \ ((PFN_REDIRECTTARGET) GetEEFuncEntryPoint(RedirectedHandledJITCaseForGCThreadControl_Stub)) @@ -41,6 +44,10 @@ extern "C" void RedirectedHandledJITCaseForUserSuspend_Stub(void); ((PFN_REDIRECTTARGET) GetEEFuncEntryPoint(RedirectedHandledJITCaseForDbgThreadControl_Stub)) #define GetRedirectHandlerForUserSuspend() \ ((PFN_REDIRECTTARGET) GetEEFuncEntryPoint(RedirectedHandledJITCaseForUserSuspend_Stub)) +#ifdef FEATURE_SPECIAL_USER_MODE_APC +#define GetRedirectHandlerForApcActivation() \ + ((PAPCFUNC) GetEEFuncEntryPoint(ApcActivationCallbackStub)) +#endif #if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) #if defined(HAVE_GCCOVER) && defined(USE_REDIRECT_FOR_GCSTRESS) // GCCOVER @@ -50,7 +57,6 @@ extern "C" void RedirectedHandledJITCaseForGCStress_Stub(void); #endif // HAVE_GCCOVER && USE_REDIRECT_FOR_GCSTRESS #endif // TARGET_AMD64 || TARGET_ARM - // Every PING_JIT_TIMEOUT ms, check to see if a thread in JITted code has wandered // into some fully interruptible code (or should have a different hijack to improve // our chances of snagging it at a safe spot). @@ -2402,7 +2408,7 @@ void Thread::RareEnablePreemptiveGC() } // Called when we are passing through a safe point in CommonTripThread or -// HandleGCSuspensionForInterruptedThread. Do the right thing with this thread, +// HandleSuspensionForInterruptedThread. Do the right thing with this thread, // which can either mean waiting for the GC to complete, or performing a // pending suspension. void Thread::PulseGCMode() @@ -3046,10 +3052,21 @@ BOOL Thread::IsAddrOfRedirectFunc(void * pFuncAddr) return TRUE; #endif // HAVE_GCCOVER && USE_REDIRECT_FOR_GCSTRESS - return - (pFuncAddr == GetRedirectHandlerForGCThreadControl()) || + if ((pFuncAddr == GetRedirectHandlerForGCThreadControl()) || (pFuncAddr == GetRedirectHandlerForDbgThreadControl()) || - (pFuncAddr == GetRedirectHandlerForUserSuspend()); + (pFuncAddr == GetRedirectHandlerForUserSuspend())) + { + return TRUE; + } + +#ifdef FEATURE_SPECIAL_USER_MODE_APC + if (pFuncAddr == GetRedirectHandlerForApcActivation()) + { + return TRUE; + } +#endif + + return FALSE; } //************************************************************************ @@ -3345,25 +3362,32 @@ void ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) // this is an interesting thread in cooperative mode, let's guide it to preemptive -#ifdef DISABLE_THREADSUSPEND - // On platforms that do not support safe thread suspension, we do one of two things: - // - // - If we're on a Unix platform where hijacking is enabled, we attempt - // to inject a GC suspension which will try to redirect or hijack the - // thread to get it to a safe point. - // - // - Otherwise, we rely on the GCPOLL mechanism enabled by - // TrapReturningThreads. - -#if defined(FEATURE_HIJACK) && defined(TARGET_UNIX) - bool gcSuspensionSignalSuccess = thread->InjectGcSuspension(); - if (!gcSuspensionSignalSuccess) + if (!Thread::UseContextBasedThreadRedirection()) { - STRESS_LOG1(LF_SYNC, LL_INFO1000, "Thread::SuspendRuntime() - Failed to raise GC suspension signal for thread %p.\n", thread); - } -#endif // FEATURE_HIJACK && TARGET_UNIX + // On platforms that do not support safe thread suspension, we do one of the following: + // + // - If we're on a Unix platform where hijacking is enabled, we attempt + // to inject an activation which will try to redirect or hijack the + // thread to get it to a safe point. + // + // - Similarly to above, if we're on a Windows platform where the special + // user-mode APC is available, that is used if redirection is necessary. + // + // - Otherwise, we rely on the GCPOLL mechanism enabled by + // TrapReturningThreads. -#else // DISABLE_THREADSUSPEND +#ifdef FEATURE_THREAD_ACTIVATION + bool success = thread->InjectActivation(Thread::ActivationReason::SuspendForGC); + if (!success) + { + STRESS_LOG1(LF_SYNC, LL_INFO1000, "Thread::SuspendRuntime() - Failed to inject an activation for thread %p.\n", thread); + } +#endif // FEATURE_THREAD_ACTIVATION + + continue; + } + +#ifndef DISABLE_THREADSUSPEND if (thread->HasThreadStateOpportunistic(Thread::TS_GCSuspendRedirected)) { @@ -3469,7 +3493,7 @@ void ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) thread->ResumeThread(); STRESS_LOG1(LF_SYNC, LL_INFO1000, " Thread 0x%x is in cooperative needs to rendezvous\n", thread); -#endif // DISABLE_THREADSUSPEND +#endif // !DISABLE_THREADSUSPEND } if (countThreads == 0) @@ -4122,50 +4146,56 @@ bool Thread::SysStartSuspendForDebug(AppDomain *pAppDomain) // switch back and forth during a debug suspension -- until we // can get their Pending bit set. -#if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) +#if !defined(DISABLE_THREADSUSPEND) && defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) DWORD dwSwitchCount = 0; RetrySuspension: -#endif // FEATURE_HIJACK && !TARGET_UNIX +#endif // !DISABLE_THREADSUSPEND && FEATURE_HIJACK && !TARGET_UNIX -#ifdef DISABLE_THREADSUSPEND - // On platforms that do not support safe thread suspension we have - // to rely on the GCPOLL mechanism. - - // When we do not suspend the target thread we rely on the GCPOLL - // mechanism enabled by TrapReturningThreads. However when reading - // shared state we need to erect appropriate memory barriers. So - // the interlocked operation below ensures that any future reads on - // this thread will happen after any earlier writes on a different - // thread. SuspendThreadResult str = STR_Success; - FastInterlockOr(&thread->m_fPreemptiveGCDisabled, 0); -#else - // We can not allocate memory after we suspend a thread. - // Otherwise, we may deadlock if suspended thread holds allocator locks. - ThreadStore::AllocateOSContext(); - SuspendThreadResult str = thread->SuspendThread(); -#endif // DISABLE_THREADSUSPEND + if (!UseContextBasedThreadRedirection()) + { + // On platforms that do not support safe thread suspension we either + // rely on the GCPOLL mechanism mechanism enabled by TrapReturningThreads, + // or we try to hijack/redirect the thread using a thread activation. + + // When we do not suspend the target thread, when reading shared state + // we need to erect appropriate memory barriers. So the interlocked + // operation below ensures that any future reads on this thread will + // happen after any earlier writes on a different thread. + FastInterlockOr(&thread->m_fPreemptiveGCDisabled, 0); + } + else + { +#ifndef DISABLE_THREADSUSPEND + // We can not allocate memory after we suspend a thread. + // Otherwise, we may deadlock if suspended thread holds allocator locks. + ThreadStore::AllocateOSContext(); + str = thread->SuspendThread(); +#endif // !DISABLE_THREADSUSPEND + } if (thread->m_fPreemptiveGCDisabled && str == STR_Success) { - -#if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) - WorkingOnThreadContextHolder workingOnThreadContext(thread); - if (workingOnThreadContext.Acquired() && thread->HandledJITCase()) +#if !defined(DISABLE_THREADSUSPEND) && defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) + if (UseContextBasedThreadRedirection()) { - // Redirect thread so we can capture a good thread context - // (GetThreadContext is not sufficient, due to an OS bug). - // If we don't succeed (should only happen on Win9X, due to - // a different OS bug), we must resume the thread and try - // again. - if (!thread->CheckForAndDoRedirectForDbg()) + WorkingOnThreadContextHolder workingOnThreadContext(thread); + if (workingOnThreadContext.Acquired() && thread->HandledJITCase()) { - thread->ResumeThread(); - __SwitchToThread(0, ++dwSwitchCount); - goto RetrySuspension; + // Redirect thread so we can capture a good thread context + // (GetThreadContext is not sufficient, due to an OS bug). + // If we don't succeed (should only happen on Win9X, due to + // a different OS bug), we must resume the thread and try + // again. + if (!thread->CheckForAndDoRedirectForDbg()) + { + thread->ResumeThread(); + __SwitchToThread(0, ++dwSwitchCount); + goto RetrySuspension; + } } } -#endif // FEATURE_HIJACK && !TARGET_UNIX +#endif // !DISABLE_THREADSUSPEND && FEATURE_HIJACK && !TARGET_UNIX // Remember that this thread will be running to a safe point FastInterlockIncrement(&m_DebugWillSyncCount); @@ -4177,20 +4207,30 @@ bool Thread::SysStartSuspendForDebug(AppDomain *pAppDomain) TS_DebugWillSync ); -#ifdef DISABLE_THREADSUSPEND - // There'a a race above between the moment we first check m_fPreemptiveGCDisabled - // and the moment we enable TrapReturningThreads in MarkForSuspension. However, - // nothing bad happens if the thread has transitioned to preemptive before marking - // the thread for suspension; the thread will later be identified as Synced in - // SysSweepThreadsForDebug. - // - // If the thread transitions to preemptive mode and into a forbid-suspend-for-debugger - // region, SysSweepThreadsForDebug would similarly identify the thread as synced - // after it leaves the forbid region. -#else // DISABLE_THREADSUSPEND - // Resume the thread and let it run to a safe point - thread->ResumeThread(); -#endif // DISABLE_THREADSUSPEND + if (!UseContextBasedThreadRedirection()) + { + // There'a a race above between the moment we first check m_fPreemptiveGCDisabled + // and the moment we enable TrapReturningThreads in MarkForSuspension. However, + // nothing bad happens if the thread has transitioned to preemptive before marking + // the thread for suspension; the thread will later be identified as Synced in + // SysSweepThreadsForDebug. + // + // If the thread transitions to preemptive mode and into a forbid-suspend-for-debugger + // region, SysSweepThreadsForDebug would similarly identify the thread as synced + // after it leaves the forbid region. + +#if defined(FEATURE_THREAD_ACTIVATION) && defined(TARGET_WINDOWS) + // Inject an activation that will interrupt the thread and try to bring it to a safe point + thread->InjectActivation(Thread::ActivationReason::SuspendForDebugger); +#endif // FEATURE_THREAD_ACTIVATION && TARGET_WINDOWS + } + else + { +#ifndef DISABLE_THREADSUSPEND + // Resume the thread and let it run to a safe point + thread->ResumeThread(); +#endif // !DISABLE_THREADSUSPEND + } LOG((LF_CORDB, LL_INFO1000, "[0x%x] SUSPEND: gc disabled - will sync.\n", @@ -4203,14 +4243,13 @@ bool Thread::SysStartSuspendForDebug(AppDomain *pAppDomain) thread->MarkForSuspension(TS_DebugSuspendPending); if ( -#ifdef DISABLE_THREADSUSPEND // There'a a race above between the moment we first check m_fPreemptiveGCDisabled // and the moment we enable TrapReturningThreads in MarkForSuspension. To account // for that we check whether the thread moved into cooperative mode, and if it had // we mark it as a DebugWillSync thread, that will be handled later in // SysSweepThreadsForDebug. - thread->m_fPreemptiveGCDisabled || -#endif // DISABLE_THREADSUSPEND + (!UseContextBasedThreadRedirection() && thread->m_fPreemptiveGCDisabled) || + // The thread may have been suspended in a forbid-suspend-for-debugger region, or // before the state change to set TS_DebugSuspendPending is made visible to other // threads, the thread may have transitioned into a forbid region. In either case, @@ -4224,7 +4263,7 @@ bool Thread::SysStartSuspendForDebug(AppDomain *pAppDomain) } #ifndef DISABLE_THREADSUSPEND - if (str == STR_Success) { + if (str == STR_Success && UseContextBasedThreadRedirection()) { thread->ResumeThread(); } #endif // !DISABLE_THREADSUSPEND @@ -4294,30 +4333,38 @@ bool Thread::SysSweepThreadsForDebug(bool forceSync) if ((thread->m_State & TS_DebugWillSync) == 0) continue; -#ifdef DISABLE_THREADSUSPEND - - // On platforms that do not support safe thread suspension we have - // to rely on the GCPOLL mechanism. - - // When we do not suspend the target thread we rely on the GCPOLL - // mechanism enabled by TrapReturningThreads. However when reading - // shared state we need to erect appropriate memory barriers. So - // the interlocked operation below ensures that any future reads on - // this thread will happen after any earlier writes on a different - // thread. - FastInterlockOr(&thread->m_fPreemptiveGCDisabled, 0); - if (!thread->m_fPreemptiveGCDisabled && !thread->IsInForbidSuspendForDebuggerRegion()) - { - // If the thread toggled to preemptive mode and is not in a - // forbid-suspend-for-debugger region, then it's synced. - goto Label_MarkThreadAsSynced; - } - else + if (!UseContextBasedThreadRedirection()) { + // On platforms that do not support safe thread suspension we either + // rely on the GCPOLL mechanism mechanism enabled by TrapReturningThreads, + // or we try to hijack/redirect the thread using a thread activation. + + // When we do not suspend the target thread, when reading shared state + // we need to erect appropriate memory barriers. So the interlocked + // operation below ensures that any future reads on this thread will + // happen after any earlier writes on a different thread. + FastInterlockOr(&thread->m_fPreemptiveGCDisabled, 0); + if (!thread->m_fPreemptiveGCDisabled) + { + if (thread->IsInForbidSuspendForDebuggerRegion()) + { + continue; + } + + // If the thread toggled to preemptive mode and is not in a + // forbid-suspend-for-debugger region, then it's synced. + goto Label_MarkThreadAsSynced; + } + +#if defined(FEATURE_THREAD_ACTIVATION) && defined(TARGET_WINDOWS) + // Inject an activation that will interrupt the thread and try to bring it to a safe point + thread->InjectActivation(Thread::ActivationReason::SuspendForDebugger); +#endif // FEATURE_THREAD_ACTIVATION && TARGET_WINDOWS + continue; } -#else // DISABLE_THREADSUSPEND +#ifndef DISABLE_THREADSUSPEND // Suspend the thread #if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) @@ -4402,7 +4449,7 @@ RetrySuspension: thread->ResumeThread(); continue; -#endif // DISABLE_THREADSUSPEND +#endif // !DISABLE_THREADSUSPEND // The thread is synced. Remove the sync bits and dec the sync count. Label_MarkThreadAsSynced: @@ -5215,7 +5262,7 @@ BOOL Thread::GetSafelyRedirectableThreadContext(DWORD dwOptions, CONTEXT * pCtx, { // If we are running under the control of a managed debugger that may have placed breakpoints in the code stream, // then there is a special case that we need to check. See the comments in debugger.cpp for more information. - if (CORDebuggerAttached() && (g_pDebugInterface->IsThreadContextInvalid(this))) + if (CORDebuggerAttached() && (g_pDebugInterface->IsThreadContextInvalid(this, NULL))) return FALSE; } #endif // DEBUGGING_SUPPORTED @@ -5709,10 +5756,10 @@ retry_for_debugger: #endif //TARGET_ARM || TARGET_ARM64 } -#if defined(FEATURE_HIJACK) && defined(TARGET_UNIX) +#ifdef FEATURE_THREAD_ACTIVATION -// This function is called by PAL to check if the specified instruction pointer -// is in a function where we can safely inject activation. +// This function is called by a thread activation to check if the specified instruction pointer +// is in a function where we can safely handle an activation. BOOL CheckActivationSafePoint(SIZE_T ip, BOOL checkingCurrentThread) { Thread *pThread = GetThreadNULLOk(); @@ -5725,7 +5772,7 @@ BOOL CheckActivationSafePoint(SIZE_T ip, BOOL checkingCurrentThread) return checkForManagedCode && ExecutionManager::IsManagedCode(ip); } -// This function is called when a GC is pending. It tries to ensure that the current +// This function is called when thread suspension is pending. It tries to ensure that the current // thread is taken to a GC-safe place as quickly as possible. It does this by doing // one of the following: // @@ -5740,7 +5787,7 @@ BOOL CheckActivationSafePoint(SIZE_T ip, BOOL checkingCurrentThread) // address to take the thread to the appropriate stub (based on the return // type of the method) which will then handle preparing the thread for GC. // -void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) +void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) { Thread *pThread = GetThread(); @@ -5762,6 +5809,13 @@ void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) if (!workingOnThreadContext.Acquired()) return; +#if defined(DEBUGGING_SUPPORTED) && defined(TARGET_WINDOWS) + // If we are running under the control of a managed debugger that may have placed breakpoints in the code stream, + // then there is a special case that we need to check. See the comments in debugger.cpp for more information. + if (CORDebuggerAttached() && g_pDebugInterface->IsThreadContextInvalid(pThread, interruptedContext)) + return; +#endif // DEBUGGING_SUPPORTED && TARGET_WINDOWS + EECodeInfo codeInfo(ip); if (!codeInfo.IsValid()) return; @@ -5783,6 +5837,8 @@ void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) pThread->PulseGCMode(); frame.Pop(pThread); + + // TODO: Windows - Raise thread abort exception if ready for abort } else { @@ -5833,8 +5889,73 @@ void HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) } } -bool Thread::InjectGcSuspension() +#ifdef FEATURE_SPECIAL_USER_MODE_APC +void Thread::ApcActivationCallback(ULONG_PTR Parameter) { + // Cannot use contracts here because the thread may be interrupted at any point + + _ASSERTE(UseSpecialUserModeApc()); + _ASSERTE(Parameter != 0); + + CLONE_PAPC_CALLBACK_DATA pData = (CLONE_PAPC_CALLBACK_DATA)Parameter; + ActivationReason reason = (ActivationReason)pData->Parameter; + PCONTEXT pContext = pData->ContextRecord; + + struct AutoClearPendingThreadActivation + { + ~AutoClearPendingThreadActivation() + { + GetThread()->m_hasPendingActivation = false; + } + } autoClearPendingThreadActivation; + + if (!CheckActivationSafePoint(GetIP(pContext), true /* checkingCurrentThread */)) + { + return; + } + + switch (reason) + { + case ActivationReason::SuspendForGC: + case ActivationReason::SuspendForDebugger: + HandleSuspensionForInterruptedThread(pContext); + break; + + default: + UNREACHABLE_MSG("Unexpected ActivationReason"); + } +} +#endif // FEATURE_SPECIAL_USER_MODE_APC + +bool Thread::InjectActivation(ActivationReason reason) +{ +#ifdef FEATURE_SPECIAL_USER_MODE_APC + _ASSERTE(UseSpecialUserModeApc()); + + if (m_hasPendingActivation) + { + // Try to avoid nesting special user-mode APCs, as they can interrupt one another + return true; + } + + HANDLE hThread = GetThreadHandle(); + if (hThread == INVALID_HANDLE_VALUE) + { + return false; + } + + m_hasPendingActivation = true; + BOOL success = + (*s_pfnQueueUserAPC2Proc)( + GetRedirectHandlerForApcActivation(), + hThread, + (ULONG_PTR)reason, + SpecialUserModeApcWithContextFlags); + _ASSERTE(success); + return true; +#elif defined(TARGET_UNIX) + _ASSERTE(reason == ActivationReason::SuspendForGC); + static ConfigDWORD injectionEnabled; if (injectionEnabled.val(CLRConfig::INTERNAL_ThreadSuspendInjection) == 0) return false; @@ -5844,15 +5965,18 @@ bool Thread::InjectGcSuspension() return ::PAL_InjectActivation(hThread); return false; +#else +#error Unknown platform. +#endif // FEATURE_SPECIAL_USER_MODE_APC || TARGET_UNIX } -#endif // FEATURE_HIJACK && TARGET_UNIX +#endif // FEATURE_THREAD_ACTIVATION // Initialize thread suspension support void ThreadSuspend::Initialize() { #if defined(FEATURE_HIJACK) && defined(TARGET_UNIX) - ::PAL_SetActivationFunction(HandleGCSuspensionForInterruptedThread, CheckActivationSafePoint); + ::PAL_SetActivationFunction(HandleSuspensionForInterruptedThread, CheckActivationSafePoint); #endif } diff --git a/src/libraries/Native/Windows/CMakeLists.txt b/src/libraries/Native/Windows/CMakeLists.txt index b9b6c823d0b..e96ec87ddb1 100644 --- a/src/libraries/Native/Windows/CMakeLists.txt +++ b/src/libraries/Native/Windows/CMakeLists.txt @@ -63,7 +63,14 @@ endif () # enable control-flow-guard support for native components add_compile_options(/guard:cf) -list(APPEND __SharedLinkArgs /GUARD:CF) +list(APPEND __SharedLinkArgs /guard:cf) + +#if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "x64") +# # Enable EH continuation table and CETCOMPAT for native components +# add_compile_options(/guard:ehcont) +# list(APPEND __SharedLinkArgs /guard:ehcont) +# list(APPEND __SharedLinkArgs /CETCOMPAT) +#endif () # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid # linking in VCRUNTIME140.DLL for a simplified xcopy experience by reducing the dependency on VC REDIST. @@ -109,7 +116,7 @@ endif() # Debug build specific flags list(INSERT __SharedLinkArgs 0 $<$,$>:/NOVCFEATURE>) -if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64") +if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "x64") add_definitions(-DTARGET_64BIT=1) endif () diff --git a/src/tests/Interop/IJW/IJW.cmake b/src/tests/Interop/IJW/IJW.cmake index b78fcb2bb16..b41a83794d9 100644 --- a/src/tests/Interop/IJW/IJW.cmake +++ b/src/tests/Interop/IJW/IJW.cmake @@ -22,6 +22,11 @@ if (CLR_CMAKE_HOST_WIN32) string(REPLACE "/guard:cf" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endif() + # IJW isn't compatible with EHCONT, which requires CFG + if(CMAKE_CXX_FLAGS MATCHES "/guard:ehcont") + string(REPLACE "/guard:ehcont" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + endif() + # IJW isn't compatible with GR- if(CMAKE_CXX_FLAGS MATCHES "/GR-") string(REPLACE "/GR-" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") From 71d0d2b275efa091f235c569f01869f39bfb7638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Thu, 15 Jul 2021 09:52:42 -0700 Subject: [PATCH 592/926] Relax LinkTarget so it always returns null when steps on an error (#55668) --- .../src/System/IO/FileSystem.Windows.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 5f88f53a7c5..698e3bc5724 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -427,20 +427,20 @@ namespace System.IO { string? targetPath = returnFinalTarget ? GetFinalLinkTarget(linkPath, isDirectory) : - GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: true, returnFullPath: true); + GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: true, returnFullPath: true); return targetPath == null ? null : isDirectory ? new DirectoryInfo(targetPath) : new FileInfo(targetPath); } internal static string? GetLinkTarget(string linkPath, bool isDirectory) - => GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: false); + => GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: false); /// /// Gets reparse point information associated to . /// /// The immediate link target, absolute or relative or null if the file is not a supported link. - internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnUnreachable, bool returnFullPath) + internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnError, bool returnFullPath) { using SafeFileHandle handle = OpenSafeFileHandle(linkPath, Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | @@ -448,13 +448,12 @@ namespace System.IO if (handle.IsInvalid) { - int error = Marshal.GetLastWin32Error(); - - if (!throwOnUnreachable && IsPathUnreachableError(error)) + if (!throwOnError) { return null; } + int error = Marshal.GetLastWin32Error(); // File not found doesn't make much sense coming from a directory. if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND) { @@ -479,6 +478,11 @@ namespace System.IO if (!success) { + if (!throwOnError) + { + return null; + } + int error = Marshal.GetLastWin32Error(); // The file or directory is not a reparse point. if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT) @@ -600,13 +604,15 @@ namespace System.IO { // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span. // I am not sure if it's possible to change CreateFile's param to ROS and avoid all these allocations. - string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: true); + + // We don't throw on error since we already did all the proper validations before. + string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: true); string? prev = null; while (current != null) { prev = current; - current = GetImmediateLinkTarget(current, isDirectory, throwOnUnreachable: false, returnFullPath: true); + current = GetImmediateLinkTarget(current, isDirectory, throwOnError: false, returnFullPath: true); } return prev; From 04e6dcc6ae1425f90dc9a2a34629e7f53a3275dc Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 15 Jul 2021 19:23:27 +0200 Subject: [PATCH 593/926] inFlight ref-counting of strong GCHandle for JSObject, while in flight over the managed/JS boundary (#54453) --- .../src/Interop/Browser/Interop.Runtime.cs | 8 ++ .../HttpClientHandlerTest.RemoteServer.cs | 1 - .../System/Net/Http/HttpClientHandlerTest.cs | 1 - .../tests/SendReceiveTest.cs | 2 - .../InteropServices/JavaScript/AnyRef.cs | 34 ++++++ .../InteropServices/JavaScript/Array.cs | 1 + .../InteropServices/JavaScript/JSObject.cs | 2 + .../InteropServices/JavaScript/Runtime.cs | 17 ++- .../JavaScript/JavaScriptTests.cs | 108 ++++++++++++++++++ src/mono/wasm/runtime/binding_support.js | 28 +++-- 10 files changed, 185 insertions(+), 17 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index e325e0489fa..2c31b557cdc 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -70,6 +70,7 @@ internal static partial class Interop object res = CompileFunction(snippet, out int exception); if (exception != 0) throw new JSException((string)res); + ReleaseInFlight(res); return res as System.Runtime.InteropServices.JavaScript.Function; } @@ -97,6 +98,7 @@ internal static partial class Interop if (exception != 0) throw new JSException($"Error obtaining a handle to global {str}"); + ReleaseInFlight(globalHandle); return globalHandle; } @@ -121,5 +123,11 @@ internal static partial class Interop module.SetObjectProperty("aot_profile_data", Uint8Array.From(span)); } } + + public static void ReleaseInFlight(object? obj) + { + JSObject? jsObj = obj as JSObject; + jsObj?.ReleaseInFlight(); + } } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs index 316a58cb33f..9b99ef1d13f 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.RemoteServer.cs @@ -178,7 +178,6 @@ namespace System.Net.Http.Functional.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [Theory] [MemberData(nameof(RemoteServersAndHeaderEchoUrisMemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/53872", TestPlatforms.Browser)] public async Task GetAsync_RequestHeadersAddCustomHeaders_HeaderAndEmptyValueSent(Configuration.Http.RemoteServer remoteServer, Uri uri) { if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1709OrGreater) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 54497e28618..27a8fc9bed2 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -577,7 +577,6 @@ namespace System.Net.Http.Functional.Tests [Theory] [MemberData(nameof(GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54655", TestPlatforms.Browser)] public async Task GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly(string newline, string fold, bool dribble) { if (LoopbackServerFactory.Version >= HttpVersion20.Value) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index bc70d2a4fdf..6266f01e8ed 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -217,7 +217,6 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task ReceiveAsync_MultipleOutstandingReceiveOperations_Throws(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) @@ -321,7 +320,6 @@ namespace System.Net.WebSockets.Client.Tests [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task SendReceive_VaryingLengthBuffers_Success(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs index 5cfb8aa1e1e..58587eb15a4 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs @@ -11,6 +11,8 @@ namespace System.Runtime.InteropServices.JavaScript { public abstract class AnyRef : SafeHandleMinusOneIsInvalid { + private GCHandle? InFlight; + private int InFlightCounter; private GCHandle AnyRefHandle; public int JSHandle => (int)handle; @@ -21,9 +23,41 @@ namespace System.Runtime.InteropServices.JavaScript { SetHandle(jsHandle); AnyRefHandle = GCHandle.Alloc(this, ownsHandle ? GCHandleType.Weak : GCHandleType.Normal); + InFlight = null; + InFlightCounter = 0; } internal int Int32Handle => (int)(IntPtr)AnyRefHandle; + internal void AddInFlight() + { + lock (this) + { + InFlightCounter++; + if (InFlightCounter == 1) + { + Debug.Assert(InFlight == null); + InFlight = GCHandle.Alloc(this, GCHandleType.Normal); + } + } + } + + internal void ReleaseInFlight() + { + lock (this) + { + Debug.Assert(InFlightCounter != 0); + + InFlightCounter--; + if (InFlightCounter == 0) + { + Debug.Assert(InFlight.HasValue); + InFlight.Value.Free(); + InFlight = null; + } + } + } + + protected void FreeGCHandle() { AnyRefHandle.Free(); diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs index 3ca4f4ac45f..591c125dbb4 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs @@ -86,6 +86,7 @@ namespace System.Runtime.InteropServices.JavaScript if (exception != 0) throw new JSException((string)indexValue); + Interop.Runtime.ReleaseInFlight(indexValue); return indexValue; } set diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs index af4b00060d9..73032ff7b3a 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs @@ -73,6 +73,7 @@ namespace System.Runtime.InteropServices.JavaScript object res = Interop.Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); if (exception != 0) throw new JSException((string)res); + Interop.Runtime.ReleaseInFlight(res); return res; } @@ -103,6 +104,7 @@ namespace System.Runtime.InteropServices.JavaScript object propertyValue = Interop.Runtime.GetObjectProperty(JSHandle, name, out int exception); if (exception != 0) throw new JSException((string)propertyValue); + Interop.Runtime.ReleaseInFlight(propertyValue); return propertyValue; } diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs index 09e30d4bb46..34467dbdb9a 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs @@ -91,6 +91,8 @@ namespace System.Runtime.InteropServices.JavaScript } } + target.AddInFlight(); + return target.Int32Handle; } @@ -236,12 +238,21 @@ namespace System.Runtime.InteropServices.JavaScript return jsObject?.JSHandle ?? -1; } - public static object? GetDotNetObject(int gcHandle) + /// + /// when true, we would create Normal GCHandle to the JSObject, so that it would not get collected before passing it back to managed code + public static object? GetDotNetObject(int gcHandle, int shouldAddInflight) { GCHandle h = (GCHandle)(IntPtr)gcHandle; - return h.Target is JSObject js ? - js.GetWrappedObject() ?? h.Target : h.Target; + if (h.Target is JSObject jso) + { + if (shouldAddInflight != 0) + { + jso.AddInFlight(); + } + return jso.GetWrappedObject() ?? jso; + } + return h.Target; } public static bool IsSimpleArray(object a) diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs index 23d57c25b55..f06939870b5 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs @@ -1,7 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices.JavaScript; +using System.Threading.Tasks; using Xunit; namespace System.Runtime.InteropServices.JavaScript.Tests @@ -115,5 +118,110 @@ namespace System.Runtime.InteropServices.JavaScript.Tests minValue = (int)mathMin.Call(null, 5, 6, 2, 3, 7); Assert.Equal(2, minValue); } + + [Fact] + public static async Task BagIterator() + { + await Task.Delay(1); + + var bagFn = new Function(@" + var same = { + x:1 + }; + return Object.entries({ + a:1, + b:'two', + c:{fold:{}}, + d:same, + e:same, + f:same + }); + "); + + for (int attempt = 0; attempt < 100_000; attempt++) + { + try + { + using var bag = (JSObject)bagFn.Call(null); + using var entriesIterator = (JSObject)bag.Invoke("entries"); + + var cnt = entriesIterator.ToEnumerable().Count(); + Assert.Equal(6, cnt); + + // fill GC helps to repro + var x = new byte[100 + attempt / 100]; + if (attempt % 1000 == 0) + { + GC.Collect(); + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + " At attempt=" + attempt, ex); + } + } + } + + [Fact] + public static async Task Iterator() + { + await Task.Delay(1); + + var makeRangeIterator = new Function("start", "end", "step", @" + let nextIndex = start; + let iterationCount = 0; + + const rangeIterator = { + next: function() { + let result; + if (nextIndex < end) { + result = { value: {}, done: false } + nextIndex += step; + iterationCount++; + return result; + } + return { value: {}, done: true } + } + }; + return rangeIterator; + "); + + for (int attempt = 0; attempt < 100_000; attempt++) + { + try + { + using (var entriesIterator = (JSObject)makeRangeIterator.Call(null, 0, 500)) + { + var cnt = entriesIterator.ToEnumerable().Count(); + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + " At attempt=" + attempt, ex); + } + } + } + + public static IEnumerable ToEnumerable(this JSObject iterrator) + { + JSObject nextResult = null; + try + { + nextResult = (JSObject)iterrator.Invoke("next"); + var done = (bool)nextResult.GetObjectProperty("done"); + while (!done) + { + object value = nextResult.GetObjectProperty("value"); + nextResult.Dispose(); + yield return value; + nextResult = (JSObject)iterrator.Invoke("next"); + done = (bool)nextResult.GetObjectProperty("done"); + } + } + finally + { + nextResult?.Dispose(); + } + } } } diff --git a/src/mono/wasm/runtime/binding_support.js b/src/mono/wasm/runtime/binding_support.js index 916032d0db0..d96eeed4ff6 100644 --- a/src/mono/wasm/runtime/binding_support.js +++ b/src/mono/wasm/runtime/binding_support.js @@ -126,7 +126,7 @@ var BindingSupportLib = { this._bind_existing_obj = bind_runtime_method ("BindExistingObject", "mi"); this._unbind_raw_obj_and_free = bind_runtime_method ("UnBindRawJSObjectAndFree", "ii"); this._get_js_id = bind_runtime_method ("GetJSObjectId", "m"); - this._get_raw_mono_obj = bind_runtime_method ("GetDotNetObject", "i!"); + this._get_raw_mono_obj = bind_runtime_method ("GetDotNetObject", "ii!"); this._is_simple_array = bind_runtime_method ("IsSimpleArray", "m"); this.setup_js_cont = get_method ("SetupJSContinuation"); @@ -779,15 +779,21 @@ var BindingSupportLib = { return this._get_js_id (mono_obj); }, - wasm_get_raw_obj: function (gchandle) + // when should_add_in_flight === true, the JSObject would be temporarily hold by Normal GCHandle, so that it would not get collected during transition to the managed stack. + // its InFlight handle would be freed when the instance arrives to managed side via Interop.Runtime.ReleaseInFlight + wasm_get_raw_obj: function (gchandle, should_add_in_flight) { - return this._get_raw_mono_obj (gchandle); + if(!gchandle){ + return 0; + } + + return this._get_raw_mono_obj (gchandle, should_add_in_flight ? 1 : 0); }, try_extract_mono_obj:function (js_obj) { if (js_obj === null || typeof js_obj === "undefined" || typeof js_obj.__mono_gchandle__ === "undefined") return 0; - return this.wasm_get_raw_obj (js_obj.__mono_gchandle__); + return this.wasm_get_raw_obj (js_obj.__mono_gchandle__, true); }, mono_method_get_call_signature: function(method, mono_obj) { @@ -810,7 +816,7 @@ var BindingSupportLib = { tcs.is_mono_tcs_task_bound = true; js_obj.__mono_bound_tcs__ = tcs.__mono_gchandle__; tcs.__mono_bound_task__ = js_obj.__mono_gchandle__; - return this.wasm_get_raw_obj (js_obj.__mono_gchandle__); + return this.wasm_get_raw_obj (js_obj.__mono_gchandle__, true); }, free_task_completion_source: function (tcs) { @@ -831,7 +837,7 @@ var BindingSupportLib = { var result = null; var gc_handle = js_obj.__mono_gchandle__; if (gc_handle) { - result = this.wasm_get_raw_obj (gc_handle); + result = this.wasm_get_raw_obj (gc_handle, true); // It's possible the managed object corresponding to this JS object was collected, // in which case we need to make a new one. @@ -842,8 +848,8 @@ var BindingSupportLib = { } if (!result) { - gc_handle = this.mono_wasm_register_obj(js_obj); - result = this.wasm_get_raw_obj (gc_handle); + var { gc_handle: new_gc_handle, should_add_in_flight } = this.mono_wasm_register_obj(js_obj); + result = this.wasm_get_raw_obj (new_gc_handle, should_add_in_flight); } return result; @@ -1613,10 +1619,12 @@ var BindingSupportLib = { js_obj.__owns_handle__ = true; gc_handle = js_obj.__mono_gchandle__ = this.wasm_binding_obj_new(handle + 1, js_obj.__owns_handle__, typeof wasm_type === "undefined" ? -1 : wasm_type); this.mono_wasm_object_registry[handle] = js_obj; - + // as this instance was just created, it was already created with Inflight strong GCHandle, so we do not have to do it again + return { gc_handle, should_add_in_flight: false }; } } - return gc_handle; + // this is pre-existing instance, we need to add Inflight strong GCHandle before passing it to managed + return { gc_handle, should_add_in_flight: true }; }, mono_wasm_require_handle: function(handle) { if (handle > 0) From 82c75a9119bb394bb3ac273e5d27798cb9446240 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 15 Jul 2021 19:45:43 +0200 Subject: [PATCH 594/926] Fix crash with unmapped shuffle thunk stub (#55718) This change fixes an intermittent issue that was showing up in the System.Linq.Expressions.Tests suite. When the delegate class was in a collectible ALC, some of the stubs were being used even after the underlying loader allocator was deleted, thus the memory the stubs occupied was already unmapped. Before my recent W^X change, the stubs were always being allocated from an executable allocator / executable heap in the global loader allocator due to a bug in the AssemblyLoaderAllocator::SetCollectible and AssemblyLoaderAllocator::Init ordering (the SetCollectible was being called before the Init, so the m_pStubHeap was not yet set and the ShuffleThunkCache was being passed NULL as the heap pointer. The cache handles that case as a request to allocate from global executable heap. In this fix, I've changed the AssemblyLoaderAllocator::Init to pass the SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap() as the heap to the ShuffleThunkCache constructor. It is a workaround until the actual issue with stubs outliving the delegate classes is understood and fixed. Besides the fix, this change also fixes two unrelated issues that would only possibly cause trouble when the W^X is enabled. There were two places where memory was being reserved by the new ExecutableAllocator, but cleaned up using the ClrVirtualFree. --- src/coreclr/vm/dynamicmethod.cpp | 2 +- src/coreclr/vm/loaderallocator.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp index 541d88dc168..6b6c5ffacce 100644 --- a/src/coreclr/vm/dynamicmethod.cpp +++ b/src/coreclr/vm/dynamicmethod.cpp @@ -380,7 +380,7 @@ HostCodeHeap::~HostCodeHeap() delete[] m_pHeapList->pHdrMap; if (m_pBaseAddr) - ClrVirtualFree(m_pBaseAddr, 0, MEM_RELEASE); + ExecutableAllocator::Instance()->Release(m_pBaseAddr); LOG((LF_BCL, LL_INFO10, "Level1 - CodeHeap destroyed {0x%p}\n", this)); } diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 0a77e4445f0..08ef69c1281 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1414,7 +1414,7 @@ void LoaderAllocator::Terminate() // This was the block reserved by BaseDomain::Init for the loaderheaps. if (m_InitialReservedMemForLoaderHeaps) { - ClrVirtualFree (m_InitialReservedMemForLoaderHeaps, 0, MEM_RELEASE); + ExecutableAllocator::Instance()->Release(m_InitialReservedMemForLoaderHeaps); m_InitialReservedMemForLoaderHeaps=NULL; } @@ -1687,7 +1687,11 @@ void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain) LoaderAllocator::Init((BaseDomain *)pAppDomain); if (IsCollectible()) { - m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap); + // TODO: the ShuffleThunkCache should really be using the m_pStubHeap, however the unloadability support + // doesn't track the stubs or the related delegate classes and so we get crashes when a stub is used after + // the AssemblyLoaderAllocator is gone (the stub memory is unmapped). + // https://github.com/dotnet/runtime/issues/55697 tracks this issue. + m_pShuffleThunkCache = new ShuffleThunkCache(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()); } } From 25686d54fe207f09d093d539fa93a1ce78171879 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Thu, 15 Jul 2021 10:52:36 -0700 Subject: [PATCH 595/926] Increase max loops optimized by RyuJIT from 16 to 64. (#55614) 16 seems remarkably small. Note that this number is used to allocate the statically-sized loop table, as well as for memory allocation for value numbering, so there is some overhead to increasing it. A few microbenchmarks that have diffs show benefit, including 9% for MulMatrix | Method | Job | Toolchain | Mean | Error | StdDev | Median | Min | Max | Ratio | |------- |----------- |---------------------------------------------------------------------------------- |---------:|--------:|--------:|---------:|---------:|---------:|------:| | LLoops | Job-JXEMSM | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 559.9 ms | 8.20 ms | 7.67 ms | 556.4 ms | 550.6 ms | 576.0 ms | 1.00 | | LLoops | Job-MUOLTV | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 552.3 ms | 5.84 ms | 5.46 ms | 552.0 ms | 542.4 ms | 561.1 ms | 0.99 | | MulMatrix | Job-JXEMSM | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 369.7 ms | 4.01 ms | 3.56 ms | 369.9 ms | 364.6 ms | 376.9 ms | 1.00 | | MulMatrix | Job-MUOLTV | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 338.1 ms | 2.69 ms | 2.51 ms | 337.7 ms | 332.2 ms | 341.9 ms | 0.91 | | Puzzle | Job-JXEMSM | \runtime2\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 403.4 ms | 6.93 ms | 6.48 ms | 402.3 ms | 394.8 ms | 412.5 ms | 1.00 | | Puzzle | Job-MUOLTV | \runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\CoreRun.exe | 394.9 ms | 4.16 ms | 3.68 ms | 395.5 ms | 388.2 ms | 401.8 ms | 0.98 | spmi diffs: ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 6264 Total bytes of diff: 6285 Total bytes of delta: 21 (0.34% of base) Total relative delta: 0.00 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 21 : 42451.dasm (0.34% of base) 1 total files with Code Size differences (0 improved, 1 regressed), 0 unchanged. Top method regressions (bytes): 21 ( 0.34% of base) : 42451.dasm - RelationalModel:Create(IModel,IRelationalAnnotationProvider):IRelationalModel Top method regressions (percentages): 21 ( 0.34% of base) : 42451.dasm - RelationalModel:Create(IModel,IRelationalAnnotationProvider):IRelationalModel 1 total methods with Code Size differences (0 improved, 1 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 39243 Total bytes of diff: 39940 Total bytes of delta: 697 (1.78% of base) Total relative delta: 0.16 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 536 : 25723.dasm (18.29% of base) 185 : 26877.dasm (2.21% of base) 66 : 16212.dasm (1.39% of base) 16 : 16196.dasm (0.36% of base) 13 : 13322.dasm (0.27% of base) 9 : 15596.dasm (0.16% of base) Top file improvements (bytes): -125 : 27270.dasm (-6.93% of base) -3 : 13993.dasm (-0.05% of base) 8 total files with Code Size differences (2 improved, 6 regressed), 0 unchanged. Top method regressions (bytes): 536 (18.29% of base) : 25723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 185 ( 2.21% of base) : 26877.dasm - Benchstone.BenchF.LLoops:Main1(int):this 66 ( 1.39% of base) : 16212.dasm - Jil.Deserialize.Methods:SkipWithLeadChar(System.IO.TextReader,int) 16 ( 0.36% of base) : 16196.dasm - DynamicClass:_DynamicMethod9(System.IO.TextReader,int):MicroBenchmarks.Serializers.MyEventsListerViewModel 13 ( 0.27% of base) : 13322.dasm - DynamicClass:Regex1_Go(System.Text.RegularExpressions.RegexRunner) 9 ( 0.16% of base) : 15596.dasm - DynamicClass:_DynamicMethod9(byref,int):MicroBenchmarks.Serializers.MyEventsListerViewModel Top method improvements (bytes): -125 (-6.93% of base) : 27270.dasm - Benchstone.BenchI.Puzzle:DoIt():bool:this -3 (-0.05% of base) : 13993.dasm - Jil.Deserialize.Methods:SkipWithLeadCharThunkReader(byref,int) Top method regressions (percentages): 536 (18.29% of base) : 25723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 185 ( 2.21% of base) : 26877.dasm - Benchstone.BenchF.LLoops:Main1(int):this 66 ( 1.39% of base) : 16212.dasm - Jil.Deserialize.Methods:SkipWithLeadChar(System.IO.TextReader,int) 16 ( 0.36% of base) : 16196.dasm - DynamicClass:_DynamicMethod9(System.IO.TextReader,int):MicroBenchmarks.Serializers.MyEventsListerViewModel 13 ( 0.27% of base) : 13322.dasm - DynamicClass:Regex1_Go(System.Text.RegularExpressions.RegexRunner) 9 ( 0.16% of base) : 15596.dasm - DynamicClass:_DynamicMethod9(byref,int):MicroBenchmarks.Serializers.MyEventsListerViewModel Top method improvements (percentages): -125 (-6.93% of base) : 27270.dasm - Benchstone.BenchI.Puzzle:DoIt():bool:this -3 (-0.05% of base) : 13993.dasm - Jil.Deserialize.Methods:SkipWithLeadCharThunkReader(byref,int) 8 total methods with Code Size differences (2 improved, 6 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 142908 Total bytes of diff: 143387 Total bytes of delta: 479 (0.34% of base) Total relative delta: 0.42 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 536 : 248723.dasm (18.29% of base) 390 : 220441.dasm (14.45% of base) 390 : 220391.dasm (14.61% of base) 150 : 239253.dasm (1.78% of base) 71 : 234803.dasm (1.72% of base) 16 : 225588.dasm (1.00% of base) 16 : 225590.dasm (1.00% of base) 5 : 225285.dasm (0.26% of base) Top file improvements (bytes): -359 : 215690.dasm (-0.99% of base) -320 : 215701.dasm (-1.16% of base) -128 : 215723.dasm (-0.73% of base) -128 : 215666.dasm (-0.62% of base) -125 : 239280.dasm (-6.93% of base) -29 : 216754.dasm (-0.33% of base) -3 : 225316.dasm (-0.15% of base) -3 : 225313.dasm (-0.15% of base) 16 total files with Code Size differences (8 improved, 8 regressed), 0 unchanged. Top method regressions (bytes): 536 (18.29% of base) : 248723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 390 (14.45% of base) : 220441.dasm - VectorTest:Main():int 390 (14.61% of base) : 220391.dasm - VectorTest:Main():int 150 ( 1.78% of base) : 239253.dasm - Benchstone.BenchF.LLoops:Main1(int):this 71 ( 1.72% of base) : 234803.dasm - SmallLoop1:Main():int 16 ( 1.00% of base) : 225588.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int 16 ( 1.00% of base) : 225590.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int 5 ( 0.26% of base) : 225285.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int Top method improvements (bytes): -359 (-0.99% of base) : 215690.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -320 (-1.16% of base) : 215701.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -128 (-0.73% of base) : 215723.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -128 (-0.62% of base) : 215666.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -125 (-6.93% of base) : 239280.dasm - Benchstone.BenchI.Puzzle:DoIt():bool:this -29 (-0.33% of base) : 216754.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -3 (-0.15% of base) : 225316.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -3 (-0.15% of base) : 225313.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int Top method regressions (percentages): 536 (18.29% of base) : 248723.dasm - Benchstone.BenchI.MulMatrix:Inner(System.Int32[][],System.Int32[][],System.Int32[][]) 390 (14.61% of base) : 220391.dasm - VectorTest:Main():int 390 (14.45% of base) : 220441.dasm - VectorTest:Main():int 150 ( 1.78% of base) : 239253.dasm - Benchstone.BenchF.LLoops:Main1(int):this 71 ( 1.72% of base) : 234803.dasm - SmallLoop1:Main():int 16 ( 1.00% of base) : 225588.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int 16 ( 1.00% of base) : 225590.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int 5 ( 0.26% of base) : 225285.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int Top method improvements (percentages): -125 (-6.93% of base) : 239280.dasm - Benchstone.BenchI.Puzzle:DoIt():bool:this -320 (-1.16% of base) : 215701.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -359 (-0.99% of base) : 215690.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -128 (-0.73% of base) : 215723.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -128 (-0.62% of base) : 215666.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -29 (-0.33% of base) : 216754.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -3 (-0.15% of base) : 225316.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int -3 (-0.15% of base) : 225313.dasm - IntelHardwareIntrinsicTest.Program:Main(System.String[]):int 16 total methods with Code Size differences (8 improved, 8 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 15351 Total bytes of diff: 15448 Total bytes of delta: 97 (0.63% of base) Total relative delta: 0.08 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 71 : 63973.dasm (7.63% of base) 26 : 106039.dasm (0.19% of base) 2 total files with Code Size differences (0 improved, 2 regressed), 1 unchanged. Top method regressions (bytes): 71 ( 7.63% of base) : 63973.dasm - Microsoft.Diagnostics.Tracing.Parsers.Symbol.FileVersionTraceData:ToXml(System.Text.StringBuilder):System.Text.StringBuilder:this 26 ( 0.19% of base) : 106039.dasm - Microsoft.VisualBasic.CompilerServices.VBBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method regressions (percentages): 71 ( 7.63% of base) : 63973.dasm - Microsoft.Diagnostics.Tracing.Parsers.Symbol.FileVersionTraceData:ToXml(System.Text.StringBuilder):System.Text.StringBuilder:this 26 ( 0.19% of base) : 106039.dasm - Microsoft.VisualBasic.CompilerServices.VBBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 2 total methods with Code Size differences (0 improved, 2 regressed), 1 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 22540 Total bytes of diff: 22576 Total bytes of delta: 36 (0.16% of base) Total relative delta: 0.01 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 23 : 104600.dasm (0.15% of base) 14 : 43143.dasm (0.47% of base) Top file improvements (bytes): -1 : 35267.dasm (-0.03% of base) 3 total files with Code Size differences (1 improved, 2 regressed), 0 unchanged. Top method regressions (bytes): 23 ( 0.15% of base) : 104600.dasm - Microsoft.VisualBasic.CompilerServices.VBBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this 14 ( 0.47% of base) : 43143.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.SourceMemberContainerTypeSymbol:ForceComplete(Microsoft.CodeAnalysis.SourceLocation,System.Threading.CancellationToken):this Top method improvements (bytes): -1 (-0.03% of base) : 35267.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.LanguageParser:ParseNamespaceBody(byref,byref,byref,ushort):this Top method regressions (percentages): 14 ( 0.47% of base) : 43143.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.SourceMemberContainerTypeSymbol:ForceComplete(Microsoft.CodeAnalysis.SourceLocation,System.Threading.CancellationToken):this 23 ( 0.15% of base) : 104600.dasm - Microsoft.VisualBasic.CompilerServices.VBBinder:BindToMethod(int,System.Reflection.MethodBase[],byref,System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[],byref):System.Reflection.MethodBase:this Top method improvements (percentages): -1 (-0.03% of base) : 35267.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.LanguageParser:ParseNamespaceBody(byref,byref,byref,ushort):this 3 total methods with Code Size differences (1 improved, 2 regressed), 0 unchanged. ```
-------------------------------------------------------------------------------- ``` Summary of Code Size diffs: (Lower is better) Total bytes of base: 53782 Total bytes of diff: 53837 Total bytes of delta: 55 (0.10% of base) Total relative delta: 0.00 diff is a regression. relative diff is a regression. ```
Detail diffs ``` Top file regressions (bytes): 28 : 28889.dasm (0.16% of base) 27 : 28887.dasm (0.15% of base) 2 total files with Code Size differences (0 improved, 2 regressed), 2 unchanged. Top method regressions (bytes): 28 ( 0.16% of base) : 28889.dasm - ManagedTests.DynamicCSharp.Conformance.dynamic.statements.freach.freach003.freach003.Test:MainMethod():int 27 ( 0.15% of base) : 28887.dasm - ManagedTests.DynamicCSharp.Conformance.dynamic.statements.freach.freach004.freach004.Test:MainMethod():int Top method regressions (percentages): 28 ( 0.16% of base) : 28889.dasm - ManagedTests.DynamicCSharp.Conformance.dynamic.statements.freach.freach003.freach003.Test:MainMethod():int 27 ( 0.15% of base) : 28887.dasm - ManagedTests.DynamicCSharp.Conformance.dynamic.statements.freach.freach004.freach004.Test:MainMethod():int 2 total methods with Code Size differences (0 improved, 2 regressed), 2 unchanged. ```
-------------------------------------------------------------------------------- --- src/coreclr/jit/block.cpp | 2 +- src/coreclr/jit/block.h | 6 ++---- src/coreclr/jit/optimizer.cpp | 12 ++++++------ src/coreclr/jit/valuenum.cpp | 19 ++++++++++--------- src/coreclr/jit/valuenum.h | 11 +++++++---- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 5df8c1e4358..d0eb6c31481 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -1504,7 +1504,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind) } // Make sure we reserve a NOT_IN_LOOP value that isn't a legal table index. - static_assert_no_msg(MAX_LOOP_NUM < BasicBlock::NOT_IN_LOOP); + static_assert_no_msg(BasicBlock::MAX_LOOP_NUM < BasicBlock::NOT_IN_LOOP); block->bbNatLoopNum = BasicBlock::NOT_IN_LOOP; diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 5d42a162653..57816463a05 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -1019,14 +1019,12 @@ struct BasicBlock : private LIR::Range // The following fields are used for loop detection typedef unsigned char loopNumber; - static const unsigned NOT_IN_LOOP = UCHAR_MAX; + static const unsigned NOT_IN_LOOP = UCHAR_MAX; + static const unsigned MAX_LOOP_NUM = 64; loopNumber bbNatLoopNum; // Index, in optLoopTable, of most-nested loop that contains this block, // or else NOT_IN_LOOP if this block is not in a loop. -#define MAX_LOOP_NUM 16 // we're using a 'short' for the mask -#define LOOP_MASK_TP unsigned // must be big enough for a mask - // TODO-Cleanup: Get rid of bbStkDepth and use bbStackDepthOnEntry() instead union { unsigned short bbStkDepth; // stack depth on entry diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index b5eae0f8fab..3f8e1ee99cb 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -1045,8 +1045,8 @@ bool Compiler::optRecordLoop(BasicBlock* head, { // Record this loop in the table, if there's room. - assert(optLoopCount <= MAX_LOOP_NUM); - if (optLoopCount == MAX_LOOP_NUM) + assert(optLoopCount <= BasicBlock::MAX_LOOP_NUM); + if (optLoopCount == BasicBlock::MAX_LOOP_NUM) { #if COUNT_LOOPS loopOverflowThisMethod = true; @@ -1065,7 +1065,7 @@ bool Compiler::optRecordLoop(BasicBlock* head, if (optLoopTable == nullptr) { assert(loopInd == 0); - optLoopTable = getAllocator(CMK_LoopOpt).allocate(MAX_LOOP_NUM); + optLoopTable = getAllocator(CMK_LoopOpt).allocate(BasicBlock::MAX_LOOP_NUM); } else { @@ -2391,13 +2391,13 @@ void Compiler::optFindNaturalLoops() loopExitCountTable.record(static_cast(search.GetExitCount())); // Note that we continue to look for loops even if - // (optLoopCount == MAX_LOOP_NUM), in contrast to the !COUNT_LOOPS code below. + // (optLoopCount == BasicBlock::MAX_LOOP_NUM), in contrast to the !COUNT_LOOPS code below. // This gives us a better count and stats. Hopefully it doesn't affect actual codegen. CLANG_FORMAT_COMMENT_ANCHOR; #else // COUNT_LOOPS assert(recordedLoop); - if (optLoopCount == MAX_LOOP_NUM) + if (optLoopCount == BasicBlock::MAX_LOOP_NUM) { // We won't be able to record any more loops, so stop looking. goto NO_MORE_LOOPS; @@ -6588,7 +6588,7 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* loo // their definition occurs. BasicBlock::loopNumber vnLoopNum = vnStore->LoopOfVN(vn); - if (vnLoopNum == MAX_LOOP_NUM) + if (vnLoopNum == BasicBlock::MAX_LOOP_NUM) { res = false; } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index c69e8bc3cbd..abda97b17e3 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -454,7 +454,7 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc) // We have no current allocation chunks. for (unsigned i = 0; i < TYP_COUNT; i++) { - for (unsigned j = CEA_None; j <= CEA_Count + MAX_LOOP_NUM; j++) + for (unsigned j = CEA_None; j <= CEA_Count + BasicBlock::MAX_LOOP_NUM; j++) { m_curAllocChunk[i][j] = NoChunk; } @@ -466,7 +466,8 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc) } // We will reserve chunk 0 to hold some special constants, like the constant NULL, the "exception" value, and the // "zero map." - Chunk* specialConstChunk = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const, MAX_LOOP_NUM); + Chunk* specialConstChunk = + new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const, BasicBlock::MAX_LOOP_NUM); specialConstChunk->m_numUsed += SRC_NumSpecialRefConsts; // Implicitly allocate 0 ==> NULL, and 1 ==> Exception, 2 ==> ZeroMap. ChunkNum cn = m_chunks.Push(specialConstChunk); @@ -1624,7 +1625,7 @@ ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, { Chunk* res; unsigned index; - if (loopNum == MAX_LOOP_NUM) + if (loopNum == BasicBlock::MAX_LOOP_NUM) { // Loop nest is unknown/irrelevant for this VN. index = attribs; @@ -1634,8 +1635,8 @@ ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, // Loop nest is interesting. Since we know this is only true for unique VNs, we know attribs will // be CEA_None and can just index based on loop number. noway_assert(attribs == CEA_None); - // Map NOT_IN_LOOP -> MAX_LOOP_NUM to make the index range contiguous [0..MAX_LOOP_NUM] - index = CEA_Count + (loopNum == BasicBlock::NOT_IN_LOOP ? MAX_LOOP_NUM : loopNum); + // Map NOT_IN_LOOP -> BasicBlock::MAX_LOOP_NUM to make the index range contiguous [0..BasicBlock::MAX_LOOP_NUM] + index = CEA_Count + (loopNum == BasicBlock::NOT_IN_LOOP ? BasicBlock::MAX_LOOP_NUM : loopNum); } ChunkNum cn = m_curAllocChunk[typ][index]; if (cn != NoChunk) @@ -3667,7 +3668,7 @@ ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ) BasicBlock::loopNumber loopNum; if (block == nullptr) { - loopNum = MAX_LOOP_NUM; + loopNum = BasicBlock::MAX_LOOP_NUM; } else { @@ -4344,21 +4345,21 @@ var_types ValueNumStore::TypeOfVN(ValueNum vn) //------------------------------------------------------------------------ // LoopOfVN: If the given value number is an opaque one associated with a particular // expression in the IR, give the loop number where the expression occurs; otherwise, -// returns MAX_LOOP_NUM. +// returns BasicBlock::MAX_LOOP_NUM. // // Arguments: // vn - Value number to query // // Return Value: // The correspondingblock's bbNatLoopNum, which may be BasicBlock::NOT_IN_LOOP. -// Returns MAX_LOOP_NUM if this VN is not an opaque value number associated with +// Returns BasicBlock::MAX_LOOP_NUM if this VN is not an opaque value number associated with // a particular expression/location in the IR. BasicBlock::loopNumber ValueNumStore::LoopOfVN(ValueNum vn) { if (vn == NoVN) { - return MAX_LOOP_NUM; + return BasicBlock::MAX_LOOP_NUM; } Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn)); diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index c1cf8af6c2a..481622a8198 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -596,7 +596,7 @@ public: // Returns TYP_UNKNOWN if the given value number has not been given a type. var_types TypeOfVN(ValueNum vn); - // Returns MAX_LOOP_NUM if the given value number's loop nest is unknown or ill-defined. + // Returns BasicBlock::MAX_LOOP_NUM if the given value number's loop nest is unknown or ill-defined. BasicBlock::loopNumber LoopOfVN(ValueNum vn); // Returns true iff the VN represents a (non-handle) constant. @@ -1121,14 +1121,17 @@ private: JitExpandArrayStack m_chunks; // These entries indicate the current allocation chunk, if any, for each valid combination of . Valid combinations require attribs==CEA_None or loopNum==MAX_LOOP_NUM. + // ChunkExtraAttribute, loopNumber>. Valid combinations require attribs==CEA_None or + // loopNum==BasicBlock::MAX_LOOP_NUM. // If the value is NoChunk, it indicates that there is no current allocation chunk for that pair, otherwise // it is the index in "m_chunks" of a chunk with the given attributes, in which the next allocation should // be attempted. - ChunkNum m_curAllocChunk[TYP_COUNT][CEA_Count + MAX_LOOP_NUM + 1]; + ChunkNum m_curAllocChunk[TYP_COUNT][CEA_Count + BasicBlock::MAX_LOOP_NUM + 1]; // Returns a (pointer to a) chunk in which a new value number may be allocated. - Chunk* GetAllocChunk(var_types typ, ChunkExtraAttribs attribs, BasicBlock::loopNumber loopNum = MAX_LOOP_NUM); + Chunk* GetAllocChunk(var_types typ, + ChunkExtraAttribs attribs, + BasicBlock::loopNumber loopNum = BasicBlock::MAX_LOOP_NUM); // First, we need mechanisms for mapping from constants to value numbers. // For small integers, we'll use an array. From 6d3bb36ca4e1adeda33af1d4e20fad1adc752496 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Thu, 15 Jul 2021 11:13:15 -0700 Subject: [PATCH 596/926] Improve `TryTransformStoreObjAsStoreInd` optimization. (#55727) * Fix `TryTransformStoreObjAsStoreInd` optimization. * Disable the assert. It was failing on DevDiv_280120 arm32 linux, did not repro with an altjit. --- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/lower.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 4b7374573ef..dd04a63789e 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1251,7 +1251,7 @@ public: return OperIsInitVal(OperGet()); } - bool IsConstInitVal() + bool IsConstInitVal() const { return (gtOper == GT_CNS_INT) || (OperIsInitVal() && (gtGetOp1()->gtOper == GT_CNS_INT)); } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index a5b0ba16fdc..c5b6a75ef2f 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -6730,7 +6730,7 @@ void Lowering::LowerBlockStoreCommon(GenTreeBlk* blkNode) bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode) { assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK, GT_STORE_OBJ)); - if (comp->opts.OptimizationEnabled()) + if (!comp->opts.OptimizationEnabled()) { return false; } @@ -6751,7 +6751,9 @@ bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode) { return false; } - if (varTypeIsSIMD(regType)) + + GenTree* src = blkNode->Data(); + if (varTypeIsSIMD(regType) && src->IsConstInitVal()) { // TODO-CQ: support STORE_IND SIMD16(SIMD16, CNT_INT 0). return false; @@ -6764,13 +6766,12 @@ bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode) return false; } - GenTree* src = blkNode->Data(); if (src->OperIsInitVal() && !src->IsConstInitVal()) { return false; } - if (varTypeIsSmall(regType) && !src->IsConstInitVal()) + if (varTypeIsSmall(regType) && !src->IsConstInitVal() && !src->IsLocal()) { // source operand INDIR will use a widening instruction // and generate worse code, like `movzx` instead of `mov` From 0a5cf4e7a1b1fb97b72685d752125a8962f7a154 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 15 Jul 2021 11:41:56 -0700 Subject: [PATCH 597/926] Support more collections in STJ source generator (#55566) * Support more collections in STJ source generator * Fix tests * Revert redundant MLC change and address feedback --- .../Common/ReflectionExtensions.cs | 216 +++++ .../System.Text.Json/gen/CollectionType.cs | 29 +- .../gen/ContextGenerationSpec.cs | 4 +- .../gen/JsonSourceGenerator.Emitter.cs | 170 ++-- .../gen/JsonSourceGenerator.Parser.cs | 310 ++++++-- .../Reflection/CustomAttributeDataWrapper.cs | 10 +- .../Reflection/MetadataLoadContextInternal.cs | 2 - .../gen/Reflection/TypeExtensions.cs | 78 +- .../gen/Reflection/TypeWrapper.cs | 80 +- .../gen/TypeGenerationSpec.cs | 73 +- .../System.Text.Json/ref/System.Text.Json.cs | 17 + .../ref/System.Text.Json.csproj | 2 + .../IAsyncEnumerableConverterFactory.cs | 4 +- .../Collection/IEnumerableConverterFactory.cs | 1 + .../IEnumerableConverterFactoryHelpers.cs | 204 +---- .../IEnumerableWithAddMethodConverter.cs | 22 +- ...mmutableDictionaryOfTKeyTValueConverter.cs | 28 +- .../ImmutableEnumerableOfTConverter.cs | 23 +- .../Object/KeyValuePairConverter.cs | 2 +- .../Text/Json/Serialization/JsonConverter.cs | 4 +- .../JsonSerializer.Read.HandlePropertyName.cs | 1 + .../Serialization/JsonSerializerContext.cs | 1 + .../JsonMetadataServices.Collections.cs | 468 +++++++++++ .../JsonMetadataServices.Converters.cs | 2 +- .../Serialization/Metadata/JsonTypeInfo.cs | 10 + .../Metadata/JsonTypeInfoInternalOfT.cs | 12 +- .../CollectionTests.AsyncEnumerable.cs | 70 +- .../CollectionTests.Concurrent.cs | 70 ++ .../CollectionTests.Dictionary.KeyPolicy.cs | 104 +-- ...CollectionTests.Dictionary.NonStringKey.cs | 0 .../CollectionTests.Dictionary.cs | 529 +++++++------ .../CollectionTests.Generic.Read.cs | 407 +++++----- .../CollectionTests.Generic.Write.cs | 313 ++++---- .../CollectionTests.Immutable.Read.cs | 637 +++++++++++++++ .../CollectionTests.Immutable.Write.cs | 193 ++--- .../CollectionTests.KeyValuePair.cs | 133 ++-- .../CollectionTests.NonGeneric.Read.cs | 173 ++-- .../CollectionTests.NonGeneric.Write.cs | 147 ++-- .../CollectionTests.ObjectModel.Read.cs | 53 ++ .../CollectionTests.ObjectModel.Write.cs | 19 +- .../CollectionTests.Specialized.Read.cs | 52 ++ .../CollectionTests.Specialized.Write.cs | 38 + .../Common/CollectionTests/CollectionTests.cs | 11 + .../Common/JsonSerializerWrapperForStream.cs | 22 + .../tests/Common/JsonTestHelper.cs | 17 + .../tests/Common/SerializerTests.cs | 5 +- .../tests/Common/TestClasses/TestClasses.cs | 16 + .../JsonSerializerContextTests.cs | 42 + .../RealWorldContextTests.cs | 38 - .../Serialization/CollectionTests.cs | 740 ++++++++++++++++++ ..._SourceGen.cs => JsonSerializerWrapper.cs} | 32 +- .../Serialization/PropertyVisibilityTests.cs | 7 +- .../SerializationLogicTests.cs | 4 +- ...em.Text.Json.SourceGeneration.Tests.csproj | 22 +- .../TestClasses.cs | 2 + .../System.Text.Json.Tests/JsonTestHelper.cs | 5 - .../Serialization/CollectionTests.cs | 10 + .../CollectionTests.Concurrent.Write.cs | 29 - .../CollectionTests.Concurrent.cs | 51 -- .../CollectionTests.Immutable.Read.cs | 636 --------------- .../CollectionTests.ObjectModel.Read.cs | 52 -- .../CollectionTests.Specialized.Read.cs | 51 -- .../CollectionTests.Specialized.Write.cs | 37 - ...m.cs => JsonSerializerWrapperForStream.cs} | 17 +- .../JsonSerializerWrapperForString_Dynamic.cs | 32 - .../Serialization/PropertyNameTests.cs | 16 - .../Serialization/PropertyVisibilityTests.cs | 4 +- .../Serialization/Stream.Collections.cs | 1 - .../Stream.DeserializeAsyncEnumerable.cs | 2 +- .../Serialization/StreamTests.cs | 8 +- .../System.Text.Json.Tests.csproj | 39 +- 71 files changed, 4217 insertions(+), 2442 deletions(-) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.AsyncEnumerable.cs (78%) create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Concurrent.cs rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs (71%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs (100%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Dictionary.cs (67%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Generic.Read.cs (55%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Generic.Write.cs (69%) create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Read.cs rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.Immutable.Write.cs (64%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.KeyValuePair.cs (68%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.NonGeneric.Read.cs (59%) rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.NonGeneric.Write.cs (64%) create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Read.cs rename src/libraries/System.Text.Json/tests/{System.Text.Json.Tests/Serialization => Common}/CollectionTests/CollectionTests.ObjectModel.Write.cs (51%) create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Read.cs create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs create mode 100644 src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.cs create mode 100644 src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForStream.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/CollectionTests.cs rename src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/{JsonSerializerWrapperForString_SourceGen.cs => JsonSerializerWrapper.cs} (70%) create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.Write.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Read.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Read.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Read.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Write.cs rename src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/{JsonSerializationWrapperForStream.cs => JsonSerializerWrapperForStream.cs} (77%) delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs diff --git a/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs index 5d9ef545b8d..623aa48fcee 100644 --- a/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs +++ b/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs @@ -3,11 +3,227 @@ using System.Diagnostics; using System.Reflection; +#if !BUILDING_SOURCE_GENERATOR +using System.Diagnostics.CodeAnalysis; +#endif namespace System.Text.Json.Reflection { internal static partial class ReflectionExtensions { + // Immutable collection types. + private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1"; + private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1"; + private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1"; + private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1"; + private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1"; + private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1"; + private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1"; + private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1"; + private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1"; + private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1"; + private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2"; + private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2"; + private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2"; + + // Immutable collection builder types. + private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray"; + private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList"; + private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack"; + private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue"; + private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet"; + private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet"; + private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary"; + private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary"; + + public const string CreateRangeMethodName = "CreateRange"; + + public static Type? GetCompatibleGenericBaseClass( + this Type type, + Type baseType, + Type? objectType = null, + bool sourceGenType = false) + { + Debug.Assert(baseType.IsGenericType); + Debug.Assert(!baseType.IsInterface); + Debug.Assert(baseType == baseType.GetGenericTypeDefinition()); + + // Work around not being able to use typeof(object) directly during compile-time src gen type analysis. + objectType ??= typeof(object); + + Type? baseTypeToCheck = type; + + while (baseTypeToCheck != null && baseTypeToCheck != typeof(object)) + { + if (baseTypeToCheck.IsGenericType) + { + Type genericTypeToCheck = baseTypeToCheck.GetGenericTypeDefinition(); + if (genericTypeToCheck == baseType || + (sourceGenType && (OpenGenericTypesHaveSamePrefix(baseType, genericTypeToCheck)))) + { + return baseTypeToCheck; + } + } + + baseTypeToCheck = baseTypeToCheck.BaseType; + } + + return null; + } + +#if !BUILDING_SOURCE_GENERATOR + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "The 'interfaceType' must exist and so trimmer kept it. In which case " + + "It also kept it on any type which implements it. The below call to GetInterfaces " + + "may return fewer results when trimmed but it will return the 'interfaceType' " + + "if the type implemented it, even after trimming.")] +#endif + public static Type? GetCompatibleGenericInterface(this Type type, Type interfaceType) + { + Debug.Assert(interfaceType.IsGenericType); + Debug.Assert(interfaceType.IsInterface); + Debug.Assert(interfaceType == interfaceType.GetGenericTypeDefinition()); + + Type interfaceToCheck = type; + + if (interfaceToCheck.IsGenericType) + { + interfaceToCheck = interfaceToCheck.GetGenericTypeDefinition(); + } + + if (interfaceToCheck == interfaceType) + { + return type; + } + + foreach (Type typeToCheck in type.GetInterfaces()) + { + if (typeToCheck.IsGenericType) + { + Type genericInterfaceToCheck = typeToCheck.GetGenericTypeDefinition(); + if (genericInterfaceToCheck == interfaceType) + { + return typeToCheck; + } + } + } + + return null; + } + + public static bool IsImmutableDictionaryType(this Type type, bool sourceGenType = false) + { + if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringComparison.Ordinal)) + { + return false; + } + + switch (GetBaseNameFromGenericType(type, sourceGenType)) + { + case ImmutableDictionaryGenericTypeName: + case ImmutableDictionaryGenericInterfaceTypeName: + case ImmutableSortedDictionaryGenericTypeName: + return true; + default: + return false; + } + } + + public static bool IsImmutableEnumerableType(this Type type, bool sourceGenType = false) + { + if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringComparison.Ordinal)) + { + return false; + } + + switch (GetBaseNameFromGenericType(type, sourceGenType)) + { + case ImmutableArrayGenericTypeName: + case ImmutableListGenericTypeName: + case ImmutableListGenericInterfaceTypeName: + case ImmutableStackGenericTypeName: + case ImmutableStackGenericInterfaceTypeName: + case ImmutableQueueGenericTypeName: + case ImmutableQueueGenericInterfaceTypeName: + case ImmutableSortedSetGenericTypeName: + case ImmutableHashSetGenericTypeName: + case ImmutableSetGenericInterfaceTypeName: + return true; + default: + return false; + } + } + + public static string? GetImmutableDictionaryConstructingTypeName(this Type type, bool sourceGenType = false) + { + Debug.Assert(type.IsImmutableDictionaryType(sourceGenType)); + + // Use the generic type definition of the immutable collection to determine + // an appropriate constructing type, i.e. a type that we can invoke the + // `CreateRange` method on, which returns the desired immutable collection. + switch (GetBaseNameFromGenericType(type, sourceGenType)) + { + case ImmutableDictionaryGenericTypeName: + case ImmutableDictionaryGenericInterfaceTypeName: + return ImmutableDictionaryTypeName; + case ImmutableSortedDictionaryGenericTypeName: + return ImmutableSortedDictionaryTypeName; + default: + // We verified that the type is an immutable collection, so the + // generic definition is one of the above. + return null; + } + } + + public static string? GetImmutableEnumerableConstructingTypeName(this Type type, bool sourceGenType = false) + { + Debug.Assert(type.IsImmutableEnumerableType(sourceGenType)); + + // Use the generic type definition of the immutable collection to determine + // an appropriate constructing type, i.e. a type that we can invoke the + // `CreateRange` method on, which returns the desired immutable collection. + switch (GetBaseNameFromGenericType(type, sourceGenType)) + { + case ImmutableArrayGenericTypeName: + return ImmutableArrayTypeName; + case ImmutableListGenericTypeName: + case ImmutableListGenericInterfaceTypeName: + return ImmutableListTypeName; + case ImmutableStackGenericTypeName: + case ImmutableStackGenericInterfaceTypeName: + return ImmutableStackTypeName; + case ImmutableQueueGenericTypeName: + case ImmutableQueueGenericInterfaceTypeName: + return ImmutableQueueTypeName; + case ImmutableSortedSetGenericTypeName: + return ImmutableSortedSetTypeName; + case ImmutableHashSetGenericTypeName: + case ImmutableSetGenericInterfaceTypeName: + return ImmutableHashSetTypeName; + default: + // We verified that the type is an immutable collection, so the + // generic definition is one of the above. + return null; + } + } + + private static bool OpenGenericTypesHaveSamePrefix(Type t1, Type t2) + => t1.FullName == GetBaseNameFromGenericTypeDef(t2); + + private static string GetBaseNameFromGenericType(Type genericType, bool sourceGenType) + { + Type genericTypeDef = genericType.GetGenericTypeDefinition(); + return sourceGenType ? GetBaseNameFromGenericTypeDef(genericTypeDef) : genericTypeDef.FullName!; + } + + private static string GetBaseNameFromGenericTypeDef(Type genericTypeDef) + { + Debug.Assert(genericTypeDef.IsGenericType); + string fullName = genericTypeDef.FullName!; + int length = fullName.IndexOf("`") + 2; + return fullName.Substring(0, length); + } + public static bool IsVirtual(this PropertyInfo? propertyInfo) { Debug.Assert(propertyInfo != null); diff --git a/src/libraries/System.Text.Json/gen/CollectionType.cs b/src/libraries/System.Text.Json/gen/CollectionType.cs index 6f736698f47..26e5bca1e6e 100644 --- a/src/libraries/System.Text.Json/gen/CollectionType.cs +++ b/src/libraries/System.Text.Json/gen/CollectionType.cs @@ -9,11 +9,28 @@ namespace System.Text.Json.SourceGeneration { internal enum CollectionType { - NotApplicable = 0, - Array = 1, - List = 2, - IEnumerable = 3, - IList = 4, - Dictionary = 5 + NotApplicable, + // Dictionary types + IDictionary, + Dictionary, + ImmutableDictionary, + IDictionaryOfTKeyTValue, + IReadOnlyDictionary, + // Non-dictionary types + Array, + List, + IEnumerable, + IList, + IListOfT, + ISet, + ICollectionOfT, + StackOfT, + QueueOfT, + ConcurrentStack, + ConcurrentQueue, + IEnumerableOfT, + Stack, + Queue, + ImmutableEnumerable } } diff --git a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs index 8020f798086..1339e4b9d16 100644 --- a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using System.Text.Json.Reflection; +using System.Diagnostics; namespace System.Text.Json.SourceGeneration { @@ -11,6 +12,7 @@ namespace System.Text.Json.SourceGeneration /// Represents the set of input types and options needed to provide an /// implementation for a user-provided JsonSerializerContext-derived type. /// + [DebuggerDisplay("ContextTypeRef={ContextTypeRef}")] internal sealed class ContextGenerationSpec { public JsonSourceGenerationOptionsAttribute GenerationOptions { get; init; } @@ -34,6 +36,6 @@ namespace System.Text.Json.SourceGeneration /// public HashSet RuntimePropertyNames { get; } = new(); - public string ContextTypeRef => $"global::{ContextType.GetUniqueCompilableTypeName()}"; + public string ContextTypeRef => ContextType.GetCompilableName(); } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index cbab663971e..fe8447e1309 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -328,12 +328,9 @@ namespace {_currentContext.ContextType.Namespace} private string GenerateForCollection(TypeGenerationSpec typeGenerationSpec) { - string typeCompilableName = typeGenerationSpec.TypeRef; - string typeFriendlyName = typeGenerationSpec.TypeInfoPropertyName; - // Key metadata TypeGenerationSpec? collectionKeyTypeMetadata = typeGenerationSpec.CollectionKeyTypeMetadata; - Debug.Assert(!(typeGenerationSpec.CollectionType == CollectionType.Dictionary && collectionKeyTypeMetadata == null)); + Debug.Assert(!(typeGenerationSpec.ClassType == ClassType.Dictionary && collectionKeyTypeMetadata == null)); string? keyTypeCompilableName = collectionKeyTypeMetadata?.TypeRef; string? keyTypeReadableName = collectionKeyTypeMetadata?.TypeInfoPropertyName; @@ -361,11 +358,8 @@ namespace {_currentContext.ContextType.Namespace} string numberHandlingArg = $"{GetNumberHandlingAsStr(typeGenerationSpec.NumberHandling)}"; - string serializeMethodName = $"{typeFriendlyName}{SerializeMethodNameSuffix}"; string serializeFuncNamedArg; - CollectionType collectionType = typeGenerationSpec.CollectionType; - string? serializeFuncSource; if (!typeGenerationSpec.GenerateSerializationLogic) { @@ -374,81 +368,149 @@ namespace {_currentContext.ContextType.Namespace} } else { - bool canBeNull = typeGenerationSpec.CanBeNull; + serializeFuncSource = typeGenerationSpec.ClassType == ClassType.Enumerable + ? GenerateFastPathFuncForEnumerable(typeGenerationSpec) + : GenerateFastPathFuncForDictionary(typeGenerationSpec); - switch (collectionType) - { - case CollectionType.Array: - serializeFuncSource = GenerateFastPathFuncForEnumerable(typeCompilableName, serializeMethodName, canBeNull, isArray: true, collectionValueTypeMetadata); - break; - case CollectionType.List: - serializeFuncSource = GenerateFastPathFuncForEnumerable(typeCompilableName, serializeMethodName, canBeNull, isArray: false, collectionValueTypeMetadata); - break; - case CollectionType.Dictionary: - serializeFuncSource = GenerateFastPathFuncForDictionary(typeCompilableName, serializeMethodName, canBeNull, collectionKeyTypeMetadata, collectionValueTypeMetadata); - break; - default: - serializeFuncSource = null; - break; - } - - serializeFuncNamedArg = $"serializeFunc: {serializeMethodName}"; + serializeFuncNamedArg = $"serializeFunc: {typeGenerationSpec.FastPathSerializeMethodName}"; } - string collectionTypeInfoValue = collectionType switch + CollectionType collectionType = typeGenerationSpec.CollectionType; + + string typeRef = typeGenerationSpec.TypeRef; + string createObjectFuncArg = typeGenerationSpec.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor + ? $"createObjectFunc: () => new {typeRef}()" + : "createObjectFunc: null"; + + string collectionInfoCreationPrefix = collectionType switch { - CollectionType.Array => $"{JsonMetadataServicesTypeRef}.CreateArrayInfo<{valueTypeCompilableName}>({OptionsInstanceVariableName}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})", - CollectionType.List => $"{JsonMetadataServicesTypeRef}.CreateListInfo<{typeCompilableName}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new {ListTypeRef}<{valueTypeCompilableName}>(), {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})", - CollectionType.Dictionary => $"{JsonMetadataServicesTypeRef}.CreateDictionaryInfo<{typeCompilableName}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new {DictionaryTypeRef}<{keyTypeCompilableName}, {valueTypeCompilableName}>(), {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})", - _ => throw new NotSupportedException() + CollectionType.IListOfT => $"{JsonMetadataServicesTypeRef}.CreateIListInfo<", + CollectionType.ICollectionOfT => $"{JsonMetadataServicesTypeRef}.CreateICollectionInfo<", + CollectionType.StackOfT => $"{JsonMetadataServicesTypeRef}.CreateStackInfo<", + CollectionType.QueueOfT => $"{JsonMetadataServicesTypeRef}.CreateQueueInfo<", + CollectionType.Stack => $"{JsonMetadataServicesTypeRef}.CreateStackOrQueueInfo<", + CollectionType.Queue => $"{JsonMetadataServicesTypeRef}.CreateStackOrQueueInfo<", + CollectionType.IEnumerableOfT => $"{JsonMetadataServicesTypeRef}.CreateIEnumerableInfo<", + CollectionType.IDictionaryOfTKeyTValue => $"{JsonMetadataServicesTypeRef}.CreateIDictionaryInfo<", + _ => $"{JsonMetadataServicesTypeRef}.Create{collectionType}Info<" }; - string metadataInitSource = @$"_{typeFriendlyName} = {collectionTypeInfoValue};"; + string dictInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}"; + string enumerableInfoCreationPrefix = $"{collectionInfoCreationPrefix}{typeRef}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}"; + string immutableCollectionCreationSuffix = $"createRangeFunc: {typeGenerationSpec.ImmutableCollectionBuilderName}"; + + string collectionTypeInfoValue; + + switch (collectionType) + { + case CollectionType.Array: + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{valueTypeCompilableName}>({OptionsInstanceVariableName}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + break; + case CollectionType.IEnumerable: + case CollectionType.IList: + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + break; + case CollectionType.Stack: + case CollectionType.Queue: + string addMethod = collectionType == CollectionType.Stack ? "Push" : "Enqueue"; + string addFuncNamedArg = $"addFunc: (collection, {ValueVarName}) => collection.{addMethod}({ValueVarName})"; + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg}, {addFuncNamedArg})"; + break; + case CollectionType.ImmutableEnumerable: + collectionTypeInfoValue = $"{enumerableInfoCreationPrefix}, {immutableCollectionCreationSuffix})"; + break; + case CollectionType.IDictionary: + collectionTypeInfoValue = $"{collectionInfoCreationPrefix}{typeRef}>({OptionsInstanceVariableName}, {createObjectFuncArg}, {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg}, {serializeFuncNamedArg})"; + break; + case CollectionType.Dictionary: + case CollectionType.IDictionaryOfTKeyTValue: + case CollectionType.IReadOnlyDictionary: + collectionTypeInfoValue = $"{dictInfoCreationPrefix})"; + break; + case CollectionType.ImmutableDictionary: + collectionTypeInfoValue = $"{dictInfoCreationPrefix}, {immutableCollectionCreationSuffix})"; + break; + default: + collectionTypeInfoValue = $"{enumerableInfoCreationPrefix})"; + break; + } + + string metadataInitSource = @$"_{typeGenerationSpec.TypeInfoPropertyName} = {collectionTypeInfoValue};"; return GenerateForType(typeGenerationSpec, metadataInitSource, serializeFuncSource); } - private string GenerateFastPathFuncForEnumerable(string typeInfoRef, string serializeMethodName, bool canBeNull, bool isArray, TypeGenerationSpec valueTypeGenerationSpec) + private string GenerateFastPathFuncForEnumerable(TypeGenerationSpec typeGenerationSpec) { - string? writerMethodToCall = GetWriterMethod(valueTypeGenerationSpec.Type); - string valueToWrite = $"{ValueVarName}[i]"; - string lengthPropName = isArray ? "Length" : "Count"; + TypeGenerationSpec valueTypeGenerationSpec = typeGenerationSpec.CollectionValueTypeMetadata; - string elementSerializationLogic; - if (writerMethodToCall != null) + Type elementType = valueTypeGenerationSpec.Type; + string? writerMethodToCall = GetWriterMethod(elementType); + + string iterationLogic; + string valueToWrite; + + switch (typeGenerationSpec.CollectionType) { - elementSerializationLogic = $"{writerMethodToCall}Value({valueToWrite});"; - } - else + case CollectionType.Array: + iterationLogic = $"for (int i = 0; i < {ValueVarName}.Length; i++)"; + valueToWrite = $"{ValueVarName}[i]"; + break; + case CollectionType.IListOfT: + case CollectionType.List: + case CollectionType.IList: + iterationLogic = $"for (int i = 0; i < {ValueVarName}.Count; i++)"; + valueToWrite = $"{ValueVarName}[i]"; + break; + default: + const string elementVarName = "element"; + iterationLogic = $"foreach ({valueTypeGenerationSpec.TypeRef} {elementVarName} in {ValueVarName})"; + valueToWrite = elementVarName; + break; + }; + + if (elementType == _generationSpec.CharType) { - elementSerializationLogic = GetSerializeLogicForNonPrimitiveType(valueTypeGenerationSpec.TypeInfoPropertyName, valueToWrite, valueTypeGenerationSpec.GenerateSerializationLogic); + valueToWrite = $"{valueToWrite}.ToString()"; } + string elementSerializationLogic = writerMethodToCall == null + ? GetSerializeLogicForNonPrimitiveType(valueTypeGenerationSpec.TypeInfoPropertyName, valueToWrite, valueTypeGenerationSpec.GenerateSerializationLogic) + : $"{writerMethodToCall}Value({valueToWrite});"; + string serializationLogic = $@"{WriterVarName}.WriteStartArray(); - for (int i = 0; i < {ValueVarName}.{lengthPropName}; i++) + {iterationLogic} {{ {elementSerializationLogic} }} {WriterVarName}.WriteEndArray();"; - return GenerateFastPathFuncForType(serializeMethodName, typeInfoRef, serializationLogic, canBeNull); + return GenerateFastPathFuncForType( + typeGenerationSpec.FastPathSerializeMethodName, + typeGenerationSpec.TypeRef, + serializationLogic, + typeGenerationSpec.CanBeNull); } - private string GenerateFastPathFuncForDictionary( - string typeInfoRef, - string serializeMethodName, - bool canBeNull, - TypeGenerationSpec keyTypeGenerationSpec, - TypeGenerationSpec valueTypeGenerationSpec) + private string GenerateFastPathFuncForDictionary(TypeGenerationSpec typeGenerationSpec) { + TypeGenerationSpec keyTypeGenerationSpec = typeGenerationSpec.CollectionKeyTypeMetadata; + TypeGenerationSpec valueTypeGenerationSpec = typeGenerationSpec.CollectionValueTypeMetadata; + + Type elementType = valueTypeGenerationSpec.Type; + string? writerMethodToCall = GetWriterMethod(elementType); + string elementSerializationLogic; + const string pairVarName = "pair"; string keyToWrite = $"{pairVarName}.Key"; string valueToWrite = $"{pairVarName}.Value"; - string? writerMethodToCall = GetWriterMethod(valueTypeGenerationSpec.Type); - string elementSerializationLogic; + if (elementType == _generationSpec.CharType) + { + valueToWrite = $"{valueToWrite}.ToString()"; + } if (writerMethodToCall != null) { @@ -469,7 +531,11 @@ namespace {_currentContext.ContextType.Namespace} {WriterVarName}.WriteEndObject();"; - return GenerateFastPathFuncForType(serializeMethodName, typeInfoRef, serializationLogic, canBeNull); + return GenerateFastPathFuncForType( + typeGenerationSpec.FastPathSerializeMethodName, + typeGenerationSpec.TypeRef, + serializationLogic, + typeGenerationSpec.CanBeNull); } private string GenerateForObject(TypeGenerationSpec typeMetadata) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 727d80c6b10..2290738c51a 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -21,42 +22,53 @@ namespace System.Text.Json.SourceGeneration private sealed class Parser { private const string SystemTextJsonNamespace = "System.Text.Json"; - private const string JsonConverterAttributeFullName = "System.Text.Json.Serialization.JsonConverterAttribute"; - + private const string JsonElementFullName = "System.Text.Json.JsonElement"; private const string JsonIgnoreAttributeFullName = "System.Text.Json.Serialization.JsonIgnoreAttribute"; - private const string JsonIgnoreConditionFullName = "System.Text.Json.Serialization.JsonIgnoreCondition"; - private const string JsonIncludeAttributeFullName = "System.Text.Json.Serialization.JsonIncludeAttribute"; - private const string JsonNumberHandlingAttributeFullName = "System.Text.Json.Serialization.JsonNumberHandlingAttribute"; - private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute"; private const string JsonPropertyOrderAttributeFullName = "System.Text.Json.Serialization.JsonPropertyOrderAttribute"; private readonly GeneratorExecutionContext _executionContext; - private readonly MetadataLoadContextInternal _metadataLoadContext; + private readonly Type _ilistOfTType; + private readonly Type _icollectionOfTType; private readonly Type _ienumerableType; - private readonly Type _listOfTType; - private readonly Type _dictionaryType; + private readonly Type _ienumerableOfTType; + + private readonly Type? _listOfTType; + private readonly Type? _dictionaryType; + private readonly Type? _idictionaryOfTKeyTValueType; + private readonly Type? _ireadonlyDictionaryType; + private readonly Type? _isetType; + private readonly Type? _stackOfTType; + private readonly Type? _queueOfTType; + private readonly Type? _concurrentStackType; + private readonly Type? _concurrentQueueType; + private readonly Type? _idictionaryType; + private readonly Type? _ilistType; + private readonly Type? _stackType; + private readonly Type? _queueType; private readonly Type _booleanType; - private readonly Type _byteArrayType; private readonly Type _charType; private readonly Type _dateTimeType; - private readonly Type _dateTimeOffsetType; - private readonly Type _guidType; private readonly Type _nullableOfTType; + private readonly Type _objectType; private readonly Type _stringType; - private readonly Type _uriType; - private readonly Type _versionType; + + private readonly Type? _dateTimeOffsetType; + private readonly Type? _byteArrayType; + private readonly Type? _guidType; + private readonly Type? _uriType; + private readonly Type? _versionType; + private readonly Type? _jsonElementType; private readonly HashSet _numberTypes = new(); - private readonly HashSet _knownTypes = new(); /// @@ -81,20 +93,38 @@ namespace System.Text.Json.SourceGeneration _executionContext = executionContext; _metadataLoadContext = new MetadataLoadContextInternal(executionContext.Compilation); - _ienumerableType = _metadataLoadContext.Resolve(typeof(IEnumerable)); - _listOfTType = _metadataLoadContext.Resolve(typeof(List<>)); - _dictionaryType = _metadataLoadContext.Resolve(typeof(Dictionary<,>)); + _ilistOfTType = ResolveType(SpecialType.System_Collections_Generic_IList_T); + _icollectionOfTType = ResolveType(SpecialType.System_Collections_Generic_ICollection_T); + _ienumerableOfTType = ResolveType(SpecialType.System_Collections_Generic_IEnumerable_T); + _ienumerableType = ResolveType(SpecialType.System_Collections_IEnumerable); - _booleanType = _metadataLoadContext.Resolve(typeof(bool)); - _byteArrayType = _metadataLoadContext.Resolve(typeof(byte[])); - _charType = _metadataLoadContext.Resolve(typeof(char)); - _dateTimeType = _metadataLoadContext.Resolve(typeof(DateTime)); - _dateTimeOffsetType = _metadataLoadContext.Resolve(typeof(DateTimeOffset)); - _guidType = _metadataLoadContext.Resolve(typeof(Guid)); - _nullableOfTType = _metadataLoadContext.Resolve(typeof(Nullable<>)); - _stringType = _metadataLoadContext.Resolve(typeof(string)); - _uriType = _metadataLoadContext.Resolve(typeof(Uri)); - _versionType = _metadataLoadContext.Resolve(typeof(Version)); + _listOfTType = ResolveType(typeof(List<>).FullName!); + _dictionaryType = ResolveType(typeof(Dictionary<,>).FullName!); + _idictionaryOfTKeyTValueType = ResolveType(typeof(IDictionary<,>).FullName!); + _ireadonlyDictionaryType = ResolveType(typeof(IReadOnlyDictionary<,>).FullName!); + _isetType = ResolveType(typeof(ISet<>).FullName!); + _stackOfTType = ResolveType(typeof(Stack<>).FullName!); + _queueOfTType = ResolveType(typeof(Queue<>).FullName!); + _concurrentStackType = ResolveType(typeof(ConcurrentStack<>).FullName!); + _concurrentQueueType = ResolveType(typeof(ConcurrentQueue<>).FullName!); + _idictionaryType = ResolveType(typeof(IDictionary).FullName!); + _ilistType = ResolveType(typeof(IList).FullName!); + _stackType = ResolveType(typeof(Stack).FullName!); + _queueType = ResolveType(typeof(Queue).FullName!); + + _booleanType = ResolveType(SpecialType.System_Boolean); + _charType = ResolveType(SpecialType.System_Char); + _dateTimeType = ResolveType(SpecialType.System_DateTime); + _nullableOfTType = ResolveType(SpecialType.System_Nullable_T); + _objectType = ResolveType(SpecialType.System_Object); + _stringType = ResolveType(SpecialType.System_String); + + _dateTimeOffsetType = ResolveType(typeof(DateTimeOffset).FullName!); + _byteArrayType = ResolveType(typeof(byte[]).FullName!); + _guidType = ResolveType(typeof(Guid).FullName!); + _uriType = ResolveType(typeof(Uri).FullName!); + _versionType = ResolveType(typeof(Version).FullName!); + _jsonElementType = ResolveType(JsonElementFullName); PopulateKnownTypes(); } @@ -499,13 +529,11 @@ namespace System.Text.Json.SourceGeneration Type? collectionValueType = null; TypeGenerationSpec? nullableUnderlyingTypeGenSpec = null; List? propGenSpecList = null; - CollectionType collectionType = CollectionType.NotApplicable; ObjectConstructionStrategy constructionStrategy = default; + CollectionType collectionType = CollectionType.NotApplicable; JsonNumberHandling? numberHandling = null; - bool foundDesignTimeCustomConverter = false; string? converterInstatiationLogic = null; - bool implementsIJsonOnSerialized = false; bool implementsIJsonOnSerializing = false; @@ -526,6 +554,15 @@ namespace System.Text.Json.SourceGeneration } } + if (type.Name.StartsWith("StackWrapper")) + { + } + + if (type.GetConstructor(Type.EmptyTypes) != null && !type.IsAbstract && !type.IsInterface) + { + constructionStrategy = ObjectConstructionStrategy.ParameterlessConstructor; + } + if (foundDesignTimeCustomConverter) { classType = converterInstatiationLogic != null @@ -549,53 +586,150 @@ namespace System.Text.Json.SourceGeneration } else if (_ienumerableType.IsAssignableFrom(type)) { - // Only T[], List, and Dictionary are supported. + Type actualTypeToConvert; if (type.IsArray) { - classType = ClassType.Enumerable; + classType = type.GetArrayRank() > 1 + ? ClassType.TypeUnsupportedBySourceGen // Multi-dimentional arrays are not supported in STJ. + : ClassType.Enumerable; collectionType = CollectionType.Array; collectionValueType = type.GetElementType(); } - else if (!type.IsGenericType) + else if ((actualTypeToConvert = GetCompatibleGenericBaseClass(type, _listOfTType)) != null) { - classType = ClassType.TypeUnsupportedBySourceGen; + classType = ClassType.Enumerable; + collectionType = CollectionType.List; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = GetCompatibleGenericBaseClass(type, _dictionaryType)) != null) + { + classType = ClassType.Dictionary; + collectionType = CollectionType.Dictionary; + + Type[] genericArgs = actualTypeToConvert.GetGenericArguments(); + collectionKeyType = genericArgs[0]; + collectionValueType = genericArgs[1]; + } + else if (type.IsImmutableDictionaryType(sourceGenType: true)) + { + classType = ClassType.Dictionary; + collectionType = CollectionType.ImmutableDictionary; + + Type[] genericArgs = type.GetGenericArguments(); + collectionKeyType = genericArgs[0]; + collectionValueType = genericArgs[1]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_idictionaryOfTKeyTValueType)) != null) + { + classType = ClassType.Dictionary; + collectionType = CollectionType.IDictionaryOfTKeyTValue; + + Type[] genericArgs = actualTypeToConvert.GetGenericArguments(); + collectionKeyType = genericArgs[0]; + collectionValueType = genericArgs[1]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_ireadonlyDictionaryType)) != null) + { + classType = ClassType.Dictionary; + collectionType = CollectionType.IReadOnlyDictionary; + + Type[] genericArgs = actualTypeToConvert.GetGenericArguments(); + collectionKeyType = genericArgs[0]; + collectionValueType = genericArgs[1]; + } + else if (type.IsImmutableEnumerableType(sourceGenType: true)) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.ImmutableEnumerable; + collectionValueType = type.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_ilistOfTType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.IListOfT; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_isetType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.ISet; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_icollectionOfTType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.ICollectionOfT; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = GetCompatibleGenericBaseClass(type, _stackOfTType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.StackOfT; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = GetCompatibleGenericBaseClass(type, _queueOfTType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.QueueOfT; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericBaseClass(_concurrentStackType, _objectType, sourceGenType: true)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.ConcurrentStack; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericBaseClass(_concurrentQueueType, _objectType, sourceGenType: true)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.ConcurrentQueue; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if ((actualTypeToConvert = type.GetCompatibleGenericInterface(_ienumerableOfTType)) != null) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.IEnumerableOfT; + collectionValueType = actualTypeToConvert.GetGenericArguments()[0]; + } + else if (_idictionaryType.IsAssignableFrom(type)) + { + classType = ClassType.Dictionary; + collectionType = CollectionType.IDictionary; + collectionKeyType = _stringType; + collectionValueType = _objectType; + } + else if (_ilistType.IsAssignableFrom(type)) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.IList; + collectionValueType = _objectType; + } + else if (_stackType.IsAssignableFrom(type)) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.Stack; + collectionValueType = _objectType; + } + else if (_queueType.IsAssignableFrom(type)) + { + classType = ClassType.Enumerable; + collectionType = CollectionType.Queue; + collectionValueType = _objectType; } else { - Type genericTypeDef = type.GetGenericTypeDefinition(); - Type[] genericTypeArgs = type.GetGenericArguments(); - - if (genericTypeDef == _listOfTType) - { - classType = ClassType.Enumerable; - collectionType = CollectionType.List; - collectionValueType = genericTypeArgs[0]; - } - else if (genericTypeDef == _dictionaryType) - { - classType = ClassType.Dictionary; - collectionType = CollectionType.Dictionary; - collectionKeyType = genericTypeArgs[0]; - collectionValueType = genericTypeArgs[1]; - } - else - { - classType = ClassType.TypeUnsupportedBySourceGen; - } + classType = ClassType.Enumerable; + collectionType = CollectionType.IEnumerable; + collectionValueType = _objectType; } } else { classType = ClassType.Object; - if (type.GetConstructor(Type.EmptyTypes) != null && !type.IsAbstract && !type.IsInterface) - { - constructionStrategy = ObjectConstructionStrategy.ParameterlessConstructor; - } - // GetInterface() is currently not implemented, so we use GetInterfaces(). - IEnumerable interfaces = type.GetInterfaces().Select(interfaceType => interfaceType.FullName); + IEnumerable interfaces = type.GetInterfaces().Select(interfaceType => interfaceType.FullName!); implementsIJsonOnSerialized = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializedFullName) != null; implementsIJsonOnSerializing = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializingFullName) != null; @@ -648,11 +782,8 @@ namespace System.Text.Json.SourceGeneration typeMetadata.Initialize( generationMode, - typeRef: type.GetUniqueCompilableTypeName(), - typeInfoPropertyName: type.GetFriendlyTypeName(), type, classType, - isValueType: type.IsValueType, numberHandling, propGenSpecList, collectionType, @@ -667,6 +798,9 @@ namespace System.Text.Json.SourceGeneration return typeMetadata; } + private Type GetCompatibleGenericBaseClass(Type type, Type baseType) + => type.GetCompatibleGenericBaseClass(baseType, _objectType); + private void CacheMember( PropertyGenerationSpec propGenSpec, ref List propGenSpecList, @@ -860,7 +994,7 @@ namespace System.Text.Json.SourceGeneration Order = order, HasJsonInclude = hasJsonInclude, TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode), - DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}", + DeclaringTypeRef = memberInfo.DeclaringType.GetCompilableName(), ConverterInstantiationLogic = converterInstantiationLogic }; } @@ -883,7 +1017,7 @@ namespace System.Text.Json.SourceGeneration return null; } - return $"new {converterType.GetUniqueCompilableTypeName()}()"; + return $"new {converterType.GetCompilableName()}()"; } private static string DetermineRuntimePropName(string clrPropName, string? jsonPropName, JsonKnownNamingPolicy namingPolicy) @@ -909,23 +1043,22 @@ namespace System.Text.Json.SourceGeneration private void PopulateNumberTypes() { Debug.Assert(_numberTypes != null); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(byte))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(decimal))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(double))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(short))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(sbyte))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(int))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(long))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(float))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(ushort))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(uint))); - _numberTypes.Add(_metadataLoadContext.Resolve(typeof(ulong))); + _numberTypes.Add(ResolveType(SpecialType.System_Byte)); + _numberTypes.Add(ResolveType(SpecialType.System_Decimal)); + _numberTypes.Add(ResolveType(SpecialType.System_Double)); + _numberTypes.Add(ResolveType(SpecialType.System_Int16)); + _numberTypes.Add(ResolveType(SpecialType.System_SByte)); + _numberTypes.Add(ResolveType(SpecialType.System_Int32)); + _numberTypes.Add(ResolveType(SpecialType.System_Int64)); + _numberTypes.Add(ResolveType(SpecialType.System_Single)); + _numberTypes.Add(ResolveType(SpecialType.System_UInt64)); + _numberTypes.Add(ResolveType(SpecialType.System_UInt32)); + _numberTypes.Add(ResolveType(SpecialType.System_UInt64)); } private void PopulateKnownTypes() { PopulateNumberTypes(); - Debug.Assert(_knownTypes != null); Debug.Assert(_numberTypes != null); @@ -936,16 +1069,23 @@ namespace System.Text.Json.SourceGeneration _knownTypes.Add(_dateTimeType); _knownTypes.Add(_dateTimeOffsetType); _knownTypes.Add(_guidType); - _knownTypes.Add(_metadataLoadContext.Resolve(typeof(object))); + _knownTypes.Add(_objectType); _knownTypes.Add(_stringType); + _knownTypes.Add(_uriType); + _knownTypes.Add(_versionType); + _knownTypes.Add(_jsonElementType); + } - // System.Private.Uri may not be loaded in input compilation. - if (_uriType != null) - { - _knownTypes.Add(_uriType); - } + private Type ResolveType(string fullyQualifiedMetadataName) + { + INamedTypeSymbol? typeSymbol = _executionContext.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName); + return typeSymbol.AsType(_metadataLoadContext); + } - _knownTypes.Add(_metadataLoadContext.Resolve(typeof(Version))); + private Type ResolveType(SpecialType specialType) + { + INamedTypeSymbol? typeSymbol = _executionContext.Compilation.GetSpecialType(specialType); + return typeSymbol.AsType(_metadataLoadContext); } } } diff --git a/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs index e518212ce65..58354168b71 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/CustomAttributeDataWrapper.cs @@ -25,10 +25,18 @@ namespace System.Text.Json.Reflection } var constructorArguments = new List(); + foreach (TypedConstant ca in a.ConstructorArguments) { - constructorArguments.Add(new CustomAttributeTypedArgument(ca.Type.AsType(metadataLoadContext), ca.Value)); + if (ca.Kind == TypedConstantKind.Error) + { + continue; + } + + object value = ca.Kind == TypedConstantKind.Array ? ca.Values : ca.Value; + constructorArguments.Add(new CustomAttributeTypedArgument(ca.Type.AsType(metadataLoadContext), value)); } + Constructor = new ConstructorInfoWrapper(a.AttributeConstructor!, metadataLoadContext); NamedArguments = namedArguments; ConstructorArguments = constructorArguments; diff --git a/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs b/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs index d12f3f8ed60..1d87aa8d583 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/MetadataLoadContextInternal.cs @@ -43,8 +43,6 @@ namespace System.Text.Json.Reflection MainAssembly = new AssemblyWrapper(compilation.Assembly, this); } - public Type Resolve() => Resolve(typeof(T)); - public Type? Resolve(Type type) { string assemblyName = type.Assembly.GetName().Name; diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs index 6ad76f42b7b..ba20339565c 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/TypeExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -8,34 +9,75 @@ namespace System.Text.Json.Reflection { internal static class TypeExtensions { - public static string GetUniqueCompilableTypeName(this Type type) => GetCompilableTypeName(type, type.FullName); - - public static string GetCompilableTypeName(this Type type) => GetCompilableTypeName(type, type.Name); - - private static string GetCompilableTypeName(Type type, string name) + public static string GetCompilableName(this Type type) { - if (!type.IsGenericType) + if (type.IsArray) { - return name.Replace('+', '.'); + return GetCompilableName(type.GetElementType()) + "[]"; } - // TODO: Guard upstream against open generics. - Debug.Assert(!type.ContainsGenericParameters); + string compilableName; - int backTickIndex = name.IndexOf('`'); - string baseName = name.Substring(0, backTickIndex).Replace('+', '.'); + if (!type.IsGenericType) + { + compilableName = type.FullName; + } + else + { + StringBuilder sb = new(); - return $"{baseName}<{string.Join(",", type.GetGenericArguments().Select(arg => GetUniqueCompilableTypeName(arg)))}>"; + string fullName = type.FullName; + int backTickIndex = fullName.IndexOf('`'); + + string baseName = fullName.Substring(0, backTickIndex); + + sb.Append(baseName); + + sb.Append("<"); + + Type[] genericArgs = type.GetGenericArguments(); + int genericArgCount = genericArgs.Length; + List genericArgNames = new(genericArgCount); + + for (int i = 0; i < genericArgCount; i++) + { + genericArgNames.Add(GetCompilableName(genericArgs[i])); + } + + sb.Append(string.Join(", ", genericArgNames)); + + sb.Append(">"); + + compilableName = sb.ToString(); + } + + compilableName = compilableName.Replace("+", "."); + return "global::" + compilableName; } - public static string GetFriendlyTypeName(this Type type) + public static string GetTypeInfoPropertyName(this Type type) { - return GetFriendlyTypeName(type.GetCompilableTypeName()); - } + if (type.IsArray) + { + return GetTypeInfoPropertyName(type.GetElementType()) + "Array"; + } + else if (!type.IsGenericType) + { + return type.Name; + } - private static string GetFriendlyTypeName(string compilableName) - { - return compilableName.Replace(".", "").Replace("<", "").Replace(">", "").Replace(",", "").Replace("[]", "Array"); + StringBuilder sb = new(); + + string name = ((TypeWrapper)type).SimpleName; + + sb.Append(name); + + foreach (Type genericArg in type.GetGenericArguments()) + { + sb.Append(GetTypeInfoPropertyName(genericArg)); + } + + return sb.ToString(); } public static bool IsNullableValueType(this Type type, Type nullableOfTType, out Type? underlyingType) diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs index abeed3dfaaf..db2c79a9490 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs @@ -43,9 +43,38 @@ namespace System.Text.Json.Reflection { StringBuilder sb = new(); - AssemblyIdentity identity = _typeSymbol.ContainingAssembly.Identity; + AssemblyIdentity identity; - sb.Append(FullName); + if (_arrayTypeSymbol == null) + { + identity = _typeSymbol.ContainingAssembly.Identity; + sb.Append(FullName); + } + else + { + TypeWrapper currentType = this; + int nestCount = 1; + + while (true) + { + currentType = (TypeWrapper)currentType.GetElementType(); + + if (!currentType.IsArray) + { + break; + } + + nestCount++; + } + + identity = currentType._typeSymbol.ContainingAssembly.Identity; + sb.Append(currentType.FullName); + + for (int i = 0; i < nestCount; i++) + { + sb.Append("[]"); + } + } sb.Append(", "); sb.Append(identity.Name); @@ -97,6 +126,10 @@ namespace System.Text.Json.Reflection sb.Append(underlyingType.AssemblyQualifiedName); sb.Append("]]"); } + else if (IsArray) + { + sb.Append(GetElementType().FullName + "[]"); + } else { sb.Append(Name); @@ -110,7 +143,22 @@ namespace System.Text.Json.Reflection { sb.Insert(0, $"{Namespace}."); } + + if (this.IsGenericType && !ContainsGenericParameters) + { + sb.Append("["); + + foreach (Type genericArg in GetGenericArguments()) + { + sb.Append("["); + sb.Append(genericArg.AssemblyQualifiedName); + sb.Append("]"); + } + + sb.Append("]"); + } } + _fullName = sb.ToString(); } @@ -144,6 +192,8 @@ namespace System.Text.Json.Reflection } } + public string SimpleName => _typeSymbol.Name; + private Type _enumType; public override bool IsEnum @@ -190,11 +240,21 @@ namespace System.Text.Json.Reflection public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) { - var ctors = new List(); + if (_namedTypeSymbol == null) + { + return Array.Empty(); + } + + List ctors = new(); + foreach (IMethodSymbol c in _namedTypeSymbol.Constructors) { - ctors.Add(new ConstructorInfoWrapper(c, _metadataLoadContext)); + if (c.DeclaredAccessibility == Accessibility.Public) + { + ctors.Add(new ConstructorInfoWrapper(c, _metadataLoadContext)); + } } + return ctors.ToArray(); } @@ -268,7 +328,7 @@ namespace System.Text.Json.Reflection public override Type[] GetInterfaces() { var interfaces = new List(); - foreach (INamedTypeSymbol i in _typeSymbol.Interfaces) + foreach (INamedTypeSymbol i in _typeSymbol.AllInterfaces) { interfaces.Add(i.AsType(_metadataLoadContext)); } @@ -484,6 +544,16 @@ namespace System.Text.Json.Reflection public override int GetHashCode() => _typeSymbol.GetHashCode(); #pragma warning restore RS1024 // Compare symbols correctly + public override int GetArrayRank() + { + if (_arrayTypeSymbol == null) + { + throw new ArgumentException("Must be an array type."); + } + + return _arrayTypeSymbol.Rank; + } + public override bool Equals(object o) { if (o is TypeWrapper tw) diff --git a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs index 865134823f9..9241d57c1d5 100644 --- a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs +++ b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs @@ -57,13 +57,43 @@ namespace System.Text.Json.SourceGeneration public string? ConverterInstantiationLogic { get; private set; } + public string FastPathSerializeMethodName + { + get + { + Debug.Assert(GenerateSerializationLogic); + return $"{TypeInfoPropertyName}Serialize"; + } + } + + public string? ImmutableCollectionBuilderName + { + get + { + string builderName; + + if (CollectionType == CollectionType.ImmutableDictionary) + { + builderName = Type.GetImmutableDictionaryConstructingTypeName(sourceGenType: true); + } + else if (CollectionType == CollectionType.ImmutableEnumerable) + { + builderName = Type.GetImmutableEnumerableConstructingTypeName(sourceGenType: true); + } + else + { + return null; + } + + Debug.Assert(builderName != null); + return $"global::{builderName}.{ReflectionExtensions.CreateRangeMethodName}"; + } + } + public void Initialize( JsonSourceGenerationMode generationMode, - string typeRef, - string typeInfoPropertyName, Type type, ClassType classType, - bool isValueType, JsonNumberHandling? numberHandling, List? propertyGenSpecList, CollectionType collectionType, @@ -76,12 +106,12 @@ namespace System.Text.Json.SourceGeneration bool implementsIJsonOnSerializing) { GenerationMode = generationMode; - TypeRef = $"global::{typeRef}"; - TypeInfoPropertyName = typeInfoPropertyName; + TypeRef = type.GetCompilableName(); + TypeInfoPropertyName = type.GetTypeInfoPropertyName(); Type = type; ClassType = classType; - IsValueType = isValueType; - CanBeNull = !isValueType || nullableUnderlyingTypeMetadata != null; + IsValueType = type.IsValueType; + CanBeNull = !IsValueType || nullableUnderlyingTypeMetadata != null; NumberHandling = numberHandling; PropertyGenSpecList = propertyGenSpecList; CollectionType = collectionType; @@ -178,20 +208,31 @@ ReturnFalse: { if (ClassType == ClassType.Object) { + foreach (PropertyGenerationSpec property in PropertyGenSpecList) + { + if (property.TypeGenerationSpec.Type.IsObjectType()) + { + return false; + } + } + return true; } - if (CollectionType == CollectionType.Array || CollectionType == CollectionType.List) + switch (CollectionType) { - return !CollectionValueTypeMetadata!.Type.IsObjectType(); + case CollectionType.NotApplicable: + return false; + case CollectionType.IDictionary: + case CollectionType.Dictionary: + case CollectionType.ImmutableDictionary: + case CollectionType.IDictionaryOfTKeyTValue: + case CollectionType.IReadOnlyDictionary: + return CollectionKeyTypeMetadata!.Type.IsStringType() && !CollectionValueTypeMetadata!.Type.IsObjectType(); + default: + // Non-dictionary collections + return !CollectionValueTypeMetadata!.Type.IsObjectType(); } - - if (CollectionType == CollectionType.Dictionary) - { - return CollectionKeyTypeMetadata!.Type.IsStringType() && !CollectionValueTypeMetadata!.Type.IsObjectType(); - } - - return false; } private bool GenerationModeIsSpecified(JsonSourceGenerationMode mode) => GenerationMode == JsonSourceGenerationMode.Default || (mode & GenerationMode) != 0; diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 97e39311a09..58b6d523aad 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -919,6 +919,7 @@ namespace System.Text.Json.Serialization.Metadata public static System.Text.Json.Serialization.JsonConverter Int16Converter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter Int32Converter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter Int64Converter { get { throw null; } } + public static System.Text.Json.Serialization.JsonConverter JsonElementConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter ObjectConverter { get { throw null; } } [System.CLSCompliantAttribute(false)] public static System.Text.Json.Serialization.JsonConverter SByteConverter { get { throw null; } } @@ -934,10 +935,26 @@ namespace System.Text.Json.Serialization.Metadata public static System.Text.Json.Serialization.JsonConverter UriConverter { get { throw null; } } public static System.Text.Json.Serialization.JsonConverter VersionConverter { get { throw null; } } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateArrayInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Concurrent.ConcurrentQueue { throw null; } + public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateConcurrentStackInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Concurrent.ConcurrentStack { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo keyInfo, System.Text.Json.Serialization.Metadata.JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Dictionary where TKey : notnull { throw null; } + public static JsonTypeInfo CreateICollectionInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.ICollection { throw null; } + public static JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, JsonTypeInfo stringInfo, JsonTypeInfo objectInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IDictionary { throw null; } + public static JsonTypeInfo CreateIDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IDictionary where TKey : notnull { throw null; } + public static JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IEnumerable { throw null; } + public static JsonTypeInfo CreateIEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IEnumerable { throw null; } + public static JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo objectInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.IList { throw null; } + public static JsonTypeInfo CreateIListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IList { throw null; } + public static JsonTypeInfo CreateImmutableDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Func>, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } + public static JsonTypeInfo CreateImmutableEnumerableInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Func, TCollection> createRangeFunc) where TCollection : System.Collections.Generic.IEnumerable { throw null; } + public static JsonTypeInfo CreateIReadOnlyDictionaryInfo(System.Text.Json.JsonSerializerOptions options, System.Func createObjectFunc, JsonTypeInfo keyInfo, JsonTypeInfo valueInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.IReadOnlyDictionary where TKey : notnull { throw null; } + public static JsonTypeInfo CreateISetInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.ISet { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateListInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.List { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateObjectInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Func? propInitFunc, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where T : notnull { throw null; } public static System.Text.Json.Serialization.Metadata.JsonPropertyInfo CreatePropertyInfo(System.Text.Json.JsonSerializerOptions options, bool isProperty, bool isPublic, bool isVirtual, System.Type declaringType, System.Text.Json.Serialization.Metadata.JsonTypeInfo propertyTypeInfo, System.Text.Json.Serialization.JsonConverter? converter, System.Func? getter, System.Action? setter, System.Text.Json.Serialization.JsonIgnoreCondition? ignoreCondition, bool hasJsonInclude, System.Text.Json.Serialization.JsonNumberHandling? numberHandling, string propertyName, string? jsonPropertyName) { throw null; } + public static JsonTypeInfo CreateQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Queue { throw null; } + public static JsonTypeInfo CreateStackInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc) where TCollection : System.Collections.Generic.Stack { throw null; } + public static JsonTypeInfo CreateStackOrQueueInfo(System.Text.Json.JsonSerializerOptions options, System.Func? createObjectFunc, System.Text.Json.Serialization.Metadata.JsonTypeInfo elementInfo, System.Text.Json.Serialization.JsonNumberHandling numberHandling, System.Action? serializeFunc, System.Action addFunc) where TCollection : System.Collections.IEnumerable { throw null; } public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateValueInfo(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.JsonConverter converter) { throw null; } public static System.Text.Json.Serialization.JsonConverter GetEnumConverter(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; } public static System.Text.Json.Serialization.JsonConverter GetNullableConverter(System.Text.Json.Serialization.Metadata.JsonTypeInfo underlyingTypeInfo) where T : struct { throw null; } diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj index f015d29c355..5244502eb33 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj @@ -13,11 +13,13 @@ + + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs index d9ad1615bb4..9de2b8219bf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableConverterFactory.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Reflection; using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization @@ -26,6 +26,6 @@ namespace System.Text.Json.Serialization } private static Type? GetAsyncEnumerableInterface(Type type) - => IEnumerableConverterFactoryHelpers.GetCompatibleGenericInterface(type, typeof(IAsyncEnumerable<>)); + => type.GetCompatibleGenericInterface(typeof(IAsyncEnumerable<>)); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs index d90b9c1d0c8..ca1e09228b1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Text.Json.Reflection; namespace System.Text.Json.Serialization.Converters { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs index 819de1fec20..d015f7f6a83 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Text.Json.Serialization.Metadata; +using System.Text.Json.Reflection; namespace System.Text.Json.Serialization { @@ -14,142 +14,9 @@ namespace System.Text.Json.Serialization // any netstandard2.0 consumers don't need to reference System.Collections.Immutable. // So instead, implement a "weak reference" by using strings to check for Immutable types. - // Immutable collection types. - private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1"; - private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1"; - private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1"; - private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1"; - private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1"; - private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1"; - private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1"; - private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1"; - private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1"; - private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1"; - private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2"; - private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2"; - private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2"; - - // Immutable collection builder types. - private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray"; - private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList"; - private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack"; - private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue"; - private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet"; - private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet"; - private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary"; - private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary"; - - private const string CreateRangeMethodName = "CreateRange"; - // Don't use DynamicDependency attributes to the Immutable Collection types so they can be trimmed in applications that don't use Immutable Collections. internal const string ImmutableConvertersUnreferencedCodeMessage = "System.Collections.Immutable converters use Reflection to find and create Immutable Collection types, which requires unreferenced code."; - internal static Type? GetCompatibleGenericBaseClass(this Type type, Type baseType) - { - Debug.Assert(baseType.IsGenericType); - Debug.Assert(!baseType.IsInterface); - Debug.Assert(baseType == baseType.GetGenericTypeDefinition()); - - Type? baseTypeToCheck = type; - - while (baseTypeToCheck != null && baseTypeToCheck != JsonTypeInfo.ObjectType) - { - if (baseTypeToCheck.IsGenericType) - { - Type genericTypeToCheck = baseTypeToCheck.GetGenericTypeDefinition(); - if (genericTypeToCheck == baseType) - { - return baseTypeToCheck; - } - } - - baseTypeToCheck = baseTypeToCheck.BaseType; - } - - return null; - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The 'interfaceType' must exist and so trimmer kept it. In which case " + - "It also kept it on any type which implements it. The below call to GetInterfaces " + - "may return fewer results when trimmed but it will return the 'interfaceType' " + - "if the type implemented it, even after trimming.")] - internal static Type? GetCompatibleGenericInterface(this Type type, Type interfaceType) - { - Debug.Assert(interfaceType.IsGenericType); - Debug.Assert(interfaceType.IsInterface); - Debug.Assert(interfaceType == interfaceType.GetGenericTypeDefinition()); - - Type interfaceToCheck = type; - - if (interfaceToCheck.IsGenericType) - { - interfaceToCheck = interfaceToCheck.GetGenericTypeDefinition(); - } - - if (interfaceToCheck == interfaceType) - { - return type; - } - - foreach (Type typeToCheck in type.GetInterfaces()) - { - if (typeToCheck.IsGenericType) - { - Type genericInterfaceToCheck = typeToCheck.GetGenericTypeDefinition(); - if (genericInterfaceToCheck == interfaceType) - { - return typeToCheck; - } - } - } - - return null; - } - - public static bool IsImmutableDictionaryType(this Type type) - { - if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable,", StringComparison.Ordinal)) - { - return false; - } - - switch (type.GetGenericTypeDefinition().FullName) - { - case ImmutableDictionaryGenericTypeName: - case ImmutableDictionaryGenericInterfaceTypeName: - case ImmutableSortedDictionaryGenericTypeName: - return true; - default: - return false; - } - } - - public static bool IsImmutableEnumerableType(this Type type) - { - if (!type.IsGenericType|| !type.Assembly.FullName!.StartsWith("System.Collections.Immutable,", StringComparison.Ordinal)) - { - return false; - } - - switch (type.GetGenericTypeDefinition().FullName) - { - case ImmutableArrayGenericTypeName: - case ImmutableListGenericTypeName: - case ImmutableListGenericInterfaceTypeName: - case ImmutableStackGenericTypeName: - case ImmutableStackGenericInterfaceTypeName: - case ImmutableQueueGenericTypeName: - case ImmutableQueueGenericInterfaceTypeName: - case ImmutableSortedSetGenericTypeName: - case ImmutableHashSetGenericTypeName: - case ImmutableSetGenericInterfaceTypeName: - return true; - default: - return false; - } - } - [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] public static MethodInfo GetImmutableEnumerableCreateRangeMethod(this Type type, Type elementType) { @@ -159,7 +26,7 @@ namespace System.Text.Json.Serialization MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); foreach (MethodInfo method in constructingTypeMethods) { - if (method.Name == CreateRangeMethodName && + if (method.Name == ReflectionExtensions.CreateRangeMethodName && method.GetParameters().Length == 1 && method.IsGenericMethod && method.GetGenericArguments().Length == 1) @@ -182,7 +49,7 @@ namespace System.Text.Json.Serialization MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); foreach (MethodInfo method in constructingTypeMethods) { - if (method.Name == CreateRangeMethodName && + if (method.Name == ReflectionExtensions.CreateRangeMethodName && method.GetParameters().Length == 1 && method.IsGenericMethod && method.GetGenericArguments().Length == 2) @@ -201,43 +68,11 @@ namespace System.Text.Json.Serialization { Debug.Assert(type.IsImmutableEnumerableType()); - // Use the generic type definition of the immutable collection to determine - // an appropriate constructing type, i.e. a type that we can invoke the - // `CreateRange` method on, which returns the desired immutable collection. - Type underlyingType = type.GetGenericTypeDefinition(); - string constructingTypeName; + string? constructingTypeName = type.GetImmutableEnumerableConstructingTypeName(); - switch (underlyingType.FullName) - { - case ImmutableArrayGenericTypeName: - constructingTypeName = ImmutableArrayTypeName; - break; - case ImmutableListGenericTypeName: - case ImmutableListGenericInterfaceTypeName: - constructingTypeName = ImmutableListTypeName; - break; - case ImmutableStackGenericTypeName: - case ImmutableStackGenericInterfaceTypeName: - constructingTypeName = ImmutableStackTypeName; - break; - case ImmutableQueueGenericTypeName: - case ImmutableQueueGenericInterfaceTypeName: - constructingTypeName = ImmutableQueueTypeName; - break; - case ImmutableSortedSetGenericTypeName: - constructingTypeName = ImmutableSortedSetTypeName; - break; - case ImmutableHashSetGenericTypeName: - case ImmutableSetGenericInterfaceTypeName: - constructingTypeName = ImmutableHashSetTypeName; - break; - default: - // We verified that the type is an immutable collection, so the - // generic definition is one of the above. - return null; - } - - return underlyingType.Assembly.GetType(constructingTypeName); + return constructingTypeName == null + ? null + : type.Assembly.GetType(constructingTypeName); } [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] @@ -245,28 +80,11 @@ namespace System.Text.Json.Serialization { Debug.Assert(type.IsImmutableDictionaryType()); - // Use the generic type definition of the immutable collection to determine - // an appropriate constructing type, i.e. a type that we can invoke the - // `CreateRange` method on, which returns the desired immutable collection. - Type underlyingType = type.GetGenericTypeDefinition(); - string constructingTypeName; + string? constructingTypeName = type.GetImmutableDictionaryConstructingTypeName(); - switch (underlyingType.FullName) - { - case ImmutableDictionaryGenericTypeName: - case ImmutableDictionaryGenericInterfaceTypeName: - constructingTypeName = ImmutableDictionaryTypeName; - break; - case ImmutableSortedDictionaryGenericTypeName: - constructingTypeName = ImmutableSortedDictionaryTypeName; - break; - default: - // We verified that the type is an immutable collection, so the - // generic definition is one of the above. - return null; - } - - return underlyingType.Assembly.GetType(constructingTypeName); + return constructingTypeName == null + ? null + : type.Assembly.GetType(constructingTypeName); } public static bool IsNonGenericStackOrQueue(this Type type) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs index 6aa72b31588..6363e8aa3c6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs @@ -15,6 +15,9 @@ namespace System.Text.Json.Serialization.Converters [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] public IEnumerableWithAddMethodConverter() { } + // Used by source-gen initialization for reflection-free serialization. + public IEnumerableWithAddMethodConverter(bool dummy) { } + protected override void Add(in object? value, ref ReadStack state) { var addMethodDelegate = ((Action?)state.Current.JsonTypeInfo.AddMethodDelegate); @@ -22,8 +25,6 @@ namespace System.Text.Json.Serialization.Converters addMethodDelegate((TCollection)state.Current.ReturnValue!, value); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", - Justification = "The ctor is marked RequiresUnreferencedCode.")] protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; @@ -36,12 +37,7 @@ namespace System.Text.Json.Serialization.Converters state.Current.ReturnValue = constructorDelegate(); - // Initialize add method used to populate the collection. - if (typeInfo.AddMethodDelegate == null) - { - // We verified this exists when we created the converter in the enumerable converter factory. - typeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); - } + Debug.Assert(typeInfo.AddMethodDelegate != null); } protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) @@ -79,5 +75,15 @@ namespace System.Text.Json.Serialization.Converters return true; } + + internal override bool RequiresDynamicMemberAccessors => true; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs index 7923890979b..316a9a35d9b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; @@ -17,6 +18,9 @@ namespace System.Text.Json.Serialization.Converters { } + // Used by source-gen initialization for reflection-free serialization. + public ImmutableDictionaryOfTKeyTValueConverter(bool dummy) { } + protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) { ((Dictionary)state.Current.ReturnValue!)[key] = value; @@ -24,24 +28,18 @@ namespace System.Text.Json.Serialization.Converters internal override bool CanHaveIdMetadata => false; + internal override bool RequiresDynamicMemberAccessors => true; + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { state.Current.ReturnValue = new Dictionary(); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { - JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - - Func>, TCollection>? creator = (Func>, TCollection>?)typeInfo.CreateObjectWithArgs; - if (creator == null) - { - creator = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); - typeInfo.CreateObjectWithArgs = creator; - } - + Func>, TCollection>? creator = + (Func>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs; + Debug.Assert(creator != null); state.Current.ReturnValue = creator((Dictionary)state.Current.ReturnValue!); } @@ -95,5 +93,13 @@ namespace System.Text.Json.Serialization.Converters enumerator.Dispose(); return true; } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs index d65807bf25c..f2f2810e9ba 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; @@ -16,6 +17,9 @@ namespace System.Text.Json.Serialization.Converters { } + // Used by source-gen initialization for reflection-free serialization. + public ImmutableEnumerableOfTConverter(bool dummy) { } + protected override void Add(in TElement value, ref ReadStack state) { ((List)state.Current.ReturnValue!).Add(value); @@ -23,24 +27,19 @@ namespace System.Text.Json.Serialization.Converters internal override bool CanHaveIdMetadata => false; + internal override bool RequiresDynamicMemberAccessors => true; + protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; Func, TCollection>? creator = (Func, TCollection>?)typeInfo.CreateObjectWithArgs; - if (creator == null) - { - creator = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); - typeInfo.CreateObjectWithArgs = creator; - } - + Debug.Assert(creator != null); state.Current.ReturnValue = creator((List)state.Current.ReturnValue!); } @@ -81,5 +80,13 @@ namespace System.Text.Json.Serialization.Converters enumerator.Dispose(); return true; } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/KeyValuePairConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/KeyValuePairConverter.cs index 016702cf6cb..f769427e0b6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/KeyValuePairConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/KeyValuePairConverter.cs @@ -23,7 +23,7 @@ namespace System.Text.Json.Serialization.Converters private static readonly ConstructorInfo s_constructorInfo = typeof(KeyValuePair).GetConstructor(new[] { typeof(TKey), typeof(TValue) })!; - internal override void Initialize(JsonSerializerOptions options) + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) { JsonNamingPolicy? namingPolicy = options.PropertyNamingPolicy; if (namingPolicy == null) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index f324f672c91..f5902929b18 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -114,7 +114,9 @@ namespace System.Text.Json.Serialization internal ConstructorInfo? ConstructorInfo { get; set; } - internal virtual void Initialize(JsonSerializerOptions options) { } + internal virtual bool RequiresDynamicMemberAccessors { get; } + + internal virtual void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) { } /// /// Creates the instance and assigns it to state.Current.ReturnValue. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs index 014ea974ed2..1d65fc920f4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Text.Json.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs index a302d92878b..e953bdf34da 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs @@ -51,6 +51,7 @@ namespace System.Text.Json.Serialization Options.IgnoreReadOnlyProperties == _defaultOptions.IgnoreReadOnlyProperties && Options.IncludeFields == _defaultOptions.IncludeFields && Options.PropertyNamingPolicy == _defaultOptions.PropertyNamingPolicy && + Options.DictionaryKeyPolicy == _defaultOptions.DictionaryKeyPolicy && Options.WriteIndented == _defaultOptions.WriteIndented; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs index e0f846ddaf2..ef2b4d1a319 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Text.Json.Serialization.Converters; @@ -90,5 +92,471 @@ namespace System.Text.Json.Serialization.Metadata serializeFunc, typeof(TKey), typeof(TValue)); + + +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved + /// + /// Creates metadata for and + /// types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the key type. + /// The generic definition of the value type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the key type. + /// A instance representing the value type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// A method to create an immutable dictionary instance. + /// +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved + public static JsonTypeInfo CreateImmutableDictionaryInfo( + JsonSerializerOptions options, + Func createObjectFunc, + JsonTypeInfo keyInfo, + JsonTypeInfo valueInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc, + Func>, TCollection> createRangeFunc) + where TCollection : IReadOnlyDictionary + where TKey : notnull + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ImmutableDictionaryOfTKeyTValueConverter(dummy: false), + keyInfo, + valueInfo, + numberHandling, + serializeFunc, + typeof(TKey), + typeof(TValue), + createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the key type. + /// The generic definition of the value type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the key type. + /// A instance representing the value type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIDictionaryInfo( + JsonSerializerOptions options, + Func createObjectFunc, + JsonTypeInfo keyInfo, + JsonTypeInfo valueInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IDictionary + where TKey : notnull + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IDictionaryOfTKeyTValueConverter(), + keyInfo, + valueInfo, + numberHandling, + serializeFunc, + typeof(TKey), + typeof(TValue)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the key type. + /// The generic definition of the value type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the key type. + /// A instance representing the value type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIReadOnlyDictionaryInfo( + JsonSerializerOptions options, + Func createObjectFunc, + JsonTypeInfo keyInfo, + JsonTypeInfo valueInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IReadOnlyDictionary + where TKey : notnull + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IReadOnlyDictionaryOfTKeyTValueConverter(), + keyInfo, + valueInfo, + numberHandling, + serializeFunc, + typeof(TKey), + typeof(TValue)); + +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved + /// + /// Creates metadata for non-dictionary immutable collection types. + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// A method to create an immutable dictionary instance. + /// +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved + public static JsonTypeInfo CreateImmutableEnumerableInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc, + Func, TCollection> createRangeFunc) + where TCollection : IEnumerable + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ImmutableEnumerableOfTConverter(dummy: false), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement), + createRangeFunc ?? throw new ArgumentNullException(nameof(createRangeFunc))); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIListInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo objectInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IList + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IListConverter(), + objectInfo, + numberHandling, + serializeFunc, + typeof(object)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIListInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IList + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IListOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateISetInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : ISet + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ISetOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateICollectionInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : ICollection + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ICollectionOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateStackInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : Stack + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new StackOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateQueueInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : Queue + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new QueueOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateConcurrentStackInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : ConcurrentStack + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ConcurrentStackOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateConcurrentQueueInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : ConcurrentQueue + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new ConcurrentQueueOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// The generic definition of the element type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIEnumerableInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IEnumerable + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IEnumerableOfTConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(TElement)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing instances. + /// A instance representing instances. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIDictionaryInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo stringInfo, + JsonTypeInfo objectInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IDictionary + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IDictionaryConverter(), + keyInfo: stringInfo, + valueInfo: objectInfo, + numberHandling, + serializeFunc, + typeof(string), + typeof(object)); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// A method for adding elements to the collection when using the serializer's code-paths. + /// + public static JsonTypeInfo CreateStackOrQueueInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc, + Action addFunc) + where TCollection : IEnumerable + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IEnumerableWithAddMethodConverter(dummy: false), + elementInfo, + numberHandling, + serializeFunc, + typeof(object), + createObjectWithArgs: null, + addFunc: addFunc ?? throw new ArgumentNullException(nameof(addFunc))); + + /// + /// Creates metadata for types assignable to . + /// + /// The generic definition of the type. + /// + /// A to create an instance of the list when deserializing. + /// A instance representing the element type. + /// The option to apply to number collection elements. + /// An optimized serialization implementation assuming pre-determined defaults. + /// + public static JsonTypeInfo CreateIEnumerableInfo( + JsonSerializerOptions options, + Func? createObjectFunc, + JsonTypeInfo elementInfo, + JsonNumberHandling numberHandling, + Action? serializeFunc) + where TCollection : IEnumerable + => new JsonTypeInfoInternal( + options, + createObjectFunc, + () => new IEnumerableConverter(), + elementInfo, + numberHandling, + serializeFunc, + typeof(object)); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs index f51bc851a0a..2a4078f178e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs @@ -82,7 +82,7 @@ namespace System.Text.Json.Serialization.Metadata /// /// Returns a instance that converts values. /// - internal static JsonConverter JsonElementConverter => s_jsonElementConverter ??= new JsonElementConverter(); + public static JsonConverter JsonElementConverter => s_jsonElementConverter ??= new JsonElementConverter(); private static JsonConverter? s_jsonElementConverter; /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 956fefeee47..a88cbd39c64 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -303,12 +303,22 @@ namespace System.Text.Json.Serialization.Metadata case ConverterStrategy.Enumerable: { CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType); + + if (converter.RequiresDynamicMemberAccessors) + { + converter.Initialize(Options, this); + } } break; case ConverterStrategy.Dictionary: { KeyType = converter.KeyType; CreateObject = Options.MemberAccessorStrategy.CreateConstructor(runtimeType); + + if (converter.RequiresDynamicMemberAccessors) + { + converter.Initialize(Options, this); + } } break; case ConverterStrategy.Value: diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs index b016ef98757..6b59b13b973 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoInternalOfT.cs @@ -58,7 +58,10 @@ namespace System.Text.Json.Serialization.Metadata JsonTypeInfo? elementInfo, JsonNumberHandling numberHandling, Action? serializeFunc, - Type elementType) : base(typeof(T), options, ConverterStrategy.Enumerable) + Type elementType, + object? createObjectWithArgs = null, + object? addFunc = null) + : base(typeof(T), options, ConverterStrategy.Enumerable) { JsonConverter converter = new JsonMetadataServicesConverter(converterCreator, ConverterStrategy.Enumerable, keyType: null, elementType); @@ -67,6 +70,8 @@ namespace System.Text.Json.Serialization.Metadata NumberHandling = numberHandling; PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, options); Serialize = serializeFunc; + CreateObjectWithArgs = createObjectWithArgs; + AddMethodDelegate = addFunc; SetCreateObjectFunc(createObjectFunc); } @@ -82,7 +87,9 @@ namespace System.Text.Json.Serialization.Metadata JsonNumberHandling numberHandling, Action? serializeFunc, Type keyType, - Type elementType) : base(typeof(T), options, ConverterStrategy.Dictionary) + Type elementType, + object? createObjectWithArgs = null) + : base(typeof(T), options, ConverterStrategy.Dictionary) { JsonConverter converter = new JsonMetadataServicesConverter(converterCreator, ConverterStrategy.Dictionary, keyType, elementType); @@ -94,6 +101,7 @@ namespace System.Text.Json.Serialization.Metadata NumberHandling = numberHandling; PropertyInfoForTypeInfo = JsonMetadataServices.CreateJsonPropertyInfoForClassInfo(typeof(T), this, converter, options); Serialize = serializeFunc; + CreateObjectWithArgs = createObjectWithArgs; SetCreateObjectFunc(createObjectFunc); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs similarity index 78% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs index 05520dcf9c2..9e57524697c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.AsyncEnumerable.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs @@ -9,24 +9,25 @@ using System.Threading; using System.Threading.Tasks; using Xunit; -namespace System.Text.Json.Tests.Serialization +namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests +#if !BUILDING_SOURCE_GENERATOR_TESTS + public abstract partial class CollectionTests { [Theory] [MemberData(nameof(GetAsyncEnumerableSources))] - public static async Task WriteRootLevelAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) + public async Task WriteRootLevelAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) { JsonSerializerOptions options = new JsonSerializerOptions { DefaultBufferSize = bufferSize }; - string expectedJson = JsonSerializer.Serialize(source); + string expectedJson = await JsonSerializerWrapperForString.SerializeWrapper(source); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); - await JsonSerializer.SerializeAsync(stream, asyncEnumerable, options); + await JsonSerializerWrapperForStream.SerializeWrapper(stream, asyncEnumerable, options); JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); @@ -35,18 +36,18 @@ namespace System.Text.Json.Tests.Serialization [Theory] [MemberData(nameof(GetAsyncEnumerableSources))] - public static async Task WriteNestedAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) + public async Task WriteNestedAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) { JsonSerializerOptions options = new JsonSerializerOptions { DefaultBufferSize = bufferSize }; - string expectedJson = JsonSerializer.Serialize(new { Data = source }); + string expectedJson = await JsonSerializerWrapperForString.SerializeWrapper(new { Data = source }); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); - await JsonSerializer.SerializeAsync(stream, new { Data = asyncEnumerable }, options); + await JsonSerializerWrapperForStream.SerializeWrapper(stream, new { Data = asyncEnumerable }, options); JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); @@ -55,18 +56,18 @@ namespace System.Text.Json.Tests.Serialization [Theory] [MemberData(nameof(GetAsyncEnumerableSources))] - public static async Task WriteNestedAsyncEnumerable_DTO(IEnumerable source, int delayInterval, int bufferSize) + public async Task WriteNestedAsyncEnumerable_DTO(IEnumerable source, int delayInterval, int bufferSize) { JsonSerializerOptions options = new JsonSerializerOptions { DefaultBufferSize = bufferSize }; - string expectedJson = JsonSerializer.Serialize(new { Data = source }); + string expectedJson = await JsonSerializerWrapperForString.SerializeWrapper(new { Data = source }); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); - await JsonSerializer.SerializeAsync(stream, new AsyncEnumerableDto { Data = asyncEnumerable }, options); + await JsonSerializerWrapperForStream.SerializeWrapper(stream, new AsyncEnumerableDto { Data = asyncEnumerable }, options); JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); @@ -74,7 +75,7 @@ namespace System.Text.Json.Tests.Serialization } [Fact, OuterLoop] - public static async Task WriteAsyncEnumerable_LongRunningEnumeration_Cancellation() + public async Task WriteAsyncEnumerable_LongRunningEnumeration_Cancellation() { var longRunningEnumerable = new MockedAsyncEnumerable( source: Enumerable.Range(1, 100), @@ -97,18 +98,18 @@ namespace System.Text.Json.Tests.Serialization [Theory] [MemberData(nameof(GetAsyncEnumerableSources))] - public static async Task WriteSequentialNestedAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) + public async Task WriteSequentialNestedAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) { JsonSerializerOptions options = new JsonSerializerOptions { DefaultBufferSize = bufferSize }; - string expectedJson = JsonSerializer.Serialize(new { Data1 = source, Data2 = source }); + string expectedJson = await JsonSerializerWrapperForString.SerializeWrapper(new { Data1 = source, Data2 = source }); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); - await JsonSerializer.SerializeAsync(stream, new { Data1 = asyncEnumerable, Data2 = asyncEnumerable }, options); + await JsonSerializerWrapperForStream.SerializeWrapper(stream, new { Data1 = asyncEnumerable, Data2 = asyncEnumerable }, options); JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); Assert.Equal(2, asyncEnumerable.TotalCreatedEnumerators); @@ -117,7 +118,7 @@ namespace System.Text.Json.Tests.Serialization [Theory] [MemberData(nameof(GetAsyncEnumerableSources))] - public static async Task WriteAsyncEnumerableOfAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) + public async Task WriteAsyncEnumerableOfAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) { JsonSerializerOptions options = new JsonSerializerOptions { @@ -125,7 +126,7 @@ namespace System.Text.Json.Tests.Serialization }; const int OuterEnumerableCount = 5; - string expectedJson = JsonSerializer.Serialize(Enumerable.Repeat(source, OuterEnumerableCount)); + string expectedJson = await JsonSerializerWrapperForString.SerializeWrapper(Enumerable.Repeat(source, OuterEnumerableCount)); var innerAsyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); var outerAsyncEnumerable = @@ -133,7 +134,7 @@ namespace System.Text.Json.Tests.Serialization Enumerable.Repeat(innerAsyncEnumerable, OuterEnumerableCount), delayInterval); using var stream = new Utf8MemoryStream(); - await JsonSerializer.SerializeAsync(stream, outerAsyncEnumerable, options); + await JsonSerializerWrapperForStream.SerializeWrapper(stream, outerAsyncEnumerable, options); JsonTestHelper.AssertJsonEqual(expectedJson, stream.ToString()); Assert.Equal(1, outerAsyncEnumerable.TotalCreatedEnumerators); @@ -143,26 +144,26 @@ namespace System.Text.Json.Tests.Serialization } [Fact] - public static void WriteRootLevelAsyncEnumerableSync_ThrowsNotSupportedException() + public async Task WriteRootLevelAsyncEnumerableSync_ThrowsNotSupportedException() { IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); - Assert.Throws(() => JsonSerializer.Serialize(asyncEnumerable)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(asyncEnumerable)); } [Fact] - public static void WriteNestedAsyncEnumerableSync_ThrowsNotSupportedException() + public async Task WriteNestedAsyncEnumerableSync_ThrowsNotSupportedException() { IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); - Assert.Throws(() => JsonSerializer.Serialize(new { Data = asyncEnumerable })); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new { Data = asyncEnumerable })); } [Fact] - public static async Task WriteAsyncEnumerable_ElementSerializationThrows_ShouldDisposeEnumerator() + public async Task WriteAsyncEnumerable_ElementSerializationThrows_ShouldDisposeEnumerator() { using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable>(Enumerable.Repeat(ThrowingEnumerable(), 2)); - await Assert.ThrowsAsync(() => JsonSerializer.SerializeAsync(stream, new { Data = asyncEnumerable })); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForStream.SerializeWrapper(stream, new { Data = asyncEnumerable })); Assert.Equal(1, asyncEnumerable.TotalCreatedEnumerators); Assert.Equal(1, asyncEnumerable.TotalDisposedEnumerators); @@ -174,7 +175,7 @@ namespace System.Text.Json.Tests.Serialization } [Fact] - public static async Task ReadRootLevelAsyncEnumerable() + public async Task ReadRootLevelAsyncEnumerable() { var utf8Stream = new Utf8MemoryStream("[0,1,2,3,4]"); @@ -183,7 +184,7 @@ namespace System.Text.Json.Tests.Serialization } [Fact] - public static async Task ReadNestedAsyncEnumerable() + public async Task ReadNestedAsyncEnumerable() { var utf8Stream = new Utf8MemoryStream(@"{ ""Data"" : [0,1,2,3,4] }"); @@ -192,7 +193,7 @@ namespace System.Text.Json.Tests.Serialization } [Fact] - public static async Task ReadAsyncEnumerableOfAsyncEnumerables() + public async Task ReadAsyncEnumerableOfAsyncEnumerables() { var utf8Stream = new Utf8MemoryStream("[[0,1,2,3,4], []]"); @@ -205,7 +206,7 @@ namespace System.Text.Json.Tests.Serialization } [Fact] - public static async Task ReadRootLevelAsyncEnumerableDerivative_ThrowsNotSupportedException() + public async Task ReadRootLevelAsyncEnumerableDerivative_ThrowsNotSupportedException() { var utf8Stream = new Utf8MemoryStream("[0,1,2,3,4]"); await Assert.ThrowsAsync(async () => await JsonSerializer.DeserializeAsync>(utf8Stream)); @@ -224,16 +225,6 @@ namespace System.Text.Json.Tests.Serialization static object[] WrapArgs(IEnumerable source, int delayInterval, int bufferSize) => new object[]{ source, delayInterval, bufferSize }; } - private static async Task> ToListAsync(this IAsyncEnumerable source) - { - var list = new List(); - await foreach (T item in source) - { - list.Add(item); - } - return list; - } - private class MockedAsyncEnumerable : IAsyncEnumerable, IEnumerable { private readonly IEnumerable _source; @@ -314,7 +305,8 @@ namespace System.Text.Json.Tests.Serialization { } - public override string ToString() => Encoding.UTF8.GetString(ToArray()); + public override string ToString () => Encoding.UTF8.GetString(ToArray()); } } +#endif } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Concurrent.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Concurrent.cs new file mode 100644 index 00000000000..2b4f2026f07 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Concurrent.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests + { + [Fact] + public async Task Read_ConcurrentCollection() + { + ConcurrentDictionary cd = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""key"":""value""}"); + Assert.Equal(1, cd.Count); + Assert.Equal("value", cd["key"]); + + ConcurrentQueue qc = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[""1""]"); + Assert.Equal(1, qc.Count); + bool found = qc.TryPeek(out string val); + Assert.True(found); + Assert.Equal("1", val); + + ConcurrentStack qs = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[""1""]"); + Assert.Equal(1, qs.Count); + found = qs.TryPeek(out val); + Assert.True(found); + Assert.Equal("1", val); + } + + [Theory] + [InlineData(typeof(BlockingCollection), @"[""1""]")] // Not supported. Not IList, and we don't detect the add method for this collection. + [InlineData(typeof(ConcurrentBag), @"[""1""]")] // Not supported. Not IList, and we don't detect the add method for this collection. + public async Task Read_ConcurrentCollection_Throws(Type type, string json) + { + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); + Assert.Contains(type.ToString(), ex.Message); + } + + [Theory] + [InlineData(typeof(GenericConcurrentQueuePrivateConstructor), @"[""1""]")] + [InlineData(typeof(GenericConcurrentQueueInternalConstructor), @"[""1""]")] + [InlineData(typeof(GenericConcurrentStackPrivateConstructor), @"[""1""]")] + [InlineData(typeof(GenericConcurrentStackInternalConstructor), @"[""1""]")] + public async Task Read_ConcurrentCollection_NoPublicConstructor_Throws(Type type, string json) + { + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); + Assert.Contains(type.ToString(), ex.Message); + } + + [Fact] + public async Task Write_ConcurrentCollection() + { + Assert.Equal(@"[""1""]", await JsonSerializerWrapperForString.SerializeWrapper(new BlockingCollection { "1" })); + + Assert.Equal(@"[""1""]", await JsonSerializerWrapperForString.SerializeWrapper(new ConcurrentBag { "1" })); + + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new ConcurrentDictionary { ["key"] = "value" })); + + ConcurrentQueue qc = new ConcurrentQueue(); + qc.Enqueue("1"); + Assert.Equal(@"[""1""]", await JsonSerializerWrapperForString.SerializeWrapper(qc)); + + ConcurrentStack qs = new ConcurrentStack(); + qs.Push("1"); + Assert.Equal(@"[""1""]", await JsonSerializerWrapperForString.SerializeWrapper(qs)); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs similarity index 71% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs index d2ea8ea5478..1a90f20a274 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.KeyPolicy.cs @@ -2,14 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static class DictionaryKeyPolicyTests + public abstract partial class CollectionTests { [Fact] - public static void CamelCaseDeserialize() + public async Task CamelCaseDeserialize() { var options = new JsonSerializerOptions { @@ -19,7 +20,7 @@ namespace System.Text.Json.Serialization.Tests const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]"; // Without key policy, deserialize keys as they are. - Dictionary[] obj = JsonSerializer.Deserialize[]>(JsonString); + Dictionary[] obj = await JsonSerializerWrapperForString.DeserializeWrapper[]>(JsonString); Assert.Equal(2, obj.Length); @@ -32,7 +33,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(4, obj[1]["Key2"]); // Ensure we ignore key policy and deserialize keys as they are. - obj = JsonSerializer.Deserialize[]>(JsonString, options); + obj = await JsonSerializerWrapperForString.DeserializeWrapper[]>(JsonString, options); Assert.Equal(2, obj.Length); @@ -46,7 +47,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void IgnoreKeyPolicyForExtensionData() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Need extension data support.")] +#endif + public async Task IgnoreKeyPolicyForExtensionData() { var options = new JsonSerializerOptions { @@ -54,12 +58,12 @@ namespace System.Text.Json.Serialization.Tests }; // Ensure we ignore key policy for extension data and deserialize keys as they are. - ClassWithExtensionData myClass = JsonSerializer.Deserialize(@"{""Key1"":1, ""Key2"":2}", options); + ClassWithExtensionData myClass = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""Key1"":1, ""Key2"":2}", options); Assert.Equal(1, (myClass.ExtensionData["Key1"]).GetInt32()); Assert.Equal(2, (myClass.ExtensionData["Key2"]).GetInt32()); // Ensure we ignore key policy for extension data and serialize keys as they are. - Assert.Equal(@"{""Key1"":1,""Key2"":2}", JsonSerializer.Serialize(myClass, options)); + Assert.Equal(@"{""Key1"":1,""Key2"":2}", await JsonSerializerWrapperForString.SerializeWrapper(myClass, options)); } public class ClassWithExtensionData @@ -69,7 +73,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void CamelCaseSerialize() + public async Task CamelCaseSerialize() { var options = new JsonSerializerOptions() { @@ -86,16 +90,16 @@ namespace System.Text.Json.Serialization.Tests const string JsonCamel = @"[{""key1"":1,""key2"":2},{""key1"":3,""key2"":4}]"; // Without key policy option, serialize keys as they are. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(Json, json); // With key policy option, serialize keys with camel casing. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal(JsonCamel, json); } [Fact] - public static void CamelCaseSerialize_Null_Values() + public async Task CamelCaseSerialize_Null_Values() { var options = new JsonSerializerOptions() { @@ -111,16 +115,16 @@ namespace System.Text.Json.Serialization.Tests const string JsonCamel = @"[{""key1"":null,""key2"":null}]"; // Without key policy option, serialize keys as they are. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(Json, json); // With key policy option, serialize keys with camel casing. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal(JsonCamel, json); } [Fact] - public static void CamelCaseSerialize_Null_Nullable_Values() + public async Task CamelCaseSerialize_Null_Nullable_Values() { var options = new JsonSerializerOptions() { @@ -136,16 +140,16 @@ namespace System.Text.Json.Serialization.Tests const string JsonCamel = @"[{""key1"":null,""key2"":null}]"; // Without key policy option, serialize keys as they are. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(Json, json); // With key policy option, serialize keys with camel casing. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal(JsonCamel, json); } [Fact] - public static void CustomNameDeserialize() + public async Task CustomNameDeserialize() { var options = new JsonSerializerOptions { @@ -154,16 +158,16 @@ namespace System.Text.Json.Serialization.Tests // Without key policy, deserialize keys as they are. - Dictionary obj = JsonSerializer.Deserialize>(@"{""myint"":1}"); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""myint"":1}"); Assert.Equal(1, obj["myint"]); // Ensure we ignore key policy and deserialize keys as they are. - obj = JsonSerializer.Deserialize>(@"{""myint"":1}", options); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""myint"":1}", options); Assert.Equal(1, obj["myint"]); } [Fact] - public static void CustomNameSerialize() + public async Task CustomNameSerialize() { var options = new JsonSerializerOptions { @@ -176,16 +180,16 @@ namespace System.Text.Json.Serialization.Tests const string JsonCustomKey = @"{""MYINT1"":1,""MYINT2"":2}"; // Without key policy option, serialize keys as they are. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(Json, json); // With key policy option, serialize keys honoring the custom key policy. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal(JsonCustomKey, json); } [Fact] - public static void NullNamePolicy() + public async Task NullNamePolicy() { var options = new JsonSerializerOptions { @@ -193,17 +197,17 @@ namespace System.Text.Json.Serialization.Tests }; // A naming policy that returns null is not allowed. - Assert.Throws(() => JsonSerializer.Serialize(new Dictionary { { "onlyKey", 1 } }, options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new Dictionary { { "onlyKey", 1 } }, options)); // We don't use policy on deserialize, so we populate dictionary. - Dictionary obj = JsonSerializer.Deserialize>(@"{""onlyKey"": 1}", options); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""onlyKey"": 1}", options); Assert.Equal(1, obj.Count); Assert.Equal(1, obj["onlyKey"]); } [Fact] - public static void CustomNameSerialize_NullableValue() + public async Task CustomNameSerialize_NullableValue() { var options = new JsonSerializerOptions { @@ -216,16 +220,16 @@ namespace System.Text.Json.Serialization.Tests const string JsonCustomKey = @"{""MYINT1"":1,""MYINT2"":2}"; // Without key policy option, serialize keys as they are. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(Json, json); // With key policy option, serialize keys honoring the custom key policy. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal(JsonCustomKey, json); } [Fact] - public static void NullNamePolicy_NullableValue() + public async Task NullNamePolicy_NullableValue() { var options = new JsonSerializerOptions { @@ -233,17 +237,17 @@ namespace System.Text.Json.Serialization.Tests }; // A naming policy that returns null is not allowed. - Assert.Throws(() => JsonSerializer.Serialize(new Dictionary { { "onlyKey", 1 } }, options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new Dictionary { { "onlyKey", 1 } }, options)); // We don't use policy on deserialize, so we populate dictionary. - Dictionary obj = JsonSerializer.Deserialize>(@"{""onlyKey"": 1}", options); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""onlyKey"": 1}", options); Assert.Equal(1, obj.Count); Assert.Equal(1, obj["onlyKey"]); } [Fact] - public static void KeyConflict_Serialize_WriteAll() + public async Task KeyConflict_Serialize_WriteAll() { var options = new JsonSerializerOptions { @@ -252,14 +256,14 @@ namespace System.Text.Json.Serialization.Tests // The camel case policy resolves two keys to the same output key. Dictionary obj = new Dictionary { { "myInt", 1 }, { "MyInt", 2 } }; - string json = JsonSerializer.Serialize(obj, options); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); // Check that we write all. Assert.Equal(@"{""myInt"":1,""myInt"":2}", json); } [Fact] - public static void CamelCaseSerialize_ApplyDictionaryKeyPolicy() + public async Task CamelCaseSerialize_ApplyDictionaryKeyPolicy() { const string JsonCamel = @"{""keyDict"":{""keyString"":""text"",""keyNumber"":1000,""keyBool"":true},""keyList"":[1,2,3]}"; var options = new JsonSerializerOptions @@ -276,7 +280,7 @@ namespace System.Text.Json.Serialization.Tests }; obj["KeyList"] = new List() { 1, 2, 3 }; - var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + var json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); @@ -285,7 +289,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void SerializationWithJsonExtensionDataAttribute_IgoneDictionaryKeyPolicy() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Need extension data support.")] +#endif + public async Task SerializationWithJsonExtensionDataAttribute_IgoneDictionaryKeyPolicy() { var expectedJson = @"{""KeyInt"":1000,""KeyString"":""text"",""KeyBool"":true,""KeyObject"":{},""KeyList"":[],""KeyDictionary"":{}}"; var obj = new ClassWithExtensionDataProperty(); @@ -298,7 +305,7 @@ namespace System.Text.Json.Serialization.Tests { "KeyList", new List() }, { "KeyDictionary", new Dictionary() } }; - string json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); @@ -312,14 +319,14 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void CamelCaseSerialize_ForTypedDictionary_ApplyDictionaryKeyPolicy() + public async Task CamelCaseSerialize_ForTypedDictionary_ApplyDictionaryKeyPolicy() { const string JsonCamel = @"{""keyDict"":{""Name"":""text"",""Number"":1000,""isValid"":true,""Values"":[1,2,3]}}"; var obj = new Dictionary() { { "KeyDict", CreateCustomObject() } }; - var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + var json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); @@ -327,7 +334,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(JsonCamel, json); } - private class CustomClass + public class CustomClass { public string Name { get; set; } public int Number { get; set; } @@ -341,7 +348,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void CamelCaseSerialize_ForNestedTypedDictionary_ApplyDictionaryKeyPolicy() + public async Task CamelCaseSerialize_ForNestedTypedDictionary_ApplyDictionaryKeyPolicy() { const string JsonCamel = @"{""keyDict"":{""nestedKeyDict"":{""Name"":""text"",""Number"":1000,""isValid"":true,""Values"":[1,2,3]}}}"; var options = new JsonSerializerOptions @@ -352,7 +359,7 @@ namespace System.Text.Json.Serialization.Tests { "KeyDict", new Dictionary() {{ "NestedKeyDict", CreateCustomObject() }} }}; - var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + var json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); @@ -360,20 +367,20 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(JsonCamel, json); } - private class TestClassWithDictionary + public class TestClassWithDictionary { public Dictionary Data { get; set; } } [Fact] - public static void CamelCaseSerialize_ForClassWithDictionaryProperty_ApplyDictionaryKeyPolicy() + public async Task CamelCaseSerialize_ForClassWithDictionaryProperty_ApplyDictionaryKeyPolicy() { const string JsonCamel = @"{""Data"":{""keyObj"":{""Name"":""text"",""Number"":1000,""isValid"":true,""Values"":[1,2,3]}}}"; var obj = new TestClassWithDictionary(); obj.Data = new Dictionary { {"KeyObj", CreateCustomObject() } }; - var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + var json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); @@ -381,7 +388,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void CamelCaseSerialize_ForKeyValuePairWithDictionaryValue_ApplyDictionaryKeyPolicy() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Need KVP support.")] +#endif + public async Task CamelCaseSerialize_ForKeyValuePairWithDictionaryValue_ApplyDictionaryKeyPolicy() { const string JsonCamel = @"{""Key"":""KeyPair"",""Value"":{""keyDict"":{""Name"":""text"",""Number"":1000,""isValid"":true,""Values"":[1,2,3]}}}"; var options = new JsonSerializerOptions @@ -392,7 +402,7 @@ namespace System.Text.Json.Serialization.Tests ("KeyPair", new Dictionary { {"KeyDict", CreateCustomObject() } }); - var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions() + var json = await JsonSerializerWrapperForString.SerializeWrapper(obj, new JsonSerializerOptions() { DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs similarity index 100% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs similarity index 67% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs index b878829bf07..738679e1beb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Dictionary.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs @@ -7,210 +7,211 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.Specialized; using System.Text.Encodings.Web; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class DictionaryTests + public abstract partial class CollectionTests { [Fact] - public static void DictionaryOfString() + public async Task DictionaryOfString() { const string JsonString = @"{""Hello"":""World"",""Hello2"":""World2""}"; const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}"; { - IDictionary obj = JsonSerializer.Deserialize(JsonString); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - Dictionary obj = JsonSerializer.Deserialize>(JsonString); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - SortedDictionary obj = JsonSerializer.Deserialize>(JsonString); + SortedDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - IDictionary obj = JsonSerializer.Deserialize>(JsonString); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - IReadOnlyDictionary obj = JsonSerializer.Deserialize>(JsonString); + IReadOnlyDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - ImmutableDictionary obj = JsonSerializer.Deserialize>(JsonString); + ImmutableDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); } { - IImmutableDictionary obj = JsonSerializer.Deserialize>(JsonString); + IImmutableDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); } { - ImmutableSortedDictionary obj = JsonSerializer.Deserialize>(JsonString); + ImmutableSortedDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json); } { - Hashtable obj = JsonSerializer.Deserialize(JsonString); + Hashtable obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); } { - SortedList obj = JsonSerializer.Deserialize(JsonString); + SortedList obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } } [Fact] - public static void ImplementsDictionary_DictionaryOfString() + public async Task ImplementsDictionary_DictionaryOfString() { const string JsonString = @"{""Hello"":""World"",""Hello2"":""World2""}"; const string ReorderedJsonString = @"{""Hello2"":""World2"",""Hello"":""World""}"; { - WrapperForIDictionary obj = JsonSerializer.Deserialize(JsonString); + WrapperForIDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - StringToStringDictionaryWrapper obj = JsonSerializer.Deserialize(JsonString); + StringToStringDictionaryWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - StringToStringSortedDictionaryWrapper obj = JsonSerializer.Deserialize(JsonString); + StringToStringSortedDictionaryWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - GenericIDictionaryWrapper obj = JsonSerializer.Deserialize>(JsonString); + GenericIDictionaryWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - Assert.Throws(() => JsonSerializer.Deserialize>(JsonString)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString)); GenericIReadOnlyDictionaryWrapper obj = new GenericIReadOnlyDictionaryWrapper(new Dictionary() { { "Hello", "World" }, { "Hello2", "World2" }, }); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - Assert.Throws(() => JsonSerializer.Deserialize(JsonString)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(JsonString)); StringToStringIImmutableDictionaryWrapper obj = new StringToStringIImmutableDictionaryWrapper(new Dictionary() { @@ -218,111 +219,111 @@ namespace System.Text.Json.Serialization.Tests { "Hello2", "World2" }, }); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); } { - HashtableWrapper obj = JsonSerializer.Deserialize(JsonString); + HashtableWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.True(JsonString == json || ReorderedJsonString == json); } { - SortedListWrapper obj = JsonSerializer.Deserialize(JsonString); + SortedListWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal("World", ((JsonElement)obj["Hello"]).GetString()); Assert.Equal("World2", ((JsonElement)obj["Hello2"]).GetString()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - GenericStructIDictionaryWrapper obj = JsonSerializer.Deserialize>(JsonString); + GenericStructIDictionaryWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal("World", obj["Hello"]); Assert.Equal("World2", obj["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - GenericStructIDictionaryWrapper? obj = JsonSerializer.Deserialize?>(JsonString); + GenericStructIDictionaryWrapper? obj = await JsonSerializerWrapperForString.DeserializeWrapper?>(JsonString); Assert.True(obj.HasValue); Assert.Equal("World", obj.Value["Hello"]); Assert.Equal("World2", obj.Value["Hello2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - GenericStructIDictionaryWrapper? obj = JsonSerializer.Deserialize?>("null"); + GenericStructIDictionaryWrapper? obj = await JsonSerializerWrapperForString.DeserializeWrapper?>("null"); Assert.False(obj.HasValue); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal("null", json); } { GenericStructIDictionaryWrapper obj = default; - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal("{}", json); } { StructWrapperForIDictionary obj = default; - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal("{}", json); } } [Fact] - public static void DictionaryOfObject() + public async Task DictionaryOfObject() { { - Dictionary obj = JsonSerializer.Deserialize>(@"{""Key1"":1}"); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key1"":1}"); Assert.Equal(1, obj.Count); JsonElement element = (JsonElement)obj["Key1"]; Assert.Equal(JsonValueKind.Number, element.ValueKind); Assert.Equal(1, element.GetInt32()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(@"{""Key1"":1}", json); } { - IDictionary obj = JsonSerializer.Deserialize>(@"{""Key1"":1}"); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key1"":1}"); Assert.Equal(1, obj.Count); JsonElement element = (JsonElement)obj["Key1"]; Assert.Equal(JsonValueKind.Number, element.ValueKind); Assert.Equal(1, element.GetInt32()); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(@"{""Key1"":1}", json); } } [Fact] - public static void ImplementsIDictionaryOfObject() + public async Task ImplementsIDictionaryOfObject() { var input = new GenericIDictionaryWrapper(new Dictionary { @@ -330,17 +331,17 @@ namespace System.Text.Json.Serialization.Tests { "Age", 32 } }); - string json = JsonSerializer.Serialize(input, typeof(IDictionary)); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input, typeof(IDictionary)); Assert.Equal(@"{""Name"":""David"",""Age"":32}", json); - IDictionary obj = JsonSerializer.Deserialize>(json); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); Assert.Equal("David", ((JsonElement)obj["Name"]).GetString()); Assert.Equal(32, ((JsonElement)obj["Age"]).GetInt32()); } [Fact] - public static void ImplementsIDictionaryOfString() + public async Task ImplementsIDictionaryOfString() { var input = new GenericIDictionaryWrapper(new Dictionary { @@ -348,25 +349,25 @@ namespace System.Text.Json.Serialization.Tests { "Job", "Software Architect" } }); - string json = JsonSerializer.Serialize(input, typeof(IDictionary)); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input, typeof(IDictionary)); Assert.Equal(@"{""Name"":""David"",""Job"":""Software Architect""}", json); - IDictionary obj = JsonSerializer.Deserialize>(json); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); Assert.Equal("David", obj["Name"]); Assert.Equal("Software Architect", obj["Job"]); } [Fact] - public static void PocoWithDictionaryObject() + public async Task PocoWithDictionaryObject() { - PocoDictionary dict = JsonSerializer.Deserialize("{\n\t\"key\" : {\"a\" : \"b\", \"c\" : \"d\"}}"); + PocoDictionary dict = await JsonSerializerWrapperForString.DeserializeWrapper("{\n\t\"key\" : {\"a\" : \"b\", \"c\" : \"d\"}}"); Assert.Equal("b", dict.key["a"]); Assert.Equal("d", dict.key["c"]); } [Fact] - public static void DictionaryOfObject_NonPrimitiveTypes() + public async Task DictionaryOfObject_NonPrimitiveTypes() { // https://github.com/dotnet/runtime/issues/29504 Dictionary dictionary = new Dictionary @@ -374,22 +375,22 @@ namespace System.Text.Json.Serialization.Tests ["key"] = new Poco { Id = 10 }, }; - string json = JsonSerializer.Serialize(dictionary); + string json = await JsonSerializerWrapperForString.SerializeWrapper(dictionary); Assert.Equal(@"{""key"":{""Id"":10}}", json); - dictionary = JsonSerializer.Deserialize>(json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(1, dictionary.Count); JsonElement element = (JsonElement)dictionary["key"]; Assert.Equal(@"{""Id"":10}", element.ToString()); } [Fact] - public static void DictionaryOfList() + public async Task DictionaryOfList() { const string JsonString = @"{""Key1"":[1,2],""Key2"":[3,4]}"; { - IDictionary obj = JsonSerializer.Deserialize(JsonString); + IDictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper(JsonString); Assert.Equal(2, obj.Count); @@ -407,12 +408,12 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expectedNumber++, value.GetInt32()); } - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - IDictionary> obj = JsonSerializer.Deserialize>>(JsonString); + IDictionary> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -422,12 +423,12 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj["Key2"][0]); Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - ImmutableDictionary> obj = JsonSerializer.Deserialize>>(JsonString); + ImmutableDictionary> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -437,13 +438,13 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj["Key2"][0]); Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); const string ReorderedJsonString = @"{""Key2"":[3,4],""Key1"":[1,2]}"; Assert.True(JsonString == json || ReorderedJsonString == json); } { - IImmutableDictionary> obj = JsonSerializer.Deserialize>>(JsonString); + IImmutableDictionary> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -454,17 +455,17 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); const string ReorderedJsonString = @"{""Key2"":[3,4],""Key1"":[1,2]}"; Assert.True(JsonString == json || ReorderedJsonString == json); } } [Fact] - public static void DictionaryOfArray() + public async Task DictionaryOfArray() { const string JsonString = @"{""Key1"":[1,2],""Key2"":[3,4]}"; - Dictionary obj = JsonSerializer.Deserialize>(JsonString); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Length); @@ -474,17 +475,17 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj["Key2"][0]); Assert.Equal(4, obj["Key2"][1]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } [Fact] - public static void ListOfDictionary() + public async Task ListOfDictionary() { const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]"; { - List> obj = JsonSerializer.Deserialize>>(JsonString); + List> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj[0].Count); @@ -494,14 +495,14 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj[1]["Key1"]); Assert.Equal(4, obj[1]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - List> obj = JsonSerializer.Deserialize>>(JsonString); + List> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj[0].Count); @@ -511,21 +512,21 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj[1]["Key1"]); Assert.Equal(4, obj[1]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } } [Fact] - public static void ArrayOfDictionary() + public async Task ArrayOfDictionary() { const string JsonString = @"[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}]"; { - Dictionary[] obj = JsonSerializer.Deserialize[]>(JsonString); + Dictionary[] obj = await JsonSerializerWrapperForString.DeserializeWrapper[]>(JsonString); Assert.Equal(2, obj.Length); Assert.Equal(2, obj[0].Count); @@ -535,15 +536,15 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj[1]["Key1"]); Assert.Equal(4, obj[1]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - ImmutableSortedDictionary[] obj = JsonSerializer.Deserialize[]>(JsonString); + ImmutableSortedDictionary[] obj = await JsonSerializerWrapperForString.DeserializeWrapper[]>(JsonString); Assert.Equal(2, obj.Length); Assert.Equal(2, obj[0].Count); @@ -553,21 +554,21 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj[1]["Key1"]); Assert.Equal(4, obj[1]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } } [Fact] - public static void DictionaryOfDictionary() + public async Task DictionaryOfDictionary() { const string JsonString = @"{""Key1"":{""Key1a"":1,""Key1b"":2},""Key2"":{""Key2a"":3,""Key2b"":4}}"; { - Dictionary> obj = JsonSerializer.Deserialize>>(JsonString); + Dictionary> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -577,15 +578,15 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj["Key2"]["Key2a"]); Assert.Equal(4, obj["Key2"]["Key2b"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } { - ImmutableSortedDictionary> obj = JsonSerializer.Deserialize>>(JsonString); + ImmutableSortedDictionary> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -595,19 +596,19 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, obj["Key2"]["Key2a"]); Assert.Equal(4, obj["Key2"]["Key2b"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } } [Fact] - public static void DictionaryOfDictionaryOfDictionary() + public async Task DictionaryOfDictionaryOfDictionary() { const string JsonString = @"{""Key1"":{""Key1"":{""Key1"":1,""Key2"":2},""Key2"":{""Key1"":3,""Key2"":4}},""Key2"":{""Key1"":{""Key1"":5,""Key2"":6},""Key2"":{""Key1"":7,""Key2"":8}}}"; - Dictionary>> obj = JsonSerializer.Deserialize>>>(JsonString); + Dictionary>> obj = await JsonSerializerWrapperForString.DeserializeWrapper>>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Count); @@ -628,19 +629,19 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(7, obj["Key2"]["Key2"]["Key1"]); Assert.Equal(8, obj["Key2"]["Key2"]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); // Verify that typeof(object) doesn't interfere. - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } [Fact] - public static void DictionaryOfArrayOfDictionary() + public async Task DictionaryOfArrayOfDictionary() { const string JsonString = @"{""Key1"":[{""Key1"":1,""Key2"":2},{""Key1"":3,""Key2"":4}],""Key2"":[{""Key1"":5,""Key2"":6},{""Key1"":7,""Key2"":8}]}"; - Dictionary[]> obj = JsonSerializer.Deserialize[]>>(JsonString); + Dictionary[]> obj = await JsonSerializerWrapperForString.DeserializeWrapper[]>>(JsonString); Assert.Equal(2, obj.Count); Assert.Equal(2, obj["Key1"].Length); @@ -661,11 +662,11 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(7, obj["Key2"][1]["Key1"]); Assert.Equal(8, obj["Key2"][1]["Key2"]); - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); // Verify that typeof(object) doesn't interfere. - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(JsonString, json); } @@ -786,33 +787,39 @@ namespace System.Text.Json.Serialization.Tests return tests; } +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Too many dynamically generated serializable types to manually add to a serialization context.")] +#endif [Fact] - public static void NestedDictionariesRoundtrip() + public async Task NestedDictionariesRoundtrip() { JsonSerializerOptions options = new JsonSerializerOptions(); options.Converters.Add(new MyFactory()); foreach ((Type dictionaryType, string testJson) in NestedDictionaryTypeData()) { - object dict = JsonSerializer.Deserialize(testJson, dictionaryType, options); - Assert.Equal(testJson, JsonSerializer.Serialize(dict, options)); + object dict = await JsonSerializerWrapperForString.DeserializeWrapper(testJson, dictionaryType, options); + Assert.Equal(testJson, await JsonSerializerWrapperForString.SerializeWrapper(dict, options)); } } [Fact] - public static void DictionaryOfClasses() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Needs full SimpleTestClass support.")] +#endif + public async Task DictionaryOfClasses() { { IDictionary obj; { string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; - obj = JsonSerializer.Deserialize(json); + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, obj.Count); if (obj["Key1"] is JsonElement element) { - SimpleTestClass result = JsonSerializer.Deserialize(element.GetRawText()); + SimpleTestClass result = await JsonSerializerWrapperForString.DeserializeWrapper(element.GetRawText()); result.Verify(); } else @@ -825,13 +832,13 @@ namespace System.Text.Json.Serialization.Tests { // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) // so just round-trip the json and compare. - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, obj.Count); if (obj["Key1"] is JsonElement element) { - SimpleTestClass result = JsonSerializer.Deserialize(element.GetRawText()); + SimpleTestClass result = await JsonSerializerWrapperForString.DeserializeWrapper(element.GetRawText()); result.Verify(); } else @@ -842,13 +849,13 @@ namespace System.Text.Json.Serialization.Tests } { - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, obj.Count); if (obj["Key1"] is JsonElement element) { - SimpleTestClass result = JsonSerializer.Deserialize(element.GetRawText()); + SimpleTestClass result = await JsonSerializerWrapperForString.DeserializeWrapper(element.GetRawText()); result.Verify(); } else @@ -864,7 +871,7 @@ namespace System.Text.Json.Serialization.Tests { string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; - obj = JsonSerializer.Deserialize>(json); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); @@ -873,16 +880,16 @@ namespace System.Text.Json.Serialization.Tests { // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) // so just round-trip the json and compare. - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize>(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); } { - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize>(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); @@ -894,7 +901,7 @@ namespace System.Text.Json.Serialization.Tests { string json = @"{""Key1"":" + SimpleTestClass.s_json + @",""Key2"":" + SimpleTestClass.s_json + "}"; - obj = JsonSerializer.Deserialize>(json); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); @@ -903,16 +910,16 @@ namespace System.Text.Json.Serialization.Tests { // We can't compare against the json string above because property ordering is not deterministic (based on reflection order) // so just round-trip the json and compare. - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize>(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); } { - string json = JsonSerializer.Serialize(obj); - obj = JsonSerializer.Deserialize>(json); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(2, obj.Count); obj["Key1"].Verify(); obj["Key2"].Verify(); @@ -921,7 +928,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void UnicodePropertyNames() + public async Task UnicodePropertyNames() { var options = new JsonSerializerOptions(); options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; @@ -929,20 +936,20 @@ namespace System.Text.Json.Serialization.Tests { Dictionary obj; - obj = JsonSerializer.Deserialize>(@"{""A\u0467"":1}"); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""A\u0467"":1}"); Assert.Equal(1, obj["A\u0467"]); // Specifying encoder on options does not impact deserialize. - obj = JsonSerializer.Deserialize>(@"{""A\u0467"":1}", options); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""A\u0467"":1}", options); Assert.Equal(1, obj["A\u0467"]); string json; // Verify the name is escaped after serialize. - json = JsonSerializer.Serialize(obj); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(@"{""A\u0467"":1}", json); // Verify with encoder. - json = JsonSerializer.Serialize(obj, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(obj, options); Assert.Equal("{\"A\u0467\":1}", json); } @@ -952,11 +959,11 @@ namespace System.Text.Json.Serialization.Tests string longPropertyName = new string('\u0467', charsInProperty); - Dictionary obj = JsonSerializer.Deserialize>($"{{\"{longPropertyName}\":1}}"); + Dictionary obj = await JsonSerializerWrapperForString.DeserializeWrapper>($"{{\"{longPropertyName}\":1}}"); Assert.Equal(1, obj[longPropertyName]); // Verify the name is escaped after serialize. - string json = JsonSerializer.Serialize(obj); + string json = await JsonSerializerWrapperForString.SerializeWrapper(obj); // Duplicate the unicode character 'charsInProperty' times. string longPropertyNameEscaped = new StringBuilder().Insert(0, @"\u0467", charsInProperty).ToString(); @@ -965,106 +972,106 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expectedJson, json); // Verify the name is unescaped after deserialize. - obj = JsonSerializer.Deserialize>(json); + obj = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(1, obj[longPropertyName]); } } [Fact] - public static void CustomEscapingOnPropertyNameAndValue() + public async Task CustomEscapingOnPropertyNameAndValue() { var dict = new Dictionary(); dict.Add("A\u046701", "Value\u0467"); // Baseline with no escaping. - var json = JsonSerializer.Serialize(dict); + var json = await JsonSerializerWrapperForString.SerializeWrapper(dict); Assert.Equal("{\"A\\u046701\":\"Value\\u0467\"}", json); // Enable escaping. var options = new JsonSerializerOptions(); options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; - json = JsonSerializer.Serialize(dict, options); + json = await JsonSerializerWrapperForString.SerializeWrapper(dict, options); Assert.Equal("{\"A\u046701\":\"Value\u0467\"}", json); } [Fact] - public static void ObjectToStringFail() + public async Task ObjectToStringFail() { // Baseline string json = @"{""MyDictionary"":{""Key"":""Value""}}"; JsonSerializer.Deserialize>(json); - Assert.Throws(() => JsonSerializer.Deserialize>(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json)); } [Fact] - public static void ObjectToJsonElement() + public async Task ObjectToJsonElement() { string json = @"{""MyDictionary"":{""Key"":""Value""}}"; - Dictionary result = JsonSerializer.Deserialize>(json); + Dictionary result = await JsonSerializerWrapperForString.DeserializeWrapper>(json); JsonElement element = result["MyDictionary"]; Assert.Equal(JsonValueKind.Object, element.ValueKind); Assert.Equal("Value", element.GetProperty("Key").GetString()); } [Fact] - public static void Hashtable() + public async Task Hashtable() { const string Json = @"{""Key"":""Value""}"; IDictionary ht = new Hashtable(); ht.Add("Key", "Value"); - string json = JsonSerializer.Serialize(ht); + string json = await JsonSerializerWrapperForString.SerializeWrapper(ht); Assert.Equal(Json, json); - ht = JsonSerializer.Deserialize(json); + ht = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.IsType(ht["Key"]); Assert.Equal("Value", ((JsonElement)ht["Key"]).GetString()); // Verify round-tripped JSON. - json = JsonSerializer.Serialize(ht); + json = await JsonSerializerWrapperForString.SerializeWrapper(ht); Assert.Equal(Json, json); } [Fact] - public static void DeserializeDictionaryWithDuplicateKeys() + public async Task DeserializeDictionaryWithDuplicateKeys() { // Non-generic IDictionary case. - IDictionary iDictionary = JsonSerializer.Deserialize(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); + IDictionary iDictionary = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); Assert.Equal("NewValue", iDictionary["Hello"].ToString()); // Generic IDictionary case. - IDictionary iNonGenericDictionary = JsonSerializer.Deserialize>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); + IDictionary iNonGenericDictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); Assert.Equal("NewValue", iNonGenericDictionary["Hello"]); - IDictionary iNonGenericObjectDictionary = JsonSerializer.Deserialize>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); + IDictionary iNonGenericObjectDictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); Assert.Equal("NewValue", iNonGenericObjectDictionary["Hello"].ToString()); // Strongly-typed IDictionary<,> case. - Dictionary dictionary = JsonSerializer.Deserialize>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); + Dictionary dictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Hello"":""World"", ""Hello"":""NewValue""}"); Assert.Equal("NewValue", dictionary["Hello"]); - dictionary = JsonSerializer.Deserialize>(@"{""Hello"":""World"", ""myKey"" : ""myValue"", ""Hello"":""NewValue""}"); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Hello"":""World"", ""myKey"" : ""myValue"", ""Hello"":""NewValue""}"); Assert.Equal("NewValue", dictionary["Hello"]); // Weakly-typed IDictionary case. - Dictionary dictionaryObject = JsonSerializer.Deserialize>(@"{""Hello"":""World"", ""Hello"": null}"); + Dictionary dictionaryObject = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Hello"":""World"", ""Hello"": null}"); Assert.Null(dictionaryObject["Hello"]); } [Fact] - public static void DeserializeDictionaryWithDuplicateProperties() + public async Task DeserializeDictionaryWithDuplicateProperties() { - PocoDuplicate foo = JsonSerializer.Deserialize(@"{""BoolProperty"": false, ""BoolProperty"": true}"); + PocoDuplicate foo = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""BoolProperty"": false, ""BoolProperty"": true}"); Assert.True(foo.BoolProperty); - foo = JsonSerializer.Deserialize(@"{""BoolProperty"": false, ""IntProperty"" : 1, ""BoolProperty"": true , ""IntProperty"" : 2}"); + foo = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""BoolProperty"": false, ""IntProperty"" : 1, ""BoolProperty"": true , ""IntProperty"" : 2}"); Assert.True(foo.BoolProperty); Assert.Equal(2, foo.IntProperty); - foo = JsonSerializer.Deserialize(@"{""DictProperty"" : {""a"" : ""b"", ""c"" : ""d""},""DictProperty"" : {""b"" : ""b"", ""c"" : ""e""}}"); + foo = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""DictProperty"" : {""a"" : ""b"", ""c"" : ""d""},""DictProperty"" : {""b"" : ""b"", ""c"" : ""e""}}"); Assert.Equal(2, foo.DictProperty.Count); // We don't concat. Assert.Equal("e", foo.DictProperty["c"]); } @@ -1088,20 +1095,20 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ClassWithNoSetterAndDictionary() + public async Task ClassWithNoSetterAndDictionary() { // We don't attempt to deserialize into dictionaries without a setter. string json = @"{""MyDictionary"":{""Key1"":""Value1"", ""Key2"":""Value2""}}"; - ClassWithPopulatedDictionaryAndNoSetter obj = JsonSerializer.Deserialize(json); + ClassWithPopulatedDictionaryAndNoSetter obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(1, obj.MyDictionary.Count); } [Fact] - public static void ClassWithNoSetterAndImmutableDictionary() + public async Task ClassWithNoSetterAndImmutableDictionary() { // We don't attempt to deserialize into dictionaries without a setter. string json = @"{""MyImmutableDictionary"":{""Key1"":""Value1"", ""Key2"":""Value2""}}"; - ClassWithPopulatedDictionaryAndNoSetter obj = JsonSerializer.Deserialize(json); + ClassWithPopulatedDictionaryAndNoSetter obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(1, obj.MyImmutableDictionary.Count); } @@ -1184,10 +1191,10 @@ namespace System.Text.Json.Serialization.Tests [InlineData(@"{""Parsed1"":{""Key"":1},""Skipped2"":{""Key"":[1,2,3]}, ""Parsed3"":{""Key"":2}}")] [InlineData(@"{""Parsed1"":{""Key"":1},""Skipped2"":{""Key"":{}}, ""Parsed3"":{""Key"":2}}")] [InlineData(@"{""Parsed1"":{""Key"":1},""Skipped2"":{""Key"":null}, ""Parsed3"":{""Key"":2}}")] - public static void IgnoreDictionaryProperty(string json) + public async Task IgnoreDictionaryProperty(string json) { // Verify deserialization - ClassWithIgnoredDictionary2 obj = JsonSerializer.Deserialize(json); + ClassWithIgnoredDictionary2 obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(1, obj.Parsed1.Count); Assert.Equal(1, obj.Parsed1["Key"]); Assert.Null(obj.Skipped2); @@ -1195,8 +1202,8 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(2, obj.Parsed3["Key"]); // Round-trip and verify. - string jsonRoundTripped = JsonSerializer.Serialize(obj); - ClassWithIgnoredDictionary2 objRoundTripped = JsonSerializer.Deserialize(jsonRoundTripped); + string jsonRoundTripped = await JsonSerializerWrapperForString.SerializeWrapper(obj); + ClassWithIgnoredDictionary2 objRoundTripped = await JsonSerializerWrapperForString.DeserializeWrapper(jsonRoundTripped); Assert.Equal(1, objRoundTripped.Parsed1.Count); Assert.Equal(1, objRoundTripped.Parsed1["Key"]); Assert.Null(objRoundTripped.Skipped2); @@ -1205,25 +1212,25 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void IgnoreDictionaryPropertyWithDifferentOrdering() + public async Task IgnoreDictionaryPropertyWithDifferentOrdering() { // Verify all combinations of 3 properties with at least one ignore. - VerifyIgnore(false, false, true); - VerifyIgnore(false, true, false); - VerifyIgnore(false, true, true); - VerifyIgnore(true, false, false); - VerifyIgnore(true, false, true); - VerifyIgnore(true, true, false); - VerifyIgnore(true, true, true); + await VerifyIgnore(false, false, true); + await VerifyIgnore(false, true, false); + await VerifyIgnore(false, true, true); + await VerifyIgnore(true, false, false); + await VerifyIgnore(true, false, true); + await VerifyIgnore(true, true, false); + await VerifyIgnore(true, true, true); // Verify single case for IDictionary, [Ignore] and ImmutableDictionary. // Also specify addMissing to add additional skipped JSON that does not have a corresponding property. - VerifyIgnore(false, true, false, addMissing: true); - VerifyIgnore(false, true, false, addMissing: true); - VerifyIgnore(false, true, false, addMissing: true); + await VerifyIgnore(false, true, false, addMissing: true); + await VerifyIgnore(false, true, false, addMissing: true); + await VerifyIgnore(false, true, false, addMissing: true); } - private static void VerifyIgnore(bool skip1, bool skip2, bool skip3, bool addMissing = false) + private async Task VerifyIgnore(bool skip1, bool skip2, bool skip3, bool addMissing = false) { static IDictionary GetProperty(T objectToVerify, string propertyName) { @@ -1307,13 +1314,13 @@ namespace System.Text.Json.Serialization.Tests // Deserialize and verify. string jsonString = json.ToString(); - T obj = JsonSerializer.Deserialize(jsonString); + T obj = await JsonSerializerWrapperForString.DeserializeWrapper(jsonString); Verify(obj); // Round-trip and verify. // Any skipped properties due to lack of a setter will now be "null" when serialized instead of "{}". - string jsonStringRoundTripped = JsonSerializer.Serialize(obj); - T objRoundTripped = JsonSerializer.Deserialize(jsonStringRoundTripped); + string jsonStringRoundTripped = await JsonSerializerWrapperForString.SerializeWrapper(obj); + T objRoundTripped = await JsonSerializerWrapperForString.DeserializeWrapper(jsonStringRoundTripped); Verify(objRoundTripped); } @@ -1329,45 +1336,51 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ClassWithPopulatedDictionary() + public async Task ClassWithPopulatedDictionary() { // We replace the contents. string json = @"{""MyDictionary"":{""Key1"":""Value1"", ""Key2"":""Value2""}}"; - ClassWithPopulatedDictionaryAndSetter obj = JsonSerializer.Deserialize(json); + ClassWithPopulatedDictionaryAndSetter obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, obj.MyDictionary.Count); } [Fact] - public static void ClassWithPopulatedImmutableDictionary() + public async Task ClassWithPopulatedImmutableDictionary() { // We replace the contents. string json = @"{""MyImmutableDictionary"":{""Key1"":""Value1"", ""Key2"":""Value2""}}"; - ClassWithPopulatedDictionaryAndSetter obj = JsonSerializer.Deserialize(json); + ClassWithPopulatedDictionaryAndSetter obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, obj.MyImmutableDictionary.Count); } [Fact] - public static void DictionaryNotSupported() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Multi-dim arrays not supported.")] +#endif + public async Task DictionaryNotSupported() { string json = @"{""MyDictionary"":{""Key"":""Value""}}"; - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); // The exception contains the type. Assert.Contains(typeof(Dictionary).ToString(), ex.Message); } [Fact] - public static void DictionaryNotSupportedButIgnored() +#if BUILDING_SOURCE_GENERATOR_TESTS + [ActiveIssue("Multi-dim arrays not supported.")] +#endif + public async Task DictionaryNotSupportedButIgnored() { string json = @"{""MyDictionary"":{""Key"":1}}"; - ClassWithNotSupportedDictionaryButIgnored obj = JsonSerializer.Deserialize(json); + ClassWithNotSupportedDictionaryButIgnored obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Null(obj.MyDictionary); } // https://github.com/dotnet/runtime/issues/29933 [Fact] - public static void Serialize_IDictionaryOfPoco() + public async Task Serialize_IDictionaryOfPoco() { // Arrange var value = new AllSingleUpperPropertiesParent() @@ -1386,7 +1399,7 @@ namespace System.Text.Json.Serialization.Tests } }; - var actual = JsonSerializer.Serialize(value, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + var actual = await JsonSerializerWrapperForString.SerializeWrapper(value, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); // Assert Assert.NotNull(actual); @@ -1395,12 +1408,12 @@ namespace System.Text.Json.Serialization.Tests // https://github.com/dotnet/runtime/issues/29933 [Fact] - public static void Deserialize_IDictionaryOfPoco() + public async Task Deserialize_IDictionaryOfPoco() { // Arrange string json = "{\"child\":{\"1\":{\"a\":\"1\",\"b\":\"\",\"c\":[],\"d\":[],\"e\":null,\"f\":[],\"g\":null,\"h\":null,\"i\":null,\"j\":null,\"k\":[]}}}"; - var actual = JsonSerializer.Deserialize(json, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + var actual = await JsonSerializerWrapperForString.DeserializeWrapper(json, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); // Assert Assert.NotNull(actual); @@ -1412,7 +1425,7 @@ namespace System.Text.Json.Serialization.Tests // https://github.com/dotnet/runtime/issues/29893 [Fact] - public static void ShouldHandleNullInDictionaries_Serialize() + public async Task ShouldHandleNullInDictionaries_Serialize() { var value = new ClassWithDictionaryOfString_ChildWithDictionaryOfString() { @@ -1420,16 +1433,16 @@ namespace System.Text.Json.Serialization.Tests Child = new ClassWithDictionaryOfString() }; - var actual = JsonSerializer.Serialize(value); + var actual = await JsonSerializerWrapperForString.SerializeWrapper(value); Assert.Equal("{\"Test\":\"value1\",\"Dict\":null,\"Child\":{\"Test\":null,\"Dict\":null}}", actual); } // https://github.com/dotnet/runtime/issues/29893 [Fact] - public static void ShouldHandleNullInDictionaries_Deserialize() + public async Task ShouldHandleNullInDictionaries_Deserialize() { var json = "{\"Test\":\"value1\",\"Dict\":null,\"Child\":{\"Test\":null,\"Dict\":null}}"; - ClassWithDictionaryOfString_ChildWithDictionaryOfString actual = JsonSerializer.Deserialize(json); + ClassWithDictionaryOfString_ChildWithDictionaryOfString actual = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", actual.Test); Assert.Null(actual.Dict); @@ -1440,7 +1453,7 @@ namespace System.Text.Json.Serialization.Tests // https://github.com/dotnet/runtime/issues/29893 [Fact] - public static void ShouldHandleNullInDictionaries_Serialize_IgnoreNullValues() + public async Task ShouldHandleNullInDictionaries_Serialize_IgnoreNullValues() { var value = new ClassWithDictionaryOfString_ChildWithDictionaryOfString() { @@ -1448,16 +1461,16 @@ namespace System.Text.Json.Serialization.Tests Child = new ClassWithDictionaryOfString() }; - var actual = JsonSerializer.Serialize(value, new JsonSerializerOptions { IgnoreNullValues = true }); + var actual = await JsonSerializerWrapperForString.SerializeWrapper(value, new JsonSerializerOptions { IgnoreNullValues = true }); Assert.Equal("{\"Test\":\"value1\",\"Child\":{}}", actual); } // https://github.com/dotnet/runtime/issues/29893 [Fact] - public static void ShouldHandleNullInDictionaries_Deserialize_IgnoreNullValues() + public async Task ShouldHandleNullInDictionaries_Deserialize_IgnoreNullValues() { var json = "{\"Test\":\"value1\",\"Child\":{}}"; - ClassWithDictionaryOfString_ChildWithDictionaryOfString actual = JsonSerializer.Deserialize(json); + ClassWithDictionaryOfString_ChildWithDictionaryOfString actual = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", actual.Test); Assert.Null(actual.Dict); @@ -1468,14 +1481,14 @@ namespace System.Text.Json.Serialization.Tests // https://github.com/dotnet/runtime/issues/29888 [Fact] - public static void DictionaryWithNullShouldPreserveOrder_Serialize() + public async Task DictionaryWithNullShouldPreserveOrder_Serialize() { var dictionaryFirst = new ClassWithDictionaryAndProperty_DictionaryFirst() { Test = "value1" }; - var actual = JsonSerializer.Serialize(dictionaryFirst); + var actual = await JsonSerializerWrapperForString.SerializeWrapper(dictionaryFirst); Assert.Equal("{\"Dict\":null,\"Test\":\"value1\"}", actual); var dictionaryLast = new ClassWithDictionaryAndProperty_DictionaryLast() @@ -1483,22 +1496,22 @@ namespace System.Text.Json.Serialization.Tests Test = "value1" }; - actual = JsonSerializer.Serialize(dictionaryLast); + actual = await JsonSerializerWrapperForString.SerializeWrapper(dictionaryLast); Assert.Equal("{\"Test\":\"value1\",\"Dict\":null}", actual); } // https://github.com/dotnet/runtime/issues/29888 [Fact] - public static void DictionaryWithNullShouldPreserveOrder_Deserialize() + public async Task DictionaryWithNullShouldPreserveOrder_Deserialize() { var json = "{\"Dict\":null,\"Test\":\"value1\"}"; - ClassWithDictionaryAndProperty_DictionaryFirst dictionaryFirst = JsonSerializer.Deserialize(json); + ClassWithDictionaryAndProperty_DictionaryFirst dictionaryFirst = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", dictionaryFirst.Test); Assert.Null(dictionaryFirst.Dict); json = "{\"Test\":\"value1\",\"Dict\":null}"; - ClassWithDictionaryAndProperty_DictionaryLast dictionaryLast = JsonSerializer.Deserialize(json); + ClassWithDictionaryAndProperty_DictionaryLast dictionaryLast = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", dictionaryLast.Test); Assert.Null(dictionaryLast.Dict); @@ -1506,14 +1519,14 @@ namespace System.Text.Json.Serialization.Tests // https://github.com/dotnet/runtime/issues/29888 [Fact] - public static void DictionaryWithNullShouldPreserveOrder_Serialize_IgnoreNullValues() + public async Task DictionaryWithNullShouldPreserveOrder_Serialize_IgnoreNullValues() { var dictionaryFirst = new ClassWithDictionaryAndProperty_DictionaryFirst() { Test = "value1" }; - var actual = JsonSerializer.Serialize(dictionaryFirst, new JsonSerializerOptions { IgnoreNullValues = true }); + var actual = await JsonSerializerWrapperForString.SerializeWrapper(dictionaryFirst, new JsonSerializerOptions { IgnoreNullValues = true }); Assert.Equal("{\"Test\":\"value1\"}", actual); var dictionaryLast = new ClassWithDictionaryAndProperty_DictionaryLast() @@ -1521,29 +1534,29 @@ namespace System.Text.Json.Serialization.Tests Test = "value1" }; - actual = JsonSerializer.Serialize(dictionaryLast, new JsonSerializerOptions { IgnoreNullValues = true }); + actual = await JsonSerializerWrapperForString.SerializeWrapper(dictionaryLast, new JsonSerializerOptions { IgnoreNullValues = true }); Assert.Equal("{\"Test\":\"value1\"}", actual); } // https://github.com/dotnet/runtime/issues/29888 [Fact] - public static void DictionaryWithNullShouldPreserveOrder_Deserialize_IgnoreNullValues() + public async Task DictionaryWithNullShouldPreserveOrder_Deserialize_IgnoreNullValues() { var json = "{\"Test\":\"value1\"}"; - ClassWithDictionaryAndProperty_DictionaryFirst dictionaryFirst = JsonSerializer.Deserialize(json); + ClassWithDictionaryAndProperty_DictionaryFirst dictionaryFirst = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", dictionaryFirst.Test); Assert.Null(dictionaryFirst.Dict); json = "{\"Test\":\"value1\"}"; - ClassWithDictionaryAndProperty_DictionaryLast dictionaryLast = JsonSerializer.Deserialize(json); + ClassWithDictionaryAndProperty_DictionaryLast dictionaryLast = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("value1", dictionaryLast.Test); Assert.Null(dictionaryLast.Dict); } [Fact] - public static void NullDictionaryValuesShouldDeserializeAsNull() + public async Task NullDictionaryValuesShouldDeserializeAsNull() { const string json = @"{" + @@ -1564,7 +1577,7 @@ namespace System.Text.Json.Serialization.Tests @"}" + @"}"; - SimpleClassWithDictionaries obj = JsonSerializer.Deserialize(json); + SimpleClassWithDictionaries obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Null(obj.StringVals["key"]); Assert.Null(obj.ObjectVals["key"]); Assert.Null(obj.StringDictVals["key"]); @@ -1726,32 +1739,32 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void DictionaryOfTOnlyWithStringTValueAsInt() + public async Task DictionaryOfTOnlyWithStringTValueAsInt() { const string Json = @"{""One"":1,""Two"":2}"; DictionaryThatOnlyImplementsIDictionaryOfStringTValue dictionary; - dictionary = JsonSerializer.Deserialize>(Json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(Json); Assert.Equal(1, dictionary["One"]); Assert.Equal(2, dictionary["Two"]); - string json = JsonSerializer.Serialize(dictionary); + string json = await JsonSerializerWrapperForString.SerializeWrapper(dictionary); Assert.Equal(Json, json); } [Fact] - public static void DictionaryOfTOnlyWithStringTValueAsPoco() + public async Task DictionaryOfTOnlyWithStringTValueAsPoco() { const string Json = @"{""One"":{""Id"":1},""Two"":{""Id"":2}}"; DictionaryThatOnlyImplementsIDictionaryOfStringTValue dictionary; - dictionary = JsonSerializer.Deserialize>(Json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper>(Json); Assert.Equal(1, dictionary["One"].Id); Assert.Equal(2, dictionary["Two"].Id); - string json = JsonSerializer.Serialize(dictionary); + string json = await JsonSerializerWrapperForString.SerializeWrapper(dictionary); Assert.Equal(Json, json); } @@ -1760,17 +1773,17 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void DictionaryOfTOnlyWithStringPoco() + public async Task DictionaryOfTOnlyWithStringPoco() { const string Json = @"{""One"":{""Id"":1},""Two"":{""Id"":2}}"; DictionaryThatOnlyImplementsIDictionaryOfStringPoco dictionary; - dictionary = JsonSerializer.Deserialize(Json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper(Json); Assert.Equal(1, dictionary["One"].Id); Assert.Equal(2, dictionary["Two"].Id); - string json = JsonSerializer.Serialize(dictionary); + string json = await JsonSerializerWrapperForString.SerializeWrapper(dictionary); Assert.Equal(Json, json); } @@ -1854,56 +1867,56 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void VerifyDictionaryThatHasIncomatibleEnumeratorWithInt() + public async Task VerifyDictionaryThatHasIncomatibleEnumeratorWithInt() { const string Json = @"{""One"":1,""Two"":2}"; DictionaryThatHasIncompatibleEnumerator dictionary; - dictionary = JsonSerializer.Deserialize(Json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper(Json); Assert.Equal(1, ((JsonElement)dictionary["One"]).GetInt32()); Assert.Equal(2, ((JsonElement)dictionary["Two"]).GetInt32()); - Assert.Throws(() => JsonSerializer.Serialize(dictionary)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(dictionary)); } [Fact] - public static void VerifyDictionaryThatHasIncomatibleEnumeratorWithPoco() + public async Task VerifyDictionaryThatHasIncomatibleEnumeratorWithPoco() { const string Json = @"{""One"":{""Id"":1},""Two"":{""Id"":2}}"; DictionaryThatHasIncompatibleEnumerator dictionary; - dictionary = JsonSerializer.Deserialize(Json); + dictionary = await JsonSerializerWrapperForString.DeserializeWrapper(Json); Assert.Equal(1, ((JsonElement)dictionary["One"]).GetProperty("Id").GetInt32()); Assert.Equal(2, ((JsonElement)dictionary["Two"]).GetProperty("Id").GetInt32()); - Assert.Throws(() => JsonSerializer.Serialize(dictionary)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(dictionary)); } - private class ClassWithoutParameterlessCtor + public class ClassWithoutParameterlessCtor { public ClassWithoutParameterlessCtor(int num) { } public string Name { get; set; } } - private class ClassWithInternalParameterlessConstructor + public class ClassWithInternalParameterlessConstructor { internal ClassWithInternalParameterlessConstructor() { } public string Name { get; set; } } - private class ClassWithPrivateParameterlessConstructor + public class ClassWithPrivateParameterlessConstructor { private ClassWithPrivateParameterlessConstructor() { } public string Name { get; set; } } [Fact] - public static void DictionaryWith_ObjectWithNoParameterlessCtor_AsValue_Throws() + public async Task DictionaryWith_ObjectWithNoParameterlessCtor_AsValue_Throws() { - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""key"":{}}")); - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""key"":{}}")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""key"":{}}")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""key"":{}}")); } [Fact] - public static void DictionaryWith_ObjectWithNoParameterlessCtor_Serialize_Works() + public async Task DictionaryWith_ObjectWithNoParameterlessCtor_Serialize_Works() { var noParameterless = new Dictionary() { @@ -1913,7 +1926,7 @@ namespace System.Text.Json.Serialization.Tests } }; - string json = JsonSerializer.Serialize(noParameterless); + string json = await JsonSerializerWrapperForString.SerializeWrapper(noParameterless); Assert.Equal("{\"key\":{\"Name\":\"parameterless\"}}", json); var onlyInternal = new Dictionary() @@ -1924,7 +1937,7 @@ namespace System.Text.Json.Serialization.Tests } }; - json = JsonSerializer.Serialize(onlyInternal); + json = await JsonSerializerWrapperForString.SerializeWrapper(onlyInternal); Assert.Equal("{\"key\":{\"Name\":\"internal\"}}", json); var onlyPrivate = new Dictionary() @@ -1932,7 +1945,7 @@ namespace System.Text.Json.Serialization.Tests ["key"] = null }; - json = JsonSerializer.Serialize(onlyPrivate); + json = await JsonSerializerWrapperForString.SerializeWrapper(onlyPrivate); Assert.Equal("{\"key\":null}", json); } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Read.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Read.cs similarity index 55% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Read.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Read.cs index c5b865860ef..9b7900c692f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Read.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Read.cs @@ -3,23 +3,24 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void ReadListOfList() + public async Task ReadListOfList() { - List> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + List> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); Assert.Equal(1, result[0][0]); Assert.Equal(2, result[0][1]); Assert.Equal(3, result[1][0]); Assert.Equal(4, result[1][1]); - GenericListWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); Assert.Equal("1", result2[0][0]); Assert.Equal("2", result2[0][1]); @@ -28,16 +29,16 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadListOfArray() + public async Task ReadListOfArray() { - List result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + List result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); Assert.Equal(1, result[0][0]); Assert.Equal(2, result[0][1]); Assert.Equal(3, result[1][0]); Assert.Equal(4, result[1][1]); - GenericListWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); Assert.Equal("1", result2[0][0]); Assert.Equal("2", result2[0][1]); @@ -46,16 +47,16 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfList() + public async Task ReadArrayOfList() { - List[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + List[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); Assert.Equal(1, result[0][0]); Assert.Equal(2, result[0][1]); Assert.Equal(3, result[1][0]); Assert.Equal(4, result[1][1]); - StringListWrapper[] result2 = JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]"); + StringListWrapper[] result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]"); Assert.Equal("1", result2[0][0]); Assert.Equal("2", result2[0][1]); Assert.Equal("3", result2[1][0]); @@ -63,27 +64,27 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleList() + public async Task ReadSimpleList() { - List i = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + List i = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); Assert.Equal(1, i[0]); Assert.Equal(2, i[1]); - i = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + i = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, i.Count); - StringListWrapper i2 = JsonSerializer.Deserialize(@"[""1"",""2""]"); + StringListWrapper i2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]"); Assert.Equal("1", i2[0]); Assert.Equal("2", i2[1]); - i2 = JsonSerializer.Deserialize(@"[]"); + i2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); Assert.Equal(0, i2.Count); } [Fact] - public static void ReadGenericIEnumerableOfGenericIEnumerable() + public async Task ReadGenericIEnumerableOfGenericIEnumerable() { - IEnumerable> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IEnumerable ie in result) @@ -95,13 +96,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadIEnumerableTOfArray() + public async Task ReadIEnumerableTOfArray() { - IEnumerable result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -113,13 +114,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[1,2],[3, 4]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3, 4]]")); } [Fact] - public static void ReadArrayOfIEnumerableT() + public async Task ReadArrayOfIEnumerableT() { - IEnumerable[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IEnumerable arr in result) @@ -131,13 +132,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadSimpleGenericIEnumerable() + public async Task ReadSimpleGenericIEnumerable() { - IEnumerable result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + IEnumerable result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -145,18 +146,18 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); // There is no way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); - Assert.Throws(() => JsonSerializer.Deserialize(@"[]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[]")); } [Fact] - public static void ReadIListTOfIListT() + public async Task ReadIListTOfIListT() { - IList> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IList ie in result) @@ -167,7 +168,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericIListWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericIListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringIListWrapper il in result2) @@ -180,9 +181,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericIListOfArray() + public async Task ReadGenericIListOfArray() { - IList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -193,7 +194,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericIListWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericIListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (string[] arr in result2) @@ -206,9 +207,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfIListT() + public async Task ReadArrayOfIListT() { - IList[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IList arr in result) @@ -219,7 +220,7 @@ namespace System.Text.Json.Serialization.Tests } } - StringIListWrapper[] result2 = JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]"); + StringIListWrapper[] result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringIListWrapper il in result2) @@ -232,9 +233,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleGenericIList() + public async Task ReadSimpleGenericIList() { - IList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + IList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -242,10 +243,10 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); - StringIListWrapper result2 = JsonSerializer.Deserialize(@"[""1"",""2""]"); + StringIListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]"); expected = 1; foreach (string str in result2) @@ -253,15 +254,15 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal($"{expected++}", str); } - result2 = JsonSerializer.Deserialize(@"[]"); + result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); Assert.Equal(0, result2.Count()); } [Fact] - public static void ReadGenericStructIList() + public async Task ReadGenericStructIList() { string json = "[10,20,30]"; - var wrapper = JsonSerializer.Deserialize>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(3, wrapper.Count); Assert.Equal(10, wrapper[0]); Assert.Equal(20, wrapper[1]); @@ -269,10 +270,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructIList() + public async Task ReadNullableGenericStructIList() { string json = "[10,20,30]"; - var wrapper = JsonSerializer.Deserialize?>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>(json); Assert.True(wrapper.HasValue); Assert.Equal(3, wrapper.Value.Count); Assert.Equal(10, wrapper.Value[0]); @@ -281,17 +282,17 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructIListWithNullJson() + public async Task ReadNullableGenericStructIListWithNullJson() { - var wrapper = JsonSerializer.Deserialize?>("null"); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>("null"); Assert.False(wrapper.HasValue); } [Fact] - public static void ReadGenericStructICollection() + public async Task ReadGenericStructICollection() { string json = "[10,20,30]"; - var wrapper = JsonSerializer.Deserialize>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(3, wrapper.Count); Assert.Equal(10, wrapper.ElementAt(0)); Assert.Equal(20, wrapper.ElementAt(1)); @@ -299,10 +300,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructICollection() + public async Task ReadNullableGenericStructICollection() { string json = "[10,20,30]"; - var wrapper = JsonSerializer.Deserialize?>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>(json); Assert.True(wrapper.HasValue); Assert.Equal(3, wrapper.Value.Count); Assert.Equal(10, wrapper.Value.ElementAt(0)); @@ -311,16 +312,16 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructICollectionWithNullJson() + public async Task ReadNullableGenericStructICollectionWithNullJson() { - var wrapper = JsonSerializer.Deserialize?>("null"); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>("null"); Assert.False(wrapper.HasValue); } [Fact] - public static void ReadGenericICollectionOfGenericICollection() + public async Task ReadGenericICollectionOfGenericICollection() { - ICollection> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (ICollection ie in result) @@ -345,9 +346,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericICollectionOfArray() + public async Task ReadGenericICollectionOfArray() { - ICollection result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -358,7 +359,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericICollectionWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericICollectionWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (string[] arr in result2) @@ -371,9 +372,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfGenericICollection() + public async Task ReadArrayOfGenericICollection() { - ICollection[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (ICollection arr in result) @@ -386,9 +387,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleGenericICollection() + public async Task ReadSimpleGenericICollection() { - ICollection result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + ICollection result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -396,10 +397,10 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); - GenericICollectionWrapper result2 = JsonSerializer.Deserialize>(@"[""1"",""2""]"); + GenericICollectionWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[""1"",""2""]"); expected = 1; foreach (string str in result2) @@ -407,14 +408,14 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal($"{expected++}", str); } - result2 = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result2.Count()); } [Fact] - public static void ReadGenericIReadOnlyCollectionOfGenericIReadOnlyCollection() + public async Task ReadGenericIReadOnlyCollectionOfGenericIReadOnlyCollection() { - IReadOnlyCollection> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyCollection> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IReadOnlyCollection ie in result) @@ -426,14 +427,14 @@ namespace System.Text.Json.Serialization.Tests } // There's no way to populate this collection. - Assert.Throws( - () => JsonSerializer.Deserialize>>(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync( + async () => await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadGenericIReadOnlyCollectionOfArray() + public async Task ReadGenericIReadOnlyCollectionOfArray() { - IReadOnlyCollection result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyCollection result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -444,13 +445,13 @@ namespace System.Text.Json.Serialization.Tests } } - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[1,2],[3,4]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]")); } [Fact] - public static void ReadArrayOfIReadOnlyCollectionT() + public async Task ReadArrayOfIReadOnlyCollectionT() { - IReadOnlyCollection[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyCollection[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IReadOnlyCollection arr in result) @@ -462,13 +463,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize[]>(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadGenericSimpleIReadOnlyCollection() + public async Task ReadGenericSimpleIReadOnlyCollection() { - IReadOnlyCollection result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + IReadOnlyCollection result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -476,17 +477,17 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[""1"",""2""]")); } [Fact] - public static void ReadGenericIReadOnlyListOfGenericIReadOnlyList() + public async Task ReadGenericIReadOnlyListOfGenericIReadOnlyList() { - IReadOnlyList> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyList> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IReadOnlyList ie in result) @@ -497,13 +498,13 @@ namespace System.Text.Json.Serialization.Tests } } - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadGenericIReadOnlyListOfArray() + public async Task ReadGenericIReadOnlyListOfArray() { - IReadOnlyList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -515,13 +516,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadArrayOfGenericIReadOnlyList() + public async Task ReadArrayOfGenericIReadOnlyList() { - IReadOnlyList[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IReadOnlyList[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IReadOnlyList arr in result) @@ -533,13 +534,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]")); } [Fact] - public static void ReadSimpleGenericIReadOnlyList() + public async Task ReadSimpleGenericIReadOnlyList() { - IReadOnlyList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + IReadOnlyList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -547,17 +548,17 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); } [Fact] - public static void ReadGenericISetOfGenericISet() + public async Task ReadGenericISetOfGenericISet() { - ISet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ISet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); if (result.First().Contains(1)) { @@ -570,7 +571,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(new HashSet { 1, 2 }, result.Last()); } - GenericISetWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericISetWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); if (result2.First().Contains("1")) { @@ -585,10 +586,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericStructISet() + public async Task ReadGenericStructISet() { string json = "[10, 20, 30]"; - var wrapper = JsonSerializer.Deserialize>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper>(json); Assert.Equal(3, wrapper.Count); Assert.Equal(10, wrapper.ElementAt(0)); Assert.Equal(20, wrapper.ElementAt(1)); @@ -596,10 +597,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructISet() + public async Task ReadNullableGenericStructISet() { string json = "[10, 20, 30]"; - var wrapper = JsonSerializer.Deserialize?>(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>(json); Assert.True(wrapper.HasValue); Assert.Equal(3, wrapper.Value.Count); Assert.Equal(10, wrapper.Value.ElementAt(0)); @@ -608,17 +609,17 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableGenericStructISetWithNullJson() + public async Task ReadNullableGenericStructISetWithNullJson() { - var wrapper = JsonSerializer.Deserialize?>("null"); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper?>("null"); Assert.False(wrapper.HasValue); } [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/50721", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] - public static void ReadISetTOfHashSetT() + public async Task ReadISetTOfHashSetT() { - ISet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ISet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); if (result.First().Contains(1)) { @@ -633,9 +634,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadHashSetTOfISetT() + public async Task ReadHashSetTOfISetT() { - HashSet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + HashSet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); if (result.First().Contains(1)) { @@ -650,9 +651,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadISetTOfArray() + public async Task ReadISetTOfArray() { - ISet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ISet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); if (result.First().Contains(1)) { @@ -667,29 +668,29 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfISetT() + public async Task ReadArrayOfISetT() { - ISet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ISet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); Assert.Equal(new HashSet { 1, 2 }, result.First()); Assert.Equal(new HashSet { 3, 4 }, result.Last()); } [Fact] - public static void ReadSimpleISetT() + public async Task ReadSimpleISetT() { - ISet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + ISet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); Assert.Equal(new HashSet { 1, 2 }, result); - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); } [Fact] - public static void StackTOfStackT() + public async Task StackTOfStackT() { - Stack> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 4; foreach (Stack st in result) @@ -700,7 +701,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericStackWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericStackWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 4; foreach (StringStackWrapper st in result2) @@ -713,9 +714,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericStackOfArray() + public async Task ReadGenericStackOfArray() { - Stack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 3; foreach (int[] arr in result) @@ -728,7 +729,7 @@ namespace System.Text.Json.Serialization.Tests expected = 1; } - GenericStackWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericStackWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 3; foreach (string[] arr in result2) @@ -743,9 +744,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfGenericStack() + public async Task ReadArrayOfGenericStack() { - Stack[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 2; foreach (Stack st in result) @@ -758,7 +759,7 @@ namespace System.Text.Json.Serialization.Tests expected = 4; } - StringStackWrapper[] result2 = JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]"); + StringStackWrapper[] result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]"); expected = 2; foreach (StringStackWrapper st in result2) @@ -773,9 +774,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleGenericStack() + public async Task ReadSimpleGenericStack() { - Stack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + Stack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 2; foreach (int i in result) @@ -783,10 +784,10 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected--, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); - StringStackWrapper result2 = JsonSerializer.Deserialize(@"[""1"",""2""]"); + StringStackWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]"); expected = 2; foreach (string str in result2) @@ -794,14 +795,14 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal($"{expected--}", str); } - result2 = JsonSerializer.Deserialize(@"[]"); + result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); Assert.Equal(0, result2.Count()); } [Fact] - public static void ReadQueueTOfQueueT() + public async Task ReadQueueTOfQueueT() { - Queue> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (Queue q in result) @@ -812,7 +813,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericQueueWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericQueueWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringQueueWrapper q in result2) @@ -825,9 +826,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadQueueTOfArray() + public async Task ReadQueueTOfArray() { - Queue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -840,9 +841,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfIQueueT() + public async Task ReadArrayOfIQueueT() { - Queue[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (Queue q in result) @@ -855,24 +856,24 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleQueueT() + public async Task ReadSimpleQueueT() { - Queue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + Queue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) { Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); } [Fact] - public static void ReadHashSetTOfHashSetT() + public async Task ReadHashSetTOfHashSetT() { - HashSet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + HashSet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (HashSet hs in result) @@ -883,7 +884,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericHashSetWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericHashSetWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringHashSetWrapper hs in result2) @@ -896,9 +897,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadHashSetTOfArray() + public async Task ReadHashSetTOfArray() { - HashSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + HashSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -911,9 +912,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfIHashSetT() + public async Task ReadArrayOfIHashSetT() { - HashSet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + HashSet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (HashSet hs in result) @@ -926,9 +927,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleHashSetT() + public async Task ReadSimpleHashSetT() { - HashSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + HashSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -936,14 +937,14 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); } [Fact] - public static void ReadGenericLinkedListOfGenericLinkedList() + public async Task ReadGenericLinkedListOfGenericLinkedList() { - LinkedList> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + LinkedList> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); int expected = 1; foreach (LinkedList l in result) @@ -954,7 +955,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericLinkedListWrapper result2 = JsonSerializer.Deserialize>(@"[[""1"",""2""],[""3"",""4""]]"); + GenericLinkedListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringLinkedListWrapper l in result2) @@ -967,9 +968,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadLinkedListTOfArray() + public async Task ReadLinkedListTOfArray() { - LinkedList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + LinkedList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (int[] arr in result) @@ -982,9 +983,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfILinkedListT() + public async Task ReadArrayOfILinkedListT() { - LinkedList[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + LinkedList[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (LinkedList l in result) @@ -997,9 +998,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleLinkedListT() + public async Task ReadSimpleLinkedListT() { - LinkedList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + LinkedList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -1007,14 +1008,14 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); } [Fact] - public static void ReadArrayOfSortedSetT() + public async Task ReadArrayOfSortedSetT() { - SortedSet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + SortedSet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); int expected = 1; foreach (SortedSet s in result) @@ -1025,7 +1026,7 @@ namespace System.Text.Json.Serialization.Tests } } - StringSortedSetWrapper[] result2 = JsonSerializer.Deserialize(@"[[""1"",""2""],[""3"",""4""]]"); + StringSortedSetWrapper[] result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[""1"",""2""],[""3"",""4""]]"); expected = 1; foreach (StringSortedSetWrapper s in result2) @@ -1038,9 +1039,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleSortedSetT() + public async Task ReadSimpleSortedSetT() { - SortedSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); + SortedSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); int expected = 1; foreach (int i in result) @@ -1048,31 +1049,31 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i); } - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); Assert.Equal(0, result.Count()); } [Fact] - public static void ReadClass_WithGenericStructCollectionWrapper_NullJson_Throws() + public async Task ReadClass_WithGenericStructCollectionWrapper_NullJson_Throws() { - Assert.Throws(() => JsonSerializer.Deserialize(@"{ ""List"": null }")); - Assert.Throws(() => JsonSerializer.Deserialize(@"{ ""Collection"": null }")); - Assert.Throws(() => JsonSerializer.Deserialize(@"{ ""Dictionary"": null }")); - Assert.Throws(() => JsonSerializer.Deserialize(@"{ ""Set"": null }")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""List"": null }")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Collection"": null }")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Dictionary"": null }")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{ ""Set"": null }")); } [Fact] - public static void ReadSimpleTestClass_GenericStructCollectionWrappers() + public async Task ReadSimpleTestClass_GenericStructCollectionWrappers() { - SimpleTestClassWithGenericStructCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestClassWithGenericStructCollectionWrappers.s_json); + SimpleTestClassWithGenericStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithGenericStructCollectionWrappers.s_json); obj.Verify(); } [Fact] - public static void ReadSimpleTestStruct_NullableGenericStructCollectionWrappers() + public async Task ReadSimpleTestStruct_NullableGenericStructCollectionWrappers() { { - SimpleTestStructWithNullableGenericStructCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestStructWithNullableGenericStructCollectionWrappers.s_json); + SimpleTestStructWithNullableGenericStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestStructWithNullableGenericStructCollectionWrappers.s_json); obj.Verify(); } @@ -1084,7 +1085,7 @@ namespace System.Text.Json.Serialization.Tests @"""Set"" : null," + @"""Dictionary"" : null" + @"}"; - SimpleTestStructWithNullableGenericStructCollectionWrappers obj = JsonSerializer.Deserialize(json); + SimpleTestStructWithNullableGenericStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.False(obj.List.HasValue); Assert.False(obj.Collection.HasValue); Assert.False(obj.Set.HasValue); @@ -1093,17 +1094,17 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleTestClass_GenericCollectionWrappers() + public async Task ReadSimpleTestClass_GenericCollectionWrappers() { - SimpleTestClassWithGenericCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestClassWithGenericCollectionWrappers.s_json); + SimpleTestClassWithGenericCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithGenericCollectionWrappers.s_json); obj.Verify(); } [Theory] [MemberData(nameof(ReadSimpleTestClass_GenericWrappers_NoAddMethod))] - public static void ReadSimpleTestClass_GenericWrappers_NoAddMethod_Throws(Type type, string json, Type exceptionMessageType) + public async Task ReadSimpleTestClass_GenericWrappers_NoAddMethod_Throws(Type type, string json, Type exceptionMessageType) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); Assert.Contains(exceptionMessageType.ToString(), ex.Message); } @@ -1145,22 +1146,22 @@ namespace System.Text.Json.Serialization.Tests [InlineData(typeof(ReadOnlyStringISetWrapper), @"[""1"", ""2""]")] [InlineData(typeof(ReadOnlyWrapperForIDictionary), @"{""Key"":""key"",""Value"":""value""}")] [InlineData(typeof(ReadOnlyStringToStringIDictionaryWrapper), @"{""Key"":""key"",""Value"":""value""}")] - public static void ReadReadOnlyCollections_Throws(Type type, string json) + public async Task ReadReadOnlyCollections_Throws(Type type, string json) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); Assert.Contains(type.ToString(), ex.Message); } [Fact] - public static void Read_HigherOrderCollectionInheritance_Works() + public async Task Read_HigherOrderCollectionInheritance_Works() { const string json = "[\"test\"]"; - Assert.Equal("test", JsonSerializer.Deserialize(json)[0]); - Assert.Equal("test", JsonSerializer.Deserialize>(json).First()); - Assert.Equal("test", JsonSerializer.Deserialize(json).First()); - Assert.Equal("test", JsonSerializer.Deserialize>(json).First()); - Assert.Equal("test", JsonSerializer.Deserialize>(json).First()); - Assert.Equal("test", JsonSerializer.Deserialize(json).First()); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper(json))[0]); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper>(json)).First()); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper(json)).First()); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper>(json)).First()); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper>(json)).First()); + Assert.Equal("test", (await JsonSerializerWrapperForString.DeserializeWrapper(json)).First()); } [Theory] @@ -1184,21 +1185,21 @@ namespace System.Text.Json.Serialization.Tests [InlineData(typeof(GenericStackWrapperInternalConstructor), @"[""1""]")] [InlineData(typeof(StringToGenericDictionaryWrapperPrivateConstructor), @"{""Key"":""Value""}")] [InlineData(typeof(StringToGenericDictionaryWrapperInternalConstructor), @"{""Key"":""Value""}")] - public static void Read_Generic_NoPublicConstructor_Throws(Type type, string json) + public async Task Read_Generic_NoPublicConstructor_Throws(Type type, string json) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); Assert.Contains(type.ToString(), ex.Message); } [Fact] - public static void DoesNotCall_CollectionPropertyGetter_EveryTimeElementIsAdded() + public async Task DoesNotCall_CollectionPropertyGetter_EveryTimeElementIsAdded() { var networkList = new List { "Network1", "Network2" }; - string serialized = JsonSerializer.Serialize(new NetworkWrapper { NetworkList = networkList }); + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(new NetworkWrapper { NetworkList = networkList }); Assert.Equal(@"{""NetworkList"":[""Network1"",""Network2""]}", serialized); - NetworkWrapper obj = JsonSerializer.Deserialize(serialized); + NetworkWrapper obj = await JsonSerializerWrapperForString.DeserializeWrapper(serialized); int i = 0; foreach (string network in obj.NetworkList) @@ -1227,18 +1228,18 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void CollectionWith_BackingField_CanRoundtrip() + public async Task CollectionWith_BackingField_CanRoundtrip() { string json = "{\"AllowedGrantTypes\":[\"client_credentials\"]}"; - Client obj = JsonSerializer.Deserialize(json); + Client obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("client_credentials", obj.AllowedGrantTypes.First()); - string serialized = JsonSerializer.Serialize(obj); + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(obj); Assert.Equal(json, serialized); } - private class Client + public class Client { private ICollection _allowedGrantTypes = new HashSetWithBackingCollection(); @@ -1251,17 +1252,17 @@ namespace System.Text.Json.Serialization.Tests [Theory] [MemberData(nameof(CustomInterfaces_Enumerables))] - public static void CustomInterfacesNotSupported_Enumerables(Type type) + public async Task CustomInterfacesNotSupported_Enumerables(Type type) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize("[]", type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("[]", type)); Assert.Contains(type.ToString(), ex.ToString()); } [Theory] [MemberData(nameof(CustomInterfaces_Dictionaries))] - public static void CustomInterfacesNotSupported_Dictionaries(Type type) + public async Task CustomInterfacesNotSupported_Dictionaries(Type type) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize("{}", type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper("{}", type)); Assert.Contains(type.ToString(), ex.ToString()); } @@ -1278,10 +1279,10 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void IReadOnlyDictionary_NotSupportedKey() + public async Task IReadOnlyDictionary_NotSupportedKey() { - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""http://foo"":1}")); - Assert.Throws(() => JsonSerializer.Serialize(new GenericIReadOnlyDictionaryWrapper(new Dictionary { { new Uri("http://foo"), 1 } }))); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""http://foo"":1}")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new GenericIReadOnlyDictionaryWrapper(new Dictionary { { new Uri("http://foo"), 1 } }))); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Write.cs similarity index 69% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Write.cs index e6963f2ca07..33015af00e8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Generic.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Write.cs @@ -3,14 +3,15 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void WriteListOfList() + public async Task WriteListOfList() { var input = new List> { @@ -18,7 +19,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); var input2 = new GenericListWrapper @@ -27,12 +28,12 @@ namespace System.Text.Json.Serialization.Tests new StringListWrapper() { "3", "4" } }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteListOfArray() + public async Task WriteListOfArray() { var input = new List { @@ -40,32 +41,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfList() + public async Task WriteArrayOfList() { var input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveList() + public async Task WritePrimitiveList() { var input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteGenericIEnumerableOfGenericIEnumerable() + public async Task WriteGenericIEnumerableOfGenericIEnumerable() { IEnumerable> input = new List> { @@ -73,7 +74,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericIEnumerableWrapper input2 = new GenericIEnumerableWrapper(new List @@ -82,12 +83,12 @@ namespace System.Text.Json.Serialization.Tests new StringIEnumerableWrapper(new List { "3", "4" }), }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteIEnumerableTOfArray() + public async Task WriteIEnumerableTOfArray() { IEnumerable input = new List { @@ -95,32 +96,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIEnumerableT() + public async Task WriteArrayOfIEnumerableT() { IEnumerable[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIEnumerableT() + public async Task WritePrimitiveIEnumerableT() { IEnumerable input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteGenericIListOfGenericIList() + public async Task WriteGenericIListOfGenericIList() { IList> input = new List> { @@ -128,7 +129,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericIListWrapper input2 = new GenericIListWrapper @@ -137,12 +138,12 @@ namespace System.Text.Json.Serialization.Tests new StringIListWrapper() { "3", "4" } }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteIListTOfArray() + public async Task WriteIListTOfArray() { IList input = new List { @@ -150,60 +151,60 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIListT() + public async Task WriteArrayOfIListT() { IList[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIListT() + public async Task WritePrimitiveIListT() { IList input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteGenericStructIListWrapperT() + public async Task WriteGenericStructIListWrapperT() { { GenericStructIListWrapper obj = new GenericStructIListWrapper() { 10, 20 }; - Assert.Equal("[10,20]", JsonSerializer.Serialize(obj)); + Assert.Equal("[10,20]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { GenericStructIListWrapper obj = default; - Assert.Equal("[]", JsonSerializer.Serialize(obj)); + Assert.Equal("[]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteGenericStructICollectionWrapperT() + public async Task WriteGenericStructICollectionWrapperT() { { GenericStructICollectionWrapper obj = new GenericStructICollectionWrapper() { 10, 20 }; - Assert.Equal("[10,20]", JsonSerializer.Serialize(obj)); + Assert.Equal("[10,20]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { GenericStructICollectionWrapper obj = default; - Assert.Equal("[]", JsonSerializer.Serialize(obj)); + Assert.Equal("[]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteGenericICollectionOfGenericICollection() + public async Task WriteGenericICollectionOfGenericICollection() { ICollection> input = new List> { @@ -211,7 +212,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericICollectionWrapper> input2 = new GenericICollectionWrapper> @@ -220,12 +221,12 @@ namespace System.Text.Json.Serialization.Tests new GenericICollectionWrapper() { "3", "4" } }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteICollectionTOfArray() + public async Task WriteICollectionTOfArray() { ICollection input = new List { @@ -233,32 +234,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfICollectionT() + public async Task WriteArrayOfICollectionT() { ICollection[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveICollectionT() + public async Task WritePrimitiveICollectionT() { ICollection input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteGenericIReadOnlyCollectionOfGenericIReadOnlyCollection() + public async Task WriteGenericIReadOnlyCollectionOfGenericIReadOnlyCollection() { IReadOnlyCollection> input = new List> { @@ -266,7 +267,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericIReadOnlyCollectionWrapper> input2 = @@ -276,12 +277,12 @@ namespace System.Text.Json.Serialization.Tests new WrapperForIReadOnlyCollectionOfT(new List { "3", "4" }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteIReadOnlyCollectionTOfArray() + public async Task WriteIReadOnlyCollectionTOfArray() { IReadOnlyCollection input = new List { @@ -289,32 +290,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIReadOnlyCollectionT() + public async Task WriteArrayOfIReadOnlyCollectionT() { IReadOnlyCollection[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIReadOnlyCollectionT() + public async Task WritePrimitiveIReadOnlyCollectionT() { IReadOnlyCollection input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteIReadOnlyListTOfIReadOnlyListT() + public async Task WriteIReadOnlyListTOfIReadOnlyListT() { IReadOnlyList> input = new List> { @@ -322,7 +323,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericIReadOnlyListWrapper input2 = new GenericIReadOnlyListWrapper(new List @@ -331,12 +332,12 @@ namespace System.Text.Json.Serialization.Tests new StringIReadOnlyListWrapper(new List { "3", "4" }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteIReadOnlyListTOfArray() + public async Task WriteIReadOnlyListTOfArray() { IReadOnlyList input = new List { @@ -344,47 +345,47 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIReadOnlyListT() + public async Task WriteArrayOfIReadOnlyListT() { IReadOnlyList[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIReadOnlyListT() + public async Task WritePrimitiveIReadOnlyListT() { IReadOnlyList input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void GenericStructISetWrapperT() + public async Task GenericStructISetWrapperT() { { GenericStructISetWrapper obj = new GenericStructISetWrapper() { 10, 20 }; - Assert.Equal("[10,20]", JsonSerializer.Serialize(obj)); + Assert.Equal("[10,20]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { GenericStructISetWrapper obj = default; - Assert.Equal("[]", JsonSerializer.Serialize(obj)); + Assert.Equal("[]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/50721", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] - public static void WriteISetTOfISetT() + public async Task WriteISetTOfISetT() { ISet> input = new HashSet> { @@ -392,10 +393,10 @@ namespace System.Text.Json.Serialization.Tests new HashSet() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize>>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper>>(json); if (input.First().Contains(1)) { @@ -414,10 +415,10 @@ namespace System.Text.Json.Serialization.Tests new StringISetWrapper() { "3", "4" }, }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input2 = JsonSerializer.Deserialize>(json); + input2 = await JsonSerializerWrapperForString.DeserializeWrapper>(json); if (input2.First().Contains("1")) { @@ -433,7 +434,7 @@ namespace System.Text.Json.Serialization.Tests [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/50721", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] - public static void WriteISetTOfHashSetT() + public async Task WriteISetTOfHashSetT() { ISet> input = new HashSet> { @@ -441,10 +442,10 @@ namespace System.Text.Json.Serialization.Tests new HashSet() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize>>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper>>(json); if (input.First().Contains(1)) { @@ -459,7 +460,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void WriteHashSetTOfISetT() + public async Task WriteHashSetTOfISetT() { HashSet> input = new HashSet> { @@ -467,10 +468,10 @@ namespace System.Text.Json.Serialization.Tests new HashSet() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize>>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper>>(json); if (input.First().Contains(1)) { @@ -485,7 +486,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void WriteISetTOfArray() + public async Task WriteISetTOfArray() { ISet input = new HashSet { @@ -493,37 +494,37 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] - public static void WriteArrayOfISetT() + public async Task WriteArrayOfISetT() { ISet[] input = new HashSet[2]; input[0] = new HashSet() { 1, 2 }; input[1] = new HashSet() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize[]>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper[]>(json); Assert.Equal(new HashSet { 1, 2 }, input.First()); Assert.Equal(new HashSet { 3, 4 }, input.Last()); } [Fact] - public static void WritePrimitiveISetT() + public async Task WritePrimitiveISetT() { ISet input = new HashSet { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.True(json == "[1,2]" || json == "[2,1]"); } [Fact] - public static void WriteStackTOfStackT() + public async Task WriteStackTOfStackT() { Stack> input = new Stack>(new List> { @@ -531,7 +532,7 @@ namespace System.Text.Json.Serialization.Tests new Stack(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[4,3],[2,1]]", json); GenericStackWrapper input2 = new GenericStackWrapper(new List @@ -540,12 +541,12 @@ namespace System.Text.Json.Serialization.Tests new StringStackWrapper(new List() { "3", "4" }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""4"",""3""],[""2"",""1""]]", json); } [Fact] - public static void WriteStackTOfArray() + public async Task WriteStackTOfArray() { Stack input = new Stack(new List { @@ -553,32 +554,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[3,4],[1,2]]", json); } [Fact] - public static void WriteArrayOfStackT() + public async Task WriteArrayOfStackT() { Stack[] input = new Stack[2]; input[0] = new Stack(new List { 1, 2 }); input[1] = new Stack(new List { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[2,1],[4,3]]", json); } [Fact] - public static void WritePrimitiveStackT() + public async Task WritePrimitiveStackT() { Stack input = new Stack(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[2,1]", json); } [Fact] - public static void WriteQueueTOfQueueT() + public async Task WriteQueueTOfQueueT() { Queue> input = new Queue>(new List> { @@ -586,7 +587,7 @@ namespace System.Text.Json.Serialization.Tests new Queue(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericQueueWrapper input2 = new GenericQueueWrapper(new List @@ -595,12 +596,12 @@ namespace System.Text.Json.Serialization.Tests new StringQueueWrapper(new List() { "3", "4" }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteQueueTOfArray() + public async Task WriteQueueTOfArray() { Queue input = new Queue(new List { @@ -608,32 +609,32 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfQueueT() + public async Task WriteArrayOfQueueT() { Queue[] input = new Queue[2]; input[0] = new Queue(new List { 1, 2 }); input[1] = new Queue(new List { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveQueueT() + public async Task WritePrimitiveQueueT() { Queue input = new Queue(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteHashSetTOfHashSetT() + public async Task WriteHashSetTOfHashSetT() { HashSet> input = new HashSet>(new List> { @@ -641,10 +642,10 @@ namespace System.Text.Json.Serialization.Tests new HashSet(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize>>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper>>(json); if (input.First().Contains(1)) { @@ -663,10 +664,10 @@ namespace System.Text.Json.Serialization.Tests new StringHashSetWrapper(new List() { "3", "4" }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input2 = JsonSerializer.Deserialize>(json); + input2 = await JsonSerializerWrapperForString.DeserializeWrapper>(json); if (input2.First().Contains("1")) { @@ -681,7 +682,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void WriteHashSetTOfArray() + public async Task WriteHashSetTOfArray() { HashSet input = new HashSet(new List { @@ -689,38 +690,38 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/50721", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] - public static void WriteArrayOfHashSetT() + public async Task WriteArrayOfHashSetT() { HashSet[] input = new HashSet[2]; input[0] = new HashSet(new List { 1, 2 }); input[1] = new HashSet(new List { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); // Because order isn't guaranteed, roundtrip data to ensure write was accurate. - input = JsonSerializer.Deserialize[]>(json); + input = await JsonSerializerWrapperForString.DeserializeWrapper[]>(json); Assert.Equal(new HashSet { 1, 2 }, input.First()); Assert.Equal(new HashSet { 3, 4 }, input.Last()); } [Fact] - public static void WritePrimitiveHashSetT() + public async Task WritePrimitiveHashSetT() { HashSet input = new HashSet(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.True(json == "[1,2]" || json == "[2,1]"); } [Fact] - public static void WriteLinkedListTOfLinkedListT() + public async Task WriteLinkedListTOfLinkedListT() { LinkedList> input = new LinkedList>(new List> { @@ -728,7 +729,7 @@ namespace System.Text.Json.Serialization.Tests new LinkedList(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericLinkedListWrapper input2 = new GenericLinkedListWrapper(new List @@ -737,12 +738,12 @@ namespace System.Text.Json.Serialization.Tests new StringLinkedListWrapper(new List() { "3", "4" }), }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[[""1"",""2""],[""3"",""4""]]", json); } [Fact] - public static void WriteLinkedListTOfArray() + public async Task WriteLinkedListTOfArray() { LinkedList input = new LinkedList(new List { @@ -750,52 +751,52 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfLinkedListT() + public async Task WriteArrayOfLinkedListT() { LinkedList[] input = new LinkedList[2]; input[0] = new LinkedList(new List { 1, 2 }); input[1] = new LinkedList(new List { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveLinkedListT() + public async Task WritePrimitiveLinkedListT() { LinkedList input = new LinkedList(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteArrayOfSortedSetT() + public async Task WriteArrayOfSortedSetT() { SortedSet[] input = new SortedSet[2]; input[0] = new SortedSet(new List { 1, 2 }); input[1] = new SortedSet(new List { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveSortedSetT() + public async Task WritePrimitiveSortedSetT() { SortedSet input = new SortedSet(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteGenericCollectionWrappers() + public async Task WriteGenericCollectionWrappers() { SimpleTestClassWithGenericCollectionWrappers obj1 = new SimpleTestClassWithGenericCollectionWrappers(); SimpleTestClassWithStringIEnumerableWrapper obj2 = new SimpleTestClassWithStringIEnumerableWrapper(); @@ -809,29 +810,29 @@ namespace System.Text.Json.Serialization.Tests obj4.Initialize(); obj5.Initialize(); - Assert.Equal(SimpleTestClassWithGenericCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); - Assert.Equal(SimpleTestClassWithGenericCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); + Assert.Equal(SimpleTestClassWithGenericCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); + Assert.Equal(SimpleTestClassWithGenericCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); - Assert.Equal(SimpleTestClassWithStringIEnumerableWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); - Assert.Equal(SimpleTestClassWithStringIEnumerableWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); + Assert.Equal(SimpleTestClassWithStringIEnumerableWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); + Assert.Equal(SimpleTestClassWithStringIEnumerableWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); - Assert.Equal(SimpleTestClassWithStringIReadOnlyCollectionWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); - Assert.Equal(SimpleTestClassWithStringIReadOnlyCollectionWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); + Assert.Equal(SimpleTestClassWithStringIReadOnlyCollectionWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); + Assert.Equal(SimpleTestClassWithStringIReadOnlyCollectionWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); - Assert.Equal(SimpleTestClassWithStringIReadOnlyListWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); - Assert.Equal(SimpleTestClassWithStringIReadOnlyListWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); + Assert.Equal(SimpleTestClassWithStringIReadOnlyListWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); + Assert.Equal(SimpleTestClassWithStringIReadOnlyListWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); - Assert.Equal(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); - Assert.Equal(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); + Assert.Equal(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); + Assert.Equal(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); } [Fact] - public static void WriteSimpleTestClassWithGenericStructCollectionWrappers() + public async Task WriteSimpleTestClassWithGenericStructCollectionWrappers() { { SimpleTestClassWithGenericStructCollectionWrappers obj = new SimpleTestClassWithGenericStructCollectionWrappers(); obj.Initialize(); - Assert.Equal(SimpleTestClassWithGenericStructCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestClassWithGenericStructCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { @@ -849,17 +850,17 @@ namespace System.Text.Json.Serialization.Tests @"""Set"" : []," + @"""Dictionary"" : {}" + @"}"; - Assert.Equal(json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteSimpleTestStructWithNullableGenericStructCollectionWrappers() + public async Task WriteSimpleTestStructWithNullableGenericStructCollectionWrappers() { { SimpleTestStructWithNullableGenericStructCollectionWrappers obj = new SimpleTestStructWithNullableGenericStructCollectionWrappers(); obj.Initialize(); - Assert.Equal(SimpleTestStructWithNullableGenericStructCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestStructWithNullableGenericStructCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { @@ -871,87 +872,87 @@ namespace System.Text.Json.Serialization.Tests @"""Set"" : null," + @"""Dictionary"" : null" + @"}"; - Assert.Equal(json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void ConvertIEnumerableValueTypesThenSerialize() + public async Task ConvertIEnumerableValueTypesThenSerialize() { IEnumerable valueAs = Enumerable.Range(0, 5).Select(x => new ValueA { Value = x }).ToList(); IEnumerable valueBs = valueAs.Select(x => new ValueB { Value = x.Value }); string expectedJson = @"[{""Value"":0},{""Value"":1},{""Value"":2},{""Value"":3},{""Value"":4}]"; - Assert.Equal(expectedJson, JsonSerializer.Serialize>(valueBs)); + Assert.Equal(expectedJson, await JsonSerializerWrapperForString.SerializeWrapper>(valueBs)); } [Fact] - public static void WriteIEnumerableT_DisposesEnumerators() + public async Task WriteIEnumerableT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var items = new RefCountedList(Enumerable.Range(1, count)); - _ = JsonSerializer.Serialize(items.AsEnumerable()); + _ = await JsonSerializerWrapperForString.SerializeWrapper(items.AsEnumerable()); Assert.Equal(0, items.RefCount); } } [Fact] - public static void WriteICollectionT_DisposesEnumerators() + public async Task WriteICollectionT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var items = new RefCountedList(Enumerable.Range(1, count)); - _ = JsonSerializer.Serialize((ICollection)items); + _ = await JsonSerializerWrapperForString.SerializeWrapper((ICollection)items); Assert.Equal(0, items.RefCount); } } [Fact] - public static void WriteIListT_DisposesEnumerators() + public async Task WriteIListT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var items = new RefCountedList(Enumerable.Range(1, count)); - _ = JsonSerializer.Serialize((IList)items); + _ = await JsonSerializerWrapperForString.SerializeWrapper((IList)items); Assert.Equal(0, items.RefCount); } } [Fact] - public static void WriteIDictionaryT_DisposesEnumerators() + public async Task WriteIDictionaryT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var pairs = new RefCountedDictionary(Enumerable.Range(1, count).Select(x => new KeyValuePair(x, x))); - _ = JsonSerializer.Serialize((IDictionary)pairs); + _ = await JsonSerializerWrapperForString.SerializeWrapper((IDictionary)pairs); Assert.Equal(0, pairs.RefCount); } } [Fact] - public static void WriteIReadOnlyDictionaryT_DisposesEnumerators() + public async Task WriteIReadOnlyDictionaryT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var pairs = new RefCountedDictionary(Enumerable.Range(1, count).Select(x => new KeyValuePair(x, x))); - _ = JsonSerializer.Serialize((IReadOnlyDictionary)pairs); + _ = await JsonSerializerWrapperForString.SerializeWrapper((IReadOnlyDictionary)pairs); Assert.Equal(0, pairs.RefCount); } } [Fact] - public static void WriteISetT_DisposesEnumerators() + public async Task WriteISetT_DisposesEnumerators() { for (int count = 0; count < 5; count++) { var items = new RefCountedSet(Enumerable.Range(1, count)); - _ = JsonSerializer.Serialize((ISet)items); + _ = await JsonSerializerWrapperForString.SerializeWrapper((ISet)items); Assert.Equal(0, items.RefCount); } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Read.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Read.cs new file mode 100644 index 00000000000..ca63692b2b2 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Read.cs @@ -0,0 +1,637 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests + { + [Fact] + public async Task ReadImmutableArrayOfImmutableArray() + { + ImmutableArray> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableArray l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadImmutableArrayOfArray() + { + ImmutableArray result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadArrayOfImmutableArray() + { + ImmutableArray[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableArray l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadSimpleImmutableArray() + { + ImmutableArray result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadSimpleClassWithImmutableArray() + { + SimpleTestClassWithImmutableArray obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithImmutableArray.s_json); + obj.Verify(); + } + + [Fact] + public async Task ReadIImmutableListTOfIImmutableListT() + { + IImmutableList> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (IImmutableList l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadIImmutableListTOfArray() + { + IImmutableList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadArrayOfIIImmutableListT() + { + IImmutableList[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (IImmutableList l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadPrimitiveIImmutableListT() + { + IImmutableList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[]")); + } + + [Fact] + public async Task ReadIImmutableStackTOfIImmutableStackT() + { + IImmutableStack> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 4; + + foreach (IImmutableStack l in result) + { + foreach (int i in l) + { + Assert.Equal(expected--, i); + } + } + } + + [Fact] + public async Task ReadIImmutableStackTOfArray() + { + IImmutableStack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 3; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + + expected = 1; + } + } + + [Fact] + public async Task ReadArrayOfIIImmutableStackT() + { + IImmutableStack[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 2; + + foreach (IImmutableStack l in result) + { + foreach (int i in l) + { + Assert.Equal(expected--, i); + } + + expected = 4; + } + } + + [Fact] + public async Task ReadPrimitiveIImmutableStackT() + { + IImmutableStack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 2; + + foreach (int i in result) + { + Assert.Equal(expected--, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[]")); + } + + [Fact] + public async Task ReadIImmutableQueueTOfIImmutableQueueT() + { + IImmutableQueue> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (IImmutableQueue l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadIImmutableQueueTOfArray() + { + IImmutableQueue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadArrayOfIImmutableQueueT() + { + IImmutableQueue[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (IImmutableQueue l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadPrimitiveIImmutableQueueT() + { + IImmutableQueue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[]")); + } + + [Fact] + public async Task ReadIImmutableSetTOfIImmutableSetT() + { + IImmutableSet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (IImmutableSet l in result) + { + foreach (int i in l) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadIImmutableSetTOfArray() + { + IImmutableSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadArrayOfIImmutableSetT() + { + IImmutableSet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (IImmutableSet l in result) + { + foreach (int i in l) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadPrimitiveIImmutableSetT() + { + IImmutableSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + List expected = new List { 1, 2 }; + + foreach (int i in result) + { + expected.Remove(i); + } + + Assert.Equal(0, expected.Count); + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"",""2""]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[]")); + } + + [Fact] + public async Task ReadImmutableHashSetTOfImmutableHashSetT() + { + ImmutableHashSet> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (ImmutableHashSet l in result) + { + foreach (int i in l) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadImmutableHashSetTOfArray() + { + ImmutableHashSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadArrayOfIImmutableHashSetT() + { + ImmutableHashSet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + List expected = new List { 1, 2, 3, 4 }; + + foreach (ImmutableHashSet l in result) + { + foreach (int i in l) + { + expected.Remove(i); + } + } + + Assert.Equal(0, expected.Count); + } + + [Fact] + public async Task ReadPrimitiveImmutableHashSetT() + { + ImmutableHashSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + List expected = new List { 1, 2 }; + + foreach (int i in result) + { + expected.Remove(i); + } + + Assert.Equal(0, expected.Count); + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadImmutableListTOfImmutableListT() + { + ImmutableList> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableList l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadImmutableListTOfArray() + { + ImmutableList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadArrayOfIImmutableListT() + { + ImmutableList[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableList l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadPrimitiveImmutableListT() + { + ImmutableList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadImmutableStackTOfImmutableStackT() + { + ImmutableStack> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 4; + + foreach (ImmutableStack l in result) + { + foreach (int i in l) + { + Assert.Equal(expected--, i); + } + } + } + + [Fact] + public async Task ReadImmutableStackTOfArray() + { + ImmutableStack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 3; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + + expected = 1; + } + } + + [Fact] + public async Task ReadArrayOfIImmutableStackT() + { + ImmutableStack[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 2; + + foreach (ImmutableStack l in result) + { + foreach (int i in l) + { + Assert.Equal(expected--, i); + } + + expected = 4; + } + } + + [Fact] + public async Task ReadPrimitiveImmutableStackT() + { + ImmutableStack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 2; + + foreach (int i in result) + { + Assert.Equal(expected--, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadImmutableQueueTOfImmutableQueueT() + { + ImmutableQueue> result = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableQueue l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadImmutableQueueTOfArray() + { + ImmutableQueue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (int[] arr in result) + { + foreach (int i in arr) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadArrayOfImmutableQueueT() + { + ImmutableQueue[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableQueue l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadPrimitiveImmutableQueueT() + { + ImmutableQueue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadArrayOfIImmutableSortedSetT() + { + ImmutableSortedSet[] result = await JsonSerializerWrapperForString.DeserializeWrapper[]>(@"[[1,2],[3,4]]"); + int expected = 1; + + foreach (ImmutableSortedSet l in result) + { + foreach (int i in l) + { + Assert.Equal(expected++, i); + } + } + } + + [Fact] + public async Task ReadPrimitiveImmutableSortedSetT() + { + ImmutableSortedSet result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[1,2]"); + int expected = 1; + + foreach (int i in result) + { + Assert.Equal(expected++, i); + } + + result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[]"); + Assert.Equal(0, result.Count()); + } + + [Fact] + public async Task ReadSimpleTestClass_ImmutableCollectionWrappers_Throws() + { + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithIImmutableDictionaryWrapper.s_json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithImmutableListWrapper.s_json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithImmutableStackWrapper.s_json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithImmutableQueueWrapper.s_json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithImmutableSetWrapper.s_json)); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs similarity index 64% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Write.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs index c994d45de7f..3f7295f7bb8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Immutable.Write.cs @@ -3,26 +3,27 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void WriteImmutableArrayOfImmutableArray() + public async Task WriteImmutableArrayOfImmutableArray() { ImmutableArray> input = ImmutableArray.CreateRange(new List>{ ImmutableArray.CreateRange(new List() { 1, 2 }), ImmutableArray.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteImmutableArrayOfArray() + public async Task WriteImmutableArrayOfArray() { ImmutableArray input = ImmutableArray.CreateRange(new List { @@ -30,44 +31,44 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfImmutableArray() + public async Task WriteArrayOfImmutableArray() { ImmutableArray[] input = new ImmutableArray[2]; input[0] = ImmutableArray.CreateRange(new List() { 1, 2 }); input[1] = ImmutableArray.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteSimpleImmutableArray() + public async Task WriteSimpleImmutableArray() { ImmutableArray input = ImmutableArray.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteIImmutableListTOfIImmutableListT() + public async Task WriteIImmutableListTOfIImmutableListT() { IImmutableList> input = ImmutableList.CreateRange(new List>{ ImmutableList.CreateRange(new List() { 1, 2 }), ImmutableList.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteIImmutableListTOfArray() + public async Task WriteIImmutableListTOfArray() { IImmutableList input = ImmutableList.CreateRange(new List { @@ -75,67 +76,67 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteSimpleClassWithImmutableArray() + public async Task WriteSimpleClassWithImmutableArray() { SimpleTestClassWithImmutableArray obj = new SimpleTestClassWithImmutableArray(); obj.Initialize(); - Assert.Equal(SimpleTestClassWithImmutableArray.s_json, JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestClassWithImmutableArray.s_json, await JsonSerializerWrapperForString.SerializeWrapper(obj)); } [Fact] - public static void WriteSimpleClassWithObjectImmutableArray() + public async Task WriteSimpleClassWithObjectImmutableArray() { SimpleTestClassWithObjectImmutableArray obj = new SimpleTestClassWithObjectImmutableArray(); obj.Initialize(); - Assert.Equal(SimpleTestClassWithObjectImmutableArray.s_json, JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestClassWithObjectImmutableArray.s_json, await JsonSerializerWrapperForString.SerializeWrapper(obj)); } [Fact] - public static void WriteArrayOfIImmutableListT() + public async Task WriteArrayOfIImmutableListT() { IImmutableList[] input = new IImmutableList[2]; input[0] = ImmutableList.CreateRange(new List() { 1, 2 }); input[1] = ImmutableList.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIImmutableListT() + public async Task WritePrimitiveIImmutableListT() { IImmutableList input = ImmutableList.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); StringIImmutableListWrapper input2 = new StringIImmutableListWrapper(new List { "1", "2" }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[""1"",""2""]", json); } [Fact] - public static void WriteIImmutableStackTOfIImmutableStackT() + public async Task WriteIImmutableStackTOfIImmutableStackT() { IImmutableStack> input = ImmutableStack.CreateRange(new List>{ ImmutableStack.CreateRange(new List() { 1, 2 }), ImmutableStack.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[4,3],[2,1]]", json); } [Fact] - public static void WriteIImmutableStackTOfArray() + public async Task WriteIImmutableStackTOfArray() { IImmutableStack input = ImmutableStack.CreateRange(new List { @@ -143,49 +144,49 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[3,4],[1,2]]", json); } [Fact] - public static void WriteArrayOfIImmutableStackT() + public async Task WriteArrayOfIImmutableStackT() { IImmutableStack[] input = new IImmutableStack[2]; input[0] = ImmutableStack.CreateRange(new List() { 1, 2 }); input[1] = ImmutableStack.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[2,1],[4,3]]", json); } [Fact] - public static void WritePrimitiveIImmutableStackT() + public async Task WritePrimitiveIImmutableStackT() { IImmutableStack input = ImmutableStack.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[2,1]", json); StringIImmutableStackWrapper input2 = new StringIImmutableStackWrapper(new List { "1", "2" }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[""2"",""1""]", json); } [Fact] - public static void WriteIImmutableQueueTOfIImmutableQueueT() + public async Task WriteIImmutableQueueTOfIImmutableQueueT() { IImmutableQueue> input = ImmutableQueue.CreateRange(new List>{ ImmutableQueue.CreateRange(new List() { 1, 2 }), ImmutableQueue.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteIImmutableQueueTOfArray() + public async Task WriteIImmutableQueueTOfArray() { IImmutableQueue input = ImmutableQueue.CreateRange(new List { @@ -193,50 +194,50 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIImmutableQueueT() + public async Task WriteArrayOfIImmutableQueueT() { IImmutableQueue[] input = new IImmutableQueue[2]; input[0] = ImmutableQueue.CreateRange(new List() { 1, 2 }); input[1] = ImmutableQueue.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIImmutableQueueT() + public async Task WritePrimitiveIImmutableQueueT() { IImmutableQueue input = ImmutableQueue.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); StringIImmutableQueueWrapper input2 = new StringIImmutableQueueWrapper(new List { "1", "2" }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal(@"[""1"",""2""]", json); } [Fact] - public static void WriteIImmutableSetTOfIImmutableSetT() + public async Task WriteIImmutableSetTOfIImmutableSetT() { IImmutableSet> input = ImmutableHashSet.CreateRange(new List>{ ImmutableHashSet.CreateRange(new List() { 1, 2 }), ImmutableHashSet.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] - public static void WriteIImmutableSetTOfArray() + public async Task WriteIImmutableSetTOfArray() { IImmutableSet input = ImmutableHashSet.CreateRange(new List { @@ -244,51 +245,51 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] - public static void WriteArrayOfIImmutableSetT() + public async Task WriteArrayOfIImmutableSetT() { IImmutableSet[] input = new IImmutableSet[2]; input[0] = ImmutableHashSet.CreateRange(new List() { 1, 2 }); input[1] = ImmutableHashSet.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIImmutableSetT() + public async Task WritePrimitiveIImmutableSetT() { IImmutableSet input = ImmutableHashSet.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); StringIImmutableSetWrapper input2 = new StringIImmutableSetWrapper(new List { "1", "2" }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.True(json == @"[""1"",""2""]" || json == @"[""2"",""1""]"); } [Fact] - public static void WriteImmutableHashSetTOfImmutableHashSetT() + public async Task WriteImmutableHashSetTOfImmutableHashSetT() { ImmutableHashSet> input = ImmutableHashSet.CreateRange(new List>{ ImmutableHashSet.CreateRange(new List() { 1, 2 }), ImmutableHashSet.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] - public static void WriteImmutableHashSetTOfArray() + public async Task WriteImmutableHashSetTOfArray() { ImmutableHashSet input = ImmutableHashSet.CreateRange(new List { @@ -296,45 +297,45 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Contains("[1,2]", json); Assert.Contains("[3,4]", json); } [Fact] - public static void WriteArrayOfImmutableHashSetT() + public async Task WriteArrayOfImmutableHashSetT() { ImmutableHashSet[] input = new ImmutableHashSet[2]; input[0] = ImmutableHashSet.CreateRange(new List() { 1, 2 }); input[1] = ImmutableHashSet.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveImmutableHashSetT() + public async Task WritePrimitiveImmutableHashSetT() { ImmutableHashSet input = ImmutableHashSet.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteImmutableListTOfImmutableListT() + public async Task WriteImmutableListTOfImmutableListT() { ImmutableList> input = ImmutableList.CreateRange(new List>{ ImmutableList.CreateRange(new List() { 1, 2 }), ImmutableList.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteImmutableListTOfArray() + public async Task WriteImmutableListTOfArray() { ImmutableList input = ImmutableList.CreateRange(new List { @@ -342,44 +343,44 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfImmutableListT() + public async Task WriteArrayOfImmutableListT() { ImmutableList[] input = new ImmutableList[2]; input[0] = ImmutableList.CreateRange(new List() { 1, 2 }); input[1] = ImmutableList.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveImmutableListT() + public async Task WritePrimitiveImmutableListT() { ImmutableList input = ImmutableList.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteImmutableStackTOfImmutableStackT() + public async Task WriteImmutableStackTOfImmutableStackT() { ImmutableStack> input = ImmutableStack.CreateRange(new List>{ ImmutableStack.CreateRange(new List() { 1, 2 }), ImmutableStack.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[4,3],[2,1]]", json); } [Fact] - public static void WriteImmutableStackTOfArray() + public async Task WriteImmutableStackTOfArray() { ImmutableStack input = ImmutableStack.CreateRange(new List { @@ -387,44 +388,44 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[3,4],[1,2]]", json); } [Fact] - public static void WriteArrayOfImmutableStackT() + public async Task WriteArrayOfImmutableStackT() { ImmutableStack[] input = new ImmutableStack[2]; input[0] = ImmutableStack.CreateRange(new List() { 1, 2 }); input[1] = ImmutableStack.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[2,1],[4,3]]", json); } [Fact] - public static void WritePrimitiveImmutableStackT() + public async Task WritePrimitiveImmutableStackT() { ImmutableStack input = ImmutableStack.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[2,1]", json); } [Fact] - public static void WriteImmutableQueueTOfImmutableQueueT() + public async Task WriteImmutableQueueTOfImmutableQueueT() { ImmutableQueue> input = ImmutableQueue.CreateRange(new List>{ ImmutableQueue.CreateRange(new List() { 1, 2 }), ImmutableQueue.CreateRange(new List() { 3, 4 }) }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteImmutableQueueTOfArray() + public async Task WriteImmutableQueueTOfArray() { ImmutableQueue input = ImmutableQueue.CreateRange(new List { @@ -432,52 +433,52 @@ namespace System.Text.Json.Serialization.Tests new int[] { 3, 4 } }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfImmutableQueueT() + public async Task WriteArrayOfImmutableQueueT() { ImmutableQueue[] input = new ImmutableQueue[2]; input[0] = ImmutableQueue.CreateRange(new List() { 1, 2 }); input[1] = ImmutableQueue.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveImmutableQueueT() + public async Task WritePrimitiveImmutableQueueT() { ImmutableQueue input = ImmutableQueue.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteArrayOfImmutableSortedSetT() + public async Task WriteArrayOfImmutableSortedSetT() { ImmutableSortedSet[] input = new ImmutableSortedSet[2]; input[0] = ImmutableSortedSet.CreateRange(new List() { 1, 2 }); input[1] = ImmutableSortedSet.CreateRange(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveImmutableSortedSetT() + public async Task WritePrimitiveImmutableSortedSetT() { ImmutableSortedSet input = ImmutableSortedSet.CreateRange(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteImmutableCollectionWrappers() + public async Task WriteImmutableCollectionWrappers() { SimpleTestClassWithIImmutableDictionaryWrapper obj1 = new SimpleTestClassWithIImmutableDictionaryWrapper(); SimpleTestClassWithImmutableListWrapper obj2 = new SimpleTestClassWithImmutableListWrapper(); @@ -491,20 +492,20 @@ namespace System.Text.Json.Serialization.Tests obj4.Initialize(); obj5.Initialize(); - Assert.Equal(SimpleTestClassWithIImmutableDictionaryWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); - Assert.Equal(SimpleTestClassWithIImmutableDictionaryWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); + Assert.Equal(SimpleTestClassWithIImmutableDictionaryWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); + Assert.Equal(SimpleTestClassWithIImmutableDictionaryWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); - Assert.Equal(SimpleTestClassWithImmutableListWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); - Assert.Equal(SimpleTestClassWithImmutableListWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); + Assert.Equal(SimpleTestClassWithImmutableListWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); + Assert.Equal(SimpleTestClassWithImmutableListWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); - Assert.Equal(SimpleTestClassWithImmutableStackWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); - Assert.Equal(SimpleTestClassWithImmutableStackWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); + Assert.Equal(SimpleTestClassWithImmutableStackWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); + Assert.Equal(SimpleTestClassWithImmutableStackWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); - Assert.Equal(SimpleTestClassWithImmutableQueueWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); - Assert.Equal(SimpleTestClassWithImmutableQueueWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); + Assert.Equal(SimpleTestClassWithImmutableQueueWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); + Assert.Equal(SimpleTestClassWithImmutableQueueWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); - Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); - Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); + Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); + Assert.Equal(SimpleTestClassWithImmutableSetWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.KeyValuePair.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs similarity index 68% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.KeyValuePair.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs index c016b5caaf2..2fec3ce9738 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.KeyValuePair.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs @@ -3,29 +3,31 @@ using System.Collections.Generic; using System.Text.Encodings.Web; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests +#if !BUILDING_SOURCE_GENERATOR_TESTS + public abstract partial class CollectionTests { [Fact] - public static void ReadSimpleKeyValuePairFail() + public async Task ReadSimpleKeyValuePairFail() { // Invalid form: no Value - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""Key"": 123}")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"": 123}")); // Invalid form: extra property - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""Key"": ""Key"", ""Value"": 123, ""Value2"": 456}")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"": ""Key"", ""Value"": 123, ""Value2"": 456}")); // Invalid form: does not contain both Key and Value properties - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""Key"": ""Key"", ""Val"": 123")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"": ""Key"", ""Val"": 123")); } [Fact] - public static void ReadListOfKeyValuePair() + public async Task ReadListOfKeyValuePair() { - List> input = JsonSerializer.Deserialize>>(@"[{""Key"": ""123"", ""Value"": 123},{""Key"": ""456"", ""Value"": 456}]"); + List> input = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"[{""Key"": ""123"", ""Value"": 123},{""Key"": ""456"", ""Value"": 456}]"); Assert.Equal(2, input.Count); Assert.Equal("123", input[0].Key); @@ -35,9 +37,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadKeyValuePairOfList() + public async Task ReadKeyValuePairOfList() { - KeyValuePair> input = JsonSerializer.Deserialize>>(@"{""Key"":""Key"", ""Value"":[1, 2, 3]}"); + KeyValuePair> input = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"{""Key"":""Key"", ""Value"":[1, 2, 3]}"); Assert.Equal("Key", input.Key); Assert.Equal(3, input.Value.Count); @@ -51,9 +53,9 @@ namespace System.Text.Json.Serialization.Tests [InlineData(@"{""Key"":""Key"", ""Value"":{""Value"":2, ""Key"":1}}")] [InlineData(@"{""Value"":{""Key"":1, ""Value"":2}, ""Key"":""Key""}")] [InlineData(@"{""Value"":{""Value"":2, ""Key"":1}, ""Key"":""Key""}")] - public static void ReadKeyValuePairOfKeyValuePair(string json) + public async Task ReadKeyValuePairOfKeyValuePair(string json) { - KeyValuePair> input = JsonSerializer.Deserialize>>(json); + KeyValuePair> input = await JsonSerializerWrapperForString.DeserializeWrapper>>(json); Assert.Equal("Key", input.Key); Assert.Equal(1, input.Value.Key); @@ -61,42 +63,42 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadKeyValuePairWithNullValues() + public async Task ReadKeyValuePairWithNullValues() { { - KeyValuePair kvp = JsonSerializer.Deserialize>(@"{""Key"":""key"",""Value"":null}"); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"":""key"",""Value"":null}"); Assert.Equal("key", kvp.Key); Assert.Null(kvp.Value); } { - KeyValuePair kvp = JsonSerializer.Deserialize>(@"{""Key"":""key"",""Value"":null}"); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"":""key"",""Value"":null}"); Assert.Equal("key", kvp.Key); Assert.Null(kvp.Value); } { - KeyValuePair kvp = JsonSerializer.Deserialize>(@"{""Key"":""key"",""Value"":null}"); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"":""key"",""Value"":null}"); Assert.Equal("key", kvp.Key); Assert.Null(kvp.Value); } { - KeyValuePair> kvp = JsonSerializer.Deserialize>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); + KeyValuePair> kvp = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); Assert.Equal("key", kvp.Key); Assert.Equal("key", kvp.Value.Key); Assert.Null(kvp.Value.Value); } { - KeyValuePair> kvp = JsonSerializer.Deserialize>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); + KeyValuePair> kvp = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); Assert.Equal("key", kvp.Key); Assert.Equal("key", kvp.Value.Key); Assert.Null(kvp.Value.Value); } { - KeyValuePair> kvp = JsonSerializer.Deserialize>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); + KeyValuePair> kvp = await JsonSerializerWrapperForString.DeserializeWrapper>>(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}"); Assert.Equal("key", kvp.Key); Assert.Equal("key", kvp.Value.Key); Assert.Null(kvp.Value.Value); @@ -104,7 +106,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadClassWithNullKeyValuePairValues() + public async Task ReadClassWithNullKeyValuePairValues() { string json = @"{" + @@ -142,7 +144,7 @@ namespace System.Text.Json.Serialization.Tests @"}" + @"}" + @"}"; - SimpleClassWithKeyValuePairs obj = JsonSerializer.Deserialize(json); + SimpleClassWithKeyValuePairs obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("key", obj.KvpWStrVal.Key); Assert.Equal("key", obj.KvpWObjVal.Key); @@ -163,24 +165,24 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void Kvp_NullKeyIsFine() + public async Task Kvp_NullKeyIsFine() { - KeyValuePair kvp = JsonSerializer.Deserialize>(@"{""Key"":null,""Value"":null}"); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""Key"":null,""Value"":null}"); Assert.Null(kvp.Key); Assert.Null(kvp.Value); } [Fact] - public static void WritePrimitiveKeyValuePair() + public async Task WritePrimitiveKeyValuePair() { KeyValuePair input = new KeyValuePair("Key", 123); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal(@"{""Key"":""Key"",""Value"":123}", json); } [Fact] - public static void WriteListOfKeyValuePair() + public async Task WriteListOfKeyValuePair() { List> input = new List> { @@ -188,65 +190,65 @@ namespace System.Text.Json.Serialization.Tests new KeyValuePair("456", 456) }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal(@"[{""Key"":""123"",""Value"":123},{""Key"":""456"",""Value"":456}]", json); } [Fact] - public static void WriteKeyValuePairOfList() + public async Task WriteKeyValuePairOfList() { KeyValuePair> input = new KeyValuePair>("Key", new List { 1, 2, 3 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal(@"{""Key"":""Key"",""Value"":[1,2,3]}", json); } [Fact] - public static void WriteKeyValuePairOfKeyValuePair() + public async Task WriteKeyValuePairOfKeyValuePair() { KeyValuePair> input = new KeyValuePair>( "Key", new KeyValuePair("Key", 1)); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal(@"{""Key"":""Key"",""Value"":{""Key"":""Key"",""Value"":1}}", json); } [Fact] - public static void WriteKeyValuePairWithNullValues() + public async Task WriteKeyValuePairWithNullValues() { { KeyValuePair kvp = new KeyValuePair("key", null); - Assert.Equal(@"{""Key"":""key"",""Value"":null}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":null}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } { KeyValuePair kvp = new KeyValuePair("key", null); - Assert.Equal(@"{""Key"":""key"",""Value"":null}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":null}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } { KeyValuePair kvp = new KeyValuePair("key", null); - Assert.Equal(@"{""Key"":""key"",""Value"":null}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":null}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } { KeyValuePair> kvp = new KeyValuePair>("key", new KeyValuePair("key", null)); - Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } { KeyValuePair> kvp = new KeyValuePair>("key", new KeyValuePair("key", null)); - Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } { KeyValuePair> kvp = new KeyValuePair>("key", new KeyValuePair("key", null)); - Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", JsonSerializer.Serialize(kvp)); + Assert.Equal(@"{""Key"":""key"",""Value"":{""Key"":""key"",""Value"":null}}", await JsonSerializerWrapperForString.SerializeWrapper(kvp)); } } [Fact] - public static void WriteClassWithNullKeyValuePairValues_NullWrittenAsEmptyObject() + public async Task WriteClassWithNullKeyValuePairValues_NullWrittenAsEmptyObject() { var value = new SimpleClassWithKeyValuePairs() { @@ -258,10 +260,10 @@ namespace System.Text.Json.Serialization.Tests KvpWClassKvpVal = new KeyValuePair>("key", new KeyValuePair("key", null)), }; - string result = JsonSerializer.Serialize(value); + string result = await JsonSerializerWrapperForString.SerializeWrapper(value); // Roundtrip to ensure serialize was correct. - value = JsonSerializer.Deserialize(result); + value = await JsonSerializerWrapperForString.DeserializeWrapper(result); Assert.Equal("key", value.KvpWStrVal.Key); Assert.Equal("key", value.KvpWObjVal.Key); Assert.Equal("key", value.KvpWClassVal.Key); @@ -281,7 +283,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void HonorNamingPolicy() + public async Task HonorNamingPolicy() { var kvp = new KeyValuePair("Hello, World!", 1); @@ -290,22 +292,22 @@ namespace System.Text.Json.Serialization.Tests PropertyNamingPolicy = new LeadingUnderscorePolicy() }; - string serialized = JsonSerializer.Serialize(kvp, options); + string serialized = await JsonSerializerWrapperForString.SerializeWrapper(kvp, options); // We know serializer writes the key first. Assert.Equal(@"{""_Key"":""Hello, World!"",""_Value"":1}", serialized); - kvp = JsonSerializer.Deserialize>(serialized, options); + kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(serialized, options); Assert.Equal("Hello, World!", kvp.Key); Assert.Equal(1, kvp.Value); } [Fact] - public static void HonorNamingPolicy_CaseInsensitive() + public async Task HonorNamingPolicy_CaseInsensitive() { const string json = @"{""key"":""Hello, World!"",""value"":1}"; // Baseline - with case-sensitive matching, the payload doesn't have mapping properties. - Assert.Throws(() => JsonSerializer.Deserialize>(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json)); // Test - with case-insensitivity on, we have property matches. var options = new JsonSerializerOptions @@ -313,13 +315,13 @@ namespace System.Text.Json.Serialization.Tests PropertyNameCaseInsensitive = true }; - KeyValuePair kvp = JsonSerializer.Deserialize>(json, options); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(json, options); Assert.Equal("Hello, World!", kvp.Key); Assert.Equal(1, kvp.Value); } [Fact] - public static void HonorCLRProperties() + public async Task HonorCLRProperties() { var options = new JsonSerializerOptions { @@ -330,13 +332,13 @@ namespace System.Text.Json.Serialization.Tests // "Key" and "Value" are special cased to accomodate content serialized with previous // versions of the serializer (.NET Core 3.x/System.Text.Json 4.7.x). string json = @"{""Key"":""Hello, World!"",""Value"":1}"; - KeyValuePair kvp = JsonSerializer.Deserialize>(json, options); + KeyValuePair kvp = await JsonSerializerWrapperForString.DeserializeWrapper>(json, options); Assert.Equal("Hello, World!", kvp.Key); Assert.Equal(1, kvp.Value); // "Key" and "Value" matching is case sensitive. json = @"{""key"":""Hello, World!"",""value"":1}"; - Assert.Throws(() => JsonSerializer.Deserialize>(json, options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json, options)); // "Key" and "Value" matching is case sensitive, even when case insensitivity is on. // Case sensitivity only applies to the result of converting the CLR property names @@ -347,7 +349,7 @@ namespace System.Text.Json.Serialization.Tests PropertyNameCaseInsensitive = true }; - Assert.Throws(() => JsonSerializer.Deserialize>(json, options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json, options)); } private class LeadingUnderscorePolicy : JsonNamingPolicy @@ -356,7 +358,7 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void HonorCustomEncoder() + public async Task HonorCustomEncoder() { var kvp = new KeyValuePair(1, 2); @@ -368,7 +370,7 @@ namespace System.Text.Json.Serialization.Tests PropertyNamingPolicy = namingPolicy, }; - Assert.Equal(@"{""Key\u003C"":1,""Value\u003C"":2}", JsonSerializer.Serialize(kvp, options)); + Assert.Equal(@"{""Key\u003C"":1,""Value\u003C"":2}", await JsonSerializerWrapperForString.SerializeWrapper(kvp, options)); // Test - serializer honors custom encoder. options = new JsonSerializerOptions @@ -377,7 +379,7 @@ namespace System.Text.Json.Serialization.Tests Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - Assert.Equal(@"{""Key<"":1,""Value<"":2}", JsonSerializer.Serialize(kvp, options)); + Assert.Equal(@"{""Key<"":1,""Value<"":2}", await JsonSerializerWrapperForString.SerializeWrapper(kvp, options)); } private class TrailingAngleBracketPolicy : JsonNamingPolicy @@ -388,18 +390,18 @@ namespace System.Text.Json.Serialization.Tests [Theory] [InlineData(typeof(KeyNameNullPolicy), "Key")] [InlineData(typeof(ValueNameNullPolicy), "Value")] - public static void InvalidPropertyNameFail(Type policyType, string offendingProperty) + public async Task InvalidPropertyNameFail(Type policyType, string offendingProperty) { var options = new JsonSerializerOptions { PropertyNamingPolicy = (JsonNamingPolicy)Activator.CreateInstance(policyType) }; - InvalidOperationException ex = Assert.Throws(() => JsonSerializer.Deserialize>("", options)); + InvalidOperationException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>("", options)); string exAsStr = ex.ToString(); Assert.Contains(offendingProperty, exAsStr); - Assert.Throws(() => JsonSerializer.Serialize(new KeyValuePair("", ""), options)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.SerializeWrapper(new KeyValuePair("", ""), options)); } private class KeyNameNullPolicy : JsonNamingPolicy @@ -432,9 +434,9 @@ namespace System.Text.Json.Serialization.Tests [InlineData(@"{""Value"":1,""Value"":1}")] [InlineData(@"{""Value"":1,null:1}")] [InlineData(@"{""Value"":1,""Value"":2}")] - public static void InvalidJsonFail(string json) + public async Task InvalidJsonFail(string json) { - Assert.Throws(() => JsonSerializer.Deserialize>(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json)); } [Theory] @@ -445,34 +447,35 @@ namespace System.Text.Json.Serialization.Tests [InlineData(@"{""Extra"":3,""Key"":1,""Value"":2}", "$.Extra")] [InlineData(@"{""Key"":1,""Extra"":3,""Value"":2}", "$.Extra")] [InlineData(@"{""Key"":1,""Value"":2,""Extra"":3}", "$.Extra")] - public static void JsonPathIsAccurate(string json, string expectedPath) + public async Task JsonPathIsAccurate(string json, string expectedPath) { - JsonException ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + JsonException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json)); Assert.Contains(expectedPath, ex.ToString()); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json)); Assert.Contains(expectedPath, ex.ToString()); } [Theory] [InlineData(@"{""kEy"":""1"",""vAlUe"":2}", "$.kEy")] [InlineData(@"{""kEy"":1,""vAlUe"":""2""}", "$.vAlUe")] - public static void JsonPathIsAccurate_CaseInsensitive(string json, string expectedPath) + public async Task JsonPathIsAccurate_CaseInsensitive(string json, string expectedPath) { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - JsonException ex = Assert.Throws(() => JsonSerializer.Deserialize>(json, options)); + JsonException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json, options)); Assert.Contains(expectedPath, ex.ToString()); } [Theory] [InlineData(@"{""_Key"":""1"",""_Value"":2}", "$._Key")] [InlineData(@"{""_Key"":1,""_Value"":""2""}", "$._Value")] - public static void JsonPathIsAccurate_PropertyNamingPolicy(string json, string expectedPath) + public async Task JsonPathIsAccurate_PropertyNamingPolicy(string json, string expectedPath) { var options = new JsonSerializerOptions { PropertyNamingPolicy = new LeadingUnderscorePolicy() }; - JsonException ex = Assert.Throws(() => JsonSerializer.Deserialize>(json, options)); + JsonException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(json, options)); Assert.Contains(expectedPath, ex.ToString()); } } +#endif } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Read.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Read.cs similarity index 59% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Read.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Read.cs index 1f32ab0b80f..4187cefde1a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Read.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Read.cs @@ -3,16 +3,17 @@ using System.Collections; using System.Collections.Generic; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void ReadGenericIEnumerableOfIEnumerable() + public async Task ReadGenericIEnumerableOfIEnumerable() { - IEnumerable result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IEnumerable ie in result) @@ -24,13 +25,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[1,2],[3,4]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]")); } [Fact] - public static void ReadIEnumerableOfArray() + public async Task ReadIEnumerableOfArray() { - IEnumerable result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (JsonElement arr in result) @@ -43,9 +44,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfIEnumerable() + public async Task ReadArrayOfIEnumerable() { - IEnumerable[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IEnumerable[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (IEnumerable arr in result) @@ -58,9 +59,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadPrimitiveIEnumerable() + public async Task ReadPrimitiveIEnumerable() { - IEnumerable result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + IEnumerable result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 1; foreach (JsonElement i in result) @@ -68,7 +69,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -80,9 +81,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericIListOfIList() + public async Task ReadGenericIListOfIList() { - IList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (IList list in result) @@ -93,7 +94,7 @@ namespace System.Text.Json.Serialization.Tests } } - GenericIListWrapper result2 = JsonSerializer.Deserialize>(@"[[1,2],[3,4]]"); + GenericIListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); expected = 1; foreach (WrapperForIList list in result2) @@ -106,9 +107,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadIListOfArray() + public async Task ReadIListOfArray() { - IList result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (JsonElement arr in result) @@ -121,9 +122,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfIList() + public async Task ReadArrayOfIList() { - IList[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + IList[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (IList arr in result) @@ -136,20 +137,20 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadStructIList() + public async Task ReadStructIList() { string json = @"[""a"",20]"; - var wrapper = JsonSerializer.Deserialize(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal(2, wrapper.Count); Assert.Equal("a", ((JsonElement)wrapper[0]).GetString()); Assert.Equal(20, ((JsonElement)wrapper[1]).GetInt32()); } [Fact] - public static void ReadNullableStructIList() + public async Task ReadNullableStructIList() { string json = @"[""a"",20]"; - var wrapper = JsonSerializer.Deserialize(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.True(wrapper.HasValue); Assert.Equal(2, wrapper.Value.Count); Assert.Equal("a", ((JsonElement)wrapper.Value[0]).GetString()); @@ -157,54 +158,54 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadNullableStructIListWithNullJson() + public async Task ReadNullableStructIListWithNullJson() { - var wrapper = JsonSerializer.Deserialize("null"); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper("null"); Assert.False(wrapper.HasValue); } [Fact] - public static void ReadClassWithStructIListWrapper_NullJson_Throws() + public async Task ReadClassWithStructIListWrapper_NullJson_Throws() { string json = @"{ ""List"" : null }"; - Assert.Throws(() => JsonSerializer.Deserialize(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); } [Fact] - public static void ReadStructIDictionary() + public async Task ReadStructIDictionary() { string json = @"{""Key"":""Value""}"; - var wrapper = JsonSerializer.Deserialize(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.Equal("Value", wrapper["Key"].ToString()); } [Fact] - public static void ReadNullableStructIDictionary() + public async Task ReadNullableStructIDictionary() { string json = @"{""Key"":""Value""}"; - var wrapper = JsonSerializer.Deserialize(json); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.True(wrapper.HasValue); Assert.Equal("Value", wrapper.Value["Key"].ToString()); } [Fact] - public static void ReadNullableStructIDictionaryWithNullJson() + public async Task ReadNullableStructIDictionaryWithNullJson() { - var wrapper = JsonSerializer.Deserialize("null"); + var wrapper = await JsonSerializerWrapperForString.DeserializeWrapper("null"); Assert.False(wrapper.HasValue); } [Fact] - public static void ReadClassWithStructIDictionaryWrapper_NullJson_Throws() + public async Task ReadClassWithStructIDictionaryWrapper_NullJson_Throws() { string json = @"{ ""Dictionary"" : null }"; - Assert.Throws(() => JsonSerializer.Deserialize(json)); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json)); } [Fact] - public static void ReadPrimitiveIList() + public async Task ReadPrimitiveIList() { - IList result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + IList result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 1; foreach (JsonElement i in result) @@ -212,7 +213,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -222,7 +223,7 @@ namespace System.Text.Json.Serialization.Tests } Assert.Equal(0, count); - WrapperForIList result2 = JsonSerializer.Deserialize(@"[1,2]"); + WrapperForIList result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); expected = 1; foreach (JsonElement i in result2) @@ -232,9 +233,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericICollectionOfICollection() + public async Task ReadGenericICollectionOfICollection() { - ICollection result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (ICollection ie in result) @@ -246,13 +247,13 @@ namespace System.Text.Json.Serialization.Tests } // No way to populate this collection. - Assert.Throws(() => JsonSerializer.Deserialize>(@"[[1,2],[3,4]]")); + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]")); } [Fact] - public static void ReadICollectionOfArray() + public async Task ReadICollectionOfArray() { - ICollection result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (JsonElement arr in result) @@ -265,9 +266,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfICollection() + public async Task ReadArrayOfICollection() { - ICollection[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ICollection[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (ICollection arr in result) @@ -280,9 +281,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadPrimitiveICollection() + public async Task ReadPrimitiveICollection() { - ICollection result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + ICollection result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 1; foreach (JsonElement i in result) @@ -290,7 +291,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -302,9 +303,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericStackOfStack() + public async Task ReadGenericStackOfStack() { - Stack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 4; foreach (Stack stack in result) @@ -317,9 +318,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadStackOfArray() + public async Task ReadStackOfArray() { - Stack result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 3; foreach (JsonElement arr in result) @@ -333,9 +334,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfStack() + public async Task ReadArrayOfStack() { - Stack[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Stack[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 2; foreach (Stack arr in result) @@ -349,9 +350,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadPrimitiveStack() + public async Task ReadPrimitiveStack() { - Stack result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + Stack result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 2; foreach (JsonElement i in result) @@ -359,7 +360,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected--, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -379,9 +380,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadGenericQueueOfQueue() + public async Task ReadGenericQueueOfQueue() { - Queue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue result = await JsonSerializerWrapperForString.DeserializeWrapper>(@"[[1,2],[3,4]]"); int expected = 1; foreach (Queue ie in result) @@ -394,9 +395,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadQueueOfArray() + public async Task ReadQueueOfArray() { - Queue result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (JsonElement arr in result) @@ -409,9 +410,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfQueue() + public async Task ReadArrayOfQueue() { - Queue[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + Queue[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (Queue arr in result) @@ -424,9 +425,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadPrimitiveQueue() + public async Task ReadPrimitiveQueue() { - Queue result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + Queue result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 1; foreach (JsonElement i in result) @@ -434,7 +435,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -444,7 +445,7 @@ namespace System.Text.Json.Serialization.Tests } Assert.Equal(0, count); - QueueWrapper wrapper = JsonSerializer.Deserialize(@"[1,2]"); + QueueWrapper wrapper = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); expected = 1; foreach (JsonElement i in wrapper) @@ -454,9 +455,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayListOfArray() + public async Task ReadArrayListOfArray() { - ArrayList result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ArrayList result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (JsonElement arr in result) @@ -467,7 +468,7 @@ namespace System.Text.Json.Serialization.Tests } } - ArrayListWrapper result2 = JsonSerializer.Deserialize(@"[[1,2],[3,4]]"); + ArrayListWrapper result2 = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); expected = 1; foreach (JsonElement arr in result2) @@ -480,9 +481,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadArrayOfArrayList() + public async Task ReadArrayOfArrayList() { - ArrayList[] result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); + ArrayList[] result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[[1,2],[3,4]]"); int expected = 1; foreach (ArrayList arr in result) @@ -495,9 +496,9 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadPrimitiveArrayList() + public async Task ReadPrimitiveArrayList() { - ArrayList result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[1,2]")); + ArrayList result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[1,2]"); int expected = 1; foreach (JsonElement i in result) @@ -505,7 +506,7 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(expected++, i.GetInt32()); } - result = JsonSerializer.Deserialize(Encoding.UTF8.GetBytes(@"[]")); + result = await JsonSerializerWrapperForString.DeserializeWrapper(@"[]"); int count = 0; IEnumerator e = result.GetEnumerator(); @@ -517,24 +518,24 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void ReadSimpleTestClass_NonGenericCollectionWrappers() + public async Task ReadSimpleTestClass_NonGenericCollectionWrappers() { - SimpleTestClassWithNonGenericCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestClassWithNonGenericCollectionWrappers.s_json); + SimpleTestClassWithNonGenericCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithNonGenericCollectionWrappers.s_json); obj.Verify(); } [Fact] - public static void ReadSimpleTestClass_StructCollectionWrappers() + public async Task ReadSimpleTestClass_StructCollectionWrappers() { - SimpleTestClassWithStructCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestClassWithStructCollectionWrappers.s_json); + SimpleTestClassWithStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestClassWithStructCollectionWrappers.s_json); obj.Verify(); } [Fact] - public static void ReadSimpleTestStruct_NullableStructCollectionWrappers() + public async Task ReadSimpleTestStruct_NullableStructCollectionWrappers() { { - SimpleTestStructWithNullableStructCollectionWrappers obj = JsonSerializer.Deserialize(SimpleTestStructWithNullableStructCollectionWrappers.s_json); + SimpleTestStructWithNullableStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(SimpleTestStructWithNullableStructCollectionWrappers.s_json); obj.Verify(); } @@ -545,7 +546,7 @@ namespace System.Text.Json.Serialization.Tests @"""Dictionary"" : null" + @"}"; - SimpleTestStructWithNullableStructCollectionWrappers obj = JsonSerializer.Deserialize(json); + SimpleTestStructWithNullableStructCollectionWrappers obj = await JsonSerializerWrapperForString.DeserializeWrapper(json); Assert.False(obj.List.HasValue); Assert.False(obj.Dictionary.HasValue); } @@ -553,9 +554,9 @@ namespace System.Text.Json.Serialization.Tests [Theory] [MemberData(nameof(ReadSimpleTestClass_NonGenericWrappers_NoAddMethod))] - public static void ReadSimpleTestClass_NonGenericWrappers_NoAddMethod_Throws(Type type, string json, Type exceptionMessageType) + public async Task ReadSimpleTestClass_NonGenericWrappers_NoAddMethod_Throws(Type type, string json, Type exceptionMessageType) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); Assert.Contains(exceptionMessageType.ToString(), ex.Message); } @@ -587,9 +588,9 @@ namespace System.Text.Json.Serialization.Tests [InlineData(typeof(WrapperForIListInternalConstructor), @"[""1""]")] [InlineData(typeof(WrapperForIDictionaryPrivateConstructor), @"{""Key"":""Value""}")] [InlineData(typeof(WrapperForIDictionaryInternalConstructor), @"{""Key"":""Value""}")] - public static void Read_NonGeneric_NoPublicConstructor_Throws(Type type, string json) + public async Task Read_NonGeneric_NoPublicConstructor_Throws(Type type, string json) { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); + NotSupportedException ex = await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(json, type)); Assert.Contains(type.ToString(), ex.Message); } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Write.cs similarity index 64% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Write.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Write.cs index f626437f4ba..4c4fd353047 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.NonGeneric.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.NonGeneric.Write.cs @@ -3,14 +3,15 @@ using System.Collections; using System.Collections.Generic; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void WriteIEnumerableOfIEnumerable() + public async Task WriteIEnumerableOfIEnumerable() { IEnumerable input = new List> { @@ -18,7 +19,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); WrapperForIEnumerable input2 = new WrapperForIEnumerable(new List @@ -27,12 +28,12 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 }, }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteGenericIEnumerableOfIEnumerable() + public async Task WriteGenericIEnumerableOfIEnumerable() { IEnumerable input = new List { @@ -40,46 +41,46 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIEnumerable() + public async Task WriteArrayOfIEnumerable() { IEnumerable[] input = new IEnumerable[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIEnumerable() + public async Task WritePrimitiveIEnumerable() { IEnumerable input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteStructWrapperForIList() + public async Task WriteStructWrapperForIList() { { StructWrapperForIList obj = new StructWrapperForIList() { 1, "Hello" }; - Assert.Equal(@"[1,""Hello""]", JsonSerializer.Serialize(obj)); + Assert.Equal(@"[1,""Hello""]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { StructWrapperForIList obj = default; - Assert.Equal("[]", JsonSerializer.Serialize(obj)); + Assert.Equal("[]", await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteIListOfIList() + public async Task WriteIListOfIList() { IList input = new List { @@ -87,7 +88,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); WrapperForIList input2 = new WrapperForIList @@ -96,12 +97,12 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 }, }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteIListGenericOfIList() + public async Task WriteIListGenericOfIList() { IList input = new List { @@ -109,32 +110,32 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfIList() + public async Task WriteArrayOfIList() { IList[] input = new IList[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveIList() + public async Task WritePrimitiveIList() { IList input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteICollectionOfICollection() + public async Task WriteICollectionOfICollection() { ICollection input = new List { @@ -142,12 +143,12 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteGenericICollectionOfICollection() + public async Task WriteGenericICollectionOfICollection() { ICollection input = new List { @@ -155,7 +156,7 @@ namespace System.Text.Json.Serialization.Tests new List() { 3, 4 } }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericICollectionWrapper input2 = new GenericICollectionWrapper @@ -164,130 +165,130 @@ namespace System.Text.Json.Serialization.Tests new WrapperForICollection(new List { 3, 4 }), }; - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfICollection() + public async Task WriteArrayOfICollection() { ICollection[] input = new List[2]; input[0] = new List() { 1, 2 }; input[1] = new List() { 3, 4 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveICollection() + public async Task WritePrimitiveICollection() { ICollection input = new List { 1, 2 }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteStackOfStack() + public async Task WriteStackOfStack() { Stack input = new Stack(); input.Push(new Stack(new List() { 1, 2 })); input.Push(new Stack(new List() { 3, 4 })); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[4,3],[2,1]]", json); } [Fact] - public static void WriteGenericStackOfStack() + public async Task WriteGenericStackOfStack() { Stack input = new Stack(); input.Push(new Stack(new List() { 1, 2 })); input.Push(new Stack(new List() { 3, 4 })); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[4,3],[2,1]]", json); GenericStackWrapper input2 = new GenericStackWrapper(); input2.Push(new StackWrapper(new List { 1, 2 })); input2.Push(new StackWrapper(new List { 3, 4 })); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[4,3],[2,1]]", json); } [Fact] - public static void WriteArrayOfStack() + public async Task WriteArrayOfStack() { Stack[] input = new Stack[2]; input[0] = new Stack(new List() { 1, 2 }); input[1] = new Stack(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[2,1],[4,3]]", json); } [Fact] - public static void WritePrimitiveStack() + public async Task WritePrimitiveStack() { Stack input = new Stack( new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[2,1]", json); } [Fact] - public static void WriteQueueOfQueue() + public async Task WriteQueueOfQueue() { Queue input = new Queue(); input.Enqueue(new Queue(new List() { 1, 2 })); input.Enqueue(new Queue(new List() { 3, 4 })); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteGenericQueueOfQueue() + public async Task WriteGenericQueueOfQueue() { Queue input = new Queue(); input.Enqueue(new Queue(new List() { 1, 2 })); input.Enqueue(new Queue(new List() { 3, 4 })); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); GenericQueueWrapper input2 = new GenericQueueWrapper(); input2.Enqueue(new QueueWrapper(new List() { 1, 2 })); input2.Enqueue(new QueueWrapper(new List() { 3, 4 })); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfQueue() + public async Task WriteArrayOfQueue() { Queue[] input = new Queue[2]; input[0] = new Queue(new List() { 1, 2 }); input[1] = new Queue(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveQueue() + public async Task WritePrimitiveQueue() { Queue input = new Queue(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteArrayListOfArrayList() + public async Task WriteArrayListOfArrayList() { ArrayList input = new ArrayList { @@ -295,7 +296,7 @@ namespace System.Text.Json.Serialization.Tests new ArrayList(new List() { 3, 4 }) }; - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); ArrayListWrapper input2 = new ArrayListWrapper(new List @@ -304,37 +305,37 @@ namespace System.Text.Json.Serialization.Tests new ArrayListWrapper(new List() { 3, 4 }) }); - json = JsonSerializer.Serialize(input2); + json = await JsonSerializerWrapperForString.SerializeWrapper(input2); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WriteArrayOfArrayList() + public async Task WriteArrayOfArrayList() { ArrayList[] input = new ArrayList[2]; input[0] = new ArrayList(new List() { 1, 2 }); input[1] = new ArrayList(new List() { 3, 4 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[[1,2],[3,4]]", json); } [Fact] - public static void WritePrimitiveArrayList() + public async Task WritePrimitiveArrayList() { ArrayList input = new ArrayList(new List { 1, 2 }); - string json = JsonSerializer.Serialize(input); + string json = await JsonSerializerWrapperForString.SerializeWrapper(input); Assert.Equal("[1,2]", json); } [Fact] - public static void WriteSimpleTestStructWithNullableStructCollectionWrappers() + public async Task WriteSimpleTestStructWithNullableStructCollectionWrappers() { { SimpleTestStructWithNullableStructCollectionWrappers obj = new SimpleTestStructWithNullableStructCollectionWrappers(); obj.Initialize(); - Assert.Equal(SimpleTestStructWithNullableStructCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestStructWithNullableStructCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { @@ -344,17 +345,17 @@ namespace System.Text.Json.Serialization.Tests @"""List"" : null," + @"""Dictionary"" : null" + @"}"; - Assert.Equal(json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteSimpleTestClassWithStructCollectionWrappers() + public async Task WriteSimpleTestClassWithStructCollectionWrappers() { { SimpleTestClassWithStructCollectionWrappers obj = new SimpleTestClassWithStructCollectionWrappers(); obj.Initialize(); - Assert.Equal(SimpleTestClassWithStructCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(SimpleTestClassWithStructCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } { @@ -368,12 +369,12 @@ namespace System.Text.Json.Serialization.Tests @"""List"" : []," + @"""Dictionary"" : {}" + @"}"; - Assert.Equal(json.StripWhitespace(), JsonSerializer.Serialize(obj)); + Assert.Equal(json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj)); } } [Fact] - public static void WriteNonGenericCollectionWrappers() + public async Task WriteNonGenericCollectionWrappers() { SimpleTestClassWithNonGenericCollectionWrappers obj1 = new SimpleTestClassWithNonGenericCollectionWrappers(); SimpleTestClassWithIEnumerableWrapper obj2 = new SimpleTestClassWithIEnumerableWrapper(); @@ -387,20 +388,20 @@ namespace System.Text.Json.Serialization.Tests obj4.Initialize(); obj5.Initialize(); - Assert.Equal(SimpleTestClassWithNonGenericCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); - Assert.Equal(SimpleTestClassWithNonGenericCollectionWrappers.s_json.StripWhitespace(), JsonSerializer.Serialize(obj1)); + Assert.Equal(SimpleTestClassWithNonGenericCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); + Assert.Equal(SimpleTestClassWithNonGenericCollectionWrappers.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj1)); - Assert.Equal(SimpleTestClassWithIEnumerableWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); - Assert.Equal(SimpleTestClassWithIEnumerableWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj2)); + Assert.Equal(SimpleTestClassWithIEnumerableWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); + Assert.Equal(SimpleTestClassWithIEnumerableWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj2)); - Assert.Equal(SimpleTestClassWithICollectionWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); - Assert.Equal(SimpleTestClassWithICollectionWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj3)); + Assert.Equal(SimpleTestClassWithICollectionWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); + Assert.Equal(SimpleTestClassWithICollectionWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj3)); - Assert.Equal(SimpleTestClassWithStackWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); - Assert.Equal(SimpleTestClassWithStackWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj4)); + Assert.Equal(SimpleTestClassWithStackWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); + Assert.Equal(SimpleTestClassWithStackWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj4)); - Assert.Equal(SimpleTestClassWithQueueWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); - Assert.Equal(SimpleTestClassWithQueueWrapper.s_json.StripWhitespace(), JsonSerializer.Serialize(obj5)); + Assert.Equal(SimpleTestClassWithQueueWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); + Assert.Equal(SimpleTestClassWithQueueWrapper.s_json.StripWhitespace(), await JsonSerializerWrapperForString.SerializeWrapper(obj5)); } } } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Read.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Read.cs new file mode 100644 index 00000000000..2fc8575f76f --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Read.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests + { + [Fact] + public async Task Read_ObjectModelCollection() + { + Collection c = await JsonSerializerWrapperForString.DeserializeWrapper>("[true,false]"); + Assert.Equal(2, c.Count); + Assert.True(c[0]); + Assert.False(c[1]); + + // Regression test for https://github.com/dotnet/runtime/issues/30686. + ObservableCollection oc = await JsonSerializerWrapperForString.DeserializeWrapper>("[true,false]"); + Assert.Equal(2, oc.Count); + Assert.True(oc[0]); + Assert.False(oc[1]); + + SimpleKeyedCollection kc = await JsonSerializerWrapperForString.DeserializeWrapper("[true]"); + Assert.Equal(1, kc.Count); + Assert.True(kc[0]); + } + + [Fact] + public async Task Read_ObjectModelCollection_Throws() + { + // No default constructor. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>("[true,false]")); + // No default constructor. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>("[true,false]")); + // No default constructor. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>(@"{""true"":false}")); + + // Abstract types can't be instantiated. This means there's no default constructor, so the type is not supported for deserialization. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper>("[true]")); + } + + public class SimpleKeyedCollection : KeyedCollection + { + protected override string GetKeyForItem(bool item) + { + return item.ToString(); + } + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Write.cs similarity index 51% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Write.cs rename to src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Write.cs index ab2e231fcb5..f9c0e6834de 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.ObjectModel.Write.cs @@ -3,33 +3,34 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests { - public static partial class CollectionTests + public abstract partial class CollectionTests { [Fact] - public static void Write_ObjectModelCollection() + public async Task Write_ObjectModelCollection() { Collection c = new Collection() { true, false }; - Assert.Equal("[true,false]", JsonSerializer.Serialize(c)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper(c)); ObservableCollection oc = new ObservableCollection() { true, false }; - Assert.Equal("[true,false]", JsonSerializer.Serialize(oc)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper(oc)); SimpleKeyedCollection kc = new SimpleKeyedCollection() { true, false }; - Assert.Equal("[true,false]", JsonSerializer.Serialize(kc)); - Assert.Equal("[true,false]", JsonSerializer.Serialize>(kc)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper(kc)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper>(kc)); ReadOnlyCollection roc = new ReadOnlyCollection(new List { true, false }); - Assert.Equal("[true,false]", JsonSerializer.Serialize(roc)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper(roc)); ReadOnlyObservableCollection rooc = new ReadOnlyObservableCollection(oc); - Assert.Equal("[true,false]", JsonSerializer.Serialize(rooc)); + Assert.Equal("[true,false]", await JsonSerializerWrapperForString.SerializeWrapper(rooc)); ReadOnlyDictionary rod = new ReadOnlyDictionary(new Dictionary { ["true"] = false }); - Assert.Equal(@"{""true"":false}", JsonSerializer.Serialize(rod)); + Assert.Equal(@"{""true"":false}", await JsonSerializerWrapperForString.SerializeWrapper(rod)); } } } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Read.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Read.cs new file mode 100644 index 00000000000..22bb9b20670 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Read.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Specialized; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests + { + [Fact] + public async Task Read_SpecializedCollection() + { + BitVector32 bv32 = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""Data"":4}"); + // Data property is skipped because it doesn't have a setter. + Assert.Equal(0, bv32.Data); + + HybridDictionary hd = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""key"":""value""}"); + Assert.Equal(1, hd.Count); + Assert.Equal("value", ((JsonElement)hd["key"]).GetString()); + + IOrderedDictionary iod = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""key"":""value""}"); + Assert.Equal(1, iod.Count); + Assert.Equal("value", ((JsonElement)iod["key"]).GetString()); + + ListDictionary ld = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""key"":""value""}"); + Assert.Equal(1, ld.Count); + Assert.Equal("value", ((JsonElement)ld["key"]).GetString()); + } + + [Fact] + public async Task Read_SpecializedCollection_Throws() + { + // Add method for this collection only accepts strings, even though it only implements IList which usually + // indicates that the element type is typeof(object). + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""1"", ""2""]")); + + // Not supported. Not IList, and we don't detect the add method for this collection. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[{""Key"": ""key"",""Value"":""value""}]")); + + // Int key is not allowed. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{1:""value""}")); + + // Runtime type in this case is IOrderedDictionary (we don't replace with concrete type), which we can't instantiate. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{""first"":""John"",""second"":""Jane"",""third"":""Jet""}")); + + // Not supported. Not IList, and we don't detect the add method for this collection. + await Assert.ThrowsAsync(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"[""NameValueCollection""]")); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs new file mode 100644 index 00000000000..084777e4167 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Specialized; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests + { + [Fact] + public async Task Write_SpecializedCollection() + { + Assert.Equal(@"{""Data"":4}", await JsonSerializerWrapperForString.SerializeWrapper(new BitVector32(4))); + Assert.Equal(@"{""Data"":4}", await JsonSerializerWrapperForString.SerializeWrapper(new BitVector32(4))); + + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new HybridDictionary { ["key"] = "value" })); + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new HybridDictionary { ["key"] = "value" })); + + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new OrderedDictionary { ["key"] = "value" })); + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new OrderedDictionary { ["key"] = "value" })); + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new OrderedDictionary { ["key"] = "value" })); + + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new ListDictionary { ["key"] = "value" })); + Assert.Equal(@"{""key"":""value""}", await JsonSerializerWrapperForString.SerializeWrapper(new ListDictionary { ["key"] = "value" })); + + Assert.Equal(@"[""1"",""2""]", await JsonSerializerWrapperForString.SerializeWrapper(new StringCollection { "1", "2" })); + Assert.Equal(@"[""1"",""2""]", await JsonSerializerWrapperForString.SerializeWrapper(new StringCollection { "1", "2" })); + + Assert.Equal(@"[{""Key"":""key"",""Value"":""value""}]", await JsonSerializerWrapperForString.SerializeWrapper(new StringDictionary { ["key"] = "value" })); + Assert.Equal(@"[{""Key"":""key"",""Value"":""value""}]", await JsonSerializerWrapperForString.SerializeWrapper(new StringDictionary { ["key"] = "value" })); + + // Element type returned by .GetEnumerator for this type is string, specifically the key. + Assert.Equal(@"[""key""]", await JsonSerializerWrapperForString.SerializeWrapper(new NameValueCollection { ["key"] = "value" })); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.cs new file mode 100644 index 00000000000..82ac3b7eb10 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Tests +{ + public abstract partial class CollectionTests : SerializerTests + { + public CollectionTests(JsonSerializerWrapperForString stringSerializerWrapper, JsonSerializerWrapperForStream streamSerializerWrapper) + : base(stringSerializerWrapper, streamSerializerWrapper) { } + } +} diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForStream.cs b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForStream.cs new file mode 100644 index 00000000000..d3e9994c6e7 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForStream.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace System.Text.Json.Serialization.Tests +{ + /// + /// Base class for wrapping Stream-based JsonSerializer methods which allows tests to run under different configurations. + /// + public abstract partial class JsonSerializerWrapperForStream + { + protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null); + protected internal abstract Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null); + protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null); + protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo); + } +} diff --git a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs index 8d53fc3c563..528ac2903e4 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json @@ -67,5 +69,20 @@ namespace System.Text.Json break; } } + + public static async Task> ToListAsync(this IAsyncEnumerable source) + { + var list = new List(); + await foreach (T item in source) + { + list.Add(item); + } + return list; + } + + private static readonly Regex s_stripWhitespace = new Regex(@"\s+", RegexOptions.Compiled); + + public static string StripWhitespace(this string value) + => s_stripWhitespace.Replace(value, string.Empty); } } diff --git a/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs index c7198b1e306..e1536add900 100644 --- a/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs @@ -7,6 +7,9 @@ namespace System.Text.Json.Serialization.Tests { protected JsonSerializerWrapperForString JsonSerializerWrapperForString { get; } - protected SerializerTests(JsonSerializerWrapperForString serializerWrapper) => JsonSerializerWrapperForString = serializerWrapper; + protected JsonSerializerWrapperForStream JsonSerializerWrapperForStream { get; } + + protected SerializerTests(JsonSerializerWrapperForString stringSerializerWrapper, JsonSerializerWrapperForStream? streamSerializerWrapper = null) + => (JsonSerializerWrapperForString, JsonSerializerWrapperForStream) = (stringSerializerWrapper, streamSerializerWrapper); } } diff --git a/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs index a7251994412..9f423cfedb0 100644 --- a/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs +++ b/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs @@ -1967,4 +1967,20 @@ namespace System.Text.Json.Serialization.Tests { public int Id { get; set; } } + + public class UppercaseNamingPolicy : JsonNamingPolicy + { + public override string ConvertName(string name) + { + return name.ToUpperInvariant(); + } + } + + public class NullNamingPolicy : JsonNamingPolicy + { + public override string ConvertName(string name) + { + return null; + } + } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs index 77882f662d6..79d4378203b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; using System.Text.Json.Serialization; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Text.Json.SourceGeneration.Tests @@ -17,6 +19,46 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.NotNull(NestedPublicContext.NestedProtectedInternalClass.Default); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void Converters_AndTypeInfoCreator_NotRooted_WhenMetadataNotPresent() + { + RemoteExecutor.Invoke( + new Action(() => + { + object[] objArr = new object[] { new MyStruct() }; + + // Metadata not generated for MyStruct without JsonSerializableAttribute. + NotSupportedException ex = Assert.Throws( + () => JsonSerializer.Serialize(objArr, MetadataContext.Default.ObjectArray)); + string exAsStr = ex.ToString(); + Assert.Contains(typeof(MyStruct).ToString(), exAsStr); + Assert.Contains("JsonSerializerOptions", exAsStr); + + // This test uses reflection to: + // - Access JsonSerializerOptions.s_defaultSimpleConverters + // - Access JsonSerializerOptions.s_defaultFactoryConverters + // - Access JsonSerializerOptions._typeInfoCreationFunc + // + // If any of them changes, this test will need to be kept in sync. + + // Confirm built-in converters not set. + AssertFieldNull("s_defaultSimpleConverters", optionsInstance: null); + AssertFieldNull("s_defaultFactoryConverters", optionsInstance: null); + + // Confirm type info dynamic creator not set. + AssertFieldNull("_typeInfoCreationFunc", MetadataContext.Default.Options); + + static void AssertFieldNull(string fieldName, JsonSerializerOptions? optionsInstance) + { + BindingFlags bindingFlags = BindingFlags.NonPublic | (optionsInstance == null ? BindingFlags.Static : BindingFlags.Instance); + FieldInfo fieldInfo = typeof(JsonSerializerOptions).GetField(fieldName, bindingFlags); + Assert.NotNull(fieldInfo); + Assert.Null(fieldInfo.GetValue(optionsInstance)); + } + }), + new RemoteInvokeOptions() { ExpectedExitCode = 0 }).Dispose(); + } + [JsonSerializable(typeof(JsonMessage))] internal partial class NestedContext : JsonSerializerContext { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 0017021fb4e..db55741cdca 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using Xunit; @@ -473,41 +472,6 @@ namespace System.Text.Json.SourceGeneration.Tests public DayOfWeek? NullableDay { get; set; } } - [Fact] - public void Converters_AndTypeInfoCreator_NotRooted_WhenMetadataNotPresent() - { - object[] objArr = new object[] { new MyStruct() }; - - // Metadata not generated for MyStruct without JsonSerializableAttribute. - NotSupportedException ex = Assert.Throws( - () => JsonSerializer.Serialize(objArr, DefaultContext.ObjectArray)); - string exAsStr = ex.ToString(); - Assert.Contains(typeof(MyStruct).ToString(), exAsStr); - Assert.Contains("JsonSerializerOptions", exAsStr); - - // This test uses reflection to: - // - Access JsonSerializerOptions.s_defaultSimpleConverters - // - Access JsonSerializerOptions.s_defaultFactoryConverters - // - Access JsonSerializerOptions._typeInfoCreationFunc - // - // If any of them changes, this test will need to be kept in sync. - - // Confirm built-in converters not set. - AssertFieldNull("s_defaultSimpleConverters", optionsInstance: null); - AssertFieldNull("s_defaultFactoryConverters", optionsInstance: null); - - // Confirm type info dynamic creator not set. - AssertFieldNull("_typeInfoCreationFunc", ((JsonSerializerContext)DefaultContext).Options); - - static void AssertFieldNull(string fieldName, JsonSerializerOptions? optionsInstance) - { - BindingFlags bindingFlags = BindingFlags.NonPublic | (optionsInstance == null ? BindingFlags.Static : BindingFlags.Instance); - FieldInfo fieldInfo = typeof(JsonSerializerOptions).GetField(fieldName, bindingFlags); - Assert.NotNull(fieldInfo); - Assert.Null(fieldInfo.GetValue(optionsInstance)); - } - } - private const string ExceptionMessageFromCustomContext = "Exception thrown from custom context."; [Fact] @@ -529,8 +493,6 @@ namespace System.Text.Json.SourceGeneration.Tests Assert.Contains(ExceptionMessageFromCustomContext, ex.ToString()); } - internal struct MyStruct { } - internal class CustomContext : JsonSerializerContext { public CustomContext(JsonSerializerOptions options) : base(options, null) { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/CollectionTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/CollectionTests.cs new file mode 100644 index 00000000000..8d581aed66e --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/CollectionTests.cs @@ -0,0 +1,740 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Tests; + +namespace System.Text.Json.SourceGeneration.Tests +{ + public partial class CollectionTests_Metadata : CollectionTests + { + public CollectionTests_Metadata() + : this(new StringSerializerWrapper(CollectionTestsContext_Metadata.Default, (options) => new CollectionTestsContext_Metadata(options))) + { + } + + protected CollectionTests_Metadata(JsonSerializerWrapperForString serializerWrapper) + : base(serializerWrapper, new StreamSerializerWrapper()) + { + } + + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(ConcurrentDictionary))] + [JsonSerializable(typeof(ConcurrentQueue))] + [JsonSerializable(typeof(ConcurrentStack))] + [JsonSerializable(typeof(BlockingCollection))] + [JsonSerializable(typeof(ConcurrentBag))] + [JsonSerializable(typeof(GenericConcurrentQueuePrivateConstructor))] + [JsonSerializable(typeof(GenericConcurrentQueueInternalConstructor))] + [JsonSerializable(typeof(GenericConcurrentStackPrivateConstructor))] + [JsonSerializable(typeof(GenericConcurrentStackInternalConstructor))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(SortedDictionary))] + [JsonSerializable(typeof(object))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(ImmutableDictionary))] + [JsonSerializable(typeof(IImmutableDictionary))] + [JsonSerializable(typeof(ImmutableSortedDictionary))] + [JsonSerializable(typeof(Hashtable))] + [JsonSerializable(typeof(SortedList))] + [JsonSerializable(typeof(WrapperForIDictionary))] + [JsonSerializable(typeof(StringToStringDictionaryWrapper))] + [JsonSerializable(typeof(StringToStringSortedDictionaryWrapper))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(GenericIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(StringToStringIImmutableDictionaryWrapper))] + [JsonSerializable(typeof(HashtableWrapper))] + [JsonSerializable(typeof(SortedListWrapper))] + [JsonSerializable(typeof(GenericStructIDictionaryWrapper))] + [JsonSerializable(typeof(GenericStructIDictionaryWrapper?))] + [JsonSerializable(typeof(StructWrapperForIDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(PocoDictionary))] + [JsonSerializable(typeof(IDictionary>))] + [JsonSerializable(typeof(ImmutableDictionary>))] + [JsonSerializable(typeof(IImmutableDictionary>))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(Dictionary[]), TypeInfoPropertyName = "ArrayOfDictionaryStringInt")] + [JsonSerializable(typeof(ImmutableSortedDictionary[]))] + [JsonSerializable(typeof(Dictionary>))] + [JsonSerializable(typeof(ImmutableSortedDictionary>))] + [JsonSerializable(typeof(Dictionary>>))] + [JsonSerializable(typeof(Dictionary[]>))] + [JsonSerializable(typeof(SimpleTestClass))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(PocoDuplicate))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary1))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary2))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary3))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary4))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary5))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary6))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary7))] + [JsonSerializable(typeof(ClassWithIgnoredIDictionary))] + [JsonSerializable(typeof(ClassWithIgnoreAttributeDictionary))] + [JsonSerializable(typeof(ClassWithIgnoredImmutableDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(AllSingleUpperPropertiesParent))] + [JsonSerializable(typeof(ClassWithDictionaryOfString_ChildWithDictionaryOfString))] + [JsonSerializable(typeof(ClassWithDictionaryOfString_ChildWithDictionaryOfString))] + [JsonSerializable(typeof(ClassWithDictionaryAndProperty_DictionaryFirst))] + [JsonSerializable(typeof(ClassWithDictionaryAndProperty_DictionaryLast))] + [JsonSerializable(typeof(SimpleClassWithDictionaries))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringTValue))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringTValue))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringPoco))] + [JsonSerializable(typeof(DictionaryThatHasIncompatibleEnumerator))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(StructWrapperForIDictionary?))] + [JsonSerializable(typeof(ClassWithStructIDictionaryWrapper))] + [JsonSerializable(typeof(Poco))] + [JsonSerializable(typeof(JsonElement))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary>))] + [JsonSerializable(typeof(TestClassWithDictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(GenericIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(IDerivedIDictionaryOfTKeyTValue))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List[]), TypeInfoPropertyName = "ArrayOfIntList")] + [JsonSerializable(typeof(StringListWrapper[]))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(StringListWrapper))] + [JsonSerializable(typeof(IEnumerable>))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable[]), TypeInfoPropertyName = "ArrayOfIntIEnumerable")] + [JsonSerializable(typeof(StringIEnumerableWrapper[]))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(StringIEnumerableWrapper))] + [JsonSerializable(typeof(IList>))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList[]), TypeInfoPropertyName = "ArrayOfIntIList")] + [JsonSerializable(typeof(StringIListWrapper[]))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(StringIListWrapper))] + [JsonSerializable(typeof(GenericStructIListWrapper))] + [JsonSerializable(typeof(GenericStructIListWrapper?))] + [JsonSerializable(typeof(GenericStructICollectionWrapper))] + [JsonSerializable(typeof(GenericStructICollectionWrapper?))] + [JsonSerializable(typeof(ICollection>))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(ICollection[]), TypeInfoPropertyName = "ArrayOfIntICollection")] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(IReadOnlyCollection>))] + [JsonSerializable(typeof(GenericIReadOnlyCollectionWrapper>))] + [JsonSerializable(typeof(IReadOnlyCollection))] + [JsonSerializable(typeof(GenericIReadOnlyCollectionWrapper))] + [JsonSerializable(typeof(IReadOnlyCollection[]), TypeInfoPropertyName = "ArrayOfIntIReadOnlyCollection")] + [JsonSerializable(typeof(WrapperForIReadOnlyCollectionOfT[]))] + [JsonSerializable(typeof(IReadOnlyCollection))] + [JsonSerializable(typeof(WrapperForIReadOnlyCollectionOfT))] + [JsonSerializable(typeof(IReadOnlyList>))] + [JsonSerializable(typeof(GenericIReadOnlyListWrapper))] + [JsonSerializable(typeof(IReadOnlyList))] + [JsonSerializable(typeof(GenericIReadOnlyListWrapper))] + [JsonSerializable(typeof(IReadOnlyList[]), TypeInfoPropertyName = "ArrayOfIntIReadOnlyList")] + [JsonSerializable(typeof(StringIReadOnlyListWrapper[]))] + [JsonSerializable(typeof(IReadOnlyList))] + [JsonSerializable(typeof(StringIReadOnlyListWrapper))] + [JsonSerializable(typeof(ISet>))] + [JsonSerializable(typeof(GenericISetWrapper))] + [JsonSerializable(typeof(GenericStructISetWrapper))] + [JsonSerializable(typeof(GenericStructISetWrapper?))] + [JsonSerializable(typeof(ISet>))] + [JsonSerializable(typeof(HashSet>))] + [JsonSerializable(typeof(ISet))] + [JsonSerializable(typeof(ISet[]), TypeInfoPropertyName = "ArrayOfIntISet")] + [JsonSerializable(typeof(ISet))] + [JsonSerializable(typeof(Stack>))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(Stack[]), TypeInfoPropertyName = "ArrayOfIntStack")] + [JsonSerializable(typeof(StringStackWrapper[]))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(StringStackWrapper))] + [JsonSerializable(typeof(Queue>))] + [JsonSerializable(typeof(GenericQueueWrapper))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue[]), TypeInfoPropertyName = "ArrayOfIntQueue")] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(HashSet>))] + [JsonSerializable(typeof(GenericHashSetWrapper))] + [JsonSerializable(typeof(HashSet))] + [JsonSerializable(typeof(HashSet[]), TypeInfoPropertyName = "ArrayOfIntHashSet")] + [JsonSerializable(typeof(HashSet))] + [JsonSerializable(typeof(LinkedList>))] + [JsonSerializable(typeof(GenericLinkedListWrapper))] + [JsonSerializable(typeof(LinkedList))] + [JsonSerializable(typeof(LinkedList[]), TypeInfoPropertyName = "ArrayOfIntLinkedList")] + [JsonSerializable(typeof(LinkedList))] + [JsonSerializable(typeof(SortedSet[]))] + [JsonSerializable(typeof(StringSortedSetWrapper[]))] + [JsonSerializable(typeof(SortedSet))] + [JsonSerializable(typeof(ClassWithGenericStructIListWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructICollectionWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructIDictionaryWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructISetWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithGenericStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestStructWithNullableGenericStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithGenericCollectionWrappers))] + [JsonSerializable(typeof(string[]))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(MyMyList))] + [JsonSerializable(typeof(MyListString))] + [JsonSerializable(typeof(NetworkWrapper))] + [JsonSerializable(typeof(Client))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(IEnumerable[]))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(IList[]))] + [JsonSerializable(typeof(StructWrapperForIList))] + [JsonSerializable(typeof(StructWrapperForIList?))] + [JsonSerializable(typeof(ClassWithStructIListWrapper))] + [JsonSerializable(typeof(StructWrapperForIDictionary))] + [JsonSerializable(typeof(StructWrapperForIDictionary?))] + [JsonSerializable(typeof(ClassWithStructIDictionaryWrapper))] + [JsonSerializable(typeof(WrapperForIList))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(ICollection[]))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(Stack[]))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue[]))] + [JsonSerializable(typeof(QueueWrapper))] + [JsonSerializable(typeof(ArrayList))] + [JsonSerializable(typeof(ArrayListWrapper))] + [JsonSerializable(typeof(ArrayList[]))] + [JsonSerializable(typeof(SimpleTestClassWithNonGenericCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestStructWithNullableStructCollectionWrappers))] + [JsonSerializable(typeof(Collection))] + [JsonSerializable(typeof(ObservableCollection))] + [JsonSerializable(typeof(SimpleKeyedCollection))] + [JsonSerializable(typeof(ReadOnlyCollection))] + [JsonSerializable(typeof(ReadOnlyObservableCollection))] + [JsonSerializable(typeof(ReadOnlyDictionary))] + [JsonSerializable(typeof(KeyedCollection))] + [JsonSerializable(typeof(BitVector32))] + [JsonSerializable(typeof(HybridDictionary))] + [JsonSerializable(typeof(OrderedDictionary))] + [JsonSerializable(typeof(ListDictionary))] + [JsonSerializable(typeof(StringCollection))] + [JsonSerializable(typeof(StringDictionary))] + [JsonSerializable(typeof(IOrderedDictionary))] + [JsonSerializable(typeof(NameValueCollection))] + [JsonSerializable(typeof(ImmutableArray>))] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(ImmutableArray[]), TypeInfoPropertyName = "ArrayOfImmutableIntArray")] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableArray))] + [JsonSerializable(typeof(IImmutableList>))] + [JsonSerializable(typeof(IImmutableList))] + [JsonSerializable(typeof(IImmutableList[]), TypeInfoPropertyName = "ArrayOfIImmutableIntArrayList")] + [JsonSerializable(typeof(IImmutableList))] + [JsonSerializable(typeof(IImmutableStack>))] + [JsonSerializable(typeof(IImmutableStack))] + [JsonSerializable(typeof(IImmutableStack[]), TypeInfoPropertyName = "ArrayOfIImmutableIntStack")] + [JsonSerializable(typeof(IImmutableStack))] + [JsonSerializable(typeof(IImmutableQueue>))] + [JsonSerializable(typeof(IImmutableQueue))] + [JsonSerializable(typeof(IImmutableQueue[]), TypeInfoPropertyName = "ArrayOfIImmutableIntQueue")] + [JsonSerializable(typeof(IImmutableQueue))] + [JsonSerializable(typeof(IImmutableSet>))] + [JsonSerializable(typeof(IImmutableSet))] + [JsonSerializable(typeof(IImmutableSet[]), TypeInfoPropertyName = "ArrayOfIImmutableIntSet")] + [JsonSerializable(typeof(IImmutableSet))] + [JsonSerializable(typeof(ImmutableHashSet>))] + [JsonSerializable(typeof(ImmutableHashSet))] + [JsonSerializable(typeof(ImmutableHashSet[]), TypeInfoPropertyName = "ArrayOfImmutableIntHashSet")] + [JsonSerializable(typeof(ImmutableHashSet))] + [JsonSerializable(typeof(ImmutableList>))] + [JsonSerializable(typeof(ImmutableList))] + [JsonSerializable(typeof(ImmutableList[]), TypeInfoPropertyName = "ArrayOfImmutableIntList")] + [JsonSerializable(typeof(ImmutableList))] + [JsonSerializable(typeof(ImmutableStack>))] + [JsonSerializable(typeof(ImmutableStack))] + [JsonSerializable(typeof(ImmutableStack[]), TypeInfoPropertyName = "ArrayOfImmutableIntStack")] + [JsonSerializable(typeof(ImmutableStack))] + [JsonSerializable(typeof(ImmutableQueue>))] + [JsonSerializable(typeof(ImmutableQueue))] + [JsonSerializable(typeof(ImmutableQueue[]), TypeInfoPropertyName = "ArrayOfImmutableIntQueue")] + [JsonSerializable(typeof(ImmutableQueue))] + [JsonSerializable(typeof(ImmutableSortedSet[]))] + [JsonSerializable(typeof(ImmutableSortedSet))] + [JsonSerializable(typeof(SimpleTestClassWithIImmutableDictionaryWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableStackWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableQueueWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableSetWrapper))] + [JsonSerializable(typeof(ClassWithPopulatedDictionaryAndNoSetter))] + [JsonSerializable(typeof(StringIImmutableQueueWrapper))] + [JsonSerializable(typeof(StringIImmutableStackWrapper))] + [JsonSerializable(typeof(ClassWithPopulatedDictionaryAndSetter))] + [JsonSerializable(typeof(StringIImmutableListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithObjectImmutableArray))] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(StringIImmutableSetWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(ICollection>))] + [JsonSerializable(typeof(SimpleTestClassWithStringIEnumerableWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringIReadOnlyCollectionWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringIReadOnlyListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(GenericICollectionWrapper>))] + [JsonSerializable(typeof(GenericIEnumerableWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIEnumerableWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericICollectionWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericICollectionWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericIListWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIListWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericISetWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericISetWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericIDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(StringToStringIReadOnlyDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(StringToStringIReadOnlyDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericListWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericListWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericQueueWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericQueueWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericStackWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericStackWrapperInternalConstructor))] + [JsonSerializable(typeof(StringToGenericDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(StringToGenericDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(SimpleTestClassWithNonGenericCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithIEnumerableWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithICollectionWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStackWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithQueueWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(WrapperForIEnumerable))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(DictionaryEntry))] + [JsonSerializable(typeof(Dictionary[]))] + [JsonSerializable(typeof(GenericQueueWrapper))] + [JsonSerializable(typeof(WrapperForIEnumerablePrivateConstructor))] + [JsonSerializable(typeof(WrapperForIEnumerableInternalConstructor))] + [JsonSerializable(typeof(WrapperForICollectionPrivateConstructor))] + [JsonSerializable(typeof(WrapperForICollectionInternalConstructor))] + [JsonSerializable(typeof(WrapperForIListPrivateConstructor))] + [JsonSerializable(typeof(WrapperForIListInternalConstructor))] + [JsonSerializable(typeof(WrapperForIDictionaryPrivateConstructor))] + [JsonSerializable(typeof(WrapperForIDictionaryInternalConstructor))] + [JsonSerializable(typeof(IDerivedICollectionOfT))] + [JsonSerializable(typeof(IDerivedIList))] + [JsonSerializable(typeof(IDerivedISetOfT))] + [JsonSerializable(typeof(ReadOnlyWrapperForIList))] + [JsonSerializable(typeof(ReadOnlyStringIListWrapper))] + [JsonSerializable(typeof(ReadOnlyStringICollectionWrapper))] + [JsonSerializable(typeof(ReadOnlyStringISetWrapper))] + [JsonSerializable(typeof(ReadOnlyWrapperForIDictionary))] + [JsonSerializable(typeof(ReadOnlyStringToStringIDictionaryWrapper))] + [JsonSerializable(typeof(Dictionary[]))] + internal sealed partial class CollectionTestsContext_Metadata : JsonSerializerContext + { + } + } + + public partial class CollectionTests_Default : CollectionTests_Metadata + { + public CollectionTests_Default() + : base(new StringSerializerWrapper(CollectionTestsContext_Default.Default, (options) => new CollectionTestsContext_Default(options))) + { + } + + [JsonSerializable(typeof(ConcurrentDictionary))] + [JsonSerializable(typeof(ConcurrentQueue))] + [JsonSerializable(typeof(ConcurrentStack))] + [JsonSerializable(typeof(BlockingCollection))] + [JsonSerializable(typeof(ConcurrentBag))] + [JsonSerializable(typeof(GenericConcurrentQueuePrivateConstructor))] + [JsonSerializable(typeof(GenericConcurrentQueueInternalConstructor))] + [JsonSerializable(typeof(GenericConcurrentStackPrivateConstructor))] + [JsonSerializable(typeof(GenericConcurrentStackInternalConstructor))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(SortedDictionary))] + [JsonSerializable(typeof(object))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(ImmutableDictionary))] + [JsonSerializable(typeof(IImmutableDictionary))] + [JsonSerializable(typeof(ImmutableSortedDictionary))] + [JsonSerializable(typeof(Hashtable))] + [JsonSerializable(typeof(SortedList))] + [JsonSerializable(typeof(WrapperForIDictionary))] + [JsonSerializable(typeof(StringToStringDictionaryWrapper))] + [JsonSerializable(typeof(StringToStringSortedDictionaryWrapper))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(GenericIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(StringToStringIImmutableDictionaryWrapper))] + [JsonSerializable(typeof(HashtableWrapper))] + [JsonSerializable(typeof(SortedListWrapper))] + [JsonSerializable(typeof(GenericStructIDictionaryWrapper))] + [JsonSerializable(typeof(GenericStructIDictionaryWrapper?))] + [JsonSerializable(typeof(StructWrapperForIDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(GenericIDictionaryWrapper))] + [JsonSerializable(typeof(PocoDictionary))] + [JsonSerializable(typeof(IDictionary>))] + [JsonSerializable(typeof(ImmutableDictionary>))] + [JsonSerializable(typeof(IImmutableDictionary>))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(Dictionary[]), TypeInfoPropertyName = "ArrayOfDictionaryStringInt")] + [JsonSerializable(typeof(ImmutableSortedDictionary[]))] + [JsonSerializable(typeof(Dictionary>))] + [JsonSerializable(typeof(ImmutableSortedDictionary>))] + [JsonSerializable(typeof(Dictionary>>))] + [JsonSerializable(typeof(Dictionary[]>))] + [JsonSerializable(typeof(SimpleTestClass))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(PocoDuplicate))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary1))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary2))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary3))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary4))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary5))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary6))] + [JsonSerializable(typeof(ClassWithIgnoredDictionary7))] + [JsonSerializable(typeof(ClassWithIgnoredIDictionary))] + [JsonSerializable(typeof(ClassWithIgnoreAttributeDictionary))] + [JsonSerializable(typeof(ClassWithIgnoredImmutableDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(AllSingleUpperPropertiesParent))] + [JsonSerializable(typeof(ClassWithDictionaryOfString_ChildWithDictionaryOfString))] + [JsonSerializable(typeof(ClassWithDictionaryOfString_ChildWithDictionaryOfString))] + [JsonSerializable(typeof(ClassWithDictionaryAndProperty_DictionaryFirst))] + [JsonSerializable(typeof(ClassWithDictionaryAndProperty_DictionaryLast))] + [JsonSerializable(typeof(SimpleClassWithDictionaries))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringTValue))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringTValue))] + [JsonSerializable(typeof(DictionaryThatOnlyImplementsIDictionaryOfStringPoco))] + [JsonSerializable(typeof(DictionaryThatHasIncompatibleEnumerator))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(StructWrapperForIDictionary?))] + [JsonSerializable(typeof(ClassWithStructIDictionaryWrapper))] + [JsonSerializable(typeof(Poco))] + [JsonSerializable(typeof(JsonElement))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Dictionary>))] + [JsonSerializable(typeof(TestClassWithDictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(GenericIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(IDerivedIDictionaryOfTKeyTValue))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List>))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(List[]), TypeInfoPropertyName = "ArrayOfIntList")] + [JsonSerializable(typeof(StringListWrapper[]))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(StringListWrapper))] + [JsonSerializable(typeof(IEnumerable>))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable[]), TypeInfoPropertyName = "ArrayOfIntIEnumerable")] + [JsonSerializable(typeof(StringIEnumerableWrapper[]))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(StringIEnumerableWrapper))] + [JsonSerializable(typeof(IList>))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList[]), TypeInfoPropertyName = "ArrayOfIntIList")] + [JsonSerializable(typeof(StringIListWrapper[]))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(StringIListWrapper))] + [JsonSerializable(typeof(GenericStructIListWrapper))] + [JsonSerializable(typeof(GenericStructIListWrapper?))] + [JsonSerializable(typeof(GenericStructICollectionWrapper))] + [JsonSerializable(typeof(GenericStructICollectionWrapper?))] + [JsonSerializable(typeof(ICollection>))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(ICollection[]), TypeInfoPropertyName = "ArrayOfIntICollection")] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(IReadOnlyCollection>))] + [JsonSerializable(typeof(GenericIReadOnlyCollectionWrapper>))] + [JsonSerializable(typeof(IReadOnlyCollection))] + [JsonSerializable(typeof(GenericIReadOnlyCollectionWrapper))] + [JsonSerializable(typeof(IReadOnlyCollection[]), TypeInfoPropertyName = "ArrayOfIntIReadOnlyCollection")] + [JsonSerializable(typeof(WrapperForIReadOnlyCollectionOfT[]))] + [JsonSerializable(typeof(IReadOnlyCollection))] + [JsonSerializable(typeof(WrapperForIReadOnlyCollectionOfT))] + [JsonSerializable(typeof(IReadOnlyList>))] + [JsonSerializable(typeof(GenericIReadOnlyListWrapper))] + [JsonSerializable(typeof(IReadOnlyList))] + [JsonSerializable(typeof(GenericIReadOnlyListWrapper))] + [JsonSerializable(typeof(IReadOnlyList[]), TypeInfoPropertyName = "ArrayOfIntIReadOnlyList")] + [JsonSerializable(typeof(StringIReadOnlyListWrapper[]))] + [JsonSerializable(typeof(IReadOnlyList))] + [JsonSerializable(typeof(StringIReadOnlyListWrapper))] + [JsonSerializable(typeof(ISet>))] + [JsonSerializable(typeof(GenericISetWrapper))] + [JsonSerializable(typeof(GenericStructISetWrapper))] + [JsonSerializable(typeof(GenericStructISetWrapper?))] + [JsonSerializable(typeof(ISet>))] + [JsonSerializable(typeof(HashSet>))] + [JsonSerializable(typeof(ISet))] + [JsonSerializable(typeof(ISet[]), TypeInfoPropertyName = "ArrayOfIntISet")] + [JsonSerializable(typeof(ISet))] + [JsonSerializable(typeof(Stack>))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(Stack[]), TypeInfoPropertyName = "ArrayOfIntStack")] + [JsonSerializable(typeof(StringStackWrapper[]))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(StringStackWrapper))] + [JsonSerializable(typeof(Queue>))] + [JsonSerializable(typeof(GenericQueueWrapper))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue[]), TypeInfoPropertyName = "ArrayOfIntQueue")] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(HashSet>))] + [JsonSerializable(typeof(GenericHashSetWrapper))] + [JsonSerializable(typeof(HashSet))] + [JsonSerializable(typeof(HashSet[]), TypeInfoPropertyName = "ArrayOfIntHashSet")] + [JsonSerializable(typeof(HashSet))] + [JsonSerializable(typeof(LinkedList>))] + [JsonSerializable(typeof(GenericLinkedListWrapper))] + [JsonSerializable(typeof(LinkedList))] + [JsonSerializable(typeof(LinkedList[]), TypeInfoPropertyName = "ArrayOfIntLinkedList")] + [JsonSerializable(typeof(LinkedList))] + [JsonSerializable(typeof(SortedSet[]))] + [JsonSerializable(typeof(StringSortedSetWrapper[]))] + [JsonSerializable(typeof(SortedSet))] + [JsonSerializable(typeof(ClassWithGenericStructIListWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructICollectionWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructIDictionaryWrapper))] + [JsonSerializable(typeof(ClassWithGenericStructISetWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithGenericStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestStructWithNullableGenericStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithGenericCollectionWrappers))] + [JsonSerializable(typeof(string[]))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(GenericListWrapper))] + [JsonSerializable(typeof(MyMyList))] + [JsonSerializable(typeof(MyListString))] + [JsonSerializable(typeof(NetworkWrapper))] + [JsonSerializable(typeof(Client))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(GenericIEnumerableWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(IEnumerable[]))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(GenericIListWrapper))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(IList[]))] + [JsonSerializable(typeof(StructWrapperForIList))] + [JsonSerializable(typeof(StructWrapperForIList?))] + [JsonSerializable(typeof(ClassWithStructIListWrapper))] + [JsonSerializable(typeof(StructWrapperForIDictionary))] + [JsonSerializable(typeof(StructWrapperForIDictionary?))] + [JsonSerializable(typeof(ClassWithStructIDictionaryWrapper))] + [JsonSerializable(typeof(WrapperForIList))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(GenericICollectionWrapper))] + [JsonSerializable(typeof(ICollection))] + [JsonSerializable(typeof(ICollection[]))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(Stack))] + [JsonSerializable(typeof(Stack[]))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(Queue[]))] + [JsonSerializable(typeof(QueueWrapper))] + [JsonSerializable(typeof(ArrayList))] + [JsonSerializable(typeof(ArrayListWrapper))] + [JsonSerializable(typeof(ArrayList[]))] + [JsonSerializable(typeof(SimpleTestClassWithNonGenericCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithStructCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestStructWithNullableStructCollectionWrappers))] + [JsonSerializable(typeof(Collection))] + [JsonSerializable(typeof(ObservableCollection))] + [JsonSerializable(typeof(SimpleKeyedCollection))] + [JsonSerializable(typeof(ReadOnlyCollection))] + [JsonSerializable(typeof(ReadOnlyObservableCollection))] + [JsonSerializable(typeof(ReadOnlyDictionary))] + [JsonSerializable(typeof(KeyedCollection))] + [JsonSerializable(typeof(BitVector32))] + [JsonSerializable(typeof(HybridDictionary))] + [JsonSerializable(typeof(OrderedDictionary))] + [JsonSerializable(typeof(ListDictionary))] + [JsonSerializable(typeof(StringCollection))] + [JsonSerializable(typeof(StringDictionary))] + [JsonSerializable(typeof(IOrderedDictionary))] + [JsonSerializable(typeof(NameValueCollection))] + [JsonSerializable(typeof(ImmutableArray>))] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(ImmutableArray[]), TypeInfoPropertyName = "ArrayOfImmutableIntArray")] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableArray))] + [JsonSerializable(typeof(IImmutableList>))] + [JsonSerializable(typeof(IImmutableList))] + [JsonSerializable(typeof(IImmutableList[]), TypeInfoPropertyName = "ArrayOfIImmutableIntArrayList")] + [JsonSerializable(typeof(IImmutableList))] + [JsonSerializable(typeof(IImmutableStack>))] + [JsonSerializable(typeof(IImmutableStack))] + [JsonSerializable(typeof(IImmutableStack[]), TypeInfoPropertyName = "ArrayOfIImmutableIntStack")] + [JsonSerializable(typeof(IImmutableStack))] + [JsonSerializable(typeof(IImmutableQueue>))] + [JsonSerializable(typeof(IImmutableQueue))] + [JsonSerializable(typeof(IImmutableQueue[]), TypeInfoPropertyName = "ArrayOfIImmutableIntQueue")] + [JsonSerializable(typeof(IImmutableQueue))] + [JsonSerializable(typeof(IImmutableSet>))] + [JsonSerializable(typeof(IImmutableSet))] + [JsonSerializable(typeof(IImmutableSet[]), TypeInfoPropertyName = "ArrayOfIImmutableIntSet")] + [JsonSerializable(typeof(IImmutableSet))] + [JsonSerializable(typeof(ImmutableHashSet>))] + [JsonSerializable(typeof(ImmutableHashSet))] + [JsonSerializable(typeof(ImmutableHashSet[]), TypeInfoPropertyName = "ArrayOfImmutableIntHashSet")] + [JsonSerializable(typeof(ImmutableHashSet))] + [JsonSerializable(typeof(ImmutableList>))] + [JsonSerializable(typeof(ImmutableList))] + [JsonSerializable(typeof(ImmutableList[]), TypeInfoPropertyName = "ArrayOfImmutableIntList")] + [JsonSerializable(typeof(ImmutableList))] + [JsonSerializable(typeof(ImmutableStack>))] + [JsonSerializable(typeof(ImmutableStack))] + [JsonSerializable(typeof(ImmutableStack[]), TypeInfoPropertyName = "ArrayOfImmutableIntStack")] + [JsonSerializable(typeof(ImmutableStack))] + [JsonSerializable(typeof(ImmutableQueue>))] + [JsonSerializable(typeof(ImmutableQueue))] + [JsonSerializable(typeof(ImmutableQueue[]), TypeInfoPropertyName = "ArrayOfImmutableIntQueue")] + [JsonSerializable(typeof(ImmutableQueue))] + [JsonSerializable(typeof(ImmutableSortedSet[]))] + [JsonSerializable(typeof(ImmutableSortedSet))] + [JsonSerializable(typeof(SimpleTestClassWithIImmutableDictionaryWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableStackWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableQueueWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithImmutableSetWrapper))] + [JsonSerializable(typeof(ClassWithPopulatedDictionaryAndNoSetter))] + [JsonSerializable(typeof(StringIImmutableQueueWrapper))] + [JsonSerializable(typeof(StringIImmutableStackWrapper))] + [JsonSerializable(typeof(ClassWithPopulatedDictionaryAndSetter))] + [JsonSerializable(typeof(StringIImmutableListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithObjectImmutableArray))] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(StringIImmutableSetWrapper))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(ICollection>))] + [JsonSerializable(typeof(SimpleTestClassWithStringIEnumerableWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringIReadOnlyCollectionWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringIReadOnlyListWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStringToStringIReadOnlyDictionaryWrapper))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(GenericICollectionWrapper>))] + [JsonSerializable(typeof(GenericIEnumerableWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIEnumerableWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericICollectionWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericICollectionWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericIListWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIListWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericISetWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericISetWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericIDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericIDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(StringToStringIReadOnlyDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(StringToStringIReadOnlyDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericListWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericListWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericQueueWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericQueueWrapperInternalConstructor))] + [JsonSerializable(typeof(GenericStackWrapperPrivateConstructor))] + [JsonSerializable(typeof(GenericStackWrapperInternalConstructor))] + [JsonSerializable(typeof(StringToGenericDictionaryWrapperPrivateConstructor))] + [JsonSerializable(typeof(StringToGenericDictionaryWrapperInternalConstructor))] + [JsonSerializable(typeof(SimpleTestClassWithNonGenericCollectionWrappers))] + [JsonSerializable(typeof(SimpleTestClassWithIEnumerableWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithICollectionWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithStackWrapper))] + [JsonSerializable(typeof(SimpleTestClassWithQueueWrapper))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(WrapperForIEnumerable))] + [JsonSerializable(typeof(GenericStackWrapper))] + [JsonSerializable(typeof(DictionaryEntry))] + [JsonSerializable(typeof(Dictionary[]))] + [JsonSerializable(typeof(GenericQueueWrapper))] + [JsonSerializable(typeof(WrapperForIEnumerablePrivateConstructor))] + [JsonSerializable(typeof(WrapperForIEnumerableInternalConstructor))] + [JsonSerializable(typeof(WrapperForICollectionPrivateConstructor))] + [JsonSerializable(typeof(WrapperForICollectionInternalConstructor))] + [JsonSerializable(typeof(WrapperForIListPrivateConstructor))] + [JsonSerializable(typeof(WrapperForIListInternalConstructor))] + [JsonSerializable(typeof(WrapperForIDictionaryPrivateConstructor))] + [JsonSerializable(typeof(WrapperForIDictionaryInternalConstructor))] + [JsonSerializable(typeof(IDerivedICollectionOfT))] + [JsonSerializable(typeof(IDerivedIList))] + [JsonSerializable(typeof(IDerivedISetOfT))] + [JsonSerializable(typeof(ReadOnlyWrapperForIList))] + [JsonSerializable(typeof(ReadOnlyStringIListWrapper))] + [JsonSerializable(typeof(ReadOnlyStringICollectionWrapper))] + [JsonSerializable(typeof(ReadOnlyStringISetWrapper))] + [JsonSerializable(typeof(ReadOnlyWrapperForIDictionary))] + [JsonSerializable(typeof(ReadOnlyStringToStringIDictionaryWrapper))] + [JsonSerializable(typeof(Dictionary[]))] + internal sealed partial class CollectionTestsContext_Default : JsonSerializerContext + { + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs similarity index 70% rename from src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs rename to src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs index dfb968371a2..c119e7fe504 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapperForString_SourceGen.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using System.Text.Json.Serialization.Tests; @@ -8,12 +9,12 @@ using System.Threading.Tasks; namespace System.Text.Json.SourceGeneration.Tests { - internal sealed class JsonSerializerWrapperForString_SourceGen : JsonSerializerWrapperForString + internal sealed class StringSerializerWrapper : JsonSerializerWrapperForString { private readonly JsonSerializerContext _defaultContext; private readonly Func _customContextCreator; - public JsonSerializerWrapperForString_SourceGen(JsonSerializerContext defaultContext, Func customContextCreator) + public StringSerializerWrapper(JsonSerializerContext defaultContext, Func customContextCreator) { _defaultContext = defaultContext ?? throw new ArgumentNullException(nameof(defaultContext)); _customContextCreator = customContextCreator ?? throw new ArgumentNullException(nameof(defaultContext)); @@ -37,6 +38,13 @@ namespace System.Text.Json.SourceGeneration.Tests protected internal override Task SerializeWrapper(T value, JsonSerializerOptions? options = null) { + Type runtimeType = GetRuntimeType(value); + + if (runtimeType != typeof(T)) + { + return SerializeWrapper(value, runtimeType, options); + } + if (options != null) { return Task.FromResult(Serialize(value, options)); @@ -53,6 +61,16 @@ namespace System.Text.Json.SourceGeneration.Tests return JsonSerializer.Serialize(value, typeInfo); } + private static Type GetRuntimeType(in TValue value) + { + if (typeof(TValue) == typeof(object) && value != null) + { + return value.GetType(); + } + + return typeof(TValue); + } + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) => throw new NotImplementedException(); @@ -99,4 +117,14 @@ namespace System.Text.Json.SourceGeneration.Tests protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) => throw new NotImplementedException(); } + + internal sealed class StreamSerializerWrapper : JsonSerializerWrapperForStream + { + protected internal override Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null) => throw new NotImplementedException(); + protected internal override Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null) => throw new NotImplementedException(); + protected internal override Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); + protected internal override Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null) => throw new NotImplementedException(); + protected internal override Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null) => throw new NotImplementedException(); + protected internal override Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); + } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs index 6ebb90f030c..cc9b3d61710 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs @@ -12,11 +12,11 @@ namespace System.Text.Json.SourceGeneration.Tests public partial class PropertyVisibilityTests_Metadata : PropertyVisibilityTests { public PropertyVisibilityTests_Metadata() - : this(new JsonSerializerWrapperForString_SourceGen(PropertyVisibilityTestsContext_Metadata.Default, (options) => new PropertyVisibilityTestsContext_Metadata(options))) + : this(new StringSerializerWrapper(PropertyVisibilityTestsContext_Metadata.Default, (options) => new PropertyVisibilityTestsContext_Metadata(options))) { } - protected PropertyVisibilityTests_Metadata(JsonSerializerWrapperForString serializerWrapper) + protected PropertyVisibilityTests_Metadata(Serialization.Tests.JsonSerializerWrapperForString serializerWrapper) : base(serializerWrapper) { } @@ -259,10 +259,9 @@ namespace System.Text.Json.SourceGeneration.Tests } public partial class PropertyVisibilityTests_Default : PropertyVisibilityTests_Metadata - //public partial class PropertyVisibilityTests_Default : PropertyVisibilityTests { public PropertyVisibilityTests_Default() - : base(new JsonSerializerWrapperForString_SourceGen(PropertyVisibilityTestsContext_Default.Default, (options) => new PropertyVisibilityTestsContext_Default(options))) + : base(new StringSerializerWrapper(PropertyVisibilityTestsContext_Default.Default, (options) => new PropertyVisibilityTestsContext_Default(options))) { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs index daa3a4cb71e..a086659da54 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs @@ -52,8 +52,8 @@ namespace System.Text.Json.SourceGeneration.Tests [Fact] public static void DictionaryFastPathPrimitiveValueSupported() { - Assert.NotNull(DictionaryTypeContext.Default.DictionarySystemStringSystemString.Serialize); - Assert.NotNull(DictionaryTypeContext.Default.DictionarySystemStringSystemTextJsonSourceGenerationTestsJsonMessage.Serialize); + Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringString.Serialize); + Assert.NotNull(DictionaryTypeContext.Default.DictionaryStringJsonMessage.Serialize); Assert.NotNull(DictionaryTypeContext.Default.JsonMessage.Serialize); Assert.Null(DictionaryTypeContext.Default.String.Serialize); Assert.Null(DictionaryTypeContext.Default.Int32.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj index 2520aa52a8b..9b8dc0b1592 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent);$(NetFrameworkCurrent) true + true $(NoWarn);SYSLIB0020 @@ -15,6 +16,24 @@ + + + + + + + + + + + + + + + + + + @@ -39,7 +58,8 @@ - + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs index ff110fe3779..50be97c82af 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs @@ -134,4 +134,6 @@ namespace System.Text.Json.SourceGeneration.Tests public string Message { get; set; } public int Length => Message?.Length ?? 0; // Read-only property } + + internal struct MyStruct { } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs index 269469a8748..d30e62a6b7a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs @@ -801,11 +801,6 @@ namespace System.Text.Json } } - private static readonly Regex s_stripWhitespace = new Regex(@"\s+", RegexOptions.Compiled); - - public static string StripWhitespace(this string value) - => s_stripWhitespace.Replace(value, string.Empty); - #if NET6_0_OR_GREATER // This is needed due to the fact that git might normalize line endings when checking-out files public static string NormalizeLineEndings(this string value) => value.ReplaceLineEndings(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs new file mode 100644 index 00000000000..d0a10baf362 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization.Tests +{ + public sealed partial class CollectionTestsDynamic : CollectionTests + { + public CollectionTestsDynamic() : base(JsonSerializerWrapperForString.StringSerializer, JsonSerializerWrapperForStream.AsyncStreamSerializer) { } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.Write.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.Write.cs deleted file mode 100644 index 0cb162bdc73..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.Write.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Concurrent; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void Write_ConcurrentCollection() - { - Assert.Equal(@"[""1""]", JsonSerializer.Serialize(new BlockingCollection { "1" })); - - Assert.Equal(@"[""1""]", JsonSerializer.Serialize(new ConcurrentBag { "1" })); - - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new ConcurrentDictionary { ["key"] = "value" })); - - ConcurrentQueue qc = new ConcurrentQueue(); - qc.Enqueue("1"); - Assert.Equal(@"[""1""]", JsonSerializer.Serialize(qc)); - - ConcurrentStack qs = new ConcurrentStack(); - qs.Push("1"); - Assert.Equal(@"[""1""]", JsonSerializer.Serialize(qs)); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.cs deleted file mode 100644 index 2a737f3c2d6..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Concurrent.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Concurrent; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void Read_ConcurrentCollection() - { - ConcurrentDictionary cd = JsonSerializer.Deserialize>(@"{""key"":""value""}"); - Assert.Equal(1, cd.Count); - Assert.Equal("value", cd["key"]); - - ConcurrentQueue qc = JsonSerializer.Deserialize>(@"[""1""]"); - Assert.Equal(1, qc.Count); - bool found = qc.TryPeek(out string val); - Assert.True(found); - Assert.Equal("1", val); - - ConcurrentStack qs = JsonSerializer.Deserialize>(@"[""1""]"); - Assert.Equal(1, qs.Count); - found = qs.TryPeek(out val); - Assert.True(found); - Assert.Equal("1", val); - } - - [Theory] - [InlineData(typeof(BlockingCollection), @"[""1""]")] // Not supported. Not IList, and we don't detect the add method for this collection. - [InlineData(typeof(ConcurrentBag), @"[""1""]")] // Not supported. Not IList, and we don't detect the add method for this collection. - public static void Read_ConcurrentCollection_Throws(Type type, string json) - { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); - Assert.Contains(type.ToString(), ex.Message); - } - - [Theory] - [InlineData(typeof(GenericConcurrentQueuePrivateConstructor), @"[""1""]")] - [InlineData(typeof(GenericConcurrentQueueInternalConstructor), @"[""1""]")] - [InlineData(typeof(GenericConcurrentStackPrivateConstructor), @"[""1""]")] - [InlineData(typeof(GenericConcurrentStackInternalConstructor), @"[""1""]")] - public static void Read_ConcurrentCollection_NoPublicConstructor_Throws(Type type, string json) - { - NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); - Assert.Contains(type.ToString(), ex.Message); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Read.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Read.cs deleted file mode 100644 index 09b656206b7..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Immutable.Read.cs +++ /dev/null @@ -1,636 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void ReadImmutableArrayOfImmutableArray() - { - ImmutableArray> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableArray l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadImmutableArrayOfArray() - { - ImmutableArray result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadArrayOfImmutableArray() - { - ImmutableArray[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableArray l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadSimpleImmutableArray() - { - ImmutableArray result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadSimpleClassWithImmutableArray() - { - SimpleTestClassWithImmutableArray obj = JsonSerializer.Deserialize(SimpleTestClassWithImmutableArray.s_json); - obj.Verify(); - } - - [Fact] - public static void ReadIImmutableListTOfIImmutableListT() - { - IImmutableList> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (IImmutableList l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadIImmutableListTOfArray() - { - IImmutableList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadArrayOfIIImmutableListT() - { - IImmutableList[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (IImmutableList l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadPrimitiveIImmutableListT() - { - IImmutableList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); - Assert.Throws(() => JsonSerializer.Deserialize(@"[]")); - } - - [Fact] - public static void ReadIImmutableStackTOfIImmutableStackT() - { - IImmutableStack> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 4; - - foreach (IImmutableStack l in result) - { - foreach (int i in l) - { - Assert.Equal(expected--, i); - } - } - } - - [Fact] - public static void ReadIImmutableStackTOfArray() - { - IImmutableStack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 3; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - - expected = 1; - } - } - - [Fact] - public static void ReadArrayOfIIImmutableStackT() - { - IImmutableStack[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 2; - - foreach (IImmutableStack l in result) - { - foreach (int i in l) - { - Assert.Equal(expected--, i); - } - - expected = 4; - } - } - - [Fact] - public static void ReadPrimitiveIImmutableStackT() - { - IImmutableStack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 2; - - foreach (int i in result) - { - Assert.Equal(expected--, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); - Assert.Throws(() => JsonSerializer.Deserialize(@"[]")); - } - - [Fact] - public static void ReadIImmutableQueueTOfIImmutableQueueT() - { - IImmutableQueue> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (IImmutableQueue l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadIImmutableQueueTOfArray() - { - IImmutableQueue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadArrayOfIImmutableQueueT() - { - IImmutableQueue[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (IImmutableQueue l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadPrimitiveIImmutableQueueT() - { - IImmutableQueue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); - Assert.Throws(() => JsonSerializer.Deserialize(@"[]")); - } - - [Fact] - public static void ReadIImmutableSetTOfIImmutableSetT() - { - IImmutableSet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (IImmutableSet l in result) - { - foreach (int i in l) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadIImmutableSetTOfArray() - { - IImmutableSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadArrayOfIImmutableSetT() - { - IImmutableSet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (IImmutableSet l in result) - { - foreach (int i in l) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadPrimitiveIImmutableSetT() - { - IImmutableSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - List expected = new List { 1, 2 }; - - foreach (int i in result) - { - expected.Remove(i); - } - - Assert.Equal(0, expected.Count); - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"",""2""]")); - Assert.Throws(() => JsonSerializer.Deserialize(@"[]")); - } - - [Fact] - public static void ReadImmutableHashSetTOfImmutableHashSetT() - { - ImmutableHashSet> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (ImmutableHashSet l in result) - { - foreach (int i in l) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadImmutableHashSetTOfArray() - { - ImmutableHashSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadArrayOfIImmutableHashSetT() - { - ImmutableHashSet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - List expected = new List { 1, 2, 3, 4 }; - - foreach (ImmutableHashSet l in result) - { - foreach (int i in l) - { - expected.Remove(i); - } - } - - Assert.Equal(0, expected.Count); - } - - [Fact] - public static void ReadPrimitiveImmutableHashSetT() - { - ImmutableHashSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - List expected = new List { 1, 2 }; - - foreach (int i in result) - { - expected.Remove(i); - } - - Assert.Equal(0, expected.Count); - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadImmutableListTOfImmutableListT() - { - ImmutableList> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableList l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadImmutableListTOfArray() - { - ImmutableList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadArrayOfIImmutableListT() - { - ImmutableList[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableList l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadPrimitiveImmutableListT() - { - ImmutableList result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadImmutableStackTOfImmutableStackT() - { - ImmutableStack> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 4; - - foreach (ImmutableStack l in result) - { - foreach (int i in l) - { - Assert.Equal(expected--, i); - } - } - } - - [Fact] - public static void ReadImmutableStackTOfArray() - { - ImmutableStack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 3; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - - expected = 1; - } - } - - [Fact] - public static void ReadArrayOfIImmutableStackT() - { - ImmutableStack[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 2; - - foreach (ImmutableStack l in result) - { - foreach (int i in l) - { - Assert.Equal(expected--, i); - } - - expected = 4; - } - } - - [Fact] - public static void ReadPrimitiveImmutableStackT() - { - ImmutableStack result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 2; - - foreach (int i in result) - { - Assert.Equal(expected--, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadImmutableQueueTOfImmutableQueueT() - { - ImmutableQueue> result = JsonSerializer.Deserialize>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableQueue l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadImmutableQueueTOfArray() - { - ImmutableQueue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (int[] arr in result) - { - foreach (int i in arr) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadArrayOfImmutableQueueT() - { - ImmutableQueue[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableQueue l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadPrimitiveImmutableQueueT() - { - ImmutableQueue result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadArrayOfIImmutableSortedSetT() - { - ImmutableSortedSet[] result = JsonSerializer.Deserialize[]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")); - int expected = 1; - - foreach (ImmutableSortedSet l in result) - { - foreach (int i in l) - { - Assert.Equal(expected++, i); - } - } - } - - [Fact] - public static void ReadPrimitiveImmutableSortedSetT() - { - ImmutableSortedSet result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[1,2]")); - int expected = 1; - - foreach (int i in result) - { - Assert.Equal(expected++, i); - } - - result = JsonSerializer.Deserialize>(Encoding.UTF8.GetBytes(@"[]")); - Assert.Equal(0, result.Count()); - } - - [Fact] - public static void ReadSimpleTestClass_ImmutableCollectionWrappers_Throws() - { - Assert.Throws(() => JsonSerializer.Deserialize(SimpleTestClassWithIImmutableDictionaryWrapper.s_json)); - Assert.Throws(() => JsonSerializer.Deserialize(SimpleTestClassWithImmutableListWrapper.s_json)); - Assert.Throws(() => JsonSerializer.Deserialize(SimpleTestClassWithImmutableStackWrapper.s_json)); - Assert.Throws(() => JsonSerializer.Deserialize(SimpleTestClassWithImmutableQueueWrapper.s_json)); - Assert.Throws(() => JsonSerializer.Deserialize(SimpleTestClassWithImmutableSetWrapper.s_json)); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Read.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Read.cs deleted file mode 100644 index a89c84af28e..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.ObjectModel.Read.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.ObjectModel; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void Read_ObjectModelCollection() - { - Collection c = JsonSerializer.Deserialize>("[true,false]"); - Assert.Equal(2, c.Count); - Assert.True(c[0]); - Assert.False(c[1]); - - // Regression test for https://github.com/dotnet/runtime/issues/30686. - ObservableCollection oc = JsonSerializer.Deserialize>("[true,false]"); - Assert.Equal(2, oc.Count); - Assert.True(oc[0]); - Assert.False(oc[1]); - - SimpleKeyedCollection kc = JsonSerializer.Deserialize("[true]"); - Assert.Equal(1, kc.Count); - Assert.True(kc[0]); - } - - [Fact] - public static void Read_ObjectModelCollection_Throws() - { - // No default constructor. - Assert.Throws(() => JsonSerializer.Deserialize>("[true,false]")); - // No default constructor. - Assert.Throws(() => JsonSerializer.Deserialize>("[true,false]")); - // No default constructor. - Assert.Throws(() => JsonSerializer.Deserialize>(@"{""true"":false}")); - - // Abstract types can't be instantiated. This means there's no default constructor, so the type is not supported for deserialization. - Assert.Throws(() => JsonSerializer.Deserialize>("[true]")); - } - - public class SimpleKeyedCollection : KeyedCollection - { - protected override string GetKeyForItem(bool item) - { - return item.ToString(); - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Read.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Read.cs deleted file mode 100644 index d4566de70ed..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Read.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Specialized; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void Read_SpecializedCollection() - { - BitVector32 bv32 = JsonSerializer.Deserialize(@"{""Data"":4}"); - // Data property is skipped because it doesn't have a setter. - Assert.Equal(0, bv32.Data); - - HybridDictionary hd = JsonSerializer.Deserialize(@"{""key"":""value""}"); - Assert.Equal(1, hd.Count); - Assert.Equal("value", ((JsonElement)hd["key"]).GetString()); - - IOrderedDictionary iod = JsonSerializer.Deserialize(@"{""key"":""value""}"); - Assert.Equal(1, iod.Count); - Assert.Equal("value", ((JsonElement)iod["key"]).GetString()); - - ListDictionary ld = JsonSerializer.Deserialize(@"{""key"":""value""}"); - Assert.Equal(1, ld.Count); - Assert.Equal("value", ((JsonElement)ld["key"]).GetString()); - } - - [Fact] - public static void Read_SpecializedCollection_Throws() - { - // Add method for this collection only accepts strings, even though it only implements IList which usually - // indicates that the element type is typeof(object). - Assert.Throws(() => JsonSerializer.Deserialize(@"[""1"", ""2""]")); - - // Not supported. Not IList, and we don't detect the add method for this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[{""Key"": ""key"",""Value"":""value""}]")); - - // Int key is not allowed. - Assert.Throws(() => JsonSerializer.Deserialize(@"{1:""value""}")); - - // Runtime type in this case is IOrderedDictionary (we don't replace with concrete type), which we can't instantiate. - Assert.Throws(() => JsonSerializer.Deserialize(@"{""first"":""John"",""second"":""Jane"",""third"":""Jet""}")); - - // Not supported. Not IList, and we don't detect the add method for this collection. - Assert.Throws(() => JsonSerializer.Deserialize(@"[""NameValueCollection""]")); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Write.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Write.cs deleted file mode 100644 index 0bc21811e65..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests/CollectionTests.Specialized.Write.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Specialized; -using Xunit; - -namespace System.Text.Json.Serialization.Tests -{ - public static partial class CollectionTests - { - [Fact] - public static void Write_SpecializedCollection() - { - Assert.Equal(@"{""Data"":4}", JsonSerializer.Serialize(new BitVector32(4))); - Assert.Equal(@"{""Data"":4}", JsonSerializer.Serialize(new BitVector32(4))); - - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new HybridDictionary { ["key"] = "value" })); - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new HybridDictionary { ["key"] = "value" })); - - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new OrderedDictionary { ["key"] = "value" })); - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new OrderedDictionary { ["key"] = "value" })); - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new OrderedDictionary { ["key"] = "value" })); - - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new ListDictionary { ["key"] = "value" })); - Assert.Equal(@"{""key"":""value""}", JsonSerializer.Serialize(new ListDictionary { ["key"] = "value" })); - - Assert.Equal(@"[""1"",""2""]", JsonSerializer.Serialize(new StringCollection { "1", "2" })); - Assert.Equal(@"[""1"",""2""]", JsonSerializer.Serialize(new StringCollection { "1", "2" })); - - Assert.Equal(@"[{""Key"":""key"",""Value"":""value""}]", JsonSerializer.Serialize(new StringDictionary { ["key"] = "value" })); - Assert.Equal(@"[{""Key"":""key"",""Value"":""value""}]", JsonSerializer.Serialize(new StringDictionary { ["key"] = "value" })); - - // Element type returned by .GetEnumerator for this type is string, specifically the key. - Assert.Equal(@"[""key""]", JsonSerializer.Serialize(new NameValueCollection { ["key"] = "value" })); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForStream.cs similarity index 77% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs rename to src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForStream.cs index 365bf23a1d2..2856ad4eac9 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializationWrapperForStream.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForStream.cs @@ -10,19 +10,12 @@ namespace System.Text.Json.Serialization.Tests /// /// Base class for wrapping Stream-based JsonSerializer methods which allows tests to run under different configurations. /// - public abstract class JsonSerializationWrapperForStream + public abstract partial class JsonSerializerWrapperForStream { - public static JsonSerializationWrapperForStream AsyncStreamSerializer => new AsyncStreamSerializerWrapper(); - public static JsonSerializationWrapperForStream SyncStreamSerializer => new SyncStreamSerializerWrapper(); + public static JsonSerializerWrapperForStream AsyncStreamSerializer => new AsyncStreamSerializerWrapper(); + public static JsonSerializerWrapperForStream SyncStreamSerializer => new SyncStreamSerializerWrapper(); - protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null); - protected internal abstract Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null); - protected internal abstract Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo); - protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions options = null); - protected internal abstract Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null); - protected internal abstract Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo); - - private class AsyncStreamSerializerWrapper : JsonSerializationWrapperForStream + private class AsyncStreamSerializerWrapper : JsonSerializerWrapperForStream { protected internal override async Task SerializeWrapper(Stream utf8Json, T value, JsonSerializerOptions options = null) { @@ -55,7 +48,7 @@ namespace System.Text.Json.Serialization.Tests } } - private class SyncStreamSerializerWrapper : JsonSerializationWrapperForStream + private class SyncStreamSerializerWrapper : JsonSerializerWrapperForStream { protected internal override Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs deleted file mode 100644 index aaee9f03d81..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString_Dynamic.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; - -namespace System.Text.Json.Serialization.Tests -{ - internal sealed class JsonSerializerWrapperForString_Dynamic - : JsonSerializerWrapperForString - { - protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) - => Task.FromResult(JsonSerializer.Deserialize(json, options)); - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) - => Task.FromResult(JsonSerializer.Deserialize(json, type, options)); - - protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); - - protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) => throw new NotImplementedException(); - - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) - => Task.FromResult(JsonSerializer.Serialize(value, inputType, options)); - - protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) - => Task.FromResult(JsonSerializer.Serialize(value, options)); - - protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) => throw new NotImplementedException(); - - protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) => throw new NotImplementedException(); - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs index 8a6535c4ee0..33f4f5f2bb5 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs @@ -576,22 +576,6 @@ namespace System.Text.Json.Serialization.Tests public int MyObject { get; set; } } - public class UppercaseNamingPolicy : JsonNamingPolicy - { - public override string ConvertName(string name) - { - return name.ToUpperInvariant(); - } - } - - public class NullNamingPolicy : JsonNamingPolicy - { - public override string ConvertName(string name) - { - return null; - } - } - public class EmptyClassWithExtensionProperty { [JsonExtensionData] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs index 85a36e92b9b..71b8830410c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyVisibilityTests.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit; - namespace System.Text.Json.Serialization.Tests { public sealed partial class PropertyVisibilityTestsDynamic : PropertyVisibilityTests { - public PropertyVisibilityTestsDynamic() : base(new JsonSerializerWrapperForString_Dynamic()) { } + public PropertyVisibilityTestsDynamic() : base(JsonSerializerWrapperForString.StringSerializer) { } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs index 91d8f7a667b..b5fa2534860 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.Collections.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs index 5f458dbb147..7d448fa7a50 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Xunit; -using Utf8MemoryStream = System.Text.Json.Tests.Serialization.CollectionTests.Utf8MemoryStream; +using Utf8MemoryStream = System.Text.Json.Serialization.Tests.CollectionTests.Utf8MemoryStream; namespace System.Text.Json.Serialization.Tests { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs index fc85e180dfe..858ececfe49 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/StreamTests.cs @@ -5,12 +5,12 @@ namespace System.Text.Json.Serialization.Tests { public sealed class StreamTests_Async : StreamTests { - public StreamTests_Async() : base(JsonSerializationWrapperForStream.AsyncStreamSerializer) { } + public StreamTests_Async() : base(JsonSerializerWrapperForStream.AsyncStreamSerializer) { } } public sealed class StreamTests_Sync : StreamTests { - public StreamTests_Sync() : base(JsonSerializationWrapperForStream.SyncStreamSerializer) { } + public StreamTests_Sync() : base(JsonSerializerWrapperForStream.SyncStreamSerializer) { } } public abstract partial class StreamTests @@ -18,9 +18,9 @@ namespace System.Text.Json.Serialization.Tests /// /// Wrapper for JsonSerializer's Serialize() and Deserialize() methods. /// - private JsonSerializationWrapperForStream Serializer { get; } + private JsonSerializerWrapperForStream Serializer { get; } - public StreamTests(JsonSerializationWrapperForStream serializer) + public StreamTests(JsonSerializerWrapperForStream serializer) { Serializer = serializer; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 9e117638ddb..d3feb54ba04 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -16,6 +16,24 @@ + + + + + + + + + + + + + + + + + + @@ -77,23 +95,7 @@ - - - - - - - - - - - - - - - - - + @@ -139,9 +141,8 @@ - + - From ce7759e356b289d37e14abd49413201386f77661 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Thu, 15 Jul 2021 11:42:40 -0700 Subject: [PATCH 598/926] Do abortive connection shutdown in MsQuicConnection.Dispose (#55493) * do abortive connection shutdown in MsQuicConnection.Dispose, add test for CloseAsync and Dispose, and remove CloseAsync from RunClientServer as it should not be necessary anymore * CloseAsync is no-op if disposed * more review feedback Co-authored-by: Geoffrey Kizer --- .../MsQuic/MsQuicConnection.cs | 11 +- .../FunctionalTests/QuicConnectionTests.cs | 108 +++++++++++++++++- .../tests/FunctionalTests/QuicTestBase.cs | 29 +++-- 3 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 3eea4d19c69..39bd5e80f6c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -710,6 +710,12 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Stream disposing {disposing}"); + // If we haven't already shutdown gracefully (via a successful CloseAsync call), then force an abortive shutdown. + MsQuicApi.Api.ConnectionShutdownDelegate( + _state.Handle, + QUIC_CONNECTION_SHUTDOWN_FLAGS.SILENT, + 0); + bool releaseHandles = false; lock (_state) { @@ -740,7 +746,10 @@ namespace System.Net.Quic.Implementations.MsQuic // It's unclear how to gracefully wait for a connection to be 100% done. internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) { - ThrowIfDisposed(); + if (_disposed == 1) + { + return default; + } return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAGS.NONE, errorCode); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index 41b8d549ed7..9988961e81e 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -11,6 +11,8 @@ namespace System.Net.Quic.Tests public abstract class QuicConnectionTests : QuicTestBase where T : IQuicImplProviderFactory, new() { + const int ExpectedErrorCode = 1234; + [Fact] public async Task TestConnect() { @@ -39,8 +41,6 @@ namespace System.Net.Quic.Tests [ActiveIssue("https://github.com/dotnet/runtime/issues/55242", TestPlatforms.Linux)] public async Task AcceptStream_ConnectionAborted_ByClient_Throws() { - const int ExpectedErrorCode = 1234; - using var sync = new SemaphoreSlim(0); await RunClientServer( @@ -56,6 +56,110 @@ namespace System.Net.Quic.Tests Assert.Equal(ExpectedErrorCode, ex.ErrorCode); }); } + + private static async Task DoWrites(QuicStream writer, int writeCount) + { + for (int i = 0; i < writeCount; i++) + { + await writer.WriteAsync(new byte[1]); + } + } + + private static async Task DoReads(QuicStream reader, int readCount) + { + for (int i = 0; i < readCount; i++) + { + int bytesRead = await reader.ReadAsync(new byte[1]); + Assert.Equal(1, bytesRead); + } + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOperationAbortedException(int writesBeforeClose) + { + if (typeof(T) == typeof(MockProviderFactory)) + { + return; + } + + using var sync = new SemaphoreSlim(0); + + await RunClientServer( + async clientConnection => + { + using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); + await DoWrites(clientStream, writesBeforeClose); + + // Wait for peer to receive data + await sync.WaitAsync(); + + await clientConnection.CloseAsync(ExpectedErrorCode); + + await Assert.ThrowsAsync(async () => await clientStream.ReadAsync(new byte[1])); + await Assert.ThrowsAsync(async () => await clientStream.WriteAsync(new byte[1])); + }, + async serverConnection => + { + using QuicStream serverStream = await serverConnection.AcceptStreamAsync(); + await DoReads(serverStream, writesBeforeClose); + + sync.Release(); + + // Since the peer did the abort, we should receive the abort error code in the exception. + QuicConnectionAbortedException ex; + ex = await Assert.ThrowsAsync(async () => await serverStream.ReadAsync(new byte[1])); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + ex = await Assert.ThrowsAsync(async () => await serverStream.WriteAsync(new byte[1])); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + }); + } + + [OuterLoop("Depends on IdleTimeout")] + [Theory] + [InlineData(1)] + [InlineData(10)] + public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationAbortedException(int writesBeforeClose) + { + if (typeof(T) == typeof(MockProviderFactory)) + { + return; + } + + // Set a short idle timeout so that after we dispose the connection, the peer will discover the connection is dead before too long. + QuicListenerOptions listenerOptions = CreateQuicListenerOptions(); + listenerOptions.IdleTimeout = TimeSpan.FromSeconds(1); + + using var sync = new SemaphoreSlim(0); + + await RunClientServer( + async clientConnection => + { + using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); + await DoWrites(clientStream, writesBeforeClose); + + // Wait for peer to receive data + await sync.WaitAsync(); + + clientConnection.Dispose(); + + await Assert.ThrowsAsync(async () => await clientStream.ReadAsync(new byte[1])); + await Assert.ThrowsAsync(async () => await clientStream.WriteAsync(new byte[1])); + }, + async serverConnection => + { + using QuicStream serverStream = await serverConnection.AcceptStreamAsync(); + await DoReads(serverStream, writesBeforeClose); + + sync.Release(); + + // The client has done an abortive shutdown of the connection, which means we are not notified that the connection has closed. + // But the connection idle timeout should kick in and eventually we will get exceptions. + await Assert.ThrowsAsync(async () => await serverStream.ReadAsync(new byte[1])); + await Assert.ThrowsAsync(async () => await serverStream.WriteAsync(new byte[1])); + }, listenerOptions: listenerOptions); + } } public sealed class QuicConnectionTests_MockProvider : QuicConnectionTests { } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index da2cfb37f41..bcb2bd247ee 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -57,15 +57,21 @@ namespace System.Net.Quic.Tests return new QuicConnection(ImplementationProvider, endpoint, GetSslClientAuthenticationOptions()); } - internal QuicListener CreateQuicListener(int maxUnidirectionalStreams = 100, int maxBidirectionalStreams = 100) + internal QuicListenerOptions CreateQuicListenerOptions() { - var options = new QuicListenerOptions() + return new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), - ServerAuthenticationOptions = GetSslServerAuthenticationOptions(), - MaxUnidirectionalStreams = maxUnidirectionalStreams, - MaxBidirectionalStreams = maxBidirectionalStreams + ServerAuthenticationOptions = GetSslServerAuthenticationOptions() }; + } + + internal QuicListener CreateQuicListener(int maxUnidirectionalStreams = 100, int maxBidirectionalStreams = 100) + { + var options = CreateQuicListenerOptions(); + options.MaxUnidirectionalStreams = maxUnidirectionalStreams; + options.MaxBidirectionalStreams = maxBidirectionalStreams; + return CreateQuicListener(options); } @@ -111,9 +117,12 @@ namespace System.Net.Quic.Tests private QuicListener CreateQuicListener(QuicListenerOptions options) => new QuicListener(ImplementationProvider, options); - internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000) + internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000, QuicListenerOptions listenerOptions = null) { - using QuicListener listener = CreateQuicListener(); + const long ClientCloseErrorCode = 11111; + const long ServerCloseErrorCode = 22222; + + using QuicListener listener = CreateQuicListener(listenerOptions ?? CreateQuicListenerOptions()); using var serverFinished = new SemaphoreSlim(0); using var clientFinished = new SemaphoreSlim(0); @@ -126,18 +135,20 @@ namespace System.Net.Quic.Tests { using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await serverFunction(serverConnection); + serverFinished.Release(); await clientFinished.WaitAsync(); - await serverConnection.CloseAsync(0); + await serverConnection.CloseAsync(ServerCloseErrorCode); }), Task.Run(async () => { using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); await clientConnection.ConnectAsync(); await clientFunction(clientConnection); + clientFinished.Release(); await serverFinished.WaitAsync(); - await clientConnection.CloseAsync(0); + await clientConnection.CloseAsync(ClientCloseErrorCode); }) }.WhenAllOrAnyFailed(millisecondsTimeout); } From 07336810acf3b4e7bdd0fb7da87b54920ea9c382 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 15 Jul 2021 11:47:04 -0700 Subject: [PATCH 599/926] Remove support for deprecated obs-fold in header values (#53505) * Remove support for deprecated obs-fold in header values * Add new-line validation to Well-Known header parsing --- .../src/Resources/Strings.resx | 4 +- .../Http/Headers/AuthenticationHeaderValue.cs | 1 + .../Net/Http/Headers/GenericHeaderParser.cs | 2 +- .../System/Net/Http/Headers/HttpHeaders.cs | 31 +++-- .../Net/Http/Headers/HttpRequestHeaders.cs | 3 +- .../Net/Http/Headers/MediaTypeHeaderParser.cs | 3 - .../Net/Http/Headers/NameValueHeaderValue.cs | 18 ++- .../src/System/Net/Http/HttpRuleParser.cs | 63 ++------- .../Headers/AltSvcHeaderParserTest.cs | 5 - .../Headers/ByteArrayHeaderParserTest.cs | 3 - .../ContentDispositionHeaderValueTest.cs | 56 ++------ .../UnitTests/Headers/DateHeaderParserTest.cs | 4 - .../Headers/EntityTagHeaderValueTest.cs | 51 +------ .../EntityTagParserTest.cs | 10 +- .../GenericHeaderParserTest/HostParserTest.cs | 2 +- .../MailAddressParserTest.cs | 77 ----------- .../NameValueParserTest.cs | 10 +- .../NameValueWithParametersParserTest.cs | 16 ++- .../StringWithQualityParserTest.cs | 8 +- .../TokenListParserTest.cs | 3 +- .../GenericHeaderParserTest/ViaParserTest.cs | 9 +- .../UnitTests/Headers/HeaderUtilitiesTest.cs | 6 +- .../Headers/HttpContentHeadersTest.cs | 5 +- .../UnitTests/Headers/HttpHeadersTest.cs | 127 +++++++++--------- .../Headers/HttpRequestHeadersTest.cs | 27 +++- .../Headers/Int32NumberHeaderParserTest.cs | 12 +- .../Headers/Int64NumberHeaderParserTest.cs | 13 +- .../Headers/MediaTypeHeaderParserTest.cs | 103 +++++++------- .../Headers/MediaTypeHeaderValueTest.cs | 60 ++------- .../MediaTypeWithQualityHeaderValueTest.cs | 88 ++++-------- .../Headers/NameValueHeaderValueTest.cs | 60 +++------ .../NameValueWithParametersHeaderValueTest.cs | 62 +++------ .../Headers/ProductHeaderValueTest.cs | 43 +----- .../Headers/ProductInfoHeaderParserTest.cs | 4 - .../Headers/ProductInfoHeaderValueTest.cs | 51 +------ .../Headers/RangeConditionHeaderValueTest.cs | 35 +---- .../UnitTests/Headers/RangeHeaderValueTest.cs | 38 +----- .../Headers/RetryConditionHeaderValueTest.cs | 37 +---- .../StringWithQualityHeaderValueTest.cs | 66 +++------ .../Headers/TimeSpanHeaderParserTest.cs | 10 +- .../Headers/TransferCodingHeaderParserTest.cs | 16 +-- .../Headers/TransferCodingHeaderValueTest.cs | 65 ++------- ...ransferCodingWithQualityHeaderValueTest.cs | 78 ++--------- .../UnitTests/Headers/ViaHeaderValueTest.cs | 54 +------- .../Headers/WarningHeaderValueTest.cs | 58 +------- .../tests/UnitTests/HttpRuleParserTest.cs | 19 +-- 46 files changed, 403 insertions(+), 1113 deletions(-) delete mode 100644 src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 501ca3c8f97..a6972e36a6b 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -157,7 +157,7 @@ Invalid range. At least one of the two parameters must not be null. - New-line characters in header values must be followed by a white-space character. + New-line characters are not allowed in header values. Cannot write more bytes to the buffer than the configured maximum buffer size: {0}. @@ -217,7 +217,7 @@ The field cannot be longer than {0} characters. - Value for header '{0}' contains invalid new-line characters. Value: '{1}'. + Value for header '{0}' contains new-line characters. Value: '{1}'. The 'q' value is invalid: '{0}'. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs index 2b6538028e1..a3197527d80 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs @@ -38,6 +38,7 @@ namespace System.Net.Http.Headers public AuthenticationHeaderValue(string scheme, string? parameter) { HeaderUtilities.CheckValidToken(scheme, nameof(scheme)); + HttpHeaders.CheckContainsNewLine(parameter); _scheme = scheme; _parameter = parameter; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs index 106c7b3ee96..c6ec5e5b0b7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs @@ -119,7 +119,7 @@ namespace System.Net.Http.Headers /// private static int ParseWithoutValidation(string value, int startIndex, out object? parsedValue) { - if (HttpRuleParser.ContainsInvalidNewLine(value, startIndex)) + if (HttpRuleParser.ContainsNewLine(value, startIndex)) { parsedValue = null; return 0; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs index d52a92875a6..c31e628159d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs @@ -221,7 +221,7 @@ namespace System.Net.Http.Headers internal bool Contains(HeaderDescriptor descriptor) { // We can't just call headerStore.ContainsKey() since after parsing the value the header may not exist - // anymore (if the value contains invalid newline chars, we remove the header). So try to parse the + // anymore (if the value contains newline chars, we remove the header). So try to parse the // header value. return _headerStore != null && TryGetAndParseHeaderInfo(descriptor, out _); } @@ -318,7 +318,7 @@ namespace System.Net.Http.Headers // values. if (!ParseRawHeaderValues(descriptor, info, removeEmptyHeader: false)) { - // We have an invalid header value (contains invalid newline chars). Delete it. + // We have an invalid header value (contains newline chars). Delete it. _headerStore.Remove(descriptor); } else @@ -726,18 +726,17 @@ namespace System.Net.Http.Headers } // At this point all values are either in info.ParsedValue, info.InvalidValue, or were removed since they - // contain invalid newline chars. Reset RawValue. + // contain newline chars. Reset RawValue. info.RawValue = null; - // During parsing, we removed the value since it contains invalid newline chars. Return false to indicate that + // During parsing, we removed the value since it contains newline chars. Return false to indicate that // this is an empty header. If the caller specified to remove empty headers, we'll remove the header before // returning. if ((info.InvalidValue == null) && (info.ParsedValue == null)) { if (removeEmptyHeader) { - // After parsing the raw value, no value is left because all values contain invalid newline - // chars. + // After parsing the raw value, no value is left because all values contain newline chars. Debug.Assert(_headerStore != null); _headerStore.Remove(descriptor); } @@ -754,7 +753,7 @@ namespace System.Net.Http.Headers { foreach (string rawValue in rawValues) { - if (!ContainsInvalidNewLine(rawValue, descriptor.Name)) + if (!ContainsNewLine(rawValue, descriptor.Name)) { AddParsedValue(info, rawValue); } @@ -779,7 +778,7 @@ namespace System.Net.Http.Headers if (descriptor.Parser == null) { - if (!ContainsInvalidNewLine(rawValue, descriptor.Name)) + if (!ContainsNewLine(rawValue, descriptor.Name)) { AddParsedValue(info, rawValue); } @@ -868,7 +867,7 @@ namespace System.Net.Http.Headers } else { - if (!ContainsInvalidNewLine(value, descriptor.Name) && addWhenInvalid) + if (!ContainsNewLine(value, descriptor.Name) && addWhenInvalid) { AddInvalidValue(info, value); } @@ -885,7 +884,7 @@ namespace System.Net.Http.Headers } Debug.Assert(value != null); - if (!ContainsInvalidNewLine(value, descriptor.Name) && addWhenInvalid) + if (!ContainsNewLine(value, descriptor.Name) && addWhenInvalid) { AddInvalidValue(info, value ?? string.Empty); } @@ -973,8 +972,8 @@ namespace System.Net.Http.Headers if (descriptor.Parser == null) { // If we don't have a parser for the header, we consider the value valid if it doesn't contains - // invalid newline characters. We add the values as "parsed value". Note that we allow empty values. - CheckInvalidNewLine(value); + // newline characters. We add the values as "parsed value". Note that we allow empty values. + CheckContainsNewLine(value); AddParsedValue(info, value ?? string.Empty); return; } @@ -1077,22 +1076,22 @@ namespace System.Net.Http.Headers return false; } - private static void CheckInvalidNewLine(string? value) + internal static void CheckContainsNewLine(string? value) { if (value == null) { return; } - if (HttpRuleParser.ContainsInvalidNewLine(value)) + if (HttpRuleParser.ContainsNewLine(value)) { throw new FormatException(SR.net_http_headers_no_newlines); } } - private static bool ContainsInvalidNewLine(string value, string name) + private static bool ContainsNewLine(string value, string name) { - if (HttpRuleParser.ContainsInvalidNewLine(value)) + if (HttpRuleParser.ContainsNewLine(value)) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_http_log_headers_no_newlines, name, value)); return true; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs index f3035cc6687..1731a954bf0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; namespace System.Net.Http.Headers { @@ -102,6 +101,8 @@ namespace System.Net.Http.Headers value = null; } + CheckContainsNewLine(value); + SetOrRemoveParsedValue(KnownHeaders.From.Descriptor, value); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs index cd9884eff16..e8d6e5c15a3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs @@ -7,7 +7,6 @@ namespace System.Net.Http.Headers { internal sealed class MediaTypeHeaderParser : BaseHeaderParser { - private readonly bool _supportsMultipleValues; private readonly Func _mediaTypeCreator; internal static readonly MediaTypeHeaderParser SingleValueParser = new MediaTypeHeaderParser(false, CreateMediaType); @@ -18,8 +17,6 @@ namespace System.Net.Http.Headers : base(supportsMultipleValues) { Debug.Assert(mediaTypeCreator != null); - - _supportsMultipleValues = supportsMultipleValues; _mediaTypeCreator = mediaTypeCreator; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/NameValueHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/NameValueHeaderValue.cs index 61caf768dfa..9ebed777381 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/NameValueHeaderValue.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/NameValueHeaderValue.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Text; namespace System.Net.Http.Headers @@ -362,18 +360,24 @@ namespace System.Net.Http.Headers // Trailing/leading space are not allowed if (value[0] == ' ' || value[0] == '\t' || value[^1] == ' ' || value[^1] == '\t') { - throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value)); + ThrowFormatException(value); } - // If it's not a token we check if it's a valid quoted string - if (HttpRuleParser.GetTokenLength(value, 0) == 0) + if (value[0] == '"') { HttpParseResult parseResult = HttpRuleParser.GetQuotedStringLength(value, 0, out int valueLength); - if ((parseResult == HttpParseResult.Parsed && valueLength != value.Length) || parseResult != HttpParseResult.Parsed) + if (parseResult != HttpParseResult.Parsed || valueLength != value.Length) { - throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value)); + ThrowFormatException(value); } } + else if (HttpRuleParser.ContainsNewLine(value)) + { + ThrowFormatException(value); + } + + static void ThrowFormatException(string value) => + throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value)); } private static NameValueHeaderValue CreateNameValue() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs index 72fed31d318..2e739009928 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Globalization; using System.Text; namespace System.Net.Http @@ -141,20 +140,6 @@ namespace System.Net.Http continue; } - if (c == '\r') - { - // If we have a #13 char, it must be followed by #10 and then at least one SP or HT. - if ((current + 2 < input.Length) && (input[current + 1] == '\n')) - { - char spaceOrTab = input[current + 2]; - if ((spaceOrTab == ' ') || (spaceOrTab == '\t')) - { - current += 3; - continue; - } - } - } - return current - startIndex; } @@ -162,42 +147,9 @@ namespace System.Net.Http return input.Length - startIndex; } - internal static bool ContainsInvalidNewLine(string value) + internal static bool ContainsNewLine(string value, int startIndex = 0) { - return ContainsInvalidNewLine(value, 0); - } - - internal static bool ContainsInvalidNewLine(string value, int startIndex) - { - // Search for newlines followed by non-whitespace: This is not allowed in any header (be it a known or - // custom header). E.g. "value\r\nbadformat: header" is invalid. However "value\r\n goodformat: header" - // is valid: newlines followed by whitespace are allowed in header values. - int current = startIndex; - while (current < value.Length) - { - if (value[current] == '\r') - { - int char10Index = current + 1; - if ((char10Index < value.Length) && (value[char10Index] == '\n')) - { - current = char10Index + 1; - - if (current == value.Length) - { - return true; // We have a string terminating with \r\n. This is invalid. - } - - char c = value[current]; - if ((c != ' ') && (c != '\t')) - { - return true; - } - } - } - current++; - } - - return false; + return value.AsSpan(startIndex).IndexOfAny('\r', '\n') != -1; } internal static int GetNumberLength(string input, int startIndex, bool allowDecimal) @@ -331,7 +283,7 @@ namespace System.Net.Http } // TEXT = - // LWS = [CRLF] 1*( SP | HT ) + // LWS = SP | HT // CTL = // // Since we don't really care about the content of a quoted string or comment, we're more tolerant and @@ -370,8 +322,15 @@ namespace System.Net.Http continue; } + char c = input[current]; + + if (c == '\r' || c == '\n') + { + return HttpParseResult.InvalidFormat; + } + // If we support nested expressions and we find an open-char, then parse the nested expressions. - if (supportsNesting && (input[current] == openChar)) + if (supportsNesting && (c == openChar)) { // Check if we exceeded the number of nested calls. if (nestedCount > MaxNestedCount) diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs index 3349ba6215f..c40eef9eba3 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs @@ -1,14 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Net.Http.Headers; using Xunit; -using Xunit.Sdk; namespace System.Net.Http.Tests { diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs index d5c7147ded0..8a28534b479 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs @@ -1,10 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Net.Http.Headers; -using System.Text; using Xunit; diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs index be4e0e65001..cb44f49b767 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -487,8 +485,8 @@ namespace System.Net.Http.Tests Assert.Equal("custom", value.Parameters.ElementAt(0).Name); Assert.Null(value.Parameters.ElementAt(0).Value); - Assert.Equal(40, ContentDispositionHeaderValue.GetDispositionTypeLength( - "inline ; custom =\r\n \"x\" ; name = myName , next", 0, out result)); + Assert.Equal(38, ContentDispositionHeaderValue.GetDispositionTypeLength( + "inline ; custom = \"x\" ; name = myName , next", 0, out result)); value = (ContentDispositionHeaderValue)result; Assert.Equal("inline", value.DispositionType); Assert.Equal("myName", value.Name); @@ -535,14 +533,14 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline"); - CheckValidParse("\r\n inline ", expected); + CheckValidParse(" inline ", expected); CheckValidParse("inline", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // Content-Disposition parser. expected.Name = "myName"; - CheckValidParse("\r\n inline ; name = myName ", expected); + CheckValidParse(" inline ; name = myName ", expected); CheckValidParse(" inline;name=myName", expected); expected.Name = null; @@ -565,35 +563,7 @@ namespace System.Net.Http.Tests CheckInvalidParse("inline; name=myName,"); CheckInvalidParse("inline; name=my\u4F1AName"); CheckInvalidParse("inline/"); - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline"); - CheckValidTryParse("\r\n inline ", expected); - CheckValidTryParse("inline", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // Content-Disposition parser. - expected.Name = "myName"; - CheckValidTryParse("\r\n inline ; name = myName ", expected); - CheckValidTryParse(" inline;name=myName", expected); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse(""); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(null); - CheckInvalidTryParse("inline\u4F1A"); - CheckInvalidTryParse("inline ,"); - CheckInvalidTryParse("inline,"); - CheckInvalidTryParse("inline; name=myName ,"); - CheckInvalidTryParse("inline; name=myName,"); - CheckInvalidTryParse("text/"); + CheckInvalidParse(" inline ; \r name = myName "); } #region Tests from HenrikN @@ -1209,24 +1179,16 @@ namespace System.Net.Http.Tests { ContentDispositionHeaderValue result = ContentDispositionHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(ContentDispositionHeaderValue.TryParse(input, out result), input); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { ContentDispositionHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, ContentDispositionHeaderValue expectedResult) - { - ContentDispositionHeaderValue result = null; - Assert.True(ContentDispositionHeaderValue.TryParse(input, out result), input); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - ContentDispositionHeaderValue result = null; - Assert.False(ContentDispositionHeaderValue.TryParse(input, out result), input); + Assert.False(ContentDispositionHeaderValue.TryParse(input, out ContentDispositionHeaderValue result), input); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs index 89ce57593e4..ebb2efe6068 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs index 290c53decc6..644c6cb1581 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -140,7 +136,7 @@ namespace System.Net.Http.Tests // Note that even if after a valid tag & whitespace there are invalid characters, GetEntityTagLength() // will return the length of the valid tag and ignore the invalid characters at the end. It is the callers // responsibility to consider the whole string invalid if after a valid ETag there are invalid chars. - Assert.Equal(11, EntityTagHeaderValue.GetEntityTagLength("\"tag\" \r\n !!", 0, out result)); + Assert.Equal(9, EntityTagHeaderValue.GetEntityTagLength("\"tag\" !!", 0, out result)); Assert.Equal("\"tag\"", result.Tag); Assert.False(result.IsWeak); @@ -198,7 +194,6 @@ namespace System.Net.Http.Tests { CheckValidParse("\"tag\"", new EntityTagHeaderValue("\"tag\"")); CheckValidParse(" \"tag\" ", new EntityTagHeaderValue("\"tag\"")); - CheckValidParse("\r\n \"tag\"\r\n ", new EntityTagHeaderValue("\"tag\"")); CheckValidParse("\"tag\"", new EntityTagHeaderValue("\"tag\"")); CheckValidParse("\"tag\u4F1A\"", new EntityTagHeaderValue("\"tag\u4F1A\"")); CheckValidParse("W/\"tag\"", new EntityTagHeaderValue("\"tag\"", true)); @@ -217,32 +212,8 @@ namespace System.Net.Http.Tests CheckInvalidParse("\"tag\" \"tag2\""); CheckInvalidParse("/\"tag\""); CheckInvalidParse("*"); // "any" is not allowed as ETag value. - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse("\"tag\"", new EntityTagHeaderValue("\"tag\"")); - CheckValidTryParse(" \"tag\" ", new EntityTagHeaderValue("\"tag\"")); - CheckValidTryParse("\r\n \"tag\"\r\n ", new EntityTagHeaderValue("\"tag\"")); - CheckValidTryParse("\"tag\"", new EntityTagHeaderValue("\"tag\"")); - CheckValidTryParse("\"tag\u4F1A\"", new EntityTagHeaderValue("\"tag\u4F1A\"")); - CheckValidTryParse("W/\"tag\"", new EntityTagHeaderValue("\"tag\"", true)); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" !"); - CheckInvalidTryParse("tag\" !"); - CheckInvalidTryParse("!\"tag\""); - CheckInvalidTryParse("\"tag\","); - CheckInvalidTryParse("\"tag\" \"tag2\""); - CheckInvalidTryParse("/\"tag\""); - CheckInvalidTryParse("*"); // "any" is not allowed as ETag value. + CheckInvalidParse("\r\n \"tag\"\r\n "); + CheckInvalidParse("\"tag\" \r\n !!"); } #region Helper methods @@ -251,24 +222,16 @@ namespace System.Net.Http.Tests { EntityTagHeaderValue result = EntityTagHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(EntityTagHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { EntityTagHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, EntityTagHeaderValue expectedResult) - { - EntityTagHeaderValue result = null; - Assert.True(EntityTagHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - EntityTagHeaderValue result = null; - Assert.False(EntityTagHeaderValue.TryParse(input, out result)); + Assert.False(EntityTagHeaderValue.TryParse(input, out EntityTagHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs index 938fce5615e..3549295f2d4 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -34,8 +30,8 @@ namespace System.Net.Http.Tests CheckValidParsedValue(" * ,", 1, EntityTagHeaderValue.Any, 5, true); CheckValidParsedValue(" \"tag\" ", 0, new EntityTagHeaderValue("\"tag\""), 7, false); CheckValidParsedValue(" \"tag\" ,", 0, new EntityTagHeaderValue("\"tag\""), 8, true); - CheckValidParsedValue("\r\n \"tag\"\r\n ", 0, new EntityTagHeaderValue("\"tag\""), 11, false); - CheckValidParsedValue("\r\n \"tag\"\r\n , ", 0, new EntityTagHeaderValue("\"tag\""), 14, true); + CheckValidParsedValue(" \"tag\" ", 0, new EntityTagHeaderValue("\"tag\""), 7, false); + CheckValidParsedValue(" \"tag\" , ", 0, new EntityTagHeaderValue("\"tag\""), 10, true); CheckValidParsedValue("!\"tag\"", 1, new EntityTagHeaderValue("\"tag\""), 6, false); CheckValidParsedValue("!\"tag\"", 1, new EntityTagHeaderValue("\"tag\""), 6, true); CheckValidParsedValue("//\"tag\u4F1A\"", 2, new EntityTagHeaderValue("\"tag\u4F1A\""), 8, false); @@ -62,6 +58,8 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("\"tag\" \"tag2\"", 0, false); CheckInvalidParsedValue("W/\"tag\"", 1, false); CheckInvalidParsedValue("*", 0, false); // "any" is not allowed as ETag value. + CheckInvalidParsedValue("\r\n \"tag\"\r\n ", 0, false); + CheckInvalidParsedValue("\r\n \"tag\"\r\n , ", 0, false); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs index 91eaa0760a8..c149c59170e 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs @@ -22,7 +22,6 @@ namespace System.Net.Http.Tests { CheckValidParsedValue("host", 0, "host", 4); CheckValidParsedValue(" host ", 0, "host", 6); - CheckValidParsedValue("\r\n host\r\n ", 0, "host", 10); CheckValidParsedValue("!host", 1, "host", 5); CheckValidParsedValue("!host:50", 1, "host:50", 8); CheckValidParsedValue("//host\u4F1A", 2, "host\u4F1A", 7); @@ -54,6 +53,7 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("host ,", 0); CheckInvalidParsedValue("/", 0); CheckInvalidParsedValue(" , ", 0); + CheckInvalidParsedValue(" host\r\n ", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs deleted file mode 100644 index 917c6e0b35b..00000000000 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Mail; -using System.Net.Http.Headers; -using System.Text; - -using Xunit; - -namespace System.Net.Http.Tests -{ - public class MailAddressParserTest - { - [Fact] - public void Properties_ReadValues_MatchExpectation() - { - HttpHeaderParser parser = GenericHeaderParser.MailAddressParser; - Assert.False(parser.SupportsMultipleValues); - Assert.Null(parser.Comparer); - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - // We don't need to validate all possible date values, since they're already tested MailAddress. - // Just make sure the parser calls MailAddressParser with correct parameters (like startIndex must be - // honored). - - // Note that we still have trailing whitespace since we don't do the parsing of the email address. - CheckValidParsedValue("!! info@example.com ", 2, "info@example.com ", 27); - CheckValidParsedValue("\r\n \"My name\" info@example.com", 0, - "\"My name\" info@example.com", 29); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidParsedValue("[info@example.com", 0); - CheckInvalidParsedValue("info@example.com\r\nother", 0); - CheckInvalidParsedValue("info@example.com\r\n other", 0); - CheckInvalidParsedValue("info@example.com\r\n", 0); - CheckInvalidParsedValue("info@example.com,", 0); - CheckInvalidParsedValue("\r\ninfo@example.com", 0); - CheckInvalidParsedValue(null, 0); - CheckInvalidParsedValue(string.Empty, 0); - CheckInvalidParsedValue(" ", 2); - } - - #region Helper methods - - private void CheckValidParsedValue(string input, int startIndex, string expectedResult, - int expectedIndex) - { - HttpHeaderParser parser = GenericHeaderParser.MailAddressParser; - object result = null; - Assert.True(parser.TryParseValue(input, null, ref startIndex, out result), - string.Format("TryParse returned false: {0}", input)); - Assert.Equal(expectedIndex, startIndex); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidParsedValue(string input, int startIndex) - { - HttpHeaderParser parser = GenericHeaderParser.MailAddressParser; - object result = null; - int newIndex = startIndex; - Assert.False(parser.TryParseValue(input, null, ref newIndex, out result), - string.Format("TryParse returned true: {0}", input)); - Assert.Null(result); - Assert.Equal(startIndex, newIndex); - } - #endregion - } -} diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs index 71604d4385d..967cac75c55 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -50,6 +46,12 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("name value", 0); CheckInvalidParsedValue("name=,value", 0); CheckInvalidParsedValue("\u4F1A", 0); + CheckInvalidParsedValue("name=\nvalue", 0); + CheckInvalidParsedValue("name=val\nue", 0); + CheckInvalidParsedValue("name=value\n", 0); + CheckInvalidParsedValue("\"name=\nvalue\"", 0); + CheckInvalidParsedValue("\"name=val\nue\"", 0); + CheckInvalidParsedValue("\"name=value\n\"", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs index 130927ad55b..916302a1bba 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -52,9 +48,9 @@ namespace System.Net.Http.Tests public void TryParse_SetOfValidValueStrings_ParsedCorrectly() { NameValueWithParametersHeaderValue expected = new NameValueWithParametersHeaderValue("custom"); - CheckValidParsedValue("\r\n custom ", 0, expected, 11); + CheckValidParsedValue(" custom ", 0, expected, 9); CheckValidParsedValue("custom", 0, expected, 6); - CheckValidParsedValue(",, ,\r\n custom , chunked", 0, expected, 17); + CheckValidParsedValue(",, , custom , chunked", 0, expected, 15); // Note that even if the whole string is invalid, the first "Expect" value is valid. When the parser // gets called again using the result-index (9), then it fails: I.e. we have 1 valid "Expect" value @@ -65,7 +61,7 @@ namespace System.Net.Http.Tests // The purpose of this test is to verify that these other parsers are combined correctly to build a // transfer-coding parser. expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidParsedValue("\r\n custom ; name = value ", 0, expected, 28); + CheckValidParsedValue(" custom ; name = value ", 0, expected, 26); CheckValidParsedValue(" custom;name=value", 2, expected, 19); CheckValidParsedValue(" custom ; name=value", 2, expected, 21); @@ -81,6 +77,12 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("custom\u4F1A", 0); CheckInvalidParsedValue("custom; name=value;", 0); CheckInvalidParsedValue("custom; name1=value1; name2=value2;", 0); + CheckInvalidParsedValue("\r\n custom ", 0); + CheckInvalidParsedValue(",, ,\r\n custom , chunked", 0); + CheckInvalidParsedValue("\r\n custom ; name = value ", 0); + CheckInvalidParsedValue("custom; name=value\r", 0); + CheckInvalidParsedValue("custom; name=value;\r", 0); + CheckInvalidParsedValue("custom; name=value;\r foo=bar", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs index ef1265db996..177d773686f 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs @@ -26,12 +26,12 @@ namespace System.Net.Http.Tests { CheckValidParsedValue("text", 0, new StringWithQualityHeaderValue("text"), 4); CheckValidParsedValue("text,", 0, new StringWithQualityHeaderValue("text"), 5); - CheckValidParsedValue("\r\n text ; q = 0.5, next_text ", 0, new StringWithQualityHeaderValue("text", 0.5), 19); + CheckValidParsedValue(" text ; q = 0.5, next_text ", 0, new StringWithQualityHeaderValue("text", 0.5), 17); CheckValidParsedValue(" text,next_text ", 2, new StringWithQualityHeaderValue("text"), 7); CheckValidParsedValue(" ,, text, , ,next", 0, new StringWithQualityHeaderValue("text"), 13); CheckValidParsedValue(" ,, text, , ,", 0, new StringWithQualityHeaderValue("text"), 13); - CheckValidParsedValue(", \r\n text \r\n ; \r\n q = 0.123", 0, - new StringWithQualityHeaderValue("text", 0.123), 27); + CheckValidParsedValue(", text ; q = 0.123", 0, + new StringWithQualityHeaderValue("text", 0.123), 21); CheckValidParsedValue(null, 0, null, 0); CheckValidParsedValue(string.Empty, 0, null, 0); @@ -52,6 +52,8 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("t;q\u4F1A=1", 0); CheckInvalidParsedValue("t y", 0); CheckInvalidParsedValue("t;q=1 y", 0); + CheckInvalidParsedValue("\r\n text ; q = 0.5, next_text ", 0); + CheckInvalidParsedValue(", \r\n text \r\n ; \r\n q = 0.123", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs index d6d1dd15c8b..3500fb9503c 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs @@ -22,7 +22,7 @@ namespace System.Net.Http.Tests { CheckValidParsedValue("text", 0, "text", 4); CheckValidParsedValue("text,", 0, "text", 5); - CheckValidParsedValue("\r\n text , next_text ", 0, "text", 10); + CheckValidParsedValue(" text , next_text ", 0, "text", 8); CheckValidParsedValue(" text,next_text ", 2, "text", 7); CheckValidParsedValue(" ,, text, , ,next", 0, "text", 13); CheckValidParsedValue(" ,, text, , ,", 0, "text", 13); @@ -39,6 +39,7 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("te\u00E4xt", 0); CheckInvalidParsedValue("text\u4F1A", 0); CheckInvalidParsedValue("\u4F1A", 0); + CheckInvalidParsedValue("\r\n text , next_text ", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs index 330b7305dec..155e5b30997 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -29,8 +25,8 @@ namespace System.Net.Http.Tests public void TryParse_SetOfValidValueStrings_ParsedCorrectly() { CheckValidParsedValue("X , , 1.1 host, ,next", 1, new ViaHeaderValue("1.1", "host"), 19); - CheckValidParsedValue("X HTTP / x11 192.168.0.1\r\n (comment) , ,next", 1, - new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)"), 44); + CheckValidParsedValue("X HTTP / x11 192.168.0.1 (comment) , ,next", 1, + new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)"), 42); CheckValidParsedValue(" ,HTTP/1.1 [::1]", 0, new ViaHeaderValue("1.1", "[::1]", "HTTP"), 16); CheckValidParsedValue("1.1 host", 0, new ViaHeaderValue("1.1", "host"), 8); @@ -54,6 +50,7 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("\u4F1A", 0); CheckInvalidParsedValue("HTTP/test [::1]:80\r(comment)", 0); CheckInvalidParsedValue("HTTP/test [::1]:80\n(comment)", 0); + CheckInvalidParsedValue("X HTTP / x11 192.168.0.1 (comment) , ,next", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs index cece26a7c5f..64a29bda743 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -69,7 +65,7 @@ namespace System.Net.Http.Tests [Fact] public void GetNextNonEmptyOrWhitespaceIndex_UseDifferentInput_MatchExpectation() { - CheckGetNextNonEmptyOrWhitespaceIndex("x , , ,, ,\t\r\n , ,x", 1, true, 18, true); + CheckGetNextNonEmptyOrWhitespaceIndex("x , , ,, ,\t , ,x", 1, true, 16, true); CheckGetNextNonEmptyOrWhitespaceIndex("x , , ", 1, false, 4, true); // stop at the second ',' CheckGetNextNonEmptyOrWhitespaceIndex("x , , ", 1, true, 8, true); CheckGetNextNonEmptyOrWhitespaceIndex(" x", 0, true, 1, false); diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs index 6191eb254f3..b50b28b56fd 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs @@ -1,12 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Headers; -using System.Text; using System.Threading.Tasks; using Xunit; @@ -72,7 +69,7 @@ namespace System.Net.Http.Tests public void ContentLength_UseAddMethod_AddedValueCanBeRetrievedUsingProperty() { _headers = new HttpContentHeaders(new ComputeLengthHttpContent(() => { throw new ShouldNotBeInvokedException(); })); - _headers.TryAddWithoutValidation(HttpKnownHeaderNames.ContentLength, " 68 \r\n "); + _headers.TryAddWithoutValidation(HttpKnownHeaderNames.ContentLength, " 68 "); Assert.Equal(68, _headers.ContentLength); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs index f2a4367c704..f56f85ac5bd 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs @@ -329,60 +329,44 @@ namespace System.Net.Http.Tests Assert.Equal(1, headers.Parser.TryParseValueCallCount); } - [Fact] - public void TryAddWithoutValidation_AddValueContainingNewLine_NewLineFollowedByWhitespaceIsOKButNewLineFollowedByNonWhitespaceIsRejected() + [Theory] + [MemberData(nameof(HeaderValuesWithNewLines))] + public void TryAddWithoutValidation_AddValueContainingNewLine_Rejected(string headerValue) { - MockHeaders headers = new MockHeaders(); - - // The header parser rejects both of the following values. Both values contain new line chars. According - // to the RFC, LWS supports newlines followed by whitespace. I.e. the first value gets rejected by the - // parser, but added to the list of invalid values. - headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\n other: value"); // OK, LWS is allowed - Assert.Equal(1, headers.Count()); - Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal(invalidHeaderValue + "\r\n other: value", headers.First().Value.First()); - Assert.Equal(1, headers.Parser.TryParseValueCallCount); + var headers = new HttpRequestHeaders(); // This value is considered invalid (newline char followed by non-whitespace). However, since // TryAddWithoutValidation() only causes the header value to be analyzed when it gets actually accessed, no // exception is thrown. Instead the value is discarded and a warning is logged. - headers.Clear(); - headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\nother:value"); - Assert.False(headers.Contains(headers.Descriptor)); + headers.TryAddWithoutValidation("foo", headerValue); + + Assert.Equal(1, headers.NonValidated.Count); + Assert.Equal(headerValue, headers.NonValidated["foo"].ToString()); + + Assert.False(headers.Contains("foo")); Assert.Equal(0, headers.Count()); - // Adding newline followed by whitespace to a custom header is OK. - headers.Clear(); - headers.TryAddWithoutValidation("custom", "value\r\n other: value"); // OK, LWS is allowed - Assert.Equal(1, headers.Count()); - Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal("value\r\n other: value", headers.First().Value.First()); + // Accessing the header forces parsing and the invalid value is removed + Assert.Equal(0, headers.NonValidated.Count); - // Adding newline followed by non-whitespace chars is invalid. The value is discarded and a warning is - // logged. - headers.Clear(); - headers.TryAddWithoutValidation("custom", "value\r\nother: value"); - Assert.False(headers.Contains("custom")); - Assert.Equal(0, headers.Count()); - - // Also ending a value with newline is invalid. Verify that valid values are added. - headers.Clear(); - headers.Parser.TryParseValueCallCount = 0; - headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "\rvalid"); - headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\n"); - headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "\n," + invalidHeaderValue + "\r\nother"); - Assert.Equal(1, headers.Count()); - Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal(parsedPrefix + "\rvalid", headers.First().Value.First()); - Assert.Equal(4, headers.Parser.TryParseValueCallCount); headers.Clear(); - headers.TryAddWithoutValidation("custom", "value\r\ninvalid"); - headers.TryAddWithoutValidation("custom", "value\r\n valid"); - headers.TryAddWithoutValidation("custom", "validvalue, invalid\r\nvalue"); + headers.TryAddWithoutValidation("foo", new[] { "valid", headerValue }); + + Assert.Equal(1, headers.NonValidated.Count); + HeaderStringValues values = headers.NonValidated["foo"]; + Assert.Equal(2, values.Count); + Assert.Equal(new[] { "valid", headerValue }, values); + Assert.Equal(1, headers.Count()); Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal("value\r\n valid", headers.First().Value.First()); + Assert.Equal("valid", headers.First().Value.First()); + + // Accessing the header forces parsing and the invalid value is removed + Assert.Equal(1, headers.NonValidated.Count); + values = headers.NonValidated["foo"]; + Assert.Equal(1, values.Count); + Assert.Equal("valid", values.ToString()); } [Fact] @@ -811,22 +795,23 @@ namespace System.Net.Http.Tests Assert.Equal(3, headers.GetValues("CuStOm-HeAdEr").Count()); } - [Fact] - public void Add_AddValueContainingNewLine_NewLineFollowedByWhitespaceIsOKButNewLineFollowedByNonWhitespaceIsRejected() + [Theory] + [MemberData(nameof(HeaderValuesWithNewLines))] + public void Add_AddValueContainingNewLine_Rejected(string headerValue) { - MockHeaders headers = new MockHeaders(); + var headers = new HttpRequestHeaders(); + + Assert.Throws(() => headers.Add("foo", headerValue)); + Assert.Equal(0, headers.Count()); + Assert.Equal(0, headers.NonValidated.Count); headers.Clear(); - headers.Add("custom", "value\r"); + Assert.Throws(() => headers.Add("foo", new[] { "valid", headerValue })); Assert.Equal(1, headers.Count()); Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal("value\r", headers.First().Value.First()); - - headers.Clear(); - Assert.Throws(() => { headers.Add("custom", new string[] { "valid\n", "invalid\r\nother" }); }); - Assert.Equal(1, headers.Count()); - Assert.Equal(1, headers.First().Value.Count()); - Assert.Equal("valid\n", headers.First().Value.First()); + Assert.Equal("valid", headers.First().Value.First()); + Assert.Equal(1, headers.NonValidated.Count); + Assert.Equal("valid", headers.NonValidated["foo"].ToString()); } [Fact] @@ -996,11 +981,11 @@ namespace System.Net.Http.Tests } [Fact] - public void RemoveParsedValue_FirstAddInvalidNewlineCharsValueThenCallRemoveParsedValue_HeaderRemoved() + public void RemoveParsedValue_FirstAddNewlineCharsValueThenCallRemoveParsedValue_HeaderRemoved() { MockHeaders headers = new MockHeaders(); - // Add header value with invalid newline chars. + // Add header value with newline chars. headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid"); Assert.Equal(0, headers.Parser.TryParseValueCallCount); @@ -1011,11 +996,11 @@ namespace System.Net.Http.Tests } [Fact] - public void RemoveParsedValue_FirstAddInvalidNewlineCharsValueThenAddValidValueThenCallAddParsedValue_HeaderRemoved() + public void RemoveParsedValue_FirstAddNewlineCharsValueThenAddValidValueThenCallAddParsedValue_HeaderRemoved() { MockHeaders headers = new MockHeaders(); - // Add header value with invalid newline chars. + // Add header value with newline chars. headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid"); headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "1"); @@ -1407,11 +1392,11 @@ namespace System.Net.Http.Tests } [Fact] - public void GetParsedValues_GetParsedValuesForKnownHeaderWithInvalidNewlineChars_ReturnsNull() + public void GetParsedValues_GetParsedValuesForKnownHeaderWithNewlineChars_ReturnsNull() { MockHeaders headers = new MockHeaders(); - // Add header value with invalid newline chars. + // Add header value with newline chars. headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid"); Assert.Equal(0, headers.Parser.TryParseValueCallCount); @@ -1436,7 +1421,6 @@ namespace System.Net.Http.Tests MockHeaderParser parser = new MockHeaderParser("---"); MockHeaders headers = new MockHeaders(parser); - // Add header value with invalid newline chars. headers.Add(headers.Descriptor, rawPrefix + "1"); headers.TryAddWithoutValidation(headers.Descriptor, "value2,value3"); headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue); @@ -1464,7 +1448,6 @@ namespace System.Net.Http.Tests MockHeaderParser parser = new MockHeaderParser(true); MockHeaders headers = new MockHeaders(parser); - // Add header value with invalid newline chars. headers.Add(headers.Descriptor, rawPrefix + "1"); headers.Add("header2", "value2"); headers.Add("header3", (string)null); @@ -1622,12 +1605,12 @@ namespace System.Net.Http.Tests Assert.True(headers.Contains(headers.Descriptor)); // Contains() should trigger parsing of values added with TryAddWithoutValidation(): If the value was invalid, - // i.e. contains invalid newline chars, then the header will be removed from the collection. + // i.e. contains newline chars, then the header will be removed from the collection. Assert.Equal(1, headers.Parser.TryParseValueCallCount); } [Fact] - public void Contains_AddValuesWithInvalidNewlineChars_HeadersGetRemovedWhenCallingContains() + public void Contains_AddValuesWithNewlineChars_HeadersGetRemovedWhenCallingContains() { MockHeaders headers = new MockHeaders(); @@ -1794,11 +1777,11 @@ namespace System.Net.Http.Tests } [Fact] - public void AddParsedValue_FirstAddInvalidNewlineCharsValueThenCallAddParsedValue_ParsedValueAdded() + public void AddParsedValue_FirstAddNewlineCharsValueThenCallAddParsedValue_ParsedValueAdded() { MockHeaders headers = new MockHeaders(); - // Add header value with invalid newline chars. + // Add header value with newline chars. headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid"); Assert.Equal(0, headers.Parser.TryParseValueCallCount); @@ -1811,11 +1794,11 @@ namespace System.Net.Http.Tests } [Fact] - public void AddParsedValue_FirstAddInvalidNewlineCharsValueThenAddValidValueThenCallAddParsedValue_ParsedValueAdded() + public void AddParsedValue_FirstAddNewlineCharsValueThenAddValidValueThenCallAddParsedValue_ParsedValueAdded() { MockHeaders headers = new MockHeaders(); - // Add header value with invalid newline chars. + // Add header value with newline chars. headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid"); headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "0"); @@ -2212,6 +2195,16 @@ namespace System.Net.Http.Tests yield return new object[] { "invalid}header" }; } + public static IEnumerable HeaderValuesWithNewLines() + { + foreach (string pattern in new[] { "*", "*foo", "* foo", "foo*", "foo* ", "foo*bar", "foo* bar" }) + foreach (string newLine in new[] { "\r", "\n", "\r\n" }) + foreach (string prefix in new[] { "", "valid, " }) + { + yield return new object[] { prefix + pattern.Replace("*", newLine) }; + } + } + #region Helper methods private class MockHeaders : HttpHeaders diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpRequestHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpRequestHeadersTest.cs index 4ce88a86089..81fa6560bc6 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpRequestHeadersTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpRequestHeadersTest.cs @@ -1,12 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Net.Mail; -using System.Text; using Xunit; @@ -71,7 +68,7 @@ namespace System.Net.Http.Tests public void Accept_UseAddMethod_AddedValueCanBeRetrievedUsingProperty() { headers.TryAddWithoutValidation("Accept", - ",, , ,,text/plain; charset=iso-8859-1; q=1.0,\r\n */xml; charset=utf-8; q=0.5,,,"); + ",, , ,,text/plain; charset=iso-8859-1; q=1.0, */xml; charset=utf-8; q=0.5,,,"); MediaTypeWithQualityHeaderValue value1 = new MediaTypeWithQualityHeaderValue("text/plain"); value1.CharSet = "iso-8859-1"; @@ -100,6 +97,12 @@ namespace System.Net.Http.Tests Assert.Equal(0, headers.Accept.Count); Assert.Equal(1, headers.GetValues("Accept").Count()); Assert.Equal("text/plain application/xml", headers.GetValues("Accept").First()); + + headers.Clear(); + headers.TryAddWithoutValidation("Accept", "text/plain; charset=iso-8859-1; q=1.0,\r\n */xml; charset=utf-8; q=0.5,,,"); + + Assert.Equal(0, headers.Accept.Count); + Assert.False(headers.Contains("Accept")); } [Fact] @@ -121,7 +124,7 @@ namespace System.Net.Http.Tests [Fact] public void AcceptCharset_UseAddMethod_AddedValueCanBeRetrievedUsingProperty() { - headers.TryAddWithoutValidation("Accept-Charset", ", ,,iso-8859-5 , \r\n utf-8 ; q=0.300 ,,,"); + headers.TryAddWithoutValidation("Accept-Charset", ", ,,iso-8859-5 , utf-8 ; q=0.300 ,,,"); Assert.Equal(new StringWithQualityHeaderValue("iso-8859-5"), headers.AcceptCharset.ElementAt(0)); @@ -142,6 +145,11 @@ namespace System.Net.Http.Tests Assert.Equal(0, headers.AcceptCharset.Count); Assert.Equal(1, headers.GetValues("Accept-Charset").Count()); Assert.Equal("utf-8; q=1; q=0.3", headers.GetValues("Accept-Charset").First()); + + headers.Clear(); + headers.TryAddWithoutValidation("Accept-Charset", "iso-8859-5, \r\n utf-8; q=0.300"); + Assert.Equal(0, headers.AcceptCharset.Count); + Assert.False(headers.Contains("Accept-Charset")); } [Fact] @@ -480,7 +488,7 @@ namespace System.Net.Http.Tests public void TE_UseAddMethod_AddedValueCanBeRetrievedUsingProperty() { headers.TryAddWithoutValidation("TE", - ",custom1; param1=value1; q=1.0,,\r\n custom2; param2=value2; q=0.5 ,"); + ",custom1; param1=value1; q=1.0,, custom2; param2=value2; q=0.5 ,"); TransferCodingWithQualityHeaderValue value1 = new TransferCodingWithQualityHeaderValue("custom1"); value1.Parameters.Add(new NameValueHeaderValue("param1", "value1")); @@ -762,6 +770,13 @@ namespace System.Net.Http.Tests Assert.Equal("info@", headers.GetValues("From").First()); } + [Fact] + public void From_ValueContainsNewLineCharacters_Throws() + { + Assert.Throws(() => headers.From = "Foo\r\nBar"); + Assert.Throws(() => headers.Add("From", "Foo\r\nBar")); + } + [Fact] public void IfModifiedSince_ReadAndWriteProperty_ValueMatchesPriorSetValue() { diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int32NumberHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int32NumberHeaderParserTest.cs index ca38f153c99..a405bab3062 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int32NumberHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int32NumberHeaderParserTest.cs @@ -1,12 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -61,8 +56,6 @@ namespace System.Net.Http.Tests CheckValidParsedValue("1234567890", 0, 1234567890, 10); CheckValidParsedValue("0", 0, 0, 1); CheckValidParsedValue("000015", 0, 15, 6); - CheckValidParsedValue(" 123 \t\r\n ", 0, 123, 9); - CheckValidParsedValue("a 5 \r\n ", 1, 5, 7); CheckValidParsedValue(" 987", 0, 987, 4); CheckValidParsedValue("987 ", 0, 987, 4); CheckValidParsedValue("a456", 1, 456, 4); @@ -89,6 +82,11 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("-123", 0); CheckInvalidParsedValue("123456789012345678901234567890", 0); // value >> Int32.MaxValue CheckInvalidParsedValue("2147483648", 0); // value = Int32.MaxValue + 1 + CheckInvalidParsedValue(" 123 \t\r\n ", 0); + CheckInvalidParsedValue("a 5 \r\n ", 1); + CheckInvalidParsedValue("1\n", 0); + CheckInvalidParsedValue("2\r", 0); + CheckInvalidParsedValue("3\r\n ", 0); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int64NumberHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int64NumberHeaderParserTest.cs index 195917eb89a..a76cc6fbd7a 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int64NumberHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/Int64NumberHeaderParserTest.cs @@ -1,14 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; + namespace System.Net.Http.Tests { public class Int64NumberHeaderParserTest @@ -60,8 +56,6 @@ namespace System.Net.Http.Tests CheckValidParsedValue("123456789012345", 0, 123456789012345, 15); CheckValidParsedValue("0", 0, 0, 1); CheckValidParsedValue("000015", 0, 15, 6); - CheckValidParsedValue(" 123 \t\r\n ", 0, 123, 9); - CheckValidParsedValue("a 5 \r\n ", 1, 5, 7); CheckValidParsedValue(" 987", 0, 987, 4); CheckValidParsedValue("987 ", 0, 987, 4); CheckValidParsedValue("a456", 1, 456, 4); @@ -88,6 +82,11 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("-123", 0); CheckInvalidParsedValue("123456789012345678901234567890", 0); // value >> Int64.MaxValue CheckInvalidParsedValue("9223372036854775808", 0); // value = Int64.MaxValue + 1 + CheckInvalidParsedValue(" 123 \t\r\n ", 0); + CheckInvalidParsedValue("a 5 \r\n ", 1); + CheckInvalidParsedValue("1\n", 0); + CheckInvalidParsedValue("2\r", 0); + CheckInvalidParsedValue("3\r\n ", 0); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderParserTest.cs index 85dc002b13b..f3a0630b29a 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -62,14 +58,12 @@ namespace System.Net.Http.Tests public void TryParse_SetOfValidValueStringsForMediaType_ParsedCorrectly() { MediaTypeHeaderValue expected = new MediaTypeHeaderValue("text/plain"); - CheckValidParsedValue("\r\n text/plain ", 0, expected, 15, false); CheckValidParsedValue("text/plain", 0, expected, 10, false); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // media-type parser. expected.CharSet = "utf-8"; - CheckValidParsedValue("\r\n text / plain ; charset = utf-8 ", 0, expected, 40, false); CheckValidParsedValue(" text/plain;charset=utf-8", 2, expected, 26, false); } @@ -77,9 +71,9 @@ namespace System.Net.Http.Tests public void TryParse_SetOfValidValueStringsForMediaTypeWithQuality_ParsedCorrectly() { MediaTypeWithQualityHeaderValue expected = new MediaTypeWithQualityHeaderValue("text/plain"); - CheckValidParsedValue("\r\n text/plain ", 0, expected, 15, true); CheckValidParsedValue("text/plain", 0, expected, 10, true); - CheckValidParsedValue("\r\n text/plain , next/mediatype", 0, expected, 17, true); + CheckValidParsedValue("text/plain,", 0, expected, 11, true); + CheckValidParsedValue("text/plain , ", 0, expected, 13, true); CheckValidParsedValue("text/plain, next/mediatype", 0, expected, 12, true); CheckValidParsedValue(" ", 0, null, 2, true); CheckValidParsedValue("", 0, null, 0, true); @@ -90,72 +84,87 @@ namespace System.Net.Http.Tests // gets called again using the result-index (13), then it fails: I.e. we have 1 valid media-type and an // invalid one. CheckValidParsedValue("text/plain , invalid", 0, expected, 13, true); + CheckValidParsedValue("text/plain , \r\n", 0, expected, 13, true); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // media-type parser. expected.CharSet = "utf-8"; - CheckValidParsedValue("\r\n text / plain ; charset = utf-8 ", 0, expected, 40, true); CheckValidParsedValue(" text/plain;charset=utf-8", 2, expected, 26, true); - CheckValidParsedValue("\r\n text / plain ; charset = utf-8 , next/mediatype", 0, expected, 43, true); + CheckValidParsedValue(" text/plain;charset=utf-8, ", 1, expected, 28, true); CheckValidParsedValue(" text/plain;charset=utf-8, next/mediatype", 2, expected, 28, true); } + [Fact] + public void TryParse_SetOfValidMultiValueStrings_ParsedCorrectly() + { + // Values that are valid for multi, but invalid for single parser + MediaTypeWithQualityHeaderValue expected = null; + Check(""); + Check(" "); + Check(null); + + expected = new MediaTypeWithQualityHeaderValue("text/plain"); + Check("text/plain ,"); + Check("text/plain,"); + + expected.CharSet = "utf-8"; + Check("text/plain; charset=utf-8 ,"); + Check("text/plain; charset=utf-8,"); + + void Check(string input) + { + CheckValidParsedValue(input, 0, expected, expectedIndex: input?.Length ?? 0, supportsMultipleValues: true); + + CheckInvalidParsedValue(input, 0, supportsMultipleValues: false); + } + } + [Fact] public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() { - CheckInvalidParsedValue("", 0, false); - CheckInvalidParsedValue(" ", 0, false); - CheckInvalidParsedValue(null, 0, false); - CheckInvalidParsedValue("text/plain\u4F1A", 0, true); - CheckInvalidParsedValue("text/plain\u4F1A", 0, false); - CheckInvalidParsedValue("text/plain ,", 0, false); - CheckInvalidParsedValue("text/plain,", 0, false); - CheckInvalidParsedValue("text/plain; charset=utf-8 ,", 0, false); - CheckInvalidParsedValue("text/plain; charset=utf-8,", 0, false); - CheckInvalidParsedValue("textplain", 0, true); - CheckInvalidParsedValue("textplain", 0, false); - CheckInvalidParsedValue("text/", 0, true); - CheckInvalidParsedValue("text/", 0, false); + CheckInvalid("text/plain\u4F1A", 0); + CheckInvalid("textplain", 0); + CheckInvalid("text/", 0); + CheckInvalid("\r\n text/plain ", 0); + CheckInvalid("\r\n text / plain ; charset = utf-8 ", 1); + CheckInvalid("\r\n text/plain , next/mediatype", 0); + CheckInvalid(" \r text/plain", 0); + CheckInvalid(" \n text/plain", 0); + CheckInvalid(" \r\n text/plain", 0); + CheckInvalid("text/plain , invalid", 12); + CheckInvalid("text/plain , invalid", 13); + CheckInvalid("text/plain , \r ", 12); + CheckInvalid("text/plain , \n ", 12); + CheckInvalid("text/plain , \r\n ", 12); + CheckInvalid("text/plain , \r\n", 13); + + static void CheckInvalid(string input, int startIndex) + { + CheckInvalidParsedValue(input, startIndex, supportsMultipleValues: true); + CheckInvalidParsedValue(input, startIndex, supportsMultipleValues: false); + } } #region Helper methods - private void CheckValidParsedValue(string input, int startIndex, MediaTypeHeaderValue expectedResult, + private static void CheckValidParsedValue(string input, int startIndex, MediaTypeHeaderValue expectedResult, int expectedIndex, bool supportsMultipleValues) { - MediaTypeHeaderParser parser = null; - if (supportsMultipleValues) - { - parser = MediaTypeHeaderParser.MultipleValuesParser; - } - else - { - parser = MediaTypeHeaderParser.SingleValueParser; - } + MediaTypeHeaderParser parser = supportsMultipleValues ? MediaTypeHeaderParser.MultipleValuesParser : MediaTypeHeaderParser.SingleValueParser; - object result = null; - Assert.True(parser.TryParseValue(input, null, ref startIndex, out result), + Assert.True(parser.TryParseValue(input, null, ref startIndex, out object result), string.Format("TryParse returned false. Input: '{0}', Index: {1}", input, startIndex)); Assert.Equal(expectedIndex, startIndex); Assert.Equal(expectedResult, result); } - private void CheckInvalidParsedValue(string input, int startIndex, bool supportsMultipleValues) + private static void CheckInvalidParsedValue(string input, int startIndex, bool supportsMultipleValues) { - MediaTypeHeaderParser parser = null; - if (supportsMultipleValues) - { - parser = MediaTypeHeaderParser.MultipleValuesParser; - } - else - { - parser = MediaTypeHeaderParser.SingleValueParser; - } + MediaTypeHeaderParser parser = supportsMultipleValues ? MediaTypeHeaderParser.MultipleValuesParser : MediaTypeHeaderParser.SingleValueParser; - object result = null; int newIndex = startIndex; - Assert.False(parser.TryParseValue(input, null, ref newIndex, out result), + Assert.False(parser.TryParseValue(input, null, ref newIndex, out object result), string.Format("TryParse returned true. Input: '{0}', Index: {1}", input, startIndex)); Assert.Null(result); Assert.Equal(startIndex, newIndex); diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderValueTest.cs index ed17d1c62a5..88d1a58df08 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeHeaderValueTest.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -228,8 +225,8 @@ namespace System.Net.Http.Tests Assert.Equal("custom", result.Parameters.ElementAt(0).Name); Assert.Null(result.Parameters.ElementAt(0).Value); - Assert.Equal(48, MediaTypeHeaderValue.GetMediaTypeLength( - "text / plain ; custom =\r\n \"x\" ; charset = utf-8 , next/mediatype", 0, DummyCreator, out result)); + Assert.Equal(46, MediaTypeHeaderValue.GetMediaTypeLength( + "text / plain ; custom = \"x\" ; charset = utf-8 , next/mediatype", 0, DummyCreator, out result)); Assert.Equal("text/plain", result.MediaType); Assert.Equal("utf-8", result.CharSet); Assert.Equal(2, result.Parameters.Count); @@ -295,14 +292,14 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { MediaTypeHeaderValue expected = new MediaTypeHeaderValue("text/plain"); - CheckValidParse("\r\n text/plain ", expected); + CheckValidParse(" text/plain ", expected); CheckValidParse("text/plain", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // media-type parser. expected.CharSet = "utf-8"; - CheckValidParse("\r\n text / plain ; charset = utf-8 ", expected); + CheckValidParse(" text / plain ; charset = utf-8 ", expected); CheckValidParse(" text/plain;charset=utf-8", expected); } @@ -319,36 +316,9 @@ namespace System.Net.Http.Tests CheckInvalidParse("text/plain; charset=utf-8,"); CheckInvalidParse("textplain"); CheckInvalidParse("text/"); - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - MediaTypeHeaderValue expected = new MediaTypeHeaderValue("text/plain"); - CheckValidTryParse("\r\n text/plain ", expected); - CheckValidTryParse("text/plain", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // media-type parser. - expected.CharSet = "utf-8"; - CheckValidTryParse("\r\n text / plain ; charset = utf-8 ", expected); - CheckValidTryParse(" text/plain;charset=utf-8", expected); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse(""); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(null); - CheckInvalidTryParse("text/plain\u4F1A"); - CheckInvalidTryParse("text/plain ,"); - CheckInvalidTryParse("text/plain,"); - CheckInvalidTryParse("text/plain; charset=utf-8 ,"); - CheckInvalidTryParse("text/plain; charset=utf-8,"); - CheckInvalidTryParse("textplain"); - CheckInvalidTryParse("text/"); + CheckInvalidParse("\r\n text/plain "); + CheckInvalidParse("\r\n text / plain ; charset = utf-8 "); + CheckInvalidParse("text / plain ; custom =\r\n \"x\" ; charset = utf-8 , next/mediatype"); } #region Helper methods @@ -357,24 +327,16 @@ namespace System.Net.Http.Tests { MediaTypeHeaderValue result = MediaTypeHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(MediaTypeHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { MediaTypeHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, MediaTypeHeaderValue expectedResult) - { - MediaTypeHeaderValue result = null; - Assert.True(MediaTypeHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - MediaTypeHeaderValue result = null; - Assert.False(MediaTypeHeaderValue.TryParse(input, out result)); + Assert.False(MediaTypeHeaderValue.TryParse(input, out MediaTypeHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeWithQualityHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeWithQualityHeaderValueTest.cs index ad8619b07c9..3c119a5cea4 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeWithQualityHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/MediaTypeWithQualityHeaderValueTest.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -116,14 +113,14 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { MediaTypeWithQualityHeaderValue expected = new MediaTypeWithQualityHeaderValue("text/plain"); - CheckValidParse("\r\n text/plain ", expected); CheckValidParse("text/plain", expected); + CheckValidParse(" text/plain ", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // media-type parser. expected.CharSet = "utf-8"; - CheckValidParse("\r\n text / plain ; charset = utf-8 ", expected); + CheckValidParse(" text / plain ; charset = utf-8 ", expected); CheckValidParse(" text/plain;charset=utf-8", expected); MediaTypeWithQualityHeaderValue value1 = new MediaTypeWithQualityHeaderValue("text/plain"); @@ -136,7 +133,7 @@ namespace System.Net.Http.Tests value2.CharSet = "utf-8"; value2.Quality = 0.5; - CheckValidParse("\r\n */xml; charset=utf-8; q=0.5", value2); + CheckValidParse(" */xml; charset=utf-8; q=0.5", value2); } [Fact] @@ -152,56 +149,20 @@ namespace System.Net.Http.Tests CheckInvalidParse("text/plain; charset=utf-8,"); CheckInvalidParse("textplain"); CheckInvalidParse("text/"); + CheckInvalidParse(",, , ,,text/plain; charset=iso-8859-1; q=1.0, */xml; charset=utf-8; q=0.5,,,"); CheckInvalidParse(",, , ,,text/plain; charset=iso-8859-1; q=1.0,\r\n */xml; charset=utf-8; q=0.5,,,"); CheckInvalidParse("text/plain; charset=iso-8859-1; q=1.0, */xml; charset=utf-8; q=0.5"); CheckInvalidParse(" , */xml; charset=utf-8; q=0.5 "); CheckInvalidParse("text/plain; charset=iso-8859-1; q=1.0 , "); - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - MediaTypeWithQualityHeaderValue expected = new MediaTypeWithQualityHeaderValue("text/plain"); - CheckValidTryParse("\r\n text/plain ", expected); - CheckValidTryParse("text/plain", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // media-type parser. - expected.CharSet = "utf-8"; - CheckValidTryParse("\r\n text / plain ; charset = utf-8 ", expected); - CheckValidTryParse(" text/plain;charset=utf-8", expected); - - MediaTypeWithQualityHeaderValue value1 = new MediaTypeWithQualityHeaderValue("text/plain"); - value1.CharSet = "iso-8859-1"; - value1.Quality = 1.0; - - CheckValidTryParse("text/plain; charset=iso-8859-1; q=1.0", value1); - - MediaTypeWithQualityHeaderValue value2 = new MediaTypeWithQualityHeaderValue("*/xml"); - value2.CharSet = "utf-8"; - value2.Quality = 0.5; - - CheckValidTryParse("\r\n */xml; charset=utf-8; q=0.5", value2); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse(""); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(null); - CheckInvalidTryParse("text/plain\u4F1A"); - CheckInvalidTryParse("text/plain ,"); - CheckInvalidTryParse("text/plain,"); - CheckInvalidTryParse("text/plain; charset=utf-8 ,"); - CheckInvalidTryParse("text/plain; charset=utf-8,"); - CheckInvalidTryParse("textplain"); - CheckInvalidTryParse("text/"); - CheckInvalidTryParse(",, , ,,text/plain; charset=iso-8859-1; q=1.0,\r\n */xml; charset=utf-8; q=0.5,,,"); - CheckInvalidTryParse("text/plain; charset=iso-8859-1; q=1.0, */xml; charset=utf-8; q=0.5"); - CheckInvalidTryParse(" , */xml; charset=utf-8; q=0.5 "); - CheckInvalidTryParse("text/plain; charset=iso-8859-1; q=1.0 , "); + CheckInvalidParse("\r\n */xml; charset=utf-8; q=0.5"); + CheckInvalidParse("text/plain\r"); + CheckInvalidParse("text/plain\n"); + CheckInvalidParse("text/plain\r\n"); + CheckInvalidParse("text/plain\r\n "); + CheckInvalidParse("\r text/plain"); + CheckInvalidParse("\n text/plain"); + CheckInvalidParse("\ntext/plain"); + CheckInvalidParse("\r\n text/plain"); } #region Helper methods @@ -210,22 +171,25 @@ namespace System.Net.Http.Tests { MediaTypeWithQualityHeaderValue result = MediaTypeWithQualityHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + result = null; + Assert.True(MediaTypeWithQualityHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); + + // New lines are never allowed + for (int i = 0; i < input.Length; i++) + { + CheckInvalidParse(input.Insert(i, "\r")); + CheckInvalidParse(input.Insert(i, "\n")); + CheckInvalidParse(input.Insert(i, "\r\n")); + CheckInvalidParse(input.Insert(i, "\r\n ")); + } } private void CheckInvalidParse(string input) { Assert.Throws(() => { MediaTypeWithQualityHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, MediaTypeWithQualityHeaderValue expectedResult) - { - MediaTypeWithQualityHeaderValue result = null; - Assert.True(MediaTypeWithQualityHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { MediaTypeWithQualityHeaderValue result = null; Assert.False(MediaTypeWithQualityHeaderValue.TryParse(input, out result)); Assert.Null(result); diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueHeaderValueTest.cs index 00ca0d306c9..dc2270e5455 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueHeaderValueTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -66,6 +62,8 @@ namespace System.Net.Http.Tests [InlineData("\"quoted string with \"two\" quotes\"")] [InlineData("\"")] [InlineData(" ")] + [InlineData("token\r")] + [InlineData("\"token\r\"")] public void Ctor_ValueInvalidFormat_ThrowFormatException(string value) { // When adding values using strongly typed objects, no leading/trailing LWS (whitespace) are allowed. @@ -308,59 +306,31 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,name=\"value\""); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" name = value ", new NameValueHeaderValue("name", "value")); - CheckValidTryParse(" name", new NameValueHeaderValue("name")); - CheckValidTryParse(" name=\"value\"", new NameValueHeaderValue("name", "\"value\"")); - CheckValidTryParse("name=value", new NameValueHeaderValue("name", "value")); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("name[value"); - CheckInvalidTryParse("name=value="); - CheckInvalidTryParse("name=\u4F1A"); - CheckInvalidTryParse("name==value"); - CheckInvalidTryParse("=value"); - CheckInvalidTryParse("name value"); - CheckInvalidTryParse("name=,value"); - CheckInvalidTryParse("\u4F1A"); - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - CheckInvalidTryParse(" , , name = value , "); - CheckInvalidTryParse(" name,"); - CheckInvalidTryParse(" ,name=\"value\""); - } - #region Helper methods private void CheckValidParse(string input, NameValueHeaderValue expectedResult) { NameValueHeaderValue result = NameValueHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(NameValueHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); + + // New lines are never allowed + for (int i = 0; i < input.Length; i++) + { + CheckInvalidParse(input.Insert(i, "\r")); + CheckInvalidParse(input.Insert(i, "\n")); + CheckInvalidParse(input.Insert(i, "\r\n")); + CheckInvalidParse(input.Insert(i, "\r\n ")); + } } private void CheckInvalidParse(string input) { Assert.Throws(() => { NameValueHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, NameValueHeaderValue expectedResult) - { - NameValueHeaderValue result = null; - Assert.True(NameValueHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - NameValueHeaderValue result = null; - Assert.False(NameValueHeaderValue.TryParse(input, out result)); + Assert.False(NameValueHeaderValue.TryParse(input, out NameValueHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueWithParametersHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueWithParametersHeaderValueTest.cs index a1974419611..5aca687e288 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueWithParametersHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/NameValueWithParametersHeaderValueTest.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Linq; using System.Net.Http.Headers; @@ -192,21 +191,25 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { NameValueWithParametersHeaderValue expected = new NameValueWithParametersHeaderValue("custom"); - CheckValidParse("\r\n custom ", expected); + CheckValidParse(" custom ", expected); CheckValidParse("custom", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // transfer-coding parser. expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidParse("\r\n custom ; name = value ", expected); + CheckValidParse(" custom ; name = value ", expected); CheckValidParse(" custom;name=value", expected); CheckValidParse(" custom ; name=value", expected); + + expected.Parameters.Add(new NameValueHeaderValue("foo", "bar")); + CheckValidParse("custom; name=value; foo=bar", expected); } [Fact] public void Parse_SetOfInvalidValueStrings_Throws() { + CheckInvalidParse("custom\n"); CheckInvalidParse("custom\u4F1A"); CheckInvalidParse("custom; name=value;"); CheckInvalidParse("custom; name1=value1; name2=value2;"); @@ -216,58 +219,31 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - NameValueWithParametersHeaderValue expected = new NameValueWithParametersHeaderValue("custom"); - CheckValidTryParse("\r\n custom ", expected); - CheckValidTryParse("custom", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // transfer-coding parser. - expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidTryParse("\r\n custom ; name = value ", expected); - CheckValidTryParse(" custom;name=value", expected); - CheckValidTryParse(" custom ; name=value", expected); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("custom\u4F1A"); - CheckInvalidTryParse("custom; name=value;"); - CheckInvalidTryParse("custom; name1=value1; name2=value2;"); - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, NameValueWithParametersHeaderValue expectedResult) { NameValueWithParametersHeaderValue result = NameValueWithParametersHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(NameValueWithParametersHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); + + // New lines are never allowed + for (int i = 0; i < input.Length; i++) + { + CheckInvalidParse(input.Insert(i, "\r")); + CheckInvalidParse(input.Insert(i, "\n")); + CheckInvalidParse(input.Insert(i, "\r\n")); + CheckInvalidParse(input.Insert(i, "\r\n ")); + } } private void CheckInvalidParse(string input) { Assert.Throws(() => { NameValueWithParametersHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, NameValueWithParametersHeaderValue expectedResult) - { - NameValueWithParametersHeaderValue result = null; - Assert.True(NameValueWithParametersHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - NameValueWithParametersHeaderValue result = null; - Assert.False(NameValueWithParametersHeaderValue.TryParse(input, out result)); + Assert.False(NameValueWithParametersHeaderValue.TryParse(input, out NameValueWithParametersHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductHeaderValueTest.cs index eeea82490b6..e810e572f07 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductHeaderValueTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -166,53 +162,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" y/1 ", new ProductHeaderValue("y", "1")); - CheckValidTryParse(" custom / 1.0 ", new ProductHeaderValue("custom", "1.0")); - CheckValidTryParse("custom / 1.0 ", new ProductHeaderValue("custom", "1.0")); - CheckValidTryParse("custom / 1.0", new ProductHeaderValue("custom", "1.0")); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("product/version="); // only delimiter ',' allowed after last product - CheckInvalidTryParse("product otherproduct"); - CheckInvalidTryParse("product["); - CheckInvalidTryParse("="); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, ProductHeaderValue expectedResult) { ProductHeaderValue result = ProductHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(ProductHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { ProductHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, ProductHeaderValue expectedResult) - { - ProductHeaderValue result = null; - Assert.True(ProductHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - ProductHeaderValue result = null; - Assert.False(ProductHeaderValue.TryParse(input, out result)); + Assert.False(ProductHeaderValue.TryParse(input, out ProductHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderParserTest.cs index b6311144a31..83bb6fa873a 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderValueTest.cs index c6b716f8167..59cacbb496d 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ProductInfoHeaderValueTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -189,61 +185,22 @@ namespace System.Net.Http.Tests CheckInvalidParse("\t"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse("product", new ProductInfoHeaderValue("product", null)); - CheckValidTryParse(" product ", new ProductInfoHeaderValue("product", null)); - - CheckValidTryParse(" (comment) ", new ProductInfoHeaderValue("(comment)")); - - CheckValidTryParse(" Mozilla/5.0 ", new ProductInfoHeaderValue("Mozilla", "5.0")); - CheckValidTryParse(" (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) ", - new ProductInfoHeaderValue("(compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)")); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("p/1.0,"); - CheckInvalidTryParse("p/1.0\r\n"); // for \r\n to be a valid whitespace, it must be followed by space/tab - CheckInvalidTryParse("p/1.0(comment)"); - CheckInvalidTryParse("(comment)["); - - CheckInvalidTryParse(" Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) "); - CheckInvalidTryParse("p/1.0 ="); - - // "User-Agent" and "Server" don't allow empty values (unlike most other headers supporting lists of values) - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse("\t"); - } - #region Helper methods private void CheckValidParse(string input, ProductInfoHeaderValue expectedResult) { ProductInfoHeaderValue result = ProductInfoHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(ProductInfoHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { ProductInfoHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, ProductInfoHeaderValue expectedResult) - { - ProductInfoHeaderValue result = null; - Assert.True(ProductInfoHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - ProductInfoHeaderValue result = null; - Assert.False(ProductInfoHeaderValue.TryParse(input, out result)); + Assert.False(ProductInfoHeaderValue.TryParse(input, out ProductInfoHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeConditionHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeConditionHeaderValueTest.cs index 87182e95da8..edb31cbe158 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeConditionHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeConditionHeaderValueTest.cs @@ -169,49 +169,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(string.Empty); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" \"x\" ", new RangeConditionHeaderValue("\"x\"")); - CheckValidTryParse(" Sun, 06 Nov 1994 08:49:37 GMT ", - new RangeConditionHeaderValue(new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero))); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("\"x\" ,"); // no delimiter allowed - CheckInvalidTryParse("Sun, 06 Nov 1994 08:49:37 GMT ,"); // no delimiter allowed - CheckInvalidTryParse("\"x\" Sun, 06 Nov 1994 08:49:37 GMT"); - CheckInvalidTryParse("Sun, 06 Nov 1994 08:49:37 GMT \"x\""); - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - } - #region Helper methods private void CheckValidParse(string input, RangeConditionHeaderValue expectedResult) { RangeConditionHeaderValue result = RangeConditionHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(RangeConditionHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { RangeConditionHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, RangeConditionHeaderValue expectedResult) - { - RangeConditionHeaderValue result = null; - Assert.True(RangeConditionHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - RangeConditionHeaderValue result = null; - Assert.False(RangeConditionHeaderValue.TryParse(input, out result)); + Assert.False(RangeConditionHeaderValue.TryParse(input, out RangeConditionHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeHeaderValueTest.cs index ee4a8ef1869..463a6fdac95 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RangeHeaderValueTest.cs @@ -170,52 +170,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(string.Empty); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" bytes=1-2 ", new RangeHeaderValue(1, 2)); - - RangeHeaderValue expected = new RangeHeaderValue(); - expected.Unit = "custom"; - expected.Ranges.Add(new RangeItemHeaderValue(null, 5)); - expected.Ranges.Add(new RangeItemHeaderValue(1, 4)); - CheckValidTryParse("custom = - 5 , 1 - 4 ,,", expected); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("bytes=1-2x"); // only delimiter ',' allowed after last range - CheckInvalidTryParse("x bytes=1-2"); - CheckInvalidTryParse("bytes=1-2.4"); - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - } - #region Helper methods private void CheckValidParse(string input, RangeHeaderValue expectedResult) { RangeHeaderValue result = RangeHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(RangeHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { RangeHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, RangeHeaderValue expectedResult) - { - RangeHeaderValue result = null; - Assert.True(RangeHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - RangeHeaderValue result = null; - Assert.False(RangeHeaderValue.TryParse(input, out result)); + Assert.False(RangeHeaderValue.TryParse(input, out RangeHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RetryConditionHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RetryConditionHeaderValueTest.cs index 7ac59bfc93d..e110666a6b8 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/RetryConditionHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/RetryConditionHeaderValueTest.cs @@ -143,6 +143,8 @@ namespace System.Net.Http.Tests CheckInvalidGetRetryConditionLength("", 0); CheckInvalidGetRetryConditionLength(null, 0); + + CheckInvalidGetRetryConditionLength(" 1234567890\n ", 0); } [Fact] @@ -164,49 +166,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(string.Empty); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" 123456789 ", new RetryConditionHeaderValue(new TimeSpan(0, 0, 123456789))); - CheckValidTryParse(" Sun, 06 Nov 1994 08:49:37 GMT ", - new RetryConditionHeaderValue(new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero))); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("123 ,"); // no delimiter allowed - CheckInvalidTryParse("Sun, 06 Nov 1994 08:49:37 GMT ,"); // no delimiter allowed - CheckInvalidTryParse("123 Sun, 06 Nov 1994 08:49:37 GMT"); - CheckInvalidTryParse("Sun, 06 Nov 1994 08:49:37 GMT \"x\""); - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - } - #region Helper methods private void CheckValidParse(string input, RetryConditionHeaderValue expectedResult) { RetryConditionHeaderValue result = RetryConditionHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(RetryConditionHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { RetryConditionHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, RetryConditionHeaderValue expectedResult) - { - RetryConditionHeaderValue result = null; - Assert.True(RetryConditionHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - RetryConditionHeaderValue result = null; - Assert.False(RetryConditionHeaderValue.TryParse(input, out result)); + Assert.False(RetryConditionHeaderValue.TryParse(input, out RetryConditionHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/StringWithQualityHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/StringWithQualityHeaderValueTest.cs index 54cf08c97a8..8e3075c1016 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/StringWithQualityHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/StringWithQualityHeaderValueTest.cs @@ -176,9 +176,9 @@ namespace System.Net.Http.Tests CheckValidParse("text", new StringWithQualityHeaderValue("text")); CheckValidParse("text;q=0.5", new StringWithQualityHeaderValue("text", 0.5)); CheckValidParse("text ; q = 0.5", new StringWithQualityHeaderValue("text", 0.5)); - CheckValidParse("\r\n text ; q = 0.5 ", new StringWithQualityHeaderValue("text", 0.5)); + CheckValidParse(" text ; q = 0.5 ", new StringWithQualityHeaderValue("text", 0.5)); CheckValidParse(" text ", new StringWithQualityHeaderValue("text")); - CheckValidParse(" \r\n text \r\n ; \r\n q = 0.123", new StringWithQualityHeaderValue("text", 0.123)); + CheckValidParse(" text ; q = 0.123", new StringWithQualityHeaderValue("text", 0.123)); } [Fact] @@ -207,67 +207,31 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse("text", new StringWithQualityHeaderValue("text")); - CheckValidTryParse("text;q=0.5", new StringWithQualityHeaderValue("text", 0.5)); - CheckValidTryParse("text ; q = 0.5", new StringWithQualityHeaderValue("text", 0.5)); - CheckValidTryParse("\r\n text ; q = 0.5 ", new StringWithQualityHeaderValue("text", 0.5)); - CheckValidTryParse(" text ", new StringWithQualityHeaderValue("text")); - CheckValidTryParse(" \r\n text \r\n ; \r\n q = 0.123", new StringWithQualityHeaderValue("text", 0.123)); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("text,"); - CheckInvalidTryParse("\r\n text ; q = 0.5, next_text "); - CheckInvalidTryParse(" text,next_text "); - CheckInvalidTryParse(" ,, text, , ,next"); - CheckInvalidTryParse(" ,, text, , ,"); - CheckInvalidTryParse(", \r\n text \r\n ; \r\n q = 0.123"); - CheckInvalidTryParse("te\u00E4xt"); - CheckInvalidTryParse("text\u4F1A"); - CheckInvalidTryParse("\u4F1A"); - CheckInvalidTryParse("t;q=\u4F1A"); - CheckInvalidTryParse("t;q="); - CheckInvalidTryParse("t;q"); - CheckInvalidTryParse("t;\u4F1A=1"); - CheckInvalidTryParse("t;q\u4F1A=1"); - CheckInvalidTryParse("t y"); - CheckInvalidTryParse("t;q=1 y"); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, StringWithQualityHeaderValue expectedResult) { StringWithQualityHeaderValue result = StringWithQualityHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(StringWithQualityHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); + + // New lines are never allowed + for (int i = 0; i < input.Length; i++) + { + CheckInvalidParse(input.Insert(i, "\r")); + CheckInvalidParse(input.Insert(i, "\n")); + CheckInvalidParse(input.Insert(i, "\r\n")); + CheckInvalidParse(input.Insert(i, "\r\n ")); + } } private void CheckInvalidParse(string input) { Assert.Throws(() => { StringWithQualityHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, StringWithQualityHeaderValue expectedResult) - { - StringWithQualityHeaderValue result = null; - Assert.True(StringWithQualityHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - StringWithQualityHeaderValue result = null; - Assert.False(StringWithQualityHeaderValue.TryParse(input, out result)); + Assert.False(StringWithQualityHeaderValue.TryParse(input, out StringWithQualityHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TimeSpanHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TimeSpanHeaderParserTest.cs index 12894aa9a79..fc59f80efcf 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TimeSpanHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TimeSpanHeaderParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -60,8 +56,8 @@ namespace System.Net.Http.Tests CheckValidParsedValue("1234567890", 0, new TimeSpan(0, 0, 1234567890), 10); CheckValidParsedValue("0", 0, new TimeSpan(0, 0, 0), 1); CheckValidParsedValue("000015", 0, new TimeSpan(0, 0, 15), 6); - CheckValidParsedValue(" 123 \t\r\n ", 0, new TimeSpan(0, 0, 123), 9); - CheckValidParsedValue("a 5 \r\n ", 1, new TimeSpan(0, 0, 5), 7); + CheckValidParsedValue(" 123 \t ", 0, new TimeSpan(0, 0, 123), 7); + CheckValidParsedValue("a 5 ", 1, new TimeSpan(0, 0, 5), 5); CheckValidParsedValue(" 987", 0, new TimeSpan(0, 0, 987), 4); CheckValidParsedValue("987 ", 0, new TimeSpan(0, 0, 987), 4); CheckValidParsedValue("a456", 1, new TimeSpan(0, 0, 456), 4); @@ -88,6 +84,8 @@ namespace System.Net.Http.Tests CheckInvalidParsedValue("-123", 0); CheckInvalidParsedValue("123456789012345678901234567890", 0); // value >> Int32.MaxValue CheckInvalidParsedValue("2147483648", 0); // value = Int32.MaxValue + 1 + CheckInvalidParsedValue(" 123 \t\r\n", 0); + CheckInvalidParsedValue("a 5 \r\n ", 0); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderParserTest.cs index a113f5f4a92..79b27b8cec1 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderParserTest.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -60,12 +56,12 @@ namespace System.Net.Http.Tests public void TryParse_SetOfValidValueStrings_ParsedCorrectly() { TransferCodingHeaderValue expected = new TransferCodingHeaderValue("custom"); - CheckValidParsedValue("\r\n custom ", 0, expected, 11); + CheckValidParsedValue(" custom ", 0, expected, 9); CheckValidParsedValue("custom", 0, expected, 6); CheckValidParsedValue(",,custom", 0, expected, 8); CheckValidParsedValue(" , , custom", 0, expected, 11); - CheckValidParsedValue("\r\n custom , chunked", 0, expected, 13); - CheckValidParsedValue("\r\n custom , , , chunked", 0, expected, 17); + CheckValidParsedValue(" custom , chunked", 0, expected, 11); + CheckValidParsedValue(" custom , , , chunked", 0, expected, 15); CheckValidParsedValue(null, 0, null, 0); CheckValidParsedValue(string.Empty, 0, null, 0); @@ -81,8 +77,8 @@ namespace System.Net.Http.Tests // The purpose of this test is to verify that these other parsers are combined correctly to build a // transfer-coding parser. expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidParsedValue("\r\n custom ; name = value ", 0, expected, 28); - CheckValidParsedValue("\r\n , , custom ; name = value ", 0, expected, 32); + CheckValidParsedValue(" custom ; name = value ", 0, expected, 26); + CheckValidParsedValue(" , , custom ; name = value ", 0, expected, 30); CheckValidParsedValue(" custom;name=value", 2, expected, 19); CheckValidParsedValue(" custom ; name=value", 2, expected, 21); } @@ -92,6 +88,8 @@ namespace System.Net.Http.Tests { CheckInvalidParsedValue("custom; name=value;", 0); CheckInvalidParsedValue("custom; name1=value1; name2=value2;", 0); + CheckInvalidParsedValue("custom; \r\n name=value;", 0); + CheckInvalidParsedValue("custom; name1=value1; name2=value2;\r\n", 0); } #region Helper methods diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderValueTest.cs index 5982b6d0c0b..e59958ea421 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingHeaderValueTest.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -171,8 +168,8 @@ namespace System.Net.Http.Tests // of 0. The caller needs to validate if that's OK or not. Assert.Equal(0, TransferCodingHeaderValue.GetTransferCodingLength("\u4F1A", 0, DummyCreator, out result)); - Assert.Equal(45, TransferCodingHeaderValue.GetTransferCodingLength( - " custom ; name1 =\r\n \"value1\" ; name2 = value2 , next", 2, DummyCreator, out result)); + Assert.Equal(43, TransferCodingHeaderValue.GetTransferCodingLength( + " custom ; name1 = \"value1\" ; name2 = value2 , next", 2, DummyCreator, out result)); Assert.Equal("custom", result.Value); Assert.Equal(2, result.Parameters.Count); Assert.Equal("name1", result.Parameters.ElementAt(0).Name); @@ -218,20 +215,23 @@ namespace System.Net.Http.Tests Assert.Equal(0, TransferCodingHeaderValue.GetTransferCodingLength(string.Empty, 0, DummyCreator, out result)); Assert.Null(result); + Assert.Equal(0, TransferCodingHeaderValue.GetTransferCodingLength("\r\nchunked", 0, DummyCreator, + out result)); + Assert.Null(result); } [Fact] public void Parse_SetOfValidValueStrings_ParsedCorrectly() { TransferCodingHeaderValue expected = new TransferCodingHeaderValue("custom"); - CheckValidParse("\r\n custom ", expected); + CheckValidParse(" custom ", expected); CheckValidParse("custom", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // transfer-coding parser. expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidParse("\r\n custom ; name = value ", expected); + CheckValidParse(" custom ; name = value ", expected); CheckValidParse(" custom;name=value", expected); CheckValidParse(" custom ; name=value", expected); } @@ -247,6 +247,7 @@ namespace System.Net.Http.Tests CheckInvalidParse("\r\n custom , , , chunked"); CheckInvalidParse("custom , \u4F1A"); CheckInvalidParse("\r\n , , custom ; name = value "); + CheckInvalidParse(" custom ; \r\n name = value "); CheckInvalidParse(null); CheckInvalidParse(string.Empty); @@ -254,64 +255,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - TransferCodingHeaderValue expected = new TransferCodingHeaderValue("custom"); - CheckValidTryParse("\r\n custom ", expected); - CheckValidTryParse("custom", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // transfer-coding parser. - expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidTryParse("\r\n custom ; name = value ", expected); - CheckValidTryParse(" custom;name=value", expected); - CheckValidTryParse(" custom ; name=value", expected); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("custom; name=value;"); - CheckInvalidTryParse("custom; name1=value1; name2=value2;"); - CheckInvalidTryParse(",,custom"); - CheckInvalidTryParse(" , , custom"); - CheckInvalidTryParse("\r\n custom , chunked"); - CheckInvalidTryParse("\r\n custom , , , chunked"); - CheckInvalidTryParse("custom , \u4F1A"); - CheckInvalidTryParse("\r\n , , custom ; name = value "); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, TransferCodingHeaderValue expectedResult) { TransferCodingHeaderValue result = TransferCodingHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(TransferCodingHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { TransferCodingHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, TransferCodingHeaderValue expectedResult) - { - TransferCodingHeaderValue result = null; - Assert.True(TransferCodingHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - TransferCodingHeaderValue result = null; - Assert.False(TransferCodingHeaderValue.TryParse(input, out result)); + Assert.False(TransferCodingHeaderValue.TryParse(input, out TransferCodingHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingWithQualityHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingWithQualityHeaderValueTest.cs index 0080a3e2ef0..2e36f6b2544 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingWithQualityHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/TransferCodingWithQualityHeaderValueTest.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; -using System.Text; using Xunit; @@ -51,14 +48,14 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { TransferCodingWithQualityHeaderValue expected = new TransferCodingWithQualityHeaderValue("custom"); - CheckValidParse("\r\n custom ", expected); + CheckValidParse(" custom ", expected); CheckValidParse("custom", expected); // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. // The purpose of this test is to verify that these other parsers are combined correctly to build a // transfer-coding parser. expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidParse("\r\n custom ; name = value ", expected); + CheckValidParse(" custom ; name = value ", expected); CheckValidParse(" custom;name=value", expected); CheckValidParse(" custom ; name=value", expected); @@ -73,7 +70,7 @@ namespace System.Net.Http.Tests value2.Parameters.Add(new NameValueHeaderValue("param2", "value2")); value2.Quality = 0.5; - CheckValidParse("\r\n custom2; param2= value2; q =0.5 ", value2); + CheckValidParse(" custom2; param2= value2; q =0.5 ", value2); } [Fact] @@ -91,6 +88,7 @@ namespace System.Net.Http.Tests CheckInvalidParse(",custom1; param1=value1; q=1.0,,\r\n custom2; param2=value2; q=0.5 ,"); CheckInvalidParse("custom1; param1=value1; q=1.0,"); CheckInvalidParse(",\r\n custom2; param2=value2; q=0.5"); + CheckInvalidParse("\r\n custom2; param2= value2; q =0.5 "); CheckInvalidParse(null); CheckInvalidParse(string.Empty); @@ -98,83 +96,25 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - TransferCodingWithQualityHeaderValue expected = new TransferCodingWithQualityHeaderValue("custom"); - CheckValidTryParse("\r\n custom ", expected); - CheckValidTryParse("custom", expected); - - // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. - // The purpose of this test is to verify that these other parsers are combined correctly to build a - // transfer-coding parser. - expected.Parameters.Add(new NameValueHeaderValue("name", "value")); - CheckValidTryParse("\r\n custom ; name = value ", expected); - CheckValidTryParse(" custom;name=value", expected); - CheckValidTryParse(" custom ; name=value", expected); - - - TransferCodingWithQualityHeaderValue value1 = new TransferCodingWithQualityHeaderValue("custom1"); - value1.Parameters.Add(new NameValueHeaderValue("param1", "value1")); - value1.Quality = 1.0; - - CheckValidTryParse("custom1 ; param1 =value1 ; q= 1.0 ", value1); - - TransferCodingWithQualityHeaderValue value2 = new TransferCodingWithQualityHeaderValue("custom2"); - value2.Parameters.Add(new NameValueHeaderValue("param2", "value2")); - value2.Quality = 0.5; - - CheckValidTryParse("\r\n custom2; param2= value2; q =0.5 ", value2); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("custom; name=value;"); - CheckInvalidTryParse("custom; name1=value1; name2=value2;"); - CheckInvalidTryParse(",,custom"); - CheckInvalidTryParse(" , , custom"); - CheckInvalidTryParse("\r\n custom , chunked"); - CheckInvalidTryParse("\r\n custom , , , chunked"); - CheckInvalidTryParse("custom , \u4F1A"); - CheckInvalidTryParse("\r\n , , custom ; name = value "); - - CheckInvalidTryParse(",custom1; param1=value1; q=1.0,,\r\n custom2; param2=value2; q=0.5 ,"); - CheckInvalidTryParse("custom1; param1=value1; q=1.0,"); - CheckInvalidTryParse(",\r\n custom2; param2=value2; q=0.5"); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, TransferCodingWithQualityHeaderValue expectedResult) { TransferCodingWithQualityHeaderValue result = TransferCodingWithQualityHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(TransferCodingWithQualityHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { TransferCodingWithQualityHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, TransferCodingWithQualityHeaderValue expectedResult) - { - TransferCodingWithQualityHeaderValue result = null; - Assert.True(TransferCodingWithQualityHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - TransferCodingWithQualityHeaderValue result = null; - Assert.False(TransferCodingWithQualityHeaderValue.TryParse(input, out result)); + Assert.False(TransferCodingWithQualityHeaderValue.TryParse(input, out TransferCodingWithQualityHeaderValue result)); Assert.Null(result); } + #endregion } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ViaHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ViaHeaderValueTest.cs index 5662201a6a0..902a5fd5ac3 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ViaHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ViaHeaderValueTest.cs @@ -229,7 +229,7 @@ namespace System.Net.Http.Tests public void Parse_SetOfValidValueStrings_ParsedCorrectly() { CheckValidParse(" 1.1 host ", new ViaHeaderValue("1.1", "host")); - CheckValidParse(" HTTP / x11 192.168.0.1\r\n (comment) ", + CheckValidParse(" HTTP / x11 192.168.0.1 (comment) ", new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)")); CheckValidParse(" HTTP/1.1 [::1]", new ViaHeaderValue("1.1", "[::1]", "HTTP")); CheckValidParse("1.1 host", new ViaHeaderValue("1.1", "host")); @@ -258,41 +258,7 @@ namespace System.Net.Http.Tests CheckInvalidParse(string.Empty); CheckInvalidParse(" "); CheckInvalidParse(" ,,"); - } - - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" 1.1 host ", new ViaHeaderValue("1.1", "host")); - CheckValidTryParse(" HTTP / x11 192.168.0.1\r\n (comment) ", - new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)")); - CheckValidTryParse(" HTTP/1.1 [::1]", new ViaHeaderValue("1.1", "[::1]", "HTTP")); - CheckValidTryParse("1.1 host", new ViaHeaderValue("1.1", "host")); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("HTTP/1.1 host (comment)invalid"); - CheckInvalidTryParse("HTTP/1.1 host (comment)="); - CheckInvalidTryParse("HTTP/1.1 host (comment) invalid"); - CheckInvalidTryParse("HTTP/1.1 host (comment) ="); - CheckInvalidTryParse("HTTP/1.1 host invalid"); - CheckInvalidTryParse("HTTP/1.1 host ="); - CheckInvalidTryParse("1.1 host invalid"); - CheckInvalidTryParse("1.1 host ="); - CheckInvalidTryParse("\u4F1A"); - CheckInvalidTryParse("HTTP/test [::1]:80\r(comment)"); - CheckInvalidTryParse("HTTP/test [::1]:80\n(comment)"); - - CheckInvalidTryParse("X , , 1.1 host, ,next"); - CheckInvalidTryParse("X HTTP / x11 192.168.0.1\r\n (comment) , ,next"); - CheckInvalidTryParse(" ,HTTP/1.1 [::1]"); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); + CheckInvalidParse("1=host\n"); } #region Helper methods @@ -301,24 +267,16 @@ namespace System.Net.Http.Tests { ViaHeaderValue result = ViaHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(ViaHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { ViaHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, ViaHeaderValue expectedResult) - { - ViaHeaderValue result = null; - Assert.True(ViaHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - ViaHeaderValue result = null; - Assert.False(ViaHeaderValue.TryParse(input, out result)); + Assert.False(ViaHeaderValue.TryParse(input, out ViaHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/WarningHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/WarningHeaderValueTest.cs index 14cafddc08b..dc867577ea7 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/WarningHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/WarningHeaderValueTest.cs @@ -147,7 +147,7 @@ namespace System.Net.Http.Tests [Fact] public void GetWarningLength_DifferentValidScenarios_AllReturnNonZero() { - CheckGetWarningLength(" 199 .host \r\n \"Miscellaneous warning\" ", 1, 38, + CheckGetWarningLength(" 199 .host \"Miscellaneous warning\" ", 1, 35, new WarningHeaderValue(199, ".host", "\"Miscellaneous warning\"")); CheckGetWarningLength("987 [FE18:AB64::156]:80 \"\" \"Tue, 20 Jul 2010 01:02:03 GMT\"", 0, 58, new WarningHeaderValue(987, "[FE18:AB64::156]:80", "\"\"", @@ -196,6 +196,8 @@ namespace System.Net.Http.Tests CheckInvalidWarningViaLength("123 host \"t\" \"Tue, 20 Jul 2010 01:02:03 GMT", 0); CheckInvalidWarningViaLength("123 host \"t\" \"Tue, 200 Jul 2010 01:02:03 GMT\"", 0); CheckInvalidWarningViaLength("123 host \"t\" \"\"", 0); + + CheckInvalidWarningViaLength(" 199 .host \r\n \"Miscellaneous warning\" \r\n ", 0); } [Fact] @@ -236,68 +238,22 @@ namespace System.Net.Http.Tests CheckInvalidParse(" ,,"); } - [Fact] - public void TryParse_SetOfValidValueStrings_ParsedCorrectly() - { - CheckValidTryParse(" 123 host \"text\"", new WarningHeaderValue(123, "host", "\"text\"")); - CheckValidTryParse(" 50 192.168.0.1 \"text \" \"Tue, 20 Jul 2010 01:02:03 GMT\" ", - new WarningHeaderValue(50, "192.168.0.1", "\"text \"", - new DateTimeOffset(2010, 7, 20, 1, 2, 3, TimeSpan.Zero))); - CheckValidTryParse(" 123 h \"t\"", new WarningHeaderValue(123, "h", "\"t\"")); - CheckValidTryParse("1 h \"t\"", new WarningHeaderValue(1, "h", "\"t\"")); - CheckValidTryParse("1 h \"t\" \"Tue, 20 Jul 2010 01:02:03 GMT\"", - new WarningHeaderValue(1, "h", "\"t\"", - new DateTimeOffset(2010, 7, 20, 1, 2, 3, TimeSpan.Zero))); - CheckValidTryParse("1 \u4F1A \"t\" ", new WarningHeaderValue(1, "\u4F1A", "\"t\"")); - } - - [Fact] - public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() - { - CheckInvalidTryParse("1.1 host \"text\""); - CheckInvalidTryParse("11 host text"); - CheckInvalidTryParse("11 host \"text\" Tue, 20 Jul 2010 01:02:03 GMT"); - CheckInvalidTryParse("11 host \"text\" 123 next \"text\""); - CheckInvalidTryParse("\u4F1A"); - CheckInvalidTryParse("123 \u4F1A"); - CheckInvalidTryParse("111 [::1]:80\r(comment) \"text\""); - CheckInvalidTryParse("111 [::1]:80\n(comment) \"text\""); - - CheckInvalidTryParse("X , , 123 host \"text\", ,next"); - CheckInvalidTryParse("X 50 192.168.0.1 \"text \" \"Tue, 20 Jul 2010 01:02:03 GMT\" , ,next"); - CheckInvalidTryParse(" ,123 h \"t\","); - CheckInvalidTryParse("1 \u4F1A \"t\" ,,"); - - CheckInvalidTryParse(null); - CheckInvalidTryParse(string.Empty); - CheckInvalidTryParse(" "); - CheckInvalidTryParse(" ,,"); - } - #region Helper methods private void CheckValidParse(string input, WarningHeaderValue expectedResult) { WarningHeaderValue result = WarningHeaderValue.Parse(input); Assert.Equal(expectedResult, result); + + Assert.True(WarningHeaderValue.TryParse(input, out result)); + Assert.Equal(expectedResult, result); } private void CheckInvalidParse(string input) { Assert.Throws(() => { WarningHeaderValue.Parse(input); }); - } - private void CheckValidTryParse(string input, WarningHeaderValue expectedResult) - { - WarningHeaderValue result = null; - Assert.True(WarningHeaderValue.TryParse(input, out result)); - Assert.Equal(expectedResult, result); - } - - private void CheckInvalidTryParse(string input) - { - WarningHeaderValue result = null; - Assert.False(WarningHeaderValue.TryParse(input, out result)); + Assert.False(WarningHeaderValue.TryParse(input, out WarningHeaderValue result)); Assert.Null(result); } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpRuleParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpRuleParserTest.cs index 1f0bac64892..707ddae2f46 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpRuleParserTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpRuleParserTest.cs @@ -189,7 +189,6 @@ namespace System.Net.Http.Tests AssertGetQuotedStringLength("\"\\xx\"", 0, 5, HttpParseResult.Parsed); // "\xx" AssertGetQuotedStringLength("\"(x)\"", 0, 5, HttpParseResult.Parsed); // "(x)" AssertGetQuotedStringLength(" \" (x) \" ", 1, 7, HttpParseResult.Parsed); // " (x) " - AssertGetQuotedStringLength("\"text\r\n new line\"", 0, 17, HttpParseResult.Parsed); // "text new line" AssertGetQuotedStringLength("\"a\\\u00FC\\\"b\\\"c\\\"\\\"d\\\"\"", 0, 18, HttpParseResult.Parsed); // "a\\u00FC\"b\"c\"\"d\"" AssertGetQuotedStringLength("\"\\\" \"", 0, 5, HttpParseResult.Parsed); // "\" " } @@ -199,6 +198,7 @@ namespace System.Net.Http.Tests { AssertGetQuotedStringLength("\"x", 0, 0, HttpParseResult.InvalidFormat); // "x AssertGetQuotedStringLength(" \"x ", 1, 0, HttpParseResult.InvalidFormat); // ' "x ' + AssertGetQuotedStringLength("\"text\r\n new line\"", 0, 0, HttpParseResult.InvalidFormat); // "text new line" } [Fact] @@ -224,7 +224,6 @@ namespace System.Net.Http.Tests AssertGetCommentLength("(\\xx)", 0, 5, HttpParseResult.Parsed); // (\xx) AssertGetCommentLength("(\"x\")", 0, 5, HttpParseResult.Parsed); // ("x") AssertGetCommentLength(" ( \"x\" ) ", 1, 7, HttpParseResult.Parsed); // ( "x" ) - AssertGetCommentLength("(text\r\n new line)", 0, 17, HttpParseResult.Parsed); // (text new line) AssertGetCommentLength("(\\) )", 0, 5, HttpParseResult.Parsed); // (\)) AssertGetCommentLength("(\\( )", 0, 5, HttpParseResult.Parsed); // (\() @@ -253,6 +252,7 @@ namespace System.Net.Http.Tests AssertGetCommentLength("((x ", 0, 0, HttpParseResult.InvalidFormat); AssertGetCommentLength("(x(x ", 0, 0, HttpParseResult.InvalidFormat); AssertGetCommentLength("(x(((((((((x ", 0, 0, HttpParseResult.InvalidFormat); + AssertGetCommentLength("(text\r\n new line)", 0, 0, HttpParseResult.InvalidFormat); // To prevent attacker from sending comments resulting in stack overflow exceptions, we limit the depth // of nested comments. I.e. the following comment is considered invalid since it is considered a @@ -284,15 +284,18 @@ namespace System.Net.Http.Tests Assert.Equal(1, HttpRuleParser.GetWhitespaceLength("a\t", 1)); Assert.Equal(3, HttpRuleParser.GetWhitespaceLength("a\t ", 1)); Assert.Equal(2, HttpRuleParser.GetWhitespaceLength("\t b", 0)); + } - // Newlines - Assert.Equal(3, HttpRuleParser.GetWhitespaceLength("a\r\n b", 1)); - Assert.Equal(3, HttpRuleParser.GetWhitespaceLength("\r\n ", 0)); - Assert.Equal(3, HttpRuleParser.GetWhitespaceLength("\r\n\t", 0)); - Assert.Equal(13, HttpRuleParser.GetWhitespaceLength(" \r\n\t\t \r\n ", 0)); + [Fact] + public void GetWhitespaceLength_NewLines_NotAllowed() + { + Assert.Equal(0, HttpRuleParser.GetWhitespaceLength("a\r\n b", 1)); + Assert.Equal(0, HttpRuleParser.GetWhitespaceLength("\r\n ", 0)); + Assert.Equal(0, HttpRuleParser.GetWhitespaceLength("\r\n\t", 0)); + Assert.Equal(2, HttpRuleParser.GetWhitespaceLength(" \r\n\t\t \r\n ", 0)); Assert.Equal(1, HttpRuleParser.GetWhitespaceLength(" \r\n", 0)); // first char considered valid whitespace Assert.Equal(1, HttpRuleParser.GetWhitespaceLength(" \r\n\r\n ", 0)); - Assert.Equal(3, HttpRuleParser.GetWhitespaceLength(" \r\n\r\n ", 3)); + Assert.Equal(0, HttpRuleParser.GetWhitespaceLength(" \r\n\r\n ", 3)); } [Fact] From 9cfb02a1bb5772c58c858a70dba5cd684cc2b9fa Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Thu, 15 Jul 2021 21:55:09 +0300 Subject: [PATCH 600/926] Use local assertion prop to omit casts on returns (#55632) When inserting normalizing casts for returns in pre-order morph, "fgMorphCast" was used, which did not account for the fact that local assertion propagation could tell us that the cast is in fact unnecessary. Fix this by calling "fgMorphTree" instead. Also some miscellaneous code cleanup. --- src/coreclr/jit/morph.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 000103b54c5..057c318e81d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11482,7 +11482,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) case GT_RETURN: // normalize small integer return values - if (fgGlobalMorph && varTypeIsSmall(info.compRetType) && (op1 != nullptr) && (op1->TypeGet() != TYP_VOID) && + if (fgGlobalMorph && varTypeIsSmall(info.compRetType) && (op1 != nullptr) && !op1->TypeIs(TYP_VOID) && fgCastNeeded(op1, info.compRetType)) { // Small-typed return values are normalized by the callee @@ -11491,11 +11491,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) // Propagate GTF_COLON_COND op1->gtFlags |= (tree->gtFlags & GTF_COLON_COND); - tree->AsOp()->gtOp1 = fgMorphCast(op1); + tree->AsOp()->gtOp1 = fgMorphTree(op1); // Propagate side effect flags - tree->gtFlags &= ~GTF_ALL_EFFECT; - tree->gtFlags |= (tree->AsOp()->gtOp1->gtFlags & GTF_ALL_EFFECT); + tree->SetAllEffectsFlags(tree->AsOp()->gtGetOp1()); return tree; } From b1452c8fcab789dd9f2921c1479425b291111c5d Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Thu, 15 Jul 2021 13:07:34 -0700 Subject: [PATCH 601/926] Localized file check-in by OneLocBuild Task: Build definition ID 679: Build ID 1240010 (#55743) * Localized file check-in by OneLocBuild Task * Remove asserts against localized text from Logging generator tests Co-authored-by: Eric StJohn --- .../Microsoft.CSharp.cs.xlf | 360 ++++++++++++++++++ .../Microsoft.CSharp.de.xlf | 179 ++++----- .../Microsoft.CSharp.es.xlf | 179 ++++----- .../Microsoft.CSharp.fr.xlf | 179 ++++----- .../Microsoft.CSharp.it.xlf | 179 ++++----- .../Microsoft.CSharp.ja.xlf | 179 ++++----- .../Microsoft.CSharp.ko.xlf | 179 ++++----- .../Microsoft.CSharp.pl.xlf | 360 ++++++++++++++++++ .../Microsoft.CSharp.pt-BR.xlf | 360 ++++++++++++++++++ .../Microsoft.CSharp.ru.xlf | 179 ++++----- .../Microsoft.CSharp.tr.xlf | 360 ++++++++++++++++++ .../Microsoft.CSharp.zh-Hans.xlf | 183 +++++---- .../Microsoft.CSharp.zh-Hant.xlf | 185 ++++----- .../gen/Resources/xlf/Strings.cs.xlf | 62 +-- .../gen/Resources/xlf/Strings.de.xlf | 62 +-- .../gen/Resources/xlf/Strings.es.xlf | 62 +-- .../gen/Resources/xlf/Strings.fr.xlf | 62 +-- .../gen/Resources/xlf/Strings.it.xlf | 62 +-- .../gen/Resources/xlf/Strings.ja.xlf | 62 +-- .../gen/Resources/xlf/Strings.ko.xlf | 62 +-- .../gen/Resources/xlf/Strings.pl.xlf | 62 +-- .../gen/Resources/xlf/Strings.pt-BR.xlf | 62 +-- .../gen/Resources/xlf/Strings.ru.xlf | 62 +-- .../gen/Resources/xlf/Strings.tr.xlf | 62 +-- .../gen/Resources/xlf/Strings.zh-Hans.xlf | 62 +-- .../gen/Resources/xlf/Strings.zh-Hant.xlf | 62 +-- .../LoggerMessageGeneratorParserTests.cs | 12 +- .../gen/Resources/xlf/Strings.cs.xlf | 12 +- .../gen/Resources/xlf/Strings.de.xlf | 12 +- .../gen/Resources/xlf/Strings.es.xlf | 12 +- .../gen/Resources/xlf/Strings.fr.xlf | 12 +- .../gen/Resources/xlf/Strings.it.xlf | 12 +- .../gen/Resources/xlf/Strings.ja.xlf | 12 +- .../gen/Resources/xlf/Strings.ko.xlf | 12 +- .../gen/Resources/xlf/Strings.pl.xlf | 12 +- .../gen/Resources/xlf/Strings.pt-BR.xlf | 12 +- .../gen/Resources/xlf/Strings.ru.xlf | 12 +- .../gen/Resources/xlf/Strings.tr.xlf | 12 +- .../gen/Resources/xlf/Strings.zh-Hans.xlf | 12 +- .../gen/Resources/xlf/Strings.zh-Hant.xlf | 12 +- 40 files changed, 2741 insertions(+), 1294 deletions(-) create mode 100644 src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.cs.xlf create mode 100644 src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pl.xlf create mode 100644 src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pt-BR.xlf create mode 100644 src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.tr.xlf diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.cs.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.cs.xlf new file mode 100644 index 00000000000..ae56c2a140d --- /dev/null +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.cs.xlf @@ -0,0 +1,360 @@ + + + +
+ +
+ + + + An unexpected exception occurred while binding a dynamic operation + PÅ™i vytváření vazby dynamické operace doÅ¡lo k neoÄekávané výjimce. + + + Cannot bind call with no calling object + Nelze vytvoÅ™it volání vazby s objektem neúÄastnícím se volání. + + + Overload resolution failed + RozliÅ¡ení pÅ™etěžování neprobÄ›hlo úspěšnÄ›. + + + Binary operators must be invoked with two arguments + Binární operátory je nutné volat se dvÄ›ma argumenty. + + + Unary operators must be invoked with one argument + Unární operátory je nutné volat s jedním argumentem. + + + The name '{0}' is bound to a method and cannot be used like a property + Název {0} je vázán na metodu a nemůže být použit jako vlastnost. + + + The event '{0}' can only appear on the left hand side of + + Událost {0} se může vyskytnout pouze na levé stranÄ› výrazu +. + + + Cannot invoke a non-delegate type + Nelze volat nedelegující typ. + + + Binary operators cannot be invoked with one argument + Binární operátory nelze volat s jedním argumentem. + + + Cannot perform member assignment on a null reference + U nulového odkazu nelze provést pÅ™iÅ™azení Älenů. + + + Cannot perform runtime binding on a null reference + U nulového odkazu nelze provést vazbu za bÄ›hu. + + + Cannot dynamically invoke method '{0}' because it has a Conditional attribute + Nelze dynamicky volat metodu {0}, protože má atribut Conditional. + + + Cannot implicitly convert type 'void' to 'object' + Nelze implicitnÄ› pÅ™evést typ void na object. + + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' + Operátor {0} nelze použít na operandy typu {1} a {2}. + + + Cannot apply indexing with [] to an expression of type '{0}' + Ve výrazu typu {0} nelze použít indexování pomocí hranatých závorek ([]). + + + Wrong number of indices inside []; expected '{0}' + UvnitÅ™ hranatých závorek ([]) byl nalezen nesprávný poÄet indexů. OÄekávaný poÄet indexů: {0} + + + Operator '{0}' cannot be applied to operand of type '{1}' + Operátor {0} nelze použít na operand typu {1}. + + + Cannot implicitly convert type '{0}' to '{1}' + Typ {0} nelze implicitnÄ› pÅ™evést na typ {1}. + + + Cannot convert type '{0}' to '{1}' + Typ {0} nelze pÅ™evést na typ {1}. + + + Constant value '{0}' cannot be converted to a '{1}' + Konstantní hodnotu {0} nelze pÅ™evést na typ {1}. + + + Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' + Operátor {0} je nejednoznaÄný na operandech typu {1} a {2}. + + + Operator '{0}' is ambiguous on an operand of type '{1}' + Operátor {0} je nejednoznaÄný na operandu typu {1}. + + + Cannot convert null to '{0}' because it is a non-nullable value type + Hodnotu null nelze pÅ™evést na typ {0}, protože se jedná o typ, který nepovoluje hodnotu null. + + + Cannot access a non-static member of outer type '{0}' via nested type '{1}' + Pomocí vnoÅ™eného typu {1} nelze pÅ™istupovat k nestatickému Älenu vnÄ›jšího typu {0}. + + + '{0}' does not contain a definition for '{1}' + {0} neobsahuje definici pro {1}. + + + An object reference is required for the non-static field, method, or property '{0}' + Pro nestatické pole, metodu nebo vlastnost {0} je vyžadován odkaz na objekt. + + + The call is ambiguous between the following methods or properties: '{0}' and '{1}' + Volání je nejednoznaÄné mezi následujícími metodami nebo vlastnostmi: {0} a {1} + + + '{0}' is inaccessible due to its protection level + Typ {0} je vzhledem k úrovni ochrany nepřístupný. + + + No overload for '{0}' matches delegate '{1}' + Žádná pÅ™etížená metoda {0} neodpovídá delegátovi {1}. + + + The left-hand side of an assignment must be a variable, property or indexer + Levou stranou pÅ™iÅ™azení musí být promÄ›nná, vlastnost nebo indexer. + + + The type '{0}' has no constructors defined + Pro typ {0} nebyly definovány žádné konstruktory. + + + The property or indexer '{0}' cannot be used in this context because it lacks the get accessor + Vlastnost nebo indexer {0} nelze v tomto kontextu použít, protože neobsahuje pÅ™istupující objekt get. + + + Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead + K Älenovi {0} nelze pÅ™istupovat pomocí odkazu na instanci. Namísto toho použijte kvalifikaci pomocí názvu typu. + + + A readonly field cannot be assigned to (except in a constructor or a variable initializer) + Do pole readonly nejde pÅ™iÅ™azovat (kromÄ› případu, kdy se nachází uvnitÅ™ konstruktoru nebo inicializátoru promÄ›nné). + + + A readonly field cannot be passed ref or out (except in a constructor) + Pole urÄené jen pro Ätení nejde pÅ™edat jako parametr Ref nebo Out (kromÄ› případu, kdy se nachází uvnitÅ™ konstruktoru). + + + A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) + Do statického pole urÄeného jen pro Ätení nejde pÅ™iÅ™azovat (kromÄ› případu, kdy se nachází uvnitÅ™ statického konstruktoru nebo inicializátoru promÄ›nné). + + + A static readonly field cannot be passed ref or out (except in a static constructor) + Statické pole urÄené jen pro Ätení nejde pÅ™edat jako parametr Ref nebo Out (kromÄ› případu, kdy se nachází uvnitÅ™ statického konstruktoru). + + + Property or indexer '{0}' cannot be assigned to -- it is read only + Vlastnost nebo indexer {0} nelze pÅ™iÅ™adit – je jen pro Ätení. + + + A property or indexer may not be passed as an out or ref parameter + Vlastnost nebo indexer nelze pÅ™edat jako parametr ref nebo out. + + + Dynamic calls cannot be used in conjunction with pointers + Dynamická volání nelze použít v kombinaci s ukazateli. + + + In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters + Má-li být uživatelem definovaný logický operátor ({0}) použitelný jako operátor zkráceného vyhodnocení, musí vracet hodnotu stejného typu jako jeho dva parametry. + + + The type ('{0}') must contain declarations of operator true and operator false + Typ ({0}) musí obsahovat deklarace operátorů true a false. + + + Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) + Konstantní hodnotu {0} nelze pÅ™evést na typ {1} (k pÅ™epsání lze použít syntaxi unchecked). + + + Ambiguity between '{0}' and '{1}' + DoÅ¡lo k nejednoznaÄnosti mezi metodami nebo vlastnostmi {0} a {1}. + + + Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) + Typ {0} nelze implicitnÄ› pÅ™evést na typ {1}. Existuje explicitní pÅ™evod. (Nechybí výraz pÅ™etypování?) + + + The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible + Vlastnost nebo indexer {0} nelze v tomto kontextu použít, protože pÅ™istupující objekt get není dostupný. + + + The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible + Vlastnost nebo indexer {0} nelze v tomto kontextu použít, protože pÅ™istupující objekt jet není dostupný. + + + Using the generic {1} '{0}' requires '{2}' type arguments + Použití obecné možnosti {1} {0} vyžaduje argumenty typu {2}. + + + The {1} '{0}' cannot be used with type arguments + {1} {0} nelze použít s argumenty typů. + + + The non-generic {1} '{0}' cannot be used with type arguments + Neobecnou možnost {1} {0} nelze použít s argumenty typů. + + + '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' + Objekt {2} musí být neabstraktního typu s veÅ™ejným konstruktorem bez parametrů, jinak jej nelze použít jako parametr {1} v obecném typu nebo metodÄ› {0}. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. + Typ {3} nelze použít jako parametr typu {2} v obecném typu nebo metodÄ› {0}. Neexistuje žádný implicitní pÅ™evod odkazu z {3} na {1}. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. + Typ {3} nelze použít jako parametr typu {2} v obecném typu nebo metodÄ› {0}. Typ {3} s možnou hodnotou null nevyhovuje omezení {1}. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. + Typ {3} nelze použít jako parametr typu {2} v obecném typu nebo metodÄ› {0}. Typ {3} s možnou hodnotou null nevyhovuje omezení {1}. Typy s možnou hodnotou null nemohou vyhovÄ›t žádným omezením rozhraní. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. + Typ {3} nelze použít jako parametr typu {2} v obecném typu nebo metodÄ› {0}. Neexistuje žádný pÅ™evod na uzavÅ™ené urÄení z {3} na {1}. + + + The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. + Argumenty typu pro metodu {0} nelze stanovit z použití. Zadejte argumenty typu explicitnÄ›. + + + The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' + Typ {2} musí být typ odkazu, aby ho bylo možné používat jako parametr {1} v obecném typu nebo metodÄ› {0}. + + + The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' + Typ {2} musí být typ, který nepovoluje hodnotu null, aby ho bylo možné používat jako parametr {1} v obecném typu nebo metodÄ› {0}. + + + Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' + PÅ™i pÅ™evodu typu {2} na typ {3} doÅ¡lo k uživatelem definovaným nejednoznaÄným pÅ™evodům typu {0} na typ {1}. + + + '{0}' is not supported by the language + {0} není tímto jazykem podporovaný. + + + '{0}': cannot explicitly call operator or accessor + {0}: Nejde explicitnÄ› volat operátor nebo pÅ™istupující objekt. + + + Cannot convert to static type '{0}' + Nelze pÅ™evést na statický typ {0}. + + + The operand of an increment or decrement operator must be a variable, property or indexer + Operandem operátoru pÅ™iÄtení nebo odeÄtení musí být promÄ›nná, vlastnost nebo indexer. + + + No overload for method '{0}' takes '{1}' arguments + Žádná pÅ™etížená metoda {0} nepoužívá tento poÄet argumentů ({1}). + + + The best overloaded method match for '{0}' has some invalid arguments + NÄ›které argumenty pÅ™etěžované metody, která je nejlepší shodou pro deklaraci {0}, jsou neplatné. + + + A ref or out argument must be an assignable variable + Argumentem ref nebo out musí být promÄ›nná s možností pÅ™iÅ™azení hodnoty + + + Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) + K chránÄ›nému Älenu {0} nelze pÅ™istupovat prostÅ™ednictvím kvalifikátoru typu {1}. Kvalifikátor musí být typu {2} (nebo musí být od tohoto typu odvozen). + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' + Vlastnost, indexer nebo událost {0} nejsou tímto jazykem podporovány. Zkuste přímo volat metody pÅ™istupujícího objektu {1} nebo {2}. + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' + Vlastnost, indexer nebo událost {0} nejsou tímto jazykem podporovány. Zkuste přímo volat metodu pÅ™istupujícího objektu {1}. + + + Delegate '{0}' does not take '{1}' arguments + PoÄet argumentů delegáta {0} neodpovídá (poÄet: {1}). + + + Delegate '{0}' has some invalid arguments + NÄ›které argumenty delegáta {0} jsou neplatné. + + + Cannot assign to '{0}' because it is read-only + K položce nelze pÅ™iÅ™adit {0} protože je jen pro Ätení. + + + Cannot pass '{0}' as a ref or out argument because it is read-only + Element {0} nelze pÅ™edat jako argument Ref nebo Out, protože je jen pro Ätení. + + + Cannot modify the return value of '{0}' because it is not a variable + Vrácenou hodnotu {0} nelze zmÄ›nit, protože se nejedná o promÄ›nnou. + + + Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) + ÄŒleny pole jen pro Ätení {0} nelze mÄ›nit (kromÄ› případu, kdy se nacházejí uvnitÅ™ konstruktoru nebo inicializátoru promÄ›nné). + + + Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) + ÄŒleny pole jen pro Ätení {0} nelze pÅ™edat jako parametr Ref nebo Out (kromÄ› případu, kdy se nacházejí uvnitÅ™ konstruktoru). + + + Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) + Pole statických polí jen pro Ätení {0} nelze pÅ™iÅ™adit (kromÄ› případu, kdy se nacházejí uvnitÅ™ statického konstruktoru nebo inicializátoru promÄ›nné). + + + Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) + Pole statického pole jen pro Ätení {0} nelze pÅ™edat jako parametr Ref nebo Out (kromÄ› případu, kdy se nacházejí uvnitÅ™ statického konstruktoru). + + + Cannot assign to '{0}' because it is a '{1}' + K položce nelze pÅ™iÅ™adit {0} protože je typu {1}. + + + Cannot pass '{0}' as a ref or out argument because it is a '{1}' + Element {0} nelze pÅ™edat jako argument Ref nebo Out, protože je {1}. + + + '{0}' does not contain a constructor that takes '{1}' arguments + {0} neobsahuje konstruktor daným poÄtem vstupních argumentů ({1}). + + + Non-invocable member '{0}' cannot be used like a method. + Nevyvolatelného Älena {0} nelze použít jako metodu. + + + Named argument specifications must appear after all fixed arguments have been specified + Specifikace pojmenovaných argumentů musí následovat po specifikaci vÅ¡ech pevných argumentů + + + The best overload for '{0}' does not have a parameter named '{1}' + Nejlepší pÅ™etížení pro {0} neobsahuje parametr s názvem {1}. + + + The delegate '{0}' does not have a parameter named '{1}' + Delegát {0} neobsahuje parametr s názvem {1}. + + + Named argument '{0}' cannot be specified multiple times + Pojmenovaný argument {0} nelze zadat vícekrát. + + + Named argument '{0}' specifies a parameter for which a positional argument has already been given + Pojmenovaný argument {0} urÄuje parametr, pro který již byl poskytnut poziÄní argument. + + + +
+
\ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.de.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.de.xlf index 3f030fd1ef8..d95dd6a6e6d 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.de.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.de.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - Unerwartete Ausnahme beim Binden eines dynamischen Vorgangs + Unerwartete Ausnahme beim Binden eines dynamischen Vorgangs Cannot bind call with no calling object - Ein Aufruf ohne aufrufendes Objekt kann nicht gebunden werden. + Ein Aufruf ohne aufrufendes Objekt kann nicht gebunden werden. Overload resolution failed - Fehler bei der Überladungsauflösung + Fehler bei der Überladungsauflösung. Binary operators must be invoked with two arguments - Binäre Operatoren müssen mit zwei Argumenten aufgerufen werden. + Binäre Operatoren müssen mit zwei Argumenten aufgerufen werden. Unary operators must be invoked with one argument - Unäre Operatoren müssen mit einem Argument aufgerufen werden. + Unäre Operatoren müssen mit einem Argument aufgerufen werden. The name '{0}' is bound to a method and cannot be used like a property - Der Name "{0}" ist an eine Methode gebunden und kann nicht wie eine Eigenschaft verwendet werden. + Der Name "{0}" ist an eine Methode gebunden und kann nicht wie eine Eigenschaft verwendet werden. The event '{0}' can only appear on the left hand side of + - Das Ereignis "{0}" kann nur verwendet werden, auf der linken Seite des + + Das Ereignis "{0}" kann nur links von "+" verwendet werden. Cannot invoke a non-delegate type - Ein Nichtdelegattyp kann nicht aufgerufen werden. + Ein Nichtdelegattyp kann nicht aufgerufen werden. Binary operators cannot be invoked with one argument - Binäre Operatoren können nicht mit einem Argument aufgerufen werden. + Binäre Operatoren können nicht mit einem Argument aufgerufen werden. Cannot perform member assignment on a null reference - Für einen NULL-Verweis kann keine Memberzuweisung ausgeführt werden. + Für einen NULL-Verweis kann keine Memberzuweisung ausgeführt werden. Cannot perform runtime binding on a null reference - Die Laufzeitbindung kann für einen NULL-Verweis nicht ausgeführt werden. + Die Laufzeitbindung kann für einen NULL-Verweis nicht ausgeführt werden. Cannot dynamically invoke method '{0}' because it has a Conditional attribute - Die {0}-Methode kann nicht dynamisch aufgerufen werden, da sie ein Conditional-Attribut enthält. + Die {0}-Methode kann nicht dynamisch aufgerufen werden, da sie ein Conditional-Attribut enthält. Cannot implicitly convert type 'void' to 'object' - Der Typ "void" kann nicht implizit in "object" konvertiert werden. + Der Typ "void" kann nicht implizit in "object" konvertiert werden. Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - Der {0}-Operator kann nicht auf Operanden vom Typ "{1}" und "{2}" angewendet werden. + Der {0}-Operator kann nicht auf Operanden vom Typ "{1}" und "{2}" angewendet werden. Cannot apply indexing with [] to an expression of type '{0}' - Eine Indizierung mit [] kann nicht auf einen Ausdruck vom Typ "{0}" angewendet werden. + Eine Indizierung mit [] kann nicht auf einen Ausdruck vom Typ "{0}" angewendet werden. Wrong number of indices inside []; expected '{0}' - Falsche Indexanzahl in []. {0} wurde erwartet + Falsche Indexanzahl in []. {0} wurde erwartet Operator '{0}' cannot be applied to operand of type '{1}' - Der {0}-Operator kann nicht auf einen Operanden vom Typ "{1}" angewendet werden. + Der {0}-Operator kann nicht auf einen Operanden vom Typ "{1}" angewendet werden. Cannot implicitly convert type '{0}' to '{1}' - Der {0}-Typ kann nicht implizit in {1} konvertiert werden. + Der {0}-Typ kann nicht implizit in {1} konvertiert werden. Cannot convert type '{0}' to '{1}' - Der {0}-Typ kann nicht in {1} konvertiert werden. + Der {0}-Typ kann nicht in {1} konvertiert werden. Constant value '{0}' cannot be converted to a '{1}' - Der Konstantenwert "{0}" kann nicht in {1} konvertiert werden. + Der Konstantenwert "{0}" kann nicht in {1} konvertiert werden. Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - Der {0}-Operator ist bei Operanden vom Typ "{1}" und "{2}" mehrdeutig. + Der {0}-Operator ist bei Operanden vom Typ "{1}" und "{2}" mehrdeutig. Operator '{0}' is ambiguous on an operand of type '{1}' - Der {0}-Operator ist für einen Operanden vom Typ "{1}" mehrdeutig. + Der {0}-Operator ist für einen Operanden vom Typ "{1}" mehrdeutig. Cannot convert null to '{0}' because it is a non-nullable value type - NULL kann nicht in {0} konvertiert werden, da dieser Werttyp nicht auf NULL festgelegt werden kann. + NULL kann nicht in {0} konvertiert werden, da dieser Werttyp nicht auf NULL festgelegt werden kann. Cannot access a non-static member of outer type '{0}' via nested type '{1}' - Auf einen nicht statischen Member des äußeren {0}-Typs kann nicht über den geschachtelten {1}-Typ zugegriffen werden. + Auf einen nicht statischen Member des äußeren {0}-Typs kann nicht über den geschachtelten {1}-Typ zugegriffen werden. '{0}' does not contain a definition for '{1}' - {0} enthält keine Definition für {1}. + "{0}" enthält keine Definition für "{1}". An object reference is required for the non-static field, method, or property '{0}' - Für das nicht statische Feld, die Methode oder die Eigenschaft "{0}" ist ein Objektverweis erforderlich. + Für das nicht statische Feld, die Methode oder die Eigenschaft "{0}" ist ein Objektverweis erforderlich. The call is ambiguous between the following methods or properties: '{0}' and '{1}' - Der Aufruf unterscheidet nicht eindeutig zwischen den folgenden Methoden oder Eigenschaften: {0} und {1} + Der Aufruf unterscheidet nicht eindeutig zwischen den folgenden Methoden oder Eigenschaften: {0} und {1} '{0}' is inaccessible due to its protection level - Der Zugriff auf "{0}" ist aufgrund des Schutzgrads nicht möglich. + Der Zugriff auf "{0}" ist aufgrund des Schutzgrads nicht möglich. No overload for '{0}' matches delegate '{1}' - Keine Überladung für {0} stimmt mit dem Delegaten "{1}" überein. + Keine Überladung für {0} stimmt mit dem Delegaten "{1}" überein. The left-hand side of an assignment must be a variable, property or indexer - Die linke Seite einer Zuweisung muss eine Variable, eine Eigenschaft oder ein Indexer sein. + Die linke Seite einer Zuweisung muss eine Variable, eine Eigenschaft oder ein Indexer sein. The type '{0}' has no constructors defined - Für den {0}-Typ sind keine Konstruktoren definiert. + Für den {0}-Typ sind keine Konstruktoren definiert. The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, weil der get-Accessor fehlt. + Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, weil der get-Accessor fehlt. Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - Auf den Member "{0}" kann nicht mit einem Instanzverweis zugegriffen werden. Qualifizieren Sie ihn stattdessen mit einem Typnamen. + Auf den Member "{0}" kann nicht mit einem Instanzverweis zugegriffen werden. Qualifizieren Sie ihn stattdessen mit einem Typnamen. A readonly field cannot be assigned to (except in a constructor or a variable initializer) - Einem schreibgeschützten Feld kann nichts zugewiesen werden (außer in einem Konstruktor oder Variableninitialisierer). + Für ein schreibgeschütztes Feld ist eine Zuweisung nicht möglich (außer in einem Konstruktor oder Variableninitialisierer). A readonly field cannot be passed ref or out (except in a constructor) - Einem schreibgeschützten Feld kann kein Verweis und keine Ausgabe übergeben werden (außer in einem Konstruktor). + An ein schreibgeschütztes Feld können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - Einem statischen, schreibgeschützten Feld kann nichts zugewiesen werden (außer in einem statischen Konstruktor oder einem Variableninitialisierer). + Für ein statisches schreibgeschütztes Feld ist eine Zuweisung nicht möglich (außer in einem statischen Konstruktor oder einem Variableninitialisierer). A static readonly field cannot be passed ref or out (except in a static constructor) - Einem statischen, schreibgeschützten Feld kann kein Verweis und keine Ausgabe übergeben werden (außer in einem statischen Konstruktor) + An ein statisches schreibgeschütztes Feld können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). Property or indexer '{0}' cannot be assigned to -- it is read only - Für die Eigenschaft oder den Indexer "{0}" ist eine Zuweisung nicht möglich -- sie sind schreibgeschützt. + Für die Eigenschaft oder den Indexer "{0}" ist eine Zuweisung nicht möglich -- sie sind schreibgeschützt. A property or indexer may not be passed as an out or ref parameter - Eine Eigenschaft oder ein Indexer kann nicht als out- oder ref-Parameter übergeben werden. + Eine Eigenschaft oder ein Indexer kann nicht als out- oder ref-Parameter übergeben werden. Dynamic calls cannot be used in conjunction with pointers - Dynamische Aufrufe können nicht in Verbindung mit Zeigern verwendet werden. + Dynamische Aufrufe können nicht in Verbindung mit Zeigern verwendet werden. In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - Um als Kurzschlussoperator anwendbar zu sein, muss der Rückgabetyp eines benutzerdefinierten logischen Operators ({0}) mit dem Typ seiner zwei Parameter übereinstimmen. + Um als Kurzschlussoperator anwendbar zu sein, muss der Rückgabetyp eines benutzerdefinierten logischen Operators ({0}) mit dem Typ seiner zwei Parameter übereinstimmen. The type ('{0}') must contain declarations of operator true and operator false - Der Typ ({0}) muss Deklarationen des True- und des False-Operators enthalten. + Der Typ ({0}) muss Deklarationen des True- und des False-Operators enthalten. Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - Der Konstantenwert "{0}" kann nicht in {1} konvertiert werden (verwenden Sie zum Außerkraftsetzen die unchecked-Syntax). + Der Konstantenwert "{0}" kann nicht in {1} konvertiert werden (verwenden Sie zum Außerkraftsetzen die unchecked-Syntax). Ambiguity between '{0}' and '{1}' - Mehrdeutigkeit zwischen {0} und {1} + Mehrdeutigkeit zwischen {0} und {1} Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - Der {0}-Typ kann nicht implizit in {1} konvertiert werden. Es ist bereits eine explizite Konvertierung vorhanden (möglicherweise fehlt eine Umwandlung). + Der {0}-Typ kann nicht implizit in {1} konvertiert werden. Es ist bereits eine explizite Konvertierung vorhanden (möglicherweise fehlt eine Umwandlung). The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, da nicht auf den get-Accessor zugegriffen werden kann. + Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, da nicht auf den get-Accessor zugegriffen werden kann. The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, da nicht auf den set-Accessor zugegriffen werden kann. + Die Eigenschaft oder der Indexer "{0}" kann in diesem Kontext nicht verwendet werden, da nicht auf den set-Accessor zugegriffen werden kann. Using the generic {1} '{0}' requires '{2}' type arguments - Die Verwendung von {1} "{0}" (generisch) erfordert {2}-Typargumente. + Die Verwendung von {1} "{0}" (generisch) erfordert {2}-Typargumente. The {1} '{0}' cannot be used with type arguments - {1} "{0}" kann nicht mit Typargumenten verwendet werden. + {1} "{0}" kann nicht mit Typargumenten verwendet werden. The non-generic {1} '{0}' cannot be used with type arguments - {1} "{0}" ist nicht generisch und kann daher nicht mit Typargumenten verwendet werden. + {1} "{0}" ist nicht generisch und kann daher nicht mit Typargumenten verwendet werden. '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - {2} muss ein nicht abstrakter Typ mit einem öffentlichen parameterlosen Konstruktor sein, um im generischen Typ oder in der generischen {0}-Methode als {1}-Parameter verwendet werden zu können. + "{2}" muss ein nicht abstrakter Typ mit einem öffentlichen parameterlosen Konstruktor sein, um im generischen Typ oder in der generischen {0}-Methode als {1}-Parameter verwendet werden zu können. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Es ist keine implizite Verweiskonvertierung von {3} in {1} vorhanden. + Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Es ist keine implizite Verweiskonvertierung von {3} in {1} vorhanden. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Der {3}-Typ, der NULL-Werte zulässt, entspricht nicht der Einschränkung von {1}. + Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Der {3}-Typ, der NULL-Werte zulässt, entspricht nicht der Einschränkung von {1}. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Der {3}-Typ, der NULL-Werte zulässt, entspricht nicht der Einschränkung von {1}. Typen, die NULL-Werte zulassen, können Schnittstelleneinschränkungen nicht entsprechen. + Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Der {3}-Typ, der NULL-Werte zulässt, entspricht nicht der Einschränkung von {1}. Typen, die NULL-Werte zulassen, können Schnittstelleneinschränkungen nicht entsprechen. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Es ist keine Boxing-Konvertierung von {3} in {1} vorhanden. + Der {3}-Typ kann nicht als {2}-Typparameter im generischen Typ oder in der generischen {0}-Methode verwendet werden. Es ist keine Boxing-Konvertierung von {3} in {1} vorhanden. The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - Die Typargumente der {0}-Methode können nicht per Rückschluss aus der Syntax abgeleitet werden. Geben Sie die Typargumente explizit an. + Die Typargumente der {0}-Methode können nicht per Rückschluss aus der Syntax abgeleitet werden. Geben Sie die Typargumente explizit an. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - Der {2}-Typ muss ein Referenztyp sein, damit er als {1}-Parameter im generischen Typ oder in der generischen {0}-Methode verwendet werden kann. + Der {2}-Typ muss ein Referenztyp sein, damit er als {1}-Parameter im generischen Typ oder in der generischen {0}-Methode verwendet werden kann. The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - Der {2}-Typ darf keine NULL-Werte zulassen, wenn er als {1}-Parameter im generischen Typ oder in der generischen {0}-Methode verwendet werden soll. + Der {2}-Typ darf keine NULL-Werte zulassen, wenn er als {1}-Parameter im generischen Typ oder in der generischen {0}-Methode verwendet werden soll. Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - Mehrdeutige benutzerdefinierte Konvertierungen von {0} und {1} bei der Konvertierung von {2} in {3} + Mehrdeutige benutzerdefinierte Konvertierungen von {0} und {1} bei der Konvertierung von {2} in {3} '{0}' is not supported by the language - {0} wird von der Sprache nicht unterstützt. + "{0}" wird von der Sprache nicht unterstützt. '{0}': cannot explicitly call operator or accessor - {0}: Der Operator oder Accessor kann nicht explizit aufgerufen werden. + {0}: Der Operator oder Accessor kann nicht explizit aufgerufen werden. Cannot convert to static type '{0}' - Die Konvertierung in den statischen {0}-Typ ist nicht möglich. + Die Konvertierung in den statischen {0}-Typ ist nicht möglich. The operand of an increment or decrement operator must be a variable, property or indexer - Der Operand eines Inkrement- oder Dekrementoperators muss eine Variable, eine Eigenschaft oder ein Indexer sein. + Der Operand eines Inkrement- oder Dekrementoperators muss eine Variable, eine Eigenschaft oder ein Indexer sein. No overload for method '{0}' takes '{1}' arguments - Keine Überladung für die {0}-Methode nimmt {1}-Argumente an. + Keine Überladung für die {0}-Methode nimmt {1}-Argumente an. The best overloaded method match for '{0}' has some invalid arguments - Die beste Übereinstimmung für die überladene {0}-Methode enthält einige ungültige Argumente. + Die beste Übereinstimmung für die überladene {0}-Methode enthält einige ungültige Argumente. A ref or out argument must be an assignable variable - Ein ref- oder out-Argument muss eine zuweisbare Variable sein. + Ein ref- oder out-Argument muss eine zuweisbare Variable sein. Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - Auf den geschützten Member "{0}" kann nicht über einen Qualifizierer vom Typ "{1}" zugegriffen werden. Der Qualifizierer muss vom Typ "{2}" (oder von ihm abgeleitet) sein. + Auf den geschützten Member "{0}" kann nicht über einen Qualifizierer vom Typ "{1}" zugegriffen werden. Der Qualifizierer muss vom Typ "{2}" (oder von ihm abgeleitet) sein. Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - Die Eigenschaft, der Indexer oder das Ereignis "{0}" wird von der Sprache nicht unterstützt. Rufen Sie die {1}- oder {2}-Accessormethoden direkt auf. + Die Eigenschaft, der Indexer oder das Ereignis "{0}" wird von der Sprache nicht unterstützt. Rufen Sie die {1}- oder {2}-Accessormethoden direkt auf. Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - Die Eigenschaft, der Indexer oder das Ereignis "{0}" wird von der Sprache nicht unterstützt. Rufen Sie die {1}-Accessormethode direkt auf. + Die Eigenschaft, der Indexer oder das Ereignis "{0}" wird von der Sprache nicht unterstützt. Rufen Sie die {1}-Accessormethode direkt auf. Delegate '{0}' does not take '{1}' arguments - Der Delegat "{0}" akzeptiert {1}-Argumente nicht. + Der Delegat "{0}" akzeptiert {1}-Argumente nicht. Delegate '{0}' has some invalid arguments - Der Delegat "{0}" enthält einige ungültige Argumente. + Der Delegat "{0}" enthält einige ungültige Argumente. Cannot assign to '{0}' because it is read-only - {0} ist schreibgeschützt. Eine Zuweisung ist daher nicht möglich. + {0} ist schreibgeschützt. Eine Zuweisung ist daher nicht möglich. Cannot pass '{0}' as a ref or out argument because it is read-only - {0} ist schreibgeschützt und kann daher nicht als ref- oder out-Argument übergeben werden. + {0} ist schreibgeschützt und kann daher nicht als ref- oder out-Argument übergeben werden. Cannot modify the return value of '{0}' because it is not a variable - Der Rückgabewert von {0} ist keine Variable und kann daher nicht geändert werden. + Der Rückgabewert von {0} ist keine Variable und kann daher nicht geändert werden. Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - Member des schreibgeschützten Felds "{0}" können nicht geändert werden (außer in einem Konstruktor oder Variableninitialisierer). + Member des schreibgeschützten Felds "{0}" können nicht geändert werden (außer in einem Konstruktor oder Variableninitialisierer). Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - An Member des schreibgeschützten Felds "{0}" können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). + An Member des schreibgeschützten Felds "{0}" können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - Für Felder eines statischen schreibgeschützten Felds "{0}" ist eine Zuweisung nicht möglich (außer in einem statischen Konstruktor oder einem Variableninitialisierer). + Für Felder eines statischen schreibgeschützten Felds "{0}" ist eine Zuweisung nicht möglich (außer in einem statischen Konstruktor oder einem Variableninitialisierer). Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - An Felder des schreibgeschützten Felds "{0}" können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). + An Felder des schreibgeschützten Felds "{0}" können keine ref- oder out-Argumente übergeben werden (außer in einem Konstruktor). Cannot assign to '{0}' because it is a '{1}' - {0} ist ein(e) {1}. Eine Zuweisung ist daher nicht möglich. + {0} ist ein(e) {1}. Eine Zuweisung ist daher nicht möglich. Cannot pass '{0}' as a ref or out argument because it is a '{1}' - {0} ist ein(e) {1} und kann daher nicht als ref- oder out-Argument übergeben werden. + {0} ist ein(e) {1} und kann daher nicht als ref- oder out-Argument übergeben werden. '{0}' does not contain a constructor that takes '{1}' arguments - "{0}" enthält keinen Konstruktor, der {1}-Argumente akzeptiert. + "{0}" enthält keinen Konstruktor, der {1}-Argumente akzeptiert. Non-invocable member '{0}' cannot be used like a method. - Der nicht aufrufbare Member "{0}" kann nicht wie eine Methode verwendet werden. + Der nicht aufrufbare Member "{0}" kann nicht wie eine Methode verwendet werden. Named argument specifications must appear after all fixed arguments have been specified - Die Spezifikationen für benannte Argumente müssen nach Angabe aller festen Argumente angezeigt werden. + Benannte Argumente müssen nach allen festen Argumenten angegeben werden. The best overload for '{0}' does not have a parameter named '{1}' - Die beste Überladung für {0} enthält keinen Parameter mit dem Namen "{1}". + Die beste Überladung für {0} enthält keinen Parameter mit dem Namen "{1}". The delegate '{0}' does not have a parameter named '{1}' - Der Delegat "{0}" enthält keinen Parameter mit dem Namen "{1}". + Der Delegat "{0}" enthält keinen Parameter mit dem Namen "{1}". Named argument '{0}' cannot be specified multiple times - Das benannte {0}-Argument kann nicht mehrmals angegeben werden. + Das benannte {0}-Argument kann nicht mehrmals angegeben werden. Named argument '{0}' specifies a parameter for which a positional argument has already been given - Das benannte {0}-Argument legt einen Parameter fest, für den bereits ein positionelles Argument angegeben wurde. + Das benannte {0}-Argument legt einen Parameter fest, für den bereits ein positionelles Argument angegeben wurde. - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.es.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.es.xlf index 53ab8a18ecb..c8516adc48f 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.es.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.es.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - Excepción inesperada al enlazar una operación dinámica. + Excepción inesperada al enlazar una operación dinámica. Cannot bind call with no calling object - No se puede enlazar la llamada sin un objeto de llamada. + No se puede enlazar la llamada sin un objeto de llamada. Overload resolution failed - Error en la resolución de sobrecarga. + Error en la resolución de sobrecarga. Binary operators must be invoked with two arguments - No se pueden invocar operadores binarios con dos argumentos. + No se pueden invocar operadores binarios con dos argumentos. Unary operators must be invoked with one argument - Los operadores unarios deben invocarse con un argumento. + Los operadores unarios deben invocarse con un argumento. The name '{0}' is bound to a method and cannot be used like a property - El nombre '{0}' está enlazado a un método y no se puede usar como propiedad. + El nombre '{0}' está enlazado a un método y no se puede usar como propiedad. The event '{0}' can only appear on the left hand side of + - El evento '{0}' sólo puede aparecer en el lado izquierdo de + + El evento '{0}' solo puede aparecer a la izquierda de + Cannot invoke a non-delegate type - No se puede invocar un tipo no delegado. + No se puede invocar un tipo no delegado. Binary operators cannot be invoked with one argument - No se pueden invocar operadores binarios con un argumento. + No se pueden invocar operadores binarios con un argumento. Cannot perform member assignment on a null reference - No se puede realizar la asignación de miembros en una referencia NULL. + No se puede realizar la asignación de miembros en una referencia NULL. Cannot perform runtime binding on a null reference - No se puede realizar enlace en tiempo de ejecución en una referencia NULL. + No se puede realizar enlace en tiempo de ejecución en una referencia NULL. Cannot dynamically invoke method '{0}' because it has a Conditional attribute - No se puede invocar dinámicamente el método '{0}' porque tiene un atributo Conditional. + No se puede invocar dinámicamente el método '{0}' porque tiene un atributo Conditional. Cannot implicitly convert type 'void' to 'object' - No se puede convertir implícitamente el tipo 'void' en 'object'. + No se puede convertir implícitamente el tipo 'void' en 'object'. Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - El operador '{0}' no se puede aplicar a operandos del tipo '{1}' y '{2}' + El operador '{0}' no se puede aplicar a operandos del tipo '{1}' y '{2}' Cannot apply indexing with [] to an expression of type '{0}' - No se puede aplicar la indización con [] a una expresión del tipo '{0}' + No se puede aplicar la indización con [] a una expresión del tipo '{0}' Wrong number of indices inside []; expected '{0}' - Número incorrecto de índices dentro de []; se esperaba '{0}' + Número incorrecto de índices dentro de []; se esperaba '{0}' Operator '{0}' cannot be applied to operand of type '{1}' - El operador '{0}' no se puede aplicar al operando del tipo '{1}' + El operador '{0}' no se puede aplicar al operando del tipo '{1}' Cannot implicitly convert type '{0}' to '{1}' - No se puede convertir implícitamente el tipo '{0}' en '{1}'. + No se puede convertir implícitamente el tipo '{0}' en '{1}'. Cannot convert type '{0}' to '{1}' - No se puede convertir el tipo '{0}' en '{1}' + No se puede convertir el tipo '{0}' a '{1}'. Constant value '{0}' cannot be converted to a '{1}' - El valor constante '{0}' no se puede convertir en '{1}' + El valor constante '{0}' no se puede convertir en '{1}' Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - El operador '{0}' es ambiguo en operandos del tipo '{1}' y '{2}' + El operador '{0}' es ambiguo en operandos del tipo '{1}' y '{2}' Operator '{0}' is ambiguous on an operand of type '{1}' - El operador '{0}' es ambiguo con un operando del tipo '{1}' + El operador '{0}' es ambiguo con un operando del tipo '{1}' Cannot convert null to '{0}' because it is a non-nullable value type - No se puede convertir NULL en '{0}' porque es un tipo de valor que no acepta valores NULL. + No se puede convertir NULL en '{0}' porque es un tipo de valor que no acepta valores NULL. Cannot access a non-static member of outer type '{0}' via nested type '{1}' - No se puede obtener acceso a un miembro no estático de tipo externo '{0}' mediante el tipo anidado '{1}'. + No se puede obtener acceso a un miembro no estático de tipo externo '{0}' mediante el tipo anidado '{1}'. '{0}' does not contain a definition for '{1}' - '{0}' no contiene una definición para '{1}'. + '{0}' no contiene una definición para '{1}'. An object reference is required for the non-static field, method, or property '{0}' - Se requiere una referencia de objeto para el campo, método o propiedad no estáticos '{0}'. + Se requiere una referencia de objeto para el campo, método o propiedad no estáticos '{0}'. The call is ambiguous between the following methods or properties: '{0}' and '{1}' - La llamada es ambigua entre los métodos o propiedades siguientes: '{0}' y '{1}' + La llamada es ambigua entre los métodos o propiedades siguientes: '{0}' y '{1}' '{0}' is inaccessible due to its protection level - '{0}' no es accesible debido a su nivel de protección + '{0}' no es accesible debido a su nivel de protección No overload for '{0}' matches delegate '{1}' - Ninguna sobrecarga correspondiente a '{0}' coincide con el '{1}' delegado + Ninguna sobrecarga correspondiente a '{0}' coincide con el '{1}' delegado The left-hand side of an assignment must be a variable, property or indexer - La parte izquierda de una asignación debe ser una variable, una propiedad o un indizador + La parte izquierda de una asignación debe ser una variable, una propiedad o un indizador The type '{0}' has no constructors defined - El tipo '{0}' no tiene constructores definidos. + El tipo '{0}' no tiene constructores definidos. The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - La propiedad o el indizador '{0}' no se puede usar en este contexto porque carece del descriptor de acceso get. + La propiedad o el indizador '{0}' no se puede usar en este contexto porque carece del descriptor de acceso get. Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - No se puede obtener acceso al miembro '{0}' con una referencia de instancia; califíquelo con un nombre de tipo en su lugar. + No se puede obtener acceso al miembro '{0}' con una referencia de instancia; califíquelo con un nombre de tipo en su lugar. A readonly field cannot be assigned to (except in a constructor or a variable initializer) - No se puede asignar un campo de sólo lectura (excepto en un constructor o inicializador de variable) + No se puede asignar un campo de solo lectura (excepto en un constructor o inicializador de variable) A readonly field cannot be passed ref or out (except in a constructor) - No se puede pasar out o ref a un campo de sólo lectura (excepto en un constructor) + No se puede pasar out o ref a un campo de solo lectura (excepto en un constructor). A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - No se puede asignar un campo de sólo lectura estático (excepto en un constructor estático o inicializador de variable) + No se puede asignar un campo de solo lectura estático (excepto en un constructor estático o inicializador de variable) A static readonly field cannot be passed ref or out (except in a static constructor) - No se puede pasar out o ref a un campo estático de sólo lectura (excepto en un constructor estático) + No se puede pasar out o ref a un campo estático de solo lectura (excepto en un constructor estático). Property or indexer '{0}' cannot be assigned to -- it is read only - No se puede asignar a la propiedad o el indizador '{0}' porque es de solo lectura + No se puede asignar a la propiedad o el indizador '{0}' porque es de solo lectura A property or indexer may not be passed as an out or ref parameter - Una propiedad o un indizador no se puede pasar como parámetro out o ref + Una propiedad o un indizador no se puede pasar como parámetro out o ref. Dynamic calls cannot be used in conjunction with pointers - No se pueden usar llamadas dinámicas en combinación con los punteros. + No se pueden usar llamadas dinámicas en combinación con los punteros. In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - Para que se pueda aplicar un operador de cortocircuito, el operador lógico definido por el usuario ('{0}') debe tener el mismo tipo de valor devuelto que sus dos parámetros + Para que se pueda aplicar un operador de cortocircuito, el operador lógico definido por el usuario ('{0}') debe tener el mismo tipo de valor devuelto que sus dos parámetros The type ('{0}') must contain declarations of operator true and operator false - El tipo ('{0}') debe incluir declaraciones de operador true y operador false. + El tipo ('{0}') debe incluir declaraciones de operador true y operador false. Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - El valor constante '{0}' no se puede convertir en '{1}' (use la sintaxis 'unchecked' para invalidar el valor) + El valor constante '{0}' no se puede convertir en '{1}' (use la sintaxis 'unchecked' para invalidar el valor) Ambiguity between '{0}' and '{1}' - Ambigüedad entre '{0}' y '{1}' + Ambigüedad entre '{0}' y '{1}' Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - No se puede convertir implícitamente el tipo '{0}' en '{1}'. Ya existe una conversión explícita (compruebe si le falta una conversión). + No se puede convertir implícitamente el tipo '{0}' en '{1}'. Ya existe una conversión explícita (compruebe si le falta una conversión). The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - La propiedad o indizador '{0}' no se puede usar en este contexto porque el descriptor de acceso get es inaccesible + La propiedad o indizador '{0}' no se puede usar en este contexto porque el descriptor de acceso get es inaccesible The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - La propiedad o indizador '{0}' no se puede usar en este contexto porque el descriptor de acceso set es inaccesible + La propiedad o indizador '{0}' no se puede usar en este contexto porque el descriptor de acceso set es inaccesible Using the generic {1} '{0}' requires '{2}' type arguments - Uso de {1} de tipo genérico ('{0}'): requiere '{2}' argumentos de tipo + Uso de {1} de tipo genérico ('{0}'): requiere '{2}' argumentos de tipo The {1} '{0}' cannot be used with type arguments - {1} '{0}' no se puede usar con argumentos de tipo. + {1} '{0}' no se puede usar con argumentos de tipo. The non-generic {1} '{0}' cannot be used with type arguments - Uso de {1} '{0}' de tipo no genérico: no se puede usar con argumentos de tipo + Uso de {1} '{0}' de tipo no genérico: no se puede usar con argumentos de tipo '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - '{2}' debe ser un tipo no abstracto con un constructor público sin parámetros para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. + '{2}' debe ser un tipo no abstracto con un constructor público sin parámetros para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. No hay ninguna conversión de referencia implícita de '{3}' a '{1}'. + El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. No hay ninguna conversión de referencia implícita de '{3}' a '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. El tipo que acepta valores NULL '{3}' no cumple la restricción de '{1}'. + El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. El tipo que acepta valores NULL '{3}' no cumple la restricción de '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. El tipo que acepta valores NULL '{3}' no cumple la restricción de '{1}'. Los tipos que aceptan valores NULL no pueden cumplir restricciones de interfaz. + El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. El tipo que acepta valores NULL '{3}' no cumple la restricción de '{1}'. Los tipos que aceptan valores NULL no pueden cumplir restricciones de interfaz. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. No hay conversión boxing de '{3}' a '{1}'. + El tipo '{3}' no se puede usar como parámetro de tipo '{2}' en el tipo o método genérico '{0}'. No hay conversión boxing de '{3}' a '{1}'. The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - Los argumentos de tipo para el método '{0}' no se pueden inferir a partir del uso. Intente especificar los argumentos de tipo explícitamente. + Los argumentos de tipo para el método '{0}' no se pueden inferir a partir del uso. Intente especificar los argumentos de tipo explícitamente. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - El tipo '{2}' debe ser un tipo de referencia para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. + El tipo '{2}' debe ser un tipo de referencia para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - El tipo '{2}' debe ser un tipo de valor que no acepte valores NULL para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. + El tipo '{2}' debe ser un tipo de valor que no acepte valores NULL para poder usarlo como parámetro '{1}' en el tipo o método genérico '{0}'. Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - Conversiones ambiguas definidas por el usuario '{0}' y '{1}' al convertir de '{2}' a '{3}' + Conversiones ambiguas definidas por el usuario '{0}' y '{1}' al convertir de '{2}' a '{3}' '{0}' is not supported by the language - '{0}' no es compatible con el lenguaje + '{0}' no es compatible con el lenguaje '{0}': cannot explicitly call operator or accessor - '{0}': no se puede llamar explícitamente al operador o al descriptor de acceso + '{0}': no se puede llamar explícitamente al operador o al descriptor de acceso Cannot convert to static type '{0}' - No se puede convertir en el tipo estático '{0}' + No se puede convertir en el tipo estático '{0}' The operand of an increment or decrement operator must be a variable, property or indexer - El operando de un operador de incremento o decremento debe ser una variable, una propiedad o un indizador + El operando de un operador de incremento o decremento debe ser una variable, una propiedad o un indizador No overload for method '{0}' takes '{1}' arguments - Ninguna sobrecarga del método '{0}' toma argumentos '{1}' + Ninguna sobrecarga del método '{0}' toma argumentos '{1}' The best overloaded method match for '{0}' has some invalid arguments - La mejor coincidencia de método sobrecargado para '{0}' tiene algunos argumentos no válidos + La mejor coincidencia de método sobrecargado para '{0}' tiene algunos argumentos no válidos A ref or out argument must be an assignable variable - Un argumento out o ref debe ser una variable asignable + Un argumento out o ref debe ser una variable asignable. Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - No se puede obtener acceso al miembro protegido '{0}' mediante un calificador del tipo '{1}'; el calificador debe ser del tipo '{2}' (o derivado de éste) + No se puede obtener acceso al miembro protegido '{0}' mediante un calificador del tipo '{1}'; el calificador debe ser del tipo '{2}' (o derivado de éste) Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - El lenguaje no admite la propiedad, el indizador o el evento '{0}'; intente llamar directamente a los métodos de descriptor de acceso '{1}' o '{2}' + El lenguaje no admite la propiedad, el indizador o el evento '{0}'; intente llamar directamente a los métodos de descriptor de acceso '{1}' o '{2}' Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - El lenguaje no admite la propiedad, el indizador o el evento '{0}'; intente llamar directamente al método de descriptor de acceso '{1}' + El lenguaje no admite la propiedad, el indizador o el evento '{0}'; intente llamar directamente al método de descriptor de acceso '{1}' Delegate '{0}' does not take '{1}' arguments - El delegado '{0}' no toma '{1}' argumentos + El delegado '{0}' no toma '{1}' argumentos Delegate '{0}' has some invalid arguments - El delegado '{0}' tiene algunos argumentos no válidos + El delegado '{0}' tiene algunos argumentos no válidos Cannot assign to '{0}' because it is read-only - No se puede asignar a '{0}' porque es de solo lectura + No se puede asignar a '{0}' porque es de solo lectura Cannot pass '{0}' as a ref or out argument because it is read-only - No se puede pasar '{0}' como argumento out o ref porque es de solo lectura. + No se puede pasar '{0}' como argumento out o ref porque es de solo lectura. Cannot modify the return value of '{0}' because it is not a variable - No se puede modificar el valor devuelto de '{0}' porque no es una variable. + No se puede modificar el valor devuelto de '{0}' porque no es una variable. Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - Los miembros del campo de solo lectura '{0}' no se pueden modificar (excepto en un constructor o inicializador de variable) + Los miembros del campo de solo lectura '{0}' no se pueden modificar (excepto en un constructor o inicializador de variable) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - No se puede pasar out o ref a los miembros del campo de solo lectura '{0}' (excepto en un constructor). + No se puede pasar out o ref a los miembros del campo de solo lectura '{0}' (excepto en un constructor). Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - No se puede asignar a los campos del campo estático de solo lectura '{0}' (excepto en un constructor estático o un inicializador de variable) + No se puede asignar a los campos del campo estático de solo lectura '{0}' (excepto en un constructor estático o un inicializador de variable) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - No se puede pasar out o ref a los campos del campo estático de solo lectura '{0}' (excepto en un constructor estático). + No se puede pasar out o ref a los campos del campo estático de solo lectura '{0}' (excepto en un constructor estático). Cannot assign to '{0}' because it is a '{1}' - No se puede asignar a '{0}' porque es '{1}' + No se puede asignar a '{0}' porque es '{1}' Cannot pass '{0}' as a ref or out argument because it is a '{1}' - No se puede pasar '{0}' como argumento out o ref porque es '{1}'. + No se puede pasar '{0}' como argumento out o ref porque es '{1}'. '{0}' does not contain a constructor that takes '{1}' arguments - '{0}' no contiene un constructor que tome '{1}' argumentos + '{0}' no contiene un constructor que tome '{1}' argumentos Non-invocable member '{0}' cannot be used like a method. - No se puede usar como método el miembro '{0}' no invocable. + No se puede usar como método el miembro '{0}' no invocable. Named argument specifications must appear after all fixed arguments have been specified - Las especificaciones de argumento con nombre deben aparecer después de haber especificado todos los argumentos fijos. + Las especificaciones de argumento con nombre deben aparecer después de haber especificado todos los argumentos fijos. The best overload for '{0}' does not have a parameter named '{1}' - La mejor sobrecarga para '{0}' no tiene un parámetro denominado '{1}' + La mejor sobrecarga para '{0}' no tiene un parámetro denominado '{1}' The delegate '{0}' does not have a parameter named '{1}' - El delegado '{0}' no tiene un parámetro denominado '{1}' + El delegado '{0}' no tiene un parámetro denominado '{1}' Named argument '{0}' cannot be specified multiple times - El argumento con nombre '{0}' no se puede especificar varias veces + El argumento con nombre '{0}' no se puede especificar varias veces Named argument '{0}' specifies a parameter for which a positional argument has already been given - El argumento con nombre '{0}' especifica un parámetro para el que ya se ha proporcionado un argumento posicional. + El argumento con nombre '{0}' especifica un parámetro para el que ya se ha proporcionado un argumento posicional. - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.fr.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.fr.xlf index 0801eab4e54..a17f3bbae20 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.fr.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.fr.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - Une exception inattendue s'est produite lors de la liaison d'une opération dynamique + Une exception inattendue s'est produite lors de la liaison d'une opération dynamique Cannot bind call with no calling object - Impossible de lier l'appel sans objet appelant + Impossible de lier l'appel sans objet appelant Overload resolution failed - Échec de la résolution de surcharge + Échec de la résolution de surcharge Binary operators must be invoked with two arguments - Les opérateurs binaires doivent être appelés avec deux arguments + Les opérateurs binaires doivent être appelés avec deux arguments Unary operators must be invoked with one argument - Les opérateurs unaires doivent être appelés à l'aide d'un seul argument + Les opérateurs unaires doivent être appelés à l'aide d'un seul argument The name '{0}' is bound to a method and cannot be used like a property - Le nom '{0}' est lié à une méthode et ne peut pas être utilisé comme une propriété + Le nom '{0}' est lié à une méthode et ne peut pas être utilisé comme une propriété The event '{0}' can only appear on the left hand side of + - L'événement « {0} » ne peut apparaître sur le côté gauche de + + L'événement « {0} » ne peut apparaître qu'à gauche de + Cannot invoke a non-delegate type - Impossible d'appeler un type non-délégué + Impossible d'appeler un type non-délégué Binary operators cannot be invoked with one argument - Impossible d'appeler les opérateurs binaires avec un argument + Impossible d'appeler les opérateurs binaires avec un argument Cannot perform member assignment on a null reference - Impossible d'effectuer une assignation de membre sur une référence null + Impossible d'effectuer une assignation de membre sur une référence null Cannot perform runtime binding on a null reference - Impossible d'effectuer une liaison au moment de l'exécution sur une référence null + Impossible d'effectuer une liaison au moment de l'exécution sur une référence null Cannot dynamically invoke method '{0}' because it has a Conditional attribute - Impossible d'appeler dynamiquement la méthode '{0}' car elle a un attribut Conditional + Impossible d'appeler dynamiquement la méthode '{0}' car elle a un attribut Conditional Cannot implicitly convert type 'void' to 'object' - Impossible de convertir implicitement le type 'void' en 'object' + Impossible de convertir implicitement le type 'void' en 'object' Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - Impossible d'appliquer l'opérateur '{0}' aux opérandes de type '{1}' et '{2}' + Impossible d'appliquer l'opérateur '{0}' aux opérandes de type '{1}' et '{2}' Cannot apply indexing with [] to an expression of type '{0}' - Impossible d'appliquer l'indexation à l'aide de [] à une expression de type '{0}' + Impossible d'appliquer l'indexation à l'aide de [] à une expression de type '{0}' Wrong number of indices inside []; expected '{0}' - Nombre d'index incorrects dans [], '{0}' attendu + Nombre d'index incorrects dans [], '{0}' attendu Operator '{0}' cannot be applied to operand of type '{1}' - Impossible d'appliquer l'opérateur '{0}' à un opérande de type '{1}' + Impossible d'appliquer l'opérateur '{0}' à un opérande de type '{1}' Cannot implicitly convert type '{0}' to '{1}' - Impossible de convertir implicitement le type '{0}' en '{1}' + Impossible de convertir implicitement le type '{0}' en '{1}' Cannot convert type '{0}' to '{1}' - Impossible de convertir le type « {0} » pour « {1} » + Impossible de convertir le type '{0}' en '{1}' Constant value '{0}' cannot be converted to a '{1}' - Impossible de convertir la valeur de constante '{0}' en '{1}' + Impossible de convertir la valeur de constante '{0}' en '{1}' Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - L'opérateur '{0}' est ambigu pour des opérandes de type '{1}' et '{2}' + L'opérateur '{0}' est ambigu pour des opérandes de type '{1}' et '{2}' Operator '{0}' is ambiguous on an operand of type '{1}' - L'opérateur '{0}' est ambigu pour un opérande de type '{1}' + L'opérateur '{0}' est ambigu pour un opérande de type '{1}' Cannot convert null to '{0}' because it is a non-nullable value type - Impossible de convertir null en '{0}', car il s'agit d'un type valeur qui n'autorise pas les valeurs null + Impossible de convertir null en '{0}', car il s'agit d'un type valeur qui n'autorise pas les valeurs null Cannot access a non-static member of outer type '{0}' via nested type '{1}' - Impossible d'accéder à un membre non statique de type externe '{0}' par l'intermédiaire du type imbriqué '{1}' + Impossible d'accéder à un membre non statique de type externe '{0}' par l'intermédiaire du type imbriqué '{1}' '{0}' does not contain a definition for '{1}' - '{0}' ne contient pas de définition pour '{1}' + '{0}' ne contient pas de définition pour '{1}' An object reference is required for the non-static field, method, or property '{0}' - Une référence d'objet est requise pour la propriété, la méthode ou le champ non statique '{0}' + Une référence d'objet est requise pour la propriété, la méthode ou le champ non statique '{0}' The call is ambiguous between the following methods or properties: '{0}' and '{1}' - L'appel est ambigu entre les méthodes ou propriétés suivantes : '{0}' et '{1}' + L'appel est ambigu entre les méthodes ou propriétés suivantes : '{0}' et '{1}' '{0}' is inaccessible due to its protection level - '{0}' est inaccessible en raison de son niveau de protection + '{0}' est inaccessible en raison de son niveau de protection No overload for '{0}' matches delegate '{1}' - Aucune surcharge pour '{0}' ne correspond au délégué '{1}' + Aucune surcharge pour '{0}' ne correspond au délégué '{1}' The left-hand side of an assignment must be a variable, property or indexer - La partie gauche d'une assignation doit être une variable, une propriété ou un indexeur + La partie gauche d'une assignation doit être une variable, une propriété ou un indexeur The type '{0}' has no constructors defined - Aucun constructeur n'est défini pour le type '{0}' + Aucun constructeur n'est défini pour le type '{0}' The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car il lui manque l'accesseur get + Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car il lui manque l'accesseur get Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - Le membre '{0}' est inaccessible avec une référence d'instance ; qualifiez-le avec un nom de type + Le membre '{0}' est inaccessible avec une référence d'instance ; qualifiez-le avec un nom de type A readonly field cannot be assigned to (except in a constructor or a variable initializer) - Un champ readonly ne peut pas être assigné (sauf s'il appartient à un constructeur ou un initialiseur de variable) + Un champ readonly ne peut pas être assigné (sauf s'il appartient à un constructeur ou un initialiseur de variable) A readonly field cannot be passed ref or out (except in a constructor) - Un champ readonly ne peut pas être passé en ref ou out (sauf s'il appartient à un constructeur) + Un champ readonly ne peut pas être passé en ref ou out (sauf s'il appartient à un constructeur) A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - Un champ readonly statique ne peut pas être assigné (sauf s'il appartient à un constructeur statique ou un initialiseur de variable) + Un champ readonly statique ne peut pas être assigné (sauf s'il appartient à un constructeur statique ou un initialiseur de variable) A static readonly field cannot be passed ref or out (except in a static constructor) - Un champ readonly statique ne peut pas être passé en ref ou out (sauf s'il appartient à un constructeur statique) + Un champ readonly statique ne peut pas être passé en ref ou out (sauf s'il appartient à un constructeur statique) Property or indexer '{0}' cannot be assigned to -- it is read only - Impossible d'assigner la propriété ou l'indexeur '{0}' -- il est en lecture seule + Impossible d'assigner la propriété ou l'indexeur '{0}' -- il est en lecture seule A property or indexer may not be passed as an out or ref parameter - Une propriété ou un indexeur ne peut pas être passé en tant que paramètre de sortie (out) ni de référence (ref) + Impossible de passer une propriété ou un indexeur en tant que paramètre de sortie (out) ni de référence (ref) Dynamic calls cannot be used in conjunction with pointers - Impossible d'utiliser des appels dynamiques avec des pointeurs + Impossible d'utiliser des appels dynamiques avec des pointeurs In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - Pour être applicable en tant qu'opérateur de court-circuit, un opérateur logique défini par l'utilisateur '{0}') doit avoir le même type de retour que le type de ses 2 paramètres + Pour être applicable en tant qu'opérateur de court-circuit, un opérateur logique défini par l'utilisateur '{0}') doit avoir le même type de retour que le type de ses 2 paramètres The type ('{0}') must contain declarations of operator true and operator false - Le type ('{0}') doit contenir les déclarations de l'opérateur true et de l'opérateur false + Le type ('{0}') doit contenir les déclarations de l'opérateur true et de l'opérateur false Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - Impossible de convertir la valeur de constante '{0}' en '{1}' (utilisez la syntaxe 'unchecked) + Impossible de convertir la valeur de constante '{0}' en '{1}' (utilisez la syntaxe 'unchecked) Ambiguity between '{0}' and '{1}' - Ambiguïté entre '{0}' et '{1}' + Ambiguïté entre '{0}' et '{1}' Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - Impossible de convertir implicitement le type '{0}' en '{1}'. Une conversion explicite existe (un cast est-il manquant ?) + Impossible de convertir implicitement le type '{0}' en '{1}'. Une conversion explicite existe (un cast est-il manquant ?) The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car l'accesseur get n'est pas accessible + Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car l'accesseur get n'est pas accessible The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car l'accesseur set n'est pas accessible + Impossible d'utiliser la propriété ou l'indexeur '{0}' dans ce contexte, car l'accesseur set n'est pas accessible Using the generic {1} '{0}' requires '{2}' type arguments - L'utilisation du {1} '{0}' générique requiert les arguments de type '{2}' + L'utilisation du {1} '{0}' générique requiert les arguments de type '{2}' The {1} '{0}' cannot be used with type arguments - Impossible d'utiliser le {1} '{0}' avec des arguments de type + Impossible d'utiliser le {1} '{0}' avec des arguments de type The non-generic {1} '{0}' cannot be used with type arguments - Impossible d'utiliser le {1} '{0}' non générique avec des arguments de type + Impossible d'utiliser le {1} '{0}' non générique avec des arguments de type '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - '{2}' doit être un type non abstrait avec un constructeur sans paramètre public afin de l'utiliser comme paramètre '{1}' dans le type ou la méthode générique '{0}' + '{2}' doit être un type non abstrait avec un constructeur sans paramètre public afin de l'utiliser comme paramètre '{1}' dans le type ou la méthode générique '{0}' The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Il n'y a pas de conversion de référence implicite de '{3}' en '{1}'. + Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Il n'y a pas de conversion de référence implicite de '{3}' en '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Le type Nullable '{3}' ne satisfait pas la contrainte de '{1}'. + Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Le type Nullable '{3}' ne satisfait pas la contrainte de '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Le type Nullable '{3}' ne satisfait pas la contrainte de '{1}'. Les types Nullable ne peuvent pas satisfaire les contraintes d'interface. + Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Le type Nullable '{3}' ne satisfait pas la contrainte de '{1}'. Les types Nullable ne peuvent pas satisfaire les contraintes d'interface. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Il n'y a pas de conversion boxing de '{3}' en '{1}'. + Impossible d'utiliser le type '{3}' comme paramètre de type '{2}' dans le type ou la méthode générique '{0}'. Il n'y a pas de conversion boxing de '{3}' en '{1}'. The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - Impossible de déduire les arguments de type pour la méthode '{0}' à partir de l'utilisation. Essayez de spécifier les arguments de type de façon explicite. + Impossible de déduire les arguments de type pour la méthode '{0}' à partir de l'utilisation. Essayez de spécifier les arguments de type de façon explicite. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - Le type '{2}' doit être un type référence afin d'être utilisé comme paramètre '{1}' dans le type ou la méthode générique '{0}' + Le type '{2}' doit être un type référence afin d'être utilisé comme paramètre '{1}' dans le type ou la méthode générique '{0}' The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - Le type '{2}' doit être un type valeur non Nullable afin d'être utilisé comme paramètre '{1}' dans le type ou la méthode générique '{0}' + Le type '{2}' doit être un type valeur non Nullable afin d'être utilisé comme paramètre '{1}' dans le type ou la méthode générique '{0}' Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - Conversions définies par l'utilisateur ambiguës '{0}' et '{1}' lors de la conversion de '{2}' en '{3}' + Conversions définies par l'utilisateur ambiguës '{0}' et '{1}' lors de la conversion de '{2}' en '{3}' '{0}' is not supported by the language - '{0}' n'est pas pris en charge par le langage + '{0}' n'est pas pris en charge par le langage '{0}': cannot explicitly call operator or accessor - '{0}' : impossible d'appeler explicitement un opérateur ou un accesseur + '{0}' : impossible d'appeler explicitement un opérateur ou un accesseur Cannot convert to static type '{0}' - Impossible de convertir en type static '{0}' + Impossible de convertir en type static '{0}' The operand of an increment or decrement operator must be a variable, property or indexer - L'opérande d'un opérateur d'incrémentation ou de décrémentation doit être une variable, une propriété ou un indexeur + L'opérande d'un opérateur d'incrémentation ou de décrémentation doit être une variable, une propriété ou un indexeur No overload for method '{0}' takes '{1}' arguments - Aucune surcharge pour la méthode '{0}' ne prend d'arguments '{1}' + Aucune surcharge pour la méthode '{0}' ne prend d'arguments '{1}' The best overloaded method match for '{0}' has some invalid arguments - La méthode surchargée correspondant le mieux à '{0}' a des arguments non valides + La méthode surchargée correspondant le mieux à '{0}' a des arguments non valides A ref or out argument must be an assignable variable - Un argument ref ou out doit être une variable qui peut être assignée + Un argument ref ou out doit être une variable qui peut être assignée Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - Impossible d'accéder au membre protégé '{0}' par l'intermédiaire d'un qualificateur de type '{1}' ; le qualificateur doit être de type '{2}' (ou dérivé de celui-ci) + Impossible d'accéder au membre protégé '{0}' par l'intermédiaire d'un qualificateur de type '{1}' ; le qualificateur doit être de type '{2}' (ou dérivé de celui-ci) Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - La propriété, l'indexeur ou l'événement '{0}' n'est pas pris en charge par le langage ; essayez d'appeler directement les méthodes d'accesseur '{1}' ou '{2}' + La propriété, l'indexeur ou l'événement '{0}' n'est pas pris en charge par le langage ; essayez d'appeler directement les méthodes d'accesseur '{1}' ou '{2}' Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - La propriété, l'indexeur ou l'événement '{0}' n'est pas pris en charge par le langage ; essayez d'appeler directement la méthode d'accesseur '{1}' + La propriété, l'indexeur ou l'événement '{0}' n'est pas pris en charge par le langage ; essayez d'appeler directement la méthode d'accesseur '{1}' Delegate '{0}' does not take '{1}' arguments - Le délégué '{0}' ne prend pas les arguments '{1}' + Le délégué '{0}' ne prend pas les arguments '{1}' Delegate '{0}' has some invalid arguments - Le délégué '{0}' utilise des arguments non valides + Le délégué '{0}' utilise des arguments non valides Cannot assign to '{0}' because it is read-only - Impossible d'assigner à '{0}', car il est en lecture seule + Impossible d'assigner à '{0}', car il est en lecture seule Cannot pass '{0}' as a ref or out argument because it is read-only - Impossible de passer '{0}' comme argument ref ou out, car il est en lecture seule + Impossible de passer '{0}' comme argument ref ou out, car il est en lecture seule Cannot modify the return value of '{0}' because it is not a variable - Impossible de modifier la valeur de retour de '{0}' car il ne s'agit pas d'une variable + Impossible de modifier la valeur de retour de '{0}' car il ne s'agit pas d'une variable Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - Impossible de modifier les membres d'un champ readonly '{0}' (sauf s'ils appartiennent à un constructeur ou un initialiseur de variable) + Impossible de modifier les membres d'un champ readonly '{0}' (sauf s'ils appartiennent à un constructeur ou un initialiseur de variable) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - Impossible de passer les membres d'un champ readonly '{0}' en ref ou out (sauf s'ils appartiennent à un constructeur) + Impossible de passer les membres d'un champ readonly '{0}' en ref ou out (sauf s'ils appartiennent à un constructeur) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - Impossible d'assigner les champs du champ readonly statique '{0}' (sauf s'ils appartiennent à un constructeur statique ou un initialiseur de variable) + Impossible d'assigner les champs du champ readonly statique '{0}' (sauf s'ils appartiennent à un constructeur statique ou un initialiseur de variable) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - Impossible de passer les champs d'un champ readonly statique '{0}' en ref ou out (sauf s'ils appartiennent à un constructeur statique) + Impossible de passer les champs d'un champ readonly statique '{0}' en ref ou out (sauf s'ils appartiennent à un constructeur statique) Cannot assign to '{0}' because it is a '{1}' - Impossible d'assigner à '{0}', car il s'agit d'un '{1}' + Impossible d'assigner à '{0}', car il s'agit d'un '{1}' Cannot pass '{0}' as a ref or out argument because it is a '{1}' - Impossible de passer '{0}' en tant qu'argument ref ou out, car il s'agit d'un '{1}' + Impossible de passer '{0}' en tant qu'argument ref ou out, car il s'agit d'un '{1}' '{0}' does not contain a constructor that takes '{1}' arguments - '{0}' ne contient pas de constructeur qui accepte des arguments '{1}' + '{0}' ne contient pas de constructeur qui accepte des arguments '{1}' Non-invocable member '{0}' cannot be used like a method. - Impossible d'utiliser un membre '{0}' ne pouvant pas être appelé comme une méthode. + Impossible d'utiliser un membre '{0}' ne pouvant pas être appelé comme une méthode. Named argument specifications must appear after all fixed arguments have been specified - Les spécifications d'argument nommé doivent s'afficher après la spécification de tous les arguments fixes + Les spécifications d'argument nommé doivent s'afficher après la spécification de tous les arguments fixes The best overload for '{0}' does not have a parameter named '{1}' - La meilleure surcharge pour '{0}' n'a pas de paramètre nommé '{1}' + La meilleure surcharge pour '{0}' n'a pas de paramètre nommé '{1}' The delegate '{0}' does not have a parameter named '{1}' - Le délégué '{0}' n'a pas de paramètre nommé '{1}' + Le délégué '{0}' n'a pas de paramètre nommé '{1}' Named argument '{0}' cannot be specified multiple times - Impossible de spécifier plusieurs fois l'argument nommé '{0}' + Impossible de spécifier plusieurs fois l'argument nommé '{0}' Named argument '{0}' specifies a parameter for which a positional argument has already been given - L'argument nommé '{0}' spécifie un paramètre pour lequel un paramètre positionnel a déjà été donné + L'argument nommé '{0}' spécifie un paramètre pour lequel un paramètre positionnel a déjà été donné - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.it.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.it.xlf index 892ee9ebe84..19b31746b14 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.it.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.it.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - Eccezione imprevista durante l'associazione di un'operazione dinamica + Eccezione imprevista durante l'associazione di un'operazione dinamica Cannot bind call with no calling object - Impossibile associare la chiamata senza oggetto chiamante + Impossibile associare la chiamata senza oggetto chiamante Overload resolution failed - Risoluzione dell'overload non riuscita + Risoluzione dell'overload non riuscita Binary operators must be invoked with two arguments - Gli operatori binari devono essere richiamati con due argomenti + Gli operatori binari devono essere richiamati con due argomenti Unary operators must be invoked with one argument - Gli operatori unari devono essere richiamati con un solo argomento + Gli operatori unari devono essere richiamati con un solo argomento The name '{0}' is bound to a method and cannot be used like a property - Il nome '{0}' è associato a un metodo e non può essere utilizzato come una proprietà + Il nome '{0}' è associato a un metodo e non può essere utilizzato come una proprietà The event '{0}' can only appear on the left hand side of + - L'evento '{0}' può trovarsi soltanto sul lato sinistro di + + L'evento '{0}' può trovarsi soltanto sul lato sinistro di + Cannot invoke a non-delegate type - Impossibile richiamare un tipo non delegato + Impossibile richiamare un tipo non delegato Binary operators cannot be invoked with one argument - Impossibile richiamare operatori binari con un solo argomento + Impossibile richiamare operatori binari con un solo argomento Cannot perform member assignment on a null reference - Impossibile eseguire un'assegnazione membro su un riferimento Null + Impossibile eseguire un'assegnazione membro su un riferimento Null Cannot perform runtime binding on a null reference - Impossibile eseguire un'associazione di runtime su un riferimento Null + Impossibile eseguire un'associazione di runtime su un riferimento Null Cannot dynamically invoke method '{0}' because it has a Conditional attribute - Impossibile richiamare in modo dinamico il metodo '{0}' perché contiene un attributo Conditional + Impossibile richiamare in modo dinamico il metodo '{0}' perché contiene un attributo Conditional Cannot implicitly convert type 'void' to 'object' - Impossibile convertire il tipo 'void' in 'object' in modo implicito + Impossibile convertire il tipo 'void' in 'object' in modo implicito Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - Impossibile applicare l'operatore '{0}' su operandi di tipo '{1}' e '{2}' + Impossibile applicare l'operatore '{0}' su operandi di tipo '{1}' e '{2}' Cannot apply indexing with [] to an expression of type '{0}' - Impossibile applicare l'indicizzazione con [] a un'espressione di tipo '{0}' + Impossibile applicare l'indicizzazione con [] a un'espressione di tipo '{0}' Wrong number of indices inside []; expected '{0}' - Numero di indici errato in [], previsto '{0}' + Numero di indici errato in [], previsto '{0}' Operator '{0}' cannot be applied to operand of type '{1}' - Impossibile applicare l'operatore '{0}' sull'operando di tipo '{1}' + Impossibile applicare l'operatore '{0}' sull'operando di tipo '{1}' Cannot implicitly convert type '{0}' to '{1}' - Impossibile convertire in modo implicito il tipo '{0}' in '{1}' + Impossibile convertire in modo implicito il tipo '{0}' in '{1}' Cannot convert type '{0}' to '{1}' - Impossibile convertire il tipo '{0}' a '{1}' + Non è possibile convertire il tipo '{0}' in '{1}' Constant value '{0}' cannot be converted to a '{1}' - Impossibile convertire il valore costante '{0}' in '{1}' + Impossibile convertire il valore costante '{0}' in '{1}' Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - L'operatore '{0}' è ambiguo su operandi di tipo '{1}' e '{2}' + L'operatore '{0}' è ambiguo su operandi di tipo '{1}' e '{2}' Operator '{0}' is ambiguous on an operand of type '{1}' - L'operatore '{0}' è ambiguo su un operando di tipo '{1}' + L'operatore '{0}' è ambiguo su un operando di tipo '{1}' Cannot convert null to '{0}' because it is a non-nullable value type - Impossibile convertire Null in '{0}' perché è un tipo di valore non nullable + Impossibile convertire Null in '{0}' perché è un tipo di valore non nullable Cannot access a non-static member of outer type '{0}' via nested type '{1}' - Impossibile accedere a un membro non statico di tipo outer '{0}' tramite il tipo annidato '{1}' + Impossibile accedere a un membro non statico di tipo outer '{0}' tramite il tipo annidato '{1}' '{0}' does not contain a definition for '{1}' - '{0}' non contiene una definizione per '{1}' + '{0}' non contiene una definizione per '{1}' An object reference is required for the non-static field, method, or property '{0}' - È necessario specificare un riferimento a un oggetto per la proprietà, il metodo o il campo non statico '{0}' + È necessario specificare un riferimento a un oggetto per la proprietà, il metodo o il campo non statico '{0}' The call is ambiguous between the following methods or properties: '{0}' and '{1}' - Chiamata ambigua tra i seguenti metodi o proprietà: '{0}' e '{1}' + Chiamata ambigua tra i seguenti metodi o proprietà: '{0}' e '{1}' '{0}' is inaccessible due to its protection level - '{0}' è inaccessibile a causa del livello di protezione. + '{0}' è inaccessibile a causa del livello di protezione. No overload for '{0}' matches delegate '{1}' - Nessun overload per '{0}' corrisponde al delegato '{1}' + Nessun overload per '{0}' corrisponde al delegato '{1}' The left-hand side of an assignment must be a variable, property or indexer - La parte sinistra di un'assegnazione deve essere una variabile, una proprietà o un indicizzatore + La parte sinistra di un'assegnazione deve essere una variabile, una proprietà o un indicizzatore The type '{0}' has no constructors defined - Per il tipo '{0}' non sono definiti costruttori + Per il tipo '{0}' non sono definiti costruttori The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - Impossibile utilizzare la proprietà o l'indicizzatore '{0}' in questo contesto perché manca la funzione di accesso get. + Non è possibile usare la proprietà o l'indicizzatore '{0}' in questo contesto perché manca la funzione di accesso get Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - Impossibile accedere al membro '{0}' con un riferimento a un'istanza. Qualificarlo con un nome di tipo + Impossibile accedere al membro '{0}' con un riferimento a un'istanza. Qualificarlo con un nome di tipo A readonly field cannot be assigned to (except in a constructor or a variable initializer) - Un campo readonly non può essere assegnato (tranne che in un costruttore o in un inizializzatore di variabile). + Un campo di sola lettura non può essere assegnato (tranne che in un costruttore o in un inizializzatore di variabile) A readonly field cannot be passed ref or out (except in a constructor) - Un campo readonly non può essere passato a un parametro ref o out (tranne che in un costruttore) + Un campo di sola lettura non può essere passato a un parametro ref o out (tranne che in un costruttore) A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - Impossibile effettuare un'assegnazione a un campo statico in sola lettura (tranne che in un costruttore statico o in un inizializzatore di variabile). + Impossibile effettuare un'assegnazione a un campo statico in sola lettura (tranne che in un costruttore statico o in un inizializzatore di variabile) A static readonly field cannot be passed ref or out (except in a static constructor) - Impossibile passare un parametro ref o out a un campo statico in sola lettura (tranne che in un costruttore statico). + Impossibile passare un parametro ref o out a un campo statico in sola lettura (tranne che in un costruttore statico) Property or indexer '{0}' cannot be assigned to -- it is read only - Impossibile assegnare un valore alla proprietà o all'indicizzatore '{0}' perché è di sola lettura + Impossibile assegnare un valore alla proprietà o all'indicizzatore '{0}' perché è di sola lettura A property or indexer may not be passed as an out or ref parameter - Una proprietà o un indicizzatore non può essere passato come parametro out o ref + Una proprietà o un indicizzatore non può essere passato come parametro out o ref Dynamic calls cannot be used in conjunction with pointers - Impossibile utilizzare le chiamate dinamiche insieme ai puntatori + Impossibile utilizzare le chiamate dinamiche insieme ai puntatori In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - Per essere utilizzato come operatore di corto circuito, un operatore logico definito dall'utente ('{0}') deve avere lo stesso tipo restituito dei 2 parametri + Per essere utilizzato come operatore di corto circuito, un operatore logico definito dall'utente ('{0}') deve avere lo stesso tipo restituito dei 2 parametri The type ('{0}') must contain declarations of operator true and operator false - Il tipo ('{0}') deve contenere dichiarazioni di operatore true e operatore false. + Il tipo ('{0}') deve contenere dichiarazioni di operatore true e operatore false. Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - Il valore costante '{0}' non può essere convertito in '{1}' (utilizzare la sintassi 'unchecked' per eseguire l'override). + Il valore costante '{0}' non può essere convertito in '{1}' (utilizzare la sintassi 'unchecked' per eseguire l'override). Ambiguity between '{0}' and '{1}' - Ambiguità tra '{0}' e '{1}' + Ambiguità tra '{0}' e '{1}' Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - Impossibile convertire in modo implicito il tipo '{0}' in '{1}'. È presente una conversione esplicita. Probabile cast mancante. + Impossibile convertire in modo implicito il tipo '{0}' in '{1}'. È presente una conversione esplicita. Probabile cast mancante. The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - Impossibile utilizzare la proprietà o l'indicizzatore '{0}' in questo contesto perché la funzione di accesso get è inaccessibile. + Impossibile utilizzare la proprietà o l'indicizzatore '{0}' in questo contesto perché la funzione di accesso get è inaccessibile. The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - Impossibile utilizzare la proprietà o l'indicizzatore '{0}' in questo contesto perché la funzione di accesso set è inaccessibile. + Impossibile utilizzare la proprietà o l'indicizzatore '{0}' in questo contesto perché la funzione di accesso set è inaccessibile. Using the generic {1} '{0}' requires '{2}' type arguments - L'utilizzo del {1} generico '{0}' richiede argomenti di tipo '{2}' + L'utilizzo del {1} generico '{0}' richiede argomenti di tipo '{2}' The {1} '{0}' cannot be used with type arguments - Impossibile utilizzare {1} '{0}' insieme ad argomenti di tipo + Impossibile utilizzare {1} '{0}' insieme ad argomenti di tipo The non-generic {1} '{0}' cannot be used with type arguments - Impossibile utilizzare {1} non generico '{0}' insieme ad argomenti di tipo + Impossibile utilizzare {1} non generico '{0}' insieme ad argomenti di tipo '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - '{2}' deve essere un tipo non astratto con un costruttore pubblico senza parametri per essere utilizzato come parametro '{1}' nel tipo o nel metodo generico '{0}' + '{2}' deve essere un tipo non astratto con un costruttore pubblico senza parametri per essere utilizzato come parametro '{1}' nel tipo o nel metodo generico '{0}' The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel metodo o nel tipo generico '{0}'. Nessuna conversione implicita di riferimenti da '{3}' a '{1}'. + Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel metodo o nel tipo generico '{0}'. Nessuna conversione implicita di riferimenti da '{3}' a '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel tipo o metodo generico '{0}'. Il tipo nullable '{3}' non soddisfa il vincolo di '{1}'. + Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel tipo o metodo generico '{0}'. Il tipo nullable '{3}' non soddisfa il vincolo di '{1}'. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel tipo o metodo generico '{0}'. Il tipo nullable '{3}' non soddisfa il vincolo di '{1}'. I tipi nullable non soddisfano i vincoli di interfaccia. + Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel tipo o metodo generico '{0}'. Il tipo nullable '{3}' non soddisfa il vincolo di '{1}'. I tipi nullable non soddisfano i vincoli di interfaccia. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel metodo o nel tipo generico '{0}'. Nessuna conversione boxing da '{3}' a '{1}'. + Impossibile utilizzare il tipo '{3}' come parametro di tipo '{2}' nel metodo o nel tipo generico '{0}'. Nessuna conversione boxing da '{3}' a '{1}'. The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - Impossibile dedurre gli argomenti di tipo per il metodo '{0}' dall'utilizzo. Provare a specificare gli argomenti di tipo in modo esplicito. + Impossibile dedurre gli argomenti di tipo per il metodo '{0}' dall'utilizzo. Provare a specificare gli argomenti di tipo in modo esplicito. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - È necessario che il tipo '{2}' sia un tipo di riferimento per essere utilizzato come parametro '{1}' nel tipo generico o nel metodo '{0}' + È necessario che il tipo '{2}' sia un tipo di riferimento per essere utilizzato come parametro '{1}' nel tipo generico o nel metodo '{0}' The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - Il tipo '{2}' deve essere un tipo di valore non nullable per poter essere utilizzato come parametro '{1}' nel metodo o nel tipo generico '{0}' + Il tipo '{2}' deve essere un tipo di valore non nullable per poter essere utilizzato come parametro '{1}' nel metodo o nel tipo generico '{0}' Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - Conversioni '{0}' e '{1}' ambigue definite dall'utente durante la conversione da '{2}' a '{3}' + Conversioni '{0}' e '{1}' ambigue definite dall'utente durante la conversione da '{2}' a '{3}' '{0}' is not supported by the language - '{0}' non è supportato dal linguaggio + '{0}' non è supportato dal linguaggio '{0}': cannot explicitly call operator or accessor - '{0}': impossibile chiamare in modo esplicito l'operatore o la funzione di accesso + '{0}': impossibile chiamare in modo esplicito l'operatore o la funzione di accesso Cannot convert to static type '{0}' - Impossibile convertire nel tipo statico '{0}' + Impossibile convertire nel tipo statico '{0}' The operand of an increment or decrement operator must be a variable, property or indexer - L'operando di un operatore di incremento o decremento deve essere una variabile, una proprietà o un indicizzatore + L'operando di un operatore di incremento o decremento deve essere una variabile, una proprietà o un indicizzatore No overload for method '{0}' takes '{1}' arguments - Nessun overload del metodo '{0}' accetta argomenti '{1}' + Nessun overload del metodo '{0}' accetta argomenti '{1}' The best overloaded method match for '{0}' has some invalid arguments - La corrispondenza migliore del metodo di overload per '{0}' presenta alcuni argomenti non validi + La corrispondenza migliore del metodo di overload per '{0}' presenta alcuni argomenti non validi A ref or out argument must be an assignable variable - Un argomento ref o out deve essere una variabile assegnabile. + Un argomento ref o out deve essere una variabile assegnabile Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - Impossibile accedere al membro protetto '{0}' tramite un qualificatore di tipo '{1}'. Il qualificatore deve essere di tipo '{2}' o derivato da esso. + Impossibile accedere al membro protetto '{0}' tramite un qualificatore di tipo '{1}'. Il qualificatore deve essere di tipo '{2}' o derivato da esso. Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - La proprietà, l'indicizzatore o l'evento '{0}' non è supportato dal linguaggio. Provare a chiamare direttamente il metodo '{1}' o '{2}' della funzione di accesso + La proprietà, l'indicizzatore o l'evento '{0}' non è supportato dal linguaggio. Provare a chiamare direttamente il metodo '{1}' o '{2}' della funzione di accesso Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - La proprietà, l'indicizzatore o l'evento '{0}' non è supportato dal linguaggio. Provare a chiamare direttamente il metodo '{1}' della funzione di accesso + La proprietà, l'indicizzatore o l'evento '{0}' non è supportato dal linguaggio. Provare a chiamare direttamente il metodo '{1}' della funzione di accesso Delegate '{0}' does not take '{1}' arguments - Il delegato '{0}' non accetta argomenti '{1}' + Il delegato '{0}' non accetta argomenti '{1}' Delegate '{0}' has some invalid arguments - Il delegato '{0}' presenta alcuni argomenti non validi + Il delegato '{0}' presenta alcuni argomenti non validi Cannot assign to '{0}' because it is read-only - Impossibile assegnare a '{0}' perché è di sola lettura + Impossibile assegnare a '{0}' perché è di sola lettura Cannot pass '{0}' as a ref or out argument because it is read-only - Impossibile passare '{0}' come argomento ref o out perché è di sola lettura + Impossibile passare '{0}' come argomento ref o out perché è di sola lettura Cannot modify the return value of '{0}' because it is not a variable - Impossibile modificare il valore restituito da '{0}' perché non è una variabile + Impossibile modificare il valore restituito da '{0}' perché non è una variabile Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - Impossibile modificare i membri del campo di sola lettura '{0}' (tranne che in un costruttore o in un inizializzatore di variabile) + Impossibile modificare i membri del campo di sola lettura '{0}' (tranne che in un costruttore o in un inizializzatore di variabile) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - Impossibile passare i membri del campo di sola lettura '{0}' come argomento ref o out (tranne che in un costruttore) + Impossibile passare i membri del campo di sola lettura '{0}' come argomento ref o out (tranne che in un costruttore) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - Impossibile effettuare un'assegnazione ai campi di un campo statico di sola lettura '{0}' (tranne che in un costruttore statico o in un inizializzatore di variabile) + Impossibile effettuare un'assegnazione ai campi di un campo statico di sola lettura '{0}' (tranne che in un costruttore statico o in un inizializzatore di variabile) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - Impossibile passare un argomento ref o out ai campi di un campo statico di sola lettura '{0}' (tranne che in un costruttore statico) + Impossibile passare un argomento ref o out ai campi di un campo statico di sola lettura '{0}' (tranne che in un costruttore statico) Cannot assign to '{0}' because it is a '{1}' - Impossibile assegnare a '{0}' perché è '{1}' + Impossibile assegnare a '{0}' perché è '{1}' Cannot pass '{0}' as a ref or out argument because it is a '{1}' - Impossibile passare '{0}' come argomento ref o out perché è '{1}' + Impossibile passare '{0}' come argomento ref o out perché è '{1}' '{0}' does not contain a constructor that takes '{1}' arguments - '{0}' non contiene un costruttore che accetta argomenti '{1}' + '{0}' non contiene un costruttore che accetta argomenti '{1}' Non-invocable member '{0}' cannot be used like a method. - Impossibile utilizzare il membro non richiamabile '{0}' come metodo. + Impossibile utilizzare il membro non richiamabile '{0}' come metodo. Named argument specifications must appear after all fixed arguments have been specified - Le specifiche di argomenti denominati devono trovarsi dopo tutti gli argomenti fissi specificati + Le specifiche di argomenti denominati devono trovarsi dopo tutti gli argomenti fissi specificati The best overload for '{0}' does not have a parameter named '{1}' - Il miglior overload per '{0}' non contiene un parametro denominato '{1}' + Il miglior overload per '{0}' non contiene un parametro denominato '{1}' The delegate '{0}' does not have a parameter named '{1}' - Il delegato '{0}' non dispone di un parametro denominato '{1}' + Il delegato '{0}' non dispone di un parametro denominato '{1}' Named argument '{0}' cannot be specified multiple times - Impossibile specificare l'argomento denominato '{0}' più volte + Impossibile specificare l'argomento denominato '{0}' più volte Named argument '{0}' specifies a parameter for which a positional argument has already been given - L'argomento denominato '{0}' specifica un parametro per cui è già stato fornito un argomento posizionale + L'argomento denominato '{0}' specifica un parametro per cui è già stato fornito un argomento posizionale - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ja.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ja.xlf index 3caea15ebc8..c55cbb57f03 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ja.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ja.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - å‹•çš„ãªæ“作をãƒã‚¤ãƒ³ãƒ‰ä¸­ã«ã€äºˆæœŸã—ãªã„例外ãŒç™ºç”Ÿã—ã¾ã—㟠+ å‹•çš„ãªæ“作をãƒã‚¤ãƒ³ãƒ‰ä¸­ã«ã€äºˆæœŸã—ãªã„例外ãŒç™ºç”Ÿã—ã¾ã—㟠Cannot bind call with no calling object - 呼ã³å‡ºã—元オブジェクトã®ãªã„呼ã³å‡ºã—ã¯ãƒã‚¤ãƒ³ãƒ‰ã§ãã¾ã›ã‚“ + 呼ã³å‡ºã—元オブジェクトã®ãªã„呼ã³å‡ºã—ã¯ãƒã‚¤ãƒ³ãƒ‰ã§ãã¾ã›ã‚“ Overload resolution failed - オーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã®è§£æ±ºã«å¤±æ•—ã—ã¾ã—㟠+ オーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã®è§£æ±ºã«å¤±æ•—ã—ã¾ã—㟠Binary operators must be invoked with two arguments - 二項演算å­ã¯ 2 ã¤ã®å¼•数を指定ã—ã¦å‘¼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ + 二項演算å­ã¯ 2 ã¤ã®å¼•数を指定ã—ã¦å‘¼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ Unary operators must be invoked with one argument - å˜é …演算å­ã¯ 1 ã¤ã®å¼•数を指定ã—ã¦å‘¼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ + å˜é …演算å­ã¯ 1 ã¤ã®å¼•数を指定ã—ã¦å‘¼ã³å‡ºã™å¿…è¦ãŒã‚りã¾ã™ The name '{0}' is bound to a method and cannot be used like a property - åå‰ '{0}' ã¯ãƒ¡ã‚½ãƒƒãƒ‰ã«ãƒã‚¤ãƒ³ãƒ‰ã•れã¦ãŠã‚Šã€ãƒ—ロパティã®ã‚ˆã†ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + åå‰ '{0}' ã¯ãƒ¡ã‚½ãƒƒãƒ‰ã«ãƒã‚¤ãƒ³ãƒ‰ã•れã¦ãŠã‚Šã€ãƒ—ロパティã®ã‚ˆã†ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ The event '{0}' can only appear on the left hand side of + - イベント '{0}' ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã®å·¦å´ã«ã®ã¿è¡¨ç¤ºã•れã¾ã™ + + イベント '{0}' 㯠+ ã®å·¦å´ã«ã®ã¿ä½¿ç”¨ã§ãã¾ã™ Cannot invoke a non-delegate type - éžãƒ‡ãƒªã‚²ãƒ¼ãƒˆåž‹ã‚’呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + éžãƒ‡ãƒªã‚²ãƒ¼ãƒˆåž‹ã‚’呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Binary operators cannot be invoked with one argument - 1 ã¤ã®å¼•数を指定ã—ã¦äºŒé …演算å­ã‚’呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + 1 ã¤ã®å¼•数を指定ã—ã¦äºŒé …演算å­ã‚’呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot perform member assignment on a null reference - null å‚ç…§ã«å¯¾ã—ã¦ãƒ¡ãƒ³ãƒãƒ¼å‰²ã‚Šå½“ã¦ã‚’実行ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + null å‚ç…§ã«å¯¾ã—ã¦ãƒ¡ãƒ³ãƒãƒ¼å‰²ã‚Šå½“ã¦ã‚’実行ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot perform runtime binding on a null reference - null å‚ç…§ã«å¯¾ã—ã¦å®Ÿè¡Œæ™‚ãƒã‚¤ãƒ³ãƒ‡ã‚£ãƒ³ã‚°ã‚’実行ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + null å‚ç…§ã«å¯¾ã—ã¦å®Ÿè¡Œæ™‚ãƒã‚¤ãƒ³ãƒ‡ã‚£ãƒ³ã‚°ã‚’実行ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot dynamically invoke method '{0}' because it has a Conditional attribute - æ¡ä»¶ä»˜ã属性ãŒã‚ã‚‹ãŸã‚ã€ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã‚’å‹•çš„ã«å‘¼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + æ¡ä»¶ä»˜ã属性ãŒã‚ã‚‹ãŸã‚ã€ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã‚’å‹•çš„ã«å‘¼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot implicitly convert type 'void' to 'object' - åž‹ 'void' ã‚’ 'object' ã«æš—黙的ã«å¤‰æ›ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + åž‹ 'void' ã‚’ 'object' ã«æš—黙的ã«å¤‰æ›ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - æ¼”ç®—å­ '{0}' ã‚’ '{1}' 㨠'{2}' åž‹ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«é©ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + æ¼”ç®—å­ '{0}' ã‚’ '{1}' 㨠'{2}' åž‹ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«é©ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot apply indexing with [] to an expression of type '{0}' - è§’ã‹ã£ã“ [] 付ãインデックスを '{0}' åž‹ã®å¼ã«é©ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + è§’ã‹ã£ã“ [] 付ãインデックスを '{0}' åž‹ã®å¼ã«é©ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Wrong number of indices inside []; expected '{0}' - è§’ã‹ã£ã“ [] 内ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹æ•°ãŒæ­£ã—ãã‚りã¾ã›ã‚“。正ã—ã„æ•°ã¯ '{0}' ã§ã™ + è§’ã‹ã£ã“ [] 内ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹æ•°ãŒæ­£ã—ãã‚りã¾ã›ã‚“。正ã—ã„æ•°ã¯ '{0}' ã§ã™ Operator '{0}' cannot be applied to operand of type '{1}' - æ¼”ç®—å­ '{0}' 㯠'{1}' åž‹ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«é©ç”¨ã§ãã¾ã›ã‚“ + æ¼”ç®—å­ '{0}' 㯠'{1}' åž‹ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«é©ç”¨ã§ãã¾ã›ã‚“ Cannot implicitly convert type '{0}' to '{1}' - åž‹ '{0}' ã‚’ '{1}' ã«æš—黙的ã«å¤‰æ›ã§ãã¾ã›ã‚“ + åž‹ '{0}' ã‚’ '{1}' ã«æš—黙的ã«å¤‰æ›ã§ãã¾ã›ã‚“ Cannot convert type '{0}' to '{1}' - åž‹ '{0}' ã‚’ '{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“。 + åž‹ '{0}' ã‚’ '{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ Constant value '{0}' cannot be converted to a '{1}' - 定数値 '{0}' ã‚’ '{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ + 定数値 '{0}' ã‚’ '{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - åž‹ '{1}' ãŠã‚ˆã³ '{2}' ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã®æ¼”ç®—å­ '{0}' ãŒã‚ã„ã¾ã„ã§ã™ + åž‹ '{1}' ãŠã‚ˆã³ '{2}' ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã®æ¼”ç®—å­ '{0}' ãŒã‚ã„ã¾ã„ã§ã™ Operator '{0}' is ambiguous on an operand of type '{1}' - æ¼”ç®—å­ '{0}' ã¯åž‹ '{1}' ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«å¯¾ã—ã¦ã‚ã„ã¾ã„ã§ã™ + æ¼”ç®—å­ '{0}' ã¯åž‹ '{1}' ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«å¯¾ã—ã¦ã‚ã„ã¾ã„ã§ã™ Cannot convert null to '{0}' because it is a non-nullable value type - Null éžè¨±å®¹ã®å€¤åž‹ã§ã‚ã‚‹ãŸã‚ã€Null ã‚’ '{0}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ + Null éžè¨±å®¹ã®å€¤åž‹ã§ã‚ã‚‹ãŸã‚ã€Null ã‚’ '{0}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ Cannot access a non-static member of outer type '{0}' via nested type '{1}' - 入れå­ã«ã•れãŸåž‹ '{1}' を経由ã—ã¦ã€å¤–ã®åž‹ '{0}' ã®é™çš„ã§ãªã„メンãƒãƒ¼ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + 入れå­ã«ã•れãŸåž‹ '{1}' を経由ã—ã¦ã€å¤–ã®åž‹ '{0}' ã®é™çš„ã§ãªã„メンãƒãƒ¼ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ '{0}' does not contain a definition for '{1}' - '{0}' ã« '{1}' ã®å®šç¾©ãŒã‚りã¾ã›ã‚“ + '{0}' ã« '{1}' ã®å®šç¾©ãŒã‚りã¾ã›ã‚“ An object reference is required for the non-static field, method, or property '{0}' - é™çš„ã§ãªã„フィールドã€ãƒ¡ã‚½ãƒƒãƒ‰ã€ã¾ãŸã¯ãƒ—ロパティ '{0}' ã§ã€ã‚ªãƒ–ジェクトå‚ç…§ãŒå¿…è¦ã§ã™ + é™çš„ã§ãªã„フィールドã€ãƒ¡ã‚½ãƒƒãƒ‰ã€ã¾ãŸã¯ãƒ—ロパティ '{0}' ã§ã€ã‚ªãƒ–ジェクトå‚ç…§ãŒå¿…è¦ã§ã™ The call is ambiguous between the following methods or properties: '{0}' and '{1}' - 次ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¾ãŸã¯ãƒ—ロパティ間ã§å‘¼ã³å‡ºã—ãŒä¸é©åˆ‡ã§ã™: '{0}' 㨠'{1}' + 次ã®ãƒ¡ã‚½ãƒƒãƒ‰ã¾ãŸã¯ãƒ—ロパティ間ã§å‘¼ã³å‡ºã—ãŒä¸é©åˆ‡ã§ã™: '{0}' 㨠'{1}' '{0}' is inaccessible due to its protection level - '{0}' ã¯ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ä¿è­·ãƒ¬ãƒ™ãƒ«ã«ãªã£ã¦ã„ã¾ã™ + '{0}' ã¯ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ä¿è­·ãƒ¬ãƒ™ãƒ«ã«ãªã£ã¦ã„ã¾ã™ No overload for '{0}' matches delegate '{1}' - デリゲート '{1}' ã«ä¸€è‡´ã™ã‚‹ '{0}' ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã¯ã‚りã¾ã›ã‚“ + デリゲート '{1}' ã«ä¸€è‡´ã™ã‚‹ '{0}' ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã¯ã‚りã¾ã›ã‚“ The left-hand side of an assignment must be a variable, property or indexer - 代入å¼ã®å·¦è¾ºã«ã¯å¤‰æ•°ã€ãƒ—ロパティã€ã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’指定ã—ã¦ãã ã•ã„。 + 代入å¼ã®å·¦è¾ºã«ã¯å¤‰æ•°ã€ãƒ—ロパティã€ã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’指定ã—ã¦ãã ã•ã„ The type '{0}' has no constructors defined - åž‹ '{0}' ã®ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ãƒ¼ãŒå®šç¾©ã•れã¦ã„ã¾ã›ã‚“ + åž‹ '{0}' ã®ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ãƒ¼ãŒå®šç¾©ã•れã¦ã„ã¾ã›ã‚“ The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - get アクセサーãŒãªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã‚’ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + get アクセサーãŒãªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã‚’ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - インスタンスå‚ç…§ã§ãƒ¡ãƒ³ãƒãƒ¼ '{0}' ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。代ã‚りã«åž‹åを使用ã—ã¦ãã ã•ã„ + インスタンスå‚ç…§ã§ãƒ¡ãƒ³ãƒãƒ¼ '{0}' ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。代ã‚りã«åž‹åを使用ã—ã¦ãã ã•ã„ A readonly field cannot be assigned to (except in a constructor or a variable initializer) - 読ã¿å–り専用フィールドã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã€å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯ )。 + 読ã¿å–り専用フィールドã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã€å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯ ) A readonly field cannot be passed ref or out (except in a constructor) - 読ã¿å–り専用フィールド㫠ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã§ã¯å¯ )。 + 読ã¿å–り専用フィールド㫠ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã§ã¯å¯ ) A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - é™çš„読ã¿å–り専用フィールドã¸ã®å‰²ã‚Šå½“ã¦ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯)。 + é™çš„読ã¿å–り専用フィールドã¸ã®å‰²ã‚Šå½“ã¦ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯) A static readonly field cannot be passed ref or out (except in a static constructor) - 読ã¿å–り専用フィールド㫠ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã§ã¯å¯)。 + 読ã¿å–り専用フィールド㫠ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã§ã¯å¯) Property or indexer '{0}' cannot be assigned to -- it is read only - プロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯èª­ã¿å–り専用ã§ã‚ã‚‹ãŸã‚ã€å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + プロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯èª­ã¿å–り専用ã§ã‚ã‚‹ãŸã‚ã€å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ A property or indexer may not be passed as an out or ref parameter - プロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’ out ã‹ ref ã®ãƒ‘ラメーターã¨ã—ã¦æ¸¡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + プロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’ out ã‹ ref ã®ãƒ‘ラメーターã¨ã—ã¦æ¸¡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Dynamic calls cannot be used in conjunction with pointers - å‹•çš„ãªå‘¼ã³å‡ºã—ã¯ãƒã‚¤ãƒ³ã‚¿ãƒ¼ã¨å…±ã«ä½¿ç”¨ã§ãã¾ã›ã‚“ + å‹•çš„ãªå‘¼ã³å‡ºã—ã¯ãƒã‚¤ãƒ³ã‚¿ãƒ¼ã¨å…±ã«ä½¿ç”¨ã§ãã¾ã›ã‚“ In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - short circuit 演算å­ã¨ã—ã¦é©ç”¨ã™ã‚‹ãŸã‚ã«ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã®è«–ç†æ¼”ç®—å­ ('{0}') ãŒãã® 2 ã¤ã®ãƒ‘ラメーターã¨åŒã˜æˆ»ã‚Šå€¤ã®åž‹ã‚’æŒã¤å¿…è¦ãŒã‚りã¾ã™ + short circuit 演算å­ã¨ã—ã¦é©ç”¨ã™ã‚‹ãŸã‚ã«ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã®è«–ç†æ¼”ç®—å­ ('{0}') ãŒãã® 2 ã¤ã®ãƒ‘ラメーターã¨åŒã˜æˆ»ã‚Šå€¤ã®åž‹ã‚’æŒã¤å¿…è¦ãŒã‚りã¾ã™ The type ('{0}') must contain declarations of operator true and operator false - åž‹ ('{0}') ã«æ¼”ç®—å­ true ãŠã‚ˆã³æ¼”ç®—å­ false ã®å®£è¨€ãŒå«ã¾ã‚Œã¦ã„ã‚‹å¿…è¦ãŒã‚りã¾ã™ + åž‹ ('{0}') ã«æ¼”ç®—å­ true ãŠã‚ˆã³æ¼”ç®—å­ false ã®å®£è¨€ãŒå«ã¾ã‚Œã¦ã„ã‚‹å¿…è¦ãŒã‚りã¾ã™ Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - 定数値 '{0}' 㯠'{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ (unchecked 構文を使ã£ã¦ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã—ã¦ãã ã•ã„) + 定数値 '{0}' 㯠'{1}' ã«å¤‰æ›ã§ãã¾ã›ã‚“ (unchecked 構文を使ã£ã¦ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã—ã¦ãã ã•ã„) Ambiguity between '{0}' and '{1}' - '{0}' 㨠'{1}' é–“ãŒã‚ã„ã¾ã„ã§ã™ + '{0}' 㨠'{1}' é–“ãŒã‚ã„ã¾ã„ã§ã™ Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - åž‹ '{0}' ã‚’ '{1}' ã«æš—黙的ã«å¤‰æ›ã§ãã¾ã›ã‚“。明示的ãªå¤‰æ›ãŒå­˜åœ¨ã—ã¾ã™ (cast ãŒä¸è¶³ã—ã¦ã„ãªã„ã‹ã©ã†ã‹ã‚’確èªã—ã¦ãã ã•ã„) + åž‹ '{0}' ã‚’ '{1}' ã«æš—黙的ã«å¤‰æ›ã§ãã¾ã›ã‚“。明示的ãªå¤‰æ›ãŒå­˜åœ¨ã—ã¾ã™ (cast ãŒä¸è¶³ã—ã¦ã„ãªã„ã‹ã©ã†ã‹ã‚’確èªã—ã¦ãã ã•ã„) The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - get アクセサーã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。 + get アクセサーã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - set アクセサーã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。 + set アクセサーã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªã„ãŸã‚ã€ãƒ—ロパティã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ '{0}' ã¯ã“ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ Using the generic {1} '{0}' requires '{2}' type arguments - ジェãƒãƒªãƒƒã‚¯ {1} '{0}' ã®ä½¿ç”¨ã«ã¯ã€'{2}' åž‹ã®å¼•æ•°ãŒå¿…è¦ã§ã™ + ジェãƒãƒªãƒƒã‚¯ {1} '{0}' ã®ä½¿ç”¨ã«ã¯ã€'{2}' åž‹ã®å¼•æ•°ãŒå¿…è¦ã§ã™ The {1} '{0}' cannot be used with type arguments - {1} '{0}' ã¯åž‹å¼•æ•°ã¨ä¸€ç·’ã«ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ + {1} '{0}' ã¯åž‹å¼•æ•°ã¨ä¸€ç·’ã«ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ The non-generic {1} '{0}' cannot be used with type arguments - éžã‚¸ã‚§ãƒãƒªãƒƒã‚¯ {1} '{0}' ã¯åž‹å¼•æ•°ã¨ä¸€ç·’ã«ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。 + éžã‚¸ã‚§ãƒãƒªãƒƒã‚¯ {1} '{0}' ã¯åž‹å¼•æ•°ã¨ä¸€ç·’ã«ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§ãƒ‘ラメーター '{1}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€ãƒ‘ブリック パラメーターãªã—ã®ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ãƒ¼ã‚’æŒã¤éžæŠ½è±¡åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ + '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§ãƒ‘ラメーター '{1}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€ãƒ‘ブリック パラメーターãªã—ã®ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ãƒ¼ã‚’æŒã¤éžæŠ½è±¡åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。'{3}' ã‹ã‚‰ '{1}' ã¸ã®æš—黙的ãªå‚照変æ›ãŒã‚りã¾ã›ã‚“。 + åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。'{3}' ã‹ã‚‰ '{1}' ã¸ã®æš—黙的ãªå‚照変æ›ãŒã‚りã¾ã›ã‚“。 The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。Null 許容型 '{3}' ã¯ã€'{1}' ã®åˆ¶ç´„を満ãŸã—ã¦ã„ã¾ã›ã‚“。 + åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。Null 許容型 '{3}' ã¯ã€'{1}' ã®åˆ¶ç´„を満ãŸã—ã¦ã„ã¾ã›ã‚“。 The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。Null 許容型 '{3}' ã¯ã€'{1}' ã®åˆ¶ç´„を満ãŸã—ã¦ã„ã¾ã›ã‚“。Null 許容型ã¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェイス制約を満ãŸã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。Null 許容型 '{3}' ã¯ã€'{1}' ã®åˆ¶ç´„を満ãŸã—ã¦ã„ã¾ã›ã‚“。Null 許容型ã¯ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェイス制約を満ãŸã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。'{3}' ã‹ã‚‰ '{1}' ã¸ã®ãƒœãƒƒã‚¯ã‚¹å¤‰æ›ãŒã‚りã¾ã›ã‚“。 + åž‹ '{3}' ã¯ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' 内ã§åž‹ãƒ‘ラメーター '{2}' ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。'{3}' ã‹ã‚‰ '{1}' ã¸ã®ãƒœãƒƒã‚¯ã‚¹å¤‰æ›ãŒã‚りã¾ã›ã‚“。 The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - メソッド '{0}' ã®åž‹å¼•æ•°ã‚’ä½¿ã„æ–¹ã‹ã‚‰æŽ¨è«–ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ã€‚åž‹å¼•æ•°ã‚’æ˜Žç¤ºçš„ã«æŒ‡å®šã—ã¦ãã ã•ã„。 + メソッド '{0}' ã®åž‹å¼•æ•°ã‚’ä½¿ã„æ–¹ã‹ã‚‰æŽ¨è«–ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ã€‚åž‹å¼•æ•°ã‚’æ˜Žç¤ºçš„ã«æŒ‡å®šã—ã¦ãã ã•ã„。 The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - åž‹ '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã®ãƒ‘ラメーター '{1}'ã€ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€å‚ç…§åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ + åž‹ '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã®ãƒ‘ラメーター '{1}'ã€ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€å‚ç…§åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - åž‹ '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã®ãƒ‘ラメーター '{1}'ã€ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€Null éžè¨±å®¹ã®å€¤åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ + åž‹ '{2}' ã¯ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯åž‹ã®ãƒ‘ラメーター '{1}'ã€ã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«ã€Null éžè¨±å®¹ã®å€¤åž‹ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - '{2}' ã‹ã‚‰ '{3}' ã¸å¤‰æ›ã™ã‚‹ã¨ãã®ã€ã‚ã„ã¾ã„ãªãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã®å¤‰æ› '{0}' ãŠã‚ˆã³ '{1}' ã§ã™ + '{2}' ã‹ã‚‰ '{3}' ã¸å¤‰æ›ã™ã‚‹ã¨ãã®ã€ã‚ã„ã¾ã„ãªãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã®å¤‰æ› '{0}' ãŠã‚ˆã³ '{1}' ã§ã™ '{0}' is not supported by the language - '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ + '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ '{0}': cannot explicitly call operator or accessor - '{0}': 演算å­ã¾ãŸã¯ã‚¢ã‚¯ã‚»ã‚µãƒ¼ã‚’明示的ã«å‘¼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + '{0}': 演算å­ã¾ãŸã¯ã‚¢ã‚¯ã‚»ã‚µãƒ¼ã‚’明示的ã«å‘¼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot convert to static type '{0}' - スタティック型 '{0}' ã¸å¤‰æ›ã§ãã¾ã›ã‚“ + スタティック型 '{0}' ã¸å¤‰æ›ã§ãã¾ã›ã‚“ The operand of an increment or decrement operator must be a variable, property or indexer - インクリメント演算å­ã¾ãŸã¯ãƒ‡ã‚¯ãƒªãƒ¡ãƒ³ãƒˆæ¼”ç®—å­ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«ã¯ã€å¤‰æ•°ã€ãƒ—ロパティã€ã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’指定ã—ã¦ãã ã•ã„。 + インクリメント演算å­ã¾ãŸã¯ãƒ‡ã‚¯ãƒªãƒ¡ãƒ³ãƒˆæ¼”ç®—å­ã®ã‚ªãƒšãƒ©ãƒ³ãƒ‰ã«ã¯ã€å¤‰æ•°ã€ãƒ—ロパティã€ã¾ãŸã¯ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã‚’指定ã—ã¦ãã ã•ã„ No overload for method '{0}' takes '{1}' arguments - 引数を '{1}' 個指定ã§ãã‚‹ã€ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã¯ã‚りã¾ã›ã‚“ + 引数を '{1}' 個指定ã§ãã‚‹ã€ãƒ¡ã‚½ãƒƒãƒ‰ '{0}' ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã¯ã‚りã¾ã›ã‚“ The best overloaded method match for '{0}' has some invalid arguments - '{0}' ã«æœ€ã‚‚é©ã—ã¦ã„るオーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ メソッドã«ã¯ç„¡åйãªå¼•æ•°ãŒã„ãã¤ã‹å«ã¾ã‚Œã¦ã„ã¾ã™ + '{0}' ã«æœ€ã‚‚é©ã—ã¦ã„るオーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ メソッドã«ã¯ç„¡åйãªå¼•æ•°ãŒã„ãã¤ã‹å«ã¾ã‚Œã¦ã„ã¾ã™ A ref or out argument must be an assignable variable - ref ã¾ãŸã¯ out 引数ã¯ã€å‰²ã‚Šå½“ã¦å¯èƒ½ãªå¤‰æ•°ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“。 + ref ã¾ãŸã¯ out 引数ã¯ã€å‰²ã‚Šå½“ã¦å¯èƒ½ãªå¤‰æ•°ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - '{1}' åž‹ã®ä¿®é£¾å­ã‚’ã¨ãŠã—ã¦ãƒ—ロテクト メンãƒãƒ¼ '{0}' ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。修飾å­ã¯ '{2}' åž‹ã€ã¾ãŸã¯ãれã‹ã‚‰æ´¾ç”Ÿã—ãŸã‚‚ã®ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ + '{1}' åž‹ã®ä¿®é£¾å­ã‚’ã¨ãŠã—ã¦ãƒ—ロテクト メンãƒãƒ¼ '{0}' ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。修飾å­ã¯ '{2}' åž‹ã€ã¾ãŸã¯ãれã‹ã‚‰æ´¾ç”Ÿã—ãŸã‚‚ã®ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“ Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - プロパティã€ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã€ã¾ãŸã¯ã‚¤ãƒ™ãƒ³ãƒˆ '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。アクセサー メソッド㮠'{1}' ã¾ãŸã¯ '{2}' を直接呼ã³å‡ºã—ã¦ãã ã•ã„ + プロパティã€ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã€ã¾ãŸã¯ã‚¤ãƒ™ãƒ³ãƒˆ '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。アクセサー メソッド㮠'{1}' ã¾ãŸã¯ '{2}' を直接呼ã³å‡ºã—ã¦ãã ã•ã„ Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - プロパティã€ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã€ã¾ãŸã¯ã‚¤ãƒ™ãƒ³ãƒˆ '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。アクセサー メソッド㮠'{1}' を直接呼ã³å‡ºã—ã¦ãã ã•ã„ + プロパティã€ã‚¤ãƒ³ãƒ‡ã‚¯ã‚µãƒ¼ã€ã¾ãŸã¯ã‚¤ãƒ™ãƒ³ãƒˆ '{0}' ã¯ã“ã®è¨€èªžã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。アクセサー メソッド㮠'{1}' を直接呼ã³å‡ºã—ã¦ãã ã•ã„ Delegate '{0}' does not take '{1}' arguments - デリゲート '{0}' ã« '{1}' 個ã®å¼•数を指定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + デリゲート '{0}' ã« '{1}' 個ã®å¼•数を指定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Delegate '{0}' has some invalid arguments - デリゲート '{0}' ã«ç„¡åйãªå¼•æ•°ãŒã‚りã¾ã™ + デリゲート '{0}' ã«ç„¡åйãªå¼•æ•°ãŒã‚りã¾ã™ Cannot assign to '{0}' because it is read-only - 読ã¿å–り専用ã§ã‚ã‚‹ãŸã‚ '{0}' ã«å‰²ã‚Šå½“ã¦ã§ãã¾ã›ã‚“ + 読ã¿å–り専用ã§ã‚ã‚‹ãŸã‚ '{0}' ã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot pass '{0}' as a ref or out argument because it is read-only - 読ã¿å–り専用ãªã®ã§ '{0}' 㯠ref ã¾ãŸã¯ out 引数ã¨ã—ã¦æ¸¡ã›ã¾ã›ã‚“ + 読ã¿å–り専用ãªã®ã§ '{0}' 㯠ref ã¾ãŸã¯ out 引数ã¨ã—ã¦æ¸¡ã›ã¾ã›ã‚“ Cannot modify the return value of '{0}' because it is not a variable - 変数ã§ã¯ãªã„ãŸã‚ã€'{0}' ã®æˆ»ã‚Šå€¤ã‚’変更ã§ãã¾ã›ã‚“ + 変数ã§ã¯ãªã„ãŸã‚ã€'{0}' ã®æˆ»ã‚Šå€¤ã‚’変更ã§ãã¾ã›ã‚“ Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - 読ã¿å–り専用フィールド '{0}' ã®ãƒ¡ãƒ³ãƒãƒ¼ã¯å¤‰æ›´ã§ãã¾ã›ã‚“ (コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯)。 + 読ã¿å–り専用フィールド '{0}' ã®ãƒ¡ãƒ³ãƒãƒ¼ã¯å¤‰æ›´ã§ãã¾ã›ã‚“ (コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - 読ã¿å–り専用フィールド '{0}' ã®ãƒ¡ãƒ³ãƒãƒ¼ã« ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã§ã¯å¯) + 読ã¿å–り専用フィールド '{0}' ã®ãƒ¡ãƒ³ãƒãƒ¼ã« ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ (コンストラクターã§ã¯å¯) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - é™çš„読ã¿å–り専用フィールド '{0}' ã®ãƒ•ィールドã¸ã®å‰²ã‚Šå½“ã¦ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯) + é™çš„読ã¿å–り専用フィールド '{0}' ã®ãƒ•ィールドã¸ã®å‰²ã‚Šå½“ã¦ã¯ã§ãã¾ã›ã‚“ (é™çš„コンストラクターã¾ãŸã¯å¤‰æ•°åˆæœŸåŒ–å­ã§ã¯å¯) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - é™çš„読ã¿å–り専用フィールド '{0}' ã«ã¯ã€é™çš„コンストラクター内を除ãã€ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + é™çš„読ã¿å–り専用フィールド '{0}' ã«ã¯ã€é™çš„コンストラクター内を除ãã€ref ã¾ãŸã¯ out を渡ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot assign to '{0}' because it is a '{1}' - '{0}' 㯠'{1}' ã§ã‚ã‚‹ãŸã‚ã€å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + '{0}' 㯠'{1}' ã§ã‚ã‚‹ãŸã‚ã€ã“れã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Cannot pass '{0}' as a ref or out argument because it is a '{1}' - '{1}' ã§ã‚ã‚‹ãŸã‚ã€'{0}' 㯠ref ã¾ãŸã¯ out 引数ã¨ã—ã¦æ¸¡ã›ã¾ã›ã‚“ + '{1}' ã§ã‚ã‚‹ãŸã‚ã€'{0}' 㯠ref ã¾ãŸã¯ out 引数ã¨ã—ã¦æ¸¡ã›ã¾ã›ã‚“ '{0}' does not contain a constructor that takes '{1}' arguments - '{0}' ã«ã€å¼•æ•°ã‚’ '{1}' 個指定ã§ãるコンストラクターãŒã‚りã¾ã›ã‚“ + '{0}' ã«ã€å¼•æ•°ã‚’ '{1}' 個指定ã§ãるコンストラクターãŒã‚りã¾ã›ã‚“ Non-invocable member '{0}' cannot be used like a method. - 実行ä¸å¯èƒ½ãªãƒ¡ãƒ³ãƒãƒ¼ '{0}' をメソッドã®ã‚ˆã†ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + 実行ä¸å¯èƒ½ãªãƒ¡ãƒ³ãƒãƒ¼ '{0}' をメソッドã®ã‚ˆã†ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 Named argument specifications must appear after all fixed arguments have been specified - åå‰ä»˜ã引数ã¯ã€ã™ã¹ã¦ã®å›ºå®šå¼•数を指定ã—ãŸå¾Œã§æŒ‡å®šã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ + åå‰ä»˜ãå¼•æ•°ã®æŒ‡å®šã¯ã€ã™ã¹ã¦ã®å›ºå®šå¼•æ•°ãŒæŒ‡å®šã•れãŸå¾Œã«å‡ºç¾ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ The best overload for '{0}' does not have a parameter named '{1}' - '{0}' ã«æœ€ã‚‚é©ã—ã¦ã„るオーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã«ã¯ '{1}' ã¨ã„ã†åå‰ã®ãƒ‘ラメーターãŒã‚りã¾ã›ã‚“ + '{0}' ã«æœ€ã‚‚é©ã—ã¦ã„るオーãƒãƒ¼ãƒ­ãƒ¼ãƒ‰ã«ã¯ '{1}' ã¨ã„ã†åå‰ã®ãƒ‘ラメーターãŒã‚りã¾ã›ã‚“ The delegate '{0}' does not have a parameter named '{1}' - デリゲート '{0}' ã«ã¯ '{1}' ã¨ã„ã†åå‰ã®ãƒ‘ラメーターãŒã‚りã¾ã›ã‚“ + デリゲート '{0}' ã«ã¯ '{1}' ã¨ã„ã†åå‰ã®ãƒ‘ラメーターãŒã‚りã¾ã›ã‚“ Named argument '{0}' cannot be specified multiple times - '{0}' ã¨ã„ã†åå‰ä»˜ã引数ãŒè¤‡æ•°æŒ‡å®šã•れã¾ã—㟠+ '{0}' ã¨ã„ã†åå‰ä»˜ã引数ãŒè¤‡æ•°æŒ‡å®šã•れã¾ã—㟠Named argument '{0}' specifies a parameter for which a positional argument has already been given - åå‰ä»˜ã引数 '{0}' ã¯ã€å ´æ‰€å¼•æ•°ãŒæ—¢ã«æŒ‡å®šã•れã¦ã„るパラメーターを指定ã—ã¾ã™ + åå‰ä»˜ã引数 '{0}' ã¯ã€å ´æ‰€å¼•æ•°ãŒæ—¢ã«æŒ‡å®šã•れã¦ã„るパラメーターを指定ã—ã¾ã™ - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ko.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ko.xlf index 17869b19b0a..28c2726017a 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ko.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ko.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - ë™ì  ìž‘ì—…ì„ ë°”ì¸ë”©í•˜ëŠ” ë™ì•ˆ 예기치 ì•Šì€ ì˜ˆì™¸ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. + ë™ì  ìž‘ì—…ì„ ë°”ì¸ë”©í•˜ëŠ” ë™ì•ˆ 예기치 ì•Šì€ ì˜ˆì™¸ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. Cannot bind call with no calling object - 호출 개체가 없으면 í˜¸ì¶œì„ ë°”ì¸ë”©í•  수 없습니다. + 호출 개체가 없으면 í˜¸ì¶œì„ ë°”ì¸ë”©í•  수 없습니다. Overload resolution failed - 오버로드 확ì¸ì— 실패했습니다. + 오버로드 확ì¸ì´ 실패함 Binary operators must be invoked with two arguments - ì´í•­ ì—°ì‚°ìžë¥¼ 호출하려면 ì¸ìˆ˜ ë‘ ê°œë¥¼ 사용해야 합니다. + ì´í•­ ì—°ì‚°ìžë¥¼ 호출하려면 ì¸ìˆ˜ ë‘ ê°œë¥¼ 사용해야 합니다. Unary operators must be invoked with one argument - 단항 ì—°ì‚°ìžë¥¼ 호출하려면 ì¸ìˆ˜ 한 개를 사용해야 합니다. + 단항 ì—°ì‚°ìžë¥¼ 호출하려면 ì¸ìˆ˜ 한 개를 사용해야 합니다. The name '{0}' is bound to a method and cannot be used like a property - ì´ë¦„ '{0}'ì€(는) ë©”ì„œë“œì— ë°”ì¸ë”©ë˜ì–´ 있으며 ì†ì„±ì²˜ëŸ¼ 사용할 수 없습니다. + ì´ë¦„ '{0}'ì€(는) ë©”ì„œë“œì— ë°”ì¸ë”©ë˜ì–´ 있으며 ì†ì„±ì²˜ëŸ¼ 사용할 수 없습니다. The event '{0}' can only appear on the left hand side of + - ì´ë²¤íЏ ' (0) 'ì˜ ì™¼ìª½ì—ë§Œ 나타날 수 있습니다 + + '{0}' ì´ë²¤íŠ¸ëŠ” +ì˜ ì™¼ìª½ì—ë§Œ 올 수 ìžˆìŒ Cannot invoke a non-delegate type - ë¹„ëŒ€ë¦¬ìž í˜•ì‹ì„ 호출할 수 없습니다. + ë¹„ëŒ€ë¦¬ìž í˜•ì‹ì„ 호출할 수 없습니다. Binary operators cannot be invoked with one argument - í•˜ë‚˜ì˜ ì¸ìˆ˜ë¥¼ 사용하여 ì´í•­ ì—°ì‚°ìžë¥¼ 호출할 수 없습니다. + í•˜ë‚˜ì˜ ì¸ìˆ˜ë¥¼ 사용하여 ì´í•­ ì—°ì‚°ìžë¥¼ 호출할 수 없습니다. Cannot perform member assignment on a null reference - null ì°¸ì¡°ì— ëŒ€í•´ 멤버 í• ë‹¹ì„ ìˆ˜í–‰í•  수 없습니다. + null ì°¸ì¡°ì— ëŒ€í•´ 멤버 í• ë‹¹ì„ ìˆ˜í–‰í•  수 없습니다. Cannot perform runtime binding on a null reference - null ì°¸ì¡°ì— ëŒ€í•´ 런타임 ë°”ì¸ë”©ì„ 수행할 수 없습니다. + null ì°¸ì¡°ì— ëŒ€í•´ 런타임 ë°”ì¸ë”©ì„ 수행할 수 없습니다. Cannot dynamically invoke method '{0}' because it has a Conditional attribute - '{0}' 메서드는 Conditional íŠ¹ì„±ì´ ìžˆìœ¼ë¯€ë¡œ ë™ì ìœ¼ë¡œ 호출할 수 없습니다. + '{0}' 메서드는 Conditional íŠ¹ì„±ì´ ìžˆìœ¼ë¯€ë¡œ ë™ì ìœ¼ë¡œ 호출할 수 없습니다. Cannot implicitly convert type 'void' to 'object' - 암시ì ìœ¼ë¡œ 'void' 형ì‹ì„ 'object' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. + 암시ì ìœ¼ë¡œ 'void' 형ì‹ì„ 'object' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - '{0}' ì—°ì‚°ìžëŠ” '{1}' ë° '{2}' 형ì‹ì˜ 피연산ìžì— ì ìš©í•  수 없습니다. + '{0}' ì—°ì‚°ìžëŠ” '{1}' ë° '{2}' 형ì‹ì˜ 피연산ìžì— ì ìš©í•  수 없습니다. Cannot apply indexing with [] to an expression of type '{0}' - []ì„ ì‚¬ìš©í•˜ëŠ” ì¸ë±ì‹±ì„ '{0}' 형ì‹ì˜ ì‹ì— ì ìš©í•  수 없습니다. + []ì„ ì‚¬ìš©í•˜ëŠ” ì¸ë±ì‹±ì„ '{0}' 형ì‹ì˜ ì‹ì— ì ìš©í•  수 없습니다. Wrong number of indices inside []; expected '{0}' - [] ë‚´ë¶€ì˜ ì¸ë±ìФ 수가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. '{0}'개가 필요합니다. + [] ë‚´ë¶€ì˜ ì¸ë±ìФ 수가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. '{0}'개가 필요합니다. Operator '{0}' cannot be applied to operand of type '{1}' - '{0}' ì—°ì‚°ìžëŠ” '{1}' 형ì‹ì˜ 피연산ìžì— ì ìš©í•  수 없습니다. + '{0}' ì—°ì‚°ìžëŠ” '{1}' 형ì‹ì˜ 피연산ìžì— ì ìš©í•  수 없습니다. Cannot implicitly convert type '{0}' to '{1}' - 암시ì ìœ¼ë¡œ '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. + 암시ì ìœ¼ë¡œ '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. Cannot convert type '{0}' to '{1}' - '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. + '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. Constant value '{0}' cannot be converted to a '{1}' - ìƒìˆ˜ ê°’ '{0}'ì„(를) '{1}'(으)로 변환할 수 없습니다. + '{0}' ìƒìˆ˜ ê°’ì„ '{1}'(으)로 변환할 수 없습니다. Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - '{0}' ì—°ì‚°ìžê°€ 모호하여 '{1}' ë° '{2}' 형ì‹ì˜ 피연산ìžì— 사용할 수 없습니다. + '{0}' ì—°ì‚°ìžê°€ 모호하여 '{1}' ë° '{2}' 형ì‹ì˜ 피연산ìžì— 사용할 수 없습니다. Operator '{0}' is ambiguous on an operand of type '{1}' - '{0}' ì—°ì‚°ìžê°€ 모호하여 '{1}' 형ì‹ì˜ 피연산ìžì— 사용할 수 없습니다. + '{0}' ì—°ì‚°ìžê°€ 모호하여 '{1}' 형ì‹ì˜ 피연산ìžì— 사용할 수 없습니다. Cannot convert null to '{0}' because it is a non-nullable value type - '{0}'ì€(는) nullì„ í—ˆìš©í•˜ì§€ 않는 ê°’ 형ì‹ì´ë¯€ë¡œ nullì„ ì´ í˜•ì‹ìœ¼ë¡œ 변환할 수 없습니다. + '{0}'ì€(는) nullì„ í—ˆìš©í•˜ì§€ 않는 ê°’ 형ì‹ì´ë¯€ë¡œ nullì„ ì´ í˜•ì‹ìœ¼ë¡œ 변환할 수 없습니다. Cannot access a non-static member of outer type '{0}' via nested type '{1}' - 중첩 í˜•ì‹ '{1}'ì„(를) 통해 외부 í˜•ì‹ '{0}'ì˜ staticì´ ì•„ë‹Œ ë©¤ë²„ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. + 중첩 í˜•ì‹ '{1}'ì„(를) 통해 외부 í˜•ì‹ '{0}'ì˜ staticì´ ì•„ë‹Œ ë©¤ë²„ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. '{0}' does not contain a definition for '{1}' - '{0}'ì— '{1}'ì— ëŒ€í•œ ì •ì˜ê°€ 없습니다. + '{0}'ì— '{1}'ì— ëŒ€í•œ ì •ì˜ê°€ 없습니다. An object reference is required for the non-static field, method, or property '{0}' - staticì´ ì•„ë‹Œ 필드, 메서드 ë˜ëŠ” ì†ì„± '{0}'ì— ê°œì²´ 참조가 필요합니다. + staticì´ ì•„ë‹Œ 필드, 메서드 ë˜ëŠ” ì†ì„± '{0}'ì— ê°œì²´ 참조가 필요합니다. The call is ambiguous between the following methods or properties: '{0}' and '{1}' - ë‹¤ìŒ ë©”ì„œë“œ ë˜ëŠ” ì†ì„± 사ì´ì˜ í˜¸ì¶œì´ ëª¨í˜¸í•©ë‹ˆë‹¤. '{0}'ê³¼(와) '{1}' + '{0}' ë° '{1}'ì˜ ë©”ì„œë“œ ë˜ëŠ” ì†ì„± ê°„ í˜¸ì¶œì´ ëª¨í˜¸í•©ë‹ˆë‹¤. '{0}' is inaccessible due to its protection level - 보호 수준 ë•Œë¬¸ì— '{0}'ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. + 보호 수준 ë•Œë¬¸ì— '{0}'ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. No overload for '{0}' matches delegate '{1}' - '{1}' 대리ìžì™€ ì¼ì¹˜í•˜ëŠ” '{0}'ì— ëŒ€í•œ 오버로드가 없습니다. + '{1}' 대리ìžì™€ ì¼ì¹˜í•˜ëŠ” '{0}'ì— ëŒ€í•œ 오버로드가 없습니다. The left-hand side of an assignment must be a variable, property or indexer - 할당ì‹ì˜ ì™¼ìª½ì€ ë³€ìˆ˜, ì†ì„± ë˜ëŠ” ì¸ë±ì„œì—¬ì•¼ 합니다. + 할당ì‹ì˜ ì™¼ìª½ì€ ë³€ìˆ˜, ì†ì„± ë˜ëŠ” ì¸ë±ì„œì—¬ì•¼ 합니다. The type '{0}' has no constructors defined - '{0}' 형ì‹ì— ì •ì˜ëœ ìƒì„±ìžê°€ 없습니다. + '{0}' 형ì‹ì— ì •ì˜ëœ ìƒì„±ìžê°€ 없습니다. The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” get ì ‘ê·¼ìžê°€ 없으므로 ì´ ì»¨í…스트ì—서 사용할 수 없습니다. + '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” get ì ‘ê·¼ìžê°€ 없으므로 ì´ ì»¨í…스트ì—서 사용할 수 없습니다. Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - '{0}' 멤버는 ì¸ìŠ¤í„´ìŠ¤ 참조를 사용하여 액세스할 수 없습니다. 대신 í˜•ì‹ ì´ë¦„ì„ ì‚¬ìš©í•˜ì—¬ 한정하십시오. + '{0}' 멤버는 ì¸ìŠ¤í„´ìŠ¤ 참조를 사용하여 액세스할 수 없습니다. 대신 í˜•ì‹ ì´ë¦„ì„ ì‚¬ìš©í•˜ì—¬ 한정하세요. A readonly field cannot be assigned to (except in a constructor or a variable initializer) - ì½ê¸° ì „ìš© 필드ì—는 할당할 수 없습니다. 단 ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. + ì½ê¸° ì „ìš© 필드ì—는 할당할 수 없습니다. 단 ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. A readonly field cannot be passed ref or out (except in a constructor) - ì½ê¸° ì „ìš© 필드는 ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ìƒì„±ìžì—서는 예외입니다. + ì½ê¸° ì „ìš© 필드는 ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ìƒì„±ìžì—서는 예외입니다. A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - ì •ì  ì½ê¸° ì „ìš© 필드ì—는 할당할 수 없습니다. 단 ì •ì  ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. + ì •ì  ì½ê¸° ì „ìš© 필드ì—는 할당할 수 없습니다. 단 ì •ì  ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. A static readonly field cannot be passed ref or out (except in a static constructor) - ì •ì  ì½ê¸° ì „ìš© 필드는 ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ì •ì  ìƒì„±ìžì—서는 예외입니다. + ì •ì  ì½ê¸° ì „ìš© 필드는 ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ì •ì  ìƒì„±ìžì—서는 예외입니다. Property or indexer '{0}' cannot be assigned to -- it is read only - '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì½ê¸° ì „ìš©ì´ë¯€ë¡œ 할당할 수 없습니다. + '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì½ê¸° ì „ìš©ì´ë¯€ë¡œ 할당할 수 없습니다. A property or indexer may not be passed as an out or ref parameter - ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” out ë˜ëŠ” ref 매개 변수로 전달할 수 없습니다. + ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” out ë˜ëŠ” ref 매개 변수로 전달할 수 없습니다. Dynamic calls cannot be used in conjunction with pointers - ë™ì  í˜¸ì¶œì„ í¬ì¸í„°ì™€ 함께 사용할 수 없습니다. + ë™ì  í˜¸ì¶œì„ í¬ì¸í„°ì™€ 함께 사용할 수 없습니다. In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - ì‚¬ìš©ìž ì •ì˜ ë…¼ë¦¬ ì—°ì‚°ìž('{0}')를 단ë½(short circuit) ì—°ì‚°ìžë¡œ 사용하려면 ì—°ì‚°ìžì˜ ë‘ ë§¤ê°œ 변수와 ê°™ì€ í˜•ì‹ì„ 반환해야 합니다. + ì‚¬ìš©ìž ì •ì˜ ë…¼ë¦¬ ì—°ì‚°ìž('{0}')를 단ë½(short circuit) ì—°ì‚°ìžë¡œ 사용하려면 ì—°ì‚°ìžì˜ ë‘ ë§¤ê°œ 변수와 ê°™ì€ í˜•ì‹ì„ 반환해야 합니다. The type ('{0}') must contain declarations of operator true and operator false - '{0}' 형ì‹ì—는 true ë° false ì—°ì‚°ìž ì„ ì–¸ì´ ìžˆì–´ì•¼ 합니다. + '{0}' 형ì‹ì—는 true ë° false ì—°ì‚°ìž ì„ ì–¸ì´ ìžˆì–´ì•¼ 합니다. Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - ìƒìˆ˜ ê°’ '{0}'ì„(를) '{1}'(으)로 변환할 수 없습니다. 재정ì˜í•˜ë ¤ë©´ 'unchecked' êµ¬ë¬¸ì„ ì‚¬ìš©í•˜ì‹­ì‹œì˜¤. + '{0}' ìƒìˆ˜ ê°’ì„ '{1}'(으)로 변환할 수 없습니다. 재정ì˜í•˜ë ¤ë©´ 'unchecked' êµ¬ë¬¸ì„ ì‚¬ìš©í•˜ì„¸ìš”. Ambiguity between '{0}' and '{1}' - '{0}'ê³¼(와) '{1}' 사ì´ì— ëª¨í˜¸ì„±ì´ ìžˆìŠµë‹ˆë‹¤. + '{0}'ê³¼(와) '{1}' 사ì´ì— ëª¨í˜¸ì„±ì´ ìžˆìŠµë‹ˆë‹¤. Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - 암시ì ìœ¼ë¡œ '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. ëª…ì‹œì  ë³€í™˜ì´ ìžˆìŠµë‹ˆë‹¤. ìºìŠ¤íŠ¸ê°€ 있는지 확ì¸í•˜ì‹­ì‹œì˜¤. + 암시ì ìœ¼ë¡œ '{0}' 형ì‹ì„ '{1}' 형ì‹ìœ¼ë¡œ 변환할 수 없습니다. ëª…ì‹œì  ë³€í™˜ì´ ìžˆìŠµë‹ˆë‹¤. ìºìŠ¤íŠ¸ê°€ 있는지 확ì¸í•˜ì‹­ì‹œì˜¤. The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - get ì ‘ê·¼ìžì— 액세스할 수 없으므로 '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì´ ì»¨í…스트ì—서 사용할 수 없습니다. + get ì ‘ê·¼ìžì— 액세스할 수 없으므로 '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì´ ì»¨í…스트ì—서 사용할 수 없습니다. The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - set ì ‘ê·¼ìžì— 액세스할 수 없으므로 '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì´ ì»¨í…스트ì—서 사용할 수 없습니다. + set ì ‘ê·¼ìžì— 액세스할 수 없으므로 '{0}' ì†ì„± ë˜ëŠ” ì¸ë±ì„œëŠ” ì´ ì»¨í…스트ì—서 사용할 수 없습니다. Using the generic {1} '{0}' requires '{2}' type arguments - 제네릭 {1} '{0}'ì„(를) 사용하려면 '{2}' í˜•ì‹ ì¸ìˆ˜ê°€ 필요합니다. + 제네릭 {1} '{0}'ì„(를) 사용하려면 '{2}' í˜•ì‹ ì¸ìˆ˜ê°€ 필요합니다. The {1} '{0}' cannot be used with type arguments - {1} '{0}'ì€(는) í˜•ì‹ ì¸ìˆ˜ì™€ 함께 사용할 수 없습니다. + {1} '{0}'ì€(는) í˜•ì‹ ì¸ìˆ˜ì™€ 함께 사용할 수 없습니다. The non-generic {1} '{0}' cannot be used with type arguments - ì œë„¤ë¦­ì´ ì•„ë‹Œ {1} '{0}'ì€(는) í˜•ì‹ ì¸ìˆ˜ì™€ 함께 사용할 수 없습니다. + ì œë„¤ë¦­ì´ ì•„ë‹Œ {1} '{0}'ì€(는) í˜•ì‹ ì¸ìˆ˜ì™€ 함께 사용할 수 없습니다. '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{1}' 매개 변수로 사용하려면 '{2}'ì´(ê°€) 매개 변수가 없는 public ìƒì„±ìžë¥¼ 사용하는 ë¹„ì¶”ìƒ í˜•ì‹ì´ì–´ì•¼ 합니다. + 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{1}' 매개 변수로 사용하려면 '{2}'ì´(ê°€) 매개 변수가 없는 public ìƒì„±ìžë¥¼ 사용하는 ë¹„ì¶”ìƒ í˜•ì‹ì´ì–´ì•¼ 합니다. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 í˜•ì‹ ë§¤ê°œ 변수 '{2}'(으)로 사용할 수 없습니다. '{3}'ì—서 '{1}'(으)ë¡œì˜ ì•”ì‹œì  ì°¸ì¡° ë³€í™˜ì´ ì—†ìŠµë‹ˆë‹¤. + '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” '{0}' 메서드ì—서 '{2}' í˜•ì‹ ë§¤ê°œ 변수로 사용할 수 없습니다. '{3}'ì—서 '{1}'(으)ë¡œì˜ ì•”ì‹œì  ì°¸ì¡° ë³€í™˜ì´ ì—†ìŠµë‹ˆë‹¤. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 í˜•ì‹ ë§¤ê°œ 변수 '{2}'(으)로 사용할 수 없습니다. nullable í˜•ì‹ '{3}'ì´(ê°€) '{1}' 제약 ì¡°ê±´ì„ ì¶©ì¡±í•˜ì§€ 않습니다. + '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” '{0}' 메서드ì—서 '{2}' í˜•ì‹ ë§¤ê°œ 변수로 사용할 수 없습니다. '{3}' null 허용 형ì‹ì´ '{1}' 제약 ì¡°ê±´ì„ ì¶©ì¡±í•˜ì§€ 않습니다. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 í˜•ì‹ ë§¤ê°œ 변수 '{2}'(으)로 사용할 수 없습니다. nullable í˜•ì‹ '{3}'ì´(ê°€) '{1}' 제약 ì¡°ê±´ì„ ì¶©ì¡±í•˜ì§€ 않습니다. nullable 형ì‹ì€ 어떠한 ì¸í„°íŽ˜ì´ìФ 제약 ì¡°ê±´ë„ ë§Œì¡±í•  수 없습니다. + '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” '{0}' 메서드ì—서 '{2}' í˜•ì‹ ë§¤ê°œ 변수로 사용할 수 없습니다. '{3}' null 허용 형ì‹ì´ '{1}' 제약 ì¡°ê±´ì„ ì¶©ì¡±í•˜ì§€ 않습니다. null 허용 형ì‹ì€ 어떠한 ì¸í„°íŽ˜ì´ìФ 제약 ì¡°ê±´ë„ ë§Œì¡±í•  수 없습니다. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 í˜•ì‹ ë§¤ê°œ 변수 '{2}'(으)로 사용할 수 없습니다. '{3}'ì—서 '{1}'(으)ë¡œì˜ boxing ë³€í™˜ì´ ì—†ìŠµë‹ˆë‹¤. + '{3}' 형ì‹ì€ 제네릭 í˜•ì‹ ë˜ëŠ” '{0}' 메서드ì—서 '{2}' í˜•ì‹ ë§¤ê°œ 변수로 사용할 수 없습니다. '{3}'ì—서 '{1}'(으)ë¡œì˜ boxing ë³€í™˜ì´ ì—†ìŠµë‹ˆë‹¤. The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - '{0}' ë©”ì„œë“œì˜ í˜•ì‹ ì¸ìˆ˜ë¥¼ 유추할 수 없습니다. í˜•ì‹ ì¸ìˆ˜ë¥¼ 명시ì ìœ¼ë¡œ 지정하십시오. + '{0}' ë©”ì„œë“œì˜ í˜•ì‹ ì¸ìˆ˜ë¥¼ 유추할 수 없습니다. í˜•ì‹ ì¸ìˆ˜ë¥¼ 명시ì ìœ¼ë¡œ 지정하십시오. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{2}' 형ì‹ì„ '{1}' 매개 변수로 사용하려면 해당 형ì‹ì´ 참조 형ì‹ì´ì–´ì•¼ 합니다. + 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{2}' 형ì‹ì„ '{1}' 매개 변수로 사용하려면 해당 형ì‹ì´ 참조 형ì‹ì´ì–´ì•¼ 합니다. The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{2}' 형ì‹ì„ '{1}' 매개 변수로 사용하려면 해당 형ì‹ì´ nullì„ í—ˆìš©í•˜ì§€ 않는 ê°’ 형ì‹ì´ì–´ì•¼ 합니다. + 제네릭 í˜•ì‹ ë˜ëŠ” 메서드 '{0}'ì—서 '{2}' 형ì‹ì„ '{1}' 매개 변수로 사용하려면 해당 형ì‹ì´ nullì„ í—ˆìš©í•˜ì§€ 않는 ê°’ 형ì‹ì´ì–´ì•¼ 합니다. Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - '{2}'ì—서 '{3}(으)로 변환하는 ë™ì•ˆ 모호한 ì‚¬ìš©ìž ì •ì˜ ë³€í™˜ '{0}' ë° '{1}'ì´(ê°€) ë°œìƒí–ˆìŠµë‹ˆë‹¤. + '{2}'ì—서 '{3}(으)로 변환하는 ë™ì•ˆ 모호한 ì‚¬ìš©ìž ì •ì˜ ë³€í™˜ '{0}' ë° '{1}'ì´(ê°€) ë°œìƒí–ˆìŠµë‹ˆë‹¤. '{0}' is not supported by the language - '{0}'ì€(는) 언어ì—서 ì§€ì›ë˜ì§€ 않습니다. + '{0}'ì€(는) 언어ì—서 ì§€ì›ë˜ì§€ 않습니다. '{0}': cannot explicitly call operator or accessor - '{0}': ì—°ì‚°ìžë‚˜ ì ‘ê·¼ìžë¥¼ 명시ì ìœ¼ë¡œ 호출할 수 없습니다. + '{0}': ì—°ì‚°ìžë‚˜ ì ‘ê·¼ìžë¥¼ 명시ì ìœ¼ë¡œ 호출할 수 없습니다. Cannot convert to static type '{0}' - '{0}' ì •ì  í˜•ì‹ìœ¼ë¡œ 변환할 수 없습니다. + '{0}' ì •ì  í˜•ì‹ìœ¼ë¡œ 변환할 수 없습니다. The operand of an increment or decrement operator must be a variable, property or indexer - ì¦ê°€ ì—°ì‚°ìž ë˜ëŠ” ê°ì†Œ ì—°ì‚°ìžì˜ 피연산ìžëŠ” 변수, ì†ì„± ë˜ëŠ” ì¸ë±ì„œì—¬ì•¼ 합니다. + ì¦ê°€ ì—°ì‚°ìž ë˜ëŠ” ê°ì†Œ ì—°ì‚°ìžì˜ 피연산ìžëŠ” 변수, ì†ì„± ë˜ëŠ” ì¸ë±ì„œì—¬ì•¼ 합니다. No overload for method '{0}' takes '{1}' arguments - '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용하는 '{0}' ë©”ì„œë“œì— ëŒ€í•œ 오버로드가 없습니다. + '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용하는 '{0}' ë©”ì„œë“œì— ëŒ€í•œ 오버로드가 없습니다. The best overloaded method match for '{0}' has some invalid arguments - '{0}'ì— ê°€ìž¥ ì¼ì¹˜í•˜ëŠ” ì˜¤ë²„ë¡œë“œëœ ë©”ì„œë“œì— ìž˜ëª»ëœ ì¸ìˆ˜ê°€ 있습니다. + '{0}'ì— ê°€ìž¥ ì¼ì¹˜í•˜ëŠ” ì˜¤ë²„ë¡œë“œëœ ë©”ì„œë“œì— ìž˜ëª»ëœ ì¸ìˆ˜ê°€ 있습니다. A ref or out argument must be an assignable variable - ref ë˜ëŠ” out ì¸ìˆ˜ëŠ” 할당 가능한 변수여야 합니다. + ref ë˜ëŠ” out ì¸ìˆ˜ëŠ” 할당 가능한 변수여야 합니다. Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - '{1}' 형ì‹ì˜ 한정ìžë¥¼ 통해 ë³´í˜¸ëœ ë©¤ë²„ '{0}'ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. 한정ìžëŠ” '{2}' 형ì‹ì´ê±°ë‚˜ 여기ì—서 파ìƒëœ 형ì‹ì´ì–´ì•¼ 합니다. + '{1}' 형ì‹ì˜ 한정ìžë¥¼ 통해 ë³´í˜¸ëœ ë©¤ë²„ '{0}'ì— ì•¡ì„¸ìŠ¤í•  수 없습니다. 한정ìžëŠ” '{2}' 형ì‹ì´ê±°ë‚˜ 여기ì—서 파ìƒëœ 형ì‹ì´ì–´ì•¼ 합니다. Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - '{0}' ì†ì„±, ì¸ë±ì„œ ë˜ëŠ” ì´ë²¤íŠ¸ëŠ” ì´ ì–¸ì–´ì—서 ì§€ì›ë˜ì§€ 않습니다. '{1}' ë˜ëŠ” '{2}' ì ‘ê·¼ìž ë©”ì„œë“œë¥¼ ì§ì ‘ 호출해 보십시오. + '{0}' ì†ì„±, ì¸ë±ì„œ ë˜ëŠ” ì´ë²¤íŠ¸ëŠ” ì´ ì–¸ì–´ì—서 ì§€ì›ë˜ì§€ 않습니다. '{1}' ë˜ëŠ” '{2}' ì ‘ê·¼ìž ë©”ì„œë“œë¥¼ ì§ì ‘ 호출해 보세요. Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - '{0}' ì†ì„±, ì¸ë±ì„œ ë˜ëŠ” ì´ë²¤íŠ¸ëŠ” ì´ ì–¸ì–´ì—서 ì§€ì›ë˜ì§€ 않습니다. '{1}' ì ‘ê·¼ìž ë©”ì„œë“œë¥¼ ì§ì ‘ 호출해 보십시오. + '{0}' ì†ì„±, ì¸ë±ì„œ ë˜ëŠ” ì´ë²¤íŠ¸ëŠ” ì´ ì–¸ì–´ì—서 ì§€ì›ë˜ì§€ 않습니다. '{1}' ì ‘ê·¼ìž ë©”ì„œë“œë¥¼ ì§ì ‘ 호출해 보세요. Delegate '{0}' does not take '{1}' arguments - '{0}' 대리ìžì—는 '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용할 수 없습니다. + '{0}' 대리ìžì—는 '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용할 수 없습니다. Delegate '{0}' has some invalid arguments - '{0}' 대리ìžì— ìž˜ëª»ëœ ì¸ìˆ˜ê°€ 있습니다. + '{0}' 대리ìžì— ìž˜ëª»ëœ ì¸ìˆ˜ê°€ 있습니다. Cannot assign to '{0}' because it is read-only - ì½ê¸° ì „ìš©ì¸ '{0}'ì—는 할당할 수 없습니다. + ì½ê¸° ì „ìš©ì¸ '{0}'ì—는 할당할 수 없습니다. Cannot pass '{0}' as a ref or out argument because it is read-only - '{0}'ì€(는) ì½ê¸° ì „ìš©ì´ë¯€ë¡œ ref ë˜ëŠ” out ì¸ìˆ˜ë¡œ 전달할 수 없습니다. + '{0}'ì€(는) ì½ê¸° ì „ìš©ì´ë¯€ë¡œ ref ë˜ëŠ” out ì¸ìˆ˜ë¡œ 전달할 수 없습니다. Cannot modify the return value of '{0}' because it is not a variable - '{0}'ì€(는) 변수가 아니므로 해당 반환 ê°’ì„ ìˆ˜ì •í•  수 없습니다. + '{0}'ì€(는) 변수가 아니므로 해당 반환 ê°’ì„ ìˆ˜ì •í•  수 없습니다. Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - ì½ê¸° ì „ìš© 필드 '{0}'ì˜ ë©¤ë²„ëŠ” 수정할 수 없습니다. 단 ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. + ì½ê¸° ì „ìš© 필드 '{0}'ì˜ ë©¤ë²„ëŠ” 수정할 수 없습니다. 단 ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - ì½ê¸° ì „ìš© 필드 '{0}'ì˜ ë©¤ë²„ëŠ” ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ìƒì„±ìžì—서는 예외입니다. + ì½ê¸° ì „ìš© 필드 '{0}'ì˜ ë©¤ë²„ëŠ” ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 ìƒì„±ìžì—서는 예외입니다. Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - static ì½ê¸° ì „ìš© 필드 '{0}'ì˜ í•„ë“œì—는 할당할 수 없습니다. 단 static ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. + static ì½ê¸° ì „ìš© 필드 '{0}'ì˜ í•„ë“œì—는 할당할 수 없습니다. 단 static ìƒì„±ìž ë˜ëŠ” 변수 ì´ë‹ˆì…œë¼ì´ì €ì—서는 예외입니다. Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - static ì½ê¸° ì „ìš© 필드 '{0}'ì˜ í•„ë“œëŠ” ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 static ìƒì„±ìžì—서는 예외입니다. + static ì½ê¸° ì „ìš© 필드 '{0}'ì˜ í•„ë“œëŠ” ref ë˜ëŠ” out으로 전달할 수 없습니다. 단 static ìƒì„±ìžì—서는 예외입니다. Cannot assign to '{0}' because it is a '{1}' - '{1}'ì¸ '{0}'ì—는 할당할 수 없습니다. + '{1}'ì¸ '{0}'ì—는 할당할 수 없습니다. Cannot pass '{0}' as a ref or out argument because it is a '{1}' - '{0}'ì€(는) '{1}'ì´ë¯€ë¡œ ref ë˜ëŠ” out ì¸ìˆ˜ë¡œ 전달할 수 없습니다. + '{0}'ì€(는) '{1}'ì´ë¯€ë¡œ ref ë˜ëŠ” out ì¸ìˆ˜ë¡œ 전달할 수 없습니다. '{0}' does not contain a constructor that takes '{1}' arguments - '{0}'ì— '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용하는 ìƒì„±ìžê°€ 없습니다. + '{0}'ì— '{1}'ê°œì˜ ì¸ìˆ˜ë¥¼ 사용하는 ìƒì„±ìžê°€ 없습니다. Non-invocable member '{0}' cannot be used like a method. - 호출할 수 없는 ë©¤ë²„ì¸ '{0}'ì€(는) 메서드처럼 사용할 수 없습니다. + 호출할 수 없는 ë©¤ë²„ì¸ '{0}'ì€(는) 메서드처럼 사용할 수 없습니다. Named argument specifications must appear after all fixed arguments have been specified - ëª…ëª…ëœ ì¸ìˆ˜ ì‚¬ì–‘ì€ ëª¨ë“  ê³ ì • ì¸ìˆ˜ë¥¼ 지정한 다ìŒì— 와야 합니다. + ëª…ëª…ëœ ì¸ìˆ˜ ì‚¬ì–‘ì€ ëª¨ë“  ê³ ì • ì¸ìˆ˜ë¥¼ 지정한 다ìŒì— 와야 합니다. The best overload for '{0}' does not have a parameter named '{1}' - '{0}'ì— ê°€ìž¥ ì í•©í•œ ì˜¤ë²„ë¡œë“œì— '{1}' 매개 변수가 없습니다. + '{0}'ì— ê°€ìž¥ ì í•©í•œ 오버로드ì—는 '{1}' 매개 변수가 없습니다. The delegate '{0}' does not have a parameter named '{1}' - '{0}' 대리ìžì— '{1}' 매개 변수가 없습니다. + '{0}' 대리ìžì—는 '{1}' 매개 변수가 없습니다. Named argument '{0}' cannot be specified multiple times - ëª…ëª…ëœ ì¸ìˆ˜ '{0}'ì„(를) 여러 번 지정할 수 없습니다. + ëª…ëª…ëœ ì¸ìˆ˜ '{0}'ì„(를) 여러 번 지정할 수 없습니다. Named argument '{0}' specifies a parameter for which a positional argument has already been given - ëª…ëª…ëœ ì¸ìˆ˜ '{0}'ì€(는) 위치 ì¸ìˆ˜ê°€ ì´ë¯¸ ì§€ì •ëœ ë§¤ê°œ 변수를 지정합니다. + ëª…ëª…ëœ ì¸ìˆ˜ '{0}'ì€(는) 위치 ì¸ìˆ˜ê°€ ì´ë¯¸ ì§€ì •ëœ ë§¤ê°œ 변수를 지정합니다. - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pl.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pl.xlf new file mode 100644 index 00000000000..754cb7bfd0e --- /dev/null +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pl.xlf @@ -0,0 +1,360 @@ + + + +
+ +
+ + + + An unexpected exception occurred while binding a dynamic operation + Podczas wiÄ…zania operacji dynamicznej wystÄ…piÅ‚ nieoczekiwany wyjÄ…tek. + + + Cannot bind call with no calling object + Nie można powiÄ…zać wywoÅ‚ania z niewywoÅ‚ujÄ…cym obiektem. + + + Overload resolution failed + Ustalanie przeciążenia nie powiodÅ‚o siÄ™. + + + Binary operators must be invoked with two arguments + Operatory binarne muszÄ… być wywoÅ‚ywane z dwoma argumentami. + + + Unary operators must be invoked with one argument + Operatory jednoargumentowe muszÄ… być wywoÅ‚ywane z jednym argumentem. + + + The name '{0}' is bound to a method and cannot be used like a property + Nazwa „{0}†jest ograniczona do metody i nie można jej używać jak wÅ‚aÅ›ciwoÅ›ci. + + + The event '{0}' can only appear on the left hand side of + + Zdarzenie „{0}†może pojawić siÄ™ tylko po lewej stronie wyrażenia + + + + Cannot invoke a non-delegate type + Nie można wywoÅ‚ać typu niebÄ™dÄ…cego delegatem. + + + Binary operators cannot be invoked with one argument + Operatorów binarnych nie można wywoÅ‚ywać z jednym argumentem. + + + Cannot perform member assignment on a null reference + Nie można wykonać przypisania czÅ‚onka na pustym odwoÅ‚aniu. + + + Cannot perform runtime binding on a null reference + Nie można wykonać wiÄ…zania w czasie wykonania na pustym odwoÅ‚aniu. + + + Cannot dynamically invoke method '{0}' because it has a Conditional attribute + Nie można dynamicznie wywoÅ‚ać metody „{0}â€, ponieważ ma atrybut Conditional. + + + Cannot implicitly convert type 'void' to 'object' + Nie można niejawnie przekonwertować typu „void†na „objectâ€. + + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' + Nie można zastosować operatora „{0}†do argumentów operacji typu „{1}†lub „{2}â€. + + + Cannot apply indexing with [] to an expression of type '{0}' + Do wyrażenia typu „{0}†nie można zastosować indeksowania przy użyciu konstrukcji []. + + + Wrong number of indices inside []; expected '{0}' + WewnÄ…trz konstrukcji [] wystÄ™puje niewÅ‚aÅ›ciwa liczba indeksów. Oczekiwana liczba to „{0}†+ + + Operator '{0}' cannot be applied to operand of type '{1}' + Nie można zastosować operatora „{0}†do argumentu operacji typu „{1}â€. + + + Cannot implicitly convert type '{0}' to '{1}' + Nie można niejawnie przekonwertować typu „{0}†na „{1}â€. + + + Cannot convert type '{0}' to '{1}' + Nie można przekonwertować typu „{0}†na „{1}â€. + + + Constant value '{0}' cannot be converted to a '{1}' + Nie można przekonwertować wartoÅ›ci staÅ‚ej „{0}†na „{1}â€. + + + Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' + Operator „{0}†jest niejednoznaczny dla operandów typu „{1}†i „{2}†+ + + Operator '{0}' is ambiguous on an operand of type '{1}' + Dla argumentu operacji typu „{0}†operator „{1}†jest niejednoznaczny. + + + Cannot convert null to '{0}' because it is a non-nullable value type + Nie można przekonwertować zera na „{0}â€, ponieważ jest to niezerowalny typ wartoÅ›ci. + + + Cannot access a non-static member of outer type '{0}' via nested type '{1}' + Za poÅ›rednictwem typu zagnieżdżonego „{1}†nie można uzyskać dostÄ™pu do niestatycznego czÅ‚onka typu zewnÄ™trznego „{0}â€. + + + '{0}' does not contain a definition for '{1}' + Element „{0}†nie zawiera definicji „{1}â€. + + + An object reference is required for the non-static field, method, or property '{0}' + Dla niestatycznego pola, metody lub wÅ‚aÅ›ciwoÅ›ci „{0}†wymagane jest odwoÅ‚anie do obiektu. + + + The call is ambiguous between the following methods or properties: '{0}' and '{1}' + WystÄ…piÅ‚o niejednoznaczne wywoÅ‚anie miÄ™dzy nastÄ™pujÄ…cymi dwiema metodami lub wÅ‚aÅ›ciwoÅ›ciami: „{0}†i „{1}†+ + + '{0}' is inaccessible due to its protection level + Element „{0}†jest niedostÄ™pny z powodu swojego poziomu ochrony. + + + No overload for '{0}' matches delegate '{1}' + Å»adne z przeciążeÅ„ dla elementu „{0}†nie pasuje do delegata „{1}â€. + + + The left-hand side of an assignment must be a variable, property or indexer + Lewa strona przypisania musi być zmiennÄ…, wÅ‚aÅ›ciwoÅ›ciÄ… lub indeksatorem + + + The type '{0}' has no constructors defined + Typ „{0}†nie ma zdefiniowanego konstruktora. + + + The property or indexer '{0}' cannot be used in this context because it lacks the get accessor + W tym kontekÅ›cie nie można użyć wÅ‚aÅ›ciwoÅ›ci lub indeksatora „{0}â€, ponieważ brakuje dla niej metody dostÄ™pu Get. + + + Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead + Nie można uzyskać dostÄ™pu do czÅ‚onka „{0}†przy użyciu odwoÅ‚ania do wystÄ…pienia. Należy użyć nazwy typu jako kwalifikatora. + + + A readonly field cannot be assigned to (except in a constructor or a variable initializer) + Nie można przypisać pola tylko do odczytu (z wyjÄ…tkiem konstruktora lub inicjatora zmiennej) + + + A readonly field cannot be passed ref or out (except in a constructor) + Polu tylko do odczytu nie można przekazać parametru ref ani out (z wyjÄ…tkiem konstruktora) + + + A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) + Nie można przypisać wartoÅ›ci do statycznego pola tylko do odczytu (jest to możliwe tylko w konstruktorze statycznym lub w inicjatorze zmiennej). + + + A static readonly field cannot be passed ref or out (except in a static constructor) + Statycznemu polu tylko do odczytu nie można przekazać parametru ref lub out (z wyjÄ…tkiem konstruktora statycznego) + + + Property or indexer '{0}' cannot be assigned to -- it is read only + Nie można przypisać wartoÅ›ci do wÅ‚aÅ›ciwoÅ›ci lub indeksatora „{0}†– jest on tylko do odczytu + + + A property or indexer may not be passed as an out or ref parameter + Nie można przekazać wÅ‚aÅ›ciwoÅ›ci lub indeksatora jako parametru „out†lub „refâ€. + + + Dynamic calls cannot be used in conjunction with pointers + WywoÅ‚aÅ„ dynamicznych nie można używać w połączeniu ze wskaźnikami. + + + In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters + Aby istniaÅ‚a możliwość zastosowania zdefiniowanego przez użytkownika operatora logicznego („{0}â€) jako operatora „short circuitâ€, typ zwracany tego operatora logicznego musi być identyczny z typem dwóch jego parametrów. + + + The type ('{0}') must contain declarations of operator true and operator false + Typ („{0}â€) musi zawierać deklaracje operatora True i operatora False + + + Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) + Nie można przekonwertować wartoÅ›ci staÅ‚ej „{0}†na „{1}†(w celu przesÅ‚oniÄ™cia należy użyć skÅ‚adni instrukcji „uncheckedâ€). + + + Ambiguity between '{0}' and '{1}' + Niejednoznaczność pomiÄ™dzy „{0}†i „{1}†+ + + Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) + Nie można niejawnie przekonwertować typu „{0}†na „{1}â€. Istnieje konwersja jawna (czy nie brakuje rzutu?). + + + The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible + WÅ‚aÅ›ciwoÅ›ci lub indeksatora „{0}†nie można użyć w tym kontekÅ›cie, ponieważ metoda dostÄ™pu Get jest niedostÄ™pna. + + + The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible + WÅ‚aÅ›ciwoÅ›ci lub indeksatora „{0}†nie można użyć w tym kontekÅ›cie, ponieważ metoda dostÄ™pu Set jest niedostÄ™pna. + + + Using the generic {1} '{0}' requires '{2}' type arguments + Użycie ogólnego elementu {1} „{0}†wymaga argumentów typu „{2}â€. + + + The {1} '{0}' cannot be used with type arguments + Elementu {1} „{0}†nie można używać z argumentami typu. + + + The non-generic {1} '{0}' cannot be used with type arguments + Nieogólnego elementu {1} „{0}†nie można używać z argumentami typu. + + + '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' + Element „{2}†musi być typem nieabstrakcyjnym z publicznym konstruktorem bez parametrów, aby można go byÅ‚o użyć jako parametru „{1}†w typie ogólnym lub metodzie „{0}â€. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. + Nie można użyć typu „{3}†jako parametru typu „{2}†w typie ogólnym lub metodzie „{0}â€. Brak niejawnej konwersji odwoÅ‚ania z typu „{3}†na „{1}â€. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. + Nie można użyć typu „{3}†jako parametru typu „{2}†w typie ogólnym lub metodzie „{0}â€. Typ zerowalny „{3}†nie speÅ‚nia ograniczenia elementu „{1}â€. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. + Nie można użyć typu „{3}†jako parametru typu „{2}†w typie ogólnym lub metodzie „{0}â€. Typ zerowalny „{3}†nie speÅ‚nia ograniczenia elementu „{1}â€. Typy zerowalne nie mogÄ… speÅ‚niać żadnych ograniczeÅ„ interfejsów. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. + Nie można użyć typu „{3}†jako parametru typu „{2}†w typie ogólnym lub metodzie „{0}â€. Brak konwersji pakujÄ…cej z „{3}†na „{1}â€. + + + The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. + Nie można wywnioskować argumentów typu dla metody „{0}†na podstawie użytkowania. Spróbuj jawnie okreÅ›lić argumenty typu. + + + The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' + Typ „{2}†musi być typem referencyjnym, aby można byÅ‚o używać go jako parametru „{1}†w typie ogólnym lub metodzie ogólnej „{0}â€. + + + The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' + Typ „{2}†musi być niezerowalnym typem wartoÅ›ci, aby można byÅ‚o użyć go jako parametru „{1}†w typie ogólnym lub metodzie ogólnej „{0}â€. + + + Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' + Niejednoznaczne zdefiniowane przez użytkownika konwersje „{0}†i „{1}†podczas konwertowania z „{2}†na „{3}â€. + + + '{0}' is not supported by the language + Element „{0}†nie jest obsÅ‚ugiwany przez jÄ™zyk. + + + '{0}': cannot explicitly call operator or accessor + „{0}â€: nie można jawnie wywoÅ‚ać operatora lub metody dostÄ™pu. + + + Cannot convert to static type '{0}' + Nie można przekonwertować na typ statyczny „{0}â€. + + + The operand of an increment or decrement operator must be a variable, property or indexer + Argument operatora zwiÄ™kszania lub zmniejszania musi być zmiennÄ…, wÅ‚aÅ›ciwoÅ›ciÄ… lub indeksatorem. + + + No overload for method '{0}' takes '{1}' arguments + Å»adna metoda przeciążenia metody „{0}†nie pobiera takiej liczby argumentów: „{1}â€. + + + The best overloaded method match for '{0}' has some invalid arguments + Najlepiej dopasowana metoda przeciążona metody „{0}†zawiera niektóre nieprawidÅ‚owe argumenty. + + + A ref or out argument must be an assignable variable + Argument „ref†lub „out†musi być zmiennÄ… umożliwiajÄ…cÄ… przypisanie. + + + Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) + Nie można uzyskać dostÄ™pu do czÅ‚onka chronionego „{0}†za poÅ›rednictwem kwalifikatora typu „{1}â€. Wymagany jest kwalifikator typu „{2}†(lub typu pochodzÄ…cego od tego typu). + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' + WÅ‚aÅ›ciwość, indeksator lub zdarzenie „{0}†nie jest obsÅ‚ugiwane przez jÄ™zyk. Spróbuj bezpoÅ›rednio wywoÅ‚ać metody dostÄ™pu „{1}†lub „{2}â€. + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' + WÅ‚aÅ›ciwość, indeksator lub zdarzenie „{0}†nie jest obsÅ‚ugiwane przez jÄ™zyk. Spróbuj bezpoÅ›rednio wywoÅ‚ać metodÄ™ dostÄ™pu „{1}â€. + + + Delegate '{0}' does not take '{1}' arguments + Delegat „{0}†nie przyjmuje argumentów „{1}†+ + + Delegate '{0}' has some invalid arguments + W delegacie „{0}†wystÄ™pujÄ… nieprawidÅ‚owe argumenty. + + + Cannot assign to '{0}' because it is read-only + Nie można przypisać wartoÅ›ci do elementu „{0}†ponieważ jest on tylko do odczytu + + + Cannot pass '{0}' as a ref or out argument because it is read-only + Nie można przekazać elementu „{0}†jako argumentu „ref†lub „outâ€, ponieważ jest on tylko do odczytu. + + + Cannot modify the return value of '{0}' because it is not a variable + Nie można zmodyfikować zwracanej wartoÅ›ci „{0}â€, ponieważ nie jest to zmienna. + + + Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) + Nie można modyfikować czÅ‚onków pola tylko do odczytu „{0}†(z wyjÄ…tkiem czÅ‚onków w konstruktorze lub inicjatorze zmiennych). + + + Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) + CzÅ‚onkom pola tylko do odczytu „{0}†nie można nadać atrybutu „ref†lub „out†(z wyjÄ…tkiem czÅ‚onków w konstruktorze). + + + Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) + Polom statycznego pola tylko do odczytu „{0}†nie można przypisać wartoÅ›ci (z wyjÄ…tkiem pól w statycznym konstruktorze lub inicjatorze zmiennych). + + + Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) + Polom statycznego pola tylko do odczytu „{0}†nie można nadać atrybutu „ref†lub „out†(z wyjÄ…tkiem pól w konstruktorze statycznym). + + + Cannot assign to '{0}' because it is a '{1}' + Nie można przypisać wartoÅ›ci do elementu „{0}â€, ponieważ jest to „{1}â€. + + + Cannot pass '{0}' as a ref or out argument because it is a '{1}' + Nie można przekazać elementu „{0}†jako argumentu „ref†lub „outâ€, ponieważ jest to „{1}â€. + + + '{0}' does not contain a constructor that takes '{1}' arguments + Element „{0}†nie zawiera konstruktora przyjmujÄ…cego nastÄ™pujÄ…cÄ… liczbÄ™ argumentów: „{1}â€. + + + Non-invocable member '{0}' cannot be used like a method. + CzÅ‚onka „{0}â€, którego nie można wywoÅ‚ywać, nie można używać jak metody. + + + Named argument specifications must appear after all fixed arguments have been specified + Specyfikacje argumentu nazwanego muszÄ… wystÄ™pować po wszystkich staÅ‚ych argumentach, które zostaÅ‚y okreÅ›lone + + + The best overload for '{0}' does not have a parameter named '{1}' + Najlepsza metoda przeÅ‚adowania dla elementu „{0}†nie ma parametru o nazwie „{1}â€. + + + The delegate '{0}' does not have a parameter named '{1}' + Delegat „{0}†nie ma parametru o nazwie „{1}†+ + + Named argument '{0}' cannot be specified multiple times + Nazwanego argumentu „{0}†nie można wprowadzać wiele razy. + + + Named argument '{0}' specifies a parameter for which a positional argument has already been given + Nazwany argument „{0}†okreÅ›la parametr, dla którego argument pozycyjny zostaÅ‚ już wskazany. + + + +
+
\ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pt-BR.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pt-BR.xlf new file mode 100644 index 00000000000..d99df49370a --- /dev/null +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.pt-BR.xlf @@ -0,0 +1,360 @@ + + + +
+ +
+ + + + An unexpected exception occurred while binding a dynamic operation + Ocorreu uma exceção inesperada ao associar uma operação dinâmica + + + Cannot bind call with no calling object + Não é possível associar chamada sem objeto de chamada + + + Overload resolution failed + Falha na resolução da sobrecarga + + + Binary operators must be invoked with two arguments + Os operadores binários devem ser chamados com dois argumentos + + + Unary operators must be invoked with one argument + Os operadores unários devem ser chamados com um argumento + + + The name '{0}' is bound to a method and cannot be used like a property + O nome '{0}' é associado a um método e não pode ser usado como uma propriedade + + + The event '{0}' can only appear on the left hand side of + + O evento '{0}' somente pode aparecer no lado esquerdo de + + + + Cannot invoke a non-delegate type + Não é possível chamar um tipo não delegado + + + Binary operators cannot be invoked with one argument + Os operadores binários não podem ser chamados com um argumento + + + Cannot perform member assignment on a null reference + Não é possível executar atribuição de membro em uma referência nula + + + Cannot perform runtime binding on a null reference + Não é possível fazer associação em tempo de execução em uma referência nula + + + Cannot dynamically invoke method '{0}' because it has a Conditional attribute + Não é possível chamar o método '{0}' dinamicamente porque ele tem um atributo Conditional + + + Cannot implicitly convert type 'void' to 'object' + Não é possível converter implicitamente o tipo 'void' em 'object' + + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' + O operador '{0}' não pode ser aplicado a operandos dos tipos '{1}' e '{2}' + + + Cannot apply indexing with [] to an expression of type '{0}' + Não é possível aplicar a indexação com [] a uma expressão do tipo '{0}' + + + Wrong number of indices inside []; expected '{0}' + Número incorreto de índices dentro de []; esperava-se '{0}' + + + Operator '{0}' cannot be applied to operand of type '{1}' + O operador '{0}' não pode ser aplicado ao operando do tipo '{1}' + + + Cannot implicitly convert type '{0}' to '{1}' + Não é possível converter implicitamente tipo '{0}' em '{1}' + + + Cannot convert type '{0}' to '{1}' + Não é possível converter tipo '{0}' em '{1}' + + + Constant value '{0}' cannot be converted to a '{1}' + O valor de constante '{0}' não pode ser convertido em '{1}' + + + Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' + O operador '{0}' é ambíguo em operandos dos tipos '{1}' e '{2}' + + + Operator '{0}' is ambiguous on an operand of type '{1}' + O operador '{0}' é ambíguo em um operando do tipo '{1}' + + + Cannot convert null to '{0}' because it is a non-nullable value type + Não é possível converter o valor nulo em '{0}' porque ele não é um tipo de valor não nulo + + + Cannot access a non-static member of outer type '{0}' via nested type '{1}' + Não é possível acessar um membro não estático do tipo externo '{0}' através do tipo aninhado '{1}' + + + '{0}' does not contain a definition for '{1}' + "{0}" não contém uma definição para "{1}" + + + An object reference is required for the non-static field, method, or property '{0}' + Uma referência de objeto é necessária para o campo, o método ou a propriedade '{0}' não estática + + + The call is ambiguous between the following methods or properties: '{0}' and '{1}' + A chamada é ambígua entre os seguintes métodos ou propriedades: '{0}' e '{1}' + + + '{0}' is inaccessible due to its protection level + "{0}" é inacessível devido ao seu nível de proteção + + + No overload for '{0}' matches delegate '{1}' + Nenhuma sobrecarga de '{0}' corresponde ao representante '{1}' + + + The left-hand side of an assignment must be a variable, property or indexer + O lado esquerdo de uma atribuição deve ser uma variável, uma propriedade ou um indexador + + + The type '{0}' has no constructors defined + O tipo '{0}' não tem construtores definidos + + + The property or indexer '{0}' cannot be used in this context because it lacks the get accessor + A propriedade ou o indexador '{0}' não pode ser usado neste contexto porque não possui o acessador get + + + Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead + O membro '{0}' não pode ser acessado com uma referência de instância; qualifique-o com um nome de tipo + + + A readonly field cannot be assigned to (except in a constructor or a variable initializer) + Um campo somente leitura não pode ser atribuído (exceto em um construtor ou inicializador de variável) + + + A readonly field cannot be passed ref or out (except in a constructor) + Um campo somente leitura não pode ser passado como ref ou out (exceto em um construtor) + + + A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) + Um campo somente leitura estático não pode ser atribuído (exceto em um construtor estático ou inicializador de variável) + + + A static readonly field cannot be passed ref or out (except in a static constructor) + Um campo somente leitura estático não pode ser passado como ref ou out (exceto em um construtor estático) + + + Property or indexer '{0}' cannot be assigned to -- it is read only + A propriedade ou o indexador '{0}' não pode ser atribuído -- ele(a) é somente leitura + + + A property or indexer may not be passed as an out or ref parameter + Talvez uma propriedade ou um indexador não possa ser passado como um parâmetro out ou ref + + + Dynamic calls cannot be used in conjunction with pointers + As chamadas dinâmicas não podem ser usadas junto com ponteiros + + + In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters + Para ser aplicado como um operador de curto-circuito, um operador lógico definido por usuário ('{0}') deve ter o mesmo tipo de retorno que o tipo dos seus dois parâmetros + + + The type ('{0}') must contain declarations of operator true and operator false + O tipo ('{0}') deve conter declarações do operador true e do operador false + + + Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) + O valor de constante '{0}' não pode ser convertido em '{1}' (use a sintaxe 'unchecked' para substituir) + + + Ambiguity between '{0}' and '{1}' + Ambiguidade entre '{0}' e '{1}' + + + Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) + Não é possível converter implicitamente tipo '{0}' em '{1}'. Existe uma conversão explícita (há uma conversão ausente?) + + + The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible + A propriedade ou o indexador '{0}' não pode ser usado neste contexto porque o acessador get é inacessível + + + The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible + A propriedade ou o indexador '{0}' não pode ser usado neste contexto porque o acessador set é inacessível + + + Using the generic {1} '{0}' requires '{2}' type arguments + O uso do {1} genérico '{0}' requer argumentos de tipo '{2}' + + + The {1} '{0}' cannot be used with type arguments + O {1} '{0}' não pode ser usado com argumentos de tipo + + + The non-generic {1} '{0}' cannot be used with type arguments + O {1} não genérico '{0}' não pode ser usado como argumentos de tipo + + + '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' + "{2}" deve ser um tipo non-abstract com um construtor público sem-parâmetros para que possa ser usado como parâmetro "{1}" no tipo ou método genérico "{0}" + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. + O tipo '{3}' não pode ser usado como parâmetro de tipo '{2}' no tipo ou método genérico '{0}'. Não há conversão de referência implícita de '{3}' em '{1}'. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. + O tipo '{3}' não pode ser usado como parâmetro de tipo '{2}' no tipo ou método genérico '{0}'. O tipo '{3}' que permite valores nulos não satisfaz a restrição de '{1}'. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. + O tipo '{3}' não pode ser usado como parâmetro de tipo '{2}' no tipo ou método genérico '{0}'. O tipo '{3}' que permite valores nulos não satisfaz a restrição de '{1}'. Os tipos que permitem valores nulos não satisfazem as restrições de interface. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. + O tipo '{3}' não pode ser usado como parâmetro de tipo '{2}' no tipo ou método genérico '{0}'. Não há conversão boxing de '{3}' em '{1}'. + + + The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. + Os argumentos de tipo do método '{0}' não podem ser inferidos a partir do uso. Tente especificar explicitamente os argumentos de tipo. + + + The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' + O tipo '{2}' deve ser um tipo de referência para que seja usado como parâmetro '{1}' no tipo ou método genérico '{0}' + + + The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' + O tipo '{2}' deve ser um tipo de valor não nulo para que seja usado como parâmetro '{1}' no tipo ou método genérico '{0}' + + + Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' + Conversões ambíguas definidas por usuário '{0}' e '{1}' ao realizar a conversão de '{2}' em '{3}' + + + '{0}' is not supported by the language + O idioma não oferece suporte a '{0}' + + + '{0}': cannot explicitly call operator or accessor + "{0}": não é possível chamar explicitamente o operador ou acessador + + + Cannot convert to static type '{0}' + Não é possível converter em tipo estático '{0}' + + + The operand of an increment or decrement operator must be a variable, property or indexer + O operando de incremento ou decremento deve ser uma variável, uma propriedade ou um indexador + + + No overload for method '{0}' takes '{1}' arguments + Nenhuma sobrecarga do método '{0}' obtém argumentos '{1}' + + + The best overloaded method match for '{0}' has some invalid arguments + A melhor correspondência de método sobrecarregado '{0}' tem alguns argumentos inválidos + + + A ref or out argument must be an assignable variable + Um argumento ref ou out deve ser uma variável que possa ser atribuída + + + Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) + Não é possível acessar membro protegido '{0}' através de um qualificador do tipo '{1}'; o qualificador deve ser do tipo '{2}' (ou derivado dele) + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' + O idioma não oferece suporte à propriedade, ao indexador ou ao evento '{0}'; tente chamar diretamente o método de acessador '{1}' ou '{2}' + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' + O idioma não oferece suporte à propriedade, ao indexador ou ao evento '{0}'; tente chamar diretamente o método de acessador '{1}' + + + Delegate '{0}' does not take '{1}' arguments + O representante '{0}' não obtém argumentos '{1}' + + + Delegate '{0}' has some invalid arguments + O representante '{0}' tem alguns argumentos inválidos + + + Cannot assign to '{0}' because it is read-only + Não é possível atribuir a '{0}' porque ele é somente leitura + + + Cannot pass '{0}' as a ref or out argument because it is read-only + Não é possível passar '{0}' como um argumento ref ou out porque ele é somente leitura + + + Cannot modify the return value of '{0}' because it is not a variable + Não é possível modificar o valor de retorno '{0}' porque ele não é uma variável + + + Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) + Os membros do campo somente leitura '{0}' não podem ser modificados (exceto em um construtor ou inicializador de variável) + + + Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) + Os membros do campo somente leitura '{0}' não podem ser passados como ref ou out (a não ser em um construtor) + + + Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) + Campos do campo estático somente leitura '{0}' não podem ser atribuídos (exceto em um construtor estático ou inicializador de variável) + + + Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) + Os campos do campo somente leitura estático '{0}' não podem ser passados como ref ou out (exceto em um construtor estático) + + + Cannot assign to '{0}' because it is a '{1}' + Não é possível atribuir a '{0}' porque ele é um '{1}' + + + Cannot pass '{0}' as a ref or out argument because it is a '{1}' + Não é possível passar '{0}' como um argumento ref ou out porque ele é '{1}' + + + '{0}' does not contain a constructor that takes '{1}' arguments + '{0}' não contém um construtor que obtém argumentos '{1}' + + + Non-invocable member '{0}' cannot be used like a method. + O membro não invocável '{0}' não pode ser usado como um método. + + + Named argument specifications must appear after all fixed arguments have been specified + As especificações de argumento nomeado devem aparecer depois que todos os argumentos fixos forem especificados + + + The best overload for '{0}' does not have a parameter named '{1}' + A melhor sobrecarga de '{0}' não tem um parâmetro chamado '{1}' + + + The delegate '{0}' does not have a parameter named '{1}' + O representante '{0}' não tem um parâmetro chamado '{1}' + + + Named argument '{0}' cannot be specified multiple times + O argumento nomeado '{0}' não pode ser especificado várias vezes + + + Named argument '{0}' specifies a parameter for which a positional argument has already been given + O argumento nomeado '{0}' especifica um parâmetro para o qual já foi atribuído um argumento posicional + + + +
+
\ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ru.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ru.xlf index cb80c34e81d..8f6f3934fcc 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ru.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.ru.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - Возникло непредвиденное иÑключение при выполнении привÑзки динамичеÑкой операции + Возникло непредвиденное иÑключение при выполнении привÑзки динамичеÑкой операции Cannot bind call with no calling object - Ðевозможно выполнить привÑзку вызова к невызывающему объекту + Ðевозможно выполнить привÑзку вызова к невызывающему объекту Overload resolution failed - Сбой при разрешении перегрузки + Ошибка при разрешении перегрузки Binary operators must be invoked with two arguments - Бинарные операторы должны вызыватьÑÑ Ñ Ð¸Ñпользованием двух аргументов + Бинарные операторы должны вызыватьÑÑ Ñ Ð¸Ñпользованием двух аргументов Unary operators must be invoked with one argument - Унарные операторы должны быть вызваны Ñ Ð¸Ñпользованием одного аргумента + Унарные операторы должны быть вызваны Ñ Ð¸Ñпользованием одного аргумента The name '{0}' is bound to a method and cannot be used like a property - Ð”Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸ "{0}" выполнена привÑзка к методу. Ðевозможно иÑпользовать его как ÑвойÑтво + Ð”Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸ "{0}" выполнена привÑзка к методу. Ðевозможно иÑпользовать его как ÑвойÑтво The event '{0}' can only appear on the left hand side of + - Событие «{0}» может поÑвлÑтьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ на Ñтороне левой руки + + Событие "{0}" может отображатьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñлева от "+" Cannot invoke a non-delegate type - Ðе удалоÑÑŒ вызвать тип, не ÑвлÑющийÑÑ Ð´ÐµÐ»ÐµÐ³Ð°Ñ‚Ð¾Ð¼ + Ðе удалоÑÑŒ вызвать тип, не ÑвлÑющийÑÑ Ð´ÐµÐ»ÐµÐ³Ð°Ñ‚Ð¾Ð¼ Binary operators cannot be invoked with one argument - Ðевозможно вызвать бинарные операторы Ñ Ð¸Ñпользованием одного аргумента + Ðевозможно вызвать бинарные операторы Ñ Ð¸Ñпользованием одного аргумента Cannot perform member assignment on a null reference - Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑваивание члена по нулевой ÑÑылке + Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑваивание члена по нулевой ÑÑылке Cannot perform runtime binding on a null reference - Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ привÑзки иÑполнÑющей Ñреды по нулевой ÑÑылке + Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ привÑзки иÑполнÑющей Ñреды по нулевой ÑÑылке Cannot dynamically invoke method '{0}' because it has a Conditional attribute - Ðе удаетÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸Ñ‡ÐµÑки вызвать метод "{0}", так как у него еÑть уÑловный атрибут + Ðе удаетÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸Ñ‡ÐµÑки вызвать метод "{0}", так как у него еÑть уÑловный атрибут Cannot implicitly convert type 'void' to 'object' - ÐÐµÐ»ÑŒÐ·Ñ Ð½ÐµÑвно преобразовать тип "void" to "object" + ÐÐµÐ»ÑŒÐ·Ñ Ð½ÐµÑвно преобразовать тип "void" to "object" Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - Ðе удаетÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ оператор "{0}" к операндам типа "{1}" и "{2}" + Ðе удаетÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ оператор "{0}" к операндам типа "{1}" и "{2}" Cannot apply indexing with [] to an expression of type '{0}' - Ðевозможно применить индекÑирование через [] к выражению типа "{0}" + Ðевозможно применить индекÑирование через [] к выражению типа "{0}" Wrong number of indices inside []; expected '{0}' - Ðеверное количеÑтво индекÑов внутри []; ожидалоÑÑŒ "{0}" + Ðеверное количеÑтво индекÑов внутри []; ожидалоÑÑŒ "{0}" Operator '{0}' cannot be applied to operand of type '{1}' - Ðе удаетÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ операнд "{0}" типа "{1}" + Ðе удаетÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ операнд "{0}" типа "{1}" Cannot implicitly convert type '{0}' to '{1}' - Ðе удаетÑÑ Ð½ÐµÑвно преобразовать тип "{0}" в "{1}" + Ðе удаетÑÑ Ð½ÐµÑвно преобразовать тип "{0}" в "{1}" Cannot convert type '{0}' to '{1}' - Ðевозможно преобразовать тип "{0}" в "{1}" + Ðевозможно преобразовать тип "{0}" в "{1}" Constant value '{0}' cannot be converted to a '{1}' - ПоÑтоÑнное значение "{0}" не может быть преобразовано в "{1}" + ПоÑтоÑнное значение "{0}" не может быть преобразовано в "{1}" Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - Оператор "{0}" ÑвлÑетÑÑ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ñ‹Ð¼ по отношению к операндам типа "{1}" и "{2}" + Оператор "{0}" ÑвлÑетÑÑ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ñ‹Ð¼ по отношению к операндам типа "{1}" и "{2}" Operator '{0}' is ambiguous on an operand of type '{1}' - Оператор "{0}" ÑвлÑетÑÑ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ñ‹Ð¼ по отношению к операнду типа "{1}" + Оператор "{0}" ÑвлÑетÑÑ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ñ‹Ð¼ по отношению к операнду типа "{1}" Cannot convert null to '{0}' because it is a non-nullable value type - Cannot convert null to "{0}" because it is a non-nullable value type + Cannot convert null to "{0}" because it is a non-nullable value type Cannot access a non-static member of outer type '{0}' via nested type '{1}' - Ðевозможно получить доÑтуп к неÑтатичеÑкому члену внешнего типа "{0}" через вложенный тип "{1}" + Ðевозможно получить доÑтуп к неÑтатичеÑкому члену внешнего типа "{0}" через вложенный тип "{1}" '{0}' does not contain a definition for '{1}' - "{0}" не Ñодержит Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ "{1}" + "{0}" не Ñодержит определение Ð´Ð»Ñ "{1}". An object reference is required for the non-static field, method, or property '{0}' - Ð”Ð»Ñ Ð½ÐµÑтатичеÑкого полÑ, метода или ÑвойÑтва "{0}" требуетÑÑ ÑÑылка на объект + Ð”Ð»Ñ Ð½ÐµÑтатичеÑкого полÑ, метода или ÑвойÑтва "{0}" требуетÑÑ ÑÑылка на объект The call is ambiguous between the following methods or properties: '{0}' and '{1}' - ПонÑтие "вызов" трактуетÑÑ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ð¾ в Ñледующих методах или ÑвойÑтвах: "{0}" и "{1}" + Ðеоднозначный вызов Ñледующих методов или ÑвойÑтв: "{0}" и "{1}" '{0}' is inaccessible due to its protection level - "{0}" недоÑтупен из-за его ÑƒÑ€Ð¾Ð²Ð½Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ + "{0}" недоÑтупен из-за его ÑƒÑ€Ð¾Ð²Ð½Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹. No overload for '{0}' matches delegate '{1}' - Ðет перегрузки Ð´Ð»Ñ "{0}", ÑоответÑтвующей делегату "{1}" + Ðет перегрузки Ð´Ð»Ñ "{0}", ÑоответÑтвующей делегату "{1}" The left-hand side of an assignment must be a variable, property or indexer - Ð›ÐµÐ²Ð°Ñ Ñ‡Ð°Ñть Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ÑÐ²Ð°Ð¸Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° быть переменной, ÑвойÑтвом или индекÑатором + Ð›ÐµÐ²Ð°Ñ Ñ‡Ð°Ñть Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ÑÐ²Ð°Ð¸Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° быть переменной, ÑвойÑтвом или индекÑатором The type '{0}' has no constructors defined - Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{0}" нет определенных конÑтрукторов + Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{0}" нет определенных конÑтрукторов The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - The property or indexer "{0}" cannot be used in this context because it lacks the get accessor + The property or indexer "{0}" cannot be used in this context because it lacks the get accessor Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - ДоÑтуп к члену "{0}" через ÑÑылку на ÑкземплÑÑ€ невозможен; вмеÑто Ñтого уточните его, указав Ð¸Ð¼Ñ Ñ‚Ð¸Ð¿Ð° + ДоÑтуп к члену "{0}" через ÑÑылку на ÑкземплÑÑ€ невозможен; вмеÑто Ñтого уточните его, указав Ð¸Ð¼Ñ Ñ‚Ð¸Ð¿Ð° A readonly field cannot be assigned to (except in a constructor or a variable initializer) - ПриÑваивание значений доÑтупному только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŽ допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в конÑтрукторе и в инициализаторе переменных + ПриÑваивание значений доÑтупному только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŽ допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в конÑтрукторе и в инициализаторе переменных A readonly field cannot be passed ref or out (except in a constructor) - ДоÑтупное только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ðµ может передаватьÑÑ ÐºÐ°Ðº параметр Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼ Ñловом ref или out только в конÑтрукторе + ДоÑтупное только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ðµ может передаватьÑÑ ÐºÐ°Ðº параметр Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼ Ñловом ref или out только в конÑтрукторе A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - ПриÑваивание значений доÑтупному только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкому полю допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в ÑтатичеÑком конÑтрукторе и в инициализаторе переменных + ПриÑваивание значений доÑтупному только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкому полю допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в ÑтатичеÑком конÑтрукторе и в инициализаторе переменных A static readonly field cannot be passed ref or out (except in a static constructor) - ДоÑтупное только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкое поле может передаватьÑÑ ÐºÐ°Ðº параметр Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼ Ñловом ref или out только в ÑтатичеÑком конÑтрукторе + ДоÑтупное только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкое поле может передаватьÑÑ ÐºÐ°Ðº ref или out только в ÑтатичеÑком конÑтрукторе Property or indexer '{0}' cannot be assigned to -- it is read only - Ðевозможно приÑвоить значение ÑвойÑтву или индекÑатору "{0}" -- доÑтуп только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + Ðевозможно приÑвоить значение ÑвойÑтву или индекÑатору "{0}" -- доÑтуп только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ A property or indexer may not be passed as an out or ref parameter - СвойÑтва и индекÑаторы не могут передаватьÑÑ ÐºÐ°Ðº параметры Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼Ð¸ Ñловами out и ref + СвойÑтва и индекÑаторы не могут передаватьÑÑ ÐºÐ°Ðº параметры Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼Ð¸ Ñловами out и ref Dynamic calls cannot be used in conjunction with pointers - ДинамичеÑкие вызовы Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать в ÑопрÑжении Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñми + ДинамичеÑкие вызовы Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать в ÑопрÑжении Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñми In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве логичеÑкого оператора краткой запиÑи тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÑкого логичеÑкого оператора ("{0}") должен быть аналогичен типам двух его параметров + Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве логичеÑкого оператора краткой запиÑи тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÑкого логичеÑкого оператора ("{0}") должен быть аналогичен типам двух его параметров The type ('{0}') must contain declarations of operator true and operator false - Тип ("{0}") должен Ñодержать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ‚Ð¾Ñ€Ð° true и оператора false + Тип ("{0}") должен Ñодержать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ‚Ð¾Ñ€Ð° true и оператора false Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - ПоÑтоÑнное значение "{0}" не может быть преобразовано в "{1}" (Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð° иÑпользуйте ÑинтакÑÐ¸Ñ "unchecked") + ПоÑтоÑнное значение "{0}" не может быть преобразовано в "{1}" (Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð° иÑпользуйте ÑинтакÑÐ¸Ñ "unchecked") Ambiguity between '{0}' and '{1}' - ÐеоднозначноÑть между "{0}" и "{1}" + ÐеоднозначноÑть между "{0}" и "{1}" Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - Ðе удаетÑÑ Ð½ÐµÑвно преобразовать тип "{0}" в "{1}". СущеÑтвует Ñвное преобразование (требуетÑÑ Ð¿Ñ€Ð¸Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ типа?) + Ðе удаетÑÑ Ð½ÐµÑвно преобразовать тип "{0}" в "{1}". СущеÑтвует Ñвное преобразование (требуетÑÑ Ð¿Ñ€Ð¸Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ типа?) The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - СвойÑтво или индекÑатор "{0}" невозможно иÑпользовать в данном контекÑте, поÑкольку метод доÑтупа get недоÑтупен + СвойÑтво или индекÑатор "{0}" невозможно иÑпользовать в данном контекÑте, поÑкольку метод доÑтупа get недоÑтупен The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - СвойÑтво или индекÑатор "{0}" невозможно иÑпользовать в данном контекÑте, поÑкольку метод доÑтупа set недоÑтупен + СвойÑтво или индекÑатор "{0}" невозможно иÑпользовать в данном контекÑте, поÑкольку метод доÑтупа set недоÑтупен Using the generic {1} '{0}' requires '{2}' type arguments - ИÑпользование универÑального {1} "{0}" требует аргументов типа "{2}" + ИÑпользование универÑального {1} "{0}" требует аргументов типа "{2}" The {1} '{0}' cannot be used with type arguments - {1} "{0}" не может иÑпользоватьÑÑ Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸ типа + {1} "{0}" не может иÑпользоватьÑÑ Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸ типа The non-generic {1} '{0}' cannot be used with type arguments - ÐеуниверÑальный {1} "{0}" не может иÑпользоватьÑÑ Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸ типа + ÐеуниверÑальный {1} "{0}" не может иÑпользоватьÑÑ Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸ типа '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - "Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть неабÑтрактным и иметь открытый конÑтруктор без параметров + "Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть неабÑтрактным и иметь открытый конÑтруктор без параметров The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ðет неÑвного Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑÑылки из "{3}" в "{1}". + Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ðет неÑвного Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑÑылки из "{3}" в "{1}". The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{3}", допуÑкающего значение null, не выполнÑетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ "{1}". + Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{3}", допуÑкающего значение null, не выполнÑетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ "{1}". The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{3}", допуÑкающего значение null, не выполнÑетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ "{1}". Типы, допуÑкающие значение null, не могут удовлетворÑть ни одному интерфейÑному ограничению. + Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ð”Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{3}", допуÑкающего значение null, не выполнÑетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ "{1}". Типы, допуÑкающие значение null, не могут удовлетворÑть ни одному интерфейÑному ограничению. The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ðет преобразованиÑ-упаковки из "{3}" в "{1}". + Ðевозможно иÑпользовать тип "{3}" в качеÑтве параметра типа "{2}" в универÑальном типе или методе "{0}". Ðет преобразованиÑ-упаковки из "{3}" в "{1}". The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - The type arguments for method "{0}" cannot be inferred from the usage. ПопытайтеÑÑŒ Ñвно определить аргументы-типы. + The type arguments for method "{0}" cannot be inferred from the usage. ПопытайтеÑÑŒ Ñвно определить аргументы-типы. The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть ÑÑылочным типом + Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть ÑÑылочным типом The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть типом значениÑ, не допуÑкающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL + Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве параметра "{1}" в универÑальном типе или методе "{0}" тип "{2}" должен быть типом значениÑ, не допуÑкающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ NULL Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - Ðеоднозначные пользовательÑкие Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ "{0}" и "{1}" при преобразовании из "{2}" в "{3}" + Ðеоднозначные пользовательÑкие Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ "{0}" и "{1}" при преобразовании из "{2}" в "{3}" '{0}' is not supported by the language - "{0}" не поддерживаетÑÑ Ñзыком + "{0}" не поддерживаетÑÑ Ñзыком '{0}': cannot explicitly call operator or accessor - "{0}": Ñвный вызов оператора или метода доÑтупа невозможен + "{0}": Ñвный вызов оператора или метода доÑтупа невозможен. Cannot convert to static type '{0}' - Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ преобразование к ÑтатичеÑкому типу "{0}" + Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ преобразование к ÑтатичеÑкому типу "{0}" The operand of an increment or decrement operator must be a variable, property or indexer - Операндом оператора инкремента или декремента должна быть переменнаÑ, ÑвойÑтво или индекÑатор + Операндом оператора инкремента или декремента должна быть переменнаÑ, ÑвойÑтво или индекÑатор No overload for method '{0}' takes '{1}' arguments - ОтÑутÑтвие перегрузки Ð´Ð»Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð° "{0}" принимает"{1}" аргументы + ОтÑутÑтвие перегрузки Ð´Ð»Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð° "{0}" принимает"{1}" аргументы The best overloaded method match for '{0}' has some invalid arguments - Ðаиболее подходÑщий перегруженный метод Ð´Ð»Ñ "{0}" имеет неÑколько недопуÑтимых аргументов + Ðаиболее подходÑщий перегруженный метод Ð´Ð»Ñ "{0}" имеет неÑколько недопуÑтимых аргументов A ref or out argument must be an assignable variable - Ðргумент Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ð¼ Ñловом ref или out должен быть переменной, которой можно приÑвоить значение + Ðргумент ref или out должен ÑвлÑтьÑÑ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹, которой можно приÑвоить значение Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к защищенному члену "{0}" через квалификатор типа "{1}"; квалификатор должен иметь тип "{2}" (или производный от него) + Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к защищенному члену "{0}" через квалификатор типа "{1}"; квалификатор должен иметь тип "{2}" (или производный от него) Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - СвойÑтво, индекÑатор или Ñобытие "{0}" не поддерживаетÑÑ Ð² данном Ñзыке; попробуйте вызвать метод доÑтупа "{1}" или "{2}" + СвойÑтво, индекÑатор или Ñобытие "{0}" не поддерживаетÑÑ Ð² данном Ñзыке; попробуйте вызвать метод доÑтупа "{1}" или "{2}" Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - СвойÑтво, индекÑатор или Ñобытие "{0}" не поддерживаетÑÑ Ð² данном Ñзыке; попробуйте вызвать метод доÑтупа "{1}" или "{2}" + СвойÑтво, индекÑатор или Ñобытие "{0}" не поддерживаетÑÑ Ð² данном Ñзыке; попробуйте напрÑмую вызвать метод доÑтупа "{1}". Delegate '{0}' does not take '{1}' arguments - Делегат "{0}" не принимает аргументы "{1}" + Делегат "{0}" не принимает аргументы "{1}" Delegate '{0}' has some invalid arguments - Делегат "{0}" имеет недопуÑтимые аргументы + Делегат "{0}" имеет недопуÑтимые аргументы Cannot assign to '{0}' because it is read-only - Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑвоение параметру "{0}", так как он доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑвоение параметру "{0}", так как он доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Cannot pass '{0}' as a ref or out argument because it is read-only - Cannot pass "{0}" as a ref or out argument because it is read-only + Cannot pass "{0}" as a ref or out argument because it is read-only Cannot modify the return value of '{0}' because it is not a variable - Cannot modify the return value of "{0}" because it is not a variable + Cannot modify the return value of "{0}" because it is not a variable Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - Члены Ð¿Ð¾Ð»Ñ "{0}", предназначенного только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, могут быть изменены только в конÑтрукторе или инициализаторе переменных + Члены Ð¿Ð¾Ð»Ñ "{0}", предназначенного только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ, могут быть изменены только в конÑтрукторе или инициализаторе переменных Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - Members of readonly field "{0}" cannot be passed ref or out (except in a constructor) + Members of readonly field "{0}" cannot be passed ref or out (except in a constructor) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - ПриÑваивание значений полÑм доÑтупного только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкого Ð¿Ð¾Ð»Ñ "{0}" допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в ÑтатичеÑком конÑтрукторе и в инициализаторе переменных + ПриÑваивание значений полÑм доÑтупного только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÑтатичеÑкого Ð¿Ð¾Ð»Ñ "{0}" допуÑкаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в ÑтатичеÑком конÑтрукторе и в инициализаторе переменных Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - Fields of static readonly field "{0}" cannot be passed ref or out (except in a static constructor) + Fields of static readonly field "{0}" cannot be passed ref or out (except in a static constructor) Cannot assign to '{0}' because it is a '{1}' - Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑвоение параметру "{0}", так как он ÑвлÑетÑÑ "{1}" + Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ приÑвоение параметру "{0}", так как он ÑвлÑетÑÑ "{1}" Cannot pass '{0}' as a ref or out argument because it is a '{1}' - Ðе удалоÑÑŒ передать "{0}" как ÑÑылку или аргумент out, так как Ñто "{1}" + Ðе удалоÑÑŒ передать "{0}" как ÑÑылку или аргумент out, так как Ñто "{1}" '{0}' does not contain a constructor that takes '{1}' arguments - "{0}" не Ñодержит конÑтруктор, принимающий аргументов: {1} + "{0}" не Ñодержит конÑтруктор, принимающий аргументов: {1} Non-invocable member '{0}' cannot be used like a method. - Ðевызываемый член "{0}" не может иÑпользоватьÑÑ ÐºÐ°Ðº метод. + Ðевызываемый член "{0}" не может иÑпользоватьÑÑ ÐºÐ°Ðº метод. Named argument specifications must appear after all fixed arguments have been specified - Спецификации именованного аргумента должны поÑвлÑтьÑÑ Ð²Ð¾ вÑех указанных фикÑированных аргументах + Спецификации именованного аргумента должны поÑвлÑтьÑÑ Ð²Ð¾ вÑех указанных фикÑированных аргументах The best overload for '{0}' does not have a parameter named '{1}' - Ðаиболее подходÑщий перегруженный метод Ð´Ð»Ñ "{0}" не имеет параметра "{1}" + Ðаиболее подходÑщий перегруженный метод Ð´Ð»Ñ "{0}" не имеет параметра "{1}" The delegate '{0}' does not have a parameter named '{1}' - Делегат "{0}" не имеет параметра "{1}" + Делегат "{0}" не имеет параметра "{1}" Named argument '{0}' cannot be specified multiple times - Ðе удаетÑÑ Ð·Ð°Ð´Ð°Ñ‚ÑŒ именованный аргумент "{0}" неÑколько раз + Ðе удаетÑÑ Ð·Ð°Ð´Ð°Ñ‚ÑŒ именованный аргумент "{0}" неÑколько раз Named argument '{0}' specifies a parameter for which a positional argument has already been given - Именованный аргумент "{0}" задает параметр, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ уже был уÑтановлен позиционный аргумент. + Именованный аргумент "{0}" задает параметр, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ уже был уÑтановлен позиционный аргумент. - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.tr.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.tr.xlf new file mode 100644 index 00000000000..d6e0cb4cd88 --- /dev/null +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.tr.xlf @@ -0,0 +1,360 @@ + + + +
+ +
+ + + + An unexpected exception occurred while binding a dynamic operation + Dinamik bir işlem bağlanırken beklenmeyen bir özel durum oluştu + + + Cannot bind call with no calling object + Çağırma nesnesi olmayan çağrı bağlanamaz + + + Overload resolution failed + Aşırı yükleme çözümlemesi başarısız oldu + + + Binary operators must be invoked with two arguments + İkili işleçler iki bağımsız değişkenle çağrılabilir + + + Unary operators must be invoked with one argument + Birli işleçler tek bağımsız değişkenle çağrılmalıdır + + + The name '{0}' is bound to a method and cannot be used like a property + '{0}' adı bir metoda bağlıdır ve bir özellik gibi kullanılamaz + + + The event '{0}' can only appear on the left hand side of + + '{0}' olayı yalnızca + işaretinin sol tarafında görünebilir + + + Cannot invoke a non-delegate type + Temsilci olmayan bir tür çağrılamaz + + + Binary operators cannot be invoked with one argument + İkili işleçler tek bağımsız değişkenle çağrılamaz + + + Cannot perform member assignment on a null reference + Null başvuruya üye ataması yapılamaz + + + Cannot perform runtime binding on a null reference + Null bir başvuruya bağlanma çalışma zamanında gerçekleştirilemez + + + Cannot dynamically invoke method '{0}' because it has a Conditional attribute + Conditional özniteliği olduğundan '{0}' metodu dinamik olarak çağrılamıyor + + + Cannot implicitly convert type 'void' to 'object' + 'void' türü örtülü olarak 'object' türüne dönüştürülemiyor + + + Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' + '{0}' işleci '{1}' ve '{2}' türündeki işlenenlere uygulanamaz + + + Cannot apply indexing with [] to an expression of type '{0}' + '{0}' türündeki bir ifadeye [] ile indis erişimi uygulanamaz + + + Wrong number of indices inside []; expected '{0}' + [] içinde yanlış sayıda dizin var; '{0}' olması bekleniyor + + + Operator '{0}' cannot be applied to operand of type '{1}' + '{0}' işleci '{1}' türündeki işlenene uygulanamaz + + + Cannot implicitly convert type '{0}' to '{1}' + '{0}' türü örtülü olarak '{1}' türüne dönüştürülemez + + + Cannot convert type '{0}' to '{1}' + '{0}' türü '{1}' olarak dönüştürülemiyor. + + + Constant value '{0}' cannot be converted to a '{1}' + '{0}' sabit değeri bir '{1}' değerine dönüştürülemez. + + + Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' + '{0}' işleci, '{1}' ve '{2}' türündeki işlenenler üzerinde belirsizdir + + + Operator '{0}' is ambiguous on an operand of type '{1}' + '{0}' işleci, '{1}' türündeki bir işlenen üzerinde belirsizdir + + + Cannot convert null to '{0}' because it is a non-nullable value type + Bu null atanamaz bir değer türü olduğundan, null değeri '{0}' değerine dönüştürülemiyor + + + Cannot access a non-static member of outer type '{0}' via nested type '{1}' + '{0}' dış türündeki statik olmayan bir öğeye '{1}' iç içe türü yoluyla erişilemez + + + '{0}' does not contain a definition for '{1}' + '{0}' bir '{1}' tanımı içermiyor + + + An object reference is required for the non-static field, method, or property '{0}' + '{0}' statik olmayan alanı, yöntemi veya özelliği için nesne başvurusu gerekiyor + + + The call is ambiguous between the following methods or properties: '{0}' and '{1}' + Çağrı şu metot veya özellikler arasında belirsiz: '{0}' ve '{1}' + + + '{0}' is inaccessible due to its protection level + '{0}' koruma düzeyi nedeniyle erişilemez + + + No overload for '{0}' matches delegate '{1}' + '{0}' için hiçbir tekrar yükleme '{1}' temsilcisiyle eşleşmiyor + + + The left-hand side of an assignment must be a variable, property or indexer + Atamanın sol tarafı değişken, özellik veya dizin oluşturucu olmalıdır + + + The type '{0}' has no constructors defined + '{0}' türünün tanımlı bir oluşturucusu yok + + + The property or indexer '{0}' cannot be used in this context because it lacks the get accessor + '{0}' özelliği veya dizin erişimcisi, alma erişimcisi olmadığından bu bağlamda kullanılamaz + + + Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead + Örnek başvurusuyla '{0}' üyesine erişilemez; bunun yerine bir tür adıyla niteleyin + + + A readonly field cannot be assigned to (except in a constructor or a variable initializer) + Salt okunur bir alana atama yapılamaz (oluşturucu veya değişken başlatıcı içinde olması dışında) + + + A readonly field cannot be passed ref or out (except in a constructor) + Salt okunur bir alan ref veya out olarak geçirilemez (oluşturucu içinde olması dışında) + + + A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) + Statik salt okunur bir alana atama yapılamaz (statik oluşturucu veya değişken başlatıcı içinde olması dışında) + + + A static readonly field cannot be passed ref or out (except in a static constructor) + Statik salt okunur bir alan ref veya out olarak geçirilemez (statik oluşturucu içinde olması dışında) + + + Property or indexer '{0}' cannot be assigned to -- it is read only + '{0}' özelliğine veya dizin oluşturucusuna, salt okunur olduğu için atama yapılamaz + + + A property or indexer may not be passed as an out or ref parameter + Bir özellik veya dizin oluşturucu out veya ref parametresi olarak geçirilemez + + + Dynamic calls cannot be used in conjunction with pointers + Dinamik çağrılar işaretçilerle birlikte kullanılamaz. + + + In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters + Kısa devre işleci olarak uygulanabilmesi için, kullanıcı tanımlı bir mantıksal işleç ('{0}') 2 parametresiyle aynı dönüş türüne sahip olmalıdır + + + The type ('{0}') must contain declarations of operator true and operator false + Tür ('{0}') true işleci ve false işleci ile ilgili bildirimler içermelidir + + + Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) + '{0}' sabit değeri bir '{1}' değerine dönüştürülemez (geçersiz kılmak için 'unchecked' sözdizimini kullanın) + + + Ambiguity between '{0}' and '{1}' + '{0}' ve '{1}' arasında belirsizlik var + + + Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) + '{0}' türü örtülü olarak '{1}' türüne dönüştürülemez. Açık bir dönüştürme var (eksik atamanız mı var?) + + + The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible + Alma erişimcisine erişilemediğinden '{0}' özelliği veya dizin oluşturucusu bu bağlamda kullanılamaz + + + The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible + Ayarlama erişimcisine erişilemediğinden '{0}' özelliği veya dizin oluşturucusu bu bağlamda kullanılamaz + + + Using the generic {1} '{0}' requires '{2}' type arguments + Genel {1} '{0}' kullanımı '{2}' tür bağımsız değişkenlerini gerektirir + + + The {1} '{0}' cannot be used with type arguments + {1} '{0}' öğesi tür bağımsız değişkenleri ile kullanılamaz + + + The non-generic {1} '{0}' cannot be used with type arguments + Genel olmayan {1} '{0}' öğesi ´tür bağımsız değişkenleriyle kullanılamaz + + + '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' + '{2}' türünün, '{0}' genel türünde veya yönteminde '{1}' parametresi olarak kullanabilmesi için ortak bir parametresiz oluşturucu içeren soyut olmayan bir tür olması gerekir + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. + '{3}' türü, '{0}' genel türü veya yöntemi için '{2}' tür parametresi olarak kullanılamaz. '{3}' türünden '{1}' türüne örtük bir başvuru dönüştürmesi yoktur. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. + '{3}' türü '{0}' genel türü veya yöntemi için '{2}' tür parametresi olarak kullanılamaz. '{3}' null yapılabilir türü, '{1}' kısıtlamasını karşılamıyor. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. + '{3}' türü, '{0}' genel türü veya yöntemi için '{2}' tür parametresi olarak kullanılamaz. '{3}' null yapılabilir türü '{1}' kısıtlamasını karşılamıyor. Null olabilen türler hiçbir arabirim kısıtlamasını karşılamaz. + + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. + '{3}' türü, '{0}' genel türü veya yöntemi için '{2}' tür parametresi olarak kullanılamaz. '{3}' türünden '{1}' türüne paketleme dönüşümü yoktur. + + + The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. + '{0}' yönteminin tür bağımsız değişkenleri kullanımdan çıkarsanamıyor. Tür bağımsız değişkenlerini açık olarak belirtmeyi deneyin. + + + The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' + '{2}' türünün '{0}' genel türünde veya yönteminde '{1}' parametresi olarak kullanabilmesi için bir başvuru türü olması gerekir + + + The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' + '{2}' türünün '{0}' genel türünde veya yönteminde '{1}' parametresi olarak kullanabilmesi için null yapılamayan bir değer türü olması gerekir + + + Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' + '{2}' iken '{3}' olarak dönüştürülürken belirsiz kullanıcı tanımlı '{0}' ve '{1}' dönüşümleri yapıldı. + + + '{0}' is not supported by the language + '{0}' dil tarafından desteklenmiyor + + + '{0}': cannot explicitly call operator or accessor + '{0}': açıkça işleç veya erişimci çağıramaz + + + Cannot convert to static type '{0}' + '{0}' statik türüne dönüştürülemiyor + + + The operand of an increment or decrement operator must be a variable, property or indexer + Artırma veya azaltma işlecinin işleneni bir değişken, özellik veya dizin oluşturucu olmalıdır + + + No overload for method '{0}' takes '{1}' arguments + '{0}' metodu için hiçbir tekrar yükleme '{1}' bağımsız değişken almaz + + + The best overloaded method match for '{0}' has some invalid arguments + '{0}' ile en iyi eşleşen tekrar yüklenen metot bazı geçersiz bağımsız değişkenlere sahip + + + A ref or out argument must be an assignable variable + Bir ref veya out bağımsız değişkeni atanabilir değişken olmalıdır + + + Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) + '{0}' korunan üyesine '{1}' türündeki niteleyici kullanılarak erişilemez; niteleyici '{2}' türünde (veya bundan türetilmiş) olmalıdır + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' + '{0}' özelliği, dizin erişimcisi veya olayı dil tarafından desteklenmiyor; '{1}' veya '{2}' erişimci yöntemlerini doğrudan çağırmayı deneyin + + + Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' + '{0}' özelliği, dizin erişimcisi veya olayı dil tarafından desteklenmiyor; '{1}' erişimci yöntemini doğrudan çağırmayı deneyin + + + Delegate '{0}' does not take '{1}' arguments + '{0}' temsilcisi '{1}' bağımsız değişken almıyor + + + Delegate '{0}' has some invalid arguments + '{0}' temsilcisinde bazı geçersiz bağımsız değişkenler var + + + Cannot assign to '{0}' because it is read-only + Salt okunur olduğu için '{0}' öğesine atama yapılamaz + + + Cannot pass '{0}' as a ref or out argument because it is read-only + Salt okunur olduğundan '{0}' öğesinin alanları bir ref veya out bağımsız değişkeni olarak geçirilemez + + + Cannot modify the return value of '{0}' because it is not a variable + Bir değişken olmadığından '{0}' öğesinin dönüş değeri değiştirilemez + + + Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) + '{0}' salt okunur alanın üyeleri (oluşturucu veya değişken başlatıcı dışında) değiştirilemez + + + Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) + '{0}' salt okunur alanının üyeleri ref veya out olarak geçirilemez (oluşturucu içinde olması dışında) + + + Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) + '{0}' statik salt okunur alanının alanlarına (statik oluşturucu veya değişken başlatıcı dışında) atama yapılamaz + + + Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) + '{0}' statik salt okunur alanının alanları ref veya out olarak geçirilemez (statik oluşturucu içinde olması dışında) + + + Cannot assign to '{0}' because it is a '{1}' + '{1}' olduğu için '{0}' öğesine atama yapılamaz + + + Cannot pass '{0}' as a ref or out argument because it is a '{1}' + '{1}' olduğundan '{0}' öğesinin alanları bir ref veya out bağımsız değişkeni olarak geçirilemez + + + '{0}' does not contain a constructor that takes '{1}' arguments + '{0}', '{1}' bağımsız değişkenlerini alan bir oluşturucu içermiyor + + + Non-invocable member '{0}' cannot be used like a method. + Çağrılamayan '{0}' üyesi metot gibi kullanılamaz. + + + Named argument specifications must appear after all fixed arguments have been specified + Adlandırılan bağımsız değişken belirtimleri, tüm sabit bağımsız değişkenler belirtildikten sonra görünmelidir + + + The best overload for '{0}' does not have a parameter named '{1}' + '{0}' için en iyi yeniden yükleme, '{1}' adlı bir parametre içermiyor + + + The delegate '{0}' does not have a parameter named '{1}' + '{0}' temsilcisi '{1}' adlı bir parametre içermiyor + + + Named argument '{0}' cannot be specified multiple times + '{0}' adlandırılmış bağımsız değişkeni bir kereden fazla belirtilemez + + + Named argument '{0}' specifies a parameter for which a positional argument has already been given + '{0}' adlandırılmış bağımsız değişkeni konumsal bir bağımsız değişkenin zaten verildiği bir parametreyi belirtiyor + + + +
+
\ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hans.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hans.xlf index f4041b65f71..35d3cb2e78e 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hans.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hans.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,357 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - ç»‘å®šåŠ¨æ€æ“作时出现异常 + ç»‘å®šåŠ¨æ€æ“作时出现异常 Cannot bind call with no calling object - 无法在没有调用对象的情况下绑定调用 + 无法在没有调用对象的情况下绑定调用 Overload resolution failed - é‡è½½å†³ç­–失败 + é‡è½½å†³ç­–失败 Binary operators must be invoked with two arguments - å¿…é¡»ä½¿ç”¨ä¸¤ä¸ªå‚æ•°è°ƒç”¨äºŒå…ƒè¿ç®—符 + å¿…é¡»ä½¿ç”¨ä¸¤ä¸ªå‚æ•°è°ƒç”¨äºŒå…ƒè¿ç®—符 Unary operators must be invoked with one argument - å¿…é¡»ä½¿ç”¨ä¸€ä¸ªå‚æ•°è°ƒç”¨ä¸€å…ƒè¿ç®—符 + å¿…é¡»ä½¿ç”¨ä¸€ä¸ªå‚æ•°è°ƒç”¨ä¸€å…ƒè¿ç®—符 The name '{0}' is bound to a method and cannot be used like a property - å称“{0}â€å·²ç»‘定到æŸä¸ªæ–¹æ³•,无法åƒå±žæ€§ä¸€æ ·ä½¿ç”¨ + å称“{0}â€å·²ç»‘定到æŸä¸ªæ–¹æ³•,无法åƒå±žæ€§ä¸€æ ·ä½¿ç”¨ The event '{0}' can only appear on the left hand side of + - 事件 '{0}' åªèƒ½å‡ºçŽ°åœ¨å·¦è¾¹çš„ + + 事件 '{0}' åªèƒ½å‡ºçŽ°åœ¨ + 的左侧 Cannot invoke a non-delegate type - 无法调用éžå§”托类型 + 无法调用éžå§”托类型 Binary operators cannot be invoked with one argument - æ— æ³•ä½¿ç”¨ä¸€ä¸ªå‚æ•°è°ƒç”¨äºŒå…ƒè¿ç®—符 + æ— æ³•ä½¿ç”¨ä¸€ä¸ªå‚æ•°è°ƒç”¨äºŒå…ƒè¿ç®—符 Cannot perform member assignment on a null reference - 无法对 null 引用执行æˆå‘˜èµ‹å€¼ + 无法对 null 引用执行æˆå‘˜èµ‹å€¼ Cannot perform runtime binding on a null reference - 无法对 null 引用执行è¿è¡Œæ—¶ç»‘定 + 无法对 null 引用执行è¿è¡Œæ—¶ç»‘定 Cannot dynamically invoke method '{0}' because it has a Conditional attribute - 无法动æ€è°ƒç”¨æ–¹æ³•“{0}â€ï¼Œå› ä¸ºå®ƒå…·æœ‰ Conditional 特性 + 无法动æ€è°ƒç”¨æ–¹æ³•“{0}â€ï¼Œå› ä¸ºå®ƒå…·æœ‰ Conditional 特性 Cannot implicitly convert type 'void' to 'object' - 无法将类型“voidâ€éšå¼è½¬æ¢ä¸ºâ€œobject†+ 无法将类型“voidâ€éšå¼è½¬æ¢ä¸ºâ€œobject†Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - è¿ç®—符“{0}â€æ— æ³•应用于“{1}â€å’Œâ€œ{2}â€ç±»åž‹çš„æ“ä½œæ•° + è¿ç®—符“{0}â€æ— æ³•应用于“{1}â€å’Œâ€œ{2}â€ç±»åž‹çš„æ“ä½œæ•° Cannot apply indexing with [] to an expression of type '{0}' - 无法将带 [] 的索引应用于“{0}â€ç±»åž‹çš„è¡¨è¾¾å¼ + 无法将带 [] 的索引应用于“{0}â€ç±»åž‹çš„è¡¨è¾¾å¼ Wrong number of indices inside []; expected '{0}' - [] 内的索引数错误;应为“{0}†+ [] 内的索引数错误;应为“{0}†Operator '{0}' cannot be applied to operand of type '{1}' - è¿ç®—符“{0}â€æ— æ³•应用于“{1}â€ç±»åž‹çš„æ“ä½œæ•° + è¿ç®—符“{0}â€æ— æ³•应用于“{1}â€ç±»åž‹çš„æ“ä½œæ•° Cannot implicitly convert type '{0}' to '{1}' - 无法将类型“{0}â€éšå¼è½¬æ¢ä¸ºâ€œ{1}†+ 无法将类型“{0}â€éšå¼è½¬æ¢ä¸ºâ€œ{1}†Cannot convert type '{0}' to '{1}' - ä¸èƒ½è½¬æ¢ç±»åž‹ '{0}' 到 '{1}' + 无法将类型“{0}â€è½¬æ¢ä¸ºâ€œ{1}†Constant value '{0}' cannot be converted to a '{1}' - 常é‡å€¼â€œ{0}â€æ— æ³•转æ¢ä¸ºâ€œ{1}†+ 常é‡å€¼â€œ{0}â€æ— æ³•转æ¢ä¸ºâ€œ{1}†Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - è¿ç®—符“{0}â€å¯¹äºŽâ€œ{1}â€å’Œâ€œ{2}â€ç±»åž‹çš„æ“ä½œæ•°å…·æœ‰äºŒä¹‰æ€§ + è¿ç®—符“{0}â€å¯¹äºŽâ€œ{1}â€å’Œâ€œ{2}â€ç±»åž‹çš„æ“ä½œæ•°å…·æœ‰äºŒä¹‰æ€§ Operator '{0}' is ambiguous on an operand of type '{1}' - è¿ç®—符“{0}â€å¯¹äºŽâ€œ{1}â€ç±»åž‹çš„æ“ä½œæ•°å…·æœ‰äºŒä¹‰æ€§ + è¿ç®—符“{0}â€å¯¹äºŽâ€œ{1}â€ç±»åž‹çš„æ“ä½œæ•°å…·æœ‰äºŒä¹‰æ€§ Cannot convert null to '{0}' because it is a non-nullable value type - 无法将 null 转æ¢ä¸ºâ€œ{0}â€ï¼Œå› ä¸ºåŽè€…是ä¸å¯ä»¥ä¸º null 的值类型 + 无法将 null 转æ¢ä¸ºâ€œ{0}â€ï¼Œå› ä¸ºåŽè€…是ä¸å¯ä»¥ä¸º null 的值类型 Cannot access a non-static member of outer type '{0}' via nested type '{1}' - 无法通过嵌套类型“{1}â€æ¥è®¿é—®å¤–部类型“{0}â€çš„éžé™æ€æˆå‘˜ + 无法通过嵌套类型“{1}â€æ¥è®¿é—®å¤–部类型“{0}â€çš„éžé™æ€æˆå‘˜ '{0}' does not contain a definition for '{1}' - “{0}â€æœªåŒ…å«â€œ{1}â€çš„定义 + “{0}â€æœªåŒ…å«â€œ{1}â€çš„定义 An object reference is required for the non-static field, method, or property '{0}' - 对象引用对于éžé™æ€çš„å­—æ®µã€æ–¹æ³•或属性“{0}â€æ˜¯å¿…需的 + 对象引用对于éžé™æ€çš„å­—æ®µã€æ–¹æ³•或属性“{0}â€æ˜¯å¿…需的 The call is ambiguous between the following methods or properties: '{0}' and '{1}' - 以下方法或属性之间的调用具有二义性:“{0}â€å’Œâ€œ{1}†+ 以下方法或属性之间的调用具有二义性:“{0}â€å’Œâ€œ{1}†'{0}' is inaccessible due to its protection level - “{0}â€ä¸å¯è®¿é—®ï¼Œå› ä¸ºå®ƒå…·æœ‰ä¸€å®šçš„ä¿æŠ¤çº§åˆ« + “{0}â€ä¸å¯è®¿é—®ï¼Œå› ä¸ºå®ƒå…·æœ‰ä¸€å®šçš„ä¿æŠ¤çº§åˆ« No overload for '{0}' matches delegate '{1}' - “{0}â€æ²¡æœ‰ä¸Žå§”托“{1}â€åŒ¹é…çš„é‡è½½ + “{0}â€æ²¡æœ‰ä¸Žå§”托“{1}â€åŒ¹é…çš„é‡è½½ The left-hand side of an assignment must be a variable, property or indexer - 赋值å·å·¦è¾¹å¿…须是å˜é‡ã€å±žæ€§æˆ–索引器 + 赋值å·å·¦è¾¹å¿…须是å˜é‡ã€å±žæ€§æˆ–索引器 The type '{0}' has no constructors defined - 类型“{0}â€æœªå®šä¹‰æž„造函数 + 类型“{0}â€æœªå®šä¹‰æž„造函数 The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸ºå®ƒç¼ºå°‘ get 访问器 + 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸ºå®ƒç¼ºå°‘ get 访问器 Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - 无法使用实例引用æ¥è®¿é—®æˆå‘˜â€œ{0}â€ï¼›è¯·æ”¹ç”¨ç±»åž‹åæ¥é™å®šå®ƒ + 无法使用实例引用æ¥è®¿é—®æˆå‘˜â€œ{0}â€ï¼›è¯·æ”¹ç”¨ç±»åž‹åæ¥é™å®šå®ƒ A readonly field cannot be assigned to (except in a constructor or a variable initializer) - 无法对åªè¯»çš„字段赋值(构造函数或å˜é‡åˆå§‹å€¼æŒ‡å®šé¡¹ä¸­é™¤å¤–) + 无法对åªè¯»å­—段赋值(在构造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) A readonly field cannot be passed ref or out (except in a constructor) - 对åªè¯»å­—段无法传递 ref 或 out 傿•°(构造函数中除外) + 无法为åªè¯»å­—段传递 ref 或 out (在构造函数中除外) A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - æ— æ³•å¯¹é™æ€åªè¯»å­—段赋值(陿€æž„造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) + æ— æ³•å¯¹é™æ€åªè¯»å­—段赋值(åœ¨é™æ€æž„造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) A static readonly field cannot be passed ref or out (except in a static constructor) - 无法å‘陿€åªè¯»å­—段传递 ref 或 out 傿•°(陿€æž„造函数中除外) + æ— æ³•å°†é™æ€åªè¯»å­—段作为 ref 或 out 傿•°ä¼ é€’(åœ¨é™æ€æž„造函数中除外) Property or indexer '{0}' cannot be assigned to -- it is read only - 无法为属性或索引器“{0}â€èµ‹å€¼ - 它是åªè¯»çš„ + 无法为属性或索引器“{0}â€èµ‹å€¼ - 它是åªè¯»çš„ A property or indexer may not be passed as an out or ref parameter - 属性或索引器ä¸å¾—作为 out 或 ref 傿•°ä¼ é€’ + 属性或索引器ä¸èƒ½ä½œä¸º out 或 ref 傿•°ä¼ é€’ Dynamic calls cannot be used in conjunction with pointers - 动æ€è°ƒç”¨ä¸èƒ½ä¸ŽæŒ‡é’ˆä¸€èµ·ä½¿ç”¨ + 动æ€è°ƒç”¨ä¸èƒ½ä¸ŽæŒ‡é’ˆä¸€èµ·ä½¿ç”¨ In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - 为了å¯ä»¥åƒçŸ­è·¯è¿ç®—符一样应用,用户定义的逻辑è¿ç®—符(“{0}â€)çš„è¿”å›žç±»åž‹å¿…é¡»ä¸Žå®ƒçš„ä¸¤ä¸ªå‚æ•°çš„ç±»åž‹ç›¸åŒ + 为了å¯ä»¥åƒçŸ­è·¯è¿ç®—符一样应用,用户定义的逻辑è¿ç®—符(“{0}â€)çš„è¿”å›žç±»åž‹å¿…é¡»ä¸Žå®ƒçš„ä¸¤ä¸ªå‚æ•°çš„ç±»åž‹ç›¸åŒ The type ('{0}') must contain declarations of operator true and operator false - 类型(“{0}â€)必须包å«è¿ç®—符 True å’Œè¿ç®—符 False 的声明 + 类型(“{0}â€)必须包å«è¿ç®—符 True å’Œè¿ç®—符 False 的声明 Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - 常é‡å€¼â€œ{0}â€æ— æ³•转æ¢ä¸ºâ€œ{1}â€(使用“uncheckedâ€è¯­æ³•é‡å†™) + 常é‡å€¼â€œ{0}â€æ— æ³•转æ¢ä¸ºâ€œ{1}â€(使用“uncheckedâ€è¯­æ³•é‡å†™) Ambiguity between '{0}' and '{1}' - 在“{0}â€å’Œâ€œ{1}â€ä¹‹é—´å…·æœ‰äºŒä¹‰æ€§ - - - '{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf) - “{0}â€æ²¡æœ‰é¢„定义的大å°ï¼Œå› æ­¤ sizeof åªèƒ½åœ¨ä¸å®‰å…¨çš„上下文中使用(请考虑使用 System.Runtime.InteropServices.Marshal.SizeOf) + 在“{0}â€å’Œâ€œ{1}â€ä¹‹é—´å…·æœ‰äºŒä¹‰æ€§ Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - 无法将类型“{0}â€éšå¼è½¬æ¢ä¸ºâ€œ{1}â€ã€‚存在一个显å¼è½¬æ¢(是å¦ç¼ºå°‘强制转æ¢?) + 无法将类型“{0}â€éšå¼è½¬æ¢ä¸ºâ€œ{1}â€ã€‚存在一个显å¼è½¬æ¢(是å¦ç¼ºå°‘强制转æ¢?) The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸º get 访问器ä¸å¯è®¿é—® + 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸º get 访问器ä¸å¯è®¿é—® The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸º set 访问器ä¸å¯è®¿é—® + 属性或索引器“{0}â€ä¸èƒ½ç”¨åœ¨æ­¤ä¸Šä¸‹æ–‡ä¸­ï¼Œå› ä¸º set 访问器ä¸å¯è®¿é—® Using the generic {1} '{0}' requires '{2}' type arguments - 使用泛型 {1}“{0}â€éœ€è¦â€œ{2}â€ä¸ªç±»åž‹å‚æ•° + 使用泛型 {1}“{0}â€éœ€è¦â€œ{2}â€ä¸ªç±»åž‹å‚æ•° The {1} '{0}' cannot be used with type arguments - {1}“{0}â€ä¸èƒ½ä¸Žç±»åž‹å‚数一起使用 + {1}“{0}â€ä¸èƒ½ä¸Žç±»åž‹å‚数一起使用 The non-generic {1} '{0}' cannot be used with type arguments - éžæ³›åž‹ {1}“{0}â€ä¸èƒ½ä¸Žç±»åž‹å‚数一起使用 + éžæ³›åž‹ {1}“{0}â€ä¸èƒ½ä¸Žç±»åž‹å‚数一起使用 '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - “{2}â€å¿…é¡»æ˜¯å…·æœ‰å…¬å…±çš„æ— å‚æ•°æž„é€ å‡½æ•°çš„éžæŠ½è±¡ç±»åž‹ï¼Œæ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†+ “{2}â€å¿…é¡»æ˜¯å…·æœ‰å…¬å…±çš„æ— å‚æ•°æž„é€ å‡½æ•°çš„éžæŠ½è±¡ç±»åž‹ï¼Œæ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚没有从“{3}â€åˆ°â€œ{1}â€çš„éšå¼å¼•用转æ¢ã€‚ + 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚没有从“{3}â€åˆ°â€œ{1}â€çš„éšå¼å¼•用转æ¢ã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚å¯ä»¥ä¸º null 的类型“{3}â€ä¸æ»¡è¶³â€œ{1}â€çš„约æŸã€‚ + 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚å¯ä»¥ä¸º null 的类型“{3}â€ä¸æ»¡è¶³â€œ{1}â€çš„约æŸã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚å¯ä»¥ä¸º null 的类型“{3}â€ä¸æ»¡è¶³â€œ{1}â€çš„约æŸã€‚å¯ä»¥ä¸º null 的类型ä¸èƒ½æ»¡è¶³ä»»ä½•接å£çº¦æŸã€‚ + 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚å¯ä»¥ä¸º null 的类型“{3}â€ä¸æ»¡è¶³â€œ{1}â€çš„约æŸã€‚å¯ä»¥ä¸º null 的类型ä¸èƒ½æ»¡è¶³ä»»ä½•接å£çº¦æŸã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚没有从“{3}â€åˆ°â€œ{1}â€çš„装箱转æ¢ã€‚ + 类型“{3}â€ä¸èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„ç±»åž‹å‚æ•°â€œ{2}â€ã€‚没有从“{3}â€åˆ°â€œ{1}â€çš„装箱转æ¢ã€‚ The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - 无法从用法中推断出方法“{0}â€çš„ç±»åž‹å‚æ•°ã€‚请å°è¯•æ˜¾å¼æŒ‡å®šç±»åž‹å‚数。 + 无法从用法中推断出方法“{0}â€çš„ç±»åž‹å‚æ•°ã€‚请å°è¯•æ˜¾å¼æŒ‡å®šç±»åž‹å‚数。 The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - 类型“{2}â€å¿…须是引用类型æ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†+ 类型“{2}â€å¿…须是引用类型æ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - 类型“{2}â€å¿…须是ä¸å¯ä»¥ä¸º null 值的类型,æ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†+ 类型“{2}â€å¿…须是ä¸å¯ä»¥ä¸º null 值的类型,æ‰èƒ½ç”¨ä½œæ³›åž‹ç±»åž‹æˆ–方法“{0}â€ä¸­çš„傿•°â€œ{1}†Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - 从“{2}â€è½¬æ¢ä¸ºâ€œ{3}â€æ—¶ï¼Œç”¨æˆ·å®šä¹‰çš„转æ¢â€œ{0}â€å’Œâ€œ{1}â€å…·æœ‰äºŒä¹‰æ€§ + 从“{2}â€è½¬æ¢ä¸ºâ€œ{3}â€æ—¶ï¼Œç”¨æˆ·å®šä¹‰çš„转æ¢â€œ{0}â€å’Œâ€œ{1}â€å…·æœ‰äºŒä¹‰æ€§ '{0}' is not supported by the language - çŽ°ç”¨è¯­è¨€ä¸æ”¯æŒâ€œ{0}†+ çŽ°ç”¨è¯­è¨€ä¸æ”¯æŒâ€œ{0}†'{0}': cannot explicitly call operator or accessor - “{0}â€: 无法显å¼è°ƒç”¨è¿ç®—符或访问器 + “{0}â€: 无法显å¼è°ƒç”¨è¿ç®—符或访问器 Cannot convert to static type '{0}' - 无法转æ¢ä¸ºé™æ€ç±»åž‹â€œ{0}†+ 无法转æ¢ä¸ºé™æ€ç±»åž‹â€œ{0}†The operand of an increment or decrement operator must be a variable, property or indexer - 递增或递å‡è¿ç®—符的æ“作数必须是å˜é‡ã€å±žæ€§æˆ–索引器 + å¢žé‡æˆ–å‡é‡è¿ç®—符的æ“作数必须为å˜é‡ã€å±žæ€§æˆ–索引器 No overload for method '{0}' takes '{1}' arguments - “{0}â€æ–¹æ³•没有采用“{1}â€ä¸ªå‚æ•°çš„é‡è½½ + “{0}â€æ–¹æ³•没有采用“{1}â€ä¸ªå‚æ•°çš„é‡è½½ The best overloaded method match for '{0}' has some invalid arguments - 与“{0}â€æœ€åŒ¹é…çš„é‡è½½æ–¹æ³•å…·æœ‰ä¸€äº›æ— æ•ˆå‚æ•° + 与“{0}â€æœ€åŒ¹é…çš„é‡è½½æ–¹æ³•å…·æœ‰ä¸€äº›æ— æ•ˆå‚æ•° A ref or out argument must be an assignable variable - ref 或 out 傿•°å¿…须是å¯ä»¥èµ‹å€¼çš„å˜é‡ + ref 或 out 傿•°å¿…须是å¯èµ‹å€¼çš„å˜é‡ Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - 无法通过“{1}â€ç±»åž‹çš„é™å®šç¬¦è®¿é—®å—ä¿æŠ¤çš„æˆå‘˜â€œ{0}â€ï¼›é™å®šç¬¦å¿…须是“{2}â€ç±»åž‹(或者从该类型派生) + 无法通过“{1}â€ç±»åž‹çš„é™å®šç¬¦è®¿é—®å—ä¿æŠ¤çš„æˆå‘˜â€œ{0}â€ï¼›é™å®šç¬¦å¿…须是“{2}â€ç±»åž‹(或者从该类型派生) Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - 属性ã€ç´¢å¼•器或事件“{0}â€ä¸å—现用语言支æŒï¼›è¯·å°è¯•直接调用访问器方法“{1}â€æˆ–“{2}†+ 属性ã€ç´¢å¼•器或事件“{0}â€ä¸å—现用语言支æŒï¼›è¯·å°è¯•直接调用访问器方法“{1}â€æˆ–“{2}†Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - 属性ã€ç´¢å¼•器或事件“{0}â€ä¸å—现用语言支æŒï¼›è¯·å°è¯•直接调用访问器方法“{1}†+ 属性ã€ç´¢å¼•器或事件“{0}â€ä¸å—现用语言支æŒï¼›è¯·å°è¯•直接调用访问器方法“{1}†Delegate '{0}' does not take '{1}' arguments - 委托“{0}â€æœªé‡‡ç”¨â€œ{1}â€ä¸ªå‚æ•° + 委托“{0}â€æœªé‡‡ç”¨â€œ{1}â€ä¸ªå‚æ•° Delegate '{0}' has some invalid arguments - 委托“{0}â€æœ‰ä¸€äº›æ— æ•ˆå‚æ•° + 委托“{0}â€æœ‰ä¸€äº›æ— æ•ˆå‚æ•° Cannot assign to '{0}' because it is read-only - 无法为“{0}â€èµ‹å€¼ï¼Œå› ä¸ºå®ƒæ˜¯åªè¯»çš„ + 无法为“{0}â€èµ‹å€¼ï¼Œå› ä¸ºå®ƒæ˜¯åªè¯»çš„ Cannot pass '{0}' as a ref or out argument because it is read-only - 无法将“{0}â€ä½œä¸º ref 或 out 傿•°ä¼ é€’,因为它是åªè¯»çš„ + 无法将“{0}â€ä½œä¸º ref 或 out 傿•°ä¼ é€’,因为它是åªè¯»çš„ Cannot modify the return value of '{0}' because it is not a variable - 无法修改“{0}â€çš„è¿”å›žå€¼ï¼Œå› ä¸ºå®ƒä¸æ˜¯å˜é‡ + 无法修改“{0}â€çš„è¿”å›žå€¼ï¼Œå› ä¸ºå®ƒä¸æ˜¯å˜é‡ Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - 无法修改åªè¯»å­—段“{0}â€çš„æˆå‘˜(在构造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) + 无法修改åªè¯»å­—段“{0}â€çš„æˆå‘˜(在构造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - 无法为åªè¯»å­—段“{0}â€çš„æˆå‘˜ä¼ é€’ ref 或 out (在构造函数中除外) + 无法为åªè¯»å­—段“{0}â€çš„æˆå‘˜ä¼ é€’ ref 或 out (在构造函数中除外) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - æ— æ³•ä¸ºé™æ€åªè¯»å­—段“{0}â€çš„字段赋值(åœ¨é™æ€æž„造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) + æ— æ³•ä¸ºé™æ€åªè¯»å­—段“{0}â€çš„字段赋值(åœ¨é™æ€æž„造函数或å˜é‡åˆå§‹å€¼è®¾å®šé¡¹ä¸­é™¤å¤–) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - æ— æ³•ä¸ºé™æ€åªè¯»å­—段“{0}â€çš„字段传递 ref 或 out (åœ¨é™æ€æž„造函数中除外) + æ— æ³•ä¸ºé™æ€åªè¯»å­—段“{0}â€çš„字段传递 ref 或 out (åœ¨é™æ€æž„造函数中除外) Cannot assign to '{0}' because it is a '{1}' - 无法为“{0}â€èµ‹å€¼ï¼Œå› ä¸ºå®ƒæ˜¯â€œ{1}†+ 无法为“{0}â€èµ‹å€¼ï¼Œå› ä¸ºå®ƒæ˜¯â€œ{1}†Cannot pass '{0}' as a ref or out argument because it is a '{1}' - “{0}â€æ˜¯ä¸€ä¸ªâ€œ{1}â€ï¼Œæ— æ³•作为 ref 或 out 傿•°è¿›è¡Œä¼ é€’ + “{0}â€æ˜¯ä¸€ä¸ªâ€œ{1}â€ï¼Œæ— æ³•作为 ref 或 out 傿•°è¿›è¡Œä¼ é€’ '{0}' does not contain a constructor that takes '{1}' arguments - “{0}â€ä¸åŒ…å«é‡‡ç”¨â€œ{1}â€ä¸ªå‚数的构造函数 + “{0}â€ä¸åŒ…å«é‡‡ç”¨â€œ{1}â€ä¸ªå‚数的构造函数 Non-invocable member '{0}' cannot be used like a method. - ä¸å¯è°ƒç”¨çš„æˆå‘˜â€œ{0}â€ä¸èƒ½åƒæ–¹æ³•一样使用。 + ä¸å¯è°ƒç”¨çš„æˆå‘˜â€œ{0}â€ä¸èƒ½åƒæ–¹æ³•一样使用。 Named argument specifications must appear after all fixed arguments have been specified - 命å傿•°è§„èŒƒå¿…é¡»å‡ºçŽ°åœ¨æ‰€æœ‰æŒ‡å®šçš„å›ºå®šå‚æ•°åŽ + åœ¨æ‰€æœ‰å›ºå®šå‚æ•°å‡å·²æŒ‡å®šåŽï¼Œå¿…须显示命å傿•°è§„范 The best overload for '{0}' does not have a parameter named '{1}' - “{0}â€çš„æœ€ä½³é‡è½½æ²¡æœ‰å为“{1}â€çš„傿•° + “{0}â€çš„æœ€ä½³é‡è½½æ²¡æœ‰å为“{1}â€çš„傿•° The delegate '{0}' does not have a parameter named '{1}' - 委托“{0}â€æ²¡æœ‰å为“{1}â€çš„傿•° + 委托“{0}â€æ²¡æœ‰å为“{1}â€çš„傿•° Named argument '{0}' cannot be specified multiple times - ä¸èƒ½å¤šæ¬¡æŒ‡å®šæ‰€å‘½åçš„å‚æ•°â€œ{0}â€ã€‚ + ä¸èƒ½å¤šæ¬¡æŒ‡å®šæ‰€å‘½åçš„å‚æ•°â€œ{0}†Named argument '{0}' specifies a parameter for which a positional argument has already been given - 命å实å‚“{0}â€æŒ‡å®šçš„å½¢å‚已被赋予ä½ç½®å®žå‚ + 命å实å‚“{0}â€æŒ‡å®šçš„å½¢å‚已被赋予ä½ç½®å®žå‚ - + \ No newline at end of file diff --git a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hant.xlf b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hant.xlf index 54fd05e4457..a573a1ddc3b 100644 --- a/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hant.xlf +++ b/src/libraries/Microsoft.CSharp/src/MultilingualResources/Microsoft.CSharp.zh-Hant.xlf @@ -1,4 +1,5 @@ - + +
@@ -7,353 +8,353 @@ An unexpected exception occurred while binding a dynamic operation - 繫çµå‹•態作業時發生æ„å¤–çš„ä¾‹å¤–ç‹€æ³ + 繫çµå‹•態作業時發生æ„å¤–çš„ä¾‹å¤–ç‹€æ³ Cannot bind call with no calling object - ç„¡æ³•ç¹«çµæ²’有呼å«ç‰©ä»¶çš„å‘¼å« + ç„¡æ³•ç¹«çµæ²’有呼å«ç‰©ä»¶çš„å‘¼å« Overload resolution failed - 多載解æžå¤±æ•— + 多載解æžå¤±æ•— Binary operators must be invoked with two arguments - 必須以兩個引數å«ç”¨äºŒå…ƒé‹ç®—å­ + 必須以兩個引數å«ç”¨äºŒå…ƒé‹ç®—å­ Unary operators must be invoked with one argument - 必須以一個引數å«ç”¨ä¸€å…ƒé‹ç®—å­ + 必須以一個引數å«ç”¨ä¸€å…ƒé‹ç®—å­ The name '{0}' is bound to a method and cannot be used like a property - å稱 '{0}' 已繫çµè‡³ä¸€å€‹æ–¹æ³•,因此ä¸èƒ½ç•¶æˆå±¬æ€§ä½¿ç”¨ + å稱 '{0}' 已繫çµè‡³ä¸€å€‹æ–¹æ³•,因此ä¸èƒ½ç•¶æˆå±¬æ€§ä½¿ç”¨ The event '{0}' can only appear on the left hand side of + - 事件 '{0}' åªèƒ½å‡ºç¾åœ¨å·¦é‚Šçš„ + + 事件 '{0}' åªèƒ½å‡ºç¾åœ¨ + 的左邊 Cannot invoke a non-delegate type - 無法å«ç”¨éžå§”派型別 + 無法å«ç”¨éžå§”派類型 Binary operators cannot be invoked with one argument - 無法以一個引數å«ç”¨äºŒå…ƒé‹ç®—å­ + 無法以一個引數å«ç”¨äºŒå…ƒé‹ç®—å­ Cannot perform member assignment on a null reference - ç„¡æ³•å° null åƒè€ƒé€²è¡Œæˆå“¡æŒ‡æ´¾ + ç„¡æ³•å° null åƒè€ƒé€²è¡Œæˆå“¡æŒ‡æ´¾ Cannot perform runtime binding on a null reference - ç„¡æ³•å° null åƒè€ƒé€²è¡ŒåŸ·è¡ŒéšŽæ®µç¹«çµ + ç„¡æ³•å° null åƒè€ƒé€²è¡ŒåŸ·è¡ŒéšŽæ®µç¹«çµ Cannot dynamically invoke method '{0}' because it has a Conditional attribute - 無法以動態方å¼å«ç”¨æ–¹æ³• '{0}',因為它有 Conditional 屬性 + 無法以動態方å¼å«ç”¨æ–¹æ³• '{0}',因為它有 Conditional 屬性 Cannot implicitly convert type 'void' to 'object' - 無法將 'void' 型別隱å«è½‰æ›ç‚º 'object' 型別 + 無法將 'void' 類型隱å«è½‰æ›ç‚º 'object' 類型 Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' - 無法將é‹ç®—å­ '{0}' 套用至型別 '{1}' å’Œ '{2}' çš„é‹ç®—å…ƒ + 無法將é‹ç®—å­ '{0}' 套用至類型 '{1}' å’Œ '{2}' çš„é‹ç®—å…ƒ Cannot apply indexing with [] to an expression of type '{0}' - 無法套用有 [] 的索引至型別 '{0}' çš„é‹ç®—å¼ + 無法套用有 [] 的索引至類型 '{0}' çš„é‹ç®—å¼ Wrong number of indices inside []; expected '{0}' - [] 內的索引數目錯誤; 必須是 '{0}' + [] 內的索引數目錯誤; 必須是 '{0}' Operator '{0}' cannot be applied to operand of type '{1}' - 無法將é‹ç®—å­ '{0}' 套用至型別 '{1}' çš„é‹ç®—å…ƒ + 無法將é‹ç®—å­ '{0}' 套用至類型 '{1}' çš„é‹ç®—å…ƒ Cannot implicitly convert type '{0}' to '{1}' - 無法將型別 '{0}' éš±å«è½‰æ›ç‚º '{1}' + 無法將類型 '{0}' éš±å«è½‰æ›ç‚º '{1}' Cannot convert type '{0}' to '{1}' - ä¸èƒ½è½‰æ›é¡žåž‹ '{0}' 到 '{1}' + 無法將類型 '{0}' 轉æ›ç‚º '{1}' Constant value '{0}' cannot be converted to a '{1}' - 無法將常數值 '{0}' 轉æ›ç‚º '{1}' + 無法將常數值 '{0}' 轉æ›ç‚º '{1}' Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' - é‹ç®—å­ '{0}' 用在型別 '{1}' å’Œ '{2}' çš„é‹ç®—元上時其æ„ç¾©æœƒæ¨¡ç¨œå…©å¯ + é‹ç®—å­ '{0}' 用在類型 '{1}' å’Œ '{2}' çš„é‹ç®—元上時其æ„ç¾©æœƒæ¨¡ç¨œå…©å¯ Operator '{0}' is ambiguous on an operand of type '{1}' - é‹ç®—å­ '{0}' 用在型別 '{1}' çš„é‹ç®—元上時其æ„ç¾©æœƒæ¨¡ç¨œå…©å¯ + é‹ç®—å­ '{0}' 用在類型 '{1}' çš„é‹ç®—元上時其æ„ç¾©æœƒæ¨¡ç¨œå…©å¯ Cannot convert null to '{0}' because it is a non-nullable value type - 無法將 null è½‰æ›æˆ '{0}',因為它是ä¸å¯ç‚º null 的實值型別 + 無法將 null è½‰æ›æˆ '{0}',因為它是ä¸å¯ç‚º null 的實值類型 Cannot access a non-static member of outer type '{0}' via nested type '{1}' - 無法é€éŽå·¢ç‹€åž‹åˆ¥ '{1}' å­˜å–外部型別 '{0}' çš„éžéœæ…‹æˆå“¡ + 無法é€éŽå·¢ç‹€é¡žåž‹ '{1}' å­˜å–外部類型 '{0}' çš„éžéœæ…‹æˆå“¡ '{0}' does not contain a definition for '{1}' - '{0}' ä¸åŒ…å« '{1}' 的定義 + '{0}' ä¸åŒ…å« '{1}' 的定義 An object reference is required for the non-static field, method, or property '{0}' - éœ€è¦æœ‰ç‰©ä»¶åƒè€ƒæ‰èƒ½ä½¿ç”¨éžéœæ…‹æ¬„ä½ã€æ–¹æ³•或屬性 '{0}' + éœ€è¦æœ‰ç‰©ä»¶åƒè€ƒï¼Œæ‰å¯ä½¿ç”¨éžéœæ…‹æ¬„ä½ã€æ–¹æ³•或屬性 '{0}' The call is ambiguous between the following methods or properties: '{0}' and '{1}' - åœ¨ä¸‹åˆ—æ–¹æ³•æˆ–å±¬æ€§ä¹‹é–“çš„å‘¼å«æ¨¡ç¨œå…©å¯: '{0}' å’Œ '{1}' + åœ¨ä¸‹åˆ—æ–¹æ³•æˆ–å±¬æ€§ä¹‹é–“çš„å‘¼å«æ¨¡ç¨œå…©å¯: '{0}' å’Œ '{1}' '{0}' is inaccessible due to its protection level - '{0}' çš„ä¿è­·å±¤ç´šå°Žè‡´ç„¡æ³•å°å…¶é€²è¡Œå­˜å– + '{0}' 由於其ä¿è­·å±¤ç´šä¹‹æ•…ï¼Œæ‰€ä»¥ç„¡æ³•å­˜å– No overload for '{0}' matches delegate '{1}' - '{0}' æ²’æœ‰ä»»ä½•å¤šè¼‰ç¬¦åˆ '{1}' 委派 + '{0}' 沒有任何多載符åˆå§”æ´¾ '{1}' The left-hand side of an assignment must be a variable, property or indexer - 指派的左方必須為變數ã€å±¬æ€§æˆ–ç´¢å¼•å­ + 指派的左å´å¿…須是變數ã€å±¬æ€§æˆ–ç´¢å¼•å­ The type '{0}' has no constructors defined - 型別 '{0}' æ²’æœ‰å·²å®šç¾©çš„å»ºæ§‹å‡½å¼ + 類型 '{0}' æ²’æœ‰å·²å®šç¾©çš„å»ºæ§‹å‡½å¼ The property or indexer '{0}' cannot be used in this context because it lacks the get accessor - ç„¡æ³•æ–¼æ­¤å…§å®¹ä¸­ä½¿ç”¨å±¬æ€§æˆ–ç´¢å¼•å­ '{0}',因為其欠缺 get å­˜å–å­ + å±¬æ€§æˆ–ç´¢å¼•å­ '{0}' 無法用在此內容中,因為它缺少 get å­˜å–å­ Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead - æˆå“¡ '{0}' 無法以執行個體åƒè€ƒå­˜å–; 請使用型別å稱代替 + æˆå“¡ '{0}' 無法以執行個體åƒè€ƒé€²è¡Œå­˜å–; 請改用類型å稱 A readonly field cannot be assigned to (except in a constructor or a variable initializer) - ç„¡æ³•æŒ‡å®šå”¯è®€æ¬„ä½ (除éžåœ¨å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) + ç„¡æ³•æŒ‡å®šå”¯è®€æ¬„ä½ (除éžåœ¨å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) A readonly field cannot be passed ref or out (except in a constructor) - ref 或 out 無法傳éžå”¯è®€æ¬„ä½ (除éžåœ¨å»ºæ§‹å‡½å¼) + ref 或 out 無法傳éžå”¯è®€æ¬„ä½ (除éžåœ¨å»ºæ§‹å‡½å¼) A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - ç„¡æ³•æŒ‡å®šéœæ…‹å”¯è®€æ¬„ä½ (除éžåœ¨éœæ…‹å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) + ç„¡æ³•æŒ‡å®šéœæ…‹å”¯è®€æ¬„ä½ (除éžåœ¨éœæ…‹å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) A static readonly field cannot be passed ref or out (except in a static constructor) - ref 或 out 無法傳éžéœæ…‹å”¯è®€æ¬„ä½ (除éžåœ¨éœæ…‹å»ºæ§‹å‡½å¼) + ref 或 out 無法傳éžéœæ…‹å”¯è®€æ¬„ä½ (除éžåœ¨éœæ…‹å»ºæ§‹å‡½å¼) Property or indexer '{0}' cannot be assigned to -- it is read only - ç„¡æ³•æŒ‡æ´¾å±¬æ€§æˆ–ç´¢å¼•å­ '{0}' -- 其為唯讀 + ç„¡æ³•æŒ‡æ´¾å±¬æ€§æˆ–ç´¢å¼•å­ '{0}' -- 其為唯讀 A property or indexer may not be passed as an out or ref parameter - 無法將屬性或索引å­ç•¶åš out 或 ref åƒæ•¸å‚³éž + å¯èƒ½ç„¡æ³•將屬性或索引å­ç•¶åš out 或 ref åƒæ•¸å‚³éž + + + Dynamic calls cannot be used in conjunction with pointers + 動態呼å«ä¸èƒ½èˆ‡æŒ‡æ¨™ä¸€èµ·ä½¿ç”¨ In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters - 為了å¯ä»¥ç•¶æˆæœ€å°‘é‹ç®— (Short Circuit) é‹ç®—å­ä½¿ç”¨ï¼Œä½¿ç”¨è€…定義的é‚輯é‹ç®—å­ ('{0}') 其傳回型別必須與其 2 å€‹åƒæ•¸çš„åž‹åˆ¥ç›¸åŒ + 為了å¯ä»¥ç•¶æˆæœ€å°‘é‹ç®— (Short Circuit) é‹ç®—å­ä½¿ç”¨ï¼Œä½¿ç”¨è€…定義的é‚輯é‹ç®—å­ ('{0}') 其傳回類型必須與其 2 å€‹åƒæ•¸çš„é¡žåž‹ç›¸åŒ The type ('{0}') must contain declarations of operator true and operator false - 型別 ('{0}') 必須包å«é‹ç®—å­ true å’Œé‹ç®—å­ false 的宣告 + 類型 ('{0}') 必須包å«é‹ç®—å­ true å’Œé‹ç®—å­ false 的宣告 Constant value '{0}' cannot be converted to a '{1}' (use 'unchecked' syntax to override) - 無法將常數值 '{0}' 轉æ›ç‚º '{1}' (請使用 'unchecked' 語法覆寫) + 無法將常數值 '{0}' 轉æ›ç‚º '{1}' (請使用 'unchecked' 語法覆寫) Ambiguity between '{0}' and '{1}' - '{0}' å’Œ '{1}' ä¹‹é–“æ¨¡ç¨œå…©å¯ - - - '{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf) - '{0}' 沒有é å…ˆå®šç¾©çš„大å°ï¼Œå› æ­¤ sizeof åªèƒ½ä½¿ç”¨æ–¼ unsafe 內容 (å¯è€ƒæ…®ä½¿ç”¨ System.Runtime.InteropServices.Marshal.SizeOf) + '{0}' 與 '{1}' ä¹‹é–“æ¨¡ç¨œå…©å¯ Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - 無法將型別 '{0}' éš±å«è½‰æ›ç‚º '{1}'。已有明確轉æ›å­˜åœ¨ (æ‚¨æ˜¯å¦æ¼æŽ‰äº†è½‰åž‹?) + 無法將類型 '{0}' éš±å«è½‰æ›ç‚º '{1}'。已有明確轉æ›å­˜åœ¨ (æ‚¨æ˜¯å¦æ¼æŽ‰äº†è½‰åž‹?) The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible - 這個內容中ä¸èƒ½ä½¿ç”¨å±¬æ€§æˆ–ç´¢å¼•å­ '{0}'ï¼Œå› ç‚ºç„¡æ³•å­˜å– get å­˜å–å­ + ç„¡æ³•åœ¨æ­¤å…§å®¹ä¸­ä½¿ç”¨å±¬æ€§æˆ–ç´¢å¼•å­ '{0}'ï¼Œå› ç‚ºç„¡æ³•å­˜å– get å­˜å–å­ The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible - 這個內容中ä¸èƒ½ä½¿ç”¨å±¬æ€§æˆ–ç´¢å¼•å­ '{0}'ï¼Œå› ç‚ºç„¡æ³•å­˜å– set å­˜å–å­ + ç„¡æ³•åœ¨æ­¤å…§å®¹ä¸­ä½¿ç”¨å±¬æ€§æˆ–ç´¢å¼•å­ '{0}'ï¼Œå› ç‚ºç„¡æ³•å­˜å– set å­˜å–å­ Using the generic {1} '{0}' requires '{2}' type arguments - 使用泛型 {1} '{0}' éœ€è¦ '{2}' 個型別引數 + 使用泛型 {1} '{0}' éœ€è¦ '{2}' 個類型引數 The {1} '{0}' cannot be used with type arguments - {1} '{0}' ä¸èƒ½é…åˆåž‹åˆ¥å¼•數使用 + {1} '{0}' ä¸èƒ½é…åˆé¡žåž‹å¼•數使用 The non-generic {1} '{0}' cannot be used with type arguments - éžæ³›åž‹ {1} '{0}' ä¸èƒ½é…åˆåž‹åˆ¥å¼•數使用 + éžæ³›åž‹ {1} '{0}' ä¸èƒ½é…åˆé¡žåž‹å¼•數使用 '{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}' - '{2}' å¿…é ˆæ˜¯å…·æœ‰å…¬ç”¨ç„¡åƒæ•¸å»ºæ§‹å‡½å¼çš„éžæŠ½è±¡åž‹åˆ¥ï¼Œæ‰èƒ½åœ¨æ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' 中åšç‚ºåƒæ•¸ '{1}' 使用 + '{2}' å¿…é ˆæ˜¯å…·æœ‰å…¬ç”¨ç„¡åƒæ•¸å»ºæ§‹å‡½å¼çš„éžæŠ½è±¡é¡žåž‹ï¼Œæ‰å¯åœ¨æ³›åž‹é¡žåž‹æˆ–方法 '{0}' 中用åšç‚ºåƒæ•¸ '{1}' The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no implicit reference conversion from '{3}' to '{1}'. - 型別 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' ä¸­çš„åž‹åˆ¥åƒæ•¸ '{2}'。沒有從 '{3}' 到 '{1}' 的隱å«åƒè€ƒè½‰æ›ã€‚ + 類型 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹é¡žåž‹æˆ–方法 '{0}' ä¸­çš„é¡žåž‹åƒæ•¸ '{2}'。沒有從 '{3}' 到 '{1}' 的隱å«åƒè€ƒè½‰æ›ã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. - 型別 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' ä¸­çš„åž‹åˆ¥åƒæ•¸ '{2}'。å¯ç‚º Null 的型別 '{3}' 未滿足 '{1}' çš„æ¢ä»¶ç´„æŸã€‚ + 類型 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹é¡žåž‹æˆ–方法 '{0}' ä¸­çš„é¡žåž‹åƒæ•¸ '{2}'。å¯ç‚º Null 的類型 '{3}' 未滿足 '{1}' çš„æ¢ä»¶ç´„æŸã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints. - 型別 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' ä¸­çš„åž‹åˆ¥åƒæ•¸ '{2}'。å¯ç‚º Null 的型別 '{3}' 未滿足 '{1}' çš„æ¢ä»¶ç´„æŸã€‚å¯ç‚º Null çš„åž‹åˆ¥ç„¡æ³•æ»¿è¶³æ‰€æœ‰ä»‹é¢æ¢ä»¶ç´„æŸã€‚ + 類型 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹é¡žåž‹æˆ–方法 '{0}' ä¸­çš„é¡žåž‹åƒæ•¸ '{2}'。å¯ç‚º Null 的類型 '{3}' 未滿足 '{1}' çš„æ¢ä»¶ç´„æŸã€‚å¯ç‚º Null çš„é¡žåž‹ç„¡æ³•æ»¿è¶³æ‰€æœ‰ä»‹é¢æ¢ä»¶ç´„æŸã€‚ The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'. - 型別 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' ä¸­çš„åž‹åˆ¥åƒæ•¸ '{2}'。沒有從 '{3}' 到 '{1}' çš„ Boxing 轉æ›ã€‚ + 類型 '{3}' ä¸èƒ½åšç‚ºæ³›åž‹é¡žåž‹æˆ–方法 '{0}' ä¸­çš„é¡žåž‹åƒæ•¸ '{2}'。沒有從 '{3}' 到 '{1}' çš„ Boxing 轉æ›ã€‚ The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly. - 方法 '{0}' 的型別引數ä¸èƒ½å¾žä½¿ç”¨æ–¹å¼æŽ¨æ–·ã€‚請嘗試明確指定型別引數。 + 方法 '{0}' 的類型引數ä¸èƒ½å¾žä½¿ç”¨æ–¹å¼æŽ¨æ–·ã€‚請嘗試明確指定類型引數。 The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}' - 型別 '{2}' 必須是åƒè€ƒåž‹åˆ¥ï¼Œæ‰èƒ½åœ¨æ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' 中åšç‚ºåƒæ•¸ '{1}' 使用 + 類型 '{2}' 必須是åƒè€ƒé¡žåž‹ï¼Œæ‰èƒ½åœ¨æ³›åž‹é¡žåž‹æˆ–方法 '{0}' 中åšç‚ºåƒæ•¸ '{1}' 使用 The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}' - 型別 '{2}' 必須是ä¸å¯ç‚º null 的實值型別,æ‰èƒ½åœ¨æ³›åž‹åž‹åˆ¥æˆ–方法 '{0}' 中åšç‚ºåƒæ•¸ '{1}' 使用 + 類型 '{2}' 必須是ä¸å¯ç‚º null 的實值類型,æ‰èƒ½åœ¨æ³›åž‹é¡žåž‹æˆ–方法 '{0}' 中åšç‚ºåƒæ•¸ '{1}' 使用 Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}' - 從 '{2}' 轉æ›ç‚º '{3}' æ™‚ï¼Œç™¼ç¾æ¨¡ç¨œå…©å¯çš„ä½¿ç”¨è€…å®šç¾©è½‰æ› '{0}' å’Œ '{1}' + 從 '{2}' è½‰æ›æˆ '{3}' æ™‚ï¼Œä½¿ç”¨è€…å®šç¾©çš„è½‰æ› '{0}' 與 '{1}' æ¨¡ç¨œå…©å¯ '{0}' is not supported by the language - æ­¤èªžè¨€ä¸æ”¯æ´ '{0}' + æ­¤èªžè¨€ä¸æ”¯æ´ '{0}' '{0}': cannot explicitly call operator or accessor - '{0}': 無法明確呼å«é‹ç®—å­æˆ–å­˜å–å­ + '{0}': 無法明確呼å«é‹ç®—å­æˆ–å­˜å–å­ Cannot convert to static type '{0}' - 無法轉æ›ç‚ºéœæ…‹åž‹åˆ¥ '{0}' + 無法轉æ›ç‚ºéœæ…‹é¡žåž‹ '{0}' The operand of an increment or decrement operator must be a variable, property or indexer - éžå¢žæˆ–éžæ¸›é‹ç®—å­çš„é‹ç®—元必須是變數ã€å±¬æ€§æˆ–索引å­ã€‚ + éžå¢žæˆ–éžæ¸›é‹ç®—å­çš„é‹ç®—元必須是變數ã€å±¬æ€§æˆ–ç´¢å¼•å­ No overload for method '{0}' takes '{1}' arguments - 方法 '{0}' æ²’æœ‰ä»»ä½•å¤šè¼‰æŽ¥å— '{1}' 個引數 + 方法 '{0}' æ²’æœ‰ä»»ä½•å¤šè¼‰æŽ¥å— '{1}' 個引數 The best overloaded method match for '{0}' has some invalid arguments - 最符åˆçš„多載方法 '{0}' 有一些無效的引數 + 最符åˆçš„多載方法 '{0}' 有一些無效的引數 A ref or out argument must be an assignable variable - ref 或 out å¼•æ•¸å¿…é ˆæ˜¯å¯æŒ‡æ´¾çš„變數 + ref 或 out å¼•æ•¸å¿…é ˆæ˜¯å¯æŒ‡æ´¾çš„變數 Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) - 無法經由型別 '{1}' çš„é™å®šè©žä¾†å­˜å–ä¿è­·çš„æˆå“¡ '{0}'; 必須經由型別 '{2}' (或從其è¡ç”Ÿçš„型別) çš„é™å®šè©ž + 無法經由類型 '{1}' çš„é™å®šè©žä¾†å­˜å–ä¿è­·çš„æˆå“¡ '{0}'; 必須經由類型 '{2}' (或從其è¡ç”Ÿçš„類型) çš„é™å®šè©ž Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}' - æ­¤èªžè¨€ä¸æ”¯æ´å±¬æ€§ã€ç´¢å¼•å­æˆ–事件 '{0}'; 請試著直接呼å«å­˜å–å­æ–¹æ³• '{1}' 或 '{2}' + æ­¤èªžè¨€ä¸æ”¯æ´å±¬æ€§ã€ç´¢å¼•å­æˆ–事件 '{0}'; 請嘗試直接呼å«å­˜å–å­æ–¹æ³• '{1}' 或 '{2}' Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}' - æ­¤èªžè¨€ä¸æ”¯æ´å±¬æ€§ã€ç´¢å¼•å­æˆ–事件 '{0}'; 請試著直接呼å«å­˜å–å­æ–¹æ³• '{1}' + æ­¤èªžè¨€ä¸æ”¯æ´å±¬æ€§ã€ç´¢å¼•å­æˆ–事件 '{0}'; 請嘗試直接呼å«å­˜å–å­æ–¹æ³• '{1}' Delegate '{0}' does not take '{1}' arguments - 委派 '{0}' ä¸æŽ¥å— '{1}' 個引數 + 委派 '{0}' ä¸æŽ¥å— '{1}' 個引數 Delegate '{0}' has some invalid arguments - 委派 '{0}' 有一些無效的引數 + 委派 '{0}' 有一些無效的引數 Cannot assign to '{0}' because it is read-only - 無法指派給 '{0}',因為它是唯讀的 + 無法指派給 '{0}',因為其為唯讀 Cannot pass '{0}' as a ref or out argument because it is read-only - 無法將 '{0}' ç•¶åš ref 或 out 引數傳éžï¼Œå› ç‚ºå®ƒæ˜¯å”¯è®€çš„ + 無法將 '{0}' ç•¶åš ref 或 out 引數傳éžï¼Œå› ç‚ºå®ƒæ˜¯å”¯è®€çš„ Cannot modify the return value of '{0}' because it is not a variable - 無法修改 '{0}' çš„å‚³å›žå€¼ï¼Œå› ç‚ºå®ƒä¸æ˜¯è®Šæ•¸ + 無法修改 '{0}' 的傳回值,因為其éžè®Šæ•¸ Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - å”¯è®€æ¬„ä½ '{0}' çš„æˆå“¡ç„¡æ³•修改 (é™¤éžæ˜¯åœ¨å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) + å”¯è®€æ¬„ä½ '{0}' çš„æˆå“¡ç„¡æ³•修改 (é™¤éžæ˜¯åœ¨å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - ref 或 out 無法傳éžå”¯è®€æ¬„ä½ '{0}' çš„æˆå“¡ (é™¤éžæ˜¯åœ¨å»ºæ§‹å‡½å¼ä¸­) + ref 或 out 無法傳éžå”¯è®€æ¬„ä½ '{0}' çš„æˆå“¡ (é™¤éžæ˜¯åœ¨å»ºæ§‹å‡½å¼ä¸­) Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - ç„¡æ³•æŒ‡æ´¾éœæ…‹å”¯è®€æ¬„ä½ '{0}' çš„æ¬„ä½ (é™¤éžæ˜¯åœ¨éœæ…‹å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) + ç„¡æ³•æŒ‡æ´¾éœæ…‹å”¯è®€æ¬„ä½ '{0}' çš„æ¬„ä½ (é™¤éžæ˜¯åœ¨éœæ…‹å»ºæ§‹å‡½å¼æˆ–變數åˆå§‹è¨­å®šå¼) Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - ref 或 out 無法傳éžéœæ…‹å”¯è®€æ¬„ä½ '{0}' çš„æ¬„ä½ (é™¤éžæ˜¯åœ¨éœæ…‹å»ºæ§‹å‡½å¼) + ref 或 out 無法傳éžéœæ…‹å”¯è®€æ¬„ä½ '{0}' çš„æ¬„ä½ (é™¤éžæ˜¯åœ¨éœæ…‹å»ºæ§‹å‡½å¼) Cannot assign to '{0}' because it is a '{1}' - 無法指派給 '{0}',因為它是 '{1}' + 無法指派給 '{0}',因為它是 '{1}' Cannot pass '{0}' as a ref or out argument because it is a '{1}' - 無法將 '{0}' ç•¶åš ref 或 out 引數傳éžï¼Œå› ç‚ºå®ƒæ˜¯ '{1}' + 無法將 '{0}' ç•¶åš ref 或 out 引數傳éžï¼Œå› ç‚ºå®ƒæ˜¯ '{1}' '{0}' does not contain a constructor that takes '{1}' arguments - '{0}' ä¸åŒ…å«æŽ¥å— '{1}' å€‹å¼•æ•¸çš„å»ºæ§‹å‡½å¼ + '{0}' ä¸åŒ…å«æŽ¥å— '{1}' å€‹å¼•æ•¸çš„å»ºæ§‹å‡½å¼ Non-invocable member '{0}' cannot be used like a method. - éžå¯å«ç”¨ (Non-invocable) æˆå“¡ '{0}' ä¸èƒ½åšç‚ºæ–¹æ³•使用。 + éžå¯å«ç”¨ (Non-invocable) æˆå“¡ '{0}' ä¸èƒ½åšç‚ºæ–¹æ³•使用。 Named argument specifications must appear after all fixed arguments have been specified - å…·åå¼•æ•¸è¦æ ¼å¿…須出ç¾åœ¨æ‰€æœ‰å·²æŒ‡å®šçš„固定引數之後 + å…·åå¼•æ•¸è¦æ ¼å¿…須出ç¾åœ¨æ‰€æœ‰å·²æŒ‡å®šçš„固定引數之後 The best overload for '{0}' does not have a parameter named '{1}' - '{0}' 的最佳多載沒有一個å為 '{1}' çš„åƒæ•¸ + æœ€ç¬¦åˆ '{0}' 的多載,沒有å稱為 '{1}' çš„åƒæ•¸ The delegate '{0}' does not have a parameter named '{1}' - 委派 '{0}' 沒有一個å為 '{1}' çš„åƒæ•¸ + 委派 '{0}' 沒有一個å為 '{1}' çš„åƒæ•¸ Named argument '{0}' cannot be specified multiple times - å…·å引數 '{0}' ä¸å¯ä»¥å¤šæ¬¡æŒ‡å®š + å…·å引數 '{0}' ä¸å¯ä»¥å¤šæ¬¡æŒ‡å®š Named argument '{0}' specifies a parameter for which a positional argument has already been given - å…·å引數 '{0}' 指定了已給定ä½ç½®å¼•æ•¸çš„åƒæ•¸ + å…·å引數 '{0}' 指定了已給定ä½ç½®å¼•æ•¸çš„åƒæ•¸ - + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf index 7e5140bd64f..807c06ba78e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + Na argument {0} se ve zprávÄ› o protokolování neodkazuje. Argument is not referenced from the logging message - Argument is not referenced from the logging message + Na argument se zpráva o protokolování neodkazuje Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + Generování více než 6 argumentů se nepodporuje. Can't have the same template with different casing - Can't have the same template with different casing + Nemůže mít stejnou Å¡ablonu s různým zápisem velkých a malých písmen. Logging method names cannot start with _ - Logging method names cannot start with _ + Názvy metod protokolování nemůžou zaÄínat podtržítkem (_). Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Názvy parametrů metody protokolování nemůžou zaÄínat podtržítkem (_). Logging methods cannot have a body - Logging methods cannot have a body + Metody protokolování nemůžou obsahovat tÄ›lo. Logging methods cannot be generic - Logging methods cannot be generic + Metody protokolování nemůžou být obecné. Logging methods must be partial - Logging methods must be partial + Metody protokolování musí být ÄásteÄné. Logging methods must return void - Logging methods must return void + Metody protokolování musí vracet void. Logging methods must be static - Logging methods must be static + Metody protokolování musí být statické. Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Nemůže mít chybnÄ› formátované Å™etÄ›zce (tÅ™eba neuzavÅ™ené závorky { atd.). A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Hodnota LogLevel musí být zadaná v atributu LoggerMessage, nebo jako parametr metody protokolování. One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Jeden z argumentů metody statického protokolování {0} musí implementovat rozhraní Microsoft.Extensions.Logging.ILogger. {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Jeden z argumentů metody statického protokolování musí implementovat rozhraní Microsoft.Extensions.Logging.ILogger. {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Ve třídÄ› {0} se nepovedlo najít pole typu Microsoft.Extensions.Logging.ILogger. {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Nepovedlo se najít pole typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Nepovedlo se najít definici pro typ {0}. Could not find a required type definition - Could not find a required type definition + Nepovedlo se najít požadovanou definici typu Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Ve třídÄ› {0} se naÅ¡lo nÄ›kolik polí typu Microsoft.Extensions.Logging.ILogger. {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + NaÅ¡lo se nÄ›kolik polí typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Odeberte redundantní kvalifikátor (Informace:, UpozornÄ›ní:, Chyba: atd.) ze zprávy o protokolování, protože je na zadané úrovni protokolu implicitní. Redundant qualifier in logging message - Redundant qualifier in logging message + Redundantní kvalifikátor ve zprávÄ› o protokolování Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Nezahrnovat parametry výjimek jako Å¡ablony do zprávy o protokolování Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Nezahrnovat do zprávy o protokolování Å¡ablonu pro {0}, protože se zpracovává implicitnÄ›. Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Nezahrnovat parametry úrovnÄ› protokolu jako Å¡ablony do zprávy o protokolování Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Nezahrnovat parametry protokolovacího nástroje jako Å¡ablony do zprávy o protokolování Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Více než jedna metoda protokolování používá ve třídÄ› {1} ID události {0}. Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Více než jedna metoda protokolování nemůže v rámci třídy používat stejné ID události. Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Å ablona {0} se neposkytuje jako argument pro metodu protokolování. Logging template has no corresponding method argument - Logging template has no corresponding method argument + Å ablona protokolování nemá žádný odpovídající argument metody diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf index c0b1d1b8165..f3be1d912ec 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + Auf das Argument „{0}“ wird in der Protokollierungsnachricht nicht verwiesen. Argument is not referenced from the logging message - Argument is not referenced from the logging message + Auf das Argument wird in der Protokollierungsmeldung nicht verwiesen Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + Das Generieren von mehr als 6 Argumenten wird nicht unterstützt. Can't have the same template with different casing - Can't have the same template with different casing + Die gleiche Vorlage darf nicht mit einer anderen Groß-/Kleinschreibung vorliegen. Logging method names cannot start with _ - Logging method names cannot start with _ + Namen für die Protokollierungsmethode dürfen nicht mit "_" beginnen. Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Parameternamen für die Protokollierungsmethode dürfen nicht mit "_" beginnen. Logging methods cannot have a body - Logging methods cannot have a body + Protokollierungsmethoden dürfen keinen Text enthalten. Logging methods cannot be generic - Logging methods cannot be generic + Protokollierungsmethoden können nicht generisch sein. Logging methods must be partial - Logging methods must be partial + Protokollierungsmethoden müssen partiell sein. Logging methods must return void - Logging methods must return void + Protokollierungsmethoden müssen leer zurückgegeben werden. Logging methods must be static - Logging methods must be static + Protokollierungsmethoden müssen statisch sein. Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Nicht wohlgeformte Formatzeichenfolgen (beispielsweise mit überzähligen geschweiften Klammern) sind unzulässig. A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Ein LogLevel-Wert muss im LoggerMessage-Attribut oder als Parameter für die Protokollierungsmethode angegeben werden. One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Eines der Argumente für die statische Protokollierungsmethode „{0}“ muss die Microsoft.Extensions.Logging.ILogger-Schnittstelle implementieren. {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Eines der Argumente für eine statische Protokollierungsmethode muss die Microsoft.Extensions.Logging.ILogger-Schnittstelle implementieren. {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + In der Klasse "{0}" wurde kein Feld vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden. {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Feld vom Typ "Microsoft.Extensions.Logging.ILogger" nicht gefunden {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Die Definition für den Typ "{0}" wurde nicht gefunden. Could not find a required type definition - Could not find a required type definition + Erforderliche Typdefinition nicht gefunden Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + In der Klasse "{0}" wurden mehrere Felder vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden. {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Mehrere Felder vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Entfernen Sie den redundanten Qualifizierer (z. B. "Info:", "Warnung:" oder "Fehler:") aus der Protokollierungsmeldung, weil er auf der angegebenen Protokollebene implizit enthalten ist. Redundant qualifier in logging message - Redundant qualifier in logging message + Redundanter Qualifizierer in Protokollierungsmeldung Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Ausnahmeparameter nicht als Vorlagen in die Protokollierungsmeldung einbeziehen Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Fügen Sie in der Protokollierungsmeldung keine Vorlage für "{0}" ein, weil dies implizit berücksichtigt wird. Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Protokolliergradparameter nicht als Vorlagen in die Protokollierungsmeldung einbeziehen Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Protokollierungsparameter nicht als Vorlagen in die Protokollierungsmeldung einbeziehen Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Die Ereignis-ID "{0}" wird von mehreren Protokollierungsmethoden in der Klasse "{1}" verwendet. Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Dieselbe Ereignis-ID kann nicht von mehreren Protokollierungsmethoden innerhalb einer Klasse verwendet werden Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Die Vorlage „{0}“ wird nicht als Argument für die Protokollierungsmethode bereitgestellt Logging template has no corresponding method argument - Logging template has no corresponding method argument + Protokollierungsvorlage weist kein entsprechendes Methodenargument auf diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf index f18fd61da78..897734cc7fc 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + No se hace referencia al argumento "{0}" en el mensaje de registro Argument is not referenced from the logging message - Argument is not referenced from the logging message + No se hace referencia al argumento en el mensaje de registro Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + No se admite la generación de más de 6 argumentos Can't have the same template with different casing - Can't have the same template with different casing + No se puede tener la misma plantilla con mayúsculas distintas Logging method names cannot start with _ - Logging method names cannot start with _ + Los nombres del método de registro no pueden empezar con _ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Los nombres de parámetro del método de registro no pueden empezar por _ Logging methods cannot have a body - Logging methods cannot have a body + Los métodos de registro no pueden tener cuerpo Logging methods cannot be generic - Logging methods cannot be generic + Los métodos de registro no pueden ser genéricos Logging methods must be partial - Logging methods must be partial + Los métodos de registro deben ser parciales Logging methods must return void - Logging methods must return void + Los métodos de registro deben devolver un valor vacío Logging methods must be static - Logging methods must be static + Los métodos de registro deben ser estáticos Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + No puede tener cadenas con formato incorrecto (como { final, etc.) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Debe proporcionarse un valor LogLevel en el atributo LoggerMessage o como parámetro en el método de registro One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Uno de los argumentos para el método de registro estático "{0}" debe implementar la interfaz Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Uno de los argumentos para un método de registro estático debe implementar la interfaz Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + No se encontró ningún campo de tipo Microsoft.Extensions.Logging.ILogger en la clase {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + No se encontró ningún campo de tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + No se pudo encontrar la definición para el tipo {0} Could not find a required type definition - Could not find a required type definition + No se encontró ninguna definición de tipo necesaria Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Se encontraron varios campos del tipo Microsoft.Extensions.Logging.ILogger en la clase {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Se encontraron varios campos de tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Quitar calificadores redundantes (Información:, Advertencia:, Error:, etc.) del mensaje de registro, ya que está implícito en el nivel de registro especificado. Redundant qualifier in logging message - Redundant qualifier in logging message + Calificador redundante en el mensaje de registro Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + No incluya parámetros de excepción como plantillas en el mensaje de registro Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + No incluya ninguna plantilla para {0} en el mensaje de registro, ya que está resuelto implícitamente Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + No incluya parámetros de nivel de registro como plantillas en el mensaje de registro Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + No incluya parámetros del registrador como plantillas en el mensaje de registro Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Varios métodos de registro usan el id. de evento {0} en la clase {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Varios métodos de registro no pueden usar un mismo id. de evento en una clase Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + La plantilla "{0}" no se proporciona como argumento para el método de registro Logging template has no corresponding method argument - Logging template has no corresponding method argument + La plantilla de registro no tiene ningún argumento de método correspondiente diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf index ae9e608c8c2..68a5c792f4c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + L’argument « {0} » n’est pas référencé à partir du message de journalisation Argument is not referenced from the logging message - Argument is not referenced from the logging message + L’argument n’est pas référencé à partir du message de journalisation Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + La génération de plus de 6 arguments n’est pas prise en charge Can't have the same template with different casing - Can't have the same template with different casing + Impossible d’avoir le même modèle avec une casse différente Logging method names cannot start with _ - Logging method names cannot start with _ + Les noms de méthode de journalisation ne peuvent pas commencer par _ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Les noms de paramètres de méthode de journalisation ne peuvent pas commencer par _ Logging methods cannot have a body - Logging methods cannot have a body + Les méthodes de journalisation ne peuvent pas avoir de corps Logging methods cannot be generic - Logging methods cannot be generic + Les méthodes de journalisation ne peuvent pas être génériques Logging methods must be partial - Logging methods must be partial + Les méthodes de journalisation doivent être partielles Logging methods must return void - Logging methods must return void + Les méthodes de journalisation doivent retourner void Logging methods must be static - Logging methods must be static + Les méthodes de journalisation doivent être statiques Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Chaînes de format incorrect (par exemple { non fermée, etc.) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Une valeur LogLevel doit être fournie dans l’attribut LoggerMessage ou en tant que paramètre de la méthode de journalisation One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + L’un des arguments d’une méthode de journalisation statique « {0} » doit implémenter l’interface Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + L’un des arguments d’une méthode de journalisation statique doit implémenter l’interface Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Impossible de trouver un champ de type Microsoft.Extensions.Logging.ILogger dans la classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Impossible de trouver un champ de type Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + La définition du type {0} n’a pas été trouvée Could not find a required type definition - Could not find a required type definition + Impossible de trouver la définition d’un type requis Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Plusieurs champs de type Microsoft.Extensions.Logging.ILogger ont été trouvés dans la classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Plusieurs champs de type Microsoft.Extensions.Logging.ILogger ont été trouvés {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Supprimez le qualificateur redondant (Info:, Warning:, Error:, etc.) du message de journalisation, car il est implicite dans le niveau de journalisation spécifié. Redundant qualifier in logging message - Redundant qualifier in logging message + Qualificateur redondant dans le message de journalisation Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Ne pas ajouter de paramètres d’exception comme modèles dans le message de journalisation Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Ne pas ajouter de modèle pour {0} dans le message de journalisation, car il est implicitement pris en charge Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Ne pas ajouter de paramètres de niveau de journalisation comme modèles dans le message de journalisation Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Ne pas ajouter de paramètres d’enregistreur de journalisation comme modèles dans le message de journalisation Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Plusieurs méthodes de journalisation utilisent l’ID d’événement {0} dans la classe {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Le même ID d’événement dans une classe ne peut pas être utilisé par plusieurs méthodes de journalisation Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Le modèle « {0} » n’est pas fourni comme argument de la méthode de journalisation Logging template has no corresponding method argument - Logging template has no corresponding method argument + Le modèle de journalisation n’a pas d’argument de méthode correspondant diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf index 241484f6356..3f856248592 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + Il messaggio di registrazione non fa riferimento all'argomento '{0}' Argument is not referenced from the logging message - Argument is not referenced from the logging message + Il messaggio di registrazione non fa riferimento all'argomento Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + La creazione di più di 6 argomenti non è supportata Can't have the same template with different casing - Can't have the same template with different casing + Impossibile avere lo stesso modello con un utilizzo di maiuscole e minuscole diverso Logging method names cannot start with _ - Logging method names cannot start with _ + I nomi dei metodi di registrazione non possono iniziare con _ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + I nomi dei parametri del metodo di registrazione non possono iniziare con _ Logging methods cannot have a body - Logging methods cannot have a body + I metodi di registrazione non possono avere un corpo Logging methods cannot be generic - Logging methods cannot be generic + I metodi di registrazione non possono essere generici Logging methods must be partial - Logging methods must be partial + I metodi di registrazione devono essere parziali Logging methods must return void - Logging methods must return void + I metodi di registrazione devono restituire void Logging methods must be static - Logging methods must be static + I metodi di registrazione devono essere statici Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Impossibile avere stringhe di formato non valido (ad esempio, { tralasciato e così via) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + È necessario specificare un valore LogLevel nell'attributo LoggerMessage o come parametro per il metodo di registrazione One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Uno degli argomenti del metodo di registrazione statico '{0}' deve implementare l'interfaccia Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Uno degli argomenti del metodo di registrazione statico deve implementare l'interfaccia Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Non è stato possibile trovare un campo di tipo Microsoft.Extensions.Logging.ILogger nella classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Non è stato possibile trovare un campo di tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Non è stato possibile trovare una definizione per il tipo {0} Could not find a required type definition - Could not find a required type definition + Non è stato possibile trovare una definizione del tipo richiesto Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Sono stati trovati più campi di tipo Microsoft.Extensions.Logging.ILogger nella classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Sono stati trovati più campi di tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Rimuovere il qualificatore ridondante (Informazioni:, Avviso:, Errore: e così via) dal messaggio di registrazione perché è implicito nel livello di log specificato. Redundant qualifier in logging message - Redundant qualifier in logging message + Qualificatore ridondante nel messaggio di registrazione Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Non includere i parametri di eccezione come modelli nel messaggio di registrazione Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Non includere un modello per {0} nel messaggio di registrazione perché è gestito in modo implicito Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Non includere i parametri del livello di log come modelli nel messaggio di registrazione Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Non includere i parametri del logger come modelli nel messaggio di registrazione Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Più metodi di registrazione stanno utilizzando l'ID evento {0} nella classe {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Più metodi di registrazione non possono utilizzare lo stesso ID evento all'interno di una classe Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Il modello ‘{0}’ non è specificato come argomento per il metodo di registrazione Logging template has no corresponding method argument - Logging template has no corresponding method argument + Il modello di registrazione non ha alcun argomento del metodo corrispondente diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf index 3854417a403..eab1acd521a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + 引数 '{0}' ã¯ãƒ­ã‚° メッセージã‹ã‚‰å‚ç…§ã•れã¦ã„ã¾ã›ã‚“ Argument is not referenced from the logging message - Argument is not referenced from the logging message + 引数ã¯ãƒ­ã‚° メッセージã‹ã‚‰å‚ç…§ã•れã¦ã„ã¾ã›ã‚“ Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + 6 ã¤ä»¥ä¸Šã®å¼•æ•°ã®ç”Ÿæˆã¯ã‚µãƒãƒ¼ãƒˆã•れã¾ã›ã‚“ Can't have the same template with different casing - Can't have the same template with different casing + 大文字ã¨å°æ–‡å­—ã«å¯¾ã—ã¦åŒã˜ãƒ†ãƒ³ãƒ—レートを使用ã§ãã¾ã›ã‚“ Logging method names cannot start with _ - Logging method names cannot start with _ + ログ メソッドåã¯ã€Œ _ ã€ã§å§‹ã¾ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Logging method パラメーターåã¯ã€Œ _ ã€ã§å§‹ã¾ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Logging methods cannot have a body - Logging methods cannot have a body + ログ ãƒ¡ã‚½ãƒƒãƒ‰ã¯æœ¬æ–‡ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ Logging methods cannot be generic - Logging methods cannot be generic + ログ メソッドを汎用ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Logging methods must be partial - Logging methods must be partial + ログ記録方法ã®ä¸€éƒ¨ãŒå¿…è¦ã§ã™ Logging methods must return void - Logging methods must return void + ログ メソッドã¯ç„¡åŠ¹ã‚’è¿”ã™å¿…è¦ãŒã‚りã¾ã™ Logging methods must be static - Logging methods must be static + ログ メソッドã¯é™çš„ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + (dangling {ãªã©) 誤ã£ãŸå½¢å¼ã®æ–‡å­—列をæŒã¤ã“ã¨ã¯ã§ãã¾ã›ã‚“ A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + LogLevel 値ã¯ã€LoggingMessage 属性ã¾ãŸã¯ logging メソッドã®ãƒ‘ラメーターã¨ã—ã¦æŒ‡å®šã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + é™çš„ログ メソッド '{0}' ã¸ã®å¼•æ•°ã® 1 ã¤ã«ã¯ã€Microsoft.Extensions.Logging.ILogger インターフェイスを実装ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + é™çš„ログ メソッドã¸ã®å¼•æ•°ã® 1 ã¤ã«ã¯ã€Microsoft.Extensions.Logging.ILogger インターフェイスを実装ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + クラス㧠Microsoft.Extensions.Logging.ILogger åž‹ã®ãƒ•ィールドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠{0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger åž‹ã®ãƒ•ィールドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠{Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + {0} åž‹ã®å®šç¾©ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ Could not find a required type definition - Could not find a required type definition + å¿…è¦ãªåž‹ã®å®šç¾©ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + クラス㫠Microsoft.Extensions.Logging.ILogger ã¨ã„ã†ç¨®é¡žã®è¤‡æ•°ã®ãƒ•ィールドãŒã‚りã¾ã™ {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger ã¨ã„ã†ç¨®é¡žã®è¤‡æ•°ã®ãƒ•ィールドãŒè¦‹ã¤ã‹ã‚Šã¾ã—㟠{Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + 指定ã•れãŸãƒ­ã‚° レベルã§ã¯æš—黙的ã§ã‚ã‚‹ãŸã‚ã€å†—é•·ãªä¿®é£¾å­ (Info:ã€Warning:ã€Error: ãªã©) をログ メッセージã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã€‚ Redundant qualifier in logging message - Redundant qualifier in logging message + ログ メッセージ内ã®å†—é•·ãªä¿®é£¾å­ Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + ログ メッセージã«ä¾‹å¤–パラメーターをテンプレートã¨ã—ã¦å«ã‚ã¾ã›ã‚“ Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + 暗黙的ã«å‡¦ç†ãŒè¡Œ ã‚れã¦ã„ã‚‹ãŸã‚ã€ãƒ­ã‚° メッセージ㫠{0} ã®ãƒ†ãƒ³ãƒ—レートをå«ã‚ã¾ã›ã‚“ Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + ログ メッセージã«ã¯ã€ãƒ­ã‚° レベル パラメーターをテンプレートã¨ã—ã¦å«ã‚ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + ログ メッセージã«ãƒ­ã‚¬ãƒ¼ パラメーターをテンプレートã¨ã—ã¦å«ã‚ã¾ã›ã‚“ Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + 複数ã®ãƒ­ã‚°è¨˜éŒ²æ–¹æ³•ã§ã‚¯ãƒ©ã‚¹ {1}内ã®ã‚¤ãƒ™ãƒ³ãƒˆ ID {0} を使用ã—ã¦ã„ã¾ã™ Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + 複数ã®ãƒ­ã‚°è¨˜éŒ²æ–¹æ³•ã§ã¯ã€ã‚¯ãƒ©ã‚¹å†…ã§åŒã˜ã‚¤ãƒ™ãƒ³ãƒˆ ID を使用ã§ãã¾ã›ã‚“ Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + テンプレート '{0}' ã¯ã€ãƒ­ã‚° メソッドã®å¼•æ•°ã¨ã—ã¦æä¾›ã•れã¾ã›ã‚“ Logging template has no corresponding method argument - Logging template has no corresponding method argument + ログ テンプレートã«å¯¾å¿œã™ã‚‹ãƒ¡ã‚½ãƒƒãƒ‰å¼•æ•°ãŒã‚りã¾ã›ã‚“ diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf index 1fec30a63fa..990e1b75f73 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + 로깅 메시지ì—서 ‘{0}’ ì¸ìˆ˜ë¥¼ 참조하지 않습니다. Argument is not referenced from the logging message - Argument is not referenced from the logging message + 로깅 메시지ì—서 ì¸ìˆ˜ë¥¼ 참조하지 ì•ŠìŒ Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + 6개가 넘는 ì¸ìˆ˜ ìƒì„±ì€ ì§€ì›ë˜ì§€ ì•ŠìŒ Can't have the same template with different casing - Can't have the same template with different casing + ë™ì¼í•œ í…œí”Œë¦¿ì„ ëŒ€/소문ìžë¥¼ 다르게 하여 사용할 수는 ì—†ìŒ Logging method names cannot start with _ - Logging method names cannot start with _ + 로깅 메서드 ì´ë¦„ì€ _로 시작할 수 ì—†ìŒ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + 로깅 메서드 매개 변수 ì´ë¦„ì€ _로 시작할 수 ì—†ìŒ Logging methods cannot have a body - Logging methods cannot have a body + 로깅 메서드ì—는 ë³¸ë¬¸ì„ ì‚¬ìš©í•  수 ì—†ìŒ Logging methods cannot be generic - Logging methods cannot be generic + 로깅 메서드는 ì œë„¤ë¦­ì¼ ìˆ˜ ì—†ìŒ Logging methods must be partial - Logging methods must be partial + 로깅 메서드는 부분ì´ì–´ì•¼ 함 Logging methods must return void - Logging methods must return void + 로깅 메서드는 void를 반환해야 함 Logging methods must be static - Logging methods must be static + 로깅 메서드는 ì •ì ì´ì–´ì•¼ 함 Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + ìž˜ëª»ëœ í˜•ì‹ì˜ 문ìžì—´(예: ì§ì´ ë§žì§€ 않는 중괄호({))ì€ ì‚¬ìš©í•  수 ì—†ìŒ A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + LogLevel ê°’ì€ LoggerMessage íŠ¹ì„±ì— ì§€ì •í•˜ê±°ë‚˜ 로깅 ë©”ì„œë“œì— ëŒ€í•œ 매개 변수로 제공해야 함 One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + ì •ì  ë¡œê¹… 메서드 ‘{0}â€™ì˜ ì¸ìˆ˜ 중 하나가 Microsoft.Extensions.Logging.ILogger ì¸í„°íŽ˜ì´ìŠ¤ë¥¼ 구현해야 함 {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + ì •ì  ë¡œê¹… 메서드 ì¸ìˆ˜ 중 하나가 Microsoft.Extensions.Logging.ILogger ì¸í„°íŽ˜ì´ìŠ¤ë¥¼ 구현해야 함 {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + {0} í´ëž˜ìФì—서 Microsoft.Extensions.Logging.ILogger 형ì‹ì˜ 필드를 ì°¾ì„ ìˆ˜ ì—†ìŒ {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger 형ì‹ì˜ 필드를 ì°¾ì„ ìˆ˜ ì—†ìŒ {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + í˜•ì‹ {0}ì— ëŒ€í•œ ì •ì˜ë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ Could not find a required type definition - Could not find a required type definition + 필요한 í˜•ì‹ ì •ì˜ë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + {0} í´ëž˜ìŠ¤ì— Microsoft.Extensions.Logging.ILogger 형ì‹ì˜ 필드가 여러 ê°œ ìžˆìŒ {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger 형ì‹ì˜ 필드가 여러 ê°œ ìžˆìŒ {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + 중복 한정ìž(ì •ë³´:, 경고:, 오류: 등)ê°€ ì§€ì •ëœ ë¡œê·¸ 수준ì—서 암시ì ì´ê¸° ë•Œë¬¸ì— ë¡œê¹… 메시지ì—서 제거합니다. Redundant qualifier in logging message - Redundant qualifier in logging message + 로깅 ë©”ì‹œì§€ì— ì¤‘ë³µ 한정ìžê°€ ìžˆìŒ Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + 로깅 ë©”ì‹œì§€ì— í…œí”Œë¦¿ìœ¼ë¡œ 예외 매개 변수를 í¬í•¨í•˜ì§€ ì•ŠìŒ Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + {0}ì— ëŒ€í•œ í…œí”Œë¦¿ì€ ì•”ì‹œì ìœ¼ë¡œ 처리ë˜ë¯€ë¡œ 로깅 ë©”ì‹œì§€ì— í¬í•¨í•˜ì§€ ì•ŠìŒ Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + 로깅 ë©”ì‹œì§€ì— í…œí”Œë¦¿ìœ¼ë¡œ 로그 수준 매개 변수를 í¬í•¨í•˜ì§€ ì•ŠìŒ Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + 로깅 ë©”ì‹œì§€ì— í…œí”Œë¦¿ìœ¼ë¡œ 로거 매개 변수를 í¬í•¨í•˜ì§€ ì•ŠìŒ Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + {1} í´ëž˜ìФì—서 여러 로깅 메서드가 ì´ë²¤íЏ ID {0}ì„(를) 사용함 Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + 여러 로깅 메서드가 한 í´ëž˜ìФ ë‚´ì—서 ë™ì¼í•œ ì´ë²¤íЏ ID를 사용할 수는 ì—†ìŒ Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + 템플릿 {0}ì´(ê°€) 로깅 ë©”ì„œë“œì— ì¸ìˆ˜ë¡œ 제공ë˜ì§€ ì•ŠìŒ Logging template has no corresponding method argument - Logging template has no corresponding method argument + 로깅 í…œí”Œë¦¿ì— í•´ë‹¹ 하는 메서드 ì¸ìˆ˜ê°€ ì—†ìŒ diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf index 3bfdf3eeeaf..667234656ff 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + Do argumentu „{0}†nie odwoÅ‚uje siÄ™ komunikat rejestrowania Argument is not referenced from the logging message - Argument is not referenced from the logging message + Do argumentu nie odwoÅ‚uje siÄ™ komunikat rejestrowania Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + Generowanie wiÄ™cej niż 6 argumentów nie jest obsÅ‚ugiwane Can't have the same template with different casing - Can't have the same template with different casing + Nie można mieć tego samego szablonu z różnÄ… wielkoÅ›ciÄ… liter Logging method names cannot start with _ - Logging method names cannot start with _ + Nazwy metody rejestrowania nie mogÄ… rozpoczynać siÄ™ od znaku „_†Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Nazwy parametrów metody rejestrowania nie mogÄ… rozpoczynać siÄ™ od znaku „_†Logging methods cannot have a body - Logging methods cannot have a body + Metody rejestrowania nie mogÄ… mieć treÅ›ci Logging methods cannot be generic - Logging methods cannot be generic + Metody rejestrowania nie mogÄ… być ogólne Logging methods must be partial - Logging methods must be partial + Metody rejestrowania muszÄ… być częściowe Logging methods must return void - Logging methods must return void + Metody rejestrowania muszÄ… zwracać wartość typu void Logging methods must be static - Logging methods must be static + Metody rejestrowania muszÄ… być statyczne Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Nie może zawierać źle sformuÅ‚owanego formatu ciÄ…gów (takich jak zawieszonego znaku „{â€, itp.) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Wartość LogLevel musi być podana w atrybucie LoggerMessage lub jako parametr metody rejestrowania One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Jeden z argumentów statycznej metody rejestrowania „{0}†musi implementować interfejs Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Jeden z argumentów statycznej metody rejestrowania musi implementować interfejs Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Nie można znaleźć pola typu Microsoft.Extensions.Logging.ILogger w klasie {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Nie można znaleźć pola typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Nie można znaleźć definicji dla typu {0} Could not find a required type definition - Could not find a required type definition + Nie można znaleźć wymaganej definicji typu Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Znaleziono wiele pól typu Microsoft.Extensions.Logging.ILogger w klasie {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Znaleziono wiele pól typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + UsuÅ„ nadmiarowy kwalifikator (Info:, Warning:, Error: itp.) z komunikatu rejestrowania, ponieważ jest on domyÅ›lny na okreÅ›lonym poziomie dziennika. Redundant qualifier in logging message - Redundant qualifier in logging message + Nadmiarowy kwalifikator w komunikacie rejestrowania Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Nie dołączać parametrów wyjÄ…tków jako szablonów w komunikatach rejestrowania Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Nie dołączać szablonu dla {0} do komunikatu o rejestracji, ponieważ jest to wykonywane domyÅ›lnie Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Nie dołączać parametrów poziomu dziennika jako szablonów w komunikatach rejestrowania Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Nie dołączać parametrów rejestratora jako szablonów do komunikatu rejestrowania Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Wiele metod rejestrowania używa identyfikatora zdarzenia {0} w klasie {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Wiele metod rejestrowania nie może używać tego samego identyfikatora zdarzenia w obrÄ™bie klasy Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Nie podano szablonu „{0}†jako argumentu dla metody rejestrowania Logging template has no corresponding method argument - Logging template has no corresponding method argument + Szablon rejestrowania nie ma odpowiadajÄ…cego mu argumentu metody diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf index 4417c68f969..d20a04512c0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + O argumento '{0}' não é referenciado na mensagem de registro em log Argument is not referenced from the logging message - Argument is not referenced from the logging message + O argumento não é referenciado na mensagem de registro em log Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + A geração de mais de 6 argumentos não é suportada Can't have the same template with different casing - Can't have the same template with different casing + Não é possível ter o mesmo modelo com diferenças entre maiúsculas e minúsculas. Logging method names cannot start with _ - Logging method names cannot start with _ + Os nomes dos métodos de registro em log não podem começar com _ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Os nomes dos parâmetros do método de registro em log não podem começar com _ Logging methods cannot have a body - Logging methods cannot have a body + Os métodos de registro em log não podem ter um corpo Logging methods cannot be generic - Logging methods cannot be generic + Os métodos de registro em log não podem ser genéricos Logging methods must be partial - Logging methods must be partial + Os métodos de registro em log devem ser parciais Logging methods must return void - Logging methods must return void + Os métodos de registro em log devem retornar nulos Logging methods must be static - Logging methods must be static + Os métodos de registro em log devem ser estáticos Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Não pode ter cadeia de caracteres de formato malformadas (como final {, etc) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Um valor LogLevel deve ser fornecido no atributo LoggerMessage ou como um parâmetro para o método de registro em log One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Um dos argumentos para um método de log estático '{0}' deve implementar a interface Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Um dos argumentos para um método de log estático deve implementar a interface Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Não foi possível encontrar um campo do tipo Microsoft.Extensions.Logging.ILogger na classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Não foi possível encontrar um campo do tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Não foi possível encontrar a definição para o tipo {0} Could not find a required type definition - Could not find a required type definition + Não foi possível encontrar uma definição de tipo necessária Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Múltiplos campos do tipo Microsoft.Extensions.Logging.ILogger encontrados na classe {0} {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Múltiplos campos encontrados do tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Remova o qualificador redundante (Info:, Aviso:, Erro:, etc) da mensagem de log, pois está implícito no nível de log especificado. Redundant qualifier in logging message - Redundant qualifier in logging message + Qualificador redundante na mensagem de registro de log Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Não inclua parâmetros de exceção como modelos na mensagem de registro em log Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Não inclua um modelo para {0} na mensagem de registro em log, pois isso é implicitamente atendido Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Não inclua parâmetros de nível de registro como modelos na mensagem de registro de log Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Não inclua parâmetros de agente como modelos na mensagem de registro em log Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Múltiplos métodos de registro em log estão usando a id de evento {0} na classe {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Múltiplos métodos de registro em log não podem usar o mesmo id de evento dentro de uma classe Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + O modelo '{0}' não é fornecido como argumento para o método de registro Logging template has no corresponding method argument - Logging template has no corresponding method argument + O modelo de registro em log não tem nenhum argumento de método correspondente diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf index 5263092fc61..f0420f50526 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + Ð’ Ñообщении Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° нет ÑÑылки на аргумент "{0}" Argument is not referenced from the logging message - Argument is not referenced from the logging message + Ð’ Ñообщении Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° нет ÑÑылки на аргумент Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + Формирование более 6 аргументов не поддерживаетÑÑ Can't have the same template with different casing - Can't have the same template with different casing + Ðевозможно иÑпользовать шаблон Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же именем в другом региÑтре Logging method names cannot start with _ - Logging method names cannot start with _ + Имена методов Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° не могут начинатьÑÑ Ñ Ñимвола "_" Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Имена параметров метода Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° не могут начинатьÑÑ Ñ Ñимвола "_" Logging methods cannot have a body - Logging methods cannot have a body + У методов Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° не может быть текÑта Logging methods cannot be generic - Logging methods cannot be generic + Методы Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° не могут быть универÑальными Logging methods must be partial - Logging methods must be partial + Методы Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° должны быть чаÑтичными Logging methods must return void - Logging methods must return void + Методы Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° должны возвращать значение void Logging methods must be static - Logging methods must be static + Методы Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° должны быть ÑтатичеÑкими Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Ð”Ð»Ñ Ñтрок формата не допуÑкаетÑÑ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¹ формат (виÑÑчие Ñимволы "{" и Ñ‚. п.) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + Значение LogLevel должно быть указано в атрибуте LoggerMessage или в качеÑтве параметра метода Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + Один из аргументов ÑтатичеÑкого метода Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° "{0}" должен реализовывать Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Один из аргументов ÑтатичеÑкого метода Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° должен реализовывать Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + Ð’ клаÑÑе {0} не найдены Ð¿Ð¾Ð»Ñ Ñ‚Ð¸Ð¿Ð° Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Ðе удалоÑÑŒ найти поле типа Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + Ðе найдено определение Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° {0} Could not find a required type definition - Could not find a required type definition + Ðе найдено требуемое определение типа Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + Ð’ клаÑÑе {0} обнаружено неÑколько полей типа Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Обнаружено неÑколько полей типа Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Удалите избыточный квалификатор (Info:, Warning:, Error:, и Ñ‚. п.) из ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð°, поÑкольку квалификатор подразумеваетÑÑ Ð½Ð° указанном уровне Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð°. Redundant qualifier in logging message - Redundant qualifier in logging message + Избыточный квалификатор в Ñообщении журнала Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Ðе включайте параметры иÑключений в качеÑтве шаблонов в Ñообщение Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Ðе включайте шаблон Ð´Ð»Ñ {0} в Ñообщение Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð°, поÑкольку он иÑпользуетÑÑ Ð½ÐµÑвным образом Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Ðе включайте параметры ÑƒÑ€Ð¾Ð²Ð½Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° в качеÑтве шаблонов в Ñообщение Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Ðе включайте параметры ÑредÑтва Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° в качеÑтве шаблонов в Ñообщение Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + ÐеÑколько методов Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° иÑпользуют идентификатор ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ {0} в клаÑÑе {1} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + ÐеÑколько методов Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° не могут иÑпользовать одинаковый ИД ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð² пределах клаÑÑа Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + Шаблон "{0}" не указан в качеÑтве аргумента Ð´Ð»Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð° Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð° Logging template has no corresponding method argument - Logging template has no corresponding method argument + У шаблона журнала нет ÑоответÑтвующего аргумента метода diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf index 539f7ecf9c1..11c1a0157d0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + '{0}' bağımsız deÄŸiÅŸkenine günlük iletisinden baÅŸvurulmuyor Argument is not referenced from the logging message - Argument is not referenced from the logging message + Bağımsız deÄŸiÅŸkene günlüğe kaydetme iletisinden baÅŸvurulmuyor Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + 6’dan fazla bağımsız deÄŸiÅŸken oluÅŸturma özelliÄŸi desteklenmiyor Can't have the same template with different casing - Can't have the same template with different casing + Aynı ÅŸablon farklı büyük/küçük harfler içeremez Logging method names cannot start with _ - Logging method names cannot start with _ + Günlüğe kaydetme yöntemi adları _ ile baÅŸlayamaz Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + Günlüğe kaydetme yöntemi parametre adları _ ile baÅŸlayamaz Logging methods cannot have a body - Logging methods cannot have a body + Günlüğe kaydetme yöntemleri gövde içeremez Logging methods cannot be generic - Logging methods cannot be generic + Günlüğe kaydetme yöntemleri genel olamaz Logging methods must be partial - Logging methods must be partial + Günlüğe kaydetme yöntemleri kısmi olmalıdır Logging methods must return void - Logging methods must return void + Günlüğe kaydetme yöntemleri boÅŸ deÄŸer döndürmelidir Logging methods must be static - Logging methods must be static + Günlüğe kaydetme yöntemleri statik olmalıdır Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + Hatalı biçimlendirilmiÅŸ biçim dizeleri (aykırı { vb. gibi) içeremez A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + LoggerMessage özniteliÄŸinde veya günlüğe kaydetme yönteminin parametresi olarak bir LogLevel deÄŸerinin belirtilmesi gerekiyor One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + '{0}' statik günlük metoduna yönelik bağımsız deÄŸiÅŸkenlerden biri Microsoft.Extensions.Logging.ILogger arabirimini uygulamalıdır {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + Statik günlük metoduna yönelik bağımsız deÄŸiÅŸkenlerden biri Microsoft.Extensions.Logging.ILogger arabirimini uygulamalıdır {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + {0} sınıfında Microsoft.Extensions.Logging.ILogger türündeki alan bulunamadı {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger türündeki alan bulunamadı {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + {0} türü için tanım bulunamadı Could not find a required type definition - Could not find a required type definition + Gerekli bir tür tanımı bulunamadı Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + {0} sınıfında Microsoft.Extensions.Logging.ILogger türünde birden çok alan bulundu {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + Microsoft.Extensions.Logging.ILogger türünde birden çok alan bulundu {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + Belirtilen günlük düzeyinde örtük olduÄŸundan gereksiz niteleyiciyi (Bilgi:, Uyarı:, Hata: vb.) günlüğe kaydetme iletisinden kaldırın. Redundant qualifier in logging message - Redundant qualifier in logging message + Günlüğe kaydetme iletisindeki gereksiz niteleyici Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + Özel durum parametrelerini günlüğe kaydetme iletisine ÅŸablon olarak eklemeyin Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + Örtük olarak dikkate alındığından günlüğe kaydetme iletisine {0} ÅŸablonu eklemeyin Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + Günlük düzeyi parametrelerini günlüğe kaydetme iletisine ÅŸablon olarak eklemeyin Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + Günlükçü parametrelerini günlüğe kaydetme iletisine ÅŸablon olarak eklemeyin Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + Birden çok günlüğe kaydetme yöntemi {1} sınıfında {0} olay kimliÄŸini kullanıyor Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + Birden çok günlüğe kaydetme yöntemi bir sınıf içinde aynı olay kimliÄŸini kullanamaz Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + ‘{0}’ ÅŸablonu günlüğe kaydetme yönteminin bağımsız deÄŸiÅŸkeni olarak saÄŸlanmadı Logging template has no corresponding method argument - Logging template has no corresponding method argument + Günlüğe kaydetme ÅŸablonunda karşılık gelen yöntem bağımsız deÄŸiÅŸkeni yok diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf index cbc17ee3d19..b814d8a0a02 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + 未从日志记录消æ¯ä¸­å¼•ç”¨å‚æ•°â€œ{0}†Argument is not referenced from the logging message - Argument is not referenced from the logging message + 未从日志记录消æ¯ä¸­å¼•ç”¨å‚æ•° Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + 䏿”¯æŒç”Ÿæˆ 6 ä¸ªä»¥ä¸Šçš„å‚æ•° Can't have the same template with different casing - Can't have the same template with different casing + ä¸èƒ½æœ‰å¤§å°å†™ä¸åŒçš„ç›¸åŒæ¨¡æ¿ Logging method names cannot start with _ - Logging method names cannot start with _ + 日志记录方法åç§°ä¸èƒ½ä»¥ _ 开头 Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + æ—¥å¿—è®°å½•æ–¹æ³•å‚æ•°åç§°ä¸èƒ½ä»¥ _ 开头 Logging methods cannot have a body - Logging methods cannot have a body + 日志记录方法ä¸èƒ½æœ‰æ­£æ–‡ Logging methods cannot be generic - Logging methods cannot be generic + 日志记录方法ä¸èƒ½ä¸ºæ³›åž‹æ–¹æ³• Logging methods must be partial - Logging methods must be partial + 日志记录方法必须为分部方法 Logging methods must return void - Logging methods must return void + 日志记录方法必须返回 void Logging methods must be static - Logging methods must be static + æ—¥å¿—è®°å½•æ–¹æ³•å¿…é¡»ä¸ºé™æ€æ–¹æ³• Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + ä¸èƒ½æœ‰æ ¼å¼é”™è¯¯çš„æ ¼å¼å­—符串(例如悬空 { ç­‰) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + 必须在 LoggerMessage 属性中æä¾› LogLevel å€¼æˆ–å°†å…¶ç”¨ä½œæ—¥å¿—è®°å½•æ–¹æ³•çš„å‚æ•° One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + 陿€æ—¥å¿—记录方法“{0}â€çš„傿•°ä¹‹ä¸€å¿…须实施 Microsoft.Extensions.Logging.ILogger æŽ¥å£ {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + 陿€æ—¥å¿—è®°å½•æ–¹æ³•çš„å‚æ•°ä¹‹ä¸€å¿…须实现 Microsoft.Extensions.Logging.ILogger æŽ¥å£ {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + 在类 {0} 中找ä¸åˆ° Microsoft.Extensions.Logging.ILogger 类型的字段 {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + 找ä¸åˆ° Microsoft.Extensions.Logging.ILogger 类型的字段。 {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + 找ä¸åˆ°ç±»åž‹ {0} 的定义 Could not find a required type definition - Could not find a required type definition + 找ä¸åˆ°æ‰€éœ€çš„类型定义 Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + 在类 {0} 中找到多个 Microsoft.Extensions.Logging.ILogger 类型的字段 {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + 找到 Microsoft.Extensions.Logging.ILogger 类型的多个字段 {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + 从日志记录消æ¯ä¸­åˆ é™¤å†—ä½™é™å®šç¬¦(ä¿¡æ¯:ã€è­¦å‘Š:ã€é”™è¯¯: ç­‰),因为其在指定的日志级别中为éšå¼å†…容。 Redundant qualifier in logging message - Redundant qualifier in logging message + æ—¥å¿—æ¶ˆæ¯æ¶ˆæ¯ä¸­çš„冗余é™å®šç¬¦ Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + ä¸è¦å°†å¼‚叏傿•°ä½œä¸ºæ¨¡æ¿åŒ…å«åœ¨æ—¥å¿—记录消æ¯ä¸­ Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + ä¸è¦å°† {0} 的模æ¿åŒ…å«åœ¨æ—¥å¿—记录消æ¯ä¸­ï¼Œå› ä¸ºå…¶è¢«éšå¼å¤„ç† Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + ä¸è¦å°†æ—¥å¿—çº§åˆ«å‚æ•°ä½œä¸ºæ¨¡æ¿åŒ…å«åœ¨æ—¥å¿—记录消æ¯ä¸­ Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + ä¸è¦å°†è®°å½•噍傿•°ä½œä¸ºæ¨¡æ¿åŒ…å«åœ¨æ—¥å¿—记录消æ¯ä¸­ Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + 多个日志记录方法正在类 {1} 中使用事件 ID {0} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + 多个日志记录方法ä¸èƒ½åœ¨ç±»ä¸­ä½¿ç”¨ç›¸åŒçš„事件 ID Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + 未将模æ¿â€œ{0}â€ä½œä¸ºå‚æ•°æä¾›ç»™æ—¥å¿—记录方法 Logging template has no corresponding method argument - Logging template has no corresponding method argument + æ—¥å¿—è®°å½•æ¨¡æ¿æ— ç›¸åº”çš„æ–¹æ³•å‚æ•° diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf index 297a39f23db..0f13b32acba 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -4,157 +4,157 @@ Argument '{0}' is not referenced from the logging message - Argument '{0}' is not referenced from the logging message + 引數 '{0}' 未åƒç…§è¨˜éŒ„è¨Šæ¯ Argument is not referenced from the logging message - Argument is not referenced from the logging message + 未從記錄訊æ¯åƒç…§å¼•數 Generating more than 6 arguments is not supported - Generating more than 6 arguments is not supported + 䏿”¯æ´ç”¢ç”Ÿ 6 個以上的引數 Can't have the same template with different casing - Can't have the same template with different casing + ä¸èƒ½æœ‰ä¸åŒå¤§å°å¯«çš„相åŒç¯„本 Logging method names cannot start with _ - Logging method names cannot start with _ + 記錄方法å稱的開頭ä¸èƒ½ç‚º _ Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ + è¨˜éŒ„æ–¹æ³•åƒæ•¸å稱的開頭ä¸èƒ½ç‚º _ Logging methods cannot have a body - Logging methods cannot have a body + 記錄方法ä¸èƒ½æœ‰ä¸»é«” Logging methods cannot be generic - Logging methods cannot be generic + 記錄方法ä¸å¯ç‚ºæ³›åž‹ Logging methods must be partial - Logging methods must be partial + 記錄方法必須是部分 Logging methods must return void - Logging methods must return void + 記錄方法必須傳回 void Logging methods must be static - Logging methods must be static + è¨˜éŒ„æ–¹æ³•å¿…é ˆæ˜¯éœæ…‹ Can't have malformed format strings (like dangling {, etc) - Can't have malformed format strings (like dangling {, etc) + ä¸èƒ½æœ‰æ ¼å¼éŒ¯èª¤çš„æ ¼å¼å­—串 (例如懸空 {, 等等) A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + 必須在 LoggerMessage 屬性中æä¾› LogLevel 值,或將其åšç‚ºè¨˜éŒ„æ–¹æ³•çš„åƒæ•¸ã€‚ One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to the static logging method '{0}' must implement the Microsoft.Extensions.Logging.ILogger interface + éœæ…‹è¨˜éŒ„方法 '{0}' 的其中一個引數必須實作 Microsoft.Extensions.Logging.ILogger ä»‹é¢ {Locked="Microsoft.Extensions.Logging.ILogger"} One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a static logging method must implement the Microsoft.Extensions.Logging.ILogger interface + éœæ…‹è¨˜éŒ„方法的其中一個引數必須實作 Microsoft.Extensions.Logging.ILogger ä»‹é¢ {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + 找ä¸åˆ°é¡žåˆ¥ {0} 中 Microsoft.Extensions.Logging.ILogger é¡žåž‹çš„æ¬„ä½ {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + 找ä¸åˆ° Microsoft.Extensions.Logging.ILogger é¡žåž‹çš„æ¬„ä½ {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} - Could not find definition for type {0} + 找ä¸åˆ°é¡žåž‹ {0} 的定義 Could not find a required type definition - Could not find a required type definition + 找ä¸åˆ°æ‰€éœ€çš„類型定義 Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + 在類別 {0} 中找到多個 Microsoft.Extensions.Logging.ILogger é¡žåž‹çš„æ¬„ä½ {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger - Found multiple fields of type Microsoft.Extensions.Logging.ILogger + 找到多個 Microsoft.Extensions.Logging.ILogger é¡žåž‹çš„æ¬„ä½ {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + 從記錄訊æ¯ä¸­ç§»é™¤å‚™æ´é™å®šè©ž (資訊:ã€è­¦å‘Š:ã€éŒ¯èª¤: 等等),因為它在指定的記錄層級中為隱å«ã€‚ Redundant qualifier in logging message - Redundant qualifier in logging message + 記錄訊æ¯ä¸­çš„å‚™æ´é™å®šè©ž Don't include exception parameters as templates in the logging message - Don't include exception parameters as templates in the logging message + 請勿在記錄訊æ¯ä¸­åŒ…å«åšç‚ºç¯„本的例外狀æ³åƒæ•¸ Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of + 請勿在記錄訊æ¯ä¸­åŒ…å« {0} 的範本,因為系統是將它隱å«è™•ç† Don't include log level parameters as templates in the logging message - Don't include log level parameters as templates in the logging message + 請勿在記錄訊æ¯ä¸­åŒ…å«åšç‚ºç¯„æœ¬çš„è¨˜éŒ„å±¤ç´šåƒæ•¸ Don't include logger parameters as templates in the logging message - Don't include logger parameters as templates in the logging message + 請勿在記錄訊æ¯ä¸­åŒ…å«åšç‚ºç¯„æœ¬çš„è¨˜éŒ„å™¨åƒæ•¸ Multiple logging methods are using event id {0} in class {1} - Multiple logging methods are using event id {0} in class {1} + 多個記錄方法是使用類別 {1} 中的事件識別碼 {0} Multiple logging methods cannot use the same event id within a class - Multiple logging methods cannot use the same event id within a class + 多個記錄方法ä¸èƒ½åœ¨é¡žåˆ¥å…§ä½¿ç”¨ç›¸åŒçš„事件識別碼 Template '{0}' is not provided as argument to the logging method - Template '{0}' is not provided as argument to the logging method + 未將範本 '{0}' æä¾›åšç‚ºè¨˜éŒ„方法的引數 Logging template has no corresponding method argument - Logging template has no corresponding method argument + è¨˜éŒ„ç¯„æœ¬æ²’æœ‰å°æ‡‰çš„æ–¹æ³•引數 diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 8e5df4972cf..b2fe61084c5 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -114,7 +114,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Single(diagnostics); Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); - Assert.Contains("Argument 'foo' is not referenced from the logging message", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + Assert.Contains("foo", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] @@ -161,7 +161,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Single(diagnostics); Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); - Assert.Contains("Argument 'foo' is not referenced from the logging message", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + Assert.Contains("foo", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] @@ -177,7 +177,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Single(diagnostics); Assert.Equal(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument.Id, diagnostics[0].Id); - Assert.Contains("Template 'foo' is not provided as argument to the logging method", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + Assert.Contains("foo", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] @@ -467,7 +467,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Single(diagnostics); Assert.Equal(DiagnosticDescriptors.ShouldntReuseEventIds.Id, diagnostics[0].Id); - Assert.Contains("in class MyClass", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + Assert.Contains("MyClass", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] @@ -500,7 +500,9 @@ namespace Microsoft.Extensions.Logging.Generators.Tests Assert.Single(diagnostics); Assert.Equal(DiagnosticDescriptors.MissingLoggerArgument.Id, diagnostics[0].Id); - Assert.Contains("One of the arguments to the static logging method 'M1' must implement the Microsoft.Extensions.Logging.ILogger interface", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); + string message = diagnostics[0].GetMessage(); + Assert.Contains("M1", message, StringComparison.InvariantCulture); + Assert.Contains("Microsoft.Extensions.Logging.ILogger", message, StringComparison.InvariantCulture); } [Fact] diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf index d27ae15d636..94f13a8bc9a 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Odvozený typ JsonSerializerContext {0} urÄuje typy serializovatelné na JSON. Typ a vÅ¡echny obsahující typy musí být ÄásteÄné, aby se zahájilo generování zdroje. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Odvozené typy JsonSerializerContext a vÅ¡echny obsahující typy musí být ÄásteÄné. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Existuje nÄ›kolik typů s názvem {0}. Zdroj se vygeneroval pro první zjiÅ¡tÄ›ný typ. Tuto kolizi vyÅ™ešíte pomocí JsonSerializableAttribute.TypeInfoPropertyName. Duplicate type name. - Duplicate type name. + Duplicitní název typu Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Nevygenerovala se metadata serializace pro typ {0}. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Nevygenerovala se metadata serializace pro typ diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf index d2e73c0b473..e23cf13d895 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Der abgeleitete JsonSerializerContext-Typ "{0}" gibt serialisierbare JSON-Typen an. Der Typ und alle enthaltenden Typen müssen partiell sein, um die Quellgenerierung zu starten. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Abgeleitete JsonSerializerContext-Typen und alle enthaltenden Typen müssen partiell sein. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Es sind mehrere Typen namens "{0}" vorhanden. Die Quelle wurde für den ersten festgestellten Typ generiert. Verwenden Sie "JsonSerializableAttribute.TypeInfoPropertyName", um diesen Konflikt zu beheben. Duplicate type name. - Duplicate type name. + Doppelter Typname Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Die Serialisierungsmetadaten für den Typ "{0}" wurden nicht generiert. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Serialisierungsmetadaten für den Typ wurden nicht generiert diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf index 176e2138360..893e9374de7 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + El tipo derivado "JsonSerializerContext" "{0}" especifica tipos de JSON serializables. El tipo y todos los tipos que contienen deben hacerse parciales para iniciar la generación de origen. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Los tipos derivados "JsonSerializerContext" y todos los tipos que contienen deben ser parciales. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Hay varios tipos denominados {0}. El origen se generó para el primero detectado. Use "JsonSerializableAttribute.TypeInfoPropertyName" para resolver esta colisión. Duplicate type name. - Duplicate type name. + Nombre de tipo duplicado. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + No generó metadatos de serialización para el tipo '{0}". Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + No generó metadatos de serialización de metadatos para el tipo. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf index 288e44e6088..1a364c16896 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Le type 'JsonSerializerContext' dérivé '{0}' spécifie des types Sérialisables JSON. Le type et tous les types contenants doivent être rendus partiels pour lancer la génération de la source. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Les types dérivés 'JsonSerializerContext' et tous les types conteneurs doivent être partiels. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Plusieurs types nommés {0}. La source a été générée pour la première détection détectée. Utilisez « JsonSerializableAttribute.TypeInfoPropertyName » pour résoudre cette collision. Duplicate type name. - Duplicate type name. + Nom de type dupliqué. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Les métadonnées de sérialisation pour le type « {0} » n’ont pas été générées. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Les métadonnées de sérialisation pour le type n’ont pas été générées. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf index 434a86ffe60..7a700a27d85 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Il tipo 'JsonSerializerContext 'derivato '{0}' specifica i tipi serializzabili JSON. Il tipo e tutti i tipi contenenti devono essere parziali per iniziare la generazione dell'origine. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + I tipi derivati 'JsonSerializerContext' e tutti i tipi contenenti devono essere parziali. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Sono presenti più tipi denominati {0}. L'origine è stata generata per il primo tipo rilevato. Per risolvere questa collisione, usare 'JsonSerializableAttribute.TypeInfoPropertyName'. Duplicate type name. - Duplicate type name. + Nome di tipo duplicato. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Non sono stati generati metadati di serializzazione per il tipo '{0}'. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Non sono stati generati metadati di serializzazione per il tipo. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf index 1ed485527a4..f05edfd844e 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + 派生ã—㟠'JsonSerializerContext'åž‹ '{0}' ã¯ã€JSON シリアル化å¯èƒ½ãªåž‹ã‚’指定ã—ã¾ã™ã€‚ソース生æˆã‚’é–‹å§‹ã™ã‚‹ã«ã¯ã€åž‹ã¨å«ã¾ã‚Œã¦ã„ã‚‹ã™ã¹ã¦ã®åž‹ã‚’部分的ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + 派生ã—㟠'JsonSerializerContext' åž‹ã¨å«ã¾ã‚Œã¦ã„ã‚‹ã™ã¹ã¦ã®åž‹ã¯éƒ¨åˆ†çš„ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + {0} ã¨åå‰ãŒä»˜ã‘られãŸç¨®é¡žãŒè¤‡æ•°ã‚りã¾ã™ã€‚最åˆã«æ¤œå‡ºã•れãŸã‚‚ã®ã«å¯¾ã—ã¦ã‚½ãƒ¼ã‚¹ãŒç”Ÿæˆã•れã¾ã—ãŸã€‚ã“ã®å•題を解決ã™ã‚‹ã«ã¯ã€'JsonSerializableAttribute.TypeInfoPropertyName' を使用ã—ã¾ã™ã€‚ Duplicate type name. - Duplicate type name. + é‡è¤‡ã—ãŸç¨®é¡žå。 Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + '{0}'åž‹ ã®ã‚·ãƒªã‚¢ãƒ«åŒ–メタデータを生æˆã¾ã›ã‚“ã§ã—ãŸã€‚ Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + åž‹ã®ã‚·ãƒªã‚¢ãƒ«åŒ–メタデータãŒç”Ÿæˆã•れã¾ã›ã‚“。 diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf index e503e6b3c15..308d672c998 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + 파ìƒëœ 'JsonSerializerContext' 유형 '{0}'ì€(는) JSON ì§ë ¬í™” 가능한 ìœ í˜•ì„ ì§€ì •í•©ë‹ˆë‹¤. 소스 ìƒì„±ì„ 위해 유형 ë° ëª¨ë“  í¬í•¨ ìœ í˜•ì„ ë¶€ë¶„ì ìœ¼ë¡œ 만들어야 합니다. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + 파ìƒëœ 'JsonSerializerContext' 형ì‹ê³¼ í¬í•¨í•˜ëŠ” 모든 형ì‹ì€ 부분ì´ì–´ì•¼ 합니다. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + ì´ë¦„ì´ {0}ì¸ í˜•ì‹ì´ 여러 ê°œ 있습니다. ì²˜ìŒ ê²€ìƒ‰í•œ ì›ë³¸ì— 대해 ì›ë³¸ì´ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤. ì´ ì¶©ëŒì„ 해결하려면 'JsonSerializableAttribute.TypeInfoPropertyName'ì„ ì‚¬ìš©í•˜ì„¸ìš”. Duplicate type name. - Duplicate type name. + ì¤‘ë³µëœ í˜•ì‹ ì´ë¦„입니다. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + '{0}' 형ì‹ì— 대한 ì§ë ¬í™” 메타ë°ì´í„°ê°€ ìƒì„±ë˜ì§€ 않았습니다. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + 형ì‹ì— 대한 ì§ë ¬í™” 메타ë°ì´í„°ê°€ ìƒì„±ë˜ì§€ 않았습니다. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf index 728d28ae5d3..45865274732 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Pochodny typ "JsonSerializerContext" "{0}" okreÅ›la typy możliwe do serializacji w notacji JSON. Typ i wszystkie zawierajÄ…ce typy muszÄ… być częściowe, aby uruchomić generowanie źródÅ‚a. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Pochodne typy "JsonSerializerContext" i wszystkich zawierajÄ…ce typy muszÄ… być częściowe. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Istnieje wiele typów o nazwie {0}. Wygenerowano źródÅ‚o dla pierwszego wykrytego elementu. Aby rozwiÄ…zać tÄ™ kolizjÄ™, użyj „JsonSerializableAttribute. TypeInfoPropertyNameâ€. Duplicate type name. - Duplicate type name. + Zduplikowana nazwa typu. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Nie wygenerowano metadanych serializacji dla typu „{0}â€. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Nie wygenerowano metadanych serializacji dla typu. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf index b02aae15c20..cb75ff91ce3 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + O tipo 'JsonSerializerContext' derivado '{0}' especifica tipos serializáveis por JSON. O tipo e todos os tipos de contenção devem ser feitos parcialmente para iniciar a geração de origem. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Os tipos derivados de 'JsonSerializerContext' e todos os tipos contidos devem ser parciais. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + Existem vários tipos chamados {0}. A fonte foi gerada para o primeiro detectado. Use 'JsonSerializableAttribute.TypeInfoPropertyName' para resolver esta colisão. Duplicate type name. - Duplicate type name. + Nome de tipo duplicado. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Não gerou metadados de serialização para o tipo '{0}'. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Não gerou metadados de serialização para o tipo. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf index ebfd3c9e24e..cfe41af9643 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + Производный тип "JsonSerializerContext"{0}' указывает Ñериализуемые типы в формате JSON. Тип и вÑе Ñодержащие типы необходимо Ñделать чаÑтичными Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° генерации иÑточника. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + Производные типы "JsonSerializerContext" и вÑе Ñодержащие типы должны быть чаÑтичными. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + СущеÑтвует неÑколько типов Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ {0}. ИÑходный код Ñформирован Ð´Ð»Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ обнаруженного типа. ИÑпользуйте JsonSerializableAttribute.TypeInfoPropertyName Ð´Ð»Ñ ÑƒÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñтого конфликта. Duplicate type name. - Duplicate type name. + ДублирующееÑÑ Ð¸Ð¼Ñ Ñ‚Ð¸Ð¿Ð°. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + Метаданные Ñериализации Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° "{0}" не Ñформированы. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Метаданные Ñериализации Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° не Ñформированы. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf index 0e590487886..3cb4589cff7 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + TüretilmiÅŸ 'JsonSerializerContext' türü '{0}' JSON ile seri hale getirilebilir türleri belirtir. kaynak oluÅŸturmayı baÅŸlatmak için bu türün ve bu türü içeren tüm türlerin kısmi yapılması gerekir. Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + TüretilmiÅŸ 'JsonSerializerContext' türleri ve bunları içeren tüm türler kısmi olmalıdır. There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + {0} adını taşıyan birden çok tür var. Kaynak, algılanan ilk tür için oluÅŸturuldu. Bu çarpışmayı çözmek için 'JsonSerializableAttribute.TypeInfoPropertyName' özelliÄŸini kullanın. Duplicate type name. - Duplicate type name. + Yinelenen tür adı. Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + '{0}' türü için serileÅŸtirme meta verileri oluÅŸturulmadı. Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + Tür için serileÅŸtirme meta verileri oluÅŸturulmadı. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf index 27a56b787c1..bd0e02092b8 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + 派生的 “JsonSerializerContext†类型“{0}â€æŒ‡å®š JSON-serializable 类型。该类型和所有包å«ç±»åž‹å¿…须设为部分,以å¯åЍæºç”Ÿæˆã€‚ Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + 派生的 “JsonSerializerContextâ€ ç±»åž‹ä»¥åŠæ‰€æœ‰åŒ…å«ç±»åž‹å¿…须是部分。 There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + 有多个å为 {0} çš„ç±»åž‹ã€‚å·²ä¸ºç¬¬ä¸€ä¸ªæ£€æµ‹åˆ°ç±»åž‹çš„ç”Ÿæˆæºã€‚请使用 'JsonSerializableAttribute.TypeInfoPropertyName' 以解决此冲çªã€‚ Duplicate type name. - Duplicate type name. + é‡å¤çš„类型å称。 Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + 未生æˆç±»åž‹ '{0}' çš„åºåˆ—化元数æ®ã€‚ Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + 未生æˆç±»åž‹çš„åºåˆ—化元数æ®ã€‚ diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf index 9ba73dc3464..756866eb4f6 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -4,32 +4,32 @@ Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. - Derived 'JsonSerializerContext' type '{0}' specifies JSON-serializable types. The type and all containing types must be made partial to kick off source generation. + è¡ç”Ÿçš„ 'JsonSerializerContext' 型別 '{0}' 指定了 JSON å¯åºåˆ—化類型。類型和所有包å«é¡žåž‹å¿…須設為部份,æ‰å¯é–‹å§‹ç”¢ç”ŸåŽŸå§‹æª”ã€‚ Derived 'JsonSerializerContext' types and all containing types must be partial. - Derived 'JsonSerializerContext' types and all containing types must be partial. + è¡ç”Ÿçš„ 'JsonSerializerContext' 類型和所有包å«é¡žåž‹å¿…須是部份。 There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. - There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision. + 有多個å為 {0} çš„é¡žåž‹ã€‚å·²ç‚ºåµæ¸¬åˆ°çš„第一個項目產生來æºã€‚請使用 'JsonSerializableAttribute.TypeInfoPropertyName' 解決此è¡çªã€‚ Duplicate type name. - Duplicate type name. + é‡è¤‡é¡žåž‹å稱。 Did not generate serialization metadata for type '{0}'. - Did not generate serialization metadata for type '{0}'. + 未產生類型 '{0}' çš„åºåˆ—化中繼資料。 Did not generate serialization metadata for type. - Did not generate serialization metadata for type. + 未產生類型的åºåˆ—化中繼資料。 From 19b1b486258d5f3ff498919670b8e68f700b2819 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Thu, 15 Jul 2021 13:37:37 -0700 Subject: [PATCH 602/926] XmlSerializer dont skip date time offset (#55101) * Add DateTimeOffset as a primitive for XmlSerializer. * Fix DefaultValueAttribute handling for DTO. * Add tests for DateTimeAttribute and XmlSerializer. * Missed a 'KnownType' spot. * Update SGen tests to use current dotnet.exe from build. * Unbreak runtimeconfig creation for testing. * Debugging failure to resolve CoreCLR path. * Test fixup. * More test cleanup. * Temporarily disable generator tests. * Use InitObj instead of constructors for DateTimeOffset and TimeSpan. --- ...osoft.XmlSerializer.Generator.Tests.csproj | 1 + .../System/Xml/Serialization/CodeGenerator.cs | 13 ++++ .../Serialization/PrimitiveXmlSerializers.cs | 44 +++++++++++ .../ReflectionXmlSerializationReader.cs | 6 ++ .../ReflectionXmlSerializationWriter.cs | 1 + .../src/System/Xml/Serialization/Types.cs | 3 + .../Serialization/XmlSerializationReader.cs | 17 +++- .../XmlSerializationReaderILGen.cs | 14 ++-- .../Serialization/XmlSerializationWriter.cs | 23 ++++++ .../System/Xml/Serialization/XmlSerializer.cs | 8 ++ .../tests/XmlSerializer/XmlSerializerTests.cs | 78 +++++++++++++++++++ .../tests/SerializationTypes.cs | 16 ++++ 12 files changed, 213 insertions(+), 11 deletions(-) diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj b/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj index 10a6631611e..23f6b79d762 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj +++ b/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj @@ -3,6 +3,7 @@ $(DefineConstants);XMLSERIALIZERGENERATORTESTS $(NetCoreAppCurrent) true + true diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index ecd33f31633..8ce0a5cc9aa 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -853,6 +853,19 @@ namespace System.Xml.Serialization New(TimeSpan_ctor); break; } + else if (valueType == typeof(DateTimeOffset)) + { + ConstructorInfo DateTimeOffset_ctor = typeof(DateTimeOffset).GetConstructor( + CodeGenerator.InstanceBindingFlags, + null, + new Type[] { typeof(long), typeof(TimeSpan) }, + null + )!; + Ldc(((DateTimeOffset)o).Ticks); // ticks + Ldc(((DateTimeOffset)o).Offset); // offset + New(DateTimeOffset_ctor); + break; + } else { throw new NotSupportedException(SR.Format(SR.UnknownConstantType, valueType.AssemblyQualifiedName)); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs index e06311de6d6..ea5827445f0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs @@ -110,6 +110,18 @@ namespace System.Xml.Serialization WriteElementStringRaw(@"dateTime", @"", FromDateTime(((System.DateTime)o))); } + internal void Write_dateTimeOffset(object? o) + { + WriteStartDocument(); + if (o == null) + { + WriteEmptyTag(@"dateTimeOffset", @""); + return; + } + DateTimeOffset dto = (DateTimeOffset)o; + WriteElementStringRaw(@"dateTimeOffset", @"", System.Xml.XmlConvert.ToString(dto)); + } + internal void Write_unsignedByte(object? o) { WriteStartDocument(); @@ -454,6 +466,36 @@ namespace System.Xml.Serialization return (object?)o; } + internal object? Read_dateTimeOffset() + { + object? o = null; + Reader.MoveToContent(); + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (((object)Reader.LocalName == (object)_id20_dateTimeOffset && (object)Reader.NamespaceURI == (object)_id2_Item)) + { + if (Reader.IsEmptyElement) + { + Reader.Skip(); + o = default(DateTimeOffset); + } + else + { + o = System.Xml.XmlConvert.ToDateTimeOffset(Reader.ReadElementString()); + } + } + else + { + throw CreateUnknownNodeException(); + } + } + else + { + UnknownNode(null); + } + return (object?)o; + } + internal object? Read_unsignedByte() { object? o = null; @@ -720,6 +762,7 @@ namespace System.Xml.Serialization private string _id15_unsignedLong = null!; private string _id7_float = null!; private string _id10_dateTime = null!; + private string _id20_dateTimeOffset = null!; private string _id6_long = null!; private string _id9_decimal = null!; private string _id8_double = null!; @@ -743,6 +786,7 @@ namespace System.Xml.Serialization _id15_unsignedLong = Reader.NameTable.Add(@"unsignedLong"); _id7_float = Reader.NameTable.Add(@"float"); _id10_dateTime = Reader.NameTable.Add(@"dateTime"); + _id20_dateTimeOffset = Reader.NameTable.Add(@"dateTimeOffset"); _id6_long = Reader.NameTable.Add(@"long"); _id9_decimal = Reader.NameTable.Add(@"decimal"); _id8_double = Reader.NameTable.Add(@"double"); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index eca311b4d31..0c55638a27a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -884,6 +884,11 @@ namespace System.Xml.Serialization Reader.Skip(); value = default(TimeSpan); } + else if (element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset) && Reader.IsEmptyElement) + { + Reader.Skip(); + value = default(DateTimeOffset); + } else { if (element.Mapping.TypeDesc == QnameTypeDesc) @@ -1219,6 +1224,7 @@ namespace System.Xml.Serialization "Guid" => XmlConvert.ToGuid(value), "Char" => XmlConvert.ToChar(value), "TimeSpan" => XmlConvert.ToTimeSpan(value), + "DateTimeOffset" => XmlConvert.ToDateTimeOffset(value), _ => throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, $"unknown FormatterName: {mapping.TypeDesc.FormatterName}")), }; return retObj; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index c40076aa838..7969b74134e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -1206,6 +1206,7 @@ namespace System.Xml.Serialization "Guid" => XmlConvert.ToString((Guid)o), "Char" => XmlConvert.ToString((char)o), "TimeSpan" => XmlConvert.ToString((TimeSpan)o), + "DateTimeOffset" => XmlConvert.ToString((DateTimeOffset)o), _ => o.ToString()!, }; return stringValue; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs index 1971544633b..32373c9d0fc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs @@ -550,6 +550,7 @@ namespace System.Xml.Serialization AddNonXsdPrimitive(typeof(Guid), "guid", UrtTypes.Namespace, "Guid", new XmlQualifiedName("string", XmlSchema.Namespace), new XmlSchemaFacet[] { guidPattern }, TypeFlags.CanBeAttributeValue | TypeFlags.CanBeElementValue | TypeFlags.XmlEncodingNotRequired | TypeFlags.IgnoreDefault); AddNonXsdPrimitive(typeof(char), "char", UrtTypes.Namespace, "Char", new XmlQualifiedName("unsignedShort", XmlSchema.Namespace), Array.Empty(), TypeFlags.CanBeAttributeValue | TypeFlags.CanBeElementValue | TypeFlags.HasCustomFormatter | TypeFlags.IgnoreDefault); AddNonXsdPrimitive(typeof(TimeSpan), "TimeSpan", UrtTypes.Namespace, "TimeSpan", new XmlQualifiedName("duration", XmlSchema.Namespace), Array.Empty(), TypeFlags.CanBeAttributeValue | TypeFlags.CanBeElementValue | TypeFlags.XmlEncodingNotRequired); + AddNonXsdPrimitive(typeof(DateTimeOffset), "dateTimeOffset", UrtTypes.Namespace, "DateTimeOffset", new XmlQualifiedName("dateTime", XmlSchema.Namespace), Array.Empty(), TypeFlags.CanBeAttributeValue | TypeFlags.CanBeElementValue | TypeFlags.XmlEncodingNotRequired); AddSoapEncodedTypes(Soap.Encoding); @@ -596,6 +597,8 @@ namespace System.Xml.Serialization return true; else if (type == typeof(TimeSpan)) return true; + else if (type == typeof(DateTimeOffset)) + return true; else if (type == typeof(XmlNode[])) return true; break; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index 931735f60c6..0ea4307b42a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -105,6 +105,7 @@ namespace System.Xml.Serialization private string _charID = null!; private string _guidID = null!; private string _timeSpanID = null!; + private string _dateTimeOffsetID = null!; protected abstract void InitIDs(); @@ -214,6 +215,7 @@ namespace System.Xml.Serialization _charID = _r.NameTable.Add("char"); _guidID = _r.NameTable.Add("guid"); _timeSpanID = _r.NameTable.Add("TimeSpan"); + _dateTimeOffsetID = _r.NameTable.Add("dateTimeOffset"); _base64ID = _r.NameTable.Add("base64"); _anyURIID = _r.NameTable.Add("anyURI"); @@ -667,6 +669,8 @@ namespace System.Xml.Serialization value = new Guid(CollapseWhitespace(ReadStringValue())); else if ((object)type.Name == (object)_timeSpanID) value = XmlConvert.ToTimeSpan(ReadStringValue()); + else if ((object)type.Name == (object)_dateTimeOffsetID) + value = XmlConvert.ToDateTimeOffset(ReadStringValue()); else value = ReadXmlNodes(elementCanBeType); } @@ -764,6 +768,8 @@ namespace System.Xml.Serialization value = default(Nullable); else if ((object)type.Name == (object)_timeSpanID) value = default(Nullable); + else if ((object)type.Name == (object)_dateTimeOffsetID) + value = default(Nullable); else value = null; } @@ -4700,13 +4706,20 @@ namespace System.Xml.Serialization } Writer.Indent++; - if (element.Mapping.TypeDesc!.Type == typeof(TimeSpan)) + if (element.Mapping.TypeDesc!.Type == typeof(TimeSpan) || element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset)) { Writer.WriteLine("if (Reader.IsEmptyElement) {"); Writer.Indent++; Writer.WriteLine("Reader.Skip();"); WriteSourceBegin(source); - Writer.Write("default(System.TimeSpan)"); + if (element.Mapping.TypeDesc!.Type == typeof(TimeSpan)) + { + Writer.Write("default(System.TimeSpan)"); + } + else if (element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset)) + { + Writer.Write("default(System.DateTimeOffset)"); + } WriteSourceEnd(source); Writer.WriteLine(";"); Writer.Indent--; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs index 41c5c83506d..73af82643af 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs @@ -3096,7 +3096,7 @@ namespace System.Xml.Serialization { } - if (element.Mapping.TypeDesc!.Type == typeof(TimeSpan)) + if ((element.Mapping.TypeDesc!.Type == typeof(TimeSpan)) || element.Mapping.TypeDesc!.Type == typeof(DateTimeOffset)) { MethodInfo XmlSerializationReader_get_Reader = typeof(XmlSerializationReader).GetMethod( "get_Reader", @@ -3121,14 +3121,10 @@ namespace System.Xml.Serialization ilg.Ldarg(0); ilg.Call(XmlSerializationReader_get_Reader); ilg.Call(XmlReader_Skip); - ConstructorInfo TimeSpan_ctor = typeof(TimeSpan).GetConstructor( - CodeGenerator.InstanceBindingFlags, - null, - new Type[] { typeof(long) }, - null - )!; - ilg.Ldc(default(TimeSpan).Ticks); - ilg.New(TimeSpan_ctor); + LocalBuilder tmpLoc = ilg.GetTempLocal(element.Mapping.TypeDesc!.Type); + ilg.Ldloca(tmpLoc); + ilg.InitObj(element.Mapping.TypeDesc!.Type); + ilg.Ldloc(tmpLoc); WriteSourceEnd(source, element.Mapping.TypeDesc.Type); ilg.Else(); WriteSourceBegin(source); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs index 8308e0de386..25bac35aea2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs @@ -227,6 +227,11 @@ namespace System.Xml.Serialization typeName = "TimeSpan"; typeNs = UrtTypes.Namespace; } + else if (type == typeof(DateTimeOffset)) + { + typeName = "dateTimeOffset"; + typeNs = UrtTypes.Namespace; + } else if (type == typeof(XmlNode[])) { typeName = Soap.UrType; @@ -345,6 +350,12 @@ namespace System.Xml.Serialization type = "TimeSpan"; typeNs = UrtTypes.Namespace; } + else if (t == typeof(DateTimeOffset)) + { + value = XmlConvert.ToString((DateTimeOffset)o); + type = "dateTimeOffset"; + typeNs = UrtTypes.Namespace; + } else if (typeof(XmlNode[]).IsAssignableFrom(t)) { if (name == null) @@ -4321,6 +4332,18 @@ namespace System.Xml.Serialization Writer.Write(((DateTime)value).Ticks.ToString(CultureInfo.InvariantCulture)); Writer.Write(")"); } + else if (type == typeof(DateTimeOffset)) + { + Writer.Write(" new "); + Writer.Write(type.FullName); + Writer.Write("("); + Writer.Write(((DateTimeOffset)value).Ticks.ToString(CultureInfo.InvariantCulture)); + Writer.Write(", new "); + Writer.Write(((DateTimeOffset)value).Offset.GetType().FullName); + Writer.Write("("); + Writer.Write(((DateTimeOffset)value).Offset.Ticks.ToString(CultureInfo.InvariantCulture)); + Writer.Write("))"); + } else if (type == typeof(TimeSpan)) { Writer.Write(" new "); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index f0310fa2abb..049836211d7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -893,6 +893,10 @@ namespace System.Xml.Serialization { writer.Write_TimeSpan(o); } + else if (_primitiveType == typeof(DateTimeOffset)) + { + writer.Write_dateTimeOffset(o); + } else { throw new InvalidOperationException(SR.Format(SR.XmlUnxpectedType, _primitiveType!.FullName)); @@ -971,6 +975,10 @@ namespace System.Xml.Serialization { o = reader.Read_TimeSpan(); } + else if (_primitiveType == typeof(DateTimeOffset)) + { + o = reader.Read_dateTimeOffset(); + } else { throw new InvalidOperationException(SR.Format(SR.XmlUnxpectedType, _primitiveType!.FullName)); diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 6df78b2ab90..00011463d1e 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -751,6 +751,84 @@ string.Format(@" } } + [Fact] + public static void Xml_TypeWithDateTimeOffsetProperty() + { + var now = new DateTimeOffset(DateTime.Now); + var defDTO = default(DateTimeOffset); + var obj = new TypeWithDateTimeOffsetProperties { DTO = now }; + var deserializedObj = SerializeAndDeserialize(obj, +@" + + " + XmlConvert.ToString(now) + @" + " + XmlConvert.ToString(defDTO) + @" + + +"); + Assert.StrictEqual(obj.DTO, deserializedObj.DTO); + Assert.StrictEqual(obj.DTO2, deserializedObj.DTO2); + Assert.StrictEqual(defDTO, deserializedObj.DTO2); + Assert.StrictEqual(obj.DTOWithDefault, deserializedObj.DTOWithDefault); + Assert.StrictEqual(defDTO, deserializedObj.DTOWithDefault); + Assert.StrictEqual(obj.NullableDTO, deserializedObj.NullableDTO); + Assert.True(deserializedObj.NullableDTO == null); + Assert.StrictEqual(obj.NullableDTOWithDefault, deserializedObj.NullableDTOWithDefault); + Assert.True(deserializedObj.NullableDTOWithDefault == null); + } + + [Fact] + public static void Xml_DeserializeTypeWithEmptyDateTimeOffsetProperties() + { + //var def = DateTimeOffset.Parse("3/17/1977 5:00:01 PM -05:00"); // "1977-03-17T17:00:01-05:00" + var defDTO = default(DateTimeOffset); + string xml = @" + + + + + + "; + XmlSerializer serializer = new XmlSerializer(typeof(TypeWithDateTimeOffsetProperties)); + + using (StringReader reader = new StringReader(xml)) + { + TypeWithDateTimeOffsetProperties deserializedObj = (TypeWithDateTimeOffsetProperties)serializer.Deserialize(reader); + Assert.NotNull(deserializedObj); + Assert.Equal(defDTO, deserializedObj.DTO); + Assert.Equal(defDTO, deserializedObj.DTO2); + Assert.Equal(defDTO, deserializedObj.DTOWithDefault); + Assert.True(deserializedObj.NullableDTO == null); + Assert.Equal(defDTO, deserializedObj.NullableDTOWithDefault); + } + } + + [Fact] + public static void Xml_DeserializeDateTimeOffsetType() + { + var now = new DateTimeOffset(DateTime.Now); + string xml = @"" + now.ToString("o") + ""; + XmlSerializer serializer = new XmlSerializer(typeof(DateTimeOffset)); + + using (StringReader reader = new StringReader(xml)) + { + DateTimeOffset deserializedObj = (DateTimeOffset)serializer.Deserialize(reader); + Assert.Equal(now, deserializedObj); + } + } + + [Fact] + public static void Xml_DeserializeEmptyDateTimeOffsetType() + { + string xml = @""; + XmlSerializer serializer = new XmlSerializer(typeof(DateTimeOffset)); + + using (StringReader reader = new StringReader(xml)) + { + DateTimeOffset deserializedObj = (DateTimeOffset)serializer.Deserialize(reader); + Assert.Equal(default(DateTimeOffset), deserializedObj); + } + } + [Fact] public static void Xml_TypeWithByteProperty() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index 6c8da649d56..bc10a370b03 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -900,6 +900,22 @@ public class TypeWithBinaryProperty public byte[] Base64Content { get; set; } } +public class TypeWithDateTimeOffsetProperties +{ + public DateTimeOffset DTO { get; set; } + public DateTimeOffset DTO2 { get; set; } + + [XmlElement(ElementName = "DefaultDTO")] + [DefaultValue(typeof(DateTimeOffset), "1/1/0001 0:00:00 AM +00:00")] + public DateTimeOffset DTOWithDefault { get; set; } + + public DateTimeOffset? NullableDTO { get; set; } + + [XmlElement(ElementName = "NullableDefaultDTO")] + [DefaultValue(typeof(DateTimeOffset), "1/1/0001 0:00:00 AM +00:00")] + public DateTimeOffset? NullableDTOWithDefault { get; set; } +} + public class TypeWithTimeSpanProperty { public TimeSpan TimeSpanProperty; From bd416d7bca63c0411547750a050aa57a902ba2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 15 Jul 2021 22:40:54 +0200 Subject: [PATCH 603/926] Add option to MonoAOTCompiler msbuild task to generate .so files (#55753) --- src/mono/mono/mini/aot-runtime.c | 2 +- .../sample/Android/AndroidSampleApp.csproj | 24 ++- src/mono/sample/Android/Makefile | 3 + src/tasks/AndroidAppBuilder/ApkBuilder.cs | 36 +++- .../AndroidAppBuilder/Templates/monodroid.c | 5 +- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 184 ++++++++++++++++-- .../AotCompilerTask/MonoAOTCompiler.props | 2 +- 7 files changed, 221 insertions(+), 35 deletions(-) diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 5efa6184fb6..bad49486d99 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -1965,7 +1965,7 @@ load_aot_module (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, gpointer g_free (err); } g_free (aot_name); -#if !defined(PLATFORM_ANDROID) && !defined(TARGET_WASM) +#if !defined(HOST_ANDROID) && !defined(HOST_WASM) if (!sofile) { char *basename = g_path_get_basename (assembly->image->name); aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, basename, MONO_SOLIB_EXT); diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index 8fb25b90b71..e85e49d7b3b 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -46,13 +46,33 @@ + + <_AotOutputType>AsmOnly + <_AotModulesTablePath>$(BundleDir)\modules.c + + + + <_AotOutputType>Library + <_AotLibraryFormat>So + <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-x86_64 + <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">darwin-x86_64 + <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows-x86_64 + <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm'">arm-linux-androideabi$ + <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android + <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android + <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android + <_AotToolPrefix>$(ANDROID_NDK_ROOT)\toolchains\llvm\prebuilt\$(_PrebuiltOS)\bin\$(_PrebuiltAbi)- + + diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 6d59ee860fe..cbcf63db861 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -3,6 +3,7 @@ MONO_ARCH?=x64 DOTNET := ../../../../dotnet.sh USE_LLVM=true AOT=false +AOT_WITH_LIBRARY_FILES=false INTERP=false DEPLOY_AND_RUN?=true @@ -26,7 +27,9 @@ run: /p:TargetOS=Android \ /p:Configuration=$(MONO_CONFIG) \ /p:DeployAndRun=$(DEPLOY_AND_RUN) \ + /p:RunAOTCompilation=$(AOT) \ /p:ForceAOT=$(AOT) \ + /p:AOTWithLibraryFiles=$(AOT_WITH_LIBRARY_FILES) \ /p:MonoForceInterpreter=$(INTERP) \ /p:UseLLVM=$(USE_LLVM) \ /p:RunActivity=false \ diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index b979ba316b3..23475c9e1e6 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -125,11 +125,13 @@ public class ApkBuilder var assemblerFiles = new StringBuilder(); var assemblerFilesToLink = new StringBuilder(); + var aotLibraryFiles = new List(); foreach (ITaskItem file in Assemblies) { // use AOT files if available var obj = file.GetMetadata("AssemblerFile"); var llvmObj = file.GetMetadata("LlvmObjectFile"); + var lib = file.GetMetadata("LibraryFile"); if (!string.IsNullOrEmpty(obj)) { @@ -143,9 +145,14 @@ public class ApkBuilder var name = Path.GetFileNameWithoutExtension(llvmObj); assemblerFilesToLink.AppendLine($" {llvmObj}"); } + + if (!string.IsNullOrEmpty(lib)) + { + aotLibraryFiles.Add(lib); + } } - if (ForceAOT && assemblerFiles.Length == 0) + if (ForceAOT && assemblerFiles.Length == 0 && aotLibraryFiles.Count == 0) { throw new InvalidOperationException("Need list of AOT files."); } @@ -165,7 +172,8 @@ public class ApkBuilder // Copy sourceDir to OutputDir/assets-tozip (ignore native files) // these files then will be zipped and copied to apk/assets/assets.zip - Utils.DirectoryCopy(AppDir, Path.Combine(OutputDir, "assets-tozip"), file => + var assetsToZipDirectory = Path.Combine(OutputDir, "assets-tozip"); + Utils.DirectoryCopy(AppDir, assetsToZipDirectory, file => { string fileName = Path.GetFileName(file); string extension = Path.GetExtension(file); @@ -184,6 +192,12 @@ public class ApkBuilder return true; }); + // add AOT .so libraries + foreach (var aotlib in aotLibraryFiles) + { + File.Copy(aotlib, Path.Combine(assetsToZipDirectory, Path.GetFileName(aotlib))); + } + // tools: string dx = Path.Combine(buildToolsFolder, "dx"); string aapt = Path.Combine(buildToolsFolder, "aapt"); @@ -195,8 +209,8 @@ public class ApkBuilder string cmake = "cmake"; string zip = "zip"; - Utils.RunProcess(zip, workingDir: Path.Combine(OutputDir, "assets-tozip"), args: "-q -r ../assets/assets.zip ."); - Directory.Delete(Path.Combine(OutputDir, "assets-tozip"), true); + Utils.RunProcess(zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip ."); + Directory.Delete(assetsToZipDirectory, true); if (!File.Exists(androidJar)) throw new ArgumentException($"API level={BuildApiLevel} is not downloaded in Android SDK"); @@ -283,22 +297,26 @@ public class ApkBuilder .Replace("%AotSources%", aotSources) .Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c"); - string defines = ""; + var defines = new StringBuilder(); if (ForceInterpreter) { - defines = "add_definitions(-DFORCE_INTERPRETER=1)"; + defines.AppendLine("add_definitions(-DFORCE_INTERPRETER=1)"); } else if (ForceAOT) { - defines = "add_definitions(-DFORCE_AOT=1)"; + defines.AppendLine("add_definitions(-DFORCE_AOT=1)"); + if (aotLibraryFiles.Count == 0) + { + defines.AppendLine("add_definitions(-DSTATIC_AOT=1)"); + } } if (!string.IsNullOrEmpty(DiagnosticPorts)) { - defines += "\nadd_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")"; + defines.AppendLine("add_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")"); } - cmakeLists = cmakeLists.Replace("%Defines%", defines); + cmakeLists = cmakeLists.Replace("%Defines%", defines.ToString()); File.WriteAllText(Path.Combine(OutputDir, "CMakeLists.txt"), cmakeLists); diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c index 7bd0464075e..4ed02eb2166 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c @@ -191,7 +191,7 @@ log_callback (const char *log_domain, const char *log_level, const char *message } } -#if FORCE_AOT +#if defined(FORCE_AOT) && defined(STATIC_AOT) void register_aot_modules (void); #endif @@ -270,7 +270,10 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed LOG_INFO("Interp Enabled"); mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY); #elif FORCE_AOT + LOG_INFO("AOT Enabled"); +#if STATIC_AOT register_aot_modules(); +#endif mono_jit_set_aot_mode(MONO_AOT_MODE_FULL); #endif diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index cb3876cfc9c..259622da707 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -48,13 +48,19 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task [Required] public string? OutputDir { get; set; } + /// + /// Target triple passed to the AOT compiler. + /// + public string? Triple { get; set; } + /// /// Assemblies which were AOT compiled. /// /// Successful AOT compilation will set the following metadata on the items: /// - AssemblerFile (when using OutputType=AsmOnly) /// - ObjectFile (when using OutputType=Normal) - /// - AotDataFile + /// - LibraryFile (when using OutputType=Library) + /// - AotDataFile (when using UseAotDataFile=true) /// - LlvmObjectFile (if using LLVM) /// - LlvmBitcodeFile (if using LLVM-only) /// @@ -73,13 +79,37 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public bool UseLLVM { get; set; } /// - /// Use a separate .aotdata file for the AOT data. + /// This instructs the AOT code generator to output certain data constructs into a separate file. This can reduce the executable images some five to twenty percent. + /// Developers need to then ship the resulting aotdata as a resource and register a hook to load the data on demand by using the mono_install_load_aot_data_hook() method. /// Defaults to true. /// public bool UseAotDataFile { get; set; } = true; /// - /// File to use for profile-guided optimization. + /// Create an ELF object file (.o) or .s file which can be statically linked into an executable when embedding the mono runtime. + /// Only valid if OutputType is ObjectFile or AsmOnly. + /// + public bool UseStaticLinking { get; set; } + + /// + /// When this option is specified, icalls (internal calls made from the standard library into the mono runtime code) are invoked directly instead of going through the operating system symbol lookup operation. + /// This requires UseStaticLinking=true. + /// + public bool UseDirectIcalls { get; set; } + + /// + /// When this option is specified, P/Invoke methods are invoked directly instead of going through the operating system symbol lookup operation + /// This requires UseStaticLinking=true. + /// + public bool UseDirectPInvoke { get; set; } + + /// + /// Instructs the AOT compiler to emit DWARF debugging information. + /// + public bool UseDwarfDebug { get; set; } + + /// + /// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled. /// public string? AotProfilePath { get; set; } @@ -89,8 +119,8 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string[]? Profilers { get; set; } /// - /// Generate a file containing mono_aot_register_module() calls for each AOT module - /// Defaults to false. + /// Generate a file containing mono_aot_register_module() calls for each AOT module which can be compiled into the app embedding mono. + /// If set, this implies UseStaticLinking=true. /// public string? AotModulesTablePath { get; set; } @@ -101,7 +131,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string? AotModulesTableLanguage { get; set; } = nameof(MonoAotModulesTableLanguage.C); /// - /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'LLVMOnly', 'LLVMOnlyInterp'. + /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'Hybrid', 'LLVMOnly', 'LLVMOnlyInterp'. /// LLVMOnly means to use only LLVM for FullAOT, AOT result will be a LLVM Bitcode file (the cross-compiler must be built with LLVM support) /// The "interp" options ('LLVMOnlyInterp' and 'FullInterp') mean generate necessary support to fall back to interpreter if AOT code is not possible for some methods. /// The difference between 'JustInterp' and 'FullInterp' is that 'FullInterp' will AOT all the methods in the given assemblies, while 'JustInterp' will only AOT the wrappers and trampolines necessary for the runtime to execute the managed methods using the interpreter and to interoperate with P/Invokes and unmanaged callbacks. @@ -109,10 +139,21 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string Mode { get; set; } = nameof(MonoAotMode.Normal); /// - /// Choose between 'Normal', 'AsmOnly' - /// AsmOnly means the AOT compiler will produce .s assembly code instead of an .o object file. + /// Choose between 'ObjectFile', 'AsmOnly', 'Library' + /// ObjectFile means the AOT compiler will produce an .o object file, AsmOnly will produce .s assembly code and Library will produce a .so/.dylib/.dll shared library. /// - public string OutputType { get; set; } = nameof(MonoAotOutputType.Normal); + public string OutputType { get; set; } = nameof(MonoAotOutputType.ObjectFile); + + /// + /// Choose between 'Dll', 'Dylib', 'So'. Only valid if OutputType is Library. + /// Dll means the AOT compiler will produce a Windows PE .dll file, Dylib means an Apple Mach-O .dylib and So means a Linux/Android ELF .so file. + /// + public string? LibraryFormat { get; set; } + + /// + /// Prefix that will be added to the library file name, e.g. to add 'lib' prefix required by some platforms. Only valid if OutputType is Library. + /// + public string LibraryFilePrefix { get; set; } = ""; /// /// Path to the directory where LLVM binaries (opt and llc) are found. @@ -120,6 +161,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task /// public string? LLVMPath { get; set; } + /// + /// Prepends a prefix to the name of tools ran by the AOT compiler, i.e. 'as'/'ld'. + /// + public string? ToolPrefix { get; set; } + /// /// Path to the directory where msym artifacts are stored. /// @@ -143,6 +189,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task private ConcurrentBag compiledAssemblies = new ConcurrentBag(); private MonoAotMode parsedAotMode; private MonoAotOutputType parsedOutputType; + private MonoAotLibraryFormat parsedLibraryFormat; private MonoAotModulesTableLanguage parsedAotModulesTableLanguage; public override bool Execute() @@ -200,10 +247,25 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task switch (OutputType) { - case "Normal": parsedOutputType = MonoAotOutputType.Normal; break; + case "ObjectFile": parsedOutputType = MonoAotOutputType.ObjectFile; break; case "AsmOnly": parsedOutputType = MonoAotOutputType.AsmOnly; break; + case "Library": parsedOutputType = MonoAotOutputType.Library; break; + case "Normal": + Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead."); + parsedOutputType = MonoAotOutputType.ObjectFile; break; default: - throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.Normal)}', '{nameof(MonoAotOutputType.AsmOnly)}'. Received: '{OutputType}'.", nameof(OutputType)); + throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.", nameof(OutputType)); + } + + switch (LibraryFormat) + { + case "Dll": parsedLibraryFormat = MonoAotLibraryFormat.Dll; break; + case "Dylib": parsedLibraryFormat = MonoAotLibraryFormat.Dylib; break; + case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break; + default: + if (parsedOutputType == MonoAotOutputType.Library) + throw new ArgumentException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.", nameof(LibraryFormat)); + break; } if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM) @@ -219,8 +281,29 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task throw new ArgumentException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.", nameof(AotModulesTableLanguage)); } - if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(Assemblies, Profilers)) - return false; + if (!string.IsNullOrEmpty(AotModulesTablePath)) + { + // AOT modules for static linking, needs the aot modules table + UseStaticLinking = true; + + if (!GenerateAotModulesTable(Assemblies, Profilers)) + return false; + } + + if (UseDirectIcalls && !UseStaticLinking) + { + throw new ArgumentException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectIcalls)); + } + + if (UseDirectPInvoke && !UseStaticLinking) + { + throw new ArgumentException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectPInvoke)); + } + + if (UseStaticLinking && (parsedOutputType == MonoAotOutputType.Library)) + { + throw new ArgumentException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.", nameof(OutputType)); + } string? monoPaths = null; if (AdditionalAssemblySearchPaths != null) @@ -287,6 +370,26 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task processArgs.Add("--nollvm"); } + if (UseStaticLinking) + { + aotArgs.Add($"static"); + } + + if (UseDwarfDebug) + { + aotArgs.Add($"dwarfdebug"); + } + + if (!string.IsNullOrEmpty(Triple)) + { + aotArgs.Add($"mtriple={Triple}"); + } + + if (!string.IsNullOrEmpty(ToolPrefix)) + { + aotArgs.Add($"tool-prefix={ToolPrefix}"); + } + string assemblyFilename = Path.GetFileName(assembly); if (isDedup) @@ -328,12 +431,23 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task aotArgs.Add("full"); } + if (parsedAotMode == MonoAotMode.Hybrid) + { + aotArgs.Add("hybrid"); + } + if (parsedAotMode == MonoAotMode.FullInterp || parsedAotMode == MonoAotMode.JustInterp) { aotArgs.Add("interp"); } - if (parsedOutputType == MonoAotOutputType.AsmOnly) + if (parsedOutputType == MonoAotOutputType.ObjectFile) + { + string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o")); + aotArgs.Add($"outfile={objectFile}"); + aotAssembly.SetMetadata("ObjectFile", objectFile); + } + else if (parsedOutputType == MonoAotOutputType.AsmOnly) { aotArgs.Add("asmonly"); @@ -341,11 +455,19 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task aotArgs.Add($"outfile={assemblerFile}"); aotAssembly.SetMetadata("AssemblerFile", assemblerFile); } - else + else if (parsedOutputType == MonoAotOutputType.Library) { - string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o")); - aotArgs.Add($"outfile={objectFile}"); - aotAssembly.SetMetadata("ObjectFile", objectFile); + string extension = parsedLibraryFormat switch { + MonoAotLibraryFormat.Dll => ".dll", + MonoAotLibraryFormat.Dylib => ".dylib", + MonoAotLibraryFormat.So => ".so", + _ => throw new ArgumentOutOfRangeException() + }; + string libraryFileName = $"{LibraryFilePrefix}{assemblyFilename}{extension}"; + string libraryFilePath = Path.Combine(OutputDir, libraryFileName); + + aotArgs.Add($"outfile={libraryFilePath}"); + aotAssembly.SetMetadata("LibraryFile", libraryFilePath); } if (UseLLVM) @@ -405,7 +527,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task else { paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}"; - processArgs.Add(assemblyFilename); + processArgs.Add('"' + assemblyFilename + '"'); } var envVariables = new Dictionary @@ -414,11 +536,20 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task {"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers }; + var responseFileContent = string.Join(" ", processArgs); + var responseFilePath = Path.GetTempFileName(); + using (var sw = new StreamWriter(responseFilePath, append: false, encoding: new UTF8Encoding(false))) + { + sw.WriteLine(responseFileContent); + } + + Log.LogMessage(MessageImportance.Low, $"AOT compiler arguments: {responseFileContent}"); + try { // run the AOT compiler (int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath, - string.Join(" ", processArgs), + $"--response=\"{responseFilePath}\"", envVariables, assemblyDir, silent: false, @@ -436,6 +567,8 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task return false; } + File.Delete(responseFilePath); + compiledAssemblies.Add(aotAssembly); return true; } @@ -560,14 +693,23 @@ public enum MonoAotMode JustInterp, Full, FullInterp, + Hybrid, LLVMOnly, LLVMOnlyInterp } public enum MonoAotOutputType { - Normal, + ObjectFile, AsmOnly, + Library, +} + +public enum MonoAotLibraryFormat +{ + Dll, + Dylib, + So, } public enum MonoAotModulesTableLanguage diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index c6b2f2ad28e..4f2721d7483 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -20,7 +20,7 @@ - + From e9cd803a1279c9915db9fd763866b614cb0a7ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 15 Jul 2021 16:44:40 -0400 Subject: [PATCH 604/926] [class] Add GC Unsafe transitions to a few more external only functions (#55748) --- src/mono/mono/metadata/class.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 957ea8f8450..c3d6d53893f 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -2965,9 +2965,12 @@ mono_type_get_checked (MonoImage *image, guint32 type_token, MonoGenericContext MonoClass * mono_class_get (MonoImage *image, guint32 type_token) { + MonoClass *result; + MONO_ENTER_GC_UNSAFE; ERROR_DECL (error); - MonoClass *result = mono_class_get_checked (image, type_token, error); + result = mono_class_get_checked (image, type_token, error); mono_error_assert_ok (error); + MONO_EXIT_GC_UNSAFE; return result; } @@ -4585,9 +4588,12 @@ gpointer mono_ldtoken (MonoImage *image, guint32 token, MonoClass **handle_class, MonoGenericContext *context) { + gpointer res; + MONO_ENTER_GC_UNSAFE; ERROR_DECL (error); - gpointer res = mono_ldtoken_checked (image, token, handle_class, context, error); + res = mono_ldtoken_checked (image, token, handle_class, context, error); mono_error_assert_ok (error); + MONO_EXIT_GC_UNSAFE; return res; } @@ -5283,7 +5289,11 @@ mono_class_is_delegate (MonoClass *klass) mono_bool mono_class_implements_interface (MonoClass* klass, MonoClass* iface) { - return mono_class_is_assignable_from_internal (iface, klass); + mono_bool result; + MONO_ENTER_GC_UNSAFE; + result = mono_class_is_assignable_from_internal (iface, klass); + MONO_EXIT_GC_UNSAFE; + return result; } static mono_bool From bcc794492e076dff00592a152c0725058a24362c Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 15 Jul 2021 16:49:16 -0400 Subject: [PATCH 605/926] [wasm] Misc follow up improvements for workloads testing (#55647) * [wasm] Move UseMonoRuntime=true to WasmApp.props * Move workloads installation for testing bits to .. `src/libraries/workloads-testing.targets`. * [wasm] EmccCompile: don't log `stderr` messages as warnings emcc emits some (debug?) messages on stderr too, which are not always errors. Emitting them as warning can be break the build too, when running with warningsAsErrors . * [wasm] Fix helix work item prefix * improve readability of messages from parallel EmccCompile * Improve emscripten version mismatch message, and convert to a warning * Fix typo, so WBT runs can be differentiated correctly --- src/libraries/Directory.Build.targets | 87 +------------------ src/libraries/sendtohelixhelp.proj | 6 +- src/libraries/workloads-testing.targets | 86 ++++++++++++++++++ .../Sdk/Sdk.props | 4 - src/mono/wasm/build/WasmApp.LocalBuild.props | 3 - .../wasm/build/WasmApp.LocalBuild.targets | 1 - src/mono/wasm/build/WasmApp.Native.targets | 3 +- src/mono/wasm/build/WasmApp.props | 1 + src/tasks/Common/Utils.cs | 69 ++++++++++----- src/tasks/WasmAppBuilder/EmccCompile.cs | 13 ++- 10 files changed, 148 insertions(+), 125 deletions(-) create mode 100644 src/libraries/workloads-testing.targets diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index dd17dace8e4..8d9099fba2e 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -335,90 +335,5 @@ Text="Analyzers must only target netstandard2.0 since they run in the compiler which targets netstandard2.0. The following files were found to target '%(_AnalyzerPackFile.TargetFramework)': @(_AnalyzerPackFile)" /> - - - - - - - - - - - - - <_DotNetInstallScriptPath Condition="!$([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.sh - <_DotNetInstallScriptPath Condition=" $([MSBuild]::IsOSPlatform('windows'))">$(RepoRoot).dotnet\dotnet-install.ps1 - - - - - - - - - - - - - - - - - - - - - - - - - <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> - <_PropsForAOTCrossBuild Include="Configuration=$(Configuration)" /> - <_PropsForAOTCrossBuild Include="TargetOS=Browser" /> - <_PropsForAOTCrossBuild Include="TargetArchitecture=wasm" /> - <_PropsForAOTCrossBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> - - <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> - - - - - - <_NuGetSourceForWorkloads Include="dotnet6" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" /> - <_BuiltNuGets Include="$(LibrariesShippingPackagesDir)\*.nupkg" /> - - - - - <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersion).nupkg - - - - - - - - - - + diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 572481d7ccb..af2885bbecd 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -317,8 +317,8 @@ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'RemoteLoopServer', '$(NetCoreAppCurrent)-$(Configuration)')) - Workloads/ - EMSDK/ + Workloads- + EMSDK- @@ -377,7 +377,7 @@ <_WorkItem Include="$(TestArchiveRoot)runonly/**/WebAssembly.Browser.*.Test.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmTestOnBrowser'" /> <_WorkItem Include="$(TestArchiveRoot)browseronly/**/*.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmTestOnBrowser'" /> - + %(Identity) $(HelixCommand) $(_workItemTimeout) diff --git a/src/libraries/workloads-testing.targets b/src/libraries/workloads-testing.targets new file mode 100644 index 00000000000..d08c5957525 --- /dev/null +++ b/src/libraries/workloads-testing.targets @@ -0,0 +1,86 @@ + + + + + + + + + + + + <_DotNetInstallScriptPath Condition="!$([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.sh + <_DotNetInstallScriptPath Condition=" $([MSBuild]::IsOSPlatform('windows'))">$(RepoRoot).dotnet\dotnet-install.ps1 + + + + + + + + + + + + + + + + + + + + + + + + + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="Configuration=$(Configuration)" /> + <_PropsForAOTCrossBuild Include="TargetOS=Browser" /> + <_PropsForAOTCrossBuild Include="TargetArchitecture=wasm" /> + <_PropsForAOTCrossBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> + + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> + + + + + + <_NuGetSourceForWorkloads Include="dotnet6" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" /> + <_BuiltNuGets Include="$(LibrariesShippingPackagesDir)\*.nupkg" /> + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersion).nupkg + + + + + + + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props index a8050972730..d292fe15f27 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props +++ b/src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/Sdk/Sdk.props @@ -1,8 +1,4 @@ - - true - - diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.props b/src/mono/wasm/build/WasmApp.LocalBuild.props index df76040d2c7..ea5624a1591 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.props +++ b/src/mono/wasm/build/WasmApp.LocalBuild.props @@ -22,9 +22,6 @@ - - true - <_NetCoreAppToolCurrent>net6.0 diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets index ae05dae8a55..c5f431d7d69 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.targets +++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets @@ -26,7 +26,6 @@ true link - true diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 3bb2154a4cd..615d8f0f1c5 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -339,7 +339,8 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ %(_ReversedVersionLines.Identity) - + <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" /> @@ -258,8 +267,9 @@ <_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" /> <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" /> - - <_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> + <_WasmNativeFileForLinking + Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" + Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" /> <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> From bcb1042f809b8502fc5d7151efa5893207806ed8 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 15 Jul 2021 18:09:02 -0400 Subject: [PATCH 608/926] Use pbeWithSHAAnd3-KeyTripleDES-CBC for SignedXml certificate. This changes the PKCS12 from being using RC2 to 3DES so that the test passes on Android, which does not support RC2. --- .../tests/TestHelpers.cs | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs index bbd5913d367..5e2074eaaa3 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs @@ -193,37 +193,37 @@ namespace System.Security.Cryptography.Xml.Tests private static readonly byte[] SampleDsaPfx = Convert.FromBase64String( // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy certificate.")] "MIIF+QIBAzCCBb8GCSqGSIb3DQEHAaCCBbAEggWsMIIFqDCCA9cGCSqGSIb3DQEH" + - "BqCCA8gwggPEAgEAMIIDvQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI8liC" + - "7ThjN10CAggAgIIDkFQEBVf6+23WfPd7Rn38z2zvzNEeq5ddwJVQQVctbFTaemHH" + - "15ygd126N+BVw5+2RX8SzDb9qwzHnBUgQQ9WThW3i2JDB9wpE94KJOKYbFTy5hOO" + - "eDw1rf+dbS89jRtWkG3kOxQpgr2I8B/MX4IUgb8SFn/Uqd3V/FcYAPBiBiZxPdHX" + - "QXruCNKXjJDeO5lyvXpB2mN4cf6uXrF44tGbKzmdS2+iFg5xmx7E3KB9CGkH6FmK" + - "O9vG35Hqe+3ZmuAR9qnJAOk9ja3Rx5XuQ248a68MZuPSo2GxMLUXh0BH1aimTHH2" + - "YQz6RzD3jBVIujZ3fhecY6BDCHOZKSjkQEnJAl/K0nzeeKaKN/biK+PouO8qlSnR" + - "lwjBng4XeolCwEE6S3EiingOZRq/IYRkwM6YUfCCsHNRnCALprxuYWRj/5vTiYD8" + - "uIIGLTimOynTTh5tOmJiCHzoEmpq0nGtM0fekbgc+xVsslsT10TtdjjRRK26aw/M" + - "BwkZ7MiPjyTKxJ21zTl0Jc6DcoZK1CKOBy4mG1VE2hZP8oGvNmy1KUZGKeFHS9dA" + - "EjMqhkqPTSOL8wZYenPhaD3dWEWX28mFib9WSy/o6n5U37bwRgI/SxeNt5Q3qbuu" + - "KNo9bC8XGnRl+AaoeUsl62353kZ7SXGTovWSSQ482CK7QMrfKU4MID0IhFxggkNP" + - "GkSYZsIGXC6fVcYjsyzdo/q4NwY3+ysCBVi0294ECvd8cAppXSRXFAjGDwtnBPQC" + - "XTN831Op9gEzId1NTFhhOvYl6S+yXJcmJ52aFj7mB58dNa1qXHmVLug70PNp4Z01" + - "ELYQ5psdEK8r2/pNM8X+e9SZETCzt5jBd5l5kQ+FB5byO2v2nVJ1hnASG3ckGDS0" + - "dl5mW1Y+qa91tu4M5AwDe0ECGirROr3qsP1w2RtPPBgYvGD1SiKp7yZ+2GVUaJEs" + - "pGlmNt90wA6ddjXkO/av3nYWhWSjI2qkNPHFcqOzFsNXsoilgbnUUImfth7U81d4" + - "hHJ6cOgDNfN+sxv6pL3nt12eK24lW2dhetyedr3lN8q6bNkC5l+Oq89CTyz0MlZk" + - "dT/eS5pHsXMFXRDF+ZIljoea13J4jRdVZeZJsqIoql3qdY1SvnyUSyRJR/VfBGWD" + - "aIJjmwG/z5VVjKPklpB72SkqVWyzLF/Vra/rqy1TlFYAZ91eYAQJ3faezB251bKl" + - "80rAXicKdsLMHEhsdjCCAckGCSqGSIb3DQEHAaCCAboEggG2MIIBsjCCAa4GCyqG" + - "SIb3DQEMCgECoIIBdjCCAXIwHAYKKoZIhvcNAQwBAzAOBAh2G75W3/WqEAICCAAE" + - "ggFQ/bprf5JYknV8tp8mcxXphVVPmWW6PxeB9YzLngbSKO7KTd8pLxG8m6eDrCbf" + - "fkgffdT+peGSUWYAu9/RgG2x1AdNTd4s9BgQ8y4nDbB7NV1BW7Cm7VOcYR6/+H+2" + - "CfNU4gPWKBwSFiZfC55/cdIkg4fh9UeoS+ImEApDQu7Thzjc+pVUEiCgnDA83wEs" + - "h7oP63eZhaH5WtPrsnOzpYW3tjLdC59LHe4W1goG8wp552MHxnOiHXZXbgNHdmwx" + - "QexumEv5CHbjW6f7NYFMBycYSPkCH22A8DCwIrid+hcvMPlsTR4Z2wL8PTk9zmi0" + - "R5daKIKRQts6hjPQH8VIXuFeb545i8U1lDw3MiF17Dp2z8vAr3TRZswN979mLVfB" + - "jyuLJ/nIyLLEvpc3iIbSsoGGa5+VxQhk0Wal0PLGdzZ/Y2QhBhixeq94JB0w2fo7" + - "Ljj3MSUwIwYJKoZIhvcNAQkVMRYEFDdYxa4ZJsCeAQZDzHLZ+cdpC2z5MDEwITAJ" + - "BgUrDgMCGgUABBQrvSbTT08JKqS+yVmoU8tj2gzcogQIGnaqGGnaa+ACAggA"); + "BqCCA8gwggPEAgEAMIIDvQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQIbzDq" + + "jr8PwpMCAggAgIIDkJrcDboxMkMgEusycJ0w7QdbskZ7SBvnziqhQ0e2DohHjhrm" + + "p19VRk8nyw0TuHgIVK39KhFbGrfOLWxAE2xvnDVOeWWMLcxDn2zoxPdM202HvXVB" + + "SFE0zAmVOVHD7NMa0pcXeCblqpihGnVgSW5RYHc3hJFaXkBmLe/GLX7rNl+LCXzb" + + "cScJfYw0CMqZHYnpcCG3M6n6aAUb79madtN7UHuVtyaCjStaPfcAUGetlacT79cV" + + "qR57yPG+5Rpb38d5oOE/Jeh/wzB/L2QOq6LWxeWHiXMmXlOJ8xCK64YvfASoIpiQ" + + "xTqtgY2SKA0bOqUcerr4945MAdjR93qLeaEOQAeR+EweSYgasGWyi5ZwDh5ul3bJ" + + "SX2OdK9IkdBnktekbnPvo1necVuN6OjLKLlXyyDLn5KEVTjsAUonLk1nXZ/aL3ZH" + + "iTHWEUB0HyvRN9sQB3Y49ggp6DwLSmKfiglPYMj95k1tAOpi/MD9LnfpTV5ur3q0" + + "sqgIgQgMctaHIUV6vVQ2Ngu8dmOObUgqgDR5Us3Hf1u71Lu2AJBGRoDm7V1hhbgv" + + "Ob4oRlJUdgssi79id5IHog1nnO7VDxxtkKujzUUbSBnrAnxc27/lNQ9jwmUYZnHH" + + "xg9lEI2HNK/3k6FZd6Tiy8hzNN1+fo8hNkwjorsnNPd/nWbVeM/Oti5jEiVoeMfV" + + "/uw1/cGj6KaTvCe3h/xgX9y0/sWZ7WqILXZ+3vAouemvmBRe531IWA1djz//hgSI" + + "VOK+OprshAeU0Pce7qXMbMlOD7CUKBDecn0L7xxWW8sg8ZWgwqZuJEcOjvUKezgK" + + "URivBV1lavevLYyLsBO8W0qLiOLYYOgQkavkz42DYvkDeCJfeE2DuY6tqRr7xBJ5" + + "qGxDWGZ4+oBZYmXkMa63A/VC/EF6ZSVooLxI8QsobyPaB7spKv+YP+LjQgJ7F7dR" + + "3YXxKivW0QuZ2cAkfWpy8CaTGUhucaay8dqBEm7i3aAXSUzWMOayYu2xieu9+2Gl" + + "UuBlr2ic63si37rYemztVcIfSg2Rrtb/ggLT5a22LYBH109LosbR1kMfk7r9Eaz+" + + "TSH3TBaKoN7L56en1I56CvikEmzCbXW9N9bih9+HL7IgHcBN0c/TLkcvTvj2Xm4l" + + "cKpITTGg+KBS7BRNT8DA9Zqpa9aPIaNMrhPPTWEPRYyiMwhFwJw+Of3YLUwmlkhf" + + "gCVpngUFmnJE19pydjCCAckGCSqGSIb3DQEHAaCCAboEggG2MIIBsjCCAa4GCyqG" + + "SIb3DQEMCgECoIIBdjCCAXIwHAYKKoZIhvcNAQwBAzAOBAjknYMUAVMMGwICCAAE" + + "ggFQ4yI0v5ZS62IfP9l1I3llZ1l8SyW8r0F6v/BXBYitXKHoCsK4WbDl6Z4enb/0" + + "DZ8/RyepbiD1S9bqJ5ICXaFEKaBpVSvkVK1EWJF6pM9qx9VzECxVMTFxewdg2UwB" + + "2JkuUAD4tXxmjwp0LQpgJdKHpq7cH0SdoEdNzTsypZnaPBXUIDHS+NfiqPScueqv" + + "W2MoIg8egHIM4qnwLy0/hSc0iMfOgAPVbDktnoq7wzPVG8L+esS84/bABOl9o/e9" + + "Cq8OjK1aVBNMLnktdt0e4I3K7yQFgEJTkq8PpHmpnesv8l7Lvjd9Mri9W8eHilOp" + + "ZICybwoK3A0APG1lswubo0ii/56+MA3DnE0irn1rn6bkpOMGJ8JZBeuGzbV14ah/" + + "4l4MMPjnIf3Cu6TGd2/YLnPdr5fE6vyJtVa1sBpawIAK7cDUJ+OOD8gs1uoMfOML" + + "k5VpMSUwIwYJKoZIhvcNAQkVMRYEFDdYxa4ZJsCeAQZDzHLZ+cdpC2z5MDEwITAJ" + + "BgUrDgMCGgUABBTv022RKjlx16b3xrtvZELrNDdQIAQIaYfksM5jcN8CAggA"); public static X509Certificate2 GetSampleX509Certificate() { From e983168f871dcba090ff6ec08c0d2c7d4353fb8e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:27:46 -0700 Subject: [PATCH 609/926] [main] Update dependencies from 8 repositories (#55636) * Update dependencies from https://github.com/dotnet/arcade build 20210713.2 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21359.3 -> To Version 6.0.0-beta.21363.2 * Update dependencies from https://github.com/dotnet/icu build 20210713.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 6.0.0-preview.7.21362.1 -> To Version 6.0.0-preview.7.21363.1 * Update dependencies from https://github.com/dotnet/xharness build 20210713.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21357.4 -> To Version 1.0.0-prerelease.21363.1 * Update dependencies from https://github.com/mono/linker build 20210713.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21362.3 -> To Version 6.0.100-preview.6.21363.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20210712.2 Microsoft.CodeAnalysis.NetAnalyzers From Version 6.0.0-rc1.21356.1 -> To Version 6.0.0-rc1.21362.2 * Update dependencies from https://github.com/dotnet/emsdk build 20210714.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-preview.7.21363.1 -> To Version 6.0.0-preview.7.21364.1 * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210714.4 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21362.2 -> To Version 1.0.0-prerelease.21364.4 * Update dependencies from https://github.com/dotnet/arcade build 20210714.3 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21359.3 -> To Version 6.0.0-beta.21364.3 * Update dependencies from https://github.com/dotnet/xharness build 20210714.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21357.4 -> To Version 1.0.0-prerelease.21364.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210714.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21362.1 -> To Version 1.0.1-alpha.0.21364.1 * Revert linker change * Roll back the version Co-authored-by: dotnet-maestro[bot] Co-authored-by: Andy Gocke Co-authored-by: Larry Ewing --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 108 +++++++++--------- eng/Versions.props | 46 ++++---- eng/common/cross/build-rootfs.sh | 6 +- eng/common/sdl/configure-sdl-tool.ps1 | 109 +++++++++++++++++++ eng/common/sdl/execute-all-sdl-tools.ps1 | 71 ++++++++++-- eng/common/sdl/extract-artifact-archives.ps1 | 63 +++++++++++ eng/common/sdl/run-sdl.ps1 | 48 ++------ eng/common/templates/job/execute-sdl.yml | 108 ++++++++++++++++-- eng/common/tools.ps1 | 51 +++++++++ global.json | 8 +- 11 files changed, 481 insertions(+), 139 deletions(-) create mode 100644 eng/common/sdl/configure-sdl-tool.ps1 create mode 100644 eng/common/sdl/extract-artifact-archives.ps1 diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8dd812c7094..85fd3e4cf6c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21357.4", + "version": "1.0.0-prerelease.21364.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 58a2c18606c..aad7949895c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,82 +1,82 @@ - + https://github.com/dotnet/icu - 1782cd21854f8cb2b60355f4773714a8e0130696 + 02f08d828a4c5e256a9d7289ab9b7495032bc9df https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - 8a6313ffbdbef76fd01e32e37c802b57945531d3 + 16140a192ea2ffd8bcc3af09f6e531b3f5a7522b - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 https://github.com/microsoft/vstest @@ -186,45 +186,45 @@ https://github.com/mono/linker 664e78edc72dd0a48e6f55e352051b6ba61bba9a - + https://github.com/dotnet/xharness - c6d444eaf7e95339589ceef371cbef0a90a4add5 + e5511489deb44b13418fe622775bd7035e21c5ae - + https://github.com/dotnet/xharness - c6d444eaf7e95339589ceef371cbef0a90a4add5 + e5511489deb44b13418fe622775bd7035e21c5ae - + https://github.com/dotnet/arcade - 55262f114b0c1b82f0b081bca0d919b657ba24c5 + 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a83e4392b6d3f377239726eedc19b6dc85b85496 + 36455244434016d40fdc8b51da033a4dce98101f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a83e4392b6d3f377239726eedc19b6dc85b85496 + 36455244434016d40fdc8b51da033a4dce98101f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a83e4392b6d3f377239726eedc19b6dc85b85496 + 36455244434016d40fdc8b51da033a4dce98101f - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - a83e4392b6d3f377239726eedc19b6dc85b85496 + 36455244434016d40fdc8b51da033a4dce98101f - + https://github.com/dotnet/hotreload-utils - 33219d2b3fbc957e05f8e52a33363cf9b858bb08 + 247ef3e12ea5ecde3c1d7ce7f8c2b0eccd6ebd49 https://github.com/dotnet/runtime-assets 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/roslyn-analyzers - 77c6f0725c26442023c8eee2b143b899cb3f4eb7 + 1e2ab2307eb1c68a3c7de1603ee1a1406300afa5 diff --git a/eng/Versions.props b/eng/Versions.props index 2e300bfd20c..161feb810c7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,21 +50,21 @@ 3.10.0 3.10.0 - 6.0.0-rc1.21356.1 + 6.0.0-rc1.21362.2 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 2.5.1-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 - 6.0.0-beta.21359.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 2.5.1-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 + 6.0.0-beta.21364.3 6.0.0-preview.1.102 @@ -124,10 +124,10 @@ 6.0.0-beta.21358.1 6.0.0-beta.21358.1 - 1.0.0-prerelease.21362.2 - 1.0.0-prerelease.21362.2 - 1.0.0-prerelease.21362.2 - 1.0.0-prerelease.21362.2 + 1.0.0-prerelease.21364.4 + 1.0.0-prerelease.21364.4 + 1.0.0-prerelease.21364.4 + 1.0.0-prerelease.21364.4 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -151,9 +151,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21357.4 - 1.0.0-prerelease.21357.4 - 1.0.1-alpha.0.21362.1 + 1.0.0-prerelease.21364.1 + 1.0.0-prerelease.21364.1 + 1.0.1-alpha.0.21364.1 2.4.1 2.4.2 1.3.0 @@ -168,7 +168,7 @@ 6.0.100-preview.6.21362.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21362.1 + 6.0.0-preview.7.21363.1 6.0.0-preview.7.21357.1 @@ -181,7 +181,7 @@ 11.1.0-alpha.1.21362.1 11.1.0-alpha.1.21362.1 - 6.0.0-preview.7.21363.1 + 6.0.0-preview.7.21364.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 591d8666a84..735a4c82838 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -33,7 +33,6 @@ __AlpinePackages="alpine-base" __AlpinePackages+=" build-base" __AlpinePackages+=" linux-headers" __AlpinePackagesEdgeCommunity=" lldb-dev" -__AlpinePackagesEdgeMain=" llvm10-libs" __AlpinePackagesEdgeMain+=" python3" __AlpinePackagesEdgeMain+=" libedit" @@ -115,6 +114,8 @@ while :; do __UbuntuArch=s390x __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/" __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libunwind8-dev//') + __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libomp-dev//') + __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libomp5//') unset __LLDB_Package ;; x86) @@ -191,6 +192,8 @@ while :; do __CodeName=alpine __UbuntuRepo= __AlpineVersion=3.9 + __AlpinePackagesEdgeMain+=" llvm11-libs" + __AlpinePackagesEdgeMain+=" clang-libs" ;; alpine3.13) __CodeName=alpine @@ -201,6 +204,7 @@ while :; do __AlpinePackagesEdgeCommunity= __AlpinePackages+=$__AlpinePackagesEdgeMain __AlpinePackagesEdgeMain= + __AlpinePackages+=" llvm10-libs" ;; freebsd11) __FreeBSDBase="11.3-RELEASE" diff --git a/eng/common/sdl/configure-sdl-tool.ps1 b/eng/common/sdl/configure-sdl-tool.ps1 new file mode 100644 index 00000000000..4999c307088 --- /dev/null +++ b/eng/common/sdl/configure-sdl-tool.ps1 @@ -0,0 +1,109 @@ +Param( + [string] $GuardianCliLocation, + [string] $WorkingDirectory, + [string] $TargetDirectory, + [string] $GdnFolder, + # The list of Guardian tools to configure. For each object in the array: + # - If the item is a [hashtable], it must contain these entries: + # - Name = The tool name as Guardian knows it. + # - Scenario = (Optional) Scenario-specific name for this configuration entry. It must be unique + # among all tool entries with the same Name. + # - Args = (Optional) Array of Guardian tool configuration args, like '@("Target > C:\temp")' + # - If the item is a [string] $v, it is treated as '@{ Name="$v" }' + [object[]] $ToolsList, + [string] $GuardianLoggerLevel='Standard', + # Optional: Additional params to add to any tool using CredScan. + [string[]] $CrScanAdditionalRunConfigParams, + # Optional: Additional params to add to any tool using PoliCheck. + [string[]] $PoliCheckAdditionalRunConfigParams +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + # Normalize tools list: all in [hashtable] form with defined values for each key. + $ToolsList = $ToolsList | + ForEach-Object { + if ($_ -is [string]) { + $_ = @{ Name = $_ } + } + + if (-not ($_['Scenario'])) { $_.Scenario = "" } + if (-not ($_['Args'])) { $_.Args = @() } + $_ + } + + Write-Host "List of tools to configure:" + $ToolsList | ForEach-Object { $_ | Out-String | Write-Host } + + # We store config files in the r directory of .gdn + $gdnConfigPath = Join-Path $GdnFolder 'r' + $ValidPath = Test-Path $GuardianCliLocation + + if ($ValidPath -eq $False) + { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Invalid Guardian CLI Location." + ExitWithExitCode 1 + } + + foreach ($tool in $ToolsList) { + # Put together the name and scenario to make a unique key. + $toolConfigName = $tool.Name + if ($tool.Scenario) { + $toolConfigName += "_" + $tool.Scenario + } + + Write-Host "=== Configuring $toolConfigName..." + + $gdnConfigFile = Join-Path $gdnConfigPath "$toolConfigName-configure.gdnconfig" + + # For some tools, add default and automatic args. + if ($tool.Name -eq 'credscan') { + if ($targetDirectory) { + $tool.Args += "TargetDirectory < $TargetDirectory" + } + $tool.Args += "OutputType < pre" + $tool.Args += $CrScanAdditionalRunConfigParams + } elseif ($tool.Name -eq 'policheck') { + if ($targetDirectory) { + $tool.Args += "Target < $TargetDirectory" + } + $tool.Args += $PoliCheckAdditionalRunConfigParams + } + + # Create variable pointing to the args array directly so we can use splat syntax later. + $toolArgs = $tool.Args + + # Configure the tool. If args array is provided or the current tool has some default arguments + # defined, add "--args" and splat each element on the end. Arg format is "{Arg id} < {Value}", + # one per parameter. Doc page for "guardian configure": + # https://dev.azure.com/securitytools/SecurityIntegration/_wiki/wikis/Guardian/1395/configure + Exec-BlockVerbosely { + & $GuardianCliLocation configure ` + --working-directory $WorkingDirectory ` + --tool $tool.Name ` + --output-path $gdnConfigFile ` + --logger-level $GuardianLoggerLevel ` + --noninteractive ` + --force ` + $(if ($toolArgs) { "--args" }) @toolArgs + Exit-IfNZEC "Sdl" + } + + Write-Host "Created '$toolConfigName' configuration file: $gdnConfigFile" + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/execute-all-sdl-tools.ps1 b/eng/common/sdl/execute-all-sdl-tools.ps1 index 2881a56083c..1157151f486 100644 --- a/eng/common/sdl/execute-all-sdl-tools.ps1 +++ b/eng/common/sdl/execute-all-sdl-tools.ps1 @@ -7,8 +7,17 @@ Param( [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY, # Required: the directory where source files are located [string] $ArtifactsDirectory = (Join-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY ('artifacts')), # Required: the directory where build artifacts are located [string] $AzureDevOpsAccessToken, # Required: access token for dnceng; should be provided via KeyVault - [string[]] $SourceToolsList, # Optional: list of SDL tools to run on source code - [string[]] $ArtifactToolsList, # Optional: list of SDL tools to run on built artifacts + + # Optional: list of SDL tools to run on source code. See 'configure-sdl-tool.ps1' for tools list + # format. + [object[]] $SourceToolsList, + # Optional: list of SDL tools to run on built artifacts. See 'configure-sdl-tool.ps1' for tools + # list format. + [object[]] $ArtifactToolsList, + # Optional: list of SDL tools to run without automatically specifying a target directory. See + # 'configure-sdl-tool.ps1' for tools list format. + [object[]] $CustomToolsList, + [bool] $TsaPublish=$False, # Optional: true will publish results to TSA; only set to true after onboarding to TSA; TSA is the automated framework used to upload test results as bugs. [string] $TsaBranchName=$env:BUILD_SOURCEBRANCH, # Optional: required for TSA publish; defaults to $(Build.SourceBranchName); TSA is the automated framework used to upload test results as bugs. [string] $TsaRepositoryName=$env:BUILD_REPOSITORY_NAME, # Optional: TSA repository name; will be generated automatically if not submitted; TSA is the automated framework used to upload test results as bugs. @@ -63,13 +72,16 @@ try { ExitWithExitCode 1 } - & $(Join-Path $PSScriptRoot 'init-sdl.ps1') -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $workingDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'init-sdl.ps1') -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $workingDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel + } $gdnFolder = Join-Path $workingDirectory '.gdn' if ($TsaOnboard) { if ($TsaCodebaseName -and $TsaNotificationEmail -and $TsaCodebaseAdmin -and $TsaBugAreaPath) { - Write-Host "$guardianCliLocation tsa-onboard --codebase-name `"$TsaCodebaseName`" --notification-alias `"$TsaNotificationEmail`" --codebase-admin `"$TsaCodebaseAdmin`" --instance-url `"$TsaInstanceUrl`" --project-name `"$TsaProjectName`" --area-path `"$TsaBugAreaPath`" --iteration-path `"$TsaIterationPath`" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel" - & $guardianCliLocation tsa-onboard --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + Exec-BlockVerbosely { + & $guardianCliLocation tsa-onboard --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } if ($LASTEXITCODE -ne 0) { Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-onboard failed with exit code $LASTEXITCODE." ExitWithExitCode $LASTEXITCODE @@ -80,11 +92,41 @@ try { } } - if ($ArtifactToolsList -and $ArtifactToolsList.Count -gt 0) { - & $(Join-Path $PSScriptRoot 'run-sdl.ps1') -GuardianCliLocation $guardianCliLocation -WorkingDirectory $workingDirectory -TargetDirectory $ArtifactsDirectory -GdnFolder $gdnFolder -ToolsList $ArtifactToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams + # Configure a list of tools with a default target directory. Populates the ".gdn/r" directory. + function Configure-ToolsList([object[]] $tools, [string] $targetDirectory) { + if ($tools -and $tools.Count -gt 0) { + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'configure-sdl-tool.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $workingDirectory ` + -TargetDirectory $targetDirectory ` + -GdnFolder $gdnFolder ` + -ToolsList $tools ` + -AzureDevOpsAccessToken $AzureDevOpsAccessToken ` + -GuardianLoggerLevel $GuardianLoggerLevel ` + -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams ` + -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams + if ($BreakOnFailure) { + Exit-IfNZEC "Sdl" + } + } + } } - if ($SourceToolsList -and $SourceToolsList.Count -gt 0) { - & $(Join-Path $PSScriptRoot 'run-sdl.ps1') -GuardianCliLocation $guardianCliLocation -WorkingDirectory $workingDirectory -TargetDirectory $SourceDirectory -GdnFolder $gdnFolder -ToolsList $SourceToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams + + # Configure Artifact and Source tools with default Target directories. + Configure-ToolsList $ArtifactToolsList $ArtifactsDirectory + Configure-ToolsList $SourceToolsList $SourceDirectory + # Configure custom tools with no default Target directory. + Configure-ToolsList $CustomToolsList $null + + # At this point, all tools are configured in the ".gdn" directory. Run them all in a single call. + # (If we used "run" multiple times, each run would overwrite data from earlier runs.) + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'run-sdl.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $workingDirectory ` + -UpdateBaseline $UpdateBaseline ` + -GdnFolder $gdnFolder } if ($TsaPublish) { @@ -92,8 +134,9 @@ try { if (-not $TsaRepositoryName) { $TsaRepositoryName = "$($Repository)-$($BranchName)" } - Write-Host "$guardianCliLocation tsa-publish --all-tools --repository-name `"$TsaRepositoryName`" --branch-name `"$TsaBranchName`" --build-number `"$BuildNumber`" --codebase-name `"$TsaCodebaseName`" --notification-alias `"$TsaNotificationEmail`" --codebase-admin `"$TsaCodebaseAdmin`" --instance-url `"$TsaInstanceUrl`" --project-name `"$TsaProjectName`" --area-path `"$TsaBugAreaPath`" --iteration-path `"$TsaIterationPath`" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel" - & $guardianCliLocation tsa-publish --all-tools --repository-name "$TsaRepositoryName" --branch-name "$TsaBranchName" --build-number "$BuildNumber" --onboard $True --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + Exec-BlockVerbosely { + & $guardianCliLocation tsa-publish --all-tools --repository-name "$TsaRepositoryName" --branch-name "$TsaBranchName" --build-number "$BuildNumber" --onboard $True --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } if ($LASTEXITCODE -ne 0) { Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-publish failed with exit code $LASTEXITCODE." ExitWithExitCode $LASTEXITCODE @@ -106,7 +149,11 @@ try { if ($BreakOnFailure) { Write-Host "Failing the build in case of breaking results..." - & $guardianCliLocation break + Exec-BlockVerbosely { + & $guardianCliLocation break --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } + } else { + Write-Host "Letting the build pass even if there were breaking results..." } } catch { diff --git a/eng/common/sdl/extract-artifact-archives.ps1 b/eng/common/sdl/extract-artifact-archives.ps1 new file mode 100644 index 00000000000..68da4fbf257 --- /dev/null +++ b/eng/common/sdl/extract-artifact-archives.ps1 @@ -0,0 +1,63 @@ +# This script looks for each archive file in a directory and extracts it into the target directory. +# For example, the file "$InputPath/bin.tar.gz" extracts to "$ExtractPath/bin.tar.gz.extracted/**". +# Uses the "tar" utility added to Windows 10 / Windows 2019 that supports tar.gz and zip. +param( + # Full path to directory where archives are stored. + [Parameter(Mandatory=$true)][string] $InputPath, + # Full path to directory to extract archives into. May be the same as $InputPath. + [Parameter(Mandatory=$true)][string] $ExtractPath +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +$disableConfigureToolsetImport = $true + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + Measure-Command { + $jobs = @() + + # Find archive files for non-Windows and Windows builds. + $archiveFiles = @( + Get-ChildItem (Join-Path $InputPath "*.tar.gz") + Get-ChildItem (Join-Path $InputPath "*.zip") + ) + + foreach ($targzFile in $archiveFiles) { + $jobs += Start-Job -ScriptBlock { + $file = $using:targzFile + $fileName = [System.IO.Path]::GetFileName($file) + $extractDir = Join-Path $using:ExtractPath "$fileName.extracted" + + New-Item $extractDir -ItemType Directory -Force | Out-Null + + Write-Host "Extracting '$file' to '$extractDir'..." + + # Pipe errors to stdout to prevent PowerShell detecting them and quitting the job early. + # This type of quit skips the catch, so we wouldn't be able to tell which file triggered the + # error. Save output so it can be stored in the exception string along with context. + $output = tar -xf $file -C $extractDir 2>&1 + # Handle NZEC manually rather than using Exit-IfNZEC: we are in a background job, so we + # don't have access to the outer scope. + if ($LASTEXITCODE -ne 0) { + throw "Error extracting '$file': non-zero exit code ($LASTEXITCODE). Output: '$output'" + } + + Write-Host "Extracted to $extractDir" + } + } + + Receive-Job $jobs -Wait + } +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/run-sdl.ps1 b/eng/common/sdl/run-sdl.ps1 index 3d9c87aba6a..2eac8c78f10 100644 --- a/eng/common/sdl/run-sdl.ps1 +++ b/eng/common/sdl/run-sdl.ps1 @@ -1,13 +1,9 @@ Param( [string] $GuardianCliLocation, [string] $WorkingDirectory, - [string] $TargetDirectory, [string] $GdnFolder, - [string[]] $ToolsList, [string] $UpdateBaseline, - [string] $GuardianLoggerLevel='Standard', - [string[]] $CrScanAdditionalRunConfigParams, - [string[]] $PoliCheckAdditionalRunConfigParams + [string] $GuardianLoggerLevel='Standard' ) $ErrorActionPreference = 'Stop' @@ -23,7 +19,6 @@ try { . $PSScriptRoot\..\tools.ps1 # We store config files in the r directory of .gdn - Write-Host $ToolsList $gdnConfigPath = Join-Path $GdnFolder 'r' $ValidPath = Test-Path $GuardianCliLocation @@ -33,37 +28,18 @@ try { ExitWithExitCode 1 } - $configParam = @('--config') + $gdnConfigFiles = Get-ChildItem $gdnConfigPath -Recurse -Include '*.gdnconfig' + Write-Host "Discovered Guardian config files:" + $gdnConfigFiles | Out-String | Write-Host - foreach ($tool in $ToolsList) { - $gdnConfigFile = Join-Path $gdnConfigPath "$tool-configure.gdnconfig" - Write-Host $tool - # We have to manually configure tools that run on source to look at the source directory only - if ($tool -eq 'credscan') { - Write-Host "$GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args `" TargetDirectory < $TargetDirectory `" `" OutputType < pre `" $(If ($CrScanAdditionalRunConfigParams) {$CrScanAdditionalRunConfigParams})" - & $GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args " TargetDirectory < $TargetDirectory " "OutputType < pre" $(If ($CrScanAdditionalRunConfigParams) {$CrScanAdditionalRunConfigParams}) - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian configure for $tool failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - } - if ($tool -eq 'policheck') { - Write-Host "$GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args `" Target < $TargetDirectory `" $(If ($PoliCheckAdditionalRunConfigParams) {$PoliCheckAdditionalRunConfigParams})" - & $GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args " Target < $TargetDirectory " $(If ($PoliCheckAdditionalRunConfigParams) {$PoliCheckAdditionalRunConfigParams}) - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian configure for $tool failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - } - - $configParam+=$gdnConfigFile - } - - Write-Host "$GuardianCliLocation run --working-directory $WorkingDirectory --baseline mainbaseline --update-baseline $UpdateBaseline --logger-level $GuardianLoggerLevel $configParam" - & $GuardianCliLocation run --working-directory $WorkingDirectory --tool $tool --baseline mainbaseline --update-baseline $UpdateBaseline --logger-level $GuardianLoggerLevel $configParam - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian run for $ToolsList using $configParam failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE + Exec-BlockVerbosely { + & $GuardianCliLocation run ` + --working-directory $WorkingDirectory ` + --baseline mainbaseline ` + --update-baseline $UpdateBaseline ` + --logger-level $GuardianLoggerLevel ` + --config @gdnConfigFiles + Exit-IfNZEC "Sdl" } } catch { diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 4a32181fd8f..69eb67849d7 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -2,17 +2,41 @@ parameters: enable: 'false' # Whether the SDL validation job should execute or not overrideParameters: '' # Optional: to override values for parameters. additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' + # Optional: if specified, restore and use this version of Guardian instead of the default. + overrideGuardianVersion: '' + # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth + # diagnosis of problems with specific tool configurations. + publishGuardianDirectoryToPipeline: false + # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL + # parameters rather than relying on YAML. It may be better to use a local script, because you can + # reproduce results locally without piecing together a command based on the YAML. + executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named # 'continueOnError', the parameter value is not correctly picked up. # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter sdlContinueOnError: false # optional: determines whether to continue the build if the step errors; - downloadArtifacts: true # optional: determines if the artifacts should be dowloaded + # optional: determines if build artifacts should be downloaded. + downloadArtifacts: true + # optional: determines if this job should search the directory of downloaded artifacts for + # 'tar.gz' and 'zip' archive files and extract them before running SDL validation tasks. + extractArchiveArtifacts: false dependsOn: '' # Optional: dependencies of the job artifactNames: '' # Optional: patterns supplied to DownloadBuildArtifacts # Usage: # artifactNames: # - 'BlobArtifacts' # - 'Artifacts_Windows_NT_Release' + # Optional: download a list of pipeline artifacts. 'downloadArtifacts' controls build artifacts, + # not pipeline artifacts, so doesn't affect the use of this parameter. + pipelineArtifactNames: [] + # Optional: location and ID of the AzDO build that the build/pipeline artifacts should be + # downloaded from. By default, uses runtime expressions to decide based on the variables set by + # the 'setupMaestroVars' dependency. Overriding this parameter is necessary if SDL tasks are + # running without Maestro++/BAR involved, or to download artifacts from a specific existing build + # to iterate quickly on SDL changes. + AzDOProjectName: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOProjectName'] ] + AzDOPipelineId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOPipelineId'] ] + AzDOBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildId'] ] jobs: - job: Run_SDL @@ -22,16 +46,29 @@ jobs: variables: - group: DotNet-VSTS-Bot - name: AzDOProjectName - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOProjectName'] ] + value: ${{ parameters.AzDOProjectName }} - name: AzDOPipelineId - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOPipelineId'] ] + value: ${{ parameters.AzDOPipelineId }} - name: AzDOBuildId - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildId'] ] + value: ${{ parameters.AzDOBuildId }} + # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in + # sync with the packages.config file. + - name: DefaultGuardianVersion + value: 0.53.3 + - name: GuardianVersion + value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} + - name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config pool: - name: Hosted VS2017 + # To extract archives (.tar.gz, .zip), we need access to "tar", added in Windows 10/2019. + ${{ if eq(parameters.extractArchiveArtifacts, 'false') }}: + name: Hosted VS2017 + ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: + vmImage: windows-2019 steps: - checkout: self clean: true + - ${{ if ne(parameters.downloadArtifacts, 'false')}}: - ${{ if ne(parameters.artifactNames, '') }}: - ${{ each artifactName in parameters.artifactNames }}: @@ -59,16 +96,51 @@ jobs: itemPattern: "**" downloadPath: $(Build.ArtifactStagingDirectory)\artifacts checkDownloadedFiles: true + + - ${{ each artifactName in parameters.pipelineArtifactNames }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: ${{ artifactName }} + downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true + - powershell: eng/common/sdl/extract-artifact-packages.ps1 -InputPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts displayName: Extract Blob Artifacts continueOnError: ${{ parameters.sdlContinueOnError }} + - powershell: eng/common/sdl/extract-artifact-packages.ps1 -InputPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts displayName: Extract Package Artifacts continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: + - powershell: eng/common/sdl/extract-artifact-archives.ps1 + -InputPath $(Build.ArtifactStagingDirectory)\artifacts + -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts + displayName: Extract Archive Artifacts + continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.overrideGuardianVersion, '') }}: + - powershell: | + $content = Get-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content was:`n$content" + + $content = $content.Replace('$(DefaultGuardianVersion)', '$(GuardianVersion)') + $content | Set-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content updated to:`n$content" + displayName: Use overridden Guardian version ${{ parameters.overrideGuardianVersion }} + - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' - task: NuGetCommand@2 @@ -79,15 +151,35 @@ jobs: nugetConfigPath: $(Build.SourcesDirectory)\eng\common\sdl\NuGet.config externalFeedCredentials: GuardianConnect restoreDirectory: $(Build.SourcesDirectory)\.packages + - ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 ${{ parameters.overrideParameters }} + - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} displayName: Execute SDL continueOnError: ${{ parameters.sdlContinueOnError }} - ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 - -GuardianPackageName Microsoft.Guardian.Cli.0.53.3 + - powershell: ${{ parameters.executeAllSdlToolsScript }} + -GuardianPackageName Microsoft.Guardian.Cli.$(GuardianVersion) -NugetPackageDirectory $(Build.SourcesDirectory)\.packages -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) ${{ parameters.additionalParameters }} displayName: Execute SDL continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: + # We want to publish the Guardian results and configuration for easy diagnosis. However, the + # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default + # tooling files. Some of these files are large and aren't useful during an investigation, so + # exclude them by simply deleting them before publishing. (As of writing, there is no documented + # way to selectively exclude a dir from the pipeline artifact publish task.) + - task: DeleteFiles@1 + displayName: Delete Guardian dependencies to avoid uploading + inputs: + SourceFolder: $(Agent.BuildDirectory)/.gdn + Contents: | + c + i + condition: succeededOrFailed() + - publish: $(Agent.BuildDirectory)/.gdn + artifact: GuardianConfiguration + displayName: Publish GuardianConfiguration + condition: succeededOrFailed() diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 2df0909937d..5d526c74d51 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -106,6 +106,46 @@ function Exec-Process([string]$command, [string]$commandArgs) { } } +# Take the given block, print it, print what the block probably references from the current set of +# variables using low-effort string matching, then run the block. +# +# This is intended to replace the pattern of manually copy-pasting a command, wrapping it in quotes, +# and printing it using "Write-Host". The copy-paste method is more readable in build logs, but less +# maintainable and less reliable. It is easy to make a mistake and modify the command without +# properly updating the "Write-Host" line, resulting in misleading build logs. The probability of +# this mistake makes the pattern hard to trust when it shows up in build logs. Finding the bug in +# existing source code can also be difficult, because the strings are not aligned to each other and +# the line may be 300+ columns long. +# +# By removing the need to maintain two copies of the command, Exec-BlockVerbosely avoids the issues. +# +# In Bash (or any posix-like shell), "set -x" prints usable verbose output automatically. +# "Set-PSDebug" appears to be similar at first glance, but unfortunately, it isn't very useful: it +# doesn't print any info about the variables being used by the command, which is normally the +# interesting part to diagnose. +function Exec-BlockVerbosely([scriptblock] $block) { + Write-Host "--- Running script block:" + $blockString = $block.ToString().Trim() + Write-Host $blockString + + Write-Host "--- List of variables that might be used:" + # For each variable x in the environment, check the block for a reference to x via simple "$x" or + # "@x" syntax. This doesn't detect other ways to reference variables ("${x}" nor "$variable:x", + # among others). It only catches what this function was originally written for: simple + # command-line commands. + $variableTable = Get-Variable | + Where-Object { + $blockString.Contains("`$$($_.Name)") -or $blockString.Contains("@$($_.Name)") + } | + Format-Table -AutoSize -HideTableHeaders -Wrap | + Out-String + Write-Host $variableTable.Trim() + + Write-Host "--- Executing:" + & $block + Write-Host "--- Done running script block!" +} + # createSdkLocationFile parameter enables a file being generated under the toolset directory # which writes the sdk's location into. This is only necessary for cmd --> powershell invocations # as dot sourcing isn't possible. @@ -632,6 +672,17 @@ function ExitWithExitCode([int] $exitCode) { exit $exitCode } +# Check if $LASTEXITCODE is a nonzero exit code (NZEC). If so, print a Azure Pipeline error for +# diagnostics, then exit the script with the $LASTEXITCODE. +function Exit-IfNZEC([string] $category = "General") { + Write-Host "Exit code $LASTEXITCODE" + if ($LASTEXITCODE -ne 0) { + $message = "Last command failed with exit code $LASTEXITCODE." + Write-PipelineTelemetryError -Force -Category $category -Message $message + ExitWithExitCode $LASTEXITCODE + } +} + function Stop-Processes() { Write-Host 'Killing running build processes...' foreach ($processName in $processesToStopOnExit) { diff --git a/global.json b/global.json index e25c47c09f7..379263436c7 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21359.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21364.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21359.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21359.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21359.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21364.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21364.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21364.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10" From 405da674b633a99cc41be9e18c2e4d84b1b01639 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 15 Jul 2021 18:23:31 -0700 Subject: [PATCH 610/926] Add a System.Runtime.Experimental package so users can still use the generic math preview (#55678) * Add a System.Runtime.Experimental package so users can still use the generic math preview This reverts commit f6eb259db626d563c15ba340feb6f440d1e1c8ee. * Simplify S.R.Experimental so it only produces a ref and its build version is 100 higher * Ensure a nuget package is being produced and resolve PR feedback * Update src/libraries/System.Runtime.Experimental/Directory.Build.props * Add a reference to System.Runtime.Experimental so mono can build * Disable System.Runtime.Experimental.Tests on WASM AOT --- src/coreclr/clr.featuredefines.props | 2 +- src/libraries/Directory.Build.targets | 18 ++-- .../Directory.Build.props | 8 ++ .../System.Runtime.Experimental/NuGet.config | 12 +++ .../System.Runtime.Experimental.sln | 85 +++++++++++++++++++ .../ref/System.Runtime.Experimental.csproj | 36 ++++++++ .../System.Runtime.Experimental.Tests.csproj | 47 ++++++++++ .../System.Runtime/src/System.Runtime.csproj | 4 +- src/libraries/libraries-packages.proj | 2 + src/libraries/tests.proj | 7 +- .../System.Private.CoreLib.csproj | 2 +- 11 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 src/libraries/System.Runtime.Experimental/Directory.Build.props create mode 100644 src/libraries/System.Runtime.Experimental/NuGet.config create mode 100644 src/libraries/System.Runtime.Experimental/System.Runtime.Experimental.sln create mode 100644 src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj create mode 100644 src/libraries/System.Runtime.Experimental/tests/System.Runtime.Experimental.Tests.csproj diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 184e885470f..08fca8de6dd 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -9,7 +9,7 @@ true true true - false + true true diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 8d9099fba2e..5e28db1deeb 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -24,7 +24,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrentBuildSettings)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'testhost', '$(NetCoreAppCurrentBuildSettings)')) $([MSBuild]::NormalizeDirectory('$(NetCoreAppCurrentTestHostPath)', 'shared', '$(MicrosoftNetCoreAppFrameworkName)', '$(ProductVersion)')) - + $(PackageOutputPath) $(NoWarn);nullable @@ -282,10 +282,14 @@ + + <_intellisenseRootFolder>$(BuildOutputTargetFolder) + <_intellisenseRootFolder Condition="'$(_intellisenseRootFolder)' == ''">lib + + PackagePath="$(_intellisenseRootFolder)/$(TargetFramework)" /> @@ -294,8 +298,8 @@ - @@ -313,9 +317,9 @@ - <_TargetPathsToSymbols Include="@(_AnalyzerFile)" TargetPath="/%(_AnalyzerFile.PackagePath)" Condition="%(_AnalyzerFile.IsSymbol)" /> @@ -331,7 +335,7 @@ <_AnalyzerPackFile Include="@(_TargetPathsToSymbols)" IsSymbol="true" /> <_AnalyzerPackFile PackagePath="$(_analyzerPath)/%(TargetPath)" /> - diff --git a/src/libraries/System.Runtime.Experimental/Directory.Build.props b/src/libraries/System.Runtime.Experimental/Directory.Build.props new file mode 100644 index 00000000000..64242a92fce --- /dev/null +++ b/src/libraries/System.Runtime.Experimental/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + false + Microsoft + + diff --git a/src/libraries/System.Runtime.Experimental/NuGet.config b/src/libraries/System.Runtime.Experimental/NuGet.config new file mode 100644 index 00000000000..a66b7f9b013 --- /dev/null +++ b/src/libraries/System.Runtime.Experimental/NuGet.config @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Experimental/System.Runtime.Experimental.sln b/src/libraries/System.Runtime.Experimental/System.Runtime.Experimental.sln new file mode 100644 index 00000000000..488f21fdfd3 --- /dev/null +++ b/src/libraries/System.Runtime.Experimental/System.Runtime.Experimental.sln @@ -0,0 +1,85 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{71AB8240-F179-4B21-A8BE-8BE6CD774ED9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Experimental", "ref\System.Runtime.Experimental.csproj", "{F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Experimental.Tests", "tests\System.Runtime.Experimental.Tests.csproj", "{4EE36055-AD7C-4779-B3F6-08687960DCC3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{BDCC9986-D51F-48D9-9650-388E172CD91E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{F4F10B6C-21C0-4C9D-8909-76FC52145120}" +EndProject +Global + GlobalSection(NestedProjects) = preSolution + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9} = {C7032EBC-8AB7-4BE6-9D95-B420F058FAAF} + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C} = {F4F10B6C-21C0-4C9D-8909-76FC52145120} + {4EE36055-AD7C-4779-B3F6-08687960DCC3} = {BDCC9986-D51F-48D9-9650-388E172CD91E} + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|Any CPU.ActiveCfg = Debug|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|Any CPU.Build.0 = Debug|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|x64.ActiveCfg = Debug|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|x64.Build.0 = Debug|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|x86.ActiveCfg = Debug|x86 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Debug|x86.Build.0 = Debug|x86 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|Any CPU.ActiveCfg = Release|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|Any CPU.Build.0 = Release|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|x64.ActiveCfg = Release|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|x64.Build.0 = Release|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|x86.ActiveCfg = Release|x86 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Release|x86.Build.0 = Release|x86 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|Any CPU.ActiveCfg = Checked|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|Any CPU.Build.0 = Checked|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|x64.ActiveCfg = Checked|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|x64.Build.0 = Checked|x64 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|x86.ActiveCfg = Checked|x86 + {71AB8240-F179-4B21-A8BE-8BE6CD774ED9}.Checked|x86.Build.0 = Checked|x86 + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|x64.ActiveCfg = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|x64.Build.0 = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|x86.ActiveCfg = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Debug|x86.Build.0 = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|Any CPU.Build.0 = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|x64.ActiveCfg = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|x64.Build.0 = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|x86.ActiveCfg = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Release|x86.Build.0 = Release|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Checked|x64.ActiveCfg = Debug|Any CPU + {F39E2C7E-5FE1-460C-AC2C-7E2B50955F2C}.Checked|x86.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|x64.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|x64.Build.0 = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|x86.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Debug|x86.Build.0 = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|Any CPU.Build.0 = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|x64.ActiveCfg = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|x64.Build.0 = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|x86.ActiveCfg = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Release|x86.Build.0 = Release|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|x64.ActiveCfg = Debug|Any CPU + {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|x86.ActiveCfg = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {19706846-1F47-42ED-B649-B0982EE96E6B} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj b/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj new file mode 100644 index 00000000000..4c6ae1a740f --- /dev/null +++ b/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj @@ -0,0 +1,36 @@ + + + System.Runtime + true + true + + v4.0.30319 + + $(NoWarn);0809;0618;CS8614;CS3015;NU5131 + $(NetCoreAppCurrent) + enable + + + + ref + $(DefineConstants);FEATURE_GENERIC_MATH + true + Exposes new experimental APIs from System.Runtime + $(MSBuildProjectName) + + + + + + + <_FileVersionMaj>$(FileVersion.Split('.')[0]) + <_FileVersionMin>$(FileVersion.Split('.')[1]) + <_FileVersionBld>$(FileVersion.Split('.')[2]) + <_FileVersionRev>$(FileVersion.Split('.')[3]) + $(_FileVersionMaj).$(_FileVersionMin).$([MSBuild]::Add($(_FileVersionBld), 100)).$(_FileVersionRev) + + + diff --git a/src/libraries/System.Runtime.Experimental/tests/System.Runtime.Experimental.Tests.csproj b/src/libraries/System.Runtime.Experimental/tests/System.Runtime.Experimental.Tests.csproj new file mode 100644 index 00000000000..b1cd60dc723 --- /dev/null +++ b/src/libraries/System.Runtime.Experimental/tests/System.Runtime.Experimental.Tests.csproj @@ -0,0 +1,47 @@ + + + true + true + $(NoWarn),1718,SYSLIB0013 + true + true + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + disable + + $(Features.Replace('nullablePublicOnly', '') + + + $(DefineConstants);FEATURE_GENERIC_MATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime/src/System.Runtime.csproj b/src/libraries/System.Runtime/src/System.Runtime.csproj index 6e3a18228e7..38cdb0e8526 100644 --- a/src/libraries/System.Runtime/src/System.Runtime.csproj +++ b/src/libraries/System.Runtime/src/System.Runtime.csproj @@ -1,11 +1,13 @@ + + $(LibrariesProjectRoot)System.Runtime.Experimental\ref\System.Runtime.Experimental.csproj true $(NetCoreAppCurrent) enable - + diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj index 383dd088e12..f67803e7997 100644 --- a/src/libraries/libraries-packages.proj +++ b/src/libraries/libraries-packages.proj @@ -3,6 +3,8 @@ + - + @@ -228,7 +228,7 @@ - + @@ -278,6 +278,9 @@ + + + diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 8488d916e6d..4edae171504 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -109,7 +109,7 @@ true true true - false + true true true true From fb2e6733d600406f573b2f960f6c1fa734723108 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Thu, 15 Jul 2021 22:32:43 -0400 Subject: [PATCH 611/926] Remove sourceFullPath from Unix FileSystem.MoveDirectory exception (#55658) * Update Path.Windows.cs * Change helper method to internal Switched method from a private protection level to an internal protection level. Also removed trailing whitespace. * Removed sourceFullPath from Unix FileSystem.MoveDirectory thrown exception * Undo commit from main fork branch * Revert FileSystem.Windows.cs changes --- .../System.Private.CoreLib/src/System/IO/Directory.cs | 2 +- .../System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs index ac61bac5226..a30543ced59 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Directory.cs @@ -288,7 +288,7 @@ namespace System.IO if (!FileSystem.DirectoryExists(fullsourceDirName) && !FileSystem.FileExists(fullsourceDirName)) throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, fullsourceDirName)); - if (!sameDirectoryDifferentCase // This check is to allowing renaming of directories + if (!sameDirectoryDifferentCase // This check is to allow renaming of directories && FileSystem.DirectoryExists(fulldestDirName)) throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, fulldestDirName)); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 0151499893a..43111fb81ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -380,7 +380,7 @@ namespace System.IO // On Windows we end up with ERROR_INVALID_NAME, which is // "The filename, directory name, or volume label syntax is incorrect." // - // This surfaces as a IOException, if we let it go beyond here it would + // This surfaces as an IOException, if we let it go beyond here it would // give DirectoryNotFound. if (Path.EndsInDirectorySeparator(sourceFullPath)) @@ -405,7 +405,7 @@ namespace System.IO case Interop.Error.EACCES: // match Win32 exception throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), errorInfo.RawErrno); default: - throw Interop.GetExceptionForIoErrno(errorInfo, sourceFullPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, isDirectory: true); } } } From 62fa1f838e48459cda27c3d3f991253fdec9b4f8 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 15 Jul 2021 21:01:18 -0700 Subject: [PATCH 612/926] Fix Unicode BiDi Category (#55790) --- .../Globalization/CharUnicodeInfoTestData.cs | 19 +++++++++++++++---- .../Globalization/CharUnicodeInfoTests.cs | 7 +++++++ .../Globalization/StrongBidiCategory.cs | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTestData.cs b/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTestData.cs index a9b4ec09896..e61b46506a8 100644 --- a/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTestData.cs +++ b/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTestData.cs @@ -44,9 +44,11 @@ namespace System.Globalization.Tests string charName = data[1]; string charCategoryString = data[2]; string numericValueString = data[8]; + StrongBidiCategory bidiCategory = data[4] == "L" ? StrongBidiCategory.StrongLeftToRight : + data[4] == "R" || data[4] == "AL" ? StrongBidiCategory.StrongRightToLeft : StrongBidiCategory.Other; int codePoint = int.Parse(charValueString, NumberStyles.HexNumber); - Parse(testCases, codePoint, charCategoryString, numericValueString); + Parse(testCases, codePoint, charCategoryString, numericValueString, bidiCategory); if (charName.EndsWith("First>")) { @@ -59,7 +61,7 @@ namespace System.Globalization.Tests { // Assumes that all code points in the range have the same numeric value // and general category - Parse(testCases, rangeCodePoint, charCategoryString, numericValueString); + Parse(testCases, rangeCodePoint, charCategoryString, numericValueString, bidiCategory); } } } @@ -99,7 +101,7 @@ namespace System.Globalization.Tests ["Lu"] = UnicodeCategory.UppercaseLetter }; - private static void Parse(List testCases, int codePoint, string charCategoryString, string numericValueString) + private static void Parse(List testCases, int codePoint, string charCategoryString, string numericValueString, StrongBidiCategory bidiCategory) { string codeValueRepresentation = codePoint > char.MaxValue ? char.ConvertFromUtf32(codePoint) : ((char)codePoint).ToString(); double numericValue = ParseNumericValueString(numericValueString); @@ -110,7 +112,8 @@ namespace System.Globalization.Tests Utf32CodeValue = codeValueRepresentation, GeneralCategory = generalCategory, NumericValue = numericValue, - CodePoint = codePoint + CodePoint = codePoint, + BidiCategory = bidiCategory }); } @@ -141,11 +144,19 @@ namespace System.Globalization.Tests } } + public enum StrongBidiCategory + { + Other = 0x00, + StrongLeftToRight = 0x20, + StrongRightToLeft = 0x40, + } + public class CharUnicodeInfoTestCase { public string Utf32CodeValue { get; set; } public int CodePoint { get; set; } public UnicodeCategory GeneralCategory { get; set; } public double NumericValue { get; set; } + public StrongBidiCategory BidiCategory { get; set; } } } diff --git a/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTests.cs b/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTests.cs index 9ea884f7802..1ec1a36f811 100644 --- a/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTests.cs +++ b/src/libraries/System.Globalization/tests/System/Globalization/CharUnicodeInfoTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; +using System.Reflection; namespace System.Globalization.Tests { @@ -12,6 +13,9 @@ namespace System.Globalization.Tests [Fact] public void GetUnicodeCategory() { + MethodInfo GetBidiCategory = Type.GetType("System.Globalization.CharUnicodeInfo").GetMethod("GetBidiCategoryNoBoundsChecks", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.InvokeMethod); + object [] parameters = new object [] { 0 }; + foreach (CharUnicodeInfoTestCase testCase in CharUnicodeInfoTestData.TestCases) { if (testCase.Utf32CodeValue.Length == 1) @@ -22,6 +26,9 @@ namespace System.Globalization.Tests // Test the string overload for a surrogate pair or a single char GetUnicodeCategoryTest_String(testCase.Utf32CodeValue, new UnicodeCategory[] { testCase.GeneralCategory }); Assert.Equal(testCase.GeneralCategory, CharUnicodeInfo.GetUnicodeCategory(testCase.CodePoint)); + + parameters[0] = (uint)testCase.CodePoint; + Assert.Equal(testCase.BidiCategory, (StrongBidiCategory)GetBidiCategory.Invoke(null, parameters)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/StrongBidiCategory.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/StrongBidiCategory.cs index b9f04470c94..b1da801c731 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/StrongBidiCategory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/StrongBidiCategory.cs @@ -10,7 +10,7 @@ namespace System.Globalization internal enum StrongBidiCategory { Other = 0x00, - StrongLeftToRight = 0x10, - StrongRightToLeft = 0x20, + StrongLeftToRight = 0x20, + StrongRightToLeft = 0x40, } } From e828a01cc9fb6846a9d24ecd14e3e106b40ec9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 16 Jul 2021 09:51:25 +0200 Subject: [PATCH 613/926] Delete dead WinRT code in Activator (#55721) --- .../src/System/Activator.RuntimeType.cs | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 2ccdc5c741d..1f1643d21fd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -102,8 +102,6 @@ namespace System [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Implementation detail of Activator that linker intrinsically recognizes")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:UnrecognizedReflectionPattern", - Justification = "Implementation detail of Activator that linker intrinsically recognizes")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", Justification = "Implementation detail of Activator that linker intrinsically recognizes")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2096:UnrecognizedReflectionPattern", @@ -118,8 +116,7 @@ namespace System object?[]? activationAttributes, ref StackCrawlMark stackMark) { - Type? type = null; - Assembly? assembly = null; + Assembly assembly; if (assemblyString == null) { assembly = Assembly.GetExecutingAssembly(ref stackMark); @@ -127,23 +124,10 @@ namespace System else { AssemblyName assemblyName = new AssemblyName(assemblyString); - - if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime) - { - // WinRT type - we have to use Type.GetType - type = Type.GetType(typeName + ", " + assemblyString, throwOnError: true, ignoreCase); - } - else - { - // Classic managed type - assembly = RuntimeAssembly.InternalLoad(assemblyName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext); - } + assembly = RuntimeAssembly.InternalLoad(assemblyName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext); } - if (type == null) - { - type = assembly!.GetType(typeName, throwOnError: true, ignoreCase); - } + Type? type = assembly.GetType(typeName, throwOnError: true, ignoreCase); object? o = CreateInstance(type!, bindingAttr, binder, args, culture, activationAttributes); From 4aecc1a4143daf1659cf1637439f1b25e3e029d0 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 16 Jul 2021 11:47:08 +0300 Subject: [PATCH 614/926] Skip System.IO.IsolatedStorage.Tests.HelperTests.GetDataDirectory(scope: Machine) on Android (#55719) --- .../tests/System/IO/IsolatedStorage/HelperTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.cs index 217f4c14de9..e1a035c456e 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.cs @@ -41,7 +41,8 @@ namespace System.IO.IsolatedStorage.Tests { // Machine scope is behind a policy that isn't enabled by default // https://github.com/dotnet/runtime/issues/21742 - if (scope == IsolatedStorageScope.Machine && PlatformDetection.IsInAppContainer) + // It's also disabled on Android, see https://github.com/dotnet/runtime/issues/55693 + if (scope == IsolatedStorageScope.Machine && (PlatformDetection.IsInAppContainer || PlatformDetection.IsAndroid)) return; string path = Helper.GetDataDirectory(scope); From 3cdc4dd7384fab1a81556d2ccc6679e86766b86c Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Fri, 16 Jul 2021 11:33:49 +0200 Subject: [PATCH 615/926] Mitigate race condition in EventSource_ConnectionPoolAtMaxConnections_LogsRequestLeftQueue test (#55729) There is a unavoidable race condition between updating event counters and reading their values in WaitForEventCountersAsync. It waits for at least 2 sets of EventCounter event groups to ensure the last group is captured after any actual work (tests check that counters reset back to 0 if there is nothing happening). Since it looks for `requests-started` which occurs before `http11-requests-queue-duration` event, what it may see is only the tail of the last group and the start of the second, without waiting for a fresh `http11-requests-queue-duration`. In this, the assert `Assert.Equal(0, http11requestQueueDurations[^1])` on the line 549 will see a non-zero counter value and fail. This PR changes the condition to `< 3` to guarantee there is at least one full group of events in the window. Co-authored by @MihaZupan Fixes #46073 --- .../System.Net.Http/tests/FunctionalTests/TelemetryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs index 660a93df25b..04328257f01 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs @@ -673,7 +673,7 @@ namespace System.Net.Http.Functional.Tests DateTime startTime = DateTime.UtcNow; int startCount = events.Count; - while (events.Skip(startCount).Count(e => IsRequestsStartedEventCounter(e.Event)) < 2) + while (events.Skip(startCount).Count(e => IsRequestsStartedEventCounter(e.Event)) < 3) { if (DateTime.UtcNow.Subtract(startTime) > TimeSpan.FromSeconds(30)) throw new TimeoutException($"Timed out waiting for EventCounters"); From 55198524dbd709ba29627774b9547f04b1b73926 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Fri, 16 Jul 2021 11:37:04 +0200 Subject: [PATCH 616/926] Annotate WebSockets.RegisterPrefixes with [Obsolete] (#55640) Fixes #50530 --- .../System.Net.WebSockets/ref/System.Net.WebSockets.cs | 1 + .../src/System/Net/WebSockets/WebSocket.cs | 1 + src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/libraries/System.Net.WebSockets/ref/System.Net.WebSockets.cs b/src/libraries/System.Net.WebSockets/ref/System.Net.WebSockets.cs index 32ebf5eb1e8..69cd0cba6f1 100644 --- a/src/libraries/System.Net.WebSockets/ref/System.Net.WebSockets.cs +++ b/src/libraries/System.Net.WebSockets/ref/System.Net.WebSockets.cs @@ -39,6 +39,7 @@ namespace System.Net.WebSockets public abstract System.Threading.Tasks.Task ReceiveAsync(System.ArraySegment buffer, System.Threading.CancellationToken cancellationToken); public virtual System.Threading.Tasks.ValueTask ReceiveAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.")] public static void RegisterPrefixes() { } public abstract System.Threading.Tasks.Task SendAsync(System.ArraySegment buffer, System.Net.WebSockets.WebSocketMessageType messageType, bool endOfMessage, System.Threading.CancellationToken cancellationToken); public virtual System.Threading.Tasks.ValueTask SendAsync(System.ReadOnlyMemory buffer, System.Net.WebSockets.WebSocketMessageType messageType, bool endOfMessage, System.Threading.CancellationToken cancellationToken) { throw null; } diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs index 044c7b95536..5f24f9a9808 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs @@ -187,6 +187,7 @@ namespace System.Net.WebSockets public static bool IsApplicationTargeting45() => true; [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.")] public static void RegisterPrefixes() { // The current WebRequest implementation in the libraries does not support upgrading diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs index 19b38d8d217..3d0b54bd8ea 100644 --- a/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs +++ b/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs @@ -91,7 +91,9 @@ namespace System.Net.WebSockets.Tests [Fact] public static void RegisterPrefixes_Unsupported() { +#pragma warning disable 0618 // Obsolete API Assert.Throws(() => WebSocket.RegisterPrefixes()); +#pragma warning restore 0618 } [Fact] From 0ac3a5b25b7e337e71155b21b140800b8ca8a97a Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Fri, 16 Jul 2021 11:58:27 +0200 Subject: [PATCH 617/926] Add a time-based decay to the linear allocation model. (#55174) Real world scenario that motivated this allocated a huge amount of data once a day, leading to high gen 0 and gen 1 budgets (several GB). After this, there was relatively little activity, causing gen 1 budget to take several hours to normalize. The new logic discounts the previous desired allocation over a period of 5 minutes. --- src/coreclr/gc/gc.cpp | 29 +++++++++++++++-------------- src/coreclr/gc/gcpriv.h | 7 +++++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 6cf5283476a..eb0d85583a8 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -20265,6 +20265,7 @@ void gc_heap::update_collection_counts () } dd_gc_clock (dd) = dd_gc_clock (dd0); + dd_previous_time_clock (dd) = dd_time_clock (dd); dd_time_clock (dd) = now; } } @@ -36712,6 +36713,7 @@ bool gc_heap::init_dynamic_data() dynamic_data* dd = dynamic_data_of (i); dd->gc_clock = 0; dd->time_clock = now; + dd->previous_time_clock = now; dd->current_size = 0; dd->promoted_size = 0; dd->collection_count = 0; @@ -36737,21 +36739,19 @@ float gc_heap::surv_to_growth (float cst, float limit, float max_limit) //not be correct (collection happened too soon). Correct with a linear estimation based on the previous //value of the budget static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, - size_t previous_desired_allocation, size_t collection_count) + size_t previous_desired_allocation, float time_since_previous_collection_secs) { if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0)) { - dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0))); - new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation); + const float decay_time = 5*60.0f; // previous desired allocation expires over 5 minutes + float decay_factor = (decay_time <= time_since_previous_collection_secs) ? + 0 : + ((decay_time - time_since_previous_collection_secs) / decay_time); + float previous_allocation_factor = (1.0f - allocation_fraction) * decay_factor; + dprintf (2, ("allocation fraction: %d, decay factor: %d, previous allocation factor: %d", + (int)(allocation_fraction*100.0), (int)(decay_factor*100.0), (int)(previous_allocation_factor*100.0))); + new_allocation = (size_t)((1.0 - previous_allocation_factor)*new_allocation + previous_allocation_factor * previous_desired_allocation); } -#if 0 - size_t smoothing = 3; // exponential smoothing factor - if (smoothing > collection_count) - smoothing = collection_count; - new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1)); -#else - UNREFERENCED_PARAMETER(collection_count); -#endif //0 return new_allocation; } @@ -36778,6 +36778,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, float f = 0; size_t max_size = dd_max_size (dd); size_t new_allocation = 0; + float time_since_previous_collection_secs = (dd_time_clock (dd) - dd_previous_time_clock (dd))*1e-6f; float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd)); if (gen_number >= max_generation) @@ -36804,7 +36805,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, new_allocation = max((new_size - current_size), min_gc_size); new_allocation = linear_allocation_model (allocation_fraction, new_allocation, - dd_desired_allocation (dd), dd_collection_count (dd)); + dd_desired_allocation (dd), time_since_previous_collection_secs); if ( #ifdef BGC_SERVO_TUNING @@ -36856,7 +36857,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, max ((current_size/4), min_gc_size)); new_allocation = linear_allocation_model (allocation_fraction, new_allocation, - dd_desired_allocation (dd), dd_collection_count (dd)); + dd_desired_allocation (dd), time_since_previous_collection_secs); } } @@ -36868,7 +36869,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size); new_allocation = linear_allocation_model (allocation_fraction, new_allocation, - dd_desired_allocation (dd), dd_collection_count (dd)); + dd_desired_allocation (dd), time_since_previous_collection_secs); if (gen_number == 0) { diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index d3f3526925d..bcdc0131c19 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -885,6 +885,7 @@ public: size_t fragmentation; //fragmentation when we don't compact size_t gc_clock; //gc# when last GC happened uint64_t time_clock; //time when last gc started + uint64_t previous_time_clock; // time when previous gc started size_t gc_elapsed_time; // Time it took for the gc to complete float gc_speed; // speed in bytes/msec for the gc to complete @@ -5004,6 +5005,12 @@ uint64_t& dd_time_clock (dynamic_data* inst) { return inst->time_clock; } +inline +uint64_t& dd_previous_time_clock (dynamic_data* inst) +{ + return inst->previous_time_clock; +} + inline size_t& dd_gc_clock_interval (dynamic_data* inst) From c7dedccd9602cfb89c2aeb6f4b479c932b822b8e Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 16 Jul 2021 06:38:51 -0400 Subject: [PATCH 618/926] Use more string interpolation (#55738) * Use more string interpolation I previously did a pass looking for opportunities. Finding a few more, as well as fixing some misuse of existing methods. * Update src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs Co-authored-by: halgab <24685886+halgab@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jan Kotas Co-authored-by: halgab <24685886+halgab@users.noreply.github.com> Co-authored-by: Jan Kotas --- .../src/CallSiteJsonFormatter.cs | 2 +- .../System/ComponentModel/Win32Exception.cs | 4 ++-- .../src/System/Data/DataViewManager.cs | 3 ++- .../tests/ProcessStartInfoTests.cs | 7 +++---- .../tests/ProcessTests.cs | 6 +++--- .../System/Globalization/GraphemeBreakTest.cs | 6 +++--- .../tests/ILReader/FormatProvider.cs | 14 ++++++------- .../src/System/Diagnostics/StackTrace.cs | 2 +- .../System/Globalization/DateTimeFormat.cs | 6 +++--- .../src/System/Globalization/SortKey.cs | 21 +++++-------------- .../System/Reflection/LocalVariableInfo.cs | 12 +++-------- .../Resources/FileBasedResourceGroveler.cs | 3 ++- .../Runtime/Loader/AssemblyLoadContext.cs | 2 +- .../Runtime/Versioning/FrameworkName.cs | 19 +++-------------- .../src/System/Text/DecoderFallback.cs | 2 +- .../src/System/Xml/XmlBaseWriter.cs | 2 +- .../src/System/Xml/XmlDictionaryWriter.cs | 3 +-- .../System.Private.Uri/src/System/Uri.cs | 6 ++---- .../tests/xNodeReader/ReadBinHex.cs | 4 ++-- .../src/System/Xml/Cache/XPathNodeInfoAtom.cs | 2 +- .../System/Xml/Core/XmlWellFormedWriter.cs | 4 ++-- .../src/System/Xml/Schema/ContentValidator.cs | 2 +- .../System/Xml/Xsl/Runtime/XmlQueryRuntime.cs | 2 +- .../tests/XmlReaderLib/ReadBase64.cs | 2 +- .../tests/XmlReaderLib/ReadBinHex.cs | 4 ++-- .../tests/DescriptionNameTests.cs | 2 +- .../XmlCanonicalizationTest.cs | 5 ++--- .../tests/ChainTests.cs | 2 +- .../tests/TextEncoderBatteryTests.cs | 2 +- .../Text/RegularExpressions/RegexCode.cs | 2 +- .../Text/RegularExpressions/RegexCompiler.cs | 2 +- .../Text/RegularExpressions/RegexNode.cs | 4 ++-- .../tests/TimerFiringTests.cs | 5 ++--- .../Transactions/InternalTransaction.cs | 2 +- .../src/System/Web/HttpUtility.cs | 14 ++++++------- 35 files changed, 72 insertions(+), 108 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs index a2b3d5f0952..eb26ebb7cfa 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs @@ -183,7 +183,7 @@ namespace Microsoft.Extensions.DependencyInjection } else { - Builder.AppendFormat( "null"); + Builder.Append( "null"); } } diff --git a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs index e3159637dc7..0fde7ca4f87 100644 --- a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs +++ b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs @@ -91,11 +91,11 @@ namespace System.ComponentModel : NativeErrorCode.ToString(CultureInfo.InvariantCulture); if (HResult == E_FAIL) { - s.AppendFormat($" ({nativeErrorString})"); + s.Append($" ({nativeErrorString})"); } else { - s.AppendFormat($" ({HResult:X8}, {nativeErrorString})"); + s.Append($" ({HResult:X8}, {nativeErrorString})"); } if (!(string.IsNullOrEmpty(message))) diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs index 02da21f71f0..80cc255a5e4 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Globalization; using System.Text; using System.Xml; @@ -92,7 +93,7 @@ namespace System.Data foreach (DataTable dt in _dataSet.Tables) { DataViewSetting ds = _dataViewSettingsCollection[dt]; - builder.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "<{0} Sort=\"{1}\" RowFilter=\"{2}\" RowStateFilter=\"{3}\"/>", dt.EncodedTableName, ds.Sort, ds.RowFilter, ds.RowStateFilter); + builder.Append(CultureInfo.InvariantCulture, $"<{dt.EncodedTableName} Sort=\"{ds.Sort}\" RowFilter=\"{ds.RowFilter}\" RowStateFilter=\"{ds.RowStateFilter}\"/>"); } builder.Append(""); return builder.ToString(); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs index b061bf43d54..223972940e8 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs @@ -1157,12 +1157,11 @@ namespace System.Diagnostics.Tests sb.AppendLine("------------------------------"); string open = GetAssociationString(0, 1 /* ASSOCSTR_COMMAND */, ".txt", "open"); - sb.AppendFormat("Open command: {0}", open); - sb.AppendLine(); + sb.AppendLine($"Open command: {open}"); string progId = GetAssociationString(0, 20 /* ASSOCSTR_PROGID */, ".txt", null); - sb.AppendFormat("ProgID: {0}", progId); - sb.AppendLine(); + sb.AppendLine($"ProgID: {progId}"); + return sb.ToString(); } diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index cbc80bebc8d..5c0a9b554b5 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -1174,10 +1174,10 @@ namespace System.Diagnostics.Tests StringBuilder builder = new StringBuilder(); foreach (Process process in Process.GetProcesses()) { - builder.AppendFormat("Pid: '{0}' Name: '{1}'", process.Id, process.ProcessName); + builder.Append($"Pid: '{process.Id}' Name: '{process.ProcessName}'"); try { - builder.AppendFormat(" Main module: '{0}'", process.MainModule.FileName); + builder.Append($" Main module: '{process.MainModule.FileName}'"); } catch { @@ -1186,7 +1186,7 @@ namespace System.Diagnostics.Tests builder.AppendLine(); } - builder.AppendFormat("Current process id: {0} Process name: '{1}'", currentProcess.Id, currentProcess.ProcessName); + builder.Append($"Current process id: {currentProcess.Id} Process name: '{currentProcess.ProcessName}'"); return builder.ToString(); } } diff --git a/src/libraries/System.Globalization/tests/System/Globalization/GraphemeBreakTest.cs b/src/libraries/System.Globalization/tests/System/Globalization/GraphemeBreakTest.cs index 678f61b6ea8..a79b3eef2ba 100644 --- a/src/libraries/System.Globalization/tests/System/Globalization/GraphemeBreakTest.cs +++ b/src/libraries/System.Globalization/tests/System/Globalization/GraphemeBreakTest.cs @@ -222,7 +222,7 @@ namespace System.Globalization.Tests if (!char.IsHighSurrogate(thisChar) || !enumerator.MoveNext()) { // not a high surrogate, or a high surrogate at the end of the sequence - it goes as-is - sb.AppendFormat(CultureInfo.InvariantCulture, "{0:X4} ", (uint)thisChar); + sb.Append($"{(uint)thisChar:X4} "); } else { @@ -230,13 +230,13 @@ namespace System.Globalization.Tests if (!char.IsLowSurrogate(secondChar)) { // previous char was a standalone high surrogate char - send it as-is - sb.AppendFormat(CultureInfo.InvariantCulture, "{0:X4} ", (uint)thisChar); + sb.Append($"{(uint)thisChar:X4} "); goto SawStandaloneChar; } else { // surrogate pair - extract supplementary code point - sb.AppendFormat(CultureInfo.InvariantCulture, "{0:X4} ", (uint)char.ConvertToUtf32(thisChar, secondChar)); + sb.Append($"{(uint)char.ConvertToUtf32(thisChar, secondChar):X4} "); } } } diff --git a/src/libraries/System.Linq.Expressions/tests/ILReader/FormatProvider.cs b/src/libraries/System.Linq.Expressions/tests/ILReader/FormatProvider.cs index fb9b9d02b7a..76479d709f6 100644 --- a/src/libraries/System.Linq.Expressions/tests/ILReader/FormatProvider.cs +++ b/src/libraries/System.Linq.Expressions/tests/ILReader/FormatProvider.cs @@ -37,11 +37,11 @@ namespace System.Linq.Expressions.Tests int length = offsets.Length; for (int i = 0; i < length; i++) { - if (i == 0) sb.AppendFormat("("); - else sb.AppendFormat(", "); + if (i == 0) sb.Append('('); + else sb.Append(", "); sb.Append(Label(offsets[i])); } - sb.AppendFormat(")"); + sb.Append(')'); return sb.ToString(); } @@ -57,7 +57,7 @@ namespace System.Linq.Expressions.Tests else if (ch == '\r') sb.Append("\\r"); else if (ch == '\"') sb.Append("\\\""); else if (ch == '\\') sb.Append("\\"); - else if (ch < 0x20 || ch >= 0x7f) sb.AppendFormat("\\u{0:x4}", (int)ch); + else if (ch < 0x20 || ch >= 0x7f) sb.Append($"\\u{(int)ch:x4}"); else sb.Append(ch); } return "\"" + sb.ToString() + "\""; @@ -69,11 +69,11 @@ namespace System.Linq.Expressions.Tests int length = sig.Length; for (int i = 0; i < length; i++) { - if (i == 0) sb.AppendFormat("SIG ["); - else sb.AppendFormat(" "); + if (i == 0) sb.Append("SIG ["); + else sb.Append(' '); sb.Append(Int8ToHex(sig[i])); } - sb.AppendFormat("]"); + sb.Append(']'); return sb.ToString(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index 50d873d90b5..0e215f478dd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -225,7 +225,7 @@ namespace System.Diagnostics else sb.AppendLine(); - sb.AppendFormat(CultureInfo.InvariantCulture, " {0} ", word_At); + sb.Append(" ").Append(word_At).Append(' '); bool isAsync = false; Type? declaringType = mb.DeclaringType; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 593a9ab1e4f..e72e7885566 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -811,16 +811,16 @@ namespace System if (tokenLen <= 1) { // 'z' format e.g "-7" - result.AppendFormat(CultureInfo.InvariantCulture, "{0:0}", offset.Hours); + result.Append(CultureInfo.InvariantCulture, $"{offset.Hours:0}"); } else { // 'zz' or longer format e.g "-07" - result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}", offset.Hours); + result.Append(CultureInfo.InvariantCulture, $"{offset.Hours:00}"); if (tokenLen >= 3) { // 'zzz*' or longer format e.g "-07:30" - result.AppendFormat(CultureInfo.InvariantCulture, ":{0:00}", offset.Minutes); + result.Append(CultureInfo.InvariantCulture, $":{offset.Minutes:00}"); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/SortKey.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/SortKey.cs index db67dce64c5..a98b7b880d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/SortKey.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/SortKey.cs @@ -6,9 +6,7 @@ using System.Diagnostics.CodeAnalysis; namespace System.Globalization { - /// - /// This class implements a set of methods for retrieving - /// + /// Represents the result of mapping a string to its sort key. public sealed partial class SortKey { private readonly CompareInfo _compareInfo; @@ -67,20 +65,11 @@ namespace System.Globalization return new ReadOnlySpan(key1Data).SequenceCompareTo(key2Data); } - public override bool Equals([NotNullWhen(true)] object? value) - { - return value is SortKey other - && new ReadOnlySpan(_keyData).SequenceEqual(other._keyData); - } + public override bool Equals([NotNullWhen(true)] object? value) => + value is SortKey other && new ReadOnlySpan(_keyData).SequenceEqual(other._keyData); - public override int GetHashCode() - { - return _compareInfo.GetHashCode(_string, _options); - } + public override int GetHashCode() => _compareInfo.GetHashCode(_string, _options); - public override string ToString() - { - return "SortKey - " + _compareInfo.Name + ", " + _options + ", " + _string; - } + public override string ToString() => $"SortKey - {_compareInfo.Name}, {_options}, {_string}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs index 9b662e713e9..2e1bf6d2c99 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs @@ -11,14 +11,8 @@ namespace System.Reflection public virtual int LocalIndex => 0; public virtual bool IsPinned => false; protected LocalVariableInfo() { } - public override string ToString() - { - string toString = LocalType.ToString() + " (" + LocalIndex + ")"; - - if (IsPinned) - toString += " (pinned)"; - - return toString; - } + public override string ToString() => IsPinned ? + $"{LocalType} ({LocalIndex}) (pinned)" : + $"{LocalType} ({LocalIndex})"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs index 77b4a681354..ad98312fadd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs @@ -49,7 +49,8 @@ namespace System.Resources { // We really don't think this should happen - we always // expect the neutral locale's resources to be present. - throw new MissingManifestResourceException(SR.MissingManifestResource_NoNeutralDisk + Environment.NewLineConst + "baseName: " + _mediator.BaseNameField + " locationInfo: " + (_mediator.LocationInfo == null ? "" : _mediator.LocationInfo.FullName) + " fileName: " + _mediator.GetResourceFileName(culture)); + string? locationInfo = _mediator.LocationInfo == null ? "" : _mediator.LocationInfo.FullName; + throw new MissingManifestResourceException($"{SR.MissingManifestResource_NoNeutralDisk}{Environment.NewLineConst}baseName: {_mediator.BaseNameField} locationInfo: {locationInfo} fileName: {_mediator.GetResourceFileName(culture)}"); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 2a5a3c1da07..b05714fd92c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -259,7 +259,7 @@ namespace System.Runtime.Loader public string? Name => _name; - public override string ToString() => "\"" + Name + "\" " + GetType().ToString() + " #" + _id; + public override string ToString() => $"\"{Name}\" {GetType()} #{_id}"; public static IEnumerable All { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs index 16f880e8591..7cff5468f96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs @@ -52,22 +52,9 @@ namespace System.Runtime.Versioning { if (_fullName == null) { - if (string.IsNullOrEmpty(Profile)) - { - _fullName = - Identifier + - ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix + - Version.ToString(); - } - else - { - _fullName = - Identifier + - ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix + - Version.ToString() + - ComponentSeparator + ProfileKey + KeyValueSeparator + - Profile; - } + _fullName = string.IsNullOrEmpty(Profile) ? + $"{Identifier}{ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix}{Version}" : + $"{Identifier}{ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix}{Version}{ComponentSeparator + ProfileKey + KeyValueSeparator}{Profile}"; } Debug.Assert(_fullName != null); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs index da8d1236f44..a584f2fd65d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs @@ -301,7 +301,7 @@ namespace System.Text { if (strBytes.Length > 0) strBytes.Append(' '); - strBytes.AppendFormat(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]); + strBytes.Append($"\\x{bytesUnknown[i]:X2}"); } // In case the string's really long if (i == 20) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs index 0b29ab21bfe..fe410e17c48 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs @@ -1769,7 +1769,7 @@ namespace System.Xml while (true) { int prefixId = _elements![_depth].PrefixId++; - prefix = string.Concat("d", _depth.ToString(CultureInfo.InvariantCulture), "p", prefixId.ToString(CultureInfo.InvariantCulture)); + prefix = string.Create(CultureInfo.InvariantCulture, $"d{_depth}p{prefixId}"); if (_nsMgr.LookupNamespace(prefix) == null) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs index 9c3b659ae76..183029efc0d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs @@ -122,8 +122,7 @@ namespace System.Xml { if (LookupPrefix(namespaceUri) != null) return; -#pragma warning suppress 56506 // Microsoft, namespaceUri is already checked - prefix = namespaceUri.Length == 0 ? string.Empty : string.Concat("d", namespaceUri.Length.ToString(System.Globalization.NumberFormatInfo.InvariantInfo)); + prefix = namespaceUri.Length == 0 ? string.Empty : $"d{namespaceUri.Length}"; } WriteAttributeString("xmlns", prefix, null, namespaceUri); } diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index dc687676f03..2c8eda44112 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -4864,12 +4864,10 @@ namespace System return basePart.Scheme + ':' + relativePart; } - // Got absolute relative path, and the base is nor FILE nor a DOS path (checked at the method start) + // Got absolute relative path, and the base is not FILE nor a DOS path (checked at the method start) if (basePart.HostType == Flags.IPv6HostType) { - left = basePart.GetParts(UriComponents.Scheme | UriComponents.UserInfo, uriFormat) - + '[' + basePart.DnsSafeHost + ']' - + basePart.GetParts(UriComponents.KeepDelimiter | UriComponents.Port, uriFormat); + left = $"{basePart.GetParts(UriComponents.Scheme | UriComponents.UserInfo, uriFormat)}[{basePart.DnsSafeHost}]{basePart.GetParts(UriComponents.KeepDelimiter | UriComponents.Port, uriFormat)}"; } else { diff --git a/src/libraries/System.Private.Xml.Linq/tests/xNodeReader/ReadBinHex.cs b/src/libraries/System.Private.Xml.Linq/tests/xNodeReader/ReadBinHex.cs index 9cac33d3f1f..dcccfdd02fc 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/xNodeReader/ReadBinHex.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/xNodeReader/ReadBinHex.cs @@ -447,7 +447,7 @@ namespace CoreXml.Test.XLinq bytes = DataReader.ReadContentAsBinHex(bbb, 0, bbb.Length); for (int i = 0; i < bytes; i++) { - output.AppendFormat(bbb[i].ToString()); + output.Append(bbb[i]); } } @@ -866,7 +866,7 @@ namespace CoreXml.Test.XLinq bytes = DataReader.ReadElementContentAsBinHex(bbb, 0, bbb.Length); for (int i = 0; i < bytes; i++) { - output.AppendFormat(bbb[i].ToString()); + output.Append(bbb[i]); } } if (TestLog.Compare(output.ToString().Length, 1735, "Expected Length : 1735")) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs index 9afbc4f400b..298001f4db9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathNodeInfoAtom.cs @@ -499,7 +499,7 @@ namespace MS.Internal.Xml.Cache for (int i = 0; i < _hashTable.Length; i++) { - bldr.AppendFormat("{0,4}: ", i); + bldr.Append($"{i,4}: "); infoAtom = _hashTable[i]; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs index f4ff03f0bf0..583bd82b6e0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs @@ -2109,7 +2109,7 @@ namespace System.Xml private string GeneratePrefix() { - string genPrefix = "p" + (_nsTop - 2).ToString("d", CultureInfo.InvariantCulture); + string genPrefix = string.Create(CultureInfo.InvariantCulture, $"p{_nsTop - 2:d}"); if (LookupNamespace(genPrefix) == null) { return genPrefix; @@ -2119,7 +2119,7 @@ namespace System.Xml string s; do { - s = string.Concat(genPrefix, i.ToString(CultureInfo.InvariantCulture)); + s = string.Create(CultureInfo.InvariantCulture, $"{genPrefix}{i}"); i++; } while (LookupNamespace(s) != null); return s; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs index 0a4ad3d346e..876fcb6abc6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs @@ -1569,7 +1569,7 @@ namespace System.Xml.Schema } else { - bb.AppendFormat(" {0:000} ", transitionTable[i][j]); + bb.Append($" {transitionTable[i][j]:000} "); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs index 4bdaf545111..4415fa1b457 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs @@ -884,7 +884,7 @@ namespace System.Xml.Xsl.Runtime /// public string GenerateId(XPathNavigator navigator) { - return string.Concat("ID", _docOrderCmp.GetDocumentIndex(navigator).ToString(CultureInfo.InvariantCulture), navigator.UniqueId); + return string.Create(CultureInfo.InvariantCulture, $"ID{_docOrderCmp.GetDocumentIndex(navigator)}{navigator.UniqueId}"); } diff --git a/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBase64.cs b/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBase64.cs index a3a7160f57b..5d7e9cc9381 100644 --- a/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBase64.cs +++ b/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBase64.cs @@ -1076,7 +1076,7 @@ namespace System.Xml.Tests for (int i = 0; i < bytes; i++) { CError.Write(bbb[i].ToString()); - output.AppendFormat(bbb[i].ToString()); + output.Append(bbb[i]); } } CError.WriteLine(); diff --git a/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBinHex.cs b/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBinHex.cs index a490ec0a8c2..f764af73700 100644 --- a/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBinHex.cs +++ b/src/libraries/System.Private.Xml/tests/XmlReaderLib/ReadBinHex.cs @@ -457,7 +457,7 @@ namespace System.Xml.Tests for (int i = 0; i < bytes; i++) { CError.Write(bbb[i].ToString()); - output.AppendFormat(bbb[i].ToString()); + output.Append(bbb[i]); } } @@ -880,7 +880,7 @@ namespace System.Xml.Tests for (int i = 0; i < bytes; i++) { CError.Write(bbb[i].ToString()); - output.AppendFormat(bbb[i].ToString()); + output.Append(bbb[i]); } } diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs index 4568724fc99..c0069896dc4 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs @@ -70,7 +70,7 @@ namespace System.Runtime.InteropServices.RuntimeInformationTests { var sb = new StringBuilder(); sb.AppendLine("### PROCESS INFORMATION:"); - sb.AppendFormat($"###\tArchitecture: {RuntimeInformation.ProcessArchitecture.ToString()}").AppendLine(); + sb.AppendLine($"###\tArchitecture: {RuntimeInformation.ProcessArchitecture}"); foreach (string prop in new string[] { nameof(p.BasePriority), diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/XmlCanonicalizationTest.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/XmlCanonicalizationTest.cs index cb61cfdaa40..a04a990f0e5 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/XmlCanonicalizationTest.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/XmlCanonicalizationTest.cs @@ -320,12 +320,11 @@ namespace System.Runtime.Serialization.Xml.Canonicalization.Tests string value = "attrValue" + i; if (prefix == null) { - sb.AppendFormat(" {0}=\"{1}\"", localName, value); + sb.Append($" {localName}=\"{value}\""); } else { - sb.AppendFormat(" {0}:{1}=\"{2}\" xmlns:{0}=\"{3}\"", - prefix, localName, value, namespaceUri); + sb.Append($" {prefix}:{localName}=\"{2}\" xmlns:{value}=\"{namespaceUri}\""); } } sb.Append(">Hello world"); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 135eed056df..bf0b9e230a1 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -741,7 +741,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests StringBuilder builder = new StringBuilder(); foreach (var status in chainValidator.ChainStatus) { - builder.AppendFormat("{0} {1}{2}", status.Status, status.StatusInformation, Environment.NewLine); + builder.AppendLine($"{status.Status} {status.StatusInformation}"); } Assert.True(chainBuildResult, diff --git a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs index 5a8f3e6a2df..f2cc020d355 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs @@ -66,7 +66,7 @@ namespace System.Text.Encodings.Web.Tests } else { - sbOutput.AppendFormat(CultureInfo.InvariantCulture, "[{0:X4}]", i); + sbOutput.Append($"[{i:X4}]"); } } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs index e76e3ea160a..ffcc7a6e074 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCode.cs @@ -292,7 +292,7 @@ namespace System.Text.RegularExpressions var sb = new StringBuilder(); int opcode = Codes[offset]; - sb.AppendFormat("{0:D6} ", offset); + sb.Append($"{offset:D6} "); sb.Append(OpcodeBacktracks(opcode & Mask) ? '*' : ' '); sb.Append(OperatorDescription(opcode)); sb.Append(Indent()); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index b9b6e791ed5..81f5ca065f0 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -5367,7 +5367,7 @@ namespace System.Text.RegularExpressions var sb = new StringBuilder(); if (_backpos > 0) { - sb.AppendFormat("{0:D6} ", _backpos); + sb.Append($"{_backpos:D6} "); } else { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs index bd55727af62..460496361d3 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs @@ -1161,11 +1161,11 @@ namespace System.Text.RegularExpressions if ((optionsAt & RegexOptions.RightToLeft) == 0) { - prev.Str += (at.Type == One) ? at.Ch.ToString() : at.Str; + prev.Str = (at.Type == One) ? $"{prev.Str}{at.Ch}" : prev.Str + at.Str; } else { - prev.Str = (at.Type == One) ? at.Ch.ToString() + prev.Str : at.Str + prev.Str; + prev.Str = (at.Type == One) ? $"{at.Ch}{prev.Str}" : at.Str + prev.Str; } } else if (at.Type == Empty) diff --git a/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs b/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs index 785fc4cc6b1..c01c8b4e644 100644 --- a/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs +++ b/src/libraries/System.Threading.Timer/tests/TimerFiringTests.cs @@ -339,12 +339,11 @@ namespace System.Threading.Tests select groupedByDueTime; var sb = new StringBuilder(); - sb.AppendFormat("{0}% out of {1} timer firings were off by more than {2}ms", - percOutOfRange, totalTimers, MillisecondsPadding); + sb.Append($"{percOutOfRange}% out of {totalTimers} timer firings were off by more than {MillisecondsPadding}ms"); foreach (IGrouping> result in results) { sb.AppendLine(); - sb.AppendFormat("Expected: {0}, Actuals: {1}", result.Key, string.Join(", ", result.Select(k => k.Value))); + sb.Append($"Expected: {result.Key}, Actuals: {string.Join(", ", result.Select(k => k.Value))}"); } Assert.True(false, sb.ToString()); diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs index b16ec9e1a17..e4633703561 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs @@ -168,7 +168,7 @@ namespace System.Transactions private static string? s_instanceIdentifier; internal static string InstanceIdentifier => - LazyInitializer.EnsureInitialized(ref s_instanceIdentifier, ref s_classSyncObject, () => Guid.NewGuid().ToString() + ":"); + LazyInitializer.EnsureInitialized(ref s_instanceIdentifier, ref s_classSyncObject, () => $"{Guid.NewGuid()}:"); // Double-checked locking pattern requires volatile for read/write synchronization private volatile bool _traceIdentifierInited; diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs index d991cc61d3f..365b6c6bcfb 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs @@ -3,7 +3,7 @@ // Authors: // Patrik Torstensson (Patrik.Torstensson@labs2.com) -// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se) +// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se) // Tim Coleman (tim@timcoleman.com) // Gonzalo Paniagua Javier (gonzalo@ximian.com) // @@ -59,19 +59,17 @@ namespace System.Web string?[] keys = AllKeys; for (int i = 0; i < count; i++) { - string[]? values = GetValues(keys[i]); + string? key = keys[i]; + string[]? values = GetValues(key); if (values != null) { foreach (string value in values) { - if (string.IsNullOrEmpty(keys[i])) + if (!string.IsNullOrEmpty(key)) { - sb.Append(UrlEncode(value)).Append('&'); - } - else - { - sb.AppendFormat($"{keys[i]}={UrlEncode(value)}&"); + sb.Append(key).Append('='); } + sb.Append(UrlEncode(value)).Append('&'); } } } From f59a132bfe9ff797f9d192d67f07673a13b6b29e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 16 Jul 2021 10:49:24 +0000 Subject: [PATCH 619/926] Update dependencies from https://github.com/dotnet/emsdk build 20210715.1 (#55784) [main] Update dependencies from dotnet/emsdk --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index aad7949895c..4e22b844f1b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - 16140a192ea2ffd8bcc3af09f6e531b3f5a7522b + 949c505e3dac05180b954b79a3fcc19009687182 diff --git a/eng/Versions.props b/eng/Versions.props index 161feb810c7..49e4c4993db 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -181,7 +181,7 @@ 11.1.0-alpha.1.21362.1 11.1.0-alpha.1.21362.1 - 6.0.0-preview.7.21364.1 + 6.0.0-rc.1.21365.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) From 87a2b500f3ec394cda327da8575b323bbc703263 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Fri, 16 Jul 2021 15:52:52 +0200 Subject: [PATCH 620/926] Disable SocketsHttpHandlerTest_Http3_MsQuic.ClientSettingsReceived_Success test (#55811) Started failing too frequently - see #55774 --- .../tests/FunctionalTests/HttpClientHandlerTest.Http3.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index b3551199a29..fe784a9c8d0 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -27,6 +27,7 @@ namespace System.Net.Http.Functional.Tests } [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55774")] [InlineData(10)] // 2 bytes settings value. [InlineData(100)] // 4 bytes settings value. [InlineData(10_000_000)] // 8 bytes settings value. From 15750305fc8773c17efe4f4224c52bf1b90b35b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 16 Jul 2021 15:53:05 +0200 Subject: [PATCH 621/926] Remove unnecessary intrinsics in mono/mini/intrinsics.c (#55806) - System.Environment.IsRunningOnWindows doesn't exist in the dotnet/runtime corelib - Microsoft.CodeAnalysis.CompilerServer.MemoryHelper.IsMemoryAvailable was used to workaround a bug that was fixed 2 years ago with https://github.com/dotnet/roslyn/pull/38239 and should no longer be relevant --- src/mono/mono/mini/intrinsics.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 90c45fd6700..f91a8a659b5 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -1623,16 +1623,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } return ins; } - } else if (in_corlib && - (strcmp (cmethod_klass_name_space, "System") == 0) && - (strcmp (cmethod_klass_name, "Environment") == 0)) { - if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) { -#ifdef TARGET_WIN32 - EMIT_NEW_ICONST (cfg, ins, 1); -#else - EMIT_NEW_ICONST (cfg, ins, 0); -#endif - } } else if (in_corlib && (strcmp (cmethod_klass_name_space, "System.Reflection") == 0) && (strcmp (cmethod_klass_name, "Assembly") == 0)) { @@ -1941,15 +1931,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } } - /* Workaround for the compiler server IsMemoryAvailable. */ - if (!strcmp ("Microsoft.CodeAnalysis.CompilerServer", cmethod_klass_name_space) && !strcmp ("MemoryHelper", cmethod_klass_name)) { - if (!strcmp (cmethod->name, "IsMemoryAvailable")) { - EMIT_NEW_ICONST (cfg, ins, 1); - ins->type = STACK_I4; - return ins; - } - } - // Return false for IsSupported for all types in System.Runtime.Intrinsics.* // if it's not handled in mono_emit_simd_intrinsics if (in_corlib && From 0acde243dabcd2e7853037fe3b390428df357fd2 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Fri, 16 Jul 2021 16:56:18 +0300 Subject: [PATCH 622/926] Make Named Pipe Server Stream name fit into allowed length on Android (#55731) * Make Named Pipe Server Stream name fit into allowed length on Android * Apply Stephen's suggestion * Remove redundant parameter * Take into account possible null terminator --- .../tests/FileSystemTest.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs index 9300660f141..fbc82ffeac6 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs @@ -61,7 +61,23 @@ namespace System.IO.Tests { return @"LOCAL\" + Guid.NewGuid().ToString("N"); } - return Guid.NewGuid().ToString("N"); + + if (PlatformDetection.IsWindows) + { + return Guid.NewGuid().ToString("N"); + } + + const int MinUdsPathLength = 104; // required min is 92, but every platform we currently target is at least 104 + const int MinAvailableForSufficientRandomness = 5; // we want enough randomness in the name to avoid conflicts between concurrent tests + string prefix = Path.Combine(Path.GetTempPath(), "CoreFxPipe_"); + int availableLength = MinUdsPathLength - prefix.Length - 1; // 1 - for possible null terminator + Assert.True(availableLength >= MinAvailableForSufficientRandomness, $"UDS prefix {prefix} length {prefix.Length} is too long"); + + return string.Create(availableLength, 0, (span, _) => + { + for (int i = 0; i < span.Length; i++) + span[i] = (char)('a' + Random.Shared.Next(0, 26)); + }); } /// From 145e58800cabb1508fea97e2eb22d01a0006bb0d Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Fri, 16 Jul 2021 07:17:58 -0700 Subject: [PATCH 623/926] increase default timout on RunClientServer methods (#55798) --- .../System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index bcb2bd247ee..e76591f4e04 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -117,7 +117,7 @@ namespace System.Net.Quic.Tests private QuicListener CreateQuicListener(QuicListenerOptions options) => new QuicListener(ImplementationProvider, options); - internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000, QuicListenerOptions listenerOptions = null) + internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000, QuicListenerOptions listenerOptions = null) { const long ClientCloseErrorCode = 11111; const long ServerCloseErrorCode = 22222; @@ -186,10 +186,10 @@ namespace System.Net.Quic.Tests ); } - internal Task RunBidirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000) + internal Task RunBidirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000) => RunStreamClientServer(clientFunction, serverFunction, bidi: true, iterations, millisecondsTimeout); - internal Task RunUnirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 10_000) + internal Task RunUnirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000) => RunStreamClientServer(clientFunction, serverFunction, bidi: false, iterations, millisecondsTimeout); internal static async Task ReadAll(QuicStream stream, byte[] buffer) From d4dcde13c969ad20deb20c99e91880de44aac9cd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 16 Jul 2021 10:49:42 -0400 Subject: [PATCH 624/926] Streamline rent/return on ArrayPool (#55710) * Streamline rent/return on ArrayPool - Stop storing and using a _bucketArraySizes. It's cheaper to recompute the shift on each use than it is to index into the array (with a bounds check). Plus less memory. - The 99% case is renting a positive length for pooled array sizes (especially now that we've bumped the limit up to a gig). Move the checks for lengths <= 0 to after the check for whether the length is poolable. - Move arrays into locals to enable the JIT to eliminate some bounds checks. - Use ThrowHelpers where we already have them - Move non-generic helpers out of generic class into Utilities - Consolidate buffer allocation in Rent to a single line - Reorganize TLS checks to be as early as possible - Use FastMod instead of % in per-core stacks * Address PR feedback --- .../TlsOverPerCoreLockedStacksArrayPool.cs | 288 +++++++----------- .../src/System/Buffers/Utilities.cs | 29 +- 2 files changed, 137 insertions(+), 180 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs index 438ca8e9cb2..3f759532614 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs @@ -34,37 +34,18 @@ namespace System.Buffers /// The maximum number of buffers to store in a bucket's global queue. private const int MaxBuffersPerArraySizePerCore = 8; - /// The length of arrays stored in the corresponding indices in and . - private readonly int[] _bucketArraySizes; + /// A per-thread array of arrays, to cache one array per array size per thread. + [ThreadStatic] + private static T[]?[]? t_tlsBuckets; + /// Used to keep track of all thread local buckets for trimming if needed. + private readonly ConditionalWeakTable _allTlsBuckets = new ConditionalWeakTable(); /// /// An array of per-core array stacks. The slots are lazily initialized to avoid creating /// lots of overhead for unused array sizes. /// private readonly PerCoreLockedStacks?[] _buckets = new PerCoreLockedStacks[NumBuckets]; - /// A per-thread array of arrays, to cache one array per array size per thread. - [ThreadStatic] - private static T[]?[]? t_tlsBuckets; - - private int _callbackCreated; - - private static readonly bool s_trimBuffers = GetTrimBuffers(); - - /// - /// Used to keep track of all thread local buckets for trimming if needed - /// - private static readonly ConditionalWeakTable? s_allTlsBuckets = - s_trimBuffers ? new ConditionalWeakTable() : null; - - /// Initialize the pool. - public TlsOverPerCoreLockedStacksArrayPool() - { - var sizes = new int[NumBuckets]; - for (int i = 0; i < sizes.Length; i++) - { - sizes[i] = Utilities.GetMaxSizeForBucket(i); - } - _bucketArraySizes = sizes; - } + /// Whether the callback to trim arrays in response to memory pressure has been created. + private int _trimCallbackCreated; /// Allocate a new PerCoreLockedStacks and try to store it into the array. private PerCoreLockedStacks CreatePerCoreLockedStacks(int bucketIndex) @@ -78,51 +59,38 @@ namespace System.Buffers public override T[] Rent(int minimumLength) { - // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though - // pooling such an array isn't valuable) as it's a valid length array, and we want the pool - // to be usable in general instead of using `new`, even for computed lengths. - if (minimumLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(minimumLength)); - } - else if (minimumLength == 0) - { - // No need to log the empty array. Our pool is effectively infinite - // and we'll never allocate for rents and never store for returns. - return Array.Empty(); - } - ArrayPoolEventSource log = ArrayPoolEventSource.Log; T[]? buffer; - // Get the bucket number for the array length + // Get the bucket number for the array length. The result may be out of range of buckets, + // either for too large a value or for 0 and negative values. int bucketIndex = Utilities.SelectBucketIndex(minimumLength); - // If the array could come from a bucket... - if (bucketIndex < _buckets.Length) + // First, try to get an array from TLS if possible. + T[]?[]? tlsBuckets = t_tlsBuckets; + if (tlsBuckets is not null && (uint)bucketIndex < (uint)tlsBuckets.Length) { - // First try to get it from TLS if possible. - T[]?[]? tlsBuckets = t_tlsBuckets; - if (tlsBuckets != null) + buffer = tlsBuckets[bucketIndex]; + if (buffer is not null) { - buffer = tlsBuckets[bucketIndex]; - if (buffer != null) + tlsBuckets[bucketIndex] = null; + if (log.IsEnabled()) { - tlsBuckets[bucketIndex] = null; - if (log.IsEnabled()) - { - log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); - } - return buffer; + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); } + return buffer; } + } - // We couldn't get a buffer from TLS, so try the global stack. - PerCoreLockedStacks? b = _buckets[bucketIndex]; - if (b != null) + // Next, try to get an array from one of the per-core stacks. + PerCoreLockedStacks?[] perCoreBuckets = _buckets; + if ((uint)bucketIndex < (uint)perCoreBuckets.Length) + { + PerCoreLockedStacks? b = perCoreBuckets[bucketIndex]; + if (b is not null) { buffer = b.TryPop(); - if (buffer != null) + if (buffer is not null) { if (log.IsEnabled()) { @@ -132,16 +100,24 @@ namespace System.Buffers } } - // No buffer available. Allocate a new buffer with a size corresponding to the appropriate bucket. - buffer = GC.AllocateUninitializedArray(_bucketArraySizes[bucketIndex]); + // No buffer available. Ensure the length we'll allocate matches that of a bucket + // so we can later return it. + minimumLength = Utilities.GetMaxSizeForBucket(bucketIndex); } - else + else if (minimumLength == 0) { - // The request was for a size too large for the pool. Allocate an array of exactly the requested length. - // When it's returned to the pool, we'll simply throw it away. - buffer = GC.AllocateUninitializedArray(minimumLength); + // We allow requesting zero-length arrays (even though pooling such an array isn't valuable) + // as it's a valid length array, and we want the pool to be usable in general instead of using + // `new`, even for computed lengths. But, there's no need to log the empty array. Our pool is + // effectively infinite for empty arrays and we'll never allocate for rents and never store for returns. + return Array.Empty(); + } + else if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); } + buffer = GC.AllocateUninitializedArray(minimumLength); if (log.IsEnabled()) { int bufferId = buffer.GetHashCode(); @@ -150,73 +126,58 @@ namespace System.Buffers ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); } - return buffer; } public override void Return(T[] array, bool clearArray = false) { - if (array == null) + if (array is null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } // Determine with what bucket this array length is associated int bucketIndex = Utilities.SelectBucketIndex(array.Length); - // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool. + // Make sure our TLS buckets are initialized. Technically we could avoid doing + // this if the array being returned is erroneous or too large for the pool, but the + // former condition is an error we don't need to optimize for, and the latter is incredibly + // rare, given a max size of 1B elements. + T[]?[] tlsBuckets = t_tlsBuckets ?? InitializeTlsBucketsAndTrimming(); + + bool haveBucket = false; bool returned = true; - bool haveBucket = bucketIndex < _buckets.Length; - if (haveBucket) + if ((uint)bucketIndex < (uint)tlsBuckets.Length) { - // Clear the array if the user requests. + haveBucket = true; + + // Clear the array if the user requested it. if (clearArray) { Array.Clear(array); } - // Check to see if the buffer is the correct size for this bucket - if (array.Length != _bucketArraySizes[bucketIndex]) + // Check to see if the buffer is the correct size for this bucket. + if (array.Length != Utilities.GetMaxSizeForBucket(bucketIndex)) { throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); } - // Write through the TLS bucket. If there weren't any buckets, create them - // and store this array into it. If there were, store this into it, and - // if there was a previous one there, push that to the global stack. This - // helps to keep LIFO access such that the most recently pushed stack will - // be in TLS and the first to be popped next. - T[]?[]? tlsBuckets = t_tlsBuckets; - if (tlsBuckets == null) + // Store the array into the TLS bucket. If there's already an array in it, + // push that array down into the per-core stacks, preferring to keep the latest + // one in TLS for better locality. + T[]? prev = tlsBuckets[bucketIndex]; + tlsBuckets[bucketIndex] = array; + if (prev is not null) { - t_tlsBuckets = tlsBuckets = new T[NumBuckets][]; - tlsBuckets[bucketIndex] = array; - if (s_trimBuffers) - { - Debug.Assert(s_allTlsBuckets != null, "Should be non-null iff s_trimBuffers is true"); - s_allTlsBuckets.Add(tlsBuckets, null); - if (Interlocked.Exchange(ref _callbackCreated, 1) != 1) - { - Gen2GcCallback.Register(Gen2GcCallbackFunc, this); - } - } - } - else - { - T[]? prev = tlsBuckets[bucketIndex]; - tlsBuckets[bucketIndex] = array; - - if (prev != null) - { - PerCoreLockedStacks stackBucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); - returned = stackBucket.TryPush(prev); - } + PerCoreLockedStacks stackBucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); + returned = stackBucket.TryPush(prev); } } // Log that the buffer was returned ArrayPoolEventSource log = ArrayPoolEventSource.Log; - if (log.IsEnabled()) + if (log.IsEnabled() && array.Length != 0) { log.BufferReturned(array.GetHashCode(), array.Length, Id); if (!(haveBucket & returned)) @@ -230,11 +191,8 @@ namespace System.Buffers public bool Trim() { - Debug.Assert(s_trimBuffers); - Debug.Assert(s_allTlsBuckets != null); - int milliseconds = Environment.TickCount; - MemoryPressure pressure = GetMemoryPressure(); + Utilities.MemoryPressure pressure = Utilities.GetMemoryPressure(); ArrayPoolEventSource log = ArrayPoolEventSource.Log; if (log.IsEnabled()) @@ -245,21 +203,21 @@ namespace System.Buffers PerCoreLockedStacks?[] perCoreBuckets = _buckets; for (int i = 0; i < perCoreBuckets.Length; i++) { - perCoreBuckets[i]?.Trim((uint)milliseconds, Id, pressure, _bucketArraySizes[i]); + perCoreBuckets[i]?.Trim((uint)milliseconds, Id, pressure, Utilities.GetMaxSizeForBucket(i)); } - if (pressure == MemoryPressure.High) + if (pressure == Utilities.MemoryPressure.High) { // Under high pressure, release all thread locals if (log.IsEnabled()) { - foreach (KeyValuePair tlsBuckets in s_allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets) { T[]?[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) { T[]? buffer = Interlocked.Exchange(ref buckets[i], null); - if (buffer != null) + if (buffer is not null) { // As we don't want to take a perf hit in the rent path it // is possible that a buffer could be rented as we "free" it. @@ -270,10 +228,9 @@ namespace System.Buffers } else { - foreach (KeyValuePair tlsBuckets in s_allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets) { - T[]?[] buckets = tlsBuckets.Key; - Array.Clear(buckets); + Array.Clear(tlsBuckets.Key); } } } @@ -281,61 +238,27 @@ namespace System.Buffers return true; } - /// - /// This is the static function that is called from the gen2 GC callback. - /// The input object is the instance we want the callback on. - /// - /// - /// The reason that we make this function static and take the instance as a parameter is that - /// we would otherwise root the instance to the Gen2GcCallback object, leaking the instance even when - /// the application no longer needs it. - /// - private static bool Gen2GcCallbackFunc(object target) + private T[]?[] InitializeTlsBucketsAndTrimming() { - return ((TlsOverPerCoreLockedStacksArrayPool)(target)).Trim(); - } + Debug.Assert(t_tlsBuckets is null); - private enum MemoryPressure - { - Low, - Medium, - High - } + T[]?[]? tlsBuckets = new T[NumBuckets][]; + t_tlsBuckets = tlsBuckets; - private static MemoryPressure GetMemoryPressure() - { - const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high" - const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium" - - GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); - if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold) + _allTlsBuckets.Add(tlsBuckets, null); + if (Interlocked.Exchange(ref _trimCallbackCreated, 1) == 0) { - return MemoryPressure.High; + Gen2GcCallback.Register(s => ((TlsOverPerCoreLockedStacksArrayPool)s).Trim(), this); } - else if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold) - { - return MemoryPressure.Medium; - } - return MemoryPressure.Low; + + return tlsBuckets; } - private static bool GetTrimBuffers() - { - // Environment uses ArrayPool, so we have to hit the API directly. -#if !CORECLR - // P/Invokes are different for CoreCLR/RT- for RT we'll not allow - // enabling/disabling for now. - return true; -#else - return CLRConfig.GetBoolValueWithFallbacks("System.Buffers.ArrayPool.TrimShared", "DOTNET_SYSTEM_BUFFERS_ARRAYPOOL_TRIMSHARED", defaultValue: true); -#endif - } - - /// - /// Stores a set of stacks of arrays, with one stack per core. - /// + /// Stores a set of stacks of arrays, with one stack per core. private sealed class PerCoreLockedStacks { + /// Number of locked stacks to employ. + private static readonly int s_lockedStackCount = Math.Min(Environment.ProcessorCount, MaxPerCorePerArraySizeStacks); /// The stacks. private readonly LockedStack[] _perCoreStacks; @@ -343,7 +266,7 @@ namespace System.Buffers public PerCoreLockedStacks() { // Create the stacks. We create as many as there are processors, limited by our max. - var stacks = new LockedStack[Math.Min(Environment.ProcessorCount, MaxPerCorePerArraySizeStacks)]; + var stacks = new LockedStack[s_lockedStackCount]; for (int i = 0; i < stacks.Length; i++) { stacks[i] = new LockedStack(); @@ -372,20 +295,19 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public T[]? TryPop() { - // Try to pop from the associated stack first. If that fails, - // round-robin through the other stacks. + // Try to pop from the associated stack first. If that fails, round-robin through the other stacks. T[]? arr; LockedStack[] stacks = _perCoreStacks; - int index = Thread.GetCurrentProcessorId() % stacks.Length; + int index = Thread.GetCurrentProcessorId() % s_lockedStackCount; // when ProcessorCount is a power of two, the JIT can optimize this in tier 1 for (int i = 0; i < stacks.Length; i++) { - if ((arr = stacks[index].TryPop()) != null) return arr; + if ((arr = stacks[index].TryPop()) is not null) return arr; if (++index == stacks.Length) index = 0; } return null; } - public void Trim(uint tickCount, int id, MemoryPressure pressure, int bucketSize) + public void Trim(uint tickCount, int id, Utilities.MemoryPressure pressure, int bucketSize) { LockedStack[] stacks = _perCoreStacks; for (int i = 0; i < stacks.Length; i++) @@ -395,7 +317,7 @@ namespace System.Buffers } } - /// Provides a simple stack of arrays, protected by a lock. + /// Provides a simple, bounded stack of arrays, protected by a lock. private sealed class LockedStack { private readonly T[]?[] _arrays = new T[MaxBuffersPerArraySizePerCore][]; @@ -407,15 +329,18 @@ namespace System.Buffers { bool enqueued = false; Monitor.Enter(this); - if (_count < MaxBuffersPerArraySizePerCore) + T[]?[] arrays = _arrays; + int count = _count; + if ((uint)count < (uint)arrays.Length) { - if (s_trimBuffers && _count == 0) + if (count == 0) { // Stash the time the bottom of the stack was filled _firstStackItemMS = (uint)Environment.TickCount; } - _arrays[_count++] = array; + arrays[count] = array; + _count = count + 1; enqueued = true; } Monitor.Exit(this); @@ -427,16 +352,19 @@ namespace System.Buffers { T[]? arr = null; Monitor.Enter(this); - if (_count > 0) + T[]?[] arrays = _arrays; + int count = _count - 1; + if ((uint)count < (uint)arrays.Length) { - arr = _arrays[--_count]; - _arrays[_count] = null; + arr = arrays[count]; + arrays[count] = null; + _count = count; } Monitor.Exit(this); return arr; } - public void Trim(uint tickCount, int id, MemoryPressure pressure, int bucketSize) + public void Trim(uint tickCount, int id, Utilities.MemoryPressure pressure, int bucketSize) { const uint StackTrimAfterMS = 60 * 1000; // Trim after 60 seconds for low/moderate pressure const uint StackHighTrimAfterMS = 10 * 1000; // Trim after 10 seconds for high pressure @@ -449,8 +377,11 @@ namespace System.Buffers const int StackLargeTypeSize = 32; // If T is larger than this we'll trim an extra (additional) when under high pressure if (_count == 0) + { return; - uint trimTicks = pressure == MemoryPressure.High ? StackHighTrimAfterMS : StackTrimAfterMS; + } + + uint trimTicks = pressure == Utilities.MemoryPressure.High ? StackHighTrimAfterMS : StackTrimAfterMS; lock (this) { @@ -464,7 +395,7 @@ namespace System.Buffers int trimCount = StackLowTrimCount; switch (pressure) { - case MemoryPressure.High: + case Utilities.MemoryPressure.High: trimCount = StackHighTrimCount; // When pressure is high, aggressively trim larger arrays. @@ -481,7 +412,8 @@ namespace System.Buffers trimCount++; } break; - case MemoryPressure.Medium: + + case Utilities.MemoryPressure.Medium: trimCount = StackMediumTrimCount; break; } @@ -489,7 +421,7 @@ namespace System.Buffers while (_count > 0 && trimCount-- > 0) { T[]? array = _arrays[--_count]; - Debug.Assert(array != null, "No nulls should have been present in slots < _count."); + Debug.Assert(array is not null, "No nulls should have been present in slots < _count."); _arrays[_count] = null; if (log.IsEnabled()) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Utilities.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Utilities.cs index b7b71ec5162..c2ca163a421 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Utilities.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Utilities.cs @@ -12,8 +12,6 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int SelectBucketIndex(int bufferSize) { - Debug.Assert(bufferSize >= 0); - // Buffers are bucketed so that a request between 2^(n-1) + 1 and 2^n is given a buffer of 2^n // Bucket index is log2(bufferSize - 1) with the exception that buffers between 1 and 16 bytes // are combined, and the index is slid down by 3 to compensate. @@ -29,5 +27,32 @@ namespace System.Buffers Debug.Assert(maxSize >= 0); return maxSize; } + + internal enum MemoryPressure + { + Low, + Medium, + High + } + + internal static MemoryPressure GetMemoryPressure() + { + const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high" + const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium" + + GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); + + if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold) + { + return MemoryPressure.High; + } + + if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold) + { + return MemoryPressure.Medium; + } + + return MemoryPressure.Low; + } } } From 04a8f5e01dd78646e574f07573b9787b3caade51 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Fri, 16 Jul 2021 17:13:03 +0200 Subject: [PATCH 625/926] Fix Http2Connection.GetIdleTicks (#55820) There is currently a wrong operand order in the subtraction. Fixes #43877 --- .../Net/Http/SocketsHttpHandler/Http2Connection.cs | 2 +- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 26058a454a9..11d4d9ce5bb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1697,7 +1697,7 @@ namespace System.Net.Http { lock (SyncObject) { - return _streamsInUse == 0 ? _idleSinceTickCount - nowTicks : 0; + return _streamsInUse == 0 ? nowTicks - _idleSinceTickCount : 0; } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index b326c8bbb02..b5988fafed3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2214,7 +2214,6 @@ namespace System.Net.Http.Functional.Tests [ConditionalFact(nameof(SupportsAlpn))] [OuterLoop("Incurs long delay")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/43877")] public async Task Http2_MultipleConnectionsEnabled_IdleConnectionTimeoutExpired_ConnectionRemovedAndNewCreated() { const int MaxConcurrentStreams = 2; @@ -2244,15 +2243,6 @@ namespace System.Net.Http.Functional.Tests // Wait until the idle connection timeout expires. await connection1.WaitForClientDisconnectAsync(false).WaitAsync(TestHelper.PassingTestTimeout).ConfigureAwait(false); - // Client connection might be still alive, so send an extra request which will either land on the shutting down connection or on a new one. - try - { - await client.GetAsync(server.Address).WaitAsync(handler.PooledConnectionIdleTimeout).ConfigureAwait(false); - } - catch (Exception) - { - // Suppress all exceptions. - } Assert.True(connection1.IsInvalid); Assert.False(connection0.IsInvalid); From 1d065b6b70c77baf7aa2a7c68d288f731261b403 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Fri, 16 Jul 2021 09:26:44 -0700 Subject: [PATCH 626/926] Fix string comparison with ordinal casing with Surrogates (#55771) * Fix string comparison with ordinal casing with Surrogates * Address the feedback --- .../CompareInfo/CompareInfoTests.Compare.cs | 14 ++ .../Globalization/InvariantModeCasing.cs | 5 +- .../System/Globalization/OrdinalCasing.Icu.cs | 122 +++++++++++------- 3 files changed, 89 insertions(+), 52 deletions(-) diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs index ca3800ccfa5..740a8a7162d 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs @@ -273,6 +273,20 @@ namespace System.Globalization.Tests yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, useNls ? 0 : -1 }; yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, useNls ? 1 : -1 }; } + + // + // Ordinal comparisons with ignore casing. + // + + yield return new object[] { s_invariantCompare, "abcd", "abcd", CompareOptions.OrdinalIgnoreCase, 0}; + yield return new object[] { s_invariantCompare, "abcd", "ABCD", CompareOptions.OrdinalIgnoreCase, 0}; + yield return new object[] { s_invariantCompare, "Hello\u00F6", "HELLO\u00D6", CompareOptions.OrdinalIgnoreCase, 0}; + yield return new object[] { s_invariantCompare, "Hello\uFE6A", "Hello\U0001F601", CompareOptions.OrdinalIgnoreCase, useNls ? 1 : -1}; + yield return new object[] { s_invariantCompare, "Hello\U0001F601", "Hello\uFE6A", CompareOptions.OrdinalIgnoreCase, useNls ? -1 : 1}; + yield return new object[] { s_invariantCompare, "\uDBFF", "\uD800\uDC00", CompareOptions.OrdinalIgnoreCase, useNls ? 1 : -1}; + yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDBFF", CompareOptions.OrdinalIgnoreCase, useNls ? -1 : 1}; + yield return new object[] { s_invariantCompare, "abcdefg\uDBFF", "abcdefg\uD800\uDC00", CompareOptions.OrdinalIgnoreCase, useNls ? 1 : -1}; + yield return new object[] { s_invariantCompare, "\U00010400", "\U00010428", CompareOptions.OrdinalIgnoreCase, useNls ? -1 : 0}; } // There is a regression in Windows 190xx version with the Kana comparison. Avoid running this test there. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs index e4b09e9855d..61564e0558b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs @@ -161,14 +161,15 @@ namespace System.Globalization } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static (uint, int) GetScalar(ref char charA, int index, int length) + private static (uint, int) GetScalar(ref char source, int index, int length) { + char charA = source; if (!char.IsHighSurrogate(charA) || index >= length - 1) { return ((uint)charA, 1); } - ref char charB = ref Unsafe.Add(ref charA, 1); + char charB = Unsafe.Add(ref source, 1); if (!char.IsLowSurrogate(charB)) { return ((uint)charA, 1); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs index 8120c0e349c..3e104cba044 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/OrdinalCasing.Icu.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; using System.Diagnostics; using System.Threading; using System.Runtime.InteropServices; @@ -196,72 +197,93 @@ namespace System.Globalization ref char charA = ref strA; ref char charB = ref strB; - while (length != 0) + int index = 0; + + while (index < length) { - // optimize for Ascii cases - if (charA <= '\u00FF' || length == 1 || !char.IsHighSurrogate(charA) || !char.IsHighSurrogate(charB)) - { - if (charA == charB) - { - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); - continue; - } - - char aUpper = OrdinalCasing.ToUpper(charA); - char bUpper = OrdinalCasing.ToUpper(charB); - - if (aUpper == bUpper) - { - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); - continue; - } - - return aUpper - bUpper; - } - - // We come here only of we have valid high surrogates and length > 1 - char a = charA; char b = charB; + char lowSurrogateA = '\0'; - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); - - if (!char.IsLowSurrogate(charA) || !char.IsLowSurrogate(charB)) + if (!char.IsHighSurrogate(a) || index >= lengthA - 1 || !char.IsLowSurrogate(lowSurrogateA = Unsafe.Add(ref charA, 1))) { - // malformed Surrogates - should be rare cases - if (a != b) + if (!char.IsHighSurrogate(b) || index >= lengthB - 1 || !char.IsLowSurrogate(Unsafe.Add(ref charB, 1))) { + // + // Neither A or B are surrogates + // + + if (b == a) + { + index++; + charA = ref Unsafe.Add(ref charA, 1); + charB = ref Unsafe.Add(ref charB, 1); + continue; + } + + char aUpper = OrdinalCasing.ToUpper(a); + char bUpper = OrdinalCasing.ToUpper(b); + + if (aUpper == bUpper) + { + index++; + charA = ref Unsafe.Add(ref charA, 1); + charB = ref Unsafe.Add(ref charB, 1); + continue; + } + return a - b; } - // Should be pointing to the right characters in the string to resume at. - // Just in case we could be pointing at high surrogate now. + // + // charA is not surrogate and charB is valid surrogate + // + + return -1; + } + + // + // A is Surrogate + // + + char lowSurrogateB = '\0'; + + if (!char.IsHighSurrogate(b) || index >= lengthB - 1 || !char.IsLowSurrogate(lowSurrogateB = Unsafe.Add(ref charB, 1))) + { + // + // charB is not surrogate and charA is surrogate + // + + return 1; + } + + // + // charA and charB are surrogates + // + + Debug.Assert(lowSurrogateA != '\0'); + Debug.Assert(lowSurrogateB != '\0'); + + if (a == b && lowSurrogateA == lowSurrogateB) + { + index += 2; + charA = ref Unsafe.Add(ref charA, 2); + charB = ref Unsafe.Add(ref charB, 2); continue; } - // we come here only if we have valid full surrogates - SurrogateCasing.ToUpper(a, charA, out char h1, out char l1); - SurrogateCasing.ToUpper(b, charB, out char h2, out char l2); + uint upperSurrogateA = CharUnicodeInfo.ToUpper(UnicodeUtility.GetScalarFromUtf16SurrogatePair(a, lowSurrogateA)); + uint upperSurrogateB = CharUnicodeInfo.ToUpper(UnicodeUtility.GetScalarFromUtf16SurrogatePair(b, lowSurrogateB)); - if (h1 != h2) + if (upperSurrogateA == upperSurrogateB) { - return (int)h1 - (int)h2; + index += 2; + charA = ref Unsafe.Add(ref charA, 2); + charB = ref Unsafe.Add(ref charB, 2); + continue; } - if (l1 != l2) - { - return (int)l1 - (int)l2; - } - - length--; - charA = ref Unsafe.Add(ref charA, 1); - charB = ref Unsafe.Add(ref charB, 1); + return (int)upperSurrogateA - (int)upperSurrogateB; } return lengthA - lengthB; From b8c799df249053d4dd006b1ac9355541bd0cbc79 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 16 Jul 2021 09:55:43 -0700 Subject: [PATCH 627/926] Fix case of DefaultValue using enum backing type. (#55793) --- .../ComponentModel/ReflectPropertyDescriptor.cs | 2 +- .../tests/DescriptorTestComponent.cs | 9 +++++++++ .../tests/PropertyDescriptorTests.cs | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs index 873ad225e3e..096fef344ad 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs @@ -300,7 +300,7 @@ namespace System.ComponentModel object? defaultValue = ((DefaultValueAttribute)a).Value; bool storedAsUnderlyingType = defaultValue != null && PropertyType.IsEnum && PropertyType.GetEnumUnderlyingType() == defaultValue.GetType(); _defaultValue = storedAsUnderlyingType ? - Enum.ToObject(PropertyType, _defaultValue!) : + Enum.ToObject(PropertyType, defaultValue!) : defaultValue; } else diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/DescriptorTestComponent.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/DescriptorTestComponent.cs index e8f2f590c5c..2602b560a4d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/DescriptorTestComponent.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/DescriptorTestComponent.cs @@ -5,6 +5,12 @@ using System.Collections.Generic; namespace System.ComponentModel.Tests { + internal enum DescriptorTestEnum + { + Value0 = 0, + Value1 = 1 + } + internal class DescriptorTestComponent : IComponent, ISite { private Dictionary _services; @@ -17,6 +23,9 @@ namespace System.ComponentModel.Tests [DefaultValue(DefaultPropertyValue)] public int Property { get; set; } + [DefaultValue(0)] + public DescriptorTestEnum EnumProperty { get; set; } + public object PropertyWhichThrows { get { throw new NotImplementedException(); } } public string StringProperty { get; private set; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs index 69f4a86bbf6..f791d46824f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs @@ -144,6 +144,17 @@ namespace System.ComponentModel.Tests Assert.True(propertyDescriptor.ShouldSerializeValue(component)); } + [Fact] + public void ShouldSerializeValueHandlesEnumWithBackingTypeDefaultValue() + { + var component = new DescriptorTestComponent(); + component.EnumProperty = DescriptorTestEnum.Value1; + var properties = TypeDescriptor.GetProperties(component.GetType()); + PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.EnumProperty), false); + + Assert.True(propertyDescriptor.ShouldSerializeValue(component)); + } + [Fact] public static void ReadOnlyPropertyReturnsTrue() { From 57c4e7e664a93934ea54f5a76e353c55d4df16c5 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 16 Jul 2021 19:35:06 +0200 Subject: [PATCH 628/926] Fix UMEntryThunkCache::GetUMEntryThunk (#55834) The function was initializing UMThunkMarshInfo allocated from Stub heap without using the ExecutableWriterHolder. That causes a crash when a hosting application calls coreclr_create_delegate. This was discovered in .NET 6 Preview 6 when running a xamarin app that uses a special host. This code path is exercised only by coreclr_create_delegate. --- src/coreclr/vm/dllimportcallback.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 4f3cf879d10..11b63fa9999 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -163,7 +163,8 @@ UMEntryThunk *UMEntryThunkCache::GetUMEntryThunk(MethodDesc *pMD) Holder miHolder; miHolder.Assign(pMarshInfo); - pMarshInfo->LoadTimeInit(pMD); + ExecutableWriterHolder marshInfoWriterHolder(pMarshInfo, sizeof(UMThunkMarshInfo)); + marshInfoWriterHolder.GetRW()->LoadTimeInit(pMD); ExecutableWriterHolder thunkWriterHolder(pThunk, sizeof(UMEntryThunk)); thunkWriterHolder.GetRW()->LoadTimeInit(pThunk, NULL, NULL, pMarshInfo, pMD); From da9b16f2804e87c9c1ca9dcd9036e7b53e724f5d Mon Sep 17 00:00:00 2001 From: Anirudh Agnihotry Date: Fri, 16 Jul 2021 10:35:38 -0700 Subject: [PATCH 629/926] update branding to rc1 (#55775) --- eng/Versions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 49e4c4993db..cb014e1dc82 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -7,8 +7,8 @@ 0 0 6.0.100 - preview - 7 + rc + 1 $(MajorVersion).$(MinorVersion).0.0 From d87454ca51e42bffefde487d495904bd8c26ab33 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Fri, 16 Jul 2021 21:58:19 +0200 Subject: [PATCH 630/926] [mono] Allow overriding GetCustomAttributesData routines (#55726) * Merge common bits of Mono and CoreCLR CustomAttributeData implementation * Have CustomAttributeData.GetCustomAttributes call target.GetCustomAttributesData to allow derived classes to override the latter (like in CoreCLR) * Use Constructor, ConstructorArguments, and NamedArguments in internal routines to allow them being overridden (like with CoreCLR). --- .../src/System/Reflection/CustomAttribute.cs | 73 +--------------- .../System.Private.CoreLib.Shared.projitems | 1 + .../System/Reflection/CustomAttributeData.cs | 84 +++++++++++++++++++ .../System.Private.CoreLib.csproj | 2 +- ...uteData.cs => CustomAttributeData.Mono.cs} | 66 +++++---------- .../src/System/Reflection/RuntimeAssembly.cs | 2 +- .../src/System/Reflection/RuntimeEventInfo.cs | 2 +- .../src/System/Reflection/RuntimeFieldInfo.cs | 2 +- .../System/Reflection/RuntimeMethodInfo.cs | 4 +- .../src/System/Reflection/RuntimeModule.cs | 2 +- .../System/Reflection/RuntimeParameterInfo.cs | 2 +- .../System/Reflection/RuntimePropertyInfo.cs | 2 +- 12 files changed, 118 insertions(+), 124 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs rename src/mono/System.Private.CoreLib/src/System/Reflection/{CustomAttributeData.cs => CustomAttributeData.Mono.cs} (79%) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index cf35de24627..96c062f4885 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -12,42 +12,8 @@ using Internal.Runtime.CompilerServices; namespace System.Reflection { - public class CustomAttributeData + public partial class CustomAttributeData { - #region Public Static Members - public static IList GetCustomAttributes(MemberInfo target) - { - if (target is null) - throw new ArgumentNullException(nameof(target)); - - return target.GetCustomAttributesData(); - } - - public static IList GetCustomAttributes(Module target) - { - if (target is null) - throw new ArgumentNullException(nameof(target)); - - return target.GetCustomAttributesData(); - } - - public static IList GetCustomAttributes(Assembly target) - { - if (target is null) - throw new ArgumentNullException(nameof(target)); - - return target.GetCustomAttributesData(); - } - - public static IList GetCustomAttributes(ParameterInfo target) - { - if (target is null) - throw new ArgumentNullException(nameof(target)); - - return target.GetCustomAttributesData(); - } - #endregion - #region Internal Static Members internal static IList GetCustomAttributesInternal(RuntimeType target) { @@ -448,44 +414,7 @@ namespace System.Reflection } #endregion - #region Object Override - public override string ToString() - { - var vsb = new ValueStringBuilder(stackalloc char[256]); - - vsb.Append('['); - vsb.Append(Constructor.DeclaringType!.FullName); - vsb.Append('('); - - bool first = true; - - int count = ConstructorArguments.Count; - for (int i = 0; i < count; i++) - { - if (!first) vsb.Append(", "); - vsb.Append(ConstructorArguments[i].ToString()); - first = false; - } - - count = NamedArguments.Count; - for (int i = 0; i < count; i++) - { - if (!first) vsb.Append(", "); - vsb.Append(NamedArguments[i].ToString()); - first = false; - } - - vsb.Append(")]"); - - return vsb.ToString(); - } - public override int GetHashCode() => base.GetHashCode(); - public override bool Equals(object? obj) => obj == (object)this; - #endregion - #region Public Members - public virtual Type AttributeType => Constructor.DeclaringType!; - public virtual ConstructorInfo Constructor => m_ctor; public virtual IList ConstructorArguments diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index aa0a8b86536..b3c6bd1b6e7 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -571,6 +571,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs new file mode 100644 index 00000000000..b704dac0549 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Text; + +namespace System.Reflection +{ + public partial class CustomAttributeData + { + #region Public Static Members + public static IList GetCustomAttributes(MemberInfo target) + { + if (target is null) + throw new ArgumentNullException(nameof(target)); + + return target.GetCustomAttributesData(); + } + + public static IList GetCustomAttributes(Module target) + { + if (target is null) + throw new ArgumentNullException(nameof(target)); + + return target.GetCustomAttributesData(); + } + + public static IList GetCustomAttributes(Assembly target) + { + if (target is null) + throw new ArgumentNullException(nameof(target)); + + return target.GetCustomAttributesData(); + } + + public static IList GetCustomAttributes(ParameterInfo target) + { + if (target is null) + throw new ArgumentNullException(nameof(target)); + + return target.GetCustomAttributesData(); + } + #endregion + + #region Object Override + public override string ToString() + { + var vsb = new ValueStringBuilder(stackalloc char[256]); + + vsb.Append('['); + vsb.Append(Constructor.DeclaringType!.FullName); + vsb.Append('('); + + bool first = true; + + int count = ConstructorArguments.Count; + for (int i = 0; i < count; i++) + { + if (!first) vsb.Append(", "); + vsb.Append(ConstructorArguments[i].ToString()); + first = false; + } + + count = NamedArguments.Count; + for (int i = 0; i < count; i++) + { + if (!first) vsb.Append(", "); + vsb.Append(NamedArguments[i].ToString()); + first = false; + } + + vsb.Append(")]"); + + return vsb.ToString(); + } + public override int GetHashCode() => base.GetHashCode(); + public override bool Equals(object? obj) => obj == (object)this; + #endregion + + #region Public Members + public virtual Type AttributeType => Constructor.DeclaringType!; + #endregion + } +} diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 4edae171504..7d9be765d16 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -195,7 +195,7 @@ - + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs similarity index 79% rename from src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs rename to src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs index f5b70f502e9..e3d393670cf 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs @@ -34,7 +34,7 @@ using System.Text; namespace System.Reflection { - public class CustomAttributeData + public partial class CustomAttributeData { private sealed class LazyCAttrData { @@ -125,62 +125,49 @@ namespace System.Reflection } } - public static IList GetCustomAttributes(Assembly target) - { - return CustomAttribute.GetCustomAttributesData(target); - } - - public static IList GetCustomAttributes(MemberInfo target) - { - return CustomAttribute.GetCustomAttributesData(target); - } - internal static IList GetCustomAttributesInternal(RuntimeType target) { return CustomAttribute.GetCustomAttributesData(target); } - public static IList GetCustomAttributes(Module target) + internal static IList GetCustomAttributesInternal(RuntimeFieldInfo target) { return CustomAttribute.GetCustomAttributesData(target); } - public static IList GetCustomAttributes(ParameterInfo target) + internal static IList GetCustomAttributesInternal(RuntimeMethodInfo target) { return CustomAttribute.GetCustomAttributesData(target); } - public virtual Type AttributeType + internal static IList GetCustomAttributesInternal(RuntimeConstructorInfo target) { - get { return ctorInfo.DeclaringType!; } + return CustomAttribute.GetCustomAttributesData(target); } - public override string ToString() + internal static IList GetCustomAttributesInternal(RuntimeEventInfo target) { - ResolveArguments(); + return CustomAttribute.GetCustomAttributesData(target); + } - StringBuilder sb = new StringBuilder(); + internal static IList GetCustomAttributesInternal(RuntimePropertyInfo target) + { + return CustomAttribute.GetCustomAttributesData(target); + } - sb.Append('[').Append(ctorInfo.DeclaringType!.FullName).Append('('); - for (int i = 0; i < ctorArgs.Count; i++) - { - sb.Append(ctorArgs[i].ToString()); - if (i + 1 < ctorArgs.Count) - sb.Append(", "); - } + internal static IList GetCustomAttributesInternal(RuntimeModule target) + { + return CustomAttribute.GetCustomAttributesData(target); + } - if (namedArgs.Count > 0) - sb.Append(", "); + internal static IList GetCustomAttributesInternal(RuntimeAssembly target) + { + return CustomAttribute.GetCustomAttributesData(target); + } - for (int j = 0; j < namedArgs.Count; j++) - { - sb.Append(namedArgs[j].ToString()); - if (j + 1 < namedArgs.Count) - sb.Append(", "); - } - sb.Append(")]"); - - return sb.ToString(); + internal static IList GetCustomAttributesInternal(RuntimeParameterInfo target) + { + return CustomAttribute.GetCustomAttributesData(target); } private static T[] UnboxValues(object[] values) @@ -191,13 +178,6 @@ namespace System.Reflection return retval; } - - public override int GetHashCode() => base.GetHashCode(); - - public override bool Equals(object? obj) - { - return obj == (object)this; - } } } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index cf2511fa283..fdcf45fac64 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -275,7 +275,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } public override object[] GetCustomAttributes(bool inherit) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index e92011c9d2f..4ba3798d241 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -201,7 +201,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } public override int MetadataToken diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 15649ac800a..06eb9ce0395 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -269,7 +269,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } private void CheckGeneric() diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index 362b99998df..b642d090363 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -750,7 +750,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); @@ -988,7 +988,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs index 22b5774f693..a07856f346e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs @@ -341,7 +341,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } internal RuntimeAssembly GetRuntimeAssembly() diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index 6bbad3a81a8..ecf4b3bf325 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -206,7 +206,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index 20defaa52c4..32d4645a674 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -480,7 +480,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributes(this); + return CustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); From 2a1dcfb2e8cb91325e9c1df1f745f2499063b167 Mon Sep 17 00:00:00 2001 From: WizardBrony <66697209+WizardBrony@users.noreply.github.com> Date: Fri, 16 Jul 2021 16:32:23 -0400 Subject: [PATCH 631/926] Access modifier cleanup in Task (#55842) * Update Task.cs * Update TaskFactory.cs --- .../src/System/Threading/Tasks/Task.cs | 6 +++--- .../src/System/Threading/Tasks/TaskFactory.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 89fab991d73..458c914773c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -796,7 +796,7 @@ namespace System.Threading.Tasks /// harmful). Derived implementations may choose to only conditionally call down to this base /// implementation. /// - internal virtual bool ShouldNotifyDebuggerOfWaitCompletion // ideally would be familyAndAssembly, but that can't be done in C# + private protected virtual bool ShouldNotifyDebuggerOfWaitCompletion { get { @@ -5889,7 +5889,7 @@ namespace System.Threading.Tasks /// Returns whether we should notify the debugger of a wait completion. This returns /// true iff at least one constituent task has its bit set. /// - internal override bool ShouldNotifyDebuggerOfWaitCompletion => + private protected override bool ShouldNotifyDebuggerOfWaitCompletion => base.ShouldNotifyDebuggerOfWaitCompletion && AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks); } @@ -6128,7 +6128,7 @@ namespace System.Threading.Tasks /// Returns whether we should notify the debugger of a wait completion. This returns true /// iff at least one constituent task has its bit set. /// - internal override bool ShouldNotifyDebuggerOfWaitCompletion => + private protected override bool ShouldNotifyDebuggerOfWaitCompletion => base.ShouldNotifyDebuggerOfWaitCompletion && AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs index a9b1bf0afdf..9156d4fcdb9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs @@ -1587,7 +1587,7 @@ namespace System.Threading.Tasks /// Returns whether we should notify the debugger of a wait completion. This returns /// true iff at least one constituent task has its bit set. /// - internal override bool ShouldNotifyDebuggerOfWaitCompletion => + private protected override bool ShouldNotifyDebuggerOfWaitCompletion => base.ShouldNotifyDebuggerOfWaitCompletion && AnyTaskRequiresNotifyDebuggerOfWaitCompletion(_tasks); } @@ -1657,7 +1657,7 @@ namespace System.Threading.Tasks /// Returns whether we should notify the debugger of a wait completion. This returns /// true iff at least one constituent task has its bit set. /// - internal override bool ShouldNotifyDebuggerOfWaitCompletion => + private protected override bool ShouldNotifyDebuggerOfWaitCompletion => base.ShouldNotifyDebuggerOfWaitCompletion && AnyTaskRequiresNotifyDebuggerOfWaitCompletion(_tasks); } From 3964b9c9a5542dedc4ef0686773b995661e58320 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 16 Jul 2021 13:32:36 -0700 Subject: [PATCH 632/926] Re-enable some netfx tests (#55837) The patch states on the test machines should be in sync now... --- .../tests/Pkcs9AttributeTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs9AttributeTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs9AttributeTests.cs index 3b653450eb4..07c192c0f7a 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs9AttributeTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs9AttributeTests.cs @@ -178,7 +178,6 @@ namespace System.Security.Cryptography.Pkcs.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/45168", TargetFrameworkMonikers.NetFramework)] public static void DocumentDescriptionMissingTerminator() { byte[] rawData = "041e4d00790020004400650073006300720069007000740069006f006e002100".HexToByteArray(); @@ -252,7 +251,6 @@ namespace System.Security.Cryptography.Pkcs.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/45168", TargetFrameworkMonikers.NetFramework)] public static void DocumentNameMissingTerminator() { byte[] rawData = "04104d00790020004e0061006d0065002100".HexToByteArray(); From 16eabee0b22596d10f123b035b2daf9f7fb67aed Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 16 Jul 2021 14:34:07 -0700 Subject: [PATCH 633/926] Disable S.S.C.OpenSSL tests on the platforms that are PNSE. --- .../tests/System.Security.Cryptography.OpenSsl.Tests.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index e2d4f1aa634..ffeee555c1d 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -4,6 +4,8 @@ true + + true From 0851fee685f5a849bb3e554c4765ab3830e6bb7b Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 16 Jul 2021 14:36:53 -0700 Subject: [PATCH 634/926] Fix SslServerAuthenticationOptions.ApplicationProtocols empty list error #55447 (#55772) --- .../Interop.OpenSsl.cs | 2 +- .../Net/Security/Pal.Android/SafeDeleteSslContext.cs | 3 ++- .../System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs | 2 +- .../src/System/Net/Security/SslStreamPal.Unix.cs | 4 +++- .../tests/FunctionalTests/SslStreamAlpnTests.cs | 8 ++++++++ 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 9dd310326b0..73a9d32bb45 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -160,7 +160,7 @@ internal static partial class Interop GCHandle alpnHandle = default; try { - if (sslAuthenticationOptions.ApplicationProtocols != null) + if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0) { if (sslAuthenticationOptions.IsServer) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs index 99cb5fa68cf..62f15599c8e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs @@ -235,7 +235,8 @@ namespace System.Net Interop.AndroidCrypto.SSLStreamSetEnabledProtocols(handle, s_orderedSslProtocols.AsSpan(minIndex, maxIndex - minIndex + 1)); } - if (authOptions.ApplicationProtocols != null && Interop.AndroidCrypto.SSLSupportsApplicationProtocolsConfiguration()) + if (authOptions.ApplicationProtocols != null && authOptions.ApplicationProtocols.Count != 0 + && Interop.AndroidCrypto.SSLSupportsApplicationProtocolsConfiguration()) { // Set application protocols if the platform supports it. Otherwise, we will silently ignore the option. Interop.AndroidCrypto.SSLStreamSetApplicationProtocols(handle, authOptions.ApplicationProtocols); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 9b4cef4873d..18585eeea01 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -77,7 +77,7 @@ namespace System.Net } } - if (sslAuthenticationOptions.ApplicationProtocols != null) + if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0) { // On OSX coretls supports only client side. For server, we will silently ignore the option. if (!sslAuthenticationOptions.IsServer) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index ed0bcaaccd1..c3b2e7e291e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -171,7 +171,9 @@ namespace System.Net.Security // We have this workaround, as openssl supports terminating handshake only from version 1.1.0, // whereas ALPN is supported from version 1.0.2. SafeSslHandle sslContext = context.SslContext; - if (done && sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslContext.AlpnHandle.IsAllocated && sslContext.AlpnHandle.Target == null) + if (done && sslAuthenticationOptions.IsServer + && sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0 + && sslContext.AlpnHandle.IsAllocated && sslContext.AlpnHandle.Target == null) { return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, Interop.OpenSsl.CreateSslException(SR.net_alpn_failed)); } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs index 7848c01e9f0..1d288e2ba8a 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs @@ -211,8 +211,12 @@ namespace System.Net.Security.Tests yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; yield return new object[] { null, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List(), null }; yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null, null }; yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http2 }, null }; + yield return new object[] { new List(), new List(), null }; + yield return new object[] { null, new List(), null }; + yield return new object[] { new List(), null, null }; yield return new object[] { null, null, null }; } else @@ -221,7 +225,11 @@ namespace System.Net.Security.Tests yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; yield return new object[] { null, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, default(SslApplicationProtocol) }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List(), default(SslApplicationProtocol) }; yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null, default(SslApplicationProtocol) }; + yield return new object[] { new List(), new List(), default(SslApplicationProtocol) }; + yield return new object[] { null, new List(), default(SslApplicationProtocol) }; + yield return new object[] { new List(), null, default(SslApplicationProtocol) }; yield return new object[] { null, null, default(SslApplicationProtocol) }; } } From 719d56525ea7b89d052420e7cea88786faf771df Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 16 Jul 2021 16:04:41 -0700 Subject: [PATCH 635/926] Fix nested calls to Assembly Load Start tracing (#55700) The name_with_pid test ends up testing the behavior of event tracing in the presence of composite images. In the presence of a composite image, it might happen attempting to send trace data may force an attempt to load System.Private.CoreLib thus causing an stackoverflow as attempting to report the load of System.Private.CoreLib will trigger a load of System.Private.CoreLib, etc. This fix steps around the issue by using a thread local variable to skip subsequent attempts to report the load of System.Private.CoreLib. Fixes #55786 --- src/coreclr/binder/bindertracing.cpp | 11 +++++--- .../complus_config/name_config_with_pid.cs | 27 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/coreclr/binder/bindertracing.cpp b/src/coreclr/binder/bindertracing.cpp index 29ba819ee6c..801226e935d 100644 --- a/src/coreclr/binder/bindertracing.cpp +++ b/src/coreclr/binder/bindertracing.cpp @@ -196,6 +196,8 @@ bool BinderTracing::IsEnabled() namespace BinderTracing { + static thread_local bool t_AssemblyLoadStartInProgress = false; + AssemblyBindOperation::AssemblyBindOperation(AssemblySpec *assemblySpec, const WCHAR *assemblyPath) : m_bindRequest { assemblySpec, nullptr, assemblyPath } , m_populatedBindRequest { false } @@ -209,6 +211,7 @@ namespace BinderTracing if (!BinderTracing::IsEnabled() || ShouldIgnoreBind()) return; + t_AssemblyLoadStartInProgress = true; PopulateBindRequest(m_bindRequest); m_populatedBindRequest = true; FireAssemblyLoadStart(m_bindRequest); @@ -218,6 +221,8 @@ namespace BinderTracing { if (BinderTracing::IsEnabled() && !ShouldIgnoreBind()) { + t_AssemblyLoadStartInProgress = false; + // Make sure the bind request is populated. Tracing may have been enabled mid-bind. if (!m_populatedBindRequest) PopulateBindRequest(m_bindRequest); @@ -244,9 +249,9 @@ namespace BinderTracing if (m_checkedIgnoreBind) return m_ignoreBind; - // ActivityTracker or EventSource may have triggered the system satellite load. - // Don't track system satellite binding to avoid potential infinite recursion. - m_ignoreBind = m_bindRequest.AssemblySpec->IsCoreLibSatellite(); + // ActivityTracker or EventSource may have triggered the system satellite load, or load of System.Private.CoreLib + // Don't track such bindings to avoid potential infinite recursion. + m_ignoreBind = t_AssemblyLoadStartInProgress && (m_bindRequest.AssemblySpec->IsCoreLib() || m_bindRequest.AssemblySpec->IsCoreLibSatellite()); m_checkedIgnoreBind = true; return m_ignoreBind; } diff --git a/src/tests/tracing/eventpipe/complus_config/name_config_with_pid.cs b/src/tests/tracing/eventpipe/complus_config/name_config_with_pid.cs index 7cca9d64320..d94f8cc49fb 100644 --- a/src/tests/tracing/eventpipe/complus_config/name_config_with_pid.cs +++ b/src/tests/tracing/eventpipe/complus_config/name_config_with_pid.cs @@ -11,10 +11,17 @@ class NameConfigWithPid { static int Main(string[] args) { + if (args.Length == 0) + Console.WriteLine("No Args"); + else + Console.WriteLine($"args[0] = `{args[0]}`"); + if (args.Length > 0 && args[0] == "waitforinput") { Console.Error.WriteLine("WaitingForInput in ErrorStream"); Console.WriteLine("WaitingForInput"); + Console.Error.Flush(); + Console.Out.Flush(); Console.ReadLine(); return 100; } @@ -31,7 +38,8 @@ class NameConfigWithPid return 100; } - string corerun = Path.Combine(Environment.GetEnvironmentVariable("CORE_ROOT"), "corerun"); + string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT"); + string corerun = Path.Combine(coreRoot, "corerun"); if (OperatingSystem.IsWindows()) corerun = corerun + ".exe"; @@ -49,15 +57,30 @@ class NameConfigWithPid process.StartInfo.Environment.Add("COMPlus_EnableEventPipe", "1"); process.StartInfo.Environment.Add("COMPlus_EventPipeConfig", "Microsoft-Windows-DotNETRuntime:4c14fccbd:4"); process.StartInfo.Environment.Add("COMPlus_EventPipeOutputPath", outputPathPattern); + process.StartInfo.Environment.Add("CORE_ROOT", coreRoot); + Console.WriteLine($"Starting process '{process.StartInfo.FileName}' '{process.StartInfo.Arguments}'"); + Console.Out.Flush(); process.Start(); - process.StandardError.ReadLine(); + string readFromTargetProcess = process.StandardError.ReadLine(); + Console.WriteLine($"Readline '{readFromTargetProcess}'"); + if (readFromTargetProcess != "WaitingForInput in ErrorStream") + { + Console.WriteLine($"Child process terminating"); + Thread.Sleep(10000); + process.Kill(); + Console.WriteLine($"Child process terminated"); + } + Console.Out.Flush(); uint pid = (uint)process.Id; string expectedPath = outputPathPattern.Replace("{pid}", pid.ToString()); process.StandardInput.WriteLine("input"); + process.StandardInput.Flush(); process.WaitForExit(); + + Console.WriteLine($"StdErr ReadToEnd from child process '{process.StandardError.ReadToEnd()}'"); if (!File.Exists(expectedPath)) { Console.WriteLine($"{expectedPath} not found"); From 3a19983475f43a4f81564eda3ca8c60ea69c43d9 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 16 Jul 2021 16:14:40 -0700 Subject: [PATCH 636/926] Reimplement dynamic member access for collection converters correctly (#55846) --- .../src/System.Text.Json.csproj | 5 ++- .../Collection/IEnumerableConverterFactory.cs | 6 ++-- ...mmutableDictionaryOfTKeyTValueConverter.cs | 31 ++++--------------- ...naryOfTKeyTValueConverterWithReflection.cs | 31 +++++++++++++++++++ .../ImmutableEnumerableOfTConverter.cs | 31 ++++--------------- ...bleEnumerableOfTConverterWithReflection.cs | 30 ++++++++++++++++++ ...dConverter.cs => StackOrQueueConverter.cs} | 25 +++------------ .../StackOrQueueConverterWithReflection.cs | 28 +++++++++++++++++ .../JsonMetadataServices.Collections.cs | 6 ++-- 9 files changed, 115 insertions(+), 78 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs rename src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/{IEnumerableWithAddMethodConverter.cs => StackOrQueueConverter.cs} (63%) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 7d428ffb194..5a9ea1839ba 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -90,6 +90,9 @@ + + + @@ -121,7 +124,7 @@ - + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs index ca1e09228b1..976d043d688 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs @@ -68,7 +68,7 @@ namespace System.Text.Json.Serialization.Converters else if (typeToConvert.IsImmutableDictionaryType()) { genericArgs = typeToConvert.GetGenericArguments(); - converterType = typeof(ImmutableDictionaryOfTKeyTValueConverter<,,>); + converterType = typeof(ImmutableDictionaryOfTKeyTValueConverterWithReflection<,,>); dictionaryKeyType = genericArgs[0]; elementType = genericArgs[1]; } @@ -91,7 +91,7 @@ namespace System.Text.Json.Serialization.Converters // Immutable non-dictionaries from System.Collections.Immutable, e.g. ImmutableStack else if (typeToConvert.IsImmutableEnumerableType()) { - converterType = typeof(ImmutableEnumerableOfTConverter<,>); + converterType = typeof(ImmutableEnumerableOfTConverterWithReflection<,>); elementType = typeToConvert.GetGenericArguments()[0]; } // IList<> @@ -163,7 +163,7 @@ namespace System.Text.Json.Serialization.Converters } else if (typeToConvert.IsNonGenericStackOrQueue()) { - converterType = typeof(IEnumerableWithAddMethodConverter<>); + converterType = typeof(StackOrQueueConverterWithReflection<>); } else { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs index 316a9a35d9b..be6d5d1c032 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs @@ -3,39 +3,28 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class ImmutableDictionaryOfTKeyTValueConverter + internal class ImmutableDictionaryOfTKeyTValueConverter : DictionaryDefaultConverter where TCollection : IReadOnlyDictionary where TKey : notnull { - [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] - public ImmutableDictionaryOfTKeyTValueConverter() - { - } - - // Used by source-gen initialization for reflection-free serialization. - public ImmutableDictionaryOfTKeyTValueConverter(bool dummy) { } - - protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) + protected sealed override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) { ((Dictionary)state.Current.ReturnValue!)[key] = value; } - internal override bool CanHaveIdMetadata => false; + internal sealed override bool CanHaveIdMetadata => false; - internal override bool RequiresDynamicMemberAccessors => true; - - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { state.Current.ReturnValue = new Dictionary(); } - protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { Func>, TCollection>? creator = (Func>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs; @@ -43,7 +32,7 @@ namespace System.Text.Json.Serialization.Converters state.Current.ReturnValue = creator((Dictionary)state.Current.ReturnValue!); } - protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected internal sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator> enumerator; if (state.Current.CollectionEnumerator == null) @@ -93,13 +82,5 @@ namespace System.Text.Json.Serialization.Converters enumerator.Dispose(); return true; } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs new file mode 100644 index 00000000000..fba0358efbd --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class ImmutableDictionaryOfTKeyTValueConverterWithReflection + : ImmutableDictionaryOfTKeyTValueConverter + where TCollection : IReadOnlyDictionary + where TKey : notnull + { + [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] + public ImmutableDictionaryOfTKeyTValueConverterWithReflection() + { + } + + internal override bool RequiresDynamicMemberAccessors => true; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs index f2f2810e9ba..f274a234daf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs @@ -3,38 +3,27 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class ImmutableEnumerableOfTConverter + internal class ImmutableEnumerableOfTConverter : IEnumerableDefaultConverter where TCollection : IEnumerable { - [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] - public ImmutableEnumerableOfTConverter() - { - } - - // Used by source-gen initialization for reflection-free serialization. - public ImmutableEnumerableOfTConverter(bool dummy) { } - - protected override void Add(in TElement value, ref ReadStack state) + protected sealed override void Add(in TElement value, ref ReadStack state) { ((List)state.Current.ReturnValue!).Add(value); } - internal override bool CanHaveIdMetadata => false; + internal sealed override bool CanHaveIdMetadata => false; - internal override bool RequiresDynamicMemberAccessors => true; - - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } - protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; @@ -43,7 +32,7 @@ namespace System.Text.Json.Serialization.Converters state.Current.ReturnValue = creator((List)state.Current.ReturnValue!); } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator enumerator; if (state.Current.CollectionEnumerator == null) @@ -80,13 +69,5 @@ namespace System.Text.Json.Serialization.Converters enumerator.Dispose(); return true; } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs new file mode 100644 index 00000000000..dd6b4345df5 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class ImmutableEnumerableOfTConverterWithReflection + : ImmutableEnumerableOfTConverter + where TCollection : IEnumerable + { + [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] + public ImmutableEnumerableOfTConverterWithReflection() + { + } + + internal override bool RequiresDynamicMemberAccessors => true; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs similarity index 63% rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs rename to src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 6363e8aa3c6..04cc6488a52 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -3,29 +3,22 @@ using System.Collections; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class IEnumerableWithAddMethodConverter + internal class StackOrQueueConverter : IEnumerableDefaultConverter where TCollection : IEnumerable { - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - public IEnumerableWithAddMethodConverter() { } - - // Used by source-gen initialization for reflection-free serialization. - public IEnumerableWithAddMethodConverter(bool dummy) { } - - protected override void Add(in object? value, ref ReadStack state) + protected sealed override void Add(in object? value, ref ReadStack state) { var addMethodDelegate = ((Action?)state.Current.JsonTypeInfo.AddMethodDelegate); Debug.Assert(addMethodDelegate != null); addMethodDelegate((TCollection)state.Current.ReturnValue!, value); } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; JsonTypeInfo.ConstructorDelegate? constructorDelegate = typeInfo.CreateObject; @@ -40,7 +33,7 @@ namespace System.Text.Json.Serialization.Converters Debug.Assert(typeInfo.AddMethodDelegate != null); } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator enumerator; if (state.Current.CollectionEnumerator == null) @@ -75,15 +68,5 @@ namespace System.Text.Json.Serialization.Converters return true; } - - internal override bool RequiresDynamicMemberAccessors => true; - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs new file mode 100644 index 00000000000..a3a87f9a50f --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class StackOrQueueConverterWithReflection + : StackOrQueueConverter + where TCollection : IEnumerable + { + internal override bool RequiresDynamicMemberAccessors => true; + + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] + public StackOrQueueConverterWithReflection() { } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs index ef2b4d1a319..ceb1fca9ad1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs @@ -124,7 +124,7 @@ namespace System.Text.Json.Serialization.Metadata => new JsonTypeInfoInternal( options, createObjectFunc, - () => new ImmutableDictionaryOfTKeyTValueConverter(dummy: false), + () => new ImmutableDictionaryOfTKeyTValueConverter(), keyInfo, valueInfo, numberHandling, @@ -224,7 +224,7 @@ namespace System.Text.Json.Serialization.Metadata => new JsonTypeInfoInternal( options, createObjectFunc, - () => new ImmutableEnumerableOfTConverter(dummy: false), + () => new ImmutableEnumerableOfTConverter(), elementInfo, numberHandling, serializeFunc, @@ -525,7 +525,7 @@ namespace System.Text.Json.Serialization.Metadata => new JsonTypeInfoInternal( options, createObjectFunc, - () => new IEnumerableWithAddMethodConverter(dummy: false), + () => new StackOrQueueConverter(), elementInfo, numberHandling, serializeFunc, From 10f38d21ab8956dc880658122d96507103664a02 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Fri, 16 Jul 2021 18:37:57 -0700 Subject: [PATCH 637/926] Fix debugger thread context validation after recent change (#55839) Followup fix to https://github.com/dotnet/runtime/pull/55185 --- src/coreclr/debug/ee/debugger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 9f7e2860594..81965f1dee4 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -15911,7 +15911,7 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread, CONTEXT *pCtx) if (!success) { ctx.ContextFlags = CONTEXT_CONTROL; - BOOL success = pThread->GetThreadContext(&ctx); + success = pThread->GetThreadContext(&ctx); if (success) { pCtx = &ctx; From f7489b96a76bc87b5b2c46cc6be1cbf713582aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Sat, 17 Jul 2021 00:37:59 -0400 Subject: [PATCH 638/926] Don't probe mono aot-cache directory for AOT images (#55832) We don't have a global AOT cache directory on any platform Fixes https://github.com/dotnet/runtime/issues/33080 --- src/mono/mono/mini/aot-runtime.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index bad49486d99..3b753a44cb0 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -1965,19 +1965,6 @@ load_aot_module (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, gpointer g_free (err); } g_free (aot_name); -#if !defined(HOST_ANDROID) && !defined(HOST_WASM) - if (!sofile) { - char *basename = g_path_get_basename (assembly->image->name); - aot_name = g_strdup_printf ("%s/mono/aot-cache/%s/%s%s", mono_assembly_getrootdir(), MONO_ARCHITECTURE, basename, MONO_SOLIB_EXT); - g_free (basename); - sofile = mono_dl_open (aot_name, MONO_DL_LAZY, &err); - if (!sofile) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: image '%s' not found: %s", aot_name, err); - g_free (err); - } - g_free (aot_name); - } -#endif if (!sofile) { GList *l; From 5842db2c301cb1752ae854232a4fbae51d4a9ed0 Mon Sep 17 00:00:00 2001 From: Alfonso Gregory Date: Sat, 17 Jul 2021 00:39:31 -0400 Subject: [PATCH 639/926] Remove old snow leopard hack (#55825) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove old hack It’s been a long time since snow leopard and the issue should be fixed by now * Remove declaration * Remove unnecessary include --- src/mono/mono/mini/mini-darwin.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/mono/mono/mini/mini-darwin.c b/src/mono/mono/mini/mini-darwin.c index 99a389d70c3..981c9195d1d 100644 --- a/src/mono/mono/mini/mini-darwin.c +++ b/src/mono/mono/mini/mini-darwin.c @@ -63,14 +63,8 @@ #include #include #include -#include #include -/* This is #define'd by Boehm GC to _GC_dlopen. */ -#undef dlopen - -void* dlopen(const char* path, int mode); - void mono_runtime_install_handlers (void) { @@ -98,21 +92,6 @@ mono_runtime_install_handlers (void) if (kr != KERN_SUCCESS) g_warning ("mono_runtime_install_handlers: task_set_exception_ports failed"); #endif - - /* Snow Leopard has a horrible bug: http://openradar.appspot.com/7209349 - * This causes obscure SIGTRAP's for any application that comes across this built on - * Snow Leopard. This is a horrible hack to ensure that the private __CFInitialize - * is run on the main thread, so that we don't get SIGTRAPs later - */ -#if defined (__APPLE__) && (defined (__i386__) || defined (__x86_64__)) - { - void *handle = dlopen ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); - if (handle == NULL) - return; - - dlclose (handle); - } -#endif } gboolean From 57e7ae04b8bd8bf8dbd22419b3896391a1733de5 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 17 Jul 2021 15:14:36 -0400 Subject: [PATCH 640/926] Use RetryHelper to improve reliability of revocation tests (#55873) --- .../RevocationTests/DynamicRevocationTests.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs index 5fc168b03e4..7c32c3cd7dc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs @@ -1502,7 +1502,6 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests using (root) using (intermediate) using (endEntity) - using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = root.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediate.CloneIssuerCert()) { @@ -1528,14 +1527,19 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests } } - X509Chain chain = holder.Chain; - chain.ChainPolicy.CustomTrustStore.Add(rootCert); - chain.ChainPolicy.ExtraStore.Add(intermediateCert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = s_urlRetrievalLimit; + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + { + X509Chain chain = holder.Chain; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.ExtraStore.Add(intermediateCert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = s_urlRetrievalLimit; - callback(root, intermediate, endEntity, holder, responder); + callback(root, intermediate, endEntity, holder, responder); + } + }); } } From 60001847980933cc72ac6aab6c7a846a631edef1 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Sat, 17 Jul 2021 12:15:04 -0700 Subject: [PATCH 641/926] Fix System.Reflection.Metadata.MetadataUpdater.IsSupported (#55874) Issue: https://github.com/dotnet/aspnetcore/issues/34440 --- src/coreclr/vm/assemblynative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index b74038fa377..6819867cdd5 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1461,7 +1461,7 @@ BOOL QCALLTYPE AssemblyNative::IsApplyUpdateSupported() BEGIN_QCALL; #ifdef EnC_SUPPORTED - BOOL result = CORDebuggerAttached() || g_pConfig->ForceEnc() || g_pConfig->DebugAssembliesModifiable(); + result = CORDebuggerAttached() || g_pConfig->ForceEnc() || g_pConfig->DebugAssembliesModifiable(); #endif END_QCALL; From 8c48f947d00d4e525680a7e8b72f949d4b1571f2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 17 Jul 2021 15:32:54 -0700 Subject: [PATCH 642/926] Refactor runtime-specific logic out of CustomAttributeData (#55870) CustomAttributeData is meant to be abstract base class of reflection object model. Move the runtime-specific part into a separate RuntimeCustomAttributeData type. --- .../System.Private.CoreLib.csproj | 2 +- .../src/System/Reflection/RuntimeAssembly.cs | 2 +- .../Reflection/RuntimeConstructorInfo.cs | 2 +- ...ibute.cs => RuntimeCustomAttributeData.cs} | 63 +++++++------------ .../src/System/Reflection/RuntimeEventInfo.cs | 2 +- .../src/System/Reflection/RuntimeFieldInfo.cs | 2 +- .../System/Reflection/RuntimeMethodInfo.cs | 2 +- .../src/System/Reflection/RuntimeModule.cs | 2 +- .../System/Reflection/RuntimeParameterInfo.cs | 6 +- .../System/Reflection/RuntimePropertyInfo.cs | 2 +- .../System/Reflection/CustomAttributeData.cs | 26 +++++--- .../CustomAttributeNamedArgument.cs | 43 ++++++------- .../CustomAttributeTypedArgument.cs | 34 +++++----- .../src/System/RuntimeType.cs | 2 +- .../System.Private.CoreLib.csproj | 3 +- .../src/System/Reflection/CustomAttribute.cs | 6 +- .../CustomAttributeTypedArgument.Mono.cs | 16 ----- .../src/System/Reflection/FieldInfo.Mono.cs | 6 +- .../src/System/Reflection/RuntimeAssembly.cs | 2 +- ....Mono.cs => RuntimeCustomAttributeData.cs} | 18 +++--- .../src/System/Reflection/RuntimeEventInfo.cs | 2 +- .../src/System/Reflection/RuntimeFieldInfo.cs | 2 +- .../System/Reflection/RuntimeMethodInfo.cs | 8 +-- .../src/System/Reflection/RuntimeModule.cs | 2 +- .../System/Reflection/RuntimeParameterInfo.cs | 10 +-- .../System/Reflection/RuntimePropertyInfo.cs | 2 +- src/mono/mono/metadata/custom-attrs.c | 6 +- src/mono/mono/metadata/icall-def-netcore.h | 6 +- 28 files changed, 124 insertions(+), 155 deletions(-) rename src/coreclr/System.Private.CoreLib/src/System/Reflection/{CustomAttribute.cs => RuntimeCustomAttributeData.cs} (97%) delete mode 100644 src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.Mono.cs rename src/mono/System.Private.CoreLib/src/System/Reflection/{CustomAttributeData.Mono.cs => RuntimeCustomAttributeData.cs} (92%) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 1075a708b8b..bc40c0e4830 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -155,7 +155,6 @@ - @@ -194,6 +193,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index a45d81d4b23..cb7bd41495f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -322,7 +322,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } internal static RuntimeAssembly InternalLoad(string assemblyName, ref StackCrawlMark stackMark, AssemblyLoadContext? assemblyLoadContext = null) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs index 21bcf10557f..1af3d758916 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -198,7 +198,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs similarity index 97% rename from src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs rename to src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs index 96c062f4885..4bdc413dfc2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -12,7 +12,7 @@ using Internal.Runtime.CompilerServices; namespace System.Reflection { - public partial class CustomAttributeData + internal sealed class RuntimeCustomAttributeData : CustomAttributeData { #region Internal Static Members internal static IList GetCustomAttributesInternal(RuntimeType target) @@ -103,7 +103,7 @@ namespace System.Reflection customAttributes.CopyTo(pca, pseudoAttributes.Count); for (int i = 0; i < pseudoAttributes.Count; i++) { - pca[i] = new CustomAttributeData(pseudoAttributes[i]); + pca[i] = new RuntimeCustomAttributeData(pseudoAttributes[i]); } return Array.AsReadOnly(pca); @@ -181,7 +181,7 @@ namespace System.Reflection } private static CustomAttributeType InitCustomAttributeType(RuntimeType parameterType) { - CustomAttributeEncoding encodedType = CustomAttributeData.TypeToCustomAttributeEncoding(parameterType); + CustomAttributeEncoding encodedType = TypeToCustomAttributeEncoding(parameterType); CustomAttributeEncoding encodedArrayType = CustomAttributeEncoding.Undefined; CustomAttributeEncoding encodedEnumType = CustomAttributeEncoding.Undefined; string? enumName = null; @@ -189,7 +189,7 @@ namespace System.Reflection if (encodedType == CustomAttributeEncoding.Array) { parameterType = (RuntimeType)parameterType.GetElementType(); - encodedArrayType = CustomAttributeData.TypeToCustomAttributeEncoding(parameterType); + encodedArrayType = TypeToCustomAttributeEncoding(parameterType); } if (encodedType == CustomAttributeEncoding.Enum || encodedArrayType == CustomAttributeEncoding.Enum) @@ -210,7 +210,7 @@ namespace System.Reflection CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length]; for (int i = 0; i < records.Length; i++) - customAttributes[i] = new CustomAttributeData(module, records[i].tkCtor, in records[i].blob); + customAttributes[i] = new RuntimeCustomAttributeData(module, records[i].tkCtor, in records[i].blob); return Array.AsReadOnly(customAttributes); } @@ -262,17 +262,13 @@ namespace System.Reflection private IList m_namedArgs = null!; #region Constructor - protected CustomAttributeData() - { - } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", Justification = "Property setters and fields which are accessed by any attribute instantiation which is present in the code linker has analyzed." + "As such enumerating all fields and properties may return different results after trimming" + "but all those which are needed to actually have data will be there.")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "We're getting a MethodBase of a constructor that we found in the metadata. The attribute constructor won't be trimmed.")] - private CustomAttributeData(RuntimeModule scope, MetadataToken caCtorToken, in ConstArray blob) + private RuntimeCustomAttributeData(RuntimeModule scope, MetadataToken caCtorToken, in ConstArray blob) { m_scope = scope; m_ctor = (RuntimeConstructorInfo)RuntimeType.GetMethodBase(scope, caCtorToken)!; @@ -301,7 +297,7 @@ namespace System.Reflection #endregion #region Pseudo Custom Attribute Constructor - internal CustomAttributeData(Attribute attribute) + internal RuntimeCustomAttributeData(Attribute attribute) { if (attribute is DllImportAttribute dllImportAttribute) Init(dllImportAttribute); @@ -415,9 +411,9 @@ namespace System.Reflection #endregion #region Public Members - public virtual ConstructorInfo Constructor => m_ctor; + public override ConstructorInfo Constructor => m_ctor; - public virtual IList ConstructorArguments + public override IList ConstructorArguments { get { @@ -439,7 +435,7 @@ namespace System.Reflection } } - public virtual IList NamedArguments + public override IList NamedArguments { get { @@ -557,17 +553,6 @@ namespace System.Reflection } #endregion - private static object CanonicalizeValue(object value) - { - Debug.Assert(value is not null); - - if (value.GetType().IsEnum) - { - return ((Enum)value).GetValue(); - } - return value; - } - internal CustomAttributeTypedArgument(RuntimeModule scope, CustomAttributeEncodedArgument encodedArg) { CustomAttributeEncoding encodedType = encodedArg.CustomAttributeType.EncodedType; @@ -577,22 +562,22 @@ namespace System.Reflection if (encodedType == CustomAttributeEncoding.Enum) { - m_argumentType = ResolveType(scope, encodedArg.CustomAttributeType.EnumName!); - m_value = EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedArg.CustomAttributeType.EncodedEnumType); + _argumentType = ResolveType(scope, encodedArg.CustomAttributeType.EnumName!); + _value = EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedArg.CustomAttributeType.EncodedEnumType); } else if (encodedType == CustomAttributeEncoding.String) { - m_argumentType = typeof(string); - m_value = encodedArg.StringValue; + _argumentType = typeof(string); + _value = encodedArg.StringValue; } else if (encodedType == CustomAttributeEncoding.Type) { - m_argumentType = typeof(Type); + _argumentType = typeof(Type); - m_value = null; + _value = null; if (encodedArg.StringValue is not null) - m_value = ResolveType(scope, encodedArg.StringValue); + _value = ResolveType(scope, encodedArg.StringValue); } else if (encodedType == CustomAttributeEncoding.Array) { @@ -608,11 +593,11 @@ namespace System.Reflection elementType = CustomAttributeEncodingToType(encodedType); } - m_argumentType = elementType.MakeArrayType(); + _argumentType = elementType.MakeArrayType(); if (encodedArg.ArrayValue is null) { - m_value = null; + _value = null; } else { @@ -620,13 +605,13 @@ namespace System.Reflection for (int i = 0; i < arrayValue.Length; i++) arrayValue[i] = new CustomAttributeTypedArgument(scope, encodedArg.ArrayValue[i]); - m_value = Array.AsReadOnly(arrayValue); + _value = Array.AsReadOnly(arrayValue); } } else { - m_argumentType = CustomAttributeEncodingToType(encodedType); - m_value = EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedType); + _argumentType = CustomAttributeEncodingToType(encodedType); + _value = EncodedValueToRawValue(encodedArg.PrimitiveValue, encodedType); } } } @@ -1168,7 +1153,7 @@ namespace System.Reflection // The derivedAttributes list must be passed by value so that it is not modified with the discovered attributes RuntimeType.ListBuilder derivedAttributes) { - CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); + CustomAttributeRecord[] car = RuntimeCustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); if (attributeFilterType is null && car.Length == 0) { @@ -1435,7 +1420,7 @@ namespace System.Reflection { RuntimeModule decoratedModule = decoratedAttribute.GetRuntimeModule(); MetadataImport scope = decoratedModule.MetadataImport; - CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedAttribute.MetadataToken); + CustomAttributeRecord[] car = RuntimeCustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedAttribute.MetadataToken); AttributeUsageAttribute? attributeUsageAttribute = null; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index 17721025b51..75404eaaae1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -104,7 +104,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 85625ac3150..193c22d849d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -89,7 +89,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index f66ab6dedbb..6e3f0b0122a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -271,7 +271,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs index 0b31bf27759..8f65ca6a66c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs @@ -425,7 +425,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index 77b2d5e8f5a..0bbfa609ded 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -320,8 +320,8 @@ namespace System.Reflection if (raw) { CustomAttributeTypedArgument value = - CustomAttributeData.Filter( - CustomAttributeData.GetCustomAttributes(this), typeof(DateTimeConstantAttribute), 0); + RuntimeCustomAttributeData.Filter( + RuntimeCustomAttributeData.GetCustomAttributes(this), typeof(DateTimeConstantAttribute), 0); if (value.ArgumentType != null) return new DateTime((long)value.Value!); @@ -541,7 +541,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index 2eb46bf49b9..5a2440f1059 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -165,7 +165,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs index b704dac0549..69c9db7aba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Text; namespace System.Reflection { - public partial class CustomAttributeData + public class CustomAttributeData { #region Public Static Members public static IList GetCustomAttributes(MemberInfo target) @@ -42,6 +43,10 @@ namespace System.Reflection } #endregion + protected CustomAttributeData() + { + } + #region Object Override public override string ToString() { @@ -53,19 +58,21 @@ namespace System.Reflection bool first = true; - int count = ConstructorArguments.Count; - for (int i = 0; i < count; i++) + IList constructorArguments = ConstructorArguments; + int constructorArgumentsCount = constructorArguments.Count; + for (int i = 0; i < constructorArgumentsCount; i++) { if (!first) vsb.Append(", "); - vsb.Append(ConstructorArguments[i].ToString()); + vsb.Append(constructorArguments[i].ToString()); first = false; } - count = NamedArguments.Count; - for (int i = 0; i < count; i++) + IList namedArguments = NamedArguments; + int namedArgumentsCount = namedArguments.Count; + for (int i = 0; i < namedArgumentsCount; i++) { if (!first) vsb.Append(", "); - vsb.Append(NamedArguments[i].ToString()); + vsb.Append(namedArguments[i].ToString()); first = false; } @@ -79,6 +86,11 @@ namespace System.Reflection #region Public Members public virtual Type AttributeType => Constructor.DeclaringType!; + + // Expected to be overriden + public virtual ConstructorInfo Constructor => null!; + public virtual IList ConstructorArguments => null!; + public virtual IList NamedArguments => null!; #endregion } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs index dd31ae35599..9699e14e5d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs @@ -8,41 +8,34 @@ namespace System.Reflection public static bool operator ==(CustomAttributeNamedArgument left, CustomAttributeNamedArgument right) => left.Equals(right); public static bool operator !=(CustomAttributeNamedArgument left, CustomAttributeNamedArgument right) => !left.Equals(right); - private readonly MemberInfo m_memberInfo; - private readonly CustomAttributeTypedArgument m_value; + private readonly MemberInfo _memberInfo; + private readonly CustomAttributeTypedArgument _value; public CustomAttributeNamedArgument(MemberInfo memberInfo, object? value) { - if (memberInfo == null) + if (memberInfo is null) throw new ArgumentNullException(nameof(memberInfo)); - Type type; - if (memberInfo is FieldInfo field) + Type type = memberInfo switch { - type = field.FieldType; - } - else if (memberInfo is PropertyInfo property) - { - type = property.PropertyType; - } - else - { - throw new ArgumentException(SR.Argument_InvalidMemberForNamedArgument); - } + FieldInfo field => field.FieldType, + PropertyInfo property => property.PropertyType, + _ => throw new ArgumentException(SR.Argument_InvalidMemberForNamedArgument) + }; - m_memberInfo = memberInfo; - m_value = new CustomAttributeTypedArgument(type, value); + _memberInfo = memberInfo; + _value = new CustomAttributeTypedArgument(type, value); } public CustomAttributeNamedArgument(MemberInfo memberInfo, CustomAttributeTypedArgument typedArgument) { - m_memberInfo = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo)); - m_value = typedArgument; + _memberInfo = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo)); + _value = typedArgument; } public override string ToString() { - if (m_memberInfo == null) + if (_memberInfo is null) return base.ToString()!; return $"{MemberInfo.Name} = {TypedValue.ToString(ArgumentType != typeof(object))}"; @@ -59,12 +52,12 @@ namespace System.Reflection } internal Type ArgumentType => - m_memberInfo is FieldInfo ? - ((FieldInfo)m_memberInfo).FieldType : - ((PropertyInfo)m_memberInfo).PropertyType; + _memberInfo is FieldInfo fi ? + fi.FieldType : + ((PropertyInfo)_memberInfo).PropertyType; - public MemberInfo MemberInfo => m_memberInfo; - public CustomAttributeTypedArgument TypedValue => m_value; + public MemberInfo MemberInfo => _memberInfo; + public CustomAttributeTypedArgument TypedValue => _value; public string MemberName => MemberInfo.Name; public bool IsField => MemberInfo is FieldInfo; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs index 46fb5dd5c68..18d033b5c43 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs @@ -9,30 +9,27 @@ namespace System.Reflection public readonly partial struct CustomAttributeTypedArgument { public static bool operator ==(CustomAttributeTypedArgument left, CustomAttributeTypedArgument right) => left.Equals(right); - public static bool operator !=(CustomAttributeTypedArgument left, CustomAttributeTypedArgument right) => !left.Equals(right); - private readonly object? m_value; - private readonly Type m_argumentType; + private readonly object? _value; + private readonly Type _argumentType; public CustomAttributeTypedArgument(Type argumentType, object? value) { - // value can be null. - if (argumentType == null) + if (argumentType is null) throw new ArgumentNullException(nameof(argumentType)); - m_value = (value is null) ? null : CanonicalizeValue(value); - m_argumentType = argumentType; + _value = CanonicalizeValue(value); + _argumentType = argumentType; } public CustomAttributeTypedArgument(object value) { - // value cannot be null. - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); - m_value = CanonicalizeValue(value); - m_argumentType = value.GetType(); + _value = CanonicalizeValue(value); + _argumentType = value.GetType(); } @@ -40,13 +37,13 @@ namespace System.Reflection internal string ToString(bool typed) { - if (m_argumentType == null) + if (_argumentType is null) return base.ToString()!; if (ArgumentType.IsEnum) return typed ? $"{Value}" : $"({ArgumentType.FullName}){Value}"; - if (Value == null) + if (Value is null) return typed ? "null" : $"({ArgumentType.Name})null"; if (ArgumentType == typeof(string)) @@ -67,10 +64,11 @@ namespace System.Reflection result.Append("new "); result.Append(elementType.IsEnum ? elementType.FullName : elementType.Name); result.Append('['); - result.Append(array.Count.ToString()); + int count = array.Count; + result.Append(count.ToString()); result.Append(']'); - for (int i = 0; i < array.Count; i++) + for (int i = 0; i < count; i++) { if (i != 0) { @@ -90,7 +88,9 @@ namespace System.Reflection public override int GetHashCode() => base.GetHashCode(); public override bool Equals(object? obj) => obj == (object)this; - public Type ArgumentType => m_argumentType; - public object? Value => m_value; + public Type ArgumentType => _argumentType; + public object? Value => _value; + + private static object? CanonicalizeValue(object? value) => (value is Enum e) ? e.GetValue() : value; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs index 307d16a0383..1eadda787d6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs @@ -68,7 +68,7 @@ namespace System public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } // GetDefaultMembers diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 7d9be765d16..de44fd1c354 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -195,12 +195,11 @@ - - + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 52ef9454f00..24d588afba9 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -309,7 +309,7 @@ namespace System.Reflection } [MethodImplAttribute(MethodImplOptions.InternalCall)] - [DynamicDependency("#ctor(System.Reflection.ConstructorInfo,System.Reflection.Assembly,System.IntPtr,System.UInt32)", typeof(CustomAttributeData))] + [DynamicDependency("#ctor(System.Reflection.ConstructorInfo,System.Reflection.Assembly,System.IntPtr,System.UInt32)", typeof(RuntimeCustomAttributeData))] [DynamicDependency("#ctor(System.Reflection.MemberInfo,System.Object)", typeof(CustomAttributeNamedArgument))] [DynamicDependency("#ctor(System.Type,System.Object)", typeof(CustomAttributeTypedArgument))] private static extern CustomAttributeData[] GetCustomAttributesDataInternal(ICustomAttributeProvider obj); @@ -544,9 +544,9 @@ namespace System.Reflection count = 0; if ((Attributes & TypeAttributes.Serializable) != 0) - attrsData[count++] = new CustomAttributeData((typeof(SerializableAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(SerializableAttribute)).GetConstructor(Type.EmptyTypes)!); if ((Attributes & TypeAttributes.Import) != 0) - attrsData[count++] = new CustomAttributeData((typeof(ComImportAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(ComImportAttribute)).GetConstructor(Type.EmptyTypes)!); return attrsData; } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.Mono.cs deleted file mode 100644 index b8ce8994fa5..00000000000 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.Mono.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Reflection -{ - public partial struct CustomAttributeTypedArgument - { - private static object CanonicalizeValue(object value) - { - if (value.GetType().IsEnum) - return ((Enum)value).GetValue(); - - return value; - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/FieldInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/FieldInfo.Mono.cs index ea843ea4089..537b06c37d1 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/FieldInfo.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/FieldInfo.Mono.cs @@ -85,11 +85,11 @@ namespace System.Reflection count = 0; if (IsNotSerialized) - attrsData[count++] = new CustomAttributeData((typeof(NonSerializedAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(NonSerializedAttribute)).GetConstructor(Type.EmptyTypes)!); if (DeclaringType.IsExplicitLayout) { var ctorArgs = new CustomAttributeTypedArgument[] { new CustomAttributeTypedArgument(typeof(int), GetFieldOffset()) }; - attrsData[count++] = new CustomAttributeData( + attrsData[count++] = new RuntimeCustomAttributeData( (typeof(FieldOffsetAttribute)).GetConstructor(new[] { typeof(int) })!, ctorArgs, Array.Empty()); @@ -98,7 +98,7 @@ namespace System.Reflection if (marshalAs != null) { var ctorArgs = new CustomAttributeTypedArgument[] { new CustomAttributeTypedArgument(typeof(UnmanagedType), marshalAs.Value) }; - attrsData[count++] = new CustomAttributeData( + attrsData[count++] = new RuntimeCustomAttributeData( (typeof(MarshalAsAttribute)).GetConstructor(new[] { typeof(UnmanagedType) })!, ctorArgs, Array.Empty());//FIXME Get named params diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index fdcf45fac64..d5fa9e4e31d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -275,7 +275,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } public override object[] GetCustomAttributes(bool inherit) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs similarity index 92% rename from src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs rename to src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs index e3d393670cf..c635e7ef28c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttributeData.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -34,7 +34,7 @@ using System.Text; namespace System.Reflection { - public partial class CustomAttributeData + internal sealed class RuntimeCustomAttributeData : CustomAttributeData { private sealed class LazyCAttrData { @@ -48,12 +48,8 @@ namespace System.Reflection private IList namedArgs = null!; private LazyCAttrData? lazyData; - protected CustomAttributeData() - { - } - // custom-attrs.c:create_custom_attr_data () - internal CustomAttributeData(ConstructorInfo ctorInfo, Assembly assembly, IntPtr data, uint data_length) + internal RuntimeCustomAttributeData(ConstructorInfo ctorInfo, Assembly assembly, IntPtr data, uint data_length) { this.ctorInfo = ctorInfo; this.lazyData = new LazyCAttrData(); @@ -62,12 +58,12 @@ namespace System.Reflection this.lazyData.data_length = data_length; } - internal CustomAttributeData(ConstructorInfo ctorInfo) + internal RuntimeCustomAttributeData(ConstructorInfo ctorInfo) : this(ctorInfo, Array.Empty(), Array.Empty()) { } - internal CustomAttributeData(ConstructorInfo ctorInfo, IList ctorArgs, IList namedArgs) + internal RuntimeCustomAttributeData(ConstructorInfo ctorInfo, IList ctorArgs, IList namedArgs) { this.ctorInfo = ctorInfo; this.ctorArgs = ctorArgs; @@ -94,7 +90,7 @@ namespace System.Reflection } public - virtual + override ConstructorInfo Constructor { get @@ -104,7 +100,7 @@ namespace System.Reflection } public - virtual + override IList ConstructorArguments { get @@ -115,7 +111,7 @@ namespace System.Reflection } public - virtual + override IList NamedArguments { get diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index 4ba3798d241..a9844f7e181 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -201,7 +201,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } public override int MetadataToken diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 06eb9ce0395..a1561787084 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -269,7 +269,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } private void CheckGeneric() diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index b642d090363..fc5cf2509b8 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -595,7 +595,7 @@ namespace System.Reflection count = 0; if ((info.iattrs & MethodImplAttributes.PreserveSig) != 0) - attrsData[count++] = new CustomAttributeData((typeof(PreserveSigAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(PreserveSigAttribute)).GetConstructor(Type.EmptyTypes)!); if ((info.attrs & MethodAttributes.PinvokeImpl) != 0) attrsData[count++] = GetDllImportAttributeData()!; @@ -657,7 +657,7 @@ namespace System.Reflection new CustomAttributeNamedArgument (attrType.GetField ("ThrowOnUnmappableChar")!, throwOnUnmappableChar) }; - return new CustomAttributeData( + return new RuntimeCustomAttributeData( attrType.GetConstructor(new[] { typeof(string) })!, ctorArgs, namedArgs); @@ -750,7 +750,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); @@ -988,7 +988,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs index a07856f346e..692d7ff2992 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs @@ -341,7 +341,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } internal RuntimeAssembly GetRuntimeAssembly() diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index ecf4b3bf325..43cc6a62454 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -206,7 +206,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -266,15 +266,15 @@ namespace System.Reflection count = 0; if (IsIn) - attrsData[count++] = new CustomAttributeData((typeof(InAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(InAttribute)).GetConstructor(Type.EmptyTypes)!); if (IsOut) - attrsData[count++] = new CustomAttributeData((typeof(OutAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(OutAttribute)).GetConstructor(Type.EmptyTypes)!); if (IsOptional) - attrsData[count++] = new CustomAttributeData((typeof(OptionalAttribute)).GetConstructor(Type.EmptyTypes)!); + attrsData[count++] = new RuntimeCustomAttributeData((typeof(OptionalAttribute)).GetConstructor(Type.EmptyTypes)!); if (marshalAs != null) { var ctorArgs = new CustomAttributeTypedArgument[] { new CustomAttributeTypedArgument(typeof(UnmanagedType), marshalAs.Value) }; - attrsData[count++] = new CustomAttributeData( + attrsData[count++] = new RuntimeCustomAttributeData( (typeof(MarshalAsAttribute)).GetConstructor(new[] { typeof(UnmanagedType) })!, ctorArgs, Array.Empty());//FIXME Get named params diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index 32d4645a674..0da72f4569f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -480,7 +480,7 @@ namespace System.Reflection public override IList GetCustomAttributesData() { - return CustomAttributeData.GetCustomAttributesInternal(this); + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); diff --git a/src/mono/mono/metadata/custom-attrs.c b/src/mono/mono/metadata/custom-attrs.c index ac72222d236..48571da6a08 100644 --- a/src/mono/mono/metadata/custom-attrs.c +++ b/src/mono/mono/metadata/custom-attrs.c @@ -48,7 +48,7 @@ static gboolean type_is_reference (MonoType *type); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_typed_argument, "System.Reflection", "CustomAttributeTypedArgument"); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_named_argument, "System.Reflection", "CustomAttributeNamedArgument"); -static GENERATE_TRY_GET_CLASS_WITH_CACHE (customattribute_data, "System.Reflection", "CustomAttributeData"); +static GENERATE_TRY_GET_CLASS_WITH_CACHE (customattribute_data, "System.Reflection", "RuntimeCustomAttributeData"); static MonoCustomAttrInfo* mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs); @@ -1350,7 +1350,7 @@ fail: } void -ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal (MonoReflectionMethodHandle ref_method_h, MonoReflectionAssemblyHandle assembly_h, +ves_icall_System_Reflection_RuntimeCustomAttributeData_ResolveArgumentsInternal (MonoReflectionMethodHandle ref_method_h, MonoReflectionAssemblyHandle assembly_h, gpointer data, guint32 len, MonoArrayHandleOut ctor_args_h, MonoArrayHandleOut named_args_h, MonoError *error) @@ -1441,7 +1441,7 @@ try_get_cattr_data_class (MonoError* error) error_init (error); MonoClass *res = mono_class_try_get_customattribute_data_class (); if (!res) - mono_error_set_execution_engine (error, "Class System.Reflection.CustomAttributeData not found, probably removed by the linker"); + mono_error_set_execution_engine (error, "Class System.Reflection.RuntimeCustomAttributeData not found, probably removed by the linker"); return res; } diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index e1fec10c366..8e026693a10 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -207,9 +207,6 @@ HANDLES(MCATTR_1, "GetCustomAttributesDataInternal", ves_icall_MonoCustomAttrs_G HANDLES(MCATTR_2, "GetCustomAttributesInternal", ves_icall_MonoCustomAttrs_GetCustomAttributesInternal, MonoArray, 3, (MonoObject, MonoReflectionType, MonoBoolean)) HANDLES(MCATTR_3, "IsDefinedInternal", ves_icall_MonoCustomAttrs_IsDefinedInternal, MonoBoolean, 2, (MonoObject, MonoReflectionType)) -ICALL_TYPE(CATTR_DATA, "System.Reflection.CustomAttributeData", CATTR_DATA_1) -HANDLES(CATTR_DATA_1, "ResolveArgumentsInternal", ves_icall_System_Reflection_CustomAttributeData_ResolveArgumentsInternal, void, 6, (MonoReflectionMethod, MonoReflectionAssembly, gpointer, guint32, MonoArrayOut, MonoArrayOut)) - ICALL_TYPE(ASSEMB, "System.Reflection.Emit.AssemblyBuilder", ASSEMB_1) HANDLES(ASSEMB_1, "UpdateNativeCustomAttributes", ves_icall_AssemblyBuilder_UpdateNativeCustomAttributes, void, 1, (MonoReflectionAssemblyBuilder)) HANDLES(ASSEMB_2, "basic_init", ves_icall_AssemblyBuilder_basic_init, void, 1, (MonoReflectionAssemblyBuilder)) @@ -279,6 +276,9 @@ HANDLES(MCMETH_1, "GetGenericMethodDefinition_impl", ves_icall_RuntimeMethodInfo HANDLES(MCMETH_2, "InternalInvoke", ves_icall_InternalInvoke, MonoObject, 4, (MonoReflectionMethod, MonoObject, MonoArray, MonoExceptionOut)) HANDLES_REUSE_WRAPPER(MCMETH_4, "get_metadata_token", ves_icall_reflection_get_token) +ICALL_TYPE(CATTR_DATA, "System.Reflection.RuntimeCustomAttributeData", CATTR_DATA_1) +HANDLES(CATTR_DATA_1, "ResolveArgumentsInternal", ves_icall_System_Reflection_RuntimeCustomAttributeData_ResolveArgumentsInternal, void, 6, (MonoReflectionMethod, MonoReflectionAssembly, gpointer, guint32, MonoArrayOut, MonoArrayOut)) + ICALL_TYPE(MEV, "System.Reflection.RuntimeEventInfo", MEV_1) HANDLES(MEV_1, "get_event_info", ves_icall_RuntimeEventInfo_get_event_info, void, 2, (MonoReflectionMonoEvent, MonoEventInfo_ref)) HANDLES_REUSE_WRAPPER(MEV_2, "get_metadata_token", ves_icall_reflection_get_token) From 3906d0df62f1d21e788bac277f346947cbddbfbb Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Sat, 17 Jul 2021 23:42:24 -0700 Subject: [PATCH 643/926] Consider CALLFINALLY block as pred of finally block during assertion props (#55674) * Consider CALLFINALLY block as one of the pred of handler block during assertion prop * Add test case * Do not perform jump threading for first block of try-region * fix bbNum * revert dataflow changes --- src/coreclr/jit/assertionprop.cpp | 15 +++++-- src/coreclr/jit/clrjit.natvis | 11 +++++ src/coreclr/jit/redundantbranchopts.cpp | 7 +++ .../JitBlue/Runtime_55131/Runtime_55131.cs | 45 +++++++++++++++++++ .../Runtime_55131/Runtime_55131.csproj | 20 +++++++++ 5 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.csproj diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ef18f2292ec..410c57264c0 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -763,7 +763,7 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse break; case O2K_SUBRANGE: - printf("[%d..%d]", curAssertion->op2.u2.loBound, curAssertion->op2.u2.hiBound); + printf("[%u..%u]", curAssertion->op2.u2.loBound, curAssertion->op2.u2.hiBound); break; default: @@ -2338,7 +2338,7 @@ AssertionIndex Compiler::optFindComplementary(AssertionIndex assertIndex) /***************************************************************************** * * Given a lclNum, a fromType and a toType, return assertion index of the assertion that - * claims that a variable's value is always a valid subrange of the formType. + * claims that a variable's value is always a valid subrange of the fromType. * Thus we can discard or omit a cast to fromType. Returns NO_ASSERTION_INDEX * if one such assertion could not be found in "assertions." */ @@ -4843,6 +4843,15 @@ public: // It means we can propagate only assertions that are valid for the whole try region. void MergeHandler(BasicBlock* block, BasicBlock* firstTryBlock, BasicBlock* lastTryBlock) { + if (VerboseDataflow()) + { + JITDUMP("Merge : " FMT_BB " ", block->bbNum); + Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; "); + JITDUMP("firstTryBlock " FMT_BB " ", firstTryBlock->bbNum); + Compiler::optDumpAssertionIndices("in -> ", firstTryBlock->bbAssertionIn, "; "); + JITDUMP("lastTryBlock " FMT_BB " ", lastTryBlock->bbNum); + Compiler::optDumpAssertionIndices("out -> ", lastTryBlock->bbAssertionOut, "\n"); + } BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, firstTryBlock->bbAssertionIn); BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, lastTryBlock->bbAssertionOut); } @@ -4946,8 +4955,8 @@ ASSERT_TP* Compiler::optComputeAssertionGen() } else // is jump edge assertion { - valueAssertionIndex = optFindComplementary(info.GetAssertionIndex()); jumpDestAssertionIndex = info.GetAssertionIndex(); + valueAssertionIndex = optFindComplementary(jumpDestAssertionIndex); } if (valueAssertionIndex != NO_ASSERTION_INDEX) diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis index b3c18747490..8eb3d683847 100644 --- a/src/coreclr/jit/clrjit.natvis +++ b/src/coreclr/jit/clrjit.natvis @@ -30,6 +30,7 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u + [{gtOper,en}, {gtType,en}] @@ -53,6 +54,16 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u [{gtOper,en}, {gtType,en}] + + + + this->m_treeList + this->gtNext + this + + + + [{lvType,en}] [{lvType,en}-{lvReason,s}] diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index 34fe9da3503..b52193f3157 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -297,6 +297,13 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock JITDUMP("Both successors of %sdom " FMT_BB " reach " FMT_BB " -- attempting jump threading\n", isIDom ? "i" : "", domBlock->bbNum, block->bbNum); + // If the block is the first block of try-region, then skip jump threading + if (bbIsTryBeg(block)) + { + JITDUMP(FMT_BB " is first block of try-region; no threading\n", block->bbNum); + return false; + } + // Since flow is going to bypass block, make sure there // is nothing in block that can cause a side effect. // diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.cs new file mode 100644 index 00000000000..65a7ebaebc2 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +public class Runtime_55131 +{ + // When merging the assertion props for the finally block, we should consider the assertions + // out of the BBJ_CALLFINALLY block. Otherwise, we could propagate the wrong assertions inside + // the finally block. + // + // Althogh there can be several ways to reproduce this problem, an easier way is to turn off + // finally cloning for below example. + [MethodImpl(MethodImplOptions.NoInlining)] + static bool False() => false; + + static ushort s_6; + static uint[] s_15 = new uint[] { 0 }; + static bool s_19 = false; + private static int Main() + { + bool condition = False(); + int result = 100; + if (condition) + { + result -= 1; + } + + try + { + if (condition) + { + result -= 1; + } + } + finally + { + if (condition) + { + result -= 1; + } + } + return result; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.csproj new file mode 100644 index 00000000000..420168c9917 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55131/Runtime_55131.csproj @@ -0,0 +1,20 @@ + + + Exe + True + None + + + + + + + + + From 96ce6b35359b3c159ef3e685dd67cf30bb46769b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 18 Jul 2021 03:12:40 -0700 Subject: [PATCH 644/926] enable two quic tests (#55880) --- .../System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs | 1 - .../QuicStreamConnectedStreamConformanceTests.cs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 884c1894756..83846baf6cd 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -158,7 +158,6 @@ namespace System.Net.Quic.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52048")] public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() { using QuicListener listener = CreateQuicListener(maxUnidirectionalStreams: 1); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index f100657cb46..4ca6e0e0556 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -23,10 +23,6 @@ namespace System.Net.Quic.Tests protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; protected override bool UsableAfterCanceledReads => false; protected override bool BlocksOnZeroByteReads => true; - - // TODO: new additions, find out the actual reason for hanging - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] - public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); } public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests From 26f5cc54f37dcdec045d22e01fcd633dc1de9c25 Mon Sep 17 00:00:00 2001 From: naminodarie Date: Sun, 18 Jul 2021 19:31:10 +0900 Subject: [PATCH 645/926] Add VersionCheck (#55763) --- .../Collections/Generic/SortedSet.TreeSubSet.cs | 2 ++ .../Generic/SortedSet/SortedSet.Generic.cs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs index 945c5b03a43..d01ab1183a5 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.TreeSubSet.cs @@ -125,6 +125,7 @@ namespace System.Collections.Generic { get { + VersionCheck(); Node? current = root; T? result = default; @@ -155,6 +156,7 @@ namespace System.Collections.Generic { get { + VersionCheck(); Node? current = root; T? result = default; diff --git a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs index 5e94a196c74..2da60215daa 100644 --- a/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs +++ b/src/libraries/System.Collections/tests/Generic/SortedSet/SortedSet.Generic.cs @@ -85,6 +85,23 @@ namespace System.Collections.Tests } } } + + [Fact] + public void SortedSet_Generic_GetViewBetween_MinMax_Updating() + { + var set = (SortedSet)CreateSortedSet(new[] { 1 }, 1, 1); + var lowerView = set.GetViewBetween(-5, -2); + var upperView = set.GetViewBetween(2, 5); + + set.Add(-3); + set.Add(2); + set.Add(3); + + Assert.Equal(-3, lowerView.Min); + Assert.Equal(-3, lowerView.Max); + Assert.Equal(2, upperView.Min); + Assert.Equal(3, upperView.Max); + } } public class SortedSet_Generic_Tests_int_With_NullComparer : SortedSet_Generic_Tests_int From d66305978cb7cfeed0cb3fa33b4758d7d034ad1b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 18 Jul 2021 06:39:34 -0400 Subject: [PATCH 646/926] Update Roslyn to 4.0.0-3.21367.2 (#55878) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index cb014e1dc82..54cdb3dbdf2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,7 +18,7 @@ true - 4.0.0-3.21362.7 + 4.0.0-3.21367.2 true false false From 1d0ee125ee1355134128922faa28f9e6400ba6df Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 18 Jul 2021 07:01:36 -0400 Subject: [PATCH 647/926] Fix strings in ApplicationId.ToString (#55879) In rolling out usage of StringBuilder.Append's support for interpolated strings, I mistook this occurrence as a StringBuilder rather than a ValueStringBuidler. This reverts the changes to its ToString, but also switches to use EncodeToUtf16 rather than a custom loop. --- .../src/System/ApplicationId.cs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs b/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs index 11f807986d3..97c63381ef9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ApplicationId.cs @@ -40,30 +40,33 @@ namespace System { var sb = new ValueStringBuilder(stackalloc char[128]); sb.Append(Name); + if (Culture != null) { - sb.Append($", culture=\"{Culture}\""); + sb.Append(", culture=\""); + sb.Append(Culture); + sb.Append('"'); } - sb.Append($", version=\"{Version}\""); + + sb.Append(", version=\""); + sb.Append(Version.ToString()); + sb.Append('"'); + if (_publicKeyToken != null) { sb.Append(", publicKeyToken=\""); - EncodeHexString(_publicKeyToken, ref sb); + HexConverter.EncodeToUtf16(_publicKeyToken, sb.AppendSpan(2 * _publicKeyToken.Length), HexConverter.Casing.Upper); sb.Append('"'); } + if (ProcessorArchitecture != null) { - sb.Append($", processorArchitecture =\"{ProcessorArchitecture}\""); + sb.Append(", processorArchitecture =\""); + sb.Append(ProcessorArchitecture); + sb.Append('"'); } - return sb.ToString(); - } - private static void EncodeHexString(byte[] sArray, ref ValueStringBuilder stringBuilder) - { - for (int i = 0; i < sArray.Length; i++) - { - HexConverter.ToCharsBuffer(sArray[i], stringBuilder.AppendSpan(2), 0, HexConverter.Casing.Upper); - } + return sb.ToString(); } public override bool Equals([NotNullWhen(true)] object? o) From 44df0a37632f1ae59f28afc7650394f614b162b9 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 18 Jul 2021 07:03:16 -0400 Subject: [PATCH 648/926] [main] Update dependencies from dnceng/internal/dotnet-optimization dotnet/arcade dotnet/icu dotnet/roslyn-analyzers (#55812) * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210715.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21364.4 -> To Version 1.0.0-prerelease.21365.5 * Update dependencies from https://github.com/dotnet/arcade build 20210715.11 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21364.3 -> To Version 6.0.0-beta.21365.11 * Update dependencies from https://github.com/dotnet/icu build 20210716.2 Microsoft.NETCore.Runtime.ICU.Transport From Version 6.0.0-preview.7.21363.1 -> To Version 6.0.0-rc.1.21366.2 * Update dependencies from https://github.com/dotnet/arcade build 20210716.1 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21364.3 -> To Version 6.0.0-beta.21366.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20210716.2 Microsoft.CodeAnalysis.NetAnalyzers From Version 6.0.0-rc1.21362.2 -> To Version 6.0.0-rc1.21366.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 92 ++++++++++++++++++++--------------------- eng/Versions.props | 38 ++++++++--------- global.json | 8 ++-- 3 files changed, 69 insertions(+), 69 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4e22b844f1b..8df0c0c553d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 02f08d828a4c5e256a9d7289ab9b7495032bc9df + fdb93aa21dfdab465e94464c585b3de56595b81b https://github.com/dotnet/msquic @@ -14,69 +14,69 @@ - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e https://github.com/microsoft/vstest @@ -194,25 +194,25 @@ https://github.com/dotnet/xharness e5511489deb44b13418fe622775bd7035e21c5ae - + https://github.com/dotnet/arcade - 6a8491b61e0c243cbb6a7ff4966b48e6d1e075b1 + b03966cd85285e425ffe56003c0ab57e103dd14e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 36455244434016d40fdc8b51da033a4dce98101f + 1657e026853add6028f94ef56c0f9a407a6dbe4c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 36455244434016d40fdc8b51da033a4dce98101f + 1657e026853add6028f94ef56c0f9a407a6dbe4c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 36455244434016d40fdc8b51da033a4dce98101f + 1657e026853add6028f94ef56c0f9a407a6dbe4c - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 36455244434016d40fdc8b51da033a4dce98101f + 1657e026853add6028f94ef56c0f9a407a6dbe4c https://github.com/dotnet/hotreload-utils @@ -222,9 +222,9 @@ https://github.com/dotnet/runtime-assets 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da - + https://github.com/dotnet/roslyn-analyzers - 1e2ab2307eb1c68a3c7de1603ee1a1406300afa5 + 7e82240306d8a7a83ebbbc1894381393cebf9fb2 diff --git a/eng/Versions.props b/eng/Versions.props index 54cdb3dbdf2..ffa58723a13 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,21 +50,21 @@ 3.10.0 3.10.0 - 6.0.0-rc1.21362.2 + 6.0.0-rc1.21366.2 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 2.5.1-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 - 6.0.0-beta.21364.3 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 2.5.1-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 + 6.0.0-beta.21366.1 6.0.0-preview.1.102 @@ -124,10 +124,10 @@ 6.0.0-beta.21358.1 6.0.0-beta.21358.1 - 1.0.0-prerelease.21364.4 - 1.0.0-prerelease.21364.4 - 1.0.0-prerelease.21364.4 - 1.0.0-prerelease.21364.4 + 1.0.0-prerelease.21365.5 + 1.0.0-prerelease.21365.5 + 1.0.0-prerelease.21365.5 + 1.0.0-prerelease.21365.5 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -168,7 +168,7 @@ 6.0.100-preview.6.21362.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-preview.7.21363.1 + 6.0.0-rc.1.21366.2 6.0.0-preview.7.21357.1 diff --git a/global.json b/global.json index 379263436c7..230a339dfe7 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21364.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21366.1", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21364.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21364.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21364.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21366.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21366.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21366.1", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10" From 6b6310b1c96457a2950c526dd1e46ed055343de2 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Sun, 18 Jul 2021 04:05:25 -0700 Subject: [PATCH 649/926] Reduce the nullability bypasses in CryptoPool.Return (#55782) * Reduce the nullability bypasses in CryptoPool.Return This is a walk over src/libraries/.../*.cs to find any calls to CryptoPool.Return which needed a "!" because of nullability flow. A couple of the bypasses were identifying legitimate issues, and were resolved by moving the Rent to the last thing before the try. A couple were because of the use of ArraySegment which can't structurally promise the array is non-null. But those were usually preceded by a call to ZeroMemory, and there is a CryptoPool.Return(ArraySegment) which does that already, so those "!"s were removed by changing to that overload. * Apply feedback Co-authored-by: Stephen Toub Co-authored-by: Stephen Toub --- .../System/Security/Cryptography/CngPkcs8.cs | 6 ++--- .../Cryptography/KeyFormatHelper.Encrypted.cs | 25 ++++++++----------- .../Security/Cryptography/RSAAndroid.cs | 5 ++-- .../Security/Cryptography/RSAOpenSsl.cs | 5 ++-- .../Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs | 6 ++--- .../Cryptography/Pal.Unix/UnixPkcs12Reader.cs | 2 +- 6 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs index e6d6ef01972..a563e4f4f41 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs @@ -198,8 +198,7 @@ namespace System.Security.Cryptography } finally { - CryptographicOperations.ZeroMemory(decryptedSpan); - CryptoPool.Return(decrypted.Array!); + CryptoPool.Return(decrypted); } } catch (AsnContentException e) @@ -277,8 +276,7 @@ namespace System.Security.Cryptography } finally { - CryptographicOperations.ZeroMemory(decryptedSpan); - CryptoPool.Return(decrypted.Array!, clearSize: 0); + CryptoPool.Return(decrypted); } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs index b4d07d7366a..8e96369bca9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs @@ -189,20 +189,19 @@ namespace System.Security.Cryptography out string encryptionAlgorithmOid, out bool isPkcs12); - byte[]? encryptedRent = null; Span encryptedSpan = default; AsnWriter? writer = null; + Debug.Assert(cipher.BlockSize <= 128, $"Encountered unexpected block size: {cipher.BlockSize}"); + Span iv = stackalloc byte[cipher.BlockSize / 8]; + Span salt = stackalloc byte[16]; + + // We need at least one block size beyond the input data size. + byte[] encryptedRent = CryptoPool.Rent( + checked(pkcs8Writer.GetEncodedLength() + (cipher.BlockSize / 8))); + try { - Debug.Assert(cipher.BlockSize <= 128, $"Encountered unexpected block size: {cipher.BlockSize}"); - Span iv = stackalloc byte[cipher.BlockSize / 8]; - Span salt = stackalloc byte[16]; - - // We need at least one block size beyond the input data size. - encryptedRent = CryptoPool.Rent( - checked(pkcs8Writer.GetEncodedLength() + (cipher.BlockSize / 8))); - RandomNumberGenerator.Fill(salt); int written = PasswordBasedEncryption.Encrypt( @@ -242,7 +241,7 @@ namespace System.Security.Cryptography finally { CryptographicOperations.ZeroMemory(encryptedSpan); - CryptoPool.Return(encryptedRent!, clearSize: 0); + CryptoPool.Return(encryptedRent, clearSize: 0); cipher.Dispose(); } @@ -348,8 +347,7 @@ namespace System.Security.Cryptography } finally { - CryptographicOperations.ZeroMemory(decrypted); - CryptoPool.Return(decrypted.Array!, clearSize: 0); + CryptoPool.Return(decrypted); } } @@ -385,8 +383,7 @@ namespace System.Security.Cryptography } finally { - CryptographicOperations.ZeroMemory(decrypted); - CryptoPool.Return(decrypted.Array!, clearSize: 0); + CryptoPool.Return(decrypted); } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs index ae8d4971964..6f5a3c88fba 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs @@ -84,12 +84,11 @@ namespace System.Security.Cryptography SafeRsaHandle key = GetKey(); int rsaSize = Interop.AndroidCrypto.RsaSize(key); - byte[]? buf = null; Span destination = default; + byte[] buf = CryptoPool.Rent(rsaSize); try { - buf = CryptoPool.Rent(rsaSize); destination = new Span(buf, 0, rsaSize); if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten)) @@ -103,7 +102,7 @@ namespace System.Security.Cryptography finally { CryptographicOperations.ZeroMemory(destination); - CryptoPool.Return(buf!, clearSize: 0); + CryptoPool.Return(buf, clearSize: 0); } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs index d6d66c78ae1..718059a6022 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -87,12 +87,11 @@ namespace System.Security.Cryptography ValidatePadding(padding); SafeEvpPKeyHandle key = GetKey(); int rsaSize = Interop.Crypto.EvpPKeySize(key); - byte[]? buf = null; Span destination = default; + byte[] buf = CryptoPool.Rent(rsaSize); try { - buf = CryptoPool.Rent(rsaSize); destination = new Span(buf, 0, rsaSize); int bytesWritten = Decrypt(key, data, destination, padding); @@ -101,7 +100,7 @@ namespace System.Security.Cryptography finally { CryptographicOperations.ZeroMemory(destination); - CryptoPool.Return(buf!, clearSize: 0); + CryptoPool.Return(buf, clearSize: 0); } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs index 57d5c6cbc00..8d9678d2149 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs @@ -197,8 +197,7 @@ namespace System.Security.Cryptography.Pkcs } finally { - CryptographicOperations.ZeroMemory(decryptedMemory.Span); - CryptoPool.Return(decrypted.Array!, clearSize: 0); + CryptoPool.Return(decrypted); } } @@ -229,8 +228,7 @@ namespace System.Security.Cryptography.Pkcs } finally { - CryptographicOperations.ZeroMemory(decryptedMemory.Span); - CryptoPool.Return(decrypted.Array!, clearSize: 0); + CryptoPool.Return(decrypted); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs index bf6cb9ce630..b227a056677 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs @@ -786,7 +786,7 @@ namespace Internal.Cryptography.Pal } finally { - CryptoPool.Return(decrypted.Array!, clearSize: decrypted.Count); + CryptoPool.Return(decrypted); } } From fe49e55c09cefcedc7b7d0a679267ce76eaf79cd Mon Sep 17 00:00:00 2001 From: John Salem Date: Sun, 18 Jul 2021 05:45:44 -0700 Subject: [PATCH 650/926] set CLOEXEC on diagnostic server FDs (#55850) * set CLOEXEC on diagnostic server FDs * Add assert and comment * fix compiler warning * simplify asserts to appease all the different builds --- src/native/eventpipe/ds-ipc-pal-socket.c | 26 +++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/native/eventpipe/ds-ipc-pal-socket.c b/src/native/eventpipe/ds-ipc-pal-socket.c index 328becb8469..766aa320735 100644 --- a/src/native/eventpipe/ds-ipc-pal-socket.c +++ b/src/native/eventpipe/ds-ipc-pal-socket.c @@ -316,11 +316,22 @@ ipc_socket_create_uds (DiagnosticsIpc *ipc) EP_ASSERT (ipc->server_address_family == AF_UNIX); ds_ipc_socket_t new_socket = DS_IPC_INVALID_SOCKET; + int socket_type = SOCK_STREAM; +#ifdef SOCK_CLOEXEC + socket_type |= SOCK_CLOEXEC; +#endif // SOCK_CLOEXEC DS_ENTER_BLOCKING_PAL_SECTION; - new_socket = socket (ipc->server_address_family, SOCK_STREAM, 0); + new_socket = socket (ipc->server_address_family, socket_type, 0); +#ifndef SOCK_CLOEXEC + if (new_socket != DS_IPC_INVALID_SOCKET) { + if (fcntl (new_socket, F_SETFD, FD_CLOEXEC) == -1) { + EP_ASSERT (!"Failed to set CLOEXEC"); + } + } +#endif // SOCK_CLOEXEC DS_EXIT_BLOCKING_PAL_SECTION; return new_socket; -#endif +#endif // DS_IPC_PAL_AF_UNIX return DS_IPC_INVALID_SOCKET; } @@ -337,9 +348,18 @@ ipc_socket_create_tcp (DiagnosticsIpc *ipc) #endif ds_ipc_socket_t new_socket = DS_IPC_INVALID_SOCKET; + int socket_type = SOCK_STREAM; +#ifdef SOCK_CLOEXEC + socket_type |= SOCK_CLOEXEC; +#endif // SOCK_CLOEXEC DS_ENTER_BLOCKING_PAL_SECTION; - new_socket = socket (ipc->server_address_family, SOCK_STREAM, IPPROTO_TCP); + new_socket = socket (ipc->server_address_family, socket_type, IPPROTO_TCP); if (new_socket != DS_IPC_INVALID_SOCKET) { +#ifndef SOCK_CLOEXEC + if (fcntl (new_socket, F_SETFD, FD_CLOEXEC) == -1) { + EP_ASSERT (!"Failed to set CLOEXEC"); + } +#endif // SOCK_CLOEXEC int option_value = 1; setsockopt (new_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&option_value, sizeof (option_value)); if (ipc->mode == DS_IPC_CONNECTION_MODE_LISTEN) { From 7eb749c8b78609865edcad67f57084aa382632a3 Mon Sep 17 00:00:00 2001 From: Prashanth Govindarajan Date: Sun, 18 Jul 2021 06:06:23 -0700 Subject: [PATCH 651/926] Eliminate backtracking in the interpreter for patterns with .* (#51508) * First cut of look up table for speeding up Go() * More efficient .* in RegexInterpreter * sq * Get more debug info * Remove assert and add unit test * Potential unit test * temp * Fix a bug * sq * Add extra protection to the backtracking optimization * Add unit test * Revert * RegexCompiler changes * sq * Remove debug unit tests * Add a length to the AsSpan call * Address RegexCompiler comments and add unit tests --- .../Text/RegularExpressions/RegexCompiler.cs | 140 +++++++++++++++--- .../RegularExpressions/RegexInterpreter.cs | 27 ++++ .../tests/Regex.Match.Tests.cs | 61 +++++++- 3 files changed, 210 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 81f5ca065f0..25b3a18b4c8 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -64,6 +64,7 @@ namespace System.Text.RegularExpressions private static readonly MethodInfo s_spanSliceIntIntMethod = typeof(ReadOnlySpan).GetMethod("Slice", new Type[] { typeof(int), typeof(int) })!; private static readonly MethodInfo s_spanStartsWith = typeof(MemoryExtensions).GetMethod("StartsWith", new Type[] { typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)) })!.MakeGenericMethod(typeof(char)); private static readonly MethodInfo s_stringAsSpanMethod = typeof(MemoryExtensions).GetMethod("AsSpan", new Type[] { typeof(string) })!; + private static readonly MethodInfo s_spanLastIndexOfMethod = typeof(MemoryExtensions).GetMethod("LastIndexOf", new Type[] { typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)) })!.MakeGenericMethod(typeof(char)); private static readonly MethodInfo s_stringAsSpanIntIntMethod = typeof(MemoryExtensions).GetMethod("AsSpan", new Type[] { typeof(string), typeof(int), typeof(int) })!; private static readonly MethodInfo s_stringGetCharsMethod = typeof(string).GetMethod("get_Chars", new Type[] { typeof(int) })!; private static readonly MethodInfo s_stringIndexOfCharInt = typeof(string).GetMethod("IndexOf", new Type[] { typeof(char), typeof(int) })!; @@ -90,6 +91,7 @@ namespace System.Text.RegularExpressions private LocalBuilder? _runstackLocal; private LocalBuilder? _textInfoLocal; // cached to avoid extraneous TLS hits from CurrentCulture and virtual calls to TextInfo private LocalBuilder? _loopTimeoutCounterLocal; // timeout counter for setrep and setloop + private LocalBuilder? _maxBacktrackPositionLocal; protected RegexOptions _options; // options protected RegexCode? _code; // the RegexCode object @@ -891,6 +893,8 @@ namespace System.Text.RegularExpressions Mvfldloc(s_runtrackposField, _runtrackposLocal!); Mvfldloc(s_runstackField, _runstackLocal!); Mvfldloc(s_runstackposField, _runstackposLocal!); + Ldc(-1); + Stloc(_maxBacktrackPositionLocal!); _backpos = -1; @@ -1705,7 +1709,7 @@ namespace System.Text.RegularExpressions // if (!CharInClass(textSpan[i + 2], prefix[2], "...")) goto returnFalse; // ... Debug.Assert(charClassIndex == 0 || charClassIndex == 1); - for ( ; charClassIndex < _leadingCharClasses.Length; charClassIndex++) + for (; charClassIndex < _leadingCharClasses.Length; charClassIndex++) { Debug.Assert(needLoop); Ldloca(textSpanLocal); @@ -3310,6 +3314,7 @@ namespace System.Text.RegularExpressions } _runtextbegLocal = DeclareInt32(); _runtextendLocal = DeclareInt32(); + _maxBacktrackPositionLocal = DeclareInt32(); InitializeCultureForGoIfNecessary(); @@ -4258,7 +4263,61 @@ namespace System.Text.RegularExpressions //: break Backward; { string str = _strings![Operand(0)]; + Label multiCode = DefineLabel(); + if (!IsRightToLeft()) + { + // if (runtextend - runtextpos < c) + Ldloc(_runtextendLocal!); + Ldloc(_runtextposLocal!); + Sub(); + Ldc(str.Length); + BgeFar(multiCode); + // if (!caseInsensitive && _maxBacktrackPosition != -1 && runtextpos > _maxBacktrackPosition) + if (!IsCaseInsensitive()) + { + Ldloc(_maxBacktrackPositionLocal!); + Ldc(-1); + BeqFar(_backtrack); + Ldloc(_runtextposLocal!); + Ldloc(_maxBacktrackPositionLocal!); + BleFar(_backtrack); + // runtextpos = _maxBacktrackPosition; + Ldloc(_maxBacktrackPositionLocal!); + Stloc(_runtextposLocal!); + // ReadOnlySpan runtextSpan = runtext.AsSpan(_maxBacktrackPosition, runtextend - _maxBacktractPosition); + Ldloc(_runtextLocal!); + Ldloc(_maxBacktrackPositionLocal!); + Ldloc(_runtextendLocal!); + Ldloc(_maxBacktrackPositionLocal!); + Sub(); + using (RentedLocalBuilder runtextSpanLocal = RentReadOnlySpanCharLocal()) + { + Call(s_stringAsSpanIntIntMethod); + Stloc(runtextSpanLocal); + using (RentedLocalBuilder lastIndexOfLocal = RentInt32Local()) + { + // int lastIndexOf = runtextSpan.LastIndexOf(str.AsSpan()); + Ldloc(runtextSpanLocal); + Ldstr(str); + Call(s_stringAsSpanMethod); + Call(s_spanLastIndexOfMethod); + Stloc(lastIndexOfLocal); + // if (lastIndexOf > -1) + Ldloc(lastIndexOfLocal); + Ldc(-1); + BleFar(_backtrack); + // runtextpos = lastIndexOf + _maxBacktrackPosition; + Ldloc(lastIndexOfLocal); + Ldloc(_maxBacktrackPositionLocal!); + Add(); + Stloc(_runtextposLocal!); + BrFar(_backtrack); + } + } + } + } + MarkLabel(multiCode); Ldc(str.Length); Ldloc(_runtextendLocal!); Ldloc(_runtextposLocal!); @@ -4598,6 +4657,9 @@ namespace System.Text.RegularExpressions using RentedLocalBuilder lenLocal = RentInt32Local(); using RentedLocalBuilder iLocal = RentInt32Local(); + using RentedLocalBuilder tempMaxBacktrackPositionLocal = RentInt32Local(); + Ldloc(_runtextposLocal!); + Stloc(tempMaxBacktrackPositionLocal); if (!IsRightToLeft()) { @@ -4847,6 +4909,12 @@ namespace System.Text.RegularExpressions DoPush(); Track(); + // if (_operator == RegexCode.Notoneloop) maxBacktrackPosition = tempMaxBacktrackPosition + if (_regexopcode == RegexCode.Notoneloop) + { + Ldloc(tempMaxBacktrackPositionLocal); + Stloc(_maxBacktrackPositionLocal!); + } } break; } @@ -4870,28 +4938,66 @@ namespace System.Text.RegularExpressions //: if (i > 0) //: Track(i - 1, pos - 1); //: Advance(2); - PopTrack(); - Stloc(_runtextposLocal!); + Label noBacktrackPositionBranch = DefineLabel(); PopTrack(); using (RentedLocalBuilder posLocal = RentInt32Local()) { Stloc(posLocal); - Ldloc(posLocal); - Ldc(0); - BleFar(AdvanceLabel()); + PopTrack(); + using (RentedLocalBuilder iBacktrackLocal = RentInt32Local()) + { + Stloc(iBacktrackLocal); + // if (!caseInsensitive && maxBacktrackPosition != -1 && pos > maxBacktrackPosition && runtextpos < pos && _operator == (RegexCode.Notoneloop | RegexCode.Back) && !_rightToLeft) + if (!IsCaseInsensitive() && _regexopcode == (RegexCode.Notoneloop | RegexCode.Back) && !IsRightToLeft()) + { + Ldloc(_maxBacktrackPositionLocal!); + Ldc(-1); + Beq(noBacktrackPositionBranch); + Ldloc(posLocal); + Ldloc(_maxBacktrackPositionLocal!); + Ble(noBacktrackPositionBranch); + Ldloc(_runtextposLocal!); + Ldloc(posLocal); + Bge(noBacktrackPositionBranch); + /* + int difference = pos - maxBacktrackPosition; + pos = runtextpos; + i -= difference; + maxBacktrackPosition = -1; + */ + // int difference = pos - maxBacktrackPosition; + Ldloc(iBacktrackLocal); + Ldloc(posLocal); + Ldloc(_maxBacktrackPositionLocal!); + Sub(); + Sub(); + Stloc(iBacktrackLocal); + Ldloc(_runtextposLocal!); + Stloc(posLocal); + Ldc(-1); + Stloc(_maxBacktrackPositionLocal!); + } + + MarkLabel(noBacktrackPositionBranch); + Ldloc(posLocal); + Stloc(_runtextposLocal!); + Ldloc(iBacktrackLocal); + Ldc(0); + BleFar(AdvanceLabel()); + ReadyPushTrack(); + Ldloc(iBacktrackLocal); + } + Ldc(1); + Sub(); + DoPush(); ReadyPushTrack(); - Ldloc(posLocal); + Ldloc(_runtextposLocal!); + Ldc(1); + Sub(IsRightToLeft()); + DoPush(); + Trackagain(); + Advance(); } - Ldc(1); - Sub(); - DoPush(); - ReadyPushTrack(); - Ldloc(_runtextposLocal!); - Ldc(1); - Sub(IsRightToLeft()); - DoPush(); - Trackagain(); - Advance(); break; case RegexCode.Onelazy: diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs index d557ec6c3aa..3e05926733f 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs @@ -20,6 +20,7 @@ namespace System.Text.RegularExpressions private int _codepos; private bool _rightToLeft; private bool _caseInsensitive; + private int _maxBacktrackPosition = -1; public RegexInterpreter(RegexCode code, CultureInfo culture) { @@ -223,6 +224,20 @@ namespace System.Text.RegularExpressions { if (runtextend - runtextpos < c) { + // If MatchString was called after a greedy op such as a .*, we would have zipped runtextpos to the end without really examining any characters. Reset to maxBacktrackPos here as an optimization + if (!_caseInsensitive && _maxBacktrackPosition != -1 && runtextpos > _maxBacktrackPosition) + { + // If lastIndexOf is -1, we backtrack to the max extent possible. + runtextpos = _maxBacktrackPosition; + ReadOnlySpan runtextSpan = runtext.AsSpan(_maxBacktrackPosition, runtextend - _maxBacktrackPosition); + int lastIndexOf = runtextSpan.LastIndexOf(str); + if (lastIndexOf > -1) + { + // Found the next position to match. Move runtextpos here + runtextpos = _maxBacktrackPosition + lastIndexOf; + } + } + return false; } @@ -1185,6 +1200,7 @@ namespace System.Text.RegularExpressions int len = Math.Min(Operand(1), Forwardchars()); char ch = (char)Operand(0); int i; + int tempMaxBacktrackPosition = runtextpos; if (!_rightToLeft && !_caseInsensitive) { @@ -1217,6 +1233,7 @@ namespace System.Text.RegularExpressions if (len > i && _operator == RegexCode.Notoneloop) { TrackPush(len - i - 1, runtextpos - Bump()); + _maxBacktrackPosition = tempMaxBacktrackPosition; } } advance = 2; @@ -1261,6 +1278,16 @@ namespace System.Text.RegularExpressions { int i = TrackPeek(); int pos = TrackPeek(1); + if (!_caseInsensitive && _maxBacktrackPosition != -1 && pos > _maxBacktrackPosition && runtextpos < pos && _operator == (RegexCode.Notoneloop | RegexCode.Back) && !_rightToLeft) + { + // The Multi node has bumped us along already + int difference = pos - _maxBacktrackPosition; + Debug.Assert(difference > 0); + pos = runtextpos; + i -= difference; + // We shouldn't be backtracking anymore. + _maxBacktrackPosition = -1; + } runtextpos = pos; if (i > 0) { diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs index 719f280c917..723fc68e53b 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Reflection; using System.Tests; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -204,9 +205,11 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "abc", "abc", RegexOptions.None, 0, 3, true, "abc" }; yield return new object[] { "abc", "aBc", RegexOptions.None, 0, 3, false, string.Empty }; yield return new object[] { "abc", "aBc", RegexOptions.IgnoreCase, 0, 3, true, "aBc" }; + yield return new object[] { @"abc.*def", "abcghiDEF", RegexOptions.IgnoreCase, 0, 9, true, "abcghiDEF" }; // Using *, +, ?, {}: Actual - "a+\\.?b*\\.?c{2}" yield return new object[] { @"a+\.?b*\.+c{2}", "ab.cc", RegexOptions.None, 0, 5, true, "ab.cc" }; + yield return new object[] { @"[^a]+\.[^z]+", "zzzzz", RegexOptions.None, 0, 5, false, string.Empty }; // RightToLeft yield return new object[] { @"\s+\d+", "sdf 12sad", RegexOptions.RightToLeft, 0, 9, true, " 12" }; @@ -381,6 +384,62 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D2", options, 0, 2, true, "\u05D0\u05D2" }; yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D4", options, 0, 0, false, "" }; } + + // .* Perf Optimization: Case sensitive + foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Compiled }) + { + yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\nFoo", $"\nFooThis should match", options, 0, 21, true, "\nFoo" }; + yield return new object[] { @".*\nfoo", "\nfooThis should match", options, 4, 17, false, "" }; + + yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; + yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; + yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 24, 5, false, "" }; + + yield return new object[] { @".*\dfoo", "1fooThis1foo should 1foo match", options, 4, 9, true, "This1foo" }; + yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; + } + + // .* Perf Optimization: Case insensitive + foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase, RegexOptions.IgnoreCase | RegexOptions.Compiled }) + { + yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; + yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; + yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; + yield return new object[] { @".*\dfoo", "1fooThis1FOO should 1foo match", options, 4, 9, true, "This1FOO" }; + } + + // .* Perf Optimization: RTL, Case-sensitive + foreach (RegexOptions options in new[] { RegexOptions.None | RegexOptions.RightToLeft, RegexOptions.Compiled | RegexOptions.RightToLeft }) + { + yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\nFoo", $"This should match\nFoo", options, 0, 21, true, "This should match\nFoo" }; + yield return new object[] { @".*\nfoo", "This should matchfoo\n", options, 4, 13, false, "" }; + + yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; + yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; + yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 19, 0, false, "" }; + + yield return new object[] { @".*\dfoo", "1fooThis2foo should 1foo match", options, 8, 4, true, "2foo" }; + yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; + } + + // .* Perf Optimization: RTL, case insensitive + foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase | RegexOptions.RightToLeft, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.RightToLeft }) + { + yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; + yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; + yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; + yield return new object[] { @".*\dfoo", "1fooThis2FOO should 1foo match", options, 8, 4, true, "2FOO" }; + } } public static IEnumerable Match_Basic_TestData_NetCore() @@ -617,7 +676,7 @@ namespace System.Text.RegularExpressions.Tests public void Match_Timeout_Repetition_Throws(RegexOptions options) { int repetitionCount = 800_000_000; - var regex = new Regex(@"a\s{" + repetitionCount+ "}", options, TimeSpan.FromSeconds(1)); + var regex = new Regex(@"a\s{" + repetitionCount + "}", options, TimeSpan.FromSeconds(1)); string input = @"a" + new string(' ', repetitionCount) + @"b"; Assert.Throws(() => regex.Match(input)); } From e8be7f26cb1b32321ddf5d2449fb9b8faddab94f Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Sun, 18 Jul 2021 17:18:25 +0300 Subject: [PATCH 652/926] Bugfix: System.Xml.Serialization: Compiler.GetTempAssemblyName is not deterministic under .NET Core (#46499) * Bugfix: System.Xml.Serialization: Compiler.GetTempAssemblyName is not persistent under .NET Core The implementation of String.GetHashCode() was persistent by default in .NET Framework. allowing to predictably name an XmlSerializers.{HashCode}.dll containing the pre-generated serializers. In .NET Core / .NET 5, String.GetHashCode() is no longer persistent, so the value of ns.GetHashCode() will change during each run, preventing it from picking up the XmlSerializers dll correctly. * Remove trailing whitespace * Use truncated SHA512 * Fixed compilation * Dispose SHA512 instance * Fixed compilation * Update src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs Co-authored-by: Stephen Toub * Update src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs Co-authored-by: Stephen Toub * Update Compiler.cs Co-authored-by: Stephen Toub --- .../src/System.Private.Xml.csproj | 1 + .../src/System/Xml/Serialization/Compiler.cs | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj index 526cc0ea846..205d11ef42f 100644 --- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj +++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj @@ -567,6 +567,7 @@ + diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs index 20522dca69f..71e2fdd1ed1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs @@ -1,13 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Reflection; +using System.Buffers.Binary; using System.Collections; -using System.IO; using System.Diagnostics; -using System.Globalization; -using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; namespace System.Xml.Serialization { @@ -89,7 +92,14 @@ namespace System.Xml.Serialization internal static string GetTempAssemblyName(AssemblyName parent, string? ns) { - return parent.Name + ".XmlSerializers" + (ns == null || ns.Length == 0 ? "" : "." + ns.GetHashCode()); + return parent.Name + ".XmlSerializers" + (string.IsNullOrEmpty(ns) ? "" : $".{GetPersistentHashCode(ns)}"); + } + + private static uint GetPersistentHashCode(string value) + { + byte[] valueBytes = Encoding.UTF8.GetBytes(value); + byte[] hash = SHA512.HashData(valueBytes); + return BinaryPrimitives.ReadUInt32BigEndian(hash); } } } From c5edf0e09541e2c9c4441c0abdf23f712507596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Sun, 18 Jul 2021 19:30:16 +0200 Subject: [PATCH 653/926] Fix WaitForConnectionAsync when NamedPipeServerStream is disposed (#52825) * Fix WaitForConnectionAsync when NamedPipeServerStream is disposed * Align Unix implementation on broken pipe IO exception as on Windows * Add missing methods to test against ObjectDisposedException * Apply suggestions from code review Co-authored-by: Stephen Toub * Rebase and fix suggestions * Cancel Accept on dispose * Improve test * Apply suggestions from code review Co-authored-by: Stephen Toub --- .../IO/Pipes/NamedPipeServerStream.Unix.cs | 34 +++++++- .../tests/PipeStreamConformanceTests.cs | 87 ++++++++++++++----- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 8a309dc9d49..4744afa6104 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -20,6 +20,7 @@ namespace System.IO.Pipes private int _inBufferSize; private int _outBufferSize; private HandleInheritability _inheritability; + private CancellationTokenSource _internalTokenSource = new CancellationTokenSource(); private void Create(string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, @@ -77,8 +78,25 @@ namespace System.IO.Pipes Task.FromCanceled(cancellationToken) : WaitForConnectionAsyncCore(); - async Task WaitForConnectionAsyncCore() => - HandleAcceptedSocket(await _instance!.ListeningSocket.AcceptAsync(cancellationToken).ConfigureAwait(false)); + async Task WaitForConnectionAsyncCore() + { + Socket acceptedSocket; + CancellationTokenSource linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_internalTokenSource.Token, cancellationToken); + try + { + acceptedSocket = await _instance!.ListeningSocket.AcceptAsync(linkedTokenSource.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + throw new IOException(SR.IO_PipeBroken); + } + finally + { + linkedTokenSource.Dispose(); + } + + HandleAcceptedSocket(acceptedSocket); + } } private void HandleAcceptedSocket(Socket acceptedSocket) @@ -116,9 +134,19 @@ namespace System.IO.Pipes State = PipeState.Connected; } - internal override void DisposeCore(bool disposing) => + internal override void DisposeCore(bool disposing) + { Interlocked.Exchange(ref _instance, null)?.Dispose(disposing); // interlocked to avoid shared state problems from erroneous double/concurrent disposes + if (disposing) + { + if (State != PipeState.Closed) + { + _internalTokenSource.Cancel(); + } + } + } + public void Disconnect() { CheckDisconnectOperations(); diff --git a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs index 805bc2983ef..930b875fe8e 100644 --- a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs +++ b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs @@ -63,7 +63,16 @@ namespace System.IO.Pipes.Tests { protected override bool BrokenPipePropagatedImmediately => OperatingSystem.IsWindows(); // On Unix, implemented on Sockets, where it won't propagate immediate - protected abstract (NamedPipeServerStream Server, NamedPipeClientStream Client) CreateServerAndClientStreams(); + protected abstract NamedPipeServerStream CreateServerStream(string pipeName, int maxInstances = 1); + protected abstract NamedPipeClientStream CreateClientStream(string pipeName); + + protected (NamedPipeServerStream Server, NamedPipeClientStream Client) CreateServerAndClientStreams() + { + string pipeName = GetUniquePipeName(); + NamedPipeServerStream server = CreateServerStream(pipeName); + NamedPipeClientStream client = CreateClientStream(pipeName); + return (server, client); + } protected sealed override async Task CreateConnectedStreamsAsync() { @@ -88,6 +97,15 @@ namespace System.IO.Pipes.Tests return ((NamedPipeServerStream)streams.Stream2, (NamedPipeClientStream)streams.Stream1); } + protected async Task ValidateDisposedExceptionsAsync(NamedPipeServerStream server) + { + Assert.Throws(() => server.Disconnect()); + Assert.Throws(() => server.GetImpersonationUserName()); + Assert.Throws(() => server.WaitForConnection()); + await Assert.ThrowsAsync(() => server.WaitForConnectionAsync()); + await ValidateDisposedExceptionsAsync(server as Stream); + } + /// /// Yields every combination of testing options for the OneWayReadWrites test /// @@ -629,6 +647,37 @@ namespace System.IO.Pipes.Tests await Assert.ThrowsAnyAsync(() => clientWriteToken); } } + + [Fact] + public async Task TwoServerInstances_OnceDisposed_Throws() + { + string pipeName = GetUniquePipeName(); + NamedPipeServerStream server1 = CreateServerStream(pipeName, 2); + using NamedPipeServerStream server2 = CreateServerStream(pipeName, 2); + + Task wait1 = server1.WaitForConnectionAsync(); + Task wait2 = server2.WaitForConnectionAsync(); + server1.Dispose(); + await ValidateDisposedExceptionsAsync(server1); + + using NamedPipeClientStream client = CreateClientStream(pipeName); + await client.ConnectAsync(); + + await Assert.ThrowsAsync(() => wait1); + + await wait2; + + foreach ((Stream writeable, Stream readable) in GetReadWritePairs((server2, client))) + { + byte[] sent = new byte[] { 123 }; + byte[] received = new byte[] { 0 }; + + Task t = Task.Run(() => writeable.Write(sent, 0, sent.Length)); + Assert.Equal(sent.Length, readable.Read(received, 0, sent.Length)); + Assert.Equal(sent, received); + await t; + } + } } public sealed class AnonymousPipeTest_ServerIn_ClientOut : AnonymousPipeStreamConformanceTests @@ -653,34 +702,28 @@ namespace System.IO.Pipes.Tests public sealed class NamedPipeTest_ServerOut_ClientIn : NamedPipeStreamConformanceTests { - protected override (NamedPipeServerStream Server, NamedPipeClientStream Client) CreateServerAndClientStreams() - { - string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); - var server = new NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - var client = new NamedPipeClientStream(".", pipeName, PipeDirection.In, PipeOptions.Asynchronous); - return (server, client); - } + protected override NamedPipeServerStream CreateServerStream(string pipeName, int maxInstances = 1) => + new NamedPipeServerStream(pipeName, PipeDirection.Out, maxInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + + protected override NamedPipeClientStream CreateClientStream(string pipeName) => + new NamedPipeClientStream(".", pipeName, PipeDirection.In, PipeOptions.Asynchronous); } public sealed class NamedPipeTest_ServerIn_ClientOut : NamedPipeStreamConformanceTests { - protected override (NamedPipeServerStream Server, NamedPipeClientStream Client) CreateServerAndClientStreams() - { - string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); - var server = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, PipeOptions.Asynchronous); - return (server, client); - } + protected override NamedPipeServerStream CreateServerStream(string pipeName, int maxInstances = 1) => + new NamedPipeServerStream(pipeName, PipeDirection.In, maxInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + + protected override NamedPipeClientStream CreateClientStream(string pipeName) => + new NamedPipeClientStream(".", pipeName, PipeDirection.Out, PipeOptions.Asynchronous); } public sealed class NamedPipeTest_ServerInOut_ClientInOut : NamedPipeStreamConformanceTests { - protected override (NamedPipeServerStream Server, NamedPipeClientStream Client) CreateServerAndClientStreams() - { - string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); - var server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - var client = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); - return (server, client); - } + protected override NamedPipeServerStream CreateServerStream(string pipeName, int maxInstances = 1) => + new NamedPipeServerStream(pipeName, PipeDirection.InOut, maxInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + + protected override NamedPipeClientStream CreateClientStream(string pipeName) => + new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); } } From f2a55e228b83df6aa6dc215e295bf3da5ab6fc17 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 18 Jul 2021 12:04:17 -0700 Subject: [PATCH 654/926] System.Text.Json: stackalloc constants + misc PR feedback (#55350) * Follow-up feedback from #54186. * Code review. * Code review. * Dedicated constant for the max number of chars permitted to be allocated on the stack. --- .../Document/JsonDocument.TryGetProperty.cs | 6 +- .../System/Text/Json/Document/JsonDocument.cs | 4 +- .../src/System/Text/Json/JsonConstants.cs | 7 +- .../src/System/Text/Json/JsonHelpers.Date.cs | 8 +- .../System/Text/Json/JsonHelpers.Escaping.cs | 8 +- .../Reader/JsonReaderHelper.Unescaping.cs | 28 +++---- .../Text/Json/Reader/JsonReaderHelper.cs | 6 +- .../Reader/Utf8JsonReader.MultiSegment.cs | 4 +- .../System/Text/Json/Reader/Utf8JsonReader.cs | 6 +- .../Converters/Value/TimeSpanConverter.cs | 2 +- .../Utf8JsonWriter.WriteProperties.Bytes.cs | 8 +- ...Utf8JsonWriter.WriteProperties.DateTime.cs | 8 +- ...onWriter.WriteProperties.DateTimeOffset.cs | 8 +- .../Utf8JsonWriter.WriteProperties.Decimal.cs | 8 +- .../Utf8JsonWriter.WriteProperties.Double.cs | 8 +- .../Utf8JsonWriter.WriteProperties.Float.cs | 8 +- ...nWriter.WriteProperties.FormattedNumber.cs | 8 +- .../Utf8JsonWriter.WriteProperties.Guid.cs | 8 +- .../Utf8JsonWriter.WriteProperties.Literal.cs | 8 +- ...JsonWriter.WriteProperties.SignedNumber.cs | 8 +- .../Utf8JsonWriter.WriteProperties.String.cs | 76 +++++++++---------- ...onWriter.WriteProperties.UnsignedNumber.cs | 8 +- .../Utf8JsonWriter.WriteValues.Helpers.cs | 4 +- .../Utf8JsonWriter.WriteValues.String.cs | 8 +- .../System/Text/Json/Writer/Utf8JsonWriter.cs | 8 +- .../CollectionTests.Dictionary.cs | 2 +- .../JsonDateTimeTestData.cs | 5 ++ .../Serialization/PropertyNameTests.cs | 2 +- .../Serialization/ReferenceHandlerTests.cs | 4 +- .../Serialization/Value.ReadTests.cs | 14 ++++ .../Utf8JsonReaderTests.TryGet.Date.cs | 12 +-- .../Utf8JsonReaderTests.TryGet.cs | 12 +-- .../Utf8JsonReaderTests.cs | 1 + 33 files changed, 169 insertions(+), 146 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.TryGetProperty.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.TryGetProperty.cs index 8d3dc6174fc..4653acd71c4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.TryGetProperty.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.TryGetProperty.cs @@ -27,9 +27,9 @@ namespace System.Text.Json int startIndex = index + DbRow.Size; int endIndex = checked(row.NumberOfRows * DbRow.Size + index); - if (maxBytes < JsonConstants.StackallocThreshold) + if (maxBytes < JsonConstants.StackallocByteThreshold) { - Span utf8Name = stackalloc byte[JsonConstants.StackallocThreshold]; + Span utf8Name = stackalloc byte[JsonConstants.StackallocByteThreshold]; int len = JsonReaderHelper.GetUtf8FromText(propertyName, utf8Name); utf8Name = utf8Name.Slice(0, len); @@ -139,7 +139,7 @@ namespace System.Text.Json out JsonElement value) { ReadOnlySpan documentSpan = _utf8Json.Span; - Span utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocThreshold]; + Span utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold]; // Move to the row before the EndObject int index = endIndex - DbRow.Size; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index fb120a49ae7..26eabb07c11 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs @@ -298,8 +298,8 @@ namespace System.Text.Json byte[]? otherUtf8TextArray = null; int length = checked(otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); - Span otherUtf8Text = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[JsonConstants.StackallocThreshold] : + Span otherUtf8Text = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (otherUtf8TextArray = ArrayPool.Shared.Rent(length)); ReadOnlySpan utf16Text = MemoryMarshal.AsBytes(otherText); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs index a2e91cd3b08..6a9969e5d8f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs @@ -51,7 +51,8 @@ namespace System.Text.Json public const int MaxWriterDepth = 1_000; public const int RemoveFlagsBitMask = 0x7FFFFFFF; - public const int StackallocThreshold = 256; + public const int StackallocByteThreshold = 256; + public const int StackallocCharThreshold = StackallocByteThreshold / 2; // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. // For example: '+' becomes '\u0043' @@ -60,7 +61,7 @@ namespace System.Text.Json public const int MaxExpansionFactorWhileEscaping = 6; // In the worst case, a single UTF-16 character could be expanded to 3 UTF-8 bytes. - // Only surrogate pairs expand to 4 UTF-8 bytes but that is a transformation of 2 UTF-16 characters goign to 4 UTF-8 bytes (factor of 2). + // Only surrogate pairs expand to 4 UTF-8 bytes but that is a transformation of 2 UTF-16 characters going to 4 UTF-8 bytes (factor of 2). // All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes. public const int MaxExpansionFactorWhileTranscoding = 3; @@ -95,6 +96,8 @@ namespace System.Text.Json public const int MinimumDateTimeParseLength = 10; // YYYY-MM-DD public const int MaximumEscapedDateTimeOffsetParseLength = MaxExpansionFactorWhileEscaping * MaximumDateTimeOffsetParseLength; + public const int MaximumLiteralLength = 5; // Must be able to fit null, true, & false. + // Encoding Helpers public const char HighSurrogateStart = '\ud800'; public const char HighSurrogateEnd = '\udbff'; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs index 7f05dfafc51..80d4bf1a1d3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Date.cs @@ -51,8 +51,8 @@ namespace System.Text.Json int maxLength = checked(source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); - Span bytes = maxLength <= JsonConstants.StackallocThreshold - ? stackalloc byte[JsonConstants.StackallocThreshold] + Span bytes = maxLength <= JsonConstants.StackallocByteThreshold + ? stackalloc byte[JsonConstants.StackallocByteThreshold] : new byte[maxLength]; int length = JsonReaderHelper.GetUtf8FromText(source, bytes); @@ -86,8 +86,8 @@ namespace System.Text.Json int maxLength = checked(source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); - Span bytes = maxLength <= JsonConstants.StackallocThreshold - ? stackalloc byte[JsonConstants.StackallocThreshold] + Span bytes = maxLength <= JsonConstants.StackallocByteThreshold + ? stackalloc byte[JsonConstants.StackallocByteThreshold] : new byte[maxLength]; int length = JsonReaderHelper.GetUtf8FromText(source, bytes); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs index a248e32735c..d8f74ddc091 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.Escaping.cs @@ -37,8 +37,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedValue = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written); @@ -65,8 +65,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedValue = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs index 0d739225d34..b539d6d82bb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs @@ -14,8 +14,8 @@ namespace System.Text.Json { byte[]? unescapedArray = null; - Span utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocThreshold ? - stackalloc byte[utf8Source.Length] : + Span utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (unescapedArray = ArrayPool.Shared.Rent(utf8Source.Length)); Unescape(utf8Source, utf8Unescaped, idx, out int written); @@ -44,8 +44,8 @@ namespace System.Text.Json int length = utf8Source.Length; byte[]? pooledName = null; - Span utf8Unescaped = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (pooledName = ArrayPool.Shared.Rent(length)); Unescape(utf8Source, utf8Unescaped, idx, out int written); @@ -71,8 +71,8 @@ namespace System.Text.Json int length = utf8Source.Length; byte[]? pooledName = null; - Span utf8Unescaped = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (pooledName = ArrayPool.Shared.Rent(length)); Unescape(utf8Source, utf8Unescaped, idx, out int written); @@ -96,8 +96,8 @@ namespace System.Text.Json byte[]? unescapedArray = null; - Span utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocThreshold ? - stackalloc byte[utf8Source.Length] : + Span utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (unescapedArray = ArrayPool.Shared.Rent(utf8Source.Length)); Unescape(utf8Source, utf8Unescaped, 0, out int written); @@ -127,12 +127,12 @@ namespace System.Text.Json int length = checked((int)utf8Source.Length); - Span utf8Unescaped = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (unescapedArray = ArrayPool.Shared.Rent(length)); - Span utf8Escaped = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span utf8Escaped = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (escapedArray = ArrayPool.Shared.Rent(length)); utf8Source.CopyTo(utf8Escaped); @@ -174,8 +174,8 @@ namespace System.Text.Json { byte[]? pooledArray = null; - Span byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocThreshold ? - stackalloc byte[utf8Unescaped.Length] : + Span byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (pooledArray = ArrayPool.Shared.Rent(utf8Unescaped.Length)); OperationStatus status = Base64.DecodeFromUtf8(utf8Unescaped, byteSpan, out int bytesConsumed, out int bytesWritten); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs index 4ba1bff616b..b9d51eff8da 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs @@ -252,7 +252,7 @@ namespace System.Text.Json Debug.Assert(backslash != -1); Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span sourceUnescaped = stackalloc byte[source.Length]; + Span sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength]; Unescape(source, sourceUnescaped, backslash, out int written); Debug.Assert(written > 0); @@ -277,7 +277,7 @@ namespace System.Text.Json Debug.Assert(backslash != -1); Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - Span sourceUnescaped = stackalloc byte[source.Length]; + Span sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength]; Unescape(source, sourceUnescaped, backslash, out int written); Debug.Assert(written > 0); @@ -303,7 +303,7 @@ namespace System.Text.Json int idx = source.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx != -1); - Span utf8Unescaped = stackalloc byte[source.Length]; + Span utf8Unescaped = stackalloc byte[JsonConstants.MaximumEscapedGuidLength]; Unescape(source, utf8Unescaped, idx, out int written); Debug.Assert(written > 0); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs index e639610cc0b..89f269bdb87 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs @@ -540,9 +540,9 @@ namespace System.Text.Json private bool CheckLiteralMultiSegment(ReadOnlySpan span, ReadOnlySpan literal, out int consumed) { - Debug.Assert(span.Length > 0 && span[0] == literal[0]); + Debug.Assert(span.Length > 0 && span[0] == literal[0] && literal.Length <= JsonConstants.MaximumLiteralLength); - Span readSoFar = stackalloc byte[literal.Length]; + Span readSoFar = stackalloc byte[JsonConstants.MaximumLiteralLength]; int written = 0; long prevTotalConsumed = _totalConsumed; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs index 5ab5fd9ce89..efafcf92b4e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs @@ -513,7 +513,7 @@ namespace System.Text.Json int length = checked(text.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { otherUtf8TextArray = ArrayPool.Shared.Rent(length); otherUtf8Text = otherUtf8TextArray; @@ -523,8 +523,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets passed to instance methods on a ref struct. unsafe { - byte* ptr = stackalloc byte[JsonConstants.StackallocThreshold]; - otherUtf8Text = new Span(ptr, JsonConstants.StackallocThreshold); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + otherUtf8Text = new Span(ptr, JsonConstants.StackallocByteThreshold); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs index 3767b7ff6d4..ddb3252c9be 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs @@ -54,7 +54,7 @@ namespace System.Text.Json.Serialization.Converters int backslash = source.IndexOf(JsonConstants.BackSlash); Debug.Assert(backslash != -1); - Span sourceUnescaped = stackalloc byte[source.Length]; + Span sourceUnescaped = stackalloc byte[MaximumEscapedTimeSpanFormatLength]; JsonReaderHelper.Unescape(source, sourceUnescaped, backslash, out int written); Debug.Assert(written > 0); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs index 7852c005200..55d8aaa515d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs @@ -139,8 +139,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -162,8 +162,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs index fa96ffc6e8d..e41427cdcdd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs @@ -144,8 +144,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -167,8 +167,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs index 3cbfef7cf4f..313496df38c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs @@ -143,8 +143,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -166,8 +166,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs index 861ce7e0059..ab98e246fab 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs @@ -143,8 +143,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -166,8 +166,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs index 359cfd43036..1fb6832ce6a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs @@ -147,8 +147,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -170,8 +170,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs index 6e46a4f6efb..3832dd4c0ce 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs @@ -147,8 +147,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -170,8 +170,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs index e654b5270f1..44f3c2f6c17 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.FormattedNumber.cs @@ -120,8 +120,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -143,8 +143,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs index 899ff6febf3..07ffde8f492 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs @@ -143,8 +143,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -166,8 +166,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs index ed8e8107145..9ab0b654cf9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs @@ -259,8 +259,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -282,8 +282,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs index b231446cd68..05d45181da0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs @@ -213,8 +213,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -236,8 +236,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs index f838d3fa193..38c64b4a400 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs @@ -112,7 +112,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocCharThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -122,8 +122,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - char* ptr = stackalloc char[length]; - escapedPropertyName = new Span(ptr, length); + char* ptr = stackalloc char[JsonConstants.StackallocCharThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocCharThreshold); } } @@ -274,7 +274,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -284,8 +284,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - byte* ptr = stackalloc byte[length]; - escapedPropertyName = new Span(ptr, length); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocByteThreshold); } } @@ -896,8 +896,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndex); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedValue = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndex, _options.Encoder, out int written); @@ -919,8 +919,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndex); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedValue = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndex, _options.Encoder, out int written); @@ -942,8 +942,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndex); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndex, _options.Encoder, out int written); @@ -965,8 +965,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndex); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndex, _options.Encoder, out int written); @@ -1068,7 +1068,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal); Span escapedValue; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocCharThreshold) { valueArray = ArrayPool.Shared.Rent(length); escapedValue = valueArray; @@ -1078,8 +1078,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - char* ptr = stackalloc char[length]; - escapedValue = new Span(ptr, length); + char* ptr = stackalloc char[JsonConstants.StackallocCharThreshold]; + escapedValue = new Span(ptr, JsonConstants.StackallocCharThreshold); } } @@ -1092,7 +1092,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocCharThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -1102,8 +1102,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - char* ptr = stackalloc char[length]; - escapedPropertyName = new Span(ptr, length); + char* ptr = stackalloc char[JsonConstants.StackallocCharThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocCharThreshold); } } @@ -1137,7 +1137,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); Span escapedValue; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { valueArray = ArrayPool.Shared.Rent(length); escapedValue = valueArray; @@ -1147,8 +1147,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - byte* ptr = stackalloc byte[length]; - escapedValue = new Span(ptr, length); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + escapedValue = new Span(ptr, JsonConstants.StackallocByteThreshold); } } @@ -1161,7 +1161,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -1171,8 +1171,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - byte* ptr = stackalloc byte[length]; - escapedPropertyName = new Span(ptr, length); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocByteThreshold); } } @@ -1206,7 +1206,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); Span escapedValue; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { valueArray = ArrayPool.Shared.Rent(length); escapedValue = valueArray; @@ -1216,8 +1216,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - byte* ptr = stackalloc byte[length]; - escapedValue = new Span(ptr, length); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + escapedValue = new Span(ptr, JsonConstants.StackallocByteThreshold); } } @@ -1230,7 +1230,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocCharThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -1240,8 +1240,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - char* ptr = stackalloc char[length]; - escapedPropertyName = new Span(ptr, length); + char* ptr = stackalloc char[JsonConstants.StackallocCharThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocCharThreshold); } } @@ -1275,7 +1275,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal); Span escapedValue; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocCharThreshold) { valueArray = ArrayPool.Shared.Rent(length); escapedValue = valueArray; @@ -1285,8 +1285,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - char* ptr = stackalloc char[length]; - escapedValue = new Span(ptr, length); + char* ptr = stackalloc char[JsonConstants.StackallocCharThreshold]; + escapedValue = new Span(ptr, JsonConstants.StackallocCharThreshold); } } @@ -1299,7 +1299,7 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); Span escapedPropertyName; - if (length > JsonConstants.StackallocThreshold) + if (length > JsonConstants.StackallocByteThreshold) { propertyArray = ArrayPool.Shared.Rent(length); escapedPropertyName = propertyArray; @@ -1309,8 +1309,8 @@ namespace System.Text.Json // Cannot create a span directly since it gets assigned to parameter and passed down. unsafe { - byte* ptr = stackalloc byte[length]; - escapedPropertyName = new Span(ptr, length); + byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold]; + escapedPropertyName = new Span(ptr, JsonConstants.StackallocByteThreshold); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs index 5b6698143f1..b7602d4f481 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs @@ -222,8 +222,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -245,8 +245,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs index 6e617bdec88..d95817a638e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs @@ -39,8 +39,8 @@ namespace System.Text.Json { byte[]? outputText = null; - Span encodedBytes = encodingLength <= JsonConstants.StackallocThreshold ? - stackalloc byte[encodingLength] : + Span encodedBytes = encodingLength <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (outputText = ArrayPool.Shared.Rent(encodingLength)); OperationStatus status = Base64.EncodeToUtf8(bytes, encodedBytes, out int consumed, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs index 15cb0b8d1af..b4be2dfe8b3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs @@ -189,8 +189,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedValue = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndexVal, _options.Encoder, out int written); @@ -336,8 +336,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); - Span escapedValue = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedValue = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (valueArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs index 2c5cc11afbd..8c9ae27228b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs @@ -662,8 +662,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc byte[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ? + stackalloc byte[JsonConstants.StackallocByteThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); @@ -805,8 +805,8 @@ namespace System.Text.Json int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp); - Span escapedPropertyName = length <= JsonConstants.StackallocThreshold ? - stackalloc char[length] : + Span escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ? + stackalloc char[JsonConstants.StackallocCharThreshold] : (propertyArray = ArrayPool.Shared.Rent(length)); JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written); diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs index 738679e1beb..61126269362 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs @@ -954,7 +954,7 @@ namespace System.Text.Json.Serialization.Tests } { - // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 200 chars and 400 bytes. + // We want to go over StackallocByteThreshold=256 to force a pooled allocation, so this property is 200 chars and 400 bytes. const int charsInProperty = 200; string longPropertyName = new string('\u0467', charsInProperty); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs index 8d4efb5e3cb..8f4f2af3269 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDateTimeTestData.cs @@ -232,6 +232,11 @@ namespace System.Text.Json.Tests // High byte expansion - parsing fails early at 254 characters. yield return new object[] { "\"" + new string('\u20AC', 250) + "\"" }; yield return new object[] { "\"" + new string('\u20AC', 260) + "\"" }; + + // Whitespace + yield return new object[] { "\"\\t1997-07-16\"" }; + yield return new object[] { "\"1997-07-16 \"" }; + yield return new object[] { "\" 1997-07-16 \"" }; } public static IEnumerable DateTimeFractionTrimBaseTests() diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs index 33f4f5f2bb5..6a14f3ed63c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs @@ -348,7 +348,7 @@ namespace System.Text.Json.Serialization.Tests [Fact] public static void UnicodePropertyNamesWithPooledAlloc() { - // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 400 chars and 401 bytes. + // We want to go over StackallocByteThreshold=256 to force a pooled allocation, so this property is 400 chars and 401 bytes. ClassWithUnicodeProperty obj = JsonSerializer.Deserialize("{\"A\u046734567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\":1}"); Assert.Equal(1, obj.A\u046734567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.cs index 7de2c8fa1e2..030f637b9c3 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReferenceHandlerTests.cs @@ -209,7 +209,7 @@ namespace System.Text.Json.Serialization.Tests objCopy = JsonSerializer.Deserialize(json, optionsWithEncoder); Assert.Equal(1, objCopy.A\u0467); - // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 400 chars and 401 bytes. + // We want to go over StackallocByteThreshold=256 to force a pooled allocation, so this property is 400 chars and 401 bytes. obj = new ClassWithUnicodeProperty { A\u046734567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1 @@ -398,7 +398,7 @@ namespace System.Text.Json.Serialization.Tests objCopy = JsonSerializer.Deserialize>(json, optionsWithEncoder); Assert.Equal(1, objCopy["A\u0467"]); - // We want to go over StackallocThreshold=256 to force a pooled allocation, so this property is 200 chars and 400 bytes. + // We want to go over StackallocByteThreshold=256 to force a pooled allocation, so this property is 200 chars and 400 bytes. const int charsInProperty = 200; string longPropertyName = new string('\u0467', charsInProperty); obj = new Dictionary { { $"{longPropertyName}", 1 } }; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs index 75ad7d1e6ba..3bfb3c9bbd6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Value.ReadTests.cs @@ -467,6 +467,20 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(value, JsonConvert.DeserializeObject($"\"{json}\"")); } + [Fact] + public static void TimeSpan_Read_Nullable_Tests() + { + TimeSpan? value = JsonSerializer.Deserialize("null"); + Assert.False(value.HasValue); + + value = JsonSerializer.Deserialize("\"23:59:59\""); + Assert.True(value.HasValue); + Assert.Equal(TimeSpan.Parse("23:59:59"), value); + Assert.Equal(value, JsonConvert.DeserializeObject("\"23:59:59\"")); + + Assert.Throws(() => JsonSerializer.Deserialize("null")); + } + [Fact] public static void TimeSpan_Read_KnownDifferences() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs index bd4cbdb864e..e5bf98633c7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.Date.cs @@ -207,7 +207,7 @@ namespace System.Text.Json.Tests [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] public static void TryGetDateTime_HasValueSequence_False(string testString) { - static void test(string testString, bool isFinalBlock) + static void Test(string testString, bool isFinalBlock) { byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); @@ -224,15 +224,15 @@ namespace System.Text.Json.Tests JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTime()); } - test(testString, isFinalBlock: true); - test(testString, isFinalBlock: false); + Test(testString, isFinalBlock: true); + Test(testString, isFinalBlock: false); } [Theory] [MemberData(nameof(JsonDateTimeTestData.InvalidISO8601Tests), MemberType = typeof(JsonDateTimeTestData))] public static void TryGetDateTimeOffset_HasValueSequence_False(string testString) { - static void test(string testString, bool isFinalBlock) + static void Test(string testString, bool isFinalBlock) { byte[] dataUtf8 = Encoding.UTF8.GetBytes(testString); ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); @@ -249,8 +249,8 @@ namespace System.Text.Json.Tests JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetDateTimeOffset()); } - test(testString, isFinalBlock: true); - test(testString, isFinalBlock: false); + Test(testString, isFinalBlock: true); + Test(testString, isFinalBlock: false); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs index 27a53606963..44b8d2bffb0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.TryGet.cs @@ -1335,7 +1335,7 @@ namespace System.Text.Json.Tests [MemberData(nameof(JsonGuidTestData.ValidGuidTests), MemberType = typeof(JsonGuidTestData))] public static void TryGetGuid_HasValueSequence_RetrievesGuid(string testString, string expectedString) { - static void test(string testString, string expectedString, bool isFinalBlock) + static void Test(string testString, string expectedString, bool isFinalBlock) { byte[] dataUtf8 = Encoding.UTF8.GetBytes($"\"{testString}\""); ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); @@ -1354,8 +1354,8 @@ namespace System.Text.Json.Tests Assert.Equal(expected, json.GetGuid()); } - test(testString, expectedString, isFinalBlock: true); - test(testString, expectedString, isFinalBlock: false); + Test(testString, expectedString, isFinalBlock: true); + Test(testString, expectedString, isFinalBlock: false); } [Theory] @@ -1378,7 +1378,7 @@ namespace System.Text.Json.Tests [MemberData(nameof(JsonGuidTestData.InvalidGuidTests), MemberType = typeof(JsonGuidTestData))] public static void TryGetGuid_HasValueSequence_False(string testString) { - static void test(string testString, bool isFinalBlock) + static void Test(string testString, bool isFinalBlock) { byte[] dataUtf8 = Encoding.UTF8.GetBytes($"\"{testString}\""); ReadOnlySequence sequence = JsonTestHelper.GetSequence(dataUtf8, 1); @@ -1395,8 +1395,8 @@ namespace System.Text.Json.Tests JsonTestHelper.AssertThrows(json, (jsonReader) => jsonReader.GetGuid()); } - test(testString, isFinalBlock: true); - test(testString, isFinalBlock: false); + Test(testString, isFinalBlock: true); + Test(testString, isFinalBlock: false); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs index 50926d7af72..274fbdf978e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs @@ -4359,6 +4359,7 @@ namespace System.Text.Json.Tests new object[] {"nul", 0, 3}, new object[] {"tru", 0, 3}, new object[] {"fals", 0, 4}, + new object[] {"falseinvalid", 0, 5}, new object[] {"\"a\u6F22\u5B57ge\":", 0, 11}, new object[] {"{\"a\u6F22\u5B57ge\":", 0, 13}, new object[] {"{\"name\":\"A\u6F22\u5B57hso", 0, 19}, From 62fad9df3967f0b506e1bd7df678405bc42149ce Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 18 Jul 2021 18:46:19 -0500 Subject: [PATCH 655/926] Disable array support for the COM variant wrapper classes when built-in COM is disabled. (#55756) * Disable array support for the COM variant wrapper classes when built-in COM is disabled. Fixes #55600 * Fix config call. * Fix one more location of wrapper class usage. * Fix method name. * Add trimming test. * Fix AV in IsArrayOfWrappers * Fix trimming test * Apply suggestions from code review Co-authored-by: Eric Erhardt * Set Trim=true Co-authored-by: Elinor Fung Co-authored-by: Eric Erhardt --- eng/testing/linker/project.csproj.template | 4 ++ eng/testing/linker/trimmingTests.targets | 10 ++++ src/coreclr/vm/mlinfo.cpp | 2 +- src/coreclr/vm/olevariant.cpp | 56 ++++++++++++------- .../System.Runtime.TrimmingTests.proj | 3 + .../TrimmingTests/TypeBuilderComDisabled.cs | 31 ++++++++++ 6 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/TrimmingTests/TypeBuilderComDisabled.cs diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index e1ba296072c..c9eeb18f271 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -19,6 +19,10 @@ <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) + + {RuntimeHostConfigurationOptions} + + {AdditionalProjectReferences} diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index 649ad3e25bf..73cb923ec60 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -68,6 +68,15 @@ <_additionalProjectSourceFiles Include="%(TestConsoleApps.AdditionalSourceFiles)" /> + + <_switchesAsItems Include="%(TestConsoleApps.DisabledFeatureSwitches)" Value="false" /> + <_switchesAsItems Include="%(TestConsoleApps.EnabledFeatureSwitches)" Value="true" /> + + + + <_runtimeHostConfigurationOptionsString>@(_switchesAsItems->'<RuntimeHostConfigurationOption Include="%(Identity)" Value="%(Value)" Trim="true" />', '%0a ') + + + System.Runtime.InteropServices.BuiltInComInterop.IsSupported + diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/TypeBuilderComDisabled.cs b/src/libraries/System.Runtime/tests/TrimmingTests/TypeBuilderComDisabled.cs new file mode 100644 index 00000000000..bb59db5e1ed --- /dev/null +++ b/src/libraries/System.Runtime/tests/TrimmingTests/TypeBuilderComDisabled.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Reflection.Emit; + +AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("GeneratedAssembly"), AssemblyBuilderAccess.Run); +ModuleBuilder module = assembly.DefineDynamicModule("GeneratedModule"); + +string typeName = "GeneratedType"; +TypeBuilder genericType = module.DefineType(typeName); +genericType.DefineField("_int", typeof(int), FieldAttributes.Private); +genericType.DefineProperty("Prop", PropertyAttributes.None, typeof(string), null); + +Type generatedType = genericType.CreateType(); +if (generatedType.Name != typeName) +{ + Console.WriteLine($"Unexpected name for generated type. Expected: {typeName}, Actual: {generatedType.Name}"); + return -1; +} + +object obj = Activator.CreateInstance(generatedType); +string objAsString = obj.ToString(); +if (objAsString != typeName) +{ + Console.WriteLine($"Unexpected result of ToString() for instance of generated type. Expected: {typeName}, Actual: {objAsString}"); + return -2; +} + +return 100; From a587fa4b8538bacb45287d1d479bef53bf3140f1 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 19 Jul 2021 05:32:33 +0200 Subject: [PATCH 656/926] [wasm] Run browser tests on helix/windows (#52699) * [wasm] Run browser tests on helix/windows * Build just wasm/browsertests on helix/windows * Use $(ChromiumRevision) in windows links * Fix conditions * Set PATH differently * Use backslash in PATH on windows * Try different version of chromium * Pass scenario and browser host to build And set browser path on windows to be able to start chrome from xharness * Try to get more info from the helix workitems * Fix dir separator, add broser path * Create WasmBuildSupportDir * Revert "Try to get more info from the helix workitems" This reverts commit 8807434a33a09a27f2c21cb321b743f155955cb2. * Put the dir cmds back, fix mkdir call * More debug info * Bump xharness * Bump xharness again With darc this time * StressLogAnalyzer didn't print the number of messages correctly if it exceeded the int range (2 billion). (#54832) Fix is to just use 64 bit ints instead. * Found a race condition where the LOH flag on a segment is set too late. This gives another thread the chance to allocate in a fresh LOH region that doesn't have the LOH flag set just yet and trip over an assert in Object::ValidateInner. (#54839) The fix is simply to set the flag in get_new_region before the region is put on the list for the LOH generation. * Try to show the chrome logs * Use different path for chrome logs * Use newer image with font for chrome The chrome was crashing, because it didn't find any sans-serif font. * Increase timeouts * Disable tests which timeout * Remove debug calls * Put back normal scenario * Do not set scenario in build args * Add browser sample exclusion * Restore the platform matrix * Remove the wasm build test changes That will be handled in https://github.com/dotnet/runtime/pull/54451 * Remove duplicate exclusion * Suggested property name change * Fix last merge * Simplify condition We don't pass Scenario anymore * Include chrome and chromedriver in the payload Co-authored-by: Peter Sollich --- .../libraries/helix-queues-setup.yml | 2 +- eng/pipelines/runtime-staging.yml | 5 ++-- eng/testing/tests.wasm.targets | 1 + src/libraries/sendtohelixhelp.proj | 29 ++++++++++++++----- src/libraries/tests.proj | 12 ++++++++ 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 75d6cbd2122..f5bb09d41b4 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -189,6 +189,6 @@ jobs: # WebAssembly windows - ${{ if eq(parameters.platform, 'Browser_wasm_win') }}: - - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210531091615-f5c7a43 + - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-webassembly-amd64-20210702131541-6837048 ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index f58caca2899..72ddbd6be47 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -354,7 +354,7 @@ jobs: timeoutInMinutes: 90 # -# Build Browser_wasm, on windows +# Build Browser_wasm, on windows, run console and browser tests # - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -374,7 +374,7 @@ jobs: testGroup: innerloop nameSuffix: Browser_wasm_Windows buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows - timeoutInMinutes: 120 + timeoutInMinutes: 180 condition: >- or( eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), @@ -389,6 +389,7 @@ jobs: extraHelixArguments: /p:BrowserHost=windows scenarios: - normal + - wasmtestonbrowser condition: >- or( eq(variables['librariesContainsChange'], true), diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index 16edf35847b..8618fdb73e3 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -22,6 +22,7 @@ <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT% <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js + <_XHarnessArgs Condition="'$(BrowserHost)' == 'windows'">$(_XHarnessArgs) --browser=chrome --browser-path=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index af2885bbecd..08bdf4fdb07 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -34,6 +34,7 @@ <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == ''">00:30:00 + <_workItemTimeout Condition="'$(Scenario)' == 'wasmtestonbrowser' and '$(BrowserHost)' == 'windows'">00:45:00 @@ -110,6 +111,10 @@ + + + + @@ -308,11 +313,6 @@ - - - 768968 - https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chrome-linux.zip - https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin', 'NetCoreServer', '$(NetCoreAppCurrent)-$(Configuration)')) @@ -320,6 +320,18 @@ Workloads- EMSDK- + + + + 768968 + https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chrome-linux.zip + https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/$(ChromiumRevision)/chromedriver_linux64.zip + + + 768983 + https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chrome-win.zip + https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/$(ChromiumRevision)/chromedriver_win32.zip + <_MonoAotCrossCompilerPath>$([MSBuild]::NormalizePath($(MonoAotCrossDir), 'mono-aot-cross')) @@ -369,8 +381,8 @@ - - + + <_WorkItem Include="$(WorkItemArchiveWildCard)" Exclude="$(HelixCorrelationPayload)" /> <_WorkItem Include="$(TestArchiveRoot)runonly/**/WebAssembly.Console.*.Test.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'" /> @@ -393,6 +405,7 @@ dotnet exec %XHARNESS_CLI_PATH% %HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output %XHARNESS_COMMAND% + --browser-path=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe @@ -411,7 +424,7 @@ %(Identity) - $(ExecXHarnessCmd) wasm $(XHarnessCommand) --app=. --browser=Chrome --html-file=index.html --output-directory=$(XHarnessOutput) -- %(FileName).dll --testing + $(ExecXHarnessCmd) wasm $(XHarnessCommand) --app=. --browser=Chrome $(XHarnessBrowserPathArg) --html-file=index.html --output-directory=$(XHarnessOutput) -- %(FileName).dll --testing diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index fbc717a1488..7fa4235510f 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -321,6 +321,18 @@ + + + + + + + + + + + + From d574b032793ae752387d32b97ff9840de17420a2 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 19 Jul 2021 01:02:01 -0400 Subject: [PATCH 657/926] [wasm] Add support for using custom native libraries (#55797) --- src/mono/wasm/build/WasmApp.Native.targets | 79 +++++++------- src/mono/wasm/build/WasmApp.props | 1 + src/mono/wasm/build/WasmApp.targets | 10 +- .../WasmAppBuilder/PInvokeTableGenerator.cs | 18 +++- .../Wasm.Build.Tests/BuildTestBase.cs | 5 +- .../Wasm.Build.Tests/NativeBuildTests.cs | 3 +- .../Wasm.Build.Tests/NativeLibraryTests.cs | 96 ++++++++++++++++++ .../data/Workloads.Directory.Build.targets | 1 - .../testassets/AppUsingNativeLib/Program.cs | 21 ++++ .../AppUsingNativeLib/native-lib.cpp | 11 ++ .../testassets/AppUsingNativeLib/native-lib.h | 17 ++++ .../testassets/AppUsingSkiaSharp/Program.cs | 17 ++++ src/tests/BuildWasmApps/testassets/mono.png | Bin 0 -> 26462 bytes .../testassets/native-libs/native-lib.o | Bin 0 -> 542 bytes 14 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs create mode 100644 src/tests/BuildWasmApps/testassets/mono.png create mode 100644 src/tests/BuildWasmApps/testassets/native-libs/native-lib.o diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 306cc52e987..2d89733447d 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -18,6 +18,7 @@ + _InitializeCommonProperties; _PrepareForWasmBuildNativeOnly; _WasmBuildNativeCore; @@ -38,13 +39,14 @@ + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> - <_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) @@ -115,8 +117,9 @@ true - false - true + true + false + true false @@ -153,6 +156,8 @@ $(_EmccOptimizationFlagDefault) -O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) + + <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp @@ -161,15 +166,41 @@ <_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" /> <_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" /> <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> + + <_EmccIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" /> + <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" /> + <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" /> + + + <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> + <_EmccCFlags Include="@(_EmccCommonFlags)" /> + + <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> + <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> + <_EmccCFlags Include="-DCORE_BINDINGS" /> + <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + + <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> + <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> + + <_EmccCFlags Include="$(EmccExtraCFlags)" /> + + <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)*.c" /> + <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> + <_WasmNativeFileForLinking Include="@(NativeFileReference)" /> - - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> - + + <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" Condition="'%(_WasmNativeFileForLinking.ScanForPInvokes)' != 'false'" /> + <_WasmPInvokeModules Include="libSystem.Native" /> <_WasmPInvokeModules Include="libSystem.IO.Compression.Native" /> <_WasmPInvokeModules Include="libSystem.Globalization.Native" /> @@ -194,38 +225,13 @@ - <_EmccIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" /> - <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" /> - <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" /> - - - <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> - <_EmccCFlags Include="@(_EmccCommonFlags)" /> - - <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> - <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> - <_EmccCFlags Include="-DCORE_BINDINGS" /> - <_EmccCFlags Include="-DGEN_PINVOKE=1" /> - <_EmccCFlags Include="-emit-llvm" /> - - <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> - <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> - <_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'"%(Identity)"', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" /> - - <_EmccCFlags Include="$(EmccExtraCFlags)" /> - - <_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" /> - <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" /> - <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + <_WasmSourceFileToCompile Remove="@(_WasmSourceFileToCompile)" /> <_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" /> <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py - <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp @@ -235,6 +241,10 @@ + + + + @@ -271,6 +281,8 @@ Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" /> + <_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" /> + <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> @@ -483,6 +495,5 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ - - + diff --git a/src/mono/wasm/build/WasmApp.props b/src/mono/wasm/build/WasmApp.props index 10b939d6da7..bc45a6d54d7 100644 --- a/src/mono/wasm/build/WasmApp.props +++ b/src/mono/wasm/build/WasmApp.props @@ -7,6 +7,7 @@ Publish + _InitializeCommonProperties; _BeforeWasmBuildApp; _WasmResolveReferences; _WasmAotCompileApp; diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 10bd9ac7973..534caf9025b 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -80,6 +80,9 @@ false + + + <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm')) <_BeforeWasmBuildAppDependsOn /> @@ -90,7 +93,7 @@ - + @@ -100,11 +103,12 @@ $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + + @@ -115,8 +119,6 @@ $(TargetFileName) $([MSBuild]::NormalizeDirectory($(WasmAppDir))) - - <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm')) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index bdd84c295ee..7ae4ca9920a 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -22,6 +22,8 @@ public class PInvokeTableGenerator : Task [Required] public string? OutputPath { get; set; } + private static char[] s_charsToReplace = new[] { '.', '-', }; + public override bool Execute() { Log.LogMessage(MessageImportance.Normal, $"Generating pinvoke table to '{OutputPath}'."); @@ -101,7 +103,7 @@ public class PInvokeTableGenerator : Task foreach (var module in modules.Keys) { - string symbol = module.Replace(".", "_") + "_imports"; + string symbol = ModuleNameToId(module) + "_imports"; w.WriteLine("static PinvokeImport " + symbol + " [] = {"); var assemblies_pinvokes = pinvokes. @@ -120,7 +122,7 @@ public class PInvokeTableGenerator : Task w.Write("static void *pinvoke_tables[] = { "); foreach (var module in modules.Keys) { - string symbol = module.Replace(".", "_") + "_imports"; + string symbol = ModuleNameToId(module) + "_imports"; w.Write(symbol + ","); } w.WriteLine("};"); @@ -130,6 +132,18 @@ public class PInvokeTableGenerator : Task w.Write("\"" + module + "\"" + ","); } w.WriteLine("};"); + + static string ModuleNameToId(string name) + { + if (name.IndexOfAny(s_charsToReplace) < 0) + return name; + + string fixedName = name; + foreach (char c in s_charsToReplace) + fixedName = fixedName.Replace(c, '_'); + + return fixedName; + } } private string MapType (Type t) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 7d211ae6189..608aee17817 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -69,6 +69,7 @@ namespace Wasm.Build.Tests public BuildTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) { + Console.WriteLine($"{Environment.NewLine}-------- New test --------{Environment.NewLine}"); _buildContext = buildContext; _testOutput = output; _logPath = s_buildEnv.LogRootPath; // FIXME: @@ -216,7 +217,8 @@ namespace Wasm.Build.Tests [MemberNotNull(nameof(_projectDir), nameof(_logPath))] protected void InitPaths(string id) { - _projectDir = Path.Combine(AppContext.BaseDirectory, id); + if (_projectDir == null) + _projectDir = Path.Combine(AppContext.BaseDirectory, id); _logPath = Path.Combine(s_buildEnv.LogRootPath, id); Directory.CreateDirectory(_logPath); @@ -539,7 +541,6 @@ namespace Wasm.Build.Tests var lastLines = outputBuilder.ToString().Split('\r', '\n').TakeLast(20); throw new XunitException($"Process timed out, output: {string.Join(Environment.NewLine, lastLines)}"); } - } lock (syncObj) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 30e487f8171..989b90676ec 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -31,9 +31,8 @@ namespace Wasm.Build.Tests { string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; - buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents }; + buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); - Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), projectContents), diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs new file mode 100644 index 00000000000..e06c099b19c --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class NativeLibraryTests : BuildTestBase + { + public NativeLibraryTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [Theory] + [BuildAndRun(aot: false)] + [BuildAndRun(aot: true)] + public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string id) + { + string projectName = $"AppUsingNativeLib-a"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, extraItems: ""); + + if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? _)) + { + InitPaths(id); + if (Directory.Exists(_projectDir)) + Directory.Delete(_projectDir, recursive: true); + + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "AppUsingNativeLib"), _projectDir); + File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "native-lib.o"), Path.Combine(_projectDir, "native-lib.o")); + } + + BuildProject(buildArgs, + dotnetWasmFromRuntimePack: false, + id: id); + + string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, + test: output => {}, + host: host, id: id); + + Assert.Contains("print_line: 100", output); + Assert.Contains("from pinvoke: 142", output); + } + + [Theory] + [BuildAndRun(aot: false)] + [BuildAndRun(aot: true)] + public void ProjectUsingSkiaSharp(BuildArgs buildArgs, RunHost host, string id) + { + string projectName = $"AppUsingSkiaSharp"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + extraItems: @$" + + + + + + "); + + string programText = @" +using System; +using SkiaSharp; + +public class Test +{ + public static int Main() + { + using SKFileStream skfs = new SKFileStream(""mono.png""); + using SKImage img = SKImage.FromEncodedData(skfs); + + Console.WriteLine ($""Size: {skfs.Length} Height: {img.Height}, Width: {img.Width}""); + return 0; + } +}"; + + BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + dotnetWasmFromRuntimePack: false, + id: id); + + string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, + test: output => {}, + host: host, id: id, + args: "mono.png"); + + Assert.Contains("Size: 26462 Height: 599, Width: 499", output); + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets index 19f795a01f2..62e463bb199 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets @@ -2,7 +2,6 @@ PrepareForWasmBuild;$(WasmBuildAppDependsOn) <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ - Microsoft.NETCore.App diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs new file mode 100644 index 00000000000..5134392c9d8 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System; +using System.Threading.Tasks; + +namespace SimpleConsole +{ + public class Test + { + public static int Main(string[] args) + { + Console.WriteLine ($"from pinvoke: {SimpleConsole.Test.print_line(100)}"); + return 0; + } + + [DllImport("native-lib")] + public static extern int print_line(int x); + } +} diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp new file mode 100644 index 00000000000..329a593279f --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "native-lib.h" +#include + +int print_line(int x) +{ + printf("print_line: %d\n", x); + return 42 + x; +} diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h new file mode 100644 index 00000000000..826637b3a2d --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _NATIVELIB_H_ +#define _NATIVELIB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int print_line(int x); + +#ifdef __cplusplus +} +#endif + +#endif // _NATIVELIB_H_ diff --git a/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs new file mode 100644 index 00000000000..0aeedffaf6d --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using SkiaSharp; + +public class Test +{ + public static int Main() + { + using SKFileStream skfs = new SKFileStream("mono.png"); + using SKImage img = SKImage.FromEncodedData(skfs); + + Console.WriteLine ($"Size: {skfs.Length} Height: {img.Height}, Width: {img.Width}"); + return 0; + } +} diff --git a/src/tests/BuildWasmApps/testassets/mono.png b/src/tests/BuildWasmApps/testassets/mono.png new file mode 100644 index 0000000000000000000000000000000000000000..7469aec9d1fcfa933c0e6c9257d3ff0375744214 GIT binary patch literal 26462 zcmb@uc{r3`_&@%jD1}OqU0Fk9XAqHP?7QsBzK20}EvRH0%a~-#o>2B(S}=@#&sN#S z9%CEp_e}54=XYJdKfl-Ytv}*<&U4Ov?sKoN`#!zW)mEjVWTb>3hzhQDUmt=<@DN05 za*iB4iE?QY1pkmf(onq*of7|MH5DX65IY3Ff6wsAyVa?ncb4`c8=D)RYiJA$^6`01 zhh4Knij`KMH~kEC1zBy2>Eudo4|~6G(b3TH)Yl1mJ#6Nw8eL+fTl~xN>ODQ% zv*xX85E-e@c!lVh%Qwz*-oRk3TJY_fcfL$5Xs`viRnM2yf;VF=W$p9zEP_i zcHNV2*sdR^JOe2(jX8Lrakqp5cl>dKC; z`di+5%?v^C=D^V-mdv3W^`c2+biUTsGXtg1j-zsE&EHW#;eLLdNL%{D6nFC$dRRv@ z_5Re4NySkLu50G+&O+hUjtgO?f+?Bwu=eKAc~QJN@+jXTlj<@#6#k}AXl!RJ(KGDV zQJ>kv|1prxgMTgh%i2SFljJ=Wu+ zHL{4;YRC~MEmXtw71%`xVi4t7TRu4MyKxi#^J-k=u2hYN{x7h`D@AUIkARu z&xng8kV2$U=kfgT4YK=4%~I6AC;6s<4jqH4^UYgjuWtE4kONjz=v=^wn&py@5>TTo zGFjJ{5kv$mz;2x^TbmkEh)OjV8tux~W<`&G&;`V18QiC3{iwr%LjL#Li`7B3Qd^~T zW+*)Np&9e?P(rN|BLr#13sU+ns0}p!H>&nMA-hMe&KVFdW!8Ei20Joub;;*eehZ zmq(7ftb*W`&0BwPL+kI<=+G$nHcj>w!mbiHR0dh@gnhp_RoAe1h+3 zG-I&qdqe9!;_LjW?x7$1pV+v$t^V%2vByUjtr0kh%3rp+;!Du?xBfFQ&=K;A-^}Ih zQPo_#hTpSX`?p6xL#bbis2Z;(tWqb=+#YNyI+Rntu$LdPJ#AJ}Esm&NDjZ!g=X6K+ zxRR5T2e#F?%d|GsYpdlY3RKZvC2S&7Fuw0Kcr=z#l@?d!<7JYhfA-rVJ$%OFIP&CE zwr6eQqlLK1nQe4Zo zbyC-`kXe+7s!XlH*3K4A#b7qSHSo9q$*A+B4d;|<$50u7Ur0x;zdi8M{qe{PUSlY6 zt+S}nEg_IAU7)6Xj-Ttci?S7@8YiL(HBaUGmf{Y?25MAPRMx4}dE@f)4@Y&5pF9&c z8#EFc4Jzp`?8v!Rj)<5v?x@t)HywCqymy;pOb1Cicu5O9(;=YIV@BZ%})Xe4wZi&b!mnI`)sgQIXB%RFNyP~M$ zpw^?ry%8137u3&37@3(TZ!hr}$Y-#}&L%eG8cez+=LLSy)JZX69WEhA<fBs+rRy^%j%o+XRrA9# z9=Eu;bY4C8!K|cbQ!Xg98=G-Uxz~%}BThJ_?DG~p{QDQz5pknO3oSXCB34fS=FOW) z2_}k6GX=u~`&L!1#48wDxN`T_xgT@ZK5=(Zbd?X62J-yp15_m{%Z!eXo2XU-W9NFc zw0b3$5W<0~7jzd`T5HnAHOO77PARWu=?bDys?G;5ZUyV`gb5CMi~HZ}XNAiPJ3nu5 zSF>cI^r5VwqNct(@ZASeEh#JWoNioScg%GTjLa2B%9#u_e)-(@lo!7`!9nPXQc-OI zcDVK8#f#gkKdXyz2|V0Fn50zMO-w|Jun zyZFX(b7ycZofT}-Ep=31KA(PJTovt@)^K$6yT4y2irQNY$%&p=vr-R}{t$MA55%Y= zW77!z$Mi@EwV%HRB0EBqteb-OU?piz+JQC;fk8owy_uqm0WSL&Cb#wBI<$N5+Ar!Z zpdG?Q#9&()eYYGYzI;*W-Oq{J)haA@+pA2lwyZ7?%@q7hBTK zt8Uio>;4$4>Qo?L9XfeGdohBQ5Iql5P25YQppF*lBo-SE^Gs$fIVxRP;gJl$c(gzG=ofJh8*jn=t3N zv%CMN@$T_*kF|_t{WlAyt4kwlKR?;_X2|5P&r@ORU6?ZkYpOf~W4JvpM8C_yR?KHd zE-h7=mC(V|tUg*b)gSg#u&sKg$~8-QZWuOm7i{T_%stDCgzssW=8i@d+SCMIb-&KY zcn~l)LjtoDhi4?$TRW9k{ju7p1DlN0Q9JNd%E>vLnPGT(E&2!7l>WFOZ^719b2MJl zY_+_>uGy-{9WkEnoODuXT)ak^&Xk^KR>C?P#MolL;EBhi{a{z+#bZCUurjg`b{{DDKcCEml6Ya^BMCoi6{k5H<55E#=n!(kAH z)5OVkPZl>A79(HbFxk-NF~)3@XnBNO6r-{amCC1~9&wquz(S_58xs>F zCt+A@xtcHOyyc**V4%AYWiz0|G>{q5Ly}c-anyxPa&R138*}IFGZFdPp?S|ozM|pd zcODd53JsboEh;!>VQD&NWW-DQf%QG92fl7>ctS$PLWx(Mb)5>gurCgdJ8Sf^}i4Pjw(h$@odj?a!$Di zZ)f|kH}K+l{=QN>*#&FN#daOXecxG3z0nzL?Qt5ofx}s5+d>_g+5P=&sjJaocp5L> zI?(OwQF^u#)KOOXFpNWGF_ok<_;uG^5n%JfaLmw_>l3$qYPj`zK#&qzCs05<%pZ3N zidWg)%NAu1;`MCxv#shabDc z_16?+8fuIu*q11>eX@?{Yp|9x7gaG~&dZ~|-DRr1;78n`C++$1IATQ+Xieq;2 zl)mkgnfmf55-{-BV+mka4N7|>d!Fv542smqxKuIDERlAciAEUVF8&*9%?2ZZn2TD? z)`E~OV3m;h3hdMv_hp5Y+PTaNETn7vDEsx-W6>-M`KgCIfV9I-M>Kt~0U-7c%k!Os zbfgDOI@#JSl>M}*@TLv%u_d?T4Q6Uy@FZ2#G3eiuvrD2#Sp;=urbSA?jLl3>9|L4l z|1S<$Z6|JRt{h8oNZz322!*2`<9nle!gmw9MB?5%c@RIRJ-b)-;=e~5>J70p=_HWD zqh*gcCLl>c7<;sNc0U_n1NL2*FoLua6`WFMNJ$*iIr!B?$?@NihKa#{lQT|>9dW={ z?OwIdmt(bHatLxriZ9<)QB~ov!yes0SRzJIKx*z7WaKAFv!c30P#=qBURyE3&O+Fs zM&;;cp{4#l={{C(vjwDuV^k~ziB@^dw#j*z(ZeWyDus4gz{)HH*_dEwAm#{HB?^V0 z4j*){(c5M|#5pLns8sCO0dSrsK+p%ha%KLg7gF$>0z#!x&yS#ff!e!iZWKG~qZ9@B z;JsAe$8gDNy34|0*D+fZ`l6|vptq2PoexwgYC)*rt^>w2w6(VgEq(5jzC>p)cWd8oHwfZYi**)5&By9kxz8nVgQ z1%S?IXbi5)Z2*mi@{2ty5z%8m#YcJZA+<+)#a>6NebSH(Y{Yr$1^R?YsuEiMsc#+P zUmDj32e0&(WPm;~cOIEMn0co-+VVo11RB))<%74#!P2Z64NHX@0JVFZn=*>rL9zmE ze5kuX@w+_i16{NZHCQwDJ8-k{EAoKm>q&&x+uqTox0BVo6e&o;IUb!H_g%h3ZXfF8 zm2^N4GPQSo_keUyre9l=Rz|Z;`veESJL?7gwAyP7kkoJisy32?jeD7Ht$k&^lsv0s zDr9dePQ6|RNFb|y=nL%5Uh<=@0wSlfYu_k8mcEbHiCQZ6Mm9<#E<#e*eU9%S$ic|F zat@_5M$vgy+0UUbPjk;sF~i8r;zTl=pQVN6Gootk`NFG3MMS)##$>tudGY95wMRM0 zQYW*3I2-w*T($`6rz7vNjy;?~lF@&lO11`ZAf9ygp$N@s5pa0X8jVKsGS z!Nf3eP6Qs&_Yj(kyY=X@MXc^ZMf;>LJTEmk8+fbpEaVD3sWX>hq+pUv_k5rF+Xr4n zh6g)+deEGzNlt5rEbx;fYVU_ythJCeuU7g=D!&k8W4ej@1#WwmAMD17`s{ac+VISf6JiG`0y?p|bB7N#mWpy+~@ZEKy~L zqyTN*g@;XUH~A*@U(KSTs7bT%Ph7rbi)M-Q;{OJ6HB>XUkA8eTtB<<_!CvO=MaG5$ zpF8oQ%S!mh!~-p)Mj|Jg2}TpXidTHXP+u%J~)A^;&<4KqRU9_wV0_->FQz zpNe?KL~6*34a&^dKgj>KinVJ=$!{*b!gFdRh%%|>z8OwE6kmA@aSq!Ia#z?`x=o?J@%qHu&Tg*Sl)wVvWJ@W={mb!s3!;n2<1wtK+91yLN8n})(97=#8WCC9 zMy&x=u8>L#0r}qd+c)bndB#t|Bon%ep2NUSwosX!G9Fhb+hQ@4oH)X5yN<-oA-BJIbV4ubY2zoI!`FlJGv= zNaiFwQ|-5=rlFbf8RDVn%YIM6MZ-c`@kT_x(hJkF=pmUpz!bT>W6qUV5i-!<-+#!} z$6Gy4*iT3?arVuewFwxtao9;em920vX<`Wm-A#7{JSRzFws*bR7PNU!5fVeQ;oqE zhRMoTR2<>&_^)XQy~4IIz*JdlSHFGB;h>L{^0!i55U(pm5q3tVj@MdSTd|XPf0z7U zImnYdLxCnKk+A5wvPs8QiyFJdAj2oV(t;u1IgY;pMw5|nke%z8tbQG^8;e4T%-kHL zQeSh8>1Z)69uB30>C*12h0K{Fn~jPLwYN9hHc|mrh%9PfL?5lCEQc8*=V&8dz4D(H zmGHB!t%aUFUZZ3osVFlj7&pM23{I7y&1$RfCCc3JtuF)^Uf=s|(mE@yqEEfHL3RiC zLD(Vb@ABv9N? zT`=XxE3Ns*ek*L-T=LkZI9#XL7@!sn5fDFY5lA9~=^#1bt>Ra}F#tDFqm}RJFkk-p zBQL6}{b%T5tHH8@cn6Is4bd-p-=se_1{8*M;Lm2PhOzG-$e$_8d{X(5bvsl|CK zrIrSL)a2tQc%;xl`<3!!A`N+268lm6IQBiWPk_ODTO^W3AS&rJ?;`>)X!C zXj~LE%6?wzZkL_)g>(*@tvlT(KBzF!EnMJ6Y%>NY!$h<$nT?HWRY+Fx!3IAq%_BT9 zHOsEZ&KZBrN$^GW_|z<7j_E}j`je)`9Dje^G1wFMa{0qm4bOPOwQQfcdps$G91+X* zuT?#t{N!;K5+#e@c=IP(6j?8pjD2rdc2viWSw z&O@Dz*wEC)xLrOk@5J227l25HTRxfbl^&T9t^wm4%u#g7(ew13N3w}3ev)mcY&kb8 z&5)cvIbwks%1iw7fiby>yFs1C7>T!p81=P$m3xwl59K3Vs$lKyJk`$LO45GH@?B~;_1_;8I}_@Eou&noxZWT_7qRv zYSg#S*L19@I)rwutIUpQAF5Ou@*M{z9ndH(c0~K};Uja?rhG%LE**f-n@;$kO^oW@ zollP!8`FCQ9~|h;I4$k^nn_-U(R%aq(RcMG5CV{yj`~Z#bau`sx^H88XTt2(YnX$M zHvR1gX#?uaEe7Q+l<6EoiYP)Ve6(w=-g0TbrY@20ZIL10ZS#7>(l^skrTdEy5;%2y zmMr|!d6tGao?uwvO!M{K98(VKH8BmI9i1xb!%}r!c?ODr#w>f*Ke^DQpK}@WmWyzh z$(Ez5x6CYuk$;+iKOQGR_+IinO@gX8MlhlH`{o6%Lq8mIfFhbqK>?0@XBgk3_W^Gg z_%t_zfq6j>RoZ9x*Pj#wRAs9}C%2 zLu+NauVxoDU^*U)okQ|nixUn5$3-iPbW4ujVPCL*e8~j^vyyTm__N+J_ww&2rhQ`; z?W%ixDM=c#!*)LUESrr>cBnSqT70=TI0*=1|JFJ0-Oqhw@36#gxvLMK$`;Mu0o%h} z`X2;mQ9${G(m&mK;8`EKkMTxVBmZ|eNtVI!cJm>qHRa{<2N4mbjkoxlHHZ>BM-7<7 zV_5_*K;s=S#ArjkUS&YswKBih>Z5$udqjzaD_1(R6f5l9;40J?sF(LZw);IUc|EWs zH#wW^H!k9xLuSuz1QfoY7PY*y^StV3o>EQ~Pu=Qi1oaCyBE26gyO=hTlOm~~t-c4p zegd_thyGKSKuQ`dLJ=-;33DQjAh)9joN~`9V-15R9#)YJs(>?QdQjj?iwuDwsO@Np zZ@)HfZuT+6YXQzwzG@8&$OSds7UFkkfWK>$3Ppe^v%okO<$6BE89NH% zz`~Z4N%o~yFp`QmG8-t(!vS%SmopLN%An}z0?11K`&lDa{>1!WBEG8%R!=i)2|cdu zYGDO=8yj&dEH>4p9VACkUmN3qFOroCz~1+Wjh?>%knOq^>VF>gl8yM$ z{6scc*0Jsa6n`T>Nu<3Du!UXeU}c_7J&+TOp(n0xmp-DUwtEbSvm#D@UDWXdi;e=I zrcDgX>my47_RR1veaWRzTvRwDP7!30e|ioi{m+SXitPDkCpgNF#qR`SK#65A0Ho0aHQr%`zX7}A z^#Fh3)P7!vQGETUE}HeX5ach2hmj(knMPbx6EAj92GU+uB5@L85@JpR-@suC5ckuQ zHv#gY2SoO~(;%is|NY3ofo~+vHmfhHvLH>ItnIdC{kAr|0i=>r#L<%nJ1gy6Qw(5P zqL`6gkN)LNJXq>q+UP|Fuo4S9-=}keymz00{>fDM^;nM^HGy zr~<-Ph~lI7;0#KY`0Z7?*7?hTko4~y;KTdiL!MV-QrvMwir7b-bpJhbdaIr{(4yj6 z53F;At&Z$x!COE?LE=c}^fo&?`%vPVYM3N&t>#3z&dnsQ3A9?wU^&I341@#cSU6`^ zoBPi9nI%bw=QFahG^yuDfB5l70Vhyr4;XRLo;qusaUn3%eEWw>RiGyBPJo?Yr>rBO zL5o;Ve#5yZM{A;9-WRad_M=1A6FjfT{=F3zzx2+9xrGan+UQl$4Fsl&_~OwM#5OI` z!*}U8?__`>8$b8@kPIlzzjs`|1Z(xGKUJV8`Cl)<$Tj8A9b#do@2NM(ZQ1J}^8|`= z$2C$_wJdp+8_pw68KI-bcP2LJ!j#@D{r|4fX%Ljg=|8H$=XKN!h_dM^wRB@l6a~98 z6>oIjr$dE3+%nmf2T9b!vHR&8yA-TrH=G>-M#j>~{N*FRTG(yE3(mUL%n$H#?#H>Z zHXr0viKMSj_xCq&hS)8mro2P4Crzv_ix^PLUWE}0aTI-5EPWPFB62g~NDU1mwX!lp zJ|6PeVGQee7N|rCoW!e!jpjqW#N**<>omiG@89V^0JI{$4wekWga?q8!obWzkVJbB z=1o=unjQaaJ$?l7{Dp0!H4sdw6vaswDeBbGdX!lZCAo^pcx}dhU zb)Qk|jlFqWi`^KPurMyQLTXzFz$*?w5PRfG6^;5q?{RY@Hpj`b$ndq2-7@cbgOaMk z3Xo+*_+ZC$qrYRVb$K)Ij;oGo^HJ@c&oJdm9)M+lrP)&x5)vl5*OVxp;4rii>#h{q z*B^{+>85`LGD76%y*$hQJY?M3sjno6p#o&?a(Se|I9R1Q@XSCN&xrraS(z#~suqLL zJwWIWm{rSci3sbFcWR$jI(p$$1T$TeZ4idzt4u=9(*p#j9#&w=M3=CP_ozk-XUwU z-$mYM{0~{(8r6V}kqU~AMiF6+5^S=#RTdWiqQwnzcYKW4Zg;5PzN90J4$CzYIQA&c@TKDf6Bc>M~0lEHsuPv)etQt9z^pp6jtr$m2m z^!BLH{3F7(lFsBCJ^)Z8p32XWU-?=}>-{Uf@m<6D+o^fhAGtBoV8q#l{%62Y!@$8- zOIWa-ywzlr#SYd_?Tov8-tJ6!(Fvh?fg}1SszJw9b8+?uCl50uM!Q=KV`e3*I1SUH zv4FiqVO#@6-dB;^jBt4wtic7rLln^RrfYRS(Hd{CJ}*Ql;?ILhq$BdQH22$@w1;4j z4v5a+{N7dkCSvmci6y&eBq-h87|&DvYnXV%#^<%<=_q5+AmZQoPcqQNqFIfIWr$tx zZc8C2<{AiXT>T8Hveh*HNBGLy7S|vA<;H}0+x3nxNnCTyG;0d<-4j@J5%OsNA6DA+ zv{6WUNbb!fZR0LCec4Vk58LcBf+Ob~IIf}o)1Ho8P&C&TO)vW<^m1aQ@!59wqtCT= z5_u#5=DeBgFu;|Y}pD*nL*Gy!lCew$wqiD^I%r*@^ zKCx!&nAt(c+lDs8u=Ts|LLJbvj>Z$St(M81Zs*21PSlj{vQWCPYc}cI6(97YUWQI) zg#}{K+*i3QVJX(R(G7l;Alc=0^uhE3-Ai8`0f1=}4LR=Iw{PDjKv1D9G9?p(>J}eg zBpL!RD7;_6$xJ zB3R}SXy@AYpM7U>E;K$Y$Bw8oW-e&x;7eao4RaD=#yf_u2m&_+%pLB5Qq@n?{B96z z%ks}ItjBVukWRzZkz;h#=ReQE;$C>yJFl+ESl{-oQ2OG6i3xJ0Sj?HZ8*NE%^k>eXsi_HFE5F|pA;S-FQfh#kQnS-Xx>m&jJX$PK z5oIM)*04AN;EKLgi*C9)c@Rke$d;O25_pYMEf0EEhlu8Z+Xubg%%^(}i!R(cReb+5 z!X$E;gAFk_;kdu0%zbDf4i`)`baE(l5YEBkUjn(7`BwGwaB|1-?fAK=|A9q8op5l~ zO|{qLg?2d6kOempr@QX;f@n zwT*Z6ceMK##H`vr9?Z}7(#Zsk9jZReiO0L&OQ+;-?Vx&`^edJ4lGQ)kwb%TyaFPc> z%W45AP&%jfByFQ}WDSL1A4RHL99X}dciwxc7f=cq@E9UyUVTn$zUv1ZTBmd$4wn;- zrjn%y+P(o*Wu}DP#nQ#zkQZnxQXr)QDMyUrq)7KhT$lv3 zM%C)^g)|FhGSkCN%>W_WFO1_VVwcl!51gg1BnLcEbt3LJ(z!~L&FSm!M-|)V-PiNd zD)Y|=#6JXE)Kn@lo)8CBQ#X%AjppM*8YyY@Q#cpACw=qAgQFDC-U4C2%q`;lBIllK zNln7NLq6+k-W8dOaUhnCpXLs0b%b98A^n5lN6zk5517xTlQ~HeN&DCkRE25od+`me z>6tNBb-3&OdH+2}NPkZTK?pfC_yW)Ijn{`+zp4dMofbB7yFTX%%A*ii_&+mLe`^5% zsYy;XwHPRj)`jG!I8HxupQfkI z33-pAKzYF$$h-N!7B-bE%ZtOVY)+{e+)M3RQtvRnt)OtzH>71wU9CT52WvRLsMMyn zAeE(_=lx!JiJr4t=F2ypx)r2Kwreet)5K(Uf4roJqB6&6^035HfmVPScn)p}8PWugdc!Fb)>wR4t&VK;+8RA6 z=VoDIkSzA!ytivOyRRj9IGQ7m^z>IY$zK2qyU5EO%#w_QLfmNDY7ZOIIL#0Bc*Qs{ z59SrTzS(|NWg(u^={kH7IaL-cD($&7FVa^$Va;*}vq>WbQLCeh zj`w^bn9+DkmZ6CWnU><>LndTrMeqk@kxjMQ$%vp!9-{_qEW=~?a zvaXdTK@$_(d~b{X14E6mvWIYYhVAg9rDVa7`)i;5{%qdb&x?+e3&41_Q@M|qN8EZc z_cg_R!sP{;!85oo>G&qyEuOz6lqQ}bJSG10O7K5F1vJWqd${7s2>#Ms1Py(|;&_(2I za@A63yu8npI)``grz)N2aS50U_dPLXYNt<$X6-x+WvUj<>X>k|{lrLG3TxP8fviy+ zyx_I6H^V)2jxuQJ9F66gXcia;(8{vfU(&_*CCraDj|uyp|?XtlM{e%th@ z@0)oIbrkhkY$n;9(>}CRjgWMgKl}@jh3qwN2?>R8PMfcQO_+{={VO0Z7M-pF+# zm4BS@n$dqX{TaQ9H@i~(WDn|1T+CzLqgk9TkS1}Dlh{7#pKu@kul{+T z8O{$f?w}m3jOOPD7FX)RtjZ&@E9{=%PFf+Y!Bwlj3og3SE_5;d+!N9YNl;A$@Q{Bz z5!3akDC?}ciAcG~rv@`A_cZxq_X+I(7HB3_j8bgYI$^4uAtE$!A{*>+?lS}(q?`~U zmReVM(0+U!?^Pdqk|BAvLvMk>Z@Jw5f_}7jHQ@DF80AUwAAJQe zIN-p|L016y_}%Hwu&lOSZ`4r_biuJp)33>&%~YvxG%j|hV!1dzT=LaZqvxw6v#`nn zgLswENT%$eDCp1 zS;fxwCLd@xzMy`w3N|LnwHd>nA0g74>TON9#cj!TvM%zaUUPSuck2`c|8gKW+5|!2 zDmF^Vr%4>Zdo&U8H-=qy&hY12+ZT^q^e(wGc<9$X<7LeS7t}${CG{org(5knF?l8c z=>Slp=p!4OtoHWy7T#Ou)6c5TU#}D7t(7RJWIc@5w`4Ek0azuCea2&g#=GE*7-WL^tJIfxTO#r|HxMl`PGADs7(Hg*24w#{{ltxpQ z-Tk`gC(Vg<(^ zh{u-QqVE!-Gx&?vg(EBG&4A4SuE3KPki-4bVwV!cMnMI?hyZmIb%au25Dvm!ObTGS ze*HRb^VKV%mnCNXvc=f#=71@=G|+EyqF;11iuwhQSpsM8oVS?R1^a1oKlhoFxFeky zQ}2%!^vXpvqGxBU^henudcu3|p(%`Sr3l8ah5^eKySINeSXw*O0z9#yy!#G=zOuOP zejFU|Yhz1_J_lI$bQy5R}$`#jK%~r|& z2VUDo$Ok#$`8ing!k^YK^K$>l9uiHZho`spo15?MUe3Hxv#kTbOUL(E^ISu|lC6%i zOJNHgrGs{%gm;R+GhCQY1FFpAZXmJ*inuWqoI!zLG0m$Q7e2091_UP^_#)||dPte& zR(!^12>aiUHCx%F{hK%tyG6wG6h zL=c!r8amP2q6ck%*z!T1f?o|Tz`1|aFWQcx9?}+2KlR65#JPcSpoB=@;j4#ZR=ALk zE7cI|zy6UEGpyMvAL9f|vD6XCzj;za8im-{;4vS8k zuu0Kds35j#PwhB=HsWW;m~roGezs}` z?^1Fk`Kj5jiR!+En6ic=i}`>20pQ15cVo5FNTv^2*Ld*@<>@|))~&`RUvBJlR2kDv zjbRM_8e}G)I)MH?iq6JYAK+V#f~Omx&1ZZQ2viABg_6~kl~jh?{JCL*)3*ILkCa@I zE@4Y<{>Oj-1r?A{aK$K&KIVd#mVV##QKWz|8suz^f=b3w7B(#l+!?YfHqN@mUGU3Z z5~QdT$(+(A`Zo}TOK;6udk#s=A|n`7orzHr4`a~ShigvF^qo6j)!!XqHBIi*(y}!= zx&iz(o7&Ok;fyO_VDsPfS-sTB559YjntTyGq=-+6yXnu$fQ470fSpNH-Z;-fN>Dv{!ie$mgo{uVxQnDdPyr0WiSB}^;jNTV>(XeYMQDr2 z>(?bMPYSI-N6f@;zmH^OWOCexQJ_+ylSH_fblBA!PdEf4vw#JlW@BS3ZNo&FeBVq| zJUiYLa=4+v7X_2wiV?}o2dJ~BUSJHT$IiJ*Ds$X`x&C-MFij?)W2b&~!`%0-O;{2^ zY<}-?PQ zbf7bd!@Im;x*sykG#Hg%iliv7_RL-WbhZ@8&sl}B22%-m)>VlKO`QeL3HWm z3+Cb4RsNKr-=>ZJx>hcH5=|*KE-_20zbiPc)X%*i_2jW$PS6psm6UTo=J@utyxeU; zO0xnwxu_4cf^A3F_wduIg=U*k6!69*>GrRTaGG3;n=wRUTQbn!7!fib*q@(jzbp+ragv8BMp?pY+`s29Bv0-l&J;cGBwo!M z>@K@xUPTI-wfYY0Hh~_=>-tei0RSKo@(nTl$B*+f9x&)NdH9;{r%SO46+JF4~g= z`J2+D2U)50d6qdf`}fyPI5C5arA%68mtYs5EL*vS(!JZ0$73`9wU$SrW2=SmM391; zu||zA63~T{6v~tZY6Yb{qg;B1RYuxb6CX`zQS4=ROi}74ZXbiDR0JntQ$tOtg-~_FZGp;ig=4 zSB_vb<3436#0;)Jg%pmK0{%kiz?kt7fS``y1eCd z26~BChaB@Uy;BA%VBcqKU#F-e(QDTk80wVuEz1Rc!q!fDwQ_JVNUVJ(YUuHJPf-+r z40eNM{ZFReDXHW=m(ONe z5*sO!@)I-BOdQ0O-@bD|(N}AQWZxJxLfS0})x7e>n`j{G!v1Wc-pa#vL_t|P{1Wm0 zA*VSn+S!|P($}7J8Pw`= zU7h>*AwEHiLsFeFS#R5VmMH?+;UXCg^QL_)H5i z*5P4gpE^X>IRxh4Y*;~(GVtCaj1yW4)JVG3% zZ#%!Tm_iC2%@3~Wq1(9JeNRu<(gRYNjfPKK7LENK%0FSQoRhO0))4ukVCa`@bBR-4 zfus24&8+a%ETO`}4VuZ#hlxg)?5;(yvloZIWGBD*;uFRx#p**K_qQw63MuHN(x7Si zK++rNyn+U8qaEGM#m!BU`rf^5#0<<|dXJfp_SYT6J(9ET*CQ0OKLztQfgX$f1=lp`=OYvyEU=+S@CAVb++gq*y}ds+_Wz^UrI))T)4^|E`0fy7X)$B zQ*Jz~qaWsfumSNpX`Ow}9}!bM_P)}l^_h1TTj9Nz!}*#Q!%aROnx>5H4l;C^k+uyj zM|;iIZ!A7JvNW80dMc|fg2vJrdWB)Xe1_VnPF*rI?w_VrtcATXE-)78`l;fKz4^5n z@;md%p2gwd+=Ie8$AtXN#K79_^%gWAlH!=Hwfup zKi>Osag1`Z2)$5#j|SZ5LVe0V?{NmoYHA93u5LZ!_(hDYFNn1ALO2}_*Yue?nGE>r zV!hWLWm&d2G>~>8br`8apVY^c;rr)=!roLJgk~4tvF^-_jg9pxo(b|KPx#E1x;ZgI#ho!dK0Yr0VUOxwgL6LL7YxJ_xy%(< zc8e_a!dLNti4)o*^C~9iLD~4!Sc->2Z=_5=M6;5z`iW2JuJWB2dP-#&NU$JpqA z6}PlxD!yfI*YS3QE%g$|uY+u}EC)>=p-UkV;U+%SA?x+0a_MS1M#3QiuAPe-KU^t< zvqe&TuibfVlTr0iNZ6Ko+2Zq^+4CiLx~kWv1E*=ACA&a2{R6Q(&CpEGp)`f@;-J** zf`#BHPquElWnDW7f)CPAw7v6k^P&R%g8Gk!OIx<&LQbBpY5IE0Ob`p{uf%(Y_`5F% zHW|+!tnLwlPj<4VickpiKzjXy%WwS3PwxgRPkU**evi6;Q@C0AN8FjEL6V=%m(6`Y zF=z2#Hb6<>_a4wN3?4B*ZEpL5g4`G@<&B=4RoJ^cSW>6IN%F$(hoG`nYh(SDe0s=j zES_<;Ef{ms<}{Py+~r1NL=aav6w95t9-TVu6`}vIG)lr~L^C9@wdKsGx+p(YiihV@ zb+9r$Gi3OC@d%NoxSM+y$=Bi@^Ms__t4&J{OZRyfa8E{*F<&P`*2~^?!Jl zvsnx4-k`yJf)T0ZIh$gY;J@L+ko9F(_v7*R%F)wIg!yjFHYSm$)QKUC`Eus++HT#s zvMax9K6?|kA~aCV43(l+hOS~oQ+Cc=7j3*`_c!}&dtF7!D0d=8>yh2jn=GBe47oG0 zDiG1JR?fMK0|9NxMuD6&sJ|x(SKT&#L zu}@yS3`6?AD|qIYPbZQeK%(FnV6)8fx;=m1yUxC<`9_+iM|*B5^a(|AU^SrhA`u9M zPG9VkSN5hQl=~9GJqjVx>i4#&&y6Nkoq*Q&z@RylBn<~SUQ>j?5WnRiuDIlmSe{G!p(aHJD@P@SXJA?3HhtDY;VG%$d(&RcCf_dHvd!-y-KTeA&(>lj zEiJpG?L7BVm{XS z&5e_a&eS3{l7n~gAKRk-7>&5>I+Z^z&_Y+EnKoJ&zJxvt4Y(&S%!1;-7RFY|k#i%h zyBc@WIeveiHT?X&!`T~MXfKa)JIlKKR>f2`^O4Kn_j62k&po-YIhOR>+b~}fE%Gop zc+wYY{*zoPux-~kaGK{Z^`}a}mFYQQ>+_v^R!rm>+#>~Z8hK~$9S-nvq_gpnhxG^P zHSO7)-c9TFNUi!91)Vr|kV!*saCP|%;qJH9yZS{#2+5IC=C5oWBsR>nGpX?W=4+K4 zRY)Mmz4M*;`}@Le#_SZ~F)ts(QKs)uz_6TI!v@}=f@Q2oPy}o4t=x#)$$z_0Jv%#_ zp*ncR8~W9oWV>0A&vnI|Vh!U2=P41r*mlA8c03tG((rWX=%f8H#{fV*_uXC ztaYf_bq{&zu8Ts|R6prWog9tZd>$TZpKHt+l}KikO?6DfP*WYq{GXDpJD$z&ZAV0` z*t_=DqGFVyc507m6*Xg)qD5>$>=-qx2vu6N_KsO1MXR>jdwi|hrD&~Ryzl?{oO3@{ zo_k%_ea?e&wQ>80lbJ*-u~O?(`ffSHs<96|`jv7aqFaGC8TLBv4(yN}Z1&AR+sCl8K#ez$aOfu2v|+b@Cb4D}L3kF0eB zDq88|VEO4;gv7@SN*l^jd9XZ$Sr^V&@8JI~nwzH}+sUc<=g=^}y(EO3BK) z+*_gx0TVXN%R|U>*tY1O?mHEptVA8zwFTN!@y`v?#h}{|7P=KjlQm4cKGHmmcCAx8BuZ>bSSm6TM<=|-OrNwEqcl8fH|kztd#sSSISMCSSHnLqQOLd z={N;99JcqDv*!0BC3>Gy%MFpyah-$3gpxVfD|&i}fY)r83rVNlu|=b^{@kjHq+42K z)7GbEdz3326KK~G2pkiaUBmn}>nL>ca}Btl!XIDC4cY}iG8iS}le2sf-)s z*3O43H36Kzz8*Br&dvbEacMe^wUD`lWJCHb7anM3Z}H=EFu_Fsmn*Y09VTYVoQqI zensI#va}MZd)zyQo`15Kc*%1W{T}lb2Y5?#CZS467C@&;g!=`bFU2P!Fm_DF#0J(l z2N~#cSH9`S+QOA>WQ_*xOyn@7r@ycM2oT5&oTJ*GS&_vH-8vTTtF?&qa}>ebICq1Nl^7DZ9o_k@JRFhyl(XyZ{am1knDB(Vrq4k6=>^!7?AW5>DuRrK!37 zq*#AAF^9OE%p5+f0lP@F4|R@P6~ix8Fpk$dj*dbVvX1irA=Kk7n6FwW0h?&o+}ISGSdU`34fueJ zflCtgO`iFD-?Jg7ZhB{Z)Bm~D;cb^Z#Dp@yKec!^sQH2a=U6YH^y!EnK|&1g69LNX zqIpE`BLdyp)_|zX%9HV-rCIVO-u?T@1=x&m8>>4^kF1LpNljV{XU)L8ZBj^DP!-8dPOmt-3XOWYo16bq&qYkA2)Yw<==$9&nSp`H>7;JMHkUPI={j(84(;-=mCy=2 zzZm`@P%*1Y5N-};L05r&g*s+FetsvxPCiS2PZ5CkzkjTM_w+As@NKRBQ%VyE{iH?Z z*YUKkKS-h((U-o1ko<1QXCK@_}w56N2zS_M_&6eVJe&B9!U)ao}>~Pk`>RF zZQ~Y;U}qMC*lS+F6x|}_N|{B9>pN~4R_d*e+XKTO1RhC(bc^%oSjy-vcfF#C9E^Rn zaq45ZaQjN%qR681%{snHN6Nu)PW(OJlw^{W^RNDJ(0-)J-6TSXKFb*t|$`4pshtuif~iLQ9+ym{WQM zo@n74RRjq#$K>9jj=cINV^HF~;Y&9NA8e{w zVoZ+xX0VD#I(i^I_-EAlT@w#d`uXez{^L+n)AK`i05{S5!8w@&uiLZZL8}*t+vnU7 zp26FNI>EEp%TjJ{B_*Y@-$@~?L~>^Zq<9)kk)9c6Ti?>O{H!t!mx|~_3L<}A@vhB)!At2j`o2OTO_A$<$a(ocS5 z9i7f|OuSifY)id#k9_tXzU%mPo5$jR4a;q-4@lrilYXSp^1Mzh<50ujCDuEuj0v1; zYiu^Jo8-SKGov;GPXO$VtUc4pn`Z-xJEck#Nk^#qlz4U(&@g`m@o=FJpZIiDUY0qM zof{D8i7YT_S|qKYF&1NHsWA=yn#`ndB=>X6$Hn3>Mm)>SB_!=XHd`a3p*uiQv$tk|#}%)T#r@UWKL+x2?cgBBC*sN74Me1ssgXv5>pyJE&s5am_L45pm?<;(oP6r1f2t3vN!%P~GwIMG61#%6m?fo!js zH@h^j1J8xq{}3=)h@ShoD=;q11{lUK_RYM2V%?%l?_#*bMsX$ca`+#hQnidBS{g?L z05NwE8$wdygSyJgXtE6&{UG3Tub~w=wCCveDtAU@@&RjD3B@wunu<33tLZ_fccQ93 znf27fmbz0Oas!O0XY|-fd#0LT3cPU*mHejwP84ahlTiKVsZ{)}dvwL{D=R`{zbGLI z{9AkvH~zP>O@e_8d?i?^wouhi%5{I#KUmRH$Pl9EY7S5x#O{Y9W6u4y9DgJ5w?@mQ z>Z!~BF-tFs-c=WPO!-w%foNqYSXp>-JR@_Qm8qQR9cb%uU(^%Yh&XR_g#7i0*}a%g zljfSLb#_)unda}em1z#AYfiuH^}%8_{>kZ_EduBlG=-dViI%06BPhBlK7^iS+E_oW zASF;9L|a}(yfp)c{b%v0`35i91@}5tO92|q8gn1CXM1&jeYw-FP`MIF-aoSuYKRs}ZyZ6gc_Snx!#+8)oW$L<=H3 zoqu#?&pM#UylCc=Wo)*3$%eW{$ETcjJ|L8@fMtqQP`i6)1c{T*>mW%Bi@QJ`4EI-k z+mw1+N?!8yxo2X`oVHB88PtAm1w}(1V2ytZJ{(+Na9=Gp$}>4w=z=0rPKTTXbLRcM z@&c>fP^2vGVoUvDJHpVs9FK3LaUmWOZ$YL^%Bny*Bnui(8*6XA~IM7Q#b(|OdA=16Ldr4?wl`P+}W1KZkPS2>j?HkEwUa`(%S)^9! zbf}6B1a;0IyL>2%I3WVybY&-Frp~3 zA?*J|En>tkFO^9Ul{{RT^B8tenr-%KMZjEZF>lv-ptBp4)t@xrQ=2p6CbP~tNLh8X zoiV%B^v z1%4+jrJ2Qgfa4?Rw@mH`ns&YtAS?`PZ`kF4{4HbblJ|P8X1v_0rv%cUdTOfL-d6F6 zb|^!p3hiX(#-))FkTQf;Tbf*RPtWX}1HjA@X8shYW@OU#a!sd*wlPRS!buTO68T5n zW^Vnuo=Y^JL_4HTsrORqD~iR+ptDov>BcZgrJJfw~z+!vd=BjT;WWY_1b+Pgi@l^J!Ac3gRgw0$v_O{2G&0X|JYUb&cSZnK zyFuDg&vd9GEW}(F1?k22x0uANV(Pf2lDnSKz{M@`Po?GNr!1O$wsJsif=;${X^*j5 zW7N6hX#H9=TMA+mZL~Jk@~(=O$Fmv_%944&N=ORFmue zVU@b$Pe=gnlICLTki;@oh0i2h{e%f)pct_T(s?kzZ0w2^Rli_mf|7U49&S(*vpUx5 zo8LZVW@>>`e^}2FVUSI)f8{doBYJd4AnZ5WY?HOU!HfRc&?(csLsuUl>{Hu6)kezKhJVGP;0fK!j^YrRI?u5bKI08C+2k?cT zKN%dMnL-rn{J8js-1jk#Z$<1fu)#fka(d4qpDZ#gZ!Du~%E z)kp~p;m%Clx2W2VS6T4ym9#o?9#EEn2Ke_qbU+Nf_&bGS-p-ot@x&|C9e>%xO#v9< zf}gEF$H&=B)BvF*2GtIT)^bLQ#v4xF3J9J5JMr<3}2{UEsfsfJxH>T{4B1=)3xRLHG00sj+5SDL1Uo$`_^ zWF`L*uXo7_0cke@NZG~wW36fVe%CXtw|LfBRmGdGzvjUP3H8p<;$70-eW$+@o>-9| z8CaLsJQh!LIhwuPyR}4|KFdk6L)&UL;u#w;gKue6TXUL$OZBtyIY1TF9_d_$cNo zt1yJy5B5M!^aj?bhD|zVU-yNPHx3E74-S3ML_*7mJci4{_L0D;$5ZWO<*v1#Lspp5 zPWU6V=;D$p7Tzutb65gk-%N`hr-l|&=`!~71EZ;?D+57H<}4q3;M1f|`STR~NVPM? z30=9hDSNo6+EJhTEcJhHO>cf%R8>_)84;HPnP;5uoQRHtwMx*{iMy)sfke3R& zXbwnPQd$$oC?CC~(EG6S0pBBV(CdV8U7<%7Kd=Iz1;f$M_Y(%whj%{snKzX^OZn2e z-K`_frY(e{BayBA0Oz86AkY{AUvhZsbZ>Qf0ULo`+}_(`-rnM%qt;n$j%*+ky$C%(Y}b2 zSizfp>eQ}FHvql0@9Hi2Qfb8*mj%1B1YK>}11v^EVF7UFtID`jhK7CK`L|z4i5N%( z^Y(XkAmu~`H$ZO`P1;zJ59>wHAHFr;fvTP7rQRDblH$oOJbGDUe$kkJiKgu=Uyqw1 zrOs6jtaU^kLnhARKZh*Yrk!Z<5q4ijLCwaLj|x7VC`A-dr`8)qeIjzvd5u2Ew;0m{ z2#BT`n2$^G9+w?uXR#44t3G22eLQh>03-Zi9MDB^QPtpv#s+fc1ee?!kp>X<#3br3 zm+?7W0bgd{pYVhHLNG(v)6sSoAK`bzyp}z_f6kt!b1~Pe@EJps$w&gf1p;wWYUCfw z6YD^`a{7zGWz@0mB|5uzd}9fgdwOS9UlP~#I)2{Im0q63)6@9h-Ga+{4-uYvqzNBb# zvoS*$Gqytw@=M06sSmoBvW6lIgnM{%#_})YoR4=%~xSedGkz6 zcSn3vlop9m^FQA*#qXZfyKi59J++$koeE>o8)LXDy@&v2<`3ySpG?R+Y!#t; zNKnbLyhu^xEA}v!ID$QS;XR_0|9Bd#IetyN^P9Q77da8+m^QHmQl2I)DJuIB5_YWi zN$3V{!YQ@CnMCb2HgQ6do6GuATW)3z?o!jnM3)SxGd}@hqYaPXqt6k47}vw z#fvZZUqBuuB@HMF#ko~HoT(iIcVChqAi=6=R!%`4iirI(oK$}}?o#QI(gU?yIn zy|}uPD*#{{k*O1dQH77Z1aB(n!tR<8`L-K*i^J!#bS&g7-Zm6P;cgV4VuNja6c?bn zB&w$cdNznGVRZSji9S#}JYxb-DEAteVKzP)9Ocl)%axe?S_QAVf(_HERYb~%)V~LD z-1u94Qx^+r$u?F3KLU0z?vWWfE@d#$()!kCWI<;D zyPLV~U@DC3;LI5y_p$D&y~=o@H*oO7Md6*Hv%z*y(O-GM&o8x>#Es0lMOqE_Kk;vatwhd#uwE_O`{(l>wG;58#d)02_g&BGe!1LZkX>XOR!7bd^gp9cTkP`z&&SgD;Tg?}ofxpw6ds zz(uqDw18A5iKp0r;qqCT#3-r6xXOuioJK;7+tP*=W+#>r4P+q^R(HOs;#Hi)}>Y(nN;#>Na>!HHU`*&{AyNh{=?=F?-Rk+u?t$H6Q)`vv_)3Zp+HzOg!t?Os zKDaF`N-;UZZ>ZMW2RKG-C`<{>+0{BOheNbp?18Od%uj?28W z^bS$<`1x?s*pKSY-0*AN_;kFw&N)B+4MT%b8*JR^^pog z1UF6z1%1B*<~H%nRRL|m{QXZ~kX4$TXWORNT1!B*E60G>ZP95Vx3iM|ZVGjeq&rPW zbAAgHbTQH-XjTscpzsZRU1WXbKod3Tu;lA?AkYCKvqRZq#YuMxv$Y}&m@ ze?ZXcTJ6|k1y|FfS_u+6S=3~tpC&pK)t)0Ut@WRGv!~#-q>7k>xr&fOqZh00iu7rT zE;0`_-*UrOzgg*7fV9v%9CN%wMO&08{VrM=AgHEf{9r`pnT2+F09nVMIcH86zbbmk z{(ri3tsmh!HD@5)!ag;0-Nf>COhTK7N`1@Rdp^7o?JyHJ+u5l}oYQZ^L-nYGaEp1t zd=jo4>xJ4Ek6rXeYw=D%(^s~0b!SxjyRw>u$SHci`w5es7HWf_`uiWS!xea!%@s`& zHe}=o`_E!+1(%Gtw%e6M*I-p16Aueaf*`oCwuT==F;47n;@a{|pmh|;4^oUbM5;IT z(xIt`8q}`h5_ao9LB5>~|J;w>dI-MR78|wUJi~Z4AH9uQLzREilMidmk-bN_9*%gD z!AD{>yDYzxq$9JOiCxx(l<$&d{%L+0+-}mg?)5-fi2m}$oec}#TymO5iMXwLS}GH& zgOY!lwjmQooMi`OZ=Ij2QcY!BNzBR@T=z1%HOq##sP z5Y|Zmdh+mO9C8)Z>9uz}BOyVMqqyB9?P&m0U+xk|&E95Kyp!4YciM6eK^T*g>On#y zsfnS)ount%w9C|)n}fe5JPEO`#8#2FSeRBaB)ZZkS^7*!MGy9A&E4|g%5}By=ho{tWkU{2mY?O(hZKDOobcGY ztfJ5L+_Z}xr!yUe4^L4>DG|A^MecY5jsSUj{b7R{x?CG>prLx0z1t-Ent6#>^AH{OLB&uaJEeV}j-@1}< z&^-x83hojvYF|0@072PQKN^87u;dR+gprt)o-+JxkNfh)tmAmbO_beh%*gLXzM6g2 z9+(THw(rRUSbzhkTLD~72b_c=m&%}CoN!@IF+0Y{5v~HcI{e&m}^)~7+B!ZEz3U6i%c?g_IS zd!=s{((|!So8ev$EeqP;)tIr05URUd@+6p8ubvL&Uln23G|Iw6+Dyfal{fRlIu8wD JmFn<_{{hgsTz3Ef literal 0 HcmV?d00001 diff --git a/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o b/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o new file mode 100644 index 0000000000000000000000000000000000000000..10ccf42c5ff2397c2201e74ed6ffa7237ed4f24f GIT binary patch literal 542 zcmYLG&5qMB5T0?8rcEn0XSg^5qy;HSn=0MHt^C}Oc!aL&m^5t?S5CUio>Dk*;KYF& z2adc055Vj21~}eI$kzOP^Z6T(4XmjU0PySSbOLgX9d_5G)0X`15z7+w&Bc0M7PVk% zU5QGnI|6uyo0jqY`XGzC70T)iJUA$we$brHy#m7IlkwhP<0N|BV;!9!z?B~mij?dF zi&{?%NG%ErGNINbnFO}Z>;-|Q`YwR{slyeAA+&jXhZsJjb>T5P2Gqg~)p?<_XF z7>~dAy%1I~_RY=AEHCNP%met-*IYf^zNm9D{Mn~A*y#Od5+9HZqIXf#Dga+E=bKS{ z5Gk3lmPO|p!jB&Jlqe;SUcZ0y)^^+hx{t6Q0bW_|7U7qE3m6^r$s-r4!HEvJXd01) zn>%!P$?E(I&gRiHn$pWy9@@BxZ{tG6obj!Q^P-LUR>`V3R#7fxUW!H@6&DegRVY5{ zZ>U)rYTEX=F<4pNRG~4U?zI!VrRg$F(q)p)lg(V{&6XKUXKW^tt0Y@y)7jM~n=kl_ GXa4|FRg;1M literal 0 HcmV?d00001 From 3301e9dc7288654d6458150bc220bab95ce92686 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 19 Jul 2021 01:55:28 -0400 Subject: [PATCH 658/926] [wasm] Use compile rsp instead of link, for compiling native files (#55848) .. and fix logging that broke recently. `tasks/Common/Utils.cs`: TaskLoggingHelper Utils.Logger is a static field, which must be set by task else any methods in Utils, eg. RunProcess, silently fail to log any messages. Also, this would be a problem when building multiple projects in parallel, since the logger is a task-specific one. Instead, we pass logger as an arg to all the methods. --- src/mono/wasm/build/WasmApp.Native.targets | 2 +- .../AndroidApkFileReplacerTask.cs | 3 +- .../AndroidAppBuilder/AndroidAppBuilder.cs | 4 +- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 40 +++++++------ src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 18 ++++-- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 5 +- src/tasks/AppleAppBuilder/Xcode.cs | 24 ++++---- src/tasks/Common/Utils.cs | 56 ++++++------------- src/tasks/WasmAppBuilder/EmccCompile.cs | 2 +- .../InstallWorkloadFromArtifacts.cs | 3 +- .../WorkloadBuildTasks/PackageInstaller.cs | 2 +- 11 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 2d89733447d..375747cd4e6 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -269,7 +269,7 @@ diff --git a/src/tasks/AndroidAppBuilder/AndroidApkFileReplacerTask.cs b/src/tasks/AndroidAppBuilder/AndroidApkFileReplacerTask.cs index a0af313aa1e..8a8149ef154 100644 --- a/src/tasks/AndroidAppBuilder/AndroidApkFileReplacerTask.cs +++ b/src/tasks/AndroidAppBuilder/AndroidApkFileReplacerTask.cs @@ -26,8 +26,7 @@ public class AndroidApkFileReplacerTask : Task public override bool Execute() { - Utils.Logger = Log; - var apkBuilder = new ApkBuilder(); + var apkBuilder = new ApkBuilder(Log); apkBuilder.OutputDir = OutputDir; apkBuilder.AndroidSdk = AndroidSdk; apkBuilder.MinApiLevel = MinApiLevel; diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs index 345ad721999..fcf71c0e6f6 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs @@ -87,11 +87,9 @@ public class AndroidAppBuilderTask : Task public override bool Execute() { - Utils.Logger = Log; - string abi = DetermineAbi(); - var apkBuilder = new ApkBuilder(); + var apkBuilder = new ApkBuilder(Log); apkBuilder.ProjectName = ProjectName; apkBuilder.AppDir = AppDir; apkBuilder.OutputDir = OutputDir; diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 23475c9e1e6..c69288218d6 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; public class ApkBuilder { @@ -32,6 +33,13 @@ public class ApkBuilder public string? DiagnosticPorts { get; set; } public ITaskItem[] Assemblies { get; set; } = Array.Empty(); + private TaskLoggingHelper logger; + + public ApkBuilder(TaskLoggingHelper logger) + { + this.logger = logger; + } + public (string apk, string packageId) BuildApk( string abi, string mainLibraryFileName, @@ -209,7 +217,7 @@ public class ApkBuilder string cmake = "cmake"; string zip = "zip"; - Utils.RunProcess(zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip ."); + Utils.RunProcess(logger, zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip ."); Directory.Delete(assetsToZipDirectory, true); if (!File.Exists(androidJar)) @@ -274,7 +282,7 @@ public class ApkBuilder // if lib doesn't exist (primarly due to runtime build without static lib support), fallback linking stub lib. if (!File.Exists(componentLibToLink)) { - Utils.LogInfo($"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); + logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); componentLibToLink = staticComponentStubLib; } @@ -339,8 +347,8 @@ public class ApkBuilder cmakeBuildArgs += " --config Debug"; } - Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeGenArgs); - Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeBuildArgs); + Utils.RunProcess(logger, cmake, workingDir: OutputDir, args: cmakeGenArgs); + Utils.RunProcess(logger, cmake, workingDir: OutputDir, args: cmakeBuildArgs); // 2. Compile Java files @@ -369,15 +377,15 @@ public class ApkBuilder .Replace("%MinSdkLevel%", MinApiLevel)); string javaCompilerArgs = $"-d obj -classpath src -bootclasspath {androidJar} -source 1.8 -target 1.8 "; - Utils.RunProcess(javac, javaCompilerArgs + javaActivityPath, workingDir: OutputDir); - Utils.RunProcess(javac, javaCompilerArgs + monoRunnerPath, workingDir: OutputDir); - Utils.RunProcess(dx, "--dex --output=classes.dex obj", workingDir: OutputDir); + Utils.RunProcess(logger, javac, javaCompilerArgs + javaActivityPath, workingDir: OutputDir); + Utils.RunProcess(logger, javac, javaCompilerArgs + monoRunnerPath, workingDir: OutputDir); + Utils.RunProcess(logger, dx, "--dex --output=classes.dex obj", workingDir: OutputDir); // 3. Generate APK string debugModeArg = StripDebugSymbols ? string.Empty : "--debug-mode"; string apkFile = Path.Combine(OutputDir, "bin", $"{ProjectName}.unaligned.apk"); - Utils.RunProcess(aapt, $"package -f -m -F {apkFile} -A assets -M AndroidManifest.xml -I {androidJar} {debugModeArg}", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"package -f -m -F {apkFile} -A assets -M AndroidManifest.xml -I {androidJar} {debugModeArg}", workingDir: OutputDir); var dynamicLibs = new List(); dynamicLibs.Add(Path.Combine(OutputDir, "monodroid", "libmonodroid.so")); @@ -433,21 +441,21 @@ public class ApkBuilder // NOTE: we can run android-strip tool from NDK to shrink native binaries here even more. File.Copy(dynamicLib, Path.Combine(OutputDir, destRelative), true); - Utils.RunProcess(aapt, $"add {apkFile} {destRelative}", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"add {apkFile} {destRelative}", workingDir: OutputDir); } - Utils.RunProcess(aapt, $"add {apkFile} classes.dex", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"add {apkFile} classes.dex", workingDir: OutputDir); // 4. Align APK string alignedApk = Path.Combine(OutputDir, "bin", $"{ProjectName}.apk"); - Utils.RunProcess(zipalign, $"-v 4 {apkFile} {alignedApk}", workingDir: OutputDir); + Utils.RunProcess(logger, zipalign, $"-v 4 {apkFile} {alignedApk}", workingDir: OutputDir); // we don't need the unaligned one any more File.Delete(apkFile); // 5. Generate key (if needed) & sign the apk SignApk(alignedApk, apksigner); - Utils.LogInfo($"\nAPK size: {(new FileInfo(alignedApk).Length / 1000_000.0):0.#} Mb.\n"); + logger.LogMessage(MessageImportance.High, $"\nAPK size: {(new FileInfo(alignedApk).Length / 1000_000.0):0.#} Mb.\n"); return (alignedApk, packageId); } @@ -460,7 +468,7 @@ public class ApkBuilder if (!File.Exists(signingKey)) { - Utils.RunProcess("keytool", "-genkey -v -keystore debug.keystore -storepass android -alias " + + Utils.RunProcess(logger, "keytool", "-genkey -v -keystore debug.keystore -storepass android -alias " + "androiddebugkey -keypass android -keyalg RSA -keysize 2048 -noprompt " + "-dname \"CN=Android Debug,O=Android,C=US\"", workingDir: OutputDir, silent: true); } @@ -468,7 +476,7 @@ public class ApkBuilder { File.Copy(signingKey, Path.Combine(OutputDir, "debug.keystore")); } - Utils.RunProcess(apksigner, $"sign --min-sdk-version {MinApiLevel} --ks debug.keystore " + + Utils.RunProcess(logger, apksigner, $"sign --min-sdk-version {MinApiLevel} --ks debug.keystore " + $"--ks-pass pass:android --key-pass pass:android {apkPath}", workingDir: OutputDir); } @@ -499,8 +507,8 @@ public class ApkBuilder if (!File.Exists(apkPath)) throw new Exception($"{apkPath} was not found"); - Utils.RunProcess(aapt, $"remove -v bin/{Path.GetFileName(apkPath)} {file}", workingDir: OutputDir); - Utils.RunProcess(aapt, $"add -v bin/{Path.GetFileName(apkPath)} {file}", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"remove -v bin/{Path.GetFileName(apkPath)} {file}", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"add -v bin/{Path.GetFileName(apkPath)} {file}", workingDir: OutputDir); // we need to re-sign the apk SignApk(apkPath, apksigner); diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 259622da707..465c8845209 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -194,8 +194,6 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public override bool Execute() { - Utils.Logger = Log; - if (string.IsNullOrEmpty(CompilerBinaryPath)) { throw new ArgumentException($"'{nameof(CompilerBinaryPath)}' is required.", nameof(CompilerBinaryPath)); @@ -545,15 +543,25 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task Log.LogMessage(MessageImportance.Low, $"AOT compiler arguments: {responseFileContent}"); + string args = $"--response=\"{responseFilePath}\""; + + // Log the command in a compact format which can be copy pasted + StringBuilder envStr = new StringBuilder(string.Empty); + foreach (KeyValuePair kvp in envVariables) + envStr.Append($"{kvp.Key}={kvp.Value} "); + Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}{CompilerBinaryPath} {args}"); + try { // run the AOT compiler - (int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath, - $"--response=\"{responseFilePath}\"", + (int exitCode, string output) = Utils.TryRunProcess(Log, + CompilerBinaryPath, + args, envVariables, assemblyDir, silent: false, - debugMessageImportance: MessageImportance.Low); + debugMessageImportance: MessageImportance.Low, + label: assembly); if (exitCode != 0) { Log.LogError($"Precompiling failed for {assembly}: {output}"); diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 0b20d27f388..a0637d8d845 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -153,7 +153,6 @@ public class AppleAppBuilderTask : Task public override bool Execute() { - Utils.Logger = Log; bool isDevice = (TargetOS == TargetNames.iOS || TargetOS == TargetNames.tvOS); if (!File.Exists(Path.Combine(AppDir, MainLibraryFileName))) @@ -227,7 +226,7 @@ public class AppleAppBuilderTask : Task if (GenerateXcodeProject) { - Xcode generator = new Xcode(TargetOS, Arch); + Xcode generator = new Xcode(Log, TargetOS, Arch); generator.EnableRuntimeLogging = EnableRuntimeLogging; generator.DiagnosticPorts = DiagnosticPorts; @@ -239,7 +238,7 @@ public class AppleAppBuilderTask : Task if (isDevice && string.IsNullOrEmpty(DevTeamProvisioning)) { // DevTeamProvisioning shouldn't be empty for arm64 builds - Utils.LogInfo("DevTeamProvisioning is not set, BuildAppBundle step is skipped."); + Log.LogMessage(MessageImportance.High, "DevTeamProvisioning is not set, BuildAppBundle step is skipped."); } else { diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index b79387da224..2e938d8ea0e 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; internal class Xcode { @@ -13,27 +15,29 @@ internal class Xcode private string SysRoot { get; set; } private string Target { get; set; } private string XcodeArch { get; set; } + private TaskLoggingHelper Logger { get; set; } - public Xcode(string target, string arch) + public Xcode(TaskLoggingHelper logger, string target, string arch) { + Logger = logger; Target = target; XcodeArch = (arch == "x64") ? "x86_64" : arch; switch (Target) { case TargetNames.iOS: - SysRoot = Utils.RunProcess("xcrun", "--sdk iphoneos --show-sdk-path"); + SysRoot = Utils.RunProcess(Logger, "xcrun", "--sdk iphoneos --show-sdk-path"); break; case TargetNames.iOSsim: - SysRoot = Utils.RunProcess("xcrun", "--sdk iphonesimulator --show-sdk-path"); + SysRoot = Utils.RunProcess(Logger, "xcrun", "--sdk iphonesimulator --show-sdk-path"); break; case TargetNames.tvOS: - SysRoot = Utils.RunProcess("xcrun", "--sdk appletvos --show-sdk-path"); + SysRoot = Utils.RunProcess(Logger, "xcrun", "--sdk appletvos --show-sdk-path"); break; case TargetNames.tvOSsim: - SysRoot = Utils.RunProcess("xcrun", "--sdk appletvsimulator --show-sdk-path"); + SysRoot = Utils.RunProcess(Logger, "xcrun", "--sdk appletvsimulator --show-sdk-path"); break; default: - SysRoot = Utils.RunProcess("xcrun", "--sdk macosx --show-sdk-path"); + SysRoot = Utils.RunProcess(Logger, "xcrun", "--sdk macosx --show-sdk-path"); break; } @@ -145,7 +149,7 @@ internal class Xcode // if lib doesn't exist (primarly due to runtime build without static lib support), fallback linking stub lib. if (!File.Exists(componentLibToLink)) { - Utils.LogInfo($"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); + Logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); componentLibToLink = staticComponentStubLib; } @@ -298,7 +302,7 @@ internal class Xcode .Replace("//%APPLE_RUNTIME_IDENTIFIER%", RuntimeIdentifier) .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib))); - Utils.RunProcess("cmake", cmakeArgs.ToString(), workingDir: binDir); + Utils.RunProcess(Logger, "cmake", cmakeArgs.ToString(), workingDir: binDir); return Path.Combine(binDir, projectName, projectName + ".xcodeproj"); } @@ -391,7 +395,7 @@ internal class Xcode string config = optimized ? "Release" : "Debug"; args.Append(" -configuration ").Append(config); - Utils.RunProcess("xcodebuild", args.ToString(), workingDir: Path.GetDirectoryName(xcodePrjPath)); + Utils.RunProcess(Logger, "xcodebuild", args.ToString(), workingDir: Path.GetDirectoryName(xcodePrjPath)); string appPath = Path.Combine(Path.GetDirectoryName(xcodePrjPath)!, config + "-" + sdk, Path.GetFileNameWithoutExtension(xcodePrjPath) + ".app"); @@ -400,7 +404,7 @@ internal class Xcode .EnumerateFiles("*", SearchOption.AllDirectories) .Sum(file => file.Length); - Utils.LogInfo($"\nAPP size: {(appSize / 1000_000.0):0.#} Mb.\n"); + Logger.LogMessage(MessageImportance.High, $"\nAPP size: {(appSize / 1000_000.0):0.#} Mb.\n"); return appPath; } diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 8756a3156c2..3376b4d5e7b 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -22,10 +22,11 @@ internal static class Utils return reader.ReadToEnd(); } - public static (int exitCode, string output) RunShellCommand(string command, + public static (int exitCode, string output) RunShellCommand( + TaskLoggingHelper logger, + string command, IDictionary envVars, string workingDir, - TaskLoggingHelper logger, bool silent=false, bool logStdErrAsMessage=false, MessageImportance debugMessageImportance=MessageImportance.Low, @@ -37,16 +38,16 @@ internal static class Utils : ("/bin/sh", $"\"{scriptFileName}\""); string msgPrefix = label == null ? string.Empty : $"[{label}] "; - LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:", msgPrefix); - LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName), msgPrefix); + logger.LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:", msgPrefix); + logger.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName), msgPrefix); - return TryRunProcess(shell, + return TryRunProcess(logger, + shell, args, envVars, workingDir, silent: silent, logStdErrAsMessage: logStdErrAsMessage, - logger: logger, label: label, debugMessageImportance: debugMessageImportance); @@ -74,6 +75,7 @@ internal static class Utils } public static string RunProcess( + TaskLoggingHelper logger, string path, string args = "", IDictionary? envVars = null, @@ -83,12 +85,12 @@ internal static class Utils MessageImportance debugMessageImportance=MessageImportance.High) { (int exitCode, string output) = TryRunProcess( + logger, path, args, envVars, workingDir, silent: silent, - logger: Logger, debugMessageImportance: debugMessageImportance); if (exitCode != 0 && !ignoreErrors) @@ -98,20 +100,18 @@ internal static class Utils } public static (int, string) TryRunProcess( + TaskLoggingHelper logger, string path, string args = "", IDictionary? envVars = null, string? workingDir = null, bool silent = true, bool logStdErrAsMessage = false, - TaskLoggingHelper? logger = null, MessageImportance debugMessageImportance=MessageImportance.High, string? label=null) { - Logger = logger; - string msgPrefix = label == null ? string.Empty : $"[{label}] "; - LogMessage(debugMessageImportance, $"Running: {path} {args}", msgPrefix); + logger.LogMessage(debugMessageImportance, $"Running: {path} {args}", msgPrefix); var outputBuilder = new StringBuilder(); var processStartInfo = new ProcessStartInfo { @@ -126,17 +126,17 @@ internal static class Utils if (workingDir != null) processStartInfo.WorkingDirectory = workingDir; - LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}", msgPrefix); + logger.LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}", msgPrefix); if (envVars != null) { if (envVars.Count > 0) - LogMessage(MessageImportance.Low, $"Setting environment variables for execution:", msgPrefix); + logger.LogMessage(MessageImportance.Low, $"Setting environment variables for execution:", msgPrefix); foreach (KeyValuePair envVar in envVars) { processStartInfo.EnvironmentVariables[envVar.Key] = envVar.Value; - Logger?.LogMessage(MessageImportance.Low, $"{msgPrefix}\t{envVar.Key} = {envVar.Value}"); + logger.LogMessage(MessageImportance.Low, $"{msgPrefix}\t{envVar.Key} = {envVar.Value}"); } } @@ -155,9 +155,9 @@ internal static class Utils if (!silent) { if (logStdErrAsMessage) - LogMessage(debugMessageImportance, e.Data, msgPrefix); + logger.LogMessage(debugMessageImportance, e.Data, msgPrefix); else - Logger?.LogWarning(msg); + logger.LogWarning(msg); } outputBuilder.AppendLine(e.Data); } @@ -170,7 +170,7 @@ internal static class Utils return; if (!silent) - LogMessage(debugMessageImportance, e.Data, msgPrefix); + logger.LogMessage(debugMessageImportance, e.Data, msgPrefix); outputBuilder.AppendLine(e.Data); } }; @@ -178,7 +178,7 @@ internal static class Utils process.BeginErrorReadLine(); process.WaitForExit(); - Logger?.LogMessage(debugMessageImportance, $"{msgPrefix}Exit code: {process.ExitCode}"); + logger.LogMessage(debugMessageImportance, $"{msgPrefix}Exit code: {process.ExitCode}"); return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); } @@ -223,24 +223,4 @@ internal static class Utils } } #endif - - public static TaskLoggingHelper? Logger { get; set; } - - public static void LogWarning(string? msg) - { - if (msg != null) - Logger?.LogWarning(msg); - } - - public static void LogInfo(string? msg, MessageImportance importance=MessageImportance.High) - { - if (msg != null) - Logger?.LogMessage(importance, msg); - } - - public static void LogMessage(MessageImportance importance, string? msg, string prefix="") - { - if (msg != null) - Logger?.LogMessage(importance, $"{prefix}{msg}"); - } } diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index fa6f2c9c3ca..89bea79454a 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -120,10 +120,10 @@ namespace Microsoft.WebAssembly.Build.Tasks envStr.Append($"{key}={envVarsDict[key]} "); Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}{command}"); (int exitCode, string output) = Utils.RunShellCommand( + Log, command, envVarsDict, workingDir: Environment.CurrentDirectory, - logger: Log, logStdErrAsMessage: true, debugMessageImportance: MessageImportance.High, label: Path.GetFileName(srcFile)); diff --git a/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs index c76659d5db3..4b4a283dc65 100644 --- a/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs +++ b/src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs @@ -34,8 +34,6 @@ namespace Microsoft.Workload.Build.Tasks public override bool Execute() { - Utils.Logger = Log; - if (!HasMetadata(WorkloadId, nameof(WorkloadId), "Version") || !HasMetadata(WorkloadId, nameof(WorkloadId), "ManifestName")) { @@ -59,6 +57,7 @@ namespace Microsoft.Workload.Build.Tasks Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** workload install **{Environment.NewLine}"); (int exitCode, string output) = Utils.TryRunProcess( + Log, Path.Combine(SdkDir, "dotnet"), $"workload install --skip-manifest-update --no-cache --configfile \"{nugetConfigPath}\" {WorkloadId.ItemSpec}", workingDir: Path.GetTempPath(), diff --git a/src/tasks/WorkloadBuildTasks/PackageInstaller.cs b/src/tasks/WorkloadBuildTasks/PackageInstaller.cs index f6d4e85cf76..227dcb4fbf9 100644 --- a/src/tasks/WorkloadBuildTasks/PackageInstaller.cs +++ b/src/tasks/WorkloadBuildTasks/PackageInstaller.cs @@ -60,7 +60,7 @@ namespace Microsoft.Workload.Build.Tasks _logger.LogMessage(MessageImportance.Low, $"Restoring packages: {string.Join(", ", references.Select(r => $"{r.Name}/{r.Version}"))}"); string args = $"restore \"{projectPath}\" /p:RestorePackagesPath=\"{_packagesDir}\""; - (int exitCode, string output) = Utils.TryRunProcess("dotnet", args, silent: false, debugMessageImportance: MessageImportance.Low); + (int exitCode, string output) = Utils.TryRunProcess(_logger, "dotnet", args, silent: false, debugMessageImportance: MessageImportance.Low); if (exitCode != 0) { LogErrorOrWarning($"Restoring packages failed with exit code: {exitCode}. Output:{Environment.NewLine}{output}", stopOnMissing); From 5da7094bb1a2a0b957b17c83c67ce7c1c1eb5052 Mon Sep 17 00:00:00 2001 From: Maxim Lipnin Date: Mon, 19 Jul 2021 09:09:35 +0300 Subject: [PATCH 659/926] Disable some library tests due to failures/crash (#55826) --- .../tests/Enumeration/ErrorHandlingTests.cs | 1 + .../System.IO.FileSystem/tests/Enumeration/RootTests.cs | 1 + src/libraries/tests.proj | 3 +++ 3 files changed, 5 insertions(+) diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.cs index 7d45d65f66d..08f7279d4d0 100644 --- a/src/libraries/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.cs @@ -82,6 +82,7 @@ namespace System.IO.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55821", TestPlatforms.Android)] public void DeleteDirectoryAfterOpening() { // We shouldn't prevent the directory from being deleted, even though we've diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/RootTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/RootTests.cs index cea744085f7..e6925ccb8f2 100644 --- a/src/libraries/System.IO.FileSystem/tests/Enumeration/RootTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/RootTests.cs @@ -31,6 +31,7 @@ namespace System.IO.Tests.Enumeration } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55821", TestPlatforms.Android)] public void CanRecurseFromRoot() { string root = Path.GetPathRoot(Path.GetTempPath()); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 7fa4235510f..de7976410e6 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -52,6 +52,9 @@ + + + From a47b8dca77fe6b5f9514cfced139b00a8243e5a1 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 19 Jul 2021 10:49:06 +0300 Subject: [PATCH 660/926] Fix mono build on illumos (#55897) --- src/mono/CMakeLists.txt | 8 ++++++++ src/mono/cmake/configure.cmake | 13 ++++++++++++- src/mono/mono.proj | 7 +++++++ src/mono/mono/mini/CMakeLists.txt | 4 +++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 4c25d97b7a5..e7a494c22cc 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -252,6 +252,9 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") add_compile_options(/GL) # whole program optimization add_link_options(/LTCG) # link-time code generation endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + set(HOST_SOLARIS 1) + add_definitions(-DGC_SOLARIS_THREADS -DGC_SOLARIS_PTHREADS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -DUSE_MMAP -DUSE_MUNMAP -DHOST_SOLARIS -D__EXTENSIONS__ -D_XPG4_2) else() message(FATAL_ERROR "Host '${CMAKE_SYSTEM_NAME}' not supported.") endif() @@ -290,6 +293,8 @@ elseif(TARGET_SYSTEM_NAME STREQUAL "Emscripten") endif() elseif(TARGET_SYSTEM_NAME STREQUAL "Windows") set(TARGET_WIN32 1) +elseif(TARGET_SYSTEM_NAME STREQUAL "SunOS") + set(TARGET_SOLARIS 1) else() message(FATAL_ERROR "Target '${TARGET_SYSTEM_NAME}' not supported.") endif() @@ -589,6 +594,9 @@ elseif(HOST_LINUX) elseif(HOST_WIN32) set(ICU_FLAGS "-DTARGET_WINDOWS -DPALEXPORT=EXTERN_C") set(HAVE_SYS_ICU 1) +elseif(HOST_SOLARIS) + set(ICU_FLAGS "-DPALEXPORT=\"\" -DTARGET_UNIX -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option") + set(HAVE_SYS_ICU 1) else() message(FATAL_ERROR "Unknown host") endif() diff --git a/src/mono/cmake/configure.cmake b/src/mono/cmake/configure.cmake index 34fa15f556c..b3d3f47c894 100644 --- a/src/mono/cmake/configure.cmake +++ b/src/mono/cmake/configure.cmake @@ -16,6 +16,10 @@ if(C_SUPPORTS_WUNGUARDED_AVAILABILITY) set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unguarded-availability") endif() +if(HOST_SOLARIS) + set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DGC_SOLARIS_THREADS -DGC_SOLARIS_PTHREADS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -DUSE_MMAP -DUSE_MUNMAP -DHOST_SOLARIS -D__EXTENSIONS__ -D_XPG4_2") +endif() + function(ac_check_headers) foreach(arg ${ARGN}) check_include_file ("${arg}" FOUND_${arg}) @@ -66,7 +70,7 @@ ac_check_headers ( ac_check_funcs ( sigaction kill clock_nanosleep kqueue backtrace_symbols mkstemp mmap - madvise getrusage dladdr sysconf getrlimit prctl nl_langinfo + getrusage dladdr sysconf getrlimit prctl nl_langinfo sched_getaffinity sched_setaffinity getpwuid_r readlink chmod lstat getdtablesize ftruncate msync getpeername utime utimes openlog closelog atexit popen strerror_r inet_pton inet_aton shm_open poll getfsstat mremap posix_fadvise vsnprintf sendfile statfs statvfs setpgid system @@ -89,6 +93,7 @@ ac_check_funcs( pthread_attr_setstacksize pthread_get_stackaddr_np ) +check_symbol_exists(madvise "sys/mman.h" HAVE_MADVISE) check_symbol_exists(pthread_mutexattr_setprotocol "pthread.h" HAVE_DECL_PTHREAD_MUTEXATTR_SETPROTOCOL) check_symbol_exists(CLOCK_MONOTONIC "time.h" HAVE_CLOCK_MONOTONIC) check_symbol_exists(CLOCK_MONOTONIC_COARSE "time.h" HAVE_CLOCK_MONOTONIC_COARSE) @@ -164,3 +169,9 @@ endif() if(HOST_BROWSER) set(HAVE_FORK 0) endif() + +if(HOST_SOLARIS) + set(HAVE_GETPROTOBYNAME 1) + set(HAVE_NETINET_TCP_H 1) + set(HAVE_GETADDRINFO 1) +endif() diff --git a/src/mono/mono.proj b/src/mono/mono.proj index a329c0ac299..8b1fb3b13a4 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -229,6 +229,13 @@ <_MonoBuildEnv Condition="'$(Platform)' == 'arm'" Include="PKG_CONFIG_PATH=$(MonoCrossDir)/usr/lib/arm-linux-gnueabihf/pkgconfig" /> + + + <_MonoCMakeArgs Include="-DCMAKE_TOOLCHAIN_FILE=$([MSBuild]::NormalizePath('$(RepositoryEngineeringDir)', 'common', 'cross', 'toolchain.cmake'))" /> + <_MonoBuildEnv Include="TARGET_BUILD_ARCH=x64" /> + <_MonoBuildEnv Include="PKG_CONFIG_PATH=$(MonoCrossDir)/lib/pkgconfig" /> + + <_MonoCMakeArgs Include="-DCMAKE_TOOLCHAIN_FILE=$([MSBuild]::NormalizePath('$(RepositoryEngineeringDir)', 'common', 'cross', 'toolchain.cmake'))" /> diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 841bff5f566..b07e5856916 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -33,6 +33,8 @@ elseif(HOST_LINUX) set(OS_LIBS pthread m dl) elseif(HOST_WIN32) set(OS_LIBS bcrypt.lib Mswsock.lib ws2_32.lib psapi.lib version.lib advapi32.lib winmm.lib kernel32.lib) +elseif(HOST_SOLARIS) + set(OS_LIBS socket pthread m ${CMAKE_DL_LIBS}) endif() # @@ -253,7 +255,7 @@ set(posix_sources if(HOST_DARWIN) set(os_sources "${darwin_sources};${posix_sources}") -elseif(HOST_LINUX) +elseif(HOST_LINUX OR HOST_SOLARIS) set(os_sources "${posix_sources}") elseif(HOST_WIN32) set(os_sources "${windows_sources}") From f7e4c261815c66fde2c1e750b744f193e236c17e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 19 Jul 2021 10:01:44 +0200 Subject: [PATCH 661/926] Add --debuginfo to superpmi (#55808) --- src/coreclr/scripts/superpmi.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index c516ec6c3a2..eb1defc6ba7 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -308,6 +308,7 @@ asm_diff_parser.add_argument("-base_git_hash", help="Use this git hash as the ba asm_diff_parser.add_argument("--diff_jit_dump", action="store_true", help="Generate JitDump output for diffs. Default: only generate asm, not JitDump.") asm_diff_parser.add_argument("-temp_dir", help="Specify a temporary directory used for a previous ASM diffs run (for which --skip_cleanup was used) to view the results. The replay command is skipped.") asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump/COMPlus_NgenGCDump; requires instructions to be prefixed by offsets).") +asm_diff_parser.add_argument("--debuginfo", action="store_true", help="Include debug info after disassembly (sets COMPlus_JitDebugDump/COMPlus_NgenDebugDump).") asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baseline JIT. Format is key=value, where key is the option name without leading COMPlus_...") asm_diff_parser.add_argument("-diff_jit_option", action="append", help="Option to pass to the diff JIT. Format is key=value, where key is the option name without leading COMPlus_...") asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed") @@ -1718,6 +1719,11 @@ class SuperPMIReplayAsmDiffs: "COMPlus_JitGCDump": "*", "COMPlus_NgenGCDump": "*" }) + if self.coreclr_args.debuginfo: + asm_complus_vars.update({ + "COMPlus_JitDebugDump": "*", + "COMPlus_NgenDebugDump": "*" }) + jit_dump_complus_vars = asm_complus_vars.copy() jit_dump_complus_vars.update({ "COMPlus_JitDump": "*", @@ -3635,6 +3641,11 @@ def setup_args(args): lambda unused: True, "Unable to set gcinfo.") + coreclr_args.verify(args, + "debuginfo", + lambda unused: True, + "Unable to set debuginfo.") + coreclr_args.verify(args, "diff_jit_dump", lambda unused: True, From b5ef659c04a90963d061ca40aef8913d7ebd4f29 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 19 Jul 2021 10:37:27 -0400 Subject: [PATCH 662/926] Fix WriteGatherAtOffset IOVector count (#55909) --- .../System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index b82627112f3..f98e9874bd4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -148,7 +148,7 @@ namespace System.IO totalBytesToWrite += buffer.Length; MemoryHandle memoryHandle = buffer.Pin(); - vectors[i] = new Interop.Sys.IOVector { Base = firstBufferOffset + (byte*)memoryHandle.Pointer, Count = (UIntPtr)buffer.Length }; + vectors[i] = new Interop.Sys.IOVector { Base = firstBufferOffset + (byte*)memoryHandle.Pointer, Count = (UIntPtr)(buffer.Length - firstBufferOffset) }; handles[i] = memoryHandle; firstBufferOffset = 0; From 1d144234376edb7206058d7d9229f84ce24a70aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Mon, 19 Jul 2021 14:38:30 +0000 Subject: [PATCH 663/926] Comments from code review (#55819) * Comments * Update src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs * Update src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs Co-authored-by: Natalia Kondratyeva Co-authored-by: Natalia Kondratyeva --- .../Net/Quic/Implementations/MsQuic/MsQuicStream.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 0c29a9285d6..d5792c9f0a8 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -18,6 +18,7 @@ namespace System.Net.Quic.Implementations.MsQuic // Delegate that wraps the static function that will be called when receiving an event. internal static readonly StreamCallbackDelegate s_streamDelegate = new StreamCallbackDelegate(NativeCallbackHandler); + // The state is passed to msquic and then it's passed back by msquic to the callback handler. private readonly State _state = new State(); private readonly bool _canRead; @@ -31,6 +32,8 @@ namespace System.Net.Quic.Implementations.MsQuic private sealed class State { public SafeMsQuicStreamHandle Handle = null!; // set in ctor. + // Roots the state in GC and it won't get collected while this exist. + // It must be kept alive until we receive SHUTDOWN_COMPLETE event public GCHandle StateGCHandle; public MsQuicStream? Stream; // roots the stream in the pinned state to prevent GC during an async read I/O. @@ -71,6 +74,7 @@ namespace System.Net.Quic.Implementations.MsQuic public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); public ShutdownState ShutdownState; + // The value makes sure that we release the handles only once. public int ShutdownDone; // Set once stream have been shutdown. @@ -769,6 +773,10 @@ namespace System.Net.Quic.Implementations.MsQuic QuicExceptionHelpers.ThrowIfFailed(status, "StreamReceiveSetEnabled failed."); } + /// + /// Callback calls for a single instance of a stream are serialized by msquic. + /// They happen on a msquic thread and shouldn't take too long to not to block msquic. + /// private static uint NativeCallbackHandler( IntPtr stream, IntPtr context, From ca3b27963931172f0535fff644e1fae23f406193 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Mon, 19 Jul 2021 15:39:25 +0100 Subject: [PATCH 664/926] Delete IDE dispose analyzer rules (#55920) The IDE dispose analyzer rules have been deleted from Roslyn: https://github.com/dotnet/roslyn/commit/eeba499ecf839ec35bca25062d69d2fc5c4885b9 --- eng/CodeAnalysis.ruleset | 3 --- eng/CodeAnalysis.test.ruleset | 3 --- 2 files changed, 6 deletions(-) diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index 133252bae8f..0be33b74564 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -478,9 +478,6 @@ - - - diff --git a/eng/CodeAnalysis.test.ruleset b/eng/CodeAnalysis.test.ruleset index 1be671aaa3a..285b9ff6bbf 100644 --- a/eng/CodeAnalysis.test.ruleset +++ b/eng/CodeAnalysis.test.ruleset @@ -478,9 +478,6 @@ - - - From b120764b0e5cc73247e87e67c75167ac48109d67 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 19 Jul 2021 07:41:13 -0700 Subject: [PATCH 665/926] Wait for the request line before cancelling the request in EventSource_UnsuccessfulRequest_LogsStartFailedStop (#55913) --- .../System.Net.Http/tests/FunctionalTests/TelemetryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs index 04328257f01..9cbba2ad038 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs @@ -272,10 +272,10 @@ namespace System.Net.Http.Functional.Tests { await server.AcceptConnectionAsync(async connection => { + await connection.ReadRequestDataAsync(); await WaitForEventCountersAsync(events); cts.Cancel(); Assert.True(await semaphore.WaitAsync(TimeSpan.FromSeconds(30))); - connection.Dispose(); }); }); From 9796891f38673a391c51fad2ae592f4048f232a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Mon, 19 Jul 2021 15:14:34 +0000 Subject: [PATCH 666/926] disabled test (#55912) --- .../tests/FunctionalTests/HttpClientHandlerTest.Http3.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index fe784a9c8d0..80c97b15e6c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -167,6 +167,7 @@ namespace System.Net.Http.Functional.Tests [InlineData(10)] [InlineData(100)] [InlineData(1000)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55901")] public async Task SendMoreThanStreamLimitRequestsConcurrently_LastWaits(int streamLimit) { // This combination leads to a hang manifesting in CI only. Disabling it until there's more time to investigate. From d05950ba1ad440855f9851857bbad6f9d1a34321 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 19 Jul 2021 08:39:28 -0700 Subject: [PATCH 667/926] JIT: refactor how opaque VNs are represented (#55853) Use a unary function to capture opaque VN loop dependence, rather than factoring that out as part of the owning chunk. As a result the VN chunk data no longer has a per-loop component (no dependence on MAX_LOOP_NUM). This gives us the flexibility to address #54118 by doing something similar for `MapUpdate` VNs. --- src/coreclr/jit/optimizer.cpp | 20 +--- src/coreclr/jit/valuenum.cpp | 173 ++++++++++++++++++-------------- src/coreclr/jit/valuenum.h | 28 ++---- src/coreclr/jit/valuenumfuncs.h | 1 + 4 files changed, 115 insertions(+), 107 deletions(-) diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 3f8e1ee99cb..340dd64e7e6 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -6567,6 +6567,11 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* loo BasicBlock* defnBlk = reinterpret_cast(vnStore->ConstantValue(funcApp.m_args[0])); res = !optLoopContains(lnum, defnBlk->bbNatLoopNum); } + else if (funcApp.m_func == VNF_MemOpaque) + { + const unsigned vnLoopNum = funcApp.m_args[0]; + res = !optLoopContains(lnum, vnLoopNum); + } else { for (unsigned i = 0; i < funcApp.m_arity; i++) @@ -6582,21 +6587,6 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* loo } } } - else - { - // Non-function "new, unique" VN's may be annotated with the loop nest where - // their definition occurs. - BasicBlock::loopNumber vnLoopNum = vnStore->LoopOfVN(vn); - - if (vnLoopNum == BasicBlock::MAX_LOOP_NUM) - { - res = false; - } - else - { - res = !optLoopContains(lnum, vnLoopNum); - } - } loopVnInvariantCache->Set(vn, res); return res; diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index abda97b17e3..8bc2ace8f6b 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -454,7 +454,7 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc) // We have no current allocation chunks. for (unsigned i = 0; i < TYP_COUNT; i++) { - for (unsigned j = CEA_None; j <= CEA_Count + BasicBlock::MAX_LOOP_NUM; j++) + for (unsigned j = CEA_Const; j <= CEA_Count; j++) { m_curAllocChunk[i][j] = NoChunk; } @@ -466,8 +466,7 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc) } // We will reserve chunk 0 to hold some special constants, like the constant NULL, the "exception" value, and the // "zero map." - Chunk* specialConstChunk = - new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const, BasicBlock::MAX_LOOP_NUM); + Chunk* specialConstChunk = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const); specialConstChunk->m_numUsed += SRC_NumSpecialRefConsts; // Implicitly allocate 0 ==> NULL, and 1 ==> Exception, 2 ==> ZeroMap. ChunkNum cn = m_chunks.Push(specialConstChunk); @@ -1552,17 +1551,12 @@ bool ValueNumStore::IsSharedStatic(ValueNum vn) return GetVNFunc(vn, &funcAttr) && (s_vnfOpAttribs[funcAttr.m_func] & VNFOA_SharedStatic) != 0; } -ValueNumStore::Chunk::Chunk(CompAllocator alloc, - ValueNum* pNextBaseVN, - var_types typ, - ChunkExtraAttribs attribs, - BasicBlock::loopNumber loopNum) - : m_defs(nullptr), m_numUsed(0), m_baseVN(*pNextBaseVN), m_typ(typ), m_attribs(attribs), m_loopNum(loopNum) +ValueNumStore::Chunk::Chunk(CompAllocator alloc, ValueNum* pNextBaseVN, var_types typ, ChunkExtraAttribs attribs) + : m_defs(nullptr), m_numUsed(0), m_baseVN(*pNextBaseVN), m_typ(typ), m_attribs(attribs) { // Allocate "m_defs" here, according to the typ/attribs pair. switch (attribs) { - case CEA_None: case CEA_NotAField: break; // Nothing to do. case CEA_Const: @@ -1619,26 +1613,11 @@ ValueNumStore::Chunk::Chunk(CompAllocator alloc, *pNextBaseVN += ChunkSize; } -ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, - ChunkExtraAttribs attribs, - BasicBlock::loopNumber loopNum) +ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, ChunkExtraAttribs attribs) { Chunk* res; - unsigned index; - if (loopNum == BasicBlock::MAX_LOOP_NUM) - { - // Loop nest is unknown/irrelevant for this VN. - index = attribs; - } - else - { - // Loop nest is interesting. Since we know this is only true for unique VNs, we know attribs will - // be CEA_None and can just index based on loop number. - noway_assert(attribs == CEA_None); - // Map NOT_IN_LOOP -> BasicBlock::MAX_LOOP_NUM to make the index range contiguous [0..BasicBlock::MAX_LOOP_NUM] - index = CEA_Count + (loopNum == BasicBlock::NOT_IN_LOOP ? BasicBlock::MAX_LOOP_NUM : loopNum); - } - ChunkNum cn = m_curAllocChunk[typ][index]; + unsigned index = attribs; + ChunkNum cn = m_curAllocChunk[typ][index]; if (cn != NoChunk) { res = m_chunks.Get(cn); @@ -1648,7 +1627,7 @@ ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, } } // Otherwise, must allocate a new one. - res = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, typ, attribs, loopNum); + res = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, typ, attribs); cn = m_chunks.Push(res); m_curAllocChunk[typ][index] = cn; return res; @@ -1782,10 +1761,13 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags) } else { - Chunk* c = GetAllocChunk(TYP_I_IMPL, CEA_Handle); - unsigned offsetWithinChunk = c->AllocVN(); - res = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = handle; + Chunk* const c = GetAllocChunk(TYP_I_IMPL, CEA_Handle); + unsigned const offsetWithinChunk = c->AllocVN(); + VNHandle* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = handle; + res = c->m_baseVN + offsetWithinChunk; + GetHandleMap()->Set(handle, res); return res; } @@ -1886,10 +1868,12 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func) if (!GetVNFunc0Map()->Lookup(func, &resultVN)) { // Allocate a new ValueNum for 'func' - Chunk* c = GetAllocChunk(typ, CEA_Func0); - unsigned offsetWithinChunk = c->AllocVN(); - resultVN = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = func; + Chunk* const c = GetAllocChunk(typ, CEA_Func0); + unsigned const offsetWithinChunk = c->AllocVN(); + VNFunc* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = func; + resultVN = c->m_baseVN + offsetWithinChunk; GetVNFunc0Map()->Set(func, resultVN); } return resultVN; @@ -1911,6 +1895,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func) // ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN) { + assert(func != VNF_MemOpaque); assert(arg0VN == VNNormalValue(arg0VN)); // Arguments don't carry exceptions. // Try to perform constant-folding. @@ -1922,16 +1907,18 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN) ValueNum resultVN; // Have we already assigned a ValueNum for 'func'('arg0VN') ? - // VNDefFunc1Arg fstruct(func, arg0VN); if (!GetVNFunc1Map()->Lookup(fstruct, &resultVN)) { // Otherwise, Allocate a new ValueNum for 'func'('arg0VN') // - Chunk* c = GetAllocChunk(typ, CEA_Func1); - unsigned offsetWithinChunk = c->AllocVN(); - resultVN = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = fstruct; + Chunk* const c = GetAllocChunk(typ, CEA_Func1); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc1Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + resultVN = c->m_baseVN + offsetWithinChunk; + // Record 'resultVN' in the Func1Map GetVNFunc1Map()->Set(fstruct, resultVN); } @@ -2047,10 +2034,12 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V { // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN') // - Chunk* c = GetAllocChunk(typ, CEA_Func2); - unsigned offsetWithinChunk = c->AllocVN(); - resultVN = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = fstruct; + Chunk* const c = GetAllocChunk(typ, CEA_Func2); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc2Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + resultVN = c->m_baseVN + offsetWithinChunk; // Record 'resultVN' in the Func2Map GetVNFunc2Map()->Set(fstruct, resultVN); } @@ -2108,10 +2097,13 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V { // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN') // - Chunk* c = GetAllocChunk(typ, CEA_Func3); - unsigned offsetWithinChunk = c->AllocVN(); - resultVN = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = fstruct; + Chunk* const c = GetAllocChunk(typ, CEA_Func3); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc3Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + resultVN = c->m_baseVN + offsetWithinChunk; + // Record 'resultVN' in the Func3Map GetVNFunc3Map()->Set(fstruct, resultVN); } @@ -2156,10 +2148,13 @@ ValueNum ValueNumStore::VNForFunc( { // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN') // - Chunk* c = GetAllocChunk(typ, CEA_Func4); - unsigned offsetWithinChunk = c->AllocVN(); - resultVN = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = fstruct; + Chunk* const c = GetAllocChunk(typ, CEA_Func4); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc4Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + resultVN = c->m_baseVN + offsetWithinChunk; + // Record 'resultVN' in the Func4Map GetVNFunc4Map()->Set(fstruct, resultVN); } @@ -2465,10 +2460,13 @@ TailCall: if (!GetVNFunc2Map()->Lookup(fstruct, &res)) { // Otherwise, assign a new VN for the function application. - Chunk* c = GetAllocChunk(typ, CEA_Func2); - unsigned offsetWithinChunk = c->AllocVN(); - res = c->m_baseVN + offsetWithinChunk; - reinterpret_cast(c->m_defs)[offsetWithinChunk] = fstruct; + Chunk* const c = GetAllocChunk(typ, CEA_Func2); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc2Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + res = c->m_baseVN + offsetWithinChunk; + GetVNFunc2Map()->Set(fstruct, res); } return res; @@ -3675,12 +3673,17 @@ ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ) loopNum = block->bbNatLoopNum; } - // We always allocate a new, unique VN in this call. - // The 'typ' is used to partition the allocation of VNs into different chunks. - Chunk* c = GetAllocChunk(typ, CEA_None, loopNum); - unsigned offsetWithinChunk = c->AllocVN(); - ValueNum result = c->m_baseVN + offsetWithinChunk; - return result; + // VNForFunc(typ, func, vn) but bypasses looking in the cache + // + VNDefFunc1Arg fstruct(VNF_MemOpaque, loopNum); + Chunk* const c = GetAllocChunk(typ, CEA_Func1); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFunc1Arg* const chunkSlots = reinterpret_cast(c->m_defs); + + chunkSlots[offsetWithinChunk] = fstruct; + + ValueNum resultVN = c->m_baseVN + offsetWithinChunk; + return resultVN; } ValueNum ValueNumStore::VNApplySelectors(ValueNumKind vnk, @@ -4343,27 +4346,25 @@ var_types ValueNumStore::TypeOfVN(ValueNum vn) } //------------------------------------------------------------------------ -// LoopOfVN: If the given value number is an opaque one associated with a particular -// expression in the IR, give the loop number where the expression occurs; otherwise, -// returns BasicBlock::MAX_LOOP_NUM. +// LoopOfVN: If the given value number is VNF_MemOpaque, return +// the loop number where the memory update occurs, otherwise returns MAX_LOOP_NUM. // // Arguments: // vn - Value number to query // // Return Value: -// The correspondingblock's bbNatLoopNum, which may be BasicBlock::NOT_IN_LOOP. -// Returns BasicBlock::MAX_LOOP_NUM if this VN is not an opaque value number associated with -// a particular expression/location in the IR. - +// The memory loop number, which may be BasicBlock::NOT_IN_LOOP. +// Returns BasicBlock::MAX_LOOP_NUM if this VN is not a memory value number. +// BasicBlock::loopNumber ValueNumStore::LoopOfVN(ValueNum vn) { - if (vn == NoVN) + VNFuncApp funcApp; + if (GetVNFunc(vn, &funcApp) && (funcApp.m_func == VNF_MemOpaque)) { - return BasicBlock::MAX_LOOP_NUM; + return (BasicBlock::loopNumber)funcApp.m_args[0]; } - Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn)); - return c->m_loopNum; + return BasicBlock::MAX_LOOP_NUM; } bool ValueNumStore::IsVNConstant(ValueNum vn) @@ -5554,6 +5555,9 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr) case VNF_ValWithExc: vnDumpValWithExc(comp, &funcApp); break; + case VNF_MemOpaque: + vnDumpMemOpaque(comp, &funcApp); + break; #ifdef FEATURE_SIMD case VNF_SimdType: vnDumpSimdType(comp, &funcApp); @@ -5712,6 +5716,25 @@ void ValueNumStore::vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore) printf("]"); } +void ValueNumStore::vnDumpMemOpaque(Compiler* comp, VNFuncApp* memOpaque) +{ + assert(memOpaque->m_func == VNF_MemOpaque); // Precondition. + const unsigned loopNum = memOpaque->m_args[0]; + + if (loopNum == BasicBlock::NOT_IN_LOOP) + { + printf("MemOpaque:NotInLoop"); + } + else if (loopNum == BasicBlock::MAX_LOOP_NUM) + { + printf("MemOpaque:Indeterminate"); + } + else + { + printf("MemOpaque:L%02u", loopNum); + } +} + #ifdef FEATURE_SIMD void ValueNumStore::vnDumpSimdType(Compiler* comp, VNFuncApp* simdType) { diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 481622a8198..b64d19993fa 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -880,6 +880,10 @@ public: // Prints a representation of a MapStore operation on standard out. void vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore); + // Requires "memOpaque" to be a mem opaque VNFuncApp + // Prints a representation of a MemOpaque state on standard out. + void vnDumpMemOpaque(Compiler* comp, VNFuncApp* memOpaque); + // Requires "valWithExc" to be a value with an exeception set VNFuncApp. // Prints a representation of the exeception set on standard out. void vnDumpValWithExc(Compiler* comp, VNFuncApp* valWithExc); @@ -935,7 +939,6 @@ private: enum ChunkExtraAttribs : BYTE { - CEA_None, // No extra attributes. CEA_Const, // This chunk contains constant values. CEA_Handle, // This chunk contains handle constants. CEA_NotAField, // This chunk contains "not a field" values. @@ -961,18 +964,12 @@ private: ValueNum m_baseVN; // The common attributes of this chunk. - var_types m_typ; - ChunkExtraAttribs m_attribs; - BasicBlock::loopNumber m_loopNum; + var_types m_typ; + ChunkExtraAttribs m_attribs; - // Initialize a chunk, starting at "*baseVN", for the given "typ", "attribs", and "loopNum" (using "alloc" for - // allocations). + // Initialize a chunk, starting at "*baseVN", for the given "typ", and "attribs", using "alloc" for allocations. // (Increments "*baseVN" by ChunkSize.) - Chunk(CompAllocator alloc, - ValueNum* baseVN, - var_types typ, - ChunkExtraAttribs attribs, - BasicBlock::loopNumber loopNum); + Chunk(CompAllocator alloc, ValueNum* baseVN, var_types typ, ChunkExtraAttribs attribs); // Requires that "m_numUsed < ChunkSize." Returns the offset of the allocated VN within the chunk; the // actual VN is this added to the "m_baseVN" of the chunk. @@ -1121,17 +1118,14 @@ private: JitExpandArrayStack m_chunks; // These entries indicate the current allocation chunk, if any, for each valid combination of . Valid combinations require attribs==CEA_None or - // loopNum==BasicBlock::MAX_LOOP_NUM. + // ChunkExtraAttribute>. // If the value is NoChunk, it indicates that there is no current allocation chunk for that pair, otherwise // it is the index in "m_chunks" of a chunk with the given attributes, in which the next allocation should // be attempted. - ChunkNum m_curAllocChunk[TYP_COUNT][CEA_Count + BasicBlock::MAX_LOOP_NUM + 1]; + ChunkNum m_curAllocChunk[TYP_COUNT][CEA_Count + 1]; // Returns a (pointer to a) chunk in which a new value number may be allocated. - Chunk* GetAllocChunk(var_types typ, - ChunkExtraAttribs attribs, - BasicBlock::loopNumber loopNum = BasicBlock::MAX_LOOP_NUM); + Chunk* GetAllocChunk(var_types typ, ChunkExtraAttribs attribs); // First, we need mechanisms for mapping from constants to value numbers. // For small integers, we'll use an array. diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 2ddfff34dfa..af53a7b1e05 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -6,6 +6,7 @@ // ) // clang-format off +ValueNumFuncDef(MemOpaque, 1, false, false, false) // Args: 0: loop num ValueNumFuncDef(MapStore, 3, false, false, false) // Args: 0: map, 1: index (e. g. field handle), 2: value being stored. ValueNumFuncDef(MapSelect, 2, false, false, false) // Args: 0: map, 1: key. From ae7f47d456a1e5d8b0cb479f69d39a889412d25c Mon Sep 17 00:00:00 2001 From: Vitek Karas Date: Mon, 19 Jul 2021 18:54:03 +0200 Subject: [PATCH 668/926] Fixes building Host tests from VS (#55917) When building the Host tests projects from VS the CrossGen2 related properties are not set (probably because the build type is not set to Core - which doesn't matter for the tests). Changed the includes to be conditional. --- .../Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props index 28ac4374be2..3fe16b05bac 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props @@ -155,10 +155,10 @@ - + - + true From e0024e22c7761cbbdd10a845f8b0269ea654cbfc Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Mon, 19 Jul 2021 10:22:33 -0700 Subject: [PATCH 669/926] Switch handwritten test serializer contexts to use source generator (#55844) --- .../FunctionalTests/JsonContext/Int32.cs | 33 -- .../JsonContext/JsonContext.cs | 60 ---- .../FunctionalTests/JsonContext/Person.cs | 114 ------- .../FunctionalTests/JsonContext/String.cs | 33 -- ...stem.Net.Http.Json.Functional.Tests.csproj | 9 +- .../tests/FunctionalTests/TestClasses.cs | 5 + .../JsonContext/DateTimeOffset.cs | 33 -- .../MetadataTests/JsonContext/Dictionary.cs | 34 -- .../MetadataTests/JsonContext/HighLowTemps.cs | 82 ----- .../MetadataTests/JsonContext/Int32.cs | 33 -- .../JsonContext.GetJsonTypeInfo.cs | 21 -- .../MetadataTests/JsonContext/JsonContext.cs | 48 --- .../MetadataTests/JsonContext/List.cs | 34 -- .../MetadataTests/JsonContext/String.cs | 33 -- .../MetadataTests/JsonContext/StringArray.cs | 33 -- .../JsonContext/WeatherForecastWithPOCOs.cs | 162 --------- .../MetadataTests.JsonMetadataServices.cs | 308 ------------------ .../MetadataTests/MetadataTests.cs | 6 + .../System.Text.Json.Tests.csproj | 14 +- 19 files changed, 18 insertions(+), 1077 deletions(-) delete mode 100644 src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Int32.cs delete mode 100644 src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/JsonContext.cs delete mode 100644 src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs delete mode 100644 src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/String.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/DateTimeOffset.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Dictionary.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Int32.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.GetJsonTypeInfo.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/List.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/String.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/StringArray.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs delete mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Int32.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Int32.cs deleted file mode 100644 index 02e8bdf65d9..00000000000 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Int32.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Net.Http.Json.Functional.Tests -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _Int32; - public JsonTypeInfo Int32 - { - get - { - if (_Int32 == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(int))) != null) - { - _Int32 = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _Int32 = JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.Int32Converter); - } - } - - return _Int32; - } - } - } -} diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/JsonContext.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/JsonContext.cs deleted file mode 100644 index 56a4ff57c30..00000000000 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/JsonContext.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Net.Http.Json.Functional.Tests -{ - internal partial class JsonContext : JsonSerializerContext - { - private static JsonContext s_default; - public static JsonContext Default => s_default ??= new JsonContext(new JsonSerializerOptions()); - - public JsonContext() : base(null, null) - { - } - - public JsonContext(JsonSerializerOptions options) : base(options, null) - { - } - - private JsonConverter GetRuntimeProvidedCustomConverter(Type type) - { - IList converters = Options.Converters; - - for (int i = 0; i < converters.Count; i++) - { - JsonConverter converter = converters[i]; - - if (converter.CanConvert(type)) - { - if (converter is JsonConverterFactory factory) - { - converter = factory.CreateConverter(type, Options); - if (converter == null || converter is JsonConverterFactory) - { - throw new InvalidOperationException($"The converter '{factory.GetType()}' cannot return null or a JsonConverterFactory instance."); - } - } - - return converter; - } - } - - return null; - } - - public override JsonTypeInfo GetTypeInfo(Type type) - { - if (type == typeof(Person)) - { - return this.Person; - } - - return null!; - } - } -} diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs deleted file mode 100644 index 8049845e1a2..00000000000 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/Person.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Net.Http.Json.Functional.Tests -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _Person; - public JsonTypeInfo Person - { - get - { - if (_Person == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(Person))) != null) - { - _Person = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - JsonTypeInfo objectInfo = JsonMetadataServices.CreateObjectInfo( - Options, - createObjectFunc: static () => new Person(), - PersonPropInitFunc, - default, - serializeFunc: null); - - _Person = objectInfo; - } - } - - return _Person; - } - } - private static JsonPropertyInfo[] PersonPropInitFunc(JsonSerializerContext context) - { - JsonContext jsonContext = (JsonContext)context; - JsonSerializerOptions options = context.Options; - - JsonPropertyInfo[] properties = new JsonPropertyInfo[4]; - - properties[0] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(Person), - propertyTypeInfo: jsonContext.Int32, - converter: null, - getter: static (obj) => { return ((Person)obj).Age; }, - setter: static (obj, value) => { ((Person)obj).Age = value; }, - ignoreCondition: default, - hasJsonInclude: false, - numberHandling: default, - propertyName: nameof(Tests.Person.Age), - jsonPropertyName: null); - - properties[1] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(Person), - propertyTypeInfo: jsonContext.String, - converter: null, - getter: static (obj) => { return ((Person)obj).Name; }, - setter: static (obj, value) => { ((Person)obj).Name = value; }, - ignoreCondition: default, - hasJsonInclude: false, - numberHandling: default, - propertyName: nameof(Tests.Person.Name), - jsonPropertyName: null); - - properties[2] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(Person), - propertyTypeInfo: jsonContext.Person, - converter: null, - getter: static (obj) => { return ((Person)obj).Parent; }, - setter: static (obj, value) => { ((Person)obj).Parent = value; }, - ignoreCondition: default, - hasJsonInclude: false, - numberHandling: default, - propertyName: nameof(Tests.Person.Parent), - jsonPropertyName: null); - - properties[3] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(Person), - propertyTypeInfo: jsonContext.String, - converter: null, - getter: static (obj) => { return ((Person)obj).PlaceOfBirth; }, - setter: static (obj, value) => { ((Person)obj).PlaceOfBirth = value; }, - ignoreCondition: default, - hasJsonInclude: false, - numberHandling: default, - propertyName: nameof(Tests.Person.PlaceOfBirth), - jsonPropertyName: null); - - return properties; - } - } -} diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/String.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/String.cs deleted file mode 100644 index 861a451d7be..00000000000 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/JsonContext/String.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Net.Http.Json.Functional.Tests -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _String; - public JsonTypeInfo String - { - get - { - if (_String == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(string))) != null) - { - _String = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _String = JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.StringConverter); - } - } - - return _String; - } - } - } -} diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj index 4b10c2cc7b8..9fe7bbb28a7 100644 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent);net48 @@ -6,10 +6,6 @@ - - - - @@ -30,4 +26,7 @@ + + + diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/TestClasses.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/TestClasses.cs index 5cf68522004..fc8f0c04469 100644 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/TestClasses.cs +++ b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/TestClasses.cs @@ -81,4 +81,9 @@ namespace System.Net.Http.Json.Functional.Tests [JsonConverter(typeof(EnsureDefaultOptionsConverter))] internal class EnsureDefaultOptions { } + + [JsonSerializable(typeof(Person))] + internal sealed partial class JsonContext : JsonSerializerContext + { + } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/DateTimeOffset.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/DateTimeOffset.cs deleted file mode 100644 index c11ab0b179f..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/DateTimeOffset.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _DateTimeOffset; - public JsonTypeInfo DateTimeOffset - { - get - { - if (_DateTimeOffset == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(DateTimeOffset))) != null) - { - _DateTimeOffset = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _DateTimeOffset = JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.DateTimeOffsetConverter); - } - } - - return _DateTimeOffset; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Dictionary.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Dictionary.cs deleted file mode 100644 index b3e3bb3e863..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Dictionary.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo> _Dictionary; - public JsonTypeInfo> Dictionary - { - get - { - if (_Dictionary == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(Dictionary))) != null) - { - _Dictionary = JsonMetadataServices.CreateValueInfo>(Options, customConverter); - } - else - { - _Dictionary = JsonMetadataServices.CreateDictionaryInfo, string, HighLowTemps>(Options, () => new Dictionary(), this.String, this.HighLowTemps, default, serializeFunc: null); - } - } - - return _Dictionary; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs deleted file mode 100644 index c6135602978..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/HighLowTemps.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _HighLowTemps; - public JsonTypeInfo HighLowTemps - { - get - { - if (_HighLowTemps == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(HighLowTemps))) != null) - { - _HighLowTemps = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - JsonTypeInfo objectInfo = JsonMetadataServices.CreateObjectInfo( - Options, - createObjectFunc: static () => new HighLowTemps(), - HighLowTempsPropInitFunc, - default, - serializeFunc: null); - - _HighLowTemps = objectInfo; - } - } - - return _HighLowTemps; - } - } - - private static JsonPropertyInfo[] HighLowTempsPropInitFunc(JsonSerializerContext context) - { - JsonContext jsonContext = (JsonContext)context; - JsonSerializerOptions options = context.Options; - - JsonPropertyInfo[] properties = new JsonPropertyInfo[2]; - - properties[0] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(HighLowTemps), - propertyTypeInfo: jsonContext.Int32, - converter: null, - getter: static (obj) => { return ((HighLowTemps)obj).High; }, - setter: static (obj, value) => { ((HighLowTemps)obj).High = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.HighLowTemps.High), - jsonPropertyName: null); - - properties[1] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(HighLowTemps), - propertyTypeInfo: jsonContext.Int32, - converter: null, - getter: static (obj) => { return ((HighLowTemps)obj).Low; }, - setter: static (obj, value) => { ((HighLowTemps)obj).Low = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.HighLowTemps.Low), - jsonPropertyName: null); - - return properties; - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Int32.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Int32.cs deleted file mode 100644 index 4afacd38ce7..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/Int32.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _Int32; - public JsonTypeInfo Int32 - { - get - { - if (_Int32 == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(int))) != null) - { - _Int32 = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _Int32 = JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.Int32Converter); - } - } - - return _Int32; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.GetJsonTypeInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.GetJsonTypeInfo.cs deleted file mode 100644 index 19ae4904f19..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.GetJsonTypeInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - public override JsonTypeInfo GetTypeInfo(System.Type type) - { - if (type == typeof(WeatherForecastWithPOCOs)) - { - return this.WeatherForecastWithPOCOs; - } - - return null!; - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.cs deleted file mode 100644 index c16add28cc2..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/JsonContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private static JsonContext s_default; - public static JsonContext Default => s_default ??= new JsonContext(new JsonSerializerOptions()); - - public JsonContext() : base(null, null) - { - } - - public JsonContext(JsonSerializerOptions options) : base(options, null) - { - } - - private JsonConverter GetRuntimeProvidedCustomConverter(System.Type type) - { - IList converters = Options.Converters; - - for (int i = 0; i < converters.Count; i++) - { - JsonConverter converter = converters[i]; - - if (converter.CanConvert(type)) - { - if (converter is JsonConverterFactory factory) - { - converter = factory.CreateConverter(type, Options); - if (converter == null || converter is JsonConverterFactory) - { - throw new System.InvalidOperationException($"The converter '{factory.GetType()}' cannot return null or a JsonConverterFactory instance."); - } - } - - return converter; - } - } - - return null; - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/List.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/List.cs deleted file mode 100644 index 3a092554913..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/List.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo> _ListSystemDateTimeOffset; - public JsonTypeInfo> ListSystemDateTimeOffset - { - get - { - if (_ListSystemDateTimeOffset == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(List))) != null) - { - _ListSystemDateTimeOffset = JsonMetadataServices.CreateValueInfo>(Options, customConverter); - } - else - { - _ListSystemDateTimeOffset = JsonMetadataServices.CreateListInfo, DateTimeOffset>(Options, () => new List(), this.DateTimeOffset, default, serializeFunc: null); - } - } - - return _ListSystemDateTimeOffset; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/String.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/String.cs deleted file mode 100644 index 19f7cfb3db6..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/String.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization.Metadata; -using System.Text.Json.Serialization; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _String; - public JsonTypeInfo String - { - get - { - if (_String == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(string))) != null) - { - _String = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _String = JsonMetadataServices.CreateValueInfo (Options, JsonMetadataServices.StringConverter); - } - } - - return _String; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/StringArray.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/StringArray.cs deleted file mode 100644 index 6fab57d0630..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/StringArray.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization.Metadata; -using System.Text.Json.Serialization; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _StringArray; - public JsonTypeInfo StringArray - { - get - { - if (_StringArray == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(string[]))) != null) - { - _StringArray = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - _StringArray = JsonMetadataServices.CreateArrayInfo(Options, this.String, default, serializeFunc: null); - } - } - - return _StringArray; - } - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs deleted file mode 100644 index 7d3f8445100..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonContext/WeatherForecastWithPOCOs.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace System.Text.Json.Tests.Serialization -{ - internal partial class JsonContext : JsonSerializerContext - { - private JsonTypeInfo _WeatherForecastWithPOCOs; - public JsonTypeInfo WeatherForecastWithPOCOs - { - get - { - if (_WeatherForecastWithPOCOs == null) - { - JsonConverter customConverter; - if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(WeatherForecastWithPOCOs))) != null) - { - _WeatherForecastWithPOCOs = JsonMetadataServices.CreateValueInfo(Options, customConverter); - } - else - { - JsonTypeInfo objectInfo = JsonMetadataServices.CreateObjectInfo( - Options, - createObjectFunc: static () => new WeatherForecastWithPOCOs(), - WeatherForecastWithPOCOsPropInitFunc, - default, - serializeFunc: null); - - _WeatherForecastWithPOCOs = objectInfo; - } - } - - return _WeatherForecastWithPOCOs; - } - } - - private static JsonPropertyInfo[] WeatherForecastWithPOCOsPropInitFunc(JsonSerializerContext context) - { - JsonContext jsonContext = (JsonContext)context; - JsonSerializerOptions options = context.Options; - - JsonPropertyInfo[] properties = new JsonPropertyInfo[7]; - - properties[0] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.DateTimeOffset, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).Date; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).Date = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.Date), - jsonPropertyName: null); - - properties[1] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.Int32, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).TemperatureCelsius; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).TemperatureCelsius = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.TemperatureCelsius), - jsonPropertyName: null); - - properties[2] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.String, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).Summary; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).Summary = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.Summary), - jsonPropertyName: null); - - properties[3] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.ListSystemDateTimeOffset, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).DatesAvailable; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).DatesAvailable = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.DatesAvailable), - jsonPropertyName: null); - - properties[4] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.Dictionary, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).TemperatureRanges; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).TemperatureRanges = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.TemperatureRanges), - jsonPropertyName: null); - - properties[5] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: true, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.StringArray, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).SummaryWords; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).SummaryWords = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.SummaryWords), - jsonPropertyName: null); - - properties[6] = JsonMetadataServices.CreatePropertyInfo( - options, - isProperty: false, - isPublic: true, - isVirtual: false, - declaringType: typeof(WeatherForecastWithPOCOs), - propertyTypeInfo: jsonContext.String, - converter: null, - getter: static (obj) => { return ((WeatherForecastWithPOCOs)obj).SummaryField; }, - setter: static (obj, value) => { ((WeatherForecastWithPOCOs)obj).SummaryField = value; }, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: nameof(Serialization.WeatherForecastWithPOCOs.SummaryField), - jsonPropertyName: null); - - return properties; - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs deleted file mode 100644 index a583b3a121b..00000000000 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonMetadataServices.cs +++ /dev/null @@ -1,308 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Drawing; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using System.Text.Json.Serialization.Tests; -using Xunit; - -namespace System.Text.Json.Tests.Serialization -{ - public abstract partial class MetadataTests - { - [Fact] - public void CreatePropertyInfo() - { - JsonSerializerOptions options = new(); - - // Null options - ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: null, - isProperty: true, - isPublic: false, - isVirtual: false, - declaringType: typeof(Point), - propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: "MyInt", - jsonPropertyName: null)); - Assert.Contains("options", ane.ToString()); - - // Null declaring type - ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: options, - isProperty: true, - isPublic: false, - isVirtual: false, - declaringType: null, - propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: "MyInt", - jsonPropertyName: null)); - Assert.Contains("declaringType", ane.ToString()); - - // Null property type info - ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: options, - isProperty: true, - isPublic: false, - isVirtual: false, - declaringType: typeof(Point), - propertyTypeInfo: null, - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: "MyInt", - jsonPropertyName: null)); - Assert.Contains("propertyTypeInfo", ane.ToString()); - - // Null property name - ane = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: options, - isProperty: true, - isPublic: false, - isVirtual: false, - declaringType: typeof(Point), - propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: null, - jsonPropertyName: null)); - Assert.Contains("propertyName", ane.ToString()); - - // Invalid converter - InvalidOperationException ioe = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: options, - isProperty: true, - isPublic: false, - isVirtual: false, - declaringType: typeof(Point), - // Converter invalid because you'd need to create with JsonMetadataServices.CreatePropertyInfo instead. - propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, new DerivedClassConverter()), - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: "MyProp", - jsonPropertyName: null)); - string ioeAsStr = ioe.ToString(); - Assert.Contains("Point.MyProp", ioeAsStr); - Assert.Contains("MyClass", ioeAsStr); - - // Fields cannot be virtual. - ioe = Assert.Throws(() => JsonMetadataServices.CreatePropertyInfo( - options: options, - isProperty: false, - isPublic: false, - isVirtual: true, - declaringType: typeof(Point), - propertyTypeInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - converter: null, - getter: null, - setter: null, - ignoreCondition: default, - numberHandling: default, - hasJsonInclude: false, - propertyName: "X", - jsonPropertyName: null)); - Assert.Contains("field", ioe.ToString()); - - // Source generator tests verify that generated metadata is actually valid. - } - - private class MyClass { } - private class MyDerivedClass : MyClass { } - - [Fact] - public void CreateObjectInfo() - { - JsonSerializerOptions options = new(); - - // Null options - ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreateObjectInfo( - options: null, - createObjectFunc: null, - propInitFunc: (context) => Array.Empty(), - numberHandling: default, - serializeFunc: null)); - Assert.Contains("options", ane.ToString()); - - // Null prop init func is fine if serialize func is provided. - JsonMetadataServices.CreateObjectInfo( - options, - createObjectFunc: null, - propInitFunc: null, - numberHandling: default, - serializeFunc: (writer, obj) => { }); - - // Null serialize func is fine if prop init func is provided. - JsonMetadataServices.CreateObjectInfo( - options, - createObjectFunc: null, - propInitFunc: (context) => Array.Empty(), - numberHandling: default, - serializeFunc: null); - - // Null prop init func and serialize func - InvalidOperationException ioe = Assert.Throws(() => JsonMetadataServices.CreateObjectInfo( - options, - createObjectFunc: null, - propInitFunc: null, - numberHandling: default, - serializeFunc: null)); - string ioeAsStr = ioe.ToString(); - Assert.Contains("propInitFunc", ioeAsStr); - Assert.Contains("serializeFunc", ioeAsStr); - } - - [Fact] - public void CreateValueInfo() - { - JsonSerializerOptions options = new(); - - // Use converter that returns same type. - Assert.NotNull(JsonMetadataServices.CreateValueInfo(options, new ClassConverter())); - - // Use converter that returns derived type. - Assert.NotNull(JsonMetadataServices.CreateValueInfo(options, new DerivedClassConverter())); - - // Null options - ArgumentNullException ex = Assert.Throws(() => JsonMetadataServices.CreateValueInfo(options: null, new DerivedClassConverter())); - Assert.Contains("options", ex.ToString()); - } - - [Fact] - public void CreateArrayInfo() - { - JsonSerializerOptions options = new(); - - // Null options - ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreateArrayInfo( - options: null, - elementInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - numberHandling: default, - serializeFunc: null)); - Assert.Contains("options", ane.ToString()); - - // Null element info - ane = Assert.Throws(() => JsonMetadataServices.CreateArrayInfo( - options, - elementInfo: null, - numberHandling: default, - serializeFunc: null)); - Assert.Contains("elementInfo", ane.ToString()); - } - - [Fact] - public void CreateListInfo() - { - JsonSerializerOptions options = new(); - - // Null options - ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreateListInfo, int>( - options: null, - createObjectFunc: null, - elementInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - numberHandling: default, - serializeFunc: null)); - Assert.Contains("options", ane.ToString()); - - // Null element info - ane = Assert.Throws(() => JsonMetadataServices.CreateListInfo, int>( - options: options, - createObjectFunc: null, - elementInfo: null, - numberHandling: default, - serializeFunc: null)); - Assert.Contains("elementInfo", ane.ToString()); - } - - [Fact] - public void CreateDictionaryInfo() - { - JsonSerializerOptions options = new(); - - // Null options - ArgumentNullException ane = Assert.Throws(() => JsonMetadataServices.CreateDictionaryInfo, string, int>( - options: null, - createObjectFunc: null, - keyInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.StringConverter), - valueInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - numberHandling: default, - serializeFunc: null)); - Assert.Contains("options", ane.ToString()); - - // Null key info - ane = Assert.Throws(() => JsonMetadataServices.CreateDictionaryInfo, string, int>( - options: options, - createObjectFunc: null, - keyInfo: null, - valueInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.Int32Converter), - numberHandling: default, - serializeFunc: null)); - Assert.Contains("keyInfo", ane.ToString()); - - // Null value info - ane = Assert.Throws(() => JsonMetadataServices.CreateDictionaryInfo, string, int>( - options: options, - createObjectFunc: null, - keyInfo: JsonMetadataServices.CreateValueInfo(options, JsonMetadataServices.StringConverter), - valueInfo: null, - numberHandling: default, - serializeFunc: null)); - Assert.Contains("valueInfo", ane.ToString()); - } - - private class ClassConverter : JsonConverter - { - public override MyClass? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); - public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerOptions options) => throw new NotImplementedException(); - } - - private class DerivedClassConverter : JsonConverter - { - public override MyDerivedClass? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); - public override void Write(Utf8JsonWriter writer, MyDerivedClass value, JsonSerializerOptions options) => throw new NotImplementedException(); - } - - [Fact] - public void GetEnumConverter() - { - JsonConverter converter = JsonMetadataServices.GetEnumConverter(new JsonSerializerOptions()); - Assert.NotNull(converter); - Assert.Throws(() => JsonMetadataServices.GetEnumConverter(null!)); - } - - [Fact] - public void GetNullableConverter() - { - JsonSerializerOptions options = new(); - JsonConverter enumConverter = JsonMetadataServices.GetEnumConverter(options); - JsonTypeInfo enumInfo = JsonMetadataServices.CreateValueInfo(options, enumConverter); - JsonConverter nullableConverter = JsonMetadataServices.GetNullableConverter(enumInfo); - Assert.NotNull(nullableConverter); - Assert.Throws(() => JsonMetadataServices.GetNullableConverter(null!)); - } - } -} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs index 7212b051e73..71aeb9fb9d4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Text.Json.Serialization; using System.Text.Json.Serialization.Tests; namespace System.Text.Json.Tests.Serialization @@ -57,4 +58,9 @@ namespace System.Text.Json.Tests.Serialization public int High { get; set; } public int Low { get; set; } } + + [JsonSerializable(typeof(WeatherForecastWithPOCOs))] + internal sealed partial class JsonContext : JsonSerializerContext + { + } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index d3feb54ba04..a3d45fd72ed 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -143,18 +143,7 @@ - - - - - - - - - - - @@ -216,4 +205,7 @@ + + + From c8f9a24ca0ebf6d9593e00ccbc7353510a0e0213 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 19 Jul 2021 13:03:22 -0700 Subject: [PATCH 670/926] Add an assert concerning branch to BBJ_CALLFINALLY blocks (#55858) In the FEATURE_EH_CALLFINALLY_THUNKS case, BBJ_CALLFINALLY blocks live in the EH region enclosing the `try` block that needs to call the finally. However, we need all flow to the BBJ_CALLFINALLY to come from that try block; we don't want flow optimizations to otherwise branch directly to this BBJ_CALLFINALLY block. Add an assert to verify this is the case. The assert also covers the non-FEATURE_EH_CALLFINALLY_THUNKS case. --- src/coreclr/jit/fgdiagnostic.cpp | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 9f7d52cb24c..1cba2a5294c 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -2740,6 +2740,52 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef assert(block->getTryIndex() < compHndBBtabCount); } + // A branch or fall-through to a BBJ_CALLFINALLY block must come from the `try` region associated + // with the finally block the BBJ_CALLFINALLY is targeting. There is one special case: if the + // BBJ_CALLFINALLY is the first block of a `try`, then its predecessor can be outside the `try`: + // either a branch or fall-through to the first block. + // + // Note that this IR condition is a choice. It naturally occurs when importing EH constructs. + // This condition prevents flow optimizations from skipping blocks in a `try` and branching + // directly to the BBJ_CALLFINALLY. Relaxing this constraint would require careful thinking about + // the implications, such as data flow optimizations. + // + // Don't depend on predecessors list for the check. + for (BasicBlock* const succBlock : block->Succs()) + { + if (succBlock->bbJumpKind == BBJ_CALLFINALLY) + { + BasicBlock* finallyBlock = succBlock->bbJumpDest; + assert(finallyBlock->hasHndIndex()); + unsigned finallyIndex = finallyBlock->getHndIndex(); + + // Now make sure the block branching to the BBJ_CALLFINALLY is in the correct region. The branch + // to the BBJ_CALLFINALLY can come from the try region of the finally block, or from a more nested + // try region, e.g.: + // try { + // try { + // LEAVE L_OUTER; // this becomes a branch to a BBJ_CALLFINALLY in an outer try region + // // (in the FEATURE_EH_CALLFINALLY_THUNKS case) + // } catch { + // } + // } finally { + // } + // L_OUTER: + // + EHblkDsc* ehDsc = ehGetDsc(finallyIndex); + if (ehDsc->ebdTryBeg == succBlock) + { + // The BBJ_CALLFINALLY is the first block of it's `try` region. Don't check the predecessor. + // Note that this case won't occur in the FEATURE_EH_CALLFINALLY_THUNKS case, since the + // BBJ_CALLFINALLY in that case won't exist in the `try` region of the `finallyIndex`. + } + else + { + assert(bbInTryRegions(finallyIndex, block)); + } + } + } + /* Check if BBF_RUN_RARELY is set that we have bbWeight of zero */ if (block->isRunRarely()) { From ec3f9fa03f72ceadfa0610196a12bab3b37b63f9 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Mon, 19 Jul 2021 16:15:37 -0400 Subject: [PATCH 671/926] [mono] Split iOS/tvOS/MacCatalyst runtime packs into their own workload (#55851) The ios/tvos/maccatalyst runtime packs need to be installable on Windows and as a result each need their own workload. The microsoft-net-runtime-ios/tv/maccatalyst workloads will still function the same by extending the new runtimes-* workloads. --- .../WorkloadManifest.json.in | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index d7fec86e3a1..bd8992a751d 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -42,32 +42,48 @@ "abstract": true, "description": "iOS Mono Runtime and AOT Workload", "packs": [ - "Microsoft.NETCore.App.Runtime.Mono.ios-arm", - "Microsoft.NETCore.App.Runtime.Mono.ios-arm64", - "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64", - "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64", - "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86", "Microsoft.NETCore.App.Runtime.AOT.Cross.ios-arm", "Microsoft.NETCore.App.Runtime.AOT.Cross.ios-arm64", "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-arm64", "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x64", "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x86" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "runtimes-ios" ], "platforms": [ "osx-arm64", "osx-x64" ] }, + "runtimes-ios": { + "abstract": true, + "description": "iOS Mono Runtime Packs", + "packs": [ + "Microsoft.NETCore.App.Runtime.Mono.ios-arm", + "Microsoft.NETCore.App.Runtime.Mono.ios-arm64", + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64", + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64", + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86" + ], + "extends": [ "microsoft-net-runtime-mono-tooling" ], + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] + }, "microsoft-net-runtime-maccatalyst": { "abstract": true, "description": "MacCatalyst Mono Runtime and AOT Workload", "packs": [ - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64", - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64", "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-arm64", "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-x64" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "runtimes-maccatalyst" ], "platforms": [ "osx-arm64", "osx-x64" ] }, + "runtimes-maccatalyst": { + "abstract": true, + "description": "MacCatalyst Mono Runtime Packs", + "packs": [ + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64", + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64" + ], + "extends": [ "microsoft-net-runtime-mono-tooling" ], + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] + }, "microsoft-net-runtime-macos": { "abstract": true, "description": "MacOS CoreCLR and Mono Runtime Workload", @@ -84,16 +100,24 @@ "abstract": true, "description": "tvOS Mono Runtime and AOT Workload", "packs": [ - "Microsoft.NETCore.App.Runtime.Mono.tvos-arm64", - "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64", - "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64", "Microsoft.NETCore.App.Runtime.AOT.Cross.tvos-arm64", "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-arm64", "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-x64" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "runtimes-tvos" ], "platforms": [ "osx-arm64", "osx-x64" ] }, + "runtimes-tvos": { + "abstract": true, + "description": "tvOS Mono Runtime Packs", + "packs": [ + "Microsoft.NETCore.App.Runtime.Mono.tvos-arm64", + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64", + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64" + ], + "extends": [ "microsoft-net-runtime-mono-tooling" ], + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] + }, "microsoft-net-runtime-mono-tooling": { "abstract": true, "description": "Shared native build tooling for Mono runtime", @@ -176,7 +200,7 @@ "kind": "framework", "version": "${PackageVersion}", }, - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-64": { + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64": { "kind": "framework", "version": "${PackageVersion}", }, From 04072ff3de988de016a909597a65f29f0af5a4f9 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 19 Jul 2021 19:53:25 -0500 Subject: [PATCH 672/926] Link with EmccCompileOptimizationFlag==-Oz by default in release (#55939) --- src/mono/wasm/build/WasmApp.Native.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 375747cd4e6..48bafb1d428 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -155,7 +155,7 @@ <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz $(_EmccOptimizationFlagDefault) - -O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) + $(EmccCompileOptimizationFlag) <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp From 3d1467412eac6a5f0cce054f3d376848bbae679f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 19 Jul 2021 21:18:52 -0400 Subject: [PATCH 673/926] Revert UnicodeDebug.cs to not depend on the Debug.Assert interpolated string handler (#55954) Turns out we use this file outside of corelib, in projects that build for pre-.NET 6, and the change was causing lots of string formatting for asserts that pass. --- .../src/System/Text/UnicodeDebug.cs | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs index ecd50f3f7c5..4caacbf856c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Globalization; namespace System.Text { @@ -11,39 +10,55 @@ namespace System.Text [Conditional("DEBUG")] internal static void AssertIsBmpCodePoint(uint codePoint) { - Debug.Assert(UnicodeUtility.IsBmpCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid BMP code point."); + if (!UnicodeUtility.IsBmpCodePoint(codePoint)) + { + Debug.Fail($"The value {ToHexString(codePoint)} is not a valid BMP code point."); + } } [Conditional("DEBUG")] internal static void AssertIsHighSurrogateCodePoint(uint codePoint) { - Debug.Assert(UnicodeUtility.IsHighSurrogateCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid UTF-16 high surrogate code point."); + if (!UnicodeUtility.IsHighSurrogateCodePoint(codePoint)) + { + Debug.Fail($"The value {ToHexString(codePoint)} is not a valid UTF-16 high surrogate code point."); + } } [Conditional("DEBUG")] internal static void AssertIsLowSurrogateCodePoint(uint codePoint) { - Debug.Assert(UnicodeUtility.IsLowSurrogateCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid UTF-16 low surrogate code point."); + if (!UnicodeUtility.IsLowSurrogateCodePoint(codePoint)) + { + Debug.Fail($"The value {ToHexString(codePoint)} is not a valid UTF-16 low surrogate code point."); + } } [Conditional("DEBUG")] internal static void AssertIsValidCodePoint(uint codePoint) { - Debug.Assert(UnicodeUtility.IsValidCodePoint(codePoint), $"The value {ToHexString(codePoint)} is not a valid Unicode code point."); + if (!UnicodeUtility.IsValidCodePoint(codePoint)) + { + Debug.Fail($"The value {ToHexString(codePoint)} is not a valid Unicode code point."); + } } [Conditional("DEBUG")] internal static void AssertIsValidScalar(uint scalarValue) { - Debug.Assert(UnicodeUtility.IsValidUnicodeScalar(scalarValue), $"The value {ToHexString(scalarValue)} is not a valid Unicode scalar value."); + if (!UnicodeUtility.IsValidUnicodeScalar(scalarValue)) + { + Debug.Fail($"The value {ToHexString(scalarValue)} is not a valid Unicode scalar value."); + } } [Conditional("DEBUG")] internal static void AssertIsValidSupplementaryPlaneScalar(uint scalarValue) { - Debug.Assert( - UnicodeUtility.IsValidUnicodeScalar(scalarValue) && !UnicodeUtility.IsBmpCodePoint(scalarValue), - $"The value {ToHexString(scalarValue)} is not a valid supplementary plane Unicode scalar value."); + if (!UnicodeUtility.IsValidUnicodeScalar(scalarValue) || UnicodeUtility.IsBmpCodePoint(scalarValue)) + { + Debug.Fail($"The value {ToHexString(scalarValue)} is not a valid supplementary plane Unicode scalar value."); + } } /// @@ -52,6 +67,9 @@ namespace System.Text /// /// The input value doesn't have to be a real code point in the Unicode codespace. It can be any integer. /// - private static string ToHexString(uint codePoint) => $"U+{codePoint:X4}"; + private static string ToHexString(uint codePoint) + { + return FormattableString.Invariant($"U+{codePoint:X4}"); + } } } From 16359b229af9e7e0ed0d19eeca07c2e8ebb82c8a Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Tue, 20 Jul 2021 03:44:54 +0200 Subject: [PATCH 674/926] Disable ConnectTimeout_PlaintextStreamFilterTimesOut_Throws (#55932) Disable test ConnectTimeout_PlaintextStreamFilterTimesOut_Throws Disabled test tracked by #55931 --- .../tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index 3f7f506aaf3..e6fb25a2417 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -80,6 +80,7 @@ namespace System.Net.Http.Functional.Tests [OuterLoop] [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55931")] [InlineData(true)] [InlineData(false)] public async Task ConnectTimeout_PlaintextStreamFilterTimesOut_Throws(bool useSsl) From eb1e55e3ce30ca77e490324a0c3b60ab99ae66b0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 19 Jul 2021 19:29:13 -0700 Subject: [PATCH 675/926] Improve crossgen2 help text (#55977) - Add handling of "-?" and no command line arguments. - Print out default values for --targetos and --targetarch - Describe behavior of response files - Describe the behavior of the -- switch - Describe in much greater detail the valid values for the --instruction-set switch Fix #47486 Co-authored-by: Jan Kotas --- .../Common/CommandLine/ArgumentSyntax.cs | 15 ++++ .../Common/CommandLine/HelpTextGenerator.cs | 16 ++++- .../tools/aot/crossgen2/CommandLineOptions.cs | 71 +++++++++++++++++++ src/coreclr/tools/aot/crossgen2/Program.cs | 60 +++++++++------- .../aot/crossgen2/Properties/Resources.resx | 14 +++- 5 files changed, 148 insertions(+), 28 deletions(-) diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs b/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs index e4da81c8cc8..4cea1200b3a 100644 --- a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs +++ b/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs @@ -14,6 +14,8 @@ namespace Internal.CommandLine private readonly List _options = new List(); private readonly List _parameters = new List(); + private readonly List _extraHelpParagraphs = new List(); + private ArgumentParser _parser; private ArgumentCommand _definedCommand; private ArgumentCommand _activeCommand; @@ -452,5 +454,18 @@ namespace Internal.CommandLine { return HelpTextGenerator.Generate(this, maxWidth); } + + public IReadOnlyList ExtraHelpParagraphs + { + set + { + _extraHelpParagraphs.Clear(); + _extraHelpParagraphs.AddRange(value); + } + get + { + return _extraHelpParagraphs.ToArray(); + } + } } } diff --git a/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs b/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs index 372cf052f32..afe1f0a1a5d 100644 --- a/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs +++ b/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs @@ -27,6 +27,7 @@ namespace Internal.CommandLine public string ApplicationName; public IEnumerable SyntaxElements; public IReadOnlyList Rows; + public IReadOnlyList ExtraParagraphs; } private struct HelpRow @@ -47,6 +48,15 @@ namespace Internal.CommandLine sb.WriteRows(page.Rows, maxWidth); sb.AppendLine(); + + if (page.ExtraParagraphs != null) + { + foreach (string text in page.ExtraParagraphs) + { + var words = SplitWords(text); + sb.WriteWordWrapped(words, 0, maxWidth); + } + } } private static void WriteUsage(this StringBuilder sb, string applicationName, IEnumerable syntaxElements, int maxWidth) @@ -115,7 +125,8 @@ namespace Internal.CommandLine { ApplicationName = argumentSyntax.ApplicationName, SyntaxElements = GetGlobalSyntax(), - Rows = GetCommandRows(argumentSyntax).ToArray() + Rows = GetCommandRows(argumentSyntax).ToArray(), + ExtraParagraphs = argumentSyntax.ExtraHelpParagraphs }; } @@ -125,7 +136,8 @@ namespace Internal.CommandLine { ApplicationName = argumentSyntax.ApplicationName, SyntaxElements = GetCommandSyntax(argumentSyntax, command), - Rows = GetArgumentRows(argumentSyntax, command).ToArray() + Rows = GetArgumentRows(argumentSyntax, command).ToArray(), + ExtraParagraphs = argumentSyntax.ExtraHelpParagraphs }; } diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs index c91e615eb73..8f832d110e6 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -5,8 +5,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Text; using Internal.CommandLine; +using Internal.TypeSystem; namespace ILCompiler { @@ -88,6 +90,23 @@ namespace ILCompiler Parallelism = Environment.ProcessorCount; SingleMethodGenericArg = null; + bool forceHelp = false; + if (args.Length == 0) + { + forceHelp = true; + } + + foreach (string arg in args) + { + if (arg == "-?") + forceHelp = true; + } + + if (forceHelp) + { + args = new string[] {"--help"}; + } + ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => { syntax.ApplicationName = typeof(Program).Assembly.GetName().Name.ToString(); @@ -157,6 +176,58 @@ namespace ILCompiler if (Help) { + List extraHelp = new List(); + extraHelp.Add(SR.OptionPassingHelp); + extraHelp.Add(""); + extraHelp.Add(SR.DashDashHelp); + extraHelp.Add(""); + + string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; + string[] ValidOS = new string[] {"windows", "linux", "osx"}; + TargetOS defaultOs; + TargetArchitecture defaultArch; + Program.ComputeDefaultOptions(out defaultOs, out defaultArch); + + extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), defaultArch.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add(SR.InstructionSetHelp); + foreach (string arch in ValidArchitectures) + { + StringBuilder archString = new StringBuilder(); + + archString.Append(arch); + archString.Append(": "); + + TargetArchitecture targetArch = Program.GetTargetArchitectureFromArg(arch, out _); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + { + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) + { + if (first) + { + first = false; + } + else + { + archString.Append(", "); + } + archString.Append(instructionSet.Name); + } + } + + extraHelp.Add(archString.ToString()); + } + + argSyntax.ExtraHelpParagraphs = extraHelp; + HelpText = argSyntax.GetHelpText(); } } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index aeeb9f57bb3..22e7b3223f2 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -46,36 +46,40 @@ namespace ILCompiler { } - private void InitializeDefaultOptions() + public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) { - // We could offer this as a command line option, but then we also need to - // load a different RyuJIT, so this is a future nice to have... if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - _targetOS = TargetOS.Windows; + os = TargetOS.Windows; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - _targetOS = TargetOS.Linux; + os = TargetOS.Linux; else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - _targetOS = TargetOS.OSX; + os = TargetOS.OSX; else throw new NotImplementedException(); switch (RuntimeInformation.ProcessArchitecture) { case Architecture.X86: - _targetArchitecture = TargetArchitecture.X86; + arch = TargetArchitecture.X86; break; case Architecture.X64: - _targetArchitecture = TargetArchitecture.X64; + arch = TargetArchitecture.X64; break; case Architecture.Arm: - _targetArchitecture = TargetArchitecture.ARM; + arch = TargetArchitecture.ARM; break; case Architecture.Arm64: - _targetArchitecture = TargetArchitecture.ARM64; + arch = TargetArchitecture.ARM64; break; default: throw new NotImplementedException(); } + + } + + private void InitializeDefaultOptions() + { + ComputeDefaultOptions(out _targetOS, out _targetArchitecture); } private void ProcessCommandLine(string[] args) @@ -167,6 +171,26 @@ namespace ILCompiler } + public static TargetArchitecture GetTargetArchitectureFromArg(string archArg, out bool armelAbi) + { + armelAbi = false; + if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X86; + else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X64; + else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM; + else if (archArg.Equals("armel", StringComparison.OrdinalIgnoreCase)) + { + armelAbi = true; + return TargetArchitecture.ARM; + } + else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM64; + else + throw new CommandLineException(SR.TargetArchitectureUnsupported); + } + private void ConfigureTarget() { // @@ -174,21 +198,7 @@ namespace ILCompiler // if (_commandLineOptions.TargetArch != null) { - if (_commandLineOptions.TargetArch.Equals("x86", StringComparison.OrdinalIgnoreCase)) - _targetArchitecture = TargetArchitecture.X86; - else if (_commandLineOptions.TargetArch.Equals("x64", StringComparison.OrdinalIgnoreCase)) - _targetArchitecture = TargetArchitecture.X64; - else if (_commandLineOptions.TargetArch.Equals("arm", StringComparison.OrdinalIgnoreCase)) - _targetArchitecture = TargetArchitecture.ARM; - else if (_commandLineOptions.TargetArch.Equals("armel", StringComparison.OrdinalIgnoreCase)) - { - _targetArchitecture = TargetArchitecture.ARM; - _armelAbi = true; - } - else if (_commandLineOptions.TargetArch.Equals("arm64", StringComparison.OrdinalIgnoreCase)) - _targetArchitecture = TargetArchitecture.ARM64; - else - throw new CommandLineException(SR.TargetArchitectureUnsupported); + _targetArchitecture = GetTargetArchitectureFromArg(_commandLineOptions.TargetArch, out _armelAbi); } if (_commandLineOptions.TargetOS != null) { diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 56fbb93adb9..ab668d19120 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -348,4 +348,16 @@ Explicitly request a particular PerfMap format version - \ No newline at end of file + + Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and only the : syntax for switches is supported. + + + Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are considered to be input files. If no input files begin with '--' then this option is not necessary. + + + Valid switches for {0} are: '{1}'. The default value is '{2}'. + + + The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt' + + From fa45c1fc6f12eaae6295771dd4ba0796b0b35899 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 20 Jul 2021 05:44:20 +0300 Subject: [PATCH 676/926] fix discrepancy in STJ test namespaces (#55958) --- .../Serialization/MetadataTests/MetadataTests.JsonSerializer.cs | 2 +- .../Serialization/MetadataTests/MetadataTests.Options.cs | 2 +- .../Serialization/MetadataTests/MetadataTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs index 5e02a3b5d6b..be61553778e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading.Tasks; using Xunit; -namespace System.Text.Json.Tests.Serialization +namespace System.Text.Json.Serialization.Tests { public abstract partial class MetadataTests { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs index 7ebb217f628..c25e7803f92 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs @@ -6,7 +6,7 @@ using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using Xunit; -namespace System.Text.Json.Tests.Serialization +namespace System.Text.Json.Serialization.Tests { public abstract partial class MetadataTests { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs index 71aeb9fb9d4..0630ae2e7be 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Tests; -namespace System.Text.Json.Tests.Serialization +namespace System.Text.Json.Serialization.Tests { public sealed class MetadataTests_Span : MetadataTests { From 4e405a12423e107055874665f81f0c7ff05a03be Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 19 Jul 2021 22:44:43 -0400 Subject: [PATCH 677/926] Update analyzer rulesets (#55925) --- eng/CodeAnalysis.ruleset | 3 +++ eng/CodeAnalysis.test.ruleset | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index 0be33b74564..37968bf0bca 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -176,6 +176,7 @@ + @@ -498,6 +499,8 @@ + + diff --git a/eng/CodeAnalysis.test.ruleset b/eng/CodeAnalysis.test.ruleset index 285b9ff6bbf..40bb66f202c 100644 --- a/eng/CodeAnalysis.test.ruleset +++ b/eng/CodeAnalysis.test.ruleset @@ -67,6 +67,8 @@ + + @@ -120,11 +122,12 @@ - - - - - + + + + + + @@ -172,7 +175,8 @@ - + + @@ -496,8 +500,10 @@ - + + + From bd3563289299bbc821f43d3aa413fe1d63bf3df9 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Mon, 19 Jul 2021 20:42:04 -0700 Subject: [PATCH 678/926] add RID for Alpine 3.14 (#55857) --- .../src/runtime.compatibility.json | 107 ++++++++++++++++++ .../src/runtime.json | 23 ++++ .../src/runtimeGroups.props | 2 +- 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json index 6d11e65970e..33c8c1aabd8 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json @@ -401,6 +401,113 @@ "any", "base" ], + "alpine.3.14": [ + "alpine.3.14", + "alpine.3.13", + "alpine.3.12", + "alpine.3.11", + "alpine.3.10", + "alpine.3.9", + "alpine.3.8", + "alpine.3.7", + "alpine.3.6", + "alpine", + "linux-musl", + "linux", + "unix", + "any", + "base" + ], + "alpine.3.14-arm": [ + "alpine.3.14-arm", + "alpine.3.14", + "alpine.3.13-arm", + "alpine.3.13", + "alpine.3.12-arm", + "alpine.3.12", + "alpine.3.11-arm", + "alpine.3.11", + "alpine.3.10-arm", + "alpine.3.10", + "alpine.3.9-arm", + "alpine.3.9", + "alpine.3.8-arm", + "alpine.3.8", + "alpine.3.7-arm", + "alpine.3.7", + "alpine.3.6-arm", + "alpine.3.6", + "alpine-arm", + "alpine", + "linux-musl-arm", + "linux-musl", + "linux-arm", + "linux", + "unix-arm", + "unix", + "any", + "base" + ], + "alpine.3.14-arm64": [ + "alpine.3.14-arm64", + "alpine.3.14", + "alpine.3.13-arm64", + "alpine.3.13", + "alpine.3.12-arm64", + "alpine.3.12", + "alpine.3.11-arm64", + "alpine.3.11", + "alpine.3.10-arm64", + "alpine.3.10", + "alpine.3.9-arm64", + "alpine.3.9", + "alpine.3.8-arm64", + "alpine.3.8", + "alpine.3.7-arm64", + "alpine.3.7", + "alpine.3.6-arm64", + "alpine.3.6", + "alpine-arm64", + "alpine", + "linux-musl-arm64", + "linux-musl", + "linux-arm64", + "linux", + "unix-arm64", + "unix", + "any", + "base" + ], + "alpine.3.14-x64": [ + "alpine.3.14-x64", + "alpine.3.14", + "alpine.3.13-x64", + "alpine.3.13", + "alpine.3.12-x64", + "alpine.3.12", + "alpine.3.11-x64", + "alpine.3.11", + "alpine.3.10-x64", + "alpine.3.10", + "alpine.3.9-x64", + "alpine.3.9", + "alpine.3.8-x64", + "alpine.3.8", + "alpine.3.7-x64", + "alpine.3.7", + "alpine.3.6-x64", + "alpine.3.6", + "alpine-x64", + "alpine", + "linux-musl-x64", + "linux-musl", + "linux-x64", + "linux", + "unix-x64", + "unix", + "any", + "base" + ], "alpine.3.6": [ "alpine.3.6", "alpine", diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json index 66693df64b8..e06167e06bc 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json @@ -115,6 +115,29 @@ "alpine.3.12-x64" ] }, + "alpine.3.14": { + "#import": [ + "alpine.3.13" + ] + }, + "alpine.3.14-arm": { + "#import": [ + "alpine.3.14", + "alpine.3.13-arm" + ] + }, + "alpine.3.14-arm64": { + "#import": [ + "alpine.3.14", + "alpine.3.13-arm64" + ] + }, + "alpine.3.14-x64": { + "#import": [ + "alpine.3.14", + "alpine.3.13-x64" + ] + }, "alpine.3.6": { "#import": [ "alpine" diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props index 6344c99ae83..dd1e5b5b0c2 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props @@ -16,7 +16,7 @@ linux-musl x64;arm;arm64 - 3.6;3.7;3.8;3.9;3.10;3.11;3.12;3.13 + 3.6;3.7;3.8;3.9;3.10;3.11;3.12;3.13;3.14 From 0e5e88cb0393bfc29d58685bef0d7e6c9d138e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 20 Jul 2021 12:14:22 +0200 Subject: [PATCH 679/926] Cleanup iOS/Android samples to use xharness (#55926) Instead of manually calling adb or simctl. --- src/mono/sample/Android/AndroidSampleApp.csproj | 12 +----------- src/mono/sample/iOS/Makefile | 1 + src/mono/sample/iOS/Program.csproj | 9 +++------ src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 1 + 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index e85e49d7b3b..1abf7659bd0 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -21,7 +21,6 @@ False True - $(ANDROID_SDK_ROOT)\platform-tools\adb $(PublishDir)apk\ @@ -98,16 +97,7 @@ - - - - - - - - - + diff --git a/src/mono/sample/iOS/Makefile b/src/mono/sample/iOS/Makefile index e3b7578629b..34279c41d81 100644 --- a/src/mono/sample/iOS/Makefile +++ b/src/mono/sample/iOS/Makefile @@ -26,6 +26,7 @@ runtimepack: run: clean appbuilder $(DOTNET) publish \ -c $(MONO_CONFIG) \ + /p:TargetOS=iOS \ /p:TargetArchitecture=$(MONO_ARCH) \ /p:UseLLVM=$(USE_LLVM) \ /p:ForceAOT=$(AOT) \ diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj index 04b2bde9b0b..471abcbbe69 100644 --- a/src/mono/sample/iOS/Program.csproj +++ b/src/mono/sample/iOS/Program.csproj @@ -94,12 +94,9 @@ Condition="'$(ArchiveTests)' != 'true'"> - - - - - - + + + diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 465c8845209..0d48bf446fb 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -176,6 +176,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task /// public string? DedupAssembly { get; set; } + /// /// Debug option in llvm aot mode /// defaults to "nodebug" since some targes can't generate debug info /// From ef2fe6ea1ea1d03cccb207322a2194f7ec9e24b1 Mon Sep 17 00:00:00 2001 From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Tue, 20 Jul 2021 15:09:01 +0200 Subject: [PATCH 680/926] Complete Http2Stream after sending Reset or EndOfStream to server (#55835) It changes the order of Complete and Send Reset/Send EndOfStream operations to prevent creation of a new Http2Stream while the old one has not yet written the final frame to wire. Fixes #1586 --- .../Net/Http/SocketsHttpHandler/Http2Stream.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 0414a4f3fc3..aa31d88ee82 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -244,9 +244,8 @@ namespace System.Net.Http Debug.Assert(!sendReset); _requestCompletionState = StreamCompletionState.Failed; - Complete(); - SendReset(); + Complete(); } if (signalWaiter) @@ -270,11 +269,12 @@ namespace System.Net.Http Debug.Assert(_requestCompletionState == StreamCompletionState.InProgress, $"Request already completed with state={_requestCompletionState}"); _requestCompletionState = StreamCompletionState.Completed; + bool complete = false; if (_responseCompletionState != StreamCompletionState.InProgress) { // Note, we can reach this point if the response stream failed but cancellation didn't propagate before we finished. sendReset = _responseCompletionState == StreamCompletionState.Failed; - Complete(); + complete = true; } if (sendReset) @@ -287,6 +287,11 @@ namespace System.Net.Http // If this fails, it means that the connection is aborting and we will be reset. _connection.LogExceptions(_connection.SendEndStreamAsync(StreamId)); } + + if (complete) + { + Complete(); + } } } } @@ -392,6 +397,7 @@ namespace System.Net.Http if (sendReset) { SendReset(); + Complete(); } } @@ -414,7 +420,6 @@ namespace System.Net.Http if (_requestCompletionState != StreamCompletionState.InProgress) { sendReset = true; - Complete(); } } From 89d06508b87e524841f0194219ada6a3f24b34c3 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 20 Jul 2021 19:23:47 +0600 Subject: [PATCH 681/926] Disable implicit imports in tools (#55828) --- src/coreclr/tools/Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/Directory.Build.props b/src/coreclr/tools/Directory.Build.props index b8e4a4e912d..e699ed222e7 100644 --- a/src/coreclr/tools/Directory.Build.props +++ b/src/coreclr/tools/Directory.Build.props @@ -4,6 +4,7 @@ false false false + true From 27e2c3f9781276211a5bfe2c0215325996898986 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 20 Jul 2021 09:28:45 -0400 Subject: [PATCH 682/926] Fix % used in PerCoreLockedStacks (#55959) * Fix % used in PerCoreLockedStacks s_lockedStackCount will be a const in tier 1, and the JIT can optimize % by a const to something other than idiv. * Address PR feedback --- .../src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs index 3f759532614..fd4d242463c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs @@ -281,7 +281,7 @@ namespace System.Buffers // Try to push on to the associated stack first. If that fails, // round-robin through the other stacks. LockedStack[] stacks = _perCoreStacks; - int index = Thread.GetCurrentProcessorId() % stacks.Length; + int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)s_lockedStackCount); // mod by constant in tier 1 for (int i = 0; i < stacks.Length; i++) { if (stacks[index].TryPush(array)) return true; @@ -298,7 +298,7 @@ namespace System.Buffers // Try to pop from the associated stack first. If that fails, round-robin through the other stacks. T[]? arr; LockedStack[] stacks = _perCoreStacks; - int index = Thread.GetCurrentProcessorId() % s_lockedStackCount; // when ProcessorCount is a power of two, the JIT can optimize this in tier 1 + int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)s_lockedStackCount); // mod by constant in tier 1 for (int i = 0; i < stacks.Length; i++) { if ((arr = stacks[index].TryPop()) is not null) return arr; From 6ca2a9456ed1a88b4e6721827cb81ab022ea7653 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 20 Jul 2021 09:29:20 -0400 Subject: [PATCH 683/926] Replace a few unnecessary uses of Convert.ToString (#55831) * Replace a few unnecessary uses of Convert.ToString * Update src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs Co-authored-by: Christopher Watford <83599748+watfordsuzy@users.noreply.github.com> Co-authored-by: Christopher Watford <83599748+watfordsuzy@users.noreply.github.com> --- .../System.Console/src/System/TermInfo.cs | 2 +- .../src/System/Drawing/Color.cs | 2 +- .../src/System/Xml/Schema/ContentValidator.cs | 2 +- .../src/System/Xml/Xsl/XsltOld/NumberAction.cs | 14 ++------------ .../Security/Cryptography/CapiHelper.Windows.cs | 8 ++++---- .../System/Security/Principal/WindowsIdentity.cs | 8 ++++---- .../Syndication/Atom10FeedFormatter.cs | 2 +- .../ServiceModel/Syndication/Rss20FeedFormatter.cs | 2 +- .../src/System/Transactions/Enlistment.cs | 3 +-- .../src/System/Transactions/InternalTransaction.cs | 2 +- 10 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs index 19e94551a6f..529ff7fcb7a 100644 --- a/src/libraries/System.Console/src/System/TermInfo.cs +++ b/src/libraries/System.Console/src/System/TermInfo.cs @@ -131,7 +131,7 @@ namespace System _readAs32Bit = magic == MagicLegacyNumber ? false : magic == Magic32BitNumber ? true : - throw new InvalidOperationException(SR.Format(SR.IO_TermInfoInvalidMagicNumber, string.Concat("O" + Convert.ToString(magic, 8)))); // magic number was not recognized. Printing the magic number in octal. + throw new InvalidOperationException(SR.Format(SR.IO_TermInfoInvalidMagicNumber, "O" + Convert.ToString(magic, 8))); // magic number was not recognized. Printing the magic number in octal. _sizeOfInt = (_readAs32Bit) ? 4 : 2; _nameSectionNumBytes = ReadInt16(data, 2); diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs index a7f116edada..55ea746b2c9 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs @@ -402,7 +402,7 @@ namespace System.Drawing // if we reached here, just encode the value // - return Convert.ToString(value, 16); + return value.ToString("x"); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs index 876fcb6abc6..fc5cafd2279 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs @@ -876,7 +876,7 @@ namespace System.Xml.Schema public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) { LeftChild.Dump(bb, symbols, positions); - bb.Append($"{{{Convert.ToString(min, NumberFormatInfo.InvariantInfo)}, {Convert.ToString(max, NumberFormatInfo.InvariantInfo)}}}"); + bb.Append(NumberFormatInfo.InvariantInfo, $"{{{min}, {max}}}"); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs index f118809686f..6c5051d49ba 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs @@ -127,20 +127,10 @@ namespace System.Xml.Xsl.XsltOld } else { - str = Convert.ToString(val, CultureInfo.InvariantCulture); + str = val.ToString(CultureInfo.InvariantCulture); } - if (str.Length >= minLength) - { - return str; - } - else - { - StringBuilder sb = new StringBuilder(minLength); - sb.Append('0', minLength - str.Length); - sb.Append(str); - return sb.ToString(); - } + return str.PadLeft(minLength, '0'); } } diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs index c78a1721c72..b8d4841fee8 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs @@ -615,7 +615,7 @@ namespace Internal.NativeCrypto { case CryptGetKeyParamQueryType.KP_IV: if (!Interop.Advapi32.CryptSetKeyParam(safeKeyHandle, (int)keyParam, value, 0)) - throw new CryptographicException(SR.CryptSetKeyParam_Failed, Convert.ToString(GetErrorCode())); + throw new CryptographicException(SR.CryptSetKeyParam_Failed, GetErrorCode().ToString()); break; default: @@ -640,7 +640,7 @@ namespace Internal.NativeCrypto case CryptGetKeyParamQueryType.KP_MODE_BITS: case CryptGetKeyParamQueryType.KP_EFFECTIVE_KEYLEN: if (!Interop.Advapi32.CryptSetKeyParam(safeKeyHandle, (int)keyParam, ref value, 0)) - throw new CryptographicException(SR.CryptSetKeyParam_Failed, Convert.ToString(GetErrorCode())); + throw new CryptographicException(SR.CryptSetKeyParam_Failed, GetErrorCode().ToString()); break; default: @@ -716,7 +716,7 @@ namespace Internal.NativeCrypto CspProviderFlags.UseUserProtectedKey); if ((flags & keyFlags) != CspProviderFlags.NoFlags) { - throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, Convert.ToString(flags)), nameof(flags)); + throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, flags), nameof(flags)); } } } @@ -756,7 +756,7 @@ namespace Internal.NativeCrypto (keyType == CspAlgorithmType.Dss && dwAlgId != CALG_DSS_SIGN)) { hKey.Dispose(); - throw new CryptographicException(SR.Format(SR.Cryptography_CSP_WrongKeySpec, Convert.ToString(keyType))); + throw new CryptographicException(SR.Format(SR.Cryptography_CSP_WrongKeySpec, keyType)); } return hKey; diff --git a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs index 4bff4c48735..1079b819baf 100644 --- a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs +++ b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs @@ -1177,7 +1177,7 @@ namespace System.Security.Principal { claimType = ClaimTypes.WindowsDeviceGroup; Claim claim = new Claim(claimType, groupSid.Value, ClaimValueTypes.String, _issuerName, _issuerName, this); - claim.Properties.Add(ClaimTypes.WindowsSubAuthority, Convert.ToString(groupSid.IdentifierAuthority, CultureInfo.InvariantCulture)!); + claim.Properties.Add(ClaimTypes.WindowsSubAuthority, groupSid.IdentifierAuthority.ToString()); claim.Properties.Add(claimType, ""); instanceClaims.Add(claim); } @@ -1185,7 +1185,7 @@ namespace System.Security.Principal { claimType = ClaimTypes.DenyOnlyWindowsDeviceGroup; Claim claim = new Claim(claimType, groupSid.Value, ClaimValueTypes.String, _issuerName, _issuerName, this); - claim.Properties.Add(ClaimTypes.WindowsSubAuthority, Convert.ToString(groupSid.IdentifierAuthority, CultureInfo.InvariantCulture)!); + claim.Properties.Add(ClaimTypes.WindowsSubAuthority, groupSid.IdentifierAuthority.ToString()); claim.Properties.Add(claimType, ""); instanceClaims.Add(claim); } @@ -1244,7 +1244,7 @@ namespace System.Security.Principal for (int item = 0; item < windowsClaim.ValueCount; item++) { - Claim c = new Claim(windowsClaim.Name, Convert.ToString(intValues[item], CultureInfo.InvariantCulture), ClaimValueTypes.Integer64, _issuerName, _issuerName, this); + Claim c = new Claim(windowsClaim.Name, intValues[item].ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64, _issuerName, _issuerName, this); c.Properties.Add(propertyValue, string.Empty); instanceClaims.Add(c); } @@ -1257,7 +1257,7 @@ namespace System.Security.Principal for (int item = 0; item < windowsClaim.ValueCount; item++) { - Claim c = new Claim(windowsClaim.Name, Convert.ToString((ulong)uintValues[item], CultureInfo.InvariantCulture), ClaimValueTypes.UInteger64, _issuerName, _issuerName, this); + Claim c = new Claim(windowsClaim.Name, ((ulong)uintValues[item]).ToString(CultureInfo.InvariantCulture), ClaimValueTypes.UInteger64, _issuerName, _issuerName, this); c.Properties.Add(propertyValue, string.Empty); instanceClaims.Add(c); } diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs index 7778341f0d2..3549abab116 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs @@ -481,7 +481,7 @@ namespace System.ServiceModel.Syndication } if (link.Length != 0 && !link.AttributeExtensions.ContainsKey(s_atom10Length)) { - writer.WriteAttributeString(Atom10Constants.LengthTag, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); + writer.WriteAttributeString(Atom10Constants.LengthTag, link.Length.ToString(CultureInfo.InvariantCulture)); } if (!link.AttributeExtensions.ContainsKey(s_atom10Href)) { diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs index d6d8cb85dc9..eae365c0c67 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs @@ -1182,7 +1182,7 @@ namespace System.ServiceModel.Syndication } if (link.Length != 0 && !link.AttributeExtensions.ContainsKey(s_rss20Length)) { - writer.WriteAttributeString(Rss20Constants.LengthTag, Rss20Constants.Rss20Namespace, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); + writer.WriteAttributeString(Rss20Constants.LengthTag, Rss20Constants.Rss20Namespace, link.Length.ToString(CultureInfo.InvariantCulture)); } writer.WriteEndElement(); } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs index ffbf6d12ef8..71bdc979f2f 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Enlistment.cs @@ -226,8 +226,7 @@ namespace System.Transactions temp = new EnlistmentTraceIdentifier( Guid.Empty, new TransactionTraceIdentifier( - InternalTransaction.InstanceIdentifier + - Convert.ToString(Interlocked.Increment(ref InternalTransaction._nextHash), CultureInfo.InvariantCulture), + string.Create(CultureInfo.InvariantCulture, $"{InternalTransaction.InstanceIdentifier}{Interlocked.Increment(ref InternalTransaction._nextHash)}"), 0), _enlistmentId); } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs index e4633703561..56873516ed7 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/InternalTransaction.cs @@ -186,7 +186,7 @@ namespace System.Transactions if (!_traceIdentifierInited) { TransactionTraceIdentifier temp = new TransactionTraceIdentifier( - InstanceIdentifier + Convert.ToString(_transactionHash, CultureInfo.InvariantCulture), + string.Create(CultureInfo.InvariantCulture, $"{InstanceIdentifier}{_transactionHash}"), 0); _traceIdentifier = temp; _traceIdentifierInited = true; From e063533eb79eace045f43b41980cbed21c8d7365 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 20 Jul 2021 15:51:45 +0200 Subject: [PATCH 684/926] Fix x86 Linux build with clang < 10 (#55924) There is a bug in clang that was fixed in version 10 and that causes the build of the src/coreclr/vm/i386/jithelp.S to fail with 'error: cannot use more than one symbol in memory operand'. The problem is that it doesn't support the `offset` keyword and it thinks it is just another symbol. The fix is to use att syntax for the offending instruction. --- src/coreclr/vm/i386/jithelp.S | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/i386/jithelp.S b/src/coreclr/vm/i386/jithelp.S index dc56da1d177..4f227c9cf60 100644 --- a/src/coreclr/vm/i386/jithelp.S +++ b/src/coreclr/vm/i386/jithelp.S @@ -392,7 +392,9 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT 1: pop eax 2: - add eax, offset _GLOBAL_OFFSET_TABLE_+1 // (2b - 1b) +.att_syntax + addl $_GLOBAL_OFFSET_TABLE_+(2b-1b), %eax +.intel_syntax noprefix mov eax, dword ptr [eax + C_FUNC(JIT_WriteBarrierEAX_Loc)@GOT] xchg eax, dword ptr [esp] ret From 85cf53da2888914979e21c84c726cc98ac191391 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Tue, 20 Jul 2021 16:58:59 +0200 Subject: [PATCH 685/926] Disable QuicStreamTests_MsQuicProvider.ReadOutstanding_ReadAborted_Throws (#55997) Disable test: System.Net.Quic.Tests.QuicStreamTests_MsQuicProvider.ReadOutstanding_ReadAborted_Throws Tracked by #55948 --- .../System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 4890ebea5f3..252ecf1bb07 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -471,6 +471,7 @@ namespace System.Net.Quic.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55948")] public async Task ReadOutstanding_ReadAborted_Throws() { // aborting doesn't work properly on mock From 6b09e329a27b16d96f27f6f850838fd65d8d1d71 Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Tue, 20 Jul 2021 18:39:01 +0200 Subject: [PATCH 686/926] Fix #51171: Eager validation of named options (#55922) --- .../src/OptionsBuilderExtensions.cs | 2 +- .../src/ValidationHostedService.cs | 2 +- .../Microsoft.Extensions.Hosting/src/ValidatorOptions.cs | 4 ++-- .../tests/UnitTests/OptionsBuilderExtensionsTests.cs | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs index 94c8115d1a7..d24d54235e6 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/OptionsBuilderExtensions.cs @@ -34,7 +34,7 @@ namespace Microsoft.Extensions.DependencyInjection { // This adds an action that resolves the options value to force evaluation // We don't care about the result as duplicates are not important - vo.Validators[typeof(TOptions)] = () => options.Get(optionsBuilder.Name); + vo.Validators[(typeof(TOptions), optionsBuilder.Name)] = () => options.Get(optionsBuilder.Name); }); return optionsBuilder; diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs index 2bfc901a918..054dde6d61f 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs @@ -13,7 +13,7 @@ namespace Microsoft.Extensions.DependencyInjection { internal sealed class ValidationHostedService : IHostedService { - private readonly IDictionary _validators; + private readonly IDictionary<(Type, string), Action> _validators; public ValidationHostedService(IOptions validatorOptions) { diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs index c71d1ea5f0a..57998c3f485 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.DependencyInjection { internal sealed class ValidatorOptions { - // Maps each options type to a method that forces its evaluation, e.g. IOptionsMonitor.Get(name) - public IDictionary Validators { get; } = new Dictionary(); + // Maps each pair of a) options type and b) options name to a method that forces its evaluation, e.g. IOptionsMonitor.Get(name) + public IDictionary<(Type optionsType, string optionsName), Action> Validators { get; } = new Dictionary<(Type, string), Action>(); } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs index 5155fa46fa9..53153ac7f29 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs @@ -135,14 +135,14 @@ namespace Microsoft.Extensions.Hosting.Tests [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - private async Task ValidateOnStart_AddOptionsMultipleTimesForSameType_LastOneGetsTriggered() + private async Task ValidateOnStart_AddNamedOptionsMultipleTimesForSameType_BothGetTriggered() { bool firstOptionsBuilderTriggered = false; bool secondOptionsBuilderTriggered = false; var hostBuilder = CreateHostBuilder(services => { services.AddOptions("bad_configuration1") - .Configure(o => o.Boolean = false) + .Configure(o => o.Boolean = true) .Validate(o => { firstOptionsBuilderTriggered = true; @@ -175,7 +175,7 @@ namespace Microsoft.Extensions.Hosting.Tests ValidateFailure(error, 2, "Boolean", "Integer"); } - Assert.False(firstOptionsBuilderTriggered); + Assert.True(firstOptionsBuilderTriggered); Assert.True(secondOptionsBuilderTriggered); } From 0a5e93b09fe92cf866456552eef78a95bf6fdf27 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 20 Jul 2021 10:07:20 -0700 Subject: [PATCH 687/926] mitigation for quic tests hangs (#55985) --- .../Net/Quic/Implementations/MsQuic/MsQuicStream.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index d5792c9f0a8..773943b41e2 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -651,7 +651,9 @@ namespace System.Net.Quic.Implementations.MsQuic byte[] rentedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { - int readLength = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask().GetAwaiter().GetResult(); + Task t = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask(); + ((IAsyncResult)t).AsyncWaitHandle.WaitOne(); + int readLength = t.GetAwaiter().GetResult(); rentedBuffer.AsSpan(0, readLength).CopyTo(buffer); return readLength; } @@ -666,7 +668,9 @@ namespace System.Net.Quic.Implementations.MsQuic ThrowIfDisposed(); // TODO: optimize this. - WriteAsync(buffer.ToArray()).AsTask().GetAwaiter().GetResult(); + Task t = WriteAsync(buffer.ToArray()).AsTask(); + ((IAsyncResult)t).AsyncWaitHandle.WaitOne(); + t.GetAwaiter().GetResult(); } // MsQuic doesn't support explicit flushing From ef48adfc7c97e0ab37f0136e0588e890dfa60104 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 20 Jul 2021 10:20:51 -0700 Subject: [PATCH 688/926] remove unnecessary call (#55795) * remove unnecessary call * fix typo --- .../Net/Quic/Implementations/MsQuic/MsQuicConnection.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 39bd5e80f6c..ef881266891 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -27,7 +27,7 @@ namespace System.Net.Quic.Implementations.MsQuic // TODO: remove this. // This is only used for client-initiated connections, and isn't needed even then once Connect() has been called. - private readonly SafeMsQuicConfigurationHandle? _configuration; + private SafeMsQuicConfigurationHandle? _configuration; private readonly State _state = new State(); private int _disposed; @@ -188,9 +188,6 @@ namespace System.Net.Quic.Implementations.MsQuic _state.StateGCHandle = GCHandle.Alloc(_state); try { - // this handle is ref counted by MsQuic, so safe to dispose here. - using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); - uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, @@ -575,6 +572,10 @@ namespace System.Net.Quic.Implementations.MsQuic (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); + + // this handle is ref counted by MsQuic, so safe to dispose here. + _configuration.Dispose(); + _configuration = null; } catch { From f100379cb705322bf0a47680cc9220f069ed3754 Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Tue, 20 Jul 2021 19:21:45 +0200 Subject: [PATCH 689/926] [QUIC] Fix cancelling shutdown completion source (#55993) Fixes #55706 --- .../src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 773943b41e2..970236048b2 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -607,7 +607,6 @@ namespace System.Net.Quic.Implementations.MsQuic } } - // TODO do anything to stop writes? using CancellationTokenRegistration registration = cancellationToken.UnsafeRegister(static (s, token) => { var state = (State)s!; @@ -623,7 +622,7 @@ namespace System.Net.Quic.Implementations.MsQuic if (shouldComplete) { - state.ShutdownWriteCompletionSource.SetException( + state.ShutdownCompletionSource.SetException( ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException("Wait for shutdown was canceled", token))); } }, _state); From b29d06cfdfad15202f41def35a4dee8d869a0056 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 20 Jul 2021 10:36:08 -0700 Subject: [PATCH 690/926] Prepare for arcade changes that enable testing of meta-packages (#55976) --- .../workaroundDowngrade.targets | 6 ++++++ src/libraries/pkg/test/testPackages.proj | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/workaroundDowngrade.targets diff --git a/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/workaroundDowngrade.targets b/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/workaroundDowngrade.targets new file mode 100644 index 00000000000..eed2ad859c3 --- /dev/null +++ b/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/workaroundDowngrade.targets @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index 83ad6233b64..e4080b6116e 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -33,6 +33,8 @@ + + From cbfa7cf5dfeb0fb31d6b4d401463352d45a6cc23 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Tue, 20 Jul 2021 20:40:19 +0300 Subject: [PATCH 691/926] Add profile-use-only mode for MultiCoreJit (#55005) * Add profile-use-only mode for MultiCoreJit - memory consumption is reduced if profile is not gathered - disk/flash life is increased if profile is not saved each time * Remove non-set m_fAppxMode --- src/coreclr/inc/clrconfigvalues.h | 1 + src/coreclr/vm/multicorejit.cpp | 63 ++++++++++++++++----------- src/coreclr/vm/multicorejit.h | 4 +- src/coreclr/vm/multicorejitimpl.h | 39 +++++++++++++---- src/coreclr/vm/multicorejitplayer.cpp | 24 +++------- 5 files changed, 77 insertions(+), 54 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index e2f1a63a20f..60cdfe9ada3 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -371,6 +371,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynami RETAIL_CONFIG_STRING_INFO(INTERNAL_MultiCoreJitProfile, W("MultiCoreJitProfile"), "If set, use the file to store/control multi-core JIT.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitProfileWriteDelay, W("MultiCoreJitProfileWriteDelay"), 12, "Set the delay after which the multi-core JIT profile will be written to disk.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitMinNumCpus, W("MultiCoreJitMinNumCpus"), 2, "Minimum number of cpus that must be present to allow MultiCoreJit usage.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitNoProfileGather, W("MultiCoreJitNoProfileGather"), 0, "Set to 1 to disable profile gathering (but leave possibly enabled profile usage).") #endif diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp index ae8582f3cf7..380a007c780 100644 --- a/src/coreclr/vm/multicorejit.cpp +++ b/src/coreclr/vm/multicorejit.cpp @@ -144,6 +144,11 @@ HRESULT MulticoreJitRecorder::WriteOutput() HRESULT hr = E_FAIL; + if (m_JitInfoArray == nullptr || m_ModuleList == nullptr) + { + return S_OK; + } + // Go into preemptive mode for file operations GCX_PREEMP(); @@ -386,6 +391,9 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream) HRESULT hr = S_OK; + _ASSERTE(m_JitInfoArray != nullptr); + _ASSERTE(m_ModuleList != nullptr); + // Preprocessing Methods LONG skipped = 0; @@ -470,7 +478,6 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream) header.shortCounters[ 7] = m_stats.m_nTotalDelay; header.shortCounters[ 8] = m_stats.m_nDelayCount; header.shortCounters[ 9] = m_stats.m_nWalkBack; - header.shortCounters[10] = m_fAppxMode; _ASSERTE(HEADER_W_COUNTER >= 14); @@ -567,6 +574,8 @@ unsigned MulticoreJitRecorder::FindModule(Module * pModule) { LIMITED_METHOD_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + for (unsigned i = 0 ; i < m_ModuleCount; i ++) { if (m_ModuleList[i].pModule == pModule) @@ -585,6 +594,8 @@ unsigned MulticoreJitRecorder::GetOrAddModuleIndex(Module * pModule) { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + unsigned slot = FindModule(pModule); if ((slot == UINT_MAX) && (m_ModuleCount < MAX_MODULES)) @@ -604,7 +615,10 @@ void MulticoreJitRecorder::RecordMethodInfo(unsigned moduleIndex, MethodDesc * p { LIMITED_METHOD_CONTRACT; - if (m_JitInfoArray != nullptr && m_JitInfoCount < (LONG) MAX_METHODS) + _ASSERTE(m_JitInfoArray != nullptr); + _ASSERTE(m_ModuleList != nullptr); + + if (m_JitInfoCount < (LONG) MAX_METHODS) { m_ModuleList[moduleIndex].methodCount++; m_JitInfoArray[m_JitInfoCount++].PackMethod(moduleIndex, pMethod, application); @@ -615,6 +629,8 @@ unsigned MulticoreJitRecorder::RecordModuleInfo(Module * pModule) { LIMITED_METHOD_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + // pModule could be unknown at this point (modules not enumerated, no event received yet) unsigned moduleIndex = GetOrAddModuleIndex(pModule); @@ -675,7 +691,6 @@ void MulticoreJitRecorder::RecordOrUpdateModuleInfo(FileLoadLevel needLevel, uns class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator { MulticoreJitRecorder * m_pRecorder; - bool m_fAppxMode; HRESULT OnModule(Module * pModule) { @@ -688,7 +703,7 @@ class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator } CONTRACTL_END; - if (MulticoreJitManager::IsSupportedModule(pModule, false, m_fAppxMode)) + if (MulticoreJitManager::IsSupportedModule(pModule, false)) { m_pRecorder->AddModuleDependency(pModule, MulticoreJitManager::GetModuleFileLoadLevel(pModule)); } @@ -697,10 +712,9 @@ class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator } public: - MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder, bool fAppxMode) + MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder) { m_pRecorder = pRecorder; - m_fAppxMode = fAppxMode; } }; @@ -710,6 +724,8 @@ void MulticoreJitRecorder::AddModuleDependency(Module * pModule, FileLoadLevel l { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + MulticoreJitTrace(("AddModuleDependency(%s, %d)", pModule->GetSimpleName(), loadLevel)); _FireEtwMulticoreJitA(W("ADDMODULEDEPENDENCY"), pModule->GetSimpleName(), loadLevel, 0, 0); @@ -734,6 +750,8 @@ DWORD MulticoreJitRecorder::EncodeModule(Module * pReferencedModule) { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + unsigned slot = GetOrAddModuleIndex(pReferencedModule); FileLoadLevel loadLevel = MulticoreJitManager::GetModuleFileLoadLevel(pReferencedModule); @@ -834,19 +852,19 @@ void MulticoreJitRecorder::PreRecordFirstMethod() m_fFirstMethod = false; { - MulticoreJitRecorderModuleEnumerator enumerator(this, m_fAppxMode); + MulticoreJitRecorderModuleEnumerator enumerator(this); enumerator.EnumerateLoadedModules(m_pDomain); } - // When running under Appx or CoreCLR for K, AppDomain is normally not shut down properly (CLR in hybrid case, or Alt-F4 shutdown), + // When running under CoreCLR for K, AppDomain is normally not shut down properly (CLR in hybrid case, or Alt-F4 shutdown), // So we only allow writing out after profileWriteTimeout seconds { // Get the timeout in seconds. int profileWriteTimeout = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfileWriteDelay); #ifndef TARGET_UNIX - // Using the same threadpool timer used by UsageLog to write out profile when running under Appx or CoreCLR. + // Using the same threadpool timer used by UsageLog to write out profile when running under CoreCLR. MulticoreJitManager & manager = m_pDomain->GetMulticoreJitManager(); s_delayedWriteTimer = CreateThreadpoolTimer(MulticoreJitRecorder::WriteMulticoreJitProfiler, &manager, NULL); @@ -876,7 +894,7 @@ void MulticoreJitRecorder::RecordMethodJitOrLoad(MethodDesc * pMethod, bool appl Module * pModule = pMethod->GetModule_NoLogging(); // Skip methods from non-supported modules - if (! MulticoreJitManager::IsSupportedModule(pModule, true, m_fAppxMode)) + if (! MulticoreJitManager::IsSupportedModule(pModule, true)) { return; } @@ -1019,8 +1037,7 @@ HRESULT MulticoreJitRecorder::StartProfile(const WCHAR * pRoot, const WCHAR * pF NewHolder player(new (nothrow) MulticoreJitProfilePlayer( m_pBinderContext, - nSession, - m_fAppxMode)); + nSession)); if (player == NULL) { @@ -1185,13 +1202,17 @@ void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBin if ((pProfile != NULL) && (pProfile[0] != 0)) // Ignore empty file name, just same as StopProfile { + bool gatherProfile = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitNoProfileGather) == 0; + MulticoreJitRecorder * pRecorder = new (nothrow) MulticoreJitRecorder( pDomain, pBinderContext, - m_fAppxMode); + gatherProfile); if (pRecorder != NULL) { + gatherProfile = pRecorder->CanGatherProfile(); + m_pMulticoreJitRecorder = pRecorder; LONG sessionID = m_ProfileSession.Increment(); @@ -1200,16 +1221,9 @@ void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBin MulticoreJitTrace(("MulticoreJitRecorder session %d created: %x", sessionID, hr)); - if (m_fAppxMode) // In Appx mode, recorder is only enabled when file exists, but header is bad (e.g. zero-length) + if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Ignore COR_E_BADIMAGEFORMAT, always record new profile { - if (hr == COR_E_BADIMAGEFORMAT) - { - m_fRecorderActive = true; - } - } - else if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Otherwise, ignore COR_E_BADIMAGEFORMAT, alway record new profile - { - m_fRecorderActive = true; + m_fRecorderActive = gatherProfile; } _FireEtwMulticoreJit(W("STARTPROFILE"), W("Recorder"), m_fRecorderActive, hr, 0); @@ -1349,7 +1363,6 @@ MulticoreJitManager::MulticoreJitManager() m_fSetProfileRootCalled = 0; m_fAutoStartCalled = 0; m_fRecorderActive = false; - m_fAppxMode = false; m_playerLock.Init(CrstMulticoreJitManager, (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN)); m_MulticoreJitCodeStorage.Init(); @@ -1382,7 +1395,7 @@ void MulticoreJitManager::RecordModuleLoad(Module * pModule, FileLoadLevel loadL if (m_fRecorderActive) { - if(IsSupportedModule(pModule, false, m_fAppxMode)) // Filter out unsupported module + if(IsSupportedModule(pModule, false)) // Filter out unsupported module { CrstHolder hold(& m_playerLock); @@ -1531,7 +1544,7 @@ void MulticoreJitManager::DisableMulticoreJit() // static DWORD MulticoreJitManager::EncodeModuleHelper(void * pModuleContext, Module * pReferencedModule) { - STANDARD_VM_CONTRACT + STANDARD_VM_CONTRACT; if (pModuleContext == NULL || pReferencedModule == NULL) { diff --git a/src/coreclr/vm/multicorejit.h b/src/coreclr/vm/multicorejit.h index 3b7bae0a926..58656ec21fd 100644 --- a/src/coreclr/vm/multicorejit.h +++ b/src/coreclr/vm/multicorejit.h @@ -207,7 +207,6 @@ private: LONG m_fSetProfileRootCalled; // SetProfileRoot has been called LONG m_fAutoStartCalled; bool m_fRecorderActive; // Manager open for recording/event, turned on when initialized properly, turned off when at full capacity - bool m_fAppxMode; CrstExplicitInit m_playerLock; // Thread protection (accessing m_pMulticoreJitRecorder) MulticoreJitPlayerStat m_stats; // Statistics: normally gathered by player, written to profile @@ -229,7 +228,6 @@ public: m_fSetProfileRootCalled = 0; m_fAutoStartCalled = 0; m_fRecorderActive = false; - m_fAppxMode = false; } ~MulticoreJitManager() @@ -299,7 +297,7 @@ public: static void DisableMulticoreJit(); - static bool IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx); + static bool IsSupportedModule(Module * pModule, bool fMethodJit); static FileLoadLevel GetModuleFileLoadLevel(Module * pModule); diff --git a/src/coreclr/vm/multicorejitimpl.h b/src/coreclr/vm/multicorejitimpl.h index 234493f0b3e..d1f05816788 100644 --- a/src/coreclr/vm/multicorejitimpl.h +++ b/src/coreclr/vm/multicorejitimpl.h @@ -212,7 +212,7 @@ public: ModuleRecord(unsigned lenName = 0, unsigned lenAssemblyName = 0); - bool MatchWithModule(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const; + bool MatchWithModule(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shouldAbort) const; unsigned ModuleNameLen() const { @@ -282,7 +282,6 @@ private: MulticoreJitPlayerStat & m_stats; MulticoreJitCounter & m_appdomainSession; bool m_shouldAbort; - bool m_fAppxMode; Thread * m_pThread; @@ -320,7 +319,7 @@ private: public: - MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode); + MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession); ~MulticoreJitProfilePlayer(); @@ -619,16 +618,15 @@ private: SString m_fullFileName; MulticoreJitPlayerStat & m_stats; - RecorderModuleInfo m_ModuleList[MAX_MODULES]; + RecorderModuleInfo * m_ModuleList; unsigned m_ModuleCount; unsigned m_ModuleDepCount; - RecorderInfo m_JitInfoArray[MAX_METHODS]; + RecorderInfo * m_JitInfoArray; LONG m_JitInfoCount; bool m_fFirstMethod; bool m_fAborted; - bool m_fAppxMode; #ifndef TARGET_UNIX static TP_TIMER * s_delayedWriteTimer; @@ -657,21 +655,32 @@ private: public: - MulticoreJitRecorder(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, bool fAppxMode) + MulticoreJitRecorder(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, bool fRecorderActive) : m_stats(pDomain->GetMulticoreJitManager().GetStats()) + , m_ModuleList(nullptr) + , m_JitInfoArray(nullptr) { LIMITED_METHOD_CONTRACT; m_pDomain = pDomain; m_pBinderContext = pBinderContext; + + if (fRecorderActive) + { + m_ModuleList = new (nothrow) RecorderModuleInfo[MAX_MODULES]; + } m_ModuleCount = 0; + m_ModuleDepCount = 0; + if (fRecorderActive) + { + m_JitInfoArray = new (nothrow) RecorderInfo[MAX_METHODS]; + } m_JitInfoCount = 0; m_fFirstMethod = true; m_fAborted = false; - m_fAppxMode = fAppxMode; m_stats.Clear(); @@ -688,14 +697,26 @@ public: CloseThreadpoolTimer(pTimer); } } +#endif // !TARGET_UNIX ~MulticoreJitRecorder() { LIMITED_METHOD_CONTRACT; + delete[] m_ModuleList; + delete[] m_JitInfoArray; + +#ifndef TARGET_UNIX CloseTimer(); - } #endif // !TARGET_UNIX + } + + bool CanGatherProfile() + { + LIMITED_METHOD_CONTRACT; + + return m_ModuleList != NULL && m_JitInfoArray != NULL; + } bool IsAtFullCapacity() const { diff --git a/src/coreclr/vm/multicorejitplayer.cpp b/src/coreclr/vm/multicorejitplayer.cpp index da75fe43b39..a26e981bb0c 100644 --- a/src/coreclr/vm/multicorejitplayer.cpp +++ b/src/coreclr/vm/multicorejitplayer.cpp @@ -233,7 +233,7 @@ public: return false; } - bool MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx); + bool MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort); #ifdef MULTICOREJIT_LOGGING void Dump(const WCHAR * prefix, int index); @@ -242,11 +242,11 @@ public: }; -bool PlayerModuleInfo::MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx) +bool PlayerModuleInfo::MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort) { STANDARD_VM_CONTRACT; - if ((m_pModule == NULL) && m_pRecord->MatchWithModule(version, gotVersion, pModule, shortAbort, fAppx)) + if ((m_pModule == NULL) && m_pRecord->MatchWithModule(version, gotVersion, pModule, shortAbort)) { m_pModule = pModule; m_curLevel = (int) MulticoreJitManager::GetModuleFileLoadLevel(pModule); @@ -321,7 +321,7 @@ void PlayerModuleInfo::Dump(const WCHAR * prefix, int index) const unsigned EmptyToken = 0xFFFFFFFF; -bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const +bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion, Module * pModule, bool & shouldAbort) const { STANDARD_VM_CONTRACT; @@ -332,15 +332,6 @@ bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion if ((len == lenModuleName) && (memcmp(pModuleName, pName, lenModuleName) == 0)) { - // Ignore version check on play back when running under Appx (also GetModuleVersion is expensive) - - // For Appx, multicore JIT profile is pre-generated by application vendor, installed together with the package. - // So it may not have exact match with its own assemblies, and assemblies installed on the system. - if (fAppx) - { - return true; - } - if (! gotVersion) // Calling expensive GetModuleVersion only when simple name matches { gotVersion = true; @@ -369,7 +360,7 @@ bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion } -MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode) +MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession) : m_stats(::GetAppDomain()->GetMulticoreJitManager().GetStats()), m_appdomainSession(::GetAppDomain()->GetMulticoreJitManager().GetProfileSession()) { LIMITED_METHOD_CONTRACT; @@ -383,7 +374,6 @@ MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderCon m_nMissingModule = 0; m_nLoadedModuleCount = 0; m_shouldAbort = false; - m_fAppxMode = fAppxMode; m_pThread = NULL; m_pFileBuffer = NULL; @@ -438,7 +428,7 @@ bool MulticoreJitManager::ModuleHasNoCode(Module * pModule) // We only support default load context, non dynamic module, non domain neutral (needed for dependency) -bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx) +bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit) { CONTRACTL { @@ -651,7 +641,7 @@ HRESULT MulticoreJitProfilePlayer::OnModule(Module * pModule) // Match with simple name, and then version/flag/guid for (unsigned i = 0; i < m_moduleCount; i ++) { - if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort, m_fAppxMode)) + if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort)) { m_nLoadedModuleCount ++; return hr; From 775ecaefe7aa3be85858e612fef782f0e1991f51 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Tue, 20 Jul 2021 19:49:33 +0200 Subject: [PATCH 692/926] Disable SendStreamLimitRequestsConcurrently_Succeeds (#56001) Disabled test tracked by #56000 --- .../tests/FunctionalTests/HttpClientHandlerTest.Http3.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 80c97b15e6c..392cc9efbe7 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -118,6 +118,7 @@ namespace System.Net.Http.Functional.Tests } [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56000")] [InlineData(10)] [InlineData(100)] [InlineData(1000)] From e3cf0028c7c167ade4dd3ce4e0ee95659cab66b5 Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Madero Date: Tue, 20 Jul 2021 12:13:19 -0700 Subject: [PATCH 693/926] Fix System.Data.SqlClient dependency version on compat pack (#55956) --- .../Microsoft.Windows.Compatibility.pkgproj | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libraries/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj b/src/libraries/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj index caa2fc5f242..c1a3f16e19e 100644 --- a/src/libraries/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj +++ b/src/libraries/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj @@ -44,9 +44,6 @@ - - - @@ -59,7 +56,8 @@ - + + From b1af9bf2b6cd9f60601eb6eefb4c3a5f7e659718 Mon Sep 17 00:00:00 2001 From: Mateo Torres-Ruiz Date: Tue, 20 Jul 2021 12:38:28 -0700 Subject: [PATCH 694/926] Set use shell execute (#56010) --- .../managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs index c0e979dd28d..1ed40191989 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs @@ -251,6 +251,7 @@ namespace Microsoft.NET.HostModel.AppHost Arguments = $"-s - \"{appHostPath}\"", FileName = codesign, RedirectStandardError = true, + UseShellExecute = false, }; using (var p = Process.Start(psi)) From 5026c2582f6d936c2f3a638a8866b9bf5caba59f Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Tue, 20 Jul 2021 22:31:55 +0200 Subject: [PATCH 695/926] Disable test Connect_DualMode_DnsConnect_RetrievedEndPoints_Success on Linux (#56003) Disabled test tracked by #54677 and #55709 --- .../System.Net.Sockets/tests/FunctionalTests/Connect.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index a491fada918..e4c64fc2e35 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -109,6 +109,8 @@ namespace System.Net.Sockets.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54677", TestPlatforms.Linux)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55709", TestPlatforms.Linux)] public async Task Connect_DualMode_DnsConnect_RetrievedEndPoints_Success() { var localhostAddresses = Dns.GetHostAddresses("localhost"); From b835c8c668a914d060a6b1029ef1e7cecdd5ad44 Mon Sep 17 00:00:00 2001 From: FatTiger Date: Wed, 21 Jul 2021 04:53:46 +0800 Subject: [PATCH 696/926] Use MethodInfo.Equals to judge equality and remove special comparator Fix #453285 --- .../Reflection/DispatchProxyGenerator.cs | 85 ++----------------- 1 file changed, 5 insertions(+), 80 deletions(-) diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs index 78e1cd2095f..1a2a90a2bf6 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs +++ b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs @@ -302,7 +302,7 @@ namespace System.Reflection _tb.AddInterfaceImplementation(iface); // AccessorMethods -> Metadata mappings. - var propertyMap = new Dictionary(MethodInfoEqualityComparer.Instance); + var propertyMap = new Dictionary(); foreach (PropertyInfo pi in iface.GetRuntimeProperties()) { var ai = new PropertyAccessorInfo(pi.GetMethod, pi.SetMethod); @@ -312,7 +312,7 @@ namespace System.Reflection propertyMap[pi.SetMethod] = ai; } - var eventMap = new Dictionary(MethodInfoEqualityComparer.Instance); + var eventMap = new Dictionary(); foreach (EventInfo ei in iface.GetRuntimeEvents()) { var ai = new EventAccessorInfo(ei.AddMethod, ei.RemoveMethod, ei.RaiseMethod); @@ -336,7 +336,7 @@ namespace System.Reflection MethodBuilder mdb = AddMethodImpl(mi, methodInfoIndex); if (propertyMap.TryGetValue(mi, out PropertyAccessorInfo? associatedProperty)) { - if (MethodInfoEqualityComparer.Instance.Equals(associatedProperty.InterfaceGetMethod, mi)) + if (mi.Equals(associatedProperty.InterfaceGetMethod)) associatedProperty.GetMethodBuilder = mdb; else associatedProperty.SetMethodBuilder = mdb; @@ -344,9 +344,9 @@ namespace System.Reflection if (eventMap.TryGetValue(mi, out EventAccessorInfo? associatedEvent)) { - if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceAddMethod, mi)) + if (mi.Equals(associatedEvent.InterfaceAddMethod)) associatedEvent.AddMethodBuilder = mdb; - else if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceRemoveMethod, mi)) + else if (mi.Equals(associatedEvent.InterfaceRemoveMethod)) associatedEvent.RemoveMethodBuilder = mdb; else associatedEvent.RaiseMethodBuilder = mdb; @@ -814,81 +814,6 @@ namespace System.Reflection InterfaceRaiseMethod = interfaceRaiseMethod; } } - - private sealed class MethodInfoEqualityComparer : EqualityComparer - { - public static readonly MethodInfoEqualityComparer Instance = new MethodInfoEqualityComparer(); - - private MethodInfoEqualityComparer() { } - - public sealed override bool Equals(MethodInfo? left, MethodInfo? right) - { - if (ReferenceEquals(left, right)) - return true; - - if (left == null) - return right == null; - else if (right == null) - return false; - - // TODO: switch to use MemberInfo.MetadataToken here. - // It compares honestly referring ECMA-335 I.8.6.1.6 Signature Matching. - if (!Equals(left.DeclaringType, right.DeclaringType)) - return false; - - if (!Equals(left.ReturnType, right.ReturnType)) - return false; - - if (left.CallingConvention != right.CallingConvention) - return false; - - if (left.IsStatic != right.IsStatic) - return false; - - if (left.Name != right.Name) - return false; - - Type[] leftGenericParameters = left.GetGenericArguments(); - Type[] rightGenericParameters = right.GetGenericArguments(); - if (leftGenericParameters.Length != rightGenericParameters.Length) - return false; - - for (int i = 0; i < leftGenericParameters.Length; i++) - { - if (!Equals(leftGenericParameters[i], rightGenericParameters[i])) - return false; - } - - ParameterInfo[] leftParameters = left.GetParameters(); - ParameterInfo[] rightParameters = right.GetParameters(); - if (leftParameters.Length != rightParameters.Length) - return false; - - for (int i = 0; i < leftParameters.Length; i++) - { - if (!Equals(leftParameters[i].ParameterType, rightParameters[i].ParameterType)) - return false; - } - - return true; - } - - public sealed override int GetHashCode(MethodInfo obj) - { - if (obj == null) - return 0; - - Debug.Assert(obj.DeclaringType != null); - int hashCode = obj.DeclaringType!.GetHashCode(); - hashCode ^= obj.Name.GetHashCode(); - foreach (ParameterInfo parameter in obj.GetParameters()) - { - hashCode ^= parameter.ParameterType.GetHashCode(); - } - - return hashCode; - } - } } } } From 978b0dbc5e1475be917f4237edf5b47f4c124dba Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Tue, 20 Jul 2021 23:08:51 +0200 Subject: [PATCH 697/926] HTTP/3 & QUIC: fix abort read on dispose and cancellation (#55724) Fix abort on cancellation for HTTP/3 content stream, fix dispose when read was aborted by cancellation token. Fixes #48624 --- .../SocketsHttpHandler/Http3RequestStream.cs | 9 +- .../HttpClientHandlerTest.Headers.cs | 6 + .../HttpClientHandlerTest.Http3.cs | 180 ++++++++++++++++++ .../Implementations/MsQuic/MsQuicStream.cs | 15 +- .../tests/FunctionalTests/QuicStreamTests.cs | 3 +- 5 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 63427bb96b5..19d8eb20262 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -102,8 +102,6 @@ namespace System.Net.Http private void DisposeSyncHelper() { _connection.RemoveStream(_stream); - _connection = null!; - _stream = null!; _sendBuffer.Dispose(); _recvBuffer.Dispose(); @@ -1107,7 +1105,10 @@ namespace System.Net.Http { switch (ex) { + // Peer aborted the stream case QuicStreamAbortedException _: + // User aborted the stream + case QuicOperationAbortedException _: throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex)); case QuicConnectionAbortedException _: // Our connection was reset. Start aborting the connection. @@ -1118,11 +1119,11 @@ namespace System.Net.Http _connection.Abort(ex); throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex)); case OperationCanceledException oce when oce.CancellationToken == cancellationToken: - _stream.AbortWrite((long)Http3ErrorCode.RequestCancelled); + _stream.AbortRead((long)Http3ErrorCode.RequestCancelled); ExceptionDispatchInfo.Throw(ex); // Rethrow. return; // Never reached. default: - _stream.AbortWrite((long)Http3ErrorCode.InternalError); + _stream.AbortRead((long)Http3ErrorCode.InternalError); throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex)); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index 7a1ceaf88c7..9f03c1750e4 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -213,6 +213,12 @@ namespace System.Net.Http.Functional.Tests [InlineData("RandomCustomHeader", 12345)] public async Task GetAsync_LargeHeader_Success(string headerName, int headerValueLength) { + if (UseVersion == HttpVersion.Version30) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55508")] + return; + } + var rand = new Random(42); string headerValue = string.Concat(Enumerable.Range(0, headerValueLength).Select(_ => (char)('A' + rand.Next(26)))); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 392cc9efbe7..0817b72987d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -445,6 +445,186 @@ namespace System.Net.Http.Functional.Tests } } + public enum CancellationType + { + Dispose, + CancellationToken + } + + [ConditionalTheory(nameof(IsMsQuicSupported))] + [InlineData(CancellationType.Dispose)] + [InlineData(CancellationType.CancellationToken)] + public async Task ResponseCancellation_ServerReceivesCancellation(CancellationType type) + { + if (UseQuicImplementationProvider != QuicImplementationProviders.MsQuic) + { + return; + } + + using Http3LoopbackServer server = CreateHttp3LoopbackServer(); + + using var clientDone = new SemaphoreSlim(0); + using var serverDone = new SemaphoreSlim(0); + + Task serverTask = Task.Run(async () => + { + using Http3LoopbackConnection connection = (Http3LoopbackConnection)await server.EstablishGenericConnectionAsync(); + using Http3LoopbackStream stream = await connection.AcceptRequestStreamAsync(); + + HttpRequestData request = await stream.ReadRequestDataAsync().ConfigureAwait(false); + + int contentLength = 2*1024*1024; + var headers = new List(); + headers.Append(new HttpHeaderData("Content-Length", contentLength.ToString(CultureInfo.InvariantCulture))); + + await stream.SendResponseHeadersAsync(HttpStatusCode.OK, headers).ConfigureAwait(false); + await stream.SendDataFrameAsync(new byte[1024]).ConfigureAwait(false); + + await clientDone.WaitAsync(); + + // It is possible that PEER_RECEIVE_ABORTED event will arrive with a significant delay after peer calls AbortReceive + // In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event) + // We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually + + var ex = await Assert.ThrowsAsync(() => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(10))); + Assert.Equal((type == CancellationType.CancellationToken ? 268 : 0xffffffff), ex.ErrorCode); + + serverDone.Release(); + }); + + Task clientTask = Task.Run(async () => + { + using HttpClient client = CreateHttpClient(); + + using HttpRequestMessage request = new() + { + Method = HttpMethod.Get, + RequestUri = server.Address, + Version = HttpVersion30, + VersionPolicy = HttpVersionPolicy.RequestVersionExact + }; + HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).WaitAsync(TimeSpan.FromSeconds(10)); + + Stream stream = await response.Content.ReadAsStreamAsync(); + + int bytesRead = await stream.ReadAsync(new byte[1024]); + Assert.Equal(1024, bytesRead); + + var cts = new CancellationTokenSource(200); + + if (type == CancellationType.Dispose) + { + cts.Token.Register(() => response.Dispose()); + } + CancellationToken readCt = type == CancellationType.CancellationToken ? cts.Token : default; + + Exception ex = await Assert.ThrowsAnyAsync(() => stream.ReadAsync(new byte[1024], cancellationToken: readCt).AsTask()); + + if (type == CancellationType.CancellationToken) + { + Assert.IsType(ex); + } + else + { + var ioe = Assert.IsType(ex); + var hre = Assert.IsType(ioe.InnerException); + Assert.IsType(hre.InnerException); + } + + clientDone.Release(); + await serverDone.WaitAsync(); + }); + + await new[] { clientTask, serverTask }.WhenAllOrAnyFailed(20_000); + } + + [Fact] + public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success() + { + if (UseQuicImplementationProvider != QuicImplementationProviders.MsQuic) + { + return; + } + + using Http3LoopbackServer server = CreateHttp3LoopbackServer(); + + using var clientDone = new SemaphoreSlim(0); + using var serverDone = new SemaphoreSlim(0); + + Task serverTask = Task.Run(async () => + { + using Http3LoopbackConnection connection = (Http3LoopbackConnection)await server.EstablishGenericConnectionAsync(); + using Http3LoopbackStream stream = await connection.AcceptRequestStreamAsync(); + + HttpRequestData request = await stream.ReadRequestDataAsync().ConfigureAwait(false); + + int contentLength = 2*1024*1024; + var headers = new List(); + headers.Append(new HttpHeaderData("Content-Length", contentLength.ToString(CultureInfo.InvariantCulture))); + + await stream.SendResponseHeadersAsync(HttpStatusCode.OK, headers).ConfigureAwait(false); + await stream.SendDataFrameAsync(new byte[1024]).ConfigureAwait(false); + + await clientDone.WaitAsync(); + + // It is possible that PEER_RECEIVE_ABORTED event will arrive with a significant delay after peer calls AbortReceive + // In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event) + // We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually + + var ex = await Assert.ThrowsAsync(() => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(20))); + // exact error code depends on who won the race + Assert.True(ex.ErrorCode == 268 /* cancellation */ || ex.ErrorCode == 0xffffffff /* disposal */, $"Expected 268 or 0xffffffff, got {ex.ErrorCode}"); + + serverDone.Release(); + }); + + Task clientTask = Task.Run(async () => + { + using HttpClient client = CreateHttpClient(); + + using HttpRequestMessage request = new() + { + Method = HttpMethod.Get, + RequestUri = server.Address, + Version = HttpVersion30, + VersionPolicy = HttpVersionPolicy.RequestVersionExact + }; + HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).WaitAsync(TimeSpan.FromSeconds(20)); + + Stream stream = await response.Content.ReadAsStreamAsync(); + + int bytesRead = await stream.ReadAsync(new byte[1024]); + Assert.Equal(1024, bytesRead); + + var cts = new CancellationTokenSource(200); + cts.Token.Register(() => response.Dispose()); + + Exception ex = await Assert.ThrowsAnyAsync(() => stream.ReadAsync(new byte[1024], cancellationToken: cts.Token).AsTask()); + + // exact exception depends on who won the race + if (ex is not OperationCanceledException and not ObjectDisposedException) + { + var ioe = Assert.IsType(ex); + var hre = Assert.IsType(ioe.InnerException); + Assert.IsType(hre.InnerException); + } + + clientDone.Release(); + await serverDone.WaitAsync(); + }); + + await new[] { clientTask, serverTask }.WhenAllOrAnyFailed(200_000); + } + + private static async Task SendDataForever(Http3LoopbackStream stream) + { + var buf = new byte[100]; + while (true) + { + await stream.SendDataFrameAsync(buf); + } + } + /// /// These are public interop test servers for various QUIC and HTTP/3 implementations, /// taken from https://github.com/quicwg/base-drafts/wiki/Implementations diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index 970236048b2..b5968f70cec 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -718,6 +718,7 @@ namespace System.Net.Quic.Implementations.MsQuic bool callShutdown = false; bool abortRead = false; + bool completeRead = false; bool releaseHandles = false; lock (_state) { @@ -726,9 +727,13 @@ namespace System.Net.Quic.Implementations.MsQuic callShutdown = true; } - if (_state.ReadState < ReadState.ReadsCompleted) + // We can enter Aborted state from both AbortRead call (aborts on the wire) and a Cancellation callback (only changes state) + // We need to ensure read is aborted on the wire here. We let msquic handle a second call to abort as a no-op + if (_state.ReadState < ReadState.ReadsCompleted || _state.ReadState == ReadState.Aborted) { abortRead = true; + completeRead = _state.ReadState == ReadState.PendingRead; + _state.Stream = null; _state.ReadState = ReadState.Aborted; } @@ -762,6 +767,12 @@ namespace System.Net.Quic.Implementations.MsQuic } catch (ObjectDisposedException) { }; } + if (completeRead) + { + _state.ReceiveResettableCompletionSource.CompleteException( + ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Read was canceled"))); + } + if (releaseHandles) { _state.Cleanup(); @@ -943,7 +954,7 @@ namespace System.Net.Quic.Implementations.MsQuic shouldComplete = true; } state.SendState = SendState.Aborted; - state.SendErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; + state.SendErrorCode = (long)evt.Data.PeerReceiveAborted.ErrorCode; } if (shouldComplete) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 252ecf1bb07..2e21ddfd4b8 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -569,7 +569,8 @@ namespace System.Net.Quic.Tests await using QuicStream stream = await connection.AcceptStreamAsync(); QuicStreamAbortedException ex = await Assert.ThrowsAsync(() => WriteForever(stream)); - Assert.Equal(expectedErrorCode, ex.ErrorCode); + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55746")] + //Assert.Equal(expectedErrorCode, ex.ErrorCode); // We should still return true from CanWrite, even though the write has been aborted. Assert.True(stream.CanWrite); From fc0f7903f86850856c4618121f57d2a45d134592 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Tue, 20 Jul 2021 15:05:33 -0700 Subject: [PATCH 698/926] Fix tizen arm32 issue. (#55987) * Add asserts that we don't expect LONG copies on arm32. * Fix tizen. --- src/coreclr/jit/codegencommon.cpp | 18 +++++++++++++++++- src/coreclr/jit/instr.cpp | 10 +++++++--- src/coreclr/jit/lower.cpp | 5 +++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 73e6f196cae..b0f6aab8e30 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -12641,7 +12641,23 @@ void CodeGen::genCodeForBitCast(GenTreeOp* treeNode) } else { - genBitCast(targetType, targetReg, op1->TypeGet(), op1->GetRegNum()); +#ifdef TARGET_ARM + if (compiler->opts.compUseSoftFP && (targetType == TYP_LONG)) + { + // This is a special arm-softFP case when a TYP_LONG node was introduced during lowering + // for a call argument, so it was not handled by decomposelongs phase as all other TYP_LONG nodes. + // Example foo(double LclVar V01), LclVar V01 has to be passed in general registers r0, r1, + // so lowering will add `BITCAST long(LclVar double V01)` and codegen has to support it here. + const regNumber srcReg = op1->GetRegNum(); + const regNumber otherReg = treeNode->AsMultiRegOp()->gtOtherReg; + assert(otherReg != REG_NA); + inst_RV_RV_RV(INS_vmov_d2i, targetReg, otherReg, srcReg, EA_8BYTE); + } + else +#endif // TARGET_ARM + { + genBitCast(targetType, targetReg, op1->TypeGet(), op1->GetRegNum()); + } } genProduceReg(treeNode); } diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 752626f2018..ac37c4c86b1 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -1911,15 +1911,19 @@ instruction CodeGen::ins_Copy(regNumber srcReg, var_types dstType) return INS_mov; } #elif defined(TARGET_ARM) - // No SIMD support yet + // No SIMD support yet. assert(!varTypeIsSIMD(dstType)); if (dstIsFloatReg) { - return (dstType == TYP_DOUBLE) ? INS_vmov_i2d : INS_vmov_i2f; + // Can't have LONG in a register. + assert(dstType == TYP_FLOAT); + return INS_vmov_i2f; } else { - return (dstType == TYP_LONG) ? INS_vmov_d2i : INS_vmov_f2i; + // Can't have LONG in a register. + assert(dstType == TYP_INT); + return INS_vmov_f2i; } #else // TARGET* #error "Unknown TARGET" diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index c5b6a75ef2f..a328206151a 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -1507,6 +1507,11 @@ GenTree* Lowering::LowerFloatArgReg(GenTree* arg, regNumber regNum) #ifdef TARGET_ARM if (floatType == TYP_DOUBLE) { + // A special case when we introduce TYP_LONG + // during lowering for arm32 softFP to pass double + // in int registers. + assert(comp->opts.compUseSoftFP); + regNumber nextReg = REG_NEXT(regNum); intArg->AsMultiRegOp()->gtOtherReg = nextReg; } From 776053faf760acd17cedb51838191ba9f80a6244 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 20 Jul 2021 18:06:25 -0400 Subject: [PATCH 699/926] Revamp caching scheme in PoolingAsyncValueTaskMethodBuilder (#55955) * Revamp caching scheme in PoolingAsyncValueTaskMethodBuilder The current scheme caches one instance per thread in a ThreadStatic, and then has a locked stack that all threads contend on; then to avoid blocking a thread while accessing the cache, locking is done with TryEnter rather than Enter, simply skipping the cache if there is any contention. The locked stack is capped by default at ProcessorCount*4 objects. The new scheme is simpler: one instance per thread, one instance per core. This ends up meaning fewer objects may be cached, but it also almost entirely eliminates contention between threads trying to rent/return objects. As a result, under heavy load it can actually do a better job of using pooled objects as it doesn't bail on using the cache in the face of contention. It also reduces concerns about larger machines being more negatively impacted by the caching. Under lighter load, since we don't cache as many objects, it does mean we may end up allocating a bit more, but generally not much more (and the size of the object we do allocate is a reference-field smaller). * Address PR feedback --- .../src/Internal/Padding.cs | 8 ++ .../PoolingAsyncValueTaskMethodBuilder.cs | 8 -- .../PoolingAsyncValueTaskMethodBuilderT.cs | 131 +++++++----------- .../Sources/ManualResetValueTaskSourceCore.cs | 17 ++- 4 files changed, 72 insertions(+), 92 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Padding.cs b/src/libraries/System.Private.CoreLib/src/Internal/Padding.cs index c9d1efe7838..17f3643be3e 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/Padding.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/Padding.cs @@ -21,4 +21,12 @@ namespace Internal internal struct PaddingFor32 { } + + /// Padded reference to an object. + [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE)] + internal struct PaddedReference + { + [FieldOffset(0)] + public object? Object; + } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs index 84996a0e062..9210434e90a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; using System.Runtime.InteropServices; using System.Threading.Tasks; - using StateMachineBox = System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.StateMachineBox; namespace System.Runtime.CompilerServices @@ -13,12 +11,6 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Auto)] public struct PoolingAsyncValueTaskMethodBuilder { - /// Maximum number of boxes that are allowed to be cached per state machine type. - internal static readonly int s_valueTaskPoolingCacheSize = - int.TryParse(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLINGASYNCVALUETASKSCACHESIZE"), NumberStyles.Integer, CultureInfo.InvariantCulture, out int result) && result > 0 ? - result : - Environment.ProcessorCount * 4; // arbitrary default value - /// Sentinel object used to indicate that the builder completed synchronously and successfully. private static readonly StateMachineBox s_syncSuccessSentinel = PoolingAsyncValueTaskMethodBuilder.s_syncSuccessSentinel; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs index 02d15bb92c3..a69dbc04121 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; +using Internal; using Internal.Runtime.CompilerServices; namespace System.Runtime.CompilerServices @@ -209,7 +210,7 @@ namespace System.Runtime.CompilerServices // cases is we lose the ability to properly step in the debugger, as the debugger uses that // object's identity to track this specific builder/state machine. As such, we proceed to // overwrite whatever's there anyway, even if it's non-null. - StateMachineBox box = StateMachineBox.GetOrCreateBox(); + StateMachineBox box = StateMachineBox.RentFromCache(); boxFieldRef = box; // important: this must be done before storing stateMachine into box.StateMachine! box.StateMachine = stateMachine; box.Context = currentContext; @@ -284,114 +285,86 @@ namespace System.Runtime.CompilerServices { /// Delegate used to invoke on an ExecutionContext when passed an instance of this box type. private static readonly ContextCallback s_callback = ExecutionContextCallback; + /// Per-core cache of boxes, with one box per core. + /// Each element is padded to expected cache-line size so as to minimize false sharing. + private static readonly PaddedReference[] s_perCoreCache = new PaddedReference[Environment.ProcessorCount]; /// Thread-local cache of boxes. This currently only ever stores one. [ThreadStatic] private static StateMachineBox? t_tlsCache; - /// Lock used to protected the shared cache of boxes. 1 == held, 0 == not held. - /// The code that uses this assumes a runtime without thread aborts. - private static int s_cacheLock; - /// Singly-linked list cache of boxes. - private static StateMachineBox? s_cache; - /// The number of items stored in . - private static int s_cacheSize; - /// If this box is stored in the cache, the next box in the cache. - private StateMachineBox? _next; /// The state machine itself. public TStateMachine? StateMachine; /// Gets a box object to use for an operation. This may be a reused, pooled object, or it may be new. [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller - internal static StateMachineBox GetOrCreateBox() + internal static StateMachineBox RentFromCache() { - StateMachineBox? box; - - // First see if the thread-static cache of at most one box has one. - box = t_tlsCache; + // First try to get a box from the per-thread cache. + StateMachineBox? box = t_tlsCache; if (box is not null) { t_tlsCache = null; - return box; } - - // Try to acquire the lock to access the cache. If there's any contention, don't use the cache. - if (s_cache is not null && // hot read just to see if there's any point paying for the interlocked - Interlocked.Exchange(ref s_cacheLock, 1) == 0) + else { - // If there are any instances cached, take one from the cache stack and use it. - box = s_cache; - if (box is not null) + // If we can't, then try to get a box from the per-core cache. + ref StateMachineBox? slot = ref PerCoreCacheSlot; + if (slot is null || + (box = Interlocked.Exchange?>(ref slot, null)) is null) { - s_cache = box._next; - box._next = null; - s_cacheSize--; - Debug.Assert(s_cacheSize >= 0, "Expected the cache size to be non-negative."); - - // Release the lock and return the box. - Volatile.Write(ref s_cacheLock, 0); - return box; + // If we can't, just create a new one. + box = new StateMachineBox(); } - - // No objects were cached. We'll just create a new instance. - Debug.Assert(s_cacheSize == 0, "Expected cache size to be 0."); - - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); } - // Couldn't quickly get a cached instance, so create a new instance. - return new StateMachineBox(); + return box; } - /// Returns this instance to the cache, or drops it if the cache is full or this instance shouldn't be cached. - private void ReturnOrDropBox() + /// Returns this instance to the cache. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only two callers + private void ReturnToCache() { - Debug.Assert(_next is null, "Expected box to not be part of cached list."); - // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by - // lifted locals. We want to do this regardless of whether we end up caching the box or not, in case - // the caller keeps the box alive for an arbitrary period of time. + // lifted locals, and reset the instance for another await. ClearStateUponCompletion(); - - // Reset the MRVTSC. We can either do this here, in which case we may be paying the (small) overhead - // to reset the box even if we're going to drop it, or we could do it while holding the lock, in which - // case we'll only reset it if necessary but causing the lock to be held for longer, thereby causing - // more contention. For now at least, we do it outside of the lock. (This must not be done after - // the lock is released, since at that point the instance could already be in use elsewhere.) - // We also want to increment the version number even if we're going to drop it, to maximize the chances - // that incorrectly double-awaiting a ValueTask will produce an error. _valueTaskSource.Reset(); - // If reusing the object would result in potentially wrapping around its version number, just throw it away. - // This provides a modicum of additional safety when ValueTasks are misused (helping to avoid the case where - // a ValueTask is illegally re-awaited and happens to do so at exactly 2^16 uses later on this exact same instance), - // at the expense of potentially incurring an additional allocation every 65K uses. - if ((ushort)_valueTaskSource.Version == ushort.MaxValue) - { - return; - } - - // If the thread static cache is empty, store this into it and bail. + // If the per-thread cache is empty, store this into it.. if (t_tlsCache is null) { t_tlsCache = this; - return; } - - // Try to acquire the cache lock. If there's any contention, or if the cache is full, we just throw away the object. - if (Interlocked.Exchange(ref s_cacheLock, 1) == 0) + else { - if (s_cacheSize < PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize) + // Otherwise, store it into the per-core cache. + ref StateMachineBox? slot = ref PerCoreCacheSlot; + if (slot is null) { - // Push the box onto the cache stack for subsequent reuse. - _next = s_cache; - s_cache = this; - s_cacheSize++; - Debug.Assert(s_cacheSize > 0 && s_cacheSize <= PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize, "Expected cache size to be within bounds."); + // Try to avoid the write if we know the slot isn't empty (we may still have a benign race condition and + // overwrite what's there if something arrived in the interim). + Volatile.Write(ref slot, this); } + } + } - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); + /// Gets the slot in for the current core. + private static ref StateMachineBox? PerCoreCacheSlot + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only two callers are RentFrom/ReturnToCache + get + { + // Get the current processor ID. We need to ensure it fits within s_perCoreCache, so we + // could % by its length, but we can do so instead by Environment.ProcessorCount, which will be a const + // in tier 1, allowing better code gen, and then further use uints for even better code gen. + Debug.Assert(s_perCoreCache.Length == Environment.ProcessorCount); + int i = (int)((uint)Thread.GetCurrentProcessorId() % (uint)Environment.ProcessorCount); + + // We want an array of StateMachineBox<> objects, each consuming its own cache line so that + // elements don't cause false sharing with each other. But we can't use StructLayout.Explicit + // with generics. So we use object fields, but always reinterpret them (for all reads and writes + // to avoid any safety issues) as StateMachineBox<> instances. + Debug.Assert(s_perCoreCache[i].Object is null || s_perCoreCache[i].Object is StateMachineBox); + return ref Unsafe.As?>(ref s_perCoreCache[i].Object); } } @@ -429,7 +402,7 @@ namespace System.Runtime.CompilerServices if (context is null) { - Debug.Assert(!(StateMachine is null)); + Debug.Assert(StateMachine is not null); StateMachine.MoveNext(); } else @@ -447,8 +420,7 @@ namespace System.Runtime.CompilerServices } finally { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); + ReturnToCache(); } } @@ -461,8 +433,7 @@ namespace System.Runtime.CompilerServices } finally { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); + ReturnToCache(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs index 846e7559044..692f2fa9da8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; @@ -91,14 +90,24 @@ namespace System.Threading.Tasks.Sources [StackTraceHidden] public TResult GetResult(short token) { - ValidateToken(token); - if (!_completed) + if (token != _version || !_completed || _error is not null) + { + ThrowForFailedGetResult(token); + } + + return _result!; + } + + [StackTraceHidden] + private void ThrowForFailedGetResult(short token) + { + if (token != _version || !_completed) { ThrowHelper.ThrowInvalidOperationException(); } _error?.Throw(); - return _result!; + Debug.Fail("Should not get here"); } /// Schedules the continuation action for this operation. From 38477909eeb6296664cc184d461035b1774ea2df Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 20 Jul 2021 18:07:00 -0400 Subject: [PATCH 700/926] Revert "Eliminate backtracking in the interpreter for patterns with .* (#51508)" (#56031) This reverts commit 7eb749c8b78609865edcad67f57084aa382632a3. --- .../Text/RegularExpressions/RegexCompiler.cs | 140 +++--------------- .../RegularExpressions/RegexInterpreter.cs | 27 ---- .../tests/Regex.Match.Tests.cs | 61 +------- 3 files changed, 18 insertions(+), 210 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 25b3a18b4c8..81f5ca065f0 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -64,7 +64,6 @@ namespace System.Text.RegularExpressions private static readonly MethodInfo s_spanSliceIntIntMethod = typeof(ReadOnlySpan).GetMethod("Slice", new Type[] { typeof(int), typeof(int) })!; private static readonly MethodInfo s_spanStartsWith = typeof(MemoryExtensions).GetMethod("StartsWith", new Type[] { typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)) })!.MakeGenericMethod(typeof(char)); private static readonly MethodInfo s_stringAsSpanMethod = typeof(MemoryExtensions).GetMethod("AsSpan", new Type[] { typeof(string) })!; - private static readonly MethodInfo s_spanLastIndexOfMethod = typeof(MemoryExtensions).GetMethod("LastIndexOf", new Type[] { typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)) })!.MakeGenericMethod(typeof(char)); private static readonly MethodInfo s_stringAsSpanIntIntMethod = typeof(MemoryExtensions).GetMethod("AsSpan", new Type[] { typeof(string), typeof(int), typeof(int) })!; private static readonly MethodInfo s_stringGetCharsMethod = typeof(string).GetMethod("get_Chars", new Type[] { typeof(int) })!; private static readonly MethodInfo s_stringIndexOfCharInt = typeof(string).GetMethod("IndexOf", new Type[] { typeof(char), typeof(int) })!; @@ -91,7 +90,6 @@ namespace System.Text.RegularExpressions private LocalBuilder? _runstackLocal; private LocalBuilder? _textInfoLocal; // cached to avoid extraneous TLS hits from CurrentCulture and virtual calls to TextInfo private LocalBuilder? _loopTimeoutCounterLocal; // timeout counter for setrep and setloop - private LocalBuilder? _maxBacktrackPositionLocal; protected RegexOptions _options; // options protected RegexCode? _code; // the RegexCode object @@ -893,8 +891,6 @@ namespace System.Text.RegularExpressions Mvfldloc(s_runtrackposField, _runtrackposLocal!); Mvfldloc(s_runstackField, _runstackLocal!); Mvfldloc(s_runstackposField, _runstackposLocal!); - Ldc(-1); - Stloc(_maxBacktrackPositionLocal!); _backpos = -1; @@ -1709,7 +1705,7 @@ namespace System.Text.RegularExpressions // if (!CharInClass(textSpan[i + 2], prefix[2], "...")) goto returnFalse; // ... Debug.Assert(charClassIndex == 0 || charClassIndex == 1); - for (; charClassIndex < _leadingCharClasses.Length; charClassIndex++) + for ( ; charClassIndex < _leadingCharClasses.Length; charClassIndex++) { Debug.Assert(needLoop); Ldloca(textSpanLocal); @@ -3314,7 +3310,6 @@ namespace System.Text.RegularExpressions } _runtextbegLocal = DeclareInt32(); _runtextendLocal = DeclareInt32(); - _maxBacktrackPositionLocal = DeclareInt32(); InitializeCultureForGoIfNecessary(); @@ -4263,61 +4258,7 @@ namespace System.Text.RegularExpressions //: break Backward; { string str = _strings![Operand(0)]; - Label multiCode = DefineLabel(); - if (!IsRightToLeft()) - { - // if (runtextend - runtextpos < c) - Ldloc(_runtextendLocal!); - Ldloc(_runtextposLocal!); - Sub(); - Ldc(str.Length); - BgeFar(multiCode); - // if (!caseInsensitive && _maxBacktrackPosition != -1 && runtextpos > _maxBacktrackPosition) - if (!IsCaseInsensitive()) - { - Ldloc(_maxBacktrackPositionLocal!); - Ldc(-1); - BeqFar(_backtrack); - Ldloc(_runtextposLocal!); - Ldloc(_maxBacktrackPositionLocal!); - BleFar(_backtrack); - // runtextpos = _maxBacktrackPosition; - Ldloc(_maxBacktrackPositionLocal!); - Stloc(_runtextposLocal!); - // ReadOnlySpan runtextSpan = runtext.AsSpan(_maxBacktrackPosition, runtextend - _maxBacktractPosition); - Ldloc(_runtextLocal!); - Ldloc(_maxBacktrackPositionLocal!); - Ldloc(_runtextendLocal!); - Ldloc(_maxBacktrackPositionLocal!); - Sub(); - using (RentedLocalBuilder runtextSpanLocal = RentReadOnlySpanCharLocal()) - { - Call(s_stringAsSpanIntIntMethod); - Stloc(runtextSpanLocal); - using (RentedLocalBuilder lastIndexOfLocal = RentInt32Local()) - { - // int lastIndexOf = runtextSpan.LastIndexOf(str.AsSpan()); - Ldloc(runtextSpanLocal); - Ldstr(str); - Call(s_stringAsSpanMethod); - Call(s_spanLastIndexOfMethod); - Stloc(lastIndexOfLocal); - // if (lastIndexOf > -1) - Ldloc(lastIndexOfLocal); - Ldc(-1); - BleFar(_backtrack); - // runtextpos = lastIndexOf + _maxBacktrackPosition; - Ldloc(lastIndexOfLocal); - Ldloc(_maxBacktrackPositionLocal!); - Add(); - Stloc(_runtextposLocal!); - BrFar(_backtrack); - } - } - } - } - MarkLabel(multiCode); Ldc(str.Length); Ldloc(_runtextendLocal!); Ldloc(_runtextposLocal!); @@ -4657,9 +4598,6 @@ namespace System.Text.RegularExpressions using RentedLocalBuilder lenLocal = RentInt32Local(); using RentedLocalBuilder iLocal = RentInt32Local(); - using RentedLocalBuilder tempMaxBacktrackPositionLocal = RentInt32Local(); - Ldloc(_runtextposLocal!); - Stloc(tempMaxBacktrackPositionLocal); if (!IsRightToLeft()) { @@ -4909,12 +4847,6 @@ namespace System.Text.RegularExpressions DoPush(); Track(); - // if (_operator == RegexCode.Notoneloop) maxBacktrackPosition = tempMaxBacktrackPosition - if (_regexopcode == RegexCode.Notoneloop) - { - Ldloc(tempMaxBacktrackPositionLocal); - Stloc(_maxBacktrackPositionLocal!); - } } break; } @@ -4938,66 +4870,28 @@ namespace System.Text.RegularExpressions //: if (i > 0) //: Track(i - 1, pos - 1); //: Advance(2); - Label noBacktrackPositionBranch = DefineLabel(); + PopTrack(); + Stloc(_runtextposLocal!); PopTrack(); using (RentedLocalBuilder posLocal = RentInt32Local()) { Stloc(posLocal); - PopTrack(); - using (RentedLocalBuilder iBacktrackLocal = RentInt32Local()) - { - Stloc(iBacktrackLocal); - // if (!caseInsensitive && maxBacktrackPosition != -1 && pos > maxBacktrackPosition && runtextpos < pos && _operator == (RegexCode.Notoneloop | RegexCode.Back) && !_rightToLeft) - if (!IsCaseInsensitive() && _regexopcode == (RegexCode.Notoneloop | RegexCode.Back) && !IsRightToLeft()) - { - Ldloc(_maxBacktrackPositionLocal!); - Ldc(-1); - Beq(noBacktrackPositionBranch); - Ldloc(posLocal); - Ldloc(_maxBacktrackPositionLocal!); - Ble(noBacktrackPositionBranch); - Ldloc(_runtextposLocal!); - Ldloc(posLocal); - Bge(noBacktrackPositionBranch); - /* - int difference = pos - maxBacktrackPosition; - pos = runtextpos; - i -= difference; - maxBacktrackPosition = -1; - */ - // int difference = pos - maxBacktrackPosition; - Ldloc(iBacktrackLocal); - Ldloc(posLocal); - Ldloc(_maxBacktrackPositionLocal!); - Sub(); - Sub(); - Stloc(iBacktrackLocal); - Ldloc(_runtextposLocal!); - Stloc(posLocal); - Ldc(-1); - Stloc(_maxBacktrackPositionLocal!); - } - - MarkLabel(noBacktrackPositionBranch); - Ldloc(posLocal); - Stloc(_runtextposLocal!); - Ldloc(iBacktrackLocal); - Ldc(0); - BleFar(AdvanceLabel()); - ReadyPushTrack(); - Ldloc(iBacktrackLocal); - } - Ldc(1); - Sub(); - DoPush(); + Ldloc(posLocal); + Ldc(0); + BleFar(AdvanceLabel()); ReadyPushTrack(); - Ldloc(_runtextposLocal!); - Ldc(1); - Sub(IsRightToLeft()); - DoPush(); - Trackagain(); - Advance(); + Ldloc(posLocal); } + Ldc(1); + Sub(); + DoPush(); + ReadyPushTrack(); + Ldloc(_runtextposLocal!); + Ldc(1); + Sub(IsRightToLeft()); + DoPush(); + Trackagain(); + Advance(); break; case RegexCode.Onelazy: diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs index 3e05926733f..d557ec6c3aa 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs @@ -20,7 +20,6 @@ namespace System.Text.RegularExpressions private int _codepos; private bool _rightToLeft; private bool _caseInsensitive; - private int _maxBacktrackPosition = -1; public RegexInterpreter(RegexCode code, CultureInfo culture) { @@ -224,20 +223,6 @@ namespace System.Text.RegularExpressions { if (runtextend - runtextpos < c) { - // If MatchString was called after a greedy op such as a .*, we would have zipped runtextpos to the end without really examining any characters. Reset to maxBacktrackPos here as an optimization - if (!_caseInsensitive && _maxBacktrackPosition != -1 && runtextpos > _maxBacktrackPosition) - { - // If lastIndexOf is -1, we backtrack to the max extent possible. - runtextpos = _maxBacktrackPosition; - ReadOnlySpan runtextSpan = runtext.AsSpan(_maxBacktrackPosition, runtextend - _maxBacktrackPosition); - int lastIndexOf = runtextSpan.LastIndexOf(str); - if (lastIndexOf > -1) - { - // Found the next position to match. Move runtextpos here - runtextpos = _maxBacktrackPosition + lastIndexOf; - } - } - return false; } @@ -1200,7 +1185,6 @@ namespace System.Text.RegularExpressions int len = Math.Min(Operand(1), Forwardchars()); char ch = (char)Operand(0); int i; - int tempMaxBacktrackPosition = runtextpos; if (!_rightToLeft && !_caseInsensitive) { @@ -1233,7 +1217,6 @@ namespace System.Text.RegularExpressions if (len > i && _operator == RegexCode.Notoneloop) { TrackPush(len - i - 1, runtextpos - Bump()); - _maxBacktrackPosition = tempMaxBacktrackPosition; } } advance = 2; @@ -1278,16 +1261,6 @@ namespace System.Text.RegularExpressions { int i = TrackPeek(); int pos = TrackPeek(1); - if (!_caseInsensitive && _maxBacktrackPosition != -1 && pos > _maxBacktrackPosition && runtextpos < pos && _operator == (RegexCode.Notoneloop | RegexCode.Back) && !_rightToLeft) - { - // The Multi node has bumped us along already - int difference = pos - _maxBacktrackPosition; - Debug.Assert(difference > 0); - pos = runtextpos; - i -= difference; - // We shouldn't be backtracking anymore. - _maxBacktrackPosition = -1; - } runtextpos = pos; if (i > 0) { diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs index 723fc68e53b..719f280c917 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; -using System.Reflection; using System.Tests; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -205,11 +204,9 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "abc", "abc", RegexOptions.None, 0, 3, true, "abc" }; yield return new object[] { "abc", "aBc", RegexOptions.None, 0, 3, false, string.Empty }; yield return new object[] { "abc", "aBc", RegexOptions.IgnoreCase, 0, 3, true, "aBc" }; - yield return new object[] { @"abc.*def", "abcghiDEF", RegexOptions.IgnoreCase, 0, 9, true, "abcghiDEF" }; // Using *, +, ?, {}: Actual - "a+\\.?b*\\.?c{2}" yield return new object[] { @"a+\.?b*\.+c{2}", "ab.cc", RegexOptions.None, 0, 5, true, "ab.cc" }; - yield return new object[] { @"[^a]+\.[^z]+", "zzzzz", RegexOptions.None, 0, 5, false, string.Empty }; // RightToLeft yield return new object[] { @"\s+\d+", "sdf 12sad", RegexOptions.RightToLeft, 0, 9, true, " 12" }; @@ -384,62 +381,6 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D2", options, 0, 2, true, "\u05D0\u05D2" }; yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D4", options, 0, 0, false, "" }; } - - // .* Perf Optimization: Case sensitive - foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Compiled }) - { - yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @".*\nFoo", $"\nFooThis should match", options, 0, 21, true, "\nFoo" }; - yield return new object[] { @".*\nfoo", "\nfooThis should match", options, 4, 17, false, "" }; - - yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; - yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; - yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; - yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 24, 5, false, "" }; - - yield return new object[] { @".*\dfoo", "1fooThis1foo should 1foo match", options, 4, 9, true, "This1foo" }; - yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; - } - - // .* Perf Optimization: Case insensitive - foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase, RegexOptions.IgnoreCase | RegexOptions.Compiled }) - { - yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; - yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; - yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; - yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; - yield return new object[] { @".*\dfoo", "1fooThis1FOO should 1foo match", options, 4, 9, true, "This1FOO" }; - } - - // .* Perf Optimization: RTL, Case-sensitive - foreach (RegexOptions options in new[] { RegexOptions.None | RegexOptions.RightToLeft, RegexOptions.Compiled | RegexOptions.RightToLeft }) - { - yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @".*\nFoo", $"This should match\nFoo", options, 0, 21, true, "This should match\nFoo" }; - yield return new object[] { @".*\nfoo", "This should matchfoo\n", options, 4, 13, false, "" }; - - yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; - yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; - yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; - yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; - yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 19, 0, false, "" }; - - yield return new object[] { @".*\dfoo", "1fooThis2foo should 1foo match", options, 8, 4, true, "2foo" }; - yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; - } - - // .* Perf Optimization: RTL, case insensitive - foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase | RegexOptions.RightToLeft, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.RightToLeft }) - { - yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; - yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; - yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; - yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; - yield return new object[] { @".*\dfoo", "1fooThis2FOO should 1foo match", options, 8, 4, true, "2FOO" }; - } } public static IEnumerable Match_Basic_TestData_NetCore() @@ -676,7 +617,7 @@ namespace System.Text.RegularExpressions.Tests public void Match_Timeout_Repetition_Throws(RegexOptions options) { int repetitionCount = 800_000_000; - var regex = new Regex(@"a\s{" + repetitionCount + "}", options, TimeSpan.FromSeconds(1)); + var regex = new Regex(@"a\s{" + repetitionCount+ "}", options, TimeSpan.FromSeconds(1)); string input = @"a" + new string(' ', repetitionCount) + @"b"; Assert.Throws(() => regex.Match(input)); } From d38a5396608240a6426668238e06056c69b85c50 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 20 Jul 2021 15:23:59 -0700 Subject: [PATCH 701/926] Handle contained BITCAST for STORE_LCL_FLD (#55852) * Do not mark BITCAST as contained for STORE_LCL_FLD * Add unit test * Handle contained BITCAST in STORE_LCL_FLD * Return 100 --- src/coreclr/jit/codegenxarch.cpp | 29 +++++++++++++++-- .../JitBlue/Runtime_55141/Runtime_55141.cs | 32 +++++++++++++++++++ .../Runtime_55141/Runtime_55141.csproj | 10 ++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.csproj diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 6f605e5514b..e361a56ae47 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4425,7 +4425,7 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree) #ifdef FEATURE_SIMD // storing of TYP_SIMD12 (i.e. Vector3) field - if (tree->TypeGet() == TYP_SIMD12) + if (targetType == TYP_SIMD12) { genStoreLclTypeSIMD12(tree); return; @@ -4436,7 +4436,32 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree) assert(genTypeSize(genActualType(targetType)) == genTypeSize(genActualType(op1->TypeGet()))); genConsumeRegs(op1); - GetEmitter()->emitInsBinary(ins_Store(targetType), emitTypeSize(tree), tree, op1); + + if (op1->OperIs(GT_BITCAST) && op1->isContained()) + { + regNumber targetReg = tree->GetRegNum(); + GenTree* bitCastSrc = op1->gtGetOp1(); + var_types srcType = bitCastSrc->TypeGet(); + noway_assert(!bitCastSrc->isContained()); + + if (targetReg == REG_NA) + { + unsigned lclNum = tree->GetLclNum(); + LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum); + + GetEmitter()->emitIns_S_R(ins_Store(srcType, compiler->isSIMDTypeLocalAligned(lclNum)), + emitTypeSize(targetType), bitCastSrc->GetRegNum(), lclNum, 0); + varDsc->SetRegNum(REG_STK); + } + else + { + genBitCast(targetType, targetReg, srcType, bitCastSrc->GetRegNum()); + } + } + else + { + GetEmitter()->emitInsBinary(ins_Store(targetType), emitTypeSize(tree), tree, op1); + } // Updating variable liveness after instruction was emitted genUpdateLife(tree); diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.cs new file mode 100644 index 00000000000..bad94d8b1d6 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +struct S0 +{ + public ulong F0; + public ulong F1; + public long F2; + public uint F4; + public uint F6; +} + +public class Runtime_55141 +{ + // UDIV is lowered to the MULHI/BITCAST nodes and they are stored in field (STORE_LCL_FLD). + // BITCAST is marked as contained so the value to be stored can be used from MULHI, but marking + // the containment of BITCAST is not supported in codegen for STORE_LCL_FLD. + public static int Main() + { + return (uint)Run(0) == 0 ? 100 : 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Run(long x) + { + S0 vr1 = default(S0); + vr1.F4 = (uint)x / 254; + return vr1.F6; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.csproj new file mode 100644 index 00000000000..e8e73ed9d1a --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55141/Runtime_55141.csproj @@ -0,0 +1,10 @@ + + + Exe + True + None + + + + + \ No newline at end of file From aaba3c22eb6ea6b13a81490655130c052b9ef394 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:07:25 -0700 Subject: [PATCH 702/926] [main] Update dependencies from mono/linker (#55813) * Update dependencies from https://github.com/mono/linker build 20210715.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21362.3 -> To Version 6.0.100-preview.6.21365.1 * Update dependencies from https://github.com/mono/linker build 20210716.2 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21362.3 -> To Version 6.0.100-preview.6.21366.2 * Suppress type hierarchy warnings Suppress type hierarchy warnings for DynamicallyAccessedMembers attribute on types that have members with RequiresUnreferencedCode (or derived types with such members). * Feedback Remove unnecessary local method * Fix typo * Update dependencies from https://github.com/mono/linker build 20210719.3 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21362.3 -> To Version 6.0.100-preview.6.21369.3 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Sven Boemer --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- .../Data/Common/DbConnectionStringBuilder.cs | 13 +++++++++---- .../System.Data.Common/src/System/Data/DataSet.cs | 6 ++++-- .../src/System/Data/DataTable.cs | 6 ++++-- .../src/System/Data/TypedTableBase.cs | 4 ++++ .../Diagnostics/DiagnosticSourceEventSource.cs | 6 ++++++ .../src/System/Diagnostics/Tracing/EventSource.cs | 15 ++++++--------- 8 files changed, 36 insertions(+), 20 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8df0c0c553d..54167315e8b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -182,9 +182,9 @@ https://github.com/dotnet/runtime 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c - + https://github.com/mono/linker - 664e78edc72dd0a48e6f55e352051b6ba61bba9a + 8d49e169d872d6225bcf73ae14ae50b002f39319 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index ffa58723a13..3cac4122b1f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -165,7 +165,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21362.3 + 6.0.100-preview.6.21369.3 $(MicrosoftNETILLinkTasksVersion) 6.0.0-rc.1.21366.2 diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 1b6c5fe1b7e..74feebf9a04 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -12,6 +12,11 @@ using System.Diagnostics.CodeAnalysis; namespace System.Data.Common { + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The use of GetType preserves members with RequiresUnreferencedCode, but the GetType callsites either " + + "occur in RequiresUnreferencedCode scopes, or have individually justified suppressions.")] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public class DbConnectionStringBuilder : IDictionary, ICustomTypeDescriptor { @@ -570,7 +575,7 @@ namespace System.Data.Common } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] + Justification = "The component type's class name is preserved because this class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string? ICustomTypeDescriptor.GetClassName() { // Below call is necessary to tell the trimmer that it should mark derived types appropriately. @@ -579,7 +584,7 @@ namespace System.Data.Common return TypeDescriptor.GetClassName(this, true); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] + Justification = "The component type's component name is preserved because this class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] string? ICustomTypeDescriptor.GetComponentName() { // Below call is necessary to tell the trimmer that it should mark derived types appropriately. @@ -588,7 +593,7 @@ namespace System.Data.Common return TypeDescriptor.GetComponentName(this, true); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] + Justification = "The component type's attributes are preserved because this class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); @@ -624,7 +629,7 @@ namespace System.Data.Common return TypeDescriptor.GetDefaultEvent(this, true); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The type of component is statically known. This class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] + Justification = "The component type's events are preserved because this class is marked with [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]")] EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { // Below call is necessary to tell the trimmer that it should mark derived types appropriately. diff --git a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs index 6974aafce60..962ba8131b8 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataSet.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataSet.cs @@ -30,6 +30,10 @@ namespace System.Data [XmlSchemaProvider(nameof(GetDataSetSchema))] [XmlRoot(nameof(DataSet))] [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "CreateInstanceOfThisType's use of GetType uses only the parameterless constructor, but the annotations preserve all non-public constructors causing a warning for the serialization constructors. Those constructors won't be used here.")] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] // needed by Clone() to preserve derived ctors public class DataSet : MarshalByValueComponent, IListSource, IXmlSerializable, ISupportInitializeNotification, ISerializable { @@ -1093,8 +1097,6 @@ namespace System.Data } } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Only parameterless constructors are used here but since nonPublic=true, all non-public constructors are being preserved causing a warning for the serialization constructors. Those constructors won't be used here.")] private DataSet CreateInstanceOfThisType() { return (DataSet)Activator.CreateInstance(GetType(), true)!; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index 22ace6fce64..ad9044bd4a9 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -31,6 +31,10 @@ namespace System.Data [XmlSchemaProvider(nameof(GetDataTableSchema))] [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "CreateInstance's use of GetType uses only the parameterless constructor. Warnings are about serialization related constructors.")] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public class DataTable : MarshalByValueComponent, IListSource, ISupportInitializeNotification, ISerializable, IXmlSerializable { @@ -2307,8 +2311,6 @@ namespace System.Data // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Only parameterless constructors are used here. Warning is about serialization related constructors.")] protected virtual DataTable CreateInstance() => (DataTable)Activator.CreateInstance(GetType(), true)!; public virtual DataTable Clone() => Clone(null); diff --git a/src/libraries/System.Data.Common/src/System/Data/TypedTableBase.cs b/src/libraries/System.Data.Common/src/System/Data/TypedTableBase.cs index 5c66f688595..64fdf038785 100644 --- a/src/libraries/System.Data.Common/src/System/Data/TypedTableBase.cs +++ b/src/libraries/System.Data.Common/src/System/Data/TypedTableBase.cs @@ -12,6 +12,10 @@ namespace System.Data /// This is the generic base class for TypedDataSet /// [Serializable] + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "DataTable.CreateInstance's use of GetType uses only the parameterless constructor. Warnings are about serialization related constructors.")] public abstract class TypedTableBase : DataTable, IEnumerable where T : DataRow { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index d2ee34fe439..31daa2a57eb 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -160,6 +160,12 @@ namespace System.Diagnostics /// See the DiagnosticSourceEventSourceBridgeTest.cs for more explicit examples of using this bridge. /// [EventSource(Name = "Microsoft-Diagnostics-DiagnosticSource")] + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "In EventSource, EnsureDescriptorsInitialized's use of GetType preserves all members, so " + + "those that are marked with RequiresUnreferencedCode will warn. " + + "This method will not access any of these members and is safe to call.")] internal sealed class DiagnosticSourceEventSource : EventSource { public static DiagnosticSourceEventSource Log = new DiagnosticSourceEventSource(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 4800a2cde47..87d09e16679 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -233,6 +233,12 @@ namespace System.Diagnostics.Tracing // The EnsureDescriptorsInitialized() method might need to access EventSource and its derived type // members and the trimmer ensures that these members are preserved. [DynamicallyAccessedMembers(ManifestMemberTypes)] + // This coarse suppression silences all RequiresUnreferencedCode warnings in the class. + // https://github.com/mono/linker/issues/2136 tracks making it possible to add more granular suppressions at the member level, and with a different warning code. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "EnsureDescriptorsInitialized's use of GetType preserves all members, so " + + "those that are marked with RequiresUnreferencedCode will warn. " + + "This method will not access any of these members and is safe to call.")] #endif public partial class EventSource : IDisposable { @@ -2813,16 +2819,7 @@ namespace System.Diagnostics.Tracing { // get the metadata via reflection. Debug.Assert(m_rawManifest == null); -#if !ES_BUILD_STANDALONE - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Based on the annotation on EventSource class, Trimmer will see from its analysis members " + - "that are marked with RequiresUnreferencedCode and will warn." + - "This method will not access any of these members and is safe to call.")] - byte[]? GetCreateManifestAndDescriptorsViaLocalMethod(string name) => CreateManifestAndDescriptors(this.GetType(), name, this); - m_rawManifest = GetCreateManifestAndDescriptorsViaLocalMethod(Name); -#else m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this); -#endif Debug.Assert(m_eventData != null); // TODO Enforce singleton pattern From f85f579e36edd527542cbdd6200f91e5b68780aa Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Tue, 20 Jul 2021 22:32:42 -0300 Subject: [PATCH 703/926] [wasm][debugger] Fix step behavior (#55915) * Creating test to close 49143. * Creating test to close https://github.com/dotnet/runtime/issues/49141 * Adding test for https://github.com/dotnet/runtime/issues/49218. * Fix behavior of step to be the same of what we see when debugging using debugger-libs+mono or coreclr. Fix error message of evaluate calling methods. Adding test for https://github.com/dotnet/runtime/issues/49142 * Improving test to test what @radical asked. * Changing what was suggested by @radical. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 8 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 17 +++ .../EvaluateOnCallFrameTests.cs | 24 +--- .../DebuggerTestSuite/SteppingTests.cs | 76 ++++++++++++- .../tests/debugger-test/debugger-test.cs | 104 +++++++++++++++--- 5 files changed, 188 insertions(+), 41 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 645a6a4ac2a..77b3695ff7d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -337,6 +337,12 @@ namespace Microsoft.WebAssembly.Diagnostics DebuggerNonUserCode = 8 } + internal enum StepSize + { + Minimal, + Line + } + internal class MonoBinaryReader : BinaryReader { public MonoBinaryReader(Stream stream) : base(stream) {} @@ -897,7 +903,7 @@ namespace Microsoft.WebAssembly.Diagnostics commandParamsWriter.Write((byte)1); commandParamsWriter.Write((byte)ModifierKind.Step); commandParamsWriter.Write(thread_id); - commandParamsWriter.Write((int)0); + commandParamsWriter.Write((int)StepSize.Line); commandParamsWriter.Write((int)kind); commandParamsWriter.Write((int)(StepFilter.StaticCtor | StepFilter.DebuggerHidden)); //filter var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index b2662480715..1d30086b6b2 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -902,6 +902,23 @@ namespace DebuggerTests return res; } + internal async Task EvaluateOnCallFrameAndCheck(string call_frame_id, params (string expression, JObject expected)[] args) + { + foreach (var arg in args) + { + var (eval_val, _) = await EvaluateOnCallFrame(call_frame_id, arg.expression); + try + { + await CheckValue(eval_val, arg.expected, arg.expression); + } + catch + { + Console.WriteLine($"CheckValue failed for {arg.expression}. Expected: {arg.expected}, vs {eval_val}"); + throw; + } + } + } + internal void AssertEqual(object expected, object actual, string label) { if (expected?.Equals(actual) == true) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index d6803ee8670..a28080c4c70 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -476,23 +476,6 @@ namespace DebuggerTests await EvaluateOnCallFrameAndCheck(id, ("this.PropertyThrowException", TString("System.Exception: error"))); }); - async Task EvaluateOnCallFrameAndCheck(string call_frame_id, params (string expression, JObject expected)[] args) - { - foreach (var arg in args) - { - var (eval_val, _) = await EvaluateOnCallFrame(call_frame_id, arg.expression); - try - { - await CheckValue(eval_val, arg.expected, arg.expression); - } - catch - { - Console.WriteLine($"CheckValue failed for {arg.expression}. Expected: {arg.expected}, vs {eval_val}"); - throw; - } - } - } - async Task EvaluateOnCallFrameFail(string call_frame_id, params (string expression, string class_name)[] args) { foreach (var arg in args) @@ -513,7 +496,7 @@ namespace DebuggerTests var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (_, res) = await EvaluateOnCallFrame(id, "this.objToTest.MyMethodWrong()", expect_ok: false ); - AssertEqual("Method 'MyMethodWrong' not found in type 'DebuggerTests.EvaluateMethodTestsClass.ParmToTest'", res.Error["message"]?.Value(), "wrong error message"); + AssertEqual("Unable to evaluate method 'MyMethodWrong'", res.Error["message"]?.Value(), "wrong error message"); (_, res) = await EvaluateOnCallFrame(id, "this.objToTest.MyMethod(1)", expect_ok: false ); AssertEqual("Unable to evaluate method 'MyMethod'", res.Error["message"]?.Value(), "wrong error message"); @@ -522,10 +505,10 @@ namespace DebuggerTests AssertEqual("Unable to evaluate method 'CallMethodWithParm'", res.Error["message"]?.Value(), "wrong error message"); (_, res) = await EvaluateOnCallFrame(id, "this.ParmToTestObjNull.MyMethod()", expect_ok: false ); - AssertEqual("Object reference not set to an instance of an object.", res.Error["message"]?.Value(), "wrong error message"); + AssertEqual("Unable to evaluate method 'MyMethod'", res.Error["message"]?.Value(), "wrong error message"); (_, res) = await EvaluateOnCallFrame(id, "this.ParmToTestObjException.MyMethod()", expect_ok: false ); - AssertEqual("Object reference not set to an instance of an object.", res.Error["message"]?.Value(), "wrong error message"); + AssertEqual("Unable to evaluate method 'MyMethod'", res.Error["message"]?.Value(), "wrong error message"); }); [Fact] @@ -540,6 +523,7 @@ namespace DebuggerTests ("this.CallMethod()", TNumber(1)), ("this.CallMethod()", TNumber(1)), ("this.ParmToTestObj.MyMethod()", TString("methodOK")), + ("this.ParmToTestObj.ToString()", TString("DebuggerTests.EvaluateMethodTestsClass+ParmToTest")), ("this.objToTest.MyMethod()", TString("methodOK"))); }); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index eed238c6f59..081cd28732d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -198,7 +198,7 @@ namespace DebuggerTests CheckObject(locals_m1, "nim", "Math.NestedInMath"); // step back into OuterMethod - await StepAndCheck(StepKind.Over, debugger_test_loc, 91, 8, "OuterMethod", times: 9, + await StepAndCheck(StepKind.Over, debugger_test_loc, 91, 8, "OuterMethod", times: 6, locals_fn: (locals) => { Assert.Equal(5, locals.Count()); @@ -236,7 +236,7 @@ namespace DebuggerTests } ); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 92, 8, "OuterMethod", times: 2, + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 92, 8, "OuterMethod", times: 1, locals_fn: (locals) => { Assert.Equal(5, locals.Count()); @@ -284,7 +284,7 @@ namespace DebuggerTests // Step into InnerMethod await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 105, 8, "InnerMethod"); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 109, 12, "InnerMethod", times: 5, + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 110, 12, "InnerMethod", times: 5, locals_fn: (locals) => { Assert.Equal(4, locals.Count()); @@ -297,7 +297,7 @@ namespace DebuggerTests ); // Step back to OuterMethod - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 88, 8, "OuterMethod", times: 6, + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 90, 8, "OuterMethod", times: 6, locals_fn: (locals) => { Assert.Equal(5, locals.Count()); @@ -469,7 +469,7 @@ namespace DebuggerTests // ----------- Step back to the caller --------- pause_location = await StepAndCheck(StepKind.Over, debugger_test_loc, 30, 12, "TestStructsAsMethodArgs", - times: 2, locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); + times: 1, locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); await CheckProps(locals, new { @@ -862,5 +862,71 @@ namespace DebuggerTests Task t = await Task.WhenAny(pause_task, Task.Delay(2000)); Assert.True(t != pause_task, "Debugger unexpectedly paused"); } + + [Fact] + public async Task SimpleStep_RegressionTest_49141() + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 674, 0); + + string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] Foo:RunBart'); }, 1);"; + await EvaluateAndCheck( + expression, + "dotnet://debugger-test.dll/debugger-test.cs", 674, 12, + "Bart"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 677, 8, "Bart"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 678, 4, "Bart"); + } + + [Fact] + public async Task StepAndEvaluateExpression() + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 682, 0); + + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] Foo:RunBart'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 682, 8, + "RunBart"); + var pause_location = await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 671, 4, "Bart"); + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + await EvaluateOnCallFrameAndCheck(id, ("this.Bar", TString("Same of something"))); + pause_location = await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 673, 8, "Bart"); + id = pause_location["callFrames"][0]["callFrameId"].Value(); + await EvaluateOnCallFrameAndCheck(id, ("this.Bar", TString("Same of something"))); + } + + [Fact] + public async Task StepOverWithMoreThanOneCommandInSameLine() + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 693, 0); + + string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] Foo:RunBart'); }, 1);"; + await EvaluateAndCheck( + expression, + "dotnet://debugger-test.dll/debugger-test.cs", 693, 8, + "OtherBar"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 694, 8, "OtherBar"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 696, 8, "OtherBar"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 699, 8, "OtherBar"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 701, 8, "OtherBar"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 702, 4, "OtherBar"); + } + + [Fact] + public async Task StepOverWithMoreThanOneCommandInSameLineAsync() + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 710, 0); + + string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] Foo:RunBart'); }, 1);"; + await EvaluateAndCheck( + expression, + "dotnet://debugger-test.dll/debugger-test.cs", 710, 8, + "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 711, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 713, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 716, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 718, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 719, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 720, 4, "MoveNext"); + } } } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 228d9d1ff62..c0990612185 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Threading.Tasks; +using System.Linq; public partial class Math { //Only append content to this class as the test suite depends on line info public static int IntAdd(int a, int b) @@ -361,7 +361,7 @@ public class DebuggerTest Console.WriteLine ($"break here"); } - public static async Task BoxingTestAsync() + public static async System.Threading.Tasks.Task BoxingTestAsync() { int? n_i = 5; object o_i = n_i.Value; @@ -380,7 +380,7 @@ public class DebuggerTest object o_ia = new int[] {918, 58971}; Console.WriteLine ($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } public static void BoxedTypeObjectTest() @@ -395,7 +395,7 @@ public class DebuggerTest object oo0 = oo; Console.WriteLine ($"break here"); } - public static async Task BoxedTypeObjectTestAsync() + public static async System.Threading.Tasks.Task BoxedTypeObjectTestAsync() { int i = 5; object o0 = i; @@ -406,7 +406,7 @@ public class DebuggerTest object oo = new object(); object oo0 = oo; Console.WriteLine ($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } public static void BoxedAsClass() @@ -419,7 +419,7 @@ public class DebuggerTest Console.WriteLine ($"break here"); } - public static async Task BoxedAsClassAsync() + public static async System.Threading.Tasks.Task BoxedAsClassAsync() { ValueType vt_dt = new DateTime(4819, 5, 6, 7, 8, 9); ValueType vt_gs = new Math.GenericStruct { StringField = "vt_gs#StringField" }; @@ -427,7 +427,7 @@ public class DebuggerTest Enum ee = System.IO.FileMode.Append; Console.WriteLine ($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } } @@ -452,14 +452,14 @@ public class MulticastDelegateTestClass TestEvent?.Invoke(this, Delegate?.ToString()); } - public async Task TestAsync() + public async System.Threading.Tasks.Task TestAsync() { TestEvent += (_, s) => Console.WriteLine(s); TestEvent += (_, s) => Console.WriteLine(s + "qwe"); Delegate = TestEvent; TestEvent?.Invoke(this, Delegate?.ToString()); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } } @@ -470,10 +470,10 @@ public class EmptyClass Console.WriteLine($"break here"); } - public static async Task StaticMethodWithNoLocalsAsync() + public static async System.Threading.Tasks.Task StaticMethodWithNoLocalsAsync() { Console.WriteLine($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } public static void run() @@ -490,10 +490,10 @@ public struct EmptyStruct Console.WriteLine($"break here"); } - public static async Task StaticMethodWithNoLocalsAsync() + public static async System.Threading.Tasks.Task StaticMethodWithNoLocalsAsync() { Console.WriteLine($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } public static void StaticMethodWithLocalEmptyStruct() @@ -502,11 +502,11 @@ public struct EmptyStruct Console.WriteLine($"break here"); } - public static async Task StaticMethodWithLocalEmptyStructAsync() + public static async System.Threading.Tasks.Task StaticMethodWithLocalEmptyStructAsync() { var es = new EmptyStruct(); Console.WriteLine($"break here"); - await Task.CompletedTask; + await System.Threading.Tasks.Task.CompletedTask; } public static void run() @@ -653,3 +653,77 @@ public class LoadDebuggerTestALC { } } + +public class Something +{ + public string Name { get; set; } + public Something() => Name = "Same of something"; + public override string ToString() => Name; +} + +public class Foo +{ + public string Bar => Stuffs.First(x => x.Name.StartsWith('S')).Name; + public System.Collections.Generic.List Stuffs { get; } = Enumerable.Range(0, 10).Select(x => new Something()).ToList(); + public string Lorem { get; set; } = "Safe"; + public string Ipsum { get; set; } = "Side"; + public Something What { get; } = new Something(); + public int Bart() + { + int ret; + if (Lorem.StartsWith('S')) + ret = 0; + else + ret = 1; + return ret; + } + public static void RunBart() + { + Foo foo = new Foo(); + foo.Bart(); + Console.WriteLine(foo.OtherBar()); + foo.OtherBarAsync().Wait(10); + } + public bool OtherBar() + { + var a = 1; + var b = 2; + var x = "Stew"; + var y = "00.123"; + var c = a + b == 3 || b + a == 2; + var d = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts) && x.Contains('S'); + var e = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts1) + && x.Contains('S'); + var f = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts2) + && + x.Contains('S'); + var g = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts3) && + x.Contains('S'); + return d && e == true; + } + public async System.Threading.Tasks.Task OtherBarAsync() + { + var a = 1; + var b = 2; + var x = "Stew"; + var y = "00.123"; + var c = a + b == 3 || b + a == 2; + var d = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts) && await AsyncMethod(); + var e = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts1) + && await AsyncMethod(); + var f = TimeSpan.TryParseExact(y, @"ss\.fff", null, out var ts2) + && + await AsyncMethod(); + var g = await AsyncMethod() && + await AsyncMethod(); + Console.WriteLine(g); + await System.Threading.Tasks.Task.CompletedTask; + } + public async System.Threading.Tasks.Task AsyncMethod() + { + await System.Threading.Tasks.Task.Delay(1); + Console.WriteLine($"time for await"); + return true; + } +} + From 9338fd8415d7f48ebfe31ea86e54929ed55448bb Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 20 Jul 2021 21:48:39 -0400 Subject: [PATCH 704/926] Re-enable async method state machine clearing test (#56007) --- .../AsyncTaskMethodBuilderTests.cs | 3 +-- .../tests/Task/ExecutionContextFlowTest.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs index 647f3cbdcc6..1babd5ea925 100644 --- a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs @@ -523,8 +523,7 @@ namespace System.Threading.Tasks.Tests TaskScheduler.UnobservedTaskException -= handler; } - [ActiveIssue("https://github.com/dotnet/runtime/issues/30122")] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion() { // Create a finalizable object that'll be referenced by both an async method's diff --git a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs index a29648a7a4b..5b832f2af56 100644 --- a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs +++ b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs @@ -29,8 +29,7 @@ namespace System.Threading.Tasks.Tests } } - [ActiveIssue("https://github.com/dotnet/runtime/issues/30122")] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] public static async Task TaskDropsExecutionContextUponCompletion() { // Create a finalizable object that'll be referenced by captured ExecutionContext, From b64df2152d59680c487c6862d8cfea97084d7518 Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Madero Date: Tue, 20 Jul 2021 19:08:38 -0700 Subject: [PATCH 705/926] Disable implicit namespace imports (#56046) * Disable implicit namespace imports --- src/libraries/Directory.Build.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 5e28db1deeb..87dda78fb5b 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -59,6 +59,10 @@ '$(DisableImplicitFrameworkReferences)' != 'true' and '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and ('$(IsReferenceAssembly)' == 'true' or '$(IsSourceProject)' == 'true')">true + + + true From 5e176538e78f040526b7608590d20244def47d95 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Wed, 21 Jul 2021 04:13:10 +0200 Subject: [PATCH 706/926] Disable test Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_Success on Linux (#56002) * Disable test Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_Success on Linux Disabled test tracked by #55053 * Update Connect.cs --- .../System.Net.Sockets/tests/FunctionalTests/Connect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index e4c64fc2e35..01c6b48ed4d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -84,6 +84,7 @@ namespace System.Net.Sockets.Tests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55053", TestPlatforms.Linux)] public async Task Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_Success() { if (!SupportsMultiConnect) From 9815e190e495f3598ed12909d206de8c2b2e387c Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 20 Jul 2021 19:18:51 -0700 Subject: [PATCH 707/926] disable ClientSettingsReceived_Success on Mock (#56064) --- .../tests/FunctionalTests/HttpClientHandlerTest.Http3.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 0817b72987d..22d0370ef10 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -85,6 +85,12 @@ namespace System.Net.Http.Functional.Tests [InlineData(1000)] public async Task SendMoreThanStreamLimitRequests_Succeeds(int streamLimit) { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55957")] + if (this.UseQuicImplementationProvider == QuicImplementationProviders.Mock) + { + return; + } + using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options(){ MaxBidirectionalStreams = streamLimit }); Task serverTask = Task.Run(async () => From 5d4860a853228a56e8f00ad3c8ef6cc5ea3b60f4 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Wed, 21 Jul 2021 04:54:55 +0200 Subject: [PATCH 708/926] Disable Http2_ServerSendsInvalidSettingsValue_Error (#56041) Tracked by #1581 --- .../tests/FunctionalTests/HttpClientHandlerTest.Http2.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index c9c5db60d0f..1aadb515353 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -243,6 +243,7 @@ namespace System.Net.Http.Functional.Tests } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/1581")] [ConditionalTheory(nameof(SupportsAlpn))] [InlineData(SettingId.MaxFrameSize, 16383)] [InlineData(SettingId.MaxFrameSize, 162777216)] From 577136cefbce63471c01028a5c7968113f8db86b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 20 Jul 2021 20:10:23 -0700 Subject: [PATCH 709/926] check handle before shutdown in quic connection Dispose (#56047) * check handle before shutdown in quic connection Dispose * add comment --- .../Quic/Implementations/MsQuic/MsQuicConnection.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index ef881266891..6fb65889585 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -712,10 +712,14 @@ namespace System.Net.Quic.Implementations.MsQuic if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(_state, $"{TraceId()} Stream disposing {disposing}"); // If we haven't already shutdown gracefully (via a successful CloseAsync call), then force an abortive shutdown. - MsQuicApi.Api.ConnectionShutdownDelegate( - _state.Handle, - QUIC_CONNECTION_SHUTDOWN_FLAGS.SILENT, - 0); + if (_state.Handle != null) + { + // Handle can be null if outbound constructor failed and we are called from finalizer. + MsQuicApi.Api.ConnectionShutdownDelegate( + _state.Handle, + QUIC_CONNECTION_SHUTDOWN_FLAGS.SILENT, + 0); + } bool releaseHandles = false; lock (_state) From b2107c5e48d2fa5163aa6bf3182a530a04d1533c Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 20 Jul 2021 20:12:31 -0700 Subject: [PATCH 710/926] throw PNSE for unsupported SSL options in Quic. (#55877) * throw PNSP for unsupported SSL options * add missing resource file change * fix spacing --- .../src/Resources/Strings.resx | 3 ++ .../Interop/SafeMsQuicConfigurationHandle.cs | 53 +++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/Resources/Strings.resx b/src/libraries/System.Net.Quic/src/Resources/Strings.resx index a29352a0578..061e1647eb8 100644 --- a/src/libraries/System.Net.Quic/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Quic/src/Resources/Strings.resx @@ -150,5 +150,8 @@ Writing is not allowed on stream. + + The '{0}' is not supported by System.Net.Quic. + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index df48e0db377..96b168977af 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -36,20 +36,39 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) { X509Certificate? certificate = null; - if (options.ClientAuthenticationOptions?.ClientCertificates != null) + + if (options.ClientAuthenticationOptions != null) { - foreach (var cert in options.ClientAuthenticationOptions.ClientCertificates) + if (options.ClientAuthenticationOptions.CipherSuitesPolicy != null) { - try + throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.CipherSuitesPolicy))); + } + + if (options.ClientAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.NoEncryption) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.EncryptionPolicy))); + } + + if (options.ClientAuthenticationOptions.LocalCertificateSelectionCallback != null) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.LocalCertificateSelectionCallback))); + } + + if (options.ClientAuthenticationOptions.ClientCertificates != null) + { + foreach (var cert in options.ClientAuthenticationOptions.ClientCertificates) { - if (((X509Certificate2)cert).HasPrivateKey) + try { - // Pick first certificate with private key. - certificate = cert; - break; + if (((X509Certificate2)cert).HasPrivateKey) + { + // Pick first certificate with private key. + certificate = cert; + break; + } } + catch { } } - catch { } } } @@ -59,9 +78,23 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal public static unsafe SafeMsQuicConfigurationHandle Create(QuicListenerOptions options) { QUIC_CREDENTIAL_FLAGS flags = QUIC_CREDENTIAL_FLAGS.NONE; - if (options.ServerAuthenticationOptions != null && options.ServerAuthenticationOptions.ClientCertificateRequired) + + if (options.ServerAuthenticationOptions != null) { - flags |= QUIC_CREDENTIAL_FLAGS.REQUIRE_CLIENT_AUTHENTICATION | QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION; + if (options.ServerAuthenticationOptions.CipherSuitesPolicy != null) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ServerAuthenticationOptions.CipherSuitesPolicy))); + } + + if (options.ServerAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.NoEncryption) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ServerAuthenticationOptions.EncryptionPolicy))); + } + + if (options.ServerAuthenticationOptions.ClientCertificateRequired) + { + flags |= QUIC_CREDENTIAL_FLAGS.REQUIRE_CLIENT_AUTHENTICATION | QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION; + } } return Create(options, flags, options.ServerAuthenticationOptions?.ServerCertificate, options.ServerAuthenticationOptions?.ServerCertificateContext, options.ServerAuthenticationOptions?.ApplicationProtocols); From efbe6fd7095f241520c9df284215fe25579c2612 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang <16830051+mdh1418@users.noreply.github.com> Date: Wed, 21 Jul 2021 02:15:52 -0400 Subject: [PATCH 711/926] [libraries][Android][iOSSimulator] Remove Active Issues from tests no longer failing due to properly set feature switches (#55974) --- .../ComponentModel/DataAnnotations/ValidationExceptionTests.cs | 1 - .../tests/CalendarTestWithConfigSwitch/CalendarTests.cs | 1 - .../System.ObjectModel/tests/KeyedCollection/Serialization.cs | 1 - .../ObservableCollection/ObservableCollection_Serialization.cs | 1 - .../ReadOnlyDictionary_SerializationTests.cs | 1 - .../ReadOnlyObservableCollection_SerializationTests.cs | 1 - .../Runtime/Serialization/InvalidDataContractExceptionTests.cs | 1 - .../tests/RegexMatchTimeoutExceptionTests.cs | 1 - .../tests/RegexParserTests.netcoreapp.cs | 3 +-- .../tests/ChannelClosedExceptionTests.netcoreapp.cs | 1 - src/libraries/tests.proj | 3 --- 11 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationExceptionTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationExceptionTests.cs index 5c57e392ec4..65831b548f1 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationExceptionTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationExceptionTests.cs @@ -81,7 +81,6 @@ namespace System.ComponentModel.DataAnnotations.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50882", TestPlatforms.Android)] public void Ctor_SerializationInfo_StreamingContext() { using (var stream = new MemoryStream()) diff --git a/src/libraries/System.Globalization.Calendars/tests/CalendarTestWithConfigSwitch/CalendarTests.cs b/src/libraries/System.Globalization.Calendars/tests/CalendarTestWithConfigSwitch/CalendarTests.cs index a8b0dbbabbe..4d1205811b8 100644 --- a/src/libraries/System.Globalization.Calendars/tests/CalendarTestWithConfigSwitch/CalendarTests.cs +++ b/src/libraries/System.Globalization.Calendars/tests/CalendarTestWithConfigSwitch/CalendarTests.cs @@ -10,7 +10,6 @@ namespace System.Globalization.Tests public static class CalendarTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/36883", TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS | TestPlatforms.Android)] public static void TestJapaneseCalendarDateParsing() { CultureInfo ciJapanese = new CultureInfo("ja-JP") { DateTimeFormat = { Calendar = new JapaneseCalendar() } }; diff --git a/src/libraries/System.ObjectModel/tests/KeyedCollection/Serialization.cs b/src/libraries/System.ObjectModel/tests/KeyedCollection/Serialization.cs index 7c73b108a5b..1c144060067 100644 --- a/src/libraries/System.ObjectModel/tests/KeyedCollection/Serialization.cs +++ b/src/libraries/System.ObjectModel/tests/KeyedCollection/Serialization.cs @@ -18,7 +18,6 @@ namespace System.Collections.ObjectModel.Tests [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] [MemberData(nameof(SerializeDeserialize_Roundtrips_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50933", TestPlatforms.Android)] public void SerializeDeserialize_Roundtrips(TestCollection c) { TestCollection clone = BinaryFormatterHelpers.Clone(c); diff --git a/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_Serialization.cs b/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_Serialization.cs index 73ab89a3e6f..65d5694a93a 100644 --- a/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_Serialization.cs +++ b/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_Serialization.cs @@ -19,7 +19,6 @@ namespace System.Collections.ObjectModel.Tests [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] [MemberData(nameof(SerializeDeserialize_Roundtrips_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50933", TestPlatforms.Android)] public void SerializeDeserialize_Roundtrips(ObservableCollection c) { ObservableCollection clone = BinaryFormatterHelpers.Clone(c); diff --git a/src/libraries/System.ObjectModel/tests/ReadOnlyDictionary/ReadOnlyDictionary_SerializationTests.cs b/src/libraries/System.ObjectModel/tests/ReadOnlyDictionary/ReadOnlyDictionary_SerializationTests.cs index bc6981b05c7..d0bb38d2162 100644 --- a/src/libraries/System.ObjectModel/tests/ReadOnlyDictionary/ReadOnlyDictionary_SerializationTests.cs +++ b/src/libraries/System.ObjectModel/tests/ReadOnlyDictionary/ReadOnlyDictionary_SerializationTests.cs @@ -18,7 +18,6 @@ namespace System.Collections.ObjectModel.Tests [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] [MemberData(nameof(SerializeDeserialize_Roundtrips_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50933", TestPlatforms.Android)] public void SerializeDeserialize_Roundtrips(ReadOnlyDictionary d) { ReadOnlyDictionary clone = BinaryFormatterHelpers.Clone(d); diff --git a/src/libraries/System.ObjectModel/tests/ReadOnlyObservableCollection/ReadOnlyObservableCollection_SerializationTests.cs b/src/libraries/System.ObjectModel/tests/ReadOnlyObservableCollection/ReadOnlyObservableCollection_SerializationTests.cs index 599e9026666..ed818b6a619 100644 --- a/src/libraries/System.ObjectModel/tests/ReadOnlyObservableCollection/ReadOnlyObservableCollection_SerializationTests.cs +++ b/src/libraries/System.ObjectModel/tests/ReadOnlyObservableCollection/ReadOnlyObservableCollection_SerializationTests.cs @@ -19,7 +19,6 @@ namespace System.Collections.ObjectModel.Tests [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] [MemberData(nameof(SerializeDeserialize_Roundtrips_MemberData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50933", TestPlatforms.Android)] public void SerializeDeserialize_Roundtrips(ReadOnlyObservableCollection c) { ReadOnlyObservableCollection clone = BinaryFormatterHelpers.Clone(c); diff --git a/src/libraries/System.Runtime.Serialization.Primitives/tests/System/Runtime/Serialization/InvalidDataContractExceptionTests.cs b/src/libraries/System.Runtime.Serialization.Primitives/tests/System/Runtime/Serialization/InvalidDataContractExceptionTests.cs index 8dabe28fdf1..57a4d5ac54f 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/tests/System/Runtime/Serialization/InvalidDataContractExceptionTests.cs +++ b/src/libraries/System.Runtime.Serialization.Primitives/tests/System/Runtime/Serialization/InvalidDataContractExceptionTests.cs @@ -37,7 +37,6 @@ namespace System.Runtime.Serialization.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50936", TestPlatforms.Android)] public void Ctor_SerializationInfo_StreamingContext() { using (var memoryStream = new MemoryStream()) diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexMatchTimeoutExceptionTests.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexMatchTimeoutExceptionTests.cs index 5ecff0a4c60..438c7659a46 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/RegexMatchTimeoutExceptionTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/RegexMatchTimeoutExceptionTests.cs @@ -44,7 +44,6 @@ namespace System.Text.RegularExpressions.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50942", TestPlatforms.Android)] public void SerializationRoundtrip() { const string Input = "abcdef"; diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netcoreapp.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netcoreapp.cs index d3cfa07dd4a..9e246688ea6 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netcoreapp.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netcoreapp.cs @@ -97,7 +97,6 @@ namespace System.Text.RegularExpressions.Tests } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50942", TestPlatforms.Android)] public void RegexParseException_Serializes() { #pragma warning disable RE0001 // Regex issue: Not enough )'s @@ -141,7 +140,7 @@ namespace System.Text.RegularExpressions.Tests throw new XunitException($"Expected RegexParseException with error: ({error}) -> Actual error: {regexParseError})"); } catch (Exception e) - { + { throw new XunitException($"Expected RegexParseException -> Actual: ({e})"); } diff --git a/src/libraries/System.Threading.Channels/tests/ChannelClosedExceptionTests.netcoreapp.cs b/src/libraries/System.Threading.Channels/tests/ChannelClosedExceptionTests.netcoreapp.cs index c222af4f3c3..70eca333658 100644 --- a/src/libraries/System.Threading.Channels/tests/ChannelClosedExceptionTests.netcoreapp.cs +++ b/src/libraries/System.Threading.Channels/tests/ChannelClosedExceptionTests.netcoreapp.cs @@ -10,7 +10,6 @@ namespace System.Threading.Channels.Tests public partial class ChannelClosedExceptionTests { [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50943", TestPlatforms.Android)] public void Serialization_Roundtrip() { var s = new MemoryStream(); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index de7976410e6..5217eb58564 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -65,9 +65,6 @@ - - - From 7c72b729ac129c6c7918b5592d30e06c191a6f71 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 21 Jul 2021 02:18:20 -0400 Subject: [PATCH 712/926] Add back regex tests from reverted .* optimization (#56070) --- .../tests/Regex.Match.Tests.cs | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs index 719f280c917..740d0ef91bf 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs @@ -204,9 +204,11 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "abc", "abc", RegexOptions.None, 0, 3, true, "abc" }; yield return new object[] { "abc", "aBc", RegexOptions.None, 0, 3, false, string.Empty }; yield return new object[] { "abc", "aBc", RegexOptions.IgnoreCase, 0, 3, true, "aBc" }; + yield return new object[] { @"abc.*def", "abcghiDEF", RegexOptions.IgnoreCase, 0, 9, true, "abcghiDEF" }; // Using *, +, ?, {}: Actual - "a+\\.?b*\\.?c{2}" yield return new object[] { @"a+\.?b*\.+c{2}", "ab.cc", RegexOptions.None, 0, 5, true, "ab.cc" }; + yield return new object[] { @"[^a]+\.[^z]+", "zzzzz", RegexOptions.None, 0, 5, false, string.Empty }; // RightToLeft yield return new object[] { @"\s+\d+", "sdf 12sad", RegexOptions.RightToLeft, 0, 9, true, " 12" }; @@ -381,6 +383,62 @@ namespace System.Text.RegularExpressions.Tests yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D2", options, 0, 2, true, "\u05D0\u05D2" }; yield return new object[] { "\u05D0(?:\u05D1|\u05D2|\u05D3)", "\u05D0\u05D4", options, 0, 0, false, "" }; } + + // .* : Case sensitive + foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Compiled }) + { + yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\nFoo", $"\nFooThis should match", options, 0, 21, true, "\nFoo" }; + yield return new object[] { @".*\nfoo", "\nfooThis should match", options, 4, 17, false, "" }; + + yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; + yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; + yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 24, 5, false, "" }; + + yield return new object[] { @".*\dfoo", "1fooThis1foo should 1foo match", options, 4, 9, true, "This1foo" }; + yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; + } + + // .* : Case insensitive + foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase, RegexOptions.IgnoreCase | RegexOptions.Compiled }) + { + yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; + yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; + yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; + yield return new object[] { @".*\dfoo", "1fooThis1FOO should 1foo match", options, 4, 9, true, "This1FOO" }; + } + + // .* : RTL, Case-sensitive + foreach (RegexOptions options in new[] { RegexOptions.None | RegexOptions.RightToLeft, RegexOptions.Compiled | RegexOptions.RightToLeft }) + { + yield return new object[] { @".*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @"a.*\nfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\nFoo", $"This should match\nFoo", options, 0, 21, true, "This should match\nFoo" }; + yield return new object[] { @".*\nfoo", "This should matchfoo\n", options, 4, 13, false, "" }; + + yield return new object[] { @".*\dfoo", "This shouldn't match", options, 0, 20, false, "" }; + yield return new object[] { @".*\dFoo", "This1Foo should match", options, 0, 21, true, "This1Foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2Foo match", options, 0, 26, true, "This1foo should 2Foo" }; + yield return new object[] { @".*\dFoo", "This1foo shouldn't 2foo match", options, 0, 29, false, "" }; + yield return new object[] { @".*\dfoo", "This1foo shouldn't 2foo match", options, 19, 0, false, "" }; + + yield return new object[] { @".*\dfoo", "1fooThis2foo should 1foo match", options, 8, 4, true, "2foo" }; + yield return new object[] { @".*\dfoo", "This shouldn't match 1foo", options, 0, 20, false, "" }; + } + + // .* : RTL, case insensitive + foreach (RegexOptions options in new[] { RegexOptions.IgnoreCase | RegexOptions.RightToLeft, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.RightToLeft }) + { + yield return new object[] { @".*\nFoo", "\nfooThis should match", options, 0, 21, true, "\nfoo" }; + yield return new object[] { @".*\dFoo", "This1foo should match", options, 0, 21, true, "This1foo" }; + yield return new object[] { @".*\dFoo", "This1foo should 2FoO match", options, 0, 26, true, "This1foo should 2FoO" }; + yield return new object[] { @".*\dFoo", "This1Foo should 2fOo match", options, 0, 26, true, "This1Foo should 2fOo" }; + yield return new object[] { @".*\dfoo", "1fooThis2FOO should 1foo match", options, 8, 4, true, "2FOO" }; + } } public static IEnumerable Match_Basic_TestData_NetCore() @@ -617,7 +675,7 @@ namespace System.Text.RegularExpressions.Tests public void Match_Timeout_Repetition_Throws(RegexOptions options) { int repetitionCount = 800_000_000; - var regex = new Regex(@"a\s{" + repetitionCount+ "}", options, TimeSpan.FromSeconds(1)); + var regex = new Regex(@"a\s{" + repetitionCount + "}", options, TimeSpan.FromSeconds(1)); string input = @"a" + new string(' ', repetitionCount) + @"b"; Assert.Throws(() => regex.Match(input)); } From 3746672c5b78f0aacf43ee1d170c3ca88c34ce91 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Wed, 21 Jul 2021 04:37:55 -0300 Subject: [PATCH 713/926] [debugger] Export mono_debugger_agent_unhandled_exception to avoid usage of Debugger.Mono_UnhandledException (#55963) --- src/mono/mono/metadata/debug-helpers.h | 3 +++ src/mono/mono/mini/debugger-agent-external.c | 9 +++++++++ src/mono/mono/mini/mini-runtime.c | 5 ----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/metadata/debug-helpers.h b/src/mono/mono/metadata/debug-helpers.h index 8ecdf8edc58..795eb53d724 100644 --- a/src/mono/mono/metadata/debug-helpers.h +++ b/src/mono/mono/metadata/debug-helpers.h @@ -48,6 +48,9 @@ MONO_API char* mono_method_get_reflection_name (MonoMethod *method); MONO_API char* mono_field_full_name (MonoClassField *field); +MONO_API MONO_RT_EXTERNAL_ONLY void +mono_debugger_agent_unhandled_exception (MonoException *e); + MONO_END_DECLS #endif /* __MONO_DEBUG_HELPERS_H__ */ diff --git a/src/mono/mono/mini/debugger-agent-external.c b/src/mono/mono/mini/debugger-agent-external.c index af2d1057359..2fc584c5ad9 100644 --- a/src/mono/mono/mini/debugger-agent-external.c +++ b/src/mono/mono/mini/debugger-agent-external.c @@ -5,6 +5,7 @@ #include #include #include "debugger-agent-external.h" +#include #ifndef DISABLE_SDB @@ -59,4 +60,12 @@ mono_debugger_agent_get_sdb_options (void) { return sdb_options; } + +void +mono_debugger_agent_unhandled_exception (MonoException *e) +{ + MONO_ENTER_GC_UNSAFE; + MONO_EXTERNAL_ONLY_VOID (mono_component_debugger ()->unhandled_exception (e)); + MONO_EXIT_GC_UNSAFE; +} #endif /* DISABLE_SDB */ diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 7ba23bd560f..36400c7e9fd 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4607,11 +4607,6 @@ register_icalls (void) mono_add_internal_call_internal ("Mono.Runtime::mono_runtime_install_handlers", mono_runtime_install_handlers); -#if defined(HOST_ANDROID) || defined(TARGET_ANDROID) - mono_add_internal_call_internal ("System.Diagnostics.Debugger::Mono_UnhandledException_internal", - mono_component_debugger ()->unhandled_exception); -#endif - /* * It's important that we pass `TRUE` as the last argument here, as * it causes the JIT to omit a wrapper for these icalls. If the JIT From 415356fd661764d9413ab6d41d827868dbb81447 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 21 Jul 2021 01:08:52 -0700 Subject: [PATCH 714/926] Convert the last crossgen tests to use crossgen2 (#56061) - take advantage of the AlwaysUseCrossGen2 flag instead of manually updating all the crossgen commands - remove version bubbles test. We have much more complex testing for that scenario now - remove regular crossgen logic from the script generator --- src/tests/Common/CLRTest.CrossGen.targets | 58 +++---------------- .../BasicTest_DefaultMode_R2r.csproj | 13 +---- .../BasicTest_QuickJitForLoopsOff_R2r.csproj | 5 +- .../BasicTest_QuickJitForLoopsOn_R2r.csproj | 5 +- .../BasicTest_QuickJitOff_R2r.csproj | 5 +- .../BasicTest_QuickJitOn_R2r.csproj | 5 +- .../DynamicMethodGCStress.csproj | 7 +-- .../fileversionpreservation.csproj | 10 ---- .../tests/genericsload/callgenericctor.csproj | 17 +----- .../tests/genericsload/usegenericfield.csproj | 16 +---- .../readytorun/tests/versionbubbles/helper.cs | 13 ----- .../tests/versionbubbles/helper.csproj | 10 ---- .../tests/versionbubbles/versionbubbles.cs | 41 ------------- .../versionbubbles/versionbubbles.csproj | 39 ------------- 14 files changed, 32 insertions(+), 212 deletions(-) delete mode 100644 src/tests/readytorun/tests/versionbubbles/helper.cs delete mode 100644 src/tests/readytorun/tests/versionbubbles/helper.csproj delete mode 100644 src/tests/readytorun/tests/versionbubbles/versionbubbles.cs delete mode 100644 src/tests/readytorun/tests/versionbubbles/versionbubbles.csproj diff --git a/src/tests/Common/CLRTest.CrossGen.targets b/src/tests/Common/CLRTest.CrossGen.targets index a5b98a2e45a..8557a71f0d5 100644 --- a/src/tests/Common/CLRTest.CrossGen.targets +++ b/src/tests/Common/CLRTest.CrossGen.targets @@ -32,6 +32,8 @@ WARNING: When setting properties based on their current state (for example: --> + + @@ -185,31 +165,6 @@ if /i "$(AlwaysUseCrossGen2)" == "true" ( set CompositeBuildMode=1 ) -REM CrossGen Script -if defined RunCrossGen ( - if defined LargeVersionBubble ( set OptionalArguments=!OptionalArguments! /largeversionbubble) - set COMPlus_ZapRequire=$(ZapRequire) - set COMPlus_ZapRequireList=$(MSBuildProjectName) - if not exist "$(MSBuildProjectName).org" ( - call :TakeLock - set CrossGenStatus=0 - if not exist "$(MSBuildProjectName).org" ( - mkdir IL - copy $(MSBuildProjectName).dll IL\$(MSBuildProjectName).dll - ren $(MSBuildProjectName).dll $(MSBuildProjectName).org - set __Command=!_DebuggerFullPath! "!CORE_ROOT!\crossgen.exe" !OptionalArguments! /Platform_Assemblies_Paths !CORE_ROOT!%3B%25cd%25 /in %21scriptPath%21$(MSBuildProjectName).org /out %21scriptPath%21\$(MSBuildProjectName).dll - echo "!__Command!" - call !__Command! - set CrossGenStatus=!ERRORLEVEL! - ) - call :ReleaseLock - IF NOT !CrossGenStatus!==0 ( - ECHO Crossgen failed with exitcode - !CrossGenStatus! - Exit /b 1 - ) - ) -) - REM CrossGen2 Script if defined RunCrossGen2 ( set ExtraCrossGen2Args=!ExtraCrossGen2Args! $(CrossGen2TestExtraArguments) @@ -309,6 +264,9 @@ if defined RunCrossGen2 ( ECHO Crossgen2 failed with exitcode - !CrossGen2Status! Exit /b 1 ) + + set COMPlus_ZapRequire=$(ZapRequire) + set COMPlus_ZapRequireList=$(MSBuildProjectName) ) ]]> diff --git a/src/tests/baseservices/TieredCompilation/BasicTest_DefaultMode_R2r.csproj b/src/tests/baseservices/TieredCompilation/BasicTest_DefaultMode_R2r.csproj index c70b31875bc..0fc2f0156e2 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTest_DefaultMode_R2r.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTest_DefaultMode_R2r.csproj @@ -4,18 +4,11 @@ true true 0 + + + true - - - - diff --git a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOff_R2r.csproj b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOff_R2r.csproj index 12a7d3b53f1..cde6bf57b4f 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOff_R2r.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOff_R2r.csproj @@ -4,6 +4,9 @@ true true 0 + + + true @@ -11,12 +14,10 @@ diff --git a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOn_R2r.csproj b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOn_R2r.csproj index dd8ccaf5371..d6bff3ce0ca 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOn_R2r.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitForLoopsOn_R2r.csproj @@ -4,6 +4,9 @@ true true 0 + + + true @@ -11,12 +14,10 @@ diff --git a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOff_R2r.csproj b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOff_R2r.csproj index 2c6b31add62..351d8426cc8 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOff_R2r.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOff_R2r.csproj @@ -4,6 +4,9 @@ true true 0 + + + true @@ -11,12 +14,10 @@ diff --git a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOn_R2r.csproj b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOn_R2r.csproj index a1c9ebb3ef0..a8bfb516443 100644 --- a/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOn_R2r.csproj +++ b/src/tests/baseservices/TieredCompilation/BasicTest_QuickJitOn_R2r.csproj @@ -4,6 +4,9 @@ true true 0 + + + true @@ -11,12 +14,10 @@ diff --git a/src/tests/readytorun/DynamicMethodGCStress/DynamicMethodGCStress.csproj b/src/tests/readytorun/DynamicMethodGCStress/DynamicMethodGCStress.csproj index 2e7ffa53a2a..00065733e7d 100644 --- a/src/tests/readytorun/DynamicMethodGCStress/DynamicMethodGCStress.csproj +++ b/src/tests/readytorun/DynamicMethodGCStress/DynamicMethodGCStress.csproj @@ -5,6 +5,9 @@ true true + + + true @@ -12,14 +15,10 @@ diff --git a/src/tests/readytorun/tests/fileversionpreservation/fileversionpreservation.csproj b/src/tests/readytorun/tests/fileversionpreservation/fileversionpreservation.csproj index 1ceecd2a654..754833e6443 100644 --- a/src/tests/readytorun/tests/fileversionpreservation/fileversionpreservation.csproj +++ b/src/tests/readytorun/tests/fileversionpreservation/fileversionpreservation.csproj @@ -10,14 +10,4 @@ - - - - diff --git a/src/tests/readytorun/tests/genericsload/callgenericctor.csproj b/src/tests/readytorun/tests/genericsload/callgenericctor.csproj index fea80503152..248d330fc53 100644 --- a/src/tests/readytorun/tests/genericsload/callgenericctor.csproj +++ b/src/tests/readytorun/tests/genericsload/callgenericctor.csproj @@ -2,9 +2,10 @@ exe BuildAndRun - 1 1 - false + + + true @@ -14,16 +15,4 @@ - - - - diff --git a/src/tests/readytorun/tests/genericsload/usegenericfield.csproj b/src/tests/readytorun/tests/genericsload/usegenericfield.csproj index b5a3e659606..da88c6e840e 100644 --- a/src/tests/readytorun/tests/genericsload/usegenericfield.csproj +++ b/src/tests/readytorun/tests/genericsload/usegenericfield.csproj @@ -4,7 +4,9 @@ BuildAndRun 1 1 - false + + + true @@ -14,16 +16,4 @@ - - - - diff --git a/src/tests/readytorun/tests/versionbubbles/helper.cs b/src/tests/readytorun/tests/versionbubbles/helper.cs deleted file mode 100644 index 0fc5aa66ec4..00000000000 --- a/src/tests/readytorun/tests/versionbubbles/helper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -public class Helper -{ - [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] - public string GetLastMethodName() - { - StackTrace st = new StackTrace(); - return st.GetFrame(0).GetMethod().Name; - } -} diff --git a/src/tests/readytorun/tests/versionbubbles/helper.csproj b/src/tests/readytorun/tests/versionbubbles/helper.csproj deleted file mode 100644 index 9395dc39839..00000000000 --- a/src/tests/readytorun/tests/versionbubbles/helper.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - library - SharedLibrary - True - - - - - diff --git a/src/tests/readytorun/tests/versionbubbles/versionbubbles.cs b/src/tests/readytorun/tests/versionbubbles/versionbubbles.cs deleted file mode 100644 index fd1f596571e..00000000000 --- a/src/tests/readytorun/tests/versionbubbles/versionbubbles.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -public class Program -{ - public static int Main() - { - return RunTest(); - } - - public static int RunTest() - { - - Helper helper = new Helper(); - string lastMethodName = String.Empty; - - lastMethodName = helper.GetLastMethodName(); - - if((System.Environment.GetEnvironmentVariable("LargeVersionBubble") == null)) - { - // Cross-Assembly inlining is only allowed in multi-module version bubbles - Console.WriteLine("Large Version Bubble is disabled."); - Console.WriteLine("PASS"); - return 100; - } - - Console.WriteLine("Large Version Bubble is enabled."); - // Helper returns the name of the method in the last stack frame - // Check to see if the method has been inlined cross-module - if (lastMethodName != "GetLastMethodName") - { - // method in helper.cs has been inlined - Console.WriteLine("PASS"); - return 100; - } - else - { - Console.WriteLine("FAIL"); - return 101; - } - } -} diff --git a/src/tests/readytorun/tests/versionbubbles/versionbubbles.csproj b/src/tests/readytorun/tests/versionbubbles/versionbubbles.csproj deleted file mode 100644 index d9e83b34f0e..00000000000 --- a/src/tests/readytorun/tests/versionbubbles/versionbubbles.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - exe - BuildAndRun - 1 - 1 - PdbOnly - True - false - - - - {F74F55A1-DFCF-4C7C-B462-E96E1D0BB667} - - - - - - - - - - From 1a84b29e367493c50dc979d7897a386734b8fd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Wed, 21 Jul 2021 08:24:09 +0000 Subject: [PATCH 715/926] Removed AppContext switch checking from S.N.Quic (#56027) --- .../MsQuic/Internal/MsQuicApi.cs | 38 ------------------- .../System.Net.Quic.Functional.Tests.csproj | 3 -- 2 files changed, 41 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index b691d3c5c14..e2b46f2bb89 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -125,16 +125,6 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal static MsQuicApi() { - if (!IsHttp3Enabled()) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(null, $"HTTP/3 and QUIC is not enabled, see 'System.Net.SocketsHttpHandler.Http3Support' AppContext switch."); - } - - return; - } - if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported()) { if (NetEventSource.Log.IsEnabled()) @@ -171,34 +161,6 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal } } - // Note that this is copy-pasted from S.N.Http just to hide S.N.Quic behind the same AppContext switch - // since this library is considered "private" for 6.0. - // We should get rid of this once S.N.Quic API surface is officially exposed. - private static bool IsHttp3Enabled() - { - bool value; - - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch("System.Net.SocketsHttpHandler.Http3Support", out value)) - { - return value; - } - - // AppContext switch wasn't used. Check the environment variable. - string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT"); - - if (bool.TryParse(envVar, out value)) - { - return value; - } - else if (uint.TryParse(envVar, out uint intVal)) - { - return intVal != 0; - } - - return false; - } - private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major, MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index 583b2b6c486..2a17894a555 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -4,9 +4,6 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix - - - From 94a843027c5fe3550ec9ba8568b8872aff47779d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 21 Jul 2021 11:29:36 +0200 Subject: [PATCH 716/926] Disallow widening for explicit tailcalls (#55989) It is a runtime detail that the managed calling convention widens return values, so only allow this behavior for opportunistic tailcalls. Fix #55253 --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/importer.cpp | 46 ++++++++++++++----- src/coreclr/jit/morph.cpp | 2 +- .../JitBlue/Runtime_54842/Runtime_54842.cs | 3 ++ .../JitBlue/Runtime_55140/Runtime_55140.cs | 3 ++ .../JitBlue/Runtime_55253/Runtime_55253.cs | 24 ++++++++++ .../Runtime_55253/Runtime_55253.csproj | 22 +++++++++ 7 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.csproj diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b34535a6146..435b38d251f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4603,7 +4603,8 @@ private: bool exactContextNeedsRuntimeLookup, CORINFO_CALL_INFO* callInfo); - bool impTailCallRetTypeCompatible(var_types callerRetType, + bool impTailCallRetTypeCompatible(bool allowWidening, + var_types callerRetType, CORINFO_CLASS_HANDLE callerRetTypeClass, CorInfoCallConvExtension callerCallConv, var_types calleeRetType, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5d6c0d7354f..f6ec7f38241 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -7863,11 +7863,32 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo) impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); } -// Checks whether the return types of caller and callee are compatible -// so that callee can be tail called. Note that here we don't check -// compatibility in IL Verifier sense, but on the lines of return type -// sizes are equal and get returned in the same return register. -bool Compiler::impTailCallRetTypeCompatible(var_types callerRetType, +//------------------------------------------------------------------------ +// impTailCallRetTypeCompatible: Checks whether the return types of caller +// and callee are compatible so that calle can be tail called. +// sizes are not supported integral type sizes return values to temps. +// +// Arguments: +// allowWidening -- whether to allow implicit widening by the callee. +// For instance, allowing int32 -> int16 tailcalls. +// The managed calling convention allows this, but +// we don't want explicit tailcalls to depend on this +// detail of the managed calling convention. +// callerRetType -- the caller's return type +// callerRetTypeClass - the caller's return struct type +// callerCallConv -- calling convention of the caller +// calleeRetType -- the callee's return type +// calleeRetTypeClass - the callee return struct type +// calleeCallConv -- calling convention of the callee +// +// Returns: +// True if the tailcall types are compatible. +// +// Remarks: +// Note that here we don't check compatibility in IL Verifier sense, but on the +// lines of return types getting returned in the same return register. +bool Compiler::impTailCallRetTypeCompatible(bool allowWidening, + var_types callerRetType, CORINFO_CLASS_HANDLE callerRetTypeClass, CorInfoCallConvExtension callerCallConv, var_types calleeRetType, @@ -7886,7 +7907,7 @@ bool Compiler::impTailCallRetTypeCompatible(var_types callerRetTy bool isManaged = (callerCallConv == CorInfoCallConvExtension::Managed) && (calleeCallConv == CorInfoCallConvExtension::Managed); - if (isManaged && varTypeIsIntegral(callerRetType) && varTypeIsIntegral(calleeRetType) && + if (allowWidening && isManaged && varTypeIsIntegral(callerRetType) && varTypeIsIntegral(calleeRetType) && (genTypeSize(callerRetType) <= 4) && (genTypeSize(calleeRetType) <= genTypeSize(callerRetType))) { return true; @@ -9096,13 +9117,14 @@ DONE: BADCODE("Stack should be empty after tailcall"); } - // Note that we can not relax this condition with genActualType() as - // the calling convention dictates that the caller of a function with - // a small-typed return value is responsible for normalizing the return val - + // For opportunistic tailcalls we allow implicit widening, i.e. tailcalls from int32 -> int16, since the + // managed calling convention dictates that the callee widens the value. For explicit tailcalls we don't + // want to require this detail of the calling convention to bubble up to the tailcall helpers + bool allowWidening = isImplicitTailCall; if (canTailCall && - !impTailCallRetTypeCompatible(info.compRetType, info.compMethodInfo->args.retTypeClass, info.compCallConv, - callRetTyp, sig->retTypeClass, call->AsCall()->GetUnmanagedCallConv())) + !impTailCallRetTypeCompatible(allowWidening, info.compRetType, info.compMethodInfo->args.retTypeClass, + info.compCallConv, callRetTyp, sig->retTypeClass, + call->AsCall()->GetUnmanagedCallConv())) { canTailCall = false; szCanTailCallFailReason = "Return types are not tail call compatible"; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 057c318e81d..1ab0f2a050e 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6872,7 +6872,7 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) if (callee->IsTailPrefixedCall()) { var_types retType = info.compRetType; - assert(impTailCallRetTypeCompatible(retType, info.compMethodInfo->args.retTypeClass, info.compCallConv, + assert(impTailCallRetTypeCompatible(false, retType, info.compMethodInfo->args.retTypeClass, info.compCallConv, (var_types)callee->gtReturnType, callee->gtRetClsHnd, callee->GetUnmanagedCallConv())); } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs index 8941e1c2d59..c5d607d23f2 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54842/Runtime_54842.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System; using System.Runtime.CompilerServices; diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55140/Runtime_55140.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55140/Runtime_55140.cs index 220d40aa877..3c22e543902 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_55140/Runtime_55140.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55140/Runtime_55140.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System; using System.Runtime.CompilerServices; diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.cs b/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.cs new file mode 100644 index 00000000000..bccdf99b8f3 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +class Runtime_55253 +{ + static int Main() + { + int errors = 0; + if (AsInt32() != -1) + errors |= 1; + if (AsUInt32() != 255) + errors |= 2; + + return 100 + errors; + } + + static uint AsUInt32() => AsUInt16(); + static uint AsUInt16() => AsUInt8(); + static uint AsUInt8() => 255; + + static int AsInt32() => AsInt16(); + static short AsInt16() => AsInt8(); + static sbyte AsInt8() => -1; +} \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.csproj new file mode 100644 index 00000000000..94bfaab314c --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.csproj @@ -0,0 +1,22 @@ + + + Exe + + + None + False + + + + + + + + + \ No newline at end of file From 2ad8c9022540f8b44950623f4fb01f95f8ad7354 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 21 Jul 2021 06:12:46 -0400 Subject: [PATCH 717/926] Fix outerloop System.Buffers tests after s_trimBuffers removal (#56068) We deleted this switch. Turns out there were outerloop tests looking for it. --- .../tests/ArrayPool/ArrayPoolTest.cs | 16 +---- .../tests/ArrayPool/CollectionTests.cs | 63 +++++-------------- .../tests/ArrayPool/UnitTests.cs | 1 - 3 files changed, 18 insertions(+), 62 deletions(-) diff --git a/src/libraries/System.Buffers/tests/ArrayPool/ArrayPoolTest.cs b/src/libraries/System.Buffers/tests/ArrayPool/ArrayPoolTest.cs index 01fb4d819f5..754814d41ca 100644 --- a/src/libraries/System.Buffers/tests/ArrayPool/ArrayPoolTest.cs +++ b/src/libraries/System.Buffers/tests/ArrayPool/ArrayPoolTest.cs @@ -10,8 +10,6 @@ namespace System.Buffers.ArrayPool.Tests { public abstract class ArrayPoolTest { - protected const string TrimSwitchName = "DOTNET_SYSTEM_BUFFERS_ARRAYPOOL_TRIMSHARED"; - protected static class EventIds { public const int BufferRented = 1; @@ -35,16 +33,7 @@ namespace System.Buffers.ArrayPool.Tests } } - protected static void RemoteInvokeWithTrimming(Action action, bool trim = false) - { - RemoteInvokeOptions options = new RemoteInvokeOptions(); - options.StartInfo.UseShellExecute = false; - options.StartInfo.EnvironmentVariables.Add(TrimSwitchName, trim.ToString()); - - RemoteExecutor.Invoke(action).Dispose(); - } - - protected static void RemoteInvokeWithTrimming(Action method, bool trim = false, int timeout = RemoteExecutor.FailWaitTimeoutMilliseconds) + protected static void RemoteInvokeWithTrimming(Action method, int timeout = RemoteExecutor.FailWaitTimeoutMilliseconds) { var options = new RemoteInvokeOptions { @@ -52,9 +41,8 @@ namespace System.Buffers.ArrayPool.Tests }; options.StartInfo.UseShellExecute = false; - options.StartInfo.EnvironmentVariables.Add(TrimSwitchName, trim.ToString()); - RemoteExecutor.Invoke(method, trim.ToString(), options).Dispose(); + RemoteExecutor.Invoke(method, options).Dispose(); } } } diff --git a/src/libraries/System.Buffers/tests/ArrayPool/CollectionTests.cs b/src/libraries/System.Buffers/tests/ArrayPool/CollectionTests.cs index d29e1c71416..6b39f5409bd 100644 --- a/src/libraries/System.Buffers/tests/ArrayPool/CollectionTests.cs +++ b/src/libraries/System.Buffers/tests/ArrayPool/CollectionTests.cs @@ -15,22 +15,16 @@ namespace System.Buffers.ArrayPool.Tests public class CollectionTests : ArrayPoolTest { [OuterLoop("This is a long running test (over 2 minutes)")] - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported)), - InlineData(true), - InlineData(false)] - public void BuffersAreCollectedWhenStale(bool trim) + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void BuffersAreCollectedWhenStale() { - RemoteInvokeWithTrimming((trimString) => + RemoteInvokeWithTrimming(() => { - // Check that our environment is as we expect - Assert.Equal(trimString, Environment.GetEnvironmentVariable(TrimSwitchName)); - const int BufferCount = 8; const int BufferSize = 1025; // Get the pool and check our trim setting var pool = ArrayPool.Shared; - bool parsedTrim = ValidateTrimState(pool, trimString); List rentedBuffers = new List(); @@ -71,27 +65,21 @@ namespace System.Buffers.ArrayPool.Tests } // Should only have found a new buffer if we're trimming - Assert.Equal(parsedTrim, foundNewBuffer); - }, trim, 3 * 60 * 1000); // This test has to wait for the buffers to go stale (give it three minutes) + Assert.True(foundNewBuffer); + }, 3 * 60 * 1000); // This test has to wait for the buffers to go stale (give it three minutes) } private static bool IsStressModeEnabledAndRemoteExecutorSupported => TestEnvironment.IsStressModeEnabled && RemoteExecutor.IsSupported; // This test can cause problems for other tests run in parallel (from other assemblies) as // it pushes the physical memory usage above 80% temporarily. - [ConditionalTheory(nameof(IsStressModeEnabledAndRemoteExecutorSupported)), - InlineData(true), - InlineData(false)] - public unsafe void ThreadLocalIsCollectedUnderHighPressure(bool trim) + [ConditionalFact(nameof(IsStressModeEnabledAndRemoteExecutorSupported))] + public unsafe void ThreadLocalIsCollectedUnderHighPressure() { - RemoteInvokeWithTrimming((trimString) => + RemoteInvokeWithTrimming(() => { - // Check that our environment is as we expect - Assert.Equal(trimString, Environment.GetEnvironmentVariable(TrimSwitchName)); - // Get the pool and check our trim setting var pool = ArrayPool.Shared; - bool parsedTrim = ValidateTrimState(pool, trimString); // Create our buffer, return it, re-rent it and ensure we have the same one const int BufferSize = 4097; @@ -119,40 +107,21 @@ namespace System.Buffers.ArrayPool.Tests } while ((int)pressureMethod.Invoke(null, null) != 2); GC.WaitForPendingFinalizers(); - if (parsedTrim) - { - // Should have a new buffer now - Assert.NotSame(buffer, pool.Rent(BufferSize)); - } - else - { - // Disabled, should not have trimmed buffer - Assert.Same(buffer, pool.Rent(BufferSize)); - } - }, trim); - } - private static bool ValidateTrimState(object pool, string trimString) - { - Assert.StartsWith("TlsOverPerCoreLockedStacksArrayPool", pool.GetType().Name); - bool parsedTrim = bool.Parse(trimString); - var trimField = pool.GetType().GetField("s_trimBuffers", BindingFlags.Static | BindingFlags.NonPublic); - Assert.Equal(parsedTrim, (bool)trimField.GetValue(null)); - return parsedTrim; + // Should have a new buffer now + Assert.NotSame(buffer, pool.Rent(BufferSize)); + }); } private static bool IsPreciseGcSupportedAndRemoteExecutorSupported => PlatformDetection.IsPreciseGcSupported && RemoteExecutor.IsSupported; [ActiveIssue("https://github.com/dotnet/runtime/issues/44037")] - [ConditionalTheory(nameof(IsPreciseGcSupportedAndRemoteExecutorSupported))] - [InlineData(true)] - [InlineData(false)] - public void PollingEventFires(bool trim) + [ConditionalFact(nameof(IsPreciseGcSupportedAndRemoteExecutorSupported))] + public void PollingEventFires() { - RemoteInvokeWithTrimming((trimString) => + RemoteInvokeWithTrimming(() => { var pool = ArrayPool.Shared; - bool parsedTrim = ValidateTrimState(pool, trimString); bool pollEventFired = false; var buffer = pool.Rent(10); @@ -187,8 +156,8 @@ namespace System.Buffers.ArrayPool.Tests }); // Polling events should only fire when trimming is enabled - Assert.Equal(parsedTrim, pollEventFired); - }, trim); + Assert.True(pollEventFired); + }); } } } diff --git a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs index 5673d22d363..84246f7d170 100644 --- a/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs +++ b/src/libraries/System.Buffers/tests/ArrayPool/UnitTests.cs @@ -355,7 +355,6 @@ namespace System.Buffers.ArrayPool.Tests { var options = new RemoteInvokeOptions(); options.StartInfo.UseShellExecute = false; - options.StartInfo.EnvironmentVariables.Add(TrimSwitchName, "false"); RemoteExecutor.Invoke((lengthStr, expectPooledStr) => { From 913facdca8b04cc674163e31a7650ef6868a7d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 21 Jul 2021 07:26:17 -0400 Subject: [PATCH 718/926] [aot] Don't direct pinvoke objc_msgSend family on Apple (#55845) --- src/mono/mono/mini/aot-compiler.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 1574acda348..d691444ab20 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -6237,6 +6237,18 @@ get_file_index (MonoAotCompile *acfg, const char *source_file) #define INST_LEN 1 #endif +static gboolean +never_direct_pinvoke (const char *pinvoke_symbol) +{ +#if defined(TARGET_IOS) || defined (TARGET_TVOS) || defined (TARGET_OSX) || defined (TARGET_WATCHOS) + /* XI must be able to override the functions that start with objc_msgSend. + * (there are a few variants with the same prefix) */ + return !strncmp (pinvoke_symbol, "objc_msgSend", 12); +#else + return FALSE; +#endif +} + /* * emit_and_reloc_code: * @@ -6388,7 +6400,7 @@ emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, gui direct_pinvoke = lookup_icall_symbol_name_aot (patch_info->data.method); else direct_pinvoke = get_pinvoke_import (acfg, patch_info->data.method); - if (direct_pinvoke) { + if (direct_pinvoke && !never_direct_pinvoke (direct_pinvoke)) { direct_call = TRUE; g_assert (strlen (direct_pinvoke) < 1000); direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, direct_pinvoke); From b823f14ab759b4cc32c7315b1c278c226dc2af20 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:15:08 +0200 Subject: [PATCH 719/926] [main] Update dependencies from 8 repositories (#55883) * Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-optimization build 20210717.5 optimization.linux-x64.MIBC.Runtime , optimization.windows_nt-x64.MIBC.Runtime , optimization.windows_nt-x86.MIBC.Runtime , optimization.PGO.CoreCLR From Version 1.0.0-prerelease.21365.5 -> To Version 1.0.0-prerelease.21367.5 * Update dependencies from https://github.com/dotnet/runtime build 20210719.2 Microsoft.NETCore.DotNetHost , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , Microsoft.NET.Sdk.IL , System.Runtime.CompilerServices.Unsafe , System.Text.Json From Version 6.0.0-preview.7.21361.10 -> To Version 6.0.0-rc.1.21369.2 * Update dependencies from https://github.com/dotnet/emsdk build 20210719.1 Microsoft.NET.Workload.Emscripten.Manifest-6.0.100 From Version 6.0.0-rc.1.21365.1 -> To Version 6.0.0-rc.1.21369.1 * Update dependencies from https://github.com/dotnet/arcade build 20210719.3 Microsoft.DotNet.XUnitExtensions , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.ApiCompat , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.GenFacades , Microsoft.DotNet.GenAPI , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.SharedFramework.Sdk From Version 6.0.0-beta.21366.1 -> To Version 6.0.0-beta.21369.3 * Update dependencies from https://github.com/dotnet/icu build 20210719.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 6.0.0-rc.1.21366.2 -> To Version 6.0.0-rc.1.21369.1 * Update dependencies from https://github.com/dotnet/llvm-project build 20210719.1 runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools , runtime.osx.10.12-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk , runtime.linux-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 From Version 11.1.0-alpha.1.21362.1 -> To Version 11.1.0-alpha.1.21369.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20210719.1 System.ComponentModel.TypeConverter.TestData , System.Drawing.Common.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.Windows.Extensions.TestData From Version 6.0.0-beta.21358.1 -> To Version 6.0.0-beta.21369.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20210719.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 1.0.1-alpha.0.21364.1 -> To Version 1.0.1-alpha.0.21369.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 196 ++++++++++++++++++++-------------------- eng/Versions.props | 88 +++++++++--------- global.json | 10 +- 3 files changed, 147 insertions(+), 147 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 54167315e8b..4ba2cd28427 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,186 +1,186 @@ - + https://github.com/dotnet/icu - fdb93aa21dfdab465e94464c585b3de56595b81b + 08293141bc33a81b7e58120535079d8eac36519f https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b - + https://github.com/dotnet/emsdk - 949c505e3dac05180b954b79a3fcc19009687182 + 70549df51ec4b2561e2d11caa542e0b2a2903e0a - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 - + https://github.com/dotnet/llvm-project - 97d42b6f43e31449758c353cfbaf83322941611b + 0b7a170df4f963b7b91e368f5d14a0be9e984c82 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/dotnet/runtime - 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c + f7e4c261815c66fde2c1e750b744f193e236c17e https://github.com/mono/linker @@ -194,33 +194,33 @@ https://github.com/dotnet/xharness e5511489deb44b13418fe622775bd7035e21c5ae - + https://github.com/dotnet/arcade - b03966cd85285e425ffe56003c0ab57e103dd14e + e97027cf100d2b532adce387e5cb93a373de93c9 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1657e026853add6028f94ef56c0f9a407a6dbe4c + d77585ed642412eb5e2c36bec4c626aef387060e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1657e026853add6028f94ef56c0f9a407a6dbe4c + d77585ed642412eb5e2c36bec4c626aef387060e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1657e026853add6028f94ef56c0f9a407a6dbe4c + d77585ed642412eb5e2c36bec4c626aef387060e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1657e026853add6028f94ef56c0f9a407a6dbe4c + d77585ed642412eb5e2c36bec4c626aef387060e - + https://github.com/dotnet/hotreload-utils - 247ef3e12ea5ecde3c1d7ce7f8c2b0eccd6ebd49 + 589840a79ed6335a310a011ee9e244e660798cf5 - + https://github.com/dotnet/runtime-assets - 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da + 8693e5f9d69ddf21543072fca8e4314b9f5e0682 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 3cac4122b1f..b9df86f4f7e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -52,28 +52,28 @@ 3.10.0 6.0.0-rc1.21366.2 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 2.5.1-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 - 6.0.0-beta.21366.1 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 2.5.1-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 6.0.0-preview.1.102 6.0.0-alpha.1.20612.4 - 6.0.0-preview.7.21361.10 - 6.0.0-preview.7.21361.10 + 6.0.0-rc.1.21369.2 + 6.0.0-rc.1.21369.2 3.1.0 - 6.0.0-preview.7.21361.10 + 6.0.0-rc.1.21369.2 5.0.0 4.3.0 @@ -107,27 +107,27 @@ 5.0.0 5.0.0 4.8.1 - 6.0.0-preview.7.21361.10 - 6.0.0-preview.7.21361.10 + 6.0.0-rc.1.21369.2 + 6.0.0-rc.1.21369.2 4.5.4 4.5.0 - 6.0.0-preview.7.21361.10 + 6.0.0-rc.1.21369.2 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 - 6.0.0-beta.21358.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 + 6.0.0-beta.21369.1 - 1.0.0-prerelease.21365.5 - 1.0.0-prerelease.21365.5 - 1.0.0-prerelease.21365.5 - 1.0.0-prerelease.21365.5 + 1.0.0-prerelease.21367.5 + 1.0.0-prerelease.21367.5 + 1.0.0-prerelease.21367.5 + 1.0.0-prerelease.21367.5 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -153,7 +153,7 @@ 16.9.0-preview-20201201-01 1.0.0-prerelease.21364.1 1.0.0-prerelease.21364.1 - 1.0.1-alpha.0.21364.1 + 1.0.1-alpha.0.21369.1 2.4.1 2.4.2 1.3.0 @@ -168,20 +168,20 @@ 6.0.100-preview.6.21369.3 $(MicrosoftNETILLinkTasksVersion) - 6.0.0-rc.1.21366.2 + 6.0.0-rc.1.21369.1 6.0.0-preview.7.21357.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 - 11.1.0-alpha.1.21362.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 + 11.1.0-alpha.1.21369.1 - 6.0.0-rc.1.21365.1 + 6.0.0-rc.1.21369.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) diff --git a/global.json b/global.json index 230a339dfe7..863e9ef68eb 100644 --- a/global.json +++ b/global.json @@ -12,13 +12,13 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21366.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21369.3", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21366.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21366.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21366.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21369.3", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21369.3", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21369.3", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10" + "Microsoft.NET.Sdk.IL": "6.0.0-rc.1.21369.2" } } From a2f2a629328a136c2cd18f2cd729e3854de2aeb4 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 21 Jul 2021 09:51:16 -0500 Subject: [PATCH 720/926] Resolve ILLink warnings in System.Linq.Expressions (Final) (#55856) * Resolve ILLink warnings in System.Linq.Expressions (Final) Suppress ILLink warnings for operator methods now that https://github.com/mono/linker/issues/1821 is resolved. Add TrimmingTests for Linq.Expressions operators. Fix #45623 --- .../ref/System.Linq.Expressions.cs | 5 - .../src/ILLink/ILLink.Suppressions.xml | 17 --- .../System/Dynamic/Utils/TypeExtensions.cs | 6 +- .../src/System/Dynamic/Utils/TypeUtils.cs | 7 +- .../Linq/Expressions/BinaryExpression.cs | 2 + .../Linq/Expressions/UnaryExpression.cs | 11 +- .../TrimmingTests/BinaryOperatorTests.cs | 65 ++++++++++ .../TrimmingTests/ConvertOperatorTests.cs | 91 ++++++++++++++ .../tests/TrimmingTests/EqualOperatorTests.cs | 115 ++++++++++++++++++ ...System.Linq.Expressions.TrimmingTests.proj | 8 ++ 10 files changed, 294 insertions(+), 33 deletions(-) delete mode 100644 src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs diff --git a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs index b9f19db7f34..862a87c1583 100644 --- a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs +++ b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs @@ -451,11 +451,8 @@ namespace System.Linq.Expressions public static System.Linq.Expressions.GotoExpression Continue(System.Linq.Expressions.LabelTarget target) { throw null; } public static System.Linq.Expressions.GotoExpression Continue(System.Linq.Expressions.LabelTarget target, System.Type type) { throw null; } public static System.Linq.Expressions.UnaryExpression Convert(System.Linq.Expressions.Expression expression, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression Convert(System.Linq.Expressions.Expression expression, System.Type type, System.Reflection.MethodInfo? method) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression ConvertChecked(System.Linq.Expressions.Expression expression, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression ConvertChecked(System.Linq.Expressions.Expression expression, System.Type type, System.Reflection.MethodInfo? method) { throw null; } public static System.Linq.Expressions.DebugInfoExpression DebugInfo(System.Linq.Expressions.SymbolDocumentInfo document, int startLine, int startColumn, int endLine, int endColumn) { throw null; } public static System.Linq.Expressions.UnaryExpression Decrement(System.Linq.Expressions.Expression expression) { throw null; } @@ -573,9 +570,7 @@ namespace System.Linq.Expressions public static System.Linq.Expressions.IndexExpression MakeIndex(System.Linq.Expressions.Expression instance, System.Reflection.PropertyInfo? indexer, System.Collections.Generic.IEnumerable? arguments) { throw null; } public static System.Linq.Expressions.MemberExpression MakeMemberAccess(System.Linq.Expressions.Expression? expression, System.Reflection.MemberInfo member) { throw null; } public static System.Linq.Expressions.TryExpression MakeTry(System.Type? type, System.Linq.Expressions.Expression body, System.Linq.Expressions.Expression? @finally, System.Linq.Expressions.Expression? fault, System.Collections.Generic.IEnumerable? handlers) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression MakeUnary(System.Linq.Expressions.ExpressionType unaryType, System.Linq.Expressions.Expression operand, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression MakeUnary(System.Linq.Expressions.ExpressionType unaryType, System.Linq.Expressions.Expression operand, System.Type type, System.Reflection.MethodInfo? method) { throw null; } public static System.Linq.Expressions.MemberMemberBinding MemberBind(System.Reflection.MemberInfo member, System.Collections.Generic.IEnumerable bindings) { throw null; } public static System.Linq.Expressions.MemberMemberBinding MemberBind(System.Reflection.MemberInfo member, params System.Linq.Expressions.MemberBinding[] bindings) { throw null; } diff --git a/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index f58a4f028b1..00000000000 --- a/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression,System.Type) - - - ILLink - IL2070 - member - M:System.Dynamic.Utils.TypeExtensions.GetAnyStaticMethodValidated(System.Type,System.String,System.Type[]) - - - diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs index 5d5390a8743..4b57dba0172 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.Dynamic.Utils @@ -15,7 +16,10 @@ namespace System.Dynamic.Utils /// Returns the matching method if the parameter types are reference /// assignable from the provided type arguments, otherwise null. /// - public static MethodInfo? GetAnyStaticMethodValidated(this Type type, string name, Type[] types) + public static MethodInfo? GetAnyStaticMethodValidated( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type, + string name, + Type[] types) { Debug.Assert(types != null); MethodInfo? method = type.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly, null, types, null); diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs index 3a0fc56030a..f676a13dce7 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs @@ -623,7 +623,8 @@ namespace System.Dynamic.Utils || IsImplicitBoxingConversion(source, destination) || IsImplicitNullableConversion(source, destination); - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] public static MethodInfo? GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType) { Type nnExprType = GetNonNullableType(convertFrom); @@ -829,8 +830,12 @@ namespace System.Dynamic.Utils /// op_False, because we have to do runtime lookup for those. It may /// not work right for unary operators in general. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] public static MethodInfo? GetBooleanOperator(Type type, string name) { + Debug.Assert(name == "op_False" || name == "op_True"); + do { MethodInfo? result = type.GetAnyStaticMethodValidated(name, new[] { type }); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs index bccac308a01..f0581c4e0d0 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs @@ -710,6 +710,8 @@ namespace System.Linq.Expressions return b; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] private static MethodInfo? GetUserDefinedBinaryOperator(ExpressionType binaryType, Type leftType, Type rightType, string name) { // This algorithm is wrong, we should be checking for uniqueness and erroring if diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs index 99fb5c4ba26..eea96673e50 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs @@ -279,8 +279,6 @@ namespace System.Linq.Expressions /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "A UnaryExpression has already been created. The original creator will get a warning that it is not trim compatible.")] public UnaryExpression Update(Expression operand) { if (operand == Operand) @@ -302,7 +300,6 @@ namespace System.Linq.Expressions /// The that results from calling the appropriate factory method. /// Thrown when does not correspond to a unary expression. /// Thrown when is null. - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type) { return MakeUnary(unaryType, operand, type, method: null); @@ -318,7 +315,6 @@ namespace System.Linq.Expressions /// The that results from calling the appropriate factory method. /// Thrown when does not correspond to a unary expression. /// Thrown when is null. - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type, MethodInfo? method) => unaryType switch { @@ -356,6 +352,8 @@ namespace System.Linq.Expressions throw Error.UnaryOperatorNotDefined(unaryType, operand.Type); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] private static UnaryExpression? GetUserDefinedUnaryOperator(ExpressionType unaryType, string name, Expression operand) { Type operandType = operand.Type; @@ -402,7 +400,6 @@ namespace System.Linq.Expressions throw Error.OperandTypesDoNotMatchParameters(unaryType, method.Name); } - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] private static UnaryExpression GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType) { UnaryExpression? u = GetUserDefinedCoercion(coercionType, expression, convertToType); @@ -413,7 +410,6 @@ namespace System.Linq.Expressions throw Error.CoercionOperatorNotDefined(expression.Type, convertToType); } - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] private static UnaryExpression? GetUserDefinedCoercion(ExpressionType coercionType, Expression expression, Type convertToType) { MethodInfo? method = TypeUtils.GetUserDefinedCoercionMethod(expression.Type, convertToType); @@ -746,7 +742,6 @@ namespace System.Linq.Expressions /// is not null and the method it represents returns void, is not static (Shared in Visual Basic), or does not take exactly one argument. /// More than one method that matches the description was found. /// No conversion operator is defined between .Type and .-or-.Type is not assignable to the argument type of the method represented by .-or-The return type of the method represented by is not assignable to .-or-.Type or is a nullable value type and the corresponding non-nullable value type does not equal the argument type or the return type, respectively, of the method represented by . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression Convert(Expression expression, Type type, MethodInfo? method) { ExpressionUtils.RequiresCanRead(expression, nameof(expression)); @@ -771,7 +766,6 @@ namespace System.Linq.Expressions /// /// or is null. /// No conversion operator is defined between .Type and . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression ConvertChecked(Expression expression, Type type) { return ConvertChecked(expression, type, method: null); @@ -788,7 +782,6 @@ namespace System.Linq.Expressions /// is not null and the method it represents returns void, is not static (Shared in Visual Basic), or does not take exactly one argument. /// More than one method that matches the description was found. /// No conversion operator is defined between .Type and .-or-.Type is not assignable to the argument type of the method represented by .-or-The return type of the method represented by is not assignable to .-or-.Type or is a nullable value type and the corresponding non-nullable value type does not equal the argument type or the return type, respectively, of the method represented by . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression ConvertChecked(Expression expression, Type type, MethodInfo? method) { ExpressionUtils.RequiresCanRead(expression, nameof(expression)); diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs new file mode 100644 index 00000000000..5598b2d92af --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq.Expressions; + +/// +/// Tests that Expression.Add expressions still work correctly and find +/// the + operator in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + ParameterExpression leftParameter = Expression.Parameter(typeof(Class1)); + ParameterExpression rightParameter = Expression.Parameter(typeof(Class1)); + ParameterExpression result = Expression.Variable(typeof(Class1)); + + Func func = + Expression.Lambda>( + Expression.Block( + new[] { result }, + Expression.Assign(result, Expression.Add(leftParameter, rightParameter)), + result), + leftParameter, rightParameter) + .Compile(); + + Class1 actual = func(new Class1("left"), new Class1("right")); + if (actual.Name != "left+right") + { + return -1; + } + + // make sure Class2 was trimmed since it wasn't used, even though Class1 has a binary operator using it + int i = 2; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -2; + } + + return 100; + } +} + +internal class Class1 +{ + public Class1(string name) => Name = name; + + public string Name { get; set; } + + public static Class1 operator +(Class1 left, Class1 right) => + new Class1($"{left.Name}+{right.Name}"); + + public static Class1 operator +(Class1 left, Class2 right) => + new Class1($"{left.Name}+{right.Name}2"); + public static Class2 operator +(Class2 left, Class1 right) => + new Class2($"{left.Name}2+{right.Name}"); +} + +internal class Class2 +{ + public Class2(string name) => Name = name; + + public string Name { get; set; } +} diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs new file mode 100644 index 00000000000..de29045cb60 --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq.Expressions; +using System.Reflection; + +/// +/// Tests that Expression.Convert expressions still work correctly and find +/// implicit and explicit operators in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + Type[] convertTypes = new Type[] { typeof(Class2), typeof(Class3) }; + + ParameterExpression class1Parameter = Expression.Parameter(typeof(Class1), "class1"); + MethodInfo getNameMethodInfo = typeof(Program).GetMethod("GetName"); + foreach (Type convertType in convertTypes) + { + UnaryExpression conversion = Expression.Convert(class1Parameter, convertType); + + Func getNameFunc = + Expression.Lambda>( + Expression.Call(null, getNameMethodInfo, conversion), + class1Parameter) + .Compile(); + + string name = getNameFunc(new Class1() { Name = convertType.Name }); + if (convertType.Name == "Class2") + { + if (name != "Class2_implicit") + { + return -1; + } + } + else if (convertType.Name == "Class3") + { + if (name != "Class3_explicit") + { + return -2; + } + } + else + { + return -3; + } + } + + // make sure Class4 was trimmed since it wasn't used, even though Class1 has a conversion operator to it + int i = 4; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -4; + } + + return 100; + } + + public static string GetName(IHasName hasName) => hasName.Name; +} + +interface IHasName +{ + string Name { get; } +} + +internal class Class1 : IHasName +{ + public string Name { get; set; } + + public static implicit operator Class2(Class1 class1) => new Class2() { Name = class1.Name + "_implicit" }; + public static explicit operator Class3(Class1 class1) => new Class3() { Name = class1.Name + "_explicit" }; + public static implicit operator Class4(Class1 class1) => new Class4() { Name = class1.Name + "_implicit" }; +} + +internal class Class2 : IHasName +{ + public string Name { get; set; } +} + +internal class Class3 : IHasName +{ + public string Name { get; set; } +} + +internal class Class4 : IHasName +{ + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs new file mode 100644 index 00000000000..41a9d4a47e2 --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +/// +/// Tests that Expression.Equal/NotEqual expressions still work correctly and find +/// equality operators in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + List<(object left, object right, ExpressionType expressionType, bool expected)> testData = new() + { + (new Class1("left"), new Class1("right"), ExpressionType.Equal, true), + (new Class1("left"), new Class1("notright"), ExpressionType.Equal, false), + (new Class1("left"), new Class1("right"), ExpressionType.NotEqual, false), + (new Class1("left"), new Class1("notright"), ExpressionType.NotEqual, true), + + (new Class1("left"), new Class2("right"), ExpressionType.Equal, true), + (new Class1("left"), new Class2("notright"), ExpressionType.Equal, false), + (new Class1("left"), new Class2("right"), ExpressionType.NotEqual, false), + (new Class1("left"), new Class2("notright"), ExpressionType.NotEqual, true), + }; + + foreach ((object left, object right, ExpressionType expressionType, bool expected) in testData) + { + ParameterExpression leftParameter = Expression.Parameter(typeof(object)); + ParameterExpression rightParameter = Expression.Parameter(typeof(object)); + + Expression leftConverted = Expression.Convert(leftParameter, left.GetType()); + Expression rightConverted = Expression.Convert(rightParameter, right.GetType()); + Expression condition; + if (expressionType == ExpressionType.Equal) + { + condition = Expression.Equal(leftConverted, rightConverted); + } + else + { + condition = Expression.NotEqual(leftConverted, rightConverted); + } + + ParameterExpression result = Expression.Variable(typeof(bool)); + + Func func = + Expression.Lambda>( + Expression.Block( + new[] { result }, + Expression.IfThenElse( + condition, + Expression.Assign(result, Expression.Constant(true)), + Expression.Assign(result, Expression.Constant(false))), + result), + leftParameter, rightParameter) + .Compile(); + + bool actual = func(left, right); + if (actual != expected) + { + return -1; + } + } + + // make sure Class3 was trimmed since it wasn't used, even though Class1 has equality operators to it + int i = 3; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -2; + } + + return 100; + } +} + +#pragma warning disable CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() +internal class Class1 +#pragma warning restore CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() +#pragma warning restore CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) +{ + public Class1(string name) => Name = name; + + public string Name { get; set; } + + // use very unique rules to ensure these operators get invoked + public static bool operator ==(Class1 left, Class1 right) => + left.Name == "left" && right.Name == "right"; + public static bool operator !=(Class1 left, Class1 right) => + !(left.Name == "left" && right.Name == "right"); + + public static bool operator ==(Class1 left, Class2 right) => + left.Name == "left" && right.Name == "right"; + public static bool operator !=(Class1 left, Class2 right) => + !(left.Name == "left" && right.Name == "right"); + + public static bool operator ==(Class1 left, Class3 right) => left.Name == right.Name; + public static bool operator !=(Class1 left, Class3 right) => left.Name == right.Name; +} + +internal class Class2 +{ + public Class2(string name) => Name = name; + + public string Name { get; set; } +} + +internal class Class3 +{ + public Class3(string name) => Name = name; + + public string Name { get; set; } +} diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj index da4a46f2ae1..a05afbc2b4c 100644 --- a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj @@ -1,5 +1,13 @@ + + + + + + + + From 1d8ad03abb72fe79c44030f2806a0109a7909d25 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 21 Jul 2021 11:47:18 -0400 Subject: [PATCH 721/926] [wasm] Fix regression in compiling .bc -> .o files (#56063) * [wasm] Add back --emit-llvm that got removed mistakenly, in an earlier commit .. found thanks to Jerome Laban. * [wasm] Set EmccCompile's messages to MessageImportance.Low by default. .. and to MessageImportance.Normal if `$(EmccVerbose)==true`. * [wasm] Quote filenames passed to emcc compile command line * Add more blazorwasm tests - for debug/release, aot/relinking * Bump sdk for workload testing to 6.0.100-rc.1.21370.2 * [wasm] Fix regression in compiling bitcode -> .o The `-emit-llvm` arg has been incorrectly added, and removed from the args used for compiling .bc->.o . This commit fixes it, and adds a crude test for it, so we don't regress again. * Fix build --- eng/Versions.props | 2 +- src/mono/wasm/build/WasmApp.Native.targets | 14 ++++++-- src/tasks/WasmAppBuilder/EmccCompile.cs | 11 ++++-- .../Wasm.Build.Tests/BlazorWasmTests.cs | 18 +++++++--- .../Wasm.Build.Tests/BuildTestBase.cs | 3 -- .../Wasm.Build.Tests/NativeBuildTests.cs | 34 ++++++++++++++++++- .../SharedBuildPerTestClassFixture.cs | 2 +- .../Wasm.Build.Tests/ToolCommand.cs | 2 ++ 8 files changed, 70 insertions(+), 16 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index b9df86f4f7e..88efb3204e5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -161,7 +161,7 @@ 2.0.4 4.12.0 2.14.3 - 6.0.100-preview.7.21362.5 + 6.0.100-rc.1.21370.2 5.0.0-preview-20201009.2 diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 48bafb1d428..85d301885db 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -158,6 +158,8 @@ $(EmccCompileOptimizationFlag) <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp + <_EmccCompileOutputMessageImportance Condition="'$(EmccVerbose)' == 'true'">Normal + <_EmccCompileOutputMessageImportance Condition="'$(EmccVerbose)' != 'true'">Low @@ -181,6 +183,7 @@ <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DCORE_BINDINGS" /> <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + <_EmccCFlags Include="-emit-llvm" /> <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> @@ -240,7 +243,11 @@ - + @@ -269,8 +276,9 @@ + Arguments=""@$(_EmccDefaultFlagsRsp)" @(_EmccLDFlags, ' ')" + EnvironmentVariables="@(EmscriptenEnvVars)" + OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" /> diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index 89bea79454a..eed8bb52690 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -33,6 +33,7 @@ namespace Microsoft.WebAssembly.Build.Tasks public bool DisableParallelCompile { get; set; } public string Arguments { get; set; } = string.Empty; public string? WorkingDirectory { get; set; } + public string OutputMessageImportance{ get; set; } = "Low"; [Output] public ITaskItem[]? OutputFiles { get; private set; } @@ -54,6 +55,12 @@ namespace Microsoft.WebAssembly.Build.Tasks return false; } + if (!Enum.TryParse(OutputMessageImportance, ignoreCase: true, out MessageImportance messageImportance)) + { + Log.LogError($"Invalid value for OutputMessageImportance={OutputMessageImportance}. Valid values: {string.Join(", ", Enum.GetNames(typeof(MessageImportance)))}"); + return false; + } + IDictionary envVarsDict = GetEnvironmentVariablesDict(); ConcurrentBag outputItems = new(); try @@ -112,7 +119,7 @@ namespace Microsoft.WebAssembly.Build.Tasks try { - string command = $"emcc {Arguments} -c -o {objFile} {srcFile}"; + string command = $"emcc {Arguments} -c -o \"{objFile}\" \"{srcFile}\""; // Log the command in a compact format which can be copy pasted StringBuilder envStr = new StringBuilder(string.Empty); @@ -125,7 +132,7 @@ namespace Microsoft.WebAssembly.Build.Tasks envVarsDict, workingDir: Environment.CurrentDirectory, logStdErrAsMessage: true, - debugMessageImportance: MessageImportance.High, + debugMessageImportance: messageImportance, label: Path.GetFileName(srcFile)); if (exitCode != 0) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs index dacf67ff4f3..002ca248031 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs @@ -16,10 +16,16 @@ namespace Wasm.Build.Tests { } - [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] - public void PublishTemplateProject() + // TODO: invariant case? + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + [InlineData("Debug", false)] + [InlineData("Debug", true)] // just aot + [InlineData("Release", false)] // should re-link + [InlineData("Release", true)] + public void PublishTemplateProject(string config, bool aot) { - string id = "blazorwasm"; + string id = $"blazorwasm_{config}_aot_{aot}"; InitPaths(id); if (Directory.Exists(_projectDir)) Directory.Delete(_projectDir, recursive: true); @@ -37,14 +43,16 @@ namespace Wasm.Build.Tests .ExecuteWithCapturedOutput("new blazorwasm") .EnsureSuccessful(); - string publishLogPath = Path.Combine(logPath, $"{id}.publish.binlog"); + string publishLogPath = Path.Combine(logPath, $"{id}.binlog"); new DotNetCommand(s_buildEnv) .WithWorkingDirectory(_projectDir) - .ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", "-p:RunAOTCompilation=true") + .ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", aot ? "-p:RunAOTCompilation=true" : "", $"-p:Configuration={config}") .EnsureSuccessful(); //TODO: validate the build somehow? // compare dotnet.wasm? + // relinking - dotnet.wasm should be smaller + // // playwright? } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 608aee17817..4dd5144729c 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -332,10 +332,7 @@ namespace Wasm.Build.Tests } if (useCache) - { _buildContext.CacheBuild(buildArgs, new BuildProduct(_projectDir, logFilePath, true)); - Console.WriteLine($"caching build for {buildArgs}"); - } return (_projectDir, result.buildOutput); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 989b90676ec..80c7ba5fe85 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.IO; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -43,5 +43,37 @@ namespace Wasm.Build.Tests test: output => {}, host: host, id: id); } + + [Theory] + [BuildAndRun(host: RunHost.None, aot: true)] + public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, string id) + { + string printFileTypeTarget = @" + + + + + + + + + "; + string projectName = $"bc_to_o_{buildArgs.Config}"; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, insertAtEnd: printFileTypeTarget); + + (_, string output) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + dotnetWasmFromRuntimePack: false, + id: id); + + if (!output.Contains("wasm-dis exit code: 0")) + throw new XunitException($"Expected to successfully run wasm-dis on System.Private.CoreLib.dll.o ." + + " It might fail if it was incorrectly compiled to a bitcode file, instead of wasm."); + } } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index 2c43614ea41..e84a151bd5b 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -44,7 +44,7 @@ namespace Wasm.Build.Tests { try { - Directory.Delete(path, recursive: true); + Directory.Delete(path, recursive: true); } catch (Exception ex) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs index b45fefac9ac..9d39dcce04a 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs @@ -88,6 +88,7 @@ namespace Wasm.Build.Tests return; output.Add($"[{_label}] {e.Data}"); + Console.WriteLine($"[{_label}] {e.Data}"); ErrorDataReceived?.Invoke(s, e); }; @@ -97,6 +98,7 @@ namespace Wasm.Build.Tests return; output.Add($"[{_label}] {e.Data}"); + Console.WriteLine($"[{_label}] {e.Data}"); OutputDataReceived?.Invoke(s, e); }; From 46d9b317a7829489ca89fc34f0e7c17a7bdb850b Mon Sep 17 00:00:00 2001 From: Mitchell Hwang <16830051+mdh1418@users.noreply.github.com> Date: Wed, 21 Jul 2021 12:39:13 -0400 Subject: [PATCH 722/926] [Android][libraries] TimeZoneInfo Android imp (#54845) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #41867 Android has removed zone.tab, so TimeZoneInfo.Unix.cs will no longer work properly on Android as GetTimeZoneIds directly depends on the zone.tab file. The chain of dependency is as follows GetSystemTimeZones -> PopulateAllSystemTimeZones -> GetTimeZoneIds -> zone.tab TZI.cs TZI.Unix.cs TZI.Unix.cs TZI.Unix.cs Where TZI is TimeZoneInfo zone.tab is a file that is found on the unix system under /usr/share/zoneinfo/ GetTimeZoneIds reads zone.tab to obtain the TimeZoneId in that file PopulateAllSystemTimeZones caches all the TimeZone Ids in cachedData GetSystemTimeZones returns a ReadOnlyCollection containing all valid TimeZone’s from the local machine, and the entries are sorted by their DisplayName. It relies on cachedData._systemTimeZones being populated. The problem is that the time zone data for Android can be found in the file tzdata at the possible locations /apex/com.android.tzdata/etc/tz/tzdata /apex/com.android.runtime/etc/tz/tzdata /data/misc/zoneinfo/tzdata /system/usr/share/zoneinfo/tzdata The rest of unix the time zone data can be found in the file zone.tab at /usr/share/zoneinfo/zone.tab Android's TimeZoneInfo implementation should read time zone data from its locations instead of the general /usr/share/zoneinfo/zone.tab path. Moreover, tzdata contains all timezones byte data. This PR achieves the following: 1. Splits TimeZoneInfo.Unix.cs into TimeZoneInfo.Unix.cs, TimeZoneInfo.Unix.NonAndroid.cs (non-Android), and TimeZoneInfo.Unix.Android.cs (Android specific) 2. Adds an interop to obtain the default time zone on Android based on persist.sys.timezone 3. Implements GetLocalTimeZoneCore TryGetTimeZoneFromLocalMachineCore and GetTimeZoneIds for Android based on mono/mono implementation https://github.com/mono/mono/blob/main/mcs/class/corlib/System/TimeZoneInfo.Android.cs 4. Adds new string resources to throw exceptions 5. Refactors the mono/mono implementation of parsing tzdata Android tzdata files are found in the format of Header Entry Entry Entry ... Entry https://github.com/aosp-mirror/platform_bionic/blob/master/libc/tzcode/bionic.cpp The header (24 bytes) contains the following information signature - 12 bytes of the form "tzdata2012f\0" where 2012f is subject to change index offset - 4 bytes that denotes the offset at which the index of the tzdata file starts data offset - 4 bytes that denotes the offset at which the data of the tzdata file starts final offset - 4 bytes that used to denote the final offset, which we don't use but will note. Each Data Entry (52 bytes) can be used to generate a TimeZoneInfo and contain the following information id - 40 bytes that contain the id of the time zone data entry timezone byte offset - 4 bytes that denote the offset from the data offset timezone data can be found length - 4 bytes that denote the length of the data for timezone unused - 4 bytes that used to be raw GMT offset, but now is always 0 since tzdata2014f (L). When GetLocalTimeZoneCore TryGetTimeZoneFromLocalMachineCore or GetTimeZoneIds are called, an android timezone data instance is instantiated and loaded by attempting to load a tzdata file that can be found at four locations mentioned earlier. The file is parsed by first loading the header which contains information about where the data index and data begin. The data index is then parsed to obtain the timezone and the corresponding bytes location in the file to fill the three arrays _ids _byteOffsets _lengths. These arrays are referenced to obtain the corresponding byte data for a timezone, and functions from TimeZoneInfo.Unix.cs are leveraged to create a TimeZoneInfo from there. --- .../Interop.GetDefaultTimeZone.Android.cs | 14 + .../Native/Unix/System.Native/pal_datetime.c | 29 +- .../Native/Unix/System.Native/pal_datetime.h | 5 + .../src/Resources/Strings.resx | 12 + .../System.Private.CoreLib.Shared.projitems | 5 + .../src/System/TimeZoneInfo.Unix.Android.cs | 404 +++++++++++++++ .../System/TimeZoneInfo.Unix.NonAndroid.cs | 466 ++++++++++++++++++ .../src/System/TimeZoneInfo.Unix.cs | 457 +---------------- 8 files changed, 943 insertions(+), 449 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetDefaultTimeZone.Android.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetDefaultTimeZone.Android.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetDefaultTimeZone.Android.cs new file mode 100644 index 00000000000..b9a9cd50409 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetDefaultTimeZone.Android.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultTimeZone", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern string? GetDefaultTimeZone(); + } +} diff --git a/src/libraries/Native/Unix/System.Native/pal_datetime.c b/src/libraries/Native/Unix/System.Native/pal_datetime.c index 30f93ad05a1..3832114b4eb 100644 --- a/src/libraries/Native/Unix/System.Native/pal_datetime.c +++ b/src/libraries/Native/Unix/System.Native/pal_datetime.c @@ -2,13 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_config.h" - -#include -#include -#include -#include - #include "pal_datetime.h" +#include +#include +#include +#include +#if defined(TARGET_ANDROID) +#include +#endif +#include static const int64_t TICKS_PER_SECOND = 10000000; /* 10^7 */ #if HAVE_CLOCK_REALTIME @@ -39,3 +41,18 @@ int64_t SystemNative_GetSystemTimeAsTicks() // in failure we return 00:00 01 January 1970 UTC (Unix epoch) return 0; } + +#if defined(TARGET_ANDROID) +char* SystemNative_GetDefaultTimeZone() +{ + char defaulttimezone[PROP_VALUE_MAX]; + if (__system_property_get("persist.sys.timezone", defaulttimezone)) + { + return strdup(defaulttimezone); + } + else + { + return NULL; + } +} +#endif diff --git a/src/libraries/Native/Unix/System.Native/pal_datetime.h b/src/libraries/Native/Unix/System.Native/pal_datetime.h index 564a69a4857..1b88d472780 100644 --- a/src/libraries/Native/Unix/System.Native/pal_datetime.h +++ b/src/libraries/Native/Unix/System.Native/pal_datetime.h @@ -4,5 +4,10 @@ #pragma once #include "pal_compiler.h" +#include "pal_types.h" PALEXPORT int64_t SystemNative_GetSystemTimeAsTicks(void); + +#if defined(TARGET_ANDROID) +PALEXPORT char* SystemNative_GetDefaultTimeZone(void); +#endif diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index a9910865f00..82195aeb457 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3802,4 +3802,16 @@ A MemberInfo that matches '{0}' could not be found. + + Bad magic in '{0}': Header starts with '{1}' instead of 'tzdata' + + + Unable to fully read from file '{0}' at offset {1} length {2}; read {3} bytes expected {4}. + + + Length in index file less than AndroidTzDataHeader + + + Unable to properly load any time zone data files. + diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b3c6bd1b6e7..2c6783465d5 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1958,6 +1958,9 @@ Common\Interop\Unix\System.Native\Interop.GetCwd.cs + + Common\Interop\Unix\System.Native\Interop.GetDefaultTimeZone.Android.cs + Common\Interop\Unix\System.Native\Interop.GetEGid.cs @@ -2131,6 +2134,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs new file mode 100644 index 00000000000..53baf4eb651 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs @@ -0,0 +1,404 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private const string TimeZoneFileName = "tzdata"; + + private static AndroidTzData? s_tzData; + + private static AndroidTzData AndroidTzDataInstance + { + get + { + if (s_tzData == null) + { + Interlocked.CompareExchange(ref s_tzData, new AndroidTzData(), null); + } + + return s_tzData; + } + } + + // This should be called when name begins with GMT + private static int ParseGMTNumericZone(string name) + { + int sign; + if (name[3] == '+') + { + sign = 1; + } + else if (name[3] == '-') + { + sign = -1; + } + else + { + return 0; + } + + int where; + int hour = 0; + bool colon = false; + for (where = 4; where < name.Length; where++) + { + char c = name[where]; + + if (c == ':') + { + where++; + colon = true; + break; + } + + if (c >= '0' && c <= '9') + { + hour = hour * 10 + c - '0'; + } + else + { + return 0; + } + } + + int min = 0; + for (; where < name.Length; where++) + { + char c = name [where]; + + if (c >= '0' && c <= '9') + { + min = min * 10 + c - '0'; + } + else + { + return 0; + } + } + + if (colon) + { + return sign * (hour * 60 + min) * 60; + } + else if (hour >= 100) + { + return sign * ((hour / 100) * 60 + (hour % 100)) * 60; + } + else + { + return sign * (hour * 60) * 60; + } + } + + private static TimeZoneInfo? GetTimeZone(string id, string name) + { + if (name == "GMT" || name == "UTC") + { + return new TimeZoneInfo(id, TimeSpan.FromSeconds(0), id, name, name, null, disableDaylightSavingTime:true); + } + if (name.StartsWith("GMT", StringComparison.Ordinal)) + { + return new TimeZoneInfo(id, TimeSpan.FromSeconds(ParseGMTNumericZone(name)), id, name, name, null, disableDaylightSavingTime:true); + } + + try + { + byte[] buffer = AndroidTzDataInstance.GetTimeZoneData(name); + return GetTimeZoneFromTzData(buffer, id); + } + catch + { + return null; + } + } + + // Core logic to retrieve the local system time zone. + // Obtains Android's system local time zone id to get the corresponding time zone + // Defaults to Utc if local time zone cannot be found + private static TimeZoneInfo GetLocalTimeZoneCore() + { + string? id = Interop.Sys.GetDefaultTimeZone(); + if (!string.IsNullOrEmpty(id)) + { + TimeZoneInfo? defaultTimeZone = GetTimeZone(id, id); + + if (defaultTimeZone != null) + { + return defaultTimeZone; + } + } + + return Utc; + } + + private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id, out TimeZoneInfo? value, out Exception? e) + { + + value = id == LocalId ? GetLocalTimeZoneCore() : GetTimeZone(id, id); + + if (value == null) + { + e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, AndroidTzDataInstance.GetTimeZoneDirectory() + TimeZoneFileName)); + return TimeZoneInfoResult.TimeZoneNotFoundException; + } + + e = null; + return TimeZoneInfoResult.Success; + } + + private static string[] GetTimeZoneIds() + { + return AndroidTzDataInstance.GetTimeZoneIds(); + } + + /* + * Android v4.3 Timezone support infrastructure. + * + * Android tzdata files are found in the format of + * Header Entry Entry Entry ... Entry + * + * https://github.com/aosp-mirror/platform_bionic/blob/master/libc/tzcode/bionic.cpp + * + * The header (24 bytes) contains the following information + * signature - 12 bytes of the form "tzdata2012f\0" where 2012f is subject to change + * index offset - 4 bytes that denotes the offset at which the index of the tzdata file starts + * data offset - 4 bytes that denotes the offset at which the data of the tzdata file starts + * final offset - 4 bytes that used to denote the final offset, which we don't use but will note. + * + * Each Data Entry (52 bytes) can be used to generate a TimeZoneInfo and contain the following information + * id - 40 bytes that contain the id of the time zone data entry timezone + * byte offset - 4 bytes that denote the offset from the data offset timezone data can be found + * length - 4 bytes that denote the length of the data for timezone + * unused - 4 bytes that used to be raw GMT offset, but now is always 0 since tzdata2014f (L). + * + * This is needed in order to read Android v4.3 tzdata files. + * + * Android 10+ moved the up-to-date tzdata location to a module updatable via the Google Play Store and the + * database location changed (https://source.android.com/devices/architecture/modular-system/runtime#time-zone-data-interactions) + * The older locations still exist (at least the `/system/usr/share/zoneinfo` one) but they won't be updated. + */ + private sealed class AndroidTzData + { + private string[] _ids; + private int[] _byteOffsets; + private int[] _lengths; + private string _tzFileDir; + private string _tzFilePath; + + private static string GetApexTimeDataRoot() + { + string? ret = Environment.GetEnvironmentVariable("ANDROID_TZDATA_ROOT"); + if (!string.IsNullOrEmpty(ret)) + { + return ret; + } + + return "/apex/com.android.tzdata"; + } + + private static string GetApexRuntimeRoot() + { + string? ret = Environment.GetEnvironmentVariable("ANDROID_RUNTIME_ROOT"); + if (!string.IsNullOrEmpty(ret)) + { + return ret; + } + + return "/apex/com.android.runtime"; + } + + public AndroidTzData() + { + // On Android, time zone data is found in tzdata + // Based on https://github.com/mono/mono/blob/main/mcs/class/corlib/System/TimeZoneInfo.Android.cs + // Also follows the locations found at the bottom of https://github.com/aosp-mirror/platform_bionic/blob/master/libc/tzcode/bionic.cpp + string[] tzFileDirList = new string[] {GetApexTimeDataRoot() + "/etc/tz/", // Android 10+, TimeData module where the updates land + GetApexRuntimeRoot() + "/etc/tz/", // Android 10+, Fallback location if the above isn't found or corrupted + Environment.GetEnvironmentVariable("ANDROID_DATA") + "/misc/zoneinfo/", + Environment.GetEnvironmentVariable("ANDROID_ROOT") + DefaultTimeZoneDirectory}; + foreach (var tzFileDir in tzFileDirList) + { + string tzFilePath = Path.Combine(tzFileDir, TimeZoneFileName); + if (LoadData(tzFilePath)) + { + _tzFileDir = tzFileDir; + _tzFilePath = tzFilePath; + return; + } + } + + throw new TimeZoneNotFoundException(SR.TimeZoneNotFound_ValidTimeZoneFileMissing); + } + + [MemberNotNullWhen(true, nameof(_ids))] + [MemberNotNullWhen(true, nameof(_byteOffsets))] + [MemberNotNullWhen(true, nameof(_lengths))] + private bool LoadData(string path) + { + if (!File.Exists(path)) + { + return false; + } + try + { + using (FileStream fs = File.OpenRead(path)) + { + LoadTzFile(fs); + } + return true; + } + catch {} + + return false; + } + + [MemberNotNull(nameof(_ids))] + [MemberNotNull(nameof(_byteOffsets))] + [MemberNotNull(nameof(_lengths))] + private void LoadTzFile(Stream fs) + { + const int HeaderSize = 24; + Span buffer = stackalloc byte[HeaderSize]; + + ReadTzDataIntoBuffer(fs, 0, buffer); + + LoadHeader(buffer, out int indexOffset, out int dataOffset); + ReadIndex(fs, indexOffset, dataOffset); + } + + private void LoadHeader(Span buffer, out int indexOffset, out int dataOffset) + { + // tzdata files are expected to start with the form of "tzdata2012f\0" depending on the year of the tzdata used which is 2012 in this example + // since we're not differentiating on year, check for tzdata and the ending \0 + var tz = (ushort)TZif_ToInt16(buffer.Slice(0, 2)); + var data = (uint)TZif_ToInt32(buffer.Slice(2, 4)); + + if (tz != 0x747A || data != 0x64617461 || buffer[11] != 0) + { + // 0x747A 0x64617461 = {0x74, 0x7A} {0x64, 0x61, 0x74, 0x61} = "tz" "data" + var b = new StringBuilder(buffer.Length); + for (int i = 0; i < 12; ++i) + { + b.Append(' ').Append(HexConverter.ToCharLower(buffer[i])); + } + + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_BadTZHeader, TimeZoneFileName, b.ToString())); + } + + indexOffset = TZif_ToInt32(buffer.Slice(12, 4)); + dataOffset = TZif_ToInt32(buffer.Slice(16, 4)); + } + + [MemberNotNull(nameof(_ids))] + [MemberNotNull(nameof(_byteOffsets))] + [MemberNotNull(nameof(_lengths))] + private void ReadIndex(Stream fs, int indexOffset, int dataOffset) + { + int indexSize = dataOffset - indexOffset; + const int entrySize = 52; // Data entry size + int entryCount = indexSize / entrySize; + + _byteOffsets = new int[entryCount]; + _ids = new string[entryCount]; + _lengths = new int[entryCount]; + + for (int i = 0; i < entryCount; ++i) + { + LoadEntryAt(fs, indexOffset + (entrySize*i), out string id, out int byteOffset, out int length); + + _byteOffsets[i] = byteOffset + dataOffset; + _ids[i] = id; + _lengths[i] = length; + + if (length < 24) // Header Size + { + throw new InvalidOperationException(SR.InvalidOperation_BadIndexLength); + } + } + } + + private void ReadTzDataIntoBuffer(Stream fs, long position, Span buffer) + { + fs.Position = position; + + int bytesRead = 0; + int bytesLeft = buffer.Length; + + while (bytesLeft > 0) + { + int b = fs.Read(buffer.Slice(bytesRead)); + if (b == 0) + { + break; + } + + bytesRead += b; + bytesLeft -= b; + } + + if (bytesLeft != 0) + { + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ReadTZError, _tzFilePath, position, buffer.Length, bytesRead, buffer.Length)); + } + } + + private void LoadEntryAt(Stream fs, long position, out string id, out int byteOffset, out int length) + { + const int size = 52; // data entry size + Span entryBuffer = stackalloc byte[size]; + + ReadTzDataIntoBuffer(fs, position, entryBuffer); + + int index = 0; + while (entryBuffer[index] != 0 && index < 40) + { + index += 1; + } + id = Encoding.UTF8.GetString(entryBuffer.Slice(0, index)); + byteOffset = TZif_ToInt32(entryBuffer.Slice(40, 4)); + length = TZif_ToInt32(entryBuffer.Slice(44, 4)); + } + + public string[] GetTimeZoneIds() + { + return _ids; + } + + public string GetTimeZoneDirectory() + { + return _tzFilePath; + } + + public byte[] GetTimeZoneData(string id) + { + int i = Array.BinarySearch(_ids, id, StringComparer.Ordinal); + if (i < 0) + { + throw new InvalidOperationException(SR.Format(SR.TimeZoneNotFound_MissingData, id)); + } + + int offset = _byteOffsets[i]; + int length = _lengths[i]; + byte[] buffer = new byte[length]; + + using (FileStream fs = File.OpenRead(_tzFilePath)) + { + ReadTzDataIntoBuffer(fs, offset, buffer); + } + + return buffer; + } + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs new file mode 100644 index 00000000000..c5ad4b0f426 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs @@ -0,0 +1,466 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text; +using System.Security; +using Microsoft.Win32.SafeHandles; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private const string TimeZoneFileName = "zone.tab"; + private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR"; + private const string TimeZoneEnvironmentVariable = "TZ"; + + private static TimeZoneInfo GetLocalTimeZoneCore() + { + // Without Registry support, create the TimeZoneInfo from a TZ file + return GetLocalTimeZoneFromTzFile(); + } + + private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id, out TimeZoneInfo? value, out Exception? e) + { + value = null; + e = null; + + string timeZoneDirectory = GetTimeZoneDirectory(); + string timeZoneFilePath = Path.Combine(timeZoneDirectory, id); + byte[] rawData; + try + { + rawData = File.ReadAllBytes(timeZoneFilePath); + } + catch (UnauthorizedAccessException ex) + { + e = ex; + return TimeZoneInfoResult.SecurityException; + } + catch (FileNotFoundException ex) + { + e = ex; + return TimeZoneInfoResult.TimeZoneNotFoundException; + } + catch (DirectoryNotFoundException ex) + { + e = ex; + return TimeZoneInfoResult.TimeZoneNotFoundException; + } + catch (IOException ex) + { + e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath), ex); + return TimeZoneInfoResult.InvalidTimeZoneException; + } + + value = GetTimeZoneFromTzData(rawData, id); + + if (value == null) + { + e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath)); + return TimeZoneInfoResult.InvalidTimeZoneException; + } + + return TimeZoneInfoResult.Success; + } + + /// + /// Returns a collection of TimeZone Id values from the time zone file in the timeZoneDirectory. + /// + /// + /// Lines that start with # are comments and are skipped. + /// + private static List GetTimeZoneIds() + { + List timeZoneIds = new List(); + + try + { + using (StreamReader sr = new StreamReader(Path.Combine(GetTimeZoneDirectory(), TimeZoneFileName), Encoding.UTF8)) + { + string? zoneTabFileLine; + while ((zoneTabFileLine = sr.ReadLine()) != null) + { + if (!string.IsNullOrEmpty(zoneTabFileLine) && zoneTabFileLine[0] != '#') + { + // the format of the line is "country-code \t coordinates \t TimeZone Id \t comments" + + int firstTabIndex = zoneTabFileLine.IndexOf('\t'); + if (firstTabIndex != -1) + { + int secondTabIndex = zoneTabFileLine.IndexOf('\t', firstTabIndex + 1); + if (secondTabIndex != -1) + { + string timeZoneId; + int startIndex = secondTabIndex + 1; + int thirdTabIndex = zoneTabFileLine.IndexOf('\t', startIndex); + if (thirdTabIndex != -1) + { + int length = thirdTabIndex - startIndex; + timeZoneId = zoneTabFileLine.Substring(startIndex, length); + } + else + { + timeZoneId = zoneTabFileLine.Substring(startIndex); + } + + if (!string.IsNullOrEmpty(timeZoneId)) + { + timeZoneIds.Add(timeZoneId); + } + } + } + } + } + } + } + catch (IOException) { } + catch (UnauthorizedAccessException) { } + + return timeZoneIds; + } + + private static string? GetTzEnvironmentVariable() + { + string? result = Environment.GetEnvironmentVariable(TimeZoneEnvironmentVariable); + if (!string.IsNullOrEmpty(result)) + { + if (result[0] == ':') + { + // strip off the ':' prefix + result = result.Substring(1); + } + } + + return result; + } + + /// + /// Finds the time zone id by using 'readlink' on the path to see if tzFilePath is + /// a symlink to a file. + /// + private static string? FindTimeZoneIdUsingReadLink(string tzFilePath) + { + string? id = null; + + string? symlinkPath = Interop.Sys.ReadLink(tzFilePath); + if (symlinkPath != null) + { + // symlinkPath can be relative path, use Path to get the full absolute path. + symlinkPath = Path.GetFullPath(symlinkPath, Path.GetDirectoryName(tzFilePath)!); + + string timeZoneDirectory = GetTimeZoneDirectory(); + if (symlinkPath.StartsWith(timeZoneDirectory, StringComparison.Ordinal)) + { + id = symlinkPath.Substring(timeZoneDirectory.Length); + } + } + + return id; + } + + private static string? GetDirectoryEntryFullPath(ref Interop.Sys.DirectoryEntry dirent, string currentPath) + { + ReadOnlySpan direntName = dirent.GetName(stackalloc char[Interop.Sys.DirectoryEntry.NameBufferSize]); + + if ((direntName.Length == 1 && direntName[0] == '.') || + (direntName.Length == 2 && direntName[0] == '.' && direntName[1] == '.')) + return null; + + return Path.Join(currentPath.AsSpan(), direntName); + } + + /// + /// Enumerate files + /// + private static unsafe void EnumerateFilesRecursively(string path, Predicate condition) + { + List? toExplore = null; // List used as a stack + + int bufferSize = Interop.Sys.GetReadDirRBufferSize(); + byte[]? dirBuffer = null; + try + { + dirBuffer = ArrayPool.Shared.Rent(bufferSize); + string currentPath = path; + + fixed (byte* dirBufferPtr = dirBuffer) + { + while (true) + { + IntPtr dirHandle = Interop.Sys.OpenDir(currentPath); + if (dirHandle == IntPtr.Zero) + { + throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), currentPath, isDirectory: true); + } + + try + { + // Read each entry from the enumerator + Interop.Sys.DirectoryEntry dirent; + while (Interop.Sys.ReadDirR(dirHandle, dirBufferPtr, bufferSize, out dirent) == 0) + { + string? fullPath = GetDirectoryEntryFullPath(ref dirent, currentPath); + if (fullPath == null) + continue; + + // Get from the dir entry whether the entry is a file or directory. + // We classify everything as a file unless we know it to be a directory. + bool isDir; + if (dirent.InodeType == Interop.Sys.NodeType.DT_DIR) + { + // We know it's a directory. + isDir = true; + } + else if (dirent.InodeType == Interop.Sys.NodeType.DT_LNK || dirent.InodeType == Interop.Sys.NodeType.DT_UNKNOWN) + { + // It's a symlink or unknown: stat to it to see if we can resolve it to a directory. + // If we can't (e.g. symlink to a file, broken symlink, etc.), we'll just treat it as a file. + + Interop.Sys.FileStatus fileinfo; + if (Interop.Sys.Stat(fullPath, out fileinfo) >= 0) + { + isDir = (fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR; + } + else + { + isDir = false; + } + } + else + { + // Otherwise, treat it as a file. This includes regular files, FIFOs, etc. + isDir = false; + } + + // Yield the result if the user has asked for it. In the case of directories, + // always explore it by pushing it onto the stack, regardless of whether + // we're returning directories. + if (isDir) + { + toExplore ??= new List(); + toExplore.Add(fullPath); + } + else if (condition(fullPath)) + { + return; + } + } + } + finally + { + if (dirHandle != IntPtr.Zero) + Interop.Sys.CloseDir(dirHandle); + } + + if (toExplore == null || toExplore.Count == 0) + break; + + currentPath = toExplore[toExplore.Count - 1]; + toExplore.RemoveAt(toExplore.Count - 1); + } + } + } + finally + { + if (dirBuffer != null) + ArrayPool.Shared.Return(dirBuffer); + } + } + + private static bool CompareTimeZoneFile(string filePath, byte[] buffer, byte[] rawData) + { + try + { + // bufferSize == 1 used to avoid unnecessary buffer in FileStream + using (SafeFileHandle sfh = File.OpenHandle(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + long fileLength = RandomAccess.GetLength(sfh); + if (fileLength == rawData.Length) + { + int index = 0; + int count = rawData.Length; + + while (count > 0) + { + int n = RandomAccess.Read(sfh, buffer.AsSpan(index, count), index); + if (n == 0) + ThrowHelper.ThrowEndOfFileException(); + + int end = index + n; + for (; index < end; index++) + { + if (buffer[index] != rawData[index]) + { + return false; + } + } + + count -= n; + } + + return true; + } + } + } + catch (IOException) { } + catch (SecurityException) { } + catch (UnauthorizedAccessException) { } + + return false; + } + + /// + /// Find the time zone id by searching all the tzfiles for the one that matches rawData + /// and return its file name. + /// + private static string FindTimeZoneId(byte[] rawData) + { + // default to "Local" if we can't find the right tzfile + string id = LocalId; + string timeZoneDirectory = GetTimeZoneDirectory(); + string localtimeFilePath = Path.Combine(timeZoneDirectory, "localtime"); + string posixrulesFilePath = Path.Combine(timeZoneDirectory, "posixrules"); + byte[] buffer = new byte[rawData.Length]; + + try + { + EnumerateFilesRecursively(timeZoneDirectory, (string filePath) => + { + // skip the localtime and posixrules file, since they won't give us the correct id + if (!string.Equals(filePath, localtimeFilePath, StringComparison.OrdinalIgnoreCase) + && !string.Equals(filePath, posixrulesFilePath, StringComparison.OrdinalIgnoreCase)) + { + if (CompareTimeZoneFile(filePath, buffer, rawData)) + { + // if all bytes are the same, this must be the right tz file + id = filePath; + + // strip off the root time zone directory + if (id.StartsWith(timeZoneDirectory, StringComparison.Ordinal)) + { + id = id.Substring(timeZoneDirectory.Length); + } + return true; + } + } + return false; + }); + } + catch (IOException) { } + catch (SecurityException) { } + catch (UnauthorizedAccessException) { } + + return id; + } + + private static bool TryLoadTzFile(string tzFilePath, [NotNullWhen(true)] ref byte[]? rawData, [NotNullWhen(true)] ref string? id) + { + if (File.Exists(tzFilePath)) + { + try + { + rawData = File.ReadAllBytes(tzFilePath); + if (string.IsNullOrEmpty(id)) + { + id = FindTimeZoneIdUsingReadLink(tzFilePath); + + if (string.IsNullOrEmpty(id)) + { + id = FindTimeZoneId(rawData); + } + } + return true; + } + catch (IOException) { } + catch (SecurityException) { } + catch (UnauthorizedAccessException) { } + } + return false; + } + + /// + /// Gets the tzfile raw data for the current 'local' time zone using the following rules. + /// 1. Read the TZ environment variable. If it is set, use it. + /// 2. Look for the data in /etc/localtime. + /// 3. Look for the data in GetTimeZoneDirectory()/localtime. + /// 4. Use UTC if all else fails. + /// + private static bool TryGetLocalTzFile([NotNullWhen(true)] out byte[]? rawData, [NotNullWhen(true)] out string? id) + { + rawData = null; + id = null; + string? tzVariable = GetTzEnvironmentVariable(); + + // If the env var is null, use the localtime file + if (tzVariable == null) + { + return + TryLoadTzFile("/etc/localtime", ref rawData, ref id) || + TryLoadTzFile(Path.Combine(GetTimeZoneDirectory(), "localtime"), ref rawData, ref id); + } + + // If it's empty, use UTC (TryGetLocalTzFile() should return false). + if (tzVariable.Length == 0) + { + return false; + } + + // Otherwise, use the path from the env var. If it's not absolute, make it relative + // to the system timezone directory + string tzFilePath; + if (tzVariable[0] != '/') + { + id = tzVariable; + tzFilePath = Path.Combine(GetTimeZoneDirectory(), tzVariable); + } + else + { + tzFilePath = tzVariable; + } + return TryLoadTzFile(tzFilePath, ref rawData, ref id); + } + + /// + /// Helper function used by 'GetLocalTimeZone()' - this function wraps the call + /// for loading time zone data from computers without Registry support. + /// + /// The TryGetLocalTzFile() call returns a Byte[] containing the compiled tzfile. + /// + private static TimeZoneInfo GetLocalTimeZoneFromTzFile() + { + byte[]? rawData; + string? id; + if (TryGetLocalTzFile(out rawData, out id)) + { + TimeZoneInfo? result = GetTimeZoneFromTzData(rawData, id); + if (result != null) + { + return result; + } + } + + // if we can't find a local time zone, return UTC + return Utc; + } + + private static string GetTimeZoneDirectory() + { + string? tzDirectory = Environment.GetEnvironmentVariable(TimeZoneDirectoryEnvironmentVariable); + + if (tzDirectory == null) + { + tzDirectory = DefaultTimeZoneDirectory; + } + else if (!tzDirectory.EndsWith(Path.DirectorySeparatorChar)) + { + tzDirectory += PathInternal.DirectorySeparatorCharAsString; + } + + return tzDirectory; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 4756a932715..622195782cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; @@ -18,9 +17,6 @@ namespace System public sealed partial class TimeZoneInfo { private const string DefaultTimeZoneDirectory = "/usr/share/zoneinfo/"; - private const string ZoneTabFileName = "zone.tab"; - private const string TimeZoneEnvironmentVariable = "TZ"; - private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR"; // UTC aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml // Hard-coded because we need to treat all aliases of UTC the same even when globalization data is not available. @@ -231,8 +227,7 @@ namespace System { Debug.Assert(Monitor.IsEntered(cachedData)); - string timeZoneDirectory = GetTimeZoneDirectory(); - foreach (string timeZoneId in GetTimeZoneIds(timeZoneDirectory)) + foreach (string timeZoneId in GetTimeZoneIds()) { TryGetTimeZone(timeZoneId, false, out _, out _, cachedData, alwaysFallbackToLocalMachine: true); // populate the cache } @@ -248,432 +243,12 @@ namespace System { Debug.Assert(Monitor.IsEntered(cachedData)); - // Without Registry support, create the TimeZoneInfo from a TZ file - return GetLocalTimeZoneFromTzFile(); + return GetLocalTimeZoneCore(); } private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out TimeZoneInfo? value, out Exception? e) { - value = null; - e = null; - - string timeZoneDirectory = GetTimeZoneDirectory(); - string timeZoneFilePath = Path.Combine(timeZoneDirectory, id); - byte[] rawData; - try - { - rawData = File.ReadAllBytes(timeZoneFilePath); - } - catch (UnauthorizedAccessException ex) - { - e = ex; - return TimeZoneInfoResult.SecurityException; - } - catch (FileNotFoundException ex) - { - e = ex; - return TimeZoneInfoResult.TimeZoneNotFoundException; - } - catch (DirectoryNotFoundException ex) - { - e = ex; - return TimeZoneInfoResult.TimeZoneNotFoundException; - } - catch (IOException ex) - { - e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath), ex); - return TimeZoneInfoResult.InvalidTimeZoneException; - } - - value = GetTimeZoneFromTzData(rawData, id); - - if (value == null) - { - e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath)); - return TimeZoneInfoResult.InvalidTimeZoneException; - } - - return TimeZoneInfoResult.Success; - } - - /// - /// Returns a collection of TimeZone Id values from the zone.tab file in the timeZoneDirectory. - /// - /// - /// Lines that start with # are comments and are skipped. - /// - private static List GetTimeZoneIds(string timeZoneDirectory) - { - List timeZoneIds = new List(); - - try - { - using (StreamReader sr = new StreamReader(Path.Combine(timeZoneDirectory, ZoneTabFileName), Encoding.UTF8)) - { - string? zoneTabFileLine; - while ((zoneTabFileLine = sr.ReadLine()) != null) - { - if (!string.IsNullOrEmpty(zoneTabFileLine) && zoneTabFileLine[0] != '#') - { - // the format of the line is "country-code \t coordinates \t TimeZone Id \t comments" - - int firstTabIndex = zoneTabFileLine.IndexOf('\t'); - if (firstTabIndex != -1) - { - int secondTabIndex = zoneTabFileLine.IndexOf('\t', firstTabIndex + 1); - if (secondTabIndex != -1) - { - string timeZoneId; - int startIndex = secondTabIndex + 1; - int thirdTabIndex = zoneTabFileLine.IndexOf('\t', startIndex); - if (thirdTabIndex != -1) - { - int length = thirdTabIndex - startIndex; - timeZoneId = zoneTabFileLine.Substring(startIndex, length); - } - else - { - timeZoneId = zoneTabFileLine.Substring(startIndex); - } - - if (!string.IsNullOrEmpty(timeZoneId)) - { - timeZoneIds.Add(timeZoneId); - } - } - } - } - } - } - } - catch (IOException) { } - catch (UnauthorizedAccessException) { } - - return timeZoneIds; - } - - /// - /// Gets the tzfile raw data for the current 'local' time zone using the following rules. - /// 1. Read the TZ environment variable. If it is set, use it. - /// 2. Look for the data in /etc/localtime. - /// 3. Look for the data in GetTimeZoneDirectory()/localtime. - /// 4. Use UTC if all else fails. - /// - private static bool TryGetLocalTzFile([NotNullWhen(true)] out byte[]? rawData, [NotNullWhen(true)] out string? id) - { - rawData = null; - id = null; - string? tzVariable = GetTzEnvironmentVariable(); - - // If the env var is null, use the localtime file - if (tzVariable == null) - { - return - TryLoadTzFile("/etc/localtime", ref rawData, ref id) || - TryLoadTzFile(Path.Combine(GetTimeZoneDirectory(), "localtime"), ref rawData, ref id); - } - - // If it's empty, use UTC (TryGetLocalTzFile() should return false). - if (tzVariable.Length == 0) - { - return false; - } - - // Otherwise, use the path from the env var. If it's not absolute, make it relative - // to the system timezone directory - string tzFilePath; - if (tzVariable[0] != '/') - { - id = tzVariable; - tzFilePath = Path.Combine(GetTimeZoneDirectory(), tzVariable); - } - else - { - tzFilePath = tzVariable; - } - return TryLoadTzFile(tzFilePath, ref rawData, ref id); - } - - private static string? GetTzEnvironmentVariable() - { - string? result = Environment.GetEnvironmentVariable(TimeZoneEnvironmentVariable); - if (!string.IsNullOrEmpty(result)) - { - if (result[0] == ':') - { - // strip off the ':' prefix - result = result.Substring(1); - } - } - - return result; - } - - private static bool TryLoadTzFile(string tzFilePath, [NotNullWhen(true)] ref byte[]? rawData, [NotNullWhen(true)] ref string? id) - { - if (File.Exists(tzFilePath)) - { - try - { - rawData = File.ReadAllBytes(tzFilePath); - if (string.IsNullOrEmpty(id)) - { - id = FindTimeZoneIdUsingReadLink(tzFilePath); - - if (string.IsNullOrEmpty(id)) - { - id = FindTimeZoneId(rawData); - } - } - return true; - } - catch (IOException) { } - catch (SecurityException) { } - catch (UnauthorizedAccessException) { } - } - return false; - } - - /// - /// Finds the time zone id by using 'readlink' on the path to see if tzFilePath is - /// a symlink to a file. - /// - private static string? FindTimeZoneIdUsingReadLink(string tzFilePath) - { - string? id = null; - - string? symlinkPath = Interop.Sys.ReadLink(tzFilePath); - if (symlinkPath != null) - { - // symlinkPath can be relative path, use Path to get the full absolute path. - symlinkPath = Path.GetFullPath(symlinkPath, Path.GetDirectoryName(tzFilePath)!); - - string timeZoneDirectory = GetTimeZoneDirectory(); - if (symlinkPath.StartsWith(timeZoneDirectory, StringComparison.Ordinal)) - { - id = symlinkPath.Substring(timeZoneDirectory.Length); - } - } - - return id; - } - - private static string? GetDirectoryEntryFullPath(ref Interop.Sys.DirectoryEntry dirent, string currentPath) - { - ReadOnlySpan direntName = dirent.GetName(stackalloc char[Interop.Sys.DirectoryEntry.NameBufferSize]); - - if ((direntName.Length == 1 && direntName[0] == '.') || - (direntName.Length == 2 && direntName[0] == '.' && direntName[1] == '.')) - return null; - - return Path.Join(currentPath.AsSpan(), direntName); - } - - /// - /// Enumerate files - /// - private static unsafe void EnumerateFilesRecursively(string path, Predicate condition) - { - List? toExplore = null; // List used as a stack - - int bufferSize = Interop.Sys.GetReadDirRBufferSize(); - byte[]? dirBuffer = null; - try - { - dirBuffer = ArrayPool.Shared.Rent(bufferSize); - string currentPath = path; - - fixed (byte* dirBufferPtr = dirBuffer) - { - while (true) - { - IntPtr dirHandle = Interop.Sys.OpenDir(currentPath); - if (dirHandle == IntPtr.Zero) - { - throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), currentPath, isDirectory: true); - } - - try - { - // Read each entry from the enumerator - Interop.Sys.DirectoryEntry dirent; - while (Interop.Sys.ReadDirR(dirHandle, dirBufferPtr, bufferSize, out dirent) == 0) - { - string? fullPath = GetDirectoryEntryFullPath(ref dirent, currentPath); - if (fullPath == null) - continue; - - // Get from the dir entry whether the entry is a file or directory. - // We classify everything as a file unless we know it to be a directory. - bool isDir; - if (dirent.InodeType == Interop.Sys.NodeType.DT_DIR) - { - // We know it's a directory. - isDir = true; - } - else if (dirent.InodeType == Interop.Sys.NodeType.DT_LNK || dirent.InodeType == Interop.Sys.NodeType.DT_UNKNOWN) - { - // It's a symlink or unknown: stat to it to see if we can resolve it to a directory. - // If we can't (e.g. symlink to a file, broken symlink, etc.), we'll just treat it as a file. - - Interop.Sys.FileStatus fileinfo; - if (Interop.Sys.Stat(fullPath, out fileinfo) >= 0) - { - isDir = (fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR; - } - else - { - isDir = false; - } - } - else - { - // Otherwise, treat it as a file. This includes regular files, FIFOs, etc. - isDir = false; - } - - // Yield the result if the user has asked for it. In the case of directories, - // always explore it by pushing it onto the stack, regardless of whether - // we're returning directories. - if (isDir) - { - toExplore ??= new List(); - toExplore.Add(fullPath); - } - else if (condition(fullPath)) - { - return; - } - } - } - finally - { - if (dirHandle != IntPtr.Zero) - Interop.Sys.CloseDir(dirHandle); - } - - if (toExplore == null || toExplore.Count == 0) - break; - - currentPath = toExplore[toExplore.Count - 1]; - toExplore.RemoveAt(toExplore.Count - 1); - } - } - } - finally - { - if (dirBuffer != null) - ArrayPool.Shared.Return(dirBuffer); - } - } - - /// - /// Find the time zone id by searching all the tzfiles for the one that matches rawData - /// and return its file name. - /// - private static string FindTimeZoneId(byte[] rawData) - { - // default to "Local" if we can't find the right tzfile - string id = LocalId; - string timeZoneDirectory = GetTimeZoneDirectory(); - string localtimeFilePath = Path.Combine(timeZoneDirectory, "localtime"); - string posixrulesFilePath = Path.Combine(timeZoneDirectory, "posixrules"); - byte[] buffer = new byte[rawData.Length]; - - try - { - EnumerateFilesRecursively(timeZoneDirectory, (string filePath) => - { - // skip the localtime and posixrules file, since they won't give us the correct id - if (!string.Equals(filePath, localtimeFilePath, StringComparison.OrdinalIgnoreCase) - && !string.Equals(filePath, posixrulesFilePath, StringComparison.OrdinalIgnoreCase)) - { - if (CompareTimeZoneFile(filePath, buffer, rawData)) - { - // if all bytes are the same, this must be the right tz file - id = filePath; - - // strip off the root time zone directory - if (id.StartsWith(timeZoneDirectory, StringComparison.Ordinal)) - { - id = id.Substring(timeZoneDirectory.Length); - } - return true; - } - } - return false; - }); - } - catch (IOException) { } - catch (SecurityException) { } - catch (UnauthorizedAccessException) { } - - return id; - } - - private static bool CompareTimeZoneFile(string filePath, byte[] buffer, byte[] rawData) - { - try - { - // bufferSize == 1 used to avoid unnecessary buffer in FileStream - using (SafeFileHandle sfh = File.OpenHandle(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - long fileLength = RandomAccess.GetLength(sfh); - if (fileLength == rawData.Length) - { - int index = 0; - int count = rawData.Length; - - while (count > 0) - { - int n = RandomAccess.Read(sfh, buffer.AsSpan(index, count), index); - if (n == 0) - ThrowHelper.ThrowEndOfFileException(); - - int end = index + n; - for (; index < end; index++) - { - if (buffer[index] != rawData[index]) - { - return false; - } - } - - count -= n; - } - - return true; - } - } - } - catch (IOException) { } - catch (SecurityException) { } - catch (UnauthorizedAccessException) { } - - return false; - } - - /// - /// Helper function used by 'GetLocalTimeZone()' - this function wraps the call - /// for loading time zone data from computers without Registry support. - /// - /// The TryGetLocalTzFile() call returns a Byte[] containing the compiled tzfile. - /// - private static TimeZoneInfo GetLocalTimeZoneFromTzFile() - { - byte[]? rawData; - string? id; - if (TryGetLocalTzFile(out rawData, out id)) - { - TimeZoneInfo? result = GetTimeZoneFromTzData(rawData, id); - if (result != null) - { - return result; - } - } - - // if we can't find a local time zone, return UTC - return Utc; + return TryGetTimeZoneFromLocalMachineCore(id, out value, out e); } private static TimeZoneInfo? GetTimeZoneFromTzData(byte[]? rawData, string id) @@ -697,21 +272,6 @@ namespace System return null; } - private static string GetTimeZoneDirectory() - { - string? tzDirectory = Environment.GetEnvironmentVariable(TimeZoneDirectoryEnvironmentVariable); - - if (tzDirectory == null) - { - tzDirectory = DefaultTimeZoneDirectory; - } - else if (!tzDirectory.EndsWith(Path.DirectorySeparatorChar)) - { - tzDirectory += PathInternal.DirectorySeparatorCharAsString; - } - - return tzDirectory; - } /// /// Helper function for retrieving a TimeZoneInfo object by time_zone_name. @@ -1531,16 +1091,27 @@ namespace System zoneAbbreviations.Substring(index); } + // Converts a span of bytes into a long - always using standard byte order (Big Endian) + // per TZif file standard + private static short TZif_ToInt16(ReadOnlySpan value) + => BinaryPrimitives.ReadInt16BigEndian(value); + // Converts an array of bytes into an int - always using standard byte order (Big Endian) // per TZif file standard private static int TZif_ToInt32(byte[] value, int startIndex) => BinaryPrimitives.ReadInt32BigEndian(value.AsSpan(startIndex)); + // Converts a span of bytes into an int - always using standard byte order (Big Endian) + // per TZif file standard + private static int TZif_ToInt32(ReadOnlySpan value) + => BinaryPrimitives.ReadInt32BigEndian(value); + // Converts an array of bytes into a long - always using standard byte order (Big Endian) // per TZif file standard private static long TZif_ToInt64(byte[] value, int startIndex) => BinaryPrimitives.ReadInt64BigEndian(value.AsSpan(startIndex)); + private static long TZif_ToUnixTime(byte[] value, int startIndex, TZVersion version) => version != TZVersion.V1 ? TZif_ToInt64(value, startIndex) : From 52262b88b992a3e8eb292a824102031dff1d6828 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Wed, 21 Jul 2021 18:59:43 +0200 Subject: [PATCH 723/926] Revert "throw PNSE for unsupported SSL options in Quic. (#55877)" (#56097) This reverts commit b2107c5e48d2fa5163aa6bf3182a530a04d1533c. --- .../src/Resources/Strings.resx | 3 -- .../Interop/SafeMsQuicConfigurationHandle.cs | 53 ++++--------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/Resources/Strings.resx b/src/libraries/System.Net.Quic/src/Resources/Strings.resx index 061e1647eb8..a29352a0578 100644 --- a/src/libraries/System.Net.Quic/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Quic/src/Resources/Strings.resx @@ -150,8 +150,5 @@ Writing is not allowed on stream. - - The '{0}' is not supported by System.Net.Quic. - diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index 96b168977af..df48e0db377 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -36,39 +36,20 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) { X509Certificate? certificate = null; - - if (options.ClientAuthenticationOptions != null) + if (options.ClientAuthenticationOptions?.ClientCertificates != null) { - if (options.ClientAuthenticationOptions.CipherSuitesPolicy != null) + foreach (var cert in options.ClientAuthenticationOptions.ClientCertificates) { - throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.CipherSuitesPolicy))); - } - - if (options.ClientAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.NoEncryption) - { - throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.EncryptionPolicy))); - } - - if (options.ClientAuthenticationOptions.LocalCertificateSelectionCallback != null) - { - throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ClientAuthenticationOptions.LocalCertificateSelectionCallback))); - } - - if (options.ClientAuthenticationOptions.ClientCertificates != null) - { - foreach (var cert in options.ClientAuthenticationOptions.ClientCertificates) + try { - try + if (((X509Certificate2)cert).HasPrivateKey) { - if (((X509Certificate2)cert).HasPrivateKey) - { - // Pick first certificate with private key. - certificate = cert; - break; - } + // Pick first certificate with private key. + certificate = cert; + break; } - catch { } } + catch { } } } @@ -78,23 +59,9 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal public static unsafe SafeMsQuicConfigurationHandle Create(QuicListenerOptions options) { QUIC_CREDENTIAL_FLAGS flags = QUIC_CREDENTIAL_FLAGS.NONE; - - if (options.ServerAuthenticationOptions != null) + if (options.ServerAuthenticationOptions != null && options.ServerAuthenticationOptions.ClientCertificateRequired) { - if (options.ServerAuthenticationOptions.CipherSuitesPolicy != null) - { - throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ServerAuthenticationOptions.CipherSuitesPolicy))); - } - - if (options.ServerAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.NoEncryption) - { - throw new PlatformNotSupportedException(SR.Format(SR.net_quic_ssl_option, nameof(options.ServerAuthenticationOptions.EncryptionPolicy))); - } - - if (options.ServerAuthenticationOptions.ClientCertificateRequired) - { - flags |= QUIC_CREDENTIAL_FLAGS.REQUIRE_CLIENT_AUTHENTICATION | QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION; - } + flags |= QUIC_CREDENTIAL_FLAGS.REQUIRE_CLIENT_AUTHENTICATION | QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED | QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION; } return Create(options, flags, options.ServerAuthenticationOptions?.ServerCertificate, options.ServerAuthenticationOptions?.ServerCertificateContext, options.ServerAuthenticationOptions?.ApplicationProtocols); From 37b36491145068c271c047941091203b3d5f5653 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 21 Jul 2021 11:02:36 -0700 Subject: [PATCH 724/926] Re-enable tests for runtime handles in crossgen2 (#56065) --- src/tests/issues.targets | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index ccf5fcc479c..c2214eee83c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -959,24 +959,6 @@ Not compatible with crossgen2 - - https://github.com/dotnet/runtime/issues/43460 - - - https://github.com/dotnet/runtime/issues/43460 - - - https://github.com/dotnet/runtime/issues/43460 - - - https://github.com/dotnet/runtime/issues/43460 - - - https://github.com/dotnet/runtime/issues/43460 - - - https://github.com/dotnet/runtime/issues/43460 - https://github.com/dotnet/runtime/issues/43461 From e6fc5d288cf63afcddedc4d3377f9689b846166a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 21 Jul 2021 20:08:33 +0200 Subject: [PATCH 725/926] Update dependencies from https://github.com/mono/linker build 20210720.1 (#56086) Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.6.21369.3 -> To Version 6.0.100-preview.6.21370.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4ba2cd28427..d3afda097cd 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -182,9 +182,9 @@ https://github.com/dotnet/runtime f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/mono/linker - 8d49e169d872d6225bcf73ae14ae50b002f39319 + 6eae01980dc694107bdee0bc723d75a0dd601f0e https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 88efb3204e5..04aad29b272 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -165,7 +165,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21369.3 + 6.0.100-preview.6.21370.1 $(MicrosoftNETILLinkTasksVersion) 6.0.0-rc.1.21369.1 From b4328ed77fb468e2673a420dd295a9a8b0aa68d6 Mon Sep 17 00:00:00 2001 From: Karel Zikmund Date: Wed, 21 Jul 2021 20:41:14 +0200 Subject: [PATCH 726/926] Disable [QUIC] WriteCanceled_NextWriteThrows on Mock (#56078) Disable [QUIC] WriteCanceled_NextWriteThrows on Mock Disabled test tracked by #55995 --- .../tests/FunctionalTests/QuicStreamTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 2e21ddfd4b8..af45e791a48 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -617,6 +617,12 @@ namespace System.Net.Quic.Tests [Fact] public async Task WriteCanceled_NextWriteThrows() { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/55995")] + if (typeof(T) == typeof(MockProviderFactory)) + { + return; + } + const long expectedErrorCode = 1234; await RunClientServer( From e8d536f66e918d47a0b680386d887a27812ebaf8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 21 Jul 2021 12:44:30 -0700 Subject: [PATCH 727/926] Remove diagnostic printfs inadvertently left in PGO code. (#56024) --- src/coreclr/vm/pgo.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/vm/pgo.cpp b/src/coreclr/vm/pgo.cpp index 2ba24960f09..b2dc0dc6b7f 100644 --- a/src/coreclr/vm/pgo.cpp +++ b/src/coreclr/vm/pgo.cpp @@ -685,10 +685,8 @@ HRESULT PgoManager::allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD, { if (!ComparePgoSchemaEquals(existingData->header.GetData(), existingData->header.countsOffset, pSchema, countSchemaItems)) { - printf("@@@ Alloc failing, existing schema clash...\n"); return E_NOTIMPL; } - printf("@@@ Alloc returning existing schema + data...\n"); *pInstrumentationData = existingData->header.GetData(); return S_OK; } From 8ece868ab4771c2fb5c09ad836f095723705b379 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Wed, 21 Jul 2021 15:37:20 -0700 Subject: [PATCH 728/926] Remove QuicStream.ShutdownWriteCompleted method (#55981) * remove QuicStream.ShutdownWriteCompleted API and associated implementation logic Co-authored-by: Geoffrey Kizer --- .../System/Net/Http/Http3LoopbackStream.cs | 8 +- .../System.Net.Quic/ref/System.Net.Quic.cs | 1 - .../Quic/Implementations/Mock/MockStream.cs | 8 -- .../Implementations/MsQuic/MsQuicStream.cs | 113 ------------------ .../Implementations/QuicStreamProvider.cs | 2 - .../src/System/Net/Quic/QuicStream.cs | 2 - 6 files changed, 1 insertion(+), 133 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs index 8bccc13cce2..b93807a0321 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs @@ -154,12 +154,6 @@ namespace System.Net.Test.Common await _stream.WriteAsync(framePayload).ConfigureAwait(false); } - public async Task ShutdownSendAsync() - { - _stream.Shutdown(); - await _stream.ShutdownWriteCompleted().ConfigureAwait(false); - } - static int EncodeHttpInteger(long longToEncode, Span buffer) { Debug.Assert(longToEncode >= 0); @@ -273,7 +267,7 @@ namespace System.Net.Test.Common if (isFinal) { - await ShutdownSendAsync().ConfigureAwait(false); + _stream.Shutdown(); await _stream.ShutdownCompleted().ConfigureAwait(false); Dispose(); } diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs index be6df9a7c47..28d43324661 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -104,7 +104,6 @@ namespace System.Net.Quic public override void SetLength(long value) { } public void Shutdown() { } public System.Threading.Tasks.ValueTask ShutdownCompleted(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override void Write(byte[] buffer, int offset, int count) { } public override void Write(System.ReadOnlySpan buffer) { } public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs index 1d09b633174..313fc1eb6b6 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs @@ -203,14 +203,6 @@ namespace System.Net.Quic.Implementations.Mock WriteStreamBuffer?.EndWrite(); } - internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) - { - CheckDisposed(); - - return default; - } - - internal override ValueTask ShutdownCompleted(CancellationToken cancellationToken = default) { CheckDisposed(); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index b5968f70cec..616d83788a6 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -68,11 +68,6 @@ namespace System.Net.Quic.Implementations.MsQuic // Resettable completions to be used for multiple calls to send. public readonly ResettableCompletionSource SendResettableCompletionSource = new ResettableCompletionSource(); - public ShutdownWriteState ShutdownWriteState; - - // Set once writes have been shutdown. - public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - public ShutdownState ShutdownState; // The value makes sure that we release the handles only once. public int ShutdownDone; @@ -528,26 +523,12 @@ namespace System.Net.Quic.Implementations.MsQuic { ThrowIfDisposed(); - bool shouldComplete = false; - lock (_state) { if (_state.SendState < SendState.Aborted) { _state.SendState = SendState.Aborted; } - - if (_state.ShutdownWriteState == ShutdownWriteState.None) - { - _state.ShutdownWriteState = ShutdownWriteState.Canceled; - shouldComplete = true; - } - } - - if (shouldComplete) - { - _state.ShutdownWriteCompletionSource.SetException( - ExceptionDispatchInfo.SetCurrentStackTrace(new QuicStreamAbortedException("Shutdown was aborted.", errorCode))); } StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND, errorCode); @@ -559,42 +540,6 @@ namespace System.Net.Quic.Implementations.MsQuic QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); } - internal override async ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) - { - ThrowIfDisposed(); - - lock (_state) - { - if (_state.ShutdownWriteState == ShutdownWriteState.ConnectionClosed) - { - throw GetConnectionAbortedException(_state); - } - } - - // TODO do anything to stop writes? - using CancellationTokenRegistration registration = cancellationToken.UnsafeRegister(static (s, token) => - { - var state = (State)s!; - bool shouldComplete = false; - lock (state) - { - if (state.ShutdownWriteState == ShutdownWriteState.None) - { - state.ShutdownWriteState = ShutdownWriteState.Canceled; // TODO: should we separate states for cancelling here vs calling Abort? - shouldComplete = true; - } - } - - if (shouldComplete) - { - state.ShutdownWriteCompletionSource.SetException( - ExceptionDispatchInfo.SetCurrentStackTrace(new OperationCanceledException("Wait for shutdown write was canceled", token))); - } - }, _state); - - await _state.ShutdownWriteCompletionSource.Task.ConfigureAwait(false); - } - internal override async ValueTask ShutdownCompleted(CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -835,11 +780,6 @@ namespace System.Net.Quic.Implementations.MsQuic // Peer has stopped receiving data, don't send anymore. case QUIC_STREAM_EVENT_TYPE.PEER_RECEIVE_ABORTED: return HandleEventPeerRecvAborted(state, ref evt); - // Occurs when shutdown is completed for the send side. - // This only happens for shutdown on sending, not receiving - // Receive shutdown can only be abortive. - case QUIC_STREAM_EVENT_TYPE.SEND_SHUTDOWN_COMPLETE: - return HandleEventSendShutdownComplete(state, ref evt); // Shutdown for both sending and receiving is completed. case QUIC_STREAM_EVENT_TYPE.SHUTDOWN_COMPLETE: return HandleEventShutdownComplete(state, ref evt); @@ -974,26 +914,6 @@ namespace System.Net.Quic.Implementations.MsQuic return MsQuicStatusCodes.Success; } - private static uint HandleEventSendShutdownComplete(State state, ref StreamEvent evt) - { - bool shouldComplete = false; - lock (state) - { - if (state.ShutdownWriteState == ShutdownWriteState.None) - { - state.ShutdownWriteState = ShutdownWriteState.Finished; - shouldComplete = true; - } - } - - if (shouldComplete) - { - state.ShutdownWriteCompletionSource.SetResult(); - } - - return MsQuicStatusCodes.Success; - } - private static uint HandleEventShutdownComplete(State state, ref StreamEvent evt) { StreamEventDataShutdownComplete shutdownCompleteEvent = evt.Data.ShutdownComplete; @@ -1004,7 +924,6 @@ namespace System.Net.Quic.Implementations.MsQuic } bool shouldReadComplete = false; - bool shouldShutdownWriteComplete = false; bool shouldShutdownComplete = false; lock (state) @@ -1023,12 +942,6 @@ namespace System.Net.Quic.Implementations.MsQuic state.ReadState = ReadState.ReadsCompleted; } - if (state.ShutdownWriteState == ShutdownWriteState.None) - { - state.ShutdownWriteState = ShutdownWriteState.Finished; - shouldShutdownWriteComplete = true; - } - if (state.ShutdownState == ShutdownState.None) { state.ShutdownState = ShutdownState.Finished; @@ -1041,11 +954,6 @@ namespace System.Net.Quic.Implementations.MsQuic state.ReceiveResettableCompletionSource.Complete(0); } - if (shouldShutdownWriteComplete) - { - state.ShutdownWriteCompletionSource.SetResult(); - } - if (shouldShutdownComplete) { state.ShutdownCompletionSource.SetResult(); @@ -1389,7 +1297,6 @@ namespace System.Net.Quic.Implementations.MsQuic bool shouldCompleteRead = false; bool shouldCompleteSend = false; - bool shouldCompleteShutdownWrite = false; bool shouldCompleteShutdown = false; lock (state) @@ -1406,12 +1313,6 @@ namespace System.Net.Quic.Implementations.MsQuic } state.SendState = SendState.ConnectionClosed; - if (state.ShutdownWriteState == ShutdownWriteState.None) - { - shouldCompleteShutdownWrite = true; - } - state.ShutdownWriteState = ShutdownWriteState.ConnectionClosed; - if (state.ShutdownState == ShutdownState.None) { shouldCompleteShutdown = true; @@ -1431,12 +1332,6 @@ namespace System.Net.Quic.Implementations.MsQuic ExceptionDispatchInfo.SetCurrentStackTrace(GetConnectionAbortedException(state))); } - if (shouldCompleteShutdownWrite) - { - state.ShutdownWriteCompletionSource.SetException( - ExceptionDispatchInfo.SetCurrentStackTrace(GetConnectionAbortedException(state))); - } - if (shouldCompleteShutdown) { state.ShutdownCompletionSource.SetException( @@ -1499,14 +1394,6 @@ namespace System.Net.Quic.Implementations.MsQuic Closed } - private enum ShutdownWriteState - { - None = 0, - Canceled, - Finished, - ConnectionClosed - } - private enum ShutdownState { None = 0, diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs index 2be277a6125..a90bcf9bb89 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs @@ -37,8 +37,6 @@ namespace System.Net.Quic.Implementations internal abstract ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default); - internal abstract ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default); - internal abstract ValueTask ShutdownCompleted(CancellationToken cancellationToken = default); internal abstract void Shutdown(); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs index e1724eee535..778a4cd1efa 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs @@ -99,8 +99,6 @@ namespace System.Net.Quic public ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffers, endStream, cancellationToken); - public ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) => _provider.ShutdownWriteCompleted(cancellationToken); - public ValueTask ShutdownCompleted(CancellationToken cancellationToken = default) => _provider.ShutdownCompleted(cancellationToken); public void Shutdown() => _provider.Shutdown(); From 1fe60d3221614bfccb4a931c424b394bb21692c0 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Wed, 21 Jul 2021 15:38:27 -0700 Subject: [PATCH 729/926] Fix an issue where we are not injecting more HTTP2 connections when we should (#56062) * fix an issue where we are not injecting more HTTP2 connections when we should Co-authored-by: Geoffrey Kizer Co-authored-by: Stephen Toub --- .../SocketsHttpHandler/HttpConnectionPool.cs | 3 + .../FunctionalTests/SocketsHttpHandlerTest.cs | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index c624cbd677e..fc5149397a3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -1822,6 +1822,9 @@ namespace System.Net.Http if (NetEventSource.Log.IsEnabled()) connection.Trace("Dequeued waiting HTTP/2 request."); } + // Since we only inject one connection at a time, we may want to inject another now. + CheckForHttp2ConnectionInjection(); + if (_disposed) { // If the pool has been disposed of, we want to dispose the connection being returned, as the pool is being deactivated. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index b5988fafed3..a95168ec63e 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -2118,6 +2118,63 @@ namespace System.Net.Http.Functional.Tests } } + [ConditionalFact(nameof(SupportsAlpn))] + public async Task Http2_MultipleConnectionsEnabled_ManyRequestsEnqueuedSimultaneously_SufficientConnectionsCreated() + { + // This is equal to Http2Connection.InitialMaxConcurrentStreams, which is the limit we impose before we have received the peer's initial SETTINGS frame. + // Setting it to this value avoids any complexity that would occur from having to retry requests if the actual limit from the peer is lower. + const int MaxConcurrentStreams = 100; + + const int ConnectionCount = 3; + + // Just enough to force the third connection to be created. + const int RequestCount = (ConnectionCount - 1) * MaxConcurrentStreams + 1; + + using Http2LoopbackServer server = Http2LoopbackServer.CreateServer(); + server.AllowMultipleConnections = true; + + using SocketsHttpHandler handler = CreateHandler(); + using HttpClient client = CreateHttpClient(handler); + + List> sendTasks = new List>(); + for (int i = 0; i < RequestCount; i++) + { + var sendTask = client.GetAsync(server.Address); + sendTasks.Add(sendTask); + } + + List<(Http2LoopbackConnection connection, int streamId)> acceptedRequests = new List<(Http2LoopbackConnection connection, int streamId)>(); + + using Http2LoopbackConnection c1 = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = SettingId.MaxConcurrentStreams, Value = 100 }); + for (int i = 0; i < MaxConcurrentStreams; i++) + { + (int streamId, _) = await c1.ReadAndParseRequestHeaderAsync(); + acceptedRequests.Add((c1, streamId)); + } + + using Http2LoopbackConnection c2 = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = SettingId.MaxConcurrentStreams, Value = 100 }); + for (int i = 0; i < MaxConcurrentStreams; i++) + { + (int streamId, _) = await c2.ReadAndParseRequestHeaderAsync(); + acceptedRequests.Add((c2, streamId)); + } + + using Http2LoopbackConnection c3 = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = SettingId.MaxConcurrentStreams, Value = 100 }); + (int finalStreamId, _) = await c3.ReadAndParseRequestHeaderAsync(); + acceptedRequests.Add((c3, finalStreamId)); + + foreach ((Http2LoopbackConnection connection, int streamId) request in acceptedRequests) + { + await request.connection.SendDefaultResponseAsync(request.streamId); + } + + foreach (Task t in sendTasks) + { + HttpResponseMessage response = await t; + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + } + [ConditionalFact(nameof(SupportsAlpn))] [ActiveIssue("https://github.com/dotnet/runtime/issues/45204")] public async Task Http2_MultipleConnectionsEnabled_InfiniteRequestsCompletelyBlockOneConnection_RemaningRequestsAreHandledByNewConnection() From 016851a183c66333f3087312b458452a90154726 Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Wed, 21 Jul 2021 15:39:51 -0700 Subject: [PATCH 730/926] reenable ConnectTimeout_PlaintextStreamFilterTimesOut_Throws test (#55982) * reenable ConnectTimeout_PlaintextStreamFilterTimesOut_Throws test Co-authored-by: Geoffrey Kizer --- .../SocketsHttpHandlerTest.Cancellation.cs | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index e6fb25a2417..e232ee94419 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -79,11 +79,8 @@ namespace System.Net.Http.Functional.Tests } [OuterLoop] - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55931")] - [InlineData(true)] - [InlineData(false)] - public async Task ConnectTimeout_PlaintextStreamFilterTimesOut_Throws(bool useSsl) + [Fact] + public async Task ConnectTimeout_PlaintextStreamFilterTimesOut_Throws() { if (UseVersion == HttpVersion.Version30) { @@ -91,33 +88,20 @@ namespace System.Net.Http.Functional.Tests return; } - var releaseServer = new TaskCompletionSource(); await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using (var handler = CreateHttpClientHandler()) using (var invoker = new HttpMessageInvoker(handler)) { - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; var socketsHandler = GetUnderlyingSocketsHttpHandler(handler); socketsHandler.ConnectTimeout = TimeSpan.FromSeconds(1); + socketsHandler.ConnectCallback = (context, token) => new ValueTask(new MemoryStream()); socketsHandler.PlaintextStreamFilter = async (context, token) => { await Task.Delay(-1, token); return null; }; await ValidateConnectTimeout(invoker, uri, 500, 85_000); - - releaseServer.SetResult(); } - }, - async server => - { - try - { - await server.AcceptConnectionAsync(async c => - { - await releaseServer.Task; - }); - } - catch (Exception) { } // Eat exception from client timing out - }, options: new GenericLoopbackOptions() { UseSsl = useSsl }); + }, server => Task.CompletedTask, // doesn't actually connect to server + options: new GenericLoopbackOptions() { UseSsl = false }); } [OuterLoop("Incurs significant delay")] From 2ccf787faaf5798253a66d81be716798c751175f Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Madero Date: Wed, 21 Jul 2021 15:59:52 -0700 Subject: [PATCH 731/926] Make System.Drawing.Common throw on Unix unless a config switch is set (#55962) * Make System.Drawing.Common throw on Unix unless a config switch is set --- .../Directory.Build.props | 7 ++++--- .../src/Resources/Strings.resx | 5 ++++- .../src/System.Drawing.Common.csproj | 7 ++++--- .../src/System/Drawing/LibraryResolver.cs | 3 ++- .../Drawing/LocalAppContextSwitches.Unix.cs | 20 +++++++++++++++++++ ....cs => LocalAppContextSwitches.Windows.cs} | 0 .../tests/BitmapTests.cs | 13 ++++++++++++ .../tests/runtimeconfig.template.json | 5 +++++ .../tests/runtimeconfig.template.json | 5 +++++ .../tests/runtimeconfig.template.json | 5 +++++ .../tests/runtimeconfig.template.json | 5 +++++ 11 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Unix.cs rename src/libraries/System.Drawing.Common/src/System/Drawing/{LocalAppContextSwitches.cs => LocalAppContextSwitches.Windows.cs} (100%) create mode 100644 src/libraries/System.Drawing.Common/tests/runtimeconfig.template.json create mode 100644 src/libraries/System.Resources.Extensions/tests/runtimeconfig.template.json create mode 100644 src/libraries/System.Resources.ResourceManager/tests/runtimeconfig.template.json create mode 100644 src/libraries/System.Runtime.Serialization.Formatters/tests/runtimeconfig.template.json diff --git a/src/libraries/System.Drawing.Common/Directory.Build.props b/src/libraries/System.Drawing.Common/Directory.Build.props index addd07be3bb..9ef62c61e92 100644 --- a/src/libraries/System.Drawing.Common/Directory.Build.props +++ b/src/libraries/System.Drawing.Common/Directory.Build.props @@ -2,8 +2,7 @@ Open - true - browser + windows Provides access to GDI+ graphics functionality. Commonly Used Types: @@ -12,6 +11,8 @@ System.Drawing.BitmapData System.Drawing.Brush System.Drawing.Font System.Drawing.Graphics -System.Drawing.Icon +System.Drawing.Icon + +Unix support is disabled by default. See https://aka.ms/systemdrawingnonwindows for more information. \ No newline at end of file diff --git a/src/libraries/System.Drawing.Common/src/Resources/Strings.resx b/src/libraries/System.Drawing.Common/src/Resources/Strings.resx index 969bd00ec5e..2d3d8134aab 100644 --- a/src/libraries/System.Drawing.Common/src/Resources/Strings.resx +++ b/src/libraries/System.Drawing.Common/src/Resources/Strings.resx @@ -353,6 +353,9 @@ System.Drawing is not supported on this platform. + + System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information. + Defines an object that sends output to a printer. @@ -464,4 +467,4 @@ System.Drawing.Common is not supported on this platform. - \ No newline at end of file + diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index ff707877c2d..f571be1d8a5 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -170,6 +170,8 @@ Link="Common\Interop\Windows\User32\Interop.LOGFONT.cs" /> + - + @@ -243,8 +245,6 @@ - + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs index 8f10899f850..cb4bdf3c73b 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs @@ -27,7 +27,8 @@ namespace System.Drawing internal static void EnsureRegistered() { - // dummy call to trigger the static constructor + if (!LocalAppContextSwitches.EnableUnixSupport) + throw new PlatformNotSupportedException(SR.PlatformNotSupported_Unix); } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Unix.cs new file mode 100644 index 00000000000..44fe88b2f6e --- /dev/null +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Unix.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_enableUnixSupport; + public static bool EnableUnixSupport + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(@"System.Drawing.EnableUnixSupport", ref s_enableUnixSupport); + } + } + } +} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Windows.cs similarity index 100% rename from src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.cs rename to src/libraries/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.Windows.cs diff --git a/src/libraries/System.Drawing.Common/tests/BitmapTests.cs b/src/libraries/System.Drawing.Common/tests/BitmapTests.cs index d861dbe0815..0b15dfc7821 100644 --- a/src/libraries/System.Drawing.Common/tests/BitmapTests.cs +++ b/src/libraries/System.Drawing.Common/tests/BitmapTests.cs @@ -29,6 +29,7 @@ using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -44,6 +45,18 @@ namespace System.Drawing.Tests yield return new object[] { "16x16_nonindexed_24bit.png", 16, 16, PixelFormat.Format24bppRgb, ImageFormat.Png }; } + [PlatformSpecific(TestPlatforms.AnyUnix)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void UnixSupportDisabledThrows() + { + RemoteExecutor.Invoke(() => + { + AppContext.SetSwitch("System.Drawing.EnableUnixSupport", false); + TypeInitializationException exception = Assert.Throws(() => new Bitmap(100, 100)); + Assert.IsType(exception.InnerException); + }).Dispose(); + } + [ConditionalTheory(Helpers.IsDrawingSupported)] [MemberData(nameof(Ctor_FilePath_TestData))] public void Ctor_FilePath(string filename, int width, int height, PixelFormat pixelFormat, ImageFormat rawFormat) diff --git a/src/libraries/System.Drawing.Common/tests/runtimeconfig.template.json b/src/libraries/System.Drawing.Common/tests/runtimeconfig.template.json new file mode 100644 index 00000000000..e3ad204dd9e --- /dev/null +++ b/src/libraries/System.Drawing.Common/tests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Drawing.EnableUnixSupport": true + } +} \ No newline at end of file diff --git a/src/libraries/System.Resources.Extensions/tests/runtimeconfig.template.json b/src/libraries/System.Resources.Extensions/tests/runtimeconfig.template.json new file mode 100644 index 00000000000..e3ad204dd9e --- /dev/null +++ b/src/libraries/System.Resources.Extensions/tests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Drawing.EnableUnixSupport": true + } +} \ No newline at end of file diff --git a/src/libraries/System.Resources.ResourceManager/tests/runtimeconfig.template.json b/src/libraries/System.Resources.ResourceManager/tests/runtimeconfig.template.json new file mode 100644 index 00000000000..e3ad204dd9e --- /dev/null +++ b/src/libraries/System.Resources.ResourceManager/tests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Drawing.EnableUnixSupport": true + } +} \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/runtimeconfig.template.json b/src/libraries/System.Runtime.Serialization.Formatters/tests/runtimeconfig.template.json new file mode 100644 index 00000000000..e3ad204dd9e --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Drawing.EnableUnixSupport": true + } +} \ No newline at end of file From 3f827217f4dbd363608a9a0b6094f700361a8fa7 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 21 Jul 2021 16:30:00 -0700 Subject: [PATCH 732/926] Use Enabled/DisabledFeatureSwitches metadata in TrimmingTests (#56107) --- .../tests/TrimmingTests/System.Runtime.TrimmingTests.proj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index 10034f2e194..4c837c0deb9 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -12,10 +12,10 @@ - --feature System.Globalization.Invariant false + System.Globalization.Invariant - --feature System.Globalization.Invariant true --feature System.Globalization.PredefinedCulturesOnly true + System.Globalization.Invariant;System.Globalization.PredefinedCulturesOnly - --feature System.Resources.UseSystemResourceKeys true + System.Resources.UseSystemResourceKeys System.Runtime.InteropServices.BuiltInComInterop.IsSupported From b2264cb9cdd625fc44516a34ef3dd6b1df981e20 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 21 Jul 2021 16:34:07 -0700 Subject: [PATCH 733/926] Lsra heuristic tuning experiment report (#56103) * lsra tuning experiments * remove the trailing space * Review comment --- .../coreclr/jit/lsra-heuristic-tuning.md | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 docs/design/coreclr/jit/lsra-heuristic-tuning.md diff --git a/docs/design/coreclr/jit/lsra-heuristic-tuning.md b/docs/design/coreclr/jit/lsra-heuristic-tuning.md new file mode 100644 index 00000000000..71b3ff36af2 --- /dev/null +++ b/docs/design/coreclr/jit/lsra-heuristic-tuning.md @@ -0,0 +1,310 @@ +- [Background](#background) +- [Register selection heuristics](#register-selection-heuristics) +- [Impact measurement](#impact-measurement) +- [Genetic Algorithm](#genetic-algorithm) +- [Experiments](#experiments) + * [Setup](#setup) + * [Outcome](#outcome) +- [Conclusion](#conclusion) + +## Background + +RyuJIT's implements [linear scan register allocation](https://en.wikipedia.org/wiki/Register_allocation#Linear_scan) (LSRA) algorithm to perform the register assignment of generated code. During register selection, LSRA has various heuristics (17 to be precise) to pick the best register candidate at a given point. Each register candidate falls in one of the two categories. Either they do not contain any variable value and so are "free" to get assigned to hold a variable value. Otherwise, they already hold some variable value and hence, are "busy". If one of the busy registers is selected during assignment, the value it currently holds needs to be first stored into memory (also called "spilling the variable") before they are assigned to something else. RyuJIT's LSRA has the heuristics (14 of them) to pick one of the free registers first, and if none found, has heuristics (4 of them) to select one of the busy registers. Busy register is selected depending on which register is cheaper to spill. + +We noticed that it is not always beneficial to give preference to free register candidates during register selection. Sometimes, it is better to pick a busy register and retain the free register for the future reference points that are part of hot code path. See [the generated code](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABABgAJiBGAOgCUBXAOwwEt8YaBhCfAB1YAbGFADKIgG6swMXAG4AsAChlxAMyUATOS7kA3svJHyygNoBZGBgAWEACYBJfoIAUlm/ad9BAeT5sIJlwaADkIByZBViZogHMASgBdVSokcmiMcgBxKzpsJjteF3j9ZQBIYgB2clJFJQBfZTN3W0dnNytWr19/VkDgsIiomKYE5KVqNOIUcgAFKAyXDPIEEoMlMoB6TcoqAE4XVbrGlQnUyhnRbGcYAEFi0o2JbChybHIAXmzc/ML8YrqZWer2An2+GDyBSK8UBwPIYDBOQhv2hsJe5DsiJ+UP+MPKcJgWOROIB+PRADMiZC/qSnujYlSUbi0a9rIySXiNuToOQXHCEGDaityAAechUUhCgDUUtWjzKQPRCHeXyR1NR5UVrwQoNV2JpnIV/IReuJBsBWpWmNN6uZmv5hJtTNpRqVlKdHIt80WysNZW9LEOwD9AYwhzAIYWgYQdkjPpgcej5MNJ39UbD2ENoZcwcB2YjefTLljhcWCdLgeTFbDCWrLmsnJO9SAA===) taken from [dotnet/runtime Issue#8846](https://github.com/dotnet/runtime/issues/8846). In this example, free registers are allocated to the variables that are out of the for-loop. During the register assignment for variables inside the loop, no free registers are available, and the algorithm spills a busy register to store their value. Astonishingly, it picks the same register for all the variables inside the loop and spill the previous variable values repeatedly. Our understanding is that it happens because of the ordering of heuristics in which we perform register selection. Perhaps, instead of having a fixed heuristics order, we should tweak the order to *sometimes* select busy registers first, before selecting from the pool of free registers. That was the inception of the idea of tuning the register selection heuristics described in [dotnet/runtime Issue# 43318](https://github.com/dotnet/runtime/issues/43318) and we wanted to conduct experiments to understand if we can do better register selection using different criteria. In this document, we will go over in detail to understand what made us pick genetic algorithm to do this experiment and what were the outcome of it. + +## Register selection heuristics + +Below are the heuristics implemented in RyuJIT to select a register: + +| Shorthand | Name | Description | +|-----------|----------------------|---------------------------------------------------------------------------------------------------------| +| A | `FREE` | Not currently assigned to an *active* interval. | +| B | `CONST_AVAILABLE` | A constant value that is already available in a register. | +| C | `THIS_ASSIGNED` | Register already assigned to the current interval. | +| D | `COVERS` | Covers the interval's current lifetime. | +| E | `OWN_PREFERENCE` | Set of preferred registers of current interval. | +| F | `COVERS_RELATED` | Set of preferred registers of interval that is related to the current interval and covers the lifetime. | +| G | `RELATED_PREFERENCE` | Set of preferred registers of interval that is related to the current interval. | +| H | `CALLER_CALLEE` | Caller or callee-saved registers. | +| I | `UNASSIGNED` | Not currently assigned to any active or inactive interval. | +| J | `COVERS_FULL` | Covers the interval's current lifetime until the end. | +| K | `BEST_FIT` | Available range is the closest match to the full range of the interval | +| L | `IS_PREV_REG` | Register was previously assigned to the current interval | +| M | `REG_ORDER` | Tie-breaker. Just pick the 1st available "free" register. | +| N | `SPILL_COST` | Lowest spill cost of all the candidates. | +| O | `FAR_NEXT_REF` | It has farther next reference than the best candidate so far. | +| P | `PREV_REG_OPT` | The previous reference of the current assigned interval was optional. | +| Q | `REG_NUM` | Tie-breaker. Just pick the 1st available "busy" register. | + +Heuristic `A` thru `M` are for selecting one of the free registers, while `N` thru `Q` are for selecting one of the busy registers. A simple demonstration of how heuristic selection worked earlier is shown below. We start with free candidates and for each heuristic, narrow those candidates. Whenever, we see that there are more than one registers to pick from, we keep trying heuristics (in the above order) until a point when there is just one register left. If we don't find any register, we continue our search using heuristic `N` to find one of the busy registers that can be spilled. + +```c# +registerCandidates = 0; // bit-mask of all registers + +LinearScan::allocateReg(RefPostion refPosition, Inteval* interval) +{ + bool found = false; + registerCandidates = allFreeCandidates(); + + if (!found) { + found = applyHeuristics(FREE, FREE_Candidates()); + } + + if (!found) { + found = applyHeuristics(CONST_AVAILABLE_Candidates()); + } + + ... + + if (!found) { + found = applyHeuristics(REG_ORDER_Candidates()); + } + + // No free register was available, try to select one of + // the busy register + registerCandidates = allBusyCandidates(); + if (!found) { + found = applyHeuristics(SPILL_COST_Candidates()); + } + + if (!found) { + found = applyHeuristics(FAR_NEXT_REF_Candidates()); + } + ... +} + +// Filters the register candidates and returns true only there +// is one candidate. +bool applyHeuristics(selected_candidates) +{ + filtered_candidates = registerCandidates & selected_candidates; + if (filtered_candidates != 0) { + registerCandidates = filtered_candidates; + return isSingleRegister(registerCandidates); + } + return false; +} + +``` + +If we wanted to change the order of heuristics, we would have to update above code to rearrange the portion of heuristics we apply. To experiment with different heuristics ordering, it is not feasible to do such refactoring for every combination. After doing some research on which design pattern to pick for such problems, we went the old school way and moved the individual heuristics code in its own method (marked with `__forceinline`, to eliminate the throughput impact of refactoring changes). We could use function pointer to invoke one of these methods in any order we wanted. The last bit was an ability to add a way for user to specify heuristic order they want to try. We assigned a single letter to each heuristic (`Shorthand` column in above table) and we exposed `COMPlus_JitLsraOrdering` environment variable to specify the ordering. The default ordering is `"ABCDEFGHIJKLMNOPQ"` (the current order), but if given something else like `"PEHDCGAIJNLKOBFMQ"`, it would apply heuristic in that order. In this example, heuristic corresponding to `P` is `PREV_REG_OPT` and thus would apply busy register heuristics first, followed by `OWN_PREFERENCE`, `CALLER_CALLEE` and so forth. As you notice, now we will be able to apply the busy register heuristics before applying the ones for free registers. + +After stitching all this together, the refactored code looked like this: + +```c# + +typedef void (RegisterSelection::*HeuristicFn)(); +HashTable ScoreMappingTable = { + {'A', try_FREE}, + {'B', try_CONST_AVAILABLE}, + ... + {'Q', try_REG_NUM} +}; + +LinearScan::allocateReg(RefPostion refPosition, Inteval* interval) +{ + char *ordering = Read_COMPlus_LsraOrdering(); + HeuristicFn fn; + for (char order in ordering) { + if (ScoreMappingTable->Lookup(order, &fn)) { + bool found = (this->*fn)(); + if (found) { + break; + } + } + } +} + +bool LinearScan::try_FREE() { + ... + return applyHeuristics(); +} +... +bool LinearScan::try_CONST_AVAILABLE() { + ... + return applyHeuristics(); +} +... +bool LinearScan::try_REG_NUM() { + ... + return applyHeuristics(); +} +``` + +[dotnet/runtime #52832](https://github.com/dotnet/runtime/pull/52832) contains all the refactoring changes that are described above. + +## Impact measurement + +Now that rearranging the heuristic ordering is possible with `COMPlus_JitLsraOrdering`, we decided to measure the impact of the reordering by running [superpmi](https://github.com/dotnet/runtime/blob/e063533eb79eace045f43b41980cbed21c8d7365/src/coreclr/ToolBox/superpmi/readme.md) tool. `superpmi` tool JITs all the methods of a given assembly file (`*.dll` or `*.exe`) without executing the generated machine code. Given two versions of `clrjit.dll` (RyuJIT binary), it also has an ability to perform the comparison of generated code and reporting back the number of methods that got improved/regressed in terms of `CodeSize` (machine code size), `PerfScore` (instruction latency/throughput measurements), `InstructionCount` (number of instructions present), etc. We picked `PerfScore` metrics because that accurately includes the cost of register spilling. If LSRA doesn't come up with optimal register choice, we would see several `mov` instructions that load/store into memory and that would decrease the throughput, increase the latency, and hence lower the `PerfScore`. If the spilling happens inside a loop, `PerfScore` metrics accounts for that by considering the product of loop block weights and `PerfScore`. Thus, our goal would be to reduce the `PerfScore` as much possible, lower the `PerfScore`, better is the code we generated. The baseline for the comparison was the default ordering, and we wanted to compare it with an ordering specified in `COMPlus_JitLsraOrdering`. We could specify any combination of sequence `A` thru `Q` and tweak the LSRA algorithm to apply a different heuristics order. But since there are 17 heuristics, there would be **355,687,428,096,000** (17!) possibilities to try out and it will not be practical to do so. We ought to find a better way! + +## Genetic Algorithm + +[Genetic algorithm](https://en.wikipedia.org/wiki/Genetic_algorithm) is the perfect solution to solve these kind of problems. For those who are not familiar, here is a quick summary - The algorithm starts with a community that has few candidates whose fitness score is predetermined. Each candidate is made up of sequence of genes and all candidates have same number of genes in them. The algorithm picks a pair of fit candidates (parents) and mutate their genes to produce offsprings. The algorithm calculates the fitness of the new offsprings and add them (along with the fitness score) back to the community pool. As the community evolves, more and more candidates who has fitness score equivalent or better than the initial population are added to the community. Of course, the community cannot grow infinitely, so the least fit candidates die. When there are no more candidates that are fit than the fittest candidate, the algorithm stops, giving us a set of fit candidates. + +This can be perfectly mapped to the heuristic selection ordering problem. We want to start with `"ABCDEFGHIJKLMNOPQ"` (default selection order) and each letter in this combination can be represented as a gene. Genetic algorithm would mutate the gene to produce a different order say `"ABMCDEFGHIKLNJOPQ"` and we will set that value in `COMPlus_JitLsraOrdering` variable. We would then run `superpmi.py` to produce the generated code and compare the `PerfScore` with that of the one produced by the default order. `PerfScore` represents the fitness, lower the value of that metric, more fit is the corresponding candidate, in our case, better is the heuristic ordering. + +Below is the pseudo code of genetic algorithm that we experimented with to find optimal heuristic ordering. + +```c# +// Maximum population per generation +int MaxPopulation = 100; + +HashMap Community = new HashMap(); +HashMap NextGen = new HashMap(); + + +void GeneticAlgorithm() { + PopulateCommunity(); + + do { + // new generation + NextGen = new HashMap(); + candidateCount = 0; + + while(candidateCount++ < MaxPopulation) { + // Use tournament selection method to pick + // 2 candidates from "Community". + // https://en.wikipedia.org/wiki/Tournament_selection + (parent1, parent2) = DoSelection(); + + // Mutate genes of parent1 and parent2 to produce + // 2 offsprings + (offspring0, offspring1) = MutateGenes(parent1, parent2) + + // Add offsprings to the community + AddNewOffspring(offspring0) + AddNewOffspring(offspring1) + } + Community = NextGen; + + // Loop until there are unique candidates are being produced in the + // community + + } while (uniqueCandidates); + +} + +// Populate the community with random candidates +void PopulateCommunity() { + candidateCount = 0; + while(candidateCount < MaxPopulation) { + newCandidate = GetRandomCombination("ABCDEFGHIJKLMNOPQ") + AddNewOffspring(newCandidate) + } +} + +// Trigger superpmi tool and read back the PerfScore +void ComputeFitness(candidate) { + perfScore = exec("superpmi.py asmdiffs -base_jit_path default\clrjit.dll -diff_jit_path other\clrjit.dll -diff_jit_option JitLsraOrdering=" + candidate) + return perfScore +} + +// Compuate fitness for both offsprings +// and add them to the community +void AddNewOffspring(candidate) { + Community[candidate] = ComputeFitness(candidate) + + // Evict less fit candidate + if (Community.Count > MaxPopulation) { + weakCandidate = CandidateWithHighestPerfScore(Community); + Community.Remove(weakCandidate) + } +} + +// Perform crossover and mutation techniques +void MutateGenes(offspring0, offspring1) { + assert(offspring0.Length == offspring1.Length) + + // crossover + crossOverPoint = random(0, offspring0.Length) + i = 0 + while (i++ < crossOverPoint) { + char c = offspring0[i] + offspring0[i] = offspring1[i] + offspring1[i] = c + } + + // mutation + randomIndex = random(0, offspring0.Length) + char c = offspring0[randomIndex] + offspring0[randomIndex] = offspring1[randomIndex] + offspring1[randomIndex] = c + + return offspring0, offspring1 +} +``` + +With genetic algorithm in place, we were ready to perform some experiments to find an optimal heuristic order. + +## Experiments + +With `superpmi`, we have an ability to run JIT against all the methods present in .NET libraries and [Microbenchmarks](https://github.com/dotnet/performance/tree/main/src/Benchmarks/micro). We also need to conduct this experiment for all OS/architecture that we support - Windows/x64, Windows/arm64, Linux/x64, Linux/arm and Linux/arm64. + +### Setup + +To conduct experiments, we made few changes to the way superpmi gathers `PerfScore` and reports it back. + +1. `superpmi.exe` was modified to aggregate **relative** `PerfScore` difference of code generated by default and modified LSRA ordering. When `superpmi.exe` is run in parallel (which is by default), this number was reported back on the console by each parallel process. +2. `superpmi.py` was modified to further aggregate the relative `PerfScore` differences of parallel `superpmi.exe` processes and report back the final relative `PerfScore` difference. +3. LSRA has many asserts throughout the codebase. They assume that during register selection, all the free registers are tried first before checking for busy registers. Since we wanted to understand the impact of preferring busy registers as well, we had to disable those asserts. +4. `superpmi.exe asmdiffs` takes two versions of `clrjit.dll` that you want to compare. Both must be from different location. In our case, we only wanted to experiment with different heuristic ordering by passing different values for `COMPlus_JitLsraOrdering`, we made a copy of `clrjit.dll` -> `copy_clrjit.dll` and passed various ordering to the copied `copy_clrjit.dll`. + +Here is the sample invocation of `superpmi.py` that genetic algorithm invoked to get the `PerfScore` (fitness score) of each experimented ordering: + +``` +python superpmi.py asmdiffs -f benchmarks -base_jit_path clrjit.dll -diff_jit_path copy_clrjit.dll -target_os windows -target_arch x64 -error_limit 10 -diff_jit_option JitLsraOrdering=APCDEGHNIOFJKLBMQ -log_file benchmarks_APCDEGHNIOFJKLBMQ.log +``` + +All the above changes are in the private branch [lsra-refactoring branch](https://github.com/kunalspathak/runtime/tree/lsra-refactoring). + +### Outcome + +Below are the heuristic ordering that genetic algorithm came up with for different configuration (scenarios/OS/architectures). The `PerfScore` column represent the aggregate of relative difference of `PerfScore` of all the methods. We preferred relative difference rather than absolute difference of `PerfScore` because we didn't want a dominant method's numbers hide the impact of other smaller methods. + +| Configuration | Ordering | PerfScore | +|-------------------------|--------------------|-------------| +| windows-x64 Benchmarks | `EHPDGAJCBNKOLFIMQ` | -36.540712 | +| windows-x64 Libraries | `PEHDCGAIJNLKOBFMQ` | -271.749901 | +| windows-x86 Benchmarks | `EHDCFPGJBIALNOKMQ` | -73.004577 | +| windows-x86 Libraries | `APCDEGHNIOFJKLBMQ` | -168.335079 | +| Linux-x64 Benchmarks | `HGIDJNLCPOBKAEFMQ` | -96.966704 | +| Linux-x64 Libraries | `HDGAECNIPLBOFKJMQ` | -391.835935 | +| Linux-arm64 Libraries | `HECDBFGIANLOKJMPQ` | -249.900161 | + +As seen from the table, there are lot of better ordering than the default `"ABCDEFGHIJKLMNOPQ"`, which if used, can give us better register selection and hence, better performance. But we can also see that not all ordering that genetic algorithm came up with are same for all configurations. We wanted to find a common and similar ordering that can benefit all the scenarios across multiple platforms. As a last step of experiment, we tried to apply each of the best ordering that we had to other configurations and see how they perform. For example, `"EHPDGAJCBNKOLFIMQ"` is the most optimal ordering for windows/x64/Benchmarks configuration and we wanted to evaluate if that ordering could also be beneficial to Linux/arm64/Libraries. Likewise, for `"PEHDCGAIJNLKOBFMQ"` (optimal ordering for windows/x64/Libraries) and so forth. + +Below table shows the compiled data of `PerfScore` that we get when we applied best ordering of individual configuration to other configurations. Each row contains a configuration along with the optimal ordering that genetic algorithm came up with. The columns represent the `PerfScore` we get if we apply the optimal ordering to the configuration listed in the column title. + +| Configuration | Optimal Ordering | Linux-x64 Benchmarks | windows-x64 Benchmarks | windows-arm64 Benchmarks | Linux-x64 Libraries | Linux-arm64 Libraries | windows-x64 Libraries | windows-arm64 Libraries.pmi | windows-x86 Benchmarks | Linux-arm Libraries | windows-x86 Libraries | +|------------------------|-------------------|-----------------------|------------------------|--------------------------|-----------------------|-----------------------|-----------------------|-----------------------------|------------------------|-----------------------|-----------------------| +| windows-x64 Benchmarks | `EHPDGAJCBNKOLFIMQ` | -83.496405 | **-36.540712** | -19.09969 | -340.009195 | -103.340802 | -265.397122 | -113.718544 | -62.126579 | 11292.33497 | 18.510854 | +| windows-x64 Libraries | `PEHDCGAIJNLKOBFMQ` | -85.572973 | -35.853492 | -19.07247 | -355.615641 | -103.028599 | **-271.749901** | -114.1154 | -70.087852 | 31974.87698 | -46.803569 | +| windows-x86 Benchmarks | `EHDCFPGJBIALNOKMQ` | **-101.903471** | -19.844343 | -41.041839 | **-419.933377** | -247.95955 | -179.127655 | -265.675453 | **-73.004577** | 10679.36843 | -136.780091 | +| windows-x86 Libraries | `APCDEGHNIOFJKLBMQ` | -26.907257 | -0.284718 | -30.144657 | -164.340576 | -220.351459 | -73.413256 | -232.256476 | -10.25733 | 31979.07983 | **-168.335079** | +| linux-x64 Benchmarks | `HGIDJNLCPOBKAEFMQ` | -96.966704 | -9.29483 | -50.215283 | -361.159848 | -221.622609 | -64.308995 | -244.127555 | 13.188704 | 8392.714652 | 397.994465 | +| linux-x64 Libraries | `HDGAECNIPLBOFKJMQ` | -97.682606 | -13.882952 | -51.929281 | -391.835935 | -240.63813 | -101.495244 | -262.746033 | -22.621316 | 8456.327283 | 165.982045 | +| linux-arm64 Libraries | `HECDBFGIANLOKJMPQ` | -97.259922 | -11.159774 | **-54.424627** | -330.340402 | **-249.900161** | -52.359275 | **-270.482763** | -35.304525 | **2404.874376** | 125.707741 | +| | Max PerfScore | **`EHDCFPGJBIALNOKMQ`** | **`EHPDGAJCBNKOLFIMQ`** | **`HECDBFGIANLOKJMPQ`** | **`HDGAECNIPLBOFKJMQ`** | **`HECDBFGIANLOKJMPQ`** | **`PEHDCGAIJNLKOBFMQ`** | **`HECDBFGIANLOKJMPQ`** | **`EHDCFPGJBIALNOKMQ`** | **`HECDBFGIANLOKJMPQ`** | **`APCDEGHNIOFJKLBMQ`** | + + The last row in the above table tells the best ordering for the configuration (of that column) out of optimal orderings of all configurations. Below table summarizes 1st and 2nd best ordering for individual configuration. + +| Configuration | 1st best | 2nd best | +|--------------------------|----------------------|----------------------| +| windows-x64 Benchmarks | `EHPDGAJCBNKOLFIMQ` | `PEHDCGAIJNLKOBFMQ` | +| windows-x64 Libraries | `PEHDCGAIJNLKOBFMQ` | `EHPDGAJCBNKOLFIMQ` | +| windows-x86 Benchmarks | `EHDCFPGJBIALNOKMQ` | `PEHDCGAIJNLKOBFMQ` | +| windows-x86 Libraries | `APCDEGHNIOFJKLBMQ` | `EHDCFPGJBIALNOKMQ` | +| windows-arm64 Benchmarks | `HECDBFGIANLOKJMPQ` | `HDGAECNIPLBOFKJMQ` | +| windows-arm64 Libraries | `HECDBFGIANLOKJMPQ` | `EHDCFPGJBIALNOKMQ` | + +If we see the pattern under the "1st best" column, we see that the sequence `E` and `H` are towards the beginning, meaning that overall, it is profitable to have `OWN_PREFERENCE` (one of the preferred registers for a given interval) or `CALLEE_CALLER` (caller and callee registers) as one of the first heuristic criteria. Next, most of the ordering has `C` and `D` that are also popular that maps to `THIS_ASSIGNED` (already assigned to the current interval) and `COVERS` (covers the lifetime of an interval). One of the busy register heuristics `P` that maps to `PREV_REG_OPT` (Previous reference of the currently assigned interval was optional) is also present at the beginning. + +While these ordering gives good `PerfScore`, there were several regressions observed for other methods. Most of the regressions falls under one or more of the following categories: +1. There are some key challenges in LSRA's resolution phase highlighted in [dotnet/runtime #47194](https://github.com/dotnet/runtime/issues/47194). Once resolution moves are identified for all the blocks, there is a need to revisit those moves to see if there are some that can be optimized out. Several methods regressed their `PerfScore` because we added lot of resolution moves at block boundaries. +2. Even though there is a flexibility of trying different register selection ordering, LSRA has limited knowledge about the method and portion of code for which it is allocating register. For example, during allocation, it doesn't know if it is allocating for code inside loop and that it should keep spare registers to use in that code. There has to be a phase before LSRA that consolidates this information in a data structure that can be used by LSRA during register selection. +3. While doing the experiments, we realized other low hanging fruits in LSRA that amplifies the regression caused by reordering the register selection heuristics. For example, if a variable is defined just once, it can be spilled at the place where it is defined and then, it doesn't need to be spilled throughout the method. This was achieved in [dotnet/runtime #54345](https://github.com/dotnet/runtime/pull/54345). + +## Conclusion + +Register allocation is a complex topic, slight change in algorithm could have huge impact on the generated code. We explored various ideas for finding optimal heuristic selection ordering. Using Genetic algorithm, we could find optimal ordering and there was also some commonality in the heuristics order that was performance efficient for majority of configuration that we tested. However, with many improvements, there were also regressions in many methods across all configurations. We discovered that there was other area of improvements that need to be fixed first before we enable heuristic tuning feature, [[RyuJIT][LSRA]](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+%22%5BRyuJIT%5D%5BLSRA%5D%22+) captures some of the issues. Hence, we decided not to change any heuristic ordering at the current time. We will focus on fixing these issues first and once the existing LSRA weakness are addressed, we can choose to return to this experiment and use the techniques, tools, and knowledge here to inform a heuristic re-ordering. Going forward, we could also auto tune the heuristic ordering based on various factors like how many method parameters are present, if loops are present or not, exception handling, etc. Opportunities are endless, time is limited, so got to make better choices! \ No newline at end of file From 9391b4d5717c0fea3028cbb8dd8d09eabfeb294f Mon Sep 17 00:00:00 2001 From: old zhu Date: Thu, 22 Jul 2021 08:05:09 +0800 Subject: [PATCH 734/926] proposed fix #33727 for illegal instruction in arm (#55502) * proposed fix #33727 * fix for arm checked build error to crossgen the native System.Private.CoreLib.dll * fix for error:unannotated fall-through between switch labels * fix clang-format error * fix clang-format error * apply format.patch --- src/coreclr/jit/emitarm.cpp | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 5b01adbd14a..387b0bb8f5d 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -621,18 +621,24 @@ bool emitter::emitInsWritesToLclVarStackLoc(instrDesc* id) bool emitter::emitInsMayWriteMultipleRegs(instrDesc* id) { instruction ins = id->idIns(); + insFormat fmt = id->idInsFmt(); switch (ins) { case INS_ldm: case INS_ldmdb: - case INS_pop: case INS_smlal: case INS_smull: case INS_umlal: case INS_umull: case INS_vmov_d2i: return true; + case INS_pop: + if (fmt != IF_T2_E2) // T2_E2 is pop single register encoding + { + return true; + } + return false; default: return false; } @@ -1504,12 +1510,11 @@ void emitter::emitIns(instruction ins) void emitter::emitIns_I(instruction ins, emitAttr attr, target_ssize_t imm) { - insFormat fmt = IF_NONE; - bool hasLR = false; - bool hasPC = false; - bool useT2 = false; - bool onlyT1 = false; - + insFormat fmt = IF_NONE; + bool hasLR = false; + bool hasPC = false; + bool useT2 = false; + bool isSingleBit = false; /* Figure out the encoding format of the instruction */ switch (ins) { @@ -1559,10 +1564,9 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, target_ssize_t imm) if (((imm - 1) & imm) == 0) // Is only one or zero bits set in imm? { - if (((imm == 0) && !hasLR) || // imm has no bits set, but hasLR is set - (!hasPC && !hasLR)) // imm has one bit set, and neither of hasPC/hasLR are set + if (imm != 0) { - onlyT1 = true; // if only one bit is set we must use the T1 encoding + isSingleBit = true; // only one bits set in imm } } @@ -1570,15 +1574,21 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, target_ssize_t imm) if (((imm & 0x00ff) == imm) && !useT2) { + // for push {LR,} and pop {PC,} encoding fmt = IF_T1_L1; } - else if (!onlyT1) + else if (!isSingleBit) { + // for other push and pop multiple registers encoding fmt = IF_T2_I1; } else { - // We have to use the Thumb-2 push single register encoding + // We have to use the Thumb-2 push/pop single register encoding + if (hasLR) + { + imm |= 0x4000; + } regNumber reg = genRegNumFromMask(imm); emitIns_R(ins, attr, reg); return; From cc7a8efb1849aef9e6bb98a0dcea3189ec0cf868 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Thu, 22 Jul 2021 02:27:39 +0200 Subject: [PATCH 735/926] Big-endian fixes: various problems in ilasm (#55349) * Byte-swap property and parameter default values of string type * Prepare custom attribute blobs in little-endian byte order * Fix byte order of ELEMENT_TYPE_TYPEDEF typespec blobs * Fix byte order of VTable blobs --- src/coreclr/ilasm/asmman.cpp | 2 +- src/coreclr/ilasm/asmparse.y | 6 +- src/coreclr/ilasm/assem.cpp | 38 ++++++- src/coreclr/ilasm/assembler.h | 6 +- src/coreclr/ilasm/grammar_after.cpp | 128 ++++++++++++++++++++++++ src/coreclr/ilasm/grammar_before.cpp | 1 + src/coreclr/ilasm/prebuilt/asmparse.cpp | 6 +- src/coreclr/ilasm/writer.cpp | 12 +-- 8 files changed, 180 insertions(+), 19 deletions(-) diff --git a/src/coreclr/ilasm/asmman.cpp b/src/coreclr/ilasm/asmman.cpp index 2e4b98b2dca..7b5fd474b65 100644 --- a/src/coreclr/ilasm/asmman.cpp +++ b/src/coreclr/ilasm/asmman.cpp @@ -353,7 +353,7 @@ void AsmMan::EmitDebuggableAttribute(mdToken tkOwner) pbsSig->appendInt8(ELEMENT_TYPE_VOID); pbsSig->append(&bsSigArg); - bsBytes->appendInt32(pAsm->m_dwIncludeDebugInfo); + bsBytes->appendInt32(VAL32(pAsm->m_dwIncludeDebugInfo)); } bsBytes->appendInt8(0); bsBytes->appendInt8(0); diff --git a/src/coreclr/ilasm/asmparse.y b/src/coreclr/ilasm/asmparse.y index 29179ffcec8..db3d82c4baf 100644 --- a/src/coreclr/ilasm/asmparse.y +++ b/src/coreclr/ilasm/asmparse.y @@ -331,14 +331,14 @@ ownerType : typeSpec { $$ = $1; } /* Verbal description of custom attribute initialization blob */ customBlobDescr : customBlobArgs customBlobNVPairs { $$ = $1; - $$->appendInt16(nCustomBlobNVPairs); + $$->appendInt16(VAL16(nCustomBlobNVPairs)); $$->append($2); nCustomBlobNVPairs = 0; } ; customBlobArgs : /* EMPTY */ { $$ = new BinStr(); $$->appendInt16(VAL16(0x0001)); } | customBlobArgs serInit { $$ = $1; - $$->appendFrom($2, (*($2->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); } + AppendFieldToCustomBlob($$,$2); } | customBlobArgs compControl { $$ = $1; } ; @@ -347,7 +347,7 @@ customBlobNVPairs : /* EMPTY */ { $$ = $1; $$->appendInt8($2); $$->append($3); AppendStringWithLength($$,$4); - $$->appendFrom($6, (*($6->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); + AppendFieldToCustomBlob($$,$6); nCustomBlobNVPairs++; } | customBlobNVPairs compControl { $$ = $1; } ; diff --git a/src/coreclr/ilasm/assem.cpp b/src/coreclr/ilasm/assem.cpp index aa94f557972..e8044d16ab2 100644 --- a/src/coreclr/ilasm/assem.cpp +++ b/src/coreclr/ilasm/assem.cpp @@ -766,7 +766,17 @@ BOOL Assembler::EmitMethod(Method *pMethod) dwCPlusTypeFlag= (DWORD)*(pMethod->m_pRetValue->ptr()); pValue = (void const *)(pMethod->m_pRetValue->ptr()+1); cbValue = pMethod->m_pRetValue->length()-1; - if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR); + if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) + { + cbValue /= sizeof(WCHAR); +#if BIGENDIAN + void* pValueTemp = _alloca(cbValue * sizeof(WCHAR)); + memcpy(pValueTemp, pValue, cbValue * sizeof(WCHAR)); + pValue = pValueTemp; + + SwapStringLength((WCHAR*)pValue, cbValue); +#endif + } } else { @@ -804,7 +814,17 @@ BOOL Assembler::EmitMethod(Method *pMethod) dwCPlusTypeFlag= (DWORD)*(pAN->pValue->ptr()); pValue = (void const *)(pAN->pValue->ptr()+1); cbValue = pAN->pValue->length()-1; - if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR); + if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) + { + cbValue /= sizeof(WCHAR); +#if BIGENDIAN + void* pValueTemp = _alloca(cbValue * sizeof(WCHAR)); + memcpy(pValueTemp, pValue, cbValue * sizeof(WCHAR)); + pValue = pValueTemp; + + SwapStringLength((WCHAR*)pValue, cbValue); +#endif + } } else { @@ -986,13 +1006,25 @@ BOOL Assembler::EmitProp(PropDescriptor* pPD) } mdOthers[nOthers] = mdMethodDefNil; // like null-terminator + void* pValue = pPD->m_pValue; +#if BIGENDIAN + if (pPD->m_dwCPlusTypeFlag == ELEMENT_TYPE_STRING) + { + void* pValueTemp = _alloca(pPD->m_cbValue * sizeof(WCHAR)); + memcpy(pValueTemp, pValue, pPD->m_cbValue * sizeof(WCHAR)); + pValue = pValueTemp; + + SwapStringLength((WCHAR*)pValue, pPD->m_cbValue); + } +#endif + if(FAILED(m_pEmitter->DefineProperty( pPD->m_tdClass, wzMemberName, pPD->m_dwAttr, pPD->m_pSig, pPD->m_dwCSig, pPD->m_dwCPlusTypeFlag, - pPD->m_pValue, + pValue, pPD->m_cbValue, mdSet, mdGet, diff --git a/src/coreclr/ilasm/assembler.h b/src/coreclr/ilasm/assembler.h index b7ec921f579..794532ab470 100644 --- a/src/coreclr/ilasm/assembler.h +++ b/src/coreclr/ilasm/assembler.h @@ -429,9 +429,9 @@ public: m_TypeSpec = type; m_pbsBlob = new BinStr(); - m_pbsBlob->appendInt16(VAL16(1)); // prolog 0x01 0x00 - m_pbsBlob->appendInt32((int)action); // 4-byte action - if(pbsPairs) // name-value pairs if any + m_pbsBlob->appendInt16(VAL16(1)); // prolog 0x01 0x00 + m_pbsBlob->appendInt32(VAL32((int)action)); // 4-byte action + if(pbsPairs) // name-value pairs if any { if(pbsPairs->length() > 2) m_pbsBlob->appendFrom(pbsPairs,2); diff --git a/src/coreclr/ilasm/grammar_after.cpp b/src/coreclr/ilasm/grammar_after.cpp index 790df23bfd4..a3f1fb7a218 100644 --- a/src/coreclr/ilasm/grammar_after.cpp +++ b/src/coreclr/ilasm/grammar_after.cpp @@ -366,6 +366,134 @@ static void AppendStringWithLength(BinStr* pbs, __in __nullterminated char* sz) } } +/********************************************************************************/ +/* Append a typed field initializer to an untyped custom attribute blob + * Since the result is untyped, we have to byte-swap here on big-endian systems + */ +#ifdef BIGENDIAN +static int ByteSwapCustomBlob(BYTE *ptr, int length, int type, bool isSZArray) +{ + BYTE *orig_ptr = ptr; + + int nElem = 1; + if (isSZArray) + { + _ASSERTE(length >= 4); + nElem = GET_UNALIGNED_32(ptr); + SET_UNALIGNED_VAL32(ptr, nElem); + if (nElem == 0xffffffff) + nElem = 0; + ptr += 4; + length -= 4; + } + + for (int i = 0; i < nElem; i++) + { + switch (type) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + _ASSERTE(length >= 1); + ptr++; + length--; + break; + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + _ASSERTE(length >= 2); + SET_UNALIGNED_VAL16(ptr, GET_UNALIGNED_16(ptr)); + ptr += 2; + length -= 2; + break; + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + _ASSERTE(length >= 4); + SET_UNALIGNED_VAL32(ptr, GET_UNALIGNED_32(ptr)); + ptr += 4; + length -= 4; + break; + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + _ASSERTE(length >= 8); + SET_UNALIGNED_VAL64(ptr, GET_UNALIGNED_64(ptr)); + ptr += 8; + length -= 8; + break; + case ELEMENT_TYPE_STRING: + case SERIALIZATION_TYPE_TYPE: + _ASSERTE(length >= 1); + if (*ptr == 0xFF) + { + ptr++; + length--; + } + else + { + int skipped = CorSigUncompressData((PCCOR_SIGNATURE&)ptr); + _ASSERTE(length >= skipped); + ptr += skipped; + length -= skipped; + } + break; + case SERIALIZATION_TYPE_TAGGED_OBJECT: + { + _ASSERTE(length >= 1); + bool objIsSZArray = false; + int objType = *ptr; + ptr++; + length--; + if (type == ELEMENT_TYPE_SZARRAY) + { + _ASSERTE(length >= 1); + objIsSZArray = false; + objType = *ptr; + ptr++; + length--; + } + int skipped = ByteSwapCustomBlob(ptr, length, objType, objIsSZArray); + _ASSERTE(length >= skipped); + ptr += skipped; + length -= skipped; + break; + } + } + } + + return ptr - orig_ptr; +} +#endif + +static void AppendFieldToCustomBlob(BinStr* pBlob, __in BinStr* pField) +{ + pBlob->appendFrom(pField, (*(pField->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); + +#ifdef BIGENDIAN + BYTE *fieldPtr = pField->ptr(); + int fieldLength = pField->length(); + + bool isSZArray = false; + int type = fieldPtr[0]; + fieldLength--; + if (type == ELEMENT_TYPE_SZARRAY) + { + isSZArray = true; + type = fieldPtr[1]; + fieldLength--; + } + + // This may be a bytearray that must not be swapped. + if (type == ELEMENT_TYPE_STRING && !isSZArray) + return; + + BYTE *blobPtr = pBlob->ptr() + (pBlob->length() - fieldLength); + ByteSwapCustomBlob(blobPtr, fieldLength, type, isSZArray); +#endif +} + + /********************************************************************************/ /* fetch the next token, and return it Also set the yylval.union if the lexical token also has a value */ diff --git a/src/coreclr/ilasm/grammar_before.cpp b/src/coreclr/ilasm/grammar_before.cpp index 43b45bf36b9..b32684ef91e 100644 --- a/src/coreclr/ilasm/grammar_before.cpp +++ b/src/coreclr/ilasm/grammar_before.cpp @@ -50,6 +50,7 @@ static char* newStringWDel(__in __nullterminated char* str1, char delimiter, __i static char* newString(__in __nullterminated const char* str1); static void corEmitInt(BinStr* buff, unsigned data); static void AppendStringWithLength(BinStr* pbs, __in __nullterminated char* sz); +static void AppendFieldToCustomBlob(BinStr* pBlob, __in BinStr* pField); bool bParsingByteArray = FALSE; int iOpcodeLen = 0; int iCallConv = 0; diff --git a/src/coreclr/ilasm/prebuilt/asmparse.cpp b/src/coreclr/ilasm/prebuilt/asmparse.cpp index 4e7131a90a7..e3afe381a62 100644 --- a/src/coreclr/ilasm/prebuilt/asmparse.cpp +++ b/src/coreclr/ilasm/prebuilt/asmparse.cpp @@ -2280,7 +2280,7 @@ case 73: case 74: #line 334 "asmparse.y" { yyval.binstr = yypvt[-1].binstr; - yyval.binstr->appendInt16(nCustomBlobNVPairs); + yyval.binstr->appendInt16(VAL16(nCustomBlobNVPairs)); yyval.binstr->append(yypvt[-0].binstr); nCustomBlobNVPairs = 0; } break; case 75: @@ -2289,7 +2289,7 @@ case 75: case 76: #line 341 "asmparse.y" { yyval.binstr = yypvt[-1].binstr; - yyval.binstr->appendFrom(yypvt[-0].binstr, (*(yypvt[-0].binstr->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); } break; + AppendFieldToCustomBlob(yyval.binstr,yypvt[-0].binstr); } break; case 77: #line 343 "asmparse.y" { yyval.binstr = yypvt[-1].binstr; } break; @@ -2301,7 +2301,7 @@ case 79: { yyval.binstr = yypvt[-5].binstr; yyval.binstr->appendInt8(yypvt[-4].int32); yyval.binstr->append(yypvt[-3].binstr); AppendStringWithLength(yyval.binstr,yypvt[-2].string); - yyval.binstr->appendFrom(yypvt[-0].binstr, (*(yypvt[-0].binstr->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); + AppendFieldToCustomBlob(yyval.binstr,yypvt[-0].binstr); nCustomBlobNVPairs++; } break; case 80: #line 353 "asmparse.y" diff --git a/src/coreclr/ilasm/writer.cpp b/src/coreclr/ilasm/writer.cpp index dd2404847c8..c7bc6d56d43 100644 --- a/src/coreclr/ilasm/writer.cpp +++ b/src/coreclr/ilasm/writer.cpp @@ -1223,12 +1223,12 @@ HRESULT Assembler::CreatePEFile(__in __nullterminated WCHAR *pwzOutputFilename) *pb = ELEMENT_TYPE_TYPEDEF; memcpy(++pb,pTDD->m_szName,namesize); pTDD->m_tkTypeSpec = ResolveLocalMemberRef(pTDD->m_tkTypeSpec); - memcpy(pb+namesize,&(pTDD->m_tkTypeSpec),sizeof(mdToken)); + SET_UNALIGNED_VAL32(pb+namesize, pTDD->m_tkTypeSpec); if(TypeFromToken(pTDD->m_tkTypeSpec)==mdtCustomAttribute) { CustomDescr* pCA = pTDD->m_pCA; - pbs->appendInt32(pCA->tkType); - pbs->appendInt32(pCA->tkOwner); + pbs->appendInt32(VAL32(pCA->tkType)); + pbs->appendInt32(VAL32(pCA->tkOwner)); if(pCA->pBlob) pbs->append(pCA->pBlob); } ResolveTypeSpec(pbs); @@ -1314,9 +1314,9 @@ HRESULT Assembler::CreatePEFile(__in __nullterminated WCHAR *pwzOutputFilename) { Method* pMD; Class* pClass; - m_pVTable->appendInt32(pGlobalLabel->m_GlobalOffset); - m_pVTable->appendInt16(pVTFEntry->m_wCount); - m_pVTable->appendInt16(pVTFEntry->m_wType); + m_pVTable->appendInt32(VAL32(pGlobalLabel->m_GlobalOffset)); + m_pVTable->appendInt16(VAL16(pVTFEntry->m_wCount)); + m_pVTable->appendInt16(VAL16(pVTFEntry->m_wType)); for(int i=0; (pClass = m_lstClass.PEEK(i)); i++) { for(WORD j = 0; (pMD = pClass->m_MethodList.PEEK(j)); j++) From 1b4f786ec254a2974c8fafa1bde4361cdcf57d1d Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 21 Jul 2021 18:03:38 -0700 Subject: [PATCH 736/926] JIT: track memory loop dependence of trees during value numbering (#55936) Leverage value numbering's alias analysis to annotate trees with the loop memory dependence of the tree's value number. First, refactor the `mapStore` value number so that it also tracks the loop number where the store occurs. This is done via an extra non-value-num arg, so add appropriate bypasses to logic in the jit that expect to only find value number args. Also update the dumping to display the loop information. Next, during VN computation, record loop memory dependence from `mapStores` with the tree currently being value numbered, whenever a value number comes from a particular map. There may be multiple such recording events per tree, so add logic on the recording side to track the most constraining dependence. Note value numbering happens in execution order, so there is an unambiguous current tree being value numbered. This dependence info is tracked via a side map. Finally, during hoisting, for each potentially hoistable tree, consult the side map to recover the loop memory dependence of a tree, and if that dependence is at or within the loop that we're hoisting from, block the hoist. I've also absorbed the former class var (static field) hosting exclusion into this new logic. This gives us slightly more relaxed dependence in some cases. Resolves #54118. --- src/coreclr/jit/compiler.cpp | 9 +- src/coreclr/jit/compiler.h | 28 ++ src/coreclr/jit/optimizer.cpp | 175 ++++++++++++- src/coreclr/jit/valuenum.cpp | 34 ++- src/coreclr/jit/valuenumfuncs.h | 2 +- .../JitBlue/Runtime_54118/Runtime_54118.cs | 239 ++++++++++++++++++ .../Runtime_54118/Runtime_54118.csproj | 12 + 7 files changed, 480 insertions(+), 19 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.csproj diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 47a8aabf980..20aafd4e1ad 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2015,10 +2015,11 @@ void Compiler::compInit(ArenaAllocator* pAlloc, } #endif // DEBUG - vnStore = nullptr; - m_opAsgnVarDefSsaNums = nullptr; - fgSsaPassesCompleted = 0; - fgVNPassesCompleted = 0; + vnStore = nullptr; + m_opAsgnVarDefSsaNums = nullptr; + m_nodeToLoopMemoryBlockMap = nullptr; + fgSsaPassesCompleted = 0; + fgVNPassesCompleted = 0; // check that HelperCallProperties are initialized diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 435b38d251f..155e8e4d1e5 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5054,6 +5054,33 @@ public: return m_opAsgnVarDefSsaNums; } + // This map tracks nodes whose value numbers explicitly or implicitly depend on memory states. + // The map provides the entry block of the most closely enclosing loop that + // defines the memory region accessed when defining the nodes's VN. + // + // This information should be consulted when considering hoisting node out of a loop, as the VN + // for the node will only be valid within the indicated loop. + // + // It is not fine-grained enough to track memory dependence within loops, so cannot be used + // for more general code motion. + // + // If a node does not have an entry in the map we currently assume the VN is not memory dependent + // and so memory does not constrain hoisting. + // + typedef JitHashTable, BasicBlock*> NodeToLoopMemoryBlockMap; + NodeToLoopMemoryBlockMap* m_nodeToLoopMemoryBlockMap; + NodeToLoopMemoryBlockMap* GetNodeToLoopMemoryBlockMap() + { + if (m_nodeToLoopMemoryBlockMap == nullptr) + { + m_nodeToLoopMemoryBlockMap = new (getAllocator()) NodeToLoopMemoryBlockMap(getAllocator()); + } + return m_nodeToLoopMemoryBlockMap; + } + + void optRecordLoopMemoryDependence(GenTree* tree, BasicBlock* block, ValueNum memoryVN); + void optCopyLoopMemoryDependence(GenTree* fromTree, GenTree* toTree); + // Requires value numbering phase to have completed. Returns the value number ("gtVN") of the // "tree," EXCEPT in the case of GTF_VAR_USEASG, because the tree node's gtVN member is the // "use" VN. Performs a lookup into the map of (use asg tree -> def VN.) to return the "def's" @@ -9966,6 +9993,7 @@ public: BasicBlock* compCurBB; // the current basic block in process Statement* compCurStmt; // the current statement in process + GenTree* compCurTree; // the current tree in process // The following is used to create the 'method JIT info' block. size_t compInfoBlkSize; diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 340dd64e7e6..8263c7db2bf 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -5486,6 +5486,9 @@ void Compiler::optPerformHoistExpr(GenTree* origExpr, unsigned lnum) // so clear the RegNum if it was set in the original expression hoistExpr->ClearRegNum(); + // Copy any loop memory dependence. + optCopyLoopMemoryDependence(origExpr, hoistExpr); + // At this point we should have a cloned expression, marked with the GTF_MAKE_CSE flag assert(hoistExpr != origExpr); assert(hoistExpr->gtFlags & GTF_MAKE_CSE); @@ -6036,6 +6039,106 @@ bool Compiler::optIsProfitableToHoistableTree(GenTree* tree, unsigned lnum) return true; } +//------------------------------------------------------------------------ +// optRecordLoopMemoryDependence: record that tree's value number +// is dependent on a particular memory VN +// +// Arguments: +// tree -- tree in question +// block -- block containing tree +// memoryVN -- VN for a "map" from a select operation encounterd +// while computing the tree's VN +// +// Notes: +// Only tracks trees in loops, and memory updates in the same loop nest. +// So this is a coarse-grained dependence that is only usable for +// hoisting tree out of its enclosing loops. +// +void Compiler::optRecordLoopMemoryDependence(GenTree* tree, BasicBlock* block, ValueNum memoryVN) +{ + // If tree is not in a loop, we don't need to track its loop dependence. + // + unsigned const loopNum = block->bbNatLoopNum; + + if (loopNum == BasicBlock::NOT_IN_LOOP) + { + return; + } + + // Find the loop associated with this memory VN. + // + unsigned const updateLoopNum = vnStore->LoopOfVN(memoryVN); + + if (updateLoopNum == BasicBlock::NOT_IN_LOOP) + { + // memoryVN defined outside of any loop, we can ignore. + // + JITDUMP(" ==> Not updating loop memory dependence of [%06u], memory " FMT_VN " not defined in a loop\n", + dspTreeID(tree), memoryVN); + return; + } + + // If the update block is not the the header of a loop containing + // block, we can also ignore the update. + // + if (!optLoopContains(updateLoopNum, loopNum)) + { + JITDUMP(" ==> Not updating loop memory dependence of [%06u]/" FMT_LP ", memory " FMT_VN "/" FMT_LP + " is not defined in an enclosing loop\n", + dspTreeID(tree), loopNum, memoryVN, updateLoopNum); + return; + } + + // If we already have a recorded a loop entry block for this + // tree, see if the new update is for a more closely nested + // loop. + // + NodeToLoopMemoryBlockMap* const map = GetNodeToLoopMemoryBlockMap(); + BasicBlock* mapBlock = nullptr; + + if (map->Lookup(tree, &mapBlock)) + { + unsigned const mapLoopNum = mapBlock->bbNatLoopNum; + + // If the update loop contains the existing map loop, + // the existing map loop is more constraining. So no + // update needed. + // + if (optLoopContains(updateLoopNum, mapLoopNum)) + { + JITDUMP(" ==> Not updating loop memory dependence of [%06u]; alrady constrained to " FMT_LP + " nested in " FMT_LP "\n", + dspTreeID(tree), mapLoopNum, updateLoopNum); + return; + } + } + + // MemoryVN now describes the most constraining loop memory dependence + // we know of. Update the map. + // + JITDUMP(" ==> Updating loop memory dependence of [%06u] to " FMT_LP "\n", dspTreeID(tree), updateLoopNum); + map->Set(tree, optLoopTable[updateLoopNum].lpEntry); +} + +//------------------------------------------------------------------------ +// optCopyLoopMemoryDependence: record that tree's loop memory dependence +// is the same as some other tree. +// +// Arguments: +// fromTree -- tree to copy dependence from +// toTree -- tree in question +// +void Compiler::optCopyLoopMemoryDependence(GenTree* fromTree, GenTree* toTree) +{ + NodeToLoopMemoryBlockMap* const map = GetNodeToLoopMemoryBlockMap(); + BasicBlock* mapBlock = nullptr; + + if (map->Lookup(fromTree, &mapBlock)) + { + map->Set(toTree, mapBlock); + } +} + //------------------------------------------------------------------------ // optHoistLoopBlocks: Hoist invariant expression out of the loop. // @@ -6093,19 +6196,63 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo bool IsTreeVNInvariant(GenTree* tree) { ValueNum vn = tree->gtVNPair.GetLiberal(); - if (m_compiler->vnStore->IsVNConstant(vn)) + bool vnIsInvariant = + m_compiler->optVNIsLoopInvariant(vn, m_loopNum, &m_hoistContext->m_curLoopVnInvariantCache); + + // Even though VN is invariant in the loop (say a constant) its value may depend on position + // of tree, so for loop hoisting we must also check that any memory read by tree + // is also invariant in the loop. + // + if (vnIsInvariant) { - // It is unsafe to allow a GT_CLS_VAR that has been assigned a constant. - // The logic in optVNIsLoopInvariant would consider it to be loop-invariant, even - // if the assignment of the constant to the GT_CLS_VAR was inside the loop. + vnIsInvariant = IsTreeLoopMemoryInvariant(tree); + } + return vnIsInvariant; + } + + //------------------------------------------------------------------------ + // IsTreeLoopMemoryInvariant: determine if the value number of tree + // is dependent on the tree being executed within the current loop + // + // Arguments: + // tree -- tree in question + // + // Returns: + // true if tree could be evaluated just before loop and get the + // same value. + // + // Note: + // Calls are optimistically assumed to be invariant. + // Caller must do their own analysis for these tree types. + // + bool IsTreeLoopMemoryInvariant(GenTree* tree) + { + if (tree->IsCall()) + { + // Calls are handled specially by hoisting, and loop memory dependence + // must be checked by other means. // - if (tree->OperIs(GT_CLS_VAR)) + return true; + } + + NodeToLoopMemoryBlockMap* const map = m_compiler->GetNodeToLoopMemoryBlockMap(); + BasicBlock* loopEntryBlock = nullptr; + if (map->Lookup(tree, &loopEntryBlock)) + { + for (MemoryKind memoryKind : allMemoryKinds()) { - return false; + ValueNum loopMemoryVN = + m_compiler->GetMemoryPerSsaData(loopEntryBlock->bbMemorySsaNumIn[memoryKind]) + ->m_vnPair.GetLiberal(); + if (!m_compiler->optVNIsLoopInvariant(loopMemoryVN, m_loopNum, + &m_hoistContext->m_curLoopVnInvariantCache)) + { + return false; + } } } - return m_compiler->optVNIsLoopInvariant(vn, m_loopNum, &m_hoistContext->m_curLoopVnInvariantCache); + return true; } public: @@ -6576,6 +6723,20 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* loo { for (unsigned i = 0; i < funcApp.m_arity; i++) { + // 4th arg of mapStore identifies the loop where the store happens. + // + if (funcApp.m_func == VNF_MapStore) + { + assert(funcApp.m_arity == 4); + + if (i == 3) + { + const unsigned vnLoopNum = funcApp.m_args[3]; + res = !optLoopContains(lnum, vnLoopNum); + break; + } + } + // TODO-CQ: We need to either make sure that *all* VN functions // always take VN args, or else have a list of arg positions to exempt, as implicitly // constant. diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 8bc2ace8f6b..86d0d5dae10 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2136,7 +2136,7 @@ ValueNum ValueNumStore::VNForFunc( assert(arg0VN == VNNormalValue(arg0VN)); assert(arg1VN == VNNormalValue(arg1VN)); assert(arg2VN == VNNormalValue(arg2VN)); - assert(arg3VN == VNNormalValue(arg3VN)); + assert((func == VNF_MapStore) || (arg3VN == VNNormalValue(arg3VN))); assert(VNFuncArity(func) == 4); ValueNum resultVN; @@ -2176,12 +2176,15 @@ ValueNum ValueNumStore::VNForFunc( ValueNum ValueNumStore::VNForMapStore(var_types typ, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN) { - ValueNum result = VNForFunc(typ, VNF_MapStore, arg0VN, arg1VN, arg2VN); + BasicBlock* const bb = m_pComp->compCurBB; + BasicBlock::loopNumber const loopNum = bb->bbNatLoopNum; + ValueNum const result = VNForFunc(typ, VNF_MapStore, arg0VN, arg1VN, arg2VN, loopNum); + #ifdef DEBUG if (m_pComp->verbose) { - printf(" VNForMapStore(" FMT_VN ", " FMT_VN ", " FMT_VN "):%s returns ", arg0VN, arg1VN, arg2VN, - varTypeName(typ)); + printf(" VNForMapStore(" FMT_VN ", " FMT_VN ", " FMT_VN "):%s in " FMT_BB " returns ", arg0VN, arg1VN, + arg2VN, varTypeName(typ), bb->bbNum); m_pComp->vnPrint(result, 1); printf("\n"); } @@ -2315,6 +2318,8 @@ TailCall: ") ==> " FMT_VN ".\n", funcApp.m_args[0], arg0VN, funcApp.m_args[1], funcApp.m_args[2], arg1VN, funcApp.m_args[2]); #endif + + m_pComp->optRecordLoopMemoryDependence(m_pComp->compCurTree, m_pComp->compCurBB, funcApp.m_args[0]); return funcApp.m_args[2]; } // i # j ==> select(store(m, i, v), j) == select(m, j) @@ -4346,7 +4351,7 @@ var_types ValueNumStore::TypeOfVN(ValueNum vn) } //------------------------------------------------------------------------ -// LoopOfVN: If the given value number is VNF_MemOpaque, return +// LoopOfVN: If the given value number is VNF_MemOpaque or VNF_MapStore, return // the loop number where the memory update occurs, otherwise returns MAX_LOOP_NUM. // // Arguments: @@ -4359,9 +4364,16 @@ var_types ValueNumStore::TypeOfVN(ValueNum vn) BasicBlock::loopNumber ValueNumStore::LoopOfVN(ValueNum vn) { VNFuncApp funcApp; - if (GetVNFunc(vn, &funcApp) && (funcApp.m_func == VNF_MemOpaque)) + if (GetVNFunc(vn, &funcApp)) { - return (BasicBlock::loopNumber)funcApp.m_args[0]; + if (funcApp.m_func == VNF_MemOpaque) + { + return (BasicBlock::loopNumber)funcApp.m_args[0]; + } + else if (funcApp.m_func == VNF_MapStore) + { + return (BasicBlock::loopNumber)funcApp.m_args[3]; + } } return BasicBlock::MAX_LOOP_NUM; @@ -5707,6 +5719,7 @@ void ValueNumStore::vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore) ValueNum mapVN = mapStore->m_args[0]; // First arg is the map id ValueNum indexVN = mapStore->m_args[1]; // Second arg is the index ValueNum newValVN = mapStore->m_args[2]; // Third arg is the new value + unsigned loopNum = mapStore->m_args[3]; // Fourth arg is the loop num comp->vnPrint(mapVN, 0); printf("["); @@ -5714,6 +5727,10 @@ void ValueNumStore::vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore) printf(" := "); comp->vnPrint(newValVN, 0); printf("]"); + if (loopNum != BasicBlock::NOT_IN_LOOP) + { + printf("@" FMT_LP, loopNum); + } } void ValueNumStore::vnDumpMemOpaque(Compiler* comp, VNFuncApp* memOpaque) @@ -6593,7 +6610,10 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk) for (GenTree* const tree : stmt->TreeList()) { + // Set up ambient var referring to current tree. + compCurTree = tree; fgValueNumberTree(tree); + compCurTree = nullptr; } #ifdef DEBUG diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index af53a7b1e05..5d3708e65da 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -7,7 +7,7 @@ // clang-format off ValueNumFuncDef(MemOpaque, 1, false, false, false) // Args: 0: loop num -ValueNumFuncDef(MapStore, 3, false, false, false) // Args: 0: map, 1: index (e. g. field handle), 2: value being stored. +ValueNumFuncDef(MapStore, 4, false, false, false) // Args: 0: map, 1: index (e. g. field handle), 2: value being stored, 3: loop num. ValueNumFuncDef(MapSelect, 2, false, false, false) // Args: 0: map, 1: key. ValueNumFuncDef(FieldSeq, 2, false, false, false) // Sequence (VN of null == empty) of (VN's of) field handles. diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.cs b/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.cs new file mode 100644 index 00000000000..dc0dd46a4ba --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.cs @@ -0,0 +1,239 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +// Various tests for memory-dependent loop hoisting + +class Runtime_54118 +{ + public static int Main() + { + _clsVar = -1; + int result = 0; + int index = 0; + void Test(string name, Func act) + { + Console.Write("{0}: ", name); + if (act()) + { + Console.WriteLine("PASS"); + } + else + { + Console.WriteLine("FAIL"); + result |= 1 << index; + } + + index++; + } + + Test(nameof(TestConstantByref), TestConstantByref); + Test(nameof(TestConstantArr), TestConstantArr); + Test(nameof(TestConstantClsVar), TestConstantClsVar); + Test(nameof(TestParamByref), () => TestParamByref(1)); + Test(nameof(TestParamArr), () => TestParamArr(1)); + Test(nameof(TestParamClsVar), () => TestParamClsVar(1)); + Test(nameof(TestPhiByref), TestPhiByref); + Test(nameof(TestPhiArr), TestPhiArr); + Test(nameof(TestPhiClsVar), TestPhiClsVar); + Test(nameof(TestCastByref), TestCastByref); + Test(nameof(TestCastArr), TestCastArr); + Test(nameof(TestCastClsVar), TestCastClsVar); + + return 100 + result; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestConstantByref() + { + int[] arr = { -1 }; + ref int r = ref arr[0]; + int val = -1; + for (int i = 0; i < 2; i++) + { + r = 1; + val = r; + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestConstantArr() + { + int[] arr = { -1 }; + int val = -1; + for (int i = 0; i < 2; i++) + { + arr[0] = 1; + val = arr[0]; + } + + return val == 1; + } + + static int _clsVar; + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestConstantClsVar() + { + _clsVar = -1; + int val = -1; + for (int i = 0; i < 2; i++) + { + _clsVar = 1; + val = _clsVar; + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestParamByref(int one) + { + int[] arr = { -1 }; + ref int r = ref arr[0]; + int val = -1; + for (int i = 0; i < 2; i++) + { + r = one; + val = r; + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestParamArr(int one) + { + int[] arr = { -1 }; + int val = -1; + for (int i = 0; i < 2; i++) + { + arr[0] = one; + val = arr[0]; + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestParamClsVar(int one) + { + _clsVar = -1; + int val = -1; + for (int i = 0; i < 2; i++) + { + _clsVar = one; + val = _clsVar; + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestPhiByref() + { + int[] arr = { -1 }; + ref int r = ref arr[0]; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + r = i; + val = r; + } + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestPhiArr() + { + int[] arr = { -1 }; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + arr[0] = i; + val = arr[0]; + } + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestPhiClsVar() + { + _clsVar = -1; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + _clsVar = i; + val = _clsVar; + } + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestCastByref() + { + int[] arr = { -1 }; + ref int r = ref arr[0]; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + r = i; + val = (byte)r; + } + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestCastArr() + { + int[] arr = { -1 }; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + arr[0] = i; + val = (byte)arr[0]; + } + } + + return val == 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool TestCastClsVar() + { + _clsVar = -1; + int val = -1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + _clsVar = i; + val = (byte)_clsVar; + } + } + + return val == 1; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.csproj new file mode 100644 index 00000000000..f3e1cbd44b4 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_54118/Runtime_54118.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + From e60882e13769df03efe6c564311b9d3793e693d6 Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Wed, 21 Jul 2021 21:37:24 -0400 Subject: [PATCH 737/926] Build VS installers for WebAssembly and Mobile workloads (#55769) In order to support generating installers, this change adds the mono.workloads subset and the associated yml. --- Directory.Build.props | 1 + eng/Subsets.props | 7 +- eng/Version.Details.xml | 4 + eng/Versions.props | 7 + .../mono/templates/workloads-build.yml | 100 ++++++++++ .../mono/templates/xplat-pipeline-job.yml | 6 + eng/pipelines/runtime-official.yml | 29 +++ src/workloads/VSSetup.props | 42 ++++ src/workloads/VSSetup.targets | 49 +++++ src/workloads/mono_wasm_mobile.vsmanproj | 68 +++++++ src/workloads/readme.md | 2 + src/workloads/workloads.csproj | 185 ++++++++++++++++++ 12 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 eng/pipelines/mono/templates/workloads-build.yml create mode 100644 src/workloads/VSSetup.props create mode 100644 src/workloads/VSSetup.targets create mode 100644 src/workloads/mono_wasm_mobile.vsmanproj create mode 100644 src/workloads/readme.md create mode 100644 src/workloads/workloads.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 6cab9bcee30..cc4715cfad4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -59,6 +59,7 @@ $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'coreclr')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'mono')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'installer')) + $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'workloads')) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'native')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'tools-local')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'tasks')) diff --git a/eng/Subsets.props b/eng/Subsets.props index 9234a8c2dde..f9bde27cb97 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -109,7 +109,8 @@ - + + @@ -247,6 +248,10 @@ + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d3afda097cd..2ba88ba501a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -54,6 +54,10 @@ https://github.com/dotnet/arcade e97027cf100d2b532adce387e5cb93a373de93c9 + + https://github.com/dotnet/arcade + e97027cf100d2b532adce387e5cb93a373de93c9 + https://github.com/dotnet/arcade e97027cf100d2b532adce387e5cb93a373de93c9 diff --git a/eng/Versions.props b/eng/Versions.props index 04aad29b272..eac0a300b1d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -61,7 +61,10 @@ 2.5.1-beta.21369.3 6.0.0-beta.21369.3 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 + 6.0.0-beta.21369.3 6.0.0-beta.21369.3 6.0.0-beta.21369.3 6.0.0-beta.21369.3 @@ -183,5 +186,9 @@ 6.0.0-rc.1.21369.1 $(MicrosoftNETWorkloadEmscriptenManifest60100Version) + + 1.1.87-gba258badda + 3.14.0-dotnet + 6.0.0-preview.5.21275.7 diff --git a/eng/pipelines/mono/templates/workloads-build.yml b/eng/pipelines/mono/templates/workloads-build.yml new file mode 100644 index 00000000000..65fcbd5ed88 --- /dev/null +++ b/eng/pipelines/mono/templates/workloads-build.yml @@ -0,0 +1,100 @@ +parameters: + archType: '' + buildConfig: '' + container: '' + dependOnEvaluatePaths: false + dependsOn: [] + isOfficialBuild: false + osGroup: '' + osSubgroup: '' + platform: '' + pool: '' + runtimeVariant: '' + stagedBuild: false + testGroup: '' + timeoutInMinutes: '' + variables: {} + +jobs: +- template: xplat-pipeline-job.yml + parameters: + archType: ${{ parameters.archType }} + buildConfig: ${{ parameters.buildConfig }} + container: ${{ parameters.container }} + condition: ${{ parameters.isOfficialBuild }} + helixType: 'build/product/' + osGroup: ${{ parameters.osGroup }} + osSubgroup: ${{ parameters.osSubgroup }} + pool: ${{ parameters.pool }} + runtimeVariant: ${{ parameters.runtimeVariant }} + stagedBuild: ${{ parameters.stagedBuild }} + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }} + + dependsOn: ${{ parameters.dependsOn }} + + name: workloadsbuild + displayName: Build Workloads + + variables: + - name: officialBuildIdArg + value: '' + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: + - name: officialBuildIdArg + value: '/p:OfficialBuildId=$(Build.BuildNumber)' + - name: SignType + value: $[ coalesce(variables.OfficialSignType, 'real') ] + - ${{ parameters.variables }} + + steps: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: 'IntermediateArtifacts' + path: $(workloadPackagesPath) + patterns: | + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.android-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.browser-wasm*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.ios-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.iossimulator-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.maccatalyst-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.tvos-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.tvossimulator-*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.Manifest*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoTargets.Sdk*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoAOTCompiler.Task*.nupkg + IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Sdk*.nupkg + + - task: CopyFiles@2 + displayName: Flatten packages + inputs: + sourceFolder: $(workloadPackagesPath) + contents: '*/Shipping/*.nupkg' + cleanTargetFolder: false + targetFolder: $(workloadPackagesPath) + flattenFolders: true + + - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -subset mono.workloads -arch $(archType) -c $(buildConfig) $(officialBuildIdArg) -ci + displayName: Build workload artifacts + + # Upload packages wrapping msis + - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml + parameters: + name: workloads + + # Delete wixpdb files before they are uploaded to artifacts + - task: DeleteFiles@1 + displayName: Delete wixpdb's + inputs: + SourceFolder: $(workloadArtifactsPath) + Contents: '*.wixpdb' + + # Upload artifacts to be used for generating VS components + - task: PublishPipelineArtifact@1 + displayName: Publish workload artifacts + inputs: + targetPath: $(Build.SourcesDirectory)/artifacts/VSSetup/$(_BuildConfig)/Insertion + artifactName: 'Insertion' + continueOnError: true + condition: always() diff --git a/eng/pipelines/mono/templates/xplat-pipeline-job.yml b/eng/pipelines/mono/templates/xplat-pipeline-job.yml index 8327e5564a1..9a070492786 100644 --- a/eng/pipelines/mono/templates/xplat-pipeline-job.yml +++ b/eng/pipelines/mono/templates/xplat-pipeline-job.yml @@ -90,6 +90,12 @@ jobs: - name: nativeTestArtifactRootFolderPath value: '$(binTestsPath)/obj/$(osGroup).$(archType).$(buildConfigUpper)' + - name: workloadPackagesPath + value: $(Build.SourcesDirectory)/artifacts/workloadPackages + + - name: workloadArtifactsPath + value: $(Build.SourcesDirectory)/artifacts/workloads + - name: liveRuntimeBuildConfigUpper ${{ if eq(parameters.liveRuntimeBuildConfig, 'release') }}: value: 'Release' diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml index 6b02686235b..5cb49dafbd9 100644 --- a/eng/pipelines/runtime-official.yml +++ b/eng/pipelines/runtime-official.yml @@ -372,6 +372,35 @@ stages: - windows_x86 - Linux_x64 + # + # Build Blazor Workload + # + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/mono/templates/workloads-build.yml + buildConfig: release + platforms: + - windows_x64 + jobParameters: + isOfficialBuild: ${{ variables.isOfficialBuild }} + dependsOn: + - Build_Android_arm_release_AllSubsets_Mono + - Build_Android_arm64_release_AllSubsets_Mono + - Build_Android_x86_release_AllSubsets_Mono + - Build_Android_x64_release_AllSubsets_Mono + - Build_Browser_wasm_release_AllSubsets_Mono + - Build_iOS_arm_release_AllSubsets_Mono + - Build_iOS_arm64_release_AllSubsets_Mono + - Build_iOSSimulator_x64_release_AllSubsets_Mono + - Build_iOSSimulator_x86_release_AllSubsets_Mono + - Build_iOSSimulator_arm64_release_AllSubsets_Mono + - Build_MacCatalyst_arm64_release_AllSubsets_Mono + - Build_MacCatalyst_x64_release_AllSubsets_Mono + - Build_tvOS_arm64_release_AllSubsets_Mono + - Build_tvOSSimulator_arm64_release_AllSubsets_Mono + - Build_tvOSSimulator_x64_release_AllSubsets_Mono + - Build_Windows_x64_release_CrossAOT_Mono + - ${{ if eq(variables.isOfficialBuild, true) }}: - template: /eng/pipelines/official/stages/publish.yml parameters: diff --git a/src/workloads/VSSetup.props b/src/workloads/VSSetup.props new file mode 100644 index 00000000000..6cfb80b3951 --- /dev/null +++ b/src/workloads/VSSetup.props @@ -0,0 +1,42 @@ + + + + 1 + + + + https://vsdrop.corp.microsoft.com/file/v1/ + https://devdiv.artifacts.visualstudio.com/ + $(MSBuildThisDirectory)Tools\Drop.App\lib\net45\Drop.exe + + 10 + $([System.DateTime]::Now.AddYears($(DropExpiration)).ToString("M/d/yyyy h:m:s tt")) + + 10 + + verbose + + + -s "$(DropServiceUri)" + --timeout "$(DropTimeout)" + --tracelevel "$(DropTraceLevel)" + -x "$(DropExpirationDate)" + + -a + + + + dotnet + installer + local_build + $([System.DateTime]::Now.ToString("yyMMdd")).1 + + + + https://vsdrop.corp.microsoft.com/file/v1/Products/$(ManifestTeamProject)/$(ManifestRepositoryName)/$(ManifestBuildBranch)/$(ManifestBuildNumber); + + + + $(OutputPath)\obj\$(MSBuildProject) + + diff --git a/src/workloads/VSSetup.targets b/src/workloads/VSSetup.targets new file mode 100644 index 00000000000..3aa4709342e --- /dev/null +++ b/src/workloads/VSSetup.targets @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + Products/$(ManifestTeamProject)/$(ManifestRepositoryName)/$(ManifestBuildBranch)/$(ManifestBuildNumber) + + -n "$(DropName)" + -d "$(VSDropSource)" + + $(DropExe) Upgrade $(DropParamService) $(DropParamAuth) $(DropParamTimeout) $(DropParamTraceLevel) + $(DropExe) Create $(DropParamService) $(DropParamAuth) $(DropParamTimeout) $(DropParamTraceLevel) $(DropParamExpirationDate) $(DropParamName) + $(DropExe) Publish $(DropParamService) $(DropParamAuth) $(DropParamTimeout) $(DropParamTraceLevel) $(DropParamName) $(DropParamSource) + $(DropExe) Finalize $(DropParamService) $(DropParamAuth) $(DropParamTimeout) $(DropParamTraceLevel) $(DropParamName) + $(DropExe) Update $(DropParamService) $(DropParamAuth) $(DropParamTimeout) $(DropParamTraceLevel) $(DropParamName) --neverExpire + + + + + + + + + + + + + + + + diff --git a/src/workloads/mono_wasm_mobile.vsmanproj b/src/workloads/mono_wasm_mobile.vsmanproj new file mode 100644 index 00000000000..95f1f240018 --- /dev/null +++ b/src/workloads/mono_wasm_mobile.vsmanproj @@ -0,0 +1,68 @@ + + + + + + false + false + false + build-manifest + true + false + DotNetOptionalWorkloads + vs + 42.42.42 + true + $(ManifestOutputPath) + + + + + / + + + /CHS/ + + + /CHT/ + + + /CSY/ + + + /DEU/ + + + /ENU/ + + + /ESN/ + + + /FRA/ + + + /ITA/ + + + /JPN/ + + + /KOR/ + + + /PLK/ + + + /PTB/ + + + /RUS/ + + + /TRK/ + + + + + diff --git a/src/workloads/readme.md b/src/workloads/readme.md new file mode 100644 index 00000000000..6ef56855890 --- /dev/null +++ b/src/workloads/readme.md @@ -0,0 +1,2 @@ +# Building +The workloads project can only be built using .NET Framework msbuild. To build locally, run ```build -project src\workloads\workloads.csproj -msbuildEngine vs``` diff --git a/src/workloads/workloads.csproj b/src/workloads/workloads.csproj new file mode 100644 index 00000000000..1f386f14795 --- /dev/null +++ b/src/workloads/workloads.csproj @@ -0,0 +1,185 @@ + + + + + netcoreapp3.1 + net5.0 + net472 + $(NuGetPackageRoot)microsoft.dotnet.build.tasks.installers\$(MicrosoftDotNetBuildTasksInstallersPackageVersion)\tools\$(MicrosoftDotNetBuildTasksInstallersTaskTargetFramework)\Microsoft.DotNet.Build.Tasks.Installers.dll + + + + + + + + $(NetCoreAppCurrent) + false + false + + $(ArtifactsObjDir)workloads/ + $(ArtifactsBinDir)workloads/ + $(workloadArtifactsPath)/ + $(ArtifactsShippingPackagesDir) + $(workloadPackagesPath)/ + + + + + + $(ArtifactsObjDir)/LightCommandPackages + + $(ArtifactsNonShippingPackagesDir) + + + + $(PkgWix)\tools + $(PkgMicroBuild_Plugins_SwixBuild_Dotnet) + $(SwixPluginPath)\build\MicroBuild.Plugins.SwixBuild.targets + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(VisualStudioSetupInsertionPath)$(SDKBundleVersion)\ + + + + + + Microsoft + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_WixObj Include="$(_WixObjDir)\**\*.wixobj" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(GitCommitCount.PadLeft(6,'0')) + + + $(_PatchNumber) + + $(GitCommitCount) + + + $(FileVersion) + + $(VersionPrefix).$(CombinedBuildNumberAndRevision) + + + From 36f3f9787eada1eb891b16a1e79a9dde2bcc6db4 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 21 Jul 2021 23:20:48 -0500 Subject: [PATCH 738/926] Consume PosixSignal in Hosting's ConsoleLifetime (#56057) * Add NetCoreAppCurrent target to Microsoft.Extensions.Hosting * Handle SIGTERM in Hosting and handle just like SIGINT (CTRL+C) Don't listen to ProcessExit on net6.0+ in Hosting anymore. This allows for Environment.Exit to not hang the app. Don't clobber ExitCode during ProcessExit now that SIGTERM is handled separately. For non-net6.0 targets, only wait for the shutdown timeout, so the process doesn't hang forever. Fix #55417 Fix #44086 Fix #50397 Fix #42224 Fix #35990 * Remove _shutdownBlock on netcoreappcurrent, as this is no longer waited on * Change Console.CancelKeyPress to use PosixSignalRegistration SIGINT and SIGQUIT * Use a no-op lifetime on mobile platforms * Add docs for shutdown --- .../tests/UseSystemdTests.cs | 5 +- .../Directory.Build.props | 3 +- .../docs/HostShutdown.md | 68 ++++++++ .../docs/images/HostShutdownSequence.png | Bin 0 -> 58076 bytes .../ref/Microsoft.Extensions.Hosting.cs | 25 +++ .../src/HostBuilder.cs | 6 +- .../src/HostBuilder.netcoreapp.cs | 24 +++ .../src/HostBuilder.notnetcoreapp.cs | 16 ++ .../src/HostingHostBuilderExtensions.cs | 35 +++- .../src/Internal/ConsoleLifetime.cs | 44 ++--- .../Internal/ConsoleLifetime.netcoreapp.cs | 39 +++++ .../Internal/ConsoleLifetime.notnetcoreapp.cs | 54 ++++++ .../src/Internal/NullLifetime.cs | 24 +++ .../src/Microsoft.Extensions.Hosting.csproj | 20 ++- .../UnitTests/ConsoleLifetimeExitTests.cs | 158 ++++++++++++++++++ ...osoft.Extensions.Hosting.Unit.Tests.csproj | 1 + 16 files changed, 481 insertions(+), 41 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md create mode 100644 src/libraries/Microsoft.Extensions.Hosting/docs/images/HostShutdownSequence.png create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.netcoreapp.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.notnetcoreapp.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.notnetcoreapp.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/Internal/NullLifetime.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/ConsoleLifetimeExitTests.cs diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs index 128858a35c3..328a1b870e0 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting.Internal; +using Microsoft.Extensions.Hosting.Systemd; using Xunit; namespace Microsoft.Extensions.Hosting @@ -19,7 +19,8 @@ namespace Microsoft.Extensions.Hosting using (host) { var lifetime = host.Services.GetRequiredService(); - Assert.IsType(lifetime); + Assert.NotNull(lifetime); + Assert.IsNotType(lifetime); } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/Directory.Build.props b/src/libraries/Microsoft.Extensions.Hosting/Directory.Build.props index 668e3954f0b..1ade7c70707 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/Directory.Build.props +++ b/src/libraries/Microsoft.Extensions.Hosting/Directory.Build.props @@ -2,5 +2,6 @@ true + true - \ No newline at end of file + diff --git a/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md b/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md new file mode 100644 index 00000000000..8126cb7bd49 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md @@ -0,0 +1,68 @@ +# Host Shutdown + +A Hosted Service process can be stopped in the following ways: + +1. If someone doesn't call `Run` or `WaitForShutdown` and the app exits normally with "Main" completing +2. A crash occurs +3. The app is forcefully shut down, e.g. SIGKILL (i.e. CTRL+Z) +4. When ConsoleLifetime is used it listens for the following signals and attempts to stop the host gracefully + 1. SIGINT (i.e. CTRL+C) + 2. SIGQUIT (i.e. CTRL+BREAK on Windows, CTRL+`\` on Unix) + 3. SIGTERM (sent by other apps, e.g. `docker stop`) +5. The app calls `Environment.Exit(code)` + +Scenarios (1), (2), and (3) aren't handled directly by the Hosting code. The owner of the process needs to deal with +them the same as any application. + +Scenarios (4) and (5) are handled specially by the built-in Hosting logic. Specifically by the ConsoleLifetime +class. ConsoleLifetime tries to handle the "shutdown" signals listed in (4) and allow for a graceful exit to the +application. + +Before .NET 6, there wasn't a way for .NET code to gracefully handle SIGTERM. To work around this limitation, +ConsoleLifetime would subscribe to `AppDomain.CurrentDomain.ProcessExit`. When `ProcessExit` was raised, +ConsoleLifetime would signal the Host to stop, and block the `ProcessExit` thread, waiting for the Host to stop. +This would allow for the clean up code in the application to run - for example, `IHostedService.StopAsync` and code after +`Host.Run` in the Main method. + +This caused other issues because SIGTERM wasn't the only way `ProcessExit` was raised. It is also raised by code +in the application calling `Environment.Exit`. `Environment.Exit` isn't a graceful way of shutting down a process. +It raises the `ProcessExit` event and then exits the process. The end of the Main method doesn't get executed. However, +since ConsoleLifetime blocked `ProcessExit` waiting for the Host to shutdown, this behavior also lead to [deadlocks][deadlocks] +due to `Environment.Exit` also blocking waiting for `ProcessExit` to return. Additionally, since SIGTERM handling was attempting +to gracefully shut down the process, ConsoleLifetime would set the ExitCode to `0`, which [clobbered][clobbered] the user's +exit code passed to `Environment.Exit`. + +[deadlocks]: https://github.com/dotnet/runtime/issues/50397 +[clobbered]: https://github.com/dotnet/runtime/issues/42224 + +In .NET 6, we added new support to handle [POSIX signals][POSIX signals]. This allows for ConsoleLifetime to specifically +handle SIGTERM gracefully, and no longer get involved when `Environment.Exit` is invoked. For .NET 6+, ConsoleLifetime no longer +has logic to handle scenario (5) above. Apps that call `Environment.Exit`, and need to do clean up logic, can subscribe to +`ProcessExit` themselves. Hosting will no longer attempt to gracefully stop the Host in this scenario. + +[POSIX signals]: https://github.com/dotnet/runtime/issues/50527 + +### Hosting Shutdown Process + +The following sequence model shows how the signals in (4) above are handled internally in the Hosting code. It isn't necessary +for the majority of users to understand this process. But for developers that need a deep understanding, this may help them +get started. + +After the Host has been started, when a user calls `Run` or `WaitForShutdown`, a handler gets registered for +`ApplicationLifetime.ApplicationStopping`. Execution is paused in `WaitForShutdown`, waiting for the `ApplicationStopping` +event to be raised. This is how the "Main" method doesn't return right away, and the app stays running until +`Run`/`WaitForShutdown` returns. + +When a signal is sent to the process, it kicks off the following sequence. + +![image](images/HostShutdownSequence.png) + +The control flows from `ConsoleLifetime` to `ApplicationLifetime` to raise the `ApplicationStopping` event. This signals +`WaitForShutdownAsync` to unblock the "Main" execution code. In the meantime, the POSIX signal handler returns with +`Cancel = true` since this POSIX signal has been handled. + +The "Main" execution code starts executing again and tells the Host to `StopAsync()`, which in turn stops all the Hosted +Services and raises any other stopped events. + +Finally, `WaitForShutdown` exits, allowing for any application clean up code to execute, and for the "Main" method +to exit gracefully. \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Hosting/docs/images/HostShutdownSequence.png b/src/libraries/Microsoft.Extensions.Hosting/docs/images/HostShutdownSequence.png new file mode 100644 index 0000000000000000000000000000000000000000..4494a35718f334277e746459e8f64e4900b4c554 GIT binary patch literal 58076 zcmd?Rd05hE_%~{+9&@@Ab;I? z6avA_^f~iW#P3^vKJM@bXz%@ZiESCjRRTZ7?lJCv7xJvDaH;(71KOHLqGSHj$!aYx zJQVxPbx**rP7jw7+BSCWS3UCMKmR;C_TcQ{50&p79?50(NR)(H>oJ0CPv(uIJqWxZ zLEV+w+N&+>ZdmFwtx1RfD`}dZ~g^m8-TeoiS`Ty`&vU+JkD5jN#6K0fg zR%nua_R8OfUC>n}+4r!^(@_ORke2c+%JEa)Bo=65a-vaM#w2s-p;|XTNsAqAXP+>g zs%@TpEVKG7I|2Rv;pj`)9;uvN#sG7f9GhnS~?YW6}Mv zy^`&IJ>{?kHc8W(d*DC5U^GY37bu&XOsy&xk{#{f-rht1e)36{;;jidL1JExi{@>b z3U8*3EPx3v6(Qe;p_Hz2kMdS3qz+Fs!g$5Wct_6lf5ov;@yynE3D_Y(A1v_|W_JDi ziPa;!{B(DooRw8HCla^{)c+a45*|-5m#Qy<3MAvS?-bksSUVsC07& z`*TXMg}EDaW>Zn;4j+gry?44dO_Llk88vicUEdYgo4*wE&=`7Ys6=uvz@Z<(J5a4X zqR|#a7%=^Q!?y>0M398xeRhHe%;VkKHbp1e+gNlcy@4dXyyIVG(HV`S-x)Y*n!p3| z!uQF{@4l|G0hE52&VOZaa#j{MA<>dnGTY6*>m4~giR7ivr+O0hS zl{=-GD{~0t!Oy-gf?G8z_r9;(F*yGVvJ`A^-nqQ<`;e4~hyGK?M_m60HGBBmY9Djv zVVBiOl^*cem53A@?!D7RtP;_i|B%*o;Gtc9N8z$16oA+EMZF?~&r%5V=uh8=eGB$b zr^4fHTiV-f1~KN}wJq6kd(m(F2mhOg{@Y#YrxJ~y_kYgVvsKM=(g5(i*Cx=7Q|&kB zM(7$Dkp}AVt^bab@&7(^mZ)kw^)$@_E{@Ws?Fb37wcGoBgavN@D=g}i<;uM@t?{)v z0xZsY^Y4neuLJ`=_Feay!VvG%IDXWi_6CsGNp54+(6Y-eR2i zyY)lQ2*8kw@p<~kmD2yoU#V8GA=oyD>AGy7s5ZQa9Ys5y)cji4X62K`Ybi(QOYxC0 ztQ{^@=Nx>a0{d4p%ntnPNB{rg^8QaP!uU5s7e0fbgMIk|Do@MSd%Pk`;fGqaZIB|m zc6&qXd@84+W(cyr801UcZvV~qtOot(0v`-C`@SarmjoA)*r>NRpv)8rjA9NXNVx;1 z1$~=#QVF6g`;?B^F+5SUDNawjx?*i}YVa`zVfn!INKv~9eN8j)XbD9p{~S4Y9U;@;UWM zM)s1EEUR0iReymvZQh?WIt#Zz$k$fVxL#?MTAsiv;U-ik@1tv@QF9I#olm8;G2t$|VKmibyn4{EYCyg2A<7O<^eQxWu} zx_yiNdMoWkvl%40fPkEbwJm0pYqtLr+BT;Rioke|C8+u0BUkJd-~Z`9W5%#=DrYLx zhsV3bO6#p1`mb9ltb{XP-5$35U(5!?ZI0NCC@P(9TkCeaffqha%Pcb|WngHuwLx!! zNmBFZK-vVsQur{SI48^Ul_hsk>UgP`*{3PAz(mMh`K{`N5weaZD|dQFSXnG{6IF<) z$|EMrMMUtzONe+|h;55#DouMb(``AN*U~KQ{aPQZjlWUoe)_9ivSL^fG97Qv*2h3` z5C#w)y%JHaeRA#<^NgEKiU)rL*fNJ?nn%gqse~nW>F#_=LZ$soMAq~UDx6}|XxJ#V zPa;LO6a9=Gi$JcCw1u2<$=m6odPnU~8kS-1)>Qs@F@e@5gHh1^k{Q0XBBB<0)i$fmg31bkFSao4?WIpEYH*qeiJGiZ4Yr* zpUr<`cA_klAS`iwShaxY?_YtYfbE$Oab$|bL^7(Z&vS{16};1_&AdVq$9U@2ooHwCBezU;3El zC)XGb0B?KHyLI@gbl3xvrHIyN!aOfcqj#uXn#K%I*593pc4uQ&fieNvwxh>R#rRS( zAR;&!0@FN|C|psuzBqw=fsesAS5m0R+) zX`r@k_;_$elZLP01etUuqM0o#n0!}A?l;})_`N^}5C&+kv{<)kU zv~JvLR2XlcCyUDpp-;kiakrt_!QE^@&xv;4OYZ~{fCL8TvvmpJXu}00#8VMrjZ(g; z>-EoN(c+8gVOy(>$tw-s9iuDVMKT}URAkteteoq}571>9T&PCLe_{=@Sl8)=H_D?qD%2f^_$1ZG zEbOT<^knQ+xUtiMeWvHBZK2VMv`9>>*Y5yx04v_Jyi5{=PEfw=7)%=usF48t^P8KgYKSW=QiIX_@tqrv$ z6b<@0ou#YR@zxUHpS3VqMVpOxw8`iFyu3K^3Lo)pZ~o?|J_3;YHpNS>=dLl+-6L@C(VKj8_pqy!`Ydn_AZ#%e*-JtX)>3f)b$?27- z(OwSa<7+{l!)Z5{76W!hghwN*p((yit*dX+TuZHYv7S+M@0Q?~?*^y0Mdu;T+*nsIBIzN>{!$-@O;zC( zrp$`-RynIH*!YmYR77L+C-Gc{$e8#dXST9v1Z4dNmg0( z!tB`cc`sDOPG608DHYXSU&F4|u0IQw{Vip&m75yHRV;v&QDyBD2fY_+sx{3IQs;>D z(WZSj4HdGPlvSP~odt)pst_TZSFUh$?c6u*E|(94gQ$mJrF6}uQyOMhYoR^S${Ae= z&Z=`(1?Nq>!Uw?1=X~Ns%}iepBd$GUTm~EJO^BRG3MzgMSsZ_ESL{J_XCoM5g~OQq z7E(*D@x11hluV+&&63GY-om}m8ogp!it}ut{~xc-L_QKWeA40|b?hfT&z7~DWLzTn ze8zun-8x$?f2N@wx<`I9aZey7l0^k)3ySwM6{KQ zCj-X)5okX;YNGL^j~qY;M_3cED$@qe&6k=fDEY8cMNKU$s~jGNlQ(kG4FbfY^@i1} zAi&A~q!E7qSLeQz#n~}eh?^MU=c4b439(syaRxn!Lv2yWwe-7Ojm#l9^Z31GcyXaO z^n*`iL~;S56TMz%8>vYSnrZDraU{SUZN%AXTf1v%yjtL zfTIN=cJgq7p-0$nA=W|390GkFnDj9uSRO>p@9&KvqrpuNi8bw1jGti5)K6IsiR@S% zY2NoA#^KFDB8|iNqf7pE?9#pwqtPE?ZX2Nw_E^niO3^PEN)g*@HeAV9j^}8m9){O0 z3-u)}3p}&;tpl>u4e6EkWY94i*N)E=0uMXTG*{VmLkLp|G$o)H?_GRQN^p!<9WaxZ zjK6&^m(6A*-z&8j-c1izkq=Y?43bDERX!T~By!x%pQeWbF-479o?VRLN#DMt%D)#G zeQ4e-(A)x*DDV9jMfi&vp1N5ZfFLoI6X`@*R(56e!koQ>^ePSNB6uV^5 zPpLq?#sW=%i0d$oDR`RUvbR`1><7fEx1U4cu(d#U4|W8G)& zAk02mhO0{p?zCT2j&;5tr;@}riJf5Y{ZanWF)HFN6-%do;PUIzTT_w+4RhVhE|#$DK{2Dyu`Rq(NZzKVlINm^Wu9y%mh<(bWg>Cs z;M#r&r6VgR-H1*T?$H-MI^1kVSQ^q*L<2Z7d+pxY+<{qd2xguAUqE6;Sb z2yB20=<|NC>4oR^0<)*U^Rc8mbCxJT6~vN0^xI|7=FGtTp|>mWWEK=?O{2HMC_#rQ z*(r-NomBQu@efMEe{NmkqT~>2YHQiBIY%%DdvDOgm({KrE=|M2PdQm*2P}TBjVJwOK1>T>;Pe*4=lK9ud>^lf50lOSVnNs37zOuM zX_4jOORWy&I5`9 zvuUI*ueiU4;FBq7EMO|Js~9Jc2`1`JJ%f9*CSaY{IMKQqCk|4-&g1ni^*vYV79eCt z+1hS7DewG5Bi3N^_`)gsl%?hb=rP2&eXCRT)qHg8?i*;Y4W4~=h1~%^D&5LJ;rqbNC6Uz%P;&AQ|-}| z6i?K$$P*K7{&qEZ49ECg{MUkY|J5WA*!pvMuy&=+m-QeAIsT6zd9b?6opd)Jc7Z56 zD}NzqHk3S%lB{)j?gU9n{myZd^x3}JSvA>yls!m_UU{+cpD;#5Gb;qYT7QOEzI>dN zPe!$_GNX)JHDhh0!$Pegn;ZV6Y<1nn2)*th3Ny?cWu?lPjw4S?95dh3EqS{uLadA5 zkSwM1As|hcG0(D@z7NqoOXbs&4(F&F#8&qmh*%4`FwZ-|Y(3K|M5x7k7O5x3_#tAB zlRWcJ9HQ?v5={V-i7zXAO2np(u7vhQIE*;xq2>0QX-ua4(;o+7*ETg9ltiUVj`vs! zGw$=7ywoz+pE}{t$K4H<9P!vac#sefE=3XoF7FLbfOV#qP z0w&!yRETDrPaZ3W<28Bneo?FP6F4?Y(AdISS{O z2{ci9ZlKV$^`f#%o zy?mb=eXMv!f!o)|_BRA;JuDXVgC!Y_A=+iMTVg!EEN)Uu(^I+jPwr0Vk9IHeqyx0( zpz@W-a;{C+Skh&WF^dO&U$Sv`)ZLF^bjO*EQBBUUoi8or? z{}gKcg7`j0_9wFtf_mOtV}E#BVCges?FxjH1RV=8 z*P#~##q8tFyvmSJ2R-X$xnMaET65wF&)O5wjcz7~1z0M4t6Qy$tg!=nO;Mt3r~QqO9WRpojg;X=7YqgWkMKzNwr@biUc}_@J@M$>nZPO@N*IFY=xt9pKkK+F4o<`y5H=)mI*xDdOIrAMsnY ztA&72sW_f5miIRf-y|^?-FZDMdk|4B-sWkW<%E=WVH^X9O~V!>H7xY_Y>M6shB?gM zBGxAmXb|r+!@o2sgjojCmtQCwf{d`kcIP7n z!Uj7m^`Yj=LZ9$wncZ(FV7Stgk^S4Zo1zTTiJNmcAXefA?~h&O*6uC`x& zG0;>R(_4MubId}OHM~l z@P-B0RsMvZYUmELa+)tDsW^{`;5)X*$fV!&kkttw$6n)XFe4Z+4;4_ z8g^gDyd49qE9b^Tb44RT?wGm{OSW^P`uq>a%Y&bu$l8vxM+`1-!<<^h&mE1*fPMMy z%phOIrX^LJb_QP_=vCy@Bf9EW?pQ`4mc$}+O-0!k$+ZZAHbV+IAI7XTkoNE+DYnE|;wX$(7Qi>Qh)wqBJ?D5Iap3a>HtEI(?L!li5da{{7RR@&5VSo{8jQ z7{pKQr#aI|J;D3NlJlVe&3`~cj{x+BmYbyxVGSejlQF%FJ29fW*#Q99rZ{7=V!fb$ zTAgcp)~nkM_buptj&o+);|m=ThhShb_*nf&BZWLaQI_YUfs(E#!EUGrua$ZjfP&YS zbX~z}pTo}D*N&xROA#_h?DFZR6c_RGi+IAjql^9d_n%P-H<$y`sl<-lL)x9g?T3z% z&Zt@X%^M^2VyoI~js*Gn>Im}g&P~TXh9>k;2aVlQ^W*w%hjNF@ui^v2#E}x?%3dNi z(02K5y&bCO^-5vR97B-Jnp1DKh-`XC%sYXT-7ePNCs<8~W=YcFUTd(hnkV-5e*pym zU`dPi{J|l1%=gQtMCz^JdKJi0c)=IZG(R^ghPoGd5!)5x^!#&ObE*p@)K#$X7?mqdl`ZB?oAgLjk>#A zw;~8T1%muW0vn(1izz+-OWO{t3xkoFQ~6Q?c6;6yQiHqMMQgtpXN+J3{&wHE4#ACv zF+6x08HQv|abivSi+m_(>HcyH><`mi+BmO9RE)czYp&98mFVZ49-EB7f@RV^NY-e; z7=KHpMdT1FSs$pyHjWsO{!z%HdEF4?-+Mv$^Id1ApT?_7rzNfx>mqy7=PkYx|0esw z%VeK~;*7Et*b}mJqdKiv3xTx;04H&9i6kr-d9`VPW;{0CGs2ih$aM{O-$Pv%I^MW5 zi|4tDT(2lU(!+H7zWS$D4WAy*8ZtyNKeov^^ODS)ER33R2drC$1{mWnweFY#h`*^{ zF7KKkiVICc8n3E}Cdut8uXkLZKlLjhz8cs+B39j0SH>j`(!yV!5qkX&GxV?T7q+*P z$Lg;Qs@+=k?Vmf>n*YS~DWts*y}2{A42#FN@1q!A>a+yRh7>LzW30Fb#Ug4awP2KY zT_5RbVt}B9u+tYwb!Gnc-s;2e^!5)PFD2D?7w|>D>(~E$V(!`Uxv=EMDC>wV=1A<8 z`Ic=H0-8_5xZ1@_l6Kj2p@@}PzjjfZ(%WT#fEo626t%6&p=%$b&D^dw_N8gOS`(UV zr)|@KxlAvNMXYSt8+ElXuo4CWkN7jgikf{ib?MnLdwuc7BfuFP_%m`OLzgCU#qO?A zKZU$f&rsx06~V+id`q8_($EL6c<`qXdcYu9#s$xC!LwdM99T+Qy9A`!9i-~rcHLMk zUuvp0@I0VYFgaPm8)y$6Z?E!-V_gC|Ut{xOQKi4e)NjE$;Qn1A@ z(Jdg+`^f2VGx*EBlN9qiL*n2_OVSx*7Vk7Ep8Ao@3_IX_E>DjAIR2~dO{=!P`+e<( zZSJQY>R!WgOG(UbK@d>GUibJNx0)?ueCGoaBr@@KuSi&358g>A^}E z`p4MQ*F1#I{MT1&BMMg2SL=>k>jzgr9eZ5jXx5-Yxa5n_o&J7gqq@$_psiH@~Um)RwCT=R>a(n8tr)M+^aqGHS!mnacuX9 z1HSeIIFQlyLGua>5IB<+!ad^crY@b}dQWmIs7Lq?`7v!#wrt0uEB^GAPZd|ma_(L$ zbH8e|7kW2~!~MWYk#D;HCN=`N<~NT!sq_ZwbarLEddFckQomdbJBpwEySr9z`=;YW zemf82Cp=CJFAcS@%qzoonHSrpvk|o53TLo)&AlrI)vdE5GgyuS`lA=W4dDbXeEv$2 zihX}&ynAHF2bB_wX}IWF_;~YZxC*_86(_N$&AK2PkH-}s&nRcFWWVyErR@KQYl*%} z%5&hp=utf&`tphMjzINQ{M+;e@$&<&f;}#nJ*}2zP3r+ae8KF zxNy|%y476j1D_W8G^-rT&SI$2eV}Qd0<@tYZbURxmo+cjZMpgzJ74|8eDHI2EO;Lg zKhV!nj{-uWHq#BnC}y)amn1o}%C-WxXDEdBy00Ny`Rn0I_$Dog>V(fr}vagh_J^H^Y7MCTB21S)jmlI$ykuZ0C z1dPITmc7=Z7&b}Szt}S3i+D&wOzSMs8X&8Fc8X3DI= z)zIHX#_}ZgGiy!v2WzZq`?##T?bHFtjtPzDkVx2aa2xe?V2#V;y;%H(lJtkP{;XbW zK54Dn_w0Q}%j|NoLx447)VWDrRB>58)!UH2?dx1R05xW1g`kg#0!nZ(%h=_~?WQOY zP#G@mucZzCj} z^&tR1W+vH3FC;d_RegbR=L)dUw{NLem%8Yb1wAEco0UBpndN%3 z--7xE)}Vg^N5MIyiSzhl%poS)lTsUVN1TTwEq6X z{yxlN(L>y$ABou=RxmEPN4rvkFKFdAv_yD!O1uk-dTlPjW`8$$og{5rQ#&TW_@{uQM=8UK!~W+0C194z5E!}LhU5z~fQ zAV@B6w;-!3+MSsjYs3m`pJ?smXC;ZNJZsim=KL1**cbXl25z}JNtYak!9`?Z@ziVo zo0z)a7!iFmMqJt_kKla1J$rzbI<`|!KjaYclWWqEu< zwOgsb($#yCf;8FNN|a9LpF1fKN}>5kLO@X4n!|&()%CjBw=!3X$1sl=b>HgI2MHGa z5AOCwe0QEuww9$~U-V-g0?&TXdes}LGK0Aeo9v>;{We)A5aBTtD|*tHOM1`i58)C- zY{{~04cVK_?(a(qNy>hOHrh*JV;RrACiEj!RygG?kb&mjMkWC4nHHm2my*G5&S^c@ zuw5k~f5_2yM;kDSt)_sK>fY~*YD6>34URmYO<7gMI|$aQmrsdogn&!PVqWN)vrgUf*swaHRGDaL z^w(Kf+KEyzmsG%X$OfstJ1H4H>=2MVzqJ;P=k12+kPZh8Dr)Yp0w1;eh@LiHF3gtq zXTA8q0>%ht#z$z{;V?v)JIITu*A#pBjhV#1J|6xAnL6^7iuRHOUWKZKm>=ymP&*RC zf(v4a=mf}{rct~;a@ z<1BAxUi0Q6cA#@)`WjGJl+A9Q<_L?nR4f%H%R@ksAo=Pbe1Y}RQEf+u#`$&d^q2x} zY`AufI=p}}@tZlawrofT$yi4L20vW`WK|g4WOF0zaEoVZpeZ!E7X)r6H|Og*q_iSu7Cc1TZwWbq_xe5av+mV{2RqD#|X2ROWJOP0ET2Z#G`LZIc+M zJak*avrFLLm!$)EHc`t5U@5axX*}#hC5n|Vs!>* z=AJ4?Ois6vv^guP+18mdIzjGbT_3J+n*de$(hNnCj%)7 zAKIGA(4oYKxmS4Y^9Y@$@d*&UH0xLD4Qhut1v(`ibA<(Y_mDNb9W`D_29N{O|Cf8!MOxijs(wF}M&kOGY53+tL1d9v%qO+w}wbd+?0biIUW?oPl}ZG@WhNKg(N4MG?ZC`Fjlt3XxrdYsblt-GtogS&;pGtV#YjtPB)R?rTTZpq z1t?Gs5fZU!vlh1Y{G_+BXn-b7W!TuqO-cU5@2vkbEC;`Qz#ANS*C}hnC#^kunGdmF zdBSYtC12;q+Q&^G8P1+%vEoD9=utTM-56y!t=y-Fx@LRe&U)nL8*~}UX~ua#ZMM63 zofPkMv-2N_F2R3IZRCZJc=tuk7m+y$1mjGJuWw1)g%k=QdOqCz3_hRH0jI|oL%YTE z9Z|eB*AIe0F1Tesn507qMajgNB6<=pep{!le3FGr!c%P%pKf=}z=^?K$#(I?QQi;y zcaZkuB%)|iYB(Y-eZ7yBEG#<3^i2^u^ig9nM2%&i;uQb$>}m@kHgo+t0W+R5yy0|&?Apv;&gB42{T^@?G z&!vABNiU!r$%iNsIVFuimPmrNP*kI!eGJhzuzCpBLpEPDY5&E<$!Nz~ms`0_QZp|` zrsUhxJr^;&Y>NI6Ca1`;VP+QXbSvRhy7+)BCu_200T+2&WSYASKgx*RI3<2|iQ5b( zQ3+;A&DZW!nmKt8N%+>gZEi(NE_);2KZh85V9xWJ&2qMBp`;&a(rOp+!mtH5LEXgF zD7XIg&$T0uIYoFfJ3R~*vwML6TpLj&5n*xFf4BT)OF2q>Y}eak^;~ev=e$=9*UqQ^ zY7WyS>=T9b>%P$FBU8JH(~4qAY-!s64pl0e0bW-W=1i)QxS*x`ufVb*sH_Vm#pt3!#1@h{i zRfu~Z(*>mhRg@<@^c?PN?0xj!WRQX}rPQ@|9S{5J(9yFliPw@B?t)j}UvWNbgS;9h zp5~rA>mw%pvpJtXh#7Vu*T0pfX^ZEdJ+YRF=sa^ znRv_}yxCfS-6OC$k-`BaP+o5seSw8ZRt4L9gvuqSs>nj|-YlEzX3aw^0{-Owb0!6! zEe`o@EZ(!@=R!nE@Hb4%lNYs{ZL*i@9XV@lojZLd&I?Y~j)g?t!O(43GZwRTou5B6 z26-R3M|3#@BZX7gk5k}#tAcU4k&k~dxE)y@+QExnrMK1iRvCEv7F$n?*4BU?&9Pvr zu%h1^3<8vui>a-*n}5Af1Mr!kEN+Z;JSV8 zy!(|+_Rz=gcwQl~S;i+(Ct&?t@fN}HZ`DAGWjCDMkm80g{hbF-5ubLvBpxX*XSm&3 z>(k#SM3+K@nmK1O-*XePG!8?eHcmW~W#&?3vo($n%M24%J64kX6qKJ>S_~I4cOY0h z5Ey7H&g@?xM!VzN6!rFN5o#&F#-0iM1^={l%V)H~SySJm!^rjzOHBW()CD)TqMw%2Tu|)Q3PX?MQX$chDiiVHwl0@@d zuY&kD=B~|CD4WQb2l-xaRT{eHQkNi+VJPI0iJlbp)=~5tJuEGFXf@o2elsu9N3qfV z?GXJ)jbYJZBX5*@?p6VX+`STlZ~n@YhVHZrG;8+8WO49z811Du@1?QesIFt~0d{Pg zxt1Q)y`CsL&();j=Q{Dvug20M;%uf@DjWjRgXnHn`zrkKT<|kT+ZXULt#K6zPFkQg zrW{{D&4Z1Bp~2&3=uaN}Dh2g%RrMS7z@qNnpR^~(N$>Wll+It*LBsa`NWq%xu%9%b zuB2|hTZ1mBdOnmJS!8Dfn3#cJ9D;#{Q+AwX)Me5b!X zoB4|GN#i_b1<_|Nrll*`XdC@5{#{|GdjY7xF>}3c|G=q-ku;HSf zGD!nsx(UZyZnMjJ=3R`Bf;$v?gwYU;-d8xJy`WtRQv#o!bb|@{21d)?a#}95;&^OM zUNClOBFxRJ_*6tm(zA4^TROl-v6%FKQcNu(iho7XV*>~qHlY83*RRMjrU za?$%PtkPL!YxkL-sSLN}uTPx8Kyd>M@ z@!rGK#t%@L0x*0~fzy3=7)*)jUqyBHN3fIZ?PTv{I_+%!G5?@;BNYHnx{I<5nX7Fv z2uaQNk&+VH_InV~J7Syg@v2hL52Z!#hBR{WJ-VZt9lczf!u8(wjd!Q_Q_Xhzvh7f; zv5=bSrsRm{+B~jhA;W-b$v+n4h8a;%*A%=wg`2#kTCcuHcCiCXcDlAazR+@&0T6m!NIk=ax9U37ud8X=IydfFL#bDP=Ic4qQP z`k4$k9ftwi()YexY}rwb(Q3y*WHzt$HQefyORYOhtL7Y*%EJcx% zjQ&t2gXUXRP4a8rcPC*Wvs`|(`SXCI-68GTe@$4bC@JZG;|E?p%_bviH&rIJ4IgoR zaT!1_DoU!X`BJfy<{NH2bqz3FbM$%=sY#3c%>}&B0fk}7Rh-zw`bs9KL7tMHAL?_B zqS`Qj@F5J}QcnPmc{PWMJKgrSgzmwhQX`78%c__x8#eh?yC~E4?nGr=RwUvu zx89YB4Njsh#0*ab(11La+N$Hlay42?8jf@Ug#fiseYvw*IWVczZ?JW)!pvnIAcD3H zk!03uS(ysIgOYf-Xp`fgh7 zqDnRu_F5mhxv?ruJNdJg3jtXTf;S5)yuHEgM@OA=S_seq^GK5k!9!Q=?z#yrfXl;7 zKK8)Uy{C!b7tuKR>D>u|G+j z0ld0OUuC?d5w`o3F!-O5LA80;S}GwbyI6G{5gF;X$|{eNAh}yR-R4&^N+O3<`+%}( zT>~o?wC&dx7xq))vtFtk9q)h7VAXsUET+iMw5rIq`rhrGtqW}5l&=_ zkMf^Hm9OHsyTsGvU-r+FU!+7-XN`WTsWt>PLn_gy;Zt?W7*Yhd^Su|q#Mh}DN6d!# zi9Z+)K3r_M*)}@~H8Fb5!5ei43i*w;TG0!)D?*az`wp95#ql2)fPHbcZ$pG%*N4Wh z@S`lt<3AS{y%jWM$JQmAz7LyA#R^>Z(HzC)$RJA`8is33A&9~0bP5Lpz*0G{U7a@# zbGgc(`2F5*^indFiGuLfZj~O z+InS&62AA#dX>fa#~9p+tfWHzxO^Ff;r>W&nkekg(Q0j}Vl!;f!+QhC=7DT}1%AU{ zS+Z2Nx_;M6Z9AsXF1~5lv(E{A6UW|TpSa9b5@7mZiF~$wygM$kNA!5AU;6n=%3VoV z8$rP(n+Q-)xL&r-?u;+0HDU#N$~3%NYkp+~_US5AW(WNj1tDnvJvPWwW05Q_f^*6E zI@tU-YKZfvt7&IcM!pf8%4JkXOy|gtcX9rd^_;8ZfXZduQ}VeZ7uZPnovI|?!RI#( zUHnn66J`_)qIS}gF+zj@@XQ$YM69n5QCmg8JZDMqphfMY=V(e2ood6`@v>(4z`cg67CxLk@f zcH90wT;(oMj%Tm4+7&Qovn%!NdmNR!Lp-+ER|QQo`itzNoWa-qc-$n?eqbz^_6UU# zxX_z(o_BF2NG~61V##I%VTo(3fKJb8cWr(`OSz32BNKXIfU9+>paRKT*T^wD8s*4O z#^!U?*^ySr?TP)>;(h{}t8LTW;m&fB*|QYPW`neEj`H`j*GAN;pvzdtJm+A-SSlR|7xrX}MC)wj|W!D@Q5phZw8gac#W zcxJ8~E|&Vq;RxLg{;R$qgJb39pwdR$W$}76QRfW^geo@pue-zfW1gu%{A|%`OT8Qw zMT_YTq6Wg_vX*-F%Znq|X)Ryg1z>m@A0;@zsUr~_5J4k(I_w8+=xkWmL2pouem3oues;AEplIcoX{yyp2@a{m}m8u=vw+Uf&?~v zn<2hDXH{{w!OGY=z4mLbvMU0xXvhH zrfK7Gz%QFWst5rm{yUsNrCUAIiX@?8^VE`}r~+R<{LQ>5z4@@SB<@=52ZU5n$v8+s zom5ha*!-QSyB{(i>Zky}pOd$XIg0(mVQ-4vJ^24HskT6ku)2^_B;jE~SL#A#Q$^Gw z4Ezgopeci>$U!lAzvxf3O%{~!PEizK^mx_(IAi3RbcMe`W0s`APEd4(^{AUHrw)~e0k$_JH7G^ zsVMi=la8U#S|C5KED4z2Pp*Vly*2i2mCBBnryGcY3`H%P@WON{_W6_%vkO_Ppf`PwF=g zwW2TU*QPJcip0nI^*?ZsO4(^Dh(6G^*g>QodUNEzClNHPyPdR(<7wJ%+V2M0U{apj zuhdypqFkWLFf_T`0|<8df9@0))~DaSEwZ|lbT0juBjuSZ*!I1DO96D0Q_6O7*|2F@x51mQe5s8C2@;kN}N2Mv{95Ys8<|sJbqq%&g zrl)ONQqPIH>J;Z;46iB}!c;{Fyt4he4P(n2sJq>!?Zq)#*DZ1}Z!YN33y%KZIToRJ zltqyJy`C&nTyMtr{Lra^{hAXM)z|ttnsg@L?_^QOqa(Xy@9mS_`5yUl*}q4Qycyr6 zxKlhDV)to>Tk>cj=aE15Ym=Hv>N>|&>6STKac)mVH1pkhUya)qmznYXmi1#vE5BFN zbmn-yf-g3EC@gZ^%+qc&HT;^C6oQic$$x2|GI?V@QCC}Dz>K$-Gyg`xIos&NB}pB^ zhl-o0tbx=vVI#)E7`BQs53%KXR@5XLnlp-ZB{9tsIxA zAU&t|$!8P=X|;CQM~(>v!o#FN$~cc zXo3exc)_xuChPCKg}dk#r2gb+o1_Pz=tKg!hGk|iy$95=E$+j+`zb}3DOmIVm-;sK z{%`EPcUY6z+ck=cB^GdCMic~e6w#43^kz^QMX4$(y@`PICM7@!D#};@6$ltgWN0#! zAR!4gG!-R4q!U622!s+s3lJcLoF{-Y&is7e_k8EO&UMbYu9H6@W$*p$a^LG-YwbsK z2M+!+B8CR22d0T(G#c$$T+AJT{$lE##-hqEv1w#$|N0!(uGyu?b=e9??j@r3yqGX3@pI4u#6whO22DTe75Iyh5nm98(JZ-y^fAlfeh)M%5wV) zx3%LxYm-B*$I3evPuF@0l`;@ufO1W&x8d^bazZ_g_uV7Ur zU(cKWSUz#B{u7sDHA9LXo~?U_S5Oz)z4);9^o{$iEZfCIhEKuqu*p3Sj%``+tNSPv zY`pt{aHg}^2|kpL{Pe5r6CR)k!f@wM)P`VFXBJmdUzi_+Wo3fl zXmMtF^qn%J%YtP<13vyo1CBHqO8suw+}1H~Q-CNlIHmF%>!Mr!iFrnx4>mG3&aK|~ z7&(W(s$KooL;504l4PNU;YTr@a~Vs*I;-~s&Fh$jeRubd1c)VoHzyttNIPeP7qVh; z^%h-yhDL421kMD`do0*MIX^LG*pds53?J`!2D+m%Mr-a}V3N<7qjP=$SbMS>w`I}t z6kYjL6Y&l4{<$Hkg{>V#M4dA3l4>!%=HKVy7}}No^FD z?qbKSHP9Hn-+;k@*()E7WLx<&Y=&%+Cmxzs1onl ziWGb(+Qy9nPBJjJUisD7er2b3=z;{&v@0X9pCmudAOzB>9`4Tq=9{yaGFGf{h9%p#t7kpb;T*`u^{=UI&ES*dZdc1#fESI6uoLF57 zQSKDk837rL694B>+8#BNvNE;!An1(5-KDN`+5)7x2-@Qu9kflgSNl8nky9?KhFXRZ zWjFQhux56!3tHBX`7`obry0j2{Q$mAC4Uy^3R+M|fSAGm&ht5J{9(qaA*bYofXfF{ z0+N3`KdL^d1$J5fsF;8K{y@%|#qG`8b!J683)mf-7D@;8j%Y`0Qp>75xshjAN0Lo$ER?p5F;fziN)j-eppm^iqVyy}JhTpvy1Jxw z3s7g$s2)+w>^}A9L$^;!JvS*BJxNzprW{cxI(`I>s*Bm*34G?q8n|cuJ71W%r?Fmg zl|RP3Gf;XDnz^Qg4Q$O4!5Z`*(EA19#6)SgF z(K0P!KGh05AH@iRyTxc74MZ@En|tvLo2?UB_eU_rDV9xA&tvzpvPn}AWw~8)k8FXB z`}%)w+|D+cCRF7;DQk|xm*5Sd@@&GH6*k2xYlkaH!<|QANxy5hf@IKjN-ssc8Zll< zd0ml4RZ+9X=-HKG<{$)DIZZ;_j_S_QczkdU$Pm_n*PWQJ8NNYIG_bMK~pLO4!cX0AWxO(tRUs5#QG8V;~4XE>0UMacf?Y|chsHj9bh-YaGN7fQ@#hVmzf7o!p%in%a zAwR+RqCyVSjz!VW-GI2=)OczBL};;mgkDT88phOB; zR;DqOc?|IYH>rl$y7&D8E<9ahVqoZIf$DcZ_44mX_J75i$o~$Rp@k5!4Tfm(C;j%l z_{->sb`EyB6h{hYRRNxDL#MtIN6Gwc>+-*A1^9KOYHoiwj%8OluRl&Mjjhrkfz5)v z^NlJQ;v4#IbhmE!1GwU>&x9?$?*2+RIw=R#t-CvN(VZSdd-W52!7cjv&pnUr4q zP`@*q!iq`T@+_y4FUjQJOW^qCe(-bYt_ZW$R)6wN_tW=g_rT%bS=xrv6<^T~p#e@U*_NU`^2}dkVULvo`aNo8 zXf!%a7B2cH#>=!FNAir;5J+X{qi(FLJGZN`xQmcv*#YXPAg6nDTLw`YL!WjO7w9fcaYaZw zK$Gnri=pzN?#wn9uBCt1P@2jLHMM-2VTGJOvkDjFII!39P}P5=nzgDCJ&{~M`GlfSeR=)%9Bs9{*ev{~P+8;~*C+M;=BNIn z*E22W3qb1XE;Hx`3K4!?sLXF<)Xx7nU|(O8*10?P-$iEHP`axJa;y6UxQoX^yY3s- zB=0ZnC~6LMv=)B9+*E$i%dO(Xb)%DgB6s1Ixc8YOw)&(43#+}NRX3V@`B4j8m*u%M zyl5?9_7Q8tmbM%`lDAMIbn~W96i^c%LHI-u>1;5$tbSXsjPep zQ+b7^;|g6_)C;oeE+_Qj&ipa0ln9tZc@wmP!q8B`>vh*NIZ!#cZ}-;Mc8yPyCD&?Om1v#qCa`d3Ndw*< z$OrjDv$9>pcgOCDG$`sxwY?plh3rrIahG(7p?^cbSu0y=DhyOacS`y3_2NU!(AL3^ ztBGQ}I}2M_T(UYTdrk(BOtbNjbCs6?7k z-8%MzeYm;^w6O+md5LP<9=7~6Sb1g z_Bp8Bhb*SrN}Lw_Oo6P_m(&f zd!}WU^WuoQ>=x@8VOT46_jA!Y-hQkg$Y19_mXB_6!+LoJp5X(~BM%R$&)UViT^ded z4A}QcPcPn^V(CmY^g1xSb><$m_88|rL|mC29+EtRC=N}_Rk4c78F>+*ROAM1mOC~*@dgLhGJgAu{ zN+8{YHezjE2by}vcGN;7{(as7xw>v__^P0?nfP)E4I;m6qA9fV#5G%wy{;Eal^3jz zCmEU6fkf!pkp}7^3kzl?^_B=B~6p`fZ?e7IaN9GMP^Z0KCI z$&E~q`YbN;Tetq3DxAYO{)kGNL6L3u;nA9H-3vFKvGqQADL$1&Hs z%>do!#b5__{}mAP!n}iN$fZG%lKMT=<=()9xxc2YJ#2^l>h6xYZ~yPpmzUU;4=bsu zxuo3v&3^_!r%m}*fQa;F!$0EVL-zQvH9GQ+#B9yNI|C zUo$V)UY!>X_f`H02&0zPqh}BEzalRP5JzbviWl25L&3~1Cip7o8ZYTE)Pa>1=UaUE znLtTZHCUe1Hczwg#2qG#1j~(@QKD=2TLS?V!fq8UtpW>Jo{r51`n@-q7o;aW?yef4 zPVdG{=w*8!2lBniJ06-+XIqYKA>&_*Ay7F&5#6y*<*IzNe3+?!>oU?qavi?o&X+wkv#ww45XR@E5+BOLxOW;?%RC%7#^AMI z8B1Aal^NoNIcN!1eqeGHFZe+yS~*dP=kHT>g2{QwIVrwVg~a9|S;>|9eIhMHzY2ys zFB;r1Y>Wtq^HFr501*;7ql;FlxiT}?B3d%k~AlWp`^}6;;QXKQbZ%a%n8!6;}1C@V2DT0LH`g~2bWK_J5-RsR; zTY^*|J~a=qjWzN-CxUW6tDdvQy&8B!HLELMQ5j9;ybN43c=%`5r;%%+a{yR6)k909 z)BccLe%?+VYydOw1@??CcyqF700*cSV`wp&Z=ja)tRuXc+etv4_0JuVls@Ng4Fp$WDqX3H1t(W1nca)K#UNWsA5fDKZf_f>{XvLMv9w=#r$J zQqd9+@KkUMH=9 zn4qKXM44!FUNIDe{5sRSl67W687^H$pi!=pECDTA>;*c;i^mzv;_x`Q&K76!4^Cx6 z6iJwYR=!jJf@*K_h{@~Z5&a}{LUFuji5mO%%MQb4MQQ6omLAq@Yj0D9{9U8h7FgH1 zrb-=DfqOZAJ@8ZF`VKO#=_@4HU~ewA_fI8Psxsy7d32qkzq}IEYgs`dPPnIOGYMe? z)ze6{8r#b*{gf-P+%kAsRgGv*CAB-*yKi}f=8m(Wznv-dZ&1^-%64^EI*~#+hB+xb z8J*`~b^4yg))Lov8lpm|*hxa_O+kWdUTc_FF35UkJ7{2zG<5&dyh>lfk+31Oul;o|DTj0Ck^%q=2%OG$J1iZ6XRsBEa$sd;V6%cghohTP`GzIpgAdsv8(4Xv$sxO=4JlZz<|f%8K?PY!7JlL@4}^d>0Zkx zS*o|s{~1{iBDZ-=D+|%2`0}<5<6XzyG5ohcefmb^OGgszeO{J-py>l4C>?%8(MTpJ z@l-QAd+`on)p==Orw8uZo)Q_@`G)d~p7G1w%yBPdF@^4Q$&`TW_igWBr+48v(gh~t zJ8@w*A&gSbwB?{gm7^Xw>|7>MrqTUpOu7Ld*flv&r@G5#abz+X&$$x6+(GD}c(vjf z8Qx+G1r4R18Z8y5FkQf)re8e->#a4g7Nu-QSuCb?bDLwABm{~cS+n@Q7JQ!H=Sza~ zfS0VWH9^=)KW)SW7VN+J!_JuODYC$sfi2CFYt73~w+@~>YGD_lDp#6%V!zxR?1EZJ zUOl!`Rc=Z4sC~!LzDx!Gw5@}#tWTK>764qi;`Lini4psL$E{=A>qgWO&K3=q_mQ>m zRY4e!?)E5DIJs`f`y%lst%f7$+M+qqM|&%{*4->$wJb`l-_n&tmsl@0sTcuP^);fU zqy0No^~C2s)6PYCFPH}(E*{yEA&WMzE9CbFb-ICi_01Em4{Q#_TUi;Ww^A-A-wW3muX+XASfF{Q)*#b@P3 zZ0?X{-Dg*v%dcMKK21cw$n79m7iwP^IhrvuRv+sgm3Oq^R+moDHR-;rB-Tz$t%`_ew(aEgdN&nqG3=P4PJNY(TUH_nDM?=!;;cKX%* z>09n#wRm7B`8WXxh{|Qn?$^qg zB9sfAEEK8cEnvv7uCaI2I+~nve2Xjg#v&}JTFTr=wrgZZ--{7LqaMJnLx#C0ZH2&4 z(4RqkuE_IXcULbY_BvdpwRgwgm2siob&nZU{}_UT7l-HmWZi8}%$T%0Aj}U`f^~le zLhGHLByG`cNroFR9U-nrUd5gc_|95ocW${^SAs_r+l)J^)H*}#K2&hFyJ#C-mX4G` z5suxnx3hakxauZhGJ!1Zcp^9Gd(};F+BPkfQ?t1J571t;QKP$X8jTYN4tnWnAMW8pnP3j(5 zxJr~(c}AG$J&wph1~=N)DyAE_i6j}COl(w4)B~##x~hJ?WvA-7|J$QBdW)EjHkovX+-419x5~myu31;V-=fjh(T=^wm$VAuvu>vv%)IW8 z?HJ7c0mefc)cE&ODhh)gnNz2!fGM>3eJU^G(_nY_XBY1K_IEGu8`R*zLk0W!vj5ls z@9b^Yi%&gf?b;4Ac#mGuTIe^$;ti0~z9vR}J}I!rQ&bb+-A~V7#HsNj@S2OqcyU?8 z76WGIcw#BpB`_`L(0Ib4R@D!@m>2LU1@o8;5w#U-UGmC$;$bQKd3*Bcx`!+;%iEz3 zln?)ZFqStIK+1b%1(@!M)_#}$XWxtPDoM+uNFC8_=9^RhpS%Sl;7P`xALw;Ywh@f* z{Vow>J?Q6s^FMB~0kCFzJ{nPA^U;OGfd(z^<8`$+W@mQRR|MHWgPR9q z#RH5OdIB-bl=-?I2O!K%lGMDv*exYIF2^4EO6zlcGV5nIuNDmVTf4xWu0SN}a0Z)a z^$x2e%6@WP9{Ba}< z@pNq=NE>i=_t>@hnT@oGkG=mMFxe-qrqo0?Z|YD`_;)XXPW|%|q5rQ}+FU$c3Ka9C4z%?Q)ZvilAl~{mw7IM83|;kJn%S9FvW| z{d+q0@}}8C5!-%&EhoOq5fa`-%w=dzd19&1>9IpK!QZQq82pb94WI>ldDg(WTUeVO4{a1wz7&STFrez3D#+ zX#g#HG*o4s>E&77EyDwg7O7#?w4Qpba;*B)^T5OQhc`!)ucn__CAv<(p`A=NH3{ix zUEe4EW{%bPRZYoq@jTvy3J)sDI3;Fff#jhU%1*^2_;8f~9OtPmcbN7QY`!lrOu|Xb z34-oxKZ%kjWAmp!2XrUDmkS$pCo}v3vkov~EAtb}ZJm*~UdN26g3rj*o@c$NxknMI zoFtH??zwltdXM*JEoD+O!M<(ts*IziFzsGKEtrZR2_rb>j$RYvEc%>Fk+xcf^vIsbWFw3jh zXg!--n^&~qh$JUBHMliZK|M* zSXfLYXfrJ=a;C78Q}t7eg)9eX-!tGVS}2YR<&*|`PDW6lQ(8fbL?T;>o9!tLhN}aoO>E)4=NJ?tIh#V~d zbhVK__q%5Bc-)Z_wFbvaewi-N&Hd=oML#SxnQK(BuR?K}-x8Gg(82W%WCOzWa3#=q|-E+x)SZK)~y>cFKQMLdb zpRf#A=0;|M#PyR*9|i1sF;Hlw-xj#9?*w|vb@^h1Z zPU6CEI%}Qa(qtKOg*wB*1zR6t2@Eub-TRRlLUtz6p`s(|edGZ1$NBzM6!e-!KYrm2 zvE2hDr0!BVea5LGrBzr_ZuD?)ttqTb`~ZdXqXl-QG`RQGD5lSOJ>FGz*KZxENmWNOtzsWO2=Kn z*pulu)qH*-8bps(T|NK)k9lt^@5B+W`rfI}ELn>dLD)pIY8x`L$WM@k~!jwBVx zjvjua+u>V2s_**t^>mog@8nb$5UPUtIE76*k;~}ow85<@I=L+M%9{S%U17xt=8pAIT5S*fi@F|cAJ308<8Z7t3 zW0&+u7{!U`2+yWG8oH}X0G8DG+4t?JiG8)StLMv#VNWjCE?It3ZSsphTi`*Ce6TPt zgcUq`+aS9CWP^j6jGWq!9fQNAv_EnZJT2t4Q^59=A1eW%p3lg%RCn5|DE_eg0^fhT(37V_q_s4@B4VDu1R&;Ep7w{unbW6q2)>qX+KKk&{;hm`V(UeK2$icVEtB+Z zN`vqGGN~>6VxV8zeq0E#oGL~oR9VEd9rv#L)RyTB;Kvz}a4Gc(Rci8;+AEVX@XxB#&%m zph9jIKXVf6qW6j1644kXxCMMu}4gVW+i3hD`+yB=ilC&>p-$m8m>;yuly7?knW zg0d7Y0iPN*Qy??6QGV3WN48;#3qc1>*(ISKvSuXs=`%aw8CLFCxc|>i)~Cea?$>l` z0q5dJ5UA|P)gVixt1kVTn7W$9ZLz#h`}m4x*GWm{N&!VzR@b2${8~YEhrm<*pR+FB z%KStsCy}n@6${wvf4ts(z9n?79P^xb8Z%jb?mE0mz9To`wS0O{e^Q{B@yqAZ7#=5Lc#({GOv|&w`}&cGEyuq27QlT^0&VJ1&Qj}e6wL3YvZ4Q5GxuD2k`p(&Q?!DU-aC%+C~Ju522S-XD1d>tNBa zm+i(eHVuz4-8xf@Vqs4J7x>WEFXyU3jPKa<;)()BEkj^R==b>X7i;puzsGY;sxv`K z<(zr^;zMuC`Eo_ig3K;g_%VVhSSePSLEcxhG8_PMZe-sS>IW8%^aj913JgGhmMUq= zsLcL28kne)KI(^1;Z-m(cVGoKgU#HsoH=y8VgJR8b$I2c6wjCtYg5t)0BUvBdW-)a zRKIvq1wn8bK4{hQHpTxKQ@+8Of}8_({AZ*zSK@g7$9)E#j<)AsCcw#ylJQUk=Z zWudY#g}LB+pdF4JinpxQNGm4*d)Wu{f6b%k3kB%s>#7V7sq6BC*15hGnLFIwt{98; zps_;6Co22M`x7t;Vk$zatW;A4gKzT;AZ$lLX5&oUVl?g&?(9CRvl&M>bw%UCvc%!p zsqR2!sQu2fg_|q=Y1yP(U1qMoSo^o;t9_Hxf5TPUXvbtcKDq*$n+nawCY^V1z5bF$yxcGx%Zg!ibfYnPROD-)%Rp6c7ecTj+r zwDVpyXI&L}msJ6;e+pi{7kBMAi1lWa;+DyT>MjAbKX?=ppd##jCyI~Rv>B`;fNMxS z?Ut^Vai*e|>xGhJzh-0IfKWl%do7BNNS^9R5}BD=T2xD3Clv2em<~hj;9>FqE2P2T z0Wcc_b6Z0Sa&l-Q+{w$|q?$lyYWnx@61-mKvlJz{nzguW5I&FVJ$g2AGZO>eV7_nb z;Jxt*pQSHou9g4913U`a^1;7ld#tVVJ4riOZh=9%#soDeV zU*C|dCk}IFX5_3rd-`aPkk^L`qp)J}16kP&diYv1!{^w?@|3Vu$F94tgm-bjvdof2 z&J5ki$o2T~uU~Z~EJNnM2Rzh-^?n?Y1*AYcQBUQ$G9Pu@^HiV!H!tBZa}l8Qdv79F z5evNg{vx2j-Kr0K2Yrfx{H+K=h+6ZpsHLrEZ-96r8bIGzseU(^Pnh9B0OKdJ38>qc z`U9N2>ahi&dTppxV3M0~j|fByj1$<7ilc2EcfAOD3)5 z5N-N)OpaijASbqe8iH3;<}CJ#;Bz$=B7rKsBW9Ds8L@C;qfy{UwcoAPJ97Hkt|_Uf z1+OTP2aoe|P0u^aeUhOTVu7?5pH&B4?Ck16=jq*m!lR^ZZ@^VrvVB>T8p`NODmTwA zuBN2&#Fj~bC`W&?qS2ywJA6oj;*>A-xy42AKBZV2^Z?L9BKz z-s=wDe*m~X;wcouj%0;avqE7=OgOA@w3?g9QV9|dr}WH~6{S{D*;UoCEFz0-+5($A zmZ*}9uPB3tuPu+_>Z8xcwbopGLC+Mi3!5DyeK_q6DB(8MdbKEgcG>cvjXYgF(wwD~EFHBJ}Zl>=04bf)%P zdOC-={OI7WL{AlCJ6IFDZMFh8ZP$p$Lo0z+QQ7Y3Fm8#5%QP!|8U^8gfN&R|B76FR zh&0+h-o2ao5BJEGzAjtrZH?P5R#3V9px%Z+?8@%@^r_M;689Y)!HHS9$SiU+c5eym zF$L~#)N!7F5-zCm1u!6eUs%1c2k=69G_xkuxA-t|=y>TQy6fIdW_L$6D3UN2jqYH( z9NH!CLVbt7ySivG!QnDtYeX77h|=4yUQ8XtMa#-Bb}~$Zt68ks@M+`+eJT}BBzwE7 z>WqDOJmT-56$+D_X05pfb5|;LMppN!DK`gA_Z&L0Cvr>IqZZN0ZW7v0`vJgF9il%P{af z5( zFT86d(UqKp8wQ2l;jgc=j7x!STui`#paGm zW#ra8|BI)8GcUzXaAp4v)tAxM{aX$H2y^^biQ)fMDu#%FyWNx^)o$v_L*A|C5uNJ> z6MW`7LHDTv@5Pdt+(oLgnKg@*kqZoAp20e@te_jPe-$W0eg*?~CYhgJhvg#Fc>QU(qA}T42OOguagM7Q^hXRiI>SiU|LqCRSeKmHvD5HY%C$Q(Ju8_`QdFYk1w=ypEXf=GYga zZO3ZoNVN{YI!5Oowchmr(3(?^dYe~t1y^eu>9fJ&i~?U!6*q34+M z?MR)F9cPzp_pR%tE2wuqrjWiT!JxyLE~z2LOx~nxHqjXr0VERU`~F$U%KMGSL~9aT z4~=Vn5#9@o7IU4xz*lr}o%$BC?mfqs|7$JK|03HB>|N>SgFO1(M(IGo@jlQ=`ZeZGo8KKeW&V7c{ufk4b<NVRobDrOuPjYV za;E3L?z8I>O5TlMd?9~nZuN)=8c(Aj7q-mS11`BW>f&M2&F*$f*))KD=%O4DD7Q9D zPUaB{$+OIs@XP)H`rZ-aeLNQw_Ab3absay>o>cbIWR7m~Oud1tRb`Ea>1yJ9T~Qkn zZ~Oa^FF+6EHMwyW5-urzU4J-7kJj>NajI`xj@8azWRmFMRsd$k(l&U`J&M0r8 zU-VmtMPtK0jx&nj0Z%3cRaJop-;}nYtAhIgRe_ifi}BeAA|U0ia2vr-#3H-FCe2?g zxAq#MBmtD@bhBw9%egX~qi*qtzZ+y)&AqDAWqf*zyV?29khBHGVMtIJU|o)suU1|0ujD8J!Mo_LorPxyUBc5rb2-fcCLV}BRCroFmGH?;$QZ4ag`(^eDV zkoN2I$KzJ^8;`B<$c|mg#*8l~nZ}Ina__##LLR}Y5bFp-^ zo+shL#sozceoY~U@<7pZ9TppjYoOBZ{cySM#SJp$D8;nbHibG)L8xXBR?SsjJNg0BB-s z|JXXP&)dgk7a%p-bUOelgp25C!EWSj8`& zkdcK7N#8Z*Bby1AqTn9XRYHl^8*BJmZ;K~*L^*{hs}fnkjGb~P6yJ5<*S&((HLJ3j z>YHt*#dyQ~=XILm{?_wayX}Qdh~s!W*Gu{iz;z;6;MA@N*T47!*IKBnGlDT`zusNf z@3u=+pu5~()F_pw&y=1mRZi>!=s~x`+jrD~+)bZi1tJM_`f_3fM!r;zAPK$ag@cUZ zYakl;X0FjC3Hu`3kNYA{IQRThxLd|M8cMxt@&U_(=h&fp-~KpkK(b#0sHs-Z+){~* z%J0}eHIe69Apyj$1GssOJ~Jg;CF+e%o>v-JxE}_Xl zzzJ;9uDYrsX3s$-RTE zQIg#P(vUCXM2`(onge5997=F^16^|mr5{Uo^(6sz$#VKWWY+JI9YcqMh93g~e2e-> zeqhpbEj(}C3YdKXL?2b+iv=)QOp3QSaJM?>jj0x5@bXe}Hc6hxdP{aC`U95)Y#e;{ z&pNuAGvM~BDz>KX*uq)M`@I`V&`X|!m8!KKpa8&o6XU@~Tx;QWqD-QPBeMf^@Sw{| z)4q+|gyqZwUj1hAXUoNw;1QaH6k^JCbkHe?w*_|vjW%4s8ucVEjK?85UN||Z10jQg z*m^L*cQ8ZQF69VDTi>*m;+!Phabl^7I~ows^QxP6W`x@q*7nQ~_i5y3l`v{3$!;nH zx^=X?Z1~zx;57zi4weP?0n{+2M5nYNgrD(Qo2^sj7w7byIB3raV#`W*vo11hHG}LR zE-E5*+b-J4fddek?6LDlwWCVJ1q}_puBq%4mja^+-C%e&gB^r1oJb6|<~B7UFGF`p zFBPzN**@7sMY13LM@5=v+QNP^Z&HZ^?mf{1OC!xzE)+@~F=5y{%zP+m6RrKrrRBdT z8Z2(lf_+gs0esPcImmKfn~P7p)2j|MMW$=kIiGK>KH?+-?pht;lezFs1gd<=a1_ZbrZ z3mV*S^qt=^Ji%~m(e}zMDZnj;9E77FxlQ{^scx_De?hJKi`&6hv*Rvjp*!|N1jKJF zJu2pd{i1-*>P+jw&8ymh|HBoQ+6?3Y1Dc4`wAp7ayB@l3Hd?hvXqV3HW0UJ*KhWNZ z2lWkB-loJ4nH(`(S33ZKWR3oj$vT~;Sh80|0c2E*I$n9(MtyCp%~WN5yw^3oqo1+l zAZ4CCO5iSq$6qr?AVC4}!5QdFi~oi^1GoUzvo8PN?tsJBBZfCoT(>9s?+8YJzwJLT zVV+~q#=kddJ`vE*H_U80D;b{N?)|GYH8pQ2-7486d@e47OIj)hTcE1B>}pA)bayh= zlcCx;R7{W)GoqOL?M)m~qi;K_vw;oQfKLpPq`|X~X;##go9tCB>awwaw@nacNcZ`| zW3A~*w78tv);1yd;`V5%OE4~*s)Hz=?F%G(X1vWV8%+zn=mJWd~JjSatBAp`WVa42!#dtj1$W&fnCcH3%&6az7a zI!t*-QPMXPt|^(0s>YYiw;3e6Xb&AS?R;qn%geev(i^+_F;-bR#pk?M+FE4wA_k>` z>+>^UYpW;-m!6^h8f)=mW!4V5ks75#d+tT7M?E1jYId8>%(kbrg#=WXlNqB;SMW?p zL3D}&3f`Rsp*6|Rz0y1E`co~g9=$Bz00RRz{_eNx1P*BLCv`11t&|d0T$Z1>#u`N&1wcn$KPGjrp%qomKkbMxLRaKI zepQ4*K?XYbV)(s5t>r>1S%FTr+##K{As)~-OL5T@1uKV_xr^VLomug;#)-Gfa{jik8>eZ1&Nag_#pbw9n z?QR3CirYz}kYVlw<2m;<@&?wz4WM3W9K8cNV`_j6>nde*CO}_#ioY_!HULYTD!bdv zQG;-u@HUQ<*bN>8ncQ(7g*%TqbvKXP>XS)LspNg6IXKf#KvMUn?&?#a0i_*Fnu3TRCWN$lyf0> zI!u~XuXL&!HqR{-=psY3hM^Ip^BUw4FmDn8a?u9~9EQ*$?(wfLMQ6?LlkHr`wsa78WWedNH{UmhssSj;R<^ znQ5$f{8VAZlv@;z8Xl_kto+oFtC7`d>`k{T-LYT-DG;P=a497_n!2`#!*PB!5j)>S zPPckcb}~H(;UK4fNsK@WhuGD1bT}JFSRF5tH99Bn^2%-JVV{~i9yrY~ITeUO5l{{3 z&dBQ*9J0mrISSuQo{sx!eisq&)`t)EmPBva+K!;;|e@DmL$4m36Y3 zw?uS7nzC6cBHH!O3K^E)BB_LMT}>>N@j}mav0My`O7Qd8y7%5jrW|q8#tL0{;gHlD znR5fz06~X)4_t5Shm+9v*^4gYBBfJIocc`7Ha$lpe-&NmgB(q$?A<#z)flGBF_#$`k zaz=@bOJ~^Koq6PTyVG&%(1X99kFW z6I|g}%sD2a5<4$$Wndom<*_t&2r!XARjvsmmwsawcti72@wV^^MscAN;LAQJh-+^% zoYR$D`5dSn16~;mOsWBtZFKL-*$)@UM~zeymxonAA#~3dqL?gaKlo|XY3PWt6p+FRq&I4u-^6qma=wX+9R zQ7F8oW;CGRwtuT@0jAhfKmA_sADI^?;po~{8@mZV8n6}sx^%%J+8RL(Q zEykU`T^D*96hzrgjqMMMW7YW&^CPSrFt9H@M^snQHdeb0^bM{}eTjaj&GPrdfBN)k znKQ+Wi}SWay;2%YnVw#)W`&1@QkTPtr|p&-%cXAUg7i+NY^w>wA1+cwsVzP|3Qul$ zmX^_cG@I$$TQ52~ba$c7vk}?g+e?%l_Q%C4b-Rev`t*5+d-8NDlD)ft}&6Z8gJF7JXWq>0aTfUo09_)R5lR^#t3+Vz)C*3!(l;7pJLq z3s2TsB{{7v(~>jn<4`MKkBe1J5r*KU;ND7KsHM`|Vy`V@*|vOp<5u=wQ4Nqd_;T!V zFtFxwJjqxPa$qkzuX-i#?m*4GyTE+SA;?wadp&HI9i5Jg5)ju9U7VbyZa4m{8~hsU z(K`V9<(|e>dA>)Np*v0{YYOs}P=ct?8wXc;=Te=$^9bwm%2%@+^T_H|LbajY+FWAZ zV75F^ye$98{@~{1=i4(6M0|2*=0EGY65z6(TEB03w64qo&Orr}h8S^!xVX6Sw+#{o zKu~70QY*PyyCNLnumR)&cx^qfDyr@R|I6aVxi=1m)ovH*K=SR4JsTV$AdzOQHr;F1 z43rhWX{w2LdwV<3)DBaV8xAv%_K8iH*n6#lFH`yJwyCHexQ4b?+`Ls2kYza7)XHjWnJwLV&vTLG4e4B_QIp z{b>iv67%O8|G?yC^22v$xwjjC^I||j1_SOXssD{LIJ>k7R&#+)zmaCti zB99?I{u3(r0%f;P;Mc^oLn34>eV#-NSG5TwIgK}jPc}^4j6f#>Lw3_7c;^-cwTbOD z3b{l+C)3d_udR6xuHA0+y%g9!nwBdegaPA=N{W$y}Hm6yvJ4Z$M zH2pw?*WVw$cN8+;s@D!ojnu-b#om%YMsN8~Nhn?@G>%%EK(+m3X8oeDa;~xbCwPP3 z8W=M6IlaV%m{tNz$PzC~d3i#P(q=khA+xit=8H{0=Z$eGdC29e9WK)M6g)SED*|o4 zR$nbqR}@^Qq<>_8_9l>%UlyYApvawfn%^~+ za8{znD>1?Wd(NsYY_(77aEU0+W%6;Zc;CC0>pc&I9#^bXJ>Tr!TI12NC&J+xa!X@& z%eHyls_pt&_*P)RHU{DQjd6Sb_RxajwqG?gUM)^htWa4cjtwy(pn>b(JkNBEyJJ^+ z(Jm&vSWhcUoR4oY&GAR;u+kU(-Fc{VT7-awBcboX#q1jdvK<~Oc z`+oc#zrvG{EeeJcN@qzOkcjX%xU;z(YAe_;J|V(A7nOT$ zS5N)>?e=#B#5Lm=CrNQwPcpo#z{(7l#_6zneVt%K)#pra-IEjb-;Thy&+Y&5$MMg` zh-+)7di7&ah;7d@zCSr=QrcF ztt8~ujVgYHw7&=bujh1Pzr<@`7ieFkt7S90j5}ocDBqcgYWHT&dXh@gf7!){{$^wT zzp|#b-JO}aTMp^Qy9p@7e7`|&a>q0NkM_PauBr1|HzG(1C{Qbc%N)oG z^nN(^o_o*!pyZdGJ-owu*Lt33t@p|Dk@(rbXBuxLD)O zH8K?h68+xTL-j=nbwaOaU-R&5s{K@&9_KVxS4=YLsC8OnmsXEWQ2H#9Tja)Rh(W)a zIOT0sU)bRq=ai)U=@KyuL#1SbWmb#14%wlCwsIRqk-K0ZFktk;2f1MAlj0mWT@4G) zo<1|WL!~@UQ-@d~d$VppD&38qgoEB)LXQ-hhe#2&E*Li#4K&Mc8z{rXsZEo*^%9{jGI6WKSS>!u(Rs>&EDdY>f|N$=xU}yvHk_Y5$47DppsHX zJS%>=oLuc;6f=P}Wp?Z31<(9O+9OA3(tIGgJ{X>1gBif)C8yPPg`fF)P8u5?E>AXZ z2Vb+egBd%Fe-92D(?8JdE7crltqsSPZR|EW>F2{raiEL#ROi`xN4^faI4;rR{b0GU zqavERnLs>?YxIDz-`|sfF!pmgGgx^+y`_$K>2EmwvJUeJZpe+(?bX~6qy(dPNDm#r zPD1ucN?BZQE%X&?1T52fFKQkmzj|tLAe7zSn7QFJ=_$v53H^R@NmfNV)+w;N@SQzp zB0V@SNUIQs#xAGu3gT+cQ>`aSPgJV_T}4Q(4NMuBiPVYAyf`QI+x(cq)+*ohEx-v3DF*LbFU%l ziyi^h86u}ujoRw3-&MeNn3oMB-_EPl#lD9e*7yKMUb{G627az8?Xmm{H>ek{xB)75 zX1J!QUCzI9SQPSs5<8Nns^qyvD=ipo5kEmHQh`Z#xSn3mU@xWf43EVrtz0q_}iH^s$|2)vdzT#et^{>7@G zsr`~OVmeLD;HEp~2WU4_~os$S-`X38*sT)lb4oRxZ2Il_y;)+=Q|TN-^aw_yoK7t7<21kqn_ zzo+c4O~33g3z)x5oZ(tJW%$meH#VeK6SBSb*0^RF2J-!C9I5)0jbP3xCbkwAz?=wR zAN3m4xdYCzsCZw2FGxIH$~@9c`bkbXK$^iJ^;ge5-)jVRVe6ymi3S62$v%0?bsfp7-B<_hhQo*iIrK>%Y(DnHyK2HYo`oj%Lq6ug$73AZW8opfSWEpvf;9EQLcl4 zwAELJ)Glg2(W<&{)J}T9WMwf~rk_ z6?E{{+Jiqc*>6&st5JcFH_Xv$W?H?nV@r9!&f*fO9fGcFSLjYSC7XK-piV!}cAh6K zle-!=y4}I|7af|V!jCH%C_{*^_Z7=UxwruB;%X^bK|R3J{$qV;wK)^FeOI`u5W28; zDhRs&;yLipcCP)iQ{sROh<8YjJzvt$-`n6c$gU@;H~NM0J*V8C-}tD%lM zz-|X$Hv->n+X@!%?q2NB_WN)hyGb+oYfpgh8h?tiL0?^X{r=&H{v-iK))Z>MGyK(w z!q=DHl^0hB|t9Ua8phG$;0r? z1sxsQ17sn8`+=-&x-7c7f6Z9hQFr~b9pQDqz{|6w$wyzb^Z!nBtTVSjyD2>D*_t=K zGkpGwVg4zo0(@Ce{?m)Vevh>W(lGwdzeZTT4B*^aGB=<936vE3&pUB_k=muE)(`I~YP~czI%P=Udqe zgnUre7xA(fF5Nu36KNdW34~^ym41}z-eMFGWV=;R>~gsFP9uxooFCupxBuha@#Qd9 zcy>EFs|#1PeBLMA``hEg?9?g6Q#KZHy--lbHztkMy8Q74p2`0dLF`Zn73Y0~@?bMdX_;B$iOX7=0b%qJ83A1@~SH%~F+ zqS zvnv;-5!)~78v_pA{klf+tgTz?c7Ma*@$*P|YYeckd@*r^uLIU#YBc7rAQ_J}+{c?2 zNG~l5cY45TRP3v-`T>Zc!Dqw!O-DEfKxY!FYM!i+grrV?TJ!#X82{C>|6kS}|FZ;x zK>rDU;D46j&-3UXEx`nbV2{gH{vH52T1Bg2GO?w2W81um`U(Jh|7t^Zmk-i=EmMxk0)n#j|p%grtTTRokDJ zn2=zWbP|BEInCcE?fqi1N*vjJC7DsnWdqE~^b;9cP`pAzUB8CFz%uO-ciwcrhxo`z z>63u5R|0O*`s5oPiT`P1SF^ua`WFGuh`0LXV@w}<2Gh4qjc(4PTnO*_#v%uUe-g$0 zJv#mu_!-{}555b)XD7awI&F^_0g5D;RincJ#Fz{1hKX@ zCf_?h6$K5`GfnW4+UMD4GT_GEmvoG9-m56EYC>> zMZF^#hx4cukMSy3fZBSaWt;Y>4Y7Q4-0nE{yi(^SR($pN$~cMjbTR!8ZdGy{ z#Pt{WylL1EMYFK|xTtGh$N>>I#kW1^FW!Ir;pJIzSHIv$^%fc5=bQo&G?=}6P>~1%E z1U@KF%eI@)h?8x;zI-?hN8`*FytLE^IbP}0ys^i(u@uvdYQ?1m&&p5XUKfucovuPoSGLOn(hE}?g?Hn z8?JR&LM(NUq=3nMcfAU{C2WBso<&9*(;hp#<>AqJPDx`o#ozDUUy+%tcSJ&wBuZ82 z0fEdQJp83y>nBaGOzdp3Y+g`gj?a_mN1mstX3PXq4d*a^NZ+5hKRWin4}Pzjiw+T% zI})P9XGLDoT@T=U3yfv?^FT12&fB6e8_E|T(#I-{4FP^ zW6pQSH4_~gif~n&_vB8fUhUMI!d4y-oad!;sN0-l*odQ@O&Y{wxx zT*|0@xiW0)Pp*PUs%F`ZGRnKF)JImK^f#^t?wpBm7pq|Z!7}xA9@O^~vmf@8_g0)= zb>26&gc->)L;+fO*GF;p$BGoWE`rcTq1lNU-@-?P4Y=cS4apH0ws} z9Wp`U=E=$l73#Mzl47gS;NQlu?=aY<+kP#_ zndKd#dJn3imf3#Bs8^w>)x?@v34#PR(FZhKlj&aL7_Tm_C*RC%Tf}tPaObFQ^6yWw z@H7Mh{=pJDt%G2I-i^>Id8DqH$OcVYp2L46l%p6QrjZ{ujwm>EHC#E6{lpZdz+@Gq zeGiV7+kezfOmS$-4;gxHGhJfdDJm!JLmg%> zXHP2jGl*_M$_|W1SPq=ALH($flgAdzRC7F7O?75QByx}yiPrSH0)mjt^br8DGcud4 z{G_TRIzqt@>7DLzU7TQ)sGQ*wXf)eLRes)VOfF-J0lNaQ?|7TLY)n1=X+)8`fjYrX zG9K8~s6`?w?W8IDHHNSc)cEV|1|EXyM)ajVF};MN1N6&Qec|j*Ps1;GKhVEmS3-uqTVO7*9|X5~ z@J*$HazK}bDW=N7V2>~jAodEAF*x!iz>DGO9e|yGk72$Q9 zS3M!a0y;yG8`T$11*kY!9@F`Vv@D@#TWgcfVOp<$-wtDE^J0scateG{NACiCGHCFJ z=6ladpxNW)nG?$$hWa0_m5xJ2yobF_=VxA@!9(5zJs0*)C?MZPajbfb-jIu6wuQ1D z9W50tddQC~w;BhY!r?7%d9IUDlHL@x>rj*2uCm5@@CCY8r(|8z<7mTK5z7o?0erQG zVPRUl3vG-CsbYPvi1o`6wq1pq*q-dL#2BaEl=icilxkka;hpb?m$8vV^)^{!KYE#D zQZf*zV~dkhr-QD_Dm(x=cG4sxy&0Yd4lYrGC5mGmcXl)QCAE?raIU?)ogbl zIrU~>^c+aWP+`p*mO6`a4Q?(B0B^K%#jGwb^@N<$AIzG>rfeKuHjErp2~VT4v7yod zxD29&n5N*Tj)#9u;qH53LOCGTzK{4w7Hd~U9BUc+AfaE2DF-UGfnRNi`uz)|`3;D) zBxyxmdXs(+j}T4Pi;i>Qs4T|LVfNJKeH>45u)%Hk(&~OC-vIm!DY1=m+p(9&h5>pR zU}B}DMmHA(TI!HW$;Lpp6vr||1F#Hfr`@Z=8~JLKYMv0fO(5Qc`NX$lWqUW2=Mrh^ zeGX$}K9zgm_-kO2ucrolehKajuPK1z z>lgnPZuEZvauvR{CisKlREJva*@Cr4bT#Aw&1(uF%(*3%Tkqp(+{Tiz4DLZH;zP0D zUXbWG1ENp&>?7_Wt!1JqPP{ z$K-F96y)}AH%a2m`51QHcC6I{3d;WK+t1+s-n!Tc7X?W*(0sgHr~`E5Vox(i*8|bm zR&sS%==>lfj&m(}h+E{_2HD#c!|iX$)xEgePvH}aSHq8-H@&|BJaW~Rp{L7X^Nl?> zyC`8F_f0L?aze11KSmeRftt+&jR2}?i==^?(13T{})`Kz%e+ilIMHc#mV;YD$DMq1c2%ZJ3_jzl18^JEul#TJLe(=$p+f$vQ zj#n_LJOu<~=R?4w3(wjZp;^}zt|Sd=du2!?p1BCwVEiCbjMd^5W>Apif69-ZV5)>+>ZM3)? z`H%*Q>oFl_x0hkHX{Ef!jIk z-S+|AhPPY+#tsxiWU6uXSxHGXjPOULh34%5FDra7(0e$q%Fja=#wbuiXstby0>TOSOMz$)-ndvBynOX ze!!H3C^jBY5o}{kC{>2yE)ChRF4c#8sb2#K!TTz6bB#P1*oi8KNl7tHu`pD|zJ``O z$CA>^ab^b@57Xi&W#q12ms5+bQjU|4fGxsf!%K7IH${IIC=`AvQ1IAt+g$J! za4Rtg83EsxCFi%fw3n_%++Ry(7jDOFiEvzMKpfD*7CCHmQs&TEBvj3EDg2kFU=Dj3 zelIJpl*CB&oqgkzYpoS^tF01Fikns*>e;K;(i~f;UHeG?5E<-L1s4cd`i*K3<_V2{ z$J|kc-btCx88m^-Di4{2U5BL=TIOeI;fU_%-S^MN;HgssvF`p-(5KC_r%_A zDCe}uWAiPCc~Jig36RgWrBQ^@3bthB2^TAJXG8ea`-B5;pfbrSb7EBMz;jBe(U!A2 zP8htIpV$GmxG0Ogo)Y0jtO;6`EA$s4FjvfNpA9MyEB1OJt(17$1LmfN1J6%9KnsAx z1_Ml%rrl;~FUx2#xtBC*Sg97B-BB90Eivi9zGz$;FY-)5@e`HOOKYQgYVhmT7@Q`6mEcni>-O0wVPP`pbvo>R}pGL zCzOSs13Jt#qV(Tr|LkqCeZlYB7Jm(;NxaE^`Pohtu$< zGf`pvgA^%{Q#LYiI;AJ1u~IabsZVfVKI&OT3Y*&FQuy*DB<99_rP4izCMD8u_;a_% zb6Xa{o>iF=%1gU>A=Xl74j_(#>cl@ZtPbgd<}wd`Q&zC~lchKW?Dg)X{~Y6a@|0xj z(Rjk7&fv^^^6i1zph=<|@(66CPxn^8+;mh(>f|qjI*)IL0KpXeUl!K~LgHbOFT7{Y zEVu$H15v%@mYYh$TS9!yw=r7zVB2|w;%NP8nP1mC19(Qdm<(?)0LRye<$n<#2i*6+0aVLV6KjfpR&lE< zE=hqBqWILm6Cm1;&&*M^py+@SVgMH==Ff8Ie#lO?ZmR<%K$Yi z2xPfd%BQ_pPOz4yReL4BmfBN$9tZp|A(?;IXKMC8%`H>HwCp(>ZEY#MpR5;$5zkY* z&|~SN2HuoZd#BqD^xpSRggWW=AHs@ODSButAcE-KH>?8Yv_^=(6jrR#^QfFlUYu@0 z90iMMiu>eaKy}yo?tJ)80Cod`qSi~)9Of4eYF|r!k~%H{G>q7jwPuJ|`)ZA~p12Bd zknDaecV8$268}Oqul9+97w7yGBDmfv(-L3%nmFosL*uY1`>8%t6IJ<8=g`Yb zY|<-J8JZrkgj{0%0a?rHT6E-$ns!6S`gz%Fh_0?1+#D<+^BT$Si9byWys_@k+B-~K zCAXzb{hEb5h7aMW-WaFXJlPMS-S@@qEI73t7I++c1QXL+3#7&LWLSm0bl8FCEYqA3 zEd1O4N-m8}q`d@EMy60m0|j0=G^eZUPbd*F*C?*&=)kO2>x}md&ICe3_1iAk%|Mf0 zOyUO@yofbNT96?Z$xG`UQHZk(=L~l#w8-behp%Zn{i_)7e3XDzf18D~E zp#~60YEJ`+N?^Od2lEhlmk}h_FbI~5cFeiu40v%`>m{Pz@QV>yueDObJtx4!)9+e) zy_S=_8b+ym)&@w>MV`1S*|c7>ZfIX7Q2?a2=86L^=(J^*Vu+CkvD}0Q9Anf^3f7|_ zbLorkuL0gRXXfW&5wo&$z)V86tz8)S$(@5gYXkoQ$G6i5zSOJp@%f*mH#N;&`cyn^Tj%#gh#-Px4gzDw|VFKXfofA z-Jmxu-2f6PDuu$wpAiM>Mn6kAe)*kOF%>xep1fBQ)x4f^5TL!i_&|M<&$Yv%F7FyF?uyiTGp=Y^*XtY%Q-Wyq|00*ohGA9z~S zN0uZgvJ?U8ByY(sjI}bIxa)Lt?7GJyV7l zI6Tkihi;z`He{)J!Rb9ZoSj$dT+xn%^><2D2Hp;Q0(}y zGI}*PuV;B>FQUf5fET|1wyf~c3+sP{;P-|}svIW~_Nj$35gfl27$EpBNDFIwNE@?t zEPVm`WgW5%?tn+WYHqUE2c3IGTj(8Uvy0;W7x7|7%a=VdTy9)n1D3I{+kJ5V_K3OF?;_JrQmqU3wu%+mZ_zGBdmVfrs!hL{ zD0eVUM4QH}vO3p7We-w(z5~8Y2t9lH8|#2 z-ODo=`eyD@343hC)QeG@pP*XeVP_9hWU&=ZIk3}JoWL+|u02Z^zpbT!JAz5|=S^mf1%em*oZl9lg3FZcmu!&Tujo`>yR_ciA+XZnXxfivzPneKYXSZS5Z= zwn!Qn2T+4<@c0lh?K|HxQ2$n#t< zp%{w7Kx_TWrJDaRc28L-o)Q@wHL@~U!c7n1*cNwK)B>BXawl(-h&dkFQk3prGvNsD zg2k?eUHP3ZN)J*H{VtM@w;mc4)u>!}?D`bt$dZZQ(P@CH&0*4ir4{TmCAcWbB{%Qz zYeN@svkQV*E6!CPlS@z#Hgwa8v0Jz^5fh2EL96oZ_UTT=9_OAt-B?<-Jg?FYg>Fi| zMDUA5n`t$ayC^~2NQGA-CJ-y#h6Wp}TnhDmeCQ#Ze93mVG`=7di6)b_xi^Jn2;c%Y zT^%xh;|ly$9^K}UE}y=;)wa6pCXM)rO9hApvkirE{H5adcah>e-|Oi{#S`Kt{xDt~ z)|(G9vd8tBm&;e`1mB!+(uh8cD??LNTJMjIKieZ5LjNw(-0>8a7E>m!5|1vhzXH9Eu$NL1Zh$<% zwVc9{{QzvgpUEyyBI}{ZMH0-5=@iA?Zy-P{^)V|)uR%zP?Ct&iONp~VVeCa*Qjt?m_aJ0>q84Z+lIU?Rz z25vMdLNM+ze%93~P`y=$A_9eAH{xDV6#Q%EYmsH#vqC^4UQfJ@MJxOGJbQ{dW2f~) zD)iY?VElpQ#wY}$Y1`mzdOvX&T6v&JD7nAfshGkEANTwBVy?QgHn0s&H&W-#%4Gy$ z>rUk^o|8&I3C#IEX?*4X@zKT?{sj%-rAlH>G6t{gyd}Qr&S7fkYwrjzt#T2jbjCnR zb6ay#3v-~d>|WWVvaIpx6e`48HlSK_c1H@{BR#^4wVF}p5%uIMnqayeClT1~(?<2S zPp7boTCSi-A;{)KA5iK3@nfrgvOUCA_(a(-I>lpKunywf8g@%U^e~9~lNpz%dLr;!NGrYM~^skcCjPA_?(28p^1FHg8Iv z7c*c$OWrc8;2)!0o;~#wN}&b|OB-wu;$^}CJ_xy+PCO#9%J=F2^xvP9IWkfjoIs}~ zN2~-5gG3}tbT*cNvs6~A7aO(%CVRZBFOyI3BqlU%OT1#EA;0~qopcYe8VMFsZFh^g z8rC=#eVUe@bOhfs_tJu4OP>QU(y|>YkTyRz*7n@8{)TQe^vVe;y`TWkh*jg%Bu9g9 ztG=h1hee^v8F4^7FD>Vo$c%bT4-NAPQi$o`5G#3^2oo6tD0eX-WG!NV)I5K3)P$}{ z@XS z!IMAluB+uKkACJj)j zW}ew9VsX~xQ}=pT&faqkT)h-Ja`oFF1BZN>PO+y4%u=x8$!|yL@z+9I3tI) zJS+xsT9Ag(2qUlgsOvr99~2%*W*&jP8&Y6w-{3HNwf~1kHND$4fxBEU8^xCsT|T0MXWDujgOqR1 zr352+l7;gms1Jl7fg+uytkgWXQAC6AMb!zU*TTS)Ib|mUtE{G0OT(mgJ+Kx!7!v&O=(8sz@hX-T zA|n~KCuYjyX>@O_rhe2Q4jb;1BtwNvx^GIA{N5s5CRy1rID1%2`B0A>hx|-u7Fr?j zd*FWWnn1=)qk@m{!|f(*4}o_x+{$%&PDu^Y4j)ntY^)qJ_DRfR=z|`-<|}aOZl2}Y z9CbS-b#N4?b!Yp7XJ~o1rfxB_>+r{f}lp%{o&*k!^M`IvQuDWd(F) z2TQx*o!s>}5#0=rZVuH)cO%XyRzy1^KZvHtU7htMM2Mpf4$}ir$54nxydj=j>w@GG zT;e2AZy(nZaRouu@H)K{KEX2NYc0n&K$pJBLXTXS0 zKT8kRrFSoFX$i#qLMY5RAP1!zryTIZnrda1_x67vzLp&N1*ZwCsU(WaHj7Zra*bCP z&;DV4RYTji1@J-Y4;=QThvrH?pIG*{X6lI#+u>kvoW?o2@;$O-guB*t`&nYPcMbR! z-Fo;y^+$d4urSM)>^7pw14A;O6C@ivtYxH_XGDLz^kY$$E~v5StMt#cK&A5{ub4yi zzrG0!wElK|(=U<_OUJ!lQH;aqFD;&JZ8=3<3aoXqOJ!-)u4GMBaX$iboU_4PaL+*c zMZsHK4-$|@FgC#YMY0UY9yB|6sLQ>(M!BJEnFFy-J0*Bua;DV0q8brG>=l!+#fK36 z*!B#?%7$jJ!XWt`3rnd>%QE7+8ej7ODBV=UH!P(pw3fh(=B&bK@tKI9hs;eZY*-4F zarlPA&$iO`#Y$*J-S@oD;2iDqp~xcGhn0RX7qx(OE-!aH(riQ{riLmG8V(vPdpW&R&Jo`pLS#}T21pk6 zr1+T7FrJ5WVA}P14V7syNa*5R63Lj&p^(*E$*IhWlK74u1ybTX{RdNVD(w=_umo0K zdTn-5!l>$uF?XO+AP4;BCa}odxz(ZjWBU;Ich@{RPRfn5*g_1uoV|^uxABk6iJBru zK{zm^FAn=DaErDjhNp89IqT>fSl0NO<5Lx}CE&AT76xr4?61 z)~5$yaiI&vBKBl=lB-ccAiAS>D6HEL$TM6Z$80vc#_cPU>5GyBHi;nT?UmS)(SSb# zv_sH(J0z0_6&7@K{MbX06L?#qOHG8=q=74or2&P>;%Dd0N%~YCpapPQ7BO2QfjAJ~ zMck-(+L1Z2aq2fZ`WWHL!ocFY7NRu*epb*~@>nQCvC6zP6Z~c%Il@Kv$17E{3$mw0 zOP#s#L%@nXnoYT@G?%4Ui`8mxW#RqBLj?h6Cyj$%G33>q!95okp{+KiFTzZ2!tqa} zZ?%s>qk;OdY%_=et{;BAX{Fpu z3;EOJ%Bvtpfl~ndaTC^wR(L%IzsM(&y()K3DX&fm(~gPDucqWi&*kk8Hm_XyjnIoi zdR`H?0fi5lmm6#QZn<9-wViM7pQQi@6X^CM(!N36X)J)Uhw>7>u=Kdv)(4Z|`q*cD9f5+igX zv5Q4pdGh0K{tW>X#ne&(?0V}HK%xZ&oGU&q#A_fg7xiGJRDfFGeE@-0dhEXZ)bZta z2?QC@NFX#^$6UhS8jeMHCEOn2<3qglU(<*WX#Qg;!cEg0*wdVU6qbC(m;Sm!!T)Pe z5^qTGO4oe&@bf@b01TVZ87FTzy%Uu18$V#=CsKq5-Sryf*U<D@1Wu_43>G{cRmWJ-2z~9np2-PCH|UQSnHL4 zFADyIIssdXVtA!EctYZENK(k$d`ZyQ3a?(lFfbxCPx28y-gk%oLTIu+?*2ac;QzxD zRbDP~H(I}2i9Fz<4CnR4@gt`XqYpXVy7*~kKXLk`;J$F{7v34barwK1^|rdc^FKcE U1#o#SPxyz7U@QIwKVANR0gPZ{ssI20 literal 0 HcmV?d00001 diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index 2c7bf580f9b..6dd8be79515 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -50,9 +50,29 @@ namespace Microsoft.Extensions.Hosting public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatform("android")] + [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatform("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatform("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatform("tvos")] public static System.Threading.Tasks.Task RunConsoleAsync(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatform("android")] + [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatform("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatform("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatform("tvos")] public static System.Threading.Tasks.Task RunConsoleAsync(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatform("android")] + [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatform("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatform("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatform("tvos")] public static Microsoft.Extensions.Hosting.IHostBuilder UseConsoleLifetime(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatform("android")] + [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatform("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatform("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatform("tvos")] public static Microsoft.Extensions.Hosting.IHostBuilder UseConsoleLifetime(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseContentRoot(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, string contentRoot) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } @@ -78,6 +98,11 @@ namespace Microsoft.Extensions.Hosting.Internal public void NotifyStopped() { } public void StopApplication() { } } + [System.Runtime.Versioning.UnsupportedOSPlatform("android")] + [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] + [System.Runtime.Versioning.UnsupportedOSPlatform("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatform("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatform("tvos")] public partial class ConsoleLifetime : Microsoft.Extensions.Hosting.IHostLifetime, System.IDisposable { public ConsoleLifetime(Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Hosting.IHostEnvironment environment, Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime, Microsoft.Extensions.Options.IOptions hostOptions) { } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs index 7ed38f701f6..3827761382a 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs @@ -19,7 +19,7 @@ namespace Microsoft.Extensions.Hosting /// /// A program initialization utility. /// - public class HostBuilder : IHostBuilder + public partial class HostBuilder : IHostBuilder { private List> _configureHostConfigActions = new List>(); private List> _configureAppConfigActions = new List>(); @@ -245,7 +245,9 @@ namespace Microsoft.Extensions.Hosting services.AddSingleton(s => (IApplicationLifetime)s.GetService()); #pragma warning restore CS0618 // Type or member is obsolete services.AddSingleton(); - services.AddSingleton(); + + AddLifetime(services); + services.AddSingleton(_ => { return new Internal.Host(_appServices, diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.netcoreapp.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.netcoreapp.cs new file mode 100644 index 00000000000..d1eb2956347 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.netcoreapp.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting.Internal; + +namespace Microsoft.Extensions.Hosting +{ + public partial class HostBuilder + { + private static void AddLifetime(ServiceCollection services) + { + if (!OperatingSystem.IsAndroid() && !OperatingSystem.IsBrowser() && !OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst() && !OperatingSystem.IsTvOS()) + { + services.AddSingleton(); + } + else + { + services.AddSingleton(); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.notnetcoreapp.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.notnetcoreapp.cs new file mode 100644 index 00000000000..71ecd0da61b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.notnetcoreapp.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting.Internal; + +namespace Microsoft.Extensions.Hosting +{ + public partial class HostBuilder + { + private static void AddLifetime(ServiceCollection services) + { + services.AddSingleton(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index 30981bc1497..9f5e509d810 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; @@ -225,7 +226,12 @@ namespace Microsoft.Extensions.Hosting }) .ConfigureLogging((hostingContext, logging) => { - bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + bool isWindows = +#if NET6_0_OR_GREATER + OperatingSystem.IsWindows(); +#else + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif // IMPORTANT: This needs to be added *before* configuration is loaded, this lets // the defaults be overridden by the configuration. @@ -236,7 +242,12 @@ namespace Microsoft.Extensions.Hosting } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); +#if NET6_0_OR_GREATER + if (!OperatingSystem.IsBrowser()) +#endif + { + logging.AddConsole(); + } logging.AddDebug(); logging.AddEventSourceLogger(); @@ -274,6 +285,11 @@ namespace Microsoft.Extensions.Hosting /// /// The to configure. /// The same instance of the for chaining. + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder) { return hostBuilder.ConfigureServices(collection => collection.AddSingleton()); @@ -286,6 +302,11 @@ namespace Microsoft.Extensions.Hosting /// The to configure. /// The delegate for configuring the . /// The same instance of the for chaining. + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder, Action configureOptions) { return hostBuilder.ConfigureServices(collection => @@ -301,6 +322,11 @@ namespace Microsoft.Extensions.Hosting /// The to configure. /// A that can be used to cancel the console. /// A that only completes when the token is triggered or shutdown is triggered. + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] public static Task RunConsoleAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default) { return hostBuilder.UseConsoleLifetime().Build().RunAsync(cancellationToken); @@ -313,6 +339,11 @@ namespace Microsoft.Extensions.Hosting /// The delegate for configuring the . /// A that can be used to cancel the console. /// A that only completes when the token is triggered or shutdown is triggered. + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] public static Task RunConsoleAsync(this IHostBuilder hostBuilder, Action configureOptions, CancellationToken cancellationToken = default) { return hostBuilder.UseConsoleLifetime(configureOptions).Build().RunAsync(cancellationToken); diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.cs index caa88e87c1f..5b964fa6a8e 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -13,9 +14,13 @@ namespace Microsoft.Extensions.Hosting.Internal /// /// Listens for Ctrl+C or SIGTERM and initiates shutdown. /// - public class ConsoleLifetime : IHostLifetime, IDisposable + [UnsupportedOSPlatform("android")] + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] + public partial class ConsoleLifetime : IHostLifetime, IDisposable { - private readonly ManualResetEvent _shutdownBlock = new ManualResetEvent(false); private CancellationTokenRegistration _applicationStartedRegistration; private CancellationTokenRegistration _applicationStoppingRegistration; @@ -57,13 +62,14 @@ namespace Microsoft.Extensions.Hosting.Internal this); } - AppDomain.CurrentDomain.ProcessExit += OnProcessExit; - Console.CancelKeyPress += OnCancelKeyPress; + RegisterShutdownHandlers(); // Console applications start immediately. return Task.CompletedTask; } + private partial void RegisterShutdownHandlers(); + private void OnApplicationStarted() { Logger.LogInformation("Application started. Press Ctrl+C to shut down."); @@ -76,29 +82,6 @@ namespace Microsoft.Extensions.Hosting.Internal Logger.LogInformation("Application is shutting down..."); } - private void OnProcessExit(object sender, EventArgs e) - { - ApplicationLifetime.StopApplication(); - if (!_shutdownBlock.WaitOne(HostOptions.ShutdownTimeout)) - { - Logger.LogInformation("Waiting for the host to be disposed. Ensure all 'IHost' instances are wrapped in 'using' blocks."); - } - _shutdownBlock.WaitOne(); - // On Linux if the shutdown is triggered by SIGTERM then that's signaled with the 143 exit code. - // Suppress that since we shut down gracefully. https://github.com/dotnet/aspnetcore/issues/6526 - System.Environment.ExitCode = 0; - } - - private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) - { - e.Cancel = true; - ApplicationLifetime.StopApplication(); - - // Don't block in process shutdown for CTRL+C/SIGINT since we can set e.Cancel to true - // we assume that application code will unwind once StopApplication signals the token - _shutdownBlock.Set(); - } - public Task StopAsync(CancellationToken cancellationToken) { // There's nothing to do here @@ -107,13 +90,12 @@ namespace Microsoft.Extensions.Hosting.Internal public void Dispose() { - _shutdownBlock.Set(); - - AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; - Console.CancelKeyPress -= OnCancelKeyPress; + UnregisterShutdownHandlers(); _applicationStartedRegistration.Dispose(); _applicationStoppingRegistration.Dispose(); } + + private partial void UnregisterShutdownHandlers(); } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs new file mode 100644 index 00000000000..174a85ec5f9 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Microsoft.Extensions.Hosting.Internal +{ + public partial class ConsoleLifetime : IHostLifetime + { + private PosixSignalRegistration _sigIntRegistration; + private PosixSignalRegistration _sigQuitRegistration; + private PosixSignalRegistration _sigTermRegistration; + + private partial void RegisterShutdownHandlers() + { + Action handler = HandlePosixSignal; + _sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, handler); + _sigQuitRegistration = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, handler); + _sigTermRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, handler); + } + + private void HandlePosixSignal(PosixSignalContext context) + { + Debug.Assert(context.Signal == PosixSignal.SIGINT || context.Signal == PosixSignal.SIGQUIT || context.Signal == PosixSignal.SIGTERM); + + context.Cancel = true; + ApplicationLifetime.StopApplication(); + } + + private partial void UnregisterShutdownHandlers() + { + _sigIntRegistration?.Dispose(); + _sigQuitRegistration?.Dispose(); + _sigTermRegistration?.Dispose(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.notnetcoreapp.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.notnetcoreapp.cs new file mode 100644 index 00000000000..7f08987205c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.notnetcoreapp.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.Hosting.Internal +{ + public partial class ConsoleLifetime : IHostLifetime + { + private readonly ManualResetEvent _shutdownBlock = new ManualResetEvent(false); + + private partial void RegisterShutdownHandlers() + { + AppDomain.CurrentDomain.ProcessExit += OnProcessExit; + Console.CancelKeyPress += OnCancelKeyPress; + } + + private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + ApplicationLifetime.StopApplication(); + + // Don't block in process shutdown for CTRL+C/SIGINT since we can set e.Cancel to true + // we assume that application code will unwind once StopApplication signals the token + _shutdownBlock.Set(); + } + + private void OnProcessExit(object sender, EventArgs e) + { + ApplicationLifetime.StopApplication(); + if (!_shutdownBlock.WaitOne(HostOptions.ShutdownTimeout)) + { + Logger.LogInformation("Waiting for the host to be disposed. Ensure all 'IHost' instances are wrapped in 'using' blocks."); + } + + // wait one more time after the above log message, but only for ShutdownTimeout, so it doesn't hang forever + _shutdownBlock.WaitOne(HostOptions.ShutdownTimeout); + + // On Linux if the shutdown is triggered by SIGTERM then that's signaled with the 143 exit code. + // Suppress that since we shut down gracefully. https://github.com/dotnet/aspnetcore/issues/6526 + System.Environment.ExitCode = 0; + } + + private partial void UnregisterShutdownHandlers() + { + _shutdownBlock.Set(); + + AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; + Console.CancelKeyPress -= OnCancelKeyPress; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/NullLifetime.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/NullLifetime.cs new file mode 100644 index 00000000000..bad58fb0e0b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/NullLifetime.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Hosting.Internal +{ + /// + /// Minimalistic lifetime that does nothing. + /// + internal class NullLifetime : IHostLifetime + { + public Task WaitForStartAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index 4b7cf54479b..b868be57845 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -1,16 +1,30 @@ - + - netstandard2.0;netstandard2.1;net461 + $(NetCoreAppCurrent);netstandard2.0;netstandard2.1;net461 true Hosting and startup infrastructures for applications. + + false - + + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/ConsoleLifetimeExitTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/ConsoleLifetimeExitTests.cs new file mode 100644 index 00000000000..ab803145a43 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/ConsoleLifetimeExitTests.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.Extensions.Hosting.Tests +{ + public class ConsoleLifetimeExitTests + { + /// + /// Tests that a Hosted process that receives SIGTERM/SIGINT/SIGQUIT completes successfully + /// and the rest of "main" gets executed. + /// + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [PlatformSpecific(TestPlatforms.AnyUnix)] + [InlineData(SIGTERM)] + [InlineData(SIGINT)] + [InlineData(SIGQUIT)] + public async Task EnsureSignalContinuesMainMethod(int signal) + { + using var remoteHandle = RemoteExecutor.Invoke(async () => + { + await Host.CreateDefaultBuilder() + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }) + .RunConsoleAsync(); + + // adding this delay ensures the "main" method loses in a race with the normal process exit + // and can cause the below message not to be written when the normal process exit isn't canceled by the + // SIGTERM handler + await Task.Delay(100); + + Console.WriteLine("Run has completed"); + return 123; + }, new RemoteInvokeOptions() { Start = false, ExpectedExitCode = 123 }); + + remoteHandle.Process.StartInfo.RedirectStandardOutput = true; + remoteHandle.Process.Start(); + + // wait for the host process to start + string line; + while ((line = remoteHandle.Process.StandardOutput.ReadLine()).EndsWith("Started")) + { + await Task.Delay(20); + } + + // send the signal to the process + kill(remoteHandle.Process.Id, signal); + + remoteHandle.Process.WaitForExit(); + + string processOutput = remoteHandle.Process.StandardOutput.ReadToEnd(); + Assert.Contains("Run has completed", processOutput); + Assert.Equal(123, remoteHandle.Process.ExitCode); + } + + private const int SIGINT = 2; + private const int SIGQUIT = 3; + private const int SIGTERM = 15; + + [DllImport("libc", SetLastError = true)] + private static extern int kill(int pid, int sig); + + private class EnsureSignalContinuesMainMethodWorker : BackgroundService + { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + await Task.Delay(20, stoppingToken); + Console.WriteLine("Started"); + } + catch (OperationCanceledException) + { + return; + } + } + } + } + + /// + /// Tests that calling Environment.Exit from a Hosted app sets the correct exit code. + /// + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + // SIGTERM is only handled on net6.0+, so the workaround to "clobber" the exit code is still in place on NetFramework + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void EnsureEnvironmentExitCode() + { + using var remoteHandle = RemoteExecutor.Invoke(async () => + { + await Host.CreateDefaultBuilder() + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }) + .RunConsoleAsync(); + }); + + remoteHandle.Process.WaitForExit(); + + Assert.Equal(124, remoteHandle.Process.ExitCode); + } + + private class EnsureEnvironmentExitCodeWorker : BackgroundService + { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await Task.Run(() => + { + Environment.Exit(124); + }); + } + } + + /// + /// Tests that calling Environment.Exit from the "main" thread doesn't hang the process forever. + /// + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void EnsureEnvironmentExitDoesntHang() + { + using var remoteHandle = RemoteExecutor.Invoke(async () => + { + await Host.CreateDefaultBuilder() + .ConfigureHostOptions(o => o.ShutdownTimeout = TimeSpan.FromMilliseconds(100)) + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }) + .RunConsoleAsync(); + }, new RemoteInvokeOptions() { TimeOut = 10_000 }); // give a 10 second time out, so if this does hang, it doesn't hang for the full timeout + + Assert.True(remoteHandle.Process.WaitForExit(10_000), "The hosted process should have exited within 10 seconds"); + + // SIGTERM is only handled on net6.0+, so the workaround to "clobber" the exit code is still in place on NetFramework + int expectedExitCode = PlatformDetection.IsNetFramework ? 0 : 125; + Assert.Equal(expectedExitCode, remoteHandle.Process.ExitCode); + } + + private class EnsureEnvironmentExitDoesntHangWorker : BackgroundService + { + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + Environment.Exit(125); + return Task.CompletedTask; + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Microsoft.Extensions.Hosting.Unit.Tests.csproj b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Microsoft.Extensions.Hosting.Unit.Tests.csproj index c5049661e96..ee0c814c41b 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Microsoft.Extensions.Hosting.Unit.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Microsoft.Extensions.Hosting.Unit.Tests.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);net461 true true + true From 136be33a05e63e3640a190cf625eeafa0920f91e Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 21 Jul 2021 22:23:23 -0700 Subject: [PATCH 739/926] Remove disabled tests that depend on the verifier (#56118) The verifier is not supported in .NET Core. Fixes #5812 --- .../coverage/importer/Desktop/bleref.il | 48 ------ .../importer/Desktop/bleref_il_d.ilproj | 14 -- .../importer/Desktop/bleref_il_r.ilproj | 15 -- .../importer/Desktop/ldelemnullarr1.il | 61 ------- .../Desktop/ldelemnullarr1_il_d.ilproj | 14 -- .../Desktop/ldelemnullarr1_il_r.ilproj | 15 -- .../coverage/importer/Desktop/nonrefsdarr.il | 53 ------ .../importer/Desktop/nonrefsdarr_il_d.ilproj | 14 -- .../importer/Desktop/nonrefsdarr_il_r.ilproj | 15 -- .../jit64/localloc/verify/verify01_dynamic.il | 89 ---------- .../localloc/verify/verify01_dynamic.ilproj | 15 -- .../jit64/localloc/verify/verify01_large.il | 86 ---------- .../localloc/verify/verify01_large.ilproj | 15 -- .../jit64/localloc/verify/verify01_small.il | 86 ---------- .../localloc/verify/verify01_small.ilproj | 15 -- .../JIT/jit64/regress/ndpw/21220/21220.il | 155 ------------------ .../jit64/regress/ndpw/21220/b21220.ilproj | 15 -- 17 files changed, 725 deletions(-) delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/bleref.il delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_d.ilproj delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_r.ilproj delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1.il delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_d.ilproj delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_r.ilproj delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr.il delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_d.ilproj delete mode 100644 src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_r.ilproj delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_dynamic.il delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_dynamic.ilproj delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_large.il delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_large.ilproj delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_small.il delete mode 100644 src/tests/JIT/jit64/localloc/verify/verify01_small.ilproj delete mode 100644 src/tests/JIT/jit64/regress/ndpw/21220/21220.il delete mode 100644 src/tests/JIT/jit64/regress/ndpw/21220/b21220.ilproj diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref.il b/src/tests/JIT/Directed/coverage/importer/Desktop/bleref.il deleted file mode 100644 index 981fb262925..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref.il +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern legacy library mscorlib {} -.assembly bleref { } -.method public static int32 f() -{ -ldnull -ldnull -ble OUT -OUT: -ldc.i4 100 -ret -} - .method public static int32 Main() cil managed - { - .entrypoint - .maxstack 5 - .try - { - call int32 f() - leave.s FAIL - } - catch [mscorlib]System.Security.VerificationException - { - pop - leave.s PASS - } - FAIL: - ldstr "Should have caught System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldstr "FAILED" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4 1 - ret - PASS: - ldstr "Caught System.Security.VerificationException as expected" - call void [System.Console]System.Console::WriteLine(string) - ldstr "PASSED" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4 100 - ret - } diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_d.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_d.ilproj deleted file mode 100644 index d1124f16379..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_d.ilproj +++ /dev/null @@ -1,14 +0,0 @@ - - - Exe - 1 - - true - - - Full - - - - - diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_r.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_r.ilproj deleted file mode 100644 index af3a708b4bf..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/bleref_il_r.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - False - - - - - diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1.il b/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1.il deleted file mode 100644 index cd6d1045fd1..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1.il +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern legacy library mscorlib {} -.assembly legacy library ldelemnullarr1 { } -.class private auto ansi beforefieldinit ldelemnullarr1 - extends [mscorlib]System.Object -{ - .method public hidebysig static void f() cil managed - { - .maxstack 2 - .locals init (int32[] V_0) - IL_0002: ldnull - IL_0003: ldc.i4.0 - IL_0004: ldelem.ref - IL_0005: call void [System.Console]System.Console::WriteLine(int32) - IL_000a: ret - } - - .method public hidebysig static int32 Main() cil managed - { - .entrypoint - .maxstack 1 - .locals init (int32 V_0) - .try - { - IL_0000: call void ldelemnullarr1::f() - IL_0005: leave.s IL_0021 - - } - catch [mscorlib]System.Security.VerificationException - { - IL_0007: pop - IL_0008: ldstr "Caught expected System.Security.VerificationException" - IL_000d: call void [System.Console]System.Console::WriteLine(string) - IL_0012: ldstr "PASSED" - IL_0017: call void [System.Console]System.Console::WriteLine(string) - IL_001c: ldc.i4.s 100 - IL_001e: stloc.0 - IL_001f: leave.s IL_0039 - - } - IL_0021: ldstr "Should have caught System.Security.VerificationException" - IL_0026: call void [System.Console]System.Console::WriteLine(string) - IL_002b: ldstr "FAILED" - IL_0030: call void [System.Console]System.Console::WriteLine(string) - IL_0035: ldc.i4.1 - IL_0036: stloc.0 - IL_0037: br.s IL_0039 - - IL_0039: ldloc.0 - IL_003a: ret - } -} diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_d.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_d.ilproj deleted file mode 100644 index 6546585fd8a..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_d.ilproj +++ /dev/null @@ -1,14 +0,0 @@ - - - Exe - 1 - - true - - - Full - - - - - diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_r.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_r.ilproj deleted file mode 100644 index 7cf7a3f8026..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/ldelemnullarr1_il_r.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - False - - - - - diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr.il b/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr.il deleted file mode 100644 index be9ffed1e48..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr.il +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern legacy library mscorlib {} -.assembly legacy library arrlen { } -.class private auto ansi beforefieldinit nonrefsdarr - extends [mscorlib]System.Object -{ - .method public static void f() cil managed - { - .maxstack 1 - IL_0000: ldc.i4.1 - IL_0003: ldlen - IL_0004: conv.i4 - IL_0005: call void [System.Console]System.Console::WriteLine(int32) - IL_0010: ret - } - .method public static int32 Main() cil managed - { - .entrypoint - .maxstack 5 - .try - { - call void nonrefsdarr::f() - leave.s FAIL - } - catch [mscorlib]System.Security.VerificationException - { - pop - leave.s PASS - } - FAIL: - ldstr "Should have caught System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldstr "FAILED" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4 1 - ret - PASS: - ldstr "Caught System.Security.VerificationException as expected" - call void [System.Console]System.Console::WriteLine(string) - ldstr "PASSED" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4 100 - ret - } -} diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_d.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_d.ilproj deleted file mode 100644 index 8fc89f05a01..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_d.ilproj +++ /dev/null @@ -1,14 +0,0 @@ - - - Exe - 1 - - true - - - Full - - - - - diff --git a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_r.ilproj b/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_r.ilproj deleted file mode 100644 index eec98028fc6..00000000000 --- a/src/tests/JIT/Directed/coverage/importer/Desktop/nonrefsdarr_il_r.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - False - - - - - diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.il b/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.il deleted file mode 100644 index 03bf832c485..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.il +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// Test Invalid program (evaluation stack is not empty before localloc call - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern mscorlib{} -.assembly eh01 -{ -} - -.class private auto ansi beforefieldinit LocallocTest - extends [mscorlib]System.Object -{ - .field public static int32 stackAllocSize - .method public hidebysig static int32 Main() cil managed - { - .entrypoint - .maxstack 1 - .locals init(int32 V_0) - ldc.i4 0x1000 - stsfld int32 LocallocTest::stackAllocSize - .try - { - IL_0000: call void LocallocTest::func() - IL_0005: leave.s IL_0027 - - } - catch [mscorlib]System.Security.VerificationException - { - IL_0007: pop - IL_0008: ldstr "Passed" - IL_000d: call void [System.Console]System.Console::WriteLine(string) - IL_0012: ldc.i4.s 100 - IL_0014: stloc.0 - IL_0015: leave.s IL_0035 - - } - catch [mscorlib]System.InvalidProgramException - { - IL_0017: pop - IL_0018: ldstr "Passed" - IL_009d: call void [System.Console]System.Console::WriteLine(string) - IL_0022: ldc.i4.s 100 - IL_0024: stloc.0 - IL_0025: leave.s IL_0035 - - } - IL_0027: ldstr "Failed" - IL_002c: call void [System.Console]System.Console::WriteLine(string) - IL_0031: ldc.i4.1 - IL_0032: stloc.0 - IL_0033: br.s IL_0035 - - IL_0035: ldloc.0 - IL_0036: ret - } - - .method private hidebysig static void func() cil managed - { - .maxstack 2 - .locals init(int32* V_0, - int32* V_1) - IL_0000: ldc.i4.4 - IL_0001: ldsfld int32 LocallocTest::stackAllocSize - IL_0006: mul - IL_0007: localloc - IL_0009: stloc.1 - IL_000a: ldc.i4.4 - IL_000b: ldsfld int32 LocallocTest::stackAllocSize - IL_0010: mul - IL_0011: localloc - IL_0013: stloc.0 - IL_0014: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } - -} diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.ilproj b/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.ilproj deleted file mode 100644 index 675a7c4acd5..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_dynamic.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - True - - - - - diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_large.il b/src/tests/JIT/jit64/localloc/verify/verify01_large.il deleted file mode 100644 index 022c2af478c..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_large.il +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// Test Invalid program (evaluation stack is not empty before localloc call - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern mscorlib{} -.assembly eh01 -{ -} - -.class private auto ansi beforefieldinit LocallocTest - extends [mscorlib]System.Object -{ - .method public hidebysig static int32 Main() cil managed - { - .entrypoint - .maxstack 1 - .locals init(int32 V_0) - .try - { - IL_0000: call void LocallocTest::func() - IL_0005: leave.s IL_0027 - - } - catch [mscorlib]System.Security.VerificationException - { - IL_0007: pop - IL_0008: ldstr "Passed" - IL_000d: call void [System.Console]System.Console::WriteLine(string) - IL_0012: ldc.i4.s 100 - IL_0014: stloc.0 - IL_0015: leave.s IL_0035 - - } - catch [mscorlib]System.InvalidProgramException - { - IL_0017: pop - IL_0018: ldstr "Passed" - IL_009d: call void [System.Console]System.Console::WriteLine(string) - IL_0022: ldc.i4.s 100 - IL_0024: stloc.0 - IL_0025: leave.s IL_0035 - - } - IL_0027: ldstr "Failed" - IL_002c: call void [System.Console]System.Console::WriteLine(string) - IL_0031: ldc.i4.1 - IL_0032: stloc.0 - IL_0033: br.s IL_0035 - - IL_0035: ldloc.0 - IL_0036: ret - } - - .method private hidebysig static void func() cil managed - { - .maxstack 2 - .locals init(int32* V_0, - int32* V_1) - IL_0000: ldc.i4.4 - IL_0001: ldc.i4 0x1000 - IL_0006: mul - IL_0007: localloc - IL_0009: stloc.1 - IL_000a: ldc.i4.4 - IL_000b: ldc.i4 0x1000 - IL_0010: mul - IL_0011: localloc - IL_0013: stloc.0 - IL_0014: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } - -} diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_large.ilproj b/src/tests/JIT/jit64/localloc/verify/verify01_large.ilproj deleted file mode 100644 index 5274b323482..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_large.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - True - - - - - diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_small.il b/src/tests/JIT/jit64/localloc/verify/verify01_small.il deleted file mode 100644 index b285a9d6a11..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_small.il +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// Test Invalid program (evaluation stack is not empty before localloc call - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern mscorlib{} -.assembly eh01 -{ -} - -.class private auto ansi beforefieldinit LocallocTest - extends [mscorlib]System.Object -{ - .method public hidebysig static int32 Main() cil managed - { - .entrypoint - .maxstack 1 - .locals init(int32 V_0) - .try - { - IL_0000: call void LocallocTest::func() - IL_0005: leave.s IL_0027 - - } - catch [mscorlib]System.Security.VerificationException - { - IL_0007: pop - IL_0008: ldstr "Passed" - IL_000d: call void [System.Console]System.Console::WriteLine(string) - IL_0012: ldc.i4.s 100 - IL_0014: stloc.0 - IL_0015: leave.s IL_0035 - - } - catch [mscorlib]System.InvalidProgramException - { - IL_0017: pop - IL_0018: ldstr "Passed" - IL_009d: call void [System.Console]System.Console::WriteLine(string) - IL_0022: ldc.i4.s 100 - IL_0024: stloc.0 - IL_0025: leave.s IL_0035 - - } - IL_0027: ldstr "Failed" - IL_002c: call void [System.Console]System.Console::WriteLine(string) - IL_0031: ldc.i4.1 - IL_0032: stloc.0 - IL_0033: br.s IL_0035 - - IL_0035: ldloc.0 - IL_0036: ret - } - - .method private hidebysig static void func() cil managed - { - .maxstack 2 - .locals init(int32* V_0, - int32* V_1) - IL_0000: ldc.i4.4 - IL_0001: ldc.i4.1 - IL_0002: mul - IL_0003: localloc - IL_0005: stloc.1 - IL_0006: ldc.i4.4 - IL_0007: ldc.i4.1 - IL_0008: mul - IL_0009: localloc - IL_000b: stloc.0 - IL_000c: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } - -} diff --git a/src/tests/JIT/jit64/localloc/verify/verify01_small.ilproj b/src/tests/JIT/jit64/localloc/verify/verify01_small.ilproj deleted file mode 100644 index 4f7aeae4bac..00000000000 --- a/src/tests/JIT/jit64/localloc/verify/verify01_small.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - True - - - - - diff --git a/src/tests/JIT/jit64/regress/ndpw/21220/21220.il b/src/tests/JIT/jit64/regress/ndpw/21220/21220.il deleted file mode 100644 index 91b68ee8f14..00000000000 --- a/src/tests/JIT/jit64/regress/ndpw/21220/21220.il +++ /dev/null @@ -1,155 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .ver 4:0:0:0 -} -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) - .hash = (0E 33 6B E4 AA 53 F0 DC 70 EA E2 E0 A3 7E BB BC 53 7B 75 B4 ) - .ver 2:0:3600:0 -} - -.assembly extern Microsoft.VisualC -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) - .hash = (C4 73 BC B5 51 0F 9C B5 ED 4B 30 6E 4A D1 62 7F 2E 43 D6 5E ) - .ver 8:0:1200:0 -} - -.assembly b21220 -{ - .hash algorithm 0x00008004 - .ver 0:0:0:0 -} - -.module b21220.exe -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 -.corflags 0x00000001 - -.method public static int32 main() cil managed -{ - .entrypoint - .custom instance void [Microsoft.VisualC]Microsoft.VisualC.DecoratedNameAttribute::.ctor(string) = ( 01 00 0F 3F 6D 61 69 6E 40 40 24 24 48 59 4D 48 58 5A 00 00 ) - .maxstack 3 - .locals init (class C V_0, int32 V_1) - - newobj instance void C::.ctor() - stloc.0 - ldc.i4.s 100 - stloc.1 - - .try - { - ldloc.0 - call instance int32& C::retLocalByref() - pop - ldstr "Failed: Expected C::retLocalByref() to throw an System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4.s 5 - stloc.1 - leave.s Exit - } - catch [mscorlib]System.Security.VerificationException - { - ldstr "Passed" - call void [System.Console]System.Console::WriteLine(string) - leave.s Next - } - catch [mscorlib]System.Exception - { - ldstr "Failed: Expected C::retLocalByref() to throw an System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4.s 5 - stloc.1 - leave.s Exit - } -Next: - .try - { - ldloc.0 - call instance int32& C::retLocalByref2() - pop - ldstr "Failed: Expected C::retLocalByref() to throw an System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4.s 5 - stloc.1 - leave.s Exit - } - catch [mscorlib]System.Security.VerificationException - { - ldstr "Passed" - call void [System.Console]System.Console::WriteLine(string) - leave.s Exit - } - catch [mscorlib]System.Exception - { - ldstr "Failed: Expected C::retLocalByref() to throw an System.Security.VerificationException" - call void [System.Console]System.Console::WriteLine(string) - ldc.i4.s 5 - stloc.1 - leave.s Exit - } - -Exit: - ldloc.1 - call void [System.Console]System.Console::WriteLine(int32) - ldloc.1 - ret -} - -.class public auto ansi C extends [mscorlib]System.Object -{ - .field public int32 i - - .method public specialname rtspecialname instance void .ctor() cil managed - { - .maxstack 1 - - ldarg.0 - call instance void [mscorlib]System.Object::.ctor() - ret - } - - .method public instance int32& retLocalByref() cil managed - { - .maxstack 4 - .locals init (int32 V_0) - - ldarg.0 - ldfld int32 C::i - ldc.i4.0 - bne.un A - ldarg.0 - ldflda int32 C::i - br Exit -A: - ldloca V_0 -Exit: - ret - } - - .method public instance int32& retLocalByref2() cil managed - { - .maxstack 4 - .locals init (int32 V_0) - - ldarg.0 - ldfld int32 C::i - ldc.i4.0 - bne.un A - ldloca V_0 - br Exit -A: - ldarg.0 - ldflda int32 C::i -Exit: - ret - } -} diff --git a/src/tests/JIT/jit64/regress/ndpw/21220/b21220.ilproj b/src/tests/JIT/jit64/regress/ndpw/21220/b21220.ilproj deleted file mode 100644 index fc2aecf7528..00000000000 --- a/src/tests/JIT/jit64/regress/ndpw/21220/b21220.ilproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - 1 - - true - - - PdbOnly - True - - - - - From d82faa5087956920b4c23734d211b7ee5f91f4d4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 22 Jul 2021 09:13:57 +0200 Subject: [PATCH 740/926] use PoolingAsyncValueTaskMethodBuilder in BufferedFileStreamStrategy (#56095) --- .../src/System/IO/Strategies/BufferedFileStreamStrategy.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 9aaa16ff519..00d0d2a59b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -423,6 +423,7 @@ namespace System.IO.Strategies } } + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] private async ValueTask ReadAsyncSlowPath(Task semaphoreLockTask, Memory buffer, CancellationToken cancellationToken) { Debug.Assert(_asyncActiveSemaphore != null); @@ -705,6 +706,7 @@ namespace System.IO.Strategies } } + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] private async ValueTask WriteAsyncSlowPath(Task semaphoreLockTask, ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(_asyncActiveSemaphore != null); From 14d6532f388903f425f0a3b9dea697c5a28293d3 Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Thu, 22 Jul 2021 00:24:38 -0700 Subject: [PATCH 741/926] Improve MetricEventSource error handling (#56083) Enabling multiple instances of a collection tool was causing both tools to lose their connections and it wasn't obvious from within the tool what had happened. I modified the behavior so that once a tool connects successfully it should never be disconnected and made a distinctive error event so that the additional tool instances can easily identify they are trying an unsupported operation. --- .../Diagnostics/Metrics/MetricsEventSource.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs index 38c4216bdcd..a785bf15e1c 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs @@ -175,6 +175,12 @@ namespace System.Diagnostics.Metrics WriteEvent(14, sessionId, errorMessage); } + [Event(15, Keywords = Keywords.TimeSeriesValues | Keywords.Messages | Keywords.InstrumentPublishing)] + public void MultipleSessionsNotSupportedError(string runningSessionId) + { + WriteEvent(15, runningSessionId); + } + /// /// Called when the EventSource gets a command from a EventListener or ETW. /// @@ -218,6 +224,19 @@ namespace System.Diagnostics.Metrics { if (_aggregationManager != null) { + if (command.Command == EventCommand.Enable || command.Command == EventCommand.Update) + { + // trying to add more sessions is not supported + // EventSource doesn't provide an API that allows us to enumerate the listeners' + // filter arguments independently or to easily track them ourselves. For example + // removing a listener still shows up as EventCommand.Enable as long as at least + // one other listener is active. In the future we might be able to figure out how + // to infer the changes from the info we do have or add a better API but for now + // I am taking the simple route and not supporting it. + Log.MultipleSessionsNotSupportedError(_sessionId); + return; + } + _aggregationManager.Dispose(); _aggregationManager = null; Log.Message($"Previous session with id {_sessionId} is stopped"); From d38202ee306533ac89a7515a206e312e7b71b6c1 Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Thu, 22 Jul 2021 10:04:11 +0200 Subject: [PATCH 742/926] Handle new DOTNET_ in complement to old COMPlus_ envvar prefix on Mono. (#56098) https://github.com/dotnet/runtime/pull/50507 added support for DOTNET_ prefix for env variables previously using COMPlus_. That change was however not implemented on Mono, still using COMPlus for some EventPipe, DiagnosticServer env variables. This fix adds similar support on Mono, first looking for a DOTNET_ version of the environment variable and if not found, fallback to old COMPlus_ as fallback. Documentation for .net6 have been updated to use new names: https://docs.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables --- src/mono/mono/eventpipe/ds-rt-mono.h | 4 +++- src/mono/mono/eventpipe/ep-rt-mono.h | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/mono/mono/eventpipe/ds-rt-mono.h b/src/mono/mono/eventpipe/ds-rt-mono.h index 5e76e45ccea..1785d6e5103 100644 --- a/src/mono/mono/eventpipe/ds-rt-mono.h +++ b/src/mono/mono/eventpipe/ds-rt-mono.h @@ -135,7 +135,9 @@ bool ds_rt_config_value_get_enable (void) { bool enable = true; - gchar *value = g_getenv ("COMPlus_EnableDiagnostics"); + gchar *value = g_getenv ("DOTNET_EnableDiagnostics"); + if (!value) + value = g_getenv ("COMPlus_EnableDiagnostics"); if (value && atoi (value) == 0) enable = false; g_free (value); diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 49b6456cdb9..b42aa108f7c 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -859,7 +859,9 @@ bool ep_rt_config_value_get_enable (void) { bool enable = false; - gchar *value = g_getenv ("COMPlus_EnableEventPipe"); + gchar *value = g_getenv ("DOTNET_EnableEventPipe"); + if (!value) + value = g_getenv ("COMPlus_EnableEventPipe"); if (value && atoi (value) == 1) enable = true; g_free (value); @@ -871,7 +873,10 @@ inline ep_char8_t * ep_rt_config_value_get_config (void) { - return g_getenv ("COMPlus_EventPipeConfig"); + gchar *value = g_getenv ("DOTNET_EventPipeConfig"); + if (!value) + value = g_getenv ("COMPlus_EventPipeConfig"); + return (ep_char8_t *)value; } static @@ -879,7 +884,10 @@ inline ep_char8_t * ep_rt_config_value_get_output_path (void) { - return g_getenv ("COMPlus_EventPipeOutputPath"); + gchar *value = g_getenv ("DOTNET_EventPipeOutputPath"); + if (!value) + value = g_getenv ("COMPlus_EventPipeOutputPath"); + return (ep_char8_t *)value; } static @@ -888,7 +896,9 @@ uint32_t ep_rt_config_value_get_circular_mb (void) { uint32_t circular_mb = 0; - gchar *value = g_getenv ("COMPlus_EventPipeCircularMB"); + gchar *value = g_getenv ("DOTNET_EventPipeCircularMB"); + if (!value) + value = g_getenv ("COMPlus_EventPipeCircularMB"); if (value) circular_mb = strtoul (value, NULL, 10); g_free (value); @@ -901,7 +911,9 @@ bool ep_rt_config_value_get_output_streaming (void) { bool enable = false; - gchar *value = g_getenv ("COMPlus_EventPipeOutputStreaming"); + gchar *value = g_getenv ("DOTNET_EventPipeOutputStreaming"); + if (!value) + value = g_getenv ("COMPlus_EventPipeOutputStreaming"); if (value && atoi (value) == 1) enable = true; g_free (value); @@ -923,7 +935,9 @@ uint32_t ep_rt_config_value_get_rundown (void) { uint32_t value_uint32_t = 1; - gchar *value = g_getenv ("COMPlus_EventPipeRundown"); + gchar *value = g_getenv ("DOTNET_EventPipeRundown"); + if (!value) + value = g_getenv ("COMPlus_EventPipeRundown"); if (value) value_uint32_t = (uint32_t)atoi (value); g_free (value); From 57fa2fc464f8533f13dad713bc2ce46e8d949e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Thu, 22 Jul 2021 01:11:41 -0700 Subject: [PATCH 743/926] Remove Jozkee from System.Text.Json (#56119) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b4f0685cf0d..46b99b8d76c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ /src/libraries/Common/src/Interop/ @dotnet/platform-deps-team /src/libraries/Common/src/System/Net/Http/aspnetcore/ @dotnet/http /src/libraries/Common/tests/Tests/System/Net/aspnetcore/ @dotnet/http -/src/libraries/System.Text.Json/ @steveharter @layomia @Jozkee @eiriktsarpalis +/src/libraries/System.Text.Json/ @steveharter @layomia @eiriktsarpalis # CoreCLR Code Owners From b937677e8f8601848d29bc072a93cc0c6e21576d Mon Sep 17 00:00:00 2001 From: Neale Ferguson Date: Thu, 22 Jul 2021 19:02:56 +1000 Subject: [PATCH 744/926] - Enable full float32 support (#56111) -- Add instructions for FP operations: abs[f]/ceil[f]/floor[f]/round[f]/sqrt[f]/trunc[f] -- Enable these instructions in mono_arch_emit_inst_for_method() -- Handle return of float32 results -- Correct rounding mode for OP_LCONV_TO_R_UN processing - Enable MONO_OPT_LINEAR optimizations -- Correct prolog processing of structure returned variable - Rework OP_LOCALLOC for cases where alloc size > 4k - Add OP_POPCNTxx support - Minor typo (missing tab) --- src/mono/mono/arch/s390x/s390x-codegen.h | 45 ++- src/mono/mono/mini/cpu-s390x.md | 17 +- src/mono/mono/mini/mini-s390x.c | 445 ++++++++++++++++++----- src/mono/mono/mini/mini-s390x.h | 3 +- 4 files changed, 406 insertions(+), 104 deletions(-) diff --git a/src/mono/mono/arch/s390x/s390x-codegen.h b/src/mono/mono/arch/s390x/s390x-codegen.h index e779f862237..ec9562a0e04 100644 --- a/src/mono/mono/arch/s390x/s390x-codegen.h +++ b/src/mono/mono/arch/s390x/s390x-codegen.h @@ -896,11 +896,18 @@ typedef struct { #define S390_RIE_4(c,opc,g1,i2,m3) do \ { \ - s390_emit16(c, ((opc & 0xff00) | (g1) << 4); \ + s390_emit16(c, ((opc & 0xff00) | (g1) << 4)); \ s390_emit16(c, (i2)); \ s390_emit16(c, ((m3) << 12 | (opc & 0xff))); \ } while (0) +#define S390_RIE_6(c,opc,g1,g2,i3,i4,i5) do \ +{ \ + s390_emit16(c, ((opc & 0xff00) | ((g1) << 4) | g2)); \ + s390_emit16(c, ((i3) << 8) | i4); \ + s390_emit16(c, ((i5) << 8 | (opc & 0xff))); \ +} while (0) + #define S390_RIL_1(c,opc,g1,m2) do \ { \ s390_emit16(c, ((opc >> 4) << 8 | (g1) << 4 | (opc & 0xf))); \ @@ -918,17 +925,24 @@ typedef struct { s390_emit16(c, ((opc, & 0xff00) | (r1) << 4) | (r2)); \ s390_emit16(c, ((b) << 12) | (d)); \ s390_emit16(c, ((i) << 4) | ((opc) & 0xff)); \ -} +} while (0) #define S390_RRS(c,opc,r1,r2,m3,b,d) do \ { \ s390_emit16(c, ((opc, & 0xff00) | (r1) << 4) | (r2)); \ s390_emit16(c, ((b) << 12) | (d)); \ s390_emit16(c, ((m3) << 12) | ((opc) & 0xff)); \ -} +} while (0) #define S390_SI(c,opc,s1,p1,m2) s390_emit32(c, (opc << 24 | (m2) << 16 | (s1) << 12 | ((p1) & 0xfff))); +#define S390_SIL(c, opc, b, d, i) do \ +{ \ + s390_emit16(c, opc); \ + s390_emit16(c, ((b) << 12) | ((d) & 0x0fff)); \ + s390_emit16(c, ((i) & 0xffff)); \ +} while (0) + #define S390_SIY(c,opc,s1,p1,m2) do \ { \ s390_emit16(c, ((opc & 0xff00) | m2)); \ @@ -1242,7 +1256,7 @@ typedef struct { #define s390_clgij(c, r, i, b) S390_RIE_3(c, 0xec7d, r, i, m, d) #define s390_clgr(c, r1, r2) S390_RRE(c, 0xb921, r1, r2) #define s390_clgdbr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb3ad, r1, m3, r2, m4) -#define s390_clgebr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb39c, r1, m3, r2, m4) +#define s390_clgebr(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb3ac, r1, m3, r2, m4) #define s390_clgrj(c, r1, r2, m, v) S390_RIE_2(c, 0xec65, r1, r2, m, v) #define s390_clgrb(c, r1, r2, m3, b, d) S390_RRS(c, 0xece5, r1, r2, m3, b, d) #define s390_cli(c, b, d, v) S390_SI(c, 0x95, b, d, v) @@ -1257,6 +1271,7 @@ typedef struct { #define s390_crl(c, r, v) S390_RIL_1(c, 0xc6d, r, v) #define s390_crt(c, r1, r2, m3) S390_RRF_2(c, 0xb972, r1, r2, m3); #define s390_cgrt(c, r1, r2, m3) S390_RRF_2(c, 0xb960, r1, r2, m3); +#define s390_cpsdr(c, r1, r2, r3) S390_RRF_2(c, 0xb372, r1, r2, r3); #define s390_cs(c, r1, r2, b, d) S390_RX(c, 0xba, r1, r2, b, d) #define s390_csg(c, r1, r2, b, d) S390_RSY_1(c, 0xeb30, r1, r2, b, d) #define s390_csst(c, d1, b1, d2, b2, r) S390_SSF(c, 0xc82, b1, d1, b2, d2, r) @@ -1272,6 +1287,9 @@ typedef struct { #define s390_dsgfr(c, r1, r2) S390_RRE(c, 0xb91d, r1, r2) #define s390_dsgr(c, r1, r2) S390_RRE(c, 0xb90d, r1, r2) #define s390_ear(c, r1, r2) S390_RRE(c, 0xb24f, r1, r2) +#define s390_fidbra(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb35f, r1, m3, r2, m4) +#define s390_fiebra(c, r1, m3, r2, m4) S390_RRF_4(c, 0xb357, r1, m3, r2, m4) +#define s390_flogr(c, r1, r2) S390_RRE(c, 0xb983, r1, r2) #define s390_ic(c, r, x, b, d) S390_RX(c, 0x43, r, x, b, d) #define s390_icm(c, r, m, b, d) S390_RX(c, 0xbf, r, m, b, d) #define s390_icmy(c, r, x, b, d) S390_RXY(c, 0xeb81, r, x, b, d) @@ -1388,6 +1406,19 @@ typedef struct { #define s390_lnebr(c, r1, r2) S390_RRE(c, 0xb301, r1, r2) #define s390_lngr(c, r1, r2) S390_RRE(c, 0xb901, r1, r2) #define s390_lnr(c, r1, r2) S390_RR(c, 0x11, r1, r2) +#define s390_loc(c, r, m, b, d) S390_RSY_2(c, 0xebf2, r, m, b, d) +#define s390_locg(c, r, m, b, d) S390_RSY_2(c, 0xebe2, r, m, b, d) +#define s390_locr(c, r1, m, r2) S390_RRF_2(c, 0xb9f2, r1, m, r2) +#define s390_locgr(c, r1, m, r2) S390_RRF_2(c, 0xb9e2, r1, m, r2) +#define s390_locfh(c, r, n, b, d) S390_RRF_2(c, 0xebe0, r, m, b, d) +#define s390_locfhr(c, r1, m, r2) S390_RRF_2(c, 0xb9e0, r1, m, r2) +#define s390_lpdbr(c, r1, r2) S390_RRE(c, 0xb310, r1, r2) +#define s390_lpebr(c, r1, r2) S390_RRE(c, 0xb300, r1, r2) +#define s390_lpgr(c, r1, r2) S390_RRE(c, 0xb900, r1, r2) +#define s390_lpr(c, r1, r2) S390_RR(c, 0x10, r1, r2) +#define s390_lr(c, r1, r2) S390_RR(c, 0x18, r1, r2) +#define s390_lrl(c, r1, d) S390_RIL_1(c, 0xc4d, r1, d) +#define s390_lt(c, r, x, b, d) S390_RXY(c, 0xe312, r, x, b, d) #define s390_lpdbr(c, r1, r2) S390_RRE(c, 0xb310, r1, r2) #define s390_lpgr(c, r1, r2) S390_RRE(c, 0xb900, r1, r2) #define s390_lpr(c, r1, r2) S390_RR(c, 0x10, r1, r2) @@ -1419,6 +1450,7 @@ typedef struct { #define s390_msr(c, r1, r2) S390_RRE(c, 0xb252, r1, r2) #define s390_msrkc(c, r1, r2, r3) S390_RRF_1(c, 0xb9fd, r1, r2, r3) #define s390_mvc(c, l, b1, d1, b2, d2) S390_SS_1(c, 0xd2, l, b1, d1, b2, d2) +#define s390_mvghi(c, b1, d1, i2) S390_SIL(c, 0xe548, b1, d1, i2) #define s390_mvcl(c, r1, r2) S390_RR(c, 0x0e, r1, r2) #define s390_mvcle(c, r1, r3, d2, b2) S390_RS_1(c, 0xa8, r1, r3, d2, b2) #define s390_mvi(c, b, d, v) S390_SI(c, 0x92, b, d, v) @@ -1439,7 +1471,7 @@ typedef struct { #define s390_mem(c) S390_RR(c, 0x07, 0xe, 0) #define s390_nr(c, r1, r2) S390_RR(c, 0x14, r1, r2) #define s390_nrk(c, r1, r2) S390_RRF_1(c, 0xb9f4, r1, r2) -#define s390_ny(c, r, x, b, d) S390_RRY(c, 0xe354, r1, r2) +#define s390_ny(c, r, x, b, d) S390_RXY(c, 0xe354, r, x, b, d) #define s390_o(c, r, x, b, d) S390_RX(c, 0x56, r, x, b, d) #define s390_oihf(c, r, v) S390_RIL_1(c, 0xc0c, r, v) #define s390_oihh(c, r, v) S390_RI(c, 0xa58, r, v) @@ -1452,6 +1484,9 @@ typedef struct { #define s390_ogr(c, r1, r2) S390_RRE(c, 0xb981, r1, r2) #define s390_ogrk(c, r1, r2, r3) S390_RRF_1(c, 0xb9e6, r1, r2, r3) #define s390_or(c, r1, r2) S390_RR(c, 0x16, r1, r2) +#define s390_oy(c, r, x, b, d) S390_RXY(c, 0xe356, r, x, b, d) +#define s390_popcnt(c, r1, m, r2) S390_RRF_2(c, 0xb9e1, r1, m, r2) +#define s390_risbg(c,r1,r2,i3,i4,i5) S390_RIE_6(c, 0xec55, r1, r2, i3, i4, i5) #define s390_s(c, r, x, b, d) S390_RX(c, 0x5b, r, x, b, d) #define s390_sdb(c, r, x, b, d) S390_RXE(c, 0xed1b, r, x, b, d) #define s390_sdbr(c, r1, r2) S390_RRE(c, 0xb31b, r1, r2) diff --git a/src/mono/mono/mini/cpu-s390x.md b/src/mono/mono/mini/cpu-s390x.md index 2af9ffc1a0e..2c896b90bde 100644 --- a/src/mono/mono/mini/cpu-s390x.md +++ b/src/mono/mono/mini/cpu-s390x.md @@ -147,11 +147,13 @@ r4_conv_to_i4: dest:i src1:f len:16 r4_conv_to_u4: dest:i src1:f len:32 r4_conv_to_i8: dest:i src1:f len:32 r4_conv_to_r8: dest:f src1:f len:17 +r4_conv_to_u8: dest:i src1:f len:17 r4_conv_to_r4: dest:f src1:f len:17 r4_add: dest:f src1:f src2:f clob:1 len:5 r4_sub: dest:f src1:f src2:f clob:1 len:5 r4_mul: dest:f src1:f src2:f clob:1 len:5 r4_div: dest:f src1:f src2:f clob:1 len:5 +r4_rem: dest:f src1:f src2:f clob:1 len:12 r4_neg: dest:f src1:f clob:1 len:23 r4_ceq: dest:i src1:f src2:f len:35 r4_cgt: dest:i src1:f src2:f len:35 @@ -187,11 +189,13 @@ loadu1_membase: dest:i src1:b len:30 loadu2_membase: dest:i src1:b len:30 loadu4_mem: dest:i len:8 loadu4_membase: dest:i src1:b len:30 -localloc: dest:i src1:i len:110 +localloc: dest:i src1:i len:180 memory_barrier: len:10 move: dest:i src1:i len:4 mul_imm: dest:i src1:i len:24 nop: len:4 +popcnt32: dest:i src1:i len:38 +popcnt64: dest:i src1:i len:34 relaxed_nop: len:4 arglist: src1:i len:28 bigmul: len:2 dest:i src1:a src2:i @@ -218,7 +222,18 @@ zext_i4: dest:i src1:i len:4 shl_imm: dest:i src1:i len:10 shr_imm: dest:i src1:i len:10 shr_un_imm: dest:i src1:i len:10 +abs: dest:f src1:f len:4 +absf: dest:f src1:f len:4 +ceil: dest:f src1:f len:4 +ceilf: dest:f src1:f len:4 +floor: dest:f src1:f len:4 +floorf: dest:f src1:f len:4 +round: dest:f src1:f len:4 sqrt: dest:f src1:f len:4 +sqrtf: dest:f src1:f len:4 +trunc: dest:f src1:f len:4 +truncf: dest:f src1:f len:4 +fcopysign: dest:f src1:f src2:f len:4 start_handler: len:26 store_membase_imm: dest:b len:46 store_membase_reg: dest:b src1:i len:26 diff --git a/src/mono/mono/mini/mini-s390x.c b/src/mono/mono/mini/mini-s390x.c index bc4e9b24d11..4f0a400673a 100644 --- a/src/mono/mono/mini/mini-s390x.c +++ b/src/mono/mono/mini/mini-s390x.c @@ -406,6 +406,9 @@ static const char *typeParm[] = { "General", "Base", "FPR8", "FPR4", "StructByVa /*====================== End of Global Variables ===================*/ +static GENERATE_TRY_GET_CLASS_WITH_CACHE (math, "System", "Math") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (mathf, "System", "MathF") + /** * * @brief Return general register name @@ -834,7 +837,7 @@ mono_arch_cpu_optimizations (guint32 *exclude_mask) /* * No s390-specific optimizations yet */ - *exclude_mask = MONO_OPT_LINEARS; + *exclude_mask = 0; return opts; } @@ -1141,8 +1144,6 @@ enum_retvalue: cinfo->struct_ret = 1; cinfo->ret.size = size; cinfo->ret.vtsize = size; - // cinfo->ret.reg = s390_r2; - // sz->code_size += 4; } break; case MONO_TYPE_VOID: @@ -1361,23 +1362,14 @@ enum_retvalue: add_general (&gr, sz, &cinfo->sigCookie); } - /*----------------------------------------------------------*/ - /* If we are passing a structure back then if it won't be */ - /* in a register(s) then we make room at the end of the */ - /* parameters that may have been placed on the stack */ - /*----------------------------------------------------------*/ + /* + * If we are passing a structure back then we make room at + * the end of the parameters that may have been placed on + * the stack + */ if (cinfo->struct_ret) { cinfo->ret.offset = sz->stack_size; - switch (cinfo->ret.size) { - case 0: - case 1: - case 2: - case 4: - case 8: - break; - default: - sz->stack_size += S390_ALIGN(cinfo->ret.size, align); - } + sz->stack_size += S390_ALIGN(cinfo->ret.size, align); } cinfo->lastgr = gr; @@ -1436,7 +1428,9 @@ mono_arch_allocate_vars (MonoCompile *cfg) sig = mono_method_signature_internal (cfg->method); - cinfo = get_call_info (cfg->mempool, sig); + if (!cfg->arch.cinfo) + cfg->arch.cinfo = get_call_info (cfg->mempool, sig); + cinfo = cfg->arch.cinfo; /*--------------------------------------------------------------*/ /* local vars are at a positive offset from the stack pointer */ @@ -1663,8 +1657,13 @@ void mono_arch_create_vars (MonoCompile *cfg) { MonoMethodSignature *sig = mono_method_signature_internal (cfg->method); + CallInfo *cinfo; - if (MONO_TYPE_ISSTRUCT (sig->ret)) { + if (!cfg->arch.cinfo) + cfg->arch.cinfo = get_call_info (cfg->mempool, sig); + cinfo = cfg->arch.cinfo; + + if (cinfo->struct_ret) { cfg->vret_addr = mono_compile_create_var (cfg, mono_get_int_type (), OP_ARG); if (G_UNLIKELY (cfg->verbose_level > 1)) { printf ("vret_addr = "); @@ -3446,6 +3445,13 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) EMIT_COND_SYSTEM_EXCEPTION (S390_CC_LT, "OverflowException"); s390_lgfr (code, ins->dreg, ins->sreg1); break; + case OP_RCONV_TO_R4: + if (ins->dreg != ins->sreg1) + s390_ler (code, ins->dreg, ins->sreg1); + break; + case OP_RCONV_TO_R8: + s390_ldebr (code, ins->dreg, ins->sreg1); + break; case OP_FMOVE: if (ins->dreg != ins->sreg1) { s390_ldr (code, ins->dreg, ins->sreg1); @@ -3476,6 +3482,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_S390_SETF4RET: if (!cfg->r4fp) s390_ledbr (code, ins->dreg, ins->sreg1); + else + s390_ldr (code, ins->dreg, ins->sreg1); break; case OP_TLS_GET: { if (s390_is_imm16 (ins->inst_offset)) { @@ -3513,7 +3521,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_TAILCALL : case OP_TAILCALL_REG : case OP_TAILCALL_MEMBASE : { - MonoCallInst *call = (MonoCallInst *) ins; + call = (MonoCallInst *) ins; /* * Restore SP to caller's SP @@ -3592,55 +3600,72 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } break; case OP_FCALL: { - MonoCallInst *call = (MonoCallInst *) ins; + call = (MonoCallInst *) ins; const MonoJumpInfoTarget patch = mono_call_to_patch (call); code = emit_call (cfg, code, patch.type, patch.target); if (!cfg->r4fp && call->signature->ret->type == MONO_TYPE_R4) s390_ldebr (code, s390_f0, s390_f0); } break; + case OP_RCALL: { + call = (MonoCallInst *) ins; + const MonoJumpInfoTarget patch = mono_call_to_patch (call); + code = emit_call (cfg, code, patch.type, patch.target); + if (ins->dreg != s390_f0) + s390_ldr (code, ins->dreg, s390_f0); + break; + } case OP_LCALL: case OP_VCALL: case OP_VCALL2: case OP_VOIDCALL: - case OP_RCALL: case OP_CALL: { - MonoCallInst *call = (MonoCallInst *) ins; + call = (MonoCallInst *) ins; const MonoJumpInfoTarget patch = mono_call_to_patch (call); code = emit_call (cfg, code, patch.type, patch.target); } break; - case OP_FCALL_REG: { + case OP_FCALL_REG: call = (MonoCallInst*)ins; s390_lgr (code, s390_r1, ins->sreg1); s390_basr (code, s390_r14, s390_r1); if (!cfg->r4fp && call->signature->ret->type == MONO_TYPE_R4) s390_ldebr (code, s390_f0, s390_f0); - } break; + case OP_RCALL_REG: + call = (MonoCallInst*)ins; + s390_lgr (code, s390_r1, ins->sreg1); + s390_basr (code, s390_r14, s390_r1); + if (ins->dreg != s390_f0) + s390_ldr (code, ins->dreg, s390_f0); + break; case OP_LCALL_REG: case OP_VCALL_REG: case OP_VCALL2_REG: case OP_VOIDCALL_REG: - case OP_RCALL_REG: case OP_CALL_REG: { s390_lgr (code, s390_r1, ins->sreg1); s390_basr (code, s390_r14, s390_r1); } break; - case OP_FCALL_MEMBASE: { + case OP_FCALL_MEMBASE: call = (MonoCallInst*)ins; s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); s390_basr (code, s390_r14, s390_r1); if (!cfg->r4fp && call->signature->ret->type == MONO_TYPE_R4) s390_ldebr (code, s390_f0, s390_f0); - } + break; + case OP_RCALL_MEMBASE: + call = (MonoCallInst*)ins; + s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); + s390_basr (code, s390_r14, s390_r1); + if (ins->dreg != s390_f0) + s390_ldr (code, ins->dreg, s390_f0); break; case OP_LCALL_MEMBASE: case OP_VCALL_MEMBASE: case OP_VCALL2_MEMBASE: case OP_VOIDCALL_MEMBASE: - case OP_RCALL_MEMBASE: case OP_CALL_MEMBASE: { s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); s390_basr (code, s390_r14, s390_r1); @@ -3656,53 +3681,99 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) area_offset = S390_ALIGN(area_offset, S390_STACK_ALIGNMENT); - /* - * Get alloc size and round to doubleword - */ - s390_lgr (code, s390_r1, ins->sreg1); - s390_aghi (code, s390_r1, 14); + /* Get current backchain pointer */ + s390_lg (code, s390_r13, 0, STK_BASE, 0); + + /* + * Round object size to doubleword + */ + s390_lgr (code, s390_r1, ins->sreg1); + s390_aghi (code, s390_r1, 7); s390_srlg (code, s390_r1, s390_r1, 0, 3); s390_sllg (code, s390_r1, s390_r1, 0, 3); - /* - * If we need to initialize then hold on to the length - */ - if (ins->flags & MONO_INST_INIT) - s390_lgr (code, s390_r0, s390_r1); + if (mono_hwcap_s390x_has_gie) { + if (ins->flags & MONO_INST_INIT) + s390_lgr (code, s390_r0, s390_r1); - /* - * Adjust the stack pointer and save the backchain - */ - s390_lg (code, s390_r13, 0, STK_BASE, 0); - s390_sgr (code, STK_BASE, s390_r1); - s390_stg (code, s390_r13, 0, STK_BASE, 0); + s390_lgr (code, s390_r0, s390_r1); + s390_risbg (code, s390_r2, s390_r1, 0, 0xb3, 0); + s390_sgrk (code, s390_r2, STK_BASE, s390_r2); - /* - * Skip the stack save requirements and point to localloc area - * and ensure it's correctly aligned - */ - s390_la (code, ins->dreg, 0, STK_BASE, area_offset); - s390_aghi (code, ins->dreg, 7); - s390_srlg (code, ins->dreg, ins->dreg, 0, 3); - s390_sllg (code, ins->dreg, ins->dreg, 0, 3); + s390_cgr (code, STK_BASE, s390_r2); /* L0: */ + s390_je (code, 9); /* je L1 */ + s390_aghi (code, STK_BASE, -4096); + s390_mvghi (code, s390_r15, 0, 0); + s390_j (code, -9); /* j L0 */ + + s390_risbg (code, s390_r2, s390_r1, 0x34, 0xbf, 0); /* L1: */ + s390_ltgr (code, s390_r2, s390_r2); + s390_jz (code, 13); /* jz L2: */ + + s390_sgr (code, STK_BASE, s390_r2); + s390_risbg (code, s390_r1, s390_r1, 0x34, 0xbf, 0); + s390_lay (code, s390_r1, s390_r1, STK_BASE, -8); + s390_mvghi (code, s390_r1, 0, 0); + /* L2: */ + } else { + s390_lgr (code, s390_r2, s390_r1); + s390_nill (code, s390_r2, 0xf000); + s390_lgr (code, s390_r0, STK_BASE); + s390_sgr (code, s390_r0, s390_r2); + s390_lgr (code, s390_r2, s390_r0); + + s390_cgr (code, STK_BASE, s390_r2); /* L0: */ + s390_je (code, 11); /* je L1 */ + s390_aghi (code, STK_BASE, -4096); + s390_lghi (code, s390_r0, 0); + s390_stg (code, s390_r0, 0, STK_BASE, 4088); + s390_j (code, -11); /* j L0 */ + + s390_lghi (code, s390_r2, 4095); /* L1: */ + s390_ngr (code, s390_r2, s390_r1); + s390_ltgr (code, s390_r2, s390_r2); + s390_jz (code, 7); /* jz L2 */ + + s390_sgr (code, STK_BASE, s390_r2); + s390_stg (code, s390_r2, s390_r1, STK_BASE, -8); + /* L2: */ + if (ins->flags & MONO_INST_INIT) + s390_lgr (code, s390_r0, s390_r1); + } + + /* + * Compute address of localloc'd object + */ + s390_lgr (code, s390_r1, STK_BASE); + if (s390_is_imm16(area_offset)) + s390_aghi (code, s390_r1, area_offset); + else + s390_agfi (code, s390_r1, area_offset); + s390_aghi (code, s390_r1, 7); + s390_srlg (code, s390_r1, s390_r1, 0, 3); + s390_sllg (code, s390_r1, s390_r1, 0, 3); + s390_lgr (code, ins->dreg, s390_r1); + + /* Save backchain pointer */ + s390_stg (code, s390_r13, 0, STK_BASE, 0); /* * If we need to zero the area then clear from localloc start * using the length we saved earlier - */ - if (ins->flags & MONO_INST_INIT) { - s390_lgr (code, s390_r1, s390_r0); - s390_lgr (code, s390_r0, ins->dreg); - s390_lgr (code, s390_r14, s390_r12); - s390_lghi (code, s390_r13, 0); - s390_mvcle(code, s390_r0, s390_r12, 0, 0); - s390_jo (code, -2); - s390_lgr (code, s390_r12, s390_r14); - } - - /* - * If we have an LMF then we have to adjust its BP */ + if (ins->flags & MONO_INST_INIT) { + s390_lgr (code, s390_r1, s390_r0); + s390_lgr (code, s390_r0, ins->dreg); + s390_lgr (code, s390_r14, s390_r12); + s390_lghi (code, s390_r13, 0); + s390_mvcle(code, s390_r0, s390_r12, 0, 0); + s390_jo (code, -2); + s390_lgr (code, s390_r12, s390_r14); + } + + /* + * If we have an LMF then we have to adjust its BP + */ if (cfg->method->save_lmf) { int lmfOffset = cfg->stack_usage - sizeof(MonoLMF); @@ -3714,7 +3785,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) S390_SET (code, s390_r13, lmfOffset); } s390_stg (code, s390_r15, s390_r13, cfg->frame_reg, - MONO_STRUCT_OFFSET(MonoLMF, ebp)); + MONO_STRUCT_OFFSET(MonoLMF, ebp)); } } break; @@ -4085,9 +4156,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } else { S390_SET (code, s390_r13, ins->inst_p0); s390_le (code, ins->dreg, 0, s390_r13, 0); - if (!cfg->r4fp) { + if (!cfg->r4fp) s390_ldebr (code, ins->dreg, ins->dreg); - } + else + s390_le (code, ins->dreg, 0, s390_r13, 0); } } break; @@ -4135,7 +4207,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case OP_LCONV_TO_R_UN: { if (mono_hwcap_s390x_has_fpe) { - s390_cdlgbr (code, ins->dreg, 5, ins->sreg1, 0); + s390_cdlgbr (code, ins->dreg, 6, ins->sreg1, 0); } else { short int *jump; s390_lgdr (code, s390_r0, s390_r15); @@ -4214,7 +4286,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_FCONV_TO_U4: case OP_FCONV_TO_U: if (mono_hwcap_s390x_has_fpe) { - s390_clfdbr (code, ins->dreg, 5, ins->sreg1, 0); + s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_double_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE); } @@ -4265,11 +4337,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case OP_RCONV_TO_I4: case OP_RCONV_TO_I: - s390_cfebr (code, ins->dreg, 5, ins->sreg1); + s390_cgebr (code, ins->dreg, 5, ins->sreg1); break; case OP_RCONV_TO_U4: if (mono_hwcap_s390x_has_fpe) { - s390_clfebr (code, ins->dreg, 5, ins->sreg1, 0); + s390_clgebr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE); } @@ -4308,14 +4380,42 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) PTRSLOT(code, o[4]); } break; - case OP_ABS: { + case OP_ABS: s390_lpdbr (code, ins->dreg, ins->sreg1); - } break; - case OP_SQRT: { + case OP_ABSF: + s390_lpebr (code, ins->dreg, ins->sreg1); + break; + case OP_CEIL: + s390_fidbra (code, ins->dreg, 6, ins->sreg1, 4); + break; + case OP_CEILF: + s390_fiebra (code, ins->dreg, 6, ins->sreg1, 4); + break; + case OP_FLOOR: + s390_fidbra (code, ins->dreg, 7, ins->sreg1, 4); + break; + case OP_FLOORF: + s390_fiebra (code, ins->dreg, 7, ins->sreg1, 4); + break; + case OP_FCOPYSIGN: + s390_cpsdr (code, ins->dreg, ins->sreg2, ins->sreg1); + break; + case OP_ROUND: + s390_fidbra (code, ins->dreg, 4, ins->sreg1, 4); + break; + case OP_SQRT: s390_sqdbr (code, ins->dreg, ins->sreg1); - } break; + case OP_SQRTF: + s390_sqebr (code, ins->dreg, ins->sreg1); + break; + case OP_TRUNC: + s390_fidbra (code, ins->dreg, 5, ins->sreg1, 4); + break; + case OP_TRUNCF: + s390_fiebra (code, ins->dreg, 5, ins->sreg1, 4); + break; case OP_FADD: { CHECK_SRCDST_COM_F; s390_adbr (code, ins->dreg, src2); @@ -4433,56 +4533,80 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } break; case OP_RCEQ: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_je (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLT: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jl (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLT_UN: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jlo (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGT: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jh (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGT_UN: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jho (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCNEQ: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jne (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGE: { - s390_cdbr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jhe (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLE: { - s390_cebr (code, ins->sreg1, ins->sreg2); + if (cfg->r4fp) + s390_cebr (code, ins->sreg1, ins->sreg2); + else + s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jle (code, 4); s390_lghi (code, ins->dreg, 0); @@ -4655,6 +4779,32 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_MEMORY_BARRIER: s390_mem (code); break; + case OP_POPCNT32: + s390_llgfr (code, s390_r1, ins->sreg1); + if (mono_hwcap_s390x_has_mie3) { + s390_popcnt (code, ins->dreg, 0x80, s390_r1); + } else { + s390_popcnt (code, s390_r0, 0, s390_r1); + s390_ahhlr (code, s390_r0, s390_r0, s390_r0); + s390_sllg (code, s390_r1, s390_r0, 0, 16); + s390_algr (code, s390_r0, s390_r1); + s390_sllg (code, s390_r1, s390_r0, 0, 8); + s390_algr (code, s390_r0, s390_r1); + s390_srlg (code, ins->dreg, s390_r0, 0, 56); + } + break; + case OP_POPCNT64: + if (mono_hwcap_s390x_has_mie3) { + s390_popcnt (code, ins->dreg, 0x80, ins->sreg1); + } else { + s390_ahhlr (code, s390_r0, s390_r0, s390_r0); + s390_sllg (code, s390_r1, s390_r0, 0, 16); + s390_algr (code, s390_r0, s390_r1); + s390_sllg (code, s390_r1, s390_r0, 0, 8); + s390_algr (code, s390_r0, s390_r1); + s390_srlg (code, ins->dreg, s390_r0, 0, 56); + } + break; case OP_LIVERANGE_START: { if (cfg->verbose_level > 1) printf ("R%d START=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code)); @@ -5131,17 +5281,19 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } break; case OP_EXTRACT_I1: + case OP_EXTRACT_U1: amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); if (ins->inst_c0) amd64_shift_reg_imm (code, X86_SHR, ins->dreg, ins->inst_c0 * 8); - amd64_widen_reg (code, ins->dreg, ins->dreg, ins->inst_c1 == MONO_TYPE_I1, FALSE); + amd64_widen_reg (code, ins->dreg, ins->dreg, ins->inst_c1 == OP_EXTRACT_I1, FALSE); break; case OP_EXTRACT_I2: + case OP_EXTRACT_U2: /*amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); if (ins->inst_c0) amd64_shift_reg_imm_size (code, X86_SHR, ins->dreg, 16, 4);*/ s390x_pextrw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); - amd64_widen_reg_size (code, ins->dreg, ins->dreg, ins->inst_c1 == MONO_TYPE_I2, TRUE, 4); + amd64_widen_reg_size (code, ins->dreg, ins->dreg, ins->inst_c1 == OP_EXTRACT_I2, TRUE, 4); break; case OP_EXTRACT_R8: if (ins->inst_c0) @@ -5554,15 +5706,19 @@ mono_arch_emit_prolog (MonoCompile *cfg) cfg->rgctx_var->inst_offset); } -#if 0 +#if 1 +char *methodName = getenv("MONO_TRACE_METHOD"); +if (methodName != NULL) { printf("ns: %s k: %s m: %s\n",method->klass->name_space,method->klass->name,method->name);fflush(stdout); // Tests:set_ip -if ((strcmp(method->klass->name_space,"") == 0) && - (strcmp(method->klass->name,"Tests") == 0) && - (strcmp(method->name, "set_ip") == 0)) { - // (strcmp("CancellationToken,TaskCreationOptions,TaskContinuationOptions,TaskScheduler",mono_signature_get_desc(method->signature, FALSE)) != 0)) { - printf("SIGNATURE: %s\n",mono_signature_get_desc(method->signature, FALSE)); fflush(stdout); - s390_j (code, 0); +//if ((strcmp(method->klass->name_space,"") == 0) && +// (strcmp(method->klass->name,"Tests") == 0) && +// (strcmp(method->name, "set_ip") == 0)) { +// (strcmp("CancellationToken,TaskCreationOptions,TaskContinuationOptions,TaskScheduler",mono_signature_get_desc(method->signature, FALSE)) != 0)) { + if ((strcmp(method->name, methodName) == 0)) { + printf("SIGNATURE: %s\n",mono_signature_get_desc(method->signature, FALSE)); fflush(stdout); + s390_j (code, 0); + } } #endif @@ -5583,13 +5739,16 @@ if ((strcmp(method->klass->name_space,"") == 0) && sig = mono_method_signature_internal (method); pos = 0; - cinfo = get_call_info (cfg->mempool, sig); + cinfo = cfg->arch.cinfo; if (cinfo->struct_ret) { ArgInfo *ainfo = &cinfo->ret; inst = cfg->vret_addr; inst->backend.size = ainfo->vtsize; - s390_stg (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); + if (inst->opcode == OP_REGVAR) + s390_lgr (code, inst->dreg, ainfo->reg); + else + s390_stg (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); } /** @@ -6067,6 +6226,98 @@ mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMetho { MonoInst *ins = NULL; + int opcode = 0; + MonoStackType stack_type = STACK_R8; + + if (cmethod->klass == mono_class_try_get_math_class ()) { + // unary double + if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R8) { + if (strcmp (cmethod->name, "Abs") == 0) { + opcode = OP_ABS; + } else if (strcmp (cmethod->name, "Ceiling") == 0) { + opcode = OP_CEIL; + } else if (strcmp (cmethod->name, "Floor") == 0) { + opcode = OP_FLOOR; + } else if (strcmp (cmethod->name, "Round") == 0) { + opcode = OP_ROUND; + } else if (strcmp (cmethod->name, "Sqrt") == 0) { + opcode = OP_SQRT; + } else if (strcmp (cmethod->name, "Truncate") == 0) { + opcode = OP_TRUNC; + } + } + // unary float (overloaded) + else if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) { + if (strcmp (cmethod->name, "Abs") == 0) { + if (cfg->r4fp) { + opcode = OP_ABSF; + stack_type = STACK_R4; + } else { + opcode = OP_ABS; + } + } + } + // binary double + else if (fsig->param_count == 2 && fsig->params [0]->type == MONO_TYPE_R8 && fsig->params [1]->type == MONO_TYPE_R8) { + if (strcmp (cmethod->name, "CopySign") == 0) { + opcode = OP_FCOPYSIGN; + } + } + } else if (cmethod->klass == mono_class_try_get_mathf_class ()) { + if (fsig->param_count == 1) { + stack_type = STACK_R4; + if (strcmp (cmethod->name, "Abs") == 0) { + if (cfg->r4fp) { + opcode = OP_ABSF; + stack_type = STACK_R4; + } else { + opcode = OP_ABS; + } + } else if (strcmp (cmethod->name, "Ceiling") == 0) { + if (cfg->r4fp) { + opcode = OP_CEILF; + stack_type = STACK_R4; + } else { + opcode = OP_CEIL; + } + } else if (strcmp (cmethod->name, "Floor") == 0) { + if (cfg->r4fp) { + opcode = OP_FLOORF; + stack_type = STACK_R4; + } else { + opcode = OP_FLOOR; + } + } else if (strcmp (cmethod->name, "Sqrt") == 0) { + if (cfg->r4fp) { + opcode = OP_SQRTF; + stack_type = STACK_R4; + } else { + opcode = OP_SQRT; + } + } else if (strcmp (cmethod->name, "Truncate") == 0) { + if (cfg->r4fp) { + opcode = OP_TRUNCF; + stack_type = STACK_R4; + } else { + opcode = OP_TRUNC; + } + opcode = OP_TRUNCF; + } + } + } + + if (opcode) { + MONO_INST_NEW (cfg, ins, opcode); + ins->type = stack_type; + ins->dreg = mono_alloc_freg (cfg); + ins->sreg1 = args [0]->dreg; + if (fsig->param_count > 1) { + ins->sreg2 = args [1]->dreg; + } + g_assert (fsig->param_count <= 2); + MONO_ADD_INS (cfg->cbb, ins); + } + return ins; } diff --git a/src/mono/mono/mini/mini-s390x.h b/src/mono/mono/mini/mini-s390x.h index d7d3566a188..9a1aee5721a 100644 --- a/src/mono/mono/mini/mini-s390x.h +++ b/src/mono/mono/mini/mini-s390x.h @@ -38,6 +38,7 @@ typedef struct MonoCompileArch { int fpSize; /** Size of floating point save area */ MonoInst *ss_tramp_var; /** Single-step variable */ MonoInst *bp_tramp_var; /** Breakpoint variable */ + CallInfo *cinfo; /** Caller information */ guint8 *thunks; /** Thunking area */ int thunks_size; /** Size of thunking area */ } MonoCompileArch; @@ -57,7 +58,6 @@ struct SeqPointInfo { }; #define MONO_ARCH_SIGSEGV_ON_ALTSTACK 1 -#define MONO_ARCH_EMULATE_LCONV_TO_R8_UN 1 #define MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS 1 #define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS 1 #define MONO_ARCH_HAVE_IS_INT_OVERFLOW 1 @@ -83,6 +83,7 @@ struct SeqPointInfo { #define MONO_ARCH_HAVE_SDB_TRAMPOLINES 1 #define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1 #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1 +#define MONO_ARCH_FLOAT32_SUPPORTED 1 #define S390_STACK_ALIGNMENT 8 #define S390_FIRST_ARG_REG s390_r2 From 290430eedbfe0e690c8dd119bba6f5a95f2bef53 Mon Sep 17 00:00:00 2001 From: Nikola Milosavljevic Date: Thu, 22 Jul 2021 08:20:34 -0700 Subject: [PATCH 745/926] Hostfxr package should depend on host package (#56008) --- src/installer/pkg/sfx/installers/dotnet-hostfxr.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj index f5992ef866a..0edd1eaa985 100644 --- a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj +++ b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj @@ -48,9 +48,9 @@ - + From bc7b910144d1f2b62de05540b9df1bb90f3729b9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 22 Jul 2021 11:26:11 -0400 Subject: [PATCH 746/926] Remove a few unnecessary state machines from SocketsHttpHandler (#56109) --- .../SocketsHttpHandler/ChunkedEncodingWriteStream.cs | 4 ++-- .../SocketsHttpHandler/ContentLengthWriteStream.cs | 4 ++-- .../Net/Http/SocketsHttpHandler/HttpConnection.cs | 12 ++++++++---- .../SocketsHttpHandler/HttpContentWriteStream.cs | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs index 1411e672c70..9daea15fcae 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs @@ -70,12 +70,12 @@ namespace System.Net.Http } } - public override async ValueTask FinishAsync(bool async) + public override Task FinishAsync(bool async) { // Send 0 byte chunk to indicate end, then final CrLf HttpConnection connection = GetConnectionOrThrow(); _connection = null; - await connection.WriteBytesAsync(s_finalChunkBytes, async).ConfigureAwait(false); + return connection.WriteBytesAsync(s_finalChunkBytes, async); } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs index 7ea59751c18..5b7b6551661 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs @@ -39,10 +39,10 @@ namespace System.Net.Http return connection.WriteAsync(buffer, async: true); } - public override ValueTask FinishAsync(bool async) + public override Task FinishAsync(bool async) { _connection = null; - return default; + return Task.CompletedTask; } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 7ef8ca09b06..c7814464a87 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -1788,20 +1788,24 @@ namespace System.Net.Http return bytesToCopy; } - private async ValueTask CopyFromBufferAsync(Stream destination, bool async, int count, CancellationToken cancellationToken) + private ValueTask CopyFromBufferAsync(Stream destination, bool async, int count, CancellationToken cancellationToken) { Debug.Assert(count <= _readLength - _readOffset); if (NetEventSource.Log.IsEnabled()) Trace($"Copying {count} bytes to stream."); + + int offset = _readOffset; + _readOffset += count; + if (async) { - await destination.WriteAsync(new ReadOnlyMemory(_readBuffer, _readOffset, count), cancellationToken).ConfigureAwait(false); + return destination.WriteAsync(new ReadOnlyMemory(_readBuffer, offset, count), cancellationToken); } else { - destination.Write(_readBuffer, _readOffset, count); + destination.Write(_readBuffer, offset, count); + return default; } - _readOffset += count; } private Task CopyToUntilEofAsync(Stream destination, bool async, int bufferSize, CancellationToken cancellationToken) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs index 5777ac8e40f..825e1f629b5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs @@ -37,7 +37,7 @@ namespace System.Net.Http public sealed override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => throw new NotSupportedException(); - public abstract ValueTask FinishAsync(bool async); + public abstract Task FinishAsync(bool async); } } } From 17ada00707f0e7346ce7be399807f80f42158119 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 22 Jul 2021 16:10:19 +0000 Subject: [PATCH 747/926] [main] Update dependencies from dnceng/internal/dotnet-optimization dotnet/arcade dotnet/xharness dotnet/runtime-assets (#56088) [main] Update dependencies from dnceng/internal/dotnet-optimization dotnet/arcade dotnet/xharness dotnet/runtime-assets - Fix package testing - Merge branch 'main' into darc-main-bdcf3772-495a-406e-a53b-da4d8acc1985 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 136 +++++++++--------- eng/Versions.props | 62 ++++---- global.json | 8 +- .../pkg/test/build/packageTest.targets | 14 +- .../netstandard2.1/settings.targets | 17 +++ .../settings.targets | 5 + 7 files changed, 127 insertions(+), 117 deletions(-) create mode 100644 src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/netstandard2.1/settings.targets create mode 100644 src/libraries/pkg/test/packageSettings/System.Runtime.Experimental/settings.targets diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 85fd3e4cf6c..b96d58bf366 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21364.1", + "version": "1.0.0-prerelease.21370.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2ba88ba501a..2f2190618a1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -14,113 +14,113 @@ - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d https://github.com/dotnet/llvm-project @@ -190,41 +190,41 @@ https://github.com/mono/linker 6eae01980dc694107bdee0bc723d75a0dd601f0e - + https://github.com/dotnet/xharness - e5511489deb44b13418fe622775bd7035e21c5ae + aa0da55569eaa42c2c735dd89abd2843fba4fe01 - + https://github.com/dotnet/xharness - e5511489deb44b13418fe622775bd7035e21c5ae + aa0da55569eaa42c2c735dd89abd2843fba4fe01 - + https://github.com/dotnet/arcade - e97027cf100d2b532adce387e5cb93a373de93c9 + 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - d77585ed642412eb5e2c36bec4c626aef387060e + 2e172488a9ee56619a08c8303a708e69dc82503e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - d77585ed642412eb5e2c36bec4c626aef387060e + 2e172488a9ee56619a08c8303a708e69dc82503e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - d77585ed642412eb5e2c36bec4c626aef387060e + 2e172488a9ee56619a08c8303a708e69dc82503e - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - d77585ed642412eb5e2c36bec4c626aef387060e + 2e172488a9ee56619a08c8303a708e69dc82503e https://github.com/dotnet/hotreload-utils 589840a79ed6335a310a011ee9e244e660798cf5 - + https://github.com/dotnet/runtime-assets - 8693e5f9d69ddf21543072fca8e4314b9f5e0682 + bcfd1993de3767c54c6d8a522bf63770fba3c98d https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index eac0a300b1d..ecd7f036d09 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -52,22 +52,20 @@ 3.10.0 6.0.0-rc1.21366.2 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 2.5.1-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 - 6.0.0-beta.21369.3 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 2.5.1-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 + 6.0.0-beta.21370.12 6.0.0-preview.1.102 @@ -116,21 +114,21 @@ 4.5.0 6.0.0-rc.1.21369.2 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 - 6.0.0-beta.21369.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 + 6.0.0-beta.21371.1 - 1.0.0-prerelease.21367.5 - 1.0.0-prerelease.21367.5 - 1.0.0-prerelease.21367.5 - 1.0.0-prerelease.21367.5 + 1.0.0-prerelease.21371.3 + 1.0.0-prerelease.21371.3 + 1.0.0-prerelease.21371.3 + 1.0.0-prerelease.21371.3 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 @@ -154,8 +152,8 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21364.1 - 1.0.0-prerelease.21364.1 + 1.0.0-prerelease.21370.1 + 1.0.0-prerelease.21370.1 1.0.1-alpha.0.21369.1 2.4.1 2.4.2 diff --git a/global.json b/global.json index 863e9ef68eb..64d00112833 100644 --- a/global.json +++ b/global.json @@ -12,11 +12,11 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21369.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21370.12", "Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21369.3", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21369.3", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21369.3", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21370.12", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21370.12", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21370.12", "Microsoft.Build.NoTargets": "3.0.4", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "6.0.0-rc.1.21369.2" diff --git a/src/libraries/pkg/test/build/packageTest.targets b/src/libraries/pkg/test/build/packageTest.targets index 28f5cc0240a..8bffafc8e91 100644 --- a/src/libraries/pkg/test/build/packageTest.targets +++ b/src/libraries/pkg/test/build/packageTest.targets @@ -74,16 +74,6 @@ - - - - - @@ -104,7 +94,7 @@ Inputs="%(RuntimeLibToTest.RuntimeIdentifier)" Outputs="unused" DependsOnTargets="PrepareForRuntimeTesting" - Condition="'$(ShouldVerifyClosure)' == 'true' and '$(SkipVerifyClosureForRuntime)' != 'true'"> + Condition="'$(ShouldVerifyClosure)' == 'true' and '$(SkipVerifyClosureForRuntime)' != 'true' and '$(SkipVerifyRuntime)' != 'true'"> <_runClosureFileNames Include="@(RuntimeLibToTestDependency->'%(FileName)');@(RuntimeLibToTest->'%(FileName)')"> @@ -123,7 +113,7 @@ Inputs="%(RuntimeLibToTest.RuntimeIdentifier)" Outputs="unused" DependsOnTargets="PrepareForRuntimeTesting" - Condition="'$(ShouldVerifyTypes)' == 'true'"> + Condition="'$(ShouldVerifyTypes)' == 'true' and '$(SkipVerifyRuntime)' != 'true'"> <_runTypesFileNames Include="@(RuntimeLibToTestDependency->'%(FileName)');@(RuntimeLibToTest->'%(FileName)')"> diff --git a/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/netstandard2.1/settings.targets b/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/netstandard2.1/settings.targets new file mode 100644 index 00000000000..6757b2ae2bc --- /dev/null +++ b/src/libraries/pkg/test/packageSettings/Microsoft.Windows.Compatibility/netstandard2.1/settings.targets @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/pkg/test/packageSettings/System.Runtime.Experimental/settings.targets b/src/libraries/pkg/test/packageSettings/System.Runtime.Experimental/settings.targets new file mode 100644 index 00000000000..eaa4cdfb1bb --- /dev/null +++ b/src/libraries/pkg/test/packageSettings/System.Runtime.Experimental/settings.targets @@ -0,0 +1,5 @@ + + + true + + \ No newline at end of file From f28702c47c6753dbaf312128ae002a69499e8364 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Thu, 22 Jul 2021 09:25:22 -0700 Subject: [PATCH 748/926] Enable `EHCONT` for some DLLs and for PGO instrumentation (#55942) * Enable `EHCONT` for some DLLs and for PGO instrumentation - PGD files used for PGO optimziation need to be collected/produced against binaries with identical compiler features - Enabled `/guard:ehcont` as a compiler option but not as a linker option - Enabled `/guard:ehcont` and `/cetcompat` as a linker options for PGO instrumentation only for now - Once new profile data is published, another PR would follow to enable `/guard:ehcont` and `/cetcompat` as linker options * Fix build --- eng/native/configurecompiler.cmake | 10 +++++----- src/coreclr/pgosupport.cmake | 7 +++++++ src/libraries/Native/Windows/CMakeLists.txt | 12 ++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 92a715f38d9..39460f32085 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -574,11 +574,11 @@ if (MSVC) # Enable EH-continuation table for native components for amd64 builds # Added using variables instead of add_compile_options to let individual projects override it - #if (CLR_CMAKE_HOST_ARCH_AMD64) - # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /guard:ehcont") - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") - # set(CMAKE_ASM_MASM_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") - #endif (CLR_CMAKE_HOST_ARCH_AMD64) + if (CLR_CMAKE_HOST_ARCH_AMD64) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /guard:ehcont") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") + set(CMAKE_ASM_MASM_FLAGS "${CMAKE_C_FLAGS} /guard:ehcont") + endif (CLR_CMAKE_HOST_ARCH_AMD64) # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid # linking in VCRUNTIME140.DLL for a simplified xcopy experience by reducing the dependency on VC REDIST. diff --git a/src/coreclr/pgosupport.cmake b/src/coreclr/pgosupport.cmake index c0791c445f0..6f07f1cd5ea 100644 --- a/src/coreclr/pgosupport.cmake +++ b/src/coreclr/pgosupport.cmake @@ -20,6 +20,13 @@ function(add_pgo TargetName) if(CLR_CMAKE_HOST_WIN32) set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /LTCG /GENPROFILE") set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /LTCG /GENPROFILE") + + if (CLR_CMAKE_HOST_ARCH_AMD64) + # The /guard:ehcont and /CETCOMPAT switches here are temporary and will be moved to a more global location once + # new profile data is published + set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /guard:ehcont /CETCOMPAT") + set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /guard:ehcont /CETCOMPAT") + endif (CLR_CMAKE_HOST_ARCH_AMD64) else(CLR_CMAKE_HOST_WIN32) if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELEASE OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELWITHDEBINFO) target_compile_options(${TargetName} PRIVATE -flto -fprofile-instr-generate) diff --git a/src/libraries/Native/Windows/CMakeLists.txt b/src/libraries/Native/Windows/CMakeLists.txt index e96ec87ddb1..9d69ca81c24 100644 --- a/src/libraries/Native/Windows/CMakeLists.txt +++ b/src/libraries/Native/Windows/CMakeLists.txt @@ -65,12 +65,12 @@ endif () add_compile_options(/guard:cf) list(APPEND __SharedLinkArgs /guard:cf) -#if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "x64") -# # Enable EH continuation table and CETCOMPAT for native components -# add_compile_options(/guard:ehcont) -# list(APPEND __SharedLinkArgs /guard:ehcont) -# list(APPEND __SharedLinkArgs /CETCOMPAT) -#endif () +if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "x64") + # Enable EH continuation table and CETCOMPAT for native components + add_compile_options(/guard:ehcont) + list(APPEND __SharedLinkArgs /guard:ehcont) + list(APPEND __SharedLinkArgs /CETCOMPAT) +endif () # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid # linking in VCRUNTIME140.DLL for a simplified xcopy experience by reducing the dependency on VC REDIST. From 6d15a020712228683ead99a3d62eee1d63d19738 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 22 Jul 2021 12:37:28 -0400 Subject: [PATCH 749/926] [wasm] Fix invariant globalization test (#56121) The following commit changed the behavior to throw `CultureNotFoundException` when creating cultures in invariant mode. ``` commit 04dac7b0fede29d44f896c5fd793754f83974175 Author: Tarek Mahmoud Sayed Date: Thu Jul 1 11:55:05 2021 -0700 Allow restricting cultures creation with any arbitrary names in Globalization Invariant Mode (#54247) ``` This commit updates the corresponding test in `Wasm.Build.Tests` to handle that explicitly. Fixes https://github.com/dotnet/runtime/issues/55838 --- .../InvariantGlobalizationTests.cs | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 6b53bc1c0b6..7d66bc32a49 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -57,25 +57,21 @@ namespace Wasm.Build.Tests string programText = @" using System; using System.Globalization; - using System.Threading.Tasks; - public class TestClass { - public static int Main() - { - CultureInfo culture; - try - { - culture = new CultureInfo(""es-ES"", false); - // https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#cultures-and-culture-data - } - catch - { - culture = new CultureInfo("""", false); - } - Console.WriteLine($""{culture.LCID == CultureInfo.InvariantCulture.LCID} - {culture.NativeName}""); - return 42; - } - }"; + // https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#cultures-and-culture-data + try + { + CultureInfo culture = new (""es-ES"", false); + Console.WriteLine($""es-ES: Is Invariant LCID: {culture.LCID == CultureInfo.InvariantCulture.LCID}, NativeName: {culture.NativeName}""); + } + catch (CultureNotFoundException cnfe) + { + Console.WriteLine($""Could not create es-ES culture: {cnfe.Message}""); + } + + Console.WriteLine($""CurrentCulture.NativeName: {CultureInfo.CurrentCulture.NativeName}""); + return 42; + "; BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), @@ -83,11 +79,19 @@ namespace Wasm.Build.Tests dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false); - string expectedOutputString = invariantGlobalization == true - ? "True - Invariant Language (Invariant Country)" - : "False - es (ES)"; - RunAndTestWasmApp(buildArgs, expectedExitCode: 42, - test: output => Assert.Contains(expectedOutputString, output), host: host, id: id); + if (invariantGlobalization == true) + { + string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id); + Assert.Contains("Could not create es-ES culture", output); + Assert.Contains("CurrentCulture.NativeName: Invariant Language (Invariant Country)", output); + } + else + { + string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id); + Assert.Contains("es-ES: Is Invariant LCID: False, NativeName: es (ES)", output); + + // ignoring the last line of the output which prints the current culture + } } } } From 0a4dde9f1c30c1a4bfa54a68872a6425982e146e Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 22 Jul 2021 12:49:45 -0400 Subject: [PATCH 750/926] [wasm] Add $(EmccTotalMemory) to allow custom `-s TOTAL_MEMORY=..` (#56117) --- src/mono/wasm/build/WasmApp.Native.targets | 4 +++- src/mono/wasm/build/WasmApp.targets | 1 + src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 21 ++++++++++---------- src/tasks/Common/Utils.cs | 8 ++++---- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 85d301885db..25dd264c7ff 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -160,6 +160,8 @@ <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp <_EmccCompileOutputMessageImportance Condition="'$(EmccVerbose)' == 'true'">Normal <_EmccCompileOutputMessageImportance Condition="'$(EmccVerbose)' != 'true'">Low + + 536870912 @@ -269,7 +271,7 @@ <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" /> <_EmccLDFlags Include="@(_EmccCommonFlags)" /> - <_EmccLDFlags Include="-s TOTAL_MEMORY=536870912" /> + <_EmccLDFlags Include="-s TOTAL_MEMORY=$(EmccTotalMemory)" /> <_EmccLDFlags Include="$(EmccExtraLDFlags)" /> diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 534caf9025b..5ecd1dd77be 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -56,6 +56,7 @@ - $(EmccFlags) - Emcc flags used for both compiling native files, and linking - $(EmccExtraLDFlags) - Extra emcc flags for linking - $(EmccExtraCFlags) - Extra emcc flags for compiling native files + - $(EmccTotalMemory) - Total memory specified with `emcc`. Default value: 536870912 Public items: diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 0d48bf446fb..ab27947c15a 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -337,6 +337,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task var aotArgs = new List(); var processArgs = new List(); bool isDedup = assembly == DedupAssembly; + string msgPrefix = $"[{Path.GetFileName(assembly)}] "; var a = assemblyItem.GetMetadata("AotArguments"); if (a != null) @@ -542,27 +543,27 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task sw.WriteLine(responseFileContent); } - Log.LogMessage(MessageImportance.Low, $"AOT compiler arguments: {responseFileContent}"); - - string args = $"--response=\"{responseFilePath}\""; + string workingDir = assemblyDir; // Log the command in a compact format which can be copy pasted - StringBuilder envStr = new StringBuilder(string.Empty); - foreach (KeyValuePair kvp in envVariables) - envStr.Append($"{kvp.Key}={kvp.Value} "); - Log.LogMessage(MessageImportance.Low, $"Exec: {envStr}{CompilerBinaryPath} {args}"); + { + StringBuilder envStr = new StringBuilder(string.Empty); + foreach (KeyValuePair kvp in envVariables) + envStr.Append($"{kvp.Key}={kvp.Value} "); + Log.LogMessage(MessageImportance.Low, $"{msgPrefix}Exec (with response file contents expanded) in {workingDir}: {envStr}{CompilerBinaryPath} {responseFileContent}"); + } try { // run the AOT compiler (int exitCode, string output) = Utils.TryRunProcess(Log, CompilerBinaryPath, - args, + $"--response=\"{responseFilePath}\"", envVariables, - assemblyDir, + workingDir, silent: false, debugMessageImportance: MessageImportance.Low, - label: assembly); + label: Path.GetFileName(assembly)); if (exitCode != 0) { Log.LogError($"Precompiling failed for {assembly}: {output}"); diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index 3376b4d5e7b..1d04c4d27dc 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -38,7 +38,7 @@ internal static class Utils : ("/bin/sh", $"\"{scriptFileName}\""); string msgPrefix = label == null ? string.Empty : $"[{label}] "; - logger.LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:", msgPrefix); + logger.LogMessage(debugMessageImportance, $"{msgPrefix}Running {command} via script {scriptFileName}:", msgPrefix); logger.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName), msgPrefix); return TryRunProcess(logger, @@ -111,7 +111,7 @@ internal static class Utils string? label=null) { string msgPrefix = label == null ? string.Empty : $"[{label}] "; - logger.LogMessage(debugMessageImportance, $"Running: {path} {args}", msgPrefix); + logger.LogMessage(debugMessageImportance, $"{msgPrefix}Running: {path} {args}"); var outputBuilder = new StringBuilder(); var processStartInfo = new ProcessStartInfo { @@ -126,12 +126,12 @@ internal static class Utils if (workingDir != null) processStartInfo.WorkingDirectory = workingDir; - logger.LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}", msgPrefix); + logger.LogMessage(debugMessageImportance, $"{msgPrefix}Using working directory: {workingDir ?? Environment.CurrentDirectory}", msgPrefix); if (envVars != null) { if (envVars.Count > 0) - logger.LogMessage(MessageImportance.Low, $"Setting environment variables for execution:", msgPrefix); + logger.LogMessage(MessageImportance.Low, $"{msgPrefix}Setting environment variables for execution:", msgPrefix); foreach (KeyValuePair envVar in envVars) { From 4bab77944c38e4c80af23b90ee24133fa1ef6c0a Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Thu, 22 Jul 2021 10:20:08 -0700 Subject: [PATCH 751/926] Stop building RPMs and Debs during PGO builds (#56113) --- eng/pipelines/installer/jobs/base-job.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/installer/jobs/base-job.yml b/eng/pipelines/installer/jobs/base-job.yml index 22c9b265c7d..bf54f31e433 100644 --- a/eng/pipelines/installer/jobs/base-job.yml +++ b/eng/pipelines/installer/jobs/base-job.yml @@ -426,7 +426,7 @@ jobs: displayName: Disk Usage after Build # Only in glibc leg, we produce RPMs and Debs - - ${{ if and(eq(parameters.runtimeFlavor, 'coreclr'), eq(parameters.platform, 'Linux_x64'), eq(parameters.osSubgroup, ''))}}: + - ${{ if and(eq(parameters.runtimeFlavor, 'coreclr'), eq(parameters.platform, 'Linux_x64'), eq(parameters.osSubgroup, ''), eq(parameters.pgoType, ''))}}: - ${{ each packageBuild in parameters.packageDistroList }}: # This leg's RID matches the build image. Build its distro-dependent packages, as well as # the distro-independent installers. (There's no particular reason to build the distro- From 12c4e4cd63e4d8daf2ad4268876a69f8cda606a7 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 22 Jul 2021 10:35:54 -0700 Subject: [PATCH 752/926] Update System.Net.Security Telemetry tests (#55910) * Update System.Net.Security Telemetry tests * Always return Task from ProcessAuthenticationAsync * Name bool arguments Co-authored-by: Stephen Toub Co-authored-by: Stephen Toub --- .../Net/Security/SslStream.Implementation.cs | 82 +++--------- .../src/System/Net/Security/SslStream.cs | 14 +-- .../tests/FunctionalTests/TelemetryTest.cs | 118 +++++++++++++----- .../Fakes/FakeSslStream.Implementation.cs | 2 +- .../tests/FunctionalTests/TelemetryTest.cs | 2 +- 5 files changed, 116 insertions(+), 102 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index f0f0b954d4a..6b1b1e0c432 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -189,93 +189,45 @@ namespace System.Net.Security // This method assumes that a SSPI context is already in a good shape. // For example it is either a fresh context or already authenticated context that needs renegotiation. // - private Task? ProcessAuthentication(bool isAsync = false, bool isApm = false, CancellationToken cancellationToken = default) + private Task ProcessAuthenticationAsync(bool isAsync = false, bool isApm = false, CancellationToken cancellationToken = default) { ThrowIfExceptional(); if (NetSecurityTelemetry.Log.IsEnabled()) { - return ProcessAuthenticationWithTelemetry(isAsync, isApm, cancellationToken); + return ProcessAuthenticationWithTelemetryAsync(isAsync, isApm, cancellationToken); } else { - if (isAsync) - { - return ForceAuthenticationAsync(new AsyncReadWriteAdapter(InnerStream, cancellationToken), _context!.IsServer, null, isApm); - } - else - { - ForceAuthenticationAsync(new SyncReadWriteAdapter(InnerStream), _context!.IsServer, null).GetAwaiter().GetResult(); - return null; - } + return isAsync ? + ForceAuthenticationAsync(new AsyncReadWriteAdapter(InnerStream, cancellationToken), _context!.IsServer, null, isApm) : + ForceAuthenticationAsync(new SyncReadWriteAdapter(InnerStream), _context!.IsServer, null); } } - private Task? ProcessAuthenticationWithTelemetry(bool isAsync, bool isApm, CancellationToken cancellationToken) + private async Task ProcessAuthenticationWithTelemetryAsync(bool isAsync, bool isApm, CancellationToken cancellationToken) { NetSecurityTelemetry.Log.HandshakeStart(_context!.IsServer, _sslAuthenticationOptions!.TargetHost); ValueStopwatch stopwatch = ValueStopwatch.StartNew(); try { - if (isAsync) - { - Task task = ForceAuthenticationAsync(new AsyncReadWriteAdapter(InnerStream, cancellationToken), _context.IsServer, null, isApm); + Task task = isAsync? + ForceAuthenticationAsync(new AsyncReadWriteAdapter(InnerStream, cancellationToken), _context!.IsServer, null, isApm) : + ForceAuthenticationAsync(new SyncReadWriteAdapter(InnerStream), _context!.IsServer, null); - return task.ContinueWith((t, s) => - { - var tuple = ((SslStream, ValueStopwatch))s!; - SslStream thisRef = tuple.Item1; - ValueStopwatch stopwatch = tuple.Item2; + await task.ConfigureAwait(false); - if (t.IsCompletedSuccessfully) - { - LogSuccess(thisRef, stopwatch); - } - else - { - LogFailure(thisRef._context!.IsServer, stopwatch, t.Exception?.Message ?? "Operation canceled."); - - // Throw the same exception we would if not using Telemetry - t.GetAwaiter().GetResult(); - } - }, - state: (this, stopwatch), - cancellationToken: default, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Current); - } - else - { - ForceAuthenticationAsync(new SyncReadWriteAdapter(InnerStream), _context.IsServer, null).GetAwaiter().GetResult(); - LogSuccess(this, stopwatch); - return null; - } - } - catch (Exception ex) when (LogFailure(_context.IsServer, stopwatch, ex.Message)) - { - Debug.Fail("LogFailure should return false"); - throw; - } - - static bool LogFailure(bool isServer, ValueStopwatch stopwatch, string exceptionMessage) - { - NetSecurityTelemetry.Log.HandshakeFailed(isServer, stopwatch, exceptionMessage); - return false; - } - - static void LogSuccess(SslStream thisRef, ValueStopwatch stopwatch) - { // SslStream could already have been disposed at this point, in which case _connectionOpenedStatus == 2 // Make sure that we increment the open connection counter only if it is guaranteed to be decremented in dispose/finalize + bool connectionOpen = Interlocked.CompareExchange(ref _connectionOpenedStatus, 1, 0) == 0; - // Using a field of a marshal-by-reference class as a ref or out value or taking its address may cause a runtime exception - // Justification: thisRef is a reference to 'this', not a proxy object -#pragma warning disable CS0197 - bool connectionOpen = Interlocked.CompareExchange(ref thisRef._connectionOpenedStatus, 1, 0) == 0; -#pragma warning restore CS0197 - - NetSecurityTelemetry.Log.HandshakeCompleted(thisRef.GetSslProtocolInternal(), stopwatch, connectionOpen); + NetSecurityTelemetry.Log.HandshakeCompleted(GetSslProtocolInternal(), stopwatch, connectionOpen); + } + catch (Exception ex) + { + NetSecurityTelemetry.Log.HandshakeFailed(_context.IsServer, stopwatch, ex.Message); + throw; } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs index 276652dbcb9..ad4398b2ab4 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -293,7 +293,7 @@ namespace System.Net.Security SetAndVerifySelectionCallback(sslClientAuthenticationOptions.LocalCertificateSelectionCallback); ValidateCreateContext(sslClientAuthenticationOptions, _userCertificateValidationCallback, _certSelectionDelegate); - ProcessAuthentication(); + ProcessAuthenticationAsync().GetAwaiter().GetResult(); } public virtual void AuthenticateAsServer(X509Certificate serverCertificate) @@ -330,7 +330,7 @@ namespace System.Net.Security SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); ValidateCreateContext(CreateAuthenticationOptions(sslServerAuthenticationOptions)); - ProcessAuthentication(); + ProcessAuthenticationAsync().GetAwaiter().GetResult(); } #endregion @@ -365,7 +365,7 @@ namespace System.Net.Security ValidateCreateContext(sslClientAuthenticationOptions, _userCertificateValidationCallback, _certSelectionDelegate); - return ProcessAuthentication(true, false, cancellationToken)!; + return ProcessAuthenticationAsync(isAsync: true, isApm: false, cancellationToken); } private Task AuthenticateAsClientApm(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken = default) @@ -375,7 +375,7 @@ namespace System.Net.Security ValidateCreateContext(sslClientAuthenticationOptions, _userCertificateValidationCallback, _certSelectionDelegate); - return ProcessAuthentication(true, true, cancellationToken)!; + return ProcessAuthenticationAsync(isAsync: true, isApm: true, cancellationToken); } public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate) => @@ -418,7 +418,7 @@ namespace System.Net.Security SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); ValidateCreateContext(CreateAuthenticationOptions(sslServerAuthenticationOptions)); - return ProcessAuthentication(true, false, cancellationToken)!; + return ProcessAuthenticationAsync(isAsync: true, isApm: false, cancellationToken); } private Task AuthenticateAsServerApm(SslServerAuthenticationOptions sslServerAuthenticationOptions, CancellationToken cancellationToken = default) @@ -426,13 +426,13 @@ namespace System.Net.Security SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); ValidateCreateContext(CreateAuthenticationOptions(sslServerAuthenticationOptions)); - return ProcessAuthentication(true, true, cancellationToken)!; + return ProcessAuthenticationAsync(isAsync: true, isApm: true, cancellationToken); } public Task AuthenticateAsServerAsync(ServerOptionsSelectionCallback optionsCallback, object? state, CancellationToken cancellationToken = default) { ValidateCreateContext(new SslAuthenticationOptions(optionsCallback, state, _userCertificateValidationCallback)); - return ProcessAuthentication(isAsync: true, isApm: false, cancellationToken)!; + return ProcessAuthenticationAsync(isAsync: true, isApm: false, cancellationToken); } public virtual Task ShutdownAsync() diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TelemetryTest.cs index 72b71d04637..eb8fa3d482a 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TelemetryTest.cs @@ -33,28 +33,44 @@ namespace System.Net.Security.Tests RemoteExecutor.Invoke(async () => { using var listener = new TestEventListener("System.Net.Security", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { // Invoke tests that'll cause some events to be generated var test = new SslStreamStreamToStreamTest_Async(); await test.SslStream_StreamToStream_Authentication_Success(); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself + Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "HandshakeStart").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == "HandshakeStart").ToArray(); Assert.Equal(2, starts.Length); - Assert.All(starts, s => Assert.Equal(2, s.Payload.Count)); - Assert.Single(starts, s => s.Payload[0] is bool isServer && isServer); - Assert.Single(starts, s => s.Payload[1] is string targetHost && targetHost.Length == 0); + Assert.All(starts, s => Assert.Equal(2, s.Event.Payload.Count)); + Assert.All(starts, s => Assert.NotEqual(Guid.Empty, s.ActivityId)); - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "HandshakeStop").ToArray(); + // isServer + (EventWrittenEventArgs Event, Guid ActivityId) serverStart = Assert.Single(starts, s => (bool)s.Event.Payload[0]); + (EventWrittenEventArgs Event, Guid ActivityId) clientStart = Assert.Single(starts, s => !(bool)s.Event.Payload[0]); + + // targetHost + Assert.Empty(Assert.IsType(serverStart.Event.Payload[1])); + Assert.NotEmpty(Assert.IsType(clientStart.Event.Payload[1])); + + Assert.NotEqual(serverStart.ActivityId, clientStart.ActivityId); + + (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == "HandshakeStop").ToArray(); Assert.Equal(2, stops.Length); - Assert.All(stops, s => ValidateHandshakeStopEventPayload(s, failure: false)); - Assert.DoesNotContain(events, e => e.EventName == "HandshakeFailed"); + EventWrittenEventArgs serverStop = Assert.Single(stops, s => s.ActivityId == serverStart.ActivityId).Event; + EventWrittenEventArgs clientStop = Assert.Single(stops, s => s.ActivityId == clientStart.ActivityId).Event; + + SslProtocols serverProtocol = ValidateHandshakeStopEventPayload(serverStop); + SslProtocols clientProtocol = ValidateHandshakeStopEventPayload(clientStop); + Assert.Equal(serverProtocol, clientProtocol); + + Assert.DoesNotContain(events, e => e.Event.EventName == "HandshakeFailed"); VerifyEventCounters(events, shouldHaveFailures: false); }).Dispose(); @@ -67,38 +83,57 @@ namespace System.Net.Security.Tests RemoteExecutor.Invoke(async () => { using var listener = new TestEventListener("System.Net.Security", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { // Invoke tests that'll cause some events to be generated var test = new SslStreamStreamToStreamTest_Async(); await test.SslStream_ServerLocalCertificateSelectionCallbackReturnsNull_Throw(); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself + Assert.DoesNotContain(events, ev => ev.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "HandshakeStart").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == "HandshakeStart").ToArray(); Assert.Equal(2, starts.Length); - Assert.All(starts, s => Assert.Equal(2, s.Payload.Count)); - Assert.Single(starts, s => s.Payload[0] is bool isServer && isServer); - Assert.Single(starts, s => s.Payload[1] is string targetHost && targetHost.Length == 0); + Assert.All(starts, s => Assert.Equal(2, s.Event.Payload.Count)); + Assert.All(starts, s => Assert.NotEqual(Guid.Empty, s.ActivityId)); - EventWrittenEventArgs[] failures = events.Where(e => e.EventName == "HandshakeFailed").ToArray(); - Assert.Equal(2, failures.Length); - Assert.All(failures, f => Assert.Equal(3, f.Payload.Count)); - Assert.Single(failures, f => f.Payload[0] is bool isServer && isServer); - Assert.All(failures, f => Assert.NotEmpty(f.Payload[2] as string)); // exceptionMessage + // isServer + (EventWrittenEventArgs Event, Guid ActivityId) serverStart = Assert.Single(starts, s => (bool)s.Event.Payload[0]); + (EventWrittenEventArgs Event, Guid ActivityId) clientStart = Assert.Single(starts, s => !(bool)s.Event.Payload[0]); - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "HandshakeStop").ToArray(); + // targetHost + Assert.Empty(Assert.IsType(serverStart.Event.Payload[1])); + Assert.NotEmpty(Assert.IsType(clientStart.Event.Payload[1])); + + Assert.NotEqual(serverStart.ActivityId, clientStart.ActivityId); + + (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == "HandshakeStop").ToArray(); Assert.Equal(2, stops.Length); - Assert.All(stops, s => ValidateHandshakeStopEventPayload(s, failure: true)); + Assert.All(stops, s => ValidateHandshakeStopEventPayload(s.Event, failure: true)); + + EventWrittenEventArgs serverStop = Assert.Single(stops, s => s.ActivityId == serverStart.ActivityId).Event; + EventWrittenEventArgs clientStop = Assert.Single(stops, s => s.ActivityId == clientStart.ActivityId).Event; + + (EventWrittenEventArgs Event, Guid ActivityId)[] failures = events.Where(e => e.Event.EventName == "HandshakeFailed").ToArray(); + Assert.Equal(2, failures.Length); + Assert.All(failures, f => Assert.Equal(3, f.Event.Payload.Count)); + Assert.All(failures, f => Assert.NotEmpty(f.Event.Payload[2] as string)); // exceptionMessage + + EventWrittenEventArgs serverFailure = Assert.Single(failures, f => f.ActivityId == serverStart.ActivityId).Event; + EventWrittenEventArgs clientFailure = Assert.Single(failures, f => f.ActivityId == clientStart.ActivityId).Event; + + // isServer + Assert.Equal(true, serverFailure.Payload[0]); + Assert.Equal(false, clientFailure.Payload[0]); VerifyEventCounters(events, shouldHaveFailures: true); }).Dispose(); } - private static void ValidateHandshakeStopEventPayload(EventWrittenEventArgs stopEvent, bool failure) + private static SslProtocols ValidateHandshakeStopEventPayload(EventWrittenEventArgs stopEvent, bool failure = false) { Assert.Equal("HandshakeStop", stopEvent.EventName); Assert.Equal(1, stopEvent.Payload.Count); @@ -114,11 +149,14 @@ namespace System.Net.Security.Tests { Assert.NotEqual(SslProtocols.None, protocol); } + + return protocol; } - private static void VerifyEventCounters(ConcurrentQueue events, bool shouldHaveFailures) + private static void VerifyEventCounters(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool shouldHaveFailures) { Dictionary eventCounters = events + .Select(e => e.Event) .Where(e => e.EventName == "EventCounters") .Select(e => (IDictionary)e.Payload.Single()) .GroupBy(d => (string)d["Name"], d => (double)(d.ContainsKey("Mean") ? d["Mean"] : d["Increment"])) @@ -174,5 +212,29 @@ namespace System.Net.Security.Tests Assert.Contains(allHandshakeDurations, d => d > 0); } } + + private static async Task WaitForEventCountersAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events) + { + DateTime startTime = DateTime.UtcNow; + int startCount = events.Count; + + while (events.Skip(startCount).Count(e => IsTlsHandshakeRateEventCounter(e.Event)) < 3) + { + if (DateTime.UtcNow.Subtract(startTime) > TimeSpan.FromSeconds(30)) + throw new TimeoutException($"Timed out waiting for EventCounters"); + + await Task.Delay(100); + } + + static bool IsTlsHandshakeRateEventCounter(EventWrittenEventArgs e) + { + if (e.EventName != "EventCounters") + return false; + + var dictionary = (IDictionary)e.Payload.Single(); + + return (string)dictionary["Name"] == "tls-handshake-rate"; + } + } } } diff --git a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs index ee160631b40..ae34301a26c 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/Fakes/FakeSslStream.Implementation.cs @@ -54,7 +54,7 @@ namespace System.Net.Security // This method assumes that a SSPI context is already in a good shape. // For example it is either a fresh context or already authenticated context that needs renegotiation. // - private Task ProcessAuthentication(bool isAsync = false, bool isApm = false, CancellationToken cancellationToken = default) + private Task ProcessAuthenticationAsync(bool isAsync = false, bool isApm = false, CancellationToken cancellationToken = default) { return Task.Run(() => {}); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index 7b507a18f40..acd9029136e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -357,7 +357,7 @@ namespace System.Net.Sockets.Tests DateTime startTime = DateTime.UtcNow; int startCount = events.Count; - while (events.Skip(startCount).Count(e => IsBytesSentEventCounter(e.Event)) < 2) + while (events.Skip(startCount).Count(e => IsBytesSentEventCounter(e.Event)) < 3) { if (DateTime.UtcNow.Subtract(startTime) > TimeSpan.FromSeconds(30)) throw new TimeoutException($"Timed out waiting for EventCounters"); From 101e68aa24ac845df9a81914d075e16289b31c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Thu, 22 Jul 2021 11:11:49 -0700 Subject: [PATCH 753/926] Make polling use the symbolic link target's LastWriteTime (#55664) * Relax LinkTarget so it always returns null when steps on an error * Make polling use the symbolic link target's LastWriteTime * Fix for the case where the link can change its target * Add more test cases and exclude them from non-netcoreapp tfms * Fix project references in ref projects * Do not use UnsupportedOSPlatforms on test project in order to fix CI issue * Do not return link's LastWriteTime when target not exists * Address feedback on tests and improve them to cover more scenarios. * Make the project unsupported in browser. * Fix duplicate reference to PlatformAttributes with IncludePlatformAttributes=false * Disable default items for browser * Undo unrelated changes to Strings.resx * Replace Thread.Sleep with Task.Delay, add assertion messages to try to debug CI failures and increase delay between writes * Replace HasChanged for RegisterChangeCallback in tests * Add messages to asserts to attempt to debug CI issues * Add date format to assertion messages. * Increase delay between writes to one second since OSX doesn't report milliseconds --- ...tensions.FileProviders.Abstractions.csproj | 5 +- ...tensions.FileProviders.Abstractions.csproj | 7 +- .../Directory.Build.props | 1 + ...t.Extensions.FileProviders.Physical.csproj | 5 +- .../src/Internal/FileSystemInfoHelper.cs | 36 +++++ ...t.Extensions.FileProviders.Physical.csproj | 20 ++- .../src/PollingFileChangeToken.cs | 8 +- .../src/PollingWildCardChangeToken.cs | 3 +- .../src/Resources/Strings.resx | 3 + ...nsions.FileProviders.Physical.Tests.csproj | 7 +- .../tests/PhysicalFileProviderTests.cs | 54 ++++++- .../PhysicalFileProviderTests.netcoreapp.cs | 139 ++++++++++++++++++ ...osoft.Extensions.FileSystemGlobbing.csproj | 6 +- ...osoft.Extensions.FileSystemGlobbing.csproj | 11 +- 14 files changed, 292 insertions(+), 13 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj index 32239798f1f..bd02f83be91 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 @@ -8,4 +8,7 @@ + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj index 43164c743a9..07289f58670 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -2,7 +2,7 @@ Microsoft.Extensions.FileProviders - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 true Abstractions of files and directories. @@ -21,4 +21,9 @@ Microsoft.Extensions.FileProviders.IFileProvider + + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props b/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props index 668e3954f0b..8ec207de7d8 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props @@ -2,5 +2,6 @@ true + browser \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj index cb4948ba130..0f7dc64cb84 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 @@ -10,4 +10,7 @@ + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index 067a1545860..5ae854ae6ef 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.IO; namespace Microsoft.Extensions.FileProviders.Physical @@ -27,5 +28,40 @@ namespace Microsoft.Extensions.FileProviders.Physical return false; } + + public static DateTime? GetFileLinkTargetLastWriteTimeUtc(string filePath) + { +#if NETCOREAPP + var fileInfo = new FileInfo(filePath); + if (fileInfo.Exists) + { + return GetFileLinkTargetLastWriteTimeUtc(fileInfo); + } +#endif + return null; + } + + // If file is a link and link target exists, return target's LastWriteTimeUtc. + // If file is a link, and link target does not exists, return DateTime.MinValue + // since the link's LastWriteTimeUtc doesn't convey anything for this scenario. + // If file is not a link, return null to inform the caller that file is not a link. + public static DateTime? GetFileLinkTargetLastWriteTimeUtc(FileInfo fileInfo) + { +#if NETCOREAPP + Debug.Assert(fileInfo.Exists); + if (fileInfo.LinkTarget != null) + { + FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); + if (targetInfo.Exists) + { + return targetInfo.LastWriteTimeUtc; + } + + return DateTime.MinValue; + } +#endif + + return null; + } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index 9801bac9fea..534a08b11ff 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -1,12 +1,17 @@ - + Microsoft.Extensions.FileProviders - netstandard2.0;net461 + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser;netstandard2.0;net461 true true File provider for physical files for Microsoft.Extensions.FileProviders. + + + false + SR.FileProvidersPhysical_PlatformNotSupported + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 23d8c8d306c..ddf2ae07423 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -48,7 +48,13 @@ namespace Microsoft.Extensions.FileProviders.Physical private DateTime GetLastWriteTimeUtc() { _fileInfo.Refresh(); - return _fileInfo.Exists ? _fileInfo.LastWriteTimeUtc : DateTime.MinValue; + + if (!_fileInfo.Exists) + { + return DateTime.MinValue; + } + + return FileSystemInfoHelper.GetFileLinkTargetLastWriteTimeUtc(_fileInfo) ?? _fileInfo.LastWriteTimeUtc; } /// diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs index 998aaa29772..28148d2315b 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs @@ -143,7 +143,8 @@ namespace Microsoft.Extensions.FileProviders.Physical /// The that the file was last modified. protected virtual DateTime GetLastWriteUtc(string path) { - return File.GetLastWriteTimeUtc(Path.Combine(_directoryInfo.FullName, path)); + string filePath = Path.Combine(_directoryInfo.FullName, path); + return FileSystemInfoHelper.GetFileLinkTargetLastWriteTimeUtc(filePath) ?? File.GetLastWriteTimeUtc(filePath); } private static bool ArrayEquals(byte[] previousHash, byte[] currentHash) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx index 4d234ccabe2..2ff2106bca0 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx @@ -129,4 +129,7 @@ Unexpected type of FileSystemInfo + + Microsoft.Extensions.FileProviders.Physical is not supported on this platform. + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj index d57fed0ca59..6b4ab37b340 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj @@ -1,10 +1,11 @@ - + Microsoft.Extensions.FileProviders.Physical $(NetCoreAppCurrent);net461 true true + false @@ -12,6 +13,10 @@ Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" /> + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 5bb882e5ccc..b4e37df4f11 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -15,7 +15,7 @@ using Xunit; namespace Microsoft.Extensions.FileProviders { - public class PhysicalFileProviderTests + public partial class PhysicalFileProviderTests { private const int WaitTimeForTokenToFire = 500; private const int WaitTimeForTokenCallback = 10000; @@ -1512,6 +1512,58 @@ namespace Microsoft.Extensions.FileProviders } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); + + // Act + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. + File.WriteAllText(filePath, "v1.2"); + + // Assert + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}"); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + string filter = useWildcard ? "*" : fileName; + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); + + // Act + File.Delete(filePath); + + // Assert + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file Exists: {File.Exists(filePath)}."); + } + [Fact] public void CreateFileWatcher_CreatesWatcherWithPollingAndActiveFlags() { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs new file mode 100644 index 00000000000..d8b332b6409 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.Extensions.FileProviders +{ + public partial class PhysicalFileProviderTests + { + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(filePath, "v1.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, filePath); + + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + + // Act + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. + File.WriteAllText(filePath, "v1.2"); + + // Assert + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) + { + // Arrange + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, "not-existent-file"); + + // Act + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + + // Assert + Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + "Change event was raised when it was not expected."); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool linkWasBroken) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + // Create file 2 first as we want to verify that the change is reported regardless of the timestamp being older. + string file2Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(file2Path, "v2.1"); + + string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + if (!linkWasBroken) + { + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. + File.WriteAllText(file1Path, "v1.1"); + } + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, file1Path); + + string filter = useWildcard ? "*" : linkName; + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + + // Act - Change link target to file 2. + File.Delete(linkPath); + File.CreateSymbolicLink(linkPath, file2Path); + + // Assert - It should report the change regardless of the timestamp being older. + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path):O}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}."); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + + string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(filePath, "v1.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, filePath); + + string filter = useWildcard ? "*" : linkName; + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); + + // Act + File.Delete(linkPath); + + // Assert + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj index b939996a35b..ade27836c9d 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -1,8 +1,12 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 + + + + diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj index 3b3239f39e1..2b6f76bd26c 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -1,7 +1,7 @@ - + - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 true File system globbing to find files matching a specified pattern. @@ -10,5 +10,10 @@ - + + + + + + From 676353c6c417d1fdbf07228b86994d1dff1dcbaa Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 22 Jul 2021 14:12:17 -0400 Subject: [PATCH 754/926] Improve Guid parsing performance for "D", "N", "B", and "P" (#55792) * Improve Guid parsing for "D", "N", "B", and "P" * Update src/libraries/System.Private.CoreLib/src/System/Guid.cs --- .../System.Private.CoreLib/src/System/Guid.cs | 189 +++++++++++------- 1 file changed, 121 insertions(+), 68 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index fa7d6acf9d2..041bc5c0ce0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -197,6 +197,13 @@ namespace System { return Unsafe.As(ref Unsafe.AsRef(in this)); } + + public void ReverseAbcEndianness() + { + _a = BinaryPrimitives.ReverseEndianness(_a); + _b = BinaryPrimitives.ReverseEndianness(_b); + _c = BinaryPrimitives.ReverseEndianness(_c); + } } // Creates a new guid based on the value in the string. The value is made up @@ -367,12 +374,6 @@ namespace System }; } - // Two helpers used for parsing components: - // - Number.TryParseUInt32HexNumberStyle(..., NumberStyles.AllowHexSpecifier, ...) - // Used when we expect the entire provided span to be filled with and only with hex digits and no overflow is possible - // - TryParseHex - // Used when the component may have an optional '+' and "0x" prefix, when it may overflow, etc. - private static bool TryParseExactB(ReadOnlySpan guidString, ref GuidResult result) { // e.g. "{d85b1407-351d-4694-9392-03acc5870eb1}" @@ -390,57 +391,86 @@ namespace System { // e.g. "d85b1407-351d-4694-9392-03acc5870eb1" - // Compat notes due to the previous implementation's implementation details. - // - Components may begin with "0x" or "0x+", but the expected length of each component + if ((uint)guidString.Length != 36 || guidString[8] != '-' || guidString[13] != '-' || guidString[18] != '-' || guidString[23] != '-') + { + result.SetFailure(overflow: false, guidString.Length != 36 ? nameof(SR.Format_GuidInvLen) : nameof(SR.Format_GuidDashes)); + return false; + } + + Span bytes = MemoryMarshal.AsBytes(new Span(ref result, 1)); + uint invalidIfFF = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfFF); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfFF); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfFF); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfFF); + bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfFF); + bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfFF); + bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfFF); + bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfFF); + bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfFF); + bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfFF); + bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfFF); + bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfFF); + bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfFF); + bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfFF); + bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfFF); + bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfFF); + + if (invalidIfFF != 0xFF) + { + if (!BitConverter.IsLittleEndian) + { + result.ReverseAbcEndianness(); + } + + return true; + } + + // The 'D' format has some undesirable behavior leftover from its original implementation: + // - Components may begin with "0x" and/or "+", but the expected length of each component // needs to include those prefixes, e.g. a four digit component could be "1234" or // "0x34" or "+0x4" or "+234", but not "0x1234" nor "+1234" nor "+0x1234". // - "0X" is valid instead of "0x" - - if ((uint)guidString.Length != 36) + // We continue to support these but expect them to be incredibly rare. As such, we + // optimize for correctly formed strings where all the digits are valid hex, and only + // fall back to supporting these other forms if parsing fails. + if (guidString.IndexOfAny('X', 'x', '+') != -1 && TryCompatParsing(guidString, ref result)) { - result.SetFailure(overflow: false, nameof(SR.Format_GuidInvLen)); - return false; - } - - if (guidString[8] != '-' || guidString[13] != '-' || guidString[18] != '-' || guidString[23] != '-') - { - result.SetFailure(overflow: false, nameof(SR.Format_GuidDashes)); - return false; - } - - if (TryParseHex(guidString.Slice(0, 8), out result._a) && // _a - TryParseHex(guidString.Slice(9, 4), out uint uintTmp)) // _b - { - result._b = (ushort)uintTmp; - - if (TryParseHex(guidString.Slice(14, 4), out uintTmp)) // _c - { - result._c = (ushort)uintTmp; - - if (TryParseHex(guidString.Slice(19, 4), out uintTmp)) // _d, _e - { - // _d, _e must be stored as a big-endian ushort - result._de = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; - - if (TryParseHex(guidString.Slice(24, 4), out uintTmp)) // _f, _g - { - // _f, _g must be stored as a big-endian ushort - result._fg = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; - - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k - { - // _h, _i, _j, _k must be stored as a big-endian uint - result._hijk = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(uintTmp) : uintTmp; - - return true; - } - } - } - } + return true; } result.SetFailure(overflow: false, nameof(SR.Format_GuidInvalidChar)); return false; + + static bool TryCompatParsing(ReadOnlySpan guidString, ref GuidResult result) + { + if (TryParseHex(guidString.Slice(0, 8), out result._a) && // _a + TryParseHex(guidString.Slice(9, 4), out uint uintTmp)) // _b + { + result._b = (ushort)uintTmp; + if (TryParseHex(guidString.Slice(14, 4), out uintTmp)) // _c + { + result._c = (ushort)uintTmp; + if (TryParseHex(guidString.Slice(19, 4), out uintTmp)) // _d, _e + { + result._de = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; + if (TryParseHex(guidString.Slice(24, 4), out uintTmp)) // _f, _g + { + result._fg = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; + + // Unlike the other components, this one never allowed 0x or +, so we can parse it as straight hex. + if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k + { + result._hijk = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(uintTmp) : uintTmp; + return true; + } + } + } + } + } + + return false; + } } private static bool TryParseExactN(ReadOnlySpan guidString, ref GuidResult result) @@ -453,28 +483,33 @@ namespace System return false; } - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(0, 8), NumberStyles.AllowHexSpecifier, out result._a) == Number.ParsingStatus.OK && // _a - Number.TryParseUInt32HexNumberStyle(guidString.Slice(8, 8), NumberStyles.AllowHexSpecifier, out uint uintTmp) == Number.ParsingStatus.OK) // _b, _c + Span bytes = MemoryMarshal.AsBytes(new Span(ref result, 1)); + uint invalidIfFF = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfFF); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfFF); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfFF); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfFF); + bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfFF); + bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfFF); + bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfFF); + bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfFF); + bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfFF); + bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfFF); + bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfFF); + bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfFF); + bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfFF); + bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfFF); + bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfFF); + bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfFF); + + if (invalidIfFF != 0xFF) { - // _b, _c are independently in machine-endian order - if (BitConverter.IsLittleEndian) { uintTmp = BitOperations.RotateRight(uintTmp, 16); } - result._bc = uintTmp; - - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(16, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _d, _e, _f, _g + if (!BitConverter.IsLittleEndian) { - // _d, _e, _f, _g must be stored as a big-endian uint - if (BitConverter.IsLittleEndian) { uintTmp = BinaryPrimitives.ReverseEndianness(uintTmp); } - result._defg = uintTmp; - - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(24, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k - { - // _h, _i, _j, _k must be stored as big-endian uint - if (BitConverter.IsLittleEndian) { uintTmp = BinaryPrimitives.ReverseEndianness(uintTmp); } - result._hijk = uintTmp; - - return true; - } + result.ReverseAbcEndianness(); } + + return true; } result.SetFailure(overflow: false, nameof(SR.Format_GuidInvalidChar)); @@ -659,6 +694,24 @@ namespace System return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte DecodeByte(char ch1, char ch2, ref uint invalidIfFF) + { + // TODO https://github.com/dotnet/runtime/issues/13464: + // Replace the Unsafe.Add with HexConverter.FromChar once the bounds checks are eliminated. + + uint h1 = 0xFF; + if (ch1 < HexConverter.CharToHexLookup.Length) + h1 = Unsafe.Add(ref MemoryMarshal.GetReference(HexConverter.CharToHexLookup), ch1); + + uint h2 = 0xFF; + if (ch2 < HexConverter.CharToHexLookup.Length) + h2 = Unsafe.Add(ref MemoryMarshal.GetReference(HexConverter.CharToHexLookup), ch2); + + invalidIfFF |= h1 | h2; + return (byte)(h1 << 4 | h2); + } + private static bool TryParseHex(ReadOnlySpan guidString, out ushort result, ref bool overflow) { bool success = TryParseHex(guidString, out uint tmp, ref overflow); From 541850ad55c031bab9c1a97dc55ca69ef73be5f9 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 22 Jul 2021 11:13:24 -0700 Subject: [PATCH 755/926] Update EnumerateLines docs (#55367) --- .../src/System/MemoryExtensions.Globalization.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs index ccf8f509a99..46c19e06b38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Globalization.cs @@ -385,8 +385,9 @@ namespace System /// Returns an enumeration of lines over the provided span. /// /// - /// See the documentation for for more information - /// on how newline sequences are detected. + /// It is recommended that protocol parsers not utilize this API. See the documentation + /// for for more information on how newline + /// sequences are detected. /// public static SpanLineEnumerator EnumerateLines(this ReadOnlySpan span) { @@ -397,8 +398,9 @@ namespace System /// Returns an enumeration of lines over the provided span. /// /// - /// See the documentation for for more information - /// on how newline sequences are detected. + /// It is recommended that protocol parsers not utilize this API. See the documentation + /// for for more information on how newline + /// sequences are detected. /// public static SpanLineEnumerator EnumerateLines(this Span span) { From 1e41fd398db1167cbfca4782c81403dc5c9ee20a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 22 Jul 2021 11:41:01 -0700 Subject: [PATCH 756/926] Re-enable test (#56120) --- src/tests/issues.targets | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index c2214eee83c..775382c7b6a 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -120,9 +120,6 @@ https://github.com/dotnet/runtime/issues/44341 - - https://github.com/dotnet/runtime/issues/44186 - https://github.com/dotnet/runtime/issues/48727 From f96ca33fbcd41407cc564a48917b3a84c4c21d26 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 22 Jul 2021 11:43:18 -0700 Subject: [PATCH 757/926] Fix EH issues with SPMI and crossgen2 (#56054) * Remove last vestiges of the PAL exception handling from the JIT-EE interfaces * Centralize the tweaks to allow finally behavior for crossgen2 exceptions in SPMI - Centralize the exception filter to capture data with completely common code - On unix, capture Crossgen2 triggered exceptions as exception code 1 (As was done for getCallInfo only before) - Use a bunch of templates to reduce the code bloat in SPMI With these changes catching exceptions should be more reliable. However, I was unable to reproduce the failure, so I need some jit team expertise to identify if these fixes are good. Fixes #49563 --- .../superpmi-shared/errorhandling.cpp | 81 ++++++ .../superpmi/superpmi-shared/errorhandling.h | 39 ++- .../superpmi/superpmi-shared/lwmlist.h | 1 - .../superpmi-shared/methodcontext.cpp | 12 - .../superpmi/superpmi-shared/methodcontext.h | 5 +- .../superpmi-shim-collector/icorjitinfo.cpp | 262 +++++------------- .../superpmi-shim-counter/icorjitinfo.cpp | 15 +- .../superpmi-shim-simple/icorjitinfo.cpp | 13 +- .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 14 +- src/coreclr/inc/corinfo.h | 15 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 7 +- src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_names.h | 2 +- src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 18 +- src/coreclr/jit/compiler.h | 8 + src/coreclr/jit/ee_il_dll.cpp | 61 ++-- src/coreclr/jit/eeinterface.cpp | 159 +++++------ .../tools/Common/JitInterface/CorInfoBase.cs | 37 +-- .../tools/Common/JitInterface/CorInfoImpl.cs | 16 +- .../ThunkGenerator/ThunkInput.txt | 2 +- .../tools/aot/jitinterface/jitinterface.cpp | 18 +- .../tools/aot/jitinterface/jitinterface.h | 9 +- src/coreclr/vm/jitinterface.cpp | 17 ++ src/coreclr/vm/jitinterface.h | 1 + 24 files changed, 417 insertions(+), 405 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.cpp index 6b11771272a..9657edea484 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.cpp @@ -121,6 +121,16 @@ LONG FilterSuperPMIExceptions_CatchNonSuperPMIException(PEXCEPTION_POINTERS pExc return !IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode); } + +// This filter function executes the handler only for SuperPMI generated exceptions, otherwise it continues the +// handler search. This allows for SuperPMI-thrown exceptions to be caught by the JIT and not be caught by the outer +// SuperPMI handler. +LONG FilterSuperPMIExceptions_CatchSuperPMIException(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam) +{ + return IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode); +} + + bool RunWithErrorTrap(void (*function)(void*), void* param) { bool success = true; @@ -145,3 +155,74 @@ bool RunWithErrorTrap(void (*function)(void*), void* param) return success; } + +bool RunWithSPMIErrorTrap(void (*function)(void*), void* param) +{ + bool success = true; + + struct TrapParam + { + void (*function)(void*); + void* param; + } trapParam; + trapParam.function = function; + trapParam.param = param; + + PAL_TRY(TrapParam*, pTrapParam, &trapParam) + { + pTrapParam->function(pTrapParam->param); + } + PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CatchSuperPMIException) + { + success = false; + } + PAL_ENDTRY + + return success; +} + +void RunWithErrorExceptionCodeCaptureAndContinueImp(void* param, void (*function)(void*), void (*finallyFunction)(void*, DWORD)) +{ + struct Param : FilterSuperPMIExceptionsParam_CaptureException + { + void (*function)(void*); + void (*finallyFunction)(void*, DWORD); + void* pParamActual; + } paramStruct; + + paramStruct.pParamActual = param; + paramStruct.function = function; + paramStruct.finallyFunction = finallyFunction; + +#ifdef HOST_UNIX + // We can't capture the exception code as a PAL exceptions when the exception is + // thrown from crossgen2 on Linux as the jitinterface dll does not use the PAL. So + // assume there will be some error, then set it to zero (no error) + // if the called function doesn't throw. + // + // While not quite matching the behavior on Windows, for C++ exceptions this will + // be equivalent. (All C++ exceptions on Windows have the same exception code.) + paramStruct.exceptionCode = 1; +#endif // HOST_UNIX + + PAL_TRY(Param*, pOuterParam, ¶mStruct) + { + PAL_TRY(Param*, pParam, pOuterParam) + { + pParam->function(pParam->pParamActual); +#ifdef HOST_UNIX + pParam->exceptionCode = 0; +#endif // HOST_UNIX + } + PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) + { + } + PAL_ENDTRY + } + PAL_FINALLY + { + paramStruct.finallyFunction(paramStruct.pParamActual, paramStruct.exceptionCode); + } + PAL_ENDTRY + +} diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.h index 81f8c2c321a..8739208aafe 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/errorhandling.h @@ -118,11 +118,46 @@ struct FilterSuperPMIExceptionsParam_CaptureException } }; -extern LONG FilterSuperPMIExceptions_CaptureExceptionAndContinue(PEXCEPTION_POINTERS pExceptionPointers, - LPVOID lpvParam); extern LONG FilterSuperPMIExceptions_CaptureExceptionAndStop(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam); extern bool RunWithErrorTrap(void (*function)(void*), void* param); +extern bool RunWithSPMIErrorTrap(void (*function)(void*), void* param); +extern void RunWithErrorExceptionCodeCaptureAndContinueImp(void* param, void (*function)(void*), void (*finallyFunction)(void*, DWORD)); + +template +class LambdaExecutor +{ +public: + LambdaType& _lambda; + + LambdaExecutor(LambdaType& lambda) : _lambda(lambda) {} +}; + +template +void RunWithErrorExceptionCodeCaptureAndContinue(LambdaTry function, LambdaFinally finally) +{ + struct LambdaArguments + { + LambdaExecutor *pTryLambda; + LambdaExecutor *pFinallyLambda; + } lambdaArgs; + + LambdaExecutor tryStorage(function); + LambdaExecutor finallyStorage(finally); + + lambdaArgs.pTryLambda = &tryStorage; + lambdaArgs.pFinallyLambda = &finallyStorage; + + RunWithErrorExceptionCodeCaptureAndContinueImp(&lambdaArgs, + [](void* pParam) + { + ((LambdaArguments*)pParam)->pTryLambda->_lambda(); + }, + [](void* pParam, DWORD exceptionCode) + { + ((LambdaArguments*)pParam)->pFinallyLambda->_lambda(exceptionCode); + }); +} class SpmiException { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h index 338a4eb935e..0f17ce7290e 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -137,7 +137,6 @@ LWM(GetUnboxedEntry, DWORDLONG, DLD); LWM(GetUnBoxHelper, DWORDLONG, DWORD) LWM(GetVarArgsHandle, GetVarArgsHandleValue, DLDL) LWM(GetVars, DWORDLONG, Agnostic_GetVars) -DENSELWM(HandleException, DWORD) LWM(InitClass, Agnostic_InitClass, DWORD) LWM(IsCompatibleDelegate, Agnostic_IsCompatibleDelegate, DD) LWM(IsDelegateCreationAllowed, DLDL, DWORD) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 7d7824e2208..ae87cb1edde 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -5039,18 +5039,6 @@ int MethodContext::repFilterException(struct _EXCEPTION_POINTERS* pExceptionPoin } } -void MethodContext::recHandleException(struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - if (HandleException == nullptr) - HandleException = new DenseLightWeightMap(); - - HandleException->Append(pExceptionPointers->ExceptionRecord->ExceptionCode); -} -void MethodContext::dmpHandleException(DWORD key, DWORD value) -{ - printf("HandleException key %u, value %u", key, value); -} - void MethodContext::recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup) { if (GetAddressOfPInvokeTarget == nullptr) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 97194bf0099..b342996b446 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -630,9 +630,6 @@ public: void dmpFilterException(DWORD key, DWORD value); int repFilterException(struct _EXCEPTION_POINTERS* pExceptionPointers); - void recHandleException(struct _EXCEPTION_POINTERS* pExceptionPointers); - void dmpHandleException(DWORD key, DWORD value); - void recGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup); void dmpGetAddressOfPInvokeTarget(DWORDLONG key, DLD value); void repGetAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, CORINFO_CONST_LOOKUP* pLookup); @@ -1042,7 +1039,7 @@ enum mcPackets Packet_GetUnmanagedCallConv = 94, Packet_GetVarArgsHandle = 95, Packet_GetVars = 96, - Packet_HandleException = 135, + Packet_HandleException = 135, // Retired 7/19/2021 Packet_InitClass = 97, Packet_InitConstraintsForVerification = 98, // Retired 2/18/2020 Packet_IsCompatibleDelegate = 99, diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index de2c555c20a..4390113cf02 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -72,37 +72,20 @@ bool interceptor_ICJI::getMethodInfo(CORINFO_METHOD_HANDLE ftn, /* IN */ CORINFO_METHOD_INFO* info /* OUT */ ) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException - { - interceptor_ICJI* pThis; - CORINFO_METHOD_HANDLE ftn; - CORINFO_METHOD_INFO* info; - bool temp; - } param; - param.pThis = this; - param.ftn = ftn; - param.info = info; - param.temp = false; + bool temp = false; - PAL_TRY(Param*, pOuterParam, ¶m) + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("getMethodInfo"); - pParam->temp = pParam->pThis->original_ICorJitInfo->getMethodInfo(pParam->ftn, pParam->info); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY + mc->cr->AddCall("getMethodInfo"); + temp = original_ICorJitInfo->getMethodInfo(ftn, info); + }, + [&](DWORD exceptionCode) { - this->mc->recGetMethodInfo(ftn, info, param.temp, param.exceptionCode); - } - PAL_ENDTRY + this->mc->recGetMethodInfo(ftn, info, temp, exceptionCode); + }); - return param.temp; + return temp; } // Decides if you have any limitations for inlining. If everything's OK, it will return @@ -119,40 +102,20 @@ CorInfoInline interceptor_ICJI::canInline(CORINFO_METHOD_HANDLE callerHnd, /* uint32_t* pRestrictions /* OUT */ ) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException - { - interceptor_ICJI* pThis; - CORINFO_METHOD_HANDLE callerHnd; - CORINFO_METHOD_HANDLE calleeHnd; - uint32_t* pRestrictions; - CorInfoInline temp; - } param; - param.pThis = this; - param.callerHnd = callerHnd; - param.calleeHnd = calleeHnd; - param.pRestrictions = pRestrictions; - param.temp = INLINE_NEVER; + CorInfoInline temp = INLINE_NEVER; - PAL_TRY(Param*, pOuterParam, ¶m) + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("canInline"); - pParam->temp = pParam->pThis->original_ICorJitInfo->canInline(pParam->callerHnd, pParam->calleeHnd, - pParam->pRestrictions); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY + mc->cr->AddCall("canInline"); + temp = original_ICorJitInfo->canInline(callerHnd, calleeHnd, pRestrictions); + }, + [&](DWORD exceptionCode) { - this->mc->recCanInline(callerHnd, calleeHnd, pRestrictions, param.temp, param.exceptionCode); - } - PAL_ENDTRY + this->mc->recCanInline(callerHnd, calleeHnd, pRestrictions, temp, exceptionCode); + }); - return param.temp; + return temp; } // Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all @@ -409,31 +372,16 @@ PatchpointInfo* interceptor_ICJI::getOSRInfo(unsigned* ilOffset) // Resolve metadata token into runtime method handles. void interceptor_ICJI::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN* pResolvedToken) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - interceptor_ICJI* pThis; - CORINFO_RESOLVED_TOKEN* pResolvedToken; - } param; - param.pThis = this; - param.pResolvedToken = pResolvedToken; - - PAL_TRY(Param*, pOuterParam, ¶m) + mc->cr->AddCall("resolveToken"); + original_ICorJitInfo->resolveToken(pResolvedToken); + }, + [&](DWORD exceptionCode) { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("resolveToken"); - pParam->pThis->original_ICorJitInfo->resolveToken(pParam->pResolvedToken); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY - { - this->mc->recResolveToken(param.pResolvedToken, param.exceptionCode); - } - PAL_ENDTRY + this->mc->recResolveToken(pResolvedToken, exceptionCode); + }); } bool interceptor_ICJI::tryResolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN* pResolvedToken) @@ -1256,44 +1204,25 @@ CorInfoTypeWithMod interceptor_ICJI::getArgType(CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE* vcTypeRet /* OUT */ ) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException - { - interceptor_ICJI* pThis; - CORINFO_SIG_INFO* sig; - CORINFO_ARG_LIST_HANDLE args; - CORINFO_CLASS_HANDLE* vcTypeRet; - CorInfoTypeWithMod temp; - } param; - param.pThis = this; - param.sig = sig; - param.args = args; - param.vcTypeRet = vcTypeRet; - param.temp = (CorInfoTypeWithMod)CORINFO_TYPE_UNDEF; + CorInfoTypeWithMod temp = (CorInfoTypeWithMod)CORINFO_TYPE_UNDEF; - PAL_TRY(Param*, pOuterParam, ¶m) + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("getArgType"); - pParam->temp = - pParam->pThis->original_ICorJitInfo->getArgType(pParam->sig, pParam->args, pParam->vcTypeRet); + mc->cr->AddCall("getArgType"); + temp = + original_ICorJitInfo->getArgType(sig, args, vcTypeRet); #ifdef fatMC - CORINFO_CLASS_HANDLE temp3 = pParam->pThis->getArgClass(pParam->sig, pParam->args); + CORINFO_CLASS_HANDLE temp3 = getArgClass(sig, args); #endif - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY + }, + [&](DWORD exceptionCode) { - this->mc->recGetArgType(sig, args, vcTypeRet, param.temp, param.exceptionCode); - } - PAL_ENDTRY + this->mc->recGetArgType(sig, args, vcTypeRet, temp, exceptionCode); + }); - return param.temp; + return temp; } // If the Arg is a CORINFO_TYPE_CLASS fetch the class handle associated with it @@ -1301,40 +1230,23 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args /* IN */ ) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException - { - interceptor_ICJI* pThis; - CORINFO_SIG_INFO* sig; - CORINFO_ARG_LIST_HANDLE args; - CORINFO_CLASS_HANDLE temp; - } param; - param.pThis = this; - param.sig = sig; - param.args = args; - param.temp = 0; + CORINFO_CLASS_HANDLE temp = 0; - PAL_TRY(Param*, pOuterParam, ¶m) + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("getArgClass"); - pParam->temp = pParam->pThis->original_ICorJitInfo->getArgClass(pParam->sig, pParam->args); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY + mc->cr->AddCall("getArgClass"); + temp = original_ICorJitInfo->getArgClass(sig, args); + }, + [&](DWORD exceptionCode) { - this->mc->recGetArgClass(sig, args, param.temp, param.exceptionCode); + this->mc->recGetArgClass(sig, args, temp, exceptionCode); // to build up a fat mc - getClassName(param.temp); - } - PAL_ENDTRY + getClassName(temp); + }); - return param.temp; + return temp; } // Returns type of HFA for valuetype @@ -1380,15 +1292,6 @@ int interceptor_ICJI::FilterException(struct _EXCEPTION_POINTERS* pExceptionPoin return temp; } -// Cleans up internal EE tracking when an exception is caught. -void interceptor_ICJI::HandleException(struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - // bswHack? - mc->cr->AddCall("HandleException"); - original_ICorJitInfo->HandleException(pExceptionPointers); - mc->recHandleException(pExceptionPointers); -} - void interceptor_ICJI::ThrowExceptionForJitResult(HRESULT result) { mc->cr->AddCall("ThrowExceptionForJitResult"); @@ -1705,51 +1608,18 @@ void interceptor_ICJI::getCallInfo( // out params CORINFO_CALL_INFO* pResult) { - struct Param : FilterSuperPMIExceptionsParam_CaptureException + RunWithErrorExceptionCodeCaptureAndContinue( + [&]() { - interceptor_ICJI* pThis; - CORINFO_RESOLVED_TOKEN* pResolvedToken; - CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken; - CORINFO_METHOD_HANDLE callerHandle; - CORINFO_CALLINFO_FLAGS flags; - CORINFO_CALL_INFO* pResult; - } param; - param.pThis = this; - param.pResolvedToken = pResolvedToken; - param.pConstrainedResolvedToken = pConstrainedResolvedToken; - param.callerHandle = callerHandle; - param.flags = flags; - param.pResult = pResult; - -#ifdef HOST_UNIX - // We don't seem to be able to capture the exception code in PAL exceptions when thrown - // from crossgen2. So assume there will be some error, then set it to zero (no error) - // if the `getCallInfo` call doesn't throw. - param.exceptionCode = 1; -#endif // HOST_UNIX - - PAL_TRY(Param*, pOuterParam, ¶m) + mc->cr->AddCall("getCallInfo"); + original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, + callerHandle, flags, pResult); + }, + [&](DWORD exceptionCode) { - PAL_TRY(Param*, pParam, pOuterParam) - { - pParam->pThis->mc->cr->AddCall("getCallInfo"); - pParam->pThis->original_ICorJitInfo->getCallInfo(pParam->pResolvedToken, pParam->pConstrainedResolvedToken, - pParam->callerHandle, pParam->flags, pParam->pResult); -#ifdef HOST_UNIX - pParam->exceptionCode = 0; -#endif // HOST_UNIX - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) - { - } - PAL_ENDTRY - } - PAL_FINALLY - { - this->mc->recGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, - param.exceptionCode); - } - PAL_ENDTRY + mc->recGetCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult, + exceptionCode); + }); } bool interceptor_ICJI::canAccessFamily(CORINFO_METHOD_HANDLE hCaller, CORINFO_CLASS_HANDLE hInstanceType) @@ -1928,6 +1798,16 @@ bool interceptor_ICJI::runWithErrorTrap(void (*function)(void*), void* param) return original_ICorJitInfo->runWithErrorTrap(function, param); } +// Runs the given function with the given parameter under an error trap +// and returns true if the function completes successfully. We don't +// record the results of the call: when this call gets played back, +// its result will depend on whether or not `function` calls something +// that throws at playback time rather than at capture time. +bool interceptor_ICJI::runWithSPMIErrorTrap(void (*function)(void*), void* param) +{ + return RunWithSPMIErrorTrap(function, param); +} + // get a block of memory for the code, readonly data, and read-write data void interceptor_ICJI::allocMem(AllocMemArgs *pArgs) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 0c9a8391148..445cc2a490e 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -859,13 +859,6 @@ int interceptor_ICJI::FilterException( return original_ICorJitInfo->FilterException(pExceptionPointers); } -void interceptor_ICJI::HandleException( - struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - mcs->AddCall("HandleException"); - original_ICorJitInfo->HandleException(pExceptionPointers); -} - void interceptor_ICJI::ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) { @@ -888,6 +881,14 @@ bool interceptor_ICJI::runWithErrorTrap( return original_ICorJitInfo->runWithErrorTrap(function, parameter); } +bool interceptor_ICJI::runWithSPMIErrorTrap( + ICorJitInfo::errorTrapFunction function, + void* parameter) +{ + mcs->AddCall("runWithSPMIErrorTrap"); + return original_ICorJitInfo->runWithSPMIErrorTrap(function, parameter); +} + void interceptor_ICJI::getEEInfo( CORINFO_EE_INFO* pEEInfoOut) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 1fd27c307ac..7b04e139c4d 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -752,12 +752,6 @@ int interceptor_ICJI::FilterException( return original_ICorJitInfo->FilterException(pExceptionPointers); } -void interceptor_ICJI::HandleException( - struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - original_ICorJitInfo->HandleException(pExceptionPointers); -} - void interceptor_ICJI::ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) { @@ -777,6 +771,13 @@ bool interceptor_ICJI::runWithErrorTrap( return original_ICorJitInfo->runWithErrorTrap(function, parameter); } +bool interceptor_ICJI::runWithSPMIErrorTrap( + ICorJitInfo::errorTrapFunction function, + void* parameter) +{ + return original_ICorJitInfo->runWithSPMIErrorTrap(function, parameter); +} + void interceptor_ICJI::getEEInfo( CORINFO_EE_INFO* pEEInfoOut) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 91bf8bf2f77..b2cb35339ad 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -1134,12 +1134,6 @@ int MyICJI::FilterException(struct _EXCEPTION_POINTERS* pExceptionPointers) return result; } -// Cleans up internal EE tracking when an exception is caught. -void MyICJI::HandleException(struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - jitInstance->mc->cr->AddCall("HandleException"); -} - void MyICJI::ThrowExceptionForJitResult(HRESULT result) { jitInstance->mc->cr->AddCall("ThrowExceptionForJitResult"); @@ -1588,6 +1582,14 @@ bool MyICJI::runWithErrorTrap(void (*function)(void*), void* param) return RunWithErrorTrap(function, param); } +// Runs the given function with the given parameter under an error trap +// and returns true if the function completes successfully. This catches +// all SPMI exceptions and lets others through. +bool MyICJI::runWithSPMIErrorTrap(void (*function)(void*), void* param) +{ + return RunWithSPMIErrorTrap(function, param); +} + // Ideally we'd just use the copies of this in standardmacros.h // however, superpmi is missing various other dependencies as well static size_t ALIGN_UP_SPMI(size_t val, size_t alignment) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index abf5527d001..a0093501b56 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2780,15 +2780,11 @@ public: // returns EXCEPTION_CONTINUE_SEARCH if exception must always be handled by the EE // things like ThreadStoppedException ... // returns EXCEPTION_CONTINUE_EXECUTION if exception is fixed up by the EE - + // Only used as a contract between the Zapper and the VM. virtual int FilterException( struct _EXCEPTION_POINTERS *pExceptionPointers ) = 0; - // Cleans up internal EE tracking when an exception is caught. - virtual void HandleException( - struct _EXCEPTION_POINTERS *pExceptionPointers - ) = 0; virtual void ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) = 0; @@ -2807,6 +2803,15 @@ public: void* parameter // The context parameter that will be passed to the function and the handler ) = 0; + // Runs the given function under an error trap. This allows the JIT to make calls + // to interface functions that may throw exceptions without needing to be aware of + // the EH ABI, exception types, etc. Returns true if the given function completed + // successfully and false otherwise. This error trap checks for SuperPMI exceptions + virtual bool runWithSPMIErrorTrap( + errorTrapFunction function, // The function to run + void* parameter // The context parameter that will be passed to the function and the handler + ) = 0; + /***************************************************************************** * ICorStaticInfo contains EE interface methods which return values that are * constant from invocation to invocation. Thus they may be embedded in diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index c65a0840d6f..b3b77ece974 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -440,9 +440,6 @@ uint32_t GetErrorMessage( int FilterException( struct _EXCEPTION_POINTERS* pExceptionPointers) override; -void HandleException( - struct _EXCEPTION_POINTERS* pExceptionPointers) override; - void ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) override; @@ -453,6 +450,10 @@ bool runWithErrorTrap( ICorJitInfo::errorTrapFunction function, void* parameter) override; +bool runWithSPMIErrorTrap( + ICorJitInfo::errorTrapFunction function, + void* parameter) override; + void getEEInfo( CORINFO_EE_INFO* pEEInfoOut) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 225c73008bb..f2d6921bc76 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* de9a9a0e-c66a-4d97-a268-92a31f99d919 */ - 0xde9a9a0e, - 0xc66a, - 0x4d97, - {0xa2, 0x68, 0x92, 0xa3, 0x1f, 0x99, 0xd9, 0x19} +constexpr GUID JITEEVersionIdentifier = { /* c2e4a466-795d-4e64-a776-0ae7b2eed65b */ + 0xc2e4a466, + 0x795d, + 0x4e64, + {0xa7, 0x76, 0x0a, 0xe7, 0xb2, 0xee, 0xd6, 0x5b} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 5588f234a2c..f29e37f91ba 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -111,10 +111,10 @@ DEF_CLR_API(getHFAType) DEF_CLR_API(GetErrorHRESULT) DEF_CLR_API(GetErrorMessage) DEF_CLR_API(FilterException) -DEF_CLR_API(HandleException) DEF_CLR_API(ThrowExceptionForJitResult) DEF_CLR_API(ThrowExceptionForHelper) DEF_CLR_API(runWithErrorTrap) +DEF_CLR_API(runWithSPMIErrorTrap) DEF_CLR_API(getEEInfo) DEF_CLR_API(getJitTimeLogFilename) DEF_CLR_API(getMethodDefFromMethod) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index fb79dc18444..66292765480 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -1051,14 +1051,6 @@ int WrapICorJitInfo::FilterException( return temp; } -void WrapICorJitInfo::HandleException( - struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - API_ENTER(HandleException); - wrapHnd->HandleException(pExceptionPointers); - API_LEAVE(HandleException); -} - void WrapICorJitInfo::ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) { @@ -1085,6 +1077,16 @@ bool WrapICorJitInfo::runWithErrorTrap( return temp; } +bool WrapICorJitInfo::runWithSPMIErrorTrap( + ICorJitInfo::errorTrapFunction function, + void* parameter) +{ + API_ENTER(runWithSPMIErrorTrap); + bool temp = wrapHnd->runWithSPMIErrorTrap(function, parameter); + API_LEAVE(runWithSPMIErrorTrap); + return temp; +} + void WrapICorJitInfo::getEEInfo( CORINFO_EE_INFO* pEEInfoOut) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 155e8e4d1e5..ad35dd6fac9 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7987,6 +7987,14 @@ public: bool eeRunWithErrorTrapImp(void (*function)(void*), void* param); + template + bool eeRunWithSPMIErrorTrap(void (*function)(ParamType*), ParamType* param) + { + return eeRunWithSPMIErrorTrapImp(reinterpret_cast(function), reinterpret_cast(param)); + } + + bool eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param); + // Utility functions const char* eeGetFieldName(CORINFO_FIELD_HANDLE fieldHnd, const char** classNamePtr = nullptr); diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 0f993113be1..8904992524f 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -1235,6 +1235,11 @@ bool Compiler::eeRunWithErrorTrapImp(void (*function)(void*), void* param) return info.compCompHnd->runWithErrorTrap(function, param); } +bool Compiler::eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param) +{ + return info.compCompHnd->runWithSPMIErrorTrap(function, param); +} + /***************************************************************************** * * Utility functions @@ -1269,19 +1274,6 @@ struct FilterSuperPMIExceptionsParam_ee_il EXCEPTION_POINTERS exceptionPointers; }; -static LONG FilterSuperPMIExceptions_ee_il(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam) -{ - FilterSuperPMIExceptionsParam_ee_il* pSPMIEParam = (FilterSuperPMIExceptionsParam_ee_il*)lpvParam; - pSPMIEParam->exceptionPointers = *pExceptionPointers; - - if (pSPMIEParam->pThis->IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode)) - { - return EXCEPTION_EXECUTE_HANDLER; - } - - return EXCEPTION_CONTINUE_SEARCH; -} - const char* Compiler::eeGetMethodName(CORINFO_METHOD_HANDLE method, const char** classNamePtr) { if (eeGetHelperNum(method) != CORINFO_HELP_UNDEF) @@ -1320,12 +1312,14 @@ const char* Compiler::eeGetMethodName(CORINFO_METHOD_HANDLE method, const char** param.method = method; param.classNamePtr = classNamePtr; - PAL_TRY(FilterSuperPMIExceptionsParam_ee_il*, pParam, ¶m) - { - pParam->fieldOrMethodOrClassNamePtr = - pParam->pJitInfo->compCompHnd->getMethodName(pParam->method, pParam->classNamePtr); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_ee_il) + bool success = eeRunWithSPMIErrorTrap( + [](FilterSuperPMIExceptionsParam_ee_il* pParam) { + pParam->fieldOrMethodOrClassNamePtr = + pParam->pJitInfo->compCompHnd->getMethodName(pParam->method, pParam->classNamePtr); + }, + ¶m); + + if (!success) { if (param.classNamePtr != nullptr) { @@ -1334,7 +1328,6 @@ const char* Compiler::eeGetMethodName(CORINFO_METHOD_HANDLE method, const char** param.fieldOrMethodOrClassNamePtr = "hackishMethodName"; } - PAL_ENDTRY return param.fieldOrMethodOrClassNamePtr; } @@ -1348,16 +1341,17 @@ const char* Compiler::eeGetFieldName(CORINFO_FIELD_HANDLE field, const char** cl param.field = field; param.classNamePtr = classNamePtr; - PAL_TRY(FilterSuperPMIExceptionsParam_ee_il*, pParam, ¶m) - { - pParam->fieldOrMethodOrClassNamePtr = - pParam->pJitInfo->compCompHnd->getFieldName(pParam->field, pParam->classNamePtr); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_ee_il) + bool success = eeRunWithSPMIErrorTrap( + [](FilterSuperPMIExceptionsParam_ee_il* pParam) { + pParam->fieldOrMethodOrClassNamePtr = + pParam->pJitInfo->compCompHnd->getFieldName(pParam->field, pParam->classNamePtr); + }, + ¶m); + + if (!success) { param.fieldOrMethodOrClassNamePtr = "hackishFieldName"; } - PAL_ENDTRY return param.fieldOrMethodOrClassNamePtr; } @@ -1370,15 +1364,16 @@ const char* Compiler::eeGetClassName(CORINFO_CLASS_HANDLE clsHnd) param.pJitInfo = &info; param.clazz = clsHnd; - PAL_TRY(FilterSuperPMIExceptionsParam_ee_il*, pParam, ¶m) - { - pParam->fieldOrMethodOrClassNamePtr = pParam->pJitInfo->compCompHnd->getClassName(pParam->clazz); - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_ee_il) + bool success = eeRunWithSPMIErrorTrap( + [](FilterSuperPMIExceptionsParam_ee_il* pParam) { + pParam->fieldOrMethodOrClassNamePtr = pParam->pJitInfo->compCompHnd->getClassName(pParam->clazz); + }, + ¶m); + + if (!success) { param.fieldOrMethodOrClassNamePtr = "hackishClassName"; } - PAL_ENDTRY return param.fieldOrMethodOrClassNamePtr; } diff --git a/src/coreclr/jit/eeinterface.cpp b/src/coreclr/jit/eeinterface.cpp index 2ddc6257458..9976b3d6763 100644 --- a/src/coreclr/jit/eeinterface.cpp +++ b/src/coreclr/jit/eeinterface.cpp @@ -46,19 +46,6 @@ struct FilterSuperPMIExceptionsParam_eeinterface EXCEPTION_POINTERS exceptionPointers; }; -static LONG FilterSuperPMIExceptions_eeinterface(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam) -{ - FilterSuperPMIExceptionsParam_eeinterface* pSPMIEParam = (FilterSuperPMIExceptionsParam_eeinterface*)lpvParam; - pSPMIEParam->exceptionPointers = *pExceptionPointers; - - if (pSPMIEParam->pThis->IsSuperPMIException(pExceptionPointers->ExceptionRecord->ExceptionCode)) - { - return EXCEPTION_EXECUTE_HANDLER; - } - - return EXCEPTION_CONTINUE_SEARCH; -} - const char* Compiler::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd) { const char* className; @@ -101,104 +88,106 @@ const char* Compiler::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd) /* add length of methodName and opening bracket */ length += strlen(methodName) + 1; - PAL_TRY(FilterSuperPMIExceptionsParam_eeinterface*, pParam, ¶m) - { + bool success = eeRunWithSPMIErrorTrap( + [](FilterSuperPMIExceptionsParam_eeinterface* pParam) { - /* figure out the signature */ + /* figure out the signature */ - pParam->pThis->eeGetMethodSig(pParam->hnd, &pParam->sig); + pParam->pThis->eeGetMethodSig(pParam->hnd, &pParam->sig); - // allocate space to hold the class names for each of the parameters + // allocate space to hold the class names for each of the parameters - if (pParam->sig.numArgs > 0) - { - pParam->pArgNames = pParam->pThis->getAllocator(CMK_DebugOnly).allocate(pParam->sig.numArgs); - } - else - { - pParam->pArgNames = nullptr; - } - - unsigned i; - pParam->argLst = pParam->sig.args; - - for (i = 0; i < pParam->sig.numArgs; i++) - { - var_types type = pParam->pThis->eeGetArgType(pParam->argLst, &pParam->sig); - switch (type) + if (pParam->sig.numArgs > 0) { - case TYP_REF: - case TYP_STRUCT: + pParam->pArgNames = + pParam->pThis->getAllocator(CMK_DebugOnly).allocate(pParam->sig.numArgs); + } + else + { + pParam->pArgNames = nullptr; + } + + unsigned i; + pParam->argLst = pParam->sig.args; + + for (i = 0; i < pParam->sig.numArgs; i++) + { + var_types type = pParam->pThis->eeGetArgType(pParam->argLst, &pParam->sig); + switch (type) { - CORINFO_CLASS_HANDLE clsHnd = pParam->pThis->eeGetArgClass(&pParam->sig, pParam->argLst); - // For some SIMD struct types we can get a nullptr back from eeGetArgClass on Linux/X64 - if (clsHnd != NO_CLASS_HANDLE) + case TYP_REF: + case TYP_STRUCT: { - const char* clsName = pParam->pThis->eeGetClassName(clsHnd); - if (clsName != nullptr) + CORINFO_CLASS_HANDLE clsHnd = pParam->pThis->eeGetArgClass(&pParam->sig, pParam->argLst); + // For some SIMD struct types we can get a nullptr back from eeGetArgClass on Linux/X64 + if (clsHnd != NO_CLASS_HANDLE) { - pParam->pArgNames[i] = clsName; - break; + const char* clsName = pParam->pThis->eeGetClassName(clsHnd); + if (clsName != nullptr) + { + pParam->pArgNames[i] = clsName; + break; + } } } + FALLTHROUGH; + default: + pParam->pArgNames[i] = varTypeName(type); + break; } - FALLTHROUGH; - default: - pParam->pArgNames[i] = varTypeName(type); - break; + pParam->siglength += strlen(pParam->pArgNames[i]); + pParam->argLst = pParam->pJitInfo->compCompHnd->getArgNext(pParam->argLst); } - pParam->siglength += strlen(pParam->pArgNames[i]); - pParam->argLst = pParam->pJitInfo->compCompHnd->getArgNext(pParam->argLst); - } - /* add ',' if there is more than one argument */ + /* add ',' if there is more than one argument */ - if (pParam->sig.numArgs > 1) - { - pParam->siglength += (pParam->sig.numArgs - 1); - } - - var_types retType = JITtype2varType(pParam->sig.retType); - if (retType != TYP_VOID) - { - switch (retType) + if (pParam->sig.numArgs > 1) { - case TYP_REF: - case TYP_STRUCT: + pParam->siglength += (pParam->sig.numArgs - 1); + } + + var_types retType = JITtype2varType(pParam->sig.retType); + if (retType != TYP_VOID) + { + switch (retType) { - CORINFO_CLASS_HANDLE clsHnd = pParam->sig.retTypeClass; - if (clsHnd != NO_CLASS_HANDLE) + case TYP_REF: + case TYP_STRUCT: { - const char* clsName = pParam->pThis->eeGetClassName(clsHnd); - if (clsName != nullptr) + CORINFO_CLASS_HANDLE clsHnd = pParam->sig.retTypeClass; + if (clsHnd != NO_CLASS_HANDLE) { - pParam->returnType = clsName; - break; + const char* clsName = pParam->pThis->eeGetClassName(clsHnd); + if (clsName != nullptr) + { + pParam->returnType = clsName; + break; + } } } + FALLTHROUGH; + default: + pParam->returnType = varTypeName(retType); + break; } - FALLTHROUGH; - default: - pParam->returnType = varTypeName(retType); - break; + pParam->siglength += strlen(pParam->returnType) + 1; // don't forget the delimiter ':' } - pParam->siglength += strlen(pParam->returnType) + 1; // don't forget the delimiter ':' - } - // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first - // element of the arg type list - if (pParam->sig.hasThis() && !pParam->sig.hasExplicitThis()) - { - assert(strlen(":this") == 5); - pParam->siglength += 5; - pParam->hasThis = true; - } - } - PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_eeinterface) + // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first + // element of the arg type list + if (pParam->sig.hasThis() && !pParam->sig.hasExplicitThis()) + { + assert(strlen(":this") == 5); + pParam->siglength += 5; + pParam->hasThis = true; + } + }, + ¶m); + + if (!success) { param.siglength = 0; } - PAL_ENDTRY /* add closing bracket and null terminator */ diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index d450ac74e6b..db9eb3f137a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1595,20 +1595,6 @@ namespace Internal.JitInterface } } - [UnmanagedCallersOnly] - static void _HandleException(IntPtr thisHandle, IntPtr* ppException, _EXCEPTION_POINTERS* pExceptionPointers) - { - var _this = GetThis(thisHandle); - try - { - _this.HandleException(pExceptionPointers); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - } - } - [UnmanagedCallersOnly] static void _ThrowExceptionForJitResult(IntPtr thisHandle, IntPtr* ppException, HRESULT result) { @@ -1652,6 +1638,21 @@ namespace Internal.JitInterface } } + [UnmanagedCallersOnly] + static byte _runWithSPMIErrorTrap(IntPtr thisHandle, IntPtr* ppException, void* function, void* parameter) + { + var _this = GetThis(thisHandle); + try + { + return _this.runWithSPMIErrorTrap(function, parameter) ? (byte)1 : (byte)0; + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static void _getEEInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_EE_INFO* pEEInfoOut) { @@ -2660,10 +2661,10 @@ namespace Internal.JitInterface callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; callbacks[106] = (delegate* unmanaged)&_FilterException; - callbacks[107] = (delegate* unmanaged)&_HandleException; - callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; callbacks[111] = (delegate* unmanaged)&_getEEInfo; callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index f102d06976a..9898ff8d55d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2853,14 +2853,6 @@ namespace Internal.JitInterface throw new NotSupportedException("FilterException"); } - private void HandleException(_EXCEPTION_POINTERS* pExceptionPointers) - { - // This method is completely handled by the C++ wrapper to the JIT-EE interface, - // and should never reach the managed implementation. - Debug.Fail("CorInfoImpl.HandleException should not be called"); - throw new NotSupportedException("HandleException"); - } - private bool runWithErrorTrap(void* function, void* parameter) { // This method is completely handled by the C++ wrapper to the JIT-EE interface, @@ -2869,6 +2861,14 @@ namespace Internal.JitInterface throw new NotSupportedException("runWithErrorTrap"); } + private bool runWithSPMIErrorTrap(void* function, void* parameter) + { + // This method is completely handled by the C++ wrapper to the JIT-EE interface, + // and should never reach the managed implementation. + Debug.Fail("CorInfoImpl.runWithSPMIErrorTrap should not be called"); + throw new NotSupportedException("runWithSPMIErrorTrap"); + } + private void ThrowExceptionForJitResult(HRESULT result) { throw new NotImplementedException("ThrowExceptionForJitResult"); } private void ThrowExceptionForHelper(ref CORINFO_HELPER_DESC throwHelper) diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index a15819e7569..22e390f5285 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -260,10 +260,10 @@ FUNCTIONS JITINTERFACE_HRESULT GetErrorHRESULT(struct _EXCEPTION_POINTERS *pExceptionPointers); uint32_t GetErrorMessage(char16_t * buffer, uint32_t bufferLength); [ManualNativeWrapper] int FilterException(struct _EXCEPTION_POINTERS* pExceptionPointers); - [ManualNativeWrapper] void HandleException(struct _EXCEPTION_POINTERS* pExceptionPointers); void ThrowExceptionForJitResult(JITINTERFACE_HRESULT result); void ThrowExceptionForHelper(const CORINFO_HELPER_DESC* throwHelper); [ManualNativeWrapper] bool runWithErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter); + [ManualNativeWrapper] bool runWithSPMIErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter); void getEEInfo(CORINFO_EE_INFO* pEEInfoOut); const char16_t * getJitTimeLogFilename(); mdMethodDef getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.cpp b/src/coreclr/tools/aot/jitinterface/jitinterface.cpp index cb185e75d9a..de7efac0b6b 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.cpp +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.cpp @@ -19,11 +19,6 @@ int JitInterfaceWrapper::FilterException(struct _EXCEPTION_POINTERS* pExceptionP return 1; // EXCEPTION_EXECUTE_HANDLER } -void JitInterfaceWrapper::HandleException(struct _EXCEPTION_POINTERS* pExceptionPointers) -{ - NotImplemented(); -} - bool JitInterfaceWrapper::runWithErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter) { try @@ -36,3 +31,16 @@ bool JitInterfaceWrapper::runWithErrorTrap(ICorJitInfo::errorTrapFunction functi } return true; } + +bool JitInterfaceWrapper::runWithSPMIErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter) +{ + try + { + (*function)(parameter); + } + catch (CorInfoExceptionClass *) + { + return false; + } + return true; +} diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 19e6073afac..86591df5df3 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -118,10 +118,10 @@ struct JitInterfaceCallbacks JITINTERFACE_HRESULT (* GetErrorHRESULT)(void * thisHandle, CorInfoExceptionClass** ppException, struct _EXCEPTION_POINTERS* pExceptionPointers); uint32_t (* GetErrorMessage)(void * thisHandle, CorInfoExceptionClass** ppException, char16_t* buffer, uint32_t bufferLength); int (* FilterException)(void * thisHandle, CorInfoExceptionClass** ppException, struct _EXCEPTION_POINTERS* pExceptionPointers); - void (* HandleException)(void * thisHandle, CorInfoExceptionClass** ppException, struct _EXCEPTION_POINTERS* pExceptionPointers); void (* ThrowExceptionForJitResult)(void * thisHandle, CorInfoExceptionClass** ppException, JITINTERFACE_HRESULT result); void (* ThrowExceptionForHelper)(void * thisHandle, CorInfoExceptionClass** ppException, const CORINFO_HELPER_DESC* throwHelper); bool (* runWithErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter); + bool (* runWithSPMIErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter); void (* getEEInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_EE_INFO* pEEInfoOut); const char16_t* (* getJitTimeLogFilename)(void * thisHandle, CorInfoExceptionClass** ppException); mdMethodDef (* getMethodDefFromMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod); @@ -1231,9 +1231,6 @@ public: virtual int FilterException( struct _EXCEPTION_POINTERS* pExceptionPointers); - virtual void HandleException( - struct _EXCEPTION_POINTERS* pExceptionPointers); - virtual void ThrowExceptionForJitResult( JITINTERFACE_HRESULT result) { @@ -1254,6 +1251,10 @@ public: ICorJitInfo::errorTrapFunction function, void* parameter); + virtual bool runWithSPMIErrorTrap( + ICorJitInfo::errorTrapFunction function, + void* parameter); + virtual void getEEInfo( CORINFO_EE_INFO* pEEInfoOut) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b19fa365323..51c87fa8d67 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10936,6 +10936,23 @@ static LONG RunWithErrorTrapFilter(struct _EXCEPTION_POINTERS* exceptionPointers #endif // !defined(TARGET_UNIX) +bool CEEInfo::runWithSPMIErrorTrap(void (*function)(void*), void* param) +{ + // No dynamic contract here because SEH is used + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_PREEMPTIVE; + + // NOTE: the lack of JIT/EE transition markers in this method is intentional. Any + // transitions into the EE proper should occur either via JIT/EE + // interface calls made by `function`. + + // As we aren't SPMI, we don't need to do anything other than call the function. + + function(param); + return true; +} + bool CEEInfo::runWithErrorTrap(void (*function)(void*), void* param) { // No dynamic contract here because SEH is used diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index a84a948cda1..feb9ed94f7e 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -411,6 +411,7 @@ class CEEInfo : public ICorJitInfo void GetTypeContext(CORINFO_CONTEXT_HANDLE context, SigTypeContext* pTypeContext); BOOL ContextIsInstantiated(CORINFO_CONTEXT_HANDLE context); + void HandleException(struct _EXCEPTION_POINTERS* pExceptionPointers); public: #include "icorjitinfoimpl_generated.h" uint32_t getClassAttribsInternal (CORINFO_CLASS_HANDLE cls); From 921bca0ae0d07a7dfd83d5d70eaf49759ab8463b Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Thu, 22 Jul 2021 12:08:27 -0700 Subject: [PATCH 758/926] instrumentation changes (#55888) We've never had ETW events that had fields about time - we rely on the the timestamps of the ETW events themselves to calculate time. This checkin introduces some new events/event fields that will include time info instead of firing individual events that otherwise don't carry much info, ie, they'd just be there so we could use their timestamps, which would be a nuisance when we have many heaps. The rationale behind firing events with time already calculated is 1) it does reduce overhead since we don't fire as many events so we can fire these in fewer events for informational level 2) firing individual vents and having the tools interpret them isn't very useful unlike events such as GCStart/GCEnd which can be correlated with other events (eg, you get GCStart, and then get a bunch of other events so you know those happened after a GC started) wheras things like very GC internal things don't have this property, ie, we are not gonna care that "these other events happened during a GC and specifically during the relocaton phase". --- Added MarkWithType events for marking due to dependent handles, newly promoted due to dead finalizable objects and mark_steal. Perfview needs to be updated to work with this otherwise you can't see the GCStats view (I'm submitting a PR for that). Recorded time for marking roots (but sizedref is separate), short weak, ScanForFinalization, long weak, relocate, compact and sweep. Added a new version that includes the size of the object that triggered the event. This is for a request from https://github.com/dotnet/runtime/issues/49424#issuecomment-810457867. Provided a new rundown GCSettings event that has info on settings hard to get from traces. Added a GCLOHCompact event which is fired for all heaps (heaps that didn't actually do LOH compact would have values of all 0s). I'm trying to add events that don't require a lot of correlation with other events to interpret. This is to help get an idea how long it takes to compact LOH and how reference rich it is. Added a verbose level GCFitBucketInfo event which helps us with FL fitting investigation. I'm firing this for 2 things in a gen1 GC: 1) for plugs that allocated with allocate_in_condemned_generations the event will capture all of them with the same bucketing as we do for gen2 FL; 2) for gen2 FL we look at the largest free items that take up 25% of the FL space, or if there are too many of them we stop after walking a certain number of free items as we have to limit the amount of time we are spending here. --- Fixed issues - For BGC we were reporting the pinned object count the same as the last FGC..and that causes confusion so fixed that. Fixed https://github.com/dotnet/runtime/issues/45375 While fixing https://github.com/dotnet/runtime/issues/45375, I noticed we have another bug related to alloc tick which is we are not firing the alloc tick events correctly for LOH and POH since the ETW alloc tracking didn't seperate them... fixed this too. Added the POH type for GCSegmentTypeMap which was missing in the manifest. --- Did some cleanup in eventtrace.h - we don't need the info that's not used which means we just ended up duplicating things like _GC_ROOT_KIND in more places than needed. --- Note, I realize that I do have some inconsistency with the FEAETURE_EVENT_TRACE here, as in, some code should be under an #ifdef check but is not. I will look into a remedy for that with a separate PR. --- src/coreclr/gc/env/etmdummy.h | 4 + src/coreclr/gc/env/gcenv.base.h | 6 +- src/coreclr/gc/gc.cpp | 1310 +++++++++++++++++++-------- src/coreclr/gc/gc.h | 8 - src/coreclr/gc/gcconfig.h | 1 + src/coreclr/gc/gcee.cpp | 32 +- src/coreclr/gc/gcevents.h | 6 +- src/coreclr/gc/gcimpl.h | 2 + src/coreclr/gc/gcinterface.ee.h | 36 +- src/coreclr/gc/gcinterface.h | 19 + src/coreclr/gc/gcpriv.h | 164 +++- src/coreclr/gc/gcscan.cpp | 8 +- src/coreclr/gc/gcscan.h | 7 +- src/coreclr/gc/objecthandle.cpp | 4 +- src/coreclr/gc/objecthandle.h | 2 +- src/coreclr/inc/eventtrace.h | 65 -- src/coreclr/inc/eventtracebase.h | 10 + src/coreclr/vm/ClrEtwAll.man | 243 ++++- src/coreclr/vm/eventtrace.cpp | 58 +- src/coreclr/vm/gcenv.h | 6 +- src/coreclr/vm/gctoclreventsink.cpp | 52 +- src/coreclr/vm/gctoclreventsink.h | 16 +- 22 files changed, 1510 insertions(+), 549 deletions(-) diff --git a/src/coreclr/gc/env/etmdummy.h b/src/coreclr/gc/env/etmdummy.h index 0605f03a98e..55637212757 100644 --- a/src/coreclr/gc/env/etmdummy.h +++ b/src/coreclr/gc/env/etmdummy.h @@ -25,6 +25,7 @@ #define FireEtwGCAllocationTick_V1(AllocationAmount, AllocationKind, ClrInstanceID) 0 #define FireEtwGCAllocationTick_V2(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex) 0 #define FireEtwGCAllocationTick_V3(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex, Address) 0 +#define FireEtwGCAllocationTick_V4(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex, Address, LastAllocationAmount) 0 #define FireEtwGCCreateConcurrentThread() 0 #define FireEtwGCCreateConcurrentThread_V1(ClrInstanceID) 0 #define FireEtwGCTerminateConcurrentThread() 0 @@ -164,8 +165,11 @@ #define FireEtwGCMarkWithType(HeapNum, ClrInstanceID, Type, Bytes) 0 #define FireEtwGCJoin_V2(Heap, JoinTime, JoinType, ClrInstanceID, JoinID) 0 #define FireEtwGCPerHeapHistory_V3(ClrInstanceID, FreeListAllocated, FreeListRejected, EndOfSegAllocated, CondemnedAllocated, PinnedAllocated, PinnedAllocatedAdvance, RunningFreeListEfficiency, CondemnReasons0, CondemnReasons1, CompactMechanisms, ExpandMechanisms, HeapIndex, ExtraGen0Commit, Count, Values_Len_, Values) 0 +#define FireEtwGCLOHCompact(ClrInstanceID, Count, Values_Len_, Values) 0 +#define FireEtwGCFitBucketInfo(ClrInstanceID, BucketKind, TotalSize, Count, Values_Len_, Values) 0 #define FireEtwGCGlobalHeapHistory_V2(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure) 0 #define FireEtwGCGlobalHeapHistory_V3(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure, CondemnReasons0, CondemnReasons1) 0 +#define FireEtwGCGlobalHeapHistory_V4(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure, CondemnReasons0, CondemnReasons1, Count, Values_Len_, Values) 0 #define FireEtwDebugIPCEventStart() 0 #define FireEtwDebugIPCEventEnd() 0 #define FireEtwDebugExceptionProcessingStart() 0 diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index 59eb74862f5..5cb2f4178ce 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -529,7 +529,11 @@ namespace ETW GC_ROOT_HANDLES = 2, GC_ROOT_OLDER = 3, GC_ROOT_SIZEDREF = 4, - GC_ROOT_OVERFLOW = 5 + GC_ROOT_OVERFLOW = 5, + GC_ROOT_DH_HANDLES = 6, + GC_ROOT_NEW_FQ = 7, + GC_ROOT_STEAL = 8, + GC_ROOT_BGC = 9 } GC_ROOT_KIND; }; diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index eb0d85583a8..c34bc7b2fbd 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -183,6 +183,19 @@ static const char* const str_gc_pause_modes[] = "sustained_low_latency", "no_gc" }; + +static const char* const str_root_kinds[] = { + "Stack", + "FinalizeQueue", + "Handles", + "OlderGen", + "SizedRef", + "Overflow", + "DependentHandles", + "NewFQ", + "Steal", + "BGC" +}; #endif //DT_LOG || TRACE_GC inline @@ -2201,8 +2214,34 @@ size_t gc_heap::committed_by_oh[total_oh_count] = {0, 0, 0, 0}; size_t gc_heap::current_total_committed_bookkeeping = 0; +#ifdef FEATURE_EVENT_TRACE +bool gc_heap::informational_event_enabled_p = false; + +uint64_t* gc_heap::gc_time_info = 0; + +#ifdef BACKGROUND_GC +uint64_t* gc_heap::bgc_time_info = 0; +#endif //BACKGROUND_GC + +size_t gc_heap::physical_memory_from_config = 0; + +size_t gc_heap::gen0_min_budget_from_config = 0; + +size_t gc_heap::gen0_max_budget_from_config = 0; + +int gc_heap::high_mem_percent_from_config = 0; + +bool gc_heap::use_frozen_segments_p = false; + +bool gc_heap::hard_limit_config_p = false; + +#ifdef FEATURE_LOH_COMPACTION +gc_heap::etw_loh_compact_info* gc_heap::loh_compact_info; +#endif //FEATURE_LOH_COMPACTION +#endif //FEATURE_EVENT_TRACE + #ifdef SHORT_PLUGS -double gc_heap::short_plugs_pad_ratio = 0; +double gc_heap::short_plugs_pad_ratio = 0; #endif //SHORT_PLUGS int gc_heap::generation_skip_ratio_threshold = 0; @@ -2340,7 +2379,7 @@ oom_history gc_heap::oomhist_per_heap[max_oom_history_count]; fgm_history gc_heap::fgm_result; -size_t gc_heap::allocated_since_last_gc[2]; +size_t gc_heap::allocated_since_last_gc[gc_oh_num::total_oh_count - 1]; BOOL gc_heap::ro_segments_in_range; @@ -2382,7 +2421,7 @@ uint8_t* gc_heap::last_gen1_pin_end; gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons; -size_t gc_heap::etw_allocation_running_amount[2]; +size_t gc_heap::etw_allocation_running_amount[gc_oh_num::total_oh_count - 1]; uint64_t gc_heap::total_alloc_bytes_soh = 0; @@ -2699,6 +2738,10 @@ size_t gc_heap::gen2_removed_no_undo = 0; size_t gc_heap::saved_pinned_plug_index = 0; #endif //DOUBLY_LINKED_FL +#ifdef FEATURE_EVENT_TRACE +etw_bucket_info gc_heap::bucket_info[NUM_GEN2_ALIST]; +#endif //FEATURE_EVENT_TRACE + dynamic_data gc_heap::dynamic_data_table [total_generation_count]; gc_history_per_heap gc_heap::gc_data_per_heap; size_t gc_heap::total_promoted_bytes = 0; @@ -2911,6 +2954,12 @@ void gc_history_global::print() #endif //DT_LOG } +uint32_t limit_time_to_uint32 (uint64_t time) +{ + time = min (time, UINT32_MAX); + return (uint32_t)time; +} + void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num) { maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info); @@ -2943,7 +2992,22 @@ void gc_heap::fire_pevents() settings.record (current_gc_data_global); current_gc_data_global->print(); - FIRE_EVENT(GCGlobalHeapHistory_V3, +#ifdef FEATURE_EVENT_TRACE + if (!informational_event_enabled_p) return; + + uint32_t count_time_info = (settings.concurrent ? max_bgc_time_type : + (settings.compaction ? max_compact_time_type : max_sweep_time_type)); + + uint64_t* time_info = (settings.concurrent ? bgc_time_info : gc_time_info); + // We don't want to have to fire the time info as 64-bit integers as there's no need to + // so compress them down to 32-bit ones. + uint32_t* time_info_32 = (uint32_t*)time_info; + for (uint32_t i = 0; i < count_time_info; i++) + { + time_info_32[i] = limit_time_to_uint32 (time_info[i]); + } + + FIRE_EVENT(GCGlobalHeapHistory_V4, current_gc_data_global->final_youngest_desired, current_gc_data_global->num_heaps, current_gc_data_global->condemned_generation, @@ -2953,7 +3017,10 @@ void gc_heap::fire_pevents() current_gc_data_global->pause_mode, current_gc_data_global->mem_pressure, current_gc_data_global->gen_to_condemn_reasons.get_reasons0(), - current_gc_data_global->gen_to_condemn_reasons.get_reasons1()); + current_gc_data_global->gen_to_condemn_reasons.get_reasons1(), + count_time_info, + (uint32_t)(sizeof (uint32_t)), + (void*)time_info_32); #ifdef MULTIPLE_HEAPS for (int i = 0; i < gc_heap::n_heaps; i++) @@ -2965,7 +3032,20 @@ void gc_heap::fire_pevents() #else gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); fire_per_heap_hist_event (current_gc_data_per_heap, heap_number); -#endif +#endif //MULTIPLE_HEAPS + +#ifdef FEATURE_LOH_COMPACTION + if (!settings.concurrent && settings.loh_compaction) + { + // Not every heap will compact LOH, the ones that didn't will just have 0s + // in its info. + FIRE_EVENT(GCLOHCompact, + get_num_heaps(), + (uint32_t)(sizeof (etw_loh_compact_info)), + (void *)loh_compact_info); + } +#endif //FEATURE_LOH_COMPACTION +#endif //FEATURE_EVENT_TRACE } inline BOOL @@ -9037,6 +9117,11 @@ void gc_heap::copy_brick_card_table() #ifdef FEATURE_BASICFREEZE BOOL gc_heap::insert_ro_segment (heap_segment* seg) { +#ifdef FEATURE_EVENT_TRACE + if (!use_frozen_segments_p) + use_frozen_segments_p = true; +#endif //FEATURE_EVENT_TRACE + enter_spin_lock (&gc_heap::gc_lock); if (!gc_heap::seg_table->ensure_space_for_insert () @@ -12353,6 +12438,29 @@ gc_heap::init_semi_shared() generation_skip_ratio_threshold = (int)GCConfig::GetGCLowSkipRatio(); +#ifdef FEATURE_EVENT_TRACE + gc_time_info = new (nothrow) uint64_t[max_compact_time_type]; + if (!gc_time_info) + { + goto cleanup; + } +#ifdef BACKGROUND_GC + bgc_time_info = new (nothrow) uint64_t[max_bgc_time_type]; + if (!bgc_time_info) + { + goto cleanup; + } +#endif //BACKGROUND_GC + +#ifdef FEATURE_LOH_COMPACTION + loh_compact_info = new (nothrow) etw_loh_compact_info [get_num_heaps()]; + if (!loh_compact_info) + { + goto cleanup; + } +#endif //FEATURE_LOH_COMPACTION +#endif //FEATURE_EVENT_TRACE + ret = 1; cleanup: @@ -12567,8 +12675,8 @@ gc_heap::init_gc_heap (int h_number) time_bgc_last = 0; - allocated_since_last_gc[0] = 0; - allocated_since_last_gc[1] = 0; + for (int oh_index = 0; oh_index < (gc_oh_num::total_oh_count - 1); oh_index++) + allocated_since_last_gc[oh_index] = 0; #ifdef SPINLOCK_HISTORY spinlock_info_index = 0; @@ -12667,6 +12775,7 @@ gc_heap::init_gc_heap (int h_number) memset (&oom_info, 0, sizeof (oom_info)); memset (&fgm_result, 0, sizeof (fgm_result)); + memset (oomhist_per_heap, 0, sizeof (oomhist_per_heap)); if (!gc_done_event.CreateManualEventNoThrow(FALSE)) { return 0; @@ -12825,8 +12934,9 @@ gc_heap::init_gc_heap (int h_number) generation_of (loh_generation)->free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST_BITS, loh_alloc_list); generation_of (poh_generation)->free_list_allocator = allocator(NUM_POH_ALIST, BASE_POH_ALIST_BITS, poh_alloc_list); - etw_allocation_running_amount[0] = 0; - etw_allocation_running_amount[1] = 0; + for (int oh_index = 0; oh_index < (gc_oh_num::total_oh_count - 1); oh_index++) + etw_allocation_running_amount[oh_index] = 0; + total_alloc_bytes_soh = 0; total_alloc_bytes_uoh = 0; @@ -14114,6 +14224,53 @@ void allocator::thread_sip_fl (heap_segment* region) } #endif //USE_REGIONS +#ifdef FEATURE_EVENT_TRACE +uint16_t allocator::count_largest_items (etw_bucket_info* bucket_info, + size_t max_size, + size_t max_item_count, + size_t* recorded_fl_info_size) +{ + assert (gen_number == max_generation); + + size_t size_counted_total = 0; + size_t items_counted_total = 0; + uint16_t bucket_info_index = 0; + for (int i = (num_buckets - 1); i >= 0; i--) + { + uint32_t items_counted = 0; + size_t size_counted = 0; + uint8_t* free_item = alloc_list_head_of ((unsigned int)i); + while (free_item) + { + assert (((CObjectHeader*)free_item)->IsFree()); + + size_t free_item_size = Align (size (free_item)); + size_counted_total += free_item_size; + size_counted += free_item_size; + items_counted_total++; + items_counted++; + + if ((size_counted_total > max_size) || (items_counted > max_item_count)) + { + bucket_info[bucket_info_index++].set ((uint16_t)i, items_counted, size_counted); + *recorded_fl_info_size = size_counted_total; + return bucket_info_index; + } + + free_item = free_list_slot (free_item); + } + + if (items_counted) + { + bucket_info[bucket_info_index++].set ((uint16_t)i, items_counted, size_counted); + } + } + + *recorded_fl_info_size = size_counted_total; + return bucket_info_index; +} +#endif //FEATURE_EVENT_TRACE + void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, alloc_context* acontext, uint32_t flags, heap_segment* seg, int align_const, int gen_number) @@ -14149,7 +14306,7 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, if (hole != 0) { size_t ac_size = (acontext->alloc_limit - acontext->alloc_ptr); - dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + ac_size + Align (min_obj_size, align_const))); + dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + ac_size + aligned_min_obj_size)); // when we are finishing an allocation from a free list // we know that the free area was Align(min_obj_size) larger acontext->alloc_bytes -= ac_size; @@ -14172,7 +14329,7 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, else #endif //USE_REGIONS { - size_t pad_size = Align (min_obj_size, align_const); + size_t pad_size = aligned_min_obj_size; dprintf (3, ("contigous ac: making min obj gap %Ix->%Ix(%Id)", acontext->alloc_ptr, (acontext->alloc_ptr + pad_size), pad_size)); make_unused_array (acontext->alloc_ptr, pad_size); @@ -14185,6 +14342,9 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, acontext->alloc_bytes += added_bytes; total_alloc_bytes += added_bytes; + size_t etw_allocation_amount = 0; + bool fire_event_p = update_alloc_info (gen_number, added_bytes, &etw_allocation_amount); + uint8_t* saved_used = 0; if (seg) @@ -14276,6 +14436,13 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size, } } +#ifdef FEATURE_EVENT_TRACE + if (fire_event_p) + { + fire_etw_allocation_event (etw_allocation_amount, gen_number, acontext->alloc_ptr, size); + } +#endif //FEATURE_EVENT_TRACE + //this portion can be done after we release the lock if (seg == ephemeral_heap_segment || ((seg == nullptr) && (gen_number == 0) && (limit_size >= CLR_SIZE / 2))) @@ -14744,6 +14911,7 @@ void gc_heap::bgc_uoh_alloc_clr (uint8_t* alloc_start, size_t size, alloc_context* acontext, uint32_t flags, + int gen_number, int align_const, int lock_index, BOOL check_used_p, @@ -14800,12 +14968,22 @@ void gc_heap::bgc_uoh_alloc_clr (uint8_t* alloc_start, } #endif //VERIFY_HEAP - total_alloc_bytes_uoh += size - Align (min_obj_size, align_const); + size_t allocated_size = size - Align (min_obj_size, align_const); + total_alloc_bytes_uoh += allocated_size; + size_t etw_allocation_amount = 0; + bool fire_event_p = update_alloc_info (gen_number, allocated_size, &etw_allocation_amount); dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear uoh obj", heap_number)); add_saved_spinlock_info (true, me_release, mt_clr_large_mem); leave_spin_lock (&more_space_lock_uoh); +#ifdef FEATURE_EVENT_TRACE + if (fire_event_p) + { + fire_etw_allocation_event (etw_allocation_amount, gen_number, alloc_start, size); + } +#endif //FEATURE_EVENT_TRACE + ((void**) alloc_start)[-1] = 0; //clear the sync block if (!(flags & GC_ALLOC_ZEROING_OPTIONAL)) { @@ -14905,7 +15083,7 @@ BOOL gc_heap::a_fit_free_list_uoh_p (size_t size, #ifdef BACKGROUND_GC if (cookie != -1) { - bgc_uoh_alloc_clr (free_list, limit, acontext, flags, align_const, cookie, FALSE, 0); + bgc_uoh_alloc_clr (free_list, limit, acontext, flags, gen_number, align_const, cookie, FALSE, 0); } else #endif //BACKGROUND_GC @@ -15037,7 +15215,7 @@ found_fit: if (cookie != -1) { allocated += limit; - bgc_uoh_alloc_clr (old_alloc, limit, acontext, flags, align_const, cookie, TRUE, seg); + bgc_uoh_alloc_clr (old_alloc, limit, acontext, flags, gen_number, align_const, cookie, TRUE, seg); } else #endif //BACKGROUND_GC @@ -16133,6 +16311,25 @@ void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr, #endif //BACKGROUND_GC } +inline +bool gc_heap::update_alloc_info (int gen_number, size_t allocated_size, size_t* etw_allocation_amount) +{ + bool exceeded_p = false; + int oh_index = gen_to_oh (gen_number); + allocated_since_last_gc[oh_index] += allocated_size; + + size_t& etw_allocated = etw_allocation_running_amount[oh_index]; + etw_allocated += allocated_size; + if (etw_allocated > etw_allocation_tick) + { + *etw_allocation_amount = etw_allocated; + exceeded_p = true; + etw_allocated = 0; + } + + return exceeded_p; +} + allocation_state gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size, uint32_t flags, int gen_number) { @@ -16247,38 +16444,6 @@ allocation_state gc_heap::try_allocate_more_space (alloc_context* acontext, size allocate_soh (gen_number, size, acontext, flags, align_const) : allocate_uoh (gen_number, size, acontext, flags, align_const)); - if (can_allocate == a_state_can_allocate) - { - size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr; - int etw_allocation_index = ((gen_number == 0) ? 0 : 1); - - etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes; - - allocated_since_last_gc[etw_allocation_index] += alloc_context_bytes; - - if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick) - { -#ifdef FEATURE_REDHAWK - FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index], - (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh); -#else - -#if defined(FEATURE_EVENT_TRACE) - // We are explicitly checking whether the event is enabled here. - // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled. - // The ones that do are much less efficient. - if (EVENT_ENABLED(GCAllocationTick_V3)) - { - fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], - gen_number, acontext->alloc_ptr); - } - -#endif //FEATURE_EVENT_TRACE -#endif //FEATURE_REDHAWK - etw_allocation_running_amount[etw_allocation_index] = 0; - } - } - return can_allocate; } @@ -23277,12 +23442,6 @@ void gc_heap::get_memory_info (uint32_t* memory_load, GCToOSInterface::GetMemoryStatus(is_restricted_physical_mem ? total_physical_mem : 0, memory_load, available_physical, available_page_file); } -void fire_mark_event (int heap_num, int root_type, size_t bytes_marked) -{ - dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked)); - FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked); -} - //returns TRUE is an overflow happened. BOOL gc_heap::process_mark_overflow(int condemned_gen_number) { @@ -23326,9 +23485,8 @@ recheck: } size_t current_promoted_bytes = get_promoted_bytes(); - if (current_promoted_bytes != last_promoted_bytes) - fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes)); + fire_mark_event (ETW::GC_ROOT_OVERFLOW, current_promoted_bytes, last_promoted_bytes); return overflow_p; } @@ -23611,6 +23769,40 @@ BOOL gc_heap::decide_on_promotion_surv (size_t threshold) return FALSE; } +inline +void gc_heap::fire_mark_event (int root_type, size_t& current_promoted_bytes, size_t& last_promoted_bytes) +{ +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + current_promoted_bytes = get_promoted_bytes(); + size_t root_promoted = current_promoted_bytes - last_promoted_bytes; + dprintf (3, ("h%d marked root %s: %Id (%Id - %Id)", + heap_number, str_root_kinds[root_type], root_promoted, + current_promoted_bytes, last_promoted_bytes)); + FIRE_EVENT(GCMarkWithType, heap_number, root_type, root_promoted); + last_promoted_bytes = current_promoted_bytes; + } +#endif // FEATURE_EVENT_TRACE +} + +#ifdef FEATURE_EVENT_TRACE +inline +void gc_heap::record_mark_time (uint64_t& mark_time, + uint64_t& current_mark_time, + uint64_t& last_mark_time) +{ + if (informational_event_enabled_p) + { + current_mark_time = GetHighPrecisionTimeStamp(); + mark_time = limit_time_to_uint32 (current_mark_time - last_mark_time); + dprintf (3, ("%I64d - %I64d = %I64d", + current_mark_time, last_mark_time, (current_mark_time - last_mark_time))); + last_mark_time = current_mark_time; + } +} +#endif // FEATURE_EVENT_TRACE + void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) { assert (settings.concurrent == FALSE); @@ -23728,6 +23920,11 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) } #endif //STRESS_REGIONS +#ifdef FEATURE_EVENT_TRACE + static uint64_t current_mark_time = 0; + static uint64_t last_mark_time = 0; +#endif //FEATURE_EVENT_TRACE + #ifdef MULTIPLE_HEAPS gc_t_join.join(this, gc_join_begin_mark_phase); if (gc_t_join.joined()) @@ -23743,6 +23940,16 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) GCToEEInterface::BeforeGcScanRoots(condemned_gen_number, /* is_bgc */ false, /* is_concurrent */ false); num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles(); +#ifdef FEATURE_EVENT_TRACE + informational_event_enabled_p = EVENT_ENABLED (GCMarkWithType); + if (informational_event_enabled_p) + { + last_mark_time = GetHighPrecisionTimeStamp(); + // We may not have SizedRefs to mark so init it to 0. + gc_time_info[time_mark_sizedref] = 0; + } +#endif //FEATURE_EVENT_TRACE + #ifdef MULTIPLE_HEAPS #ifdef MH_SC_MARK if (full_p) @@ -23820,19 +24027,22 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0)) { GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc); - size_t current_promoted_bytes = get_promoted_bytes(); - fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, - (current_promoted_bytes - last_promoted_bytes)); - last_promoted_bytes = current_promoted_bytes; + fire_mark_event (ETW::GC_ROOT_SIZEDREF, current_promoted_bytes, last_promoted_bytes); #ifdef MULTIPLE_HEAPS gc_t_join.join(this, gc_join_scan_sizedref_done); if (gc_t_join.joined()) +#endif //MULTIPLE_HEAPS { +#ifdef FEATURE_EVENT_TRACE + record_mark_time (gc_time_info[time_mark_sizedref], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots")); gc_t_join.restart(); - } #endif //MULTIPLE_HEAPS + } } dprintf(3,("Marking Roots")); @@ -23840,35 +24050,27 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) GCScan::GcScanRoots(GCHeap::Promote, condemned_gen_number, max_generation, &sc); - - current_promoted_bytes = get_promoted_bytes(); - fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (current_promoted_bytes - last_promoted_bytes)); - last_promoted_bytes = current_promoted_bytes; + fire_mark_event (ETW::GC_ROOT_STACK, current_promoted_bytes, last_promoted_bytes); #ifdef BACKGROUND_GC if (gc_heap::background_running_p()) { scan_background_roots (GCHeap::Promote, heap_number, &sc); + fire_mark_event (ETW::GC_ROOT_BGC, current_promoted_bytes, last_promoted_bytes); } #endif //BACKGROUND_GC #ifdef FEATURE_PREMORTEM_FINALIZATION dprintf(3, ("Marking finalization data")); finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0); + fire_mark_event (ETW::GC_ROOT_FQ, current_promoted_bytes, last_promoted_bytes); #endif // FEATURE_PREMORTEM_FINALIZATION - current_promoted_bytes = get_promoted_bytes(); - fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (current_promoted_bytes - last_promoted_bytes)); - last_promoted_bytes = current_promoted_bytes; - dprintf(3,("Marking handle table")); GCScan::GcScanHandles(GCHeap::Promote, condemned_gen_number, max_generation, &sc); - - current_promoted_bytes = get_promoted_bytes(); - fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (current_promoted_bytes - last_promoted_bytes)); - last_promoted_bytes = current_promoted_bytes; + fire_mark_event (ETW::GC_ROOT_HANDLES, current_promoted_bytes, last_promoted_bytes); if (!full_p) { @@ -23979,11 +24181,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) update_old_card_survived(); #endif //USE_REGIONS - current_promoted_bytes = get_promoted_bytes(); - dprintf (3, ("before cards %Id, marked by cards: %Id", - last_promoted_bytes, (current_promoted_bytes - last_promoted_bytes))); - fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (current_promoted_bytes - last_promoted_bytes)); - last_promoted_bytes = current_promoted_bytes; + fire_mark_event (ETW::GC_ROOT_OLDER, current_promoted_bytes, last_promoted_bytes); } } @@ -23991,6 +24189,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) if (do_mark_steal_p) { mark_steal(); + fire_mark_event (ETW::GC_ROOT_STEAL, current_promoted_bytes, last_promoted_bytes); } #endif //MH_SC_MARK @@ -24003,6 +24202,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) // handle table has been fully promoted. GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc); scan_dependent_handles(condemned_gen_number, &sc, true); + fire_mark_event (ETW::GC_ROOT_DH_HANDLES, current_promoted_bytes, last_promoted_bytes); #ifdef MULTIPLE_HEAPS dprintf(3, ("Joining for short weak handle scan")); @@ -24010,6 +24210,10 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) if (gc_t_join.joined()) #endif //MULTIPLE_HEAPS { +#ifdef FEATURE_EVENT_TRACE + record_mark_time (gc_time_info[time_mark_roots], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + uint64_t promoted_bytes_global = 0; #ifdef HEAP_ANALYZE heap_analyze_enabled = FALSE; @@ -24055,13 +24259,20 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) #endif // FEATURE_CARD_MARKING_STEALING // null out the target of short weakref that were not promoted. - GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc); + GCScan::GcShortWeakPtrScan (condemned_gen_number, max_generation,&sc); #ifdef MULTIPLE_HEAPS dprintf(3, ("Joining for finalization")); gc_t_join.join(this, gc_join_scan_finalization); if (gc_t_join.joined()) { +#endif //MULTIPLE_HEAPS + +#ifdef FEATURE_EVENT_TRACE + record_mark_time (gc_time_info[time_mark_short_weak], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS dprintf(3, ("Starting all gc thread for Finalization")); gc_t_join.restart(); } @@ -24073,13 +24284,14 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) #ifdef FEATURE_PREMORTEM_FINALIZATION dprintf (3, ("Finalize marking")); finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this); - + fire_mark_event (ETW::GC_ROOT_NEW_FQ, current_promoted_bytes, last_promoted_bytes); GCToEEInterface::DiagWalkFReachableObjects(__this); -#endif // FEATURE_PREMORTEM_FINALIZATION // Scan dependent handles again to promote any secondaries associated with primaries that were promoted // for finalization. As before scan_dependent_handles will also process any mark stack overflow. scan_dependent_handles(condemned_gen_number, &sc, false); + fire_mark_event (ETW::GC_ROOT_DH_HANDLES, current_promoted_bytes, last_promoted_bytes); +#endif //FEATURE_PREMORTEM_FINALIZATION total_promoted_bytes = get_promoted_bytes(); @@ -24092,6 +24304,10 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) dprintf(3, ("Starting all gc thread for weak pointer deletion")); #endif //MULTIPLE_HEAPS +#ifdef FEATURE_EVENT_TRACE + record_mark_time (gc_time_info[time_mark_scan_finalization], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + #ifdef USE_REGIONS sync_promoted_bytes(); #endif //USE_REGIONS @@ -24103,7 +24319,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) #endif //MULTIPLE_HEAPS // null out the target of long weakref that were not promoted. - GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc); + GCScan::GcWeakPtrScan (condemned_gen_number, max_generation, &sc); #ifdef MULTIPLE_HEAPS size_t total_mark_list_size = sort_mark_list(); @@ -24121,19 +24337,28 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) if (gc_t_join.joined()) #endif //MULTIPLE_HEAPS { -#ifdef MULTIPLE_HEAPS +#ifdef FEATURE_EVENT_TRACE + record_mark_time (gc_time_info[time_plan - 1], current_mark_time, last_mark_time); + gc_time_info[time_plan] = last_mark_time; +#endif //FEATURE_EVENT_TRACE + //decide on promotion if (!settings.promotion) { size_t m = 0; for (int n = 0; n <= condemned_gen_number;n++) { +#ifdef MULTIPLE_HEAPS m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1); +#else + m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06); +#endif //MULTIPLE_HEAPS } settings.promotion = decide_on_promotion_surv (m); } +#ifdef MULTIPLE_HEAPS #ifdef SNOOP_STATS if (do_mark_steal_p) { @@ -24191,19 +24416,6 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) dprintf(3, ("Starting all threads for end of mark phase")); gc_t_join.restart(); -#else //MULTIPLE_HEAPS - - //decide on promotion - if (!settings.promotion) - { - size_t m = 0; - for (int n = 0; n <= condemned_gen_number;n++) - { - m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06); - } - - settings.promotion = decide_on_promotion_surv (m); - } #endif //MULTIPLE_HEAPS } @@ -24247,6 +24459,18 @@ size_t gc_heap::get_total_pinned_objects() #endif //MULTIPLE_HEAPS } +void gc_heap::reinit_pinned_objects() +{ +#ifdef MULTIPLE_HEAPS + for (int i = 0; i < gc_heap::n_heaps; i++) + { + gc_heap::g_heaps[i]->num_pinned_objects = 0; + } +#else //MULTIPLE_HEAPS + num_pinned_objects = 0; +#endif //MULTIPLE_HEAPS +} + void gc_heap::reset_mark_stack () { reset_pinned_queue(); @@ -25133,6 +25357,15 @@ void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p) BOOL gc_heap::plan_loh() { +#ifdef FEATURE_EVENT_TRACE + uint64_t start_time, end_time; + if (informational_event_enabled_p) + { + memset (loh_compact_info, 0, (sizeof (etw_loh_compact_info) * get_num_heaps())); + start_time = GetHighPrecisionTimeStamp(); + } +#endif //FEATURE_EVENT_TRACE + if (!loh_pinned_queue) { loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]); @@ -25277,6 +25510,14 @@ BOOL gc_heap::plan_loh() generation_allocation_pointer (gen) = 0; generation_allocation_limit (gen) = 0; +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + end_time = GetHighPrecisionTimeStamp(); + loh_compact_info[heap_number].time_plan = limit_time_to_uint32 (end_time - start_time); + } +#endif //FEATURE_EVENT_TRACE + return TRUE; } @@ -25284,6 +25525,14 @@ void gc_heap::compact_loh() { assert (loh_compaction_requested() || heap_hard_limit); +#ifdef FEATURE_EVENT_TRACE + uint64_t start_time, end_time; + if (informational_event_enabled_p) + { + start_time = GetHighPrecisionTimeStamp(); + } +#endif //FEATURE_EVENT_TRACE + generation* gen = large_object_generation; heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen)); PREFIX_ASSUME(start_seg != NULL); @@ -25402,6 +25651,14 @@ void gc_heap::compact_loh() } } +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + end_time = GetHighPrecisionTimeStamp(); + loh_compact_info[heap_number].time_compact = limit_time_to_uint32 (end_time - start_time); + } +#endif //FEATURE_EVENT_TRACE + assert (loh_pinned_plug_que_empty_p()); dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", @@ -25410,12 +25667,35 @@ void gc_heap::compact_loh() generation_free_obj_space (gen))); } +#ifdef FEATURE_EVENT_TRACE +inline +void gc_heap::loh_reloc_survivor_helper (uint8_t** pval, size_t& total_refs, size_t& zero_refs) +{ + uint8_t* val = *pval; + if (!val) + zero_refs++; + total_refs++; + + reloc_survivor_helper (pval); +} +#endif //FEATURE_EVENT_TRACE + void gc_heap::relocate_in_loh_compact() { generation* gen = large_object_generation; heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); uint8_t* o = get_uoh_start_object (seg, gen); +#ifdef FEATURE_EVENT_TRACE + size_t total_refs = 0; + size_t zero_refs = 0; + uint64_t start_time, end_time; + if (informational_event_enabled_p) + { + start_time = GetHighPrecisionTimeStamp(); + } +#endif //FEATURE_EVENT_TRACE + while (1) { if (o >= heap_segment_allocated (seg)) @@ -25436,12 +25716,23 @@ void gc_heap::relocate_in_loh_compact() check_class_object_demotion (o); if (contain_pointers (o)) { - go_through_object_nostart (method_table (o), o, size(o), pval, +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) { - reloc_survivor_helper (pval); - }); + go_through_object_nostart (method_table (o), o, size(o), pval, + { + loh_reloc_survivor_helper (pval, total_refs, zero_refs); + }); + } + else +#endif //FEATURE_EVENT_TRACE + { + go_through_object_nostart (method_table (o), o, size(o), pval, + { + reloc_survivor_helper (pval); + }); + } } - o = o + size; if (o < heap_segment_allocated (seg)) { @@ -25457,6 +25748,16 @@ void gc_heap::relocate_in_loh_compact() } } +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + end_time = GetHighPrecisionTimeStamp(); + loh_compact_info[heap_number].time_relocate = limit_time_to_uint32 (end_time - start_time); + loh_compact_info[heap_number].total_refs = total_refs; + loh_compact_info[heap_number].zero_refs = zero_refs; + } +#endif //FEATURE_EVENT_TRACE + dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", generation_size (loh_generation), generation_free_list_space (gen), @@ -26111,6 +26412,20 @@ uint8_t* gc_heap::find_next_marked (uint8_t* x, uint8_t* end, return x; } +#ifdef FEATURE_EVENT_TRACE +void gc_heap::init_bucket_info() +{ + memset (bucket_info, 0, sizeof (bucket_info)); +} + +void gc_heap::add_plug_in_condemned_info (generation* gen, size_t plug_size) +{ + uint32_t bucket_index = generation_allocator (gen)->first_suitable_bucket (plug_size); + (bucket_info[bucket_index].count)++; + bucket_info[bucket_index].size += plug_size; +} +#endif //FEATURE_EVENT_TRACE + #ifdef _PREFAST_ #pragma warning(push) #pragma warning(disable:21000) // Suppress PREFast warning about overly large function @@ -26528,7 +26843,16 @@ void gc_heap::plan_phase (int condemned_gen_number) } #endif //!USE_REGIONS - BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime); +#ifdef FEATURE_EVENT_TRACE + // When verbose level is enabled we want to record some info about gen2 FL usage during gen1 GCs. + // We record the bucket info for the largest FL items and plugs that we have to allocate in condemned. + bool record_fl_info_p = (EVENT_ENABLED (GCFitBucketInfo) && (condemned_gen_number == (max_generation - 1))); + size_t recorded_fl_info_size = 0; + if (record_fl_info_p) + init_bucket_info(); + bool fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime); +#endif //FEATURE_EVENT_TRACE + size_t last_plug_len = 0; #ifdef DOUBLY_LINKED_FL @@ -26892,6 +27216,14 @@ void gc_heap::plan_phase (int condemned_gen_number) } } +#ifdef FEATURE_EVENT_TRACE + if (record_fl_info_p && !allocated_in_older_p) + { + add_plug_in_condemned_info (older_gen, ps); + recorded_fl_info_size += ps; + } +#endif //FEATURE_EVENT_TRACE + if (convert_to_pinned_p) { assert (last_npinned_plug_p != FALSE); @@ -26936,11 +27268,13 @@ void gc_heap::plan_phase (int condemned_gen_number) if (pinned_plug_p) { +#ifdef FEATURE_EVENT_TRACE if (fire_pinned_plug_events_p) { FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end, (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start))); } +#endif //FEATURE_EVENT_TRACE if (merge_with_last_pin_p) { @@ -27472,6 +27806,14 @@ void gc_heap::plan_phase (int condemned_gen_number) } } +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + gc_time_info[time_sweep] = GetHighPrecisionTimeStamp(); + gc_time_info[time_plan] = gc_time_info[time_sweep] - gc_time_info[time_plan]; + } +#endif //FEATURE_EVENT_TRACE + dprintf(3, ("Starting all gc threads after compaction decision")); gc_t_join.restart(); } @@ -27523,6 +27865,14 @@ void gc_heap::plan_phase (int condemned_gen_number) loh_alloc_since_cg = 0; } } + +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + gc_time_info[time_sweep] = GetHighPrecisionTimeStamp(); + gc_time_info[time_plan] = gc_time_info[time_sweep] - gc_time_info[time_plan]; + } +#endif //FEATURE_EVENT_TRACE #endif //MULTIPLE_HEAPS if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered) @@ -27589,6 +27939,65 @@ void gc_heap::plan_phase (int condemned_gen_number) // Fix the allocation area of the older generation fix_older_allocation_area (older_gen); + +#ifdef FEATURE_EVENT_TRACE + if (record_fl_info_p) + { + // For plugs allocated in condemned we kept track of each one but only fire the + // event for buckets with non zero items. + uint16_t non_zero_buckets = 0; + for (uint16_t bucket_index = 0; bucket_index < NUM_GEN2_ALIST; bucket_index++) + { + if (bucket_info[bucket_index].count != 0) + { + if (bucket_index != non_zero_buckets) + { + bucket_info[non_zero_buckets].set (bucket_index, + bucket_info[bucket_index].count, + bucket_info[bucket_index].size); + } + else + { + bucket_info[bucket_index].index = bucket_index; + } + non_zero_buckets++; + } + } + + if (non_zero_buckets) + { + FIRE_EVENT(GCFitBucketInfo, + (uint16_t)etw_bucket_kind::plugs_in_condemned, + recorded_fl_info_size, + non_zero_buckets, + (uint32_t)(sizeof (etw_bucket_info)), + (void *)bucket_info); + init_bucket_info(); + } + + // We want to get an idea of the sizes of free items in the top 25% of the free list + // for gen2 (to be accurate - we stop as soon as the size we count exceeds 25%. This + // is just so that if we have a really big free item we will still count that one). + // The idea is we want to see if they all in a few big ones or many smaller ones? + // To limit the amount of time we spend counting, we stop till we have counted the + // top percentage, or exceeded max_etw_item_count items. + size_t max_size_to_count = generation_free_list_space (older_gen) / 4; + non_zero_buckets = + generation_allocator (older_gen)->count_largest_items (bucket_info, + max_size_to_count, + max_etw_item_count, + &recorded_fl_info_size); + if (non_zero_buckets) + { + FIRE_EVENT(GCFitBucketInfo, + (uint16_t)etw_bucket_kind::largest_fl_items, + recorded_fl_info_size, + non_zero_buckets, + (uint32_t)(sizeof (etw_bucket_info)), + (void *)bucket_info); + } + } +#endif //FEATURE_EVENT_TRACE } #ifndef USE_REGIONS assert (generation_allocation_segment (consing_gen) == @@ -27615,17 +28024,30 @@ void gc_heap::plan_phase (int condemned_gen_number) //must serialize on deleting segments gc_t_join.join(this, gc_join_rearrange_segs_compaction); if (gc_t_join.joined()) +#endif //MULTIPLE_HEAPS { +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + uint64_t current_time = GetHighPrecisionTimeStamp(); + gc_time_info[time_compact] = current_time - gc_time_info[time_compact]; + } +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS for (int i = 0; i < n_heaps; i++) { g_heaps[i]->rearrange_heap_segments(TRUE); } - gc_t_join.restart(); - } -#else - rearrange_heap_segments(TRUE); +#else //MULTIPLE_HEAPS + rearrange_heap_segments(TRUE); #endif //MULTIPLE_HEAPS +#ifdef MULTIPLE_HEAPS + gc_t_join.restart(); +#endif //MULTIPLE_HEAPS + } + if (should_expand) { //fix the start_segment for the ephemeral generations @@ -27650,6 +28072,17 @@ void gc_heap::plan_phase (int condemned_gen_number) gc_t_join.join(this, gc_join_adjust_handle_age_compact); if (gc_t_join.joined()) { +#endif //MULTIPLE_HEAPS + +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p && (condemned_gen_number < (max_generation -1))) + { + uint64_t current_time = GetHighPrecisionTimeStamp(); + gc_time_info[time_compact] = current_time - gc_time_info[time_compact]; + } +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS //join all threads to make sure they are synchronized dprintf(3, ("Restarting after Promotion granted")); gc_t_join.restart(); @@ -27848,6 +28281,14 @@ void gc_heap::plan_phase (int condemned_gen_number) if (gc_t_join.joined()) #endif //MULTIPLE_HEAPS { +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + uint64_t current_time = GetHighPrecisionTimeStamp(); + gc_time_info[time_sweep] = current_time - gc_time_info[time_sweep]; + } +#endif //FEATURE_EVENT_TRACE + if (!special_sweep_p) { GCScan::GcPromotionsGranted(condemned_gen_number, @@ -30114,7 +30555,16 @@ void gc_heap::relocate_phase (int condemned_gen_number, gc_t_join.join(this, gc_join_begin_relocate_phase); if (gc_t_join.joined()) { +#endif //MULTIPLE_HEAPS +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + gc_time_info[time_relocate] = GetHighPrecisionTimeStamp(); + } +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS //join all threads to make sure they are synchronized dprintf(3, ("Restarting for relocation")); gc_t_join.restart(); @@ -30691,11 +31141,21 @@ void gc_heap::compact_phase (int condemned_gen_number, dprintf(3, ("Joining after end of relocation")); gc_t_join.join(this, gc_join_relocate_phase_done); if (gc_t_join.joined()) +#endif //MULTIPLE_HEAPS { +#ifdef FEATURE_EVENT_TRACE + if (informational_event_enabled_p) + { + gc_time_info[time_compact] = GetHighPrecisionTimeStamp(); + gc_time_info[time_relocate] = gc_time_info[time_compact] - gc_time_info[time_relocate]; + } +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS dprintf(3, ("Restarting for compaction")); gc_t_join.restart(); - } #endif //MULTIPLE_HEAPS + } dprintf (2, (ThreadStressLog::gcStartCompactMsg(), heap_number, first_condemned_address, brick_of (first_condemned_address))); @@ -31546,355 +32006,376 @@ void gc_heap::background_mark_phase () dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id, poh: %Id", heap_number, total_loh_size, total_soh_size, total_poh_size)); - { - //concurrent_print_time_delta ("copying stack roots"); - concurrent_print_time_delta ("CS"); + //concurrent_print_time_delta ("copying stack roots"); + concurrent_print_time_delta ("CS"); - FIRE_EVENT(BGC1stNonConEnd); + FIRE_EVENT(BGC1stNonConEnd); #ifndef USE_REGIONS - saved_overflow_ephemeral_seg = 0; + saved_overflow_ephemeral_seg = 0; #endif //!USE_REGIONS - current_bgc_state = bgc_reset_ww; + current_bgc_state = bgc_reset_ww; - // we don't need a join here - just whichever thread that gets here - // first can change the states and call restart_vm. - // this is not true - we can't let the EE run when we are scanning stack. - // since we now allow reset ww to run concurrently and have a join for it, - // we can do restart ee on the 1st thread that got here. Make sure we handle the - // sizedref handles correctly. + // we don't need a join here - just whichever thread that gets here + // first can change the states and call restart_vm. + // this is not true - we can't let the EE run when we are scanning stack. + // since we now allow reset ww to run concurrently and have a join for it, + // we can do restart ee on the 1st thread that got here. Make sure we handle the + // sizedref handles correctly. #ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_restart_ee); - if (bgc_t_join.joined()) + bgc_t_join.join(this, gc_join_restart_ee); + if (bgc_t_join.joined()) #endif //MULTIPLE_HEAPS - { + { #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset - // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while - // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below. - concurrent_print_time_delta ("CRWW begin"); + // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset + // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while + // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below. + concurrent_print_time_delta ("CRWW begin"); #ifdef MULTIPLE_HEAPS - for (int i = 0; i < n_heaps; i++) - { - g_heaps[i]->reset_write_watch (FALSE); - } + for (int i = 0; i < n_heaps; i++) + { + g_heaps[i]->reset_write_watch (FALSE); + } #else - reset_write_watch (FALSE); + reset_write_watch (FALSE); #endif //MULTIPLE_HEAPS - concurrent_print_time_delta ("CRWW"); + concurrent_print_time_delta ("CRWW"); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles(); + num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles(); - // this c_write is not really necessary because restart_vm - // has an instruction that will flush the cpu cache (interlocked - // or whatever) but we don't want to rely on that. - dprintf (GTC_LOG, ("setting cm_in_progress")); - c_write (cm_in_progress, TRUE); + // this c_write is not really necessary because restart_vm + // has an instruction that will flush the cpu cache (interlocked + // or whatever) but we don't want to rely on that. + dprintf (GTC_LOG, ("setting cm_in_progress")); + c_write (cm_in_progress, TRUE); - assert (dont_restart_ee_p); - dont_restart_ee_p = FALSE; + assert (dont_restart_ee_p); + dont_restart_ee_p = FALSE; - restart_vm(); - GCToOSInterface::YieldThread (0); + restart_vm(); + GCToOSInterface::YieldThread (0); #ifdef MULTIPLE_HEAPS - dprintf(3, ("Starting all gc threads for gc")); - bgc_t_join.restart(); + dprintf(3, ("Starting all gc threads for gc")); + bgc_t_join.restart(); #endif //MULTIPLE_HEAPS - } + } #ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_after_reset); - if (bgc_t_join.joined()) + bgc_t_join.join(this, gc_join_after_reset); + if (bgc_t_join.joined()) #endif //MULTIPLE_HEAPS - { - disable_preemptive (true); + { + disable_preemptive (true); #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - // When software write watch is enabled, resetting write watch is done while the runtime is - // suspended above. The post-reset call to revisit_written_pages is only necessary for concurrent - // reset_write_watch, to discard dirtied pages during the concurrent reset. + // When software write watch is enabled, resetting write watch is done while the runtime is + // suspended above. The post-reset call to revisit_written_pages is only necessary for concurrent + // reset_write_watch, to discard dirtied pages during the concurrent reset. #ifdef WRITE_WATCH - concurrent_print_time_delta ("CRWW begin"); + concurrent_print_time_delta ("CRWW begin"); #ifdef MULTIPLE_HEAPS - for (int i = 0; i < n_heaps; i++) - { - g_heaps[i]->reset_write_watch (TRUE); - } + for (int i = 0; i < n_heaps; i++) + { + g_heaps[i]->reset_write_watch (TRUE); + } #else - reset_write_watch (TRUE); + reset_write_watch (TRUE); #endif //MULTIPLE_HEAPS - concurrent_print_time_delta ("CRWW"); + concurrent_print_time_delta ("CRWW"); #endif //WRITE_WATCH #ifdef MULTIPLE_HEAPS - for (int i = 0; i < n_heaps; i++) - { - g_heaps[i]->revisit_written_pages (TRUE, TRUE); - } + for (int i = 0; i < n_heaps; i++) + { + g_heaps[i]->revisit_written_pages (TRUE, TRUE); + } #else - revisit_written_pages (TRUE, TRUE); + revisit_written_pages (TRUE, TRUE); #endif //MULTIPLE_HEAPS - concurrent_print_time_delta ("CRW"); + concurrent_print_time_delta ("CRW"); #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifdef MULTIPLE_HEAPS - for (int i = 0; i < n_heaps; i++) - { - g_heaps[i]->current_bgc_state = bgc_mark_handles; - } + for (int i = 0; i < n_heaps; i++) + { + g_heaps[i]->current_bgc_state = bgc_mark_handles; + } #else - current_bgc_state = bgc_mark_handles; + current_bgc_state = bgc_mark_handles; #endif //MULTIPLE_HEAPS - current_c_gc_state = c_gc_state_marking; - - enable_preemptive (); - -#ifdef MULTIPLE_HEAPS - dprintf(3, ("Joining BGC threads after resetting writewatch")); - bgc_t_join.restart(); -#endif //MULTIPLE_HEAPS - } - - disable_preemptive (true); - - if (num_sizedrefs > 0) - { - GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc); - - enable_preemptive (); - -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_scan_sizedref_done); - if (bgc_t_join.joined()) - { - dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots")); - bgc_t_join.restart(); - } -#endif //MULTIPLE_HEAPS - - disable_preemptive (true); - } - - dprintf (3,("BGC: handle table marking")); - GCScan::GcScanHandles(background_promote, - max_generation, max_generation, - &sc); - //concurrent_print_time_delta ("concurrent marking handle table"); - concurrent_print_time_delta ("CRH"); - - current_bgc_state = bgc_mark_stack; - dprintf (2,("concurrent draining mark list")); - background_drain_mark_list (thread); - //concurrent_print_time_delta ("concurrent marking stack roots"); - concurrent_print_time_delta ("CRS"); - - dprintf (2,("concurrent revisiting dirtied pages")); - - // tuning has shown that there are advantages in doing this 2 times - revisit_written_pages (TRUE); - revisit_written_pages (TRUE); - - //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH"); - concurrent_print_time_delta ("CRre"); + current_c_gc_state = c_gc_state_marking; enable_preemptive (); #ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_concurrent_overflow); + dprintf(3, ("Joining BGC threads after resetting writewatch")); + bgc_t_join.restart(); +#endif //MULTIPLE_HEAPS + } + + disable_preemptive (true); + + if (num_sizedrefs > 0) + { + GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc); + + enable_preemptive (); + +#ifdef MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_scan_sizedref_done); if (bgc_t_join.joined()) { - uint8_t* all_heaps_max = 0; - uint8_t* all_heaps_min = MAX_PTR; - int i; - for (i = 0; i < n_heaps; i++) - { - dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", - i, - g_heaps[i]->background_max_overflow_address, - g_heaps[i]->background_min_overflow_address)); - if (all_heaps_max < g_heaps[i]->background_max_overflow_address) - all_heaps_max = g_heaps[i]->background_max_overflow_address; - if (all_heaps_min > g_heaps[i]->background_min_overflow_address) - all_heaps_min = g_heaps[i]->background_min_overflow_address; - } - for (i = 0; i < n_heaps; i++) - { - g_heaps[i]->background_max_overflow_address = all_heaps_max; - g_heaps[i]->background_min_overflow_address = all_heaps_min; - } - dprintf(3, ("Starting all bgc threads after updating the overflow info")); + dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots")); bgc_t_join.restart(); } #endif //MULTIPLE_HEAPS disable_preemptive (true); + } - dprintf (2, ("before CRov count: %d", bgc_overflow_count)); - bgc_overflow_count = 0; - background_process_mark_overflow (TRUE); - dprintf (2, ("after CRov count: %d", bgc_overflow_count)); - bgc_overflow_count = 0; - //concurrent_print_time_delta ("concurrent processing mark overflow"); - concurrent_print_time_delta ("CRov"); - - // Stop all threads, crawl all stacks and revisit changed pages. - FIRE_EVENT(BGC1stConEnd); - - dprintf (2, ("Stopping the EE")); - - enable_preemptive (); - -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_suspend_ee); - if (bgc_t_join.joined()) - { - bgc_threads_sync_event.Reset(); - - dprintf(3, ("Joining BGC threads for non concurrent final marking")); - bgc_t_join.restart(); - } -#endif //MULTIPLE_HEAPS - - if (heap_number == 0) - { - enter_spin_lock (&gc_lock); - - suspended_start_time = GetHighPrecisionTimeStamp(); - bgc_suspend_EE (); - //suspend_EE (); - bgc_threads_sync_event.Set(); - } - else - { - bgc_threads_sync_event.Wait(INFINITE, FALSE); - dprintf (2, ("bgc_threads_sync_event is signalled")); - } - - assert (settings.concurrent); - assert (settings.condemned_generation == max_generation); - - dprintf (2, ("clearing cm_in_progress")); - c_write (cm_in_progress, FALSE); - - bgc_alloc_lock->check(); - - current_bgc_state = bgc_final_marking; - - //concurrent_print_time_delta ("concurrent marking ended"); - concurrent_print_time_delta ("CR"); - - FIRE_EVENT(BGC2ndNonConBegin); - - mark_absorb_new_alloc(); - - // We need a join here 'cause find_object would complain if the gen0 - // bricks of another heap haven't been fixed up. So we need to make sure - // that every heap's gen0 bricks are fixed up before we proceed. -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_after_absorb); - if (bgc_t_join.joined()) -#endif //MULTIPLE_HEAPS - { -#ifdef BGC_SERVO_TUNING - bgc_tuning::record_bgc_sweep_start(); -#endif //BGC_SERVO_TUNING - - GCToEEInterface::BeforeGcScanRoots(max_generation, /* is_bgc */ true, /* is_concurrent */ false); - -#ifdef MULTIPLE_HEAPS - dprintf(3, ("Joining BGC threads after absorb")); - bgc_t_join.restart(); -#endif //MULTIPLE_HEAPS - } - - //reset the flag, indicating that the EE no longer expect concurrent - //marking - sc.concurrent = FALSE; - - total_soh_size = generation_sizes (generation_of (max_generation)); - total_loh_size = generation_size (loh_generation); - total_poh_size = generation_size (poh_generation); - - dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id, poh: %Id", heap_number, total_loh_size, total_soh_size, total_poh_size)); - - dprintf (2, ("nonconcurrent marking stack roots")); - GCScan::GcScanRoots(background_promote, + dprintf (3,("BGC: handle table marking")); + GCScan::GcScanHandles(background_promote, max_generation, max_generation, &sc); - //concurrent_print_time_delta ("nonconcurrent marking stack roots"); - concurrent_print_time_delta ("NRS"); + //concurrent_print_time_delta ("concurrent marking handle table"); + concurrent_print_time_delta ("CRH"); - finalize_queue->GcScanRoots(background_promote, heap_number, 0); + current_bgc_state = bgc_mark_stack; + dprintf (2,("concurrent draining mark list")); + background_drain_mark_list (thread); + //concurrent_print_time_delta ("concurrent marking stack roots"); + concurrent_print_time_delta ("CRS"); - dprintf (2, ("nonconcurrent marking handle table")); - GCScan::GcScanHandles(background_promote, - max_generation, max_generation, - &sc); - //concurrent_print_time_delta ("nonconcurrent marking handle table"); - concurrent_print_time_delta ("NRH"); + dprintf (2,("concurrent revisiting dirtied pages")); - dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index))); - revisit_written_pages (FALSE); - //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH"); - concurrent_print_time_delta ("NRre LOH"); + // tuning has shown that there are advantages in doing this 2 times + revisit_written_pages (TRUE); + revisit_written_pages (TRUE); - dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count)); - bgc_overflow_count = 0; + //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH"); + concurrent_print_time_delta ("CRre"); - // Dependent handles need to be scanned with a special algorithm (see the header comment on - // scan_dependent_handles for more detail). We perform an initial scan without processing any mark - // stack overflow. This is not guaranteed to complete the operation but in a common case (where there - // are no dependent handles that are due to be collected) it allows us to optimize away further scans. - // The call to background_scan_dependent_handles is what will cycle through more iterations if - // required and will also perform processing of any mark stack overflow once the dependent handle - // table has been fully promoted. - dprintf (2, ("1st dependent handle scan and process mark overflow")); - GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc); - background_scan_dependent_handles (&sc); - //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow"); - concurrent_print_time_delta ("NR 1st Hov"); - - dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count)); - bgc_overflow_count = 0; + enable_preemptive (); #ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_null_dead_short_weak); - if (bgc_t_join.joined()) -#endif //MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_concurrent_overflow); + if (bgc_t_join.joined()) + { + uint8_t* all_heaps_max = 0; + uint8_t* all_heaps_min = MAX_PTR; + int i; + for (i = 0; i < n_heaps; i++) { + dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", + i, + g_heaps[i]->background_max_overflow_address, + g_heaps[i]->background_min_overflow_address)); + if (all_heaps_max < g_heaps[i]->background_max_overflow_address) + all_heaps_max = g_heaps[i]->background_max_overflow_address; + if (all_heaps_min > g_heaps[i]->background_min_overflow_address) + all_heaps_min = g_heaps[i]->background_min_overflow_address; + } + for (i = 0; i < n_heaps; i++) + { + g_heaps[i]->background_max_overflow_address = all_heaps_max; + g_heaps[i]->background_min_overflow_address = all_heaps_min; + } + dprintf(3, ("Starting all bgc threads after updating the overflow info")); + bgc_t_join.restart(); + } +#endif //MULTIPLE_HEAPS + + disable_preemptive (true); + + dprintf (2, ("before CRov count: %d", bgc_overflow_count)); + bgc_overflow_count = 0; + background_process_mark_overflow (TRUE); + dprintf (2, ("after CRov count: %d", bgc_overflow_count)); + bgc_overflow_count = 0; + //concurrent_print_time_delta ("concurrent processing mark overflow"); + concurrent_print_time_delta ("CRov"); + + // Stop all threads, crawl all stacks and revisit changed pages. + FIRE_EVENT(BGC1stConEnd); + + dprintf (2, ("Stopping the EE")); + + enable_preemptive (); + +#ifdef MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_suspend_ee); + if (bgc_t_join.joined()) + { + bgc_threads_sync_event.Reset(); + + dprintf(3, ("Joining BGC threads for non concurrent final marking")); + bgc_t_join.restart(); + } +#endif //MULTIPLE_HEAPS + + if (heap_number == 0) + { + enter_spin_lock (&gc_lock); + + suspended_start_time = GetHighPrecisionTimeStamp(); + bgc_suspend_EE (); + //suspend_EE (); + bgc_threads_sync_event.Set(); + } + else + { + bgc_threads_sync_event.Wait(INFINITE, FALSE); + dprintf (2, ("bgc_threads_sync_event is signalled")); + } + + assert (settings.concurrent); + assert (settings.condemned_generation == max_generation); + + dprintf (2, ("clearing cm_in_progress")); + c_write (cm_in_progress, FALSE); + + bgc_alloc_lock->check(); + + current_bgc_state = bgc_final_marking; + + //concurrent_print_time_delta ("concurrent marking ended"); + concurrent_print_time_delta ("CR"); + + FIRE_EVENT(BGC2ndNonConBegin); + + mark_absorb_new_alloc(); + +#ifdef FEATURE_EVENT_TRACE + static uint64_t current_mark_time = 0; + static uint64_t last_mark_time = 0; +#endif //FEATURE_EVENT_TRACE + + // We need a join here 'cause find_object would complain if the gen0 + // bricks of another heap haven't been fixed up. So we need to make sure + // that every heap's gen0 bricks are fixed up before we proceed. +#ifdef MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_after_absorb); + if (bgc_t_join.joined()) +#endif //MULTIPLE_HEAPS + { +#ifdef BGC_SERVO_TUNING + bgc_tuning::record_bgc_sweep_start(); +#endif //BGC_SERVO_TUNING + + GCToEEInterface::BeforeGcScanRoots(max_generation, /* is_bgc */ true, /* is_concurrent */ false); + +#ifdef FEATURE_EVENT_TRACE + informational_event_enabled_p = EVENT_ENABLED (GCMarkWithType); + if (informational_event_enabled_p) + last_mark_time = GetHighPrecisionTimeStamp(); +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS + dprintf(3, ("Joining BGC threads after absorb")); + bgc_t_join.restart(); +#endif //MULTIPLE_HEAPS + } + + //reset the flag, indicating that the EE no longer expect concurrent + //marking + sc.concurrent = FALSE; + + total_soh_size = generation_sizes (generation_of (max_generation)); + total_loh_size = generation_size (loh_generation); + total_poh_size = generation_size (poh_generation); + + dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id, poh: %Id", heap_number, total_loh_size, total_soh_size, total_poh_size)); + + dprintf (2, ("nonconcurrent marking stack roots")); + GCScan::GcScanRoots(background_promote, + max_generation, max_generation, + &sc); + //concurrent_print_time_delta ("nonconcurrent marking stack roots"); + concurrent_print_time_delta ("NRS"); + + finalize_queue->GcScanRoots(background_promote, heap_number, 0); + + dprintf (2, ("nonconcurrent marking handle table")); + GCScan::GcScanHandles(background_promote, + max_generation, max_generation, + &sc); + //concurrent_print_time_delta ("nonconcurrent marking handle table"); + concurrent_print_time_delta ("NRH"); + + dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index))); + revisit_written_pages (FALSE); + //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH"); + concurrent_print_time_delta ("NRre LOH"); + + dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count)); + bgc_overflow_count = 0; + + // Dependent handles need to be scanned with a special algorithm (see the header comment on + // scan_dependent_handles for more detail). We perform an initial scan without processing any mark + // stack overflow. This is not guaranteed to complete the operation but in a common case (where there + // are no dependent handles that are due to be collected) it allows us to optimize away further scans. + // The call to background_scan_dependent_handles is what will cycle through more iterations if + // required and will also perform processing of any mark stack overflow once the dependent handle + // table has been fully promoted. + dprintf (2, ("1st dependent handle scan and process mark overflow")); + GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc); + background_scan_dependent_handles (&sc); + //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow"); + concurrent_print_time_delta ("NR 1st Hov"); + + dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count)); + bgc_overflow_count = 0; + +#ifdef MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_null_dead_short_weak); + if (bgc_t_join.joined()) +#endif //MULTIPLE_HEAPS + { +#ifdef FEATURE_EVENT_TRACE + bgc_time_info[time_mark_sizedref] = 0; + record_mark_time (bgc_time_info[time_mark_roots], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - // The runtime is suspended, take this opportunity to pause tracking written pages to - // avoid further perf penalty after the runtime is restarted - SoftwareWriteWatch::DisableForGCHeap(); + // The runtime is suspended, take this opportunity to pause tracking written pages to + // avoid further perf penalty after the runtime is restarted + SoftwareWriteWatch::DisableForGCHeap(); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc); + GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc); #ifdef MULTIPLE_HEAPS - dprintf(3, ("Joining BGC threads for short weak handle scan")); - bgc_t_join.restart(); + dprintf(3, ("Joining BGC threads for short weak handle scan")); + bgc_t_join.restart(); #endif //MULTIPLE_HEAPS - } - - // null out the target of short weakref that were not promoted. - GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc); - - //concurrent_print_time_delta ("bgc GcShortWeakPtrScan"); - concurrent_print_time_delta ("NR GcShortWeakPtrScan"); } + // null out the target of short weakref that were not promoted. + GCScan::GcShortWeakPtrScan(max_generation, max_generation, &sc); + + //concurrent_print_time_delta ("bgc GcShortWeakPtrScan"); + concurrent_print_time_delta ("NR GcShortWeakPtrScan"); + { #ifdef MULTIPLE_HEAPS bgc_t_join.join(this, gc_join_scan_finalization); if (bgc_t_join.joined()) { +#endif //MULTIPLE_HEAPS + +#ifdef FEATURE_EVENT_TRACE + record_mark_time (bgc_time_info[time_mark_short_weak], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS dprintf(3, ("Joining BGC threads for finalization")); bgc_t_join.restart(); } @@ -31921,14 +32402,21 @@ void gc_heap::background_mark_phase () #ifdef MULTIPLE_HEAPS bgc_t_join.join(this, gc_join_null_dead_long_weak); if (bgc_t_join.joined()) +#endif //MULTIPLE_HEAPS { + +#ifdef FEATURE_EVENT_TRACE + record_mark_time (bgc_time_info[time_mark_scan_finalization], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + +#ifdef MULTIPLE_HEAPS dprintf(2, ("Joining BGC threads for weak pointer deletion")); bgc_t_join.restart(); - } #endif //MULTIPLE_HEAPS + } // null out the target of long weakref that were not promoted. - GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc); + GCScan::GcWeakPtrScan (max_generation, max_generation, &sc); concurrent_print_time_delta ("NR GcWeakPtrScan"); #ifdef MULTIPLE_HEAPS @@ -31939,6 +32427,11 @@ void gc_heap::background_mark_phase () dprintf (2, ("calling GcWeakPtrScanBySingleThread")); // scan for deleted entries in the syncblk cache GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc); + +#ifdef FEATURE_EVENT_TRACE + record_mark_time (bgc_time_info[time_mark_long_weak], current_mark_time, last_mark_time); +#endif //FEATURE_EVENT_TRACE + concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread"); #ifdef MULTIPLE_HEAPS dprintf(2, ("Starting BGC threads for end of background mark phase")); @@ -36662,6 +37155,10 @@ void gc_heap::init_static_data() if (gen0_max_size_config) { gen0_max_size = min (gen0_max_size, gen0_max_size_config); + +#ifdef FEATURE_EVENT_TRACE + gen0_max_budget_from_config = gen0_max_size; +#endif //FEATURE_EVENT_TRACE } gen0_max_size = Align (gen0_max_size); @@ -36677,6 +37174,15 @@ void gc_heap::init_static_data() max (6*1024*1024, Align(soh_segment_size/2))); #endif //MULTIPLE_HEAPS + size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget(); + + if (gen1_max_size_config) + { + gen1_max_size = min (gen1_max_size, gen1_max_size_config); + } + + gen1_max_size = Align (gen1_max_size); + dprintf (GTC_LOG, ("gen0 min: %Id, max: %Id, gen1 max: %Id", gen0_min_size, gen0_max_size, gen1_max_size)); @@ -41234,6 +41740,9 @@ HRESULT GCHeap::Initialize() if (gc_heap::total_physical_mem != 0) { gc_heap::is_restricted_physical_mem = true; +#ifdef FEATURE_EVENT_TRACE + gc_heap::physical_memory_from_config = (size_t)gc_heap::total_physical_mem; +#endif //FEATURE_EVENT_TRACE } else { @@ -41312,7 +41821,13 @@ HRESULT GCHeap::Initialize() // If the hard limit is specified, the user is saying even if the process is already // running in a container, use this limit for the GC heap. - if (!(gc_heap::heap_hard_limit)) + if (gc_heap::heap_hard_limit) + { +#ifdef FEATURE_EVENT_TRACE + gc_heap::hard_limit_config_p = true; +#endif //FEATURE_EVENT_TRACE + } + else { if (gc_heap::is_restricted_physical_mem) { @@ -41516,6 +42031,9 @@ HRESULT GCHeap::Initialize() { gc_heap::high_memory_load_th = min (99, highmem_th_from_config); gc_heap::v_high_memory_load_th = min (99, (highmem_th_from_config + 7)); +#ifdef FEATURE_EVENT_TRACE + gc_heap::high_mem_percent_from_config = highmem_th_from_config; +#endif //FEATURE_EVENT_TRACE } else { @@ -43216,6 +43734,12 @@ void gc_heap::do_post_gc() } GCHeap::UpdatePostGCCounters(); + + // We need to reinitialize the number of pinned objects because it's used in the GCHeapStats + // event fired in GCHeap::UpdatePostGCCounters. For BGC, we will get that event following an + // FGC's GCHeapStats and we wouldn't want that FGC's info to carry over to the BGC. + reinit_pinned_objects(); + #ifdef STRESS_LOG STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index), (uint32_t)settings.condemned_generation, @@ -43954,6 +44478,12 @@ size_t gc_heap::get_gen0_min_size() } } } +#ifdef FEATURE_EVENT_TRACE + else + { + gen0_min_budget_from_config = gen0size; + } +#endif //FEATURE_EVENT_TRACE size_t seg_size = gc_heap::soh_segment_size; assert (seg_size); @@ -44749,6 +45279,28 @@ void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanCo GCScan::GcScanDependentHandlesForProfilerAndETW (gen_number, context, fn); } +void GCHeap::DiagGetGCSettings(EtwGCSettingsInfo* etw_settings) +{ +#ifdef FEATURE_EVENT_TRACE + etw_settings->heap_hard_limit = gc_heap::heap_hard_limit; + etw_settings->loh_threshold = loh_size_threshold; + etw_settings->physical_memory_from_config = gc_heap::physical_memory_from_config; + etw_settings->gen0_min_budget_from_config = gc_heap::gen0_min_budget_from_config; + etw_settings->gen0_max_budget_from_config = gc_heap::gen0_max_budget_from_config; + etw_settings->high_mem_percent_from_config = gc_heap::high_mem_percent_from_config; + etw_settings->concurrent_gc_p = gc_heap::gc_can_use_concurrent; + etw_settings->use_large_pages_p = gc_heap::use_large_pages_p; + etw_settings->use_frozen_segments_p = gc_heap::use_frozen_segments_p; + etw_settings->hard_limit_config_p = gc_heap::hard_limit_config_p; + etw_settings->no_affinitize_p = +#ifdef MULTIPLE_HEAPS + gc_heap::gc_thread_no_affinitize_p; +#else + true; +#endif //MULTIPLE_HEAPS +#endif //FEATURE_EVENT_TRACE +} + #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC) // This code is designed to catch the failure to update the write barrier // The way it works is to copy the whole heap right after every GC. The write diff --git a/src/coreclr/gc/gc.h b/src/coreclr/gc/gc.h index e67db5c5eea..a47f84089bc 100644 --- a/src/coreclr/gc/gc.h +++ b/src/coreclr/gc/gc.h @@ -94,14 +94,6 @@ enum gc_etw_segment_type gc_etw_segment_pinned_object_heap = 3 }; -// Types of allocations, emitted by the GCAllocationTick ETW event. -enum gc_etw_alloc_kind -{ - gc_etw_alloc_soh = 0, - gc_etw_alloc_loh = 1, - gc_etw_alloc_poh = 2 -}; - /* forward declerations */ class CObjectHeader; class Object; diff --git a/src/coreclr/gc/gcconfig.h b/src/coreclr/gc/gcconfig.h index 0ab210bf48c..8a9112ec480 100644 --- a/src/coreclr/gc/gcconfig.h +++ b/src/coreclr/gc/gcconfig.h @@ -98,6 +98,7 @@ public: INT_CONFIG (GCHighMemPercent, "GCHighMemPercent", "System.GC.HighMemoryPercent", 0, "The percent for GC to consider as high memory") \ INT_CONFIG (GCProvModeStress, "GCProvModeStress", NULL, 0, "Stress the provisional modes") \ INT_CONFIG (GCGen0MaxBudget, "GCGen0MaxBudget", NULL, 0, "Specifies the largest gen0 allocation budget") \ + INT_CONFIG (GCGen1MaxBudget, "GCGen1MaxBudget", NULL, 0, "Specifies the largest gen1 allocation budget") \ INT_CONFIG (GCLowSkipRatio, "GCLowSkipRatio", NULL, 30, "Specifies the low generation skip ratio") \ INT_CONFIG (GCHeapHardLimit, "GCHeapHardLimit", "System.GC.HeapHardLimit", 0, "Specifies a hard limit for the GC heap") \ INT_CONFIG (GCHeapHardLimitPercent, "GCHeapHardLimitPercent", "System.GC.HeapHardLimitPercent", 0, "Specifies the GC heap usage as a percentage of the total memory") \ diff --git a/src/coreclr/gc/gcee.cpp b/src/coreclr/gc/gcee.cpp index bf0bb49cb00..afc5d3a8dd2 100644 --- a/src/coreclr/gc/gcee.cpp +++ b/src/coreclr/gc/gcee.cpp @@ -325,25 +325,21 @@ bool GCHeap::IsConcurrentGCInProgress() } #ifdef FEATURE_EVENT_TRACE -void gc_heap::fire_etw_allocation_event (size_t allocation_amount, int gen_number, uint8_t* object_address) +void gc_heap::fire_etw_allocation_event (size_t allocation_amount, + int gen_number, + uint8_t* object_address, + size_t object_size) { - gc_etw_alloc_kind kind; - switch (gen_number) - { - case 0: - kind = gc_etw_alloc_soh; - break; - case 3: - kind = gc_etw_alloc_loh; - break; - case 4: - kind = gc_etw_alloc_poh; - break; - default: - __UNREACHABLE(); - } - - FIRE_EVENT(GCAllocationTick_V3, static_cast(allocation_amount), kind, heap_number, object_address); +#ifdef FEATURE_REDHAWK + FIRE_EVENT(GCAllocationTick_V1, (uint32_t)allocation_amount, (uint32_t)gen_to_oh (gen_number)); +#else + FIRE_EVENT(GCAllocationTick_V4, + allocation_amount, + (uint32_t)gen_to_oh (gen_number), + heap_number, + object_address, + object_size); +#endif //FEATURE_REDHAWK } void gc_heap::fire_etw_pin_object_event (uint8_t* object, uint8_t** ppObject) diff --git a/src/coreclr/gc/gcevents.h b/src/coreclr/gc/gcevents.h index 5f43fe6f4e4..461c75c43ba 100644 --- a/src/coreclr/gc/gcevents.h +++ b/src/coreclr/gc/gcevents.h @@ -19,11 +19,13 @@ KNOWN_EVENT(GCTerminateConcurrentThread_V1, GCEventProvider_Default, GCEventLeve KNOWN_EVENT(GCTriggered, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) KNOWN_EVENT(GCMarkWithType, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) KNOWN_EVENT(GCJoin_V2, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) -KNOWN_EVENT(GCGlobalHeapHistory_V3, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) +KNOWN_EVENT(GCGlobalHeapHistory_V4, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) KNOWN_EVENT(GCAllocationTick_V1, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) -KNOWN_EVENT(GCAllocationTick_V3, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) +KNOWN_EVENT(GCAllocationTick_V4, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) KNOWN_EVENT(PinObjectAtGCTime, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) KNOWN_EVENT(GCPerHeapHistory_V3, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) +KNOWN_EVENT(GCLOHCompact, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GC) +KNOWN_EVENT(GCFitBucketInfo, GCEventProvider_Default, GCEventLevel_Verbose, GCEventKeyword_GC) KNOWN_EVENT(SetGCHandle, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GCHandle) KNOWN_EVENT(DestroyGCHandle, GCEventProvider_Default, GCEventLevel_Information, GCEventKeyword_GCHandle) diff --git a/src/coreclr/gc/gcimpl.h b/src/coreclr/gc/gcimpl.h index b1c8cb91a7b..2c21ceada4f 100644 --- a/src/coreclr/gc/gcimpl.h +++ b/src/coreclr/gc/gcimpl.h @@ -307,6 +307,8 @@ protected: virtual void DiagWalkHeap(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p); + virtual void DiagGetGCSettings(EtwGCSettingsInfo* etw_settings); + public: Object * NextObj (Object * object); diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h index ea5f2d2914c..6525cc653ef 100644 --- a/src/coreclr/gc/gcinterface.ee.h +++ b/src/coreclr/gc/gcinterface.ee.h @@ -90,22 +90,29 @@ public: void FireGCJoin_V2(uint32_t heap, uint32_t joinTime, uint32_t joinType, uint32_t joinId) = 0; virtual - void FireGCGlobalHeapHistory_V3(uint64_t finalYoungestDesired, - int32_t numHeaps, - uint32_t condemnedGeneration, - uint32_t gen0reductionCount, - uint32_t reason, - uint32_t globalMechanisms, - uint32_t pauseMode, - uint32_t memoryPressure, - uint32_t condemnReasons0, - uint32_t condemnReasons1) = 0; + void FireGCGlobalHeapHistory_V4(uint64_t finalYoungestDesired, + int32_t numHeaps, + uint32_t condemnedGeneration, + uint32_t gen0reductionCount, + uint32_t reason, + uint32_t globalMechanisms, + uint32_t pauseMode, + uint32_t memoryPressure, + uint32_t condemnReasons0, + uint32_t condemnReasons1, + uint32_t count, + uint32_t valuesLen, + void *values) = 0; virtual void FireGCAllocationTick_V1(uint32_t allocationAmount, uint32_t allocationKind) = 0; virtual - void FireGCAllocationTick_V3(uint64_t allocationAmount, uint32_t allocationKind, uint32_t heapIndex, void* objectAddress) = 0; + void FireGCAllocationTick_V4(uint64_t allocationAmount, + uint32_t allocationKind, + uint32_t heapIndex, + void* objectAddress, + uint64_t objectSize) = 0; virtual void FirePinObjectAtGCTime(void* object, uint8_t** ppObject) = 0; @@ -130,6 +137,13 @@ public: uint32_t count, uint32_t valuesLen, void *values) = 0; + + virtual + void FireGCLOHCompact(uint16_t count, uint32_t valuesLen, void *values) = 0; + + virtual + void FireGCFitBucketInfo(uint16_t bucketKind, size_t size, uint16_t count, uint32_t valuesLen, void *values) = 0; + virtual void FireBGCBegin() = 0; virtual diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index bfe02b7db9d..920c321da8e 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -110,6 +110,22 @@ struct WriteBarrierParameters uint8_t* write_watch_table; }; +struct EtwGCSettingsInfo +{ + size_t heap_hard_limit; + size_t loh_threshold; + size_t physical_memory_from_config; + size_t gen0_min_budget_from_config; + size_t gen0_max_budget_from_config; + uint32_t high_mem_percent_from_config; + bool concurrent_gc_p; + bool use_large_pages_p; + bool use_frozen_segments_p; + // If this is false, it means the hardlimit was set implicitly by the container. + bool hard_limit_config_p; + bool no_affinitize_p; +}; + // Opaque type for tracking object pointers #ifndef DACCESS_COMPILE struct OBJECTHANDLE__ @@ -864,6 +880,9 @@ public: // Traces all GC segments and fires ETW events with information on them. virtual void DiagTraceGCSegments() = 0; + // Get GC settings for tracing purposes. These are settings not obvious from a trace. + virtual void DiagGetGCSettings(EtwGCSettingsInfo* settings) = 0; + /* =========================================================================== GC Stress routines. Used only when running under GC Stress. diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index bcdc0131c19..c6d7dd66886 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -646,6 +646,24 @@ public: } }; +#ifdef FEATURE_EVENT_TRACE +struct etw_bucket_info +{ + uint16_t index; + uint32_t count; + size_t size; + + etw_bucket_info() {} + + void set (uint16_t _index, uint32_t _count, size_t _size) + { + index = _index; + count = _count; + size = _size; + } +}; +#endif //FEATURE_EVENT_TRACE + class allocator { int first_bucket_bits; @@ -764,6 +782,13 @@ public: #ifdef USE_REGIONS void thread_sip_fl (heap_segment* region); #endif //USE_REGIONS + +#ifdef FEATURE_EVENT_TRACE + uint16_t count_largest_items (etw_bucket_info* bucket_info, + size_t max_size, + size_t max_item_count, + size_t* recorded_fl_info_size); +#endif //FEATURE_EVENT_TRACE }; #define NUM_GEN_POWER2 (20) @@ -1667,10 +1692,9 @@ protected: void handle_failure_for_no_gc(); PER_HEAP - void fire_etw_allocation_event (size_t allocation_amount, int gen_number, uint8_t* object_address); - - PER_HEAP - void fire_etw_pin_object_event (uint8_t* object, uint8_t** ppObject); + void fire_mark_event (int root_type, + size_t& current_promoted_bytes, + size_t& last_promoted_bytes); PER_HEAP size_t limit_from_size (size_t size, uint32_t flags, size_t room, int gen_number, @@ -1707,6 +1731,7 @@ protected: size_t size, alloc_context* acontext, uint32_t flags, + int gen_number, int align_const, int lock_index, BOOL check_used_p, @@ -2660,6 +2685,9 @@ protected: PER_HEAP_ISOLATED size_t get_total_pinned_objects(); + PER_HEAP_ISOLATED + void reinit_pinned_objects(); + PER_HEAP void reset_mark_stack (); PER_HEAP @@ -2713,6 +2741,9 @@ protected: PER_HEAP void plan_phase (int condemned_gen_number); + PER_HEAP + void add_alloc_in_condemned_bucket (size_t plug_size); + PER_HEAP uint8_t* find_next_marked (uint8_t* x, uint8_t* end, BOOL use_mark_list, @@ -3118,6 +3149,10 @@ protected: size_t generation_size (int gen_number); PER_HEAP_ISOLATED size_t get_total_survived_size(); + PER_HEAP + bool update_alloc_info (int gen_number, + size_t allocated_size, + size_t* etw_allocation_amount); // this also resets allocated_since_last_gc PER_HEAP_ISOLATED size_t get_total_allocated_since_last_gc(); @@ -3684,7 +3719,7 @@ public: gen_to_condemn_tuning gen_to_condemn_reasons; PER_HEAP - size_t etw_allocation_running_amount[2]; + size_t etw_allocation_running_amount[gc_oh_num::total_oh_count - 1]; PER_HEAP uint64_t total_alloc_bytes_soh; @@ -4443,6 +4478,123 @@ protected: size_t saved_pinned_plug_index; #endif //DOUBLY_LINKED_FL +#ifdef FEATURE_EVENT_TRACE + PER_HEAP_ISOLATED + bool informational_event_enabled_p; + + // Time is all in microseconds here. These are times recorded during STW. + // + // Note that the goal of this is not to show every single type of roots + // For that you have the per heap MarkWithType events. This takes advantage + // of the joins we already have and naturally gets the time between each + // join. + enum etw_gc_time_info + { + time_mark_sizedref = 0, + // Note time_mark_roots does not include scanning sizedref handles. + time_mark_roots = 1, + time_mark_short_weak = 2, + time_mark_scan_finalization = 3, + time_mark_long_weak = 4, + max_bgc_time_type = 5, + time_plan = 5, + time_relocate = 6, + time_sweep = 6, + max_sweep_time_type = 7, + time_compact = 7, + max_compact_time_type = 8 + }; + + PER_HEAP_ISOLATED + uint64_t* gc_time_info; + +#ifdef BACKGROUND_GC + PER_HEAP_ISOLATED + uint64_t* bgc_time_info; +#endif //BACKGROUND_GC + + PER_HEAP_ISOLATED + void record_mark_time (uint64_t& mark_time, + uint64_t& current_mark_time, + uint64_t& last_mark_time); + +#define max_etw_item_count 2000 + + enum etw_bucket_kind + { + largest_fl_items = 0, + plugs_in_condemned = 1 + }; + + // This is for gen2 FL purpose so it would use sizes for gen2 buckets. + // This event is only to give us a rough idea of the largest gen2 fl + // items or plugs that we had to allocate in condemned. We only fire + // these events on verbose level and stop at max_etw_item_count items. + PER_HEAP + etw_bucket_info bucket_info[NUM_GEN2_ALIST]; + + PER_HEAP + void init_bucket_info(); + + PER_HEAP + void add_plug_in_condemned_info (generation* gen, size_t plug_size); + + PER_HEAP + void fire_etw_allocation_event (size_t allocation_amount, + int gen_number, + uint8_t* object_address, + size_t object_size); + + PER_HEAP + void fire_etw_pin_object_event (uint8_t* object, uint8_t** ppObject); + + // config stuff + PER_HEAP_ISOLATED + size_t physical_memory_from_config; + + PER_HEAP_ISOLATED + size_t gen0_min_budget_from_config; + + PER_HEAP_ISOLATED + size_t gen0_max_budget_from_config; + + PER_HEAP_ISOLATED + int high_mem_percent_from_config; + + PER_HEAP_ISOLATED + bool use_frozen_segments_p; + + PER_HEAP_ISOLATED + bool hard_limit_config_p; + +#ifdef FEATURE_LOH_COMPACTION + // This records the LOH compaction info - + // time it takes to plan, relocate and compact. + // We want to see how reference rich large objects are so + // we also record ref info. Survived bytes are already recorded + // in gc_generation_data of the perheap history event. + // + // If we don't end up actually doing LOH compaction because plan + // failed, the time would all be 0s. + struct etw_loh_compact_info + { + uint32_t time_plan; + uint32_t time_compact; + uint32_t time_relocate; + size_t total_refs; + size_t zero_refs; + }; + + PER_HEAP_ISOLATED + etw_loh_compact_info* loh_compact_info; + + PER_HEAP + void loh_reloc_survivor_helper (uint8_t** pval, + size_t& total_refs, + size_t& zero_refs); +#endif //FEATURE_LOH_COMPACTION +#endif //FEATURE_EVENT_TRACE + PER_HEAP dynamic_data dynamic_data_table [total_generation_count]; @@ -4571,7 +4723,7 @@ protected: size_t num_provisional_triggered; PER_HEAP - size_t allocated_since_last_gc[2]; + size_t allocated_since_last_gc[gc_oh_num::total_oh_count - 1]; #ifdef BACKGROUND_GC PER_HEAP_ISOLATED diff --git a/src/coreclr/gc/gcscan.cpp b/src/coreclr/gc/gcscan.cpp index 5b7b543681c..ca9b03ff36d 100644 --- a/src/coreclr/gc/gcscan.cpp +++ b/src/coreclr/gc/gcscan.cpp @@ -97,13 +97,13 @@ bool GCScan::GcDhReScan(ScanContext* sc) * Scan for dead weak pointers */ -void GCScan::GcWeakPtrScan( promote_func* fn, int condemned, int max_gen, ScanContext* sc ) +void GCScan::GcWeakPtrScan(int condemned, int max_gen, ScanContext* sc) { // Clear out weak pointers that are no longer live. Ref_CheckReachable(condemned, max_gen, (uintptr_t)sc); // Clear any secondary objects whose primary object is now definitely dead. - Ref_ScanDependentHandlesForClearing(condemned, max_gen, sc, fn); + Ref_ScanDependentHandlesForClearing(condemned, max_gen, sc); } static void CALLBACK CheckPromoted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t * /*pExtraInfo*/, uintptr_t /*lp1*/, uintptr_t /*lp2*/) @@ -137,10 +137,8 @@ void GCScan::GcScanSizedRefs(promote_func* fn, int condemned, int max_gen, ScanC Ref_ScanSizedRefHandles(condemned, max_gen, sc, fn); } -void GCScan::GcShortWeakPtrScan(promote_func* fn, int condemned, int max_gen, - ScanContext* sc) +void GCScan::GcShortWeakPtrScan(int condemned, int max_gen, ScanContext* sc) { - UNREFERENCED_PARAMETER(fn); Ref_CheckAlive(condemned, max_gen, (uintptr_t)sc); } diff --git a/src/coreclr/gc/gcscan.h b/src/coreclr/gc/gcscan.h index 5f0462ff8b0..7ddb3badf96 100644 --- a/src/coreclr/gc/gcscan.h +++ b/src/coreclr/gc/gcscan.h @@ -53,12 +53,11 @@ class GCScan static void GcScanDependentHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn); // scan for dead weak pointers - static void GcWeakPtrScan (promote_func* fn, int condemned, int max_gen, ScanContext*sc ); - static void GcWeakPtrScanBySingleThread (int condemned, int max_gen, ScanContext*sc ); + static void GcWeakPtrScan (int condemned, int max_gen, ScanContext*sc); + static void GcWeakPtrScanBySingleThread (int condemned, int max_gen, ScanContext*sc); // scan for dead weak pointers - static void GcShortWeakPtrScan (promote_func* fn, int condemned, int max_gen, - ScanContext* sc); + static void GcShortWeakPtrScan (int condemned, int max_gen, ScanContext* sc); // // Dependent handle promotion scan support diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp index 1485f56e9ff..971452fe74d 100644 --- a/src/coreclr/gc/objecthandle.cpp +++ b/src/coreclr/gc/objecthandle.cpp @@ -1254,7 +1254,7 @@ bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext) // Perform a scan of dependent handles for the purpose of clearing any that haven't had their primary // promoted. -void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn) +void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc) { LOG((LF_GC, LL_INFO10000, "Clearing dead dependent handles in generation %u\n", condemned)); uint32_t type = HNDTYPE_DEPENDENT; @@ -1271,7 +1271,7 @@ void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, Sc HHANDLETABLE hTable = walk->pBuckets[i]->pTable[getSlotNumber(sc)]; if (hTable) { - HndScanHandlesForGC(hTable, ClearDependentHandle, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags ); + HndScanHandlesForGC(hTable, ClearDependentHandle, uintptr_t(sc), 0, &type, 1, condemned, maxgen, flags ); } } } diff --git a/src/coreclr/gc/objecthandle.h b/src/coreclr/gc/objecthandle.h index bfa7feb370e..66a1026e574 100644 --- a/src/coreclr/gc/objecthandle.h +++ b/src/coreclr/gc/objecthandle.h @@ -98,7 +98,7 @@ void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Re void Ref_UpdatePinnedPointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); DhContext *Ref_GetDependentHandleContext(ScanContext* sc); bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext); -void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); +void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc); void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); #ifdef FEATURE_REDHAWK diff --git a/src/coreclr/inc/eventtrace.h b/src/coreclr/inc/eventtrace.h index 1a930deebfa..1540a7cca98 100644 --- a/src/coreclr/inc/eventtrace.h +++ b/src/coreclr/inc/eventtrace.h @@ -210,46 +210,6 @@ namespace ETW public: typedef union st_GCEventInfo { - typedef struct _GenerationInfo { - ULONGLONG GenerationSize; - ULONGLONG TotalPromotedSize; - } GenerationInfo; - - struct { - GenerationInfo GenInfo[4]; // the heap info on gen0, gen1, gen2 and the large object heap. - ULONGLONG FinalizationPromotedSize; //not available per generation - ULONGLONG FinalizationPromotedCount; //not available per generation - ULONG PinnedObjectCount; - ULONG SinkBlockCount; - ULONG GCHandleCount; - } HeapStats; - - typedef enum _HeapType { - SMALL_OBJECT_HEAP, LARGE_OBJECT_HEAP, READ_ONLY_HEAP - } HeapType; - struct { - ULONGLONG Address; - ULONGLONG Size; - HeapType Type; - } GCCreateSegment; - - struct { - ULONGLONG Address; - } GCFreeSegment; - struct { - ULONG Count; - ULONG Depth; - } GCEnd; - - typedef enum _AllocationKind { - AllocationSmall = 0, - AllocationLarge - }AllocationKind; - struct { - ULONG Allocation; - AllocationKind Kind; - } AllocationTick; - // These values are gotten from the gc_reason // in gcimpl.h typedef enum _GC_REASON { @@ -271,48 +231,23 @@ namespace ETW GC_BGC = 1, GC_FGC = 2 } GC_TYPE; - typedef enum _GC_ROOT_KIND { - GC_ROOT_STACK = 0, - GC_ROOT_FQ = 1, - GC_ROOT_HANDLES = 2, - GC_ROOT_OLDER = 3, - GC_ROOT_SIZEDREF = 4, - GC_ROOT_OVERFLOW = 5 - } GC_ROOT_KIND; struct { ULONG Count; ULONG Depth; GC_REASON Reason; GC_TYPE Type; } GCStart; - - struct { - ULONG Count; // how many finalizers we called. - } GCFinalizers; - struct { ULONG Reason; // This is only valid when SuspendEE is called by GC (ie, Reason is either // SUSPEND_FOR_GC or SUSPEND_FOR_GC_PREP. ULONG GcCount; } SuspendEE; - - struct { - ULONG HeapNum; - } GCMark; - struct { ULONGLONG SegmentSize; ULONGLONG LargeObjectSegmentSize; BOOL ServerGC; // TRUE means it's server GC; FALSE means it's workstation. } GCSettings; - - struct { - // The generation that triggered this notification. - ULONG Count; - // 1 means the notification was due to allocation; 0 means it was due to other factors. - ULONG Alloc; - } GCFullNotify; } ETW_GC_INFO, *PETW_GC_INFO; #ifdef FEATURE_EVENT_TRACE diff --git a/src/coreclr/inc/eventtracebase.h b/src/coreclr/inc/eventtracebase.h index b976a770ec0..74d2b799e94 100644 --- a/src/coreclr/inc/eventtracebase.h +++ b/src/coreclr/inc/eventtracebase.h @@ -64,6 +64,15 @@ enum EtwThreadFlags kEtwThreadFlagThreadPoolWorker = 0x00000004, }; +enum EtwGCSettingFlags +{ + kEtwGCFlagConcurrent = 0x00000001, + kEtwGCFlagLargePages = 0x00000002, + kEtwGCFlagFrozenSegs = 0x00000004, + kEtwGCFlagHardLimitConfig = 0x00000008, + kEtwGCFlagNoAffinitize = 0x00000010, +}; + #ifndef FEATURE_REDHAWK #if defined(FEATURE_EVENT_TRACE) @@ -726,6 +735,7 @@ namespace ETW friend class ETW::MethodLog; #ifdef FEATURE_EVENT_TRACE static VOID SendThreadRundownEvent(); + static VOID SendGCRundownEvent(); static VOID IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions); static VOID IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions); static VOID IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions); diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index 0eed049c17b..1841981ba3d 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -139,6 +139,8 @@ + + @@ -453,10 +455,16 @@ + + + + + + @@ -511,6 +519,10 @@ + + + + @@ -1133,6 +1145,32 @@ + +